NEOS - Discrete and Integer Optimization
https://neos-guide.org/taxonomy/term/11
enRubik's Cube Solver
https://neos-guide.org/content/rubikscube
<div class="field field-name-body field-type-text-with-summary field-label-hidden view-mode-rss"><div class="field-items"><div class="field-item even" property="content:encoded"><style>
<!--/*--><![CDATA[/* ><!--*/
h3 { text-decoration: underline; font-size: 1.5em; text-transform: uppercase; }
.indent { padding: 10px; }
.center-block { margin: auto; width: 80%; }
pre { padding: 2em; background-color: #eee; }
.rspace{ padding-right: 2em; }
/*--><!]]>*/
</style><script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script><script src="https://neos-server.org/neos/js/neos.js"></script><script type="text/javascript">
<!--//--><![CDATA[// ><!--
var gams_model;
$(function () {
$.get("/sites/default/files/guide_rubiks.gms", function (data) {
gams_model = data;
});
});
function resetInput() {
document.getElementById('email_ad').value = '';
document.getElementById('rubiks_cube').value = '';
document.getElementById('job_num').value = '';
document.getElementById('job_pass').value = '';
}
function createJobXML(category, solver) {
var email_address = document.getElementById('email_ad').value;
var xmlHead = "<document>\n<category>" + category +
"<\/category>\n<solver>" + solver + "<\/solver><inputMethod>GAMS<\/inputMethod>\n" +
"<email>" + email_address + "<\/email>\n<model><![CDATA[";
var xmlTail = "\]\]><\/model>\n<options>feasopt 1<\/options>\n<\/document>";
var jobXML = xmlHead + document.getElementById('rubiks_cube').value + "\n" + gams_model + xmlTail;
return(jobXML);
}
function solveCube() {
var job_xml = createJobXML('milp', 'CPLEX');
NEOSSubmitString(job_xml, function (submit) {
if (submit.error)
throw(submit.error);
printResults(submit.job_number, submit.password)
})
}
function printResults(number,pass) {
document.getElementById("job_num").value = number;
document.getElementById("job_pass").value = pass;
}
//--><!]]>
</script><h3>Problem Description</h3>
<p>The Rubik's cube is a three-dimensional combination puzzle invented by Emo Rubik in 1974. The physical version of the Rubik's cube consists of small blocks that rotate on a central axis. Classically, the puzzle has six faces, each with nine blocks arranged in a $3 \times 3$ square. More complicated versions of the Rubik's cube exist with $N \times N$ faces. Each block has a colored sticker in either white, red, blue, orange, green, or yellow. To solve the puzzle, the player must rotate one strip of blocks at a time until each of the six faces of the cube is populated by a single color.</p>
<p>Every year, dozens of Rubik's cube competitions are held around the world in which the objective is to solve the puzzle as fast as possible. A number of easily memorizable algorithms exists that allow players to solve any Rubik's cube configuration. However, solving these puzzles is not just of interest to players. There is also much academic interest in designing efficient algorithms to solve a Rubik's cube in as few turns as possible.</p>
<p>The goal of this case study is to present a mixed-integer program that can solve an $N \times N \times N$ Rubik's cube from any starting configuration.</p>
<h3>Methodology</h3>
<p>Given any $N \times N \times N$ Rubik's cube, a unique set of $m$ moves exists. It is important to note that moves are a sequence that involves many position-to-position mappings. We define moves to be quarter-turns of a block strip in either direction; half-turns require two moves.</p>
<div class="center-block"><figure><img src="/sites/default/files/rubiks-valid-move.png" width="65%" height="65%" /></figure></div>
<p>Consider the two-dimensional representation of a standard $3 \times 3 \times 3$ Rubik's cube in Figure (1). In this figure, a rotation of face 6 of the cube also rotates a row in the four neighboring faces. It is easy to verify that all moves can be exhausted by taking any face and considering all row- and column-wise turns, and then taking one vertically (horizontally) adjacent face and considering all row-wise (column-wise) turns. With the standard $3 \times 3 \times 3$ cube, this yields 18 unique moves. More generally, this yields $6N$ unique moves for an $N \times N \times N$ cube.</p>
<h3>Notation</h3>
<p>Notation used to define the model are introduced below.</p>
<ul><li>$N$ denotes the number of faces on the Rubik's cube (for this case study, we assume $N = 3$).
</li><li>The faces of the Rubik's cube are indexed by $f = 1, \ldots, 6$.</li>
<li>Let $r, c = 1, \ldots, N$ index the rows and columns, respectively, of each face. The tuple $(f,r,c)$ is a fixed position on the Rubik's cube.</li>
<li>The function $x(f,r,c,t)$ defines the color of the block in position $(f,r,c)$ on turn $t$. As there are six faces, $x(f,r,c,t)$ can take on values one through six, representing the six colors of the cube.</li>
<li>Let $A(f,r,c)$ denote the set of moves associated with position $(f,r,c)$; that is, the set of moves that involve the block in position $(f,r,c)$ moving, and consequently being replaced by a new block. Thus if move $m \in A(f,r,c)$ is made on turn $t$, $x(f,r,c,t) = x(f_m, r_m, c_m, t+1)$, where $(f_m,r_m,c_m)$ is the new position of the block originally occupying position $(f,r,c)$ after move $m$.</li>
</ul><h3>Computing all possible moves for Rubik's cube</h3>
<p>
Given our framework, a set of heuristics can be devised that generates all moves automatically for any $N \times N \times N$ Rubik's cube. Figure (2) is a snapshot of the code that produces the first $3$ moves on a $3 \times 3 \times 3$ cube.</p>
<div class="center-block"><figure><img src="/sites/default/files/rubiks-possible-moves.png" width="75%" height="65%" /></figure></div>
<p>Variable $p$ indexes either the rows or the columns. Let's consider the case when $p = 1$. <code>lm(m,fi,ri,ci,fj,rj,cj)</code> indicates that move $m$ is associated with transfering the block in the initial position $(f_i,r_i,c_i)$ to the terminal position $(f_j,r_j,c_j)$.</p>
<ul><li>The first condition defining the set <code>ord(m) eq ord(p)</code> assigns this mapping to the first ($p = 1)$ move.</li>
<li>The second condition <code>ord(p) eq ord(ri)</code> says that we are concerned with moving blocks in the first row of face 2.</li>
<li>The third condition <code>ord(ri) eq (cubeSize - ord(cj)+1)</code> says that the last column of face 5 is the terminal location of the blocks from face 2</li>
<li>The last condition <code>ord(rj) eq ord(ci)</code> ensures that the row index of the initial position in face 2 matches the column index in the terminal position in face 5.</li>
</ul><p>The rest of the code can be interpreted in the similar fashion. Combining all of these generates all moves displayed in Figure (1b). Counterclockwise quarter-turns move blocks from face 2 to face 5, from face 5 to face 4, from face 4 to face 1, and from face 1 to face 2. The fifth line of the code also defines the rotation of blocks within face 6 (when $p = 1$). The last line of the code describes the actions within face 3 (when $p = 3$). By turning the "middle" row of the cube, faces 3 and 6 do not change, but blocks within the other faces still rotate (when $p = 2$).
</p>
<p>
Given all possible legal moves for a Rubik's cube, the set of move associations $A(f,r,c)$ is constructed by computing within GAMS: <code>bm_assc(m,f,r,c)=YES</code>, which is equivalent to $m \in A(f,r,c)$.
</p>
<h3>The Rubik's Cube Model</h3>
<p>Assuming that all moves and move associations can be generated, we now consider the solution of a Rubik's cube. Given any $N \times N \times N$ Rubik's cube, we consider the following model:<br />
\begin{align}<br />
\min&\qquad\sum_t\sum_m y(m,t)\cdot t \\[5pt]<br />
\text{subject to}&\qquad \sum_m y(m,t)\le 1\\[5pt]<br />
&\qquad x(f,r,c,t)-M(1-y(m,t))\le x(f_{m},r_{m},c_{m},t+1)\\[5pt]<br />
&\qquad x(f,r,c,t)+M(1-y(m,t))\ge x(f_{m},r_{m},c_{m},t+1)\\[5pt]<br />
&\qquad x(f,r,c,t)-M\sum_{m\in A(f,r,c)}y(m,t)\le x(f,r,c,t+1)\\[5pt]<br />
&\qquad x(f,r,c,t)+M\sum_{m\in A(f,r,c)}y(m,t)\ge x(f,r,c,t+1)\\[5pt]<br />
&\qquad \sum_n z(f,r,c,t,n)\cdot n=x(f,r,c,t)\\[5pt]<br />
&\qquad \sum_n z(f,r,c,t,n)=1\\[5pt]<br />
&\qquad \sum_r\sum_c z(f,r,c,t,n)\ge N^2\cdot v(f,t,n)\\[5pt]<br />
&\qquad \sum_r\sum_c z(f,r,c,t,n)+1\le N^2+v(f,t,n)\\[5pt]<br />
&\qquad \sum_f\sum_n v(f,t,n)\ge 6\cdot d(t)\\[5pt]<br />
&\qquad \sum_f\sum_n v(f,t,n)\le 5+d(t)\\[5pt]<br />
&\qquad \sum_t d(t)\ge 1<br />
\end{align}<br />
The function $x(f,r,c,t)$, which indicates the color of the block in position $(f,r,c)$ on turn $t$, takes on integer values one through six; all remaining variables are binary:
</p><ul><li>$y(m,t)$ indicates whether move $m$ is made on turn $t$</li>
<li>$z(f,r,c,t,n)$ indicates whether $x(f,r,c,t) = n$</li>
<li>$v(f,t,n)$ indicates whether every block on face $f$ is color $n$ on turn $t$; and</li>
<li>$d(t)$ indicates whether the Rubik's cube is solved by turn $t$.</li>
</ul><p>The model minimizes an objective value that is increasing in the number of turns taken and increasing in the time at which a turn is made. Thus, the model aims to solve the Rubik's cube in as few steps as possible. The model constraints are described below.<br /><span class="indent"><b>Constraint 1</b>: Constraint (1) ensures that no more than one move is made per turn.</span><br /><span class="indent"><b>Constraints 2 and 3</b>: Assume $M$ is chosen such that $M \ge 5$. Constraints (2) and (3) together ensure that $x(f,r,c,t+1)$ is between $x(f,r,c,t) - M$ and $x(f,r,c,t) + M$. Consider a move $m \in A(f,r,c)$. If $y(m,t) = 0$ (move $m$ is not made on turn $t$), then the constraints leave $x(f,r,c,t+1)$ unbounded. If $y(m,t) = 1$, then the constraints ensure that $x(f,r,c,t) = x(f_m,r_m,c_m,t+1)$ so that the block in position $(f,r,c)$ on turn $t$ is moved to position $(f_m,r_m,c_m)$ on turn $t+1$.</span><br /><span class="indent"><b>Constraints 4 and 5</b>: If $y(m,t) = 0$ for all $m \in A(f,r,c)$, then the constraints force $x(f,r,c,t) = x(f,r,c,t+1)$; that is, when no move is associated with position $(f,r,c)$ is made, then the block in position $(f,r,c)$ does not move. If $y(m,t) = 1$ for some $m \in A(f,r,c)$, then the constraints leave $x(f,r,c,t)$ unbounded. When combined with constraints (2) and (3), the equations ensure that if a move $m \in A(f,r,c)$ is made on turn $t$, then $x(f,r,c,t) = x(f_m,r_m,c_m,t+1)$; otherwise, if $m \notin A(f,r,c)$ is not made on turn $t$, then $x(f,r,c,t) = x(f,r,c,t+1)$.</span><br /><span class="indent"><b>Constraints 6 and 7</b>: Constraints (6) and (7) guarantee that $z(f,r,c,t,n) = 1$ if and only if $x(f,r,c,t) = n$.</span><br /><span class="indent"><b>Constraints 8 and 9</b>: Constraints (8) and (9) define bounds on the number of blocks on face $f$ with color $n$ on turn $t$. Thus, if $v(f,t,n) = 1$, then all blocks on face $f$ are color $n$ on turn $t$.</span><br /><span class="indent"><b>Constraints 10 and 11</b>: The left-hand side of constraints (10) and (11) is equal to one if face $f$ is complete, that is, if all blocks on face $f$ have the same color, and zero, otherwise. The constraints state that the cube is solved ($d(t) = 1$) if all faces are complete.</span><br /><span class="indent"><b>Constraint 12</b>: Constraint (12) states that the cube must be solved (i.e., $d(t) = 1$ for some $t$).</span>
</p>
<p>
The GAMS model is available for download <a href="https://neos-guide.org/sites/default/files/rubiks_original.gms">here</a>.
</p>
<hr /><h3>Some Results</h3>
<p>Figure (4) displays the results for the $3 \times 3 \times 3$ cube defined in <code>rubiks3x3.dat</code>. The cube is sovled in four turns. In the first turn, Figure (4a) to (4b), the third row of the front face is quarter-turned to the left. In the second turn (4b) to (4c), the front face is rotated counterclockwise 90 degrees. In the third turn (4c) to (4d), the third row of the front face is quarter-turned to the right. Finally, in the last turn (4d) to (4e), the second row of the front face is quarter-turned to the left.</p>
<div class="center-block"><figure><img src="/sites/default/files/rubiks-fig4.png" width="45%" height="45%" /></figure></div>
<p>Figures (3) and (5) demonstrate the model on Rubik's cubes of size $2\times 2\times 2$ with 6 shuffles and $4\times 4\times 4$ with 4 shuffles.</p>
<div class="center-block"><figure><img src="/sites/default/files/rubiks-fig3.png" width="45%" height="45%" /><img src="/sites/default/files/rubiks-fig5.png" width="45%" height="45%" /></figure></div>
<h3>Limitations</h3>
<p>The number of turns allotted $T$ (length of set $t$) must be sufficiently large so that the model is solvable. For more complex cubes, larger values of $T$ are needed to maintain model feasibility. It can be verified that for an $N \times N \times N$ cube, the model can be solved for $6T(7N^2 + N + ^6) + T$ variables. For a standard cube with $N = 3$, this amounts to 433 variables per turn allotted. As a result, even for cubes with a single shuffle, larger values of $T$ increase runtime substantially. Ideally, $T$ should be the smallest value such that the model is solvable.</p>
<h3>Supporting Scripts</h3>
<p>The following Matlab scripts utilize <a href="http://www.mathworks.com/matlabcentral/fileexchange/31672-rubik-s-cube-simulator-and-solver">Joren Heit's Rubik's Cube Simulator and Solver</a> available for download from Mathworks.</p>
<p>To generate an NxNxN Rubik's cube in the appropriate format for GAMS, run <code>generateRubiks.m</code>. To create the images for a given solution to a Rubik's cube, use <code>plotRubiksCube.m</code>.</p>
<div class="center-block">
<pre>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Purpose: Creates data for NxNxN Rubik's cubes that can be fed into GAMS.
%
% Dependencies: Joren Heit's Rubik's Cube Simulator and Solver is used
% to generate a valid, random Rubik's Cube. This work can be found here:
% http://www.mathworks.com/matlabcentral/fileexchange/31672-rubik-s-cube-simulator-and-solver
%
% Inputs:
% -sizeCube: The number of blocks to be in each row and column.
% Ex: If sizeCube = 3, a 3x3x3 Rubik's Cube will be generated.
% -shuffleSteps: The number of random steps used to shuffle the cube.
% -turns: The number of turns you want to give the mixed integer
% program to solve the Rubik's cube.
% Note: If you are choosing a lot of shuffle steps, you need
% a high value of t, or else you risk infeasability.
% -directory: The directory where you want your output file to be
% stored. It's advisable to save your output file where you
% run your GAMS files.
% -fileName: Desired name of output file. This fileName will be concaten-
% ated with an n x n suffix.
% Ex: If fileName = rubiks and sizeCube = 3, the output file
% will be titled rubiks3x3.dat.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function generateRubiksData(sizeCube,shuffleSteps,turns,directory,fileName)
addpath Digital_Rubiks_Cube/src/;
% Generate valid, random Rubik's Cube.
generatedCube = rubgen(sizeCube,shuffleSteps);
rubplot(generatedCube);
% Create file suffix.
fileNameSuffix = strcat(num2str(sizeCube),'x',num2str(sizeCube));
% Output it to a file that GAMS can read.
fid = fopen(strcat(directory,fileName,fileNameSuffix,'.dat'),'w');
fprintf(fid,'sets\n');
fprintf(fid,'\tf \"faces\" / f1 * f6 /\n');
fprintf(fid,'\tr \"rows\" / r1 * r%d /\n',sizeCube);
fprintf(fid,'\tc \"columns\" / c1 * c%d /\n',sizeCube);
fprintf(fid,'\tm \"moves\" / m1 * m%d /\n',sizeCube*6);
fprintf(fid,'\tt \"turns\" / t1 * t%d /\n',turns);
fprintf(fid,'\tn \"color\" / c1 * c6 /;\n\n');
fprintf(fid,'scalar\n');
fprintf(fid,'\tcubeSize \"size of cube\" / %d /;\n\n',sizeCube);
fprintf(fid,'parameters\n');
fprintf(fid,'\tinitial_x(f,r,c) /\n');
% The faces of the generated cube must be translated to match the way
% they are expected to be input in the GAMS code.
trans = [3 5 6 1 2 4];
for i = 1:6
for j = 1:sizeCube
for k = 1:sizeCube
if (i == 6 & j == sizeCube & k == sizeCube)
fprintf(fid,'\tf%d.r%d.c%d %d /;\n',trans(i),j,k,generatedCube(j,k,i));
else
fprintf(fid,'\tf%d.r%d.c%d %d\n',trans(i),j,k,generatedCube(j,k,i));
end
end
end
end
fclose(fid);
end
</pre></div>
<div class="center-block">
<pre>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Purpose: Plot GAMS output and save it to specified outDirName.
%
% Dependencies: Joren Heit's Rubik's Cube Simulator and Solver is used
% to generate a valid, random Rubik's Cube. This work can be found here:
% http://www.mathworks.com/matlabcentral/fileexchange/31672-rubik-s-cube-simulator-and-solver
%
% Inputs:
% -solFile: The output file produced by GAMS.
% -sizeCube: The number of blocks to be in each row and column.
% Ex: If sizeCube = 3, a 3 x 3 Rubik's Cube will be generated.
% -outDirName: What you would like the output folder in the working
% directory to be called. For example, if outDirName =
% rubiks3x3plots, you can find your results in a directory
% called rubiks3x3plots in the working directory.
%
% Outputs: A directory called outDirName in which there will be images for
% all steps of the solution.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function plotResults(solFile,sizeCube,outDirName)
addpath Digital_Rubiks_Cube/src/;
fid = fopen(solFile);
tline = fgetl(fid);
f1 = [];
f2 = [];
f3 = [];
f4 = [];
f5 = [];
f6 = [];
% Collect the solutions into 6 matrices, one for each face.
while ischar(tline)
formatSpec = ' %f %f %f';
sizeFace = [sizeCube sizeCube];
face = fscanf(fid,formatSpec,sizeFace);
if (length(findstr(tline,'f1')) > 0)
f4 = [f4 face'];
elseif (length(findstr(tline,'f2')) > 0)
f5 = [f5 face'];
elseif (length(findstr(tline,'f3')) > 0)
f1 = [f1 face'];
elseif (length(findstr(tline,'f4')) > 0)
f6 = [f6 face'];
elseif (length(findstr(tline,'f5')) > 0)
f2 = [f2 face'];
elseif (length(findstr(tline,'f6')) > 0)
f3 = [f3 face'];
end
tline = fgetl(fid);
end
% Generate a cube as a base.
graphCube = rubgen(sizeCube,0);
% Create the name base of the output file names.
nameBase = strcat(num2str(sizeCube),'x',num2str(sizeCube),'c');
% Determine how many plots will need to be made.
plots = size(f1,2)/sizeCube;
mkdir(outDirName);
% Save each plot to the specified directory.
for i=1:plots
fileName = strcat(nameBase,num2str(i));
graphCube(:,:,1) = f1(:,1:sizeCube);
graphCube(:,:,2) = f2(:,1:sizeCube);
graphCube(:,:,3) = f3(:,1:sizeCube);
graphCube(:,:,4) = f4(:,1:sizeCube);
graphCube(:,:,5) = f5(:,1:sizeCube);
graphCube(:,:,6) = f6(:,1:sizeCube);
f1 = f1(:,sizeCube+1:size(f1,2));
f2 = f2(:,sizeCube+1:size(f2,2));
f3 = f3(:,sizeCube+1:size(f3,2));
f4 = f4(:,sizeCube+1:size(f4,2));
f5 = f5(:,sizeCube+1:size(f5,2));
f6 = f6(:,sizeCube+1:size(f6,2));
figure1 = figure;
rubplot(graphCube);
set(gca,'position',[0 0 1 1],'units','normalized');
saveas(figure1,[strcat(outDirName,'/') fileName '.jpg']);
end
fclose(fid);
end
</pre></div>
<!--
<h3><p>Solving the Rubik's Cube</h3>
<p>Copy and paste the Rubik's cube configuration into the area below. Click "Solve Rubik's Cube" to send the GAMS model to the NEOS server. This demo can take a long time depending on the type of cube submitted; therefore, an email will be sent to you upon job completion. You will then be able to download an output file named 'results.txt' from the NEOS server that will have a turn-by-turn solution to your Rubik's cube. The job number and password will also be displayed below after submission.</p>
<p style="padding: 0 2em 2em 1em;"><textarea name="rubiks_cube" id="rubiks_cube" rows=15 cols=120 placeholder="Copy Rubik's cube coordinates here"></textarea><br />
<span class="rspace"><input id="email_ad" type="text" placeholder="Email address" value="" size=20 class="input" required></span><span class="rspace"><button id="btnSolve" type="button" onclick="solveCube();">Solve Rubik's Cube</button></span><button id="btnDefault" type="reset" onclick="resetInput();">Clear</button><br />
<span class="rspace"><input type="text" id="job_num" value="" placeholder="Job number" readonly></span><input id="job_pass" type="text" value="" placeholder="Job password" readonly></p>
<p>-->
</div></div></div><section class="field field-name-field-method field-type-taxonomy-term-reference field-label-above view-mode-rss"><h2 class="field-label">Optimization Category (Linear Programing, Integer, MIP and etc.): </h2><ul class="field-items"><li class="field-item even"><a href="/taxonomy/term/11" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Discrete and Integer Optimization</a></li><li class="field-item odd"><a href="/taxonomy/term/10" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Integer Programming</a></li></ul></section><section class="field field-name-field-image field-type-image field-label-above view-mode-rss"><h2 class="field-label">Image: </h2><div class="field-items"><figure class="clearfix field-item even"><img typeof="foaf:Image" class="image-style-thumbnail" src="https://neos-guide.org/sites/default/files/styles/thumbnail/public/rubiks-valid-move.png?itok=WofhYuul" width="100" height="47" alt="" /></figure><figure class="clearfix field-item odd"><img typeof="foaf:Image" class="image-style-thumbnail" src="https://neos-guide.org/sites/default/files/styles/thumbnail/public/rubiks-possible-moves.png?itok=UIvjxGuk" width="100" height="57" alt="" /></figure><figure class="clearfix field-item even"><img typeof="foaf:Image" class="image-style-thumbnail" src="https://neos-guide.org/sites/default/files/styles/thumbnail/public/rubiks-fig3.png?itok=-sQCH1SB" width="100" height="93" alt="" /></figure><figure class="clearfix field-item odd"><img typeof="foaf:Image" class="image-style-thumbnail" src="https://neos-guide.org/sites/default/files/styles/thumbnail/public/rubiks-fig4.png?itok=s93UZWws" width="100" height="95" alt="" /></figure><figure class="clearfix field-item even"><img typeof="foaf:Image" class="image-style-thumbnail" src="https://neos-guide.org/sites/default/files/styles/thumbnail/public/rubiks-fig5.png?itok=4bzJZAV7" width="100" height="94" alt="" /></figure></div></section><section class="field field-name-field-file field-type-file field-label-above view-mode-rss"><h2 class="field-label">File: </h2><div class="field-items"><div class="field-item even"><span class="file"><img class="file-icon" alt="Binary Data" title="application/octet-stream" src="/modules/file/icons/application-octet-stream.png" /> <a href="https://neos-guide.org/sites/default/files/rubiks_original.gms" type="application/octet-stream; length=10720">rubiks_original.gms</a></span></div></div></section>Sun, 10 Dec 2017 21:41:16 +0000ewong564 at https://neos-guide.orghttps://neos-guide.org/content/rubikscube#commentsDomino Artwork
https://neos-guide.org/content/domino-art
<div class="field field-name-body field-type-text-with-summary field-label-hidden view-mode-rss"><div class="field-items"><div class="field-item even" property="content:encoded"><style>
<!--/*--><![CDATA[/* ><!--*/
img {
image-orientation: from-image;
}
/*--><!]]>*/
</style><p>This Domino Artwork case study describes the optimization model that underlies the <a href="http://www.neos-server.org/neos/solvers/application:Domino/jpeg.html">NEOS Domino solver</a>, which constructs pictures from target images using complete sets of <i>double-nine dominoes</i>. Complete sets of double-nine dominoes include one domino for each distinct pair of dot values from 0 to 9. The NEOS Domino solver is an implementation of the work of <a href="https://new.oberlin.edu/arts-and-sciences/departments/mathematics/faculty_detail.dot?id=20593">Robert Bosch</a> of Oberlin College.</p>
<h4>Background Information</h4>
<p>In his 2006 article <a href="http://www.maa.org/sites/default/files/pdf/upload_library/22/Evans/Horizons-Feb06-p.06-09.pdf">"Opt Art"</a>, Robert Bosch of Oberlin College describes some applications of optimization in the area of art. <i>Domino artwork</i> is a type of photomosaic, a picture made up of many smaller pictures. The small pictures can be seen up close but, at a distance, they merge into a recognizable image. </p>
<p>To create a photomosaic, we partition a target image and a blank canvas into a grid. Then, given a set \(F\) of building-block photographs, we place each photograph from \(F\) onto one square of the blank canvas grid with the goal of arranging the building blocks to resemble the target image as closely as possible. Optimization is well-suited to assigning the building blocks to the blank canvas grid but choosing the set of building blocks requires an artist's touch!</p>
<p>Here, we describe domino artwork, which is a special type of photomosaic in which the building blocks are dominoes. The building-block set in the <i>Domino solver</i> is comprised of complete sets of double-nine dominoes; double-nine domino sets contain one domino for each distinct pair of dot values from 0 to 9.</p>
<h4>Mathematical Formulation</h4>
<p>In this section, we present Bosch's integer programming model for constructing domino artwork images. Instead of a set of photographs \(F\), we have a set \(D=\lbrace d = (d_1, d_2): 0 \leq d_1 \leq d_2 \leq 9\rbrace\) of double-nine dominoes. Each domino \(d=(d_1, d_2) \in D\) is black with \(d_1\) dots on one square and \(d_2\) dots on the other square. The domino has a brightness value associated with each half; the value corresponds to the number of dots, so it is measured on a scale from 0 to 9, black to white.</p>
<p>Each domino can be positioned horizontally or vertically covering two squares of the canvas. A complete set of double-nine dominoes contains 55 dominoes, and exactly $s$ complete sets must be used. Therefore, we need to partition the target image and the canvas into \(m\) rows and \(n\) columns such that \(mn = 110s\).</p>
<p>The decision variable is a yes-no decision for each possible assignment of a domino \(d\) to a <i>pair of adjacent squares</i> of the canvas. This is more complicated than a yes-no decision associated with assigning a single photograph to a single square on a canvas. However, we can introduce a binary variable \(x_{dp}\) for each domino \(d \in D\) and each pair \(p \in P\) where \(P\) is defined as:<br />
\[P = \big\{ \lbrace(i,j),(i+1,j) \rbrace : 1\leq i\leq m-1, i \leq j \leq n \big\} \\<br />
\cup \big\{ \lbrace (i,j),(i,j+1)\rbrace : 1\leq i \leq m, 1 \leq j \leq n - 1 \big\}.\]</p>
<p>Associated with each assignment of a domino \(d\) to a pair of squares \(p\) is a cost \(c_{dp}\). The cost \(c_{dp}\) is calculated based on how close the dots of domino \(d\) match up with the brightness values of the target image. Let \(\beta_{ij}\in[-0.5,9.5]\) represent the brightness value of square \((i,j)\) on the grid representation of target image, and recall that \(d=(d_1,d_2)\) represents a domino with \(d_1\) dots on one square and \(d_2\geq d_1\) dots on the other square. An explicit expression for \(c_{dp}\) with \(p=\lbrace (i_1,j_1), (i_2,j_2)\rbrace\in P\) is<br />
\[c_{dp} = \min\lbrace<br />
\left(d_1-\beta_{i_1j_1}\right)^2 + \left(d_2-\beta_{i_2j_2}\right)^2,<br />
\left(d_1-\beta_{i_2j_2}\right)^2 + \left(d_2-\beta_{i_1j_1}\right)^2<br />
\rbrace.\]</p>
<p>The cost is calculated by adding the squares of the differences between dot values and actual brightness values for each orientation. \(c_{dp}\) becomes the minimum of these values. </p>
<p>Then, the domino optimization model can be written as:<br />
\[\begin{array}{rrcll}<br />
\text{minimize} & \sum_{d\in D}\sum_{p\in P} c_{dp}x_{dp} & & & \\<br />
\text{subject to} & \sum_{p\in P} x_{dp} &= &s & \forall d\in D, \\<br />
& \sum_{d\in D}\sum_{\substack{p\in P: \\ p\ni (i,j)}} x_{dp} & = & 1 & \forall 1\leq i\leq m, 1\leq j\leq n, \\<br />
& x_{dp}\in\{0,1\} & & & \forall d\in D, p\in P.<br />
\end{array}\]</p>
<p>The objective function measures the total cost of the domino arrangement. The first set of constraints ensures that all of the dominoes are placed on the canvas. The second set of constraints ensures that each square is covered by exactly one domino. The domino optimization model is an assignment problem <i>with side constraints</i>.</p>
<p>Assignment problems (without side constraints) are easy to solve in the sense that they exhibit the <i>integrality property</i>. That is, if we relax the integrality restrictions on the decision variables and solve the problem as a linear program, the optimal solution is guaranteed to be integer-valued. Adding side constraints makes the problem harder to solve in theory, however, many instances still can be solved easily.</p>
<h4>Implementation Details</h4>
<p>The main contribution of the NEOS Domino solver is that anyone can create his/her own domino artwork image! Underlying the Domino solver is MATLAB code that processes a target image, creates the GAMS model instance, submits the model to NEOS, reads the solution returned and constructs the image file with only one click of a button. The NEOS Domino solver was developed by Eli Towle as his final project in <i>ISyE/CS 635: Tools and Environments for Optimization</i> at the University of Wisconsin in Madison. </p>
<p>The Domino solver takes as input a JPEG image. There are two optional parameters.</p>
<ul><li>Quality (low, medium, high): the default quality is medium. Selecting <i>low</i> produces a lower quality output image more quickly. Selecting <i>high</i> produces a high-quality output image but <b>the solver may require a long time to produce the results!</b>.</li>
<li>Color (black, white): the default domino set is black dominoes with white dots. Selecting <i>white</i> will result in an image with white dominoes with black dots.</li>
</ul><p><b><i>Click <a href="http://www.neos-server.org/neos/solvers/application:Domino/jpeg.html">here</a> if you are ready to create an image!</i></b></p>
<p>The optimization model solved by the NEOS Domino solver includes one change from the original model to improve the speed of constructing the final image. The original model defined the set of valid location assignments \(P\) as shown above and then defined the cost parameter \(c_{dp}\) based on either possible orientation of domino \(d\) assigned to location \(p\in P\). However, the decision variable \(x_{dp}\) does not allow for reverse orientations of dominoes. In other words, an optimal assignment of domino \((0,9)\) at location \(\lbrace(1,1),(1,2)\rbrace\) could be flipped so that the 9 comes first, but the solution will not make the distinction between these orientations. Therefore, we introduce parameters in the GAMS optimization model that allow orientations to be inferred based on the value of \(c_{dp}\). That is, if<br />
\[\begin{align*}<br />
\left(d_1-\beta_{i_1j_1}\right)^2 + \left(d_2-\beta_{i_2j_2}\right)^2<br />
\leq \left(d_1-\beta_{i_2j_2}\right)^2 + \left(d_2-\beta_{i_1j_1}\right)^2,<br />
\end{align*}\]<br />
then the proper orientation is \((d_1,d_2)\). Otherwise, the orientation is \((d_2,d_1)\).</p>
<p>As an alternative, we could expand the set \(P\) to \(\hat{P}\) to differentiate between orientations in the solution. In this case, we define<br />
\[\begin{align*}<br />
\hat{P} = & \lbrace (t_1,t_2) : (t_1,t_2)\in P \text{ or } (t_2,t_1)\in P \rbrace,<br />
\end{align*}\]<br />
where \(t_1\) and \(t_2\) are tuples. The costs could similarly be altered to reflect the cost of each individual orientation rather than being expressed as the minimum of the two. This expansion effectively doubles the number of decision variables in the model, resulting in slower solving times. As an example, we show below a comparison of solution times for an image of Taylor Swift.</p>
<table><caption>Comparison of solution times for two methods of determining domino orientation</caption>
<tr><th>Sets</th>
<th>Infer from \(c_{dp}\)</th>
<th>Augment \(P\) to \(\hat{P}\)</th>
</tr><tr><td>18</td>
<td>11.85s</td>
<td>15.94s</td>
</tr><tr><td>72</td>
<td>86.32s</td>
<td>211.70s</td>
</tr><tr><td>162</td>
<td>349.56s</td>
<td>668.46s</td>
</tr></table><p>With minimal additional effort, calculating the correct orientations by examining \(c_{dp}\) after the solve is strongly preferred over doubling the number of acceptable orientations.</p>
<h4>Examples</h4>
<!--The image on the left is the target image and the image on the right is the image created by the NEOS Domino solver.--><p>
The first image is the target image and the second image is the image created by the NEOS Domino solver (using 896 complete sets of dominoes).
</p>
<div class="container">
<div style="float:left;width:49%">
<img src="/sites/default/files/capitol.jpg" /></div>
<div style="float:right;width:49%">
<img src="/sites/default/files/capitol_896.png" /></div>
</div>
<p style="clear: both;">
</p><p><br /></p>
<p>
The first image is the target image of Taylor Swift and the second image is the image, created by the NEOS Domino solver, constructed in dominoes! The physical construction of the domino portrait is captured <a href="https://www.youtube.com/watch?v=EMJTHanHBLQ">here</a> in a time-lapse video.
</p>
<div class="container">
<div style="float:left;width:49%">
<img src="/sites/default/files/taylor_swift_original.jpeg" /></div>
<div style="float:right;width:49%">
<img src="/sites/default/files/taylor_swift2.jpeg" /></div>
</div>
<p style="clear: both;">
</p><h4>Resources</h4>
<ul><li><a href="https://new.oberlin.edu/arts-and-sciences/departments/mathematics/faculty_detail.dot?id=20593">Robert Bosch's webpage</a> at Oberlin College</li>
<li>Robert Bosch's <a href="http://www.dominoartwork.com/">Domino Artwork</a> page</li>
<li>Robert Bosch's <a href="http://www.oberlin.edu/math/faculty/bosch/tspart-page.html">TSP Art</a> page</li>
</ul></div></div></div><section class="field field-name-field-method field-type-taxonomy-term-reference field-label-above view-mode-rss"><h2 class="field-label">Optimization Category (Linear Programing, Integer, MIP and etc.): </h2><ul class="field-items"><li class="field-item even"><a href="/taxonomy/term/11" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Discrete and Integer Optimization</a></li></ul></section><section class="field field-name-field-image field-type-image field-label-above view-mode-rss"><h2 class="field-label">Image: </h2><div class="field-items"><figure class="clearfix field-item even"><img typeof="foaf:Image" class="image-style-thumbnail" src="https://neos-guide.org/sites/default/files/styles/thumbnail/public/capitol_896.png?itok=6F9bZu0h" width="100" height="80" alt="" /></figure><figure class="clearfix field-item odd"><img typeof="foaf:Image" class="image-style-thumbnail" src="https://neos-guide.org/sites/default/files/styles/thumbnail/public/taylor_swift_original.jpeg?itok=T8Vs6tBp" width="65" height="100" alt="" /></figure><figure class="clearfix field-item even"><img typeof="foaf:Image" class="image-style-thumbnail" src="https://neos-guide.org/sites/default/files/styles/thumbnail/public/taylor_swift2.jpeg?itok=Edk1txjg" width="100" height="75" alt="" /></figure></div></section>Tue, 20 Oct 2015 19:22:54 +0000rberger528 at https://neos-guide.orghttps://neos-guide.org/content/domino-art#commentsDiscrete Optimization
https://neos-guide.org/content/discrete-optimization
<div class="field field-name-body field-type-text-with-summary field-label-hidden view-mode-rss"><div class="field-items"><div class="field-item even" property="content:encoded"><p>In <b>discrete optimization</b>, some or all of the variables in a model are required to belong to a <i>discrete set</i>; this is in contrast to <a href="/content/continuous-optimization">continuous optimization</a> in which the variables are allowed to take on any value within a range of values. Here, we consider two branches of discrete optimization. In <b><a href="/content/integer-linear-programming">integer programming</a></b>, the discrete set is a subset of integers. In <b><a href="/content/combinatorial-optimization">combinatorial optimization</a></b>, the discrete set is a set of objects, or combinatorial structures, such as assignments, combinations, routes, schedules, or sequences. </p>
<!--
[mention the broad range of applications]
--><p>
Go back to the <a href="/optimization-tree">Optimization Tree</a></p>
<ul><li><a href="/content/integer-linear-programming">Integer Linear Programming</a>
<ul><li><a href="/content/integer-linear-programming#intro">Basic Concepts</a></li>
<li><a href="/content/integer-linear-programming#software">Software Resources</a></li>
<li><a href="/content/integer-linear-programming#test_probs">Test Problems</a></li>
<li><a href="/content/integer-linear-programming#case_studies">Case Studies</a></li>
<li><a href="/content/integer-linear-programming#references">References</a></li>
</ul></li>
<li><a href="/content/combinatorial-optimization">Combinatorial Optimization</a>
<ul><li><a href="/content/combinatorial-optimization#intro">Basic Concepts</a></li>
<li><a href="/content/combinatorial-optimization#examples">Examples</a></li>
<!--
<li><p>Software Resources (include solvers on the NEOS Server and the info from the Software Guide)</li>
<li>Test Problems (TSPLIB, VRP, others)</li>
<li>Case Studies</li>
<p>-->
<li><a href="/content/combinatorial-optimization#references">References</a></li>
</ul></li>
</ul></div></div></div>Thu, 16 Jan 2014 15:48:05 +0000rberger407 at https://neos-guide.orghttps://neos-guide.org/content/discrete-optimization#comments