Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support of custom data values for JuMP model elements #816

Merged
merged 3 commits into from
Apr 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions src/MOIwrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@ mutable struct _VarInfo
index::MOI.VariableIndex
name::String
var::Variable
data::Union{Nothing, BlockDecomposition.AbstractCustomData}
end
_VarInfo(var::Variable) = _VarInfo(_NONE, _NONE, _CONT, MOI.VariableIndex(0), "", var)
_VarInfo(var::Variable) = _VarInfo(_NONE, _NONE, _CONT, MOI.VariableIndex(0), "", var, nothing)

mutable struct _ConstrInfo
name::String
index::Union{Nothing, MOI.ConstraintIndex}
constr::Constraint
data::Union{Nothing, BlockDecomposition.AbstractCustomData}
end
_ConstrInfo(constr::Constraint) = _ConstrInfo("", nothing, constr)
_ConstrInfo(constr::Constraint) = _ConstrInfo("", nothing, constr, nothing)

mutable struct Optimizer <: MOI.AbstractOptimizer
env::Env
Expand Down Expand Up @@ -166,6 +168,8 @@ MOI.supports(::Optimizer, ::MOI.ConstraintDualStart) = false
MOI.supports(::Optimizer, ::BlockDecomposition.ConstraintDecomposition) = true
MOI.supports(::Optimizer, ::BlockDecomposition.VariableDecomposition) = true
MOI.supports(::Optimizer, ::BlockDecomposition.RepresentativeVar) = true
MOI.supports(::Optimizer, ::BlockDecomposition.CustomVarValue) = true
MOI.supports(::Optimizer, ::BlockDecomposition.CustomConstrValue) = true

# Parameters
function MOI.set(model::Optimizer, param::MOI.RawOptimizerAttribute, val)
Expand Down Expand Up @@ -856,6 +860,15 @@ function MOI.set(
return
end

function MOI.set(
model::Optimizer, ::BD.CustomVarValue, varid::MOI.VariableIndex, custom_data
)
MOI.throw_if_not_valid(model, varid)
var = _info(model, varid).var
var.custom_data = custom_data
return
end

function MOI.get(model::Optimizer, ::BD.VarBranchingPriority, varid::MOI.VariableIndex)
var = _info(model, varid).var
return var.branching_priority
Expand Down Expand Up @@ -909,6 +922,16 @@ function MOI.set(
return
end

function MOI.set(
model::Optimizer, ::BlockDecomposition.CustomConstrValue, constrid::MOI.ConstraintIndex,
custom_data
)
MOI.throw_if_not_valid(model, constrid)
constr = _info(model, constrid).constr
constr.custom_data = custom_data
return
end

function MOI.get(model::Optimizer, ::BD.ConstraintDecomposition, index::MOI.ConstraintIndex)
MOI.throw_if_not_valid(model, index)
constrinfo = _info(model, index)
Expand Down
3 changes: 2 additions & 1 deletion src/MathProg/MathProg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ export Variable, Constraint, VarId, ConstrId, VarMembership, ConstrMembership,
getperenub, getcurub, setcurub!, getperenrhs, setperenrhs!, getcurrhs, setcurrhs!, getperensense, setperensense!,
getcursense, setcursense!, getperenkind, getcurkind, setcurkind!, getperenincval,
getcurincval, setcurincval!, isperenactive, iscuractive, activate!, deactivate!,
isexplicit, getname, getbranchingpriority, reset!, getreducedcost, setperenkind!, isfixed, fix!, unfix!
isexplicit, getname, getbranchingpriority, reset!, getreducedcost, setperenkind!, isfixed, fix!, unfix!,
getcustomdata

# Types & methods related to solutions & bounds
export PrimalBound, DualBound, AbstractSolution, PrimalSolution, DualSolution, ActiveBound, ObjValues,
Expand Down
10 changes: 6 additions & 4 deletions src/MathProg/clone.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ function clonevar!(
is_active::Bool = isperenactive(originform, var),
is_explicit::Bool = isexplicit(originform, var),
branching_priority::Float64 = getbranchingpriority(originform, var),
members::Union{ConstrMembership,Nothing} = nothing
members::Union{ConstrMembership,Nothing} = nothing,
custom_data = getcustomdata(originform, var)
)
id_of_clone = VarId(
getid(var);
Expand All @@ -26,7 +27,7 @@ function clonevar!(
cost = cost, lb = lb, ub = ub, kind = kind,
inc_val = inc_val, is_active = is_active, is_explicit = is_explicit,
branching_priority = branching_priority, members = members,
id = id_of_clone
id = id_of_clone, custom_data = custom_data
)
end

Expand All @@ -44,7 +45,8 @@ function cloneconstr!(
is_active::Bool = isperenactive(originform, constr),
is_explicit::Bool = isexplicit(originform, constr),
members::Union{VarMembership,Nothing} = nothing,
loc_art_var_abs_cost::Float64 = 0.0
loc_art_var_abs_cost::Float64 = 0.0,
custom_data = getcustomdata(originform, constr)
)
id_of_clone = ConstrId(
getid(constr);
Expand All @@ -56,7 +58,7 @@ function cloneconstr!(
rhs = rhs, kind = kind, sense = sense, inc_val = inc_val,
is_active = is_active, is_explicit = is_explicit, members = members,
loc_art_var_abs_cost = loc_art_var_abs_cost,
id = id_of_clone
id = id_of_clone, custom_data = custom_data
)
end

Expand Down
15 changes: 15 additions & 0 deletions src/MathProg/varconstr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,21 @@ Return the branching priority of a variable
getbranchingpriority(form::Formulation, varid::VarId) = getvar(form, varid).branching_priority
getbranchingpriority(::Formulation, var::Variable) = var.branching_priority


"""
getcustomdata(formulation, var)
getcustomdata(formulation, varid)
getcustomdata(formulation, constr)
getcustomdata(formulation, constrid)

Return the custom data of a variable or a constraint in a formulation.
"""
getcustomdata(form::Formulation, varid::VarId) = getcustomdata(form, getvar(form, varid))
getcustomdata(::Formulation, var::Variable) = var.custom_data
getcustomdata(form::Formulation, constrid::ConstrId) = getcustomdata(form, getconstr(form, constrid))
getcustomdata(::Formulation, constr::Constraint) = constr.custom_data


# Reset (this method is used only in tests... @guimarqu doesn't know if we should keep it)
"""
reset!(form, var)
Expand Down
50 changes: 50 additions & 0 deletions test/integration/custom_data/attach_custom_data.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
struct TestAttachCustomDataAlgorithm end

struct CustomVarData <: BD.AbstractCustomData
var_value::Int
end

struct CustomConstrData <: BD.AbstractCustomData
constr_value::Int
end

function Coluna.Algorithm.run!(::TestAttachCustomDataAlgorithm, _, form, _)
vars = Dict{String, Coluna.MathProg.Variable}()
for (_, var) in Coluna.MathProg.getvars(form)
vars[getname(form, var)] = var
end

constrs = Dict{String, Coluna.MathProg.Constraint}()
for (_, constr) in Coluna.MathProg.getconstrs(form)
constrs[getname(form, constr)] = constr
end

@test Coluna.MathProg.getcustomdata(form, vars["x[1]"]).var_value == 1
@test Coluna.MathProg.getcustomdata(form, vars["x[2]"]).var_value == 2
@test Coluna.MathProg.getcustomdata(form, constrs["c"]).constr_value == 3
return Coluna.Algorithm.OptimizationState(form)
end

function attach_custom_data()
coluna = JuMP.optimizer_with_attributes(
Coluna.Optimizer,
"params" => CL.Params(
solver = TestAttachCustomDataAlgorithm()
),
"default_optimizer" => GLPK.Optimizer,
)

model = BlockModel(coluna)
@variable(model, x[1:2], Bin)
@constraint(model, c, x[1] + x[2] <= 1)
@objective(model, Max, 2x[1] + 3x[2])

customvars!(model, CustomVarData)
customconstrs!(model, CustomConstrData)

customdata!(x[1], CustomVarData(1))
customdata!(x[2], CustomVarData(2))
customdata!(c, CustomConstrData(3))
optimize!(model)
end
register!(integration_tests, "attach_custom_data", attach_custom_data)
12 changes: 12 additions & 0 deletions test/integration/run.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
dirs = [
"custom_data"
]

for dir in dirs
dirpath = joinpath(@__DIR__, dir)
for filename in readdir(dirpath)
include(joinpath(dirpath, filename))
end
end

run_integration_tests() = run_tests(integration_tests)
4 changes: 3 additions & 1 deletion test/revise.jl
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,11 @@ for file in all_test_files
end

include("unit/run.jl")
include("integration/run.jl")
include("e2e/run.jl")

listen_to_tests([
run_unit_tests,
#run_unit_tests,
run_integration_tests,
#run_e2e_tests
])
4 changes: 4 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ using .TestRegistry
unit_tests = Registry()
include("parser.jl")

integration_tests = Registry()
e2e_tests = Registry()

const MODULES = [
Expand All @@ -46,8 +47,11 @@ if !isempty(ARGS)
include("revise.jl")
else
include("unit/run.jl")
include("integration/run.jl")
include("e2e/run.jl")

run_unit_tests()
run_integration_tests()

@testset "MOI integration" begin
include("MathOptInterface/MOI_wrapper.jl")
Expand Down