Programaciíon AMPL - GUI
Programaciíon AMPL - GUI
+ Application Programming
= Streamlined Deployment in AMPL
Assign
each employee to a project group
So that
the groups have about the same size
the groups are as “diverse” as possible with respect to all categories
Method-Based Approach
Define an algorithm to build a balanced assignment
Start with all groups empty
Make a list of people (employees)
For each person in the list:
Add to the group whose resulting “sameness” will be least
Model-Based Approach
Formulate a “minimal sameness” model
Define decision variables for assignment of people to groups
𝑥 1 if person 1 assigned to group 𝑗
𝑥 0 otherwise
Specify valid assignments through constraints on the variables
Formulate sameness as an objective to be minimized
Total sameness = sum of the sameness of all groups
Model-Based Formulation
Given
𝑃 set of people
𝐶 set of categories of people
𝑡 type of person 𝑖 within category 𝑘, for all 𝑖 ∈ 𝑃, 𝑘 ∈ 𝐶
and
𝐺 number of groups
𝑔 lower limit on people in a group
𝑔 upper limit on people in a group
Define
𝑠 | 𝑘 ∈ 𝐶: 𝑡 𝑡 |, for all 𝑖 ∈ 𝑃, 𝑖 ∈ 𝑃
sameness of persons 𝑖 and 𝑖
Subject to
∑ 𝑥 1, for each 𝑖 ∈ 𝑃
each person must be assigned to one group
𝑔 ∑∈ 𝑥 𝑔 , for each 𝑗 1, . . . , 𝐺
each group must be assigned an acceptable number of people
Model-Based Solution
Optimize with an off-the-shelf solver
Choose among many alternatives
Linearize and send to a mixed-integer linear solver
CPLEX, Gurobi, Xpress; CBC, MIPCL, SCIP
Send quadratic formulation to a mixed-integer solver
that automatically linearizes products involving binary variables
CPLEX, Gurobi, Xpress
Send quadratic formulation to a nonlinear solver
Mixed-integer nonlinear: Knitro, BARON
Continuous nonlinear (might come out integer): MINOS, Ipopt, . . .
Method-Based (cont’d)
Revise or replace the solution approach
Total variation is less suitable to a greedy algorithm
Total variation is harder to locally improve
Client constraints are challenging to enforce
Model-Based (cont’d)
To express client requirement for women in a group, let
𝑄 𝑖 ∈ 𝑃: 𝑡 , ⁄ female
Add constraints
∑∈ 𝑥 0 or ∑ ∈ 𝑥 2, for each 𝑗 1, . . . , 𝐺
Model-Based (cont’d)
To express client requirement for women in a group, let
𝑄 𝑖 ∈ 𝑃: 𝑡 , ⁄ female
Define logic variables
𝑧 ∈ 0,1 1 if any women assigned to group 𝑗
0 otherwise, for all 𝑗 1, . . . , 𝐺
Add constraints relating
logic variables to assignment variables
𝑧 0 ⇒ ∑∈ 𝑥 0,
𝑧 1 ⇒ ∑∈ 𝑥 2 , for each 𝑗 1, . . . , 𝐺
Model-Based (cont’d)
To express client requirement for women in a group, let
𝑄 𝑖 ∈ 𝑃: 𝑡 , ⁄ female
Define logic variables
𝑧 ∈ 0,1 1 if any women assigned to group 𝑗
0 otherwise, for all 𝑗 1, . . . , 𝐺
Linearize constraints relating
logic variables to assignment variables
2𝑧 ∑∈ 𝑥 𝑄 𝑧 , for each 𝑗 1, . . . , 𝐺
Design approaches
Matrix generators vs. modeling languages
Declarative vs. executable modeling languages
Build Model
Prepare Data
Limited drawbacks
Separate language to be learned
Overhead in translation to algorithm’s form
Confidential formulation to be protected
Great advantages
Faster modeling cycles
More reliable modeling
More maintainable applications
Design goals
Powerful, general expressions
Natural, easy-to-learn modeling principles
Efficient processing that scales well with problem size
Declarative:
Specialized modeling language with multi-solver support
http://ampl.com
Executable
Concept
Create an algebraic modeling language
inside a general-purpose programming language
Redefine operators like + and <=
to return constraint objects rather than simple values
Advantages
Ready integration with applications
Good access to advanced solver features
Disadvantages
Programming issues complicate description of the model
Modeling and programming bugs are hard to separate
Efficiency issues are more of a concern
Declarative
Concept
Design a language specifically for optimization modeling
Resembles mathematical notation as much as possible
Extend to command scripts and database links
Connect to external applications via APIs
Disadvantages
Adds a system between application and solver
Does not have a full object-oriented programming framework
Advantages
Streamlines model development
Promotes validation and maintenance of models
Can provide APIs for many popular programming languages
Determine
how much to ship over each arc
So that
demands are met by the supplies
shipping costs are minimized
Data
gurobipy AMPL
Assign values to Python Define symbolic model
lists and dictionaries sets and parameters
commodities = ['Pencils', 'Pens'] set COMMODITIES;
set NODES;
nodes = ['Detroit', 'Denver',
'Boston', 'New York', 'Seattle'] set ARCS within {NODES,NODES};
param capacity {ARCS} >= 0;
arcs, capacity = multidict({
('Detroit', 'Boston'): 100,
('Detroit', 'New York’): 80,
set COMMODITIES := Pencils Pens ;
('Detroit', 'Seattle’): 120,
('Denver', 'Boston'): 120, set NODES := Detroit Denver
('Denver', 'New York'): 120, Boston 'New York' Seattle ;
('Denver', 'Seattle’): 120 })
param: ARCS: capacity:
Boston 'New York' Seattle :=
Detroit 100 80 120
Provide data later Denver 120 120 120 ;
in a separate file
Robert Fourer, Streamlined Deployment in AMPL
INFORMS Analytics, Austin — 14-16 April 2019 — Technology Tutorials
45
Comparison
Data (cont’d)
gurobipy AMPL
inflow = { param inflow {COMMODITIES,NODES};
('Pencils', 'Detroit'): 50,
('Pencils', 'Denver'): 60,
('Pencils', 'Boston'): -50, param inflow (tr):
('Pencils', 'New York'): -50, Pencils Pens :=
('Pencils', 'Seattle'): -10, Detroit 50 60
('Pens', 'Detroit'): 60, Denver 60 40
('Pens', 'Denver'): 40, Boston -50 -40
('Pens', 'Boston'): -40, 'New York' -50 -30
('Pens', 'New York'): -30, Seattle -10 -30 ;
('Pens', 'Seattle'): -30 }
Data (cont’d)
gurobipy
cost = {
('Pencils', 'Detroit', 'Boston'): 10,
('Pencils', 'Detroit', 'New York'): 20,
('Pencils', 'Detroit', 'Seattle'): 60,
('Pencils', 'Denver', 'Boston'): 40,
('Pencils', 'Denver', 'New York'): 40,
('Pencils', 'Denver', 'Seattle'): 30,
('Pens', 'Detroit', 'Boston'): 20,
('Pens', 'Detroit', 'New York'): 20,
('Pens', 'Detroit', 'Seattle'): 80,
('Pens', 'Denver', 'Boston'): 60,
('Pens', 'Denver', 'New York'): 70,
('Pens', 'Denver', 'Seattle'): 30 }
Data (cont’d)
AMPL
param cost {COMMODITIES,ARCS} >= 0;
param cost
[Pencils,*,*] (tr) Detroit Denver :=
Boston 10 40
'New York' 20 40
Seattle 60 30
[Pens,*,*] (tr) Detroit Denver :=
Boston 20 60
'New York' 20 70
Seattle 80 30 ;
Model
gurobipy
m = Model('netflow')
flow = m.addVars(commodities, arcs, obj=cost, name="flow")
m.addConstrs(
(flow.sum('*',i,j) <= capacity[i,j] for i,j in arcs), "cap")
m.addConstrs(
(flow.sum(h,'*',j) + inflow[h,j] == flow.sum(h,j,'*')
for h in commodities for j in nodes), "node")
"cap[%s,%s]" % (i,j))
m.addConstrs(
(quicksum(flow[h,i,j] for i,j in arcs.select('*',j)) + inflow[h,j] ==
quicksum(flow[h,j,k] for j,k in arcs.select(j,'*'))
for h in commodities for j in nodes), "node")
(Note on Summations)
gurobipy quicksum
m.addConstrs(
(quicksum(flow[h,i,j] for i,j in arcs.select('*',j)) + inflow[h,j] ==
quicksum(flow[h,j,k] for j,k in arcs.select(j,'*'))
for h in commodities for j in nodes), "node")
Model (cont’d)
AMPL
var Flow {COMMODITIES,ARCS} >= 0;
minimize TotalCost:
sum {h in COMMODITIES, (i,j) in ARCS} cost[h,i,j] * Flow[h,i,j];
subject to Capacity {(i,j) in ARCS}:
sum {h in COMMODITIES} Flow[h,i,j] <= capacity[i,j];
subject to Conservation {h in COMMODITIES, j in NODES}:
sum {(i,j) in ARCS} Flow[h,i,j] + inflow[h,j] =
sum {(j,i) in ARCS} Flow[h,j,i];
Solution
gurobipy
m.optimize()
if m.status == GRB.Status.OPTIMAL:
solution = m.getAttr('x', flow)
for h in commodities:
print('\nOptimal flows for %s:' % h)
for i,j in arcs:
if solution[h,i,j] > 0:
print('%s -> %s: %g' % (i, j, solution[h,i,j]))
Solution (cont’d)
AMPL
ampl: solve;
Gurobi 8.1.0: optimal solution; objective 5500
2 simplex iterations
ampl: display Flow;
Flow [Pencils,*,*]
: Boston 'New York' Seattle :=
Denver 0 50 10
Detroit 50 0 0
[Pens,*,*]
: Boston 'New York' Seattle :=
Denver 10 0 30
Detroit 30 30 0
;
Where
𝑦 ∑∈ : 𝑥 , for each 𝑗 1, . . . , 𝐺; 𝑘 ∈ 𝐶, 𝑙 ∈ 𝑇
𝑦 ∑∈ : 𝑥 , for each 𝑗 1, . . . , 𝐺; 𝑘 ∈ 𝐶, 𝑙 ∈ 𝑇
𝑦 ∑∈ : 𝑥 , for each 𝑗 1, . . . , 𝐺; 𝑘 ∈ 𝐶, 𝑙 ∈ 𝑇
Robert Fourer, Streamlined Deployment in AMPL
INFORMS Analytics, Austin — 14-16 April 2019 — Technology Tutorials
59
Balanced Assignment
Minimize
∑ ∈ ∑∈ 𝑦 𝑦 )
sum of inter-group variation over all types in all categories
Subject to
∑ 𝑥 1, for each 𝑖 ∈ 𝑃
each person must be assigned to one group
𝑔 ∑∈ 𝑥 𝑔 , for each 𝑗 1, . . . , 𝐺
each group must be assigned an acceptable number of people
𝑔 ∑∈ 𝑥 𝑔 , for each 𝑗 1, . . . , 𝐺
2𝑧 ∑∈ 𝑥 𝑄 𝑧 , for each 𝑗 1, . . . , 𝐺
Determine
the number of times to cut each pattern
So that
demands are met (or slightly exceeded)
raw rolls cut and wasted material are minimized
Mathematical Formulation
Given
𝑤 width of “raw” rolls
𝑊 set of (smaller) ordered widths
𝑛 number of cutting patterns considered
and
𝑎 occurrences of width 𝑖 in pattern 𝑗,
for each 𝑖 ∈ 𝑊 and 𝑗 1, . . . , 𝑛
𝑏 orders for width 𝑖, for each 𝑖 ∈ 𝑊
𝑜 limit on overruns
AMPL Formulation
Symbolic model
param rawWidth;
set WIDTHS;
param nPatterns integer > 0;
set PATTERNS = 1..nPatterns;
param rolls {WIDTHS,PATTERNS} >= 0, default 0;
param order {WIDTHS} >= 0;
param overrun;
𝑏 ∑ 𝑎 𝑋 𝑏 𝑜
minimize TotalCut:
sum {p in PATTERNS} Cut[p];
minimize TotalWaste:
sum {p in PATTERNS}
Cut[p] * (rawWidth - sum {w in WIDTHS} w * rolls[w,p]);
AMPL APIs
Principles
APIs for “all” popular languages
C++, C#, Java, MATLAB, Python, R
Common overall design
Common implementation core in C++
Customizations for each language and its data structures
var Cut {PATTERNS} integer >= 0; # raw rolls to cut in each pattern
Some R Data
A float, an integer, and a dataframe
roll_width <- 64.5
overrun <- 6
orders <- data.frame(
width = c( 6.77, 7.56, 17.46, 18.76 ),
demand = c( 10, 40, 33, 10 )
)
# Enumerate patterns
widths = list(sorted(orders.keys(), reverse=True))
patmat = patternEnum(roll_width, widths)
# Set up model
ampl = AMPL()
ampl.option['ampl_include'] = 'models'
ampl.read('cut.mod')
Pattern Enumeration in R
Load & generate data, set up AMPL model
cuttingEnum <- function(dataset) {
library(rAMPL)
# Enumerate patterns
patmat <- patternEnum(roll_width, orders$width)
cat(sprintf("\n%d patterns enumerated\n\n", ncol(patmat)))
# Set up model
ampl <- new(AMPL)
ampl$setOption("ampl_include", "models")
ampl$read("cut.mod")
Pattern Enumeration in R
Send data to AMPL
# Send scalar values
ampl$getParameter("nPatterns")$set(ncol(patmat))
ampl$getParameter("overrun")$set(overrun)
ampl$getParameter("rawWidth")$set(roll_width)
# Retrieve solution
CuttingPlan = ampl.var['Cut'].getValues()
cutvec = list(CuttingPlan.getColumn('Cut.val'))
Pattern Enumeration in R
Solve and get results
# Solve
ampl$setOption("solver", "gurobi")
ampl$solve()
# Retrieve solution
CuttingPlan <- ampl$getVariable("Cut")$getValues()
solution <- CuttingPlan[CuttingPlan[,-1] != 0,]
Pattern Enumeration in R
Display solution
# Prepare solution data
data <- dataset
obj <- ampl$getObjective("TotalRawRolls")$value()
waste <- ampl$getValue(
"sum {p in PATTERNS} Cut[p] * (rawWidth - sum {w in WIDTHS} w*rolls[w,p])"
)
summary <- list(data=dataset, obj=obj, waste=waste)
Pattern Enumeration in R
Enumeration routine
patternEnum <- function(roll_width, widths, prefix=c()) {
cur_width <- widths[length(prefix)+1]
max_rep <- floor(roll_width/cur_width)
if (length(prefix)+1 == length(widths)) {
return (c(prefix, max_rep))
} else {
patterns <- matrix(nrow=length(widths), ncol=0)
for (n in 0:max_rep) {
patterns <- cbind(
patterns,
patternEnum(roll_width-n*cur_width, widths, c(prefix, n))
)
}
return (patterns)
}
}
Pattern Enumeration in R
Plotting routine
cuttingPlot <- function(roll_width, widths, patmat, summary, solution) {
pal <- rainbow(length(widths))
par(mar=c(1,1,1,1))
par(mfrow=c(1,nrow(solution)))
for(i in 1:nrow(solution)) {
pattern <- patmat[, solution[i, 1]]
data <- c()
color <- c()}
Pattern Enumeration in R
Plotting routine (cont’d)
for(j in 1:length(pattern)) {
if(pattern[j] >= 1) {
for(k in 1:pattern[j]) {
data <- rbind(data, widths[j])
color <- c(color, pal[j])
}
}
}
label <- sprintf("x %d", solution[i, -1])
barplot(data, main=label, col=color,
border="white", space=0.04, axes=FALSE, ylim=c(0, roll_width))
}
print(summary)
}
Pattern Enumeration in R
ampl.set['WIDTHS'] = widths
ampl.param['order'] = orders
ampl.param['rolls'] = {
(widths[i], 1+p): patmat[p][i]
for i in range(len(widths))
for p in range(len(patmat))
}
Callbacks
Stopping rule data in Python dictionary
stopdict = { 'time' : ( 15, 30, 60 ), stopping.py
'gaptol' : ( .0002, .002, .02 )
}
Callbacks
Set up callback and solve final integer program
# Instead of Master.solve(), export to a gurobipy object
grb_model = Master.exportGurobiModel()
Callbacks
Callback function
def callback(m, where):
"""Gurobi callback function."""
if where == gpy.GRB.Callback.MIP:
runtime = m.cbGet(gpy.GRB.Callback.RUNTIME)
if runtime >= m._stoprule['time'][m._current]:
print("Reducing gap tolerance to %f at %d seconds" % \
(m._stoprule['gaptol'][m._current], m._stoprule['time'][m._current]))
m.Params.MIPGap = m._stoprule['gaptol'][m._current]
m._current += 1
ampl = AMPL(langext=PyMPL())
ampl.read('lotsize.mod')
ampl.solve()
Client side
Interactive tool for collaboration & decision-making
Runs on any recent web browser
Java-based implementation
AMPL API for Java
Eclipse Remote Application Platform
Explorer Viewer
Report tables Input tables
section 1 Charts
section 2 Water
Scenario
comparison
All variables can
be compared
Display of relative
difference
Custom reports