Skip to content

Commit

Permalink
preliminary rework of constraints
Browse files Browse the repository at this point in the history
  • Loading branch information
mlubin committed Jun 23, 2013
1 parent c1b939b commit a378142
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 58 deletions.
57 changes: 46 additions & 11 deletions src/MathProg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ type Model
objIsQuad
objSense::Symbol

constraints
linconstr#::Vector{LinearConstraint}

# Column data
numCols::Int
Expand All @@ -82,7 +82,7 @@ function Model(sense::Symbol)
if (sense != :Max && sense != :Min)
error("Model sense must be :Max or :Min")
end
Model(AffExpr(),0,false,sense,Array(Constraint,0),
Model(AffExpr(),0,false,sense,LinearConstraint[],
0,String[],Float64[],Float64[],Int[],0,Float64[],nothing,Dict())
end

Expand Down Expand Up @@ -227,19 +227,54 @@ function quadToStr(q::QuadExpr)
end

##########################################################################
# Constraint class
# Basically an affine expression with a sense and one side.
type Constraint
lhs::AffExpr
sense::String
# LinearConstraint class
# An affine expression with lower bound (possibly -Inf) and upper bound (possibly Inf).
type LinearConstraint
terms::AffExpr
lb::Float64
ub::Float64
end

addConstraint(m::Model, c::Constraint) = push!(m.constraints,c)
addConstraint(m::Model, c::LinearConstraint) = push!(m.linconstr,c)

print(io::IO, c::LinearConstraint) = print(io, conToStr(c))
show(io::IO, c::LinearConstraint) = print(io, conToStr(c))

function sense(c::LinearConstraint)
if c.lb != -Inf
if c.ub != Inf
if c.ub == c.lb
return :(==)
else
return :range
end
else
return :>=
end
else
@assert c.ub != Inf
return :<=
end
end

print(io::IO, c::Constraint) = print(io, conToStr(c))
show(io::IO, c::Constraint) = print(io, conToStr(c))
function rhs(c::LinearConstraint)
s = sense(c)
@assert s != :range
if s == :<=
return c.ub
else
return c.lb
end
end

conToStr(c::Constraint) = string(affToStr(c.lhs,false)," ",c.sense," ",-c.lhs.constant)
function conToStr(c::LinearConstraint)
s = sense(c)
if s == :range
return string(c.lb," <= ",affToStr(c.terms,false)," <= ",c.ub)
else
return string(affToStr(c.terms,false)," ",s," ",rhs(c))
end
end

##########################################################################
# Operator overloads
Expand Down
2 changes: 1 addition & 1 deletion src/macros.jl
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ macro addConstraint(m, x)
esc(quote
$aff = AffExpr()
$(parseExpr(lhs, aff, 1.0))
addConstraint($m, Constraint($aff, $(string(x.args[2]))))
addConstraint($m, $(x.args[2])($aff,0) )
end)
end

Expand Down
21 changes: 13 additions & 8 deletions src/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -161,17 +161,22 @@ end
(*)(q1::QuadExpr, q2::QuadExpr) = error("Cannot multiply two quadratic expressions")
(/)(q1::QuadExpr, q2::QuadExpr) = error("Cannot divide a quadratic expression by a quadratic expression")

# Constraint
# Constraint--Number
# LinearConstraint
# LinearConstraint--Number
function (<=)(lhs::AffExpr, rhs::Number)
lhs.constant -= rhs #TODO: this is temporary, need to restructure
return Constraint(lhs,"<=")
rhs -= lhs.constant
lhs.constant = 0
return LinearConstraint(lhs,-Inf,rhs)
end
function (==)(lhs::AffExpr, rhs::Number)
lhs.constant -= rhs
return Constraint(lhs,"==")
rhs -= lhs.constant
lhs.constant = 0
return LinearConstraint(lhs,rhs,rhs)
end
function (>=)(lhs::AffExpr, rhs::Number)
return Constraint(lhs-rhs,">=")
rhs -= lhs.constant
lhs.constant = 0
return LinearConstraint(lhs,rhs,Inf)
end

# There's no easy way to allow operator overloads for range constraints.
# Use macros instead.
18 changes: 6 additions & 12 deletions src/solvers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,12 @@ function prepProblem(m::Model)
end

# Create row bounds
numRows = length(m.constraints)
numRows = length(m.linconstr)
rowlb = fill(-Inf, numRows)
rowub = fill(+Inf, numRows)
for c in 1:numRows
if m.constraints[c].sense == "<="
rowub[c] = -m.constraints[c].lhs.constant
elseif m.constraints[c].sense == ">="
rowlb[c] = -m.constraints[c].lhs.constant
else
rowub[c] = -m.constraints[c].lhs.constant
rowlb[c] = -m.constraints[c].lhs.constant
end
rowlb[c] = m.linconstr[c].lb
rowub[c] = m.linconstr[c].ub
end

# Create sparse A matrix
Expand All @@ -50,7 +44,7 @@ function prepProblem(m::Model)
rowptr = Array(Int,numRows+1)
nnz = 0
for c in 1:numRows
nnz += length(m.constraints[c].lhs.coeffs)
nnz += length(m.linconstr[c].terms.coeffs)
end
colval = Array(Int,nnz)
rownzval = Array(Float64,nnz)
Expand All @@ -62,8 +56,8 @@ function prepProblem(m::Model)
tmpnzidx = tmprow.nzidx
for c in 1:numRows
rowptr[c] = nnz + 1
coeffs = m.constraints[c].lhs.coeffs
vars = m.constraints[c].lhs.vars
coeffs = m.linconstr[c].terms.coeffs
vars = m.linconstr[c].terms.vars
# collect duplicates
for ind in 1:length(coeffs)
addelt(tmprow,vars[ind].col,coeffs[ind])
Expand Down
45 changes: 26 additions & 19 deletions src/writers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,22 @@ function writeMPS(m::Model, fname::String)

write(f,"NAME MathProgModel\n")

numRows = length(m.constraints)
numRows = length(m.linconstr)

# Objective and constraint names
gc_disable()
write(f,"ROWS\n")
write(f," N CON$(numRows+1)\n")
for c in 1:numRows
senseChar = 'L'
if m.constraints[c].sense == "=="
rowsense = sense(m.linconstr[c])
if rowsense == :(<=)
senseChar = 'L'
elseif rowsense == :(==)
senseChar = 'E'
elseif m.constraints[c].sense == ">="
elseif rowsense == :(>=)
senseChar = 'G'
else
error("Range rows not yet supported in MPS output")
end
@printf(f," %c CON%d\n",senseChar,c)
end
Expand All @@ -25,7 +29,7 @@ function writeMPS(m::Model, fname::String)
rowptr = Array(Int,numRows+2)
nnz = 0
for c in 1:numRows
nnz += length(m.constraints[c].lhs.coeffs)
nnz += length(m.linconstr[c].terms.coeffs)
end
objaff::AffExpr = (m.objIsQuad) ? m.objective.aff : m.objective
nnz += length(objaff.coeffs)
Expand All @@ -35,9 +39,9 @@ function writeMPS(m::Model, fname::String)
for c in 1:numRows
rowptr[c] = nnz + 1
# TODO: type assertion shouldn't be necessary
constr::Constraint = m.constraints[c]
coeffs = constr.lhs.coeffs
vars = constr.lhs.vars
constr::LinearConstraint = m.linconstr[c]
coeffs = constr.terms.coeffs
vars = constr.terms.vars
for ind in 1:length(coeffs)
nnz += 1
colval[nnz] = vars[ind].col
Expand Down Expand Up @@ -84,7 +88,7 @@ function writeMPS(m::Model, fname::String)
gc_disable()
write(f,"RHS\n")
for c in 1:numRows
@printf(f," rhs CON%d %f\n",c,-m.constraints[c].lhs.constant)
@printf(f," rhs CON%d %f\n",c,rhs(m.linconstr[c]))
end
gc_enable()

Expand Down Expand Up @@ -167,25 +171,28 @@ function writeLP(m::Model, fname::String)

# Constraints
write(f,"Subject To\n")
for i in 1:length(m.constraints)
for i in 1:length(m.linconstr)
@printf(f, " c%d: ", i)

c::Constraint = m.constraints[i]
nnz = length(c.lhs.coeffs)
c::LinearConstraint = m.linconstr[i]
nnz = length(c.terms.coeffs)
for ind in 1:(nnz-1)
@printf(f, "%f VAR%d + ", c.lhs.coeffs[ind], c.lhs.vars[ind].col)
@printf(f, "%f VAR%d + ", c.terms.coeffs[ind], c.terms.vars[ind].col)
end
if nnz >= 1
@printf(f, "%f VAR%d", c.lhs.coeffs[nnz], c.lhs.vars[nnz].col)
@printf(f, "%f VAR%d", c.terms.coeffs[nnz], c.terms.vars[nnz].col)
end

# Sense and RHS
if c.sense == "=="
@printf(f, " = %f\n", -c.lhs.constant)
elseif c.sense == "<="
@printf(f, " <= %f\n", -c.lhs.constant)
rowsense = sense(c)
if rowsense == :(==)
@printf(f, " = %f\n", rhs(c))
elseif rowsense == :<=
@printf(f, " <= %f\n", rhs(c))
elseif rowsense == :>=
@printf(f, " >= %f\n", rhs(c))
else
@printf(f, " >= %f\n", -c.lhs.constant)
error("Range rows not yet supported in MPS output")
end
end

Expand Down
10 changes: 5 additions & 5 deletions test/macros.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ let
@defVar(m, z)

@addConstraint(m, 3x - y == 3.3(w + 2z) + 5)
@test conToStr(m.constraints[end]) == "3.0 x + -1.0 y + -3.3 w + -6.6 z == 5.0"
@test conToStr(m.linconstr[end]) == "3.0 x + -1.0 y + -3.3 w + -6.6 z == 5.0"
@addConstraint(m, (x+y)/2 == 1)
@test conToStr(m.constraints[end]) == "0.5 x + 0.5 y == 1.0"
@test conToStr(m.linconstr[end]) == "0.5 x + 0.5 y == 1.0"
end

let
Expand All @@ -43,10 +43,10 @@ let
@defVar(m, y)
C = [1 2 3; 4 5 6; 7 8 9]
@addConstraint(m, sum{ C[i,j]*x[i,j], i = 1:2, j = 2:3 } <= 1)
@test conToStr(m.constraints[end]) == "2.0 _col2 + 3.0 _col3 + 5.0 _col5 + 6.0 _col6 <= 1.0"
@test conToStr(m.linconstr[end]) == "2.0 _col2 + 3.0 _col3 + 5.0 _col5 + 6.0 _col6 <= 1.0"
@addConstraint(m, sum{ C[i,j]*x[i,j], i = 1:3, j = 1:3; i != j} == y)
@test conToStr(m.constraints[end]) == "2.0 _col2 + 3.0 _col3 + 4.0 _col4 + 6.0 _col6 + 7.0 _col7 + 8.0 _col8 + -1.0 y == -0.0"
@test conToStr(m.linconstr[end]) == "2.0 _col2 + 3.0 _col3 + 4.0 _col4 + 6.0 _col6 + 7.0 _col7 + 8.0 _col8 + -1.0 y == 0.0"

@addConstraint(m, sum{ C[i,j]*x[i,j], i = 1:3, j = 1:i} == 0);
@test conToStr(m.constraints[end]) == "1.0 _col1 + 4.0 _col4 + 5.0 _col5 + 7.0 _col7 + 8.0 _col8 + 9.0 _col9 == -0.0"
@test conToStr(m.linconstr[end]) == "1.0 _col1 + 4.0 _col4 + 5.0 _col5 + 7.0 _col7 + 8.0 _col8 + 9.0 _col9 == 0.0"
end
4 changes: 2 additions & 2 deletions test/model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ modALP = ASCIIString[
"obj: 0.166667 VAR1 + 0.166667 VAR2 + 1.000000 VAR3 + 1.000000 VAR6",
"Subject To",
"c1: 1.000000 VAR6 + 1.000000 VAR7 + 1.000000 VAR8 + 0.500000 VAR1 <= 1.000000",
"c2: 7.000000 VAR2 + -1.000000 VAR3 + -1.000000 VAR12 <= -0.000000",
"c2: 7.000000 VAR2 + -1.000000 VAR3 + -1.000000 VAR12 <= 0.000000",
"Bounds",
"0.000000 <= VAR1 <= +inf",
"-inf <= VAR2 <= 5.500000",
Expand Down Expand Up @@ -75,7 +75,7 @@ modAMPS = ASCIIString[
" VAR12 CON2 -1.000000",
"RHS",
" rhs CON1 1.000000",
" rhs CON2 -0.000000",
" rhs CON2 0.000000",
"BOUNDS",
" MI BOUND VAR2",
" UP BOUND VAR2 5.500000",
Expand Down

0 comments on commit a378142

Please sign in to comment.