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).&nbsp;
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.&nbsp; Such alternatives are listed
42for completeness, and to explain why we think they're not worth trying.&nbsp;
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.&nbsp; 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.&nbsp; 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".&nbsp; 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 &amp; 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.&nbsp; 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.&nbsp; This is impractical for very large domains, and inefficient
124(in space and time) for very sparse domains.&nbsp; One alternative is to
125use a list of intervals representation &aacute; l&agrave; the FD solver.&nbsp;
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 (&amp; 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.&nbsp; 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.&nbsp; 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.&nbsp; In such cases compile-time
211transformations are disabled in order to avoid the situation where the
212transformation actually makes the constraint less efficient.&nbsp; (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.)&nbsp;
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.&nbsp; 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.&nbsp; 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&nbsp; "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.&nbsp; 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).&nbsp; 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) &amp; integer specialisation</b></dt>
325
326<dt>
327<a NAME="strict"></a><b>"Strict" flag</b></dt>
328
329<dd>
330In ECLiPSe 5.5, =&lt; and &lt; 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 =&lt;, which is not
334strict, is >, which is strict; similarly with &lt; 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 &lt;
349Y is just the logical negation of Y =&lt; X.&nbsp; Thus, by "negating"
350the reification boolean associated with a constraint, any strict inequality
351can be transformed into a non-strict inequality.&nbsp; 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.&nbsp; 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.&nbsp;
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.&nbsp; 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.&nbsp; 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.&nbsp; 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.&nbsp;
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.&nbsp;
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 =&lt;</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.&nbsp; 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.&nbsp;
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.&nbsp; This is likely to save at least a little memory;
514how much depends on other choices.&nbsp; 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.&nbsp;
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 =&lt; RHSConstant</li>
547
548<li>
549LHSLinExpr > RHSConstant</li>
550
551<li>
552LHSLinExpr &lt; RHSConstant</li>
553</ul>
554In practice,
555<blockquote>&nbsp;
556<table CELLSPACING=0 NOSAVE >
557<tr ALIGN=CENTER NOSAVE>
558<td NOSAVE>
559<center>LHSLinExpr =\= RHSConstant</center>
560</td>
561
562<td>&nbsp;&nbsp;&nbsp; is the logical negation of&nbsp;</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 =&lt; -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 =&lt; RHSConstant</td>
581</tr>
582
583<tr ALIGN=CENTER NOSAVE>
584<td>LHSLinExpr &lt; RHSConstant</td>
585
586<td>is the logical negation of</td>
587
588<td ALIGN=CENTER NOSAVE>-LHSLinExpr =&lt; -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 =&lt;.
595<p>Note that ECLiPSe 5.5 does not have just these two alternatives; it
596also has &lt;, 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.&nbsp; 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.&nbsp; 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.&nbsp; 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.&nbsp; 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.&nbsp; 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.&nbsp; The linear terms are stored in a list with one entry for
779each term, recording its variable and coefficient.&nbsp; 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>).&nbsp;
785One option would be to have the term structure contain three fields: the
786coefficient, the variable, and a pointer to the next term.&nbsp; 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.&nbsp; 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.&nbsp; 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.&nbsp; Conceptually, it consists of a vector with
804an entry for each term, recording its variable and coefficient.&nbsp; In
805practice, however, the variables should go into a separate vector in order
806to make the most efficient use of memory.&nbsp; 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.&nbsp; 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.&nbsp; 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).&nbsp; (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>).&nbsp;
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.&nbsp; In such cases, when a variable
834becomes ground we wish to eliminate it from the vector.&nbsp; 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.&nbsp; (Note that this
837means that this representation is not stable: the relative order of terms
838can change.&nbsp; It also means we cannot have any external references
839to terms in the constraint that depend on their positions.)&nbsp; 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.&nbsp;
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.&nbsp; Rather than store this in
847the trail, we can store it in the newly unused location at the end of the
848vector.&nbsp; 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.&nbsp; (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.&nbsp; Note also that we need to swap the "raw" variable
854references, without any dereferencing, so that the references remain safe
855on backtracking.)&nbsp; 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.&nbsp; 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.&nbsp; (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.&nbsp; 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>&nbsp;
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.&nbsp; 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".&nbsp; 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 =&lt; (or perhaps any of &lt;, >=, >, =\=?).</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.&nbsp;
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.&nbsp; (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.&nbsp; 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.&nbsp; Hence the same predicate
991will be called with the same format of data to restore consistency for
992all kinds of linear constraints.&nbsp; 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.&nbsp; 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.&nbsp; 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.&nbsp; For example, consider
1032the constraint
1033<pre>&nbsp;&nbsp;&nbsp; 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.&nbsp; 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.&nbsp; In other cases integrality may depend on the values taken
1039by the variables:
1040<pre>&nbsp;&nbsp;&nbsp; 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.&nbsp; :)</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).&nbsp; 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.&nbsp;
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".&nbsp; 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).&nbsp;
1096If any var grounded to non-int value, clear flag.&nbsp; If notice all vars
1097integral, clear flag?&nbsp; If notice only one var non-integral and has
1098unit coef, make integral.&nbsp; 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).&nbsp;
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.&nbsp; 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.&nbsp;
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!&nbsp; Turning off debugging slows it down!&nbsp;
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.&nbsp; 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