julia: export from google3

This commit is contained in:
Corentin Le Molgat
2025-04-25 17:23:39 +02:00
parent c51007c56d
commit eeb551a2c2
2 changed files with 169 additions and 5 deletions

View File

@@ -1924,3 +1924,164 @@ function MOI.optimize!(model::Optimizer)
return nothing
end
function MOI.get(model::Optimizer, ::MOI.RawStatusString)::String
if !isnothing(model) && !isnothing(model.solve_result)
return string(model.solve_result.termination.reason)
end
return ""
end
"""
Additional, typically solver-specific information about termination.
"""
struct ExtraTerminationDetailString <: MOI.AbstractOptimizerAttribute end
MOI.attribute_value_type(::ExtraTerminationDetailString) = String
function MOI.get(model::Optimizer, ::ExtraTerminationDetailString)::String
if !isnothing(model) && !isnothing(model.solve_result)
return model.solve_result.termination.detail
end
return ""
end
function MOI.get(model::Optimizer, ::MOI.TerminationStatus)::MOI.TerminationStatusCode
if isnothing(model) || isnothing(model.solve_result)
return MOI.OPTIMIZE_NOT_CALLED
end
if model.solve_result.termination.reason ==
TerminationReasonProto.TERMINATION_REASON_OPTIMAL
# It is expected that the LimitProto is LIMIT_UNSPECIFIED when the termination reason is OPTIMAL.
return MOI.OPTIMAL
elseif model.solve_result.termination.limit == LimitProto.LIMIT_ITERATION
return MOI.ITERATION_LIMIT
elseif model.solve_result.termination.limit == LimitProto.LIMIT_TIME
return MOI.TIME_LIMIT
elseif model.solve_result.termination.limit == LimitProto.LIMIT_NODE
return MOI.NODE_LIMIT
elseif model.solve_result.termination.limit == LimitProto.LIMIT_SOLUTION
return MOI.SOLUTION_LIMIT
elseif model.solve_result.termination.limit == LimitProto.LIMIT_MEMORY
return MOI.MEMORY_LIMIT
elseif model.solve_result.termination.limit == LimitProto.LIMIT_OBJECTIVE
return MOI.OBJECTIVE_LIMIT
elseif model.solve_result.termination.limit == LimitProto.LIMIT_NORM
return MOI.NORM_LIMIT
elseif model.solve_result.termination.limit == LimitProto.LIMIT_INTERRUPTED
return MOI.INTERRUPTED
elseif model.solve_result.termination.limit == LimitProto.LIMIT_SLOW_PROGRESS
return MOI.SLOW_PROGRESS
elseif model.solve_result.termination.limit == LimitProto.LIMIT_OTHER
return MOI.OTHER_LIMIT
elseif model.solve_result.termination.limit == LimitProto.LIMIT_UNDETERMINED
# TODO: b/411325865 Follow up on support for LIMIT_UNDETERMINED in MOI.jl
# A fallback as there's currently no associated MOI.LIMIT_* that can represent this.
@info "The underlying solver does not expose which limit was reached and the actual limit is LIMIT_UNDETERMINED " \
"However, LIMIT_UNDETERMINED is not associated with a MOI.LIMIT_* hence the returned LIMIT is MOI.OTHER_LIMIT."
return MOI.OTHER_LIMIT
elseif model.solve_result.termination.limit == LimitProto.LIMIT_CUTOFF
# TODO: b/411328356 Follow up on support for LIMIT_CUTOFF in MOI.jl
# A fallback as there's currently no associated MOI.LIMIT_* that can represent this.
@info "The solver was run with a cutoff on the objective, indicating that the user did not want any solution " \
"worse than the cutoff, and the solver concluded there were no solutions at least as good as the cutoff. " \
"Typically no further solution information is provided. The actual limit is LIMIT_CUTOFF. " \
"However, LIMIT_CUTOFF is not associated with a MOI.LIMIT_* hence the returned LIMIT is MOI.OTHER_LIMIT."
return MOI.OTHER_LIMIT
else
# TODO: b/411328207 Add attribute to capture more information about the limit when LIMIT_UNSPECIFIED is the returned limit.
# The else bit falls back to MOI.LIMIT_UNSPECIFIED if the termination reason wasn't TERMINATION_REASON_OPTIMAL
@info "The solver terminated but not from a limit and the actual limit is LIMIT_UNSPECIFIED, which is used as a null. " \
"However, LIMIT_UNSPECIFIED is not associated with a MOI.LIMIT_* hence the returned LIMIT is MOI.OTHER_LIMIT."
return MOI.OTHER_LIMIT
end
end
function MOI.get(model::Optimizer, attr::MOI.PrimalStatus)
if isnothing(model) || isnothing(model.solve_result)
return MOI.NO_SOLUTION
end
if attr.result_index != 1
return MOI.NO_SOLUTION
elseif model.solve_result.termination.problem_status.primal_status ==
FeasibilityStatusProto.FEASIBILITY_STATUS_UNDETERMINED
return MOI.UNKNOWN_RESULT_STATUS
elseif model.solve_result.termination.problem_status.primal_status ==
FeasibilityStatusProto.FEASIBILITY_STATUS_FEASIBLE
return MOI.FEASIBLE_POINT
elseif model.solve_result.termination.problem_status.primal_status ==
FeasibilityStatusProto.FEASIBILITY_STATUS_INFEASIBLE
return MOI.INFEASIBLE_POINT
else
# For FEASIBILITY_STATUS_UNSPECIFIED which is a guard value representing no status
return MOI.NO_SOLUTION
end
end
function MOI.get(model::Optimizer, attr::MOI.DualStatus)
if isnothing(model) || isnothing(model.solve_result)
return MOI.NO_SOLUTION
end
if attr.result_index != 1
return MOI.NO_SOLUTION
elseif model.solve_result.termination.problem_status.dual_status ==
FeasibilityStatusProto.FEASIBILITY_STATUS_UNDETERMINED
return MOI.UNKNOWN_RESULT_STATUS
elseif model.solve_result.termination.problem_status.dual_status ==
FeasibilityStatusProto.FEASIBILITY_STATUS_FEASIBLE
return MOI.FEASIBLE_SOLUTION
elseif model.solve_result.termination.problem_status.dual_status ==
FeasibilityStatusProto.FEASIBILITY_STATUS_INFEASIBLE
return MOI.INFEASIBLE_SOLUTION
else
# For FEASIBILITY_STATUS_UNSPECIFIED which is a guard value representing no status
return MOI.NO_SOLUTION
end
end
"""
When the solver claims the the primal or dual problem is infeasible, but
it does not know which (or if both are infeasible), this attribute returns `true`.
It can be true only when primal_status = dual_status = FEASIBILITY_STATUS_UNDETERMINED
(mapped to MOI.UNKNOWN_RESULT_STATUS). This extra information is often needed when
preprocessing determines there is no optimal solution to the problem
(but can't determine if it is due to infeasibility, unboundedness, or both).
"""
struct PrimalOrDualInfeasible <: MOI.AbstractOptimizerAttribute end
MOI.attribute_value_type(::PrimalOrDualInfeasible) = Bool
function MOI.get(model::Optimizer, ::PrimalOrDualInfeasible)::Bool
if isnothing(model) || isnothing(model.solve_result)
return false
end
return model.solve_result.termination.problem_status.primal_status.primal_or_dual_infeasible
end
function MOI.get(model::Optimizer, attr::MOI.ObjectiveBound)
if isnothing(model) || isnothing(model.solve_result)
throw(MOI.GetAttributeNotAllowed(attr))
end
return model.solve_result.termination.objective_bounds.primal_bound
end
"""
When the solver claims there exists a dual solution that is numerically feasible
(i.e. feasible up to the solvers tolerance), and whose objective value is
dual_bound, this attribute returns the dual bound.
"""
struct DualObjectiveBound <: MOI.AbstractOptimizerAttribute end
MOI.attribute_value_type(::DualObjectiveBound) = Float64
function MOI.get(model::Optimizer, attr::DualObjectiveBound)
if isnothing(model) || isnothing(model.solve_result)
throw(MOI.GetAttributeNotAllowed(attr))
end
return model.solve_result.termination.objective_bounds.dual_bound
end

View File

@@ -3,6 +3,9 @@ const OperationsResearch = ORToolsGenerated.Proto.operations_research
const MathOpt = OperationsResearch.math_opt
const SolverType = MathOpt.SolverTypeProto
const SolveResultProto = MathOpt.SolveResultProto
const TerminationReasonProto = MathOpt.TerminationReasonProto
const LimitProto = MathOpt.LimitProto
const FeasibilityStatusProto = MathOpt.FeasibilityStatusProto
const PB = MathOpt.PB
"""
@@ -933,7 +936,7 @@ mutable struct SatParameters <: AbstractSatParameters
exploit_objective::Bool
probing_period_at_root::Int64
use_probing_search::Bool
shaving_deterministic_time_in_probing_search::Float64
use_shaving_in_probing_search::Bool
shaving_search_deterministic_time::Float64
use_objective_lb_search::Bool
use_objective_shaving_search::Bool
@@ -1154,8 +1157,8 @@ mutable struct SatParameters <: AbstractSatParameters
exploit_objective = true,
probing_period_at_root = Int64(0),
use_probing_search = false,
shaving_deterministic_time_in_probing_search = Float64(0.001),
shaving_search_deterministic_time = Float64(0.1),
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),
@@ -1375,7 +1378,7 @@ mutable struct SatParameters <: AbstractSatParameters
exploit_objective,
probing_period_at_root,
use_probing_search,
shaving_deterministic_time_in_probing_search,
use_shaving_in_probing_search,
shaving_search_deterministic_time,
use_objective_lb_search,
use_objective_shaving_search,
@@ -1603,7 +1606,7 @@ function to_proto_struct(
sat_parameters.exploit_objective,
sat_parameters.probing_period_at_root,
sat_parameters.use_probing_search,
sat_parameters.shaving_deterministic_time_in_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,