001package org.clafer.ast.compiler; 002 003import gnu.trove.list.array.TIntArrayList; 004import java.util.ArrayList; 005import java.util.Arrays; 006import java.util.Collections; 007import java.util.HashMap; 008import java.util.List; 009import java.util.Map; 010import java.util.Map.Entry; 011import java.util.Set; 012import org.clafer.ast.AstAbstractClafer; 013import org.clafer.ast.AstArithm; 014import org.clafer.ast.AstBoolArithm; 015import org.clafer.ast.AstBoolExpr; 016import org.clafer.ast.AstCard; 017import org.clafer.ast.AstClafer; 018import org.clafer.ast.AstCompare; 019import org.clafer.ast.AstConcreteClafer; 020import org.clafer.ast.AstConstant; 021import org.clafer.ast.AstConstraint; 022import org.clafer.ast.AstDecl; 023import org.clafer.ast.AstDifference; 024import org.clafer.ast.AstDowncast; 025import org.clafer.ast.AstException; 026import org.clafer.ast.AstExpr; 027import org.clafer.ast.AstExprVisitor; 028import org.clafer.ast.AstGlobal; 029import org.clafer.ast.AstIfThenElse; 030import org.clafer.ast.AstIntClafer; 031import org.clafer.ast.AstIntersection; 032import org.clafer.ast.AstJoin; 033import org.clafer.ast.AstJoinParent; 034import org.clafer.ast.AstJoinRef; 035import org.clafer.ast.AstLocal; 036import org.clafer.ast.AstMembership; 037import org.clafer.ast.AstMinus; 038import org.clafer.ast.AstModel; 039import org.clafer.ast.AstNot; 040import org.clafer.ast.AstQuantify; 041import org.clafer.ast.AstQuantify.Quantifier; 042import org.clafer.ast.AstRef; 043import org.clafer.ast.AstSetExpr; 044import org.clafer.ast.AstSetTest; 045import org.clafer.ast.AstSum; 046import org.clafer.ast.AstTernary; 047import org.clafer.ast.AstThis; 048import org.clafer.ast.AstUnion; 049import org.clafer.ast.AstUpcast; 050import org.clafer.ast.AstUtil; 051import org.clafer.ast.Card; 052import org.clafer.ast.analysis.AbstractOffsetAnalyzer; 053import org.clafer.ast.analysis.Analysis; 054import org.clafer.ast.analysis.Analyzer; 055import org.clafer.ast.analysis.CardAnalyzer; 056import org.clafer.ast.analysis.Format; 057import org.clafer.ast.analysis.FormatAnalyzer; 058import org.clafer.ast.analysis.GlobalCardAnalyzer; 059import org.clafer.ast.analysis.OptimizerAnalyzer; 060import org.clafer.ast.analysis.PartialIntAnalyzer; 061import org.clafer.ast.analysis.PartialSolution; 062import org.clafer.ast.analysis.PartialSolutionAnalyzer; 063import org.clafer.ast.analysis.ScopeAnalyzer; 064import org.clafer.ast.analysis.SymmetryAnalyzer; 065import org.clafer.ast.analysis.Type; 066import org.clafer.ast.analysis.TypeAnalyzer; 067import org.clafer.collection.DisjointSets; 068import org.clafer.collection.Pair; 069import org.clafer.collection.Triple; 070import org.clafer.common.Check; 071import org.clafer.common.Util; 072import org.clafer.graph.GraphUtil; 073import org.clafer.graph.KeyGraph; 074import org.clafer.graph.Vertex; 075import org.clafer.ir.IrBoolExpr; 076import org.clafer.ir.IrBoolVar; 077import org.clafer.ir.IrDomain; 078import org.clafer.ir.IrExpr; 079import org.clafer.ir.IrIntExpr; 080import org.clafer.ir.IrIntVar; 081import org.clafer.ir.IrModule; 082import org.clafer.ir.IrSetExpr; 083import org.clafer.ir.IrSetVar; 084import org.clafer.ir.IrUtil; 085import static org.clafer.ir.Irs.*; 086import org.clafer.objective.Objective; 087import org.clafer.scope.Scope; 088 089/** 090 * Compile from AST to IR. 091 * 092 * @author jimmy 093 */ 094public class AstCompiler { 095 096 public static final Analyzer[] DefaultAnalyzers = new Analyzer[]{ 097 new TypeAnalyzer(), 098 new GlobalCardAnalyzer(), 099 new ScopeAnalyzer(), 100 new CardAnalyzer(), 101 new FormatAnalyzer(), 102 new AbstractOffsetAnalyzer(), 103 new OptimizerAnalyzer(), 104 new PartialSolutionAnalyzer(), 105 new PartialIntAnalyzer(), 106 new SymmetryAnalyzer(), 107 // Reanalyze types 108 new TypeAnalyzer() 109 }; 110 private final Analysis analysis; 111 private final IrModule module; 112 private final boolean fullSymmetryBreaking; 113 114 private AstCompiler(AstModel model, Scope scope, IrModule module, Analyzer[] analyzers, boolean fullSymmetryBreaking) { 115 this(model, scope, Collections.<Objective>emptyList(), module, analyzers, fullSymmetryBreaking); 116 } 117 118 private AstCompiler(AstModel model, Scope scope, List<Objective> objectives, IrModule module, Analyzer[] analyzers, boolean fullSymmetryBreaking) { 119 this.analysis = Analysis.analyze(model, scope, objectives, analyzers); 120 this.module = Check.notNull(module); 121 this.fullSymmetryBreaking = fullSymmetryBreaking; 122 } 123 124 public static AstSolutionMap compile(AstModel in, Scope scope, IrModule out, boolean fullSymmetryBreaking) { 125 return compile(in, scope, out, DefaultAnalyzers, fullSymmetryBreaking); 126 } 127 128 public static AstSolutionMap compile(AstModel in, Scope scope, IrModule out, Analyzer[] analyzers, boolean fullSymmetryBreaking) { 129 AstCompiler compiler = new AstCompiler(in, scope, out, analyzers, fullSymmetryBreaking); 130 return compiler.compile(); 131 } 132 133 public static AstSolutionMap compile(AstModel in, Scope scope, Objective objective, IrModule out, boolean fullSymmetryBreaking) { 134 return compile(in, scope, objective, out, DefaultAnalyzers, fullSymmetryBreaking); 135 } 136 137 public static AstSolutionMap compile(AstModel in, Scope scope, Objective objective, IrModule out, Analyzer[] analyzers, boolean fullSymmetryBreaking) { 138 AstCompiler compiler = new AstCompiler(in, scope, Collections.singletonList(objective), 139 out, analyzers, fullSymmetryBreaking); 140 return compiler.compile(); 141 } 142 143 /** 144 * @return the order to initialize regular variables 145 */ 146 private List<AstClafer> initOrder() { 147 List<AstAbstractClafer> abstractClafers = analysis.getAbstractClafers(); 148 List<AstConcreteClafer> concreteClafers = analysis.getConcreteClafers(); 149 150 KeyGraph<AstClafer> dependency = new KeyGraph<>(); 151 for (AstAbstractClafer abstractClafer : abstractClafers) { 152 Vertex<AstClafer> node = dependency.getVertex(abstractClafer); 153 for (AstClafer sub : abstractClafer.getSubs()) { 154 node.addNeighbour(dependency.getVertex(sub)); 155 } 156 } 157 for (AstConcreteClafer concreteClafer : concreteClafers) { 158 Vertex<AstClafer> node = dependency.getVertex(concreteClafer); 159 if (Format.ParentGroup.equals(getFormat(concreteClafer))) { 160 /* 161 * Low group does not create the dependency because it does not 162 * require the parent to initialize first. This allows for 163 * models like the one below. 164 * 165 * abstract Path 166 * p : Path ? 167 * 168 * If the "?" were a fixed cardinality instead, then an 169 * exception will occur, but the model would not be satisfiable 170 * anyways for any fixed cardinality greater than zero. 171 */ 172 node.addNeighbour(dependency.getVertex(concreteClafer.getParent())); 173 } 174 } 175 List<Set<AstClafer>> components = GraphUtil.computeStronglyConnectedComponents(dependency); 176 List<AstClafer> clafers = new ArrayList<>(); 177 for (Set<AstClafer> component : components) { 178 if (component.size() != 1) { 179 // See the above comment about low groups. 180 throw new AstException("Cannot satisfy the cycle " + component); 181 } 182 clafers.addAll(component); 183 } 184 return clafers; 185 } 186 187 private AstSolutionMap compile() { 188 IrSetVar rootSet = constant(new int[]{0}); 189 sets.put(analysis.getModel(), rootSet); 190 siblingSets.put(analysis.getModel(), new IrSetVar[]{rootSet}); 191 memberships.put(analysis.getModel(), new IrBoolExpr[]{True}); 192 193 List<AstClafer> clafers = initOrder(); 194 for (AstClafer clafer : clafers) { 195 if (clafer instanceof AstConcreteClafer && !AstUtil.isRoot((AstConcreteClafer) clafer)) { 196 initConcrete((AstConcreteClafer) clafer); 197 } else if (clafer instanceof AstAbstractClafer) { 198 initAbstract((AstAbstractClafer) clafer); 199 } 200 } 201 for (AstClafer clafer : clafers) { 202 if (clafer instanceof AstConcreteClafer && !AstUtil.isRoot((AstConcreteClafer) clafer)) { 203 constrainConcrete((AstConcreteClafer) clafer); 204 } else if (clafer instanceof AstAbstractClafer) { 205 constrainAbstract((AstAbstractClafer) clafer); 206 } 207 constrainGroupCardinality(clafer); 208 } 209 210 Map<AstConstraint, IrBoolVar> softVars = new HashMap<>(); 211 for (AstConstraint constraint : analysis.getConstraints()) { 212 AstClafer clafer = constraint.getContext(); 213 int scope = getScope(clafer); 214 if (analysis.isHard(constraint)) { 215 for (int j = 0; j < scope; j++) { 216 ExpressionCompiler expressionCompiler = new ExpressionCompiler(j); 217 IrBoolExpr thisConstraint = expressionCompiler.compile(analysis.getExpr(constraint)); 218 module.addConstraint(implies(memberships.get(clafer)[j], thisConstraint)); 219 } 220 } else { 221 IrBoolVar softVar = bool(constraint.toString()); 222 softVars.put(constraint, softVar); 223 for (int j = 0; j < scope; j++) { 224 ExpressionCompiler expressionCompiler = new ExpressionCompiler(j); 225 IrBoolExpr thisConstraint = expressionCompiler.compile(analysis.getExpr(constraint)); 226 module.addConstraint(ifOnlyIf(softVar, implies(memberships.get(clafer)[j], thisConstraint))); 227 } 228 module.addVariable(softVar); 229 } 230 } 231 IrIntExpr softSum = add(softVars.values()); 232 IrIntVar sumSoftVars = domainInt("SumSoftVar", softSum.getDomain()); 233 module.addConstraint(equal(sumSoftVars, softSum)); 234 235 for (IrSetVar[] childSet : siblingSets.values()) { 236 for (IrSetVar set : childSet) { 237 module.addVariable(set); 238 } 239 } 240 for (IrIntVar[] refs : refPointers.values()) { 241 for (IrIntVar ref : refs) { 242 module.addVariable(ref); 243 } 244 } 245 246 for (Set<AstClafer> component : analysis.getClafersInParentAndSubOrder()) { 247 if (component.size() > 1) { 248 /* 249 * Add additional constraints for to handle cases where a 250 * descendent inherits an ancestor. 251 * 252 * Let A be an abstract Clafer and B be a descendant of A that 253 * inherits A. Let F be a mapping every B to its ancestor A. 254 * Enforce that F is an acyclic function. 255 */ 256 List<AstClafer> types = new ArrayList<>(); 257 for (AstClafer clafer : component) { 258 types.add(clafer); 259 } 260 AstAbstractClafer unionType = (AstAbstractClafer) AstUtil.getLowestCommonSupertype(types); 261 IrIntExpr[] edges = new IrIntExpr[getScope(unionType)]; 262 IrIntExpr uninitialized = constant(edges.length); 263 Arrays.fill(edges, uninitialized); 264 for (AstClafer clafer : component) { 265 if (clafer instanceof AstConcreteClafer) { 266 AstConcreteClafer concreteChild = (AstConcreteClafer) clafer; 267 IrBoolExpr[] members = memberships.get(concreteChild); 268 IrIntExpr[] parents = parentPointers.get(concreteChild); 269 int offset = getOffset(unionType, concreteChild); 270 int parentOffset = getOffset(unionType, concreteChild.getParent()); 271 for (int i = 0; i < members.length; i++) { 272 assert edges[i + offset] == uninitialized; 273 IrIntExpr value = ternary(members[i], 274 // Add the offset to upcast the parent pointer 275 add(parents[i], parentOffset), 276 uninitialized); 277 IrIntVar edge = domainInt( 278 "Edge@" + concreteChild + "->" + concreteChild.getParent() + "#" + i, 279 value.getDomain()); 280 module.addConstraint(equal(edge, value)); 281 edges[i + offset] = edge; 282 } 283 } 284 } 285 for (AstClafer clafer : component) { 286 if (clafer instanceof AstConcreteClafer) { 287 IrBoolExpr[] members = memberships.get(clafer); 288 int offset = getOffset(unionType, clafer); 289 for (int i = 0; i < members.length; i++) { 290 for (int j = i + 1; j < members.length; j++) { 291 /* 292 * Symmetry breaking. The lower indexed element 293 * appears on top of the higher indexed element. 294 */ 295 module.addConstraint(unreachable(edges, i + offset, j + offset)); 296 } 297 } 298 } 299 } 300 module.addConstraint(acyclic(edges)); 301 } 302 } 303 304 ExpressionCompiler expressionCompiler = new ExpressionCompiler(0); 305 Map<Objective, IrIntVar> objectiveVars = new HashMap<>(); 306 Map<Objective, AstSetExpr> objectives = analysis.getObjectiveExprs(); 307 for (Entry<Objective, AstSetExpr> objective : objectives.entrySet()) { 308 IrIntExpr objectiveExpr = expressionCompiler.asInt( 309 expressionCompiler.compile(objective.getValue())); 310 IrIntVar objectiveVar = domainInt("Objective" + objective.getKey(), 311 objectiveExpr.getDomain()); 312 module.addConstraint(equal(objectiveVar, objectiveExpr)); 313 objectiveVars.put(objective.getKey(), objectiveVar); 314 } 315 return new AstSolutionMap(analysis.getModel(), siblingSets, refPointers, 316 softVars, sumSoftVars, 317 objectiveVars, analysis); 318 } 319 320 private void initConcrete(AstConcreteClafer clafer) { 321 parentPointers.put(clafer, buildParentPointers(clafer)); 322 if (clafer.hasRef()) { 323 refPointers.put(clafer.getRef(), buildRefPointers(clafer.getRef())); 324 } 325 switch (getFormat(clafer)) { 326 case LowGroup: 327 initLowGroupConcrete(clafer); 328 break; 329 case ParentGroup: 330 initParentGroupConcrete(clafer); 331 break; 332 default: 333 throw new AstException(); 334 } 335 336 IrSetVar[] siblingSet = siblingSets.get(clafer); 337 switch (siblingSet.length) { 338 case 0: 339 sets.put(clafer, EmptySet); 340 break; 341 case 1: 342 sets.put(clafer, siblingSet[0]); 343 break; 344 default: 345 IrSetExpr union = union(siblingSet, true); 346 IrSetVar set = set(clafer.getName(), union.getEnv(), union.getKer(), union.getCard()); 347 module.addConstraint(equal(set, union)); 348 sets.put(clafer, set); 349 break; 350 } 351 352 if (fullSymmetryBreaking) { 353 int scope = getScope(clafer); 354 int parentScope = getScope(clafer.getParent()); 355 IrIntExpr[][] index; 356 AstRef ref = AstUtil.getInheritedRef(clafer); 357 // If the Clafer either needs children or reference to be introduce symmetry. 358 if (analysis.hasInteritedBreakableChildren(clafer) 359 || (ref != null && analysis.isBreakableRef(ref)) 360 || analysis.isInheritedBreakableTarget(clafer)) { 361 index = new IrIntExpr[parentScope][getCard(clafer).getHigh()]; 362 for (int i = 0; i < index.length; i++) { 363 for (int j = 0; j < index[i].length; j++) { 364 index[i][j] = 365 boundInt(clafer.getName() + "@Index#" + i + "#" + j, -1, scope); 366 } 367 } 368 } else { 369 // Optimize for nonsymmetric nodes. Don't compute the smallest indices, 370 // just use the cardinalities. 371 IrSetVar[] childSet = siblingSets.get(clafer); 372 index = new IrIntExpr[childSet.length][]; 373 for (int i = 0; i < index.length; i++) { 374 index[i] = new IrIntExpr[]{card(childSet[i])}; 375 } 376 } 377 indices.put(clafer, index); 378 } 379 } 380 381 private void constrainConcrete(AstConcreteClafer clafer) { 382 IrSetExpr[] siblingSet = siblingSets.get(clafer); 383 IrIntExpr[] parents = parentPointers.get(clafer); 384 if (!getPartialSolution(clafer).parentSolutionKnown()) { 385 if (getGlobalCard(clafer).isExact()) { 386 // No unused 387 module.addConstraint(intChannel(parents, siblingSet)); 388 } else { 389 IrSetVar unused = set(clafer.getName() + "@Unused", getPartialSolution(clafer).getUnknownClafers()); 390 module.addConstraint(intChannel(parents, Util.snoc(siblingSet, unused))); 391 } 392 } 393 394 Pair<AstRef, Integer> refPair = analysis.getInheritedRefId(clafer); 395 AstRef ref = refPair == null ? null : refPair.getFst(); 396 int refOffset = refPair == null ? 0 : refPair.getSnd().intValue(); 397 398 int scope = getScope(clafer); 399 IrBoolExpr[] members = memberships.get(clafer); 400 401 // Two ids a and b are in the same partition if symmetry breaking guarantees 402 // that ref[a] and ref[b] ard different. 403 DisjointSets<Integer> refPartitions = null; 404 // If the Clafer either needs children or reference to be introduce symmetry. 405 if (fullSymmetryBreaking && scope > 1 406 && (analysis.hasInteritedBreakableChildren(clafer) 407 || (ref != null && analysis.isBreakableRef(ref)) 408 || analysis.isInheritedBreakableTarget(clafer))) { 409 410 IrIntExpr[] weight = new IrIntExpr[scope]; 411 weights.put(clafer, weight); 412 IrIntExpr[][] index = indices.get(clafer); 413 414 analysis.getHierarcyIds(clafer, refOffset); 415 IrIntExpr[][] childIndices = new IrIntExpr[weight.length][]; 416 417 List<Pair<AstClafer, Integer>> offsets = analysis.getHierarcyOffsets(clafer); 418 Collections.reverse(offsets); 419 boolean[] breakableRefIds = new boolean[childIndices.length]; 420 for (int i = 0; i < childIndices.length; i++) { 421 List<IrIntExpr> childIndex = new ArrayList<>(); 422 for (Pair<AstClafer, Integer> offset : offsets) { 423 for (AstConcreteClafer child : analysis.getBreakableChildren(offset.getFst())) { 424 childIndex.addAll(Arrays.asList(indices.get(child)[i + offset.getSnd()])); 425 } 426 } 427 if (ref != null && analysis.isBreakableRef(ref)) { 428 breakableRefIds[i] = analysis.isBreakableRefId(ref, i + refOffset); 429 // References need a positive weight, so to use their value as 430 // a weight, need to offset it so that it always positive. 431 childIndex.add( 432 breakableRefIds[i] 433 // The id of the target is the weight. 434 ? minus(refPointers.get(ref)[i + refOffset]) 435 // If analysis says that this id does not need breaking 436 // then give it a constant weight. Any constant is fine. 437 : Zero); 438 } 439 if (analysis.isInheritedBreakableTarget(clafer)) { 440 for (Pair<AstClafer, Integer> hierarchy : analysis.getHierarcyIds(clafer, i)) { 441 for (AstRef sourceRef : analysis.getBreakableTarget(hierarchy.getFst())) { 442 IrIntVar[] sourceRefs = refPointers.get(sourceRef); 443 444 IrIntExpr[] array = new IrIntExpr[sourceRefs.length]; 445 System.arraycopy(sourceRefs, 0, array, 0, array.length); 446 IrIntExpr count = count(hierarchy.getSnd().intValue(), array); 447 IrIntVar countVar = domainInt("CountVar" + countCount++, count.getDomain()); 448 module.addConstraint(equal(countVar, count)); 449 childIndex.add(countVar); 450 } 451 } 452 } 453 childIndices[i] = childIndex.toArray(new IrIntExpr[childIndex.size()]); 454 } 455 for (int i = 0; i < weight.length; i++) { 456 weight[i] = 457 childIndices[i].length == 0 ? Zero 458 : boundInt(clafer.getName() + "#" + i + "@Weight", 0, scope - 1); 459 } 460 if (getScope(clafer.getParent()) > 1) { 461 module.addConstraint(sortChannel(childIndices, weight)); 462 for (int i = 0; i < siblingSet.length; i++) { 463 module.addConstraint(filterString(siblingSet[i], weight, index[i])); 464 } 465 } 466 for (int i = 0; i < parents.length - 1; i++) { 467 if (ref != null && analysis.isBreakableRef(ref) && ref.isUnique()) { 468 assert childIndices[i + 1].length == childIndices[i].length; 469 if (childIndices[i].length == 1 && breakableRefIds[i]) { 470 if (refPartitions == null) { 471 refPartitions = new DisjointSets<>(); 472 } 473 refPartitions.union(i, i + 1); 474 } 475 // Refs are unique and part of the weight. It is impossible for 476 // two weights to be the same. Enforce a strict order. 477 module.addConstraint(implies(and(members[i], equal(parents[i], parents[i + 1])), 478 sortStrict(childIndices[i + 1], childIndices[i]))); 479 } else { 480 module.addConstraint(implies(equal(parents[i], parents[i + 1]), 481 sort(childIndices[i + 1], childIndices[i]))); 482 } 483 } 484 } 485 486 if (ref != null) { 487 AstClafer tar = ref.getTargetType(); 488 IrIntVar[] refs = Arrays.copyOfRange(refPointers.get(ref), 489 refOffset, refOffset + getScope(clafer)); 490 if (ref.isUnique()) { 491 if (getCard(clafer).getHigh() > 1) { 492 for (int i = 0; i < refs.length - 1; i++) { 493 for (int j = i + 1; j < refs.length; j++) { 494 if (refPartitions == null || !refPartitions.connected(i, j)) { 495 module.addConstraint( 496 implies(and(members[i], equal(parents[i], parents[j])), 497 notEqual(refs[i], refs[j]))); 498 } 499 } 500 } 501 } 502 IrIntExpr size = 503 ref.getTargetType() instanceof AstIntClafer 504 ? constant(analysis.getScope().getIntHigh() - analysis.getScope().getIntLow() + 1) 505 : card(sets.get(ref.getTargetType())); 506 for (IrSetExpr sibling : siblingSet) { 507 module.addConstraint(lessThanEqual(card(sibling), size)); 508 } 509 } 510 assert refs.length == members.length; 511 for (int i = 0; i < members.length; i++) { 512 // The ref pointers must point to the special uninitialized value 513 // if the Clafer owning the ref pointers does not exists. 514 module.addConstraint(ifOnlyIf(not(members[i]), equal(refs[i], getUninitalizedRef(tar)))); 515 } 516 if (!(ref.getTargetType() instanceof AstIntClafer)) { 517 IrSetVar targetSet = sets.get(ref.getTargetType()); 518 for (int i = 0; i < refs.length; i++) { 519 // The ref pointers must point to a target that exists. 520 module.addConstraint(ifOnlyIf(members[i], member(refs[i], targetSet))); 521 } 522 } 523 } 524 525 switch (getFormat(clafer)) { 526 case LowGroup: 527 constrainLowGroupConcrete(clafer); 528 break; 529 case ParentGroup: 530 constrainParentGroupConcrete(clafer); 531 break; 532 default: 533 throw new AstException(); 534 } 535 } 536 537 private void constrainGroupCardinality(AstClafer clafer) { 538 Card groupCard = clafer.getGroupCard(); 539 List<AstConcreteClafer> children = clafer.getChildren(); 540 if (groupCard.isBounded()) { 541 IrBoolExpr[] members = memberships.get(clafer); 542 IrSetVar[][] childrenSets = new IrSetVar[children.size()][]; 543 for (int i = 0; i < childrenSets.length; i++) { 544 AstConcreteClafer child = children.get(i); 545 childrenSets[i] = siblingSets.get(child); 546 } 547 int scope = getScope(clafer); 548 for (int i = 0; i < scope; i++) { 549 IrIntExpr[] cards = new IrIntExpr[childrenSets.length]; 550 for (int j = 0; j < cards.length; j++) { 551 cards[j] = card(childrenSets[j][i]); 552 } 553 module.addConstraint(implies(members[i], constrainCard(add(cards), groupCard))); 554 } 555 } 556 } 557 558 private void initLowGroupConcrete(AstConcreteClafer clafer) { 559 PartialSolution partialSolution = getPartialSolution(clafer); 560 561 IrSetVar[] childSet = buildChildSet(clafer); 562 siblingSets.put(clafer, childSet); 563 564 IrBoolExpr[] members = new IrBoolExpr[getScope(clafer)]; 565 for (int i = 0; i < members.length; i++) { 566 if (partialSolution.hasClafer(i)) { 567 members[i] = True; 568 } else { 569 members[i] = bool(clafer.getName() + "@Membership#" + i); 570 if (childSet.length == 1 && members.length == 1) { 571 module.addConstraint(equal(members[i], card(childSet[0]))); 572 } 573 } 574 } 575 Check.noNulls(members); 576 memberships.put(clafer, members); 577 } 578 579 private void constrainLowGroupConcrete(AstConcreteClafer clafer) { 580 IrBoolExpr[] members = memberships.get(clafer); 581 IrSetVar set = sets.get(clafer); 582 583 IrBoolExpr[] parentMembership = memberships.get(clafer.getParent()); 584 Card card = getCard(clafer); 585 IrSetVar[] childSet = siblingSets.get(clafer); 586 587 if (fullSymmetryBreaking) { 588 module.addConstraint(selectN(members, card(set))); 589 module.addConstraint(sort(childSet)); 590 } 591 592 for (int i = 0; i < parentMembership.length; i++) { 593 IrBoolExpr parentMember = parentMembership[i]; 594 if (card.isBounded()) { 595 // Enforce cardinality. 596 module.addConstraint(implies(parentMember, constrainCard(card(childSet[i]), card))); 597 } 598 module.addConstraint(implies(not(parentMember), equal(childSet[i], EmptySet))); 599 } 600 601 602 if (!(childSet.length == 1 && members.length == 1)) { 603 module.addConstraint(boolChannel(members, set)); 604 } 605 606 /** 607 * What is this optimization? 608 * 609 * Force the lower number atoms to choose lower number parents. For 610 * example consider the following Clafer model: 611 * 612 * <pre> 613 * Person 2 614 * Hand 2 615 * </pre> 616 * 617 * The constraint forbids the case where Hand0 belongs to Person1 and 618 * Hand1 belongs to Person0. Otherwise, the children can swap around 619 * creating many isomorphic solutions. 620 */ 621 if (fullSymmetryBreaking) { 622 module.addConstraint(sort(parentPointers.get(clafer))); 623 } 624 } 625 626 private void initParentGroupConcrete(AstConcreteClafer clafer) { 627 PartialSolution partialParentSolution = getPartialParentSolution(clafer); 628 629 IrSetVar[] children = new IrSetVar[partialParentSolution.size()]; 630 assert getCard(clafer).getLow() == getCard(clafer).getHigh(); 631 int lowCard = getCard(clafer).getLow(); 632 for (int i = 0; i < children.length; i++) { 633 if (partialParentSolution.hasClafer(i)) { 634 children[i] = constant(Util.fromTo(i * lowCard, i * lowCard + lowCard)); 635 } else { 636 children[i] = set(clafer.getName() + "#" + i, Util.fromTo(i * lowCard, i * lowCard + lowCard)); 637 } 638 } 639 640 siblingSets.put(clafer, children); 641 642 IrBoolExpr[] members = new IrBoolExpr[getScope(clafer)]; 643 IrBoolExpr[] parentMembership = memberships.get(clafer.getParent()); 644 if (lowCard == 1) { 645 if (members.length == parentMembership.length) { 646 members = parentMembership; 647 } else { 648 System.arraycopy(parentMembership, 0, members, 0, parentMembership.length); 649 Arrays.fill(members, parentMembership.length, members.length, False); 650 } 651 } else { 652 for (int i = 0; i < parentMembership.length; i++) { 653 for (int j = 0; j < lowCard; j++) { 654 members[i * lowCard + j] = parentMembership[i]; 655 } 656 } 657 Arrays.fill(members, parentMembership.length * lowCard, members.length, False); 658 } 659 Check.noNulls(members); 660 memberships.put(clafer, members); 661 } 662 663 private void constrainParentGroupConcrete(AstConcreteClafer clafer) { 664 PartialSolution partialParentSolution = getPartialParentSolution(clafer); 665 666 IrSetVar[] children = siblingSets.get(clafer); 667 assert getCard(clafer).getLow() == getCard(clafer).getHigh(); 668 int lowCard = getCard(clafer).getLow(); 669 for (int i = 0; i < children.length; i++) { 670 if (!partialParentSolution.hasClafer(i)) { 671 if (lowCard == 1) { 672 module.addConstraint(equal(memberships.get(clafer.getParent())[i], 673 card(children[i]))); 674 } 675 module.addConstraint(implies(memberships.get(clafer.getParent())[i], 676 equal(children[i], constant(Util.fromTo(i * lowCard, i * lowCard + lowCard))))); 677 module.addConstraint(implies(not(memberships.get(clafer.getParent())[i]), 678 equal(children[i], EmptySet))); 679 680 } 681 } 682 } 683 684 private void initAbstract(AstAbstractClafer clafer) { 685 IrSetVar[] subSets = new IrSetVar[clafer.getSubs().size()]; 686 IrBoolExpr[] members = new IrBoolExpr[getScope(clafer)]; 687 for (int i = 0; i < subSets.length; i++) { 688 AstClafer sub = clafer.getSubs().get(i); 689 subSets[i] = sets.get(sub); 690 IrBoolExpr[] subMembers = memberships.get(sub); 691 int offset = getOffset(clafer, sub); 692 for (int j = 0; j < subMembers.length; j++) { 693 assert members[offset + j] == null; 694 members[offset + j] = Check.notNull(subMembers[j]); 695 } 696 } 697 if (subSets.length == 1) { 698 sets.put(clafer, sets.get(clafer.getSubs().get(0))); 699 } else { 700 TIntArrayList env = new TIntArrayList(); 701 TIntArrayList ker = new TIntArrayList(); 702 for (int i = 0; i < members.length; i++) { 703 if (IrUtil.isTrue(members[i])) { 704 ker.add(i); 705 } 706 if (!IrUtil.isFalse(members[i])) { 707 env.add(i); 708 } 709 } 710 IrSetVar unionSet = set(clafer.getName(), env.toArray(), ker.toArray()); 711 if (!AstUtil.isTypeRoot(clafer)) { 712 module.addConstraint(boolChannel(members, unionSet)); 713 } 714 sets.put(clafer, unionSet); 715 } 716 Check.noNulls(members); 717 memberships.put(clafer, members); 718 719 if (clafer.hasRef()) { 720 refPointers.put(clafer.getRef(), buildRefPointers(clafer.getRef())); 721 } 722 } 723 724 private void constrainAbstract(AstAbstractClafer clafer) { 725 // Do nothing. 726 } 727 private final Map<AstClafer, IrSetVar> sets = new HashMap<>(); 728 private final Map<AstClafer, IrSetVar[]> siblingSets = new HashMap<>(); 729 private final Map<AstClafer, IrBoolExpr[]> memberships = new HashMap<>(); 730 private final Map<AstConcreteClafer, IrIntVar[]> parentPointers = new HashMap<>(); 731 private final Map<AstRef, IrIntVar[]> refPointers = new HashMap<>(); 732 private final Map<AstClafer, IrIntExpr[][]> indices = new HashMap<>(); 733 // The weight of an uninitialed Clafer is always zero. 734 private final Map<AstClafer, IrIntExpr[]> weights = new HashMap<>(); 735 private int countCount = 0; 736 private int localCount = 0; 737 738 private class ExpressionCompiler implements AstExprVisitor<Void, IrExpr> { 739 740 private final int thisId; 741 private final Map<AstLocal, IrIntExpr> locals = new HashMap<>(); 742 743 private ExpressionCompiler(int thisId) { 744 this.thisId = thisId; 745 } 746 747 private IrExpr compile(AstExpr expr) { 748 return expr.accept(this, null); 749 } 750 751 private IrExpr[] compile(AstExpr[] exprs) { 752 IrExpr[] compiled = new IrExpr[exprs.length]; 753 for (int i = 0; i < compiled.length; i++) { 754 compiled[i] = compile(exprs[i]); 755 } 756 return compiled; 757 } 758 759 private IrBoolExpr compile(AstBoolExpr expr) { 760 return (IrBoolExpr) compile((AstExpr) expr); 761 } 762 763 private IrBoolExpr[] compile(AstBoolExpr[] exprs) { 764 IrBoolExpr[] compiled = new IrBoolExpr[exprs.length]; 765 for (int i = 0; i < compiled.length; i++) { 766 compiled[i] = compile(exprs[i]); 767 } 768 return compiled; 769 } 770 771 private IrIntExpr asInt(IrExpr expr) { 772 if (expr instanceof IrIntExpr) { 773 return (IrIntExpr) expr; 774 } 775 if (expr instanceof IrSetExpr) { 776 return sum((IrSetExpr) expr); 777 } 778 // Bug. 779 throw new AstException("Should not have passed type checking."); 780 } 781 782 private IrIntExpr[] asInts(IrExpr[] exprs) { 783 IrIntExpr[] ints = new IrIntExpr[exprs.length]; 784 for (int i = 0; i < ints.length; i++) { 785 ints[i] = asInt(exprs[i]); 786 } 787 return ints; 788 } 789 790 private IrSetExpr asSet(IrExpr expr) { 791 if (expr instanceof IrIntExpr) { 792 return singleton((IrIntExpr) expr); 793 } 794 if (expr instanceof IrSetExpr) { 795 return (IrSetExpr) expr; 796 } 797 // Bug. 798 throw new AstException("Should not have passed type checking."); 799 } 800 801 private IrSetExpr[] asSets(IrExpr[] exprs) { 802 IrSetExpr[] sets = new IrSetExpr[exprs.length]; 803 for (int i = 0; i < sets.length; i++) { 804 sets[i] = asSet(exprs[i]); 805 } 806 return sets; 807 } 808 809 @Override 810 public IrExpr visit(AstThis ast, Void a) { 811 return constant(thisId); 812 } 813 814 @Override 815 public IrExpr visit(AstGlobal ast, Void a) { 816 IrSetVar global = sets.get(ast.getType()); 817 if (global.getEnv().size() == 1) { 818 int[] constant = IrUtil.getConstant(global); 819 if (constant != null) { 820 return constant(constant[0]); 821 } 822 } 823 return global; 824 } 825 826 @Override 827 public IrExpr visit(AstConstant ast, Void a) { 828 int[] value = ast.getValue(); 829 if (value.length == 1) { 830 return constant(value[0]); 831 } 832 return constant(value); 833 } 834 835 @Override 836 public IrExpr visit(AstJoin ast, Void a) { 837 return doJoin(compile(ast.getLeft()), ast.getRight()); 838 } 839 840 private IrExpr doJoin(IrExpr left, AstConcreteClafer right) { 841 if (left instanceof IrIntExpr) { 842 IrIntExpr $intLeft = (IrIntExpr) left; 843 if (Format.ParentGroup.equals(getFormat(right)) && getCard(right).getLow() == 1) { 844 assert getCard(right).isExact(); 845 return $intLeft; 846 } 847 // Why empty set? The "take" var can contain unused. 848 return joinRelation(singleton($intLeft), Util.snoc(siblingSets.get(right), EmptySet), true); 849 } else if (left instanceof IrSetExpr) { 850 IrSetExpr $setLeft = (IrSetExpr) left; 851 // Why empty set? The "take" var can contain unused. 852 return joinRelation($setLeft, Util.snoc(siblingSets.get(right), EmptySet), true); 853 } 854 throw new AstException(); 855 } 856 857 @Override 858 public IrExpr visit(AstJoinParent ast, Void a) { 859 AstConcreteClafer childrenType = (AstConcreteClafer) getCommonSupertype(ast.getChildren()); 860 861 IrExpr children = compile(ast.getChildren()); 862 if (children instanceof IrIntExpr) { 863 IrIntExpr intChildren = (IrIntExpr) children; 864 switch (getFormat(childrenType)) { 865 case ParentGroup: 866 assert getCard(childrenType).isExact(); 867 int lowCard = getCard(childrenType).getLow(); 868 return div(intChildren, constant(lowCard)); 869 case LowGroup: 870 return element(parentPointers.get(childrenType), intChildren); 871 } 872 } else if (children instanceof IrSetExpr) { 873 IrSetExpr setChildren = (IrSetExpr) children; 874 return joinFunction(setChildren, parentPointers.get(childrenType), null); 875 } 876 throw new AstException(); 877 } 878 879 @Override 880 public IrExpr visit(AstJoinRef ast, Void a) { 881 AstSetExpr deref = ast.getDeref(); 882 AstClafer derefType = getCommonSupertype(deref); 883 884 Integer globalCardinality = null; 885 IrExpr $deref; 886 if (derefType.getRef().isUnique()) { 887 if (deref instanceof AstJoin) { 888 AstJoin join = (AstJoin) deref; 889 IrExpr left = compile(join.getLeft()); 890 $deref = doJoin(left, join.getRight()); 891 892 globalCardinality = left instanceof IrSetExpr 893 ? ((IrSetExpr) left).getCard().getHighBound() 894 : 1; 895 } else { 896 $deref = compile(deref); 897 if (derefType instanceof AstConcreteClafer) { 898 globalCardinality = getScope(((AstConcreteClafer) derefType).getParent()); 899 } 900 } 901 } else { 902 $deref = compile(deref); 903 } 904 905 if ($deref instanceof IrIntExpr) { 906 IrIntExpr $intDeref = (IrIntExpr) $deref; 907 // Why zero? The "take" var can contain unused. 908 return element(Util.snoc(refPointers.get(derefType.getRef()), Zero), $intDeref); 909 } else if ($deref instanceof IrSetExpr) { 910 IrSetExpr $setDeref = (IrSetExpr) $deref; 911 // Why zero? The "take" var can contain unused. 912 return joinFunction($setDeref, Util.snoc(refPointers.get(derefType.getRef()), Zero), globalCardinality); 913 } 914 throw new AstException(); 915 } 916 917 @Override 918 public IrExpr visit(AstCard ast, Void a) { 919 IrExpr set = compile(ast.getSet()); 920 if (set instanceof IrIntExpr) { 921 return One; 922 } 923 return card((IrSetExpr) set); 924 } 925 926 @Override 927 public IrExpr visit(AstNot ast, Void a) { 928 return not(compile(ast.getExpr())); 929 } 930 931 @Override 932 public IrExpr visit(AstMinus ast, Void a) { 933 return minus(asInt(compile(ast.getExpr()))); 934 } 935 936 @Override 937 public IrExpr visit(AstSetTest ast, Void a) { 938 IrExpr left = compile(ast.getLeft()); 939 IrExpr right = compile(ast.getRight()); 940 941 if (left instanceof IrIntExpr && right instanceof IrIntExpr) { 942 IrIntExpr $intLeft = (IrIntExpr) left; 943 IrIntExpr $intRight = (IrIntExpr) right; 944 945 switch (ast.getOp()) { 946 case Equal: 947 return equal($intLeft, $intRight); 948 case NotEqual: 949 return notEqual($intLeft, $intRight); 950 } 951 } 952 953 switch (ast.getOp()) { 954 case Equal: 955 return equal(asSet(left), asSet(right)); 956 case NotEqual: 957 return notEqual(asSet(left), asSet(right)); 958 default: 959 throw new AstException(); 960 } 961 } 962 963 @Override 964 public IrExpr visit(AstCompare ast, Void a) { 965 IrIntExpr left = asInt(compile(ast.getLeft())); 966 IrIntExpr right = asInt(compile(ast.getRight())); 967 switch (ast.getOp()) { 968 case LessThan: 969 return lessThan(left, right); 970 case LessThanEqual: 971 return lessThanEqual(left, right); 972 case GreaterThan: 973 return greaterThan(left, right); 974 case GreaterThanEqual: 975 return greaterThanEqual(left, right); 976 default: 977 throw new AstException(); 978 } 979 } 980 981 @Override 982 public IrExpr visit(AstArithm ast, Void a) { 983 IrIntExpr[] operands = asInts(compile(ast.getOperands())); 984 switch (ast.getOp()) { 985 case Add: 986 return add(operands); 987 case Sub: 988 return sub(operands); 989 case Mul: 990 IrIntExpr product = operands[0]; 991 for (int i = 1; i < operands.length; i++) { 992 product = mul(product, operands[i]); 993 } 994 return product; 995 case Div: 996 IrIntExpr quotient = operands[0]; 997 for (int i = 1; i < operands.length; i++) { 998 quotient = div(quotient, operands[i]); 999 } 1000 return quotient; 1001 default: 1002 throw new AstException(); 1003 } 1004 } 1005 1006 @Override 1007 public IrExpr visit(AstSum ast, Void a) { 1008 AstSetExpr set = ast.getSet(); 1009 AstClafer setType = getCommonSupertype(set); 1010 assert setType.hasRef(); 1011 IrIntVar[] refs = refPointers.get(setType.getRef()); 1012 1013 IrBoolExpr[] members; 1014 if (ast.getSet() instanceof AstGlobal) { 1015 members = memberships.get(setType); 1016 } else { 1017 IrExpr $set = compile(ast.getSet()); 1018 if (set instanceof IrIntExpr) { 1019 IrIntExpr intSet = (IrIntExpr) set; 1020 return element(refs, intSet); 1021 } 1022 IrSetExpr setSet = (IrSetExpr) $set; 1023 assert setSet.getEnv().getLowBound() >= 0; 1024 members = new IrBoolExpr[setSet.getEnv().getHighBound() + 1]; 1025 for (int i = 0; i < members.length; i++) { 1026 members[i] = bool("SumMember@" + i); 1027 } 1028 module.addConstraint(boolChannel(members, setSet)); 1029 } 1030 assert members.length <= refs.length; 1031 1032 IrIntVar[] score = new IrIntVar[members.length]; 1033 for (int i = 0; i < members.length; i++) { 1034 IrDomain domain = refs[i].getDomain(); 1035 int uninitializedRef = getUninitalizedRef(setType.getRef().getTargetType()); 1036 // Score's use 0 as the uninitialized value. 1037 domain = IrUtil.add(IrUtil.remove(domain, uninitializedRef), 0); 1038 score[i] = domainInt("Score@" + i, domain); 1039 module.addConstraint(ifThenElse(members[i], 1040 equal(score[i], refs[i]), equal(score[i], 0))); 1041 } 1042 return add(score); 1043 } 1044 1045 @Override 1046 public IrExpr visit(AstBoolArithm ast, Void a) { 1047 IrBoolExpr[] operands = compile(ast.getOperands()); 1048 switch (ast.getOp()) { 1049 case And: 1050 return and(operands); 1051 case IfOnlyIf: 1052 IrBoolExpr ifOnlyIf = operands[0]; 1053 for (int i = 1; i < operands.length; i++) { 1054 ifOnlyIf = ifOnlyIf(ifOnlyIf, operands[i]); 1055 } 1056 return ifOnlyIf; 1057 case Implies: 1058 IrBoolExpr implies = operands[0]; 1059 for (int i = 1; i < operands.length; i++) { 1060 implies = implies(implies, operands[i]); 1061 } 1062 return implies; 1063 case Or: 1064 return or(operands); 1065 case Xor: 1066 IrBoolExpr xor = operands[0]; 1067 for (int i = 1; i < operands.length; i++) { 1068 xor = xor(xor, operands[i]); 1069 } 1070 return xor; 1071 default: 1072 throw new AstException(); 1073 } 1074 } 1075 1076 @Override 1077 public IrExpr visit(AstDifference ast, Void a) { 1078 return difference( 1079 asSet(compile(ast.getLeft())), 1080 asSet(compile(ast.getRight()))); 1081 } 1082 1083 @Override 1084 public IrExpr visit(AstIntersection ast, Void a) { 1085 return intersection( 1086 asSet(compile(ast.getLeft())), 1087 asSet(compile(ast.getRight()))); 1088 } 1089 1090 @Override 1091 public IrExpr visit(AstUnion ast, Void a) { 1092 return union( 1093 asSet(compile(ast.getLeft())), 1094 asSet(compile(ast.getRight()))); 1095 } 1096 1097 @Override 1098 public IrExpr visit(AstMembership ast, Void a) { 1099 IrExpr member = compile(ast.getMember()); 1100 IrExpr set = compile(ast.getSet()); 1101 if (member instanceof IrIntExpr && set instanceof IrIntExpr) { 1102 return AstMembership.Op.In.equals(ast.getOp()) 1103 ? equal((IrIntExpr) member, (IrIntExpr) set) 1104 : notEqual((IrIntExpr) member, (IrIntExpr) set); 1105 } 1106 if (member instanceof IrIntExpr && set instanceof IrSetExpr) { 1107 return AstMembership.Op.In.equals(ast.getOp()) 1108 ? member((IrIntExpr) member, (IrSetExpr) set) 1109 : notMember((IrIntExpr) member, (IrSetExpr) set); 1110 } 1111 if (member instanceof IrSetExpr && set instanceof IrIntExpr) { 1112 return AstMembership.Op.In.equals(ast.getOp()) 1113 ? equal((IrSetExpr) member, singleton((IrIntExpr) set)) 1114 : notEqual((IrSetExpr) member, singleton((IrIntExpr) set)); 1115 } 1116 return AstMembership.Op.In.equals(ast.getOp()) 1117 ? subsetEq(asSet(member), asSet(set)) 1118 : not(subsetEq(asSet(member), asSet(set))); 1119 } 1120 1121 @Override 1122 public IrExpr visit(AstTernary ast, Void a) { 1123 IrBoolExpr antecedent = compile(ast.getAntecedent()); 1124 IrExpr consequent = compile(ast.getConsequent()); 1125 IrExpr alternative = compile(ast.getAlternative()); 1126 if (consequent instanceof IrIntExpr && alternative instanceof IrIntExpr) { 1127 return ternary(antecedent, (IrIntExpr) consequent, (IrIntExpr) alternative); 1128 } 1129 return ternary(antecedent, asSet(consequent), asSet(alternative)); 1130 } 1131 1132 @Override 1133 public IrExpr visit(AstIfThenElse ast, Void a) { 1134 return ifThenElse(compile(ast.getAntecedent()), compile(ast.getConsequent()), compile(ast.getAlternative())); 1135 } 1136 1137 @Override 1138 public IrExpr visit(AstDowncast ast, Void a) { 1139 AstSetExpr base = ast.getBase(); 1140 int offset = getOffset((AstAbstractClafer) getCommonSupertype(base), ast.getTarget()); 1141 1142 IrExpr $base = compile(ast.getBase()); 1143 if ($base instanceof IrIntExpr) { 1144 IrIntExpr intBase = (IrIntExpr) $base; 1145 return sub(intBase, constant(offset)); 1146 } 1147 return mask((IrSetExpr) $base, offset, offset + getScope(ast.getTarget())); 1148 } 1149 1150 @Override 1151 public IrExpr visit(AstUpcast ast, Void a) { 1152 AstSetExpr base = ast.getBase(); 1153 int offset = getOffset(ast.getTarget(), getCommonSupertype(base)); 1154 1155 IrExpr $base = compile(ast.getBase()); 1156 if ($base instanceof IrIntExpr) { 1157 IrIntExpr intBase = (IrIntExpr) $base; 1158 return add(intBase, constant(offset)); 1159 } 1160 return offset((IrSetExpr) $base, offset); 1161 } 1162 1163 @Override 1164 public IrExpr visit(AstLocal ast, Void a) { 1165 return locals.get(ast); 1166 } 1167 1168 private Triple<AstLocal, IrIntExpr, IrBoolExpr>[][] compileDecl(AstDecl decl) { 1169 IrExpr body = compile(decl.getBody()); 1170 if (body instanceof IrIntExpr) { 1171 IrIntExpr intBody = (IrIntExpr) body; 1172 @SuppressWarnings("unchecked") 1173 Triple<AstLocal, IrIntExpr, IrBoolExpr>[] labeledPermutation = new Triple[decl.getLocals().length]; 1174 for (int i = 0; i < labeledPermutation.length; i++) { 1175 labeledPermutation[i] = new Triple<AstLocal, IrIntExpr, IrBoolExpr>( 1176 decl.getLocals()[i], intBody, True); 1177 } 1178 @SuppressWarnings("unchecked") 1179 Triple<AstLocal, IrIntExpr, IrBoolExpr>[][] labeledSequence = new Triple[][]{labeledPermutation}; 1180 return labeledSequence; 1181 } 1182 if (body instanceof IrSetExpr) { 1183 IrSetExpr setBody = (IrSetExpr) body; 1184 IrDomain env = setBody.getEnv(); 1185 IrDomain ker = setBody.getKer(); 1186 // TODO: need a different strategy otherwise 1187 assert env.getLowBound() >= 0; 1188 @SuppressWarnings("unchecked") 1189 Pair<IrIntExpr, IrBoolExpr>[] members = new Pair[env.getHighBound() + 1]; 1190 for (int i = 0; i < env.getLowBound(); i++) { 1191 members[i] = new Pair<IrIntExpr, IrBoolExpr>(constant(i), False); 1192 } 1193 for (int i = env.getLowBound(); i <= env.getHighBound(); i++) { 1194 members[i] = new Pair<IrIntExpr, IrBoolExpr>(constant(i), 1195 ker.contains(i) ? True 1196 : bool(Util.intercalate("/", AstUtil.getNames(decl.getLocals())) + "#" + i + "#" + localCount++)); 1197 } 1198 module.addConstraint(boolChannel(Pair.mapSnd(members), setBody)); 1199 Pair<IrIntExpr, IrBoolExpr>[][] sequence = decl.isDisjoint() ? Util.permutations(members, 1200 decl.getLocals().length) : Util.sequence(members, decl.getLocals().length); 1201 1202 @SuppressWarnings("unchecked") 1203 Triple<AstLocal, IrIntExpr, IrBoolExpr>[][] labeledSequence = new Triple[sequence.length][]; 1204 for (int i = 0; i < labeledSequence.length; i++) { 1205 Pair<IrIntExpr, IrBoolExpr>[] permutation = sequence[i]; 1206 @SuppressWarnings("unchecked") 1207 Triple<AstLocal, IrIntExpr, IrBoolExpr>[] labeledPermutation = new Triple[permutation.length]; 1208 for (int j = 0; j < labeledPermutation.length; j++) { 1209 labeledPermutation[j] = new Triple<>( 1210 decl.getLocals()[j], permutation[j]); 1211 } 1212 labeledSequence[i] = labeledPermutation; 1213 } 1214 return labeledSequence; 1215 } 1216 throw new AstException(); 1217 } 1218 1219 // TODO optimize SOME 1220 @Override 1221 public IrExpr visit(AstQuantify ast, Void a) { 1222 AstDecl decls[] = ast.getDecls(); 1223 @SuppressWarnings("unchecked") 1224 Triple<AstLocal, IrIntExpr, IrBoolExpr>[][][] compiledDecls = new Triple[decls.length][][]; 1225 for (int i = 0; i < compiledDecls.length; i++) { 1226 compiledDecls[i] = compileDecl(decls[i]); 1227 } 1228 compiledDecls = Util.sequence(compiledDecls); 1229 1230 List<IrBoolExpr> compiled = new ArrayList<>(); 1231 for (Triple<AstLocal, IrIntExpr, IrBoolExpr>[][] quants : compiledDecls) { 1232 List<IrBoolExpr> constraints = new ArrayList<>(); 1233 for (Triple<AstLocal, IrIntExpr, IrBoolExpr>[] quantDecls : quants) { 1234 for (Triple<AstLocal, IrIntExpr, IrBoolExpr> quantLocals : quantDecls) { 1235 constraints.add(quantLocals.getThd()); 1236 locals.put(quantLocals.getFst(), quantLocals.getSnd()); 1237 } 1238 } 1239 IrBoolExpr compiledBody = compile(ast.getBody()); 1240 if (Quantifier.All.equals(ast.getQuantifier())) { 1241 compiled.add(implies(and(constraints), compiledBody)); 1242 } else { 1243 constraints.add(compiledBody); 1244 compiled.add(and(constraints)); 1245 } 1246 } 1247 1248 switch (ast.getQuantifier()) { 1249 case All: 1250 return and(compiled); 1251 case Lone: 1252 return lone(compiled); 1253 case None: 1254 return not(or(compiled)); 1255 case One: 1256 return one(compiled); 1257 case Some: 1258 return or(compiled); 1259 default: 1260 throw new AstException(); 1261 1262 } 1263 } 1264 }; 1265 1266 /* 1267 ****************** 1268 * Build functions. 1269 ****************** 1270 */ 1271 /** 1272 * Build the child set for the Clafer. 1273 * 1274 * @param clafer the Clafer 1275 * @return the variables to represent the child relation 1276 */ 1277 private IrSetVar[] buildChildSet(AstConcreteClafer clafer) { 1278 assert Format.LowGroup.equals(getFormat(clafer)); 1279 1280 int parentScope = getScope(clafer.getParent()); 1281 PartialSolution partialParentSolution = getPartialSolution(clafer.getParent()); 1282 1283 int claferScope = getScope(clafer); 1284 Card card = getCard(clafer); 1285 assert card.hasHigh(); 1286 1287 int low = 0; 1288 int high = card.getHigh(); 1289 int max = claferScope - 1; 1290 1291 IrSetVar[] skip = new IrSetVar[parentScope]; 1292 for (int i = 0; i < skip.length; i++) { 1293 if (low <= max) { 1294 IrDomain env = boundDomain(low, Math.min(high - 1, max)); 1295 IrDomain ker = EmptyDomain; 1296 int cardLow = 0; 1297 int cardHigh = card.getHigh(); 1298 if (partialParentSolution.hasClafer(i)) { 1299 int prevHigh = high - card.getHigh(); 1300 int nextLow = low + card.getLow(); 1301 if (nextLow > prevHigh) { 1302 ker = boundDomain(prevHigh, Math.min(nextLow - 1, max)); 1303 } 1304 cardLow = card.getLow(); 1305 } 1306 cardLow = Math.max(cardLow, ker.size()); 1307 cardHigh = Math.min(cardHigh, env.size()); 1308 skip[i] = set(clafer.getName() + "#" + i, env, ker, boundDomain(cardLow, cardHigh)); 1309 } else { 1310 skip[i] = EmptySet; 1311 } 1312 if (partialParentSolution.hasClafer(i)) { 1313 low += card.getLow(); 1314 } 1315 high += card.getHigh(); 1316 } 1317 return skip; 1318 } 1319 1320 /** 1321 * Create the parent pointers for the Clafer. 1322 * 1323 * @param clafer the Clafer 1324 * @return the variables to represent the parent relation 1325 */ 1326 private IrIntVar[] buildParentPointers(AstConcreteClafer clafer) { 1327 PartialSolution solution = getPartialSolution(clafer); 1328 boolean known = solution.parentSolutionKnown(); 1329 IrIntVar[] pointers = new IrIntVar[solution.size()]; 1330 for (int i = 0; i < pointers.length; i++) { 1331 int[] possibleParents = solution.getPossibleParents(i); 1332 pointers[i] = enumInt(clafer.getName() + "@Parent#" + i, 1333 solution.hasClafer(i) || known 1334 ? possibleParents 1335 : Util.snoc(possibleParents, getScope(clafer.getParent()))); 1336 } 1337 return pointers; 1338 } 1339 1340 /** 1341 * Create the references pointers for the Clafer. 1342 * 1343 * @param ref the reference Clafer 1344 * @return the variables to represent the reference relation 1345 */ 1346 private IrIntVar[] buildRefPointers(AstRef ref) { 1347 AstClafer src = ref.getSourceType(); 1348 AstClafer tar = ref.getTargetType(); 1349 1350 PartialSolution partialSolution = getPartialSolution(src); 1351 IrDomain[] partialInts = getPartialInts(ref); 1352 IrIntVar[] ivs = new IrIntVar[getScope(src)]; 1353 for (int i = 0; i < ivs.length; i++) { 1354 if (partialSolution.hasClafer(i)) { 1355 ivs[i] = domainInt(src.getName() + "@Ref" + i, partialInts[i]); 1356 } else { 1357 ivs[i] = domainInt(src.getName() + "@Ref" + i, IrUtil.add(partialInts[i], getUninitalizedRef(tar))); 1358 } 1359 } 1360 1361 return ivs; 1362 } 1363 1364 /** 1365 * Enforce the size of a set to be within the cardinality. 1366 * 1367 * @param setCard the set to constrain 1368 * @param card the cardinality 1369 * @return card.low ≤ |setCard| ≤ card.high 1370 */ 1371 private IrBoolExpr constrainCard(IrIntExpr setCard, Card card) { 1372 if (card.isExact()) { 1373 return equal(setCard, card.getLow()); 1374 } 1375 if (card.hasLow() && card.hasHigh()) { 1376 return within(setCard, boundDomain(card.getLow(), card.getHigh())); 1377 } 1378 if (card.hasLow()) { 1379 return greaterThanEqual(setCard, card.getLow()); 1380 } 1381 if (card.hasHigh()) { 1382 return lessThanEqual(setCard, card.getHigh()); 1383 } 1384 return True; 1385 } 1386 1387 /* 1388 ************************ 1389 * Convenience functions. 1390 ************************ 1391 */ 1392 private int getUninitalizedRef(AstClafer clafer) { 1393 return getScopeHigh(clafer) + 1; 1394 } 1395 1396 private int getScopeLow(AstClafer clafer) { 1397 return clafer instanceof AstIntClafer ? analysis.getScope().getIntLow() : 0; 1398 } 1399 1400 private int getScopeHigh(AstClafer clafer) { 1401 return clafer instanceof AstIntClafer ? analysis.getScope().getIntHigh() : getScope(clafer) - 1; 1402 } 1403 1404 private int getScope(AstClafer clafer) { 1405 return analysis.getScope().getScope(clafer); 1406 } 1407 1408 private Format getFormat(AstClafer clafer) { 1409 return analysis.getFormat(clafer); 1410 } 1411 1412 private PartialSolution getPartialSolution(AstClafer clafer) { 1413 return analysis.getPartialSolution(clafer); 1414 } 1415 1416 private PartialSolution getPartialParentSolution(AstConcreteClafer clafer) { 1417 return getPartialSolution(clafer.getParent()); 1418 } 1419 1420 private IrDomain[] getPartialInts(AstRef ref) { 1421 return analysis.getPartialInts(ref); 1422 } 1423 1424 private int getOffset(AstAbstractClafer sup, AstClafer sub) { 1425 int offset = 0; 1426 for (AstClafer cur = sub; !sup.equals(cur); cur = cur.getSuperClafer()) { 1427 if (!cur.hasSuperClafer()) { 1428 throw new AstException(sub + " is not a sub clafer of " + sup); 1429 } 1430 offset += analysis.getOffsets(cur.getSuperClafer()).getOffset(cur); 1431 } 1432 return offset; 1433 } 1434 1435 private Card getCard(AstConcreteClafer clafer) { 1436 return analysis.getCard(clafer); 1437 } 1438 1439 private Card getGlobalCard(AstClafer clafer) { 1440 return analysis.getGlobalCard(clafer); 1441 } 1442 1443 private Type getType(AstExpr expr) { 1444 return analysis.getType(expr); 1445 } 1446 1447 private AstClafer getCommonSupertype(AstExpr expr) { 1448 return analysis.getCommonSupertype(expr); 1449 } 1450}