Thursday, April 3, 2014

Microsoft Solver Foundation – Truck load optimization

Microsoft Solver Foundation – Truck load optimization
 
Purpose: The purpose of this document is to illustrate how to use Microsoft Solver Foundation in order to resolve Truck load optimization problem.
 
Challenge: Truck load optimization problem is a typical problem in Distribution and Retail environments with Transportation management needs. For example, many companies have their own fleet or rent delivery trucks. In case you have a transportation load for a truck with packages of different sizes it may be so many ways you can load a truck. A natural question is "How do I load my truck in a best way?"
 
Solution: In this walkthrough we'll utilize the power of Microsoft Excel and Microsoft Solver Foundation in order to resolve Truck load optimization problem. Essentially our objective is to place as many packages into a truck as possible while satisfying a set constraints (2D/3D dimensions, total volume, etc.). In mathematics similar problem is known as "Squaring the square" (2D) or "Cubing the cube" (3D). Please find more info about Squaring the square problem here: http://en.wikipedia.org/wiki/Squaring_the_square
 
Schematically the solution may be presented in the following way (2D)
 
 
On this picture we have multiple packages of different sizes placed inside the rectangle (which may represent a truck). Obviously there's a number of ways you can load a truck and we'll be interested in one of feasible solutions which satisfies a set of logical constraints. At minimum we will need to make sure that our package is placed within defined truck area and there's no overlap of packages inside the truck
 
During the course of this walkthrough we'll build mathematical model to resolve the task using Microsoft Solver Foundation Add-in for Excel. And then upon resolution we'll visualize results using standard shapes in Excel. I also assume that the initial data set for analysis (Transportation loads) can be retrieved from Microsoft Dynamics AX 2012 using Web Services
 
Please note that for modeling using Microsoft Solver Foundation Add-in in Excel you will not need strong development skills. For mathematical modeling I'll be using very intuitive OML (Optimization Modeling Language). Please find detailed OML reference here: http://msdn.microsoft.com/en-us/library/ff524507(v=vs.93).aspx.
 
Walkthrough
 
Before we begin I will install Microsoft Solver Foundation Add-in for Excel
 
 
You can download it from Microsoft Solver Foundation product web site here: http://msdn.microsoft.com/en-us/devlabs/hh145003
 
Now let's start with formalization of the problem in Excel
 
First we'll address the problem of 2D Truck load optimization
 
Initial representation of the task
 
 
To even further simplify initial task we'll assume that we have to arrange squares (width = height). Thus in the matrix above TILE is a sequential square package number, SIDE is a width of square package, AREA is a calculated value of total area taken by square package, and finally X and Y define coordinates of where the package is placed inside a truck
 
On the picture below the matrix you can see how potential solution will look like with points (X, Y) defining coordinates of lower left corner of square package and SIDE defining package width and height      
 
Now let's start building Microsoft Solver Foundation model
 
First off we'll introduce a set to represent TILE (square package)
 
Sets
 
 
Then we'll add a parameter for SIDE
 
Parameters
 
 
This info resides in Excel workbook, that's why we'll bind math model parameter SIDE to appropriate cells in Excel workbook
 
Binding Editor - SIDE
 
 
And this is how you specify Table/Range in Binding Editor. Please note how we define SIDEi: i = TILE and value field itself is SIDE
 
Excel workbook - SIDE
 
 
The next step is to define the decision. For this task our goal is to find X and Y coordinates of lower left corners of square packages and then we can place a package itself knowing its SIDE dimensions. That's why we can now define X[TILE] and Y[TILE] as below
 
Decisions - X
 
 
Similarly we can bind X[TILE] and Y[TILE] to Excel workbook cells which will display the results for us
 
Binding Editor - X
 
 
Please note that I still use the same selection for Table/Range. This time I just mapped Value field to X and Y columns correspondingly
 
Excel workbook – X
 
 
Decisions - Y
 
 
Binding Editor - Y
 
 
Excel workbook – Y
 
 
Please note that we don't define Goal because we are looking for one of feasible solutions which will satisfy a set of constraints. So to complete the model now we'll need to introduce appropriate constraints
 
Constraints - WITHINAREA
 
 
WITHINAREA constraint will help us to ensure that we place the package inside a truck
 
Expression - WITHINAREA
 
Foreach[{i,TILE},X[i]<=7-SIDE[i] & Y[i]<=7-SIDE[i]]
 
Constraints - NOOVERLAP
 
 
Now NOOVERLAP constraint will help us to ensure that packages inside the truck don't overlap which is logical
 
Expression - NOOVERLAP
 
Foreach[{i,9},{j,i+1,9}, X[i]>=X[j]+SIDE[j] | X[i]+SIDE[i]<=X[j] | Y[i]>=Y[j]+SIDE[j] | Y[i]+SIDE[i]<=Y[j] ]
 
Let's review the entire model now
 
Model
 
 
OML
 
Model[
  Parameters[
    Sets[Any],
    TILE
  ],
  Parameters[
    Integers[0, Infinity],
    SIDE[TILE]
  ],
  Decisions[
    Integers[0, Infinity],
    X[TILE],
    Y[TILE]
  ],
  Constraints[
    WITHINAREA -> Foreach[{i,TILE},X[i]<=7-SIDE[i] & Y[i]<=7-SIDE[i]],
    NOOVERLAP -> Foreach[{i,9},{j,i+1,9}, X[i]>=X[j]+SIDE[j] | X[i]+SIDE[i]<=X[j] | Y[i]>=Y[j]+SIDE[j] | Y[i]+SIDE[i]<=Y[j] ]
  ]
]
 
Essentially this model is all you need to resolve a simplified Truck load optimization problem we initially formulated. And this is exactly what we are going to do right now
 
Log
 
 
Log
 
[11/18/2013 12:10:00 AM] Check started...
[11/18/2013 12:10:00 AM] Excel: 00:00:00.2699695
[11/18/2013 12:10:00 AM] Model syntax passed
[11/18/2013 12:10:00 AM] Model Check Complete
[11/18/2013 12:10:03 AM] Solve started...
[11/18/2013 12:10:04 AM] Excel: 00:00:00.0583775
[11/18/2013 12:10:04 AM] ===Solver Foundation Service Report===
Date: 11/18/2013 12:10:04 AM
Version: Microsoft Solver Foundation 3.0.2.10889 Express Edition
Model Name: DefaultModel
Capabilities Applied: CP
Solve Time (ms): 28
Total Time (ms): 142
Solve Completion Status: Feasible
Solver Selected: Microsoft.SolverFoundation.Solvers.ConstraintSystem
Algorithm: TreeSearch
Variable Selection: DomainOverWeightedDegree
Value Selection: ForwardOrder
Move Selection: Any
Backtrack Count: 24
===Solution Details===
Goals:
 
[11/18/2013 12:10:04 AM] Solve Complete
 
When you solve the model you will see the outcome as shown above
 
Please note that the system provides the solution and details of the algorithm which has been chosen to resolve the task. On the Solver Foundation Results tab you can now see values of X[TILE] and Y[TILE] corresponding to a feasible solution found
 
Result
 

Now we can also represent the optimal route visually using simple Excel graph
 
Excel workbook
 
 
For example, this is how we can represent Tile 0 (0;0) which has SIDE = 4
 
 
Eventually we'll have the following packages layout in 2D
 
 
We've resolve a simplified task and now it is time to reformulate the problem to resolve a real-world 3D model with packages of different sizes (width, height, depth) and volumes. For visualization of the result after we solve 3D model we'll be using standard Excel shapes. You can find more info about how to programmatically use standard Excel shapes here: http://msdn.microsoft.com/en-us/library/office/ff744336.aspx
 
We'll start over with initial representation of the task for 3D model
 
Initial representation of the task
 
 
As you can see PACKAGE is a sequential number of package, WIDTH, DEPTH and HEIGHT are physical dimensions of a package, VOLUME represents package volume and X, Y and Z represent coordinates of a package in 3 dimensional truck space
 
We'll start with definition of set for PACKAGE
 
Sets - PACKAGE
 
 
This time we'll have 3 parameters representing WIDTH, DEPTH and HEIGHT as shown below
 
Parameters - WIDTH
 
 
Biding Editor - WIDTH
 
 
Parameters - DEPTH
 
 
Binding Editor - DEPTH
 
 
Parameters - HEIGHT
 
Binding Editor - HEIGHT
 
 
As you can see I mapped WIDTH, DEPTH and HEIGHT parameters to appropriate columns of the same matrix
 
Excel Workbook
 
 
Similarly I'll define a decision points representing package 3-dimensional coordinates X[PACKAGE], Y[PACKAGE] and Z[PACKAGE]
 
Decisions - X
 
 
Binding Editor - X
 
 
Decisions - Y
 
 
Binding Editor - Y
 
 
Decisions - Z
 
 
Binding Editor - Z
 
 
X, Y and Z coordinates will also be mapped against the same matrix as shown below
 
Excel Workbook
 
 
Still no defined Goals as we'll looking for one of feasible solutions for this task
And finally we'll define constraints similar to 2D case but this time taking into account all 3 coordinates (X, Y and Z)
 
Constraints - WITHINAREA
 
 
Expression - WITHINAREA
 
Foreach[{i,PACKAGE},X[i]<=5-WIDTH[i] & Y[i]<=12-DEPTH[i] & Z[i]<=2-HEIGHT[i]]
  
Constraints - NOOVERLAP
 
 
Expression - NOOVERLAP
 
Foreach[{i,5},{j,i+1,5}, X[i]>=X[j]+WIDTH[j] | X[i]+WIDTH[i]<=X[j] | Y[i]>=Y[j]+DEPTH[j] | Y[i]+DEPTH[i]<=Y[j] | Z[i]>=Z[j]+HEIGHT[j] | Z[i]+HEIGHT[i]<=Z[j]]
 
At this point we can review complete model
 
Model
 
 
OML
 
Model[
  Parameters[
    Sets[Any],
    PACKAGE
  ],
  Parameters[
    Integers[0, Infinity],
    WIDTH[PACKAGE],
    DEPTH[PACKAGE],
    HEIGHT[PACKAGE]
  ],
  Decisions[
    Integers[0, Infinity],
    X[PACKAGE],
    Y[PACKAGE],
    Z[PACKAGE]
  ],
  Constraints[
    WITHINAREA -> Foreach[{i,PACKAGE},X[i]<=5-WIDTH[i] & Y[i]<=12-DEPTH[i] & Z[i]<=2-HEIGHT[i]],
    NOOVERLAP -> Foreach[{i,5},{j,i+1,5}, X[i]>=X[j]+WIDTH[j] | X[i]+WIDTH[i]<=X[j] | Y[i]>=Y[j]+DEPTH[j] | Y[i]+DEPTH[i]<=Y[j] | Z[i]>=Z[j]+HEIGHT[j] | Z[i]+HEIGHT[i]<=Z[j]]
  ]
]
 
Next step is to run the model and review the result
 
Log
 
 
Log
 
[3/2/2014 3:44:34 PM] Solve started...
[3/2/2014 3:44:35 PM] Excel: 00:00:00.2532957
[3/2/2014 3:44:35 PM] ===Solver Foundation Service Report===
Date: 3/2/2014 3:44:35 PM
Version: Microsoft Solver Foundation 3.0.2.10889 Express Edition
Model Name: DefaultModel
Capabilities Applied: CP
Solve Time (ms): 54
Total Time (ms): 244
Solve Completion Status: Feasible
Solver Selected: Microsoft.SolverFoundation.Solvers.ConstraintSystem
Algorithm: TreeSearch
Variable Selection: DomainOverWeightedDegree
Value Selection: ForwardOrder
Move Selection: Any
Backtrack Count: 187
===Solution Details===
Goals:
 
[3/2/2014 3:44:35 PM] Solve Complete
 
The system successfully found a feasible solution for 3D Truck load optimization task and presented the results as shown below
 
Result
 
 
Now as we've got the results it would be nice to visualize them in a meaningful way
In order to do that with minimum effort I'm going to use VB.NET to programmatically render 3D Truck load in Excel using standard shapes (specifically cube(s))
 
Let's get going with VB.NET Macro now
 
VB.NET
 
 
I'll call my Macro GenerateData
 
Macro
 
 
Below I'll provide an example of how to visualize a sample Truck load optimization results
 
Microsoft Visual Basic for Applications
 
 
In the code below I use msoShapeCube to visually render Truck load in Excel. Please note that the idea here is to represent each package with a collection of building blocks – cubes, I also use the same color for building blocks of the same package for consistency 
 
Below I provide a number of implementations of visualization moving from the very simple one to more and more sophisticated when I add groupings, introduce colors and more
 
Source code (1)
 
Sub GenerateData()
Set myDocument = Worksheets(1)
With myDocument.Shapes
    .AddShape(msoShapeCube, 150, 150, 100, 100).Name = "shp11"
    .AddShape(msoShapeCube, 225, 150, 100, 100).Name = "shp12"
    .AddShape(msoShapeCube, 300, 150, 100, 100).Name = "shp13"
    .AddShape(msoShapeCube, 375, 150, 100, 100).Name = "shp14"
    .AddShape(msoShapeCube, 125, 175, 100, 100).Name = "shp21"
    .AddShape(msoShapeCube, 200, 175, 100, 100).Name = "shp22"
    .AddShape(msoShapeCube, 275, 175, 100, 100).Name = "shp23"
    .AddShape(msoShapeCube, 350, 175, 100, 100).Name = "shp24"
    .AddShape(msoShapeCube, 100, 200, 100, 100).Name = "shp31"
    .AddShape(msoShapeCube, 175, 200, 100, 100).Name = "shp32"
    .AddShape(msoShapeCube, 250, 200, 100, 100).Name = "shp33"
    .AddShape(msoShapeCube, 325, 200, 100, 100).Name = "shp34"
    .Range(Array("shp11", "shp12", "shp13", "shp14", "shp21", "shp22", "shp23", "shp24", "shp31", "shp32", "shp33", "shp34")).Group
End With
End Sub
 
Source code (2)
 
Sub GenerateData()
Set myDocument = Worksheets(1)
With myDocument.Shapes
    .AddShape(msoShapeCube, 150, 150, 100, 100).Name = "shp111"
    .AddShape(msoShapeCube, 225, 150, 100, 100).Name = "shp112"
    .AddShape(msoShapeCube, 300, 150, 100, 100).Name = "shp113"
    .AddShape(msoShapeCube, 375, 150, 100, 100).Name = "shp114"
    .AddShape(msoShapeCube, 125, 175, 100, 100).Name = "shp121"
    .AddShape(msoShapeCube, 200, 175, 100, 100).Name = "shp122"
    .AddShape(msoShapeCube, 275, 175, 100, 100).Name = "shp123"
    .AddShape(msoShapeCube, 350, 175, 100, 100).Name = "shp124"
    .AddShape(msoShapeCube, 100, 200, 100, 100).Name = "shp131"
    .AddShape(msoShapeCube, 175, 200, 100, 100).Name = "shp132"
    .AddShape(msoShapeCube, 250, 200, 100, 100).Name = "shp133"
    .AddShape(msoShapeCube, 325, 200, 100, 100).Name = "shp134"
    .AddShape(msoShapeCube, 150, 75, 100, 100).Name = "shp211"
    .AddShape(msoShapeCube, 225, 75, 100, 100).Name = "shp212"
    .AddShape(msoShapeCube, 125, 100, 100, 100).Name = "shp221"
    .AddShape(msoShapeCube, 200, 100, 100, 100).Name = "shp222"
    .Range(Array("shp111", "shp112", "shp113", "shp114", "shp121", "shp122", "shp123", "shp124", "shp131", "shp132", "shp133", "shp134", "shp211", "shp212", "shp221", "shp222")).Group
End With
End Sub
 
Source code (3)
 
Sub GenerateData()
Set myDocument = Worksheets(1)
With myDocument.Shapes
    Set shp111 = .AddShape(msoShapeCube, 150, 150, 100, 100)
    shp111.Name = "shp111"
    shp111.Fill.ForeColor.RGB = vbGreen
    Set shp112 = .AddShape(msoShapeCube, 225, 150, 100, 100)
    shp112.Name = "shp112"
    shp112.Fill.ForeColor.RGB = vbGreen
    Set shp113 = .AddShape(msoShapeCube, 300, 150, 100, 100)
    shp113.Name = "shp113"
    shp113.Fill.ForeColor.RGB = vbGreen
    Set shp114 = .AddShape(msoShapeCube, 375, 150, 100, 100)
    shp114.Name = "shp114"
    shp114.Fill.ForeColor.RGB = vbGreen
    Set shp121 = .AddShape(msoShapeCube, 125, 175, 100, 100)
    shp121.Name = "shp121"
    shp121.Fill.ForeColor.RGB = vbGreen
    Set shp122 = .AddShape(msoShapeCube, 200, 175, 100, 100)
    shp122.Name = "shp122"
    shp122.Fill.ForeColor.RGB = vbGreen
    Set shp123 = .AddShape(msoShapeCube, 275, 175, 100, 100)
    shp123.Name = "shp123"
    shp123.Fill.ForeColor.RGB = vbGreen
    Set shp124 = .AddShape(msoShapeCube, 350, 175, 100, 100)
    shp124.Name = "shp124"
    shp124.Fill.ForeColor.RGB = vbGreen
    Set shp131 = .AddShape(msoShapeCube, 100, 200, 100, 100)
    shp131.Name = "shp131"
    shp131.Fill.ForeColor.RGB = vbMagenta
    Set shp132 = .AddShape(msoShapeCube, 175, 200, 100, 100)
    shp132.Name = "shp132"
    shp132.Fill.ForeColor.RGB = vbMagenta
    Set shp133 = .AddShape(msoShapeCube, 250, 200, 100, 100)
    shp133.Name = "shp133"
    shp133.Fill.ForeColor.RGB = vbBlue
    Set shp134 = .AddShape(msoShapeCube, 325, 200, 100, 100)
   shp134.Name = "shp134"
    shp134.Fill.ForeColor.RGB = vBlue
    Set shp211 = .AddShape(msoShapeCube, 150, 75, 100, 100)
    shp211.Name = "shp211"
    shp211.Fill.ForeColor.RGB = vbRed
    Set shp212 = .AddShape(msoShapeCube, 225, 75, 100, 100)
    shp212.Name = "shp212"
    shp212.Fill.ForeColor.RGB = vbRed
    Set shp221 = .AddShape(msoShapeCube, 125, 100, 100, 100)
    shp221.Name = "shp221"
    shp221.Fill.ForeColor.RGB = vbRed
    Set shp222 = .AddShape(msoShapeCube, 200, 100, 100, 100)
    shp222.Name = "shp222"
    shp222.Fill.ForeColor.RGB = vbRed
    .Range(Array("shp111", "shp112", "shp113", "shp114", "shp121", "shp122", "shp123", "shp124", "shp131", "shp132", "shp133", "shp134", "shp211", "shp212", "shp221", "shp222")).Group
End With
End Sub
 
And here is the extract from the code above which highlights essential API methods used to implement this visualization
 
Sub GenerateData()
Set myDocument = Worksheets(1)
With myDocument.Shapes
    Set shpGreen = .AddShape(msoShapeCube, 150, 150, 400, 100)
    shpGreen.Name = "shpGreen"
    shpGreen.Fill.ForeColor.RGB = vbGreen
    Set shpMagenta = .AddShape(msoShapeCube, 100, 200, 200, 100)
    shpMagenta.Name = "shpMagenta"
    shpMagenta.Fill.ForeColor.RGB = vbMagenta
    Set shpBlue = .AddShape(msoShapeCube, 250, 200, 200, 100)
    shpBlue.Name = "shpBlue"
    shpBlue.Fill.ForeColor.RGB = vbBlue
    Set shpRed = .AddShape(msoShapeCube, 150, 75, 200, 100)
    shpRed.Name = "shpRed"
    shpRed.Fill.ForeColor.RGB = vbRed
    .Range(Array("shpGreen", "shpMagenta", "shpBlue", "shpRed")).Group
End With
End Sub
 
When we implement visualization piece the next step is to embed this Macro into Excel Workbook
 
In order to save Excel workbook with VB.NET Macros you will have to Save Excel file as Excel Macro-Enabled Workbook
 
 
Now it is time to see the results of visualization in Excel
Here's how wire frame of Truck load would look like
 
 
And when we add colors and grouping here's how generated outcome will look like
 
 
Please note that I implemented 3D view in Excel by using simple standard Excel shapes (cube(s)) and I achieved 3D placement effect by working with 2D coordinates (X and Y) of those shapes 
If necessary we can also add animation in Excel to dynamically play how Truck gets loaded with packages (in which sequence do they go into a truck, where do they go precisely inside a truck, etc.)
It may also make sense to introduce 2D projections of Truck body looking from different angles (side, top, bottom, etc.) for more detailed analysis
 
Result
<![if !supportLists]>-        <![endif]>Complete 3D
 
 
<![if !supportLists]>-        <![endif]>Step by Step 3D Animation
Step 1
 
 
Step 2
 
 
Step 3
 
 
Step 4
 
<![if !supportLists]>-        <![endif]>6 x 2D Projections
Front
 
 
Side Left
 
 
Side Right
 
 
Back
 
 
Top
 
 
Bottom
 
 
In my future walkthroughs I plan to further address Truck load visualization problem by explaining how to develop Office App (HTML5/JavaScript) for Excel in order to deliver animation, rotation, drag-n-drop and other exciting interactive capabilities 
 
Summary: In this document I illustrated how to use Microsoft Solver Foundation in order to resolve Truck load optimization problem. In particular in order to resolve this problem I used Microsoft Solver Foundation and Microsoft Excel providing robust platform for solving complex mathematical problems and rich visualization means to analyze the results of optimization. Please note that the data for initial analysis can be extracted from Microsoft Dynamics AX 2012 by means of Web Services. For visualization of results I used standard Excel shapes and developed VB.NET Macro to render Truck load optimization results straight inside Excel. It is important to mention that building models in Microsoft Solver Foundation Add-in for Excel doesn't require strong development skills, thus Business Analysts and Power Users may be very proficient in doing various optimizations using familiar Excel user interface. Please find more info about Modeling in Excel here: http://msdn.microsoft.com/en-us/library/ff524510(v=vs.93).aspx
      
Note: This document is intended for information purposes only, presented as it is with no warranties from the author. This document may be updated with more content to better outline the concepts and describe the examples.
 
Tags: Dynamics ERP, Dynamics AX 2012, Truck Load Optimization, Squaring the square, Cubing the cube, Solver Foundation, Excel, VB.NET, Macro, Web Services, Distribution, Retail, Transportation Management, Loads
 
Author: Alex Anikiev, PhD, MCP