Square(const SizeOptions& opt) : Script(opt), n(opt.size()), x(*this, opt.size(), 0, maxSourroundingSquareSize()), y(*this, opt.size(), 0, maxSourroundingSquareSize()), s(*this, maxSourroundingSquareSize(), sumN()) { // Every square must fit the surrounding square. for(int i = 0; i < n - 1; i++) { rel(*this, x[i] + size(i) <= s); rel(*this, y[i] + size(i) <= s); } // Symmetry removal. rel(*this, x[0], IRT_GQ, 0); rel(*this, x[0], IRT_LE, floor(((s.max() - n) / 2))); rel(*this, y[0], IRT_LQ, x[0]); // Empty strip dominance. if(n == 2) { rel(*this, x[0], IRT_LE, 2); rel(*this, y[0], IRT_LE, 2); } else if (n == 3) { rel(*this, x[0], IRT_LE, 3); rel(*this, y[0], IRT_LE, 3); } else if (n == 4) { rel(*this, x[0], IRT_LE, 2); rel(*this, y[0], IRT_LE, 2); } else if(n >= 5 && n <= 8) { rel(*this, x[0], IRT_LE, 3); rel(*this, y[0], IRT_LE, 3); } else if (n >= 9 && n <= 11) { rel(*this, x[0], IRT_LE, 4); rel(*this, y[0], IRT_LE, 4); } else if(n >= 12 && n <= 17) { rel(*this, x[0], IRT_LE, 5); rel(*this, y[0], IRT_LE, 5); } else if (n >= 18 && n <= 21) { rel(*this, x[0], IRT_LE, 6); rel(*this, y[0], IRT_LE, 6); } else if (n >= 22 && n <= 29) { rel(*this, x[0], IRT_LE, 7); rel(*this, y[0], IRT_LE, 7); } else if (n >= 30 && n <= 34) { rel(*this, x[0], IRT_LE, 8); rel(*this, y[0], IRT_LE, 8); } else if (n >= 35 && n <= 44) { rel(*this, x[0], IRT_LE, 9); rel(*this, y[0], IRT_LE, 9); } else if (n == 45) { rel(*this, x[0], IRT_LE, 10); rel(*this, y[0], IRT_LE, 10); } // The last element (1x1) is not considered. for(int i = 0; i < n - 1; i++) { for(int j = i + 1; j < n; j++) { BoolVarArgs b(*this, 4, 0, 1); // Left constraint. rel(*this, LinIntExpr(x[i] + size(i)).post(*this, ICL_VAL), IRT_LQ, LinIntExpr(x[j]).post(*this, ICL_VAL), b[0]); // Right constraint. rel(*this, LinIntExpr(x[i]).post(*this, ICL_VAL), IRT_GQ, LinIntExpr(x[j] + size(j)).post(*this, ICL_VAL), b[1]); // Below constraint. rel(*this, LinIntExpr(y[i] + size(i)).post(*this, ICL_VAL), IRT_LQ, LinIntExpr(y[j]).post(*this, ICL_VAL), b[2]); // Above constraint. rel(*this, LinIntExpr(y[i]).post(*this, ICL_VAL), IRT_GQ, LinIntExpr(y[j] + size(j)).post(*this, ICL_VAL), b[3]); // At least one of the constraints expressed above must hold. linear(*this, b, IRT_GQ, 1); } } // The commented snippet below is the code used to test the implemented propagator no-overlap. // IntArgs w(n); // IntArgs h(n); // IntVarArgs x1(x); // IntVarArgs y1(y); // for(int i = 0; i < n; i++) { // w[i] = size(i); // h[i] = size(i); // } // nooverlap(*this, x1, w, y1, h); // Branching first on s. branch(*this, s, INT_VAL_MIN()); branch(*this, x, INT_VAR_SIZE_MIN(), INT_VAL_MIN()); branch(*this, y, INT_VAR_SIZE_MIN(), INT_VAL_MIN()); }
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); }
// n, nr of boxes, variables are from 0 to n. Should deduct index of array here. Square(const SizeOptions& opt) : Script(opt), xCoords(*this, opt.size()), yCoords(*this, opt.size()) { // Size must be at least n + (n - 1). Otherwise we cant fit both the largest and second largest. int min = n + (n - 1); // Max size of square is when all squares are on a single line. int max = 0; for (int i = 0; i < n; i++) { max += i; } // Initialize size with min and max values. size = IntVar(*this, min, max); // Initialize coordinate variables, values from 0 to max size. for (int i = 0; i < n; i++) { xCoords[i] = IntVar(*this, 0, max); yCoords[i] = IntVar(*this, 0, max); } // Constraints so squares wont escape its containing square. for (int i = 0; i < n; i++) { rel(*this, xCoords[i] + sizeOfSquare(i) <= size); rel(*this, yCoords[i] + sizeOfSquare(i) <= size); } // Constraints for non overlapping for (int i = 0; i < (n - 1); i++) { for (int j = i + 1; j < (n - 1); j++) { //Avoid some redundant constraints by initiating j = i + 1. Also never compare the square to itself. rel(*this, xCoords[i] + sizeOfSquare(i) <= xCoords[j] || xCoords[j] + sizeOfSquare(j) <= xCoords[i] || yCoords[i] + sizeOfSquare(i) <= yCoords[j] || yCoords[j] + sizeOfSquare(j) <= yCoords[i]); } } // Constraints for column/row consistency IntArgs squareSizes(n - 1); for (int i = 0; i < (n - 1); i++) { squareSizes[i] = sizeOfSquare(i); } // Loop through all columns for (int col = 0; col < size.max(); col++) { BoolVarArgs isCoveringCol(*this, (n - 1), 0, 1); // Loop through all squares for (int i = 0; i < (n - 1); i++) { // Coord of square inbetween column index - size + 1 and column index. dom(*this, xCoords[i], col - sizeOfSquare(i) + 1, col, isCoveringCol[i]); } //Get the size of squares covering column and add them. Must be less or equal to size of containing square. linear(*this, squareSizes, isCoveringCol, IRT_LQ, size); } // Loop through all rows for (int row = 0; row < size.max(); row++) { BoolVarArgs isCoveringRow(*this, (n - 1), 0, 1); // Loop through all squares for (int i = 0; i < (n - 1); i++) { dom(*this, yCoords[i], row - sizeOfSquare(i) + 1, row, isCoveringRow[i]); } linear(*this, squareSizes, isCoveringRow, IRT_LQ, size); } // Additional constraints // Problem decomposition - Size of container must be at least as large as the sum of all squares. rel(*this, (size * size) >= (n * (n + 1) * (2 * n + 1)) / 6); // Symmetry removal - Restrict coordinate of largest square to the first corner. rel(*this, xCoords[0] <= ((size - n) / 2)); rel(*this, yCoords[0] <= ((size - n) / 2)); rel(*this, yCoords[0] <= xCoords[0]); // Empty strip dominance, only disallowing placements away from top and left border. for (int i = 0; i < n; i++) { int disallowedPlacement = getDisallowedPlacement(sizeOfSquare(i)); if (disallowedPlacement > 0) { rel(*this, xCoords[i], IRT_NQ, disallowedPlacement); rel(*this, yCoords[i], IRT_NQ, disallowedPlacement); } } // TODO: Forbidden gaps between squares // Ignoring size 1 squares // Not adding constaints for non overlapping or row/column consistency with the size 1 square. Using (n - 1) instead of n. // Branching, branch on size first. branch(*this, size, INT_VAL_MIN()); //First assign x coordinates, then y coordinates. branch(*this, xCoords, INT_VAR_NONE(), INT_VAL_MIN()); //INT_VAR_NONE = Choose first unassigned value, from smaller to larger squares. //INT_VAL_MIN = Try to place from left to right, and top to bottom. branch(*this, yCoords, INT_VAR_NONE(), INT_VAL_MIN()); }