/// Actual model GolombRuler(const SizeOptions& opt) : IntMinimizeScript(opt), m(*this,opt.size(),0, (opt.size() < 31) ? (1 << (opt.size()-1))-1 : Int::Limits::max) { // Assume first mark to be zero rel(*this, m[0], IRT_EQ, 0); // Order marks rel(*this, m, IRT_LE); // Number of marks and differences const int n = m.size(); const int n_d = (n*n-n)/2; // Array of differences IntVarArgs d(n_d); // Setup difference constraints for (int k=0, i=0; i<n-1; i++) for (int j=i+1; j<n; j++, k++) // d[k] is m[j]-m[i] and must be at least sum of first j-i integers rel(*this, d[k] = expr(*this, m[j]-m[i]), IRT_GQ, (j-i)*(j-i+1)/2); distinct(*this, d, opt.icl()); // Symmetry breaking if (n > 2) rel(*this, d[0], IRT_LE, d[n_d-1]); branch(*this, m, INT_VAR_NONE(), INT_VAL_MIN()); }
SetCovering(const SizeOptions& opt) : x(*this, num_alternatives, 0, 1), z(*this, 0, 999999) { // costs per alternative int _costs[] = {19, 16, 18, 13, 15, 19, 15, 17, 16, 15}; IntArgs costs(num_alternatives, _costs); // the alternatives and the objects they contain int _a[] = { // 1 2 3 4 5 6 7 8 the objects 1,0,0,0,0,1,0,0, // alternative 1 0,1,0,0,0,1,0,1, // alternative 2 1,0,0,1,0,0,1,0, // alternative 3 0,1,1,0,1,0,0,0, // alternative 4 0,1,0,0,1,0,0,0, // alternative 5 0,1,1,0,0,0,0,0, // alternative 6 0,1,1,1,0,0,0,0, // alternative 7 0,0,0,1,1,0,0,1, // alternative 8 0,0,1,0,0,1,0,1, // alternative 9 1,0,0,0,0,1,1,0, // alternative 10 }; IntArgs a(num_alternatives*num_objects, _a); for(int j = 0; j < num_objects; j++) { IntVarArgs tmp; for(int i = 0; i < num_alternatives; i++) { tmp << expr(*this, x[i]*a[i*num_objects+j]); } if (opt.size() == 0) { // set partition problem: // objects must be covered _exactly_ once rel(*this, sum(tmp) == 1); } else { // set covering problem // all objects must be covered _at least_ once rel(*this, sum(tmp) >= 1); } } if (opt.search() == SEARCH_DFS) { if (opt.size() == 0) { rel(*this, z <= 49); } else { rel(*this, z <= 45); } } linear(*this, costs, x, IRT_EQ, z); branch(*this, x, INT_VAR_NONE(), INT_VAL_MIN()); }
/// Constructor QueenArmies(const SizeOptions& opt) : n(opt.size()), U(*this, IntSet::empty, IntSet(0, n*n)), W(*this, IntSet::empty, IntSet(0, n*n)), w(*this, n*n, 0, 1), b(*this, n*n, 0, 1), q(*this, 0, n*n) { // Basic rules of the model for (int i = n*n; i--; ) { // w[i] means that no blacks are allowed on A[i] rel(*this, w[i] == (U || A[i])); // Make sure blacks and whites are disjoint. rel(*this, !w[i] || !b[i]); // If i in U, then b[i] has a piece. rel(*this, b[i] == (singleton(i) <= U)); } // Connect optimization variable to number of pieces linear(*this, w, IRT_EQ, q); linear(*this, b, IRT_GQ, q); // Connect cardinality of U to the number of black pieces. IntVar unknowns = expr(*this, cardinality(U)); rel(*this, q <= unknowns); linear(*this, b, IRT_EQ, unknowns); if (opt.branching() == BRANCH_NAIVE) { branch(*this, w, INT_VAR_NONE, INT_VAL_MAX); branch(*this, b, INT_VAR_NONE, INT_VAL_MAX); } else { QueenBranch::post(*this); assign(*this, b, INT_ASSIGN_MAX); } }
/// Actual model TSP(const SizeOptions& opt) : p(ps[opt.size()]), succ(*this, p.size(), 0, p.size()-1), total(*this, 0, p.max()) { int n = p.size(); // Cost matrix IntArgs c(n*n, p.d()); for (int i=n; i--; ) for (int j=n; j--; ) if (p.d(i,j) == 0) rel(*this, succ[i], IRT_NQ, j); // Cost of each edge IntVarArgs costs(*this, n, Int::Limits::min, Int::Limits::max); // Enforce that the succesors yield a tour with appropriate costs circuit(*this, c, succ, costs, total, opt.icl()); // Just assume that the circle starts forwards { IntVar p0(*this, 0, n-1); element(*this, succ, p0, 0); rel(*this, p0, IRT_LE, succ[0]); } // First enumerate cost values, prefer those that maximize cost reduction branch(*this, costs, INT_VAR_REGRET_MAX_MAX(), INT_VAL_SPLIT_MIN()); // Then fix the remaining successors branch(*this, succ, INT_VAR_MIN_MIN(), INT_VAL_MIN()); }
Coins3(const SizeOptions& opt) : num_coins_val(opt.size()), x(*this, n, 0, 99), num_coins(*this, 0, 99) { // values of the coins int _variables[] = {1, 2, 5, 10, 25, 50}; IntArgs variables(n, _variables); // sum the number of coins linear(*this, x, IRT_EQ, num_coins, opt.icl()); // This is the "main loop": // Checks that all changes from 1 to 99 can be made for(int j = 0; j < 99; j++) { IntVarArray tmp(*this, n, 0, 99); linear(*this, variables, tmp, IRT_EQ, j, opt.icl()); for(int i = 0; i < n; i++) { rel(*this, tmp[i] <= x[i], opt.icl()); } } // set the number of coins (via opt.size()) // don't forget // -search dfs if (num_coins_val) { rel(*this, num_coins == num_coins_val, opt.icl()); } branch(*this, x, INT_VAR_SIZE_MAX(), INT_VAL_MIN()); }
/// Actual model Photo(const SizeOptions& opt) : IntMinimizeScript(opt), spec(opt.size() == 0 ? p_small : p_large), pos(*this,spec.n_names, 0, spec.n_names-1), violations(*this,0,spec.n_prefs) { // Map preferences to violation BoolVarArgs viol(spec.n_prefs); for (int i=0; i<spec.n_prefs; i++) { int pa = spec.prefs[2*i+0]; int pb = spec.prefs[2*i+1]; viol[i] = expr(*this, abs(pos[pa]-pos[pb]) > 1); } rel(*this, violations == sum(viol)); distinct(*this, pos, opt.icl()); // Break some symmetries rel(*this, pos[0] < pos[1]); if (opt.branching() == BRANCH_NONE) { branch(*this, pos, INT_VAR_NONE(), INT_VAL_MIN()); } else { branch(*this, pos, tiebreak(INT_VAR_DEGREE_MAX(),INT_VAR_SIZE_MIN()), INT_VAL_MIN()); } }
/// Actual model AllInterval(const SizeOptions& opt) : x(*this, opt.size(), 0, opt.size()-1), d(*this, opt.size()-1, 1, opt.size()-1) { const int n = x.size(); // Set up variables for distance for (int i=0; i<n-1; i++) rel(*this, d[i] == abs(x[i+1]-x[i]), opt.icl()); distinct(*this, x, opt.icl()); distinct(*this, d, opt.icl()); // Break mirror symmetry rel(*this, x[0], IRT_LE, x[1]); // Break symmetry of dual solution rel(*this, d[0], IRT_GR, d[n-2]); branch(*this, x, INT_VAR_SIZE_MIN(), INT_VAL_SPLIT_MIN()); }
/// Actual model OrthoLatinSquare(const SizeOptions& opt) : Script(opt), n(opt.size()), x1(*this,n*n,1,n), x2(*this,n*n,1,n) { const int nn = n*n; IntVarArgs z(*this,nn,0,n*n-1); distinct(*this, z, opt.icl()); // Connect { IntArgs mod(n*n); IntArgs div(n*n); for (int i=0; i<n; i++) for (int j=0; j<n; j++) { mod[i*n+j] = j+1; div[i*n+j] = i+1; } for (int i = nn; i--; ) { element(*this, div, z[i], x2[i]); element(*this, mod, z[i], x1[i]); } } // Rows for (int i = n; i--; ) { IntVarArgs ry(n); for (int j = n; j--; ) ry[j] = y1(i,j); distinct(*this, ry, opt.icl()); for (int j = n; j--; ) ry[j] = y2(i,j); distinct(*this, ry, opt.icl()); } for (int j = n; j--; ) { IntVarArgs cy(n); for (int i = n; i--; ) cy[i] = y1(i,j); distinct(*this, cy, opt.icl()); for (int i = n; i--; ) cy[i] = y2(i,j); distinct(*this, cy, opt.icl()); } for (int i = 1; i<n; i++) { IntVarArgs ry1(n); IntVarArgs ry2(n); for (int j = n; j--; ) { ry1[j] = y1(i-1,j); ry2[j] = y2(i,j); } rel(*this, ry1, IRT_GQ, ry2); } branch(*this, z, INT_VAR_SIZE_MIN(), INT_VAL_SPLIT_MIN()); }
/// The actual problem Queens(const SizeOptions& opt) : q(*this,opt.size(),0,opt.size()-1) { const int n = q.size(); switch (opt.propagation()) { case PROP_BINARY: for (int i = 0; i<n; i++) for (int j = i+1; j<n; j++) { rel(*this, q[i] != q[j]); rel(*this, q[i]+i != q[j]+j); rel(*this, q[i]-i != q[j]-j); } break; case PROP_MIXED: for (int i = 0; i<n; i++) for (int j = i+1; j<n; j++) { rel(*this, q[i]+i != q[j]+j); rel(*this, q[i]-i != q[j]-j); } distinct(*this, q, opt.icl()); break; case PROP_DISTINCT: distinct(*this, IntArgs::create(n,0,1), q, opt.icl()); distinct(*this, IntArgs::create(n,0,-1), q, opt.icl()); distinct(*this, q, opt.icl()); break; } switch(opt.branching()) { case BRANCH_MIN: branch(*this, q, INT_VAR_SIZE_MIN(), INT_VAL_MIN()); break; case BRANCH_MID: branch(*this, q, INT_VAR_SIZE_MIN(), INT_VAL_MIN()); break; case BRANCH_MAX_MAX: branch(*this, q, INT_VAR_SIZE_MAX(), INT_VAL_MIN()); break; case BRANCH_KNIGHT_MOVE: branch(*this, q, INT_VAR_MIN_MIN(), INT_VAL_MED()); break; } }
/// Actual model Partition(const SizeOptions& opt) : x(*this,opt.size(),1,2*opt.size()), y(*this,opt.size(),1,2*opt.size()) { const int n = opt.size(); // Break symmetries by ordering numbers in each group rel(*this, x, IRT_LE); rel(*this, y, IRT_LE); rel(*this, x[0], IRT_LE, y[0]); IntVarArgs xy(2*n); for (int i = n; i--; ) { xy[i] = x[i]; xy[n+i] = y[i]; } distinct(*this, xy, opt.icl()); IntArgs c(2*n); for (int i = n; i--; ) { c[i] = 1; c[n+i] = -1; } linear(*this, c, xy, IRT_EQ, 0); // Array of products IntVarArgs sxy(2*n), sx(n), sy(n); for (int i = n; i--; ) { sx[i] = sxy[i] = expr(*this, sqr(x[i])); sy[i] = sxy[n+i] = expr(*this, sqr(y[i])); } linear(*this, c, sxy, IRT_EQ, 0); // Redundant constraints linear(*this, x, IRT_EQ, 2*n*(2*n+1)/4); linear(*this, y, IRT_EQ, 2*n*(2*n+1)/4); linear(*this, sx, IRT_EQ, 2*n*(2*n+1)*(4*n+1)/12); linear(*this, sy, IRT_EQ, 2*n*(2*n+1)*(4*n+1)/12); branch(*this, xy, INT_VAR_AFC_SIZE_MAX(opt.decay()), INT_VAL_MIN()); }
/// The actual problem OpenShop(const SizeOptions& opt) : spec(examples[opt.size()]), b(*this, (spec.n+spec.m-2)*spec.n*spec.m/2, 0,1), makespan(*this, 0, Int::Limits::max), _start(*this, spec.m*spec.n, 0, Int::Limits::max) { Matrix<IntVarArray> start(_start, spec.m, spec.n); IntArgs _dur(spec.m*spec.n, spec.p); Matrix<IntArgs> dur(_dur, spec.m, spec.n); int minmakespan; int maxmakespan; crosh(dur, minmakespan, maxmakespan); rel(*this, makespan <= maxmakespan); rel(*this, makespan >= minmakespan); int k=0; for (int m=0; m<spec.m; m++) for (int j0=0; j0<spec.n-1; j0++) for (int j1=j0+1; j1<spec.n; j1++) { // The tasks on machine m of jobs j0 and j1 must be disjoint rel(*this, b[k] == (start(m,j0) + dur(m,j0) <= start(m,j1))); rel(*this, b[k++] == (start(m,j1) + dur(m,j1) > start(m,j0))); } for (int j=0; j<spec.n; j++) for (int m0=0; m0<spec.m-1; m0++) for (int m1=m0+1; m1<spec.m; m1++) { // The tasks in job j on machine m0 and m1 must be disjoint rel(*this, b[k] == (start(m0,j) + dur(m0,j) <= start(m1,j))); rel(*this, b[k++] == (start(m1,j) + dur(m1,j) > start(m0,j))); } // The makespan is greater than the end time of the latest job for (int m=0; m<spec.m; m++) { for (int j=0; j<spec.n; j++) { rel(*this, start(m,j) + dur(m,j) <= makespan); } } // First branch over the precedences branch(*this, b, INT_VAR_AFC_MAX, INT_VAL_MAX); // When the precedences are fixed, simply assign the start times assign(*this, _start, INT_ASSIGN_MIN); // When the start times are fixed, use the tightest makespan assign(*this, makespan, INT_ASSIGN_MIN); }
/// The actual problem Kakuro(const SizeOptions& opt) : w(examples[opt.size()][0]), h(examples[opt.size()][1]), f(*this,w*h) { IntVar black(*this,0,0); // Initialize all fields as black (unused). Only if a field // is actually used in a constraint, create a fresh variable // for it (done via init). for (int i=w*h; i--; ) f[i] = black; // Cache of already computed tuple sets Cache cache; // Matrix for accessing board fields Matrix<IntVarArray> b(f,w,h); // Access to hints const int* k = &examples[opt.size()][2]; // Process vertical hints while (*k >= 0) { int x=*k++; int y=*k++; int n=*k++; int s=*k++; IntVarArgs col(n); for (int i=n; i--; ) col[i]=init(b(x,y+i+1)); distinctlinear(cache,col,s,opt); } k++; // Process horizontal hints while (*k >= 0) { int x=*k++; int y=*k++; int n=*k++; int s=*k++; IntVarArgs row(n); for (int i=n; i--; ) row[i]=init(b(x+i+1,y)); distinctlinear(cache,row,s,opt); } branch(*this, f, INT_VAR_AFC_SIZE_MAX(opt.decay()), INT_VAL_SPLIT_MIN()); }
/// Actual model AllInterval(const SizeOptions& opt) : x(*this, opt.size(), 0, opt.size() - 1) { const int n = x.size(); IntVarArgs d(n-1); // Set up variables for distance for (int i=0; i<n-1; i++) d[i] = expr(*this, abs(x[i+1]-x[i]), opt.icl()); // Constrain them to be between 1 and n-1 dom(*this, d, 1, n-1); distinct(*this, x, opt.icl()); distinct(*this, d, opt.icl()); // Break mirror symmetry rel(*this, x[0], IRT_LE, x[1]); // Break symmetry of dual solution rel(*this, d[0], IRT_GR, d[n-2]); branch(*this, x, INT_VAR_SIZE_MIN, INT_VAL_SPLIT_MIN); }
/// Actual model Calculs(const SizeOptions& opt) : X(*this, N, -N, N), X_abs(*this, N, 0, N), x_max(*this, 0, N), show_all(opt.size()) { IntVar a(X[ 0]), b(X[ 1]), c(X[ 2]), e(X[ 4]), f(X[ 5]), g(X[ 6]), h(X[ 7]), i(X[ 8]), j(X[ 9]), k(X[10]), l(X[11]), m(X[12]), n(X[13]), o(X[14]), p(X[15]), q(X[16]), r(X[17]), s(X[18]), t(X[19]), u(X[20]), v(X[21]), w(X[22]), x(X[23]), y(X[24]), z(X[25]); // x_max is the max value of abs(X) for(int i = 0; i < N; i++) { abs(*this, X[i], X_abs[i], opt.icl()); } max(*this, X_abs, x_max, opt.icl()); rel(*this, z+e+r+o == 0, opt.icl()); rel(*this, o+n+e == 1, opt.icl()); rel(*this, t+w+o == 2, opt.icl()); rel(*this, t+h+r+e+e == 3, opt.icl()); rel(*this, f+o+u+r == 4, opt.icl()); rel(*this, f+i+v+e == 5, opt.icl()); rel(*this, s+i+x == 6, opt.icl()); rel(*this, s+e+v+e+n == 7, opt.icl()); rel(*this, e+i+g+h+t == 8, opt.icl()); rel(*this, n+i+n+e == 9, opt.icl()); rel(*this, t+e+n == 10, opt.icl()); rel(*this, e+l+e+v+e+n == 11, opt.icl()); rel(*this, t+w+e+l+f == 12, opt.icl()); distinct(*this, X, opt.icl()); // for showing all solutions (there are many) if (show_all) { rel(*this, x_max == 16, opt.icl()); } branch(*this, X, INT_VAR_DEGREE_MAX(), INT_VAL_MIN()); }
/// Actual model MineSweeper(const SizeOptions& opt) : spec(specs[opt.size()]), size(spec_size(spec)), b(*this,size*size,0,1) { Matrix<BoolVarArray> m(b, size, size); // Initialize matrix and post constraints for (int h=0; h<size; h++) for (int w=0; w<size; w++) { int v = mineField(spec, size, h, w); if (v != -1) { rel(*this, m(w, h), IRT_EQ, 0); linear(*this, fieldsAround(m, w, h), IRT_EQ, v); } } // Install branching branch(*this, b, INT_VAR_NONE(), INT_VAL_MAX()); }
LatinSquares(const SizeOptions& opt) : n(opt.size()), x(*this, n*n, 1, n) { // Matrix wrapper for the x grid Matrix<IntVarArray> m(x, n, n); latin_square(*this, m, opt.icl()); // Symmetry breaking. 0 is upper left column if (opt.symmetry() == SYMMETRY_MIN) { rel(*this, x[0] == 1, opt.icl()); } branch(*this, x, INT_VAR_SIZE_MIN(), INT_VAL_RANGE_MAX()); }
/// Actual model WordSquare(const SizeOptions& opt) : w_l(opt.size()), letters(*this, w_l*w_l) { // Initialize letters Matrix<IntVarArray> ml(letters, w_l, w_l); for (int i=0; i<w_l; i++) for (int j=i; j<w_l; j++) ml(i,j) = ml(j,i) = IntVar(*this, 'a','z'); // Number of words with that length const int n_w = dict.words(w_l); // Initialize word array IntVarArgs words(*this, w_l, 0, n_w-1); // All words must be different distinct(*this, words); // Link words with letters for (int i=0; i<w_l; i++) { // Map each word to i-th letter in that word IntSharedArray w2l(n_w); for (int n=n_w; n--; ) w2l[n]=dict.word(w_l,n)[i]; for (int j=0; j<w_l; j++) element(*this, w2l, words[j], ml(i,j)); } // Symmetry breaking: the last word must be later in the wordlist rel(*this, words[0], IRT_LE, words[w_l-1]); switch (opt.branching()) { case BRANCH_WORDS: // Branch by assigning words branch(*this, words, INT_VAR_SIZE_MIN(), INT_VAL_SPLIT_MIN()); break; case BRANCH_LETTERS: // Branch by assigning letters branch(*this, letters, INT_VAR_AFC_SIZE_MAX(opt.decay()), INT_VAL_MIN()); break; } }
/// The actual model GraphColor(const SizeOptions& opt) : g(opt.size() == 1 ? g2 : g1), v(*this,g.n_v,0,g.n_v), m(*this,0,g.n_v) { rel(*this, v, IRT_LQ, m); for (int i = 0; g.e[i] != -1; i += 2) rel(*this, v[g.e[i]], IRT_NQ, v[g.e[i+1]]); const int* c = g.c; for (int i = *c++; i--; c++) rel(*this, v[*c], IRT_EQ, i); while (*c != -1) { int n = *c; IntVarArgs x(n); c++; for (int i = n; i--; c++) x[i] = v[*c]; distinct(*this, x, opt.icl()); if (opt.model() == MODEL_CLIQUE) rel(*this, m, IRT_GQ, n-1); } branch(*this, m, INT_VAL_MIN); switch (opt.branching()) { case BRANCH_SIZE: branch(*this, v, INT_VAR_SIZE_MIN, INT_VAL_MIN); break; case BRANCH_DEGREE: branch(*this, v, tiebreak(INT_VAR_DEGREE_MAX,INT_VAR_SIZE_MIN), INT_VAL_MIN); break; case BRANCH_SIZE_DEGREE: branch(*this, v, INT_VAR_SIZE_DEGREE_MIN, INT_VAL_MIN); break; case BRANCH_SIZE_AFC: branch(*this, v, INT_VAR_SIZE_AFC_MIN, INT_VAL_MIN); break; default: break; } }
SetCovering(const SizeOptions& opt) : min_distance(opt.size()), x(*this, num_cities, 0, 1), z(*this, 0, num_cities) { // distance between the cities int distance[] = { 0,10,20,30,30,20, 10, 0,25,35,20,10, 20,25, 0,15,30,20, 30,35,15, 0,15,25, 30,20,30,15, 0,14, 20,10,20,25,14, 0 }; // z = sum of placed fire stations linear(*this, x, IRT_EQ, z, opt.icl()); // ensure that all cities are covered by at least one fire station for(int i = 0; i < num_cities; i++) { IntArgs in_distance(num_cities); // the cities within the distance for(int j = 0; j < num_cities; j++) { if (distance[i*num_cities+j] <= min_distance) { in_distance[j] = 1; } else { in_distance[j] = 0; } } linear(*this, in_distance, x, IRT_GQ, 1, opt.icl()); } branch(*this, x, INT_VAR_SIZE_MAX(), INT_VAL_SPLIT_MIN()); }
/// Post constraints MagicSquare(const SizeOptions& opt) : n(opt.size()), x(*this,n*n,1,n*n) { // Number of fields on square const int nn = n*n; // Sum of all a row, column, or diagonal const int s = nn*(nn+1) / (2*n); // Matrix-wrapper for the square Matrix<IntVarArray> m(x, n, n); for (int i = n; i--; ) { linear(*this, m.row(i), IRT_EQ, s, opt.icl()); linear(*this, m.col(i), IRT_EQ, s, opt.icl()); } // Both diagonals must have sum s { IntVarArgs d1y(n); IntVarArgs d2y(n); for (int i = n; i--; ) { d1y[i] = m(i,i); d2y[i] = m(n-i-1,i); } linear(*this, d1y, IRT_EQ, s, opt.icl()); linear(*this, d2y, IRT_EQ, s, opt.icl()); } // All fields must be distinct distinct(*this, x, opt.icl()); // Break some (few) symmetries rel(*this, m(0,0), IRT_GR, m(0,n-1)); rel(*this, m(0,0), IRT_GR, m(n-1,0)); branch(*this, x, INT_VAR_SIZE_MIN, INT_VAL_SPLIT_MIN); }
/// The actual problem DominatingQueens(const SizeOptions& opt) : IntMinimizeScript(opt), n(opt.size()), b(*this,n*n,0,n*n-1), q(*this,1,n) { // Constrain field to the fields that can attack a field for (int i=0; i<n*n; i++) dom(*this, b[i], attacked(i)); // At most q queens can be placed nvalues(*this, b, IRT_LQ, q); /* * According to: P. R. J. Östergard and W. D. Weakley, Values * of Domination Numbers of the Queen's Graph, Electronic Journal * of Combinatorics, 8:1-19, 2001, for n <= 120, the minimal number * of queens is either ceil(n/2) or ceil(n/2 + 1). */ if (n <= 120) dom(*this, q, (n+1)/2, (n+1)/2 + 1); branch(*this, b, INT_VAR_SIZE_MIN(), INT_VAL_MIN()); // Try the easier solution first branch(*this, q, INT_VAL_MAX()); }
/// Actual model Steiner(const SizeOptions& opt) : n(opt.size()), noOfTriples((n*(n-1))/6), triples(*this, noOfTriples, IntSet::empty, 1, n, 3, 3) { for (int i=0; i<noOfTriples; i++) { for (int j=i+1; j<noOfTriples; j++) { SetVar x = triples[i]; SetVar y = triples[j]; SetVar atmostOne(*this,IntSet::empty,1,n,0,1); rel(*this, (x & y) == atmostOne); IntVar x1(*this,1,n); IntVar x2(*this,1,n); IntVar x3(*this,1,n); IntVar y1(*this,1,n); IntVar y2(*this,1,n); IntVar y3(*this,1,n); if (opt.model() == MODEL_NONE) { /* Naive alternative: * just including the ints in the set */ rel(*this, singleton(x1) <= x); rel(*this, singleton(x2) <= x); rel(*this, singleton(x3) <= x); rel(*this, singleton(y1) <= y); rel(*this, singleton(y2) <= y); rel(*this, singleton(y3) <= y); } else if (opt.model() == MODEL_MATCHING) { /* Smart alternative: * Using matching constraints */ channelSorted(*this, IntVarArgs()<<x1<<x2<<x3, x); channelSorted(*this, IntVarArgs()<<y1<<y2<<y3, y); } else if (opt.model() == MODEL_SEQ) { SetVar sx1 = expr(*this, singleton(x1)); SetVar sx2 = expr(*this, singleton(x2)); SetVar sx3 = expr(*this, singleton(x3)); SetVar sy1 = expr(*this, singleton(y1)); SetVar sy2 = expr(*this, singleton(y2)); SetVar sy3 = expr(*this, singleton(y3)); sequence(*this,SetVarArgs()<<sx1<<sx2<<sx3,x); sequence(*this,SetVarArgs()<<sy1<<sy2<<sy3,y); } /* Breaking symmetries */ rel(*this, x1 < x2); rel(*this, x2 < x3); rel(*this, x1 < x3); rel(*this, y1 < y2); rel(*this, y2 < y3); rel(*this, y1 < y3); linear(*this, IntArgs(6,(n+1)*(n+1),n+1,1,-(n+1)*(n+1),-(n+1),-1), IntVarArgs()<<x1<<x2<<x3<<y1<<y2<<y3, IRT_LE, 0); } } branch(*this, triples, SET_VAR_NONE, SET_VAL_MIN_INC); }
SUSHI_EQUATION(const SizeOptions& opt) : n_x(*this, 1, wordlength), n_y(*this, 1, wordlength), n_z(*this, 1, wordlength), n_t(*this, opt.size()+1, opt.size()+1), X(*this, wordlength , val_dom_min, val_dom_max), Y(*this, wordlength , val_dom_min, val_dom_max), Z(*this, wordlength , val_dom_min, val_dom_max), T(*this, opt.size()+1 , val_dom_min, val_dom_max){ int n = opt.size(); gecode_find_solution = false; REG r_a(1); REG r_b(2); REG dum(dummy_sym); REG reg_z = r_b(n,n) + r_a + r_b(n,n); if (opt.propagation() == PROP_PAD) { reg_z += (*dum); } DFA myDFA_z(reg_z); // T = "a" . Y[0,n] rel(*this, T[0]==1); for (int i = 0; (i < n) && (i+1 < T.size()); i++){ T[i+1] = Y[i]; } rel(*this, n_y >= n); switch(opt.propagation()){ case PROP_OPEN: extensional(*this, Z, myDFA_z, n_z); //concat(*this, X, n_x, T, n_t, Z, n_z, ICL_BND); rel(*this, n_z == (n_x+n_t)); for(int i=0; i<wordlength; i++) { rel(*this, (i<n_x) >> (X[i]==Z[i])); BoolVar b = expr(*this, i==n_x); for(int j=0; (i+j)<wordlength; j++) { rel(*this, (b&&(j<n_t)) >> (T[j]==Z[i+j])); } } break; case PROP_CLOSED: extensional(*this, Z, myDFA_z); rel(*this, n_z == (n_x+n_t)); rel(*this, n_y == wordlength); for(int i=0; i<wordlength; i++) { rel(*this, (i<n_x) >> (X[i]==Z[i])); BoolVar b = expr(*this, i==n_x); for(int j=0; (j < T.size()) && ((i+j)<wordlength); j++) { rel(*this, (b&&(j<n_t)) >> (T[j]==Z[i+j])); } } break; case PROP_PAD: extensional(*this, Z, myDFA_z); for(int i=0; i<wordlength; i++) { rel(*this, (Z[i]==dummy_sym) == (n_z<=i)); rel(*this, (Y[i]==dummy_sym) == (n_y<=i)); rel(*this, (X[i]==dummy_sym) == (n_x<=i)); } rel(*this, n_z == (n_x+n_t)); for(int i=0; i<wordlength; i++) { rel(*this, (i<n_x) >> (X[i]==Z[i])); BoolVar b = expr(*this, i==n_x); for(int j=0; (j < T.size()) && ((i+j)<wordlength); j++) { rel(*this, (b&&(j<n_t)) >> (T[j]==Z[i+j])); } } break; } IntVarArgs lengths; lengths << n_z << n_x << n_y; switch(opt.branching()){ case BRANCH_N_A: branch(*this, lengths, INT_VAR_SIZE_MIN(), INT_VAL_MIN()); branch(*this, Z, INT_VAR_SIZE_MIN(), INT_VAL_MIN()); branch(*this, X, INT_VAR_SIZE_MIN(), INT_VAL_MIN()); branch(*this, Y, INT_VAR_SIZE_MIN(), INT_VAL_MIN()); break; case BRANCH_A_N: branch(*this, Z, INT_VAR_SIZE_MIN(), INT_VAL_MIN()); branch(*this, X, INT_VAR_SIZE_MIN(), INT_VAL_MIN()); branch(*this, Y, INT_VAR_SIZE_MIN(), INT_VAL_MIN()); branch(*this, lengths, INT_VAR_SIZE_MIN(), INT_VAL_MIN()); break; case BRANCH_BOUND: boundednone(*this, Z, n_z); boundednone(*this, X, n_x); boundednone(*this, Y, n_y); break; } }
Queens(const SizeOptions& opt) : q(*this, opt.size() * opt.size(), 0, 1) { Matrix<IntVarArray> matrix(q, opt.size(), opt.size()); // Distinct col for (int i = 0; i < opt.size(); ++i) { count(*this, matrix.col(i), 1, IRT_EQ, 1); count(*this, matrix.row(i), 1, IRT_EQ, 1); } // Diagonal for (int j = 0; j < opt.size(); ++j) { IntVarArray dia(*this, opt.size()-j); for (int i = 0; i+j < opt.size(); ++i) { dia[i] = matrix(i+j, i); } count(*this, dia, 1, IRT_LQ, 1); } for (int j = 0; j < opt.size(); ++j) { IntVarArray dia(*this, opt.size()-j); for (int i = 0; i+j < opt.size(); ++i) { dia[i] = matrix(i, i+j); } count(*this, dia, 1, IRT_LQ, 1); } for (int j = 0; j < opt.size(); ++j) { IntVarArray dia(*this, opt.size()-j); for (int i = 0; i+j < opt.size(); ++i) { dia[i] = matrix((opt.size() - 1) - (i + j), i); } count(*this, dia, 1, IRT_LQ, 1); } for (int j = 0; j < opt.size(); ++j) { IntVarArray dia(*this, opt.size()-j); for (int i = 0; i+j < opt.size(); ++i) { dia[i] = matrix((opt.size() - 1) - (i + 0), i + j); } count(*this, dia, 1, IRT_LQ, 1); } branch(*this, q, INT_VAR_RND, INT_VAL_MAX); }
/// Construction of the model. Pentominoes(const SizeOptions& opt) : spec(examples[opt.size()]), width(spec[0].width+1), // Add one for extra row at end. height(spec[0].height), filled(spec[0].amount), nspecs(examples_size[opt.size()]-1), ntiles(compute_number_of_tiles(spec+1, nspecs)), board(*this, width*height, filled,ntiles+1) { spec += 1; // No need for the specification-part any longer // Set end-of-line markers for (int h = 0; h < height; ++h) { for (int w = 0; w < width-1; ++w) rel(*this, board[h*width + w], IRT_NQ, ntiles+1); rel(*this, board[h*width + width - 1], IRT_EQ, ntiles+1); } // Post constraints if (opt.propagation() == PROPAGATION_INT) { int tile = 0; for (int i = 0; i < nspecs; ++i) { for (int j = 0; j < spec[i].amount; ++j) { // Color int col = tile+1; // Expression for color col REG mark(col); // Build expression for complement to color col REG other; bool first = true; for (int j = filled; j <= ntiles; ++j) { if (j == col) continue; if (first) { other = REG(j); first = false; } else { other |= REG(j); } } // End of line marker REG eol(ntiles+1); extensional(*this, board, get_constraint(i, mark, other, eol)); ++tile; } } } else { // opt.propagation() == PROPAGATION_BOOLEAN int ncolors = ntiles + 2; // Boolean variables for channeling BoolVarArgs p(*this,ncolors * board.size(),0,1); // Post channel constraints for (int i=board.size(); i--; ) { BoolVarArgs c(ncolors); for (int j=ncolors; j--; ) c[j]=p[i*ncolors+j]; channel(*this, c, board[i]); } // For placing tile i, we construct the expression over // 0/1-variables and apply it to the projection of // the board on the color for the tile. REG other(0), mark(1); int tile = 0; for (int i = 0; i < nspecs; ++i) { for (int j = 0; j < spec[i].amount; ++j) { int col = tile+1; // Projection for color col BoolVarArgs c(board.size()); for (int k = board.size(); k--; ) { c[k] = p[k*ncolors+col]; } extensional(*this, c, get_constraint(i, mark, other, other)); ++tile; } } } if (opt.symmetry() == SYMMETRY_FULL) { // Remove symmetrical boards IntVarArgs orig(board.size()-height), symm(board.size()-height); int pos = 0; for (int i = 0; i < board.size(); ++i) { if ((i+1)%width==0) continue; orig[pos++] = board[i]; } int w2, h2; bsymmfunc syms[] = {flipx, flipy, flipd1, flipd2, rot90, rot180, rot270}; int symscnt = sizeof(syms)/sizeof(bsymmfunc); for (int i = 0; i < symscnt; ++i) { syms[i](orig, width-1, height, symm, w2, h2); if (width-1 == w2 && height == h2) rel(*this, orig, IRT_LQ, symm); } } // Install branching branch(*this, board, INT_VAR_NONE, INT_VAL_MIN); }
/// Actual model AllInterval(const SizeOptions& opt) : Script(opt), x(*this, opt.size(), 0, 66) { // 66 or opt.size() - 1 const int n = x.size(); IntVarArgs d(n-1); IntVarArgs dd(66); IntVarArgs xx_(n); // pitch class for AllInterval Chords IntVar douze; Rnd r(1U); if ((opt.model() == MODEL_SET) || (opt.model() == MODEL_SET_CHORD) || (opt.model() == MODEL_SSET_CHORD) ||(opt.model() == MODEL_SYMMETRIC_SET)) // Modele original : serie { // Set up variables for distance for (int i=0; i<n-1; i++) d[i] = expr(*this, abs(x[i+1]-x[i]), opt.ipl()); // Constrain them to be between 1 and n-1 dom(*this, d, 1, n-1); dom(*this, x, 0, n-1); if((opt.model() == MODEL_SET_CHORD) || (opt.model() == MODEL_SSET_CHORD)) { /*expr(*this,dd[0]==0); // Set up variables for distance for (int i=0; i<n-1; i++) { expr(*this, dd[i+1] == (dd[i]+d[i])%12, opt.icl()); } // Constrain them to be between 1 and n-1 dom(*this, dd,0, n-1); distinct(*this, dd, opt.icl()); */ rel(*this, abs(x[0]-x[n-1]) == 6, opt.ipl()); } if(opt.symmetry()) { // Break mirror symmetry (renversement) rel(*this, x[0], IRT_LE, x[1]); // Break symmetry of dual solution (retrograde de la serie) -> 1928 solutions pour accords de 12 sons rel(*this, d[0], IRT_GR, d[n-2]); } //series symetriques if ((opt.model() == MODEL_SYMMETRIC_SET)|| (opt.model() == MODEL_SSET_CHORD)) { rel (*this, d[n/2 - 1] == 6); // pivot = triton for (int i=0; i<(n/2)-2; i++) rel(*this,d[i]+d[n-i-2]==12); } } else { for (int j=0; j<n; j++) xx_[j] = expr(*this, x[j] % 12); dom(*this, xx_, 0, 11); distinct(*this, xx_, opt.ipl()); //intervalles for (int i=0; i<n-1; i++) d[i] = expr(*this,x[i+1] - x[i],opt.ipl()); dom(*this, d, 1, n-1); dom(*this, x, 0, n * (n - 1) / 2.); //d'autres choses dont on est certain (contraintes redondantes) : rel(*this, x[0] == 0); rel(*this, x[n-1] == n * (n - 1) / 2.); // break symmetry of dual solution (renversement de l'accord) if(opt.symmetry()) rel(*this, d[0], IRT_GR, d[n-2]); //accords symetriques if (opt.model() == MODEL_SYMMETRIC_CHORD) { rel (*this, d[n/2 - 1] == 6); // pivot = triton for (int i=0; i<(n/2)-2; i++) rel(*this,d[i]+d[n-i-2]==12); } if (opt.model() == MODEL_PARALLEL_CHORD) { rel (*this, d[n/2 - 1] == 6); // pivot = triton for (int i=0; i<(n/2)-2; i++) rel(*this,d[i]+d[n/2 + i]==12); } } distinct(*this, x, opt.ipl()); distinct(*this, d, opt.ipl()); #if 0 //TEST IntVarArray counter(*this,12,0,250); IntVar testcounter(*this,0,250); for (int i=1; i<11; i++) { count(*this, d, i,IRT_EQ,testcounter,opt.icl()); // OK } count(*this, d, counter,opt.icl()); // OK #endif //END TEST if(opt.branching() == 0) branch(*this, x, INT_VAR_SIZE_MIN(), INT_VAL_SPLIT_MIN()); else branch(*this, x, INT_VAR_RND(r), INT_VAL_RND(r)); }
/// The actual model GraphColor(const SizeOptions& opt) : IntMinimizeScript(opt), g(opt.size() == 1 ? g2 : g1), v(*this,g.n_v,0,g.n_v-1), m(*this,0,g.n_v-1) { rel(*this, v, IRT_LQ, m); for (int i = 0; g.e[i] != -1; i += 2) rel(*this, v[g.e[i]], IRT_NQ, v[g.e[i+1]]); const int* c = g.c; for (int i = *c++; i--; c++) rel(*this, v[*c], IRT_EQ, i); while (*c != -1) { int n = *c; IntVarArgs x(n); c++; for (int i = n; i--; c++) x[i] = v[*c]; distinct(*this, x, opt.icl()); if (opt.model() == MODEL_CLIQUE) rel(*this, m, IRT_GQ, n-1); } /// Branching on the number of colors branch(*this, m, INT_VAL_MIN()); if (opt.symmetry() == SYMMETRY_NONE) { /// Branching without symmetry breaking switch (opt.branching()) { case BRANCH_SIZE: branch(*this, v, INT_VAR_SIZE_MIN(), INT_VAL_MIN()); break; case BRANCH_DEGREE: branch(*this, v, tiebreak(INT_VAR_DEGREE_MAX(),INT_VAR_SIZE_MIN()), INT_VAL_MIN()); break; case BRANCH_SIZE_DEGREE: branch(*this, v, INT_VAR_DEGREE_SIZE_MAX(), INT_VAL_MIN()); break; case BRANCH_SIZE_AFC: branch(*this, v, INT_VAR_AFC_SIZE_MAX(opt.decay()), INT_VAL_MIN()); break; case BRANCH_SIZE_ACTIVITY: branch(*this, v, INT_VAR_ACTIVITY_SIZE_MAX(opt.decay()), INT_VAL_MIN()); break; default: break; } } else { // opt.symmetry() == SYMMETRY_LDSB /// Branching while considering value symmetry breaking /// (every permutation of color values gives equivalent solutions) Symmetries syms; syms << ValueSymmetry(IntArgs::create(g.n_v,0)); switch (opt.branching()) { case BRANCH_SIZE: branch(*this, v, INT_VAR_SIZE_MIN(), INT_VAL_MIN(), syms); break; case BRANCH_DEGREE: branch(*this, v, tiebreak(INT_VAR_DEGREE_MAX(),INT_VAR_SIZE_MIN()), INT_VAL_MIN(), syms); break; case BRANCH_SIZE_DEGREE: branch(*this, v, INT_VAR_DEGREE_SIZE_MAX(), INT_VAL_MIN(), syms); break; case BRANCH_SIZE_AFC: branch(*this, v, INT_VAR_AFC_SIZE_MAX(opt.decay()), INT_VAL_MIN(), syms); break; case BRANCH_SIZE_ACTIVITY: branch(*this, v, INT_VAR_ACTIVITY_SIZE_MAX(opt.decay()), INT_VAL_MIN(), syms); break; default: break; } } }
SquarePacking(const SizeOptions& opt) : x(*this, opt.size()), y(*this, opt.size()) { // Step 1b: Initialize variables s, x, y int min = 0; int max = 0; for (int i = 0; i < opt.size(); ++i) { min += i*i; max += i; } s = IntVar(*this, sqrt(min), max); for (int i = 0; i < opt.size(); ++i) { x[i] = IntVar(*this, 0, max); y[i] = IntVar(*this, 0, max); } for (int i = 0; i < N; ++i) { rel(*this, (x[i] + size(i)) <= s); rel(*this, (y[i] + size(i)) <= s); } // Step 2: No overlapping between squares, expressed with // reification. for (int i = 0; i < N-1; i++) { for (int j = i+1; j < N-1; j++) { rel(*this, (x[i] + size(i) <= x[j]) || (x[j] + size(j) <= x[i]) || (y[i] + size(i) <= y[j]) || (y[j] + size(j) <= y[i])); } } // Step 3: Sum of square sizes for every row and column less than // s, expressed with reification. IntArgs sizes(N-1); for (int i = 0; i < N-1; ++i) { sizes[i] = size(i); } for (int col = 0; col < s.max(); ++col) { BoolVarArgs bx(*this, N-1, 0, 1); for (int i = 0; i < N-1; ++i) { dom(*this, y[i], col - size(i) + 1, col, bx[i]); } linear(*this, sizes, bx, IRT_LQ, s); } for (int col = 0; col < s.max(); ++col) { BoolVarArgs bx(*this, N-1, 0, 1); for (int i = 0; i < N-1; ++i) { dom(*this, x[i], col - size(i) + 1, col, bx[i]); } linear(*this, sizes, bx, IRT_LQ, s); } // Step 4.1: Problem decomposition rel(*this, (s*s) > ((N * (N+1) * (2 * N+1))/6)); // Step 4.2: Symmetry removal rel(*this, x[0] <= ((s - N)/2)); rel(*this, y[0] <= ((s - N)/2)); // Step 4.3: Empty strip dominance for (int i = 0; i < N; ++i) { int siz = size(i); int gt = 0; if (siz == 2 || siz == 4) gt = 2; else if (siz == 3 || (siz >= 5 && siz <= 8)) gt = 3; else if (siz <= 11) gt = 4; else if (siz <= 17) gt = 5; else if (siz <= 21) gt = 6; else if (siz <= 29) gt = 7; else if (siz <= 34) gt = 8; else if (siz <= 44) gt = 9; else if (siz <= 45) gt = 10; if (gt != 0) { rel(*this, x[i] != gt); rel(*this, y[i] != gt); } } // Step 4.4: Ignoring size 1 squares, this is done by only looping // until N-1 instead of N // Step 5: Branching heuristics branch(*this, s, INT_VAL_MIN); // (5a) Branch on s first // Interval branch IntArgs nsizes(N); for (int i = 0; i < N; ++i) { nsizes[i] = size(i); } interval(*this, x, nsizes, 0.6); interval(*this, y, nsizes, 0.6); // (5c) INT_VAR_NONE means going through the array in order, that // is from the biggest to the smallest square // (5d) INT_VAL_MIN chooses the minimum value, that is placing it // from left to right and top to botom. // (5b) First assign x-coordinates. branch(*this, x, INT_VAR_NONE, INT_VAL_MIN); // (5b) then y-coordinates. branch(*this, y, INT_VAR_NONE, INT_VAL_MIN); }
/// Actual model PerfectSquare(const SizeOptions& opt) : x(*this,specs[opt.size()][0],0,specs[opt.size()][1]-1), y(*this,specs[opt.size()][0],0,specs[opt.size()][1]-1) { const int* s = specs[opt.size()]; int n = *s++; int w = *s++; // Restrict position according to square size for (int i=n; i--; ) { rel(*this, x[i], IRT_LQ, w-s[i]); rel(*this, y[i], IRT_LQ, w-s[i]); } IntArgs sa(n,s); // Squares do not overlap nooverlap(*this, x, sa, y, sa); /* * Capacity constraints * */ switch (opt.propagation()) { case PROP_REIFIED: { for (int cx=0; cx<w; cx++) { BoolVarArgs bx(*this,n,0,1); for (int i=0; i<n; i++) dom(*this, x[i], cx-s[i]+1, cx, bx[i]); linear(*this, sa, bx, IRT_EQ, w); } for (int cy=0; cy<w; cy++) { BoolVarArgs by(*this,n,0,1); for (int i=0; i<n; i++) dom(*this, y[i], cy-s[i]+1, cy, by[i]); linear(*this, sa, by, IRT_EQ, w); } } break; case PROP_CUMULATIVES: { IntArgs m(n), dh(n); for (int i = n; i--; ) { m[i]=0; dh[i]=s[i]; } IntArgs limit(1, w); { // x-direction IntVarArgs e(n); for (int i=n; i--;) e[i]=expr(*this, x[i]+dh[i]); cumulatives(*this, m, x, dh, e, dh, limit, true); cumulatives(*this, m, x, dh, e, dh, limit, false); } { // y-direction IntVarArgs e(n); for (int i=n; i--;) e[i]=expr(*this, y[i]+dh[i]); cumulatives(*this, m, y, dh, e, dh, limit, true); cumulatives(*this, m, y, dh, e, dh, limit, false); } } break; default: GECODE_NEVER; } branch(*this, x, INT_VAR_MIN_MIN(), INT_VAL_MIN()); branch(*this, y, INT_VAR_MIN_MIN(), INT_VAL_MIN()); }
/// Actual model Domino(const SizeOptions& opt) : spec(specs[opt.size()]), width(spec[0]), height(spec[1]), x(*this, (width+1)*height, 0, 28) { spec+=2; // skip board size information // Copy spec information to the board IntArgs board((width+1)*height); for (int i=0; i<width; i++) for (int j=0; j<height; j++) board[j*(width+1)+i] = spec[j*width+i]; // Initialize the separator column in the board for (int i=0; i<height; i++) { board[i*(width+1)+8] = -1; rel(*this, x[i*(width+1)+8]==28); } // Variables representing the coordinates of the first // and second half of a domino piece IntVarArgs p1(*this, 28, 0, (width+1)*height-1); IntVarArgs p2(*this, 28, 0, (width+1)*height-1); if (opt.propagation() == PROP_ELEMENT) { int dominoCount = 0; int possibleDiffsA[] = {1, width+1}; IntSet possibleDiffs(possibleDiffsA, 2); for (int i=0; i<=6; i++) for (int j=i; j<=6; j++) { // The two coordinates must be adjacent. // I.e., they may differ by 1 or by the width. // The separator column makes sure that a field // at the right border is not adjacent to the first field // in the next row. IntVar diff(*this, possibleDiffs); abs(*this, expr(*this, p1[dominoCount]-p2[dominoCount]), diff, ICL_DOM); // If the piece is symmetrical, order the locations if (i == j) rel(*this, p1[dominoCount], IRT_LE, p2[dominoCount]); // Link the current piece to the board element(*this, board, p1[dominoCount], i); element(*this, board, p2[dominoCount], j); // Link the current piece to the array where its // number is stored. element(*this, x, p1[dominoCount], dominoCount); element(*this, x, p2[dominoCount], dominoCount); dominoCount++; } } else { int dominoCount = 0; for (int i=0; i<=6; i++) for (int j=i; j<=6; j++) { // Find valid placements for piece i-j // Extensional is used as a table-constraint listing all valid // tuples. // Note that when i == j, only one of the orientations are used. REG valids; for (int pos = 0; pos < (width+1)*height; ++pos) { if ((pos+1) % (width+1) != 0) { // not end-col if (board[pos] == i && board[pos+1] == j) valids |= REG(pos) + REG(pos+1); if (board[pos] == j && board[pos+1] == i && i != j) valids |= REG(pos+1) + REG(pos); } if (pos/(width+1) < height-1) { // not end-row if (board[pos] == i && board[pos+width+1] == j) valids |= REG(pos) + REG(pos+width+1); if (board[pos] == j && board[pos+width+1] == i && i != j) valids |= REG(pos+width+1) + REG(pos); } } IntVarArgs piece(2); piece[0] = p1[dominoCount]; piece[1] = p2[dominoCount]; extensional(*this, piece, valids); // Link the current piece to the array where its // number is stored. element(*this, x, p1[dominoCount], dominoCount); element(*this, x, p2[dominoCount], dominoCount); dominoCount++; } } // Branch by piece IntVarArgs ps(28*2); for (int i=0; i<28; i++) { ps[2*i] = p1[i]; ps[2*i+1] = p2[i]; } branch(*this, ps, INT_VAR_NONE, INT_VAL_MIN); }