Skip to content

Commit

Permalink
Filter out explicit zeros in new rows/columns (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
mtanneau authored Jan 8, 2021
1 parent 5066f8b commit 8c2349b
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 53 deletions.
107 changes: 59 additions & 48 deletions src/problemData.jl
Original file line number Diff line number Diff line change
Expand Up @@ -70,27 +70,27 @@ end
import Base.empty!

function Base.empty!(pb::ProblemData{T}) where{T}

pb.name = ""

pb.ncon = 0
pb.nvar = 0

pb.objsense = true
pb.obj = T[]
pb.obj0 = zero(T)

pb.arows = Row{T}[]
pb.acols = Col{T}[]

pb.lcon = T[]
pb.ucon = T[]
pb.lvar = T[]
pb.uvar = T[]

pb.con_names = String[]
pb.var_names = String[]

return pb
end

Expand Down Expand Up @@ -146,40 +146,43 @@ function add_constraint!(pb::ProblemData{T},
"Cannot add a row with $nz indices but $(length(rval)) non-zeros"
))

# Go through through rval to check all coeffs are finite and remove zeros.
_rind = Vector{Int}(undef, nz)
_rval = Vector{T}(undef, nz)
_nz = 0
for (j, aij) in zip(rind, rval)
if !iszero(aij)
isfinite(aij) || error("Invalid row coefficient: $(aij)")
_nz += 1
_rind[_nz] = j
_rval[_nz] = aij
end
end
resize!(_rind, _nz)
resize!(_rval, _nz)

# TODO: combine dupplicate indices

# Increment row counter
pb.ncon += 1
push!(pb.con_names, name)
push!(pb.lcon, l)
push!(pb.ucon, u)
push!(pb.con_names, name)

if nz == 0
# emtpy row
push!(pb.arows, Row{T}(Int[], T[]))
return pb.ncon
end

# TODO: check coefficients values, e.g.
# * all coefficients are finite
# * remove hard zeros
# * combine dupplicate indices
# * check if indices are already issorted

# Create new row
if issorted
row = Row{T}(copy(rind), copy(rval))
row = Row{T}(_rind, _rval)
else
# Sort indices first
p = sortperm(rind)
row = Row{T}(rind[p], rval[p])
p = sortperm(_rind)
row = Row{T}(_rind[p], _rval[p])
end
push!(pb.arows, row)

# Update column coefficients
for (j, v) in zip(rind, rval)
if !iszero(v)
push!(pb.acols[j].nzind, pb.ncon)
push!(pb.acols[j].nzval, v)
end
for (j, aij) in zip(_rind, _rval)
push!(pb.acols[j].nzind, pb.ncon)
push!(pb.acols[j].nzval, aij)
end

# Done
Expand Down Expand Up @@ -213,36 +216,44 @@ function add_variable!(pb::ProblemData{T},
"Cannot add a column with $nz indices but $(length(cval)) non-zeros"
))

# Go through through cval to check all coeffs are finite and remove zeros.
_cind = Vector{Int}(undef, nz)
_cval = Vector{T}(undef, nz)
_nz = 0
for (j, aij) in zip(cind, cval)
if !iszero(aij)
isfinite(aij) || error("Invalid column coefficient: $(aij)")
_nz += 1
_cind[_nz] = j
_cval[_nz] = aij
end
end
resize!(_cind, _nz)
resize!(_cval, _nz)

# Increment column counter
pb.nvar += 1
push!(pb.var_names, name)
push!(pb.lvar, l)
push!(pb.uvar, u)
push!(pb.obj, obj)
push!(pb.var_names, name)

if nz == 0
push!(pb.acols, Col{T}(Int[], T[]))
return pb.nvar # empty column
end

# TODO: check coefficients
# TODO: combine dupplicate indices

# Create a new column
if issorted
col = Col{T}(copy(cind), copy(cval))
col = Col{T}(_cind, _cval)
else
# Sort indices
p = sortperm(cind)
col = Col{T}(cind[p], cind[p])
p = sortperm(_cind)
col = Col{T}(_cind[p], _cval[p])
end
push!(pb.acols, col)

# Update row coefficients
for (i, v) in zip(cind, cval)
if !iszero(v)
push!(pb.arows[i].nzind, pb.nvar)
push!(pb.arows[i].nzval, v)
end
for (i, aij) in zip(_cind, _cval)
push!(pb.arows[i].nzind, pb.nvar)
push!(pb.arows[i].nzval, aij)
end

# Done
Expand Down Expand Up @@ -322,7 +333,7 @@ function delete_constraint!(pb::ProblemData{T}, rind::Int) where{T}
deleteat!(pb.con_names, rind)
deleteat!(pb.lcon, rind)
deleteat!(pb.ucon, rind)

# Update columns
for (j, col) in enumerate(pb.acols)
# Search for row in that column
Expand All @@ -340,7 +351,7 @@ function delete_constraint!(pb::ProblemData{T}, rind::Int) where{T}
# Decrement subsequent row indices
col.nzind[rg.start:end] .-= 1
end
end
end

# Delete row
deleteat!(pb.arows, rind)
Expand All @@ -360,7 +371,7 @@ Delete rows in collection `rind` from problem `pb`.
* `rinds`: collection of row indices to be removed
"""
function delete_constraints!(pb::ProblemData{T}, rinds) where{T}
# TODO: don't use fallback
# TODO: don't use fallback
for i in rinds
delete_constraint!(pb, i)
end
Expand All @@ -381,7 +392,7 @@ function delete_variable!(pb::ProblemData{T}, cind::Int) where{T}
deleteat!(pb.obj, cind)
deleteat!(pb.lvar, cind)
deleteat!(pb.uvar, cind)

# Update rows
for (i, row) in enumerate(pb.arows)
# Search for column in that row
Expand Down Expand Up @@ -419,7 +430,7 @@ Delete a collection of columns from problem `pb`.
* `cinds`: collection of row indices to be removed
"""
function delete_variables!(pb::ProblemData{T}, cinds) where{T}
# TODO: don't use fallback
# TODO: don't use fallback
for j in cinds
delete_variable!(pb, j)
end
Expand Down Expand Up @@ -482,4 +493,4 @@ end
# Problem queries
# =============================

# TODO
# TODO
64 changes: 59 additions & 5 deletions test/Core/problemData.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function run_tests_pbdata(T::Type)
function run_tests_pbdata(::Type{T}) where{T}

@testset "Creation" begin
pb = TLP.ProblemData{T}("test")
Expand All @@ -17,7 +17,7 @@ function run_tests_pbdata(T::Type)
=#
TLP.add_variable!(pb, Int[], T[], one(T), zero(T), T(Inf), "x1")
TLP.add_variable!(pb, Int[], T[], 2 * one(T), one(T), T(Inf), "x2")

check_problem_size(pb, 0, 2)
col1, col2 = pb.acols[1], pb.acols[2]
@test pb.obj == [one(T), 2*one(T)]
Expand All @@ -37,7 +37,7 @@ function run_tests_pbdata(T::Type)
=#
TLP.add_constraint!(pb, [1, 2], T.([-1, 1]), T(-Inf), one(T), "row1")
TLP.add_constraint!(pb, [1, 2], T.([2, -2]), -one(T), zero(T), "row2")

# Check dimensions
check_problem_size(pb, 2, 2)

Expand Down Expand Up @@ -140,8 +140,62 @@ function check_problem_size(pb::TLP.ProblemData, ncon::Int, nvar::Int)
return nothing
end

function test_pbdata_checkcoeff(::Type{T}) where{T}

@testset "Zero in row" begin
pb = TLP.ProblemData{T}("test")

Tulip.add_variable!(pb, Int[], T[], T(1), zero(T), one(T), "x1")
Tulip.add_variable!(pb, Int[], T[], T(2), zero(T), one(T), "x2")
Tulip.add_variable!(pb, Int[], T[], T(3), zero(T), one(T), "x3")
Tulip.add_constraint!(pb, [1, 2, 3], T[1, 0, 0], zero(T), one(T), "c1")
Tulip.add_constraint!(pb, [1, 2, 3], T[0, 1, 2], zero(T), one(T), "c2")

@test length(pb.arows) == 2
@test pb.arows[1].nzind == [1]
@test pb.arows[1].nzval == T[1]
@test pb.arows[2].nzind == [2, 3]
@test pb.arows[2].nzval == T[1, 2]

@test length(pb.acols) == 3
@test pb.acols[1].nzind == [1]
@test pb.acols[1].nzval == T[1]
@test pb.acols[2].nzind == [2]
@test pb.acols[2].nzval == T[1]
@test pb.acols[3].nzind == [2]
@test pb.acols[3].nzval == T[2]
end

@testset "Zero in col" begin
pb = TLP.ProblemData{T}("test")

Tulip.add_constraint!(pb, Int[], T[], zero(T), one(T), "c1")
Tulip.add_constraint!(pb, Int[], T[], zero(T), one(T), "c2")
Tulip.add_variable!(pb, [1, 2], T[1, 0], T(1), zero(T), one(T), "x1")
Tulip.add_variable!(pb, [1, 2], T[0, 1], T(2), zero(T), one(T), "x2")
Tulip.add_variable!(pb, [1, 2], T[0, 2], T(3), zero(T), one(T), "x3")

@test length(pb.arows) == 2
@test pb.arows[1].nzind == [1]
@test pb.arows[1].nzval == T[1]
@test pb.arows[2].nzind == [2, 3]
@test pb.arows[2].nzval == T[1, 2]

@test length(pb.acols) == 3
@test pb.acols[1].nzind == [1]
@test pb.acols[1].nzval == T[1]
@test pb.acols[2].nzind == [2]
@test pb.acols[2].nzval == T[1]
@test pb.acols[3].nzind == [2]
@test pb.acols[3].nzval == T[2]
end
end

@testset "ProblemData" begin
for T in TvTYPES
@testset "$T" begin run_tests_pbdata(T) end
@testset "$T" begin
run_tests_pbdata(T)
test_pbdata_checkcoeff(T)
end
end
end
end

0 comments on commit 8c2349b

Please sign in to comment.