From f95a59d217cc809cf4dfe053ad1b67b6e8b4c7ec Mon Sep 17 00:00:00 2001 From: Corentin Le Molgat Date: Mon, 10 Feb 2025 16:40:26 +0100 Subject: [PATCH] julia: export from google3 --- ortools/julia/ORTools.jl/Project.toml | 5 +- ortools/julia/ORTools.jl/src/ORTools.jl | 4 +- .../ORTools.jl/src/c_wrapper/c_wrapper.jl | 56 + .../ORTools.jl/src/moi_wrapper/MOI_wrapper.jl | 1885 +++++++++++++++++ .../src/moi_wrapper/Type_wrappers.jl | 1853 ++++++++++++++++ ortools/julia/ORTools.jl/test/cli_c_tests.jl | 45 + .../julia/ORTools.jl/test/moi/MOI_wrapper.jl | 444 ++++ ortools/julia/ORTools.jl/test/runtests.jl | 4 +- .../src/genproto/google/google.jl | 2 +- .../operations_research.jl | 2 +- 10 files changed, 4294 insertions(+), 6 deletions(-) create mode 100644 ortools/julia/ORTools.jl/src/c_wrapper/c_wrapper.jl create mode 100644 ortools/julia/ORTools.jl/src/moi_wrapper/MOI_wrapper.jl create mode 100644 ortools/julia/ORTools.jl/src/moi_wrapper/Type_wrappers.jl create mode 100755 ortools/julia/ORTools.jl/test/cli_c_tests.jl create mode 100644 ortools/julia/ORTools.jl/test/moi/MOI_wrapper.jl diff --git a/ortools/julia/ORTools.jl/Project.toml b/ortools/julia/ORTools.jl/Project.toml index 33f069be36..f24911c7c2 100644 --- a/ortools/julia/ORTools.jl/Project.toml +++ b/ortools/julia/ORTools.jl/Project.toml @@ -4,9 +4,12 @@ version = "1.0.0-DEV" [deps] MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" +ORToolsGenerated = "6b269722-41d3-11ee-be56-0242ac120002" +ProtoBuf = "3349acd9-ac6a-5e09-bcdb-63829b23a429" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] -julia = "1.6.7" +julia = "1.10" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/ortools/julia/ORTools.jl/src/ORTools.jl b/ortools/julia/ORTools.jl/src/ORTools.jl index dcdf2dca76..6bf68bee9d 100644 --- a/ortools/julia/ORTools.jl/src/ORTools.jl +++ b/ortools/julia/ORTools.jl/src/ORTools.jl @@ -1,5 +1,5 @@ module ORTools - -# Write your package code here. +# TODO: b/384496822 - Run formatter across entire package +include("moi_wrapper/MOI_wrapper.jl") end diff --git a/ortools/julia/ORTools.jl/src/c_wrapper/c_wrapper.jl b/ortools/julia/ORTools.jl/src/c_wrapper/c_wrapper.jl new file mode 100644 index 0000000000..4d1c11260f --- /dev/null +++ b/ortools/julia/ORTools.jl/src/c_wrapper/c_wrapper.jl @@ -0,0 +1,56 @@ +libortools = ORTools_jll.libortools + +function MathOptNewInterrupter() + return ccall((:MathOptNewInterrupter, libortools), + Ptr{Cvoid}, + (Cvoid,), + ptr) +end + +function MathOptFreeInterrupter(ptr) + return ccall((:MathOptFreeInterrupter, libortools), + Cvoid, + (Ptr{Cvoid},), + ptr) +end + +function MathOptInterrupt(ptr) + return ccall((:MathOptInterrupt, libortools), + Cvoid, + (Ptr{Cvoid},), + ptr) +end + +function MathOptIsInterrupted(ptr) + return ccall((:MathOptIsInterrupted, libortools), + Cint, + (Ptr{Cvoid},), + ptr) +end + +function MathOptFree(ptr) + return ccall((:MathOptFree, libortools), + Cvoid, + (Ptr{Cvoid},), + ptr) +end + +function MathOptSolve(model, model_size, solver_type, interrupter, solve_result, solve_result_size, status_msg) + return ccall((:MathOptSolve, libortools), + Cint, + (Ptr{Cvoid}, + Csize_t, + Cint, + Ptr{Cvoid}, + Ptr{Ptr{Cvoid}}, + Ptr{Csize_t}, + Ptr{Ptr{Cchar}}), + model, + model_size, + solver_type, + interrupter, + solve_result, + solve_result_size, + status_msg) +end + diff --git a/ortools/julia/ORTools.jl/src/moi_wrapper/MOI_wrapper.jl b/ortools/julia/ORTools.jl/src/moi_wrapper/MOI_wrapper.jl new file mode 100644 index 0000000000..543f8d5dff --- /dev/null +++ b/ortools/julia/ORTools.jl/src/moi_wrapper/MOI_wrapper.jl @@ -0,0 +1,1885 @@ +import MathOptInterface as MOI +include("Type_wrappers.jl") + +const PARAM_SPLITTER = "__" +const PARAM_FIELD_NAME_TO_INSTANCE_DICT = Dict( + "gscip" => GScipParameters(), + "gurobi" => GurobiParameters(), + "glop" => GlopParameters(), + "cp_sat" => SatParameters(), + "glpk" => GlpkParameters(), + "highs" => HighsOptions(), +) + +# Though these are the supported solvers, nothing in the code is really specific to them +# For other solvers with an open source implementation, like Gurobi, it is better to use their open +# source wrapper, e.g Gurobi.jl. +const SUPPORTED_SOLVER_TYPES = [ + SolverType.SOLVER_TYPE_UNSPECIFIED, + SolverType.SOLVER_TYPE_GLOP, + SolverType.SOLVER_TYPE_CP_SAT, +] + +const NON_GOOGLE_SOLVER_WARNING = """ +You have specified a non-Google solver; the solver is unlikely to be compiled into OR-Tools and you will encounter errors. +If you'd really like to use the solver you have specified, we highly recommend that you use its +available Julia wrapper. Support for remote solves in ORTools.jl will be added in the future. +""" + +# Keys to the constraint indices dict +const SCALAR_SET_WITH_VARIABLE_INDEX_CONSTRAINT_KEY = "scalar_set_with_variable_index" +const SCALAR_SET_WITH_SCALAR_FUNCTION_CONSTRAINT_KEY = "scalar_set_with_scalar_function" +const INTEGER_CONSTRAINT_KEY = "integer" +const ZERO_ONE_CONSTRAINT_KEY = "zero_one" + +""" + Optimizer() + + Create a new MathOpt optimizer. + + # Solver Type + + By default, the optimizer will use the `GLOP` solver. +""" +mutable struct Optimizer <: MOI.AbstractOptimizer + solver_type::SolverType.T + model::Union{Model,Nothing} + parameters::Union{SolveParameters,Nothing} + + # Metadata added for MOI's internal use + constraint_types_present::Set{Tuple{Type,Type}} + constraint_indices_dict::Dict{String,Vector{MOI.ConstraintIndex}} + + # Indicator of whether an objective has been set + objective_set::Bool + + # Constructor with optional parameters + function Optimizer(; + model_name::String = "", + solver_type::SolverType.T = SolverType.SOLVER_TYPE_GLOP, + parameters::Union{SolveParameters,Nothing} = SolveParameters(), + ) + if !in(solver_type, SUPPORTED_SOLVER_TYPES) + @warn NON_GOOGLE_SOLVER_WARNING + end + # Initialize a model instance + model = Model(name = model_name) + + # Initialize the constraint indices dict + # TODO: b/392072219 - replace this with member fields on the struct + constraint_indices_dict = Dict( + SCALAR_SET_WITH_VARIABLE_INDEX_CONSTRAINT_KEY => [], + SCALAR_SET_WITH_SCALAR_FUNCTION_CONSTRAINT_KEY => [], + INTEGER_CONSTRAINT_KEY => [], + ZERO_ONE_CONSTRAINT_KEY => [], + ) + + new( + solver_type, + model, + parameters, + Set{Tuple{Type,Type}}(), + constraint_indices_dict, + false, + ) + end +end + +function setproperty!(model::Optimizer, field::Symbol, value) + if field == :solver_type && !in(value, SUPPORTED_SOLVER_TYPES) + @warn NON_GOOGLE_SOLVER_WARNING + end + + setfield!(model, field, value) + + return nothing +end + +function MOI.empty!(model::Optimizer) + model.solver_type = SolverType.SOLVER_TYPE_UNSPECIFIED + model.model = nothing + model.parameters = nothing + # Clear the related metadata + model.constraint_types_present = Set{Tuple{Type,Type}}() + model.constraint_indices_dict = Dict() + model.objective_set = false + + return nothing +end + +function MOI.is_empty(model::Optimizer) + return isnothing(model.model) && + isnothing(model.parameters) && + model.solver_type == SolverType.SOLVER_TYPE_UNSPECIFIED && + !model.objective_set +end + +""" +TODO: b/384496265 - implement Base.summary(::IO, ::Optimizer) +to print a nice string when someone shows your model +""" + +function MOI.get(model::Optimizer, ::MOI.SolverName) + return "$(model.solver_type)" +end + +function MOI.get(model::Optimizer, ::MOI.SolverVersion) + return "1.0.0-DEV" +end + +function MOI.get(model::Optimizer, ::MOI.Name) + if !MOI.is_empty(model) + return model.model.name + end + + return "" +end + +function MOI.set(model::Optimizer, ::MOI.Name, name::String) + optionally_initialize_model_and_parameters!(model) + + model.model.name = name + + return nothing +end + +MOI.supports(model::Optimizer, ::MOI.Name) = true + + +function optionally_initialize_model_and_parameters!(model::Optimizer)::Nothing + if MOI.is_empty(model) + model.solver_type = SolverType.SOLVER_TYPE_UNSPECIFIED + model.model = Model() + model.parameters = SolveParameters() + # Re-initailize the associated metadata. + # TODO: b/392072219 - use emtpy! to do this after resolving this bug. + model.constraint_indices_dict = Dict( + SCALAR_SET_WITH_VARIABLE_INDEX_CONSTRAINT_KEY => [], + SCALAR_SET_WITH_SCALAR_FUNCTION_CONSTRAINT_KEY => [], + INTEGER_CONSTRAINT_KEY => [], + ZERO_ONE_CONSTRAINT_KEY => [], + ) + model.objective_set = false + end + + if isnothing(model.parameters) + model.parameters = SolveParameters() + end + + return nothing +end + +function optionally_initialize_objective!(model::Optimizer)::Nothing + if isnothing(model.model.objective) + model.model.objective = Objective() + end + + return nothing +end + + +function MOI.get(model::Optimizer, ::MOI.Silent) + if !MOI.is_empty(model) && !isnothing(model.parameters) + return !model.parameters.enable_output + end + + return true +end + +function MOI.set(model::Optimizer, ::MOI.Silent, silent::Bool) + optionally_initialize_model_and_parameters!(model) + + model.parameters.enable_output = !silent + + return nothing +end + +MOI.supports(model::Optimizer, ::MOI.Silent) = true + +function MOI.get(model::Optimizer, ::MOI.TimeLimitSec) + if !MOI.is_empty(model) && !isnothing(model.parameters) + return model.parameters.time_limit + end + + return nothing +end + +function MOI.set(model::Optimizer, ::MOI.TimeLimitSec, time_limit::Union{Nothing,Duration}) + optionally_initialize_model_and_parameters!(model) + model.parameters.time_limit = time_limit + + return nothing +end + +MOI.supports(model::Optimizer, ::MOI.TimeLimitSec) = true + +function MOI.get(model::Optimizer, ::MOI.ObjectiveLimit) + if !MOI.is_empty(model) && !isnothing(model.parameters) + objective_limit = model.parameters.objective_limit + return objective_limit == -Inf ? nothing : objective_limit + end + + return nothing +end + +function MOI.set( + model::Optimizer, + ::MOI.ObjectiveLimit, + objective_limit::Union{Nothing,Real}, +) + optionally_initialize_model_and_parameters!(model) + + if isnothing(objective_limit) + model.parameters.objective_limit = -Inf + else + model.parameters.objective_limit = Float64(objective_limit) + end + + return nothing +end + +MOI.supports(model::Optimizer, ::MOI.ObjectiveLimit) = true + +function MOI.get(model::Optimizer, ::MOI.SolutionLimit) + if !MOI.is_empty(model) && !isnothing(model.parameters) + solution_limit = model.parameters.solution_limit + return solution_limit == 0 ? nothing : solution_limit + end + + return nothing +end + +function MOI.set(model::Optimizer, ::MOI.SolutionLimit, solution_limit::Union{Nothing,Int}) + optionally_initialize_model_and_parameters!(model) + + if isnothing(solution_limit) + model.parameters.solution_limit = zero(Int32) + else + try + model.parameters.solution_limit = Int32(solution_limit) + catch err + if isa(err, InexactError) + println("Solution limit must be an Int32") + else + println("Setting solution limit failed with error: ", err) + end + end + end + + return nothing +end + +MOI.supports(model::Optimizer, ::MOI.SolutionLimit) = true + +function MOI.get(model::Optimizer, ::MOI.NodeLimit) + if !MOI.is_empty(model) && !isnothing(model.parameters) + node_limit = model.parameters.node_limit + return node_limit == 0 ? nothing : node_limit + end + + return nothing +end + +function MOI.set(model::Optimizer, ::MOI.NodeLimit, node_limit::Union{Nothing,Int}) + optionally_initialize_model_and_parameters!(model) + + if isnothing(node_limit) + model.parameters.node_limit = zero(Int64) + else + model.parameters.node_limit = Int64(node_limit) + end + + return nothing +end + +MOI.supports(model::Optimizer, ::MOI.NodeLimit) = true + +function MOI.get(model::Optimizer, ::MOI.NumberOfThreads) + if !MOI.is_empty(model) && !isnothing(model.parameters) + number_of_threads = model.parameters.threads + return number_of_threads == 0 ? nothing : number_of_threads + end + + return nothing +end + +function MOI.set( + model::Optimizer, + ::MOI.NumberOfThreads, + number_of_threads::Union{Nothing,Int}, +) + optionally_initialize_model_and_parameters!(model) + + if isnothing(number_of_threads) + model.parameters.threads = zero(Int32) + else + model.parameters.threads = Int32(number_of_threads) + end + + return nothing +end + +MOI.supports(model::Optimizer, ::MOI.NumberOfThreads) = true + +function MOI.get(model::Optimizer, ::MOI.AbsoluteGapTolerance) + if !MOI.is_empty(model) && !isnothing(model.parameters) + return model.parameters.absolute_gap_tolerance + end + + return 0 +end + +function MOI.set( + model::Optimizer, + ::MOI.AbsoluteGapTolerance, + absolute_gap_tolerance::Union{Nothing,Real}, +) + optionally_initialize_model_and_parameters!(model) + + if isnothing(absolute_gap_tolerance) + model.parameters.absolute_gap_tolerance = zero(Float64) + else + model.parameters.absolute_gap_tolerance = Float64(absolute_gap_tolerance) + end + + return nothing +end + +MOI.supports(model::Optimizer, ::MOI.AbsoluteGapTolerance) = true + +function MOI.get(model::Optimizer, ::MOI.RelativeGapTolerance) + if !MOI.is_empty(model) && !isnothing(model.parameters) + return model.parameters.relative_gap_tolerance + end + + return 0 +end + +function MOI.set( + model::Optimizer, + ::MOI.RelativeGapTolerance, + relative_gap_tolerance::Union{Nothing,Real}, +) + optionally_initialize_model_and_parameters!(model) + + if isnothing(relative_gap_tolerance) + model.parameters.relative_gap_tolerance = zero(Float64) + else + model.parameters.relative_gap_tolerance = Float64(relative_gap_tolerance) + end + + return nothing +end + +MOI.supports(model::Optimizer, ::MOI.RelativeGapTolerance) = true + +# Extra parameter attributes supported by the solver +struct CutOffLimit <: MOI.AbstractOptimizerAttribute end +MOI.attribute_value_type(::CutOffLimit) = Union{Nothing,Real} + +function MOI.set(model::Optimizer, ::CutOffLimit, cutoff_limit::Union{Nothing,Real}) + optionally_initialize_model_and_parameters!(model) + + if isnothing(cutoff_limit) + model.parameters.cutoff_limit = zero(Float64) + else + model.parameters.cutoff_limit = Float64(cutoff_limit) + end + + return nothing +end + +function MOI.get(model::Optimizer, ::CutOffLimit) + if !MOI.is_empty(model) && !isnothing(model.parameters) + return model.parameters.cutoff_limit + end + + return nothing +end + +MOI.supports(model::Optimizer, ::CutOffLimit) = true + + +struct BestBoundLimit <: MOI.AbstractOptimizerAttribute end +MOI.attribute_value_type(::BestBoundLimit) = Union{Nothing,Real} + +function MOI.set(model::Optimizer, ::BestBoundLimit, best_bound_limit::Union{Nothing,Real}) + optionally_initialize_model_and_parameters!(model) + + if isnothing(best_bound_limit) + model.parameters.best_bound_limit = zero(Float64) + else + model.parameters.best_bound_limit = Float64(best_bound_limit) + end + + return nothing +end + +function MOI.get(model::Optimizer, ::BestBoundLimit) + if !MOI.is_empty(model) && !isnothing(model.parameters) + return model.parameters.best_bound_limit + end + + return nothing +end + +MOI.supports(model::Optimizer, ::BestBoundLimit) = true + + +struct RandomSeed <: MOI.AbstractOptimizerAttribute end +MOI.attribute_value_type(::RandomSeed) = Union{Nothing,Int} + +function MOI.set(model::Optimizer, ::RandomSeed, random_seed::Union{Nothing,Int}) + optionally_initialize_model_and_parameters!(model) + + if isnothing(random_seed) + model.parameters.random_seed = zero(Int32) + else + model.parameters.random_seed = Int32(random_seed) + end + + return nothing +end + +function MOI.get(model::Optimizer, ::RandomSeed) + if !MOI.is_empty(model) && !isnothing(model.parameters) + return model.parameters.random_seed + end + + return nothing +end + +MOI.supports(model::Optimizer, ::RandomSeed) = true + + +struct SolutionPoolSize <: MOI.AbstractOptimizerAttribute end +MOI.attribute_value_type(::SolutionPoolSize) = Union{Nothing,Int} + +function MOI.set( + model::Optimizer, + ::SolutionPoolSize, + solution_pool_size::Union{Nothing,Int}, +) + optionally_initialize_model_and_parameters!(model) + + if isnothing(solution_pool_size) + model.parameters.solution_pool_size = zero(Int32) + else + model.parameters.solution_pool_size = Int32(solution_pool_size) + end + + return nothing +end + +function MOI.get(model::Optimizer, ::SolutionPoolSize) + if !MOI.is_empty(model) && !isnothing(model.parameters) + return model.parameters.solution_pool_size + end + + return nothing +end + +MOI.supports(model::Optimizer, ::SolutionPoolSize) = true + + +struct LPAlgorithmType <: MOI.AbstractOptimizerAttribute end +MOI.attribute_value_type(::LPAlgorithmType) = Union{Nothing,LPAlgorithm.T} + +# +function MOI.set( + model::Optimizer, + ::LPAlgorithmType, + lp_algorithm::Union{Nothing,LPAlgorithm.T}, +) + optionally_initialize_model_and_parameters!(model) + + if isnothing(lp_algorithm) + model.parameters.lp_algorithm = LPAlgorithm.LP_ALGORITHM_UNSPECIFIED + else + # Glop for simplex & dual simplex, PDLP/Bisco for first order (and nothing for barrier) + # The solvers above are Google first party solvers. + if lp_algorithm == LPAlgorithm.LP_ALGORITHM_BARRIER + @warn NON_GOOGLE_SOLVER_WARNING + end + + model.parameters.lp_algorithm = lp_algorithm + end + + return nothing +end + +function MOI.get(model::Optimizer, ::LPAlgorithmType) + if !MOI.is_empty(model) && !isnothing(model.parameters) + return model.parameters.lp_algorithm + end + + return nothing +end + +MOI.supports(model::Optimizer, ::LPAlgorithmType) = true + + +struct Presolve <: MOI.AbstractOptimizerAttribute end +MOI.attribute_value_type(::Presolve) = Union{Nothing,Emphasis.T} + +function MOI.set(model::Optimizer, ::Presolve, presolve::Union{Nothing,Emphasis.T}) + optionally_initialize_model_and_parameters!(model) + + if isnothing(presolve) + model.parameters.presolve = Emphasis.EMPHASIS_UNSPECIFIED + else + model.parameters.presolve = presolve + end + + return nothing +end + +function MOI.get(model::Optimizer, ::Presolve) + if !MOI.is_empty(model) && !isnothing(model.parameters) + return model.parameters.presolve + end + + return nothing +end + +MOI.supports(model::Optimizer, ::Presolve) = true + + +struct Cuts <: MOI.AbstractOptimizerAttribute end +MOI.attribute_value_type(::Cuts) = Union{Nothing,Emphasis.T} + +function MOI.set(model::Optimizer, ::Cuts, cuts::Union{Nothing,Emphasis.T}) + optionally_initialize_model_and_parameters!(model) + + if isnothing(cuts) + model.parameters.cuts = Emphasis.EMPHASIS_UNSPECIFIED + else + model.parameters.cuts = cuts + end + + return nothing +end + +function MOI.get(model::Optimizer, ::Cuts) + if !MOI.is_empty(model) && !isnothing(model.parameters) + return model.parameters.cuts + end + + return nothing +end + +MOI.supports(model::Optimizer, ::Cuts) = true + + +struct Heuristics <: MOI.AbstractOptimizerAttribute end +MOI.attribute_value_type(::Heuristics) = Union{Nothing,Emphasis.T} + +function MOI.set(model::Optimizer, ::Heuristics, heuristics::Union{Nothing,Emphasis.T}) + optionally_initialize_model_and_parameters!(model) + + if isnothing(heuristics) + model.parameters.heuristics = Emphasis.EMPHASIS_UNSPECIFIED + else + model.parameters.heuristics = heuristics + end + + return nothing +end + +function MOI.get(model::Optimizer, ::Heuristics) + if !MOI.is_empty(model) && !isnothing(model.parameters) + return model.parameters.heuristics + end + + return nothing +end + +MOI.supports(model::Optimizer, ::Heuristics) = true + +struct Scaling <: MOI.AbstractOptimizerAttribute end +MOI.attribute_value_type(::Scaling) = Union{Nothing,Emphasis.T} + +function MOI.set(model::Optimizer, ::Scaling, scaling::Union{Nothing,Emphasis.T}) + optionally_initialize_model_and_parameters!(model) + + if isnothing(scaling) + model.parameters.scaling = Emphasis.EMPHASIS_UNSPECIFIED + else + model.parameters.scaling = scaling + end + + return nothing +end + +function MOI.get(model::Optimizer, ::Scaling) + if !MOI.is_empty(model) && !isnothing(model.parameters) + return model.parameters.scaling + end + + return nothing +end + +MOI.supports(model::Optimizer, ::Scaling) = true + + +struct GscipParametersAttribute <: MOI.AbstractOptimizerAttribute end +MOI.attribute_value_type(::GscipParametersAttribute) = Union{Nothing,GScipParameters} + +function MOI.set( + model::Optimizer, + ::GscipParametersAttribute, + gscip_parameters::Union{Nothing,GScipParameters}, +) + optionally_initialize_model_and_parameters!(model) + + model.parameters.gscip = gscip_parameters + + return nothing +end + +function MOI.get(model::Optimizer, ::GscipParametersAttribute) + if !MOI.is_empty(model) && !isnothing(model.parameters) + return model.parameters.gscip + end + + return nothing +end + +MOI.supports(model::Optimizer, ::GscipParametersAttribute) = true + + +struct GurobiParametersAttribute <: MOI.AbstractOptimizerAttribute end +MOI.attribute_value_type(::GurobiParametersAttribute) = Union{Nothing,GurobiParameters} + +function MOI.set( + model::Optimizer, + ::GurobiParametersAttribute, + gurobi_parameters::Union{Nothing,GurobiParameters}, +) + optionally_initialize_model_and_parameters!(model) + + model.parameters.gurobi = gurobi_parameters + + return nothing +end + +function MOI.get(model::Optimizer, ::GurobiParametersAttribute) + if !MOI.is_empty(model) && !isnothing(model.parameters) + return model.parameters.gurobi + end + + return nothing +end + +MOI.supports(model::Optimizer, ::GurobiParametersAttribute) = true + + +struct GlopParametersAttribute <: MOI.AbstractOptimizerAttribute end +MOI.attribute_value_type(::GlopParametersAttribute) = Union{Nothing,GlopParameters} + +function MOI.set( + model::Optimizer, + ::GlopParametersAttribute, + glop_parameters::Union{Nothing,GlopParameters}, +) + optionally_initialize_model_and_parameters!(model) + + model.parameters.glop = glop_parameters + + return nothing +end + +function MOI.get(model::Optimizer, ::GlopParametersAttribute) + if !MOI.is_empty(model) && !isnothing(model.parameters) + return model.parameters.glop + end + + return nothing +end + +MOI.supports(model::Optimizer, ::GlopParametersAttribute) = true + + +struct SatParametersAttribute <: MOI.AbstractOptimizerAttribute end +MOI.attribute_value_type(::SatParametersAttribute) = Union{Nothing,CpSatParameters} + +function MOI.set( + model::Optimizer, + ::SatParametersAttribute, + cp_sat_parameters::Union{Nothing,SatParameters}, +) + optionally_initialize_model_and_parameters!(model) + + model.parameters.cp_sat = cp_sat_parameters + + return nothing +end + +function MOI.get(model::Optimizer, ::SatParametersAttribute) + if !MOI.is_empty(model) && !isnothing(model.parameters) + return model.parameters.cp_sat + end + + return nothing +end + +MOI.supports(model::Optimizer, ::SatParametersAttribute) = true + + +struct GlpkParametersAttribute <: MOI.AbstractOptimizerAttribute end +MOI.attribute_value_type(::GlpkParametersAttribute) = Union{Nothing,GlpkParameters} + +function MOI.set( + model::Optimizer, + ::GlpkParametersAttribute, + glpk_parameters::Union{Nothing,GlpkParameters}, +) + optionally_initialize_model_and_parameters!(model) + + model.parameters.glpk = glpk_parameters + + return nothing +end + +function MOI.get(model::Optimizer, ::GlpkParametersAttribute) + if !MOI.is_empty(model) && !isnothing(model.parameters) + return model.parameters.glpk + end + + return nothing +end + +MOI.supports(model::Optimizer, ::GlpkParametersAttribute) = true + + +struct HighsOptionsAttribute <: MOI.AbstractOptimizerAttribute end +MOI.attribute_value_type(::HighsOptionsAttribute) = Union{Nothing,HighsOptions} + +function MOI.set( + model::Optimizer, + ::HighsOptionsAttribute, + highs_parameters::Union{Nothing,HighsOptions}, +) + optionally_initialize_model_and_parameters!(model) + + model.parameters.highs = highs_parameters + + return nothing +end + +function MOI.get(model::Optimizer, ::HighsOptionsAttribute) + if !MOI.is_empty(model) && !isnothing(model.parameters) + return model.parameters.highs + end + + return nothing +end + +MOI.supports(model::Optimizer, ::HighsOptionsAttribute) = true + +""" +Set the parameter of the model throught the `RawOptimizerAttribute`. All fields +are set as they appear in the `SolveParameters` struct. However, the fields that +are structs (the solver specific parameter structs) themselves have their +internal fields split by the `PARAM_SPLITTER` and the solver name. For example, +`gscip_parameters.preprocessing` should be passed as `gscip__preprocessing`. +""" +function MOI.set(model::Optimizer, param::MOI.RawOptimizerAttribute, value) + optionally_initialize_model_and_parameters!(model) + + param_name = param.name + if contains(param_name, PARAM_SPLITTER) + solve_param, field = lowercase.(split(param_name, PARAM_SPLITTER)) + if solve_param == "sat" + solve_param = "cp_sat" + end + + if !haskey(PARAM_FIELD_NAME_TO_INSTANCE_DICT, solve_param) + throw( + ArgumentError( + "Unsupported parameter attribute: $param_name. Model has no support for \"$solve_param\"", + ), + ) + end + + solve_param_instance = getfield(model.parameters, Symbol(solve_param)) + + # If the parameter is not set, set it to the default instance + if isnothing(solve_param_instance) + solve_param_instance = PARAM_FIELD_NAME_TO_INSTANCE_DICT[solve_param] + setfield!(model.parameters, Symbol(solve_param), solve_param_instance) + end + + # Attempt to set the parameter value + setfield!(solve_param_instance, Symbol(field), value) + else + setfield!(model.parameters, Symbol(param_name), value) + end + + return nothing +end + +""" +Get the parameter of the model throught the `RawOptimizerAttribute`. All fields +are retrieved as they appear in the `SolveParameters` struct. However, the fields +that are structs (the solver specific parameter structs) themselves have their +internal fields split by the `PARAM_SPLITTER` and the solver name. For example, +`gscip_parameters.preprocessing` should be passed as `gscip__preprocessing`. +""" +function MOI.get(model::Optimizer, param::MOI.RawOptimizerAttribute) + if !MOI.is_empty(model) && !isnothing(model.parameters) + param_name = param.name + + if contains(param_name, PARAM_SPLITTER) + solve_param, field = lowercase.(split(param_name, PARAM_SPLITTER)) + + if solve_param == "sat" + solve_param = "cp_sat" + end + + solve_param_instance = getfield(model.parameters, Symbol(solve_param)) + + if isnothing(solve_param_instance) + throw( + error( + "cannot get field from parameter \"$solve_param\" as instance is currently set to \"nothing\"", + ), + ) + end + + return getfield(solve_param_instance, Symbol(field)) + else + return getfield(model.parameters, Symbol(param_name)) + end + end + + throw(error("Either your model or parameters is empty.")) +end + +MOI.supports(model::Optimizer, param::MOI.RawOptimizerAttribute) = true + +function MOI.get(model::Optimizer, ::MOI.ListOfModelAttributesSet) + if MOI.is_empty(model) + return [] + end + + model_attributes_set = [] + + F = MOI.get(model, MOI.ObjectiveFunctionType()) + if !isnothing(F) + push!(model_attributes_set, MOI.ObjectiveFunction{F}()) + end + + objective_sense = MOI.get(model, MOI.ObjectiveSense()) + push!(model_attributes_set, MOI.ObjectiveSense()) + + model_name = MOI.get(model, MOI.Name()) + if !isempty(model_name) + push!(model_attributes_set, MOI.Name()) + end + + return model_attributes_set +end + + +""" + + Variable overrides + +""" +function MOI.add_variable(model::Optimizer) + optionally_initialize_model_and_parameters!(model) + + # initialize variables is they were set to Nothing + if isnothing(model.model.variables) + # update the model + model.model.variables = Variables() + end + + # add a new variable to the model + variable_index = length(model.model.variables.ids) + 1 + push!(model.model.variables.ids, variable_index) + push!(model.model.variables.lower_bounds, -Inf) + push!(model.model.variables.upper_bounds, Inf) + push!(model.model.variables.integers, false) + push!(model.model.variables.names, "") + + return MOI.VariableIndex(variable_index) +end + + +function MOI.get(model::Optimizer, ::MOI.ListOfVariableIndices) + if !MOI.is_empty(model) + return MOI.VariableIndex.(model.model.variables.ids) + end + + return nothing +end + + +function MOI.get(model::Optimizer, ::MOI.NumberOfVariables) + if !MOI.is_empty(model) + return length(model.model.variables.ids) + end + + return 0 +end + +function MOI.is_valid(model::Optimizer, vi::MOI.VariableIndex) + if !MOI.is_empty(model) + return 1 <= vi.value <= MOI.get(model, MOI.NumberOfVariables()) + end + + return false +end + + +MOI.supports(::Optimizer, ::MOI.VariableName, ::Type{MOI.VariableIndex}) = true + +function MOI.set(model::Optimizer, ::MOI.VariableName, v::MOI.VariableIndex, name::String) + model.model.variables.names[v.value] = name + + return nothing +end + +function MOI.get(model::Optimizer, ::MOI.VariableName, v::MOI.VariableIndex) + if !MOI.is_empty(model) + return model.model.variables.names[v.value] + end + + return "" +end + +function MOI.get(model::Optimizer, ::Type{MOI.VariableIndex}, v::String) + if !MOI.is_empty(model) && !isempty(v) + variable_index = findfirst(x -> x == v, model.model.variables.names) + if isnothing(variable_index) + return nothing + end + + return MOI.VariableIndex(variable_index) + end + + return nothing +end + + +""" + + Constraints support + +""" +function MOI.supports_constraint( + ::Optimizer, + ::Type{MOI.VariableIndex}, + ::Type{<:SCALAR_SET}, +) + return true +end + +function MOI.add_constraint( + model::Optimizer, + vi::MOI.VariableIndex, + c::S, +) where {S<:SCALAR_SET} + if !MOI.is_empty(model) + # Check if the variable is already bounded by a constraint. + if S <: MOI.LessThan + throw_if_upper_bound_is_already_set(model, vi, c) + end + + if S <: MOI.GreaterThan + throw_if_lower_bound_is_already_set(model, vi, c) + end + + if S <: MOI.EqualTo + throw_if_upper_bound_is_already_set(model, vi, c) + throw_if_lower_bound_is_already_set(model, vi, c) + end + + if S <: MOI.Interval + throw_if_upper_bound_is_already_set(model, vi, c) + throw_if_lower_bound_is_already_set(model, vi, c) + end + + # Get the int value of the variable index + index = vi.value + + # retrieve the constraint bounds + lower_bound, upper_bound = bounds(c) + + # set the bounds on the Variable + model.model.variables.lower_bounds[index] = lower_bound + model.model.variables.upper_bounds[index] = upper_bound + + # update the associated metadata. + push!(model.constraint_types_present, (MOI.VariableIndex, typeof(c))) + push!( + model.constraint_indices_dict[SCALAR_SET_WITH_VARIABLE_INDEX_CONSTRAINT_KEY], + MOI.ConstraintIndex{typeof(vi),typeof(c)}(index), + ) + + return MOI.ConstraintIndex{typeof(vi),typeof(c)}(index) + end + + return nothing +end + + +function throw_if_upper_bound_is_already_set( + model::Optimizer, + vi::MOI.VariableIndex, + c::S, +) where {S<:SCALAR_SET} + # Assumes type consistency across all constraints. + T = typeof(c).parameters[1] + less_than_idx = MOI.ConstraintIndex{typeof(vi),MOI.LessThan{T}}(vi.value) + interval_idx = MOI.ConstraintIndex{typeof(vi),MOI.Interval{T}}(vi.value) + equal_to_idx = MOI.ConstraintIndex{typeof(vi),MOI.EqualTo{T}}(vi.value) + + if in( + less_than_idx, + model.constraint_indices_dict[SCALAR_SET_WITH_VARIABLE_INDEX_CONSTRAINT_KEY], + ) + throw(MOI.UpperBoundAlreadySet{MOI.LessThan{T},S}(vi)) + end + + if in( + interval_idx, + model.constraint_indices_dict[SCALAR_SET_WITH_VARIABLE_INDEX_CONSTRAINT_KEY], + ) + throw(MOI.UpperBoundAlreadySet{MOI.Interval{T},S}(vi)) + end + + if in( + equal_to_idx, + model.constraint_indices_dict[SCALAR_SET_WITH_VARIABLE_INDEX_CONSTRAINT_KEY], + ) + throw(MOI.UpperBoundAlreadySet{MOI.EqualTo{T},S}(vi)) + end + + return nothing +end + +function throw_if_lower_bound_is_already_set( + model::Optimizer, + vi::MOI.VariableIndex, + c::S, +) where {S<:SCALAR_SET} + # Assumes type consistency across all constraints. + T = typeof(c).parameters[1] + greater_than_idx = MOI.ConstraintIndex{typeof(vi),MOI.GreaterThan{T}}(vi.value) + interval_idx = MOI.ConstraintIndex{typeof(vi),MOI.Interval{T}}(vi.value) + equal_to_idx = MOI.ConstraintIndex{typeof(vi),MOI.EqualTo{T}}(vi.value) + + if in( + greater_than_idx, + model.constraint_indices_dict[SCALAR_SET_WITH_VARIABLE_INDEX_CONSTRAINT_KEY], + ) + throw(MOI.LowerBoundAlreadySet{MOI.GreaterThan{T},S}(vi)) + end + + if in( + interval_idx, + model.constraint_indices_dict[SCALAR_SET_WITH_VARIABLE_INDEX_CONSTRAINT_KEY], + ) + throw(MOI.LowerBoundAlreadySet{MOI.Interval{T},S}(vi)) + end + + if in( + equal_to_idx, + model.constraint_indices_dict[SCALAR_SET_WITH_VARIABLE_INDEX_CONSTRAINT_KEY], + ) + throw(MOI.LowerBoundAlreadySet{MOI.EqualTo{T},S}(vi)) + end + + return nothing +end + +function MOI.supports_constraint( + ::Optimizer, + ::Type{MOI.VariableIndex}, + ::Type{MOI.ZeroOne}, +) + return true +end + +function MOI.add_constraint(model::Optimizer, vi::MOI.VariableIndex, c::MOI.ZeroOne) + if !MOI.is_empty(model) + # Get the int value of the variable index + index = vi.value + + # Set the variable bounds + model.model.variables.lower_bounds[index] = 0.0 + model.model.variables.upper_bounds[index] = 1.0 + + # Mark it as being an integer + model.model.variables.integers[index] = true + + # Update the associated metadata. + push!(model.constraint_types_present, (MOI.VariableIndex, MOI.ZeroOne)) + push!( + model.constraint_indices_dict[ZERO_ONE_CONSTRAINT_KEY], + MOI.ConstraintIndex{typeof(vi),typeof(c)}(index), + ) + + return MOI.ConstraintIndex{typeof(vi),typeof(c)}(index) + end + + return nothing +end + + +function MOI.supports_constraint( + ::Optimizer, + ::Type{MOI.VariableIndex}, + ::Type{MOI.Integer}, +) + return true +end + +function MOI.add_constraint(model::Optimizer, vi::MOI.VariableIndex, c::MOI.Integer) + if !MOI.is_empty(model) + # Get the int value of the variable index + index = vi.value + + # Set the bounds + model.model.variables.lower_bounds[index] = Float64(typemin(Int)) + model.model.variables.upper_bounds[index] = Float64(typemax(Int)) + + # Mark it as being an integer + model.model.variables.integers[index] = true + + # Update the associated metadata. + push!(model.constraint_types_present, (MOI.VariableIndex, MOI.Integer)) + push!( + model.constraint_indices_dict[INTEGER_CONSTRAINT_KEY], + MOI.ConstraintIndex{typeof(vi),typeof(c)}(index), + ) + + return MOI.ConstraintIndex{typeof(vi),typeof(c)}(index) + end + + return nothing +end + +# Helper function to create a dictionary of terms to combine coefficients +# of terms(variables) if they are repeated. +# For example: 3x + 5x <= 10 will be combined to 8x <= 10. +function get_terms_dict( + terms::Vector{MOI.ScalarAffineTerm{T}}, +)::Dict{Int64,Float64} where {T<:Real} + terms_dict = Dict{Int64,Float64}() + + for term in terms + if !haskey(terms_dict, term.variable.value) + terms_dict[term.variable.value] = term.coefficient + else + terms_dict[term.variable.value] += term.coefficient + end + end + + return terms_dict +end + +function MOI.add_constraint( + model::Optimizer, + f::MOI.ScalarAffineFunction{T}, + c::SCALAR_SET, +) where {T<:Real} + if !MOI.is_empty(model) + # Ensure that the constant is zero else throw an error. + iszero(f.constant) || + throw(MOI.ScalarFunctionConstantNotZero{T,typeof(f),typeof(c)}(f.constant)) + + # Retrieve the terms from `f` + terms = f.terms + + constraint_index = length(model.model.linear_constraints.ids) + 1 + lower_bound, upper_bound = bounds(c) + + # Update the LinearConstraintProto + push!(model.model.linear_constraints.ids, constraint_index) + push!(model.model.linear_constraints.lower_bounds, lower_bound) + push!(model.model.linear_constraints.upper_bounds, upper_bound) + push!(model.model.linear_constraints.names, "") + + terms_dict = get_terms_dict(terms) + + # Update the LinearConstaintMatrix (SparseDoubleVectorProto) + # linear_constraint_matrix.row_ids are elements of linear_constraints.ids. + # linear_constraint_matrix.column_ids are elements of variables.ids. + # Matrix entries not specified are zero. + # linear_constraint_matrix.coefficients must all be finite. + for term_index in keys(terms_dict) + push!(model.model.linear_constraint_matrix.row_ids, constraint_index) + push!(model.model.linear_constraint_matrix.column_ids, term_index) + push!(model.model.linear_constraint_matrix.coefficients, terms_dict[term_index]) + end + + # Update the associated metadata. + push!(model.constraint_types_present, (MOI.ScalarAffineFunction{T}, typeof(c))) + push!( + model.constraint_indices_dict[SCALAR_SET_WITH_SCALAR_FUNCTION_CONSTRAINT_KEY], + MOI.ConstraintIndex{typeof(f),typeof(c)}(constraint_index), + ) + + return MOI.ConstraintIndex{typeof(f),typeof(c)}(constraint_index) + end + + return nothing +end + +function MOI.supports_constraint( + ::Optimizer, + ::Type{MOI.ScalarAffineFunction{T}}, + ::Type{<:SCALAR_SET}, +) where {T<:Real} + return true +end + + +function MOI.get(model::Optimizer, ::MOI.ListOfConstraintTypesPresent) + if !MOI.is_empty(model) + return collect(model.constraint_types_present) + end + + return Set{Tuple{Type,Type}}[] +end + + +function MOI.get( + model::Optimizer, + ::MOI.ListOfConstraintIndices{MOI.VariableIndex,S}, +) where {S<:SCALAR_SET} + if !MOI.is_empty(model) + return sort!( + model.constraint_indices_dict[SCALAR_SET_WITH_VARIABLE_INDEX_CONSTRAINT_KEY], + by = x -> x.value, + ) + end + + return [] +end + + +function MOI.get( + model::Optimizer, + ::MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{T},S}, +) where {T<:Real,S<:SCALAR_SET} + if !MOI.is_empty(model) + return sort!( + model.constraint_indices_dict[SCALAR_SET_WITH_SCALAR_FUNCTION_CONSTRAINT_KEY], + by = x -> x.value, + ) + end + + return [] +end + +function MOI.get( + model::Optimizer, + ::MOI.ListOfConstraintIndices{MOI.VariableIndex,MOI.ZeroOne}, +) + if !MOI.is_empty(model) + return sort!( + model.constraint_indices_dict[ZERO_ONE_CONSTRAINT_KEY], + by = x -> x.value, + ) + end + + return [] +end + +function MOI.get( + model::Optimizer, + ::MOI.ListOfConstraintIndices{MOI.VariableIndex,MOI.Integer}, +) + if !MOI.is_empty(model) + return sort!( + model.constraint_indices_dict[INTEGER_CONSTRAINT_KEY], + by = x -> x.value, + ) + end + + return [] +end + +function MOI.get(model::Optimizer, ::MOI.NumberOfConstraints{F,S}) where {F,S} + if !MOI.is_empty(model) + return length(MOI.get(model, MOI.ListOfConstraintIndices{F,S}())) + end + + return 0 +end + +function MOI.get( + model::Optimizer, + ::MOI.ConstraintFunction, + c::MOI.ConstraintIndex{MOI.VariableIndex,Any}, +) + if !MOI.is_empty(model) + return MOI.VariableIndex(c.value) + end + + # TODO: Replace with another error + throw(ArgumentError("ConstraintIndex $(c.value) is not valid for this model.")) +end + +function MOI.set( + ::Optimizer, + ::MOI.ConstraintFunction, + ::MOI.ConstraintIndex{MOI.VariableIndex,<:Any}, + ::MOI.VariableIndex, +) + throw(MOI.SettingVariableIndexNotAllowed()) +end + +function MOI.get( + model::Optimizer, + ::MOI.ConstraintFunction, + c::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},S}, +) where {T<:Real,S<:SCALAR_SET} + if !MOI.is_empty(model) + # Convert the linear constraint to the ScalarAffineFunction + # Return all indices that match the constraint index. + column_indices = + findall(x -> x == c.value, model.model.linear_constraint_matrix.row_ids) + + terms = MOI.ScalarAffineTerm{T}[] + + for column_index in column_indices + push!( + terms, + MOI.ScalarAffineTerm{T}( + model.model.linear_constraint_matrix.coefficients[column_index], + MOI.VariableIndex( + model.model.linear_constraint_matrix.column_ids[column_index], + ), + ), + ) + end + + return MOI.ScalarAffineFunction{T}(terms, zero(Float64)) + end + + return nothing +end + + +function MOI.set( + model::Optimizer, + ::MOI.ConstraintFunction, + c::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},S}, + f::MOI.ScalarAffineFunction{T}, +) where {T<:Real,S<:SCALAR_SET} + optionally_initialize_model_and_parameters!(model) + + if !iszero(f.constant) + throw(MOI.ScalarFunctionConstantNotZero(f.constant)) + end + + previous_fn = MOI.get(model, MOI.ConstraintFunction, c) + + if isnothing(previous_fn) + throw(ArgumentError("ConstraintIndex $(c.value) is not valid for this model.")) + end + + # Clear the column_ids and coefficients associated with the constraint index. + # Get indices that match the constraint index. + associated_indices = + findall(x -> x == c.value, model.model.linear_constraint_matrix.row_ids) + + # Clear the associated indices in the column ids and coefficients. + deleteat!(model.model.linear_constraint_matrix.row_ids, associated_indices) + deleteat!(model.model.linear_constraint_matrix.column_ids, associated_indices) + deleteat!(model.model.linear_constraint_matrix.coefficients, associated_indices) + + # Retrieve the terms from `f` + terms = f.terms + + if length(terms) == 0 + throw( + ArgumentError( + "ScalarAffineFunction has no terms. You need to specify at least one term.", + ), + ) + end + + terms_dict = get_terms_dict(terms) + + for term_index in keys(terms_dict) + push!(model.model.linear_constraint_matrix.row_ids, c.value) + push!(model.model.linear_constraint_matrix.column_ids, term_index) + push!(model.model.linear_constraint_matrix.coefficients, terms_dict[term_index]) + end + + return nothing +end + + +function MOI.get( + model::Optimizer, + ::MOI.ConstraintSet, + c::MOI.ConstraintIndex{MOI.VariableIndex,MOI.LessThan{T}}, +) where {T<:Real} + if !MOI.is_empty(model) + # Retrieve the upper bound + return MOI.LessThan{T}(mode.model.variables.upper_bounds[c.value]) + end + + return nothing +end + +function MOI.get( + model::Optimizer, + ::MOI.ConstraintSet, + c::MOI.ConstraintIndex{MOI.VariableIndex,MOI.GreaterThan{T}}, +) where {T<:Real} + if !MOI.is_empty(model) + # Retrieve the lower bound + return MOI.GreaterThan{T}(model.model.variables.lower_bounds[c.value]) + end + + return nothing +end + +function MOI.get( + model::Optimizer, + ::MOI.ConstraintSet, + c::MOI.ConstraintIndex{MOI.VariableIndex,MOI.Interval{T}}, +) where {T<:Real} + if !MOI.is_empty(model) + # Retrieve the lower and upper bounds + return MOI.Interval{T}( + model.model.variables.lower_bounds[c.value], + model.model.variables.upper_bounds[c.value], + ) + end + + return nothing +end + +function MOI.get( + model::Optimizer, + ::MOI.ConstraintSet, + c::MOI.ConstraintIndex{MOI.VariableIndex,MOI.EqualTo{T}}, +) where {T<:Real} + if !MOI.is_empty(model) + # Retrieve the lower and upper bounds + return MOI.EqualTo{T}(model.model.variables.lower_bounds[c.value]) + end + + return nothing +end + +function MOI.get( + model::Optimizer, + ::MOI.ConstraintSet, + c::MOI.ConstraintIndex{MOI.VariableIndex,MOI.Integer}, +) + if !MOI.is_empty(model) + return MOI.Integer() + end + + return nothing +end + +function MOI.get( + model::Optimizer, + ::MOI.ConstraintSet, + c::MOI.ConstraintIndex{MOI.VariableIndex,MOI.ZeroOne}, +) + if !MOI.is_empty(model) + return MOI.ZeroOne() + end + + return nothing +end + +function MOI.get( + model::Optimizer, + ::MOI.ConstraintSet, + c::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},MOI.GreaterThan{T}}, +) where {T<:Real} + if !MOI.is_empty(model) + return MOI.GreaterThan{T}(model.model.linear_constraints.lower_bounds[c.value]) + end + + return nothing +end + + +function MOI.get( + model::Optimizer, + ::MOI.ConstraintSet, + c::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},MOI.LessThan{T}}, +) where {T<:Real} + if !MOI.is_empty(model) + return MOI.LessThan{T}(model.model.linear_constraints.upper_bounds[c.value]) + end + + return nothing +end + + +function MOI.get( + model::Optimizer, + ::MOI.ConstraintSet, + c::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},MOI.EqualTo{T}}, +) where {T<:Real} + if !MOI.is_empty(model) + return MOI.EqualTo{T}(model.model.linear_constraints.lower_bounds[c.value]) + end + + return nothing +end + +function MOI.get( + model::Optimizer, + ::MOI.ConstraintSet, + c::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},MOI.Interval{T}}, +) where {T<:Real} + if !MOI.is_empty(model) + return MOI.Interval{T}( + model.model.linear_constraints.lower_bounds[c.value], + model.model.linear_constraints.upper_bounds[c.value], + ) + end + + return nothing +end + +function MOI.set( + model::Optimizer, + ::MOI.ConstraintSet, + c::MOI.ConstraintIndex{MOI.VariableIndex,S}, + s::S, +) where {S<:SCALAR_SET} + optionally_initialize_model_and_parameters!(model) + + lower_bound, upper_bound = bounds(s) + + model.model.variables.lower_bounds[c.value] = lower_bound + model.model.variables.upper_bounds[c.value] = upper_bound + + return nothing +end + +function MOI.set( + model::Optimizer, + ::MOI.ConstraintSet, + c::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},S}, + s::S, +) where {T<:Real,S<:SCALAR_SET} + optionally_initialize_model_and_parameters!(model) + + lower_bound, upper_bound = bounds(s) + + model.model.linear_constraints.lower_bounds[c.value] = lower_bound + model.model.linear_constraints.upper_bounds[c.value] = upper_bound + + return nothing +end + + +function MOI.supports( + ::Optimizer, + ::MOI.ConstraintName, + ::Type{<:MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},<:SCALAR_SET}}, +) where {T<:Real} + return true +end + +function MOI.get( + model::Optimizer, + ::MOI.ConstraintName, + c::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},<:Any}, +) where {T<:Real} + if !MOI.is_empty(model) + return model.model.linear_constraints.names[c.value] + end + + return "" +end + +function MOI.set( + model::Optimizer, + ::MOI.ConstraintName, + c::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},<:Any}, + name::String, +) where {T<:Real} + in(name, model.model.linear_constraints.names) && + throw(ErrorException("Constraint with name \"$(name)\" already exists.")) + model.model.linear_constraints.names[c.value] = name + + return nothing +end + +function MOI.get( + model::Optimizer, + ::Type{MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},S}}, + constraint_name::String, +) where {T<:Real,S<:SCALAR_SET} + if !MOI.is_empty(model) + position_index = + findfirst(x -> x == constraint_name, model.model.linear_constraints.names) + if position_index == nothing + return nothing + end + constraint_index = MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},S}( + model.model.linear_constraints.ids[position_index], + ) + + in( + constraint_index, + model.constraint_indices_dict[SCALAR_SET_WITH_SCALAR_FUNCTION_CONSTRAINT_KEY], + ) && return constraint_index + end + + return nothing +end + +function MOI.is_valid( + model::Optimizer, + c::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},S}, +) where {T<:Real,S<:SCALAR_SET} + if !MOI.is_empty(model) + return in( + c, + model.constraint_indices_dict[SCALAR_SET_WITH_SCALAR_FUNCTION_CONSTRAINT_KEY], + ) + end + + return false +end + +function MOI.is_valid( + model::Optimizer, + c::MOI.ConstraintIndex{MOI.VariableIndex,MOI.ZeroOne}, +) + if !MOI.is_empty(model) + return in(c, model.constraint_indices_dict[ZERO_ONE_CONSTRAINT_KEY]) + end + + return false +end + + +function MOI.is_valid( + model::Optimizer, + c::MOI.ConstraintIndex{MOI.VariableIndex,MOI.Integer}, +) + if !MOI.is_empty(model) + return in(c, model.constraint_indices_dict[INTEGER_CONSTRAINT_KEY]) + end + + return false +end + +function MOI.is_valid( + model::Optimizer, + c::MOI.ConstraintIndex{MOI.VariableIndex,S}, +) where {S<:SCALAR_SET} + if !MOI.is_empty(model) + return in( + c, + model.constraint_indices_dict[SCALAR_SET_WITH_VARIABLE_INDEX_CONSTRAINT_KEY], + ) + end + + return false +end + + +function MOI.get(::Optimizer, ::MOI.ListOfConstraintAttributesSet{F,S}) where {F,S} + attributes = MOI.AbstractConstraintAttribute[] + if F != MOI.VariableIndex + return push!(attributes, MOI.ConstraintName()) + end + return attributes +end + +""" + + Objective Function + +""" +function MOI.set(model::Optimizer, ::MOI.ObjectiveSense, sense::MOI.OptimizationSense) + optionally_initialize_model_and_parameters!(model) + + if sense == MOI.MAX_SENSE + optionally_initialize_objective!(model) + model.model.objective.maximize = true + elseif sense == MOI.MIN_SENSE + optionally_initialize_objective!(model) + model.model.objective.maximize = false + else + # ObjectiveFunction is not considered. This is a feasibility problem. + model.model.objective = nothing + model.objective_set = false + end +end + +function MOI.get(model::Optimizer, ::MOI.ObjectiveSense) + if !MOI.is_empty(model) && !isnothing(model.model.objective) + model.objective_set && + return model.model.objective.maximize ? MOI.MAX_SENSE : MOI.MIN_SENSE + end + + return MOI.FEASIBILITY_SENSE +end + +MOI.supports(model::Optimizer, ::MOI.ObjectiveSense) = true + + +function MOI.get(model::Optimizer, ::MOI.ObjectiveFunctionType) + if !MOI.is_empty(model) && !isnothing(model.model.objective) + if iszero(model.model.objective.offset) && + length(model.model.objective.linear_coefficients.ids) == 1 && + length(model.model.objective.quadratic_coefficients.row_ids) == 0 + return MOI.VariableIndex + elseif length(model.model.objective.quadratic_coefficients.row_ids) > 0 + # TODO: b/386359419 Add support for quadratic objectives + return MOI.ScalarQuadraticFunction{Real} + # elseif length(model.model.objective.linear_coefficients.ids) > 0 + # return MOI.ScalarAffineFunction{Real} + else + # throw(error("Failed to get objective function type; no objective function found.")) + # The above has been commented out in favor of returning the ScalarAffineFunction + # by default. This function is being used in other tests and was throwing an error resulting + # in test failures. Other solvers, such as HiGHS seem to be returning the ScalarAffineFunction + # by default. + return MOI.ScalarAffineFunction{Real} + end + end + + return nothing +end + +function reset_objective!(model::Optimizer)::Nothing + model.model.objective.offset = zero(Float64) + model.model.objective.linear_coefficients = NewSparseDoubleVector() + model.model.objective.quadratic_coefficients = NewSparseDoubleMatrix() + model.model.objective.name = "" + model.model.objective.priority = zero(Int) + + return nothing +end + +# For example: Maximize `x` +function MOI.set( + model::Optimizer, + ::MOI.ObjectiveFunction{MOI.VariableIndex}, + objective_function::MOI.VariableIndex, +) + optionally_initialize_model_and_parameters!(model) + optionally_initialize_objective!(model) + + # `zero out` the existing objective function + reset_objective!(model) + + model.objective_set = true + + push!(model.model.objective.linear_coefficients.ids, objective_function.value) + push!(model.model.objective.linear_coefficients.values, one(Float64)) + + return nothing +end + +function MOI.get(model::Optimizer, ::MOI.ObjectiveFunction{MOI.VariableIndex}) + if !MOI.is_empty(model) && !isnothing(model.model.objective) + return MOI.VariableIndex(model.model.objective.linear_coefficients.ids[1]) + end + + return nothing +end + +function MOI.set( + model::Optimizer, + ::MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}, + objective_function::MOI.ScalarAffineFunction{T}, +) where {T<:Real} + optionally_initialize_model_and_parameters!(model) + optionally_initialize_objective!(model) + + # `zero out` the existing objective function + reset_objective!(model) + + model.objective_set = true + + terms = objective_function.terms + + terms_dict = get_terms_dict(terms) + + for term in keys(terms_dict) + push!(model.model.objective.linear_coefficients.ids, term) + push!(model.model.objective.linear_coefficients.values, terms_dict[term]) + end + + model.model.objective.offset = Float64(objective_function.constant) + + return nothing +end + +function MOI.get( + model::Optimizer, + ::MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}, +) where {T<:Real} + if !MOI.is_empty(model) && !isnothing(model.model.objective) + scalar_affine_terms = map( + (linear_coefficient) -> MOI.ScalarAffineTerm{T}( + linear_coefficient[1], + MOI.VariableIndex(linear_coefficient[2]), + ), + zip( + model.model.objective.linear_coefficients.values, + model.model.objective.linear_coefficients.ids, + ), + ) + return MOI.ScalarAffineFunction{T}( + scalar_affine_terms, + model.model.objective.offset, + ) + end + + return nothing +end + + +# TODO: b/386359419 Add support for quadratic objectives +function MOI.set( + model::Optimizer, + ::MOI.ObjectiveFunction{T}, + objective_function::MOI.ScalarQuadraticFunction{T}, +) where {T<:Real} + optionally_initialize_model_and_parameters!(model) + optionally_initialize_objective!(model) + + return nothing +end + +function MOI.supports( + ::Optimizer, + ::MOI.ObjectiveFunction{F}, +) where { + F<:Union{ + MOI.VariableIndex, + MOI.ScalarAffineFunction{<:Real}, + MOI.ScalarQuadraticFunction{<:Real}, + }, +} + return true +end + +function MOI.supports( + ::Optimizer, + ::MOI.ObjectiveFunction{F}, +) where {F<:MOI.ScalarQuadraticFunction{<:Real}} + return true +end + +function MOI.optimize!(model::Optimizer) + # TODO: b/384662497 implement this + return nothing +end diff --git a/ortools/julia/ORTools.jl/src/moi_wrapper/Type_wrappers.jl b/ortools/julia/ORTools.jl/src/moi_wrapper/Type_wrappers.jl new file mode 100644 index 0000000000..10a8ae126c --- /dev/null +++ b/ortools/julia/ORTools.jl/src/moi_wrapper/Type_wrappers.jl @@ -0,0 +1,1853 @@ +using ORToolsGenerated +const OperationsResearch = ORToolsGenerated.Proto.operations_research +const MathOpt = OperationsResearch.math_opt +const SolverType = MathOpt.SolverTypeProto + +""" +Given the nature of the fields, we are using an alias for the VariablesProto struct. +""" +const VariablesProto = MathOpt.VariablesProto +const NewVariablesProto = + () -> VariablesProto( + Vector{Int64}(), + Vector{Float64}(), + Vector{Float64}(), + Vector{Bool}(), + Vector{String}(), + ) + +""" +Given the nature of the fields, we are using an alias for the VariablesProto struct. +""" +const SparseDoubleVectorProto = MathOpt.SparseDoubleVectorProto +const NewSparseDoubleVector = + () -> SparseDoubleVectorProto(Vector{Int64}(), Vector{Float64}()) + +""" +Given the nature of the fields, we are using an alias for the VariablesProto struct. +""" +const SparseDoubleMatrixProto = MathOpt.SparseDoubleMatrixProto +const NewSparseDoubleMatrix = + () -> SparseDoubleMatrixProto(Vector{Int64}(), Vector{Int64}(), Vector{Float64}()) + +""" +Given the nature of the fields, we are using an alias for the VariablesProto struct. +""" +const LinearConstraintsProto = MathOpt.LinearConstraintsProto +const NewLinearConstraints = + () -> LinearConstraintsProto( + Vector{Int64}(), + Vector{Float64}(), + Vector{Float64}(), + Vector{String}(), + ) + +const Duration = MathOpt.google.protobuf.Duration +const Emphasis = MathOpt.EmphasisProto +const LPAlgorithm = MathOpt.LPAlgorithmProto + +## Gscip parameters types +const GScipParameters_MetaParamValue = + OperationsResearch.var"GScipParameters.MetaParamValue" +const GScipParameters_Emphasis = OperationsResearch.var"GScipParameters.Emphasis" + +## Gurobi parameters types. +const GurobiParametersProto_Parameter = MathOpt.var"GurobiParametersProto.Parameter" + +## Glop parameters types. +const GlopParameters_PricingRule = OperationsResearch.glop.var"GlopParameters.PricingRule" +const GlopParameters_SolverBehavior = + OperationsResearch.glop.var"GlopParameters.SolverBehavior" +const GlopParameters_CostScalingAlgorithm = + OperationsResearch.glop.var"GlopParameters.CostScalingAlgorithm" +const GlopParameters_ScalingAlgorithm = + OperationsResearch.glop.var"GlopParameters.ScalingAlgorithm" +const GlopParameters_InitialBasisHeuristic = + OperationsResearch.glop.var"GlopParameters.InitialBasisHeuristic" + +## Sat parameter types. +const AbstractSatParameters = OperationsResearch.sat.var"##AbstractSatParameters" +const SatParameters_VariableOrder = OperationsResearch.sat.var"SatParameters.VariableOrder" +const SatParameters_Polarity = OperationsResearch.sat.var"SatParameters.Polarity" +const SatParameters_ConflictMinimizationAlgorithm = + OperationsResearch.sat.var"SatParameters.ConflictMinimizationAlgorithm" +const SatParameters_BinaryMinizationAlgorithm = + OperationsResearch.sat.var"SatParameters.BinaryMinizationAlgorithm" +const SatParameters_ClauseProtection = + OperationsResearch.sat.var"SatParameters.ClauseProtection" +const SatParameters_ClauseOrdering = + OperationsResearch.sat.var"SatParameters.ClauseOrdering" +const SatParameters_RestartAlgorithm = + OperationsResearch.sat.var"SatParameters.RestartAlgorithm" +const SatParameters_MaxSatAssumptionOrder = + OperationsResearch.sat.var"SatParameters.MaxSatAssumptionOrder" +const SatParameters_MaxSatStratificationAlgorithm = + OperationsResearch.sat.var"SatParameters.MaxSatStratificationAlgorithm" +const SatParameters_SearchBranching = + OperationsResearch.sat.var"SatParameters.SearchBranching" +const SatParameters_SharedTreeSplitStrategy = + OperationsResearch.sat.var"SatParameters.SharedTreeSplitStrategy" +const SatParameters_FPRoundingMethod = + OperationsResearch.sat.var"SatParameters.FPRoundingMethod" + +## Scalar Set constraints with bounds +const SCALAR_SET = Union{ + MOI.GreaterThan{<:Real}, + MOI.LessThan{<:Real}, + MOI.EqualTo{<:Real}, + MOI.Interval{<:Real}, +} + +## Bounds of scalar set Constraints +function bounds(s::MOI.GreaterThan{T}) where {T<:Real} + return s.lower, typemax(T) +end + +function bounds(s::MOI.LessThan{T}) where {T<:Real} + return typemin(T), s.upper +end + +function bounds(s::MOI.EqualTo{T}) where {T<:Real} + return s.value, s.value +end + +function bounds(s::MOI.Interval{T}) where {T<:Real} + return s.lower, s.upper +end + + +""" + Mutable wrapper struct for the objective proto struct. +""" +mutable struct Objective + maximize::Bool + offset::Float64 + linear_coefficients::Union{Nothing,SparseDoubleVectorProto} + quadratic_coefficients::Union{Nothing,SparseDoubleMatrixProto} + name::String + priority::Int64 + + function Objective(; + maximize = false, + offset = zero(Float64), + linear_coefficients = NewSparseDoubleVector(), + quadratic_coefficients = NewSparseDoubleMatrix(), + name = "", + priority = zero(Int64), + ) + new(maximize, offset, linear_coefficients, quadratic_coefficients, name, priority) + end +end + +function to_proto_struct(objective::Objective)::MathOpt.ObjectiveProto + return MathOpt.ObjectiveProto( + objective.maximize, + objective.offset, + objective.linear_coefficients, + objective.quadratic_coefficients, + objective.name, + objective.priority, + ) +end + + +""" + Mutable wrapper struct for the QuadraticConstraintProto struct. +""" +mutable struct QuadraticConstraint + linear_terms::Union{Nothing,SparseDoubleVectorProto} + quadratic_terms::Union{Nothing,SparseDoubleMatrixProto} + lower_bound::Float64 + upper_bound::Float64 + name::String + + function QuadraticConstraint(; + linear_terms = NewSparseDoubleVector(), + quadratic_terms = NewSparseDoubleMatrix(), + lower_bound = typemin(Float64), + upper_bound = typemax(Float64), + name = "", + ) + new(linear_terms, quadratic_terms, lower_bound, upper_bound, name) + end +end + +function to_proto_struct( + quadratic_constraint::QuadraticConstraint, +)::MathOpt.QuadraticConstraintProto + return MathOpt.QuadraticConstraintProto( + quadratic_constraint.linear_terms, + quadratic_constraint.quadratic_terms, + quadratic_constraint.lower_bound, + quadratic_constraint.upper_bound, + quadratic_constraint.name, + ) +end + + +""" + Mutable wrapper struct for the LinearExpressionProto struct. +""" +mutable struct LinearExpression + id::Vector{Int64} + coefficients::Vector{Float64} + offset::Float64 + + function LinearExpression(; + id = Vector{Int64}(), + coefficients = Vector{Float64}(), + offset = zero(Float64), + ) + new(id, coefficients, offset) + end +end + +function to_proto_struct(linear_expression::LinearExpression)::MathOpt.LinearExpressionProto + return MathOpt.LinearExpressionProto( + linear_expression.id, + linear_expression.coefficients, + linear_expression.offset, + ) +end + + +""" + Mutable wrapper struct for the SecondOrderConeConstraintProto struct. +""" +mutable struct SecondOrderConeConstraint + upper_bound::Union{Nothing,LinearExpression} + arguments_to_norm::Vector{LinearExpression} + name::String + + function SecondOrderConeConstraint(; + upper_bound = LinearExpression(), + arguments_to_norm = Vector{LinearExpression}(), + name = "", + ) + new(upper_bound, arguments_to_norm, name) + end +end + +function to_proto_struct( + second_order_cone_constraints::SecondOrderConeConstraint, +)::MathOpt.SecondOrderConeConstraintProto + return MathOpt.SecondOrderConeConstraintProto( + second_order_cone_constraints.upper_bound, + second_order_cone_constraints.arguments_to_norm, + second_order_cone_constraints.name, + ) +end + + +""" + Mutable wrapper struct for the SosConstraintProto struct. +""" +mutable struct SosConstraint + expressions::Vector{LinearExpression} + weights::Vector{Float64} + name::String + + function SosConstraint(; + expressions = Vector{LinearExpression}(), + weights = Vector{Float64}(), + name = "", + ) + new(expressions, weights, name) + end +end + +function to_proto_struct(sos_constraint::SosConstraint)::MathOpt.SosConstraintProto + return MathOpt.SosConstraintProto( + sos_constraint.expressions, + sos_constraint.weights, + sos_constraint.name, + ) +end + + +""" + Mutable wrapper struct for the IndicatorConstraintProto struct. +""" +mutable struct IndicatorConstraint + indicator_id::Int64 + activate_on_zero::Bool + expression::Union{Nothing,SparseDoubleVectorProto} + lower_bound::Float64 + upper_bound::Float64 + name::String + + function IndicatorConstraint(; + indicator_id = zero(Int64), + activate_on_zero = false, + expression = NewSparseDoubleVector(), + lower_bound = typemin(Float64), + upper_bound = typemax(Float64), + name = "", + ) + new(indicator_id, activate_on_zero, expression, lower_bound, upper_bound, name) + end +end + +function to_proto_struct( + indicator_constraint::IndicatorConstraint, +)::MathOpt.IndicatorConstraint + return MathOpt.IndicatorConstraintProto( + indicator_constraint.indicator_id, + indicator_constraint.activate_on_zero, + indicator_constraint.expression, + indicator_constraint.lower_bound, + indicator_constraint.upper_bound, + indicator_constraint.name, + ) +end + +""" + Mutable wrapper struct for the ModelProto struct. +""" +mutable struct Model + name::String + variables::Union{Nothing,VariablesProto} + objective::Union{Nothing,Objective} + auxiliary_objectives::Dict{Int64,Objective} + linear_constraints::Union{Nothing,LinearConstraintsProto} + linear_constraint_matrix::Union{Nothing,SparseDoubleMatrixProto} + quadratic_constraints::Dict{Int64,QuadraticConstraint} + second_order_cone_constraints::Dict{Int64,SecondOrderConeConstraint} + sos1_constraints::Dict{Int64,SosConstraint} + sos2_constraints::Dict{Int64,SosConstraint} + indicator_constraints::Dict{Int64,IndicatorConstraint} + + function Model(; + name = "", + variables = NewVariablesProto(), + objective = Objective(), + auxiliary_objectives = Dict{Int64,Objective}(), + linear_constraints = NewLinearConstraints(), + linear_constraint_matrix = NewSparseDoubleMatrix(), + quadratic_constraints = Dict{Int64,QuadraticConstraint}(), + second_order_cone_constraints = Dict{Int64,SecondOrderConeConstraint}(), + sos1_constraints = Dict{Int64,SosConstraint}(), + sos2_constraints = Dict{Int64,SosConstraint}(), + indicator_constraints = Dict{Int64,IndicatorConstraint}(), + ) + new( + name, + variables, + objective, + auxiliary_objectives, + linear_constraints, + linear_constraint_matrix, + quadratic_constraints, + second_order_cone_constraints, + sos1_constraints, + sos2_constraints, + indicator_constraints, + ) + end +end + +function to_proto_struct(model::Model)::MathOpt.ModelProto + auxiliary_objectives = Dict{Int64,MathOpt.ObjectiveProto}() + for (id, objective) in model.auxiliary_objectives + auxiliary_objectives[id] = to_proto_struct(objective) + end + + quadratic_constraints = Dict{Int64,MathOpt.QuadraticConstraintProto}() + for (id, quadratic_constraint) in model.quadratic_constraints + quadratic_constraints[id] = to_proto_struct(quadratic_constraint) + end + + second_order_cone_constraints = Dict{Int64,MathOpt.SecondOrderConeConstraintProto}() + for (id, second_order_cone_constraint) in model.second_order_cone_constraints + second_order_cone_constraints[id] = to_proto_struct(second_order_cone_constraint) + end + + sos1_constraints = Dict{Int64,MathOpt.SosConstraintProto}() + for (id, sos1_constraint) in model.sos1_constraints + sos1_constraints[id] = to_proto_struct(sos1_constraint) + end + + sos2_constraints = Dict{Int64,MathOpt.SosConstraintProto}() + for (id, sos2_constraint) in model.sos2_constraints + sos2_constraints[id] = to_proto_struct(sos2_constraint) + end + + indicator_constraints = Dict{Int64,MathOpt.IndicatorConstraintProto}() + for (id, indicator_constraint) in model.indicator_constraints + indicator_constraints[id] = to_proto_struct(indicator_constraint) + end + + return MathOpt.ModelProto( + model.name, + to_proto_struct(model.variables), + to_proto_struct(model.objective), + auxiliary_objectives, + to_proto_struct(model.linear_constraints), + to_proto_struct(model.linear_constraint_matrix), + quadratic_constraints, + second_order_cone_constraints, + sos1_constraints, + sos2_constraints, + indicator_constraints, + ) +end + +""" + Mutable wrapper struct for the GscipParameters struct. +""" +mutable struct GScipParameters + emphasis::GScipParameters_Emphasis.T + heuristics::GScipParameters_MetaParamValue.T + presolve::GScipParameters_MetaParamValue.T + separating::GScipParameters_MetaParamValue.T + bool_params::Dict{String,Bool} + int_params::Dict{String,Int32} + long_params::Dict{String,Int64} + real_params::Dict{String,Float64} + char_params::Dict{String,String} + string_params::Dict{String,String} + silence_output::Bool + print_detailed_solving_stats::Bool + print_scip_model::Bool + search_logs_filename::String + detailed_solving_stats_filename::String + scip_model_filename::String + num_solutions::Int32 + objective_limit::Float64 + + function GScipParameters(; + emphasis = GScipParameters_Emphasis.DEFAULT_EMPHASIS, + heuristics = GScipParameters_MetaParamValue.DEFAULT_META_PARAM_VALUE, + presolve = GScipParameters_MetaParamValue.DEFAULT_META_PARAM_VALUE, + separating = GScipParameters_MetaParamValue.DEFAULT_META_PARAM_VALUE, + bool_params = Dict{String,Bool}(), + int_params = Dict{String,Int32}(), + long_params = Dict{String,Int64}(), + real_params = Dict{String,Float64}(), + char_params = Dict{String,String}(), + string_params = Dict{String,String}(), + silence_output = false, + print_detailed_solving_stats = false, + print_scip_model = false, + search_logs_filename = "", + detailed_solving_stats_filename = "", + scip_model_filename = "", + num_solutions = zero(Int32), + objective_limit = zero(Float64), + ) + new( + emphasis, + heuristics, + presolve, + separating, + bool_params, + int_params, + long_params, + real_params, + char_params, + string_params, + silence_output, + print_detailed_solving_stats, + print_scip_model, + search_logs_filename, + detailed_solving_stats_filename, + scip_model_filename, + num_solutions, + objective_limit, + ) + end +end + +function to_proto_struct( + gscip_parameters::GScipParameters, +)::OperationsResearch.GScipParameters + return OperationsResearch.GScipParameters( + gscip_parameters.emphasis, + gscip_parameters.heuristics, + gscip_parameters.presolve, + gscip_parameters.separating, + gscip_parameters.bool_params, + gscip_parameters.int_params, + gscip_parameters.long_params, + gscip_parameters.real_params, + gscip_parameters.char_params, + gscip_parameters.string_params, + gscip_parameters.silence_output, + gscip_parameters.print_detailed_solving_stats, + gscip_parameters.print_scip_model, + gscip_parameters.search_logs_filename, + gscip_parameters.detailed_solving_stats_filename, + gscip_parameters.scip_model_filename, + gscip_parameters.num_solutions, + gscip_parameters.objective_limit, + ) +end + +""" + Mutable wrapper struct for the GurobiParametersProto.Parameter struct. +""" +mutable struct GurobiParameters_Parameter + name::String + value::String +end + +function to_proto_struct( + gurobi_parameters_parameter::GurobiParameters_Parameter, +)::GurobiParametersProto_Parameter + return GurobiParametersProto_Parameter( + gurobi_parameters_parameter.name, + gurobi_parameters_parameter.value, + ) +end + +""" + Mutable wrapper struct for the GurobiParameters struct. +""" +mutable struct GurobiParameters + parameters::Vector{GurobiParameters_Parameter} + + function GurobiParameters(; parameters = Vector{GurobiParameters_Parameter}()) + new(parameters) + end +end + +function to_proto_struct(gurobi_parameters::GurobiParameters)::MathOpt.GurobiParametersProto + parameters = + Vector{GurobiParametersProto_Parameter}(length(gurobi_parameters.parameters)) + for (index, parameter) in enumerate(gurobi_parameters.parameters) + parameters[index] = to_proto_struct(parameter) + end + + return MathOpt.GurobiParametersProto(gurobi_parameters.parameters) +end + +""" + Mutable wrapper struct for the GlopParameters struct. +""" +mutable struct GlopParameters + scaling_method::GlopParameters_ScalingAlgorithm.T + feasibility_rule::GlopParameters_PricingRule.T + optimization_rule::GlopParameters_PricingRule.T + refactorization_threshold::Float64 + recompute_reduced_costs_threshold::Float64 + recompute_edges_norm_threshold::Float64 + primal_feasibility_tolerance::Float64 + dual_feasibility_tolerance::Float64 + ratio_test_zero_threshold::Float64 + harris_tolerance_ratio::Float64 + small_pivot_threshold::Float64 + minimum_acceptable_pivot::Float64 + drop_tolerance::Float64 + use_scaling::Bool + cost_scaling::GlopParameters_CostScalingAlgorithm.T + initial_basis::GlopParameters_InitialBasisHeuristic.T + use_transposed_matrix::Bool + basis_refactorization_period::Int32 + dynamically_adjust_refactorization_period::Bool + solve_dual_problem::GlopParameters_SolverBehavior.T + dualizer_threshold::Float64 + solution_feasibility_tolerance::Float64 + provide_strong_optimal_guarantee::Bool + change_status_to_imprecise::Bool + max_number_of_reoptimizations::Float64 + lu_factorization_pivot_threshold::Float64 + max_time_in_seconds::Float64 + max_deterministic_time::Float64 + max_number_of_iterations::Int64 + markowitz_zlatev_parameter::Int32 + markowitz_singularity_threshold::Float64 + use_dual_simplex::Bool + allow_simplex_algorithm_change::Bool + devex_weights_reset_period::Int32 + use_preprocessing::Bool + use_middle_product_form_update::Bool + initialize_devex_with_column_norms::Bool + exploit_singleton_column_in_initial_basis::Bool + dual_small_pivot_threshold::Float64 + preprocessor_zero_tolerance::Float64 + objective_lower_limit::Float64 + objective_upper_limit::Float64 + degenerate_ministep_factor::Float64 + random_seed::Int32 + num_omp_threads::Int32 + perturb_costs_in_dual_simplex::Bool + use_dedicated_dual_feasibility_algorithm::Bool + relative_cost_perturbation::Float64 + relative_max_cost_perturbation::Float64 + initial_condition_number_threshold::Float64 + log_search_progress::Bool + log_to_stdout::Bool + crossover_bound_snapping_distance::Float64 + push_to_vertex::Bool + use_implied_free_preprocessor::Bool + max_valid_magnitude::Float64 + dual_price_prioritize_norm::Bool + + function GlopParameters(; + scaling_method = GlopParameters_ScalingAlgorithm.EQUILIBRATION, + feasibility_rule = GlopParameters_PricingRule.STEEPEST_EDGE, + optimization_rule = GlopParameters_PricingRule.STEEPEST_EDGE, + refactorization_threshold = Float64(1e-9), + recompute_reduced_costs_threshold = Float64(1e-8), + recompute_edges_norm_threshold = Float64(100.0), + primal_feasibility_tolerance = Float64(1e-8), + dual_feasibility_tolerance = Float64(1e-8), + ratio_test_zero_threshold = Float64(1e-9), + harris_tolerance_ratio = Float64(0.5), + small_pivot_threshold = Float64(1e-6), + minimum_acceptable_pivot = Float64(1e-6), + drop_tolerance = Float64(1e-14), + use_scaling = true, + cost_scaling = GlopParameters_CostScalingAlgorithm.CONTAIN_ONE_COST_SCALING, + initial_basis = GlopParameters_InitialBasisHeuristic.TRIANGULAR, + use_transposed_matrix = true, + basis_refactorization_period = Int32(64), + dynamically_adjust_refactorization_period = true, + solve_dual_problem = GlopParameters_SolverBehavior.LET_SOLVER_DECIDE, + dualizer_threshold = Float64(1.5), + solution_feasibility_tolerance = Float64(1e-6), + provide_strong_optimal_guarantee = true, + change_status_to_imprecise = true, + max_number_of_reoptimizations = Float64(40), + lu_factorization_pivot_threshold = Float64(0.01), + max_time_in_seconds = Float64(Inf), + max_deterministic_time = Float64(Inf), + max_number_of_iterations = Int64(-1), + markowitz_zlatev_parameter = Int32(3), + markowitz_singularity_threshold = Float64(1e-15), + use_dual_simplex = false, + allow_simplex_algorithm_change = false, + devex_weights_reset_period = Int32(150), + use_preprocessing = true, + use_middle_product_form_update = true, + initialize_devex_with_column_norms = true, + exploit_singleton_column_in_initial_basis = true, + dual_small_pivot_threshold = Float64(1e-4), + preprocessor_zero_tolerance = Float64(1e-9), + objective_lower_limit = Float64(-Inf), + objective_upper_limit = Float64(Inf), + degenerate_ministep_factor = Float64(0.01), + random_seed = Int32(1), + num_omp_threads = Int32(1), + perturb_costs_in_dual_simplex = false, + use_dedicated_dual_feasibility_algorithm = true, + relative_cost_perturbation = Float64(1e-5), + relative_max_cost_perturbation = Float64(1e-7), + initial_condition_number_threshold = Float64(1e50), + log_search_progress = false, + log_to_stdout = true, + crossover_bound_snapping_distance = Float64(Inf), + push_to_vertex = true, + use_implied_free_preprocessor = true, + max_valid_magnitude = Float64(1e30), + dual_price_prioritize_norm = false, + ) + new( + scaling_method, + feasibility_rule, + optimization_rule, + refactorization_threshold, + recompute_reduced_costs_threshold, + recompute_edges_norm_threshold, + primal_feasibility_tolerance, + dual_feasibility_tolerance, + ratio_test_zero_threshold, + harris_tolerance_ratio, + small_pivot_threshold, + minimum_acceptable_pivot, + drop_tolerance, + use_scaling, + cost_scaling, + initial_basis, + use_transposed_matrix, + basis_refactorization_period, + dynamically_adjust_refactorization_period, + solve_dual_problem, + dualizer_threshold, + solution_feasibility_tolerance, + provide_strong_optimal_guarantee, + change_status_to_imprecise, + max_number_of_reoptimizations, + lu_factorization_pivot_threshold, + max_time_in_seconds, + max_deterministic_time, + max_number_of_iterations, + markowitz_zlatev_parameter, + markowitz_singularity_threshold, + use_dual_simplex, + allow_simplex_algorithm_change, + devex_weights_reset_period, + use_preprocessing, + use_middle_product_form_update, + initialize_devex_with_column_norms, + exploit_singleton_column_in_initial_basis, + dual_small_pivot_threshold, + preprocessor_zero_tolerance, + objective_lower_limit, + objective_upper_limit, + degenerate_ministep_factor, + random_seed, + num_omp_threads, + perturb_costs_in_dual_simplex, + use_dedicated_dual_feasibility_algorithm, + relative_cost_perturbation, + relative_max_cost_perturbation, + initial_condition_number_threshold, + log_search_progress, + log_to_stdout, + crossover_bound_snapping_distance, + push_to_vertex, + use_implied_free_preprocessor, + max_valid_magnitude, + dual_price_prioritize_norm, + ) + end +end + +function to_proto_struct( + glop_parameters::GlopParameters, +)::OperationsResearch.glop.GlopParameters + return OperationsResearch.glop.GlopParameters( + glop_parameters.scaling_method, + glop_parameters.feasibility_rule, + glop_parameters.optimization_rule, + glop_parameters.refactorization_threshold, + glop_parameters.recompute_reduced_costs_threshold, + glop_parameters.recompute_edges_norm_threshold, + glop_parameters.primal_feasibility_tolerance, + glop_parameters.dual_feasibility_tolerance, + glop_parameters.ratio_test_zero_threshold, + glop_parameters.harris_tolerance_ratio, + glop_parameters.small_pivot_threshold, + glop_parameters.minimum_acceptable_pivot, + glop_parameters.drop_tolerance, + glop_parameters.use_scaling, + glop_parameters.cost_scaling, + glop_parameters.initial_basis, + glop_parameters.use_transposed_matrix, + glop_parameters.basis_refactorization_period, + glop_parameters.dynamically_adjust_refactorization_period, + glop_parameters.solve_dual_problem, + glop_parameters.dualizer_threshold, + glop_parameters.solution_feasibility_tolerance, + glop_parameters.provide_strong_optimal_guarantee, + glop_parameters.change_status_to_imprecise, + glop_parameters.max_number_of_reoptimizations, + glop_parameters.lu_factorization_pivot_threshold, + glop_parameters.max_time_in_seconds, + glop_parameters.max_deterministic_time, + glop_parameters.max_number_of_iterations, + glop_parameters.markowitz_zlatev_parameter, + glop_parameters.markowitz_singularity_threshold, + glop_parameters.use_dual_simplex, + glop_parameters.allow_simplex_algorithm_change, + glop_parameters.devex_weights_reset_period, + glop_parameters.use_preprocessing, + glop_parameters.use_middle_product_form_update, + glop_parameters.initialize_devex_with_column_norms, + glop_parameters.exploit_singleton_column_in_initial_basis, + glop_parameters.dual_small_pivot_threshold, + glop_parameters.preprocessor_zero_tolerance, + glop_parameters.objective_lower_limit, + glop_parameters.objective_upper_limit, + glop_parameters.degenerate_ministep_factor, + glop_parameters.random_seed, + glop_parameters.num_omp_threads, + glop_parameters.perturb_costs_in_dual_simplex, + glop_parameters.use_dedicated_dual_feasibility_algorithm, + glop_parameters.relative_cost_perturbation, + glop_parameters.relative_max_cost_perturbation, + glop_parameters.initial_condition_number_threshold, + glop_parameters.log_search_progress, + glop_parameters.log_to_stdout, + glop_parameters.crossover_bound_snapping_distance, + glop_parameters.push_to_vertex, + glop_parameters.use_implied_free_preprocessor, + glop_parameters.max_valid_magnitude, + glop_parameters.dual_price_prioritize_norm, + ) +end + +""" + Mutable wrapper struct for the SatParameters struct. +""" +mutable struct SatParameters <: AbstractSatParameters + name::String + preferred_variable_order::SatParameters_VariableOrder.T + initial_polarity::SatParameters_Polarity.T + use_phase_saving::Bool + polarity_rephase_increment::Int32 + random_polarity_ratio::Float64 + random_branches_ratio::Float64 + use_erwa_heuristic::Bool + initial_variables_activity::Float64 + also_bump_variables_in_conflict_reasons::Bool + minimization_algorithm::SatParameters_ConflictMinimizationAlgorithm.T + binary_minimization_algorithm::SatParameters_BinaryMinizationAlgorithm.T + subsumption_during_conflict_analysis::Bool + clause_cleanup_period::Int32 + clause_cleanup_target::Int32 + clause_cleanup_ratio::Float64 + clause_cleanup_protection::SatParameters_ClauseProtection.T + clause_cleanup_lbd_bound::Int32 + clause_cleanup_ordering::SatParameters_ClauseOrdering.T + pb_cleanup_increment::Int32 + pb_cleanup_ratio::Float64 + minimize_with_propagation_restart_period::Int32 + minimize_with_propagation_num_decisions::Int32 + variable_activity_decay::Float64 + max_variable_activity_value::Float64 + glucose_max_decay::Float64 + glucose_decay_increment::Float64 + glucose_decay_increment_period::Int32 + clause_activity_decay::Float64 + max_clause_activity_value::Float64 + restart_algorithms::Vector{SatParameters_RestartAlgorithm.T} + default_restart_algorithms::String + restart_period::Int32 + restart_running_window_size::Int32 + restart_dl_average_ratio::Float64 + restart_lbd_average_ratio::Float64 + use_blocking_restart::Bool + blocking_restart_window_size::Int32 + blocking_restart_multiplier::Float64 + num_conflicts_before_strategy_changes::Int32 + strategy_change_increase_ratio::Float64 + max_time_in_seconds::Float64 + max_deterministic_time::Float64 + max_number_of_conflicts::Int64 + max_memory_in_mb::Int64 + absolute_gap_limit::Float64 + relative_gap_limit::Float64 + random_seed::Int32 + permute_variable_randomly::Bool + permute_presolve_constraint_order::Bool + use_absl_random::Bool + log_search_progress::Bool + log_frequency_in_seconds::Float64 + model_reduction_log_frequency_in_seconds::Float64 + log_subsolver_statistics::Bool + log_prefix::String + log_to_stdout::Bool + log_to_response::Bool + use_pb_resolution::Bool + minimize_reduction_during_pb_resolution::Bool + count_assumption_levels_in_lbd::Bool + presolve_bve_threshold::Int32 + presolve_bve_clause_weight::Int32 + probing_deterministic_time_limit::Float64 + presolve_probing_deterministic_time_limit::Float64 + presolve_blocked_clause::Bool + presolve_use_bva::Bool + presolve_bva_threshold::Int32 + max_presolve_iterations::Int32 + cp_model_presolve::Bool + cp_model_probing_level::Int32 + cp_model_use_sat_presolve::Bool + use_sat_inprocessing::Bool + detect_table_with_cost::Bool + table_compression_level::Int32 + expand_alldiff_constraints::Bool + expand_reservoir_constraints::Bool + disable_constraint_expansion::Bool + encode_complex_linear_constraint_with_integer::Bool + merge_no_overlap_work_limit::Float64 + merge_at_most_one_work_limit::Float64 + presolve_substitution_level::Int32 + presolve_extract_integer_enforcement::Bool + presolve_inclusion_work_limit::Int64 + ignore_names::Bool + infer_all_diffs::Bool + find_big_linear_overlap::Bool + num_workers::Int32 + num_search_workers::Int32 + min_num_lns_workers::Int32 + subsolvers::Vector{String} + extra_subsolvers::Vector{String} + ignore_subsolvers::Vector{String} + subsolver_params::Vector{<:SatParameters} + interleave_search::Bool + interleave_batch_size::Int32 + share_objective_bounds::Bool + share_level_zero_bounds::Bool + share_binary_clauses::Bool + debug_postsolve_with_full_solver::Bool + debug_max_num_presolve_operations::Int32 + debug_crash_on_bad_hint::Bool + use_optimization_hints::Bool + minimize_core::Bool + find_multiple_cores::Bool + cover_optimization::Bool + max_sat_assumption_order::SatParameters_MaxSatAssumptionOrder.T + max_sat_reverse_assumption_order::Bool + max_sat_stratification::SatParameters_MaxSatStratificationAlgorithm.T + propagation_loop_detection_factor::Float64 + use_precedences_in_disjunctive_constraint::Bool + max_size_to_create_precedence_literals_in_disjunctive::Int32 + use_strong_propagation_in_disjunctive::Bool + use_overload_checker_in_cumulative::Bool + use_timetable_edge_finding_in_cumulative::Bool + use_hard_precedences_in_cumulative::Bool + exploit_all_precedences::Bool + use_disjunctive_constraint_in_cumulative::Bool + use_timetabling_in_no_overlap_2d::Bool + use_energetic_reasoning_in_no_overlap_2d::Bool + use_pairwise_reasoning_in_no_overlap_2d::Bool + use_dual_scheduling_heuristics::Bool + linearization_level::Int32 + boolean_encoding_level::Int32 + max_domain_size_when_encoding_eq_neq_constraints::Int32 + max_num_cuts::Int32 + cut_level::Int32 + only_add_cuts_at_level_zero::Bool + add_objective_cut::Bool + add_cg_cuts::Bool + add_mir_cuts::Bool + add_zero_half_cuts::Bool + add_clique_cuts::Bool + max_all_diff_cut_size::Int32 + add_lin_max_cuts::Bool + max_integer_rounding_scaling::Int32 + add_lp_constraints_lazily::Bool + root_lp_iterations::Int32 + min_orthogonality_for_lp_constraints::Float64 + max_cut_rounds_at_level_zero::Int32 + max_consecutive_inactive_count::Int32 + cut_max_active_count_value::Float64 + cut_active_count_decay::Float64 + cut_cleanup_target::Int32 + new_constraints_batch_size::Int32 + search_branching::SatParameters_SearchBranching.T + hint_conflict_limit::Int32 + repair_hint::Bool + fix_variables_to_their_hinted_value::Bool + exploit_integer_lp_solution::Bool + exploit_all_lp_solution::Bool + exploit_best_solution::Bool + exploit_relaxation_solution::Bool + exploit_objective::Bool + probing_period_at_root::Int64 + use_probing_search::Bool + use_shaving_in_probing_search::Bool + shaving_search_deterministic_time::Float64 + use_objective_lb_search::Bool + use_objective_shaving_search::Bool + pseudo_cost_reliability_threshold::Int64 + optimize_with_core::Bool + optimize_with_lb_tree_search::Bool + binary_search_num_conflicts::Int32 + optimize_with_max_hs::Bool + test_feasibility_jump::Bool + feasibility_jump_max_num_values_scanned::Int64 + feasibility_jump_protect_linear_feasibility::Bool + feasibility_jump_decay::Float64 + feasibility_jump_var_randomization_probability::Float64 + feasibility_jump_var_perburbation_range_ratio::Float64 + feasibility_jump_enable_restarts::Bool + num_violation_ls::Int32 + violation_ls_perturbation_period::Int32 + shared_tree_num_workers::Int32 + use_shared_tree_search::Bool + shared_tree_worker_objective_split_probability::Float64 + shared_tree_max_nodes_per_worker::Int32 + shared_tree_split_strategy::SatParameters_SharedTreeSplitStrategy.T + enumerate_all_solutions::Bool + keep_all_feasible_solutions_in_presolve::Bool + fill_tightened_domains_in_response::Bool + fill_additional_solutions_in_response::Bool + instantiate_all_variables::Bool + auto_detect_greater_than_at_least_one_of::Bool + stop_after_first_solution::Bool + stop_after_presolve::Bool + stop_after_root_propagation::Bool + use_lns_only::Bool + solution_pool_size::Int32 + use_rins_lns::Bool + use_feasibility_pump::Bool + use_lb_relax_lns::Bool + fp_rounding::SatParameters_FPRoundingMethod.T + diversify_lns_params::Bool + randomize_search::Bool + search_randomization_tolerance::Int64 + use_optional_variables::Bool + use_exact_lp_reason::Bool + use_branching_in_lp::Bool + use_combined_no_overlap::Bool + catch_sigint_signal::Bool + use_implied_bounds::Bool + polish_lp_solution::Bool + convert_intervals::Bool + symmetry_level::Int32 + new_linear_propagation::Bool + linear_split_size::Int32 + mip_max_bound::Float64 + mip_var_scaling::Float64 + mip_scale_large_domain::Bool + mip_automatically_scale_variables::Bool + only_solve_ip::Bool + mip_wanted_precision::Float64 + mip_max_activity_exponent::Int32 + mip_check_precision::Float64 + mip_compute_true_objective_bound::Bool + mip_max_valid_magnitude::Float64 + mip_drop_tolerance::Float64 + + function SatParameters(; + name = "", + preferred_variable_order = SatParameters_VariableOrder.IN_ORDER, + initial_polarity = SatParameters_Polarity.POLARITY_FALSE, + use_phase_saving = true, + polarity_rephase_increment = Int32(1000), + random_polarity_ratio = Float64(0.0), + random_branches_ratio = Float64(0.0), + use_erwa_heuristic = false, + initial_variables_activity = Float64(0.0), + also_bump_variables_in_conflict_reasons = false, + minimization_algorithm = SatParameters_ConflictMinimizationAlgorithm.RECURSIVE, + binary_minimization_algorithm = SatParameters_BinaryMinizationAlgorithm.BINARY_MINIMIZATION_FIRST, + subsumption_during_conflict_analysis = true, + clause_cleanup_period = Int32(10000), + clause_cleanup_target = Int32(0), + clause_cleanup_ratio = Float64(0.5), + clause_cleanup_protection = SatParameters_ClauseProtection.PROTECTION_NONE, + clause_cleanup_lbd_bound = Int32(5), + clause_cleanup_ordering = SatParameters_ClauseOrdering.CLAUSE_ACTIVITY, + pb_cleanup_increment = Int32(200), + pb_cleanup_ratio = Float64(0.5), + minimize_with_propagation_restart_period = Int32(10), + minimize_with_propagation_num_decisions = Int32(1000), + variable_activity_decay = Float64(0.8), + max_variable_activity_value = Float64(1e100), + glucose_max_decay = Float64(0.95), + glucose_decay_increment = Float64(0.01), + glucose_decay_increment_period = Int32(5000), + clause_activity_decay = Float64(0.999), + max_clause_activity_value = Float64(1e20), + restart_algorithms = Vector{SatParameters_RestartAlgorithm.T}(), + default_restart_algorithms = "LUBY_RESTART,LBD_MOVING_AVERAGE_RESTART,DL_MOVING_AVERAGE_RESTART", + restart_period = Int32(50), + restart_running_window_size = Int32(50), + restart_dl_average_ratio = Float64(1.0), + restart_lbd_average_ratio = Float64(1.0), + use_blocking_restart = false, + blocking_restart_window_size = Int32(5000), + blocking_restart_multiplier = Float64(1.4), + num_conflicts_before_strategy_changes = Int32(0), + strategy_change_increase_ratio = Float64(0.0), + max_time_in_seconds = Float64(Inf), + max_deterministic_time = Float64(Inf), + max_number_of_conflicts = Int64(9223372036854775807), + max_memory_in_mb = Int64(10000), + absolute_gap_limit = Float64(1e-4), + relative_gap_limit = Float64(0.0), + random_seed = Int32(1), + permute_variable_randomly = false, + permute_presolve_constraint_order = false, + use_absl_random = false, + log_search_progress = false, + log_frequency_in_seconds = Float64(-1), + model_reduction_log_frequency_in_seconds = Float64(5), + log_subsolver_statistics = false, + log_prefix = "", + log_to_stdout = true, + log_to_response = false, + use_pb_resolution = false, + minimize_reduction_during_pb_resolution = false, + count_assumption_levels_in_lbd = true, + presolve_bve_threshold = Int32(500), + presolve_bve_clause_weight = Int32(3), + probing_deterministic_time_limit = Float64(1), + presolve_probing_deterministic_time_limit = Float64(30.0), + presolve_blocked_clause = true, + presolve_use_bva = true, + presolve_bva_threshold = Int32(1), + max_presolve_iterations = Int32(3), + cp_model_presolve = true, + cp_model_probing_level = Int32(2), + cp_model_use_sat_presolve = true, + use_sat_inprocessing = false, + detect_table_with_cost = false, + table_compression_level = Int32(2), + expand_alldiff_constraints = false, + expand_reservoir_constraints = true, + disable_constraint_expansion = false, + encode_complex_linear_constraint_with_integer = false, + merge_no_overlap_work_limit = Float64(1e12), + merge_at_most_one_work_limit = Float64(1e8), + presolve_substitution_level = Int32(1), + presolve_extract_integer_enforcement = false, + presolve_inclusion_work_limit = Int64(100000000), + ignore_names = true, + infer_all_diffs = true, + find_big_linear_overlap = true, + num_workers = Int32(0), + num_search_workers = Int32(0), + min_num_lns_workers = Int32(2), + subsolvers = Vector{String}(), + extra_subsolvers = Vector{String}(), + ignore_subsolvers = Vector{String}(), + subsolver_params = Vector{SatParameters}(), + interleave_search = false, + interleave_batch_size = Int32(0), + share_objective_bounds = true, + share_level_zero_bounds = true, + share_binary_clauses = true, + debug_postsolve_with_full_solver = false, + debug_max_num_presolve_operations = Int32(0), + debug_crash_on_bad_hint = false, + use_optimization_hints = true, + minimize_core = true, + find_multiple_cores = true, + cover_optimization = true, + max_sat_assumption_order = SatParameters_MaxSatAssumptionOrder.DEFAULT_ASSUMPTION_ORDER, + max_sat_reverse_assumption_order = false, + max_sat_stratification = SatParameters_MaxSatStratificationAlgorithm.STRATIFICATION_DESCENT, + propagation_loop_detection_factor = Float64(10.0), + use_precedences_in_disjunctive_constraint = true, + max_size_to_create_precedence_literals_in_disjunctive = Int32(60), + use_strong_propagation_in_disjunctive = false, + use_overload_checker_in_cumulative = false, + use_timetable_edge_finding_in_cumulative = false, + use_hard_precedences_in_cumulative = false, + exploit_all_precedences = false, + use_disjunctive_constraint_in_cumulative = true, + use_timetabling_in_no_overlap_2d = false, + use_energetic_reasoning_in_no_overlap_2d = false, + use_pairwise_reasoning_in_no_overlap_2d = false, + use_dual_scheduling_heuristics = true, + linearization_level = Int32(1), + boolean_encoding_level = Int32(1), + max_domain_size_when_encoding_eq_neq_constraints = Int32(16), + max_num_cuts = Int32(10000), + cut_level = Int32(1), + only_add_cuts_at_level_zero = false, + add_objective_cut = false, + add_cg_cuts = true, + add_mir_cuts = true, + add_zero_half_cuts = true, + add_clique_cuts = true, + max_all_diff_cut_size = Int32(64), + add_lin_max_cuts = true, + max_integer_rounding_scaling = Int32(600), + add_lp_constraints_lazily = true, + root_lp_iterations = Int32(2000), + min_orthogonality_for_lp_constraints = Float64(0.05), + max_cut_rounds_at_level_zero = Int32(1), + max_consecutive_inactive_count = Int32(100), + cut_max_active_count_value = Float64(1e10), + cut_active_count_decay = Float64(0.8), + cut_cleanup_target = Int32(1000), + new_constraints_batch_size = Int32(50), + search_branching = SatParameters_SearchBranching.AUTOMATIC_SEARCH, + hint_conflict_limit = Int32(10), + repair_hint = false, + fix_variables_to_their_hinted_value = false, + exploit_integer_lp_solution = true, + exploit_all_lp_solution = true, + exploit_best_solution = false, + exploit_relaxation_solution = false, + exploit_objective = true, + probing_period_at_root = Int64(0), + use_probing_search = false, + use_shaving_in_probing_search = true, + shaving_search_deterministic_time = Float64(0.001), + use_objective_lb_search = false, + use_objective_shaving_search = false, + pseudo_cost_reliability_threshold = Int64(100), + optimize_with_core = false, + optimize_with_lb_tree_search = false, + binary_search_num_conflicts = Int32(-1), + optimize_with_max_hs = false, + test_feasibility_jump = false, + feasibility_jump_max_num_values_scanned = Int64(4096), + feasibility_jump_protect_linear_feasibility = true, + feasibility_jump_decay = Float64(1.0), + feasibility_jump_var_randomization_probability = Float64(0.0), + feasibility_jump_var_perburbation_range_ratio = Float64(0.2), + feasibility_jump_enable_restarts = true, + num_violation_ls = Int32(0), + violation_ls_perturbation_period = Int32(100), + shared_tree_num_workers = Int32(0), + use_shared_tree_search = false, + shared_tree_worker_objective_split_probability = Float64(0.5), + shared_tree_max_nodes_per_worker = Int32(128), + shared_tree_split_strategy = SatParameters_SharedTreeSplitStrategy.SPLIT_STRATEGY_AUTO, + enumerate_all_solutions = false, + keep_all_feasible_solutions_in_presolve = false, + fill_tightened_domains_in_response = false, + fill_additional_solutions_in_response = false, + instantiate_all_variables = true, + auto_detect_greater_than_at_least_one_of = true, + stop_after_first_solution = false, + stop_after_presolve = false, + stop_after_root_propagation = false, + use_lns_only = false, + solution_pool_size = Int32(3), + use_rins_lns = true, + use_feasibility_pump = true, + use_lb_relax_lns = false, + fp_rounding = SatParameters_FPRoundingMethod.PROPAGATION_ASSISTED, + diversify_lns_params = false, + randomize_search = false, + search_randomization_tolerance = Int64(0), + use_optional_variables = false, + use_exact_lp_reason = true, + use_branching_in_lp = false, + use_combined_no_overlap = false, + catch_sigint_signal = true, + use_implied_bounds = true, + polish_lp_solution = false, + convert_intervals = true, + symmetry_level = Int32(2), + new_linear_propagation = false, + linear_split_size = Int32(100), + mip_max_bound = Float64(1e7), + mip_var_scaling = Float64(1.0), + mip_scale_large_domain = false, + mip_automatically_scale_variables = true, + only_solve_ip = false, + mip_wanted_precision = Float64(1e-6), + mip_max_activity_exponent = Int32(53), + mip_check_precision = Float64(1e-4), + mip_compute_true_objective_bound = true, + mip_max_valid_magnitude = Float64(1e30), + mip_drop_tolerance = Float64(1e-16), + ) + new( + name, + preferred_variable_order, + initial_polarity, + use_phase_saving, + polarity_rephase_increment, + random_polarity_ratio, + random_branches_ratio, + use_erwa_heuristic, + initial_variables_activity, + also_bump_variables_in_conflict_reasons, + minimization_algorithm, + binary_minimization_algorithm, + subsumption_during_conflict_analysis, + clause_cleanup_period, + clause_cleanup_target, + clause_cleanup_ratio, + clause_cleanup_protection, + clause_cleanup_lbd_bound, + clause_cleanup_ordering, + pb_cleanup_increment, + pb_cleanup_ratio, + minimize_with_propagation_restart_period, + minimize_with_propagation_num_decisions, + variable_activity_decay, + max_variable_activity_value, + glucose_max_decay, + glucose_decay_increment, + glucose_decay_increment_period, + clause_activity_decay, + max_clause_activity_value, + restart_algorithms, + default_restart_algorithms, + restart_period, + restart_running_window_size, + restart_dl_average_ratio, + restart_lbd_average_ratio, + use_blocking_restart, + blocking_restart_window_size, + blocking_restart_multiplier, + num_conflicts_before_strategy_changes, + strategy_change_increase_ratio, + max_time_in_seconds, + max_deterministic_time, + max_number_of_conflicts, + max_memory_in_mb, + absolute_gap_limit, + relative_gap_limit, + random_seed, + permute_variable_randomly, + permute_presolve_constraint_order, + use_absl_random, + log_search_progress, + log_frequency_in_seconds, + model_reduction_log_frequency_in_seconds, + log_subsolver_statistics, + log_prefix, + log_to_stdout, + log_to_response, + use_pb_resolution, + minimize_reduction_during_pb_resolution, + count_assumption_levels_in_lbd, + presolve_bve_threshold, + presolve_bve_clause_weight, + probing_deterministic_time_limit, + presolve_probing_deterministic_time_limit, + presolve_blocked_clause, + presolve_use_bva, + presolve_bva_threshold, + max_presolve_iterations, + cp_model_presolve, + cp_model_probing_level, + cp_model_use_sat_presolve, + use_sat_inprocessing, + detect_table_with_cost, + table_compression_level, + expand_alldiff_constraints, + expand_reservoir_constraints, + disable_constraint_expansion, + encode_complex_linear_constraint_with_integer, + merge_no_overlap_work_limit, + merge_at_most_one_work_limit, + presolve_substitution_level, + presolve_extract_integer_enforcement, + presolve_inclusion_work_limit, + ignore_names, + infer_all_diffs, + find_big_linear_overlap, + num_workers, + num_search_workers, + min_num_lns_workers, + subsolvers, + extra_subsolvers, + ignore_subsolvers, + subsolver_params, + interleave_search, + interleave_batch_size, + share_objective_bounds, + share_level_zero_bounds, + share_binary_clauses, + debug_postsolve_with_full_solver, + debug_max_num_presolve_operations, + debug_crash_on_bad_hint, + use_optimization_hints, + minimize_core, + find_multiple_cores, + cover_optimization, + max_sat_assumption_order, + max_sat_reverse_assumption_order, + max_sat_stratification, + propagation_loop_detection_factor, + use_precedences_in_disjunctive_constraint, + max_size_to_create_precedence_literals_in_disjunctive, + use_strong_propagation_in_disjunctive, + use_overload_checker_in_cumulative, + use_timetable_edge_finding_in_cumulative, + use_hard_precedences_in_cumulative, + exploit_all_precedences, + use_disjunctive_constraint_in_cumulative, + use_timetabling_in_no_overlap_2d, + use_energetic_reasoning_in_no_overlap_2d, + use_pairwise_reasoning_in_no_overlap_2d, + use_dual_scheduling_heuristics, + linearization_level, + boolean_encoding_level, + max_domain_size_when_encoding_eq_neq_constraints, + max_num_cuts, + cut_level, + only_add_cuts_at_level_zero, + add_objective_cut, + add_cg_cuts, + add_mir_cuts, + add_zero_half_cuts, + add_clique_cuts, + max_all_diff_cut_size, + add_lin_max_cuts, + max_integer_rounding_scaling, + add_lp_constraints_lazily, + root_lp_iterations, + min_orthogonality_for_lp_constraints, + max_cut_rounds_at_level_zero, + max_consecutive_inactive_count, + cut_max_active_count_value, + cut_active_count_decay, + cut_cleanup_target, + new_constraints_batch_size, + search_branching, + hint_conflict_limit, + repair_hint, + fix_variables_to_their_hinted_value, + exploit_integer_lp_solution, + exploit_all_lp_solution, + exploit_best_solution, + exploit_relaxation_solution, + exploit_objective, + probing_period_at_root, + use_probing_search, + use_shaving_in_probing_search, + shaving_search_deterministic_time, + use_objective_lb_search, + use_objective_shaving_search, + pseudo_cost_reliability_threshold, + optimize_with_core, + optimize_with_lb_tree_search, + binary_search_num_conflicts, + optimize_with_max_hs, + test_feasibility_jump, + feasibility_jump_max_num_values_scanned, + feasibility_jump_protect_linear_feasibility, + feasibility_jump_decay, + feasibility_jump_var_randomization_probability, + feasibility_jump_var_perburbation_range_ratio, + feasibility_jump_enable_restarts, + num_violation_ls, + violation_ls_perturbation_period, + shared_tree_num_workers, + use_shared_tree_search, + shared_tree_worker_objective_split_probability, + shared_tree_max_nodes_per_worker, + shared_tree_split_strategy, + enumerate_all_solutions, + keep_all_feasible_solutions_in_presolve, + fill_tightened_domains_in_response, + fill_additional_solutions_in_response, + instantiate_all_variables, + auto_detect_greater_than_at_least_one_of, + stop_after_first_solution, + stop_after_presolve, + stop_after_root_propagation, + use_lns_only, + solution_pool_size, + use_rins_lns, + use_feasibility_pump, + use_lb_relax_lns, + fp_rounding, + diversify_lns_params, + randomize_search, + search_randomization_tolerance, + use_optional_variables, + use_exact_lp_reason, + use_branching_in_lp, + use_combined_no_overlap, + catch_sigint_signal, + use_implied_bounds, + polish_lp_solution, + convert_intervals, + symmetry_level, + new_linear_propagation, + linear_split_size, + mip_max_bound, + mip_var_scaling, + mip_scale_large_domain, + mip_automatically_scale_variables, + only_solve_ip, + mip_wanted_precision, + mip_max_activity_exponent, + mip_check_precision, + mip_compute_true_objective_bound, + mip_max_valid_magnitude, + mip_drop_tolerance, + ) + end +end + + +function to_proto_struct( + sat_parameters::SatParameters, +)::OperationsResearch.sat.SatParameters + return OperationsResearch.sat.SatParameters( + sat_parameters.name, + sat_parameters.preferred_variable_order, + sat_parameters.initial_polarity, + sat_parameters.use_phase_saving, + sat_parameters.polarity_rephase_increment, + sat_parameters.random_polarity_ratio, + sat_parameters.random_branches_ratio, + sat_parameters.use_erwa_heuristic, + sat_parameters.initial_variables_activity, + sat_parameters.also_bump_variables_in_conflict_reasons, + sat_parameters.minimization_algorithm, + sat_parameters.binary_minimization_algorithm, + sat_parameters.subsumption_during_conflict_analysis, + sat_parameters.clause_cleanup_period, + sat_parameters.clause_cleanup_target, + sat_parameters.clause_cleanup_ratio, + sat_parameters.clause_cleanup_protection, + sat_parameters.clause_cleanup_lbd_bound, + sat_parameters.clause_cleanup_ordering, + sat_parameters.pb_cleanup_increment, + sat_parameters.pb_cleanup_ratio, + sat_parameters.minimize_with_propagation_restart_period, + sat_parameters.minimize_with_propagation_num_decisions, + sat_parameters.variable_activity_decay, + sat_parameters.max_variable_activity_value, + sat_parameters.glucose_max_decay, + sat_parameters.glucose_decay_increment, + sat_parameters.glucose_decay_increment_period, + sat_parameters.clause_activity_decay, + sat_parameters.max_clause_activity_value, + sat_parameters.restart_algorithms, + sat_parameters.default_restart_algorithms, + sat_parameters.restart_period, + sat_parameters.restart_running_window_size, + sat_parameters.restart_dl_average_ratio, + sat_parameters.restart_lbd_average_ratio, + sat_parameters.use_blocking_restart, + sat_parameters.blocking_restart_window_size, + sat_parameters.blocking_restart_multiplier, + sat_parameters.num_conflicts_before_strategy_changes, + sat_parameters.strategy_change_increase_ratio, + sat_parameters.max_time_in_seconds, + sat_parameters.max_deterministic_time, + sat_parameters.max_number_of_conflicts, + sat_parameters.max_memory_in_mb, + sat_parameters.absolute_gap_limit, + sat_parameters.relative_gap_limit, + sat_parameters.random_seed, + sat_parameters.permute_variable_randomly, + sat_parameters.permute_presolve_constraint_order, + sat_parameters.use_absl_random, + sat_parameters.log_search_progress, + sat_parameters.log_frequency_in_seconds, + sat_parameters.model_reduction_log_frequency_in_seconds, + sat_parameters.log_subsolver_statistics, + sat_parameters.log_prefix, + sat_parameters.log_to_stdout, + sat_parameters.log_to_response, + sat_parameters.use_pb_resolution, + sat_parameters.minimize_reduction_during_pb_resolution, + sat_parameters.count_assumption_levels_in_lbd, + sat_parameters.presolve_bve_threshold, + sat_parameters.presolve_bve_clause_weight, + sat_parameters.probing_deterministic_time_limit, + sat_parameters.presolve_probing_deterministic_time_limit, + sat_parameters.presolve_blocked_clause, + sat_parameters.presolve_use_bva, + sat_parameters.presolve_bva_threshold, + sat_parameters.max_presolve_iterations, + sat_parameters.cp_model_presolve, + sat_parameters.cp_model_probing_level, + sat_parameters.cp_model_use_sat_presolve, + sat_parameters.use_sat_inprocessing, + sat_parameters.detect_table_with_cost, + sat_parameters.table_compression_level, + sat_parameters.expand_alldiff_constraints, + sat_parameters.expand_reservoir_constraints, + sat_parameters.disable_constraint_expansion, + sat_parameters.encode_complex_linear_constraint_with_integer, + sat_parameters.merge_no_overlap_work_limit, + sat_parameters.merge_at_most_one_work_limit, + sat_parameters.presolve_substitution_level, + sat_parameters.presolve_extract_integer_enforcement, + sat_parameters.presolve_inclusion_work_limit, + sat_parameters.ignore_names, + sat_parameters.infer_all_diffs, + sat_parameters.find_big_linear_overlap, + sat_parameters.num_workers, + sat_parameters.num_search_workers, + sat_parameters.min_num_lns_workers, + sat_parameters.subsolvers, + sat_parameters.extra_subsolvers, + sat_parameters.ignore_subsolvers, + sat_parameters.subsolver_params, + sat_parameters.interleave_search, + sat_parameters.interleave_batch_size, + sat_parameters.share_objective_bounds, + sat_parameters.share_level_zero_bounds, + sat_parameters.share_binary_clauses, + sat_parameters.debug_postsolve_with_full_solver, + sat_parameters.debug_max_num_presolve_operations, + sat_parameters.debug_crash_on_bad_hint, + sat_parameters.use_optimization_hints, + sat_parameters.minimize_core, + sat_parameters.find_multiple_cores, + sat_parameters.cover_optimization, + sat_parameters.max_sat_assumption_order, + sat_parameters.max_sat_reverse_assumption_order, + sat_parameters.max_sat_stratification, + sat_parameters.propagation_loop_detection_factor, + sat_parameters.use_precedences_in_disjunctive_constraint, + sat_parameters.max_size_to_create_precedence_literals_in_disjunctive, + sat_parameters.use_strong_propagation_in_disjunctive, + sat_parameters.use_overload_checker_in_cumulative, + sat_parameters.use_timetable_edge_finding_in_cumulative, + sat_parameters.use_hard_precedences_in_cumulative, + sat_parameters.exploit_all_precedences, + sat_parameters.use_disjunctive_constraint_in_cumulative, + sat_parameters.use_timetabling_in_no_overlap_2d, + sat_parameters.use_energetic_reasoning_in_no_overlap_2d, + sat_parameters.use_pairwise_reasoning_in_no_overlap_2d, + sat_parameters.use_dual_scheduling_heuristics, + sat_parameters.linearization_level, + sat_parameters.boolean_encoding_level, + sat_parameters.max_domain_size_when_encoding_eq_neq_constraints, + sat_parameters.max_num_cuts, + sat_parameters.cut_level, + sat_parameters.only_add_cuts_at_level_zero, + sat_parameters.add_objective_cut, + sat_parameters.add_cg_cuts, + sat_parameters.add_mir_cuts, + sat_parameters.add_zero_half_cuts, + sat_parameters.add_clique_cuts, + sat_parameters.max_all_diff_cut_size, + sat_parameters.add_lin_max_cuts, + sat_parameters.max_integer_rounding_scaling, + sat_parameters.add_lp_constraints_lazily, + sat_parameters.root_lp_iterations, + sat_parameters.min_orthogonality_for_lp_constraints, + sat_parameters.max_cut_rounds_at_level_zero, + sat_parameters.max_consecutive_inactive_count, + sat_parameters.cut_max_active_count_value, + sat_parameters.cut_active_count_decay, + sat_parameters.cut_cleanup_target, + sat_parameters.new_constraints_batch_size, + sat_parameters.search_branching, + sat_parameters.hint_conflict_limit, + sat_parameters.repair_hint, + sat_parameters.fix_variables_to_their_hinted_value, + sat_parameters.exploit_integer_lp_solution, + sat_parameters.exploit_all_lp_solution, + sat_parameters.exploit_best_solution, + sat_parameters.exploit_relaxation_solution, + sat_parameters.exploit_objective, + sat_parameters.probing_period_at_root, + sat_parameters.use_probing_search, + sat_parameters.use_shaving_in_probing_search, + sat_parameters.shaving_search_deterministic_time, + sat_parameters.use_objective_lb_search, + sat_parameters.use_objective_shaving_search, + sat_parameters.pseudo_cost_reliability_threshold, + sat_parameters.optimize_with_core, + sat_parameters.optimize_with_lb_tree_search, + sat_parameters.binary_search_num_conflicts, + sat_parameters.optimize_with_max_hs, + sat_parameters.test_feasibility_jump, + sat_parameters.feasibility_jump_max_num_values_scanned, + sat_parameters.feasibility_jump_protect_linear_feasibility, + sat_parameters.feasibility_jump_decay, + sat_parameters.feasibility_jump_var_randomization_probability, + sat_parameters.feasibility_jump_var_perburbation_range_ratio, + sat_parameters.feasibility_jump_enable_restarts, + sat_parameters.num_violation_ls, + sat_parameters.violation_ls_perturbation_period, + sat_parameters.shared_tree_num_workers, + sat_parameters.use_shared_tree_search, + sat_parameters.shared_tree_worker_objective_split_probability, + sat_parameters.shared_tree_max_nodes_per_worker, + sat_parameters.shared_tree_split_strategy, + sat_parameters.enumerate_all_solutions, + sat_parameters.keep_all_feasible_solutions_in_presolve, + sat_parameters.fill_tightened_domains_in_response, + sat_parameters.fill_additional_solutions_in_response, + sat_parameters.instantiate_all_variables, + sat_parameters.auto_detect_greater_than_at_least_one_of, + sat_parameters.stop_after_first_solution, + sat_parameters.stop_after_presolve, + sat_parameters.stop_after_root_propagation, + sat_parameters.use_lns_only, + sat_parameters.solution_pool_size, + sat_parameters.use_rins_lns, + sat_parameters.use_feasibility_pump, + sat_parameters.use_lb_relax_lns, + sat_parameters.fp_rounding, + sat_parameters.diversify_lns_params, + sat_parameters.randomize_search, + sat_parameters.search_randomization_tolerance, + sat_parameters.use_optional_variables, + sat_parameters.use_exact_lp_reason, + sat_parameters.use_branching_in_lp, + sat_parameters.use_combined_no_overlap, + sat_parameters.catch_sigint_signal, + sat_parameters.use_implied_bounds, + sat_parameters.polish_lp_solution, + sat_parameters.convert_intervals, + sat_parameters.symmetry_level, + sat_parameters.new_linear_propagation, + sat_parameters.linear_split_size, + sat_parameters.mip_max_bound, + sat_parameters.mip_var_scaling, + sat_parameters.mip_scale_large_domain, + sat_parameters.mip_automatically_scale_variables, + sat_parameters.only_solve_ip, + sat_parameters.mip_wanted_precision, + sat_parameters.mip_max_activity_exponent, + sat_parameters.mip_check_precision, + sat_parameters.mip_compute_true_objective_bound, + sat_parameters.mip_max_valid_magnitude, + sat_parameters.mip_drop_tolerance, + ) +end + +""" + Mutable wrapper struct for the GlpkParameters struct. +""" +mutable struct GlpkParameters + compute_unbound_rays_if_possible::Bool + + function GlpkParameters(; compute_unbound_rays_if_possible = false) + new(compute_unbound_rays_if_possible) + end +end + +function to_proto_struct(glpk_parameters::GlpkParameters)::MathOpt.GlpkParametersProto + return MathOpt.GlpkParametersProto(glpk_parameters.compute_unbound_rays_if_possible) +end + + +""" + Mutable wrapper struct for the HighsOptionsProto struct. +""" +mutable struct HighsOptions + string_options::Dict{String,String} + double_options::Dict{String,Float64} + int_options::Dict{String,Int32} + bool_options::Dict{String,Bool} + + function HighsOptions(; + string_options = Dict{String,String}(), + double_options = Dict{String,Float64}(), + int_options = Dict{String,Int32}(), + bool_options = Dict{String,Bool}(), + ) + new(string_options, double_options, int_options, bool_options) + end +end + +function to_proto_struct(highs_options::HighsOptions)::MathOpt.HighsOptionsProto + return MathOpt.HighsOptionsProto( + highs_options.string_options, + highs_options.double_options, + highs_options.int_options, + highs_options.bool_options, + ) +end + + +""" + Mutable wrapper struct for the SolveParametersProto struct. +""" +mutable struct SolveParameters + time_limit::Union{Nothing,Duration} + iteration_limit::Int64 + node_limit::Int64 + cutoff_limit::Float64 + objective_limit::Float64 + best_bound_limit::Float64 + solution_limit::Int32 + enable_output::Bool + threads::Int32 + random_seed::Int32 + absolute_gap_tolerance::Float64 + relative_gap_tolerance::Float64 + solution_pool_size::Int32 + lp_algorithm::LPAlgorithm.T + presolve::Emphasis.T + cuts::Emphasis.T + heuristics::Emphasis.T + scaling::Emphasis.T + gscip::Union{Nothing,GScipParameters} + gurobi::Union{Nothing,GurobiParameters} + glop::Union{Nothing,GlopParameters} + cp_sat::Union{Nothing,SatParameters} + glpk::Union{Nothing,GlpkParameters} + highs::Union{Nothing,HighsOptions} + + function SolveParameters(; + time_limit = nothing, + iteration_limit = zero(Int64), + node_limit = zero(Int64), + cutoff_limit = zero(Float64), + objective_limit = zero(Float64), + best_bound_limit = zero(Float64), + solution_limit = zero(Int32), + enable_output = false, + threads = zero(Int32), + random_seed = zero(Int32), + absolute_gap_tolerance = zero(Float64), + relative_gap_tolerance = zero(Float64), + solution_pool_size = zero(Int32), + lp_algorithm = LPAlgorithm.LP_ALGORITHM_UNSPECIFIED, + presolve = Emphasis.EMPHASIS_UNSPECIFIED, + cuts = Emphasis.EMPHASIS_UNSPECIFIED, + heuristics = Emphasis.EMPHASIS_UNSPECIFIED, + scaling = Emphasis.EMPHASIS_UNSPECIFIED, + gscip = nothing, + gurobi = nothing, + glop = nothing, + cp_sat = nothing, + glpk = nothing, + highs = nothing, + ) + new( + time_limit, + iteration_limit, + node_limit, + cutoff_limit, + objective_limit, + best_bound_limit, + solution_limit, + enable_output, + threads, + random_seed, + absolute_gap_tolerance, + relative_gap_tolerance, + solution_pool_size, + lp_algorithm, + presolve, + cuts, + heuristics, + scaling, + gscip, + gurobi, + glop, + cp_sat, + glpk, + highs, + ) + end +end + +function to_proto_struct(solve_parameters::SolveParameters)::MathOpt.SolveParametersProto + gscip_parameters = nothing + if !isnothing(solve_parameters.gscip) + gscip_parameters = to_proto_struct(solve_parameters.gscip) + end + + gurobi_parameters = nothing + if !isnothing(solve_parameters.gurobi) + gurobi_parameters = to_proto_struct(solve_parameters.gurobi) + end + + glop_parameters = nothing + if !isnothing(solve_parameters.glop) + glop_parameters = to_proto_struct(solve_parameters.glop) + end + + cp_sat_parameters = nothing + if !isnothing(solve_parameters.cp_sat) + cp_sat_parameters = to_proto_struct(solve_parameters.cp_sat) + end + + glpk_parameters = nothing + if !isnothing(solve_parameters.glpk) + glpk_parameters = to_proto_struct(solve_parameters.glpk) + end + + highs_parameters = nothing + if !isnothing(solve_parameters.highs) + highs_parameters = to_proto_struct(solve_parameters.highs) + end + + return MathOpt.SolveParametersProto( + solve_parameters.time_limit, + solve_parameters.iteration_limit, + solve_parameters.node_limit, + solve_parameters.cutoff_limit, + solve_parameters.objective_limit, + solve_parameters.best_bound_limit, + solve_parameters.solution_limit, + solve_parameters.enable_output, + solve_parameters.threads, + solve_parameters.random_seed, + solve_parameters.absolute_gap_tolerance, + solve_parameters.relative_gap_tolerance, + solve_parameters.solution_pool_size, + solve_parameters.lp_algorithm, + solve_parameters.presolve, + solve_parameters.cuts, + solve_parameters.heuristics, + solve_parameters.scaling, + gscip_parameters, + gurobi_parameters, + glop_parameters, + cp_sat_parameters, + glpk_parameters, + highs_parameters, + ) +end diff --git a/ortools/julia/ORTools.jl/test/cli_c_tests.jl b/ortools/julia/ORTools.jl/test/cli_c_tests.jl new file mode 100755 index 0000000000..726604c796 --- /dev/null +++ b/ortools/julia/ORTools.jl/test/cli_c_tests.jl @@ -0,0 +1,45 @@ +#!/usr/bin/env julia + +# Checks whether the given dynamic library has the required symbols for +# ORTools.jl. +# +# This script is super light so that it can be run easily from automated tests +# when building OR-Tools. + +using Libdl + +function main(ARGS) + if length(ARGS) != 1 + error("Expected usage: ./$(PROGRAM_FILE) /path/to/libortools.so") + end + + libortools = ARGS[1] # Equivalent to ORTools_jll.libortools + println("Testing shared library $(ARGS[1])...") + + lib = Libdl.dlopen(libortools) + if lib === nothing + # This should never happen, as dlopen is supposed to error if it fails. + return -1 + end + println("Successfully loaded the shared library $(ARGS[1])!") + + math_opt_fct = Libdl.dlsym(lib, :MathOptSolve) + if math_opt_fct === nothing + # This should never happen, as dlsym is supposed to error if it fails. + return -1 + end + println("Successfully loaded the function MathOptSolve from the shared " * + "library $(ARGS[1])!") + + cp_sat_fct = Libdl.dlsym(lib, :SolveCpModelWithParameters) + if cp_sat_fct === nothing + # This should never happen, as dlsym is supposed to error if it fails. + return -1 + end + println("Successfully loaded the function SolveCpModelWithParameters from " * + "the shared library $(ARGS[1])!") + return 0 +end + +# With Julia 1.11+: @main +exit(main(ARGS)) diff --git a/ortools/julia/ORTools.jl/test/moi/MOI_wrapper.jl b/ortools/julia/ORTools.jl/test/moi/MOI_wrapper.jl new file mode 100644 index 0000000000..dfc0b6a484 --- /dev/null +++ b/ortools/julia/ORTools.jl/test/moi/MOI_wrapper.jl @@ -0,0 +1,444 @@ +module TestMOIWrapper + +using Test +using ORTools +import MathOptInterface as MOI + +## Test the optimizer +function runtests() + for name in names(@__MODULE__; all = true) + if startswith("$(name)", "test_") + @testset "$(name)" begin + getfield(@__MODULE__, name)() + end + end + end +end + + +function test_runtests() + model = MOI.Bridges.full_bridge_optimizer(ORTools.Optimizer(), Float64) + + MOI.Test.runtests( + model, + MOI.Test.Config(), + exclude = [ + "_SecondOrderCone_", + "test_constraint_PrimalStart_DualStart_SecondOrderCone", + "_RotatedSecondOrderCone_", + ## TODO: b/386355769 implement the copy_to function + "test_model_copy_", + ## Silent is actually cleared when the model is emptied in our implementation + ## hence this test will keep failing. A custom test for the silent attribute + ## has been added below. + "test_attribute_after_empty", + ## TODO: b/386355105 - Add support for MOI.delete + "test_model_delete", + ## Removed as out solver supports Real type + "test_model_supports_constraint_VariableIndex_EqualTo", + ## Removed as the implementation of MOI.Optimize! is yet to be done + ## TODO: b/384662497 - implement the optimize! function + "test_attribute_RawStatusString", + "test_attribute_SolveTimeSec", + ## This is dependent on the implementation of the ConstraintSet function + ## TODO: b/384674354 + "test_basic_", + ## Replacement implemented here. + ## Excluded as out implementation does not support MOI.ConstraintIndex + ## without type parameters. + "test_constraint_get_ConstraintIndex", + "test_model_ScalarAffineFunction_ConstraintName", + ## We throw an error on set, the existing test throws an error on get + ## hence it has been replaced by out test + "test_model_duplicate_ScalarAffineFunction_ConstraintName", + ## We throw an error on set, the existing test throws an error on get + ## hence it has been replaced by out test + # TODO: b/392077251 + "test_model_VariableName", + "test_linear_complex_Zeros", + "test_linear_complex_Zeros_duplicate", + ## TODO: b/386359323 implement the solve interface + "test_solve_", + ## The default test does not support UInt8, our Optimizer supports all <: Real + "test_model_supports_constraint_ScalarAffineFunction_EqualTo", + ## Delete has not been implemented yet for this test to work + ## TODO: implement the delete function + "test_model_duplicate_VariableName", + ## TODO: b/386359144 Requires implementation of copy_to + ## TODO: b/386355769 implement the copy_to function + "test_model_ModelFilter_ListOfConstraintTypesPresent", + ## This test has been reimplemented here as it kept failing + # TODO: b/392085365 + "test_model_ScalarFunctionConstantNotZero", + # The set of tests below are failing due to the lack of support for + # the following functions in the MOI wrapper: + # TODO: b/392073143 - MOI.VariablePrimal() + # TODO:b/392071376 - MOI.ConstraintPrimal() + # TODO: b/392073145 - MOI.DualStatus() + # TODO: b/392071380 - MOI.ConstraintDual() + # TODO: b/392071459 - MOI.TerminationStatus() + # TODO: b/392071460 - MOI.ObjectiveValue() + # TODO: b/392073827 - PrimalStatus() + "test_constraint_ScalarAffineFunction_LessThan", + "test_constraint_ScalarAffineFunction_", + "test_constraint_VectorAffineFunction_", + "test_constraint_ZeroOne_bounds", + "test_constraint_ZeroOne_bounds_2", + "test_constraint_ZeroOne_bounds_3", + "test_variable_solve_", + "test_objective_", + "test_modification_", + "test_linear_", + ], + ) + + return +end + + +function test_model_supports_constraint_VariableIndex_EqualTo() + model = ORTools.Optimizer() + # Support for Ints + @test MOI.supports_constraint(model, MOI.VariableIndex, MOI.EqualTo{Int}) + # Support for Float32 + @test MOI.supports_constraint(model, MOI.VariableIndex, MOI.EqualTo{Float32}) + # Support for Float64 + @test MOI.supports_constraint(model, MOI.VariableIndex, MOI.EqualTo{Float64}) + # Scalar-in-vector + @test !MOI.supports_constraint(model, MOI.VariableIndex, MOI.Zeros) + return +end + +function test_attribute_after_empty() + model = MOI.Bridges.full_bridge_optimizer(ORTools.Optimizer(), Float64) + @test MOI.supports(model, MOI.Silent()) == true + for value in (true, false) + MOI.set(model, MOI.Silent(), value) + @test MOI.get(model, MOI.Silent()) == value + MOI.empty!(model) + @test MOI.get(model, MOI.Silent()) == true + end + return +end + + +function test_solver_specific_parameters_of_emphasis_type() + emphasis_based_attributes = + [ORTools.Presolve(), ORTools.Cuts(), ORTools.Heuristics(), ORTools.Scaling()] + model = ORTools.Optimizer() + + for attribute in emphasis_based_attributes + @test MOI.supports(model, attribute) == true + @test MOI.get(model, attribute) == ORTools.Emphasis.EMPHASIS_UNSPECIFIED + MOI.set(model, attribute, ORTools.Emphasis.EMPHASIS_HIGH) + @test MOI.get(model, attribute) == ORTools.Emphasis.EMPHASIS_HIGH + MOI.empty!(model) + @test MOI.get(model, attribute) == nothing + ## Re-initialize the model + ORTools.optionally_initialize_model_and_parameters!(model) + end + return +end + +## Test the gscip parameters attribute with a single internal attribute +function test_attributes_gscip_parameters() + model = ORTools.Optimizer() + @test MOI.supports(model, ORTools.GscipParametersAttribute()) == true + @test MOI.get(model, ORTools.GscipParametersAttribute()) == nothing + MOI.set(model, ORTools.GscipParametersAttribute(), ORTools.GScipParameters()) + @test MOI.get(model, MOI.RawOptimizerAttribute("gscip__num_solutions")) == 0 + MOI.set(model, MOI.RawOptimizerAttribute("gscip__num_solutions"), Int32(100)) + @test MOI.get(model, MOI.RawOptimizerAttribute("gscip__num_solutions")) == 100 + MOI.empty!(model) + @test MOI.get(model, ORTools.GscipParametersAttribute()) == nothing + @test_throws "Either your model or parameters is empty." MOI.get( + model, + MOI.RawOptimizerAttribute("gscip__num_solutions"), + ) + return +end + +function test_attributes_gurobi_parameters() + model = ORTools.Optimizer() + @test MOI.supports(model, ORTools.GurobiParametersAttribute()) == true + @test MOI.get(model, ORTools.GurobiParametersAttribute()) == nothing + MOI.set(model, ORTools.GurobiParametersAttribute(), ORTools.GurobiParameters()) + @test MOI.get(model, MOI.RawOptimizerAttribute("gurobi__parameters")) == [] + MOI.set( + model, + MOI.RawOptimizerAttribute("gurobi__parameters"), + [ORTools.GurobiParameters_Parameter("num_solutions", "100")], + ) + gurobi_param_vector = MOI.get(model, MOI.RawOptimizerAttribute("gurobi__parameters")) + @test length(gurobi_param_vector) == 1 + @test gurobi_param_vector[1].name == "num_solutions" + @test gurobi_param_vector[1].value == "100" + MOI.empty!(model) + @test MOI.get(model, ORTools.GurobiParametersAttribute()) == nothing + @test_throws "Either your model or parameters is empty." MOI.get( + model, + MOI.RawOptimizerAttribute("gurobi__parameters"), + ) + return +end + + +function test_attribute_glop_parameters() + model = ORTools.Optimizer() + @test MOI.supports(model, ORTools.GlopParametersAttribute()) == true + @test MOI.get(model, ORTools.GlopParametersAttribute()) == nothing + MOI.set(model, ORTools.GlopParametersAttribute(), ORTools.GlopParameters()) + @test MOI.get( + model, + MOI.RawOptimizerAttribute("glop__max_number_of_reoptimizations"), + ) == Float64(40) + MOI.set( + model, + MOI.RawOptimizerAttribute("glop__max_number_of_reoptimizations"), + Float64(100), + ) + @test MOI.get( + model, + MOI.RawOptimizerAttribute("glop__max_number_of_reoptimizations"), + ) == Float64(100) + MOI.empty!(model) + @test MOI.get(model, ORTools.GlopParametersAttribute()) == nothing + @test_throws "Either your model or parameters is empty." MOI.get( + model, + MOI.RawOptimizerAttribute("glop__max_number_of_reoptimizations"), + ) + return +end + +function test_attribute_cp_sat_parameters() + model = ORTools.Optimizer() + @test MOI.supports(model, ORTools.SatParametersAttribute()) == true + @test MOI.get(model, ORTools.SatParametersAttribute()) == nothing + MOI.set(model, ORTools.SatParametersAttribute(), ORTools.SatParameters()) + @test MOI.get(model, MOI.RawOptimizerAttribute("sat__name")) == "" + MOI.set(model, MOI.RawOptimizerAttribute("sat__name"), "Google_CP_SAT") + @test MOI.get(model, MOI.RawOptimizerAttribute("sat__name")) == "Google_CP_SAT" + MOI.empty!(model) + @test MOI.get(model, ORTools.SatParametersAttribute()) == nothing + @test_throws "Either your model or parameters is empty." MOI.get( + model, + MOI.RawOptimizerAttribute("cp_sat__name"), + ) + return +end + +function test_attribute_glpk_parameters() + model = ORTools.Optimizer() + @test MOI.supports(model, ORTools.GlpkParametersAttribute()) == true + @test MOI.get(model, ORTools.GlpkParametersAttribute()) == nothing + MOI.set(model, ORTools.GlpkParametersAttribute(), ORTools.GlpkParameters()) + @test MOI.get( + model, + MOI.RawOptimizerAttribute("glpk__compute_unbound_rays_if_possible"), + ) == false + MOI.set( + model, + MOI.RawOptimizerAttribute("glpk__compute_unbound_rays_if_possible"), + true, + ) + @test MOI.get( + model, + MOI.RawOptimizerAttribute("glpk__compute_unbound_rays_if_possible"), + ) == true + MOI.empty!(model) + @test MOI.get(model, ORTools.GlpkParametersAttribute()) == nothing + @test_throws "Either your model or parameters is empty." MOI.get( + model, + MOI.RawOptimizerAttribute("glpk__name"), + ) + return +end + +function test_attribute_highs_parameters() + model = ORTools.Optimizer() + @test MOI.supports(model, ORTools.HighsOptionsAttribute()) == true + @test MOI.get(model, ORTools.HighsOptionsAttribute()) == nothing + MOI.set(model, ORTools.HighsOptionsAttribute(), ORTools.HighsOptions()) + @test MOI.get(model, MOI.RawOptimizerAttribute("highs__int_options")) == Dict() + MOI.set( + model, + MOI.RawOptimizerAttribute("highs__int_options"), + Dict("num_threads" => Int32(10)), + ) + @test MOI.get(model, MOI.RawOptimizerAttribute("highs__int_options")) == + Dict("num_threads" => Int32(10)) + MOI.empty!(model) + @test MOI.get(model, ORTools.HighsOptionsAttribute()) == nothing + @test_throws "Either your model or parameters is empty." MOI.get( + model, + MOI.RawOptimizerAttribute("highs__name"), + ) + return +end + + +""" + test_constraint_get_ConstraintIndex() + +Test getting constraints by name. +""" +function test_constraint_get_ConstraintIndex() + model = ORTools.Optimizer() + x = MOI.add_variable(model) + f = one(Float64) * x + CI = MOI.ConstraintIndex{typeof(f),MOI.GreaterThan{Float64}} + @test MOI.supports(model, MOI.ConstraintName(), CI) == true + c1 = MOI.add_constraint(model, f, MOI.GreaterThan(one(Float64))) + MOI.set(model, MOI.ConstraintName(), c1, "c1") + c2 = MOI.add_constraint(model, f, MOI.LessThan(Float64(2))) + MOI.set(model, MOI.ConstraintName(), c2, "c2") + F = MOI.ScalarAffineFunction{Float64} + @test MOI.get(model, MOI.ConstraintIndex{F,MOI.LessThan{Float64}}, "c3") === nothing + @test MOI.get(model, MOI.ConstraintIndex{F,MOI.GreaterThan{Float64}}, "c3") === nothing + @test MOI.get(model, MOI.ConstraintIndex{F,MOI.LessThan{Float64}}, "c1") === nothing + @test MOI.get(model, MOI.ConstraintIndex{F,MOI.GreaterThan{Float64}}, "c2") === nothing + c1 = MOI.get(model, MOI.ConstraintIndex{F,MOI.GreaterThan{Float64}}, "c1") + @test MOI.get(model, MOI.ConstraintIndex{F,MOI.GreaterThan{Float64}}, "c1") == c1 + @test MOI.is_valid(model, c1) + c2 = MOI.get(model, MOI.ConstraintIndex{F,MOI.LessThan{Float64}}, "c2") + @test MOI.get(model, MOI.ConstraintIndex{F,MOI.LessThan{Float64}}, "c2") == c2 + @test MOI.is_valid(model, c2) + return +end + +""" + test_model_ScalarAffineFunction_ConstraintName() + +Test ConstraintName for ScalarAffineFunction constraints. +""" +function test_model_ScalarAffineFunction_ConstraintName() + model = ORTools.Optimizer() + T = Float64 + x = MOI.add_variable(model) + f = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(T(1), x)], T(0)) + c1 = MOI.add_constraint(model, f, MOI.GreaterThan(T(0))) + c2 = MOI.add_constraint(model, f, MOI.LessThan(T(1))) + MOI.set(model, MOI.ConstraintName(), c1, "c1") + @test MOI.get( + model, + MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},MOI.GreaterThan{Float64}}, + "c1", + ) == c1 + MOI.set(model, MOI.ConstraintName(), c1, "c2") + @test MOI.get( + model, + MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},MOI.GreaterThan{Float64}}, + "c1", + ) === nothing + @test MOI.get( + model, + MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},MOI.GreaterThan{Float64}}, + "c2", + ) == c1 + MOI.set(model, MOI.ConstraintName(), c2, "c1") + @test MOI.get( + model, + MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},MOI.LessThan{Float64}}, + "c1", + ) == c2 + @test_throws ErrorException MOI.set(model, MOI.ConstraintName(), c1, "c1") + return +end + +""" + test_model_duplicate_ScalarAffineFunction_ConstraintName() + +Test duplicate names in ScalarAffineFunction constraints. +""" +function test_model_duplicate_ScalarAffineFunction_ConstraintName() + model = ORTools.Optimizer() + T = Float64 + x = MOI.add_variables(model, 3) + fs = [MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(T(1), xi)], T(0)) for xi in x] + c = MOI.add_constraints(model, fs, MOI.GreaterThan(T(0))) + MOI.set(model, MOI.ConstraintName(), c[1], "x") + @test_throws ErrorException MOI.set(model, MOI.ConstraintName(), c[2], "x") + MOI.set(model, MOI.ConstraintName(), c[3], "z") + @test MOI.get( + model, + MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},MOI.GreaterThan{Float64}}, + "z", + ) == c[3] + MOI.set(model, MOI.ConstraintName(), c[2], "y") + @test MOI.get( + model, + MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},MOI.GreaterThan{Float64}}, + "x", + ) == c[1] + @test MOI.get( + model, + MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},MOI.GreaterThan{Float64}}, + "y", + ) == c[2] + @test_throws ErrorException MOI.set(model, MOI.ConstraintName(), c[3], "x") + return +end + +""" + test_model_VariableName() + +Test MOI.VariableName. +""" +function test_model_VariableName() + model = ORTools.Optimizer() + x = MOI.add_variables(model, 2) + MOI.set(model, MOI.VariableName(), x[1], "x1") + @test MOI.get(model, MOI.VariableIndex, "x1") == x[1] + MOI.set(model, MOI.VariableName(), x[1], "x2") + @test MOI.get(model, MOI.VariableIndex, "x1") === nothing + @test MOI.get(model, MOI.VariableIndex, "x2") == x[1] + MOI.set(model, MOI.VariableName(), x[2], "x1") + @test MOI.get(model, MOI.VariableIndex, "x1") == x[2] + return +end + + +function test_model_supports_constraint_ScalarAffineFunction_EqualTo() + model = ORTools.Optimizer() + MOI.supports_constraint(model, MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) + return +end + +""" + test_model_ScalarFunctionConstantNotZero() + +Test adding a linear constraint with a non-zero function constant. + +This should either work, or error with `MOI.ScalarFunctionConstantNotZero` if +the model does not support it. + +TODO: b/392085365 +""" +function test_model_ScalarFunctionConstantNotZero() + T = Float64 + model = ORTools.Optimizer() + function _error(S, value) + F = MOI.ScalarAffineFunction{T} + return MOI.ScalarFunctionConstantNotZero{T,F,S}(value) + end + try + f = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm{T}[], T(1)) + c = MOI.add_constraint(model, f, MOI.EqualTo(T(2))) + @test MOI.get(model, MOI.ConstraintFunction(), c) ≈ f + catch err + @test err == _error(MOI.EqualTo{T}, T(1)) + end + try + f = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm{T}[], T(2)) + c = MOI.add_constraint(model, f, MOI.GreaterThan(T(1))) + @test MOI.get(model, MOI.ConstraintFunction(), c) ≈ f + catch err + @test err == _error(MOI.GreaterThan{T}, T(2)) + end + return +end + +end + +## Run all tests. +TestMOIWrapper.runtests() diff --git a/ortools/julia/ORTools.jl/test/runtests.jl b/ortools/julia/ORTools.jl/test/runtests.jl index 8b2e109cf6..1e26b6b530 100644 --- a/ortools/julia/ORTools.jl/test/runtests.jl +++ b/ortools/julia/ORTools.jl/test/runtests.jl @@ -2,5 +2,7 @@ using ORTools using Test @testset "ORTools.jl" begin - # Write your tests here. + for file in readdir("moi") + include(joinpath("moi", file)) + end end diff --git a/ortools/julia/ORToolsGenerated.jl/src/genproto/google/google.jl b/ortools/julia/ORToolsGenerated.jl/src/genproto/google/google.jl index 25bf6b5d48..31fc82b542 100644 --- a/ortools/julia/ORToolsGenerated.jl/src/genproto/google/google.jl +++ b/ortools/julia/ORToolsGenerated.jl/src/genproto/google/google.jl @@ -1,5 +1,5 @@ module google -include("protobuf/protobuf.jl") +include("google/protobuf/protobuf.jl") end # module google diff --git a/ortools/julia/ORToolsGenerated.jl/src/genproto/operations_research/operations_research.jl b/ortools/julia/ORToolsGenerated.jl/src/genproto/operations_research/operations_research.jl index 63379fb744..f37f1760d5 100644 --- a/ortools/julia/ORToolsGenerated.jl/src/genproto/operations_research/operations_research.jl +++ b/ortools/julia/ORToolsGenerated.jl/src/genproto/operations_research/operations_research.jl @@ -19,7 +19,7 @@ include("scheduling/scheduling.jl") include("glop/glop.jl") include("bop/bop.jl") include("packing/packing.jl") -include("pdlp/pdlp.jl") +include("ortools/pdlp/pdlp.jl") include("math_opt/math_opt.jl") end # module operations_research