1<!doctype html public "-//w3c//dtd html 4.0 transitional//en"> 2<!-- BEGIN LICENSE BLOCK 3 - Version: CMPL 1.1 4 - 5 - The contents of this file are subject to the Cisco-style Mozilla Public 6 - License Version 1.1 (the "License"); you may not use this file except 7 - in compliance with the License. You may obtain a copy of the License 8 - at www.eclipse-clp.org/license. 9 - 10 - Software distributed under the License is distributed on an "AS IS" 11 - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 12 - the License for the specific language governing rights and limitations 13 - under the License. 14 - 15 - The Original Code is The ECLiPSe Constraint Logic Programming System. 16 - The Initial Developer of the Original Code is Cisco Systems, Inc. 17 - Portions created by the Initial Developer are 18 - Copyright (C) 2006 Cisco Systems, Inc. All Rights Reserved. 19 - 20 - Contributor(s): 21 - 22 - END LICENSE BLOCK --> 23<html> 24<head> 25 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> 26 <meta name="GENERATOR" content="Mozilla/4.76 [en] (X11; U; SunOS 5.7 sun4u) [Netscape]"> 27</head> 28<body> 29 30<h1> 31Factors believed to be impacting IC performance</h1> 32The following is a list of factors which are believed to be impacting the 33performance of the IC solver (as of ECLiPSe 5.5) along with how they're 34expected to be addressed in the rewrite (intended for ECLiPSe 5.6). 35Note that these issues (and the rewrite) focus on linear constraints: these 36are by far the most common kind of constraint, and thus the most important 37to make fast (and any improvements will have the most impact). 38<p>It would be undesirable to implement and evaluate all of the alternatives 39discussed here, since it would be a waste of manpower to do this for alternatives 40we have reason to believe are likely to be worse, or would be costly to 41implement for little expected benefit. Such alternatives are listed 42for completeness, and to explain why we think they're not worth trying. 43Also, it is not expected to be possible to implement and properly evaluate 44even all the "worthwhile" alternatives before the 5.6 release. 45<dl> 46<dt> 47<a NAME="rounding mode"></a><b>Processor rounding modes</b></dt> 48 49<dd> 50The use of processor rounding modes for interval arithmetic (rather than 51conservatively rounding all results) was implemented for the 5.5 release. 52It would have been quite difficult to maintain both the new and old approaches 53at the same time, and no formal evaluation of the impact of this change 54has been done, but it seems fairly clear that using processor rounding 55modes is faster and more accurate, and it allowed the simplification of 56a fair bit of code. The trade-off is that a small amount of code 57needed to be written for each combination of operating system and processor.</dd> 58 59<dd> 60(Is there still some work to be done on simplifying other code? E.g. linearize 61or something?)</dd> 62 63<dt> 64<a NAME="set_var_type"></a><b>set_var_type</b></dt> 65 66<dd> 67The primitive IC constraints that appear in code after compile-time transformation 68typically assume that all variables are already IC variables, and that 69any variables required to be integral have already been constrained to 70be integral. This results in lots of calls to set_var_type/2 in transformed 71code, most of which would be expected to be redundant, since most variables 72appearing in constraints will already have appeared in other constraints, 73and hence don't need to be "set up". It ought to be more efficient 74to handle the cases where this is not the case in the C code which sets 75up a constraint (at least for constraints which have C code to set them 76up... :) - without using exceptions if possible (I can't see why they'd 77be needed anyway).</dd> 78 79<dt> 80<a NAME="attrs vs vars"></a><b>sync_ic_bounds & attrs vs. vars</b></dt> 81 82<dd> 83Having constraints keep references to its variables' attributes rather 84than the variables themselves seemed like a good idea during the initial 85IC implementation, since it saved dereferencing the variables each time 86the constraint was propagated. Unfortunately it means that if two variables 87are unified, the attribute which would normally be thrown away must instead 88be kept, and kept in sync with the retained attribute, in case one or more 89constraints still have references to that attribute. Unifications do occur 90in "normal" programs, and the synchronisation is expensive. Plus, in a 91number of cases we want the actual variable instead or as well, which effectively 92negates the benefit. Hence we think constraints should keep references 93to the variables themselves rather than to the attributes.</dd> 94 95<dd> 96In order to evaluate the overhead of dereferencing the variables, it is 97proposed to construct an experiment which simulates the access cost of 98a pass over a constraint stored using variables (for various chain lengths) 99against the same constraint stored using attributes. By evaluating 100this overhead, it should be possible to decide which arrangement should 101be better without having to have both schemes implemented in the full-blown 102constraint solver.</dd> 103 104<dt> 105<a NAME="coefficient representation"></a><b>Coefficient representation</b></dt> 106 107<dd> 108Currently, constraint coefficients are stored in their "native" form (after 109floats are converted to bounded reals). Since these are then converted 110to a pair of floating point bounds and an integrality flag every time they 111are used, it may be worth storing them in the converted form instead (this 112may take more space, but should be faster).</dd> 113 114<dd> 115It should be fairly straightforward to have both schemes implemented, with 116a compile-time switch between them, for comparison purposes.</dd> 117 118<dt> 119<a NAME="domain representation"></a><b>Domain representation</b></dt> 120 121<dd> 122Integer domains with elements excluded are currently represented using 123a bitmap. This is impractical for very large domains, and inefficient 124(in space and time) for very sparse domains. One alternative is to 125use a list of intervals representation á là the FD solver. 126It's probably worth trying to devise a standard "holey domain" interface 127which would be supported by all alternative implementations, to facilitate 128interchanging between them (either for experimentation purposes or permanent 129substitution of one by another).</dd> 130 131<dt> 132<a NAME="constraint reduction"></a><b>Constraint reduction</b></dt> 133 134<dd> 135For the integer solvers developed for my PhD, it was shown that simplifying 136constraints as variables became ground was worthwhile. For the current 137implementation of IC, this is probably not true, at least when simplifying 138to the small "specialised" constraints, since that involves killing the 139old constraint and setting up the new one (which then gets propagated even 140though the old constraint has already done the propagation). In the new 141implementation, I plan to have (linear) constraints continue to look the 142same at the ECLiPSe level, even when simplification and specialisation 143is going on at the C level. Thus there will be no killing of constraints 144(unless they're entailed) and no setting up of new constraints just because 145the constraint has been simplified. With the reduction in overheads of 146the simplification, it should be the case that the simplification is worthwhile; 147that said, it should be straightforward to allow the simplification to 148be turned off at compile-time, for comparison purposes.</dd> 149 150<dt> 151<a NAME="waking frequency"></a><b>Waking frequency and suspension mechanisms</b></dt> 152 153<dd> 154With constraints implemented as demons, equations always wake themselves, 155even if there is no need (though there may be need in some cases). It is 156possible to avoid this if IC manages its own waking lists, though "normal" 157ECLiPSe waking lists would also be required for use by the user. Note that 158such self-managed lists, if done in the right way, could be used to support 159substitutions (of one variable by a multiple of another, or "eager" ground 160substitutions) in future.</dd> 161 162<dd> 163( What about reified (& other?) constraints which may not need to suspend 164on as many things as they used to once more information (e.g. the value 165of the boolean) is known? )</dd> 166 167<dd> 168It appears that it may be possible to do variable by variable substitutions 169in a lazy fashion, in the same way that ground substitutions are done now 170(during propagation variables are checked to see if they're ground and 171if so, are substituted out). This would mean no special infrastructure 172would be needed for substitutions, but would incur the overhead of performing 173these checks on each variable accessed during propagation. It also may 174not be compatible with techniques for efficiently propagating long linear 175constraints (this would need further investigation).</dd> 176 177<dd> 178With a modest amount of work it should be possible to have both standard 179ECLiPSe and self-managed waking lists implemented, with a compile-time 180switch between them, for comparison purposes.</dd> 181 182<dd> 183( "Delayed suspension set-up" technique? )</dd> 184 185<dt> 186<a NAME="compile transforms"></a><b>Compile-time transforms</b></dt> 187 188<dd> 189It is not clear how much benefit these actually bring. If the constraint 190was compile-time transformable and all the things that looked like variables 191at compile time are still variables at run time (i.e. they have not become 192ground) then the benefit is clear. However, in order to limit the 193amount of code duplication, the compile-time transformations as they stand 194actually introduce a reasonable amount of overhead if the goal has to be 195processed at run time. This is because the goal is first transformed 196(sharing code with the compile-time transformations), and then the transformed 197version call/1ed.</dd> 198 199<dd> 200Unfortunately, many constraints are (re-)transformed at run time:</dd> 201 202<ul> 203<li> 204Constraints where compile-time transformation is not possible since part 205or all of the constraint is unknown at compile time (e.g. constraints call/1ed, 206constraints containing eval/1).</li> 207 208<li> 209Constraints containing anything while looks non-linear at compile time, 210in case it's actually linear at run time. In such cases compile-time 211transformations are disabled in order to avoid the situation where the 212transformation actually makes the constraint less efficient. (An 213expression such as A*X compiled as a non-linear with A ground propagates 214less efficiently than it would as part of a linear expression --- and also 215less effectively if X appears elsewhere in the linear expression.) 216Note that constraints containing "constants" which are not known until 217run time are actually fairly common in "real" programs: constants are often 218computed, read from a file, extracted from a table, passed in from some 219other part of the program, etc.</li> 220</ul> 221 222<dd> 223Note that even with the above, it is possible for a compile-transformed 224version of a constraint to be less efficient and less effective than that 225same constraint processed at run time, if a linear constraint contains 226two variables which look different at compile time but end up being the 227same variable at run time.</dd> 228 229<dd> 230We propose changing the focus to making sure that run-time processing of 231a constraint is streamlined and efficient, with compile-time transformation 232machinery assessed for its impact on:</dd> 233 234<ul> 235<li> 236The efficiency of any run-time processing (both when the transformation 237was and was not possible at compile time).</li> 238 239<li> 240The efficiency and the effectiveness of the constraints that end up being 241set up at run time.</li> 242</ul> 243 244<dd> 245It would also be nice to get rid of the need for eval/1; this is an artifact 246of the way compile-time transformations are currently handled. Apart 247from simply abolishing compile-time transformations, eval/1 could be avoided 248by making the constraints the transformations produce able to cope with 249a run-time expression in any place they currently expect a variable.</dd> 250 251<dd> 252Note that the efficiency of constraint set-up is of most importance during 253search, and thus it is important to make sure that constraints which are 254likely to be imposed during search (typically simple constraints with one 255or two variables and a run-time constant) are set up efficiently.</dd> 256 257<dt> 258<a NAME="priorities"></a><b>Priorities</b></dt> 259 260<dt> 261<a NAME="bounded real arithmetic"></a><b>Floating point interval arithmetic</b></dt> 262 263<dd> 264When propagating a constraint, all computation is done using interval arithmetic 265performed on floating point bounds, even when the constraint is an integer 266constraint. Compared to a pure integer solver, this is potentially 267slower in two ways:</dd> 268 269<ul> 270<li> 271Computations are floating point rather than integer (we get almost no pipelining 272of these computations at the moment, so latency is more important than 273throughput, so integer operations would be expected to be faster even on 274processors with good floating point throughput).</li> 275 276<li> 277Computations are always done with both bounds when (for propagation) it 278may be the case that only one bound is needed.</li> 279</ul> 280 281<dd> 282Note that neither of these overheads is simply wasted:</dd> 283 284<ul> 285<li> 286The use of floating point values deals very neatly with the integer overflow 287problem; integer overflow would otherwise need to be trapped and dealt 288with, likely requiring at least some code specific to each supported operating 289system / processor combination (similar to the support needed for <a href="#rounding mode">processor 290rounding modes</a>).</li> 291 292<li> 293The "other" bound computed by the interval arithmetic, if not needed 294for propagation, is used to check entailment; if a constraint is entailed, 295it can no longer result in any propagation and may be killed, thus avoiding 296any future (futile) propagations of the constraint. Note that:</li> 297 298<ul> 299<li> 300Entailment usually cannot be detected without computing this "extra" bound.</li> 301 302<li> 303For equations, it may be that only one bound is needed, but the standard 304ECLiPSe propagation mechanism gives us no way to know which one it is or 305whether both are actually needed.</li> 306 307<li> 308Always computing both bounds keeps the code relatively simple...</li> 309</ul> 310</ul> 311 312<dd> 313I do not expect to change either of these things in the rewrite, mostly 314due to the extra complexity they entail (having integer-based versions 315of the propagation code with overflow trapping, or threading flags about 316which bounds are of interest through all the places in the code which need 317to know this information). Still, it may be worthwhile evaluating 318how much (best case) potential speed benefit there might be in using integer 319arithmetic, by creating quick-and-dirty pure integer versions of some of 320the linear constraints (ignoring overflow) and comparing the results (Andy 321Cheadle has already done some work along these lines).</dd> 322 323<dt> 324<b>Integer bounds (global constraints) & integer specialisation</b></dt> 325 326<dt> 327<a NAME="strict"></a><b>"Strict" flag</b></dt> 328 329<dd> 330In ECLiPSe 5.5, =< and < constraints were distinguished through the 331use of a "strict" flag which was passed to each relevant constraint. This 332flag (along with the reified boolean) resulted in a lot of if-then-else 333code (the boolean featured because the negation of =<, which is not 334strict, is >, which is strict; similarly with < and >=). This was a 335problem for two reasons:</dd> 336 337<ul> 338<li> 339The code was hard to maintain, since there were many cases to write/update/check/test, 340and evidenced by a number of bugs showing up later for various strictness 341and boolean combinations.</li> 342 343<li> 344The code was inefficient, due to the number of tests and amount of branching.</li> 345</ul> 346 347<dd> 348Most of these problems can be avoided, however, by observing that X < 349Y is just the logical negation of Y =< X. Thus, by "negating" 350the reification boolean associated with a constraint, any strict inequality 351can be transformed into a non-strict inequality. In a similar fashion, 352any =\= constraint may be transformed into an =:= constraint.</dd> 353 354<dd> 355This problem is addressed by the <a href="#boolean toggle">reification 356boolean toggle</a>, with the constraint transformations and canonical forms 357covered in the discussion of the <a href="#equation flag">equation/inequation 358flag</a>.</dd> 359 360<dt> 361<b>Constraint management</b></dt> 362 363<dd> 364We believe there is currently too much "management" of constraints at the 365ECLiPSe level: manipulating them, transforming them from one kind to another 366(see <a href="#constraint reduction">constraint reduction</a>), processing 367results returned from C-level propagators, etc. Such management should 368probably be kept to a minimum, and as much as possible done in C (at least 369once the constraints are set up).</dd> 370</dl> 371 372<h1> 373Design</h1> 374A constraint goes through several layers / phases during its lifetime. 375These are: 376<dl> 377<dt> 378<b>Preprocessing</b></dt> 379 380<dd> 381Substitution of floats by bounded reals, constant folding, calling user-defined 382functions, etc.</dd> 383 384<dt> 385<b>Transformation</b></dt> 386 387<dd> 388Breaking the constraint up into primitive constraints (linear constraints 389and nonlinear equations), putting them into normal form, etc.</dd> 390 391<dt> 392<b>Set-up</b></dt> 393 394<dd> 395Creating suspensions, adding them to suspension lists, setting up any required 396data structures, etc. This may be before initial consistency is achieved 397(using the propagation phase for this) or after (saving the cost of this 398set-up in the cases where attempting to achieve initial consistency would 399detect failure or entailment).</dd> 400 401<dt> 402<b>Propagation</b></dt> 403 404<dd> 405Waking up on relevant changes and maintaining consistency, etc.</dd> 406 407<dt> 408<b>Entailment</b></dt> 409 410<dd> 411Cleaning up any remaining suspensions, etc. if the propagation phase detects 412entailment.</dd> 413</dl> 414Note that the above phases need not necessarily be distinct or in order 415either in the code or during execution. For instance, the preprocessing 416may be done as part of the constraint transformation, the preprocessing 417and transformation phases may be split into compile-time and run-time components, 418and constraint set-up may be done after the first propagation pass. 419<p>That said, the preprocessing and transformation phases ought to be largely 420independent of the remaining phases. As a result, we choose to defer 421the design of the preprocessing and transformation phases at this time, 422and concentrate for now on constraint set-up, propagation and entailment. 423We also concentrate on linear constraints, since, as noted earlier, they 424are the most important; they may also be considered independent of the 425non-linear equation constraints. 426<h2> 427<a NAME="constraint struct"></a>Internal representation of linear constraints</h2> 428Linear constraints are (conceptually) stored as <i>LinExpr op Constant 429: Bool</i>, where <i>LinExpr</i> is a list of <i>Coefficient * Variable</i> 430pairs, <i>op</i> is a relational operator, <i>Constant</i> and the <i>Coefficient</i>s 431are ground numbers, <i>Bool</i> reflects the reification status of the 432constraint, and the <i>Variable</i>s are (non-ground) variables. 433They are actually stored in a structure with the following fields: 434<dl> 435<dt> 436<b>Flags</b></dt> 437 438<dl> 439<dt> 440<a NAME="struct equation flag"></a><b><a href="#equation flag">Equation/inequation</a></b></dt> 441 442<dd> 443= or =<</dd> 444 445<dt> 446<b>Integrality?</b></dt> 447 448<dd> 449Shouldn't be necessary once constraint set up, but might be worth remembering 450for printing?</dd> 451 452<dt> 453<a NAME="struct int prop flag"></a><b><a href="#int prop flag">Integrality 454propagation</a></b></dt> 455 456<dd> 457Whether the constraint could potentially propagate integrality to one of 458its variables.</dd> 459 460<dt> 461<a NAME="struct boolean toggle"></a><b><a href="#boolean toggle">Reification 462boolean toggle</a></b></dt> 463 464<dd> 465The reification boolean var/value is the opposite of what is intended.</dd> 466</dl> 467 468<dt> 469<a NAME="struct boolean"></a><b><a href="#boolean">Reification boolean 470variable</a></b></dt> 471 472<dd> 473Reified boolean variable for the constraint.</dd> 474 475<dt> 476<a NAME="struct rhs"></a><b><a href="#rhs">RHS constant</a></b></dt> 477 478<dd> 479The RHS constant of the constraint, either in "natural" form, or as integrality 480flag + bounds.</dd> 481 482<dt> 483<a NAME="struct term count"></a><b><a href="#term count">Term count</a></b></dt> 484 485<dd> 486Count of the number of linear terms in the constraint. Only needed 487if the LHS term stored as a <a href="#lhs vector">vector</a>, but might 488be useful for distinguishing 1, 2, and 3+ variable constraints.</dd> 489 490<dt> 491<a NAME="struct lhs"></a><b><a href="#lhs">LHS term vector/list</a></b></dt> 492 493<dd> 494A vector or list of the linear terms on the LHS of the constraint. 495Each term comprises:</dd> 496 497<dl> 498<dt> 499<b>Coefficient</b></dt> 500 501<dd> 502Either in "natural" form, or as integrality flag + bounds.</dd> 503 504<dt> 505<b>Variable</b></dt> 506 507<dd> 508Either as itself, or as its attribute.</dd> 509</dl> 510</dl> 511Note that the flags, the <a href="#rhs">RHS constant</a> and the <a href="#term count">term 512count</a> just contain ground "numerical" data, and so could be placed 513in a buffer structure. This is likely to save at least a little memory; 514how much depends on other choices. It can also help with trailing: 515the RHS constant and the term count are likely to change together, and 516they can either be updated in place or copied to a new buffer depending 517on whether there's been a new choice point since the last change. 518Note that the <a href="#int prop flag">integrality propagation flag</a> 519can also change (at most once) and need not (though it usually will) coincide 520with a ground term elimination; if it needs to be trailed (rather than 521in-place update) that could either be done separately or a new buffer created 522(on the assumption that some other change is likely before the next choice 523point which would have required the new buffer anyway, and the fact that 524if not, the "damage" is limited since it can only happen once during a 525forward execution). 526<h3> 527<a NAME="equation flag"></a>Equation/inequation flag</h3> 528<a href="#struct equation flag">This flag</a> is part of the <a href="#constraint struct">constraint 529structure</a>. 530<p>This flag addresses the <a href="#strict">"strict" flag performance 531issue</a>, in conjunction with the <a href="#boolean toggle">reification 532boolean toggle</a>. 533<p>This flag is fixed once the constraint is set up. 534<p>Ostensibly, this flag distinguishes between the different constraints 535<ul> 536<li> 537LHSLinExpr =:= RHSConstant</li> 538 539<li> 540LHSLinExpr =\= RHSConstant</li> 541 542<li> 543LHSLinExpr >= RHSConstant</li> 544 545<li> 546LHSLinExpr =< RHSConstant</li> 547 548<li> 549LHSLinExpr > RHSConstant</li> 550 551<li> 552LHSLinExpr < RHSConstant</li> 553</ul> 554In practice, 555<blockquote> 556<table CELLSPACING=0 NOSAVE > 557<tr ALIGN=CENTER NOSAVE> 558<td NOSAVE> 559<center>LHSLinExpr =\= RHSConstant</center> 560</td> 561 562<td> is the logical negation of </td> 563 564<td>LHSLinExpr =:= RHSConstant</td> 565</tr> 566 567<tr ALIGN=CENTER NOSAVE> 568<td NOSAVE>LHSLinExpr >= RHSConstant</td> 569 570<td>is just</td> 571 572<td NOSAVE>-LHSLinExpr =< -RHSConstant</td> 573</tr> 574 575<tr ALIGN=CENTER NOSAVE> 576<td>LHSLinExpr > RHSConstant</td> 577 578<td NOSAVE>is the logical negation of</td> 579 580<td NOSAVE>LHSLinExpr =< RHSConstant</td> 581</tr> 582 583<tr ALIGN=CENTER NOSAVE> 584<td>LHSLinExpr < RHSConstant</td> 585 586<td>is the logical negation of</td> 587 588<td ALIGN=CENTER NOSAVE>-LHSLinExpr =< -RHSConstant</td> 589</tr> 590</table> 591</blockquote> 592and so (assuming the presence of the <a href="#boolean toggle">reified 593boolean toggle</a>) we just need to be able to distinguish between =:= 594and =<. 595<p>Note that ECLiPSe 5.5 does not have just these two alternatives; it 596also has <, through the use of a <a href="#strict">"strict" flag</a>, 597which complicates the inequality code considerably. 598<h3> 599<a NAME="int prop flag"></a>Integrality propagation flag</h3> 600<a href="#struct int prop flag">This flag</a> is part of the <a href="#constraint struct">constraint 601structure</a>. 602<p>This flag may be set (i.e. to 1) when the constraint is set up, but 603after that it is only ever cleared (except for backtracking over such a 604clearing). 605<p>A general discussion of this flag and its purpose can be found in the 606section on <a href="#integrality and propagation">integrality and propagation</a>. 607<p>When the constraint is first set up, this flag is set only if: 608<ul> 609<li> 610the constraint is an equation (possibly of unknown reification status),</li> 611 612<li> 613the constraint is not an integer (#) constraint and</li> 614 615<li> 616all the coefficients and the <a href="#rhs">RHS constant</a> are integral.</li> 617</ul> 618It need not be set if: 619<ul> 620<li> 621all variables are already integral or</li> 622 623<li> 624exactly one variable is non-integral and it has a unit coefficient (in 625which case the variable should be constrained to be integral);</li> 626</ul> 627otherwise it should be set if permitted. 628<p>During execution, the flag is cleared if: 629<ul> 630<li> 631any variable has been grounded to a non-integer value or</li> 632 633<li> 634the reification status is grounded in such a way that the constraint is 635actually a disequation.</li> 636</ul> 637It may also (but need not) be cleared if: 638<ul> 639<li> 640all variables become integral;</li> 641</ul> 642otherwise the flag should not be cleared. It is expected that these 643checks and clearings may be done in the course of normal constraint propagation. 644<p>If the flag is set and there is exactly one remaining non-integer variable, 645that variable should be constrained to be integral if and only if: 646<ul> 647<li> 648the variable has a unit coefficient or</li> 649 650<li> 651the constraint is about to ground the variable to an integral value.</li> 652</ul> 653 654<h3> 655<a NAME="boolean toggle"></a>Reification boolean toggle</h3> 656<a href="#struct boolean toggle">This flag</a> is part of the <a href="#constraint struct">constraint 657structure</a>. 658<p>This flag addresses the <a href="#strict">"strict" flag performance 659issue</a>, in conjunction with the <a href="#equation flag">equation/inequation 660flag</a>. 661<p>This flag is fixed once the constraint is set up. 662<p>For any linear constraint, the user has the option of reifying the constraint 663by providing a variable which reflects the status of the constraint (a 664value 1 corresponding to entailment or enforcing of the constraint, 0 corresponding 665to disentailment or enforcing the negation of the constraint). Assuming 666that we wish to have one form of any constraint regardless of whether this 667variable is set to 0, 1 or remains undefined, this means there must be 668code to propagate both the constraint and its negation using the same constraint 669format. This suggests there should be just one form used for both a constraint 670and its negation, so that the same code can be used to propagate, say, 671<ul> 672<li> 673the constraint with reification boolean set to 1; and</li> 674 675<li> 676its negation with reification boolean set to 0</li> 677</ul> 678since they are exactly the same constraint. If the value of the reification 679boolean is provided at the time the constraint is set up then using just 680one form of the constraint is easy to support: if one is given the "negated" 681form, one simply substitutes the "normal" form and toggles the boolean. 682If, on the other hand, the reification boolean is still a variable, one 683could achieve the same effect by introducing a new boolean variable and 684constraining it to be the negation of the one provided, but this introduces 685an undesirable amount of overhead. 686<p>The alternative proposed here is to simply introduce a flag indicating 687whether the reification boolean should be "negated" whenever it is used 688or modified. This allows us to avoid introducing an extra boolean variable 689and constraint, and incurs minimal overhead: any value read from or written 690to the variable simply needs to be XORed with the flag. It also means we 691only need two types of linear constraint (see the section on the <a href="#equation flag">equation/inequation 692flag</a> for more details). 693<p>Note that it would be nice for this toggle to occupy the "1" bit of 694the flags word to facilitate its easy extraction for XORing. 695<h3> 696<a NAME="boolean"></a>Reification boolean variable</h3> 697<a href="#struct boolean">This boolean variable</a> is part of the <a href="#constraint struct">constraint 698structure</a>. 699<p>This variable may become instantiated after the constraint is set up. 700<p>This boolean is used to reflect or enforce the reification status of 701the constraint. It is stored as a reference to the variable (rather 702than a reference to the variable's attribute) since once the constraint 703is set up, the attribute(s) of the variable are irrelevant; there are only 704three states of interest: the value 0, the value 1 or still a variable. 705<p>Note that the interpretation of this boolean is modified by the <a href="#boolean toggle">reification 706boolean toggle</a>. 707<p>(Do a table?) 708<h3> 709<a NAME="constant"></a>Numeric constant</h3> 710Constants appear in several places in the <a href="#constraint struct">constraint 711structure</a>. 712<p>Their representation addresses the <a href="#coefficient representation">coefficient 713representation issue</a>. 714<p>The value of these constants may or may not change, depending on the 715nature of the constant and whether <a href="#constraint reduction">constraint 716reduction</a> is being performed or not. 717<p>Possible representations to be considered: 718<ol> 719<li> 720Native ECLiPSe format.</li> 721 722<li> 723Integrality flag plus floating point bounds.</li> 724 725<li> 726Integrality flag plus floating point bounds, except treat specially the 727case where the bounds are equal so that only one floating point number 728needs to be stored.</li> 729</ol> 730We intend to start by comparing options 1 and 2, with other alternatives 731being considered later. 732<p>It turns out that the integrality of each individual constant in a linear 733constraint need not be stored; a single <a href="#int prop flag">integer 734propagation flag</a> is sufficient. See the section on <a href="#integrality and propagation">integrality 735and propagation</a> for details. 736<h3> 737<a NAME="rhs"></a>RHS constant</h3> 738<a href="#struct rhs">This number</a> is part of the <a href="#constraint struct">constraint 739structure</a>. 740<p>This number is an instance of a <a href="#constant">numeric constant</a>, 741affected by the <a href="#coefficient representation">coefficient representation 742issue</a>. 743<p>This number may be modified after constraint set-up if <a href="#constraint reduction">constraint 744reduction</a> is being performed. 745<p>( Discuss representation? ) 746<h3> 747<a NAME="term count"></a>Term count</h3> 748<a href="#struct term count">This integer</a> is part of the <a href="#constraint struct">constraint 749structure</a>. 750<p>This integer is only needed if the <a href="#lhs">LHS terms</a> are 751being stored in <a href="#lhs vector">vector form</a> rather than <a href="#lhs list">list 752form</a> and <a href="#constraint reduction">constraint reduction</a> is 753being performed, but may also be useful for recognising the 1- and 2-variable 754special cases when using the <a href="#lhs list">list form</a>. 755<p>This integer may be modified after constraint set-up if <a href="#constraint reduction">constraint 756reduction</a> is being performed. 757<p>This integer should be stored adjacent to the <a href="#rhs">RHS constant</a> 758so that they may be trailed together (since they will usually both change 759at the same time). 760<p>This integer records the number of current entries in the <a href="#lhs vector">LHS 761term vector</a> (or list). 762<h3> 763<a NAME="lhs"></a>LHS terms</h3> 764<a href="#struct lhs">This list or vector</a> is part of the <a href="#constraint struct">constraint 765structure</a>. 766<p>This list or vector may be modified after constraint set-up if <a href="#constraint reduction">constraint 767reduction</a> is being performed. 768<p>This list or vector records the variables appearing in the linear constraint 769along with their coefficients. 770<p>The variables stored in this list or vector may become ground after 771constraint set-up. If <a href="#constraint reduction">constraint 772reduction</a> is being performed then any such ground variables will be 773removed from the representation. 774<p>We consider two basic forms for representing these linear terms: 775<h4> 776<a NAME="lhs list"></a>List representation</h4> 777This is the simpler of the two forms, and is what was in use before the 778rewrite. The linear terms are stored in a list with one entry for 779each term, recording its variable and coefficient. To save a little 780space, rather than using a normal list, one can simply add an extra field 781to the term structure giving the next term in the "list". 782<p>( Diagrams? ) 783<p>The exact form of the structure depends on choices made in representing 784the coefficient (see the section on <a href="#constant">numeric constants</a>). 785One option would be to have the term structure contain three fields: the 786coefficient, the variable, and a pointer to the next term. With this 787arrangement, looking at the tag in the coefficient field, one can distinguish 788between a structure or buffer containing a "normalised" coefficient, or 789any of the numeric types. This would allow us to efficiently support 790multiple alternative coefficient representations by simply changing the 791code which creates these terms; any code for accessing or modifying existing 792terms would not need to change. 793<p>If <a href="#constraint reduction">constraint reduction</a> is being 794performed, removing a term from this representation is simply a matter 795of setting the term's predecessor's "next" pointer to the same value as 796that of the term to be removed, trailing the change. Note that this 797representation is stable with respect to such changes; that is, the remaining 798terms are in the same relative order as they were before the removal. 799<h4> 800<a NAME="lhs vector"></a>Vector representation</h4> 801Management of this representation is a little more complicated than the 802list representation, but it should be more efficient in terms of both speed 803and memory consumption. Conceptually, it consists of a vector with 804an entry for each term, recording its variable and coefficient. In 805practice, however, the variables should go into a separate vector in order 806to make the most efficient use of memory. This is because while the 807coefficient data can be safely packed into a buffer structure without the 808need for any ECLiPSe tags on the individual entries, this is not possible 809for variables since the garbage collector needs to know the location of 810all variables. To store the variables, there are two obvious choices: 811<ul> 812<li> 813Use a normal structure, with each field being a variable, complete with 814tag.</li> 815 816<li> 817Introduce a new type of structure for holding a vector of variables without 818individual tags and teach the garbage collector about it. Note that 819such a structure can only contain references to variables, where the "true" 820variables (the self-references) reside elsewhere, since, due to their lack 821of individual tag, they may not be referenced anywhere (in particular, 822they may not reference themselves). (This is not a problem as such, 823just something to be aware of.)</li> 824</ul> 825For representing the coefficient vector in a buffer, there are a number 826of options available, depending on the choices made in representing the 827coefficient (see the section on <a href="#constant">numeric constants</a>). 828One interesting possibility is to have separate vectors for the upper and 829lower bounds of the coefficients; that way, if they are the same (e.g. 830for any integer constraint - as long as the coefficients are exactly representable 831as doubles) they can just both point to the same vector, saving space. 832<p>Where the vector representation gets interesting is when <a href="#constraint reduction">constraint 833reduction</a> is being performed. In such cases, when a variable 834becomes ground we wish to eliminate it from the vector. Since we 835cannot just delete an entry from the middle, the obvious thing to do is 836copy the last entry in the vector to fill the hole. (Note that this 837means that this representation is not stable: the relative order of terms 838can change. It also means we cannot have any external references 839to terms in the constraint that depend on their positions.) Since 840the terms no longer fill the vector we need a <a href="#term count">term 841count</a> in order to keep track of where the currently valid terms end. 842(This is necessarily distinct from the field in the vector's structure 843which records the size of that structure (and hence, indirectly, the initial 844number of terms in the constraint) for the garbage collector.) 845<p>In order to restore the constraint on backtracking, we need to have 846kept information about the removed term. Rather than store this in 847the trail, we can store it in the newly unused location at the end of the 848vector. That is, rather than simply overwriting the eliminated term 849with the last one, we can exchange their positions in the vector instead; 850on backtracking we just exchange them back. (Note that if the upper 851and lower bounds of the coefficients are in "separate" vectors which may 852be shared if identical, we need to avoid swapping the same data twice in 853the shared case. Note also that we need to swap the "raw" variable 854references, without any dereferencing, so that the references remain safe 855on backtracking.) Better yet, on backtracking we don't need to swap 856the terms back, we can just leave them where they are: if we were free 857to move the last term to fill the hole, there's no reason we can't just 858arbitrarily rearrange them as we like. This means we only need to 859trail the old values of the <a href="#rhs">RHS constant</a> and the <a href="#term count">term 860count</a>; moreover, we only need to do this once for a sequence of reductions 861performed when propagating the constraint (e.g. if a number of variables 862have become ground since the last time the constraint was propagated), 863which probably means there's little benefit to be gained from doing timestamping 864on this data. (Though if we put them in a separate (buffer) struct 865from the main constraint struct and trail the struct rather than the individual 866fields, we get timestamping anyway.) 867<p>Note that if multiple variables might have become ground, the following 868algorithm moves all the non-ground terms to the front of the vector with 869the minimal number of swaps: 870<ul> 871<li> 872Scan forward through the vector until the first ground term is found or 873the end of the constraint is reached.</li> 874 875<li> 876Scan backwards through the vector until the first non-ground term is found 877or it meets the forwards scan.</li> 878 879<li> 880If the scans have not crossed, exchange the terms and repeat, continuing 881the scans from their current positions.</li> 882</ul> 883It ought to be possible to incorporate the above into most propagation 884algorithms (at least, the ones which proceed through the constraint in 885a linear fashion but don't really care about the order); it simply corresponds 886to processing the terms in the order they are after the swaps rather than 887the order they were in before them. 888<h2> 889Internal representation of variable attributes</h2> 890 891<dl>This is expected to be more-or-less the same as the current version 892of IC, though we may consider moving some of the fields into a buffer structure 893or something, and we will be considering alternative representations of 894(holey) integer domains. Where practical, access to a variable's 895data should be mediated through a set of access routines, in order to facilitate 896trying different alternatives. 897<br> 898<dt> 899Flags</dt> 900 901<dt> 902Integral</dt> 903 904<dt> 905Lower bound</dt> 906 907<dt> 908Upper bound</dt> 909 910<dt> 911Finite domain</dt> 912 913<dd> 914Representation of holey integer domain, if required.</dd> 915 916<dt> 917ID?</dt> 918 919<dd> 920Could be useful for giving stable variable sort order (which is useful 921for detecting multiple occurrences of a variable).</dd> 922 923<dt> 924[waking lists]</dt> 925 926<dt> 927</dt> 928</dl> 929 930<h2> 931<a NAME="set-up"></a>Linear constraint set-up</h2> 932I envisage having a single predicate (implemented in C as far as practical) 933for setting up any linear constraint. The input constraint is conceptually 934of the form <i>LinExpr op 0 : Bool</i>, where <i>LinExpr</i> is a list 935of <i>Coefficient * Variable</i> pairs, <i>op</i> is a relational operator, 936the <i>Coefficient</i>s are ground numbers, <i>Bool</i> reflects the reification 937status of the constraint, and the <i>Variable</i>s are (non-ground or ground) 938"variables". The representation of the constraint would be via the 939following fields: 940<dl> 941<dt> 942<b>Operator</b></dt> 943 944<dd> 945=:= or =< (or perhaps any of <, >=, >, =\=?).</dd> 946 947<dt> 948<b>Integrality flag</b></dt> 949 950<dd> 951Whether to impose integrality on all the variables and coefficients.</dd> 952 953<dt> 954<b><a href="#boolean">Reification boolean</a></b></dt> 955 956<dd> 957Whether the constraint is to be imposed, negated, or unknown.</dd> 958 959<dt> 960<b><a href="#boolean toggle">Reification boolean toggle</a></b></dt> 961 962<dd> 963Whether the reification boolean is the opposite of what is intended. 964(Not needed if the full set of operators allowed.)</dd> 965 966<dt> 967<b>Linear expression</b></dt> 968 969<dd> 970A list of linear terms of the form A*X where A is ground but X may be a 971variable.</dd> 972</dl> 973The integrality flag and reification boolean toggle could be incorporated 974into the operator field by allowing the full set of relational operators 975(including all the # operators), but this does not seem useful since (prior 976to calling this set-up predicate) the operator will already have been examined, 977at which point it might as well be decomposed fully. (See the <a href="#equation flag">equation/inequation 978flag section</a> for a description of how the operator and boolean toggle 979fields relate to each other.) 980<p>The set-up predicate would be responsible for making sure that all variables 981are constrained to be IC variables, and, if integrality is required, imposing 982integrality on all the variables and checking the integrality of all the 983constants. It would also construct the <a href="#constraint struct">internal 984representation</a> of the constraint, enforce initial consistency and set 985up any required suspensions, etc. (though the initial consistency and suspension 986creation may be delegated to the propagation code if appropriate). 987<h2> 988<a NAME="propagation"></a>Linear constraint propagation</h2> 989As discussed above, we want to avoid having a constraint change its form 990(or really, its suspension) over its lifetime. Hence the same predicate 991will be called with the same format of data to restore consistency for 992all kinds of linear constraints. However, it is expected that, for 993efficiency, it will be worth distinguishing between: 994<dl> 995<dt> 996one variable constraints</dt> 997 998<dt> 999two variable constraints</dt> 1000 1001<dd> 1002These would use the obvious simple algorithms, and would probably also 1003specialise based on the operator and/or the state of the reification boolean.</dd> 1004 1005<dt> 1006longer constraints</dt> 1007 1008<dd> 1009These would use the "two-pass" propagation algorithm or one of its variants 1010(except for =\= constraints, which warrant a different algorithm), and 1011may use general code regardless of the operator and reification boolean 1012state.</dd> 1013</dl> 1014It is expected that always storing the constraints in general form and 1015performing some switching to choose the correct specialised algorithm will 1016result in little overhead, and will allow constraint reductions to be performed 1017without the high cost of creating and destroying suspensions that is currently 1018incurred. 1019<p>( Talk about how we always use floating point interval math even when 1020it's not needed; discuss possible alternatives? ) 1021<h4> 1022<a NAME="integrality and propagation"></a>Integrality and propagation</h4> 1023In earlier versions of IC, it was important to know whether each intermediate 1024result was integral or not, in order to improve accuracy: bounds which 1025should be integral but did not have an integral value due to floating point 1026computations could be rounded in to the next integral value. With 1027the use of <a href="#rounding mode">processor rounding modes</a>, however, 1028this is no longer necessary: if a bound should have an integral value, 1029it will have one. This means the only thing integrality of coefficients, 1030intermediate results, etc. is needed for now is deducing when a non-integer 1031variable should be constrained to be integral. For example, consider 1032the constraint 1033<pre> X + Y =:= 3</pre> 1034If Y is an integer variable, then this implies that X should be an integer 1035variable too, since giving Y any value from its domain will result in an 1036integer value for X. In this case, the integrality of X can be deduced 1037just from looking at the constraint and the types of the variables appearing 1038in it. In other cases integrality may depend on the values taken 1039by the variables: 1040<pre> 2 * X + Y =:= 3</pre> 1041In this case, X is integral only if Y ends up being fixed to an odd number. 1042<p>Some observations about integrality: 1043<ul> 1044<li> 1045A constraint can only propagate integrality to a variable if it's an equation, 1046and hence could assign a specific (potentially integer) value to that variable 1047(e.g. when all the other variables become ground).</li> 1048 1049<li> 1050A constraint can only propagate integrality if all the coefficients and 1051other constants in the constraint are integral; if this is not the case, 1052then any computed value it wants to assign will not be integral.</li> 1053 1054<li> 1055If all variables are already integral (or become so later), there's nothing 1056to do. :)</li> 1057 1058<li> 1059If any variable gets grounded to a non-integer, the constraint cannot impose 1060integrality on any other variable.</li> 1061 1062<li> 1063If more than one variable is not (yet) integral, then no integrality can 1064be inferred (yet). E.g. if two are still real then the constraint 1065may be satisfied by assigning non-integer values to them both.</li> 1066 1067<li> 1068If there is a single non-integer variable and all the other conditions 1069are satisfied, then we have two cases depending on the value of the coefficient 1070of the non-integer variable:</li> 1071 1072<ul> 1073<li> 10741 or -1: the variable may be constrained to be integral immediately</li> 1075 1076<li> 1077any other value: the integrality of the variable may depend on the values 1078the other variables take and thus cannot be determined (at least, not efficiently) 1079until the constraint is about to assign it a value.</li> 1080</ul> 1081</ul> 1082( Talk about variables getting values from bounds (all remaining variables 1083grounded simultaneously) vs values (value of last variable computed based 1084on (externally-supplied) values of other variables)? ) 1085<p>This means we don't need to store the integrality of the coefficients 1086or the <a href="#rhs">RHS constant</a>; instead we can have one flag for 1087the whole constraint, indicating the potential for integer propagation, 1088which is cleared as soon as we know that no such propagation can occur. 1089Then if the flag is set and when propagating the constraint we discover 1090that there is one remaining non-integer variable and it has a unit coefficient, 1091or if we're about to ground the only-remaining non-integral variable to 1092an integral value, we impose integrality. 1093<p>Flag for "potential int propagation". Set initially iff (non-integer) 1094eqn, all coefs integral, at least one var not integral (if only one non-int 1095var and it has unit coef, simply make the var int and clear the flag). 1096If any var grounded to non-int value, clear flag. If notice all vars 1097integral, clear flag? If notice only one var non-integral and has 1098unit coef, make integral. If grounding last var and flag set and 1099value integral, make var integer. 1100<p>It would be nice to have integrality propagation separate from the bounds 1101propagation, so that it doesn't have to be incorporated into every bounds 1102propagation algorithm we implement and so that it need not impose any overhead 1103if it's not needed (many constraints in practice won't need it). 1104But a separate propagator makes it hard to ensure a variable is made integral 1105(if appropriate) before being assigned a value by the main propagator. 1106<h2> 1107<a NAME="entailment"></a>Linear constraint entailment</h2> 1108( Talk about detecting entailment, killing suspensions, leaving delayed 1109goals if the constraint is ground but not properly entailed, etc. ) 1110<h3> 1111Other notes / ideas / etc.</h3> 1112Have IC code call i_add, i_mul, etc. directly rather than going through 1113ec_ria_binop. 1114<p>Try to avoid setting up the constraint if first propagation pass is 1115all that is ever needed. 1116<h1> 1117Experiments</h1> 1118This section describes the experiments performed and evaluates the results. 1119<h2> 1120<a NAME="exp attrs vs vars"></a>Attributes vs. variables</h2> 1121 1122<dl> 1123<dt> 1124<b>Aim</b></dt> 1125 1126<dd> 1127Assess the overhead of dereferencing variables to access their attributes 1128as part of the constraint propagation process.</dd> 1129 1130<dt> 1131<b>Assumptions</b></dt> 1132 1133<dd> 1134We will only look at linear constraints; the overhead is expected to be 1135similar for other kinds of constraints.</dd> 1136 1137<dd> 1138We will assume that the linear constraint is stored as a list of Coefficient 1139* Variable terms. Other ways of storing or representing the linear 1140terms may have somewhat different overheads, but for the most part we're 1141interested in the amount of extra overhead introduced by the variable representation 1142so this should not be a problem.</dd> 1143 1144<dd> 1145We will assume that the number of linear terms in the constraint does not 1146affect the average dereferencing cost per variable, so that we can use 1147suitably long constraints in order to help obtain a reasonably measurable 1148CPU time.</dd> 1149 1150<dd> 1151We will assume that the arrangement of the variables and terms, etc. in 1152memory does not affect the average dereferencing cost per variable, so 1153that we need not do anything fancy when setting up the constraints. 1154(Note that this is probably the most dubious assumption; it may be worth 1155doing some kind of randomised arrangement.)</dd> 1156 1157<dd> 1158We will not look at linear constraints containing ground "variables", though 1159the overhead of detecting/accessing these for each representation may also 1160be worth looking at (though the attribute version is likely slower than 1161the variable version).</dd> 1162 1163<dt> 1164<b>Method</b></dt> 1165 1166<dd> 1167Construct a list of variables such that the reference to each variable 1168in the list is the head of a variable chain of desired length (e.g. 5, 116910, etc.).</dd> 1170 1171<dd> 1172Give each variable an IC attribute.</dd> 1173 1174<dd> 1175Construct a list of Coefficient * Attribute terms and a list of Coefficient 1176* Variable terms based on the list (using, for example, 1.0 as the coefficients).</dd> 1177 1178<dd> 1179Check that, for the Coefficient * Variable list, the variables do actually 1180need dereferencing the appropriate number of times.</dd> 1181 1182<dd> 1183For the Coefficient * Attribute list, perform a number of passes over the 1184list, extracting one of the fields of the attribute (e.g. the lower bound), 1185and record the total time taken.</dd> 1186 1187<dd> 1188Repeat for the Coefficient * Variable list.</dd> 1189 1190<dd> 1191Adjust the length of the "constraint" and the number of passes over the 1192list in order to get a run time which is large enough to be measureable.</dd> 1193 1194<dd> 1195Repeat the experiment for a number of different variable chain lengths 1196and a number of different architectures.</dd> 1197 1198<dt> 1199<b>Results</b></dt> 1200 1201<dd> 1202dog, using 5.6 #6</dd> 1203 1204<center><table BORDER NOSAVE > 1205<tr ALIGN=RIGHT NOSAVE> 1206<td NOSAVE></td> 1207 1208<td NOSAVE>Attribute</td> 1209 1210<td>Variable (1)</td> 1211 1212<td>Variable (5)</td> 1213 1214<td>Variable (10)</td> 1215</tr> 1216 1217<tr ALIGN=RIGHT NOSAVE> 1218<td>access lwb, 1000/1000</td> 1219 1220<td>0.58</td> 1221 1222<td NOSAVE>1.26</td> 1223 1224<td>1.58</td> 1225 1226<td>2.14</td> 1227</tr> 1228 1229<tr ALIGN=RIGHT NOSAVE> 1230<td>nodbgcomp, access lwb, 1000/1000</td> 1231 1232<td NOSAVE>0.47</td> 1233 1234<td>0.88</td> 1235 1236<td>1.44</td> 1237 1238<td>2.45</td> 1239</tr> 1240</table></center> 1241 1242<dd> 1243Note the last column! Turning off debugging slows it down! 1244This symptom also reproducible on alpha_linux: turning off variable names 1245results in dereferencing the reference chains taking about twice as long!</dd> 1246 1247<dd> 1248</dd> 1249 1250<dd> 1251Without more information about typical variable chain lengths and how often 1252IC variables get unified, there's no clear result here. It shouldn't 1253be too hard to collect statistics on this kind of thing, and we probably 1254should do this at some point, but since it should be relatively easy to 1255have both alternatives implemented, the conclusion is to just implement 1256them both and try them out on real programs to see how they do.</dd> 1257</dl> 1258 1259</body> 1260</html> 1261