- an axis-aligned square (angle is 0°),
- a diamond shape, which is a square rotated by 45°.
- The angle is given. In modeling terms, the angle is exogenous.
- Let the model find the best angle. I.e., the angle is endogenous.
Model 1. Exogenous Angle
| Exogenous Angle Model |
|---|
| \[\begin{align} \max\> & {\color{darkred}s} \\&{\color{darkblue}p}'_{i,x} \le {\color{darkred}x}' + {\color{darkblue}M} {\color{darkred}\delta}_{i,1} && \forall i \\&{\color{darkblue}p}'_{i,x} \ge {\color{darkred}x}' + {\color{darkred}s} - {\color{darkblue}M} {\color{darkred}\delta}_{i,2} && \forall i \\&{\color{darkblue}p}'_{i,y} \le {\color{darkred}y}' + {\color{darkblue}M} {\color{darkred}\delta}_{i,3} && \forall i \\&{\color{darkblue}p}'_{i,y} \ge {\color{darkred}y}' + {\color{darkred}s} - {\color{darkblue}M} {\color{darkred}\delta}_{i,4} && \forall i \\ & \sum_k {\color{darkred}\delta}_{i,k} = 3 && \forall i \\&{\color{darkred}q}_{j,x}' = {\color{darkred}x}' + {\color{darkred}s} {\color{darkblue}\Delta}_{j,x} && \forall j \\&{\color{darkred}q}_{j,y}' = {\color{darkred}y}' + {\color{darkred}s} {\color{darkblue}\Delta}_{j,y} && \forall j \\&{\color{darkred}q}_{j,x} ={\color{darkred}q}_{j,x}' \cos({\color{darkblue}\theta}) - {\color{darkred}q}_{j,y}' \sin({\color{darkblue}\theta}) && \forall j \\&{\color{darkred}q}_{j,y} ={\color{darkred}q}_{j,x}' \sin({\color{darkblue}\theta}) + {\color{darkred}q}_{j,y}' \cos({\color{darkblue}\theta}) && \forall j \\&{\color{darkred}q}_{j,c} \in [0, {\color{darkblue}{\mathit{maxSize}}}] \\&{\color{darkred}s} \in [0,{\color{darkblue}{\mathit{maxSize}}}] \\&{\color{darkred}\delta}_{i,k} \in \{0,1\} \\&{\color{darkblue}p}'_{i,x} := {\color{darkblue}p}_{i,x} \cos({\color{darkblue}\theta}) + {\color{darkblue}p}_{i,y} \sin({\color{darkblue}\theta}) \\&{\color{darkblue}p}'_{i,y} := -{\color{darkblue}p}_{i,x} \sin({\color{darkblue}\theta}) + {\color{darkblue}p}_{i,y} \cos({\color{darkblue}\theta}) \\&{\color{darkblue}\Delta} = \begin{pmatrix} 0 & 0 \\ 0 & 1 \\ 1 & 0 \\ 1 & 1\end{pmatrix} \\ & i\in \{1,\dots,n\} \\ & c \in \{x,y\} \\& k \in \{1,\dots,4\}\\& j \in \{1,\dots,4\} \end{align}\] |
- \(\sin({\color{darkblue}\theta})\) and \(\cos({\color{darkblue}\theta})\) are constants. Unbelievably, this is actually a linear MIP model!
- \({\color{darkred}x}',{\color{darkred}y}'\) denotes the lower-left corner of the axis-aligned square (like in [1]).
- \({\color{darkred}q}_{j,c}', j=1,\dots,4\) are the four corner points (in transformed space). We map them back to \({\color{darkred}q}_{j,c}\), which form a rotated square.
- We can tighten the \(\color{darkblue}M\) values considerable by making them dependent on the point, i.e., \({\color{darkblue}M}_{i,k}\).
- Below, we plot the solution both in the transformed space and in the original coordinate system.
- This is a very easy MIP:
Proven optimal solution
MIP Solution: 3.085938 (657 iterations, 0 nodes)
Next thing to try: make \(\theta\) endogenous.
GAMS Model
$onText
Largest Empty Rotated Square. This version: exogenous angle This gives us a MIP model.
$offText
*--------------------------------------------------------------- * options *---------------------------------------------------------------
* 0: no plot * 1: produce HTML plot $set htmlplot 1
* this model turned to linear option mip=cplex;
option seed=12345;
*--------------------------------------------------------------- * data *---------------------------------------------------------------
scalar maxsize 'size of the box' / 10 /;
sets i 'points' /point1*point100/ a 'square attributes' /x,y,s/ c(a) 'coordinates' /x,y/ ;
parameter p(i,c) 'points'; p(i,c) = uniform(0,maxsize); display p;
*--------------------------------------------------------------- * MIP MODEL *---------------------------------------------------------------
scalars angle 'degrees' / 25 / theta 'angle in radians' M 'big-M' ; M = 2 * maxSize; theta = angle*pi/180; display theta;
* * transform data points * parameter q(i,c) 'transformed data points: rotate by -theta'; q(i,'x') = p(i,'x')*cos(theta) + p(i,'y')*sin(theta); q(i,'y') = -p(i,'x')*sin(theta) + p(i,'y')*cos(theta); display q;
* * transform corner points (for picture) * not used in the model, but useful for visualization *
set k 'corner points box' /0-0, 0-1, 1-0, 1-1/; parameter box(k,c) 'box corners'; box('0-0','x') = 0; box('0-0','y') = 0; box('0-1','x') = 0; box('0-1','y') = maxsize; box('1-0','x') = maxsize; box('1-0','y') = 0; box('1-1','x') = maxsize; box('1-1','y') = maxsize; display box;
parameter tbox(k,c) 'box corners (transformed)'; tbox(k,'x') = box(k,'x')*cos(theta) + box(k,'y')*sin(theta); tbox(k,'y') = -box(k,'x')*sin(theta) + box(k,'y')*cos(theta); display tbox;
set case 'for comparison' /less,greater/ corners /00,01,10,11/ ; table offset(corners,c) 'unit offset of corner points from lower-left' x y 00 0 0 01 0 1 10 1 0 11 1 1 ;
variable sq(a) 'square location and size (transformed coordinates)' sq2(corners,c) 'corner points (original coordinates)' ; sq2.lo(corners,c) = 0; sq2.up(corners,c) = maxsize;
binary variable d(i,c,case) 'd=1 means: relax no-overlap constraint'; variable z 'objective';
Equations emptysq1(i,c) 'no-overlap constraint: less-than version' emptysq2(i,c) 'no-overlap constraint: greater-than version' sumd(i) '(at least) one no-overlap constraint must hold' transformx "transform x',y' -> x back to original coordinates" transformy "transform x',y' -> y back to original coordinates" objsq 'objective' ;
* this is like the original axis-aligned square model objsq.. z =e= sq('s'); emptysq1(i,c).. q(i,c) =l= sq(c) + d(i,c,'less')*M; emptysq2(i,c).. q(i,c) =g= sq(c) + sq('s') - d(i,c,'greater')*M; sumd(i).. sum((c,case),d(i,c,case)) =e= 3;
* transform back to original coordinates transformx(corners).. sq2(corners,'x') =e= (sq('x')+offset(corners,'x')*sq('s'))*cos(theta) - (sq('y')+offset(corners,'y')*sq('s'))*sin(theta); transformy(corners).. sq2(corners,'y') =e= (sq('x')+offset(corners,'x')*sq('s'))*sin(theta) + (sq('y')+offset(corners,'y')*sq('s'))*cos(theta);
model emptysquare /all/; solve emptysquare maximizing z using mip;
parameter square(*,*) 'optimal values'; square(corners,c) = sq2.l(corners,c); square('side','-') = sq.l('s'); square('area','-') = sqr(square('side','-')); display square;
*--------------------------------------------------------------------- * visualization *---------------------------------------------------------------------
$set html plot.html $set data data.js
$if %htmlplot%==0 $goto skipplot
file fdata /%data%/; put fdata;
* points put "p=["/; loop(i, put " {i:'",i.tl:0,"',x:",p(i,'x'):0:4,",y:",p(i,'y'):0:4,"},"/; ); put "]"/;
put "q=["/; loop(i, put " {i:'",i.tl:0,"',x:",q(i,'x'):0:4,",y:",q(i,'y'):0:4,"},"/; ); put "]"/;
put "box={x:0,y:0,size:",maxsize:0:4,"}"/; put "angle=",angle:0:4/;
put "tbox=["/; loop(k, put " {k:'",k.tl:0,"',x:",tbox(k,'x'):0:4,",y:",tbox(k,'y'):0:4,"},"/; ); put "]"/;
put "tr_sq={x:",sq.l('x'):0:4,",y:",sq.l('y'):0:4,",side:",sq.l('s'):0:4,",area:",(sq.l('s')**2):0:4,"}"/;
put "cp=["/; loop(corners, put " {k:'",corners.tl:0,"',x:",square(corners,'x'):0:4,",y:",square(corners,'y'):0:4,"},"/; ); put "]"/; putclose;
$onecho > %html% <html> <script src="https://cdn.plot.ly/plotly-3.4.0.min.js" charset="utf-8"></script> <script src="%data%" charset="utf-8"></script> <style> .brdr { border: 1px solid black; border-collapse: collapse; } th, td { padding-left: 4px; padding-right: 4px; } </style>
<h1>Data</h1> <table> <tr><td> <div id="plotDiv0" style="width: 800px; height: 600px;"></div> </td><td> <div id="plotDiv1" style="width: 800px; height: 600px;"></div> </td></tr></table>
<h1>Solution in Transformed Coordinates</h1> <table><tr><td> <table class="brdr"> <tr><th colspan=2 class="brdr">Square</td></tr> <tr><td class="brdr">𝑥'</td><td class="brdr" id="tr_x"></td></tr> <tr><td class="brdr">𝑦'</td><td class="brdr" id="tr_y"></td></tr> <tr><td class="brdr">side</td><td class="brdr" id="tr_s"></td></tr> <tr><td class="brdr">area</td><td class="brdr" id="tr_area"></td></tr> </table> </td><td> <div id="plotDiv2" style="width: 800px; height: 600px;"></div> </td></tr></table>
<h1>Solution in Original Coordinates</h1> <table><tr><td> <table class="brdr"> <tr><th colspan=3 class="brdr">Rotated Square</td></tr> <tr><td class="brdr"></td><th class="brdr">𝑥</th><th class="brdr">𝑦</th></tr> <tr><td class="brdr">Corner 1</td><td class="brdr" id="c00_x"></td><td class="brdr" id="c00_y"></td></tr> <tr><td class="brdr">Corner 2</td><td class="brdr" id="c01_x"></td><td class="brdr" id="c01_y"></td></tr> <tr><td class="brdr">Corner 3</td><td class="brdr" id="c10_x"></td><td class="brdr" id="c10_y"></td></tr> <tr><td class="brdr">Corner 4</td><td class="brdr" id="c11_x"></td><td class="brdr" id="c11_y"></td></tr> </table> </td><td> <div id="plotDiv3" style="width: 800px; height: 600px;"></div> </td></tr></table>
<script> px = p.map((x) => x['x']) py = p.map((x) => x['y']) qx = q.map((x) => x['x']) qy = q.map((x) => x['y'])
document.getElementById("tr_x").innerHTML = tr_sq['x']; document.getElementById("tr_y").innerHTML = tr_sq['y']; document.getElementById("tr_s").innerHTML = tr_sq['side']; document.getElementById("tr_area").innerHTML = tr_sq['area'];
document.getElementById("c00_x").innerHTML = cp[0]['x']; document.getElementById("c00_y").innerHTML = cp[0]['y']; document.getElementById("c01_x").innerHTML = cp[1]['x']; document.getElementById("c01_y").innerHTML = cp[1]['y']; document.getElementById("c10_x").innerHTML = cp[2]['x']; document.getElementById("c10_y").innerHTML = cp[2]['y']; document.getElementById("c11_x").innerHTML = cp[3]['x']; document.getElementById("c11_y").innerHTML = cp[3]['y'];
var path2 = `M ${cp[0]['x']} ${cp[0]['y']} L ${cp[1]['x']} ${cp[1]['y']} L ${cp[3]['x']} ${cp[3]['y']} L ${cp[2]['x']} ${cp[2]['y']} Z`;
var data1 = { x: px, y: py, mode: 'markers', type: 'scatter', name: 'data points', color: 'darkblue' };
var data2 = { x: qx, y: qy, mode: 'markers', type: 'scatter', name: 'transformed', color: 'darkblue' };
var layout1 = { autosize: false, width: 650, height: 600, showlegend: true, title: {text:'Original Data'}, shapes: [{type:'rect', xref:'x', yref:'y', x0:box['x'], y0:box['y'], x1:box['x']+box['size'], y1:box['y']+box['size'], line:{color:'black',width:2,opacity:0.5}, fillcolor:'rgba(0, 250, 250, 0.2)', name:'box', showlegend:true } ] }
var path = `M ${tbox[0]['x']} ${tbox[0]['y']} L ${tbox[1]['x']} ${tbox[1]['y']} L ${tbox[3]['x']} ${tbox[3]['y']} L ${tbox[2]['x']} ${tbox[2]['y']} Z`;
var layout2 = { autosize: false, width: 650, height: 600, showlegend: true, title: {text:'Transformed Data'}, shapes: [{type:'path', path:path, xref:'x', yref:'y', line:{color:'black',width:2,opacity:0.5}, fillcolor:'rgba(0, 250, 250, 0.2)', name:'transformed box', showlegend:true } ]
}
var layout3 = { autosize: false, width: 650, height: 600, showlegend: true, title: {text:'Transformed Solution'}, shapes: [ {type:'path', path:path, xref:'x', yref:'y', line:{color:'black',width:2,opacity:0.5}, fillcolor:'rgba(0, 250, 250, 0.2)', name:'transformed box', showlegend:true }, {type:'rect', xref:'x', yref:'y', x0:tr_sq['x'], y0:tr_sq['y'], x1:tr_sq['x']+tr_sq['side'], y1:tr_sq['y']+tr_sq['side'], line:{color:'darkred',width:2,opacity:0.5}, fillcolor:'rgba(0, 150, 150, 0.5)', name:'optimal square', showlegend:true }, ]
}
var layout4 = { autosize: false, width: 650, height: 600, showlegend: true, title: {text:'Solution'}, shapes: [{type:'rect', xref:'x', yref:'y', x0:box['x'], y0:box['y'], x1:box['x']+box['size'], y1:box['y']+box['size'], line:{color:'black',width:2,opacity:0.5}, fillcolor:'rgba(0, 250, 250, 0.2)', name:'box', showlegend:true }, {type:'path', path:path2, xref:'x', yref:'y', line:{color:'darkred',width:2,opacity:0.5}, fillcolor:'rgba(0, 150, 150, 0.5)', name:'optimal square', showlegend:true },
] }
var trc1 = [data1]; var trc2 = [data2];
var options1 = {staticPlot: true, displayModeBar: false, responsive: false}; Plotly.newPlot('plotDiv0', trc1, layout1, options1); Plotly.newPlot('plotDiv1', trc2, layout2, options1); Plotly.newPlot('plotDiv2', trc2, layout3, options1); Plotly.newPlot('plotDiv3', trc1, layout4, options1);
</script> $offecho
executetool 'win32.ShellExecute "%html%"';
$label skipplot |
Model 2. Endogenous Angle
- \({\color{darkred}{\theta}}\) is now a variable. We allow it to vary between 0 and 90°, or rather between 0 and \(\pi/2\) radians.
- \({\color{darkred}p}'_{i,c}\) is now a variable. The assignments become constraints: \[\begin{align} & {\color{darkred}p}'_{i,x} = {\color{darkblue}p}_{i,x} \cos({\color{darkred}\theta}) + {\color{darkblue}p}_{i,y} \sin({\color{darkred}\theta}) \\ & {\color{darkred}p}'_{i,y} = -{\color{darkblue}p}_{i,x} \sin({\color{darkred}\theta}) + {\color{darkblue}p}_{i,y} \cos({\color{darkred}\theta}) \end{align}\]
Optimal solution found (tolerance 1.00e-04)
Best objective 3.141077560753e+00, best bound 3.141077560753e+00, gap 0.0000%
---- 136 PARAMETER square optimal values x y - 00 4.071 4.434 01 2.659 7.240 10 6.877 5.847 11 5.464 8.652 side 3.141 area 9.866 angle (rad) 0.466 angle (deg) 26.728
Optimal solution found (tolerance 1.00e-04)
Best objective 3.141077907762e+00, best bound 3.141077907762e+00, gap 0.0000%
GAMS Models
$onText
Largest Empty Rotated Square. This version: endogenous angle This gives us a MINLP or MIQCP model.
$offText
*--------------------------------------------------------------- * options *---------------------------------------------------------------
* 0: no plot * 1: produce HTML plot $set htmlplot 1
* baron, antigone don't have sin(),cos() functions, so we use xpress and gurobi for this example option minlp=xpress,miqcp=xpress; *option minlp=gurobi,miqcp=gurobi; option seed=12345;
*--------------------------------------------------------------- * data *---------------------------------------------------------------
scalar maxsize 'size of the outer box' / 10 /;
sets i 'points' /point1*point100/ a 'square attributes' /x,y,s/ c(a) 'coordinates' /x,y/ ;
parameter p(i,c) 'points'; p(i,c) = uniform(0,maxsize); display p;
*--------------------------------------------------------------- * MINLP model with angles *---------------------------------------------------------------
scalars M 'big-M'; M = 2 * maxSize;
set case 'for comparison' /less,greater/ corners 'enumerate four corners' /00,01,10,11/ ; table offset(corners,c) 'unit offset of corner points from lower-left' x y 00 0 0 01 0 1 10 1 0 11 1 1 ;
variable q(i,c) 'transformed data points: rotate by -theta' sq(a) 'square location and size (transformed coordinates)' sq2(corners,c) 'corner points (original coordinates)' theta 'angle of rotation' ; q.lo(i,c) = -2*maxsize; q.up(i,c) = 2*maxsize;
theta.lo = 0; theta.up = 90*pi/180 - 0.00001;
sq.lo(a) = -2*maxsize; sq.up(a) = 2*maxsize;
sq2.lo(corners,c) = 0; sq2.up(corners,c) = maxsize;
binary variable d(i,c,case) 'd=1 means: relax no-overlap constraint'; variable z 'objective';
Equations transfdatax(i) 'transform data points: x coordinate' transfdatay(i) 'transform data points: y coordinate' emptysq1(i,c) 'no-overlap constraint: less-than version' emptysq2(i,c) 'no-overlap constraint: greater-than version' sumd(i) '(at least) one no-overlap constraint must hold' transformx(corners) "transform x',y' -> x back to original coordinates" transformy(corners) "transform x',y' -> y back to original coordinates" objsq 'objective' ;
* transform data points transfdatax(i).. q(i,'x') =e= p(i,'x')*cos(theta) + p(i,'y')*sin(theta); transfdatay(i).. q(i,'y') =e= -p(i,'x')*sin(theta) + p(i,'y')*cos(theta);
* this is like the original axis-aligned square model objsq.. z =e= sq('s'); emptysq1(i,c).. q(i,c) =l= sq(c) + d(i,c,'less')*M; emptysq2(i,c).. q(i,c) =g= sq(c) + sq('s') - d(i,c,'greater')*M; sumd(i).. sum((c,case),d(i,c,case)) =e= 3;
* transform back to original coordinates transformx(corners).. sq2(corners,'x') =e= (sq('x')+offset(corners,'x')*sq('s'))*cos(theta) - (sq('y')+offset(corners,'y')*sq('s'))*sin(theta); transformy(corners).. sq2(corners,'y') =e= (sq('x')+offset(corners,'x')*sq('s'))*sin(theta) + (sq('y')+offset(corners,'y')*sq('s'))*cos(theta);
* to simulate earlier runs *theta.fx = 0; *theta.fx = 25*pi/180; *theta.fx = 45*pi/180;
model emptysquare1 /all/; solve emptysquare1 maximizing z using minlp;
display theta.l, sq.l, sq2.l;
parameter m1results(*,*) 'optimal values model 1'; m1results(corners,c) = sq2.l(corners,c); m1results('side','-') = sq.l('s'); m1results('area','-') = sqr(m1results('side','-')); m1results('angle (rad)','-') = theta.l; m1results('angle (deg)','-') = theta.l * 180 / pi; display m1results;
*--------------------------------------------------------------- * MIQCP model without angles *---------------------------------------------------------------
* reset variable levels so we don't have a good starting point for the MIQCP model * just in case the solver would use these sq.l(a) = 0; sq2.l(corners,c) = 0; d.l(i,c,case) = 0; q.l(i,c) = 0;
variables sintheta 'sine of angle' costheta 'cosine of angle' ; * note: angle is between 0 and 0.5pi, so cos and sin are nonnegative costheta.lo = 1e-6; sintheta.lo = 0; costheta.up = 1; sintheta.up = 1;
equations transfdata2x(i) 'transform data points: x coordinate' transfdata2y(i) 'transform data points: y coordinate' transform2x(corners) "transform x',y' -> x back to original coordinates" transform2y(corners) "transform x',y' -> y back to original coordinates" sincos 'unit circle constraint' ;
* transform data points transfdata2x(i).. q(i,'x') =e= p(i,'x')*costheta + p(i,'y')*sintheta; transfdata2y(i).. q(i,'y') =e= -p(i,'x')*sintheta + p(i,'y')*costheta;
* transform back to original coordinates transform2x(corners).. sq2(corners,'x') =e= (sq('x')+offset(corners,'x')*sq('s'))*costheta - (sq('y')+offset(corners,'y')*sq('s'))*sintheta; transform2y(corners).. sq2(corners,'y') =e= (sq('x')+offset(corners,'x')*sq('s'))*sintheta + (sq('y')+offset(corners,'y')*sq('s'))*costheta;
sincos.. sqr(costheta) + sqr(sintheta) =e= 1;
model emptysquare2 /all-transfdatax-transfdatay-transformx-transformy/; solve emptysquare2 maximizing z using miqcp;
scalar angle; * recover the angle from the sine and cosine values angle = arctan(sintheta.l/costheta.l);
display angle,sintheta.l, costheta.l, sq.l, sq2.l;
parameter m2results(*,*) 'optimal values model 2'; m2results(corners,c) = sq2.l(corners,c); m2results('side','-') = sq.l('s'); m2results('area','-') = sqr(m2results('side','-')); m2results('angle (rad)','-') = angle; m2results('angle (deg)','-') = angle * 180 / pi; display m1results,m2results;
parameter perf(*,*) 'performance comparison'; $macro modelperf(m,s) \ perf(s,'#vars') = m.numvar;\ perf(s,'#dvars') = m.numdvar;\ perf(s,'#equs') = m.numequ;\ perf(s,'obj') = m.objval;\ perf(s,'time') = m.resusd;\ perf(s,'its') = m.iterusd;\ perf(s,'nodes') = m.nodusd; modelperf(emptysquare1,'model 1') modelperf(emptysquare2,'model 2') display perf;
*--------------------------------------------------------------- * visualization *---------------------------------------------------------------
$set html plot.html $set data data.js
$if %htmlplot%==0 $goto skipplot
file fdata /%data%/; put fdata;
* points put "p=["/; loop(i, put " {i:'",i.tl:0,"',x:",p(i,'x'):0:4,",y:",p(i,'y'):0:4,"},"/; ); put "]"/;
put "box={x:0,y:0,size:",maxsize:0:4,"}"/; put "angle=",m1results('angle (rad)','-'):0:4/; put "angledeg=",m1results('angle (deg)','-'):0:4/; put "side=",m1results('side','-'):0:4/; put "area=",m1results('area','-'):0:4/;
put "cp=["/; loop(corners, put " {k:'",corners.tl:0,"',x:",m1results(corners,'x'):0:4,",y:",m1results(corners,'y'):0:4,"},"/; ); put "]"/; putclose;
$onecho > %html% <html> <script src="https://cdn.plot.ly/plotly-3.4.0.min.js" charset="utf-8"></script> <script src="%data%" charset="utf-8"></script> <style> .brdr { border: 1px solid black; border-collapse: collapse; } th, td { padding-left: 4px; padding-right: 4px; } </style>
<h1>Solution in Original Coordinates</h1> <table><tr><td> <table class="brdr"> <tr><th colspan=3 class="brdr">Rotated Square</td></tr> <tr><td class="brdr"></td><th class="brdr">𝑥</th><th class="brdr">𝑦</th></tr> <tr><td class="brdr">Corner 1</td><td class="brdr" id="c00_x"></td><td class="brdr" id="c00_y"></td></tr> <tr><td class="brdr">Corner 2</td><td class="brdr" id="c01_x"></td><td class="brdr" id="c01_y"></td></tr> <tr><td class="brdr">Corner 3</td><td class="brdr" id="c10_x"></td><td class="brdr" id="c10_y"></td></tr> <tr><td class="brdr">Corner 4</td><td class="brdr" id="c11_x"></td><td class="brdr" id="c11_y"></td></tr> <tr><td class="brdr">Angle</td><td class="brdr" id="sol_angle"></td><td class="brdr"></td></tr> <tr><td class="brdr">Side</td><td class="brdr" id="sol_side"></td><td class="brdr"></td></tr> <tr><td class="brdr">Area</td><td class="brdr" id="sol_area"></td><td class="brdr"></td></tr>
</table> </td><td> <div id="plotDiv3" style="width: 800px; height: 600px;"></div> </td></tr></table>
<script> px = p.map((x) => x['x']) py = p.map((x) => x['y'])
document.getElementById("c00_x").innerHTML = cp[0]['x']; document.getElementById("c00_y").innerHTML = cp[0]['y']; document.getElementById("c01_x").innerHTML = cp[1]['x']; document.getElementById("c01_y").innerHTML = cp[1]['y']; document.getElementById("c10_x").innerHTML = cp[2]['x']; document.getElementById("c10_y").innerHTML = cp[2]['y']; document.getElementById("c11_x").innerHTML = cp[3]['x']; document.getElementById("c11_y").innerHTML = cp[3]['y'];
document.getElementById("sol_angle").innerHTML = angledeg+'°'; document.getElementById("sol_side").innerHTML = side; document.getElementById("sol_area").innerHTML = area;
var path2 = `M ${cp[0]['x']} ${cp[0]['y']} L ${cp[1]['x']} ${cp[1]['y']} L ${cp[3]['x']} ${cp[3]['y']} L ${cp[2]['x']} ${cp[2]['y']} Z`;
var data1 = { x: px, y: py, mode: 'markers', type: 'scatter', name: 'data points', color: 'darkblue' };
var layout4 = { autosize: false, width: 650, height: 600, showlegend: true, title: {text:'Solution'}, shapes: [{type:'rect', xref:'x', yref:'y', x0:box['x'], y0:box['y'], x1:box['x']+box['size'], y1:box['y']+box['size'], line:{color:'black',width:2,opacity:0.5}, fillcolor:'rgba(0, 250, 250, 0.2)', name:'box', showlegend:true }, {type:'path', path:path2, xref:'x', yref:'y', line:{color:'darkred',width:2,opacity:0.5}, fillcolor:'rgba(0, 150, 150, 0.5)', name:'optimal square', showlegend:true },
] }
var trc1 = [data1];
var options1 = {staticPlot: true, displayModeBar: false, responsive: false}; Plotly.newPlot('plotDiv3', trc1, layout4, options1);
</script> $offecho
executetool 'win32.ShellExecute "%html%"';
$label skipplot |
I have not tried myself, but what if we do the variable re-write $a = sin(\theta)$ and $b = cos(\theta)$, with the additional constraint of $a^2 + b^2 = 1$? This would remove the trigonometric functions and the variable $\theta$, and I foresee it becoming a (non-convex) quadratic integer linear program, but again, I have not tried myself.
ReplyDeleteWorks like a charm! After adding the bounds 0 <= a,b <= 1, I get about the same performance (on the same solvers), maybe even somewhat faster. Thanks for that!
Delete