Quantcast
Channel: Yet Another Math Programming Consultant
Viewing all articles
Browse latest Browse all 804

Stacking 3d boxes under rotation

$
0
0

Assume we have an infinite collection of 3d boxes of different sizes. As an example let's use:


----     30 PARAMETER boxesavailable box sizes

size-x size-y size-z

box1 467
box2 123
box3 456
box4 101232


We want to build a tower of boxes such that the base of the box on top is strictly smaller than the underlying box. The interesting angle is: we can rotate the boxes. Without loss of generality, we can assume that for all boxes, after rotation, we have: the width (\(x\)) is less than the depth \((y\)). (This may require a bit of thinking). With this, any box can be placed in just three different ways. 

3d rotations

The objective is to build a tower that is as tall as possible. In [1], this problem is solved using a Dynamic Programming algorithm. Here I want to see how this looks using a MIP model.


Model development


The first thing to do is to create the rotations. I created a mapping set that maps from one size to another. Then I applied this mapping to all our boxes. The result is:


----     38 SET rot  rotate s1->s2

size-x size-y size-z

rot1.size-x YES
rot1.size-y YES
rot1.size-z YES
rot2.size-x YES
rot2.size-y YES
rot2.size-z YES
rot3.size-x YES
rot3.size-y YES
rot3.size-z YES


---- 44 PARAMETER sizes sizes under rotation

size-x size-y size-z

box1.rot1 467
box1.rot2 476
box1.rot3 674
box2.rot1 123
box2.rot2 132
box2.rot3 231
box3.rot1 456
box3.rot2 465
box3.rot3 564
box4.rot1 101232
box4.rot2 103212
box4.rot3 123210


I used the assumption that the data is ordered by size (i.e. \(x \le y \le z\)). See our data table to verify that this indeed holds.

With this under our belt, the model is not very difficult. We use the following indices: 

  • \(i\): the level. Just use a set that is larger than the number of levels you expect.
  • \(k\): the box type from the inventory.
  • \(r\): the rotation
  • \(s\): the sizes in each direction.
  • \({\mathit{xy}}\): subset of \(s\), only the \(x\) and \(y\) coordinates. 

I use the following variables:\[\begin{aligned}\color{darkred}{\mathit{assign}}_{i,k,r} &= \begin{cases} 1 & \text{if box $k$ under rotation $r$ is assigned to level $i$} \\ 0 & \text{otherwise}\end{cases} \\ \color{darkred}b_i &= \begin{cases} 1 & \text{if a box is assigned to level $i$}\\ 0 & \text{otherwise}\end{cases}\\ \color{darkred}{\mathit{size}}_{i,s} &= \text{the size of the box at level $i$ (or zero)} \end{aligned}\] The rotation index \(r\) essentially gives us three times as many boxes to choose from.


MIP Model
\[\begin{align}\max\>&\color{darkred}{\mathit{height}}=\sum_i \color{darkred}{\mathit{size}}_{i,z}\\ & \color{darkred}b_i = \sum_{k,r}\color{darkred}{\mathit{assign}}_{i,k,r} \\ & \color{darkred}b_i \le \color{darkred}b_{i-1} && \text{ordering} \\ & \color{darkred}{\mathit{size}}_{i,s} = \sum_{k,r}\color{darkred}{\mathit{assign}}_{i,k,r} \cdot \color{darkblue}{\mathit{sizes}}_{k,r,s} && \text{size at level $i$} \\ & \color{darkred}{\mathit{size}}_{i,xy} \le\color{darkred}{\mathit{size}}_{i-1,xy}-0.5\cdot\color{darkred}b_i && \text{smaller boxes on top} \end{align}\]


Notes:
  • We start at the bottom, i.e. \(\color{darkred}b_1 = 1\). So \(\color{darkred}b_i=0\) only happens only at the top. The constraint \(\color{darkred}b_i \le \color{darkred}b_{i-1}\) is not really needed as the equation that requires smaller boxes to be on top, has the same effect. So, we can make the model even more compact.
  • We require that the width and the depth are both 0.5 less than the box of the previous layer. We make an exception when there is no box, in which case all sizes remain zero. If we have continuous sizes, we need to change this minimum shrinkage to something like 0.001 (this number should be large than the feasibility tolerance of the solver).

This is a very small model. But it actually works. The solution looks like:


----     82 PARAMETER results  optimal stack

size-x size-y size-z

level1.box4.rot3 123210
level2.box4.rot1 101232
level3.box1.rot3 674
level4.box3.rot3 564
level5.box3.rot1 456
level6.box2.rot3 231
level7.box2.rot1 123


The total height is 60 (the sum of the \(z\) column).
 


This model is of course very different from the Dynamic Programming algorithm shown in [1].


References


Appendix: GAMS model

$ontext

  
stack boxes under rotation


  
1. Boxes on top should have a strictly smaller x/y base than the one
     
underneith
  
2. Assume always: size x < size y
  
3. In size comparison I assume integer sizes, so we want to be at least
     
0.5 smaller.


$offtext

*--------------------------------------------------------------
* data
*--------------------------------------------------------------

sets
   s
'size'/size-x,size-y,size-z/
   xy(s)
/size-x,size-y/
   r
'rotation'/rot1*rot3/
   k
'box'/box1*box4/
   i
'stack level'/level1*level10/
;

table boxes(k,s) 'available box sizes'
       
size-x   size-y   size-z
box1       4        6        7
box2       1        2        3
box3       4        5        6
box4      10       12       32
;
option boxes:0;
display boxes;

alias(s,s1,s2);

*--------------------------------------------------------------
* derived data
*--------------------------------------------------------------

set rot(r,s1,s2) 'rotate s1->s2'/
  
rot1.(size-x.size-x, size-y.size-y, size-z.size-z)
  
rot2.(size-x.size-x, size-y.size-z, size-z.size-y)
  
rot3.(size-x.size-z, size-y.size-x, size-z.size-y)
/;
display rot;

parameter sizes(k,r,s) 'sizes under rotation';
sizes(k,r,s2) =
sum(rot(r,s1,s2),boxes(k,s1));
option sizes:0;
display sizes;

*--------------------------------------------------------------
* model
*--------------------------------------------------------------


binaryvariable
   assign(i,k,r) 
'assign (k,r) to level i'
   b(i)          
'level i has a box'
;
variables
   size(i,s)
'size of box at level i'
   numboxes 
'number of stacked boxes'
   height   
'total height of stack'
;

equations
   level(i)     
'box per level'
   order(i)     
'below a box should be another box (optional: covered by eq. smaller)'
   esize(i,s)   
'size of box at level i'
   smaller(i,xy)
'smaller boxes on top of large ones (xy only)'
   countboxes   
'number of boxes we can stack'
   totalheight  
'total height of the tower'
;

level(i)..   b(i) =e=
sum((k,r),assign(i,k,r));
order(i-1).. b(i) =l= b(i-1);
esize(i,s).. size(i,s) =e=
sum((k,r),assign(i,k,r)*sizes(k,r,s));
smaller(i-1,xy).. size(i,xy) =l= size(i-1,xy)-0.5*b(i);
countboxes.. numboxes =e=
sum(i,b(i));
totalheight.. height =e=
sum(i,size(i,'size-z'));

model stackboxes /all/;
option optcr=0;

*--------------------------------------------------------------
* solve
*--------------------------------------------------------------

*solve stackboxes maximizing numboxes using mip;
solve stackboxes maximizing height using mip;

*--------------------------------------------------------------
* reporting
*--------------------------------------------------------------

parameter results(i,k,r,*) 'optimal stack';
results(i,k,r,s)$(assign.l(i,k,r)>0.5) = size.l(i,s);
option results:0:3:1;
display results;


Viewing all articles
Browse latest Browse all 804

Trending Articles