OLD | NEW |
(Empty) | |
| 1 /* This is an implementation of the DeltaBlue incremental dataflow |
| 2 constraint solver written in portable C. |
| 3 |
| 4 The original version was by John Maloney. |
| 5 This version was modified for portability and benchmarking |
| 6 by Mario Wolczko, Sun Microsystems Labs, 2 Oct 96. |
| 7 */ |
| 8 |
| 9 #include <stdio.h> |
| 10 #include <stdlib.h> |
| 11 #include <string.h> |
| 12 #include <time.h> |
| 13 |
| 14 typedef enum {false, true} Boolean; |
| 15 typedef void (*Proc)(); |
| 16 typedef void * Element; |
| 17 |
| 18 /* |
| 19 List: Supports variable sized, ordered lists of elements. */ |
| 20 |
| 21 typedef struct { |
| 22 Element *slots; /* variable-sized array of element slots */ |
| 23 int slotCount; /* number of slots currently allocated */ |
| 24 int first; /* index of first element */ |
| 25 int last; /* index of last element (first-1, if empty) */ |
| 26 } *List, ListStruct; |
| 27 |
| 28 /* |
| 29 Constraint, variable, and strength data definitions for DeltaBlue. |
| 30 */ |
| 31 |
| 32 /* Strength Constants */ |
| 33 typedef enum { |
| 34 S_required= 0, |
| 35 S_strongPreferred= 1, |
| 36 S_preferred= 2, |
| 37 S_strongDefault= 3, |
| 38 S_default= 4, |
| 39 S_weakDefault= 5, |
| 40 S_weakest= 6 |
| 41 } Strength; |
| 42 |
| 43 struct constraint; |
| 44 |
| 45 typedef struct { |
| 46 long value; |
| 47 List constraints; |
| 48 struct constraint* determinedBy; |
| 49 long mark; |
| 50 Strength walkStrength; |
| 51 Boolean stay; |
| 52 char name[10]; |
| 53 } *Variable, VariableStruct; |
| 54 |
| 55 typedef struct constraint { |
| 56 Proc execute; |
| 57 Boolean inputFlag; |
| 58 Strength strength; |
| 59 char whichMethod; |
| 60 char methodCount; |
| 61 char varCount; |
| 62 char methodOuts[7]; |
| 63 Variable variables[1]; |
| 64 } *Constraint, ConstraintStruct; |
| 65 |
| 66 /* Other Constants and Macros */ |
| 67 #define NO_METHOD (-1) |
| 68 #define SATISFIED(c) ((c)->whichMethod != NO_METHOD) |
| 69 #define Weaker(a,b) (a > b) |
| 70 |
| 71 /* |
| 72 Implementation of List |
| 73 Invariants and relationships: |
| 74 slots != NULL |
| 75 slotCount > 0 |
| 76 sizeof(*slots) == slotCount * sizeof(Element) |
| 77 0 <= first < slotCount |
| 78 -1 <= last < slotCount |
| 79 last >= first (if not empty) |
| 80 last == first - 1 (if empty) |
| 81 NumberOfItems == (last - first) + 1 |
| 82 */ |
| 83 |
| 84 /* Private Prototypes */ |
| 85 void Error(char*); |
| 86 void Grow(List); |
| 87 void MakeRoom(List); |
| 88 char* StrengthString(Strength strength); |
| 89 |
| 90 /* Variables */ |
| 91 Variable Variable_Create(char *, long); |
| 92 Variable Variable_CreateConstant(char *, long); |
| 93 void Variable_Destroy(Variable); |
| 94 void Variable_Print(Variable); |
| 95 |
| 96 /* Constraints */ |
| 97 Constraint Constraint_Create(int, Strength); |
| 98 void Constraint_Destroy(Constraint); |
| 99 void Constraint_Print(Constraint); |
| 100 |
| 101 /* Miscellaneous */ |
| 102 void ExecutePlan(List); |
| 103 |
| 104 Constraint StayC(Variable v, Strength); /* keep v constant */ |
| 105 Constraint EditC(Variable v, Strength); /* change v */ |
| 106 Constraint EqualsC(Variable a, Variable b, Strength); /* a = b */ |
| 107 /* (src * scale) + offset = dest*/ |
| 108 Constraint ScaleOffsetC(Variable src, Variable scale, Variable offset, |
| 109 Variable dest, Strength); |
| 110 |
| 111 void InitDeltaBlue(void); |
| 112 void AddVariable(Variable); |
| 113 void DestroyVariable(Variable); |
| 114 void AddConstraint(Constraint); |
| 115 void DestroyConstraint(Constraint); |
| 116 List ExtractPlan(void); |
| 117 List ExtractPlanFromConstraint(Constraint); |
| 118 List ExtractPlanFromConstraints(List); |
| 119 |
| 120 |
| 121 |
| 122 /****** Create and Destruction ******/ |
| 123 |
| 124 List List_Create(int initialCount) |
| 125 { |
| 126 List newList; |
| 127 |
| 128 newList = (List) malloc(sizeof(ListStruct)); |
| 129 if (newList == NULL) Error("out of memory"); |
| 130 newList->slots = (Element *) malloc(initialCount * sizeof(Element)); |
| 131 if (newList->slots == NULL) Error("out of memory"); |
| 132 newList->slotCount = initialCount; |
| 133 newList->first = 0; |
| 134 newList->last = -1; |
| 135 return newList; |
| 136 } |
| 137 |
| 138 void List_Destroy(List list) |
| 139 { |
| 140 if (list->slots == NULL) Error("bad ListStruct; already freed?"); |
| 141 free(list->slots); |
| 142 list->slots = NULL; |
| 143 list->slotCount = 0; |
| 144 list->first = 0; |
| 145 list->last = -1; |
| 146 free(list); |
| 147 } |
| 148 |
| 149 /****** Enumeration and Queries ******/ |
| 150 |
| 151 void List_Do(List list, Proc proc) |
| 152 { |
| 153 Element *nextPtr = &(list->slots[list->first]); |
| 154 Element *lastPtr = &(list->slots[list->last]); |
| 155 |
| 156 while (nextPtr <= lastPtr) { |
| 157 (*proc)(*nextPtr++); |
| 158 } |
| 159 } |
| 160 |
| 161 int List_Size(List list) |
| 162 { |
| 163 return (list->last - list->first) + 1; |
| 164 } |
| 165 |
| 166 void* List_At(List list, int index) |
| 167 { |
| 168 if (index < 0 || index > list->last - list->first + 1) |
| 169 Error("List access out of bounds"); |
| 170 return list->slots[index + list->first]; |
| 171 } |
| 172 |
| 173 /****** Adding ******/ |
| 174 |
| 175 void List_Add(List list, Element element) |
| 176 { |
| 177 if (list->last >= (list->slotCount - 1)) MakeRoom(list); |
| 178 list->slots[++list->last] = element; |
| 179 } |
| 180 |
| 181 void List_Append(List list1, List list2) |
| 182 { |
| 183 Element *nextPtr = &(list2->slots[list2->first]); |
| 184 Element *lastPtr = &(list2->slots[list2->last]); |
| 185 |
| 186 while (nextPtr <= lastPtr) { |
| 187 List_Add(list1, *nextPtr++); |
| 188 } |
| 189 } |
| 190 |
| 191 /****** Removing ******/ |
| 192 |
| 193 void List_Remove(List list, Element element) |
| 194 { |
| 195 Element *srcPtr = &list->slots[list->first]; |
| 196 Element *destPtr = &list->slots[0]; |
| 197 Element *lastPtr = &list->slots[list->last]; |
| 198 |
| 199 list->last = list->last - list->first; |
| 200 list->first = 0; |
| 201 while (srcPtr <= lastPtr) { |
| 202 if (*srcPtr == element) { |
| 203 list->last--; |
| 204 } else { |
| 205 *destPtr++ = *srcPtr; |
| 206 } |
| 207 srcPtr++; |
| 208 } |
| 209 } |
| 210 |
| 211 Element List_RemoveFirst(List list) |
| 212 { |
| 213 Element element; |
| 214 |
| 215 if (list->last < list->first) return NULL; |
| 216 element = list->slots[list->first++]; |
| 217 return element; |
| 218 } |
| 219 |
| 220 void List_RemoveAll(List list) |
| 221 { |
| 222 list->first = 0; |
| 223 list->last = -1; |
| 224 } |
| 225 |
| 226 /****** Private ******/ |
| 227 |
| 228 #define max(x, y) ((x) > (y) ? (x) : (y)) |
| 229 #define min(x, y) ((x) < (y) ? (x) : (y)) |
| 230 |
| 231 void Error(char *errorString) |
| 232 { |
| 233 printf("error: %s.\n", errorString); |
| 234 exit(-1); |
| 235 } |
| 236 |
| 237 void Grow(List list) |
| 238 { |
| 239 list->slotCount += min(max(list->slotCount, 2), 512); |
| 240 list->slots = realloc(list->slots, (list->slotCount * sizeof(Element))); |
| 241 if (list->slots == NULL) Error("out of memory"); |
| 242 } |
| 243 |
| 244 void MakeRoom(List list) |
| 245 { |
| 246 Element *srcPtr = &list->slots[list->first]; |
| 247 Element *destPtr = &list->slots[0]; |
| 248 Element *lastPtr = &list->slots[list->last]; |
| 249 |
| 250 if (((list->last - list->first) + 1) >= list->slotCount) Grow(list); |
| 251 if (list->first == 0) return; |
| 252 while (srcPtr <= lastPtr) { |
| 253 *destPtr++ = *srcPtr++; |
| 254 } |
| 255 list->last = list->last - list->first; |
| 256 list->first = 0; |
| 257 } |
| 258 |
| 259 /* |
| 260 Constraint, variable, and other operations for DeltaBlue. |
| 261 */ |
| 262 |
| 263 /******* Private *******/ |
| 264 |
| 265 void Execute(Constraint c) |
| 266 { |
| 267 c->execute(c); |
| 268 } |
| 269 |
| 270 void Noop(Constraint c) |
| 271 { |
| 272 /* default execute procedure; does nothing */ |
| 273 }; |
| 274 |
| 275 /******* Variables *******/ |
| 276 |
| 277 Variable Variable_Create(char *name, long initialValue) |
| 278 { |
| 279 Variable new; |
| 280 |
| 281 new = (Variable) malloc(sizeof(VariableStruct)); |
| 282 if (new == NULL) Error("out of memory"); |
| 283 new->value = initialValue; |
| 284 new->constraints = List_Create(2); |
| 285 new->determinedBy = NULL; |
| 286 new->mark = 0; |
| 287 new->walkStrength = S_weakest; |
| 288 new->stay = true; |
| 289 strncpy(new->name, name, 10); |
| 290 new->name[9] = 0; |
| 291 AddVariable(new); |
| 292 return new; |
| 293 } |
| 294 |
| 295 Variable Variable_CreateConstant(char *name, long value) |
| 296 { |
| 297 Variable new; |
| 298 |
| 299 new = (Variable) malloc(sizeof(VariableStruct)); |
| 300 if (new == NULL) Error("out of memory"); |
| 301 new->value = value; |
| 302 new->constraints = List_Create(0); |
| 303 new->determinedBy = NULL; |
| 304 new->mark = 0; |
| 305 new->walkStrength = S_required; |
| 306 new->stay = true; |
| 307 strncpy(new->name, name, 10); |
| 308 new->name[9] = 0; |
| 309 AddVariable(new); |
| 310 return new; |
| 311 } |
| 312 |
| 313 void Variable_Destroy(Variable v) |
| 314 { |
| 315 if (v->constraints == NULL) { |
| 316 Error("bad VariableStruct; already freed?"); |
| 317 } |
| 318 List_Destroy(v->constraints); |
| 319 v->constraints = NULL; |
| 320 free(v); |
| 321 } |
| 322 |
| 323 void Variable_Print(Variable v) |
| 324 { |
| 325 printf( |
| 326 "%s(%s,%ld)", |
| 327 v->name, StrengthString(v->walkStrength), v->value); |
| 328 } |
| 329 |
| 330 /******* Constraints *******/ |
| 331 |
| 332 Constraint Constraint_Create(int variableCount, Strength strength) |
| 333 { |
| 334 Constraint new; |
| 335 int i; |
| 336 |
| 337 new = (Constraint) malloc(sizeof(ConstraintStruct) |
| 338 + ((variableCount - 1) * sizeof(Variable))); |
| 339 if (new == NULL) Error("out of memory"); |
| 340 new->execute = Noop; |
| 341 new->inputFlag = false; |
| 342 new->strength = strength; |
| 343 new->whichMethod = NO_METHOD; |
| 344 new->methodCount = 0; |
| 345 for (i = 0; i < 7; i++) { |
| 346 new->methodOuts[i] = 0; |
| 347 } |
| 348 new->varCount = variableCount; |
| 349 for (i = 0; i < new->varCount; i++) { |
| 350 new->variables[i] = NULL; |
| 351 } |
| 352 return new; |
| 353 } |
| 354 |
| 355 void Constraint_Destroy(Constraint c) |
| 356 { |
| 357 if (c->execute == NULL) { |
| 358 Error("bad ConstraintStruct; already freed?"); |
| 359 } |
| 360 c->execute = NULL; |
| 361 free(c); |
| 362 } |
| 363 |
| 364 void Constraint_Print(Constraint c) |
| 365 { |
| 366 int i, outIndex; |
| 367 |
| 368 if (!SATISFIED(c)) { |
| 369 printf("Unsatisfied("); |
| 370 for (i = 0; i < c->varCount; i++) { |
| 371 Variable_Print(c->variables[i]); |
| 372 printf(" "); |
| 373 } |
| 374 printf(")"); |
| 375 } else { |
| 376 outIndex = c->methodOuts[c->whichMethod]; |
| 377 printf("Satisfied("); |
| 378 for (i = 0; i < c->varCount; i++) { |
| 379 if (i != outIndex) { |
| 380 Variable_Print(c->variables[i]); |
| 381 printf(" "); |
| 382 } |
| 383 } |
| 384 printf("-> "); |
| 385 Variable_Print(c->variables[outIndex]); |
| 386 printf(")"); |
| 387 } |
| 388 printf("\n"); |
| 389 } |
| 390 |
| 391 /******* Miscellaneous Functions *******/ |
| 392 |
| 393 char* StrengthString(Strength strength) |
| 394 { |
| 395 static char temp[20]; |
| 396 |
| 397 switch (strength) { |
| 398 case S_required: return "required"; |
| 399 case S_strongPreferred: return "strongPreferred"; |
| 400 case S_preferred: return "preferred"; |
| 401 case S_strongDefault: return "strongDefault"; |
| 402 case S_default: return "default"; |
| 403 case S_weakDefault: return "weakDefault"; |
| 404 case S_weakest: return "weakest"; |
| 405 default: |
| 406 sprintf(temp, "strength[%d]", strength); |
| 407 return temp; |
| 408 } |
| 409 } |
| 410 |
| 411 void ExecutePlan(List list) |
| 412 { |
| 413 List_Do(list, Execute); |
| 414 } |
| 415 |
| 416 |
| 417 /* |
| 418 |
| 419 DeltaBlue, an incremental dataflow constraint solver. |
| 420 |
| 421 */ |
| 422 |
| 423 /******* Private Macros and Prototypes *******/ |
| 424 |
| 425 #define OUT_VAR(c) (c->variables[c->methodOuts[c->whichMethod]]) |
| 426 |
| 427 void FreeVariable(Variable); |
| 428 void AddIfSatisfiedInput(Constraint); |
| 429 void CollectSatisfiedInputs(Variable); |
| 430 List MakePlan(void); |
| 431 void IncrementalAdd(Constraint); |
| 432 void AddAtStrength(Constraint); |
| 433 void IncrementalRemove(Constraint); |
| 434 Boolean AddPropagate(Constraint); |
| 435 void CollectUnsatisfied(Constraint); |
| 436 void RemovePropagateFrom(Variable); |
| 437 Constraint Satisfy(Constraint); |
| 438 int ChooseMethod(Constraint); |
| 439 void Recalculate(Constraint); |
| 440 int OutputWalkStrength(Constraint); |
| 441 Boolean ConstantOutput(Constraint); |
| 442 Boolean InputsKnown(Constraint); |
| 443 void NewMark(void); |
| 444 void Error(char *); |
| 445 Constraint NextDownstreamConstraint(List, Variable); |
| 446 |
| 447 /******* DeltaBlue Globals *******/ |
| 448 |
| 449 List allVariables = NULL; |
| 450 long currentMark = 0; |
| 451 |
| 452 /******** Public: Initialization *******/ |
| 453 |
| 454 void InitDeltaBlue(void) |
| 455 { |
| 456 Variable v; |
| 457 |
| 458 if (allVariables == NULL) allVariables = List_Create(128); |
| 459 v = (Variable) List_RemoveFirst(allVariables); |
| 460 while (v != NULL) { |
| 461 FreeVariable(v); |
| 462 v = (Variable) List_RemoveFirst(allVariables); |
| 463 } |
| 464 List_RemoveAll(allVariables); |
| 465 currentMark = 0; |
| 466 } |
| 467 |
| 468 /* this is used when we know we are going to throw away all variables */ |
| 469 void FreeVariable(Variable v) |
| 470 { |
| 471 Constraint c; |
| 472 int i; |
| 473 |
| 474 c = (Constraint) List_RemoveFirst(v->constraints); |
| 475 while (c != NULL) { |
| 476 for (i = c->varCount - 1; i >= 0; i--) { |
| 477 List_Remove((c->variables[i])->constraints, (Element) c); |
| 478 } |
| 479 Constraint_Destroy(c); |
| 480 c = (Constraint) List_RemoveFirst(v->constraints); |
| 481 } |
| 482 Variable_Destroy(v); |
| 483 } |
| 484 |
| 485 /******** Public: Variables and Constraints *******/ |
| 486 |
| 487 void AddVariable(Variable v) |
| 488 { |
| 489 List_Add(allVariables, v); |
| 490 } |
| 491 |
| 492 void DestroyConstraint(Constraint c); |
| 493 |
| 494 void DestroyVariable(Variable v) |
| 495 { |
| 496 Constraint c; |
| 497 |
| 498 c = (Constraint) List_RemoveFirst(v->constraints); |
| 499 while (c != NULL) { |
| 500 DestroyConstraint(c); |
| 501 c = (Constraint) List_RemoveFirst(v->constraints); |
| 502 } |
| 503 List_Remove(allVariables, v); |
| 504 Variable_Destroy(v); |
| 505 } |
| 506 |
| 507 void AddConstraint(Constraint c) |
| 508 { |
| 509 int i; |
| 510 |
| 511 for (i = c->varCount - 1; i >= 0; i--) { |
| 512 List_Add((c->variables[i])->constraints, (Element) c); |
| 513 } |
| 514 c->whichMethod = NO_METHOD; |
| 515 IncrementalAdd(c); |
| 516 } |
| 517 |
| 518 void DestroyConstraint(Constraint c) |
| 519 { |
| 520 int i; |
| 521 |
| 522 if (SATISFIED(c)) IncrementalRemove(c); |
| 523 for (i = c->varCount - 1; i >= 0; i--) { |
| 524 List_Remove((c->variables[i])->constraints, (Element) c); |
| 525 } |
| 526 Constraint_Destroy(c); |
| 527 } |
| 528 |
| 529 /******** Public: Plan Extraction *******/ |
| 530 |
| 531 List hot = NULL; /* used to collect "hot" constraints */ |
| 532 |
| 533 void AddIfSatisfiedInput(Constraint c) |
| 534 { |
| 535 if (c->inputFlag && SATISFIED(c)) { |
| 536 List_Add(hot, c); |
| 537 } |
| 538 } |
| 539 |
| 540 void CollectSatisfiedInputs(Variable v) |
| 541 { |
| 542 List_Do(v->constraints, AddIfSatisfiedInput); |
| 543 } |
| 544 |
| 545 List ExtractPlan(void) |
| 546 { |
| 547 if (hot == NULL) hot = List_Create(128); |
| 548 List_RemoveAll(hot); |
| 549 List_Do(allVariables, CollectSatisfiedInputs); |
| 550 return MakePlan(); |
| 551 } |
| 552 |
| 553 List ExtractPlanFromConstraint(Constraint c) |
| 554 { |
| 555 if (hot == NULL) hot = List_Create(128); |
| 556 List_RemoveAll(hot); |
| 557 AddIfSatisfiedInput(c); |
| 558 return MakePlan(); |
| 559 } |
| 560 |
| 561 List ExtractPlanFromConstraints(List constraints) |
| 562 { |
| 563 if (hot == NULL) hot = List_Create(128); |
| 564 List_RemoveAll(hot); |
| 565 List_Do(constraints, AddIfSatisfiedInput); |
| 566 return MakePlan(); |
| 567 } |
| 568 |
| 569 /******* Private: Plan Extraction *******/ |
| 570 |
| 571 List MakePlan() |
| 572 { |
| 573 List plan; |
| 574 Constraint nextC; |
| 575 Variable out; |
| 576 |
| 577 NewMark(); |
| 578 plan = List_Create(128); |
| 579 nextC = (Constraint) List_RemoveFirst(hot); |
| 580 while (nextC != NULL) { |
| 581 out = OUT_VAR(nextC); |
| 582 if ((out->mark != currentMark) && InputsKnown(nextC)) { |
| 583 List_Add(plan, nextC); |
| 584 out->mark = currentMark; |
| 585 nextC = NextDownstreamConstraint(hot, out); |
| 586 } else { |
| 587 nextC = (Constraint) List_RemoveFirst(hot); |
| 588 } |
| 589 } |
| 590 return plan; |
| 591 } |
| 592 |
| 593 Boolean InputsKnown(Constraint c) |
| 594 { |
| 595 int outIndex, i; |
| 596 Variable in; |
| 597 |
| 598 outIndex = c->methodOuts[c->whichMethod]; |
| 599 for (i = c->varCount - 1; i >= 0; i--) { |
| 600 if (i != outIndex) { |
| 601 in = c->variables[i]; |
| 602 if ((in->mark != currentMark) && |
| 603 (!in->stay) && |
| 604 (in->determinedBy != NULL)) { |
| 605 return false; |
| 606 } |
| 607 } |
| 608 } |
| 609 return true; |
| 610 } |
| 611 |
| 612 /******* Private: Adding *******/ |
| 613 |
| 614 void IncrementalAdd(Constraint c) |
| 615 { |
| 616 Constraint overridden; |
| 617 |
| 618 NewMark(); |
| 619 overridden = Satisfy(c); |
| 620 while (overridden != NULL) { |
| 621 overridden = Satisfy(overridden); |
| 622 } |
| 623 } |
| 624 |
| 625 Constraint Satisfy(Constraint c) |
| 626 { |
| 627 int outIndex, i; |
| 628 Constraint overridden; |
| 629 Variable out; |
| 630 |
| 631 c->whichMethod = ChooseMethod(c); |
| 632 if (SATISFIED(c)) { |
| 633 /* mark inputs to allow cycle detection in AddPropagate */ |
| 634 outIndex = c->methodOuts[c->whichMethod]; |
| 635 for (i = c->varCount - 1; i >= 0; i--) { |
| 636 if (i != outIndex) { |
| 637 c->variables[i]->mark = currentMark; |
| 638 } |
| 639 } |
| 640 out = c->variables[outIndex]; |
| 641 overridden = (Constraint) out->determinedBy; |
| 642 if (overridden != NULL) overridden->whichMethod = NO_METHOD; |
| 643 out->determinedBy = c; |
| 644 if (!AddPropagate(c)) { |
| 645 Error("Cycle encountered"); |
| 646 return NULL; |
| 647 } |
| 648 out->mark = currentMark; |
| 649 return overridden; |
| 650 } else { |
| 651 if (c->strength == S_required) { |
| 652 Error("Could not satisfy a required constraint"); |
| 653 } |
| 654 return NULL; |
| 655 } |
| 656 } |
| 657 |
| 658 int ChooseMethod(Constraint c) |
| 659 { |
| 660 int best, m; |
| 661 Strength bestOutStrength; |
| 662 Variable mOut; |
| 663 |
| 664 best = NO_METHOD; |
| 665 bestOutStrength = c->strength; |
| 666 for (m = c->methodCount - 1; m >= 0; m--) { |
| 667 mOut = c->variables[c->methodOuts[m]]; |
| 668 if ((mOut->mark != currentMark) && |
| 669 (Weaker(mOut->walkStrength, bestOutStrength))) { |
| 670 best = m; |
| 671 bestOutStrength = mOut->walkStrength; |
| 672 } |
| 673 } |
| 674 return best; |
| 675 } |
| 676 |
| 677 Boolean AddPropagate(Constraint c) |
| 678 { |
| 679 List todo; |
| 680 Constraint nextC; |
| 681 Variable out; |
| 682 |
| 683 todo = List_Create(8); /* unprocessed constraints */ |
| 684 nextC = c; |
| 685 while (nextC != NULL) { |
| 686 out = OUT_VAR(nextC); |
| 687 if (out->mark == currentMark) { |
| 688 /* remove the cycle-causing constraint */ |
| 689 IncrementalRemove(c); |
| 690 return false; |
| 691 } |
| 692 Recalculate(nextC); |
| 693 nextC = NextDownstreamConstraint(todo, out); |
| 694 } |
| 695 List_Destroy(todo); |
| 696 return true; |
| 697 } |
| 698 |
| 699 /******* Private: Removing *******/ |
| 700 |
| 701 List unsatisfied; /* used to collect unsatisfied downstream constraints */ |
| 702 Strength strength; /* used to add unsatisfied constraints in strength order */ |
| 703 |
| 704 void AddAtStrength(Constraint c) |
| 705 { |
| 706 if (c->strength == strength) IncrementalAdd(c); |
| 707 } |
| 708 |
| 709 void CollectUnsatisfied(Constraint c) |
| 710 { |
| 711 if (!SATISFIED(c)) List_Add(unsatisfied, c); |
| 712 } |
| 713 |
| 714 void IncrementalRemove(Constraint c) |
| 715 { |
| 716 Variable out; |
| 717 int i; |
| 718 |
| 719 out = OUT_VAR(c); |
| 720 c->whichMethod = NO_METHOD; |
| 721 for (i = c->varCount - 1; i >= 0; i--) { |
| 722 List_Remove((c->variables[i])->constraints, (Element) c); |
| 723 } |
| 724 unsatisfied = List_Create(8); |
| 725 RemovePropagateFrom(out); |
| 726 for (strength = S_required; strength <= S_weakest; strength++) { |
| 727 List_Do(unsatisfied, AddAtStrength); |
| 728 } |
| 729 List_Destroy(unsatisfied); |
| 730 } |
| 731 |
| 732 void RemovePropagateFrom(Variable v) |
| 733 { |
| 734 Constraint nextC; |
| 735 List todo; |
| 736 |
| 737 v->determinedBy = NULL; |
| 738 v->walkStrength = S_weakest; |
| 739 v->stay = true; |
| 740 todo = List_Create(8); |
| 741 while (true) { |
| 742 List_Do(v->constraints, CollectUnsatisfied); |
| 743 nextC = NextDownstreamConstraint(todo, v); |
| 744 if (nextC == NULL) { |
| 745 break; |
| 746 } else { |
| 747 Recalculate(nextC); |
| 748 v = OUT_VAR(nextC); |
| 749 } |
| 750 } |
| 751 List_Destroy(todo); |
| 752 } |
| 753 |
| 754 /******* Private: Recalculation *******/ |
| 755 |
| 756 void Recalculate(Constraint c) |
| 757 { |
| 758 Variable out; |
| 759 |
| 760 out = OUT_VAR(c); |
| 761 out->walkStrength = OutputWalkStrength(c); |
| 762 out->stay = ConstantOutput(c); |
| 763 if (out->stay) c->execute(c); |
| 764 } |
| 765 |
| 766 int OutputWalkStrength(Constraint c) |
| 767 { |
| 768 int outIndex, m, mOutIndex; |
| 769 Strength minStrength; |
| 770 |
| 771 minStrength = c->strength; |
| 772 outIndex = c->methodOuts[c->whichMethod]; |
| 773 for (m = c->methodCount - 1; m >= 0; m--) { |
| 774 mOutIndex = c->methodOuts[m]; |
| 775 if ((mOutIndex != outIndex) && |
| 776 (Weaker(c->variables[mOutIndex]->walkStrength, minStrength))) { |
| 777 minStrength = c->variables[mOutIndex]->walkStrength; |
| 778 } |
| 779 } |
| 780 return minStrength; |
| 781 } |
| 782 |
| 783 Boolean ConstantOutput(Constraint c) |
| 784 { |
| 785 int outIndex, i; |
| 786 |
| 787 if (c->inputFlag) return false; |
| 788 outIndex = c->methodOuts[c->whichMethod]; |
| 789 for (i = c->varCount - 1; i >= 0; i--) { |
| 790 if (i != outIndex) { |
| 791 if (!c->variables[i]->stay) return false; |
| 792 } |
| 793 } |
| 794 return true; |
| 795 } |
| 796 |
| 797 /******* Private: Miscellaneous *******/ |
| 798 |
| 799 void NewMark(void) |
| 800 { |
| 801 currentMark++; |
| 802 } |
| 803 |
| 804 Constraint NextDownstreamConstraint(List todo, Variable variable) |
| 805 { |
| 806 List allC = variable->constraints; |
| 807 Constraint *nextPtr = (Constraint *) &(allC->slots[allC->first]); |
| 808 Constraint *lastPtr = (Constraint *) &(allC->slots[allC->last]); |
| 809 Constraint determiningC = variable->determinedBy; |
| 810 Constraint first = NULL; |
| 811 |
| 812 for ( ; nextPtr <= lastPtr; nextPtr++) { |
| 813 if ((*nextPtr != determiningC) && SATISFIED(*nextPtr)) { |
| 814 if (first == NULL) { |
| 815 first = *nextPtr; |
| 816 } else { |
| 817 List_Add(todo, *nextPtr); |
| 818 } |
| 819 } |
| 820 } |
| 821 if (first == NULL) { |
| 822 first = (Constraint) List_RemoveFirst(todo); |
| 823 } |
| 824 return first; |
| 825 } |
| 826 |
| 827 |
| 828 /* |
| 829 Some useful constraints. Each function instantiates and installs |
| 830 a constraint on the argument variables. |
| 831 */ |
| 832 |
| 833 |
| 834 /* macro to reference a constraint variable value */ |
| 835 #define var(i) ((c->variables[i])->value) |
| 836 |
| 837 /******* Stay Constraint *******/ |
| 838 |
| 839 Constraint StayC(Variable v, Strength strength) |
| 840 { |
| 841 Constraint new = Constraint_Create(1, strength); |
| 842 new->variables[0] = v; |
| 843 new->methodCount = 1; |
| 844 new->methodOuts[0] = 0; |
| 845 AddConstraint(new); |
| 846 return new; |
| 847 }; |
| 848 |
| 849 /******* Edit Constraint *******/ |
| 850 |
| 851 Constraint EditC(Variable v, Strength strength) |
| 852 { |
| 853 Constraint new = Constraint_Create(1, strength); |
| 854 new->inputFlag = true; |
| 855 new->variables[0] = v; |
| 856 new->methodCount = 1; |
| 857 new->methodOuts[0] = 0; |
| 858 AddConstraint(new); |
| 859 return new; |
| 860 }; |
| 861 |
| 862 /****** Equals Constraint ******/ |
| 863 |
| 864 void EqualsC_Execute(Constraint c) |
| 865 { |
| 866 /* a = b */ |
| 867 switch (c->whichMethod) { |
| 868 case 0: |
| 869 var(0) = var(1); |
| 870 break; |
| 871 case 1: |
| 872 var(1) = var(0); |
| 873 break; |
| 874 } |
| 875 } |
| 876 |
| 877 Constraint EqualsC(Variable a, Variable b, Strength strength) |
| 878 { |
| 879 Constraint new = Constraint_Create(2, strength); |
| 880 new->execute = EqualsC_Execute; |
| 881 new->variables[0] = a; |
| 882 new->variables[1] = b; |
| 883 new->methodCount = 2; |
| 884 new->methodOuts[0] = 0; |
| 885 new->methodOuts[1] = 1; |
| 886 AddConstraint(new); |
| 887 return new; |
| 888 }; |
| 889 |
| 890 /******** Add Constraint *******/ |
| 891 |
| 892 void AddC_Execute(Constraint c) |
| 893 { |
| 894 /* a + b = sum */ |
| 895 switch (c->whichMethod) { |
| 896 case 0: |
| 897 var(2) = var(0) + var(1); |
| 898 break; |
| 899 case 1: |
| 900 var(1) = var(2) - var(0); |
| 901 break; |
| 902 case 2: |
| 903 var(0) = var(2) - var(1); |
| 904 break; |
| 905 } |
| 906 } |
| 907 |
| 908 Constraint AddC(Variable a, Variable b, Variable sum, Strength strength) |
| 909 { |
| 910 Constraint new = Constraint_Create(3, strength); |
| 911 new->execute = AddC_Execute; |
| 912 new->variables[0] = a; |
| 913 new->variables[1] = b; |
| 914 new->variables[2] = sum; |
| 915 new->methodCount = 3; |
| 916 new->methodOuts[0] = 2; |
| 917 new->methodOuts[1] = 1; |
| 918 new->methodOuts[2] = 0; |
| 919 AddConstraint(new); |
| 920 return new; |
| 921 }; |
| 922 |
| 923 /******** ScaleOffset Constraint *******/ |
| 924 |
| 925 void ScaleOffsetC_Execute(Constraint c) |
| 926 { |
| 927 /* (src * scale) + offset = dest */ |
| 928 switch (c->whichMethod) { |
| 929 case 0: |
| 930 var(3) = (var(0) * var(1)) + var(2); |
| 931 break; |
| 932 case 1: |
| 933 var(0) = (var(3) - var(2)) / var(1); |
| 934 break; |
| 935 } |
| 936 } |
| 937 |
| 938 Constraint ScaleOffsetC(Variable src, Variable scale, Variable offset, |
| 939 Variable dest, Strength strength) |
| 940 { |
| 941 Constraint new = Constraint_Create(4, strength); |
| 942 new->execute = ScaleOffsetC_Execute; |
| 943 new->variables[0] = src; |
| 944 new->variables[1] = scale; |
| 945 new->variables[2] = offset; |
| 946 new->variables[3] = dest; |
| 947 new->methodCount = 2; |
| 948 new->methodOuts[0] = 3; |
| 949 new->methodOuts[1] = 0; |
| 950 AddConstraint(new); |
| 951 return new; |
| 952 }; |
| 953 |
| 954 /*************************************************************************** |
| 955 |
| 956 Timing Functions |
| 957 |
| 958 ****************************************************************************/ |
| 959 |
| 960 long startTime; |
| 961 |
| 962 long Milliseconds() |
| 963 { |
| 964 int millisecondsPerClock = CLOCKS_PER_SEC / 1000; |
| 965 return (clock() / millisecondsPerClock); |
| 966 } |
| 967 |
| 968 void Start() |
| 969 { |
| 970 startTime = Milliseconds(); |
| 971 } |
| 972 |
| 973 void Finish(long *milliseconds) |
| 974 { |
| 975 *milliseconds = Milliseconds() - startTime; |
| 976 } |
| 977 |
| 978 /*************************************************************************** |
| 979 * |
| 980 * This is the standard DeltaBlue benchmark. A long chain of equality |
| 981 * constraints is constructed with a stay constraint on one end. An edit |
| 982 * constraint is then added to the opposite end and the time is measured for |
| 983 * adding and removing this constraint, and extracting and executing a |
| 984 * constraint satisfaction plan. There are two cases. In case 1, the added |
| 985 * constraint is stronger than the stay constraint and values must propagate |
| 986 * down the entire length of the chain. In case 2, the added constraint is |
| 987 * weaker than the stay constraint so it cannot be accomodated. The cost in |
| 988 * this case is, of course, very low. Typical situations lie somewhere between |
| 989 * these two extremes. |
| 990 * |
| 991 ****************************************************************************/ |
| 992 |
| 993 void ChainTest(int n) |
| 994 { |
| 995 long msecs, i; |
| 996 char name[20]; |
| 997 Variable prev, v, first, last; |
| 998 Constraint editC; |
| 999 List plan; |
| 1000 |
| 1001 InitDeltaBlue(); |
| 1002 prev = first = last = NULL; |
| 1003 |
| 1004 for (i = 0; i < n; i++) { |
| 1005 sprintf(name, "v%ld", i); |
| 1006 v = Variable_Create(name, 0); |
| 1007 if (prev != NULL) { |
| 1008 EqualsC(prev, v, S_required); |
| 1009 } |
| 1010 if (i == 0) first = v; |
| 1011 if (i == (n-1)) last = v; |
| 1012 prev = v; |
| 1013 } |
| 1014 StayC(last, S_default); |
| 1015 editC = EditC(first, S_strongDefault); |
| 1016 plan = ExtractPlanFromConstraint(editC); |
| 1017 for (i = 0; i < 100; i++) { |
| 1018 first->value = i; |
| 1019 ExecutePlan(plan); |
| 1020 if (last->value != i) |
| 1021 Error("ChainTest failed!"); |
| 1022 } |
| 1023 List_Destroy(plan); |
| 1024 DestroyConstraint(editC); |
| 1025 } |
| 1026 |
| 1027 /*************************************************************************** |
| 1028 * |
| 1029 * This test constructs a two sets of variables related to each other by a |
| 1030 * simple linear transformation (scale and offset). The time is measured to |
| 1031 * change a variable on either side of the mapping and to change the scale or |
| 1032 * offset factors. It has been tested for up to 2000 variable pairs. |
| 1033 * |
| 1034 ****************************************************************************/ |
| 1035 |
| 1036 void Change(Variable v, long newValue) |
| 1037 { |
| 1038 Constraint editC; |
| 1039 long i, msecs; |
| 1040 List plan; |
| 1041 |
| 1042 editC = EditC(v, S_strongDefault); |
| 1043 plan = ExtractPlanFromConstraint(editC); |
| 1044 v->value = newValue; |
| 1045 for (i = 0; i < 10; i++) { |
| 1046 ExecutePlan(plan); |
| 1047 } |
| 1048 List_Destroy(plan); |
| 1049 DestroyConstraint(editC); |
| 1050 } |
| 1051 |
| 1052 void ProjectionTest(int n) |
| 1053 { |
| 1054 Variable src, scale, offset, dest; |
| 1055 long msecs, i; |
| 1056 char name[20]; |
| 1057 List dests; |
| 1058 |
| 1059 InitDeltaBlue(); |
| 1060 |
| 1061 scale = Variable_Create("scale", 10); |
| 1062 offset = Variable_Create("offset", 1000); |
| 1063 dests = List_Create(n); |
| 1064 |
| 1065 for (i = 1; i <= n; i++) { |
| 1066 /* make src and dest variables */ |
| 1067 sprintf(name, "src%ld", i); |
| 1068 src = Variable_Create(name, i); |
| 1069 sprintf(name, "dest%ld", i); |
| 1070 dest = Variable_Create(name, i); |
| 1071 List_Add(dests, dest); |
| 1072 |
| 1073 /* add stay on src */ |
| 1074 StayC(src, S_default); |
| 1075 |
| 1076 /* add scale/offset constraint */ |
| 1077 ScaleOffsetC(src, scale, offset, dest, S_required); |
| 1078 } |
| 1079 Change(src, 17); |
| 1080 if (dest->value != 1170) |
| 1081 Error("Projection Test 1 failed!"); |
| 1082 |
| 1083 Change(dest, 1050); |
| 1084 if (src->value != 5) |
| 1085 Error("Projection Test 2 failed!"); |
| 1086 |
| 1087 Change(scale, 5); |
| 1088 for (i = 1; i < List_Size(dests); ++i) |
| 1089 if (((Variable)List_At(dests, i - 1))->value != i * 5 + 1000) |
| 1090 Error("Projection Test 3 failed!"); |
| 1091 |
| 1092 Change(offset, 2000); |
| 1093 for (i = 1; i < List_Size(dests); ++i) |
| 1094 if (((Variable)List_At(dests, i - 1))->value != i * 5 + 2000) |
| 1095 Error("Projection Test 4 failed!"); |
| 1096 |
| 1097 List_Destroy(dests); |
| 1098 } |
| 1099 |
| 1100 int run_deltablue(int n) |
| 1101 { |
| 1102 const int iterations= 1; |
| 1103 int j; |
| 1104 long msecs; |
| 1105 |
| 1106 Start(); |
| 1107 for (j= 0; j < iterations; ++j) { |
| 1108 ChainTest(n); |
| 1109 ProjectionTest(n); |
| 1110 } |
| 1111 Finish(&msecs); |
| 1112 /*printf("Total time for %d iterations of chain and projection tests: %ld ms\n
", |
| 1113 iterations, msecs); |
| 1114 printf("Average time per iteration: %g ms\n", (double)msecs / iteration
s);*/ |
| 1115 return 0; |
| 1116 } |
OLD | NEW |