Skip to content

Commit 5e598c0

Browse files
committed
julia: export from google3
1 parent be8b7ac commit 5e598c0

File tree

7 files changed

+215
-95
lines changed

7 files changed

+215
-95
lines changed

ortools/julia/ORTools.jl/Project.toml

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ version = "1.0.0-DEV"
44

55
[deps]
66
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
7+
ORTools_jll = "717719f8-c30c-5086-8f3c-70cd6a1e3a46"
78
ORToolsGenerated = "6b269722-41d3-11ee-be56-0242ac120002"
89
ProtoBuf = "3349acd9-ac6a-5e09-bcdb-63829b23a429"
910
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
+7-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
module ORTools
2-
# TODO: b/384496822 - Run formatter across entire package
2+
3+
import MathOptInterface as MOI
4+
using ORTools_jll
5+
6+
include("moi_wrapper/Type_wrappers.jl")
7+
include("c_wrapper/c_wrapper.jl")
38
include("moi_wrapper/MOI_wrapper.jl")
49

10+
511
end
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using ORTools_jll
2+
13
libortools = ORTools_jll.libortools
24

35
# Keep this file in sync with math_opt/core/c_api/solver.h.
@@ -6,54 +8,52 @@ libortools = ORTools_jll.libortools
68
# on the Julia side, as it's only an opaque pointer for this API.
79

810
function MathOptNewInterrupter()
9-
return ccall((:MathOptNewInterrupter, libortools),
10-
Ptr{Cvoid},
11-
())
11+
return ccall((:MathOptNewInterrupter, libortools), Ptr{Cvoid}, ())
1212
end
1313

1414
function MathOptFreeInterrupter(ptr)
15-
return ccall((:MathOptFreeInterrupter, libortools),
16-
Cvoid,
17-
(Ptr{Cvoid},),
18-
ptr)
15+
return ccall((:MathOptFreeInterrupter, libortools), Cvoid, (Ptr{Cvoid},), ptr)
1916
end
2017

2118
function MathOptInterrupt(ptr)
22-
return ccall((:MathOptInterrupt, libortools),
23-
Cvoid,
24-
(Ptr{Cvoid},),
25-
ptr)
19+
return ccall((:MathOptInterrupt, libortools), Cvoid, (Ptr{Cvoid},), ptr)
2620
end
2721

2822
function MathOptIsInterrupted(ptr)
29-
return ccall((:MathOptIsInterrupted, libortools),
30-
Cint,
31-
(Ptr{Cvoid},),
32-
ptr)
23+
return ccall((:MathOptIsInterrupted, libortools), Cint, (Ptr{Cvoid},), ptr)
3324
end
3425

3526
function MathOptFree(ptr)
36-
return ccall((:MathOptFree, libortools),
37-
Cvoid,
38-
(Ptr{Cvoid},),
39-
ptr)
27+
return ccall((:MathOptFree, libortools), Cvoid, (Ptr{Cvoid},), ptr)
4028
end
4129

42-
function MathOptSolve(model, model_size, solver_type, interrupter, solve_result, solve_result_size, status_msg)
43-
return ccall((:MathOptSolve, libortools),
44-
Cint,
45-
(Ptr{Cvoid},
46-
Csize_t,
47-
Cint,
48-
Ptr{Cvoid},
49-
Ptr{Ptr{Cvoid}},
50-
Ptr{Csize_t},
51-
Ptr{Ptr{Cchar}}),
52-
model,
53-
model_size,
54-
solver_type,
55-
interrupter,
56-
solve_result,
57-
solve_result_size,
58-
status_msg)
30+
function MathOptSolve(
31+
model,
32+
model_size,
33+
solver_type,
34+
interrupter,
35+
solve_result,
36+
solve_result_size,
37+
status_msg,
38+
)
39+
return ccall(
40+
(:MathOptSolve, libortools),
41+
Cint,
42+
(
43+
Ptr{Cvoid},
44+
Csize_t,
45+
Cint,
46+
Ptr{Cvoid},
47+
Ptr{Ptr{Cvoid}},
48+
Ptr{Csize_t},
49+
Ptr{Ptr{Cchar}},
50+
),
51+
model,
52+
model_size,
53+
solver_type,
54+
interrupter,
55+
solve_result,
56+
solve_result_size,
57+
status_msg,
58+
)
5959
end
+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# TODO: b/407034730 - remove this file.
2+
import MathOptInterface as MOI
3+
include("ORTools.jl")
4+
5+
"""
6+
Solving the following optimization problem:
7+
8+
max x + 2⋅y
9+
10+
subject to: x + y ≤ 1.5
11+
-1 ≤ x ≤ 1.5
12+
0 ≤ y ≤ 1
13+
"""
14+
15+
function main(ARGS)
16+
optimizer = ORTools.Optimizer()
17+
c = [1.0, 2.0]
18+
19+
# Variables definition
20+
x = MOI.add_variables(optimizer, length(c))
21+
22+
# Set the objective
23+
MOI.set(
24+
optimizer,
25+
MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(),
26+
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(c, x), 0.0),
27+
)
28+
29+
MOI.set(optimizer, MOI.ObjectiveSense(), MOI.MAX_SENSE)
30+
31+
# The first constraint (x + y <= 1.5)
32+
C1 = 1.5
33+
MOI.add_constraint(
34+
optimizer,
35+
MOI.ScalarAffineFunction(
36+
[MOI.ScalarAffineTerm(1.0, x[1]), MOI.ScalarAffineTerm(1.0, x[2])],
37+
0.0,
38+
),
39+
MOI.LessThan(C1),
40+
)
41+
42+
## The second constraint (-1 <= x <= 1.5)
43+
MOI.add_constraint(optimizer, MOI.VariableIndex(1), MOI.Interval(-1.0, 1.5))
44+
45+
## The third constraint (0 <= y <= 1)
46+
MOI.add_constraint(optimizer, MOI.VariableIndex(2), MOI.Interval(0.0, 1.0))
47+
48+
print(optimizer)
49+
50+
# Optimize the model
51+
MOI.optimize!(optimizer)
52+
53+
# Print the solution
54+
print(optimizer.solve_result)
55+
56+
return 0
57+
end
58+
59+
# TODO: Understand why the solver stopped.
60+
# TODO: Understand what solution was returned.
61+
# TODO: Query the objective.
62+
# TODO: Query the primal solution.
63+
main(ARGS)

ortools/julia/ORTools.jl/src/moi_wrapper/MOI_wrapper.jl

+66-25
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
import MathOptInterface as MOI
2-
include("Type_wrappers.jl")
3-
41
const PARAM_SPLITTER = "__"
52
const PARAM_FIELD_NAME_TO_INSTANCE_DICT = Dict(
63
"gscip" => GScipParameters(),
@@ -53,6 +50,10 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
5350
# Indicator of whether an objective has been set
5451
objective_set::Bool
5552

53+
# Store solve results
54+
# This structure is update after running the optimize! function
55+
solve_result::Union{SolveResultProto,Nothing}
56+
5657
# Constructor with optional parameters
5758
function Optimizer(;
5859
model_name::String = "",
@@ -81,6 +82,7 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
8182
Set{Tuple{Type,Type}}(),
8283
constraint_indices_dict,
8384
false,
85+
nothing,
8486
)
8587
end
8688
end
@@ -103,6 +105,7 @@ function MOI.empty!(model::Optimizer)
103105
model.constraint_types_present = Set{Tuple{Type,Type}}()
104106
model.constraint_indices_dict = Dict()
105107
model.objective_set = false
108+
model.solve_result = nothing
106109

107110
return nothing
108111
end
@@ -111,7 +114,8 @@ function MOI.is_empty(model::Optimizer)
111114
return isnothing(model.model) &&
112115
isnothing(model.parameters) &&
113116
model.solver_type == SolverType.SOLVER_TYPE_UNSPECIFIED &&
114-
!model.objective_set
117+
!model.objective_set &&
118+
isnothing(model.solve_result)
115119
end
116120

117121
"""
@@ -1166,20 +1170,21 @@ end
11661170
# Helper function to create a dictionary of terms to combine coefficients
11671171
# of terms(variables) if they are repeated.
11681172
# For example: 3x + 5x <= 10 will be combined to 8x <= 10.
1169-
function get_terms_dict(
1173+
function get_terms_pairs(
11701174
terms::Vector{MOI.ScalarAffineTerm{T}},
1171-
)::Dict{Int64,Float64} where {T<:Real}
1172-
terms_dict = Dict{Int64,Float64}()
1175+
)::Vector{Pair{Int64,Float64}} where {T<:Real}
1176+
terms_pairs = Dict{Int64,Float64}()
11731177

11741178
for term in terms
1175-
if !haskey(terms_dict, term.variable.value)
1176-
terms_dict[term.variable.value] = term.coefficient
1179+
if !haskey(terms_pairs, term.variable.value)
1180+
terms_pairs[term.variable.value] = term.coefficient
11771181
else
1178-
terms_dict[term.variable.value] += term.coefficient
1182+
terms_pairs[term.variable.value] += term.coefficient
11791183
end
11801184
end
11811185

1182-
return terms_dict
1186+
sorted_pairs = sort(collect(terms_pairs), by = x -> x[1])
1187+
return sorted_pairs
11831188
end
11841189

11851190
function MOI.add_constraint(
@@ -1204,17 +1209,17 @@ function MOI.add_constraint(
12041209
push!(model.model.linear_constraints.upper_bounds, upper_bound)
12051210
push!(model.model.linear_constraints.names, "")
12061211

1207-
terms_dict = get_terms_dict(terms)
1212+
terms_pairs = get_terms_pairs(terms)
12081213

12091214
# Update the LinearConstaintMatrix (SparseDoubleVectorProto)
12101215
# linear_constraint_matrix.row_ids are elements of linear_constraints.ids.
12111216
# linear_constraint_matrix.column_ids are elements of variables.ids.
12121217
# Matrix entries not specified are zero.
12131218
# linear_constraint_matrix.coefficients must all be finite.
1214-
for term_index in keys(terms_dict)
1219+
for term_index in terms_pairs
12151220
push!(model.model.linear_constraint_matrix.row_ids, constraint_index)
1216-
push!(model.model.linear_constraint_matrix.column_ids, term_index)
1217-
push!(model.model.linear_constraint_matrix.coefficients, terms_dict[term_index])
1221+
push!(model.model.linear_constraint_matrix.column_ids, term_index[1])
1222+
push!(model.model.linear_constraint_matrix.coefficients, term_index[2])
12181223
end
12191224

12201225
# Update the associated metadata.
@@ -1316,7 +1321,7 @@ end
13161321
function MOI.get(
13171322
model::Optimizer,
13181323
::MOI.ConstraintFunction,
1319-
c::MOI.ConstraintIndex{MOI.VariableIndex,Any},
1324+
c::MOI.ConstraintIndex{MOI.VariableIndex,<:Any},
13201325
)
13211326
if !MOI.is_empty(model)
13221327
return MOI.VariableIndex(c.value)
@@ -1406,12 +1411,12 @@ function MOI.set(
14061411
)
14071412
end
14081413

1409-
terms_dict = get_terms_dict(terms)
1414+
terms_pairs = get_terms_pairs(terms)
14101415

1411-
for term_index in keys(terms_dict)
1416+
for term_index in terms_pairs
14121417
push!(model.model.linear_constraint_matrix.row_ids, c.value)
1413-
push!(model.model.linear_constraint_matrix.column_ids, term_index)
1414-
push!(model.model.linear_constraint_matrix.coefficients, terms_dict[term_index])
1418+
push!(model.model.linear_constraint_matrix.column_ids, term_index[1])
1419+
push!(model.model.linear_constraint_matrix.coefficients, term_index[2])
14151420
end
14161421

14171422
return nothing
@@ -1810,11 +1815,11 @@ function MOI.set(
18101815

18111816
terms = objective_function.terms
18121817

1813-
terms_dict = get_terms_dict(terms)
1818+
terms_pairs = get_terms_pairs(terms)
18141819

1815-
for term in keys(terms_dict)
1816-
push!(model.model.objective.linear_coefficients.ids, term)
1817-
push!(model.model.objective.linear_coefficients.values, terms_dict[term])
1820+
for term in terms_pairs
1821+
push!(model.model.objective.linear_coefficients.ids, term[1])
1822+
push!(model.model.objective.linear_coefficients.values, term[2])
18181823
end
18191824

18201825
model.model.objective.offset = Float64(objective_function.constant)
@@ -1880,6 +1885,42 @@ function MOI.supports(
18801885
end
18811886

18821887
function MOI.optimize!(model::Optimizer)
1883-
# TODO: b/384662497 implement this
1888+
status_msg = Ref(pointer(zeros(Int8, 1)))
1889+
1890+
# Serialize the model
1891+
io = IOBuffer()
1892+
e = PB.ProtoEncoder(io)
1893+
PB.encode(e, to_proto_struct(model.model))
1894+
model_proto = take!(io)
1895+
model_size = encoded_model_size(model.model)[1]
1896+
1897+
# Result proto with its accompanying size
1898+
solve_result_proto = Ref{Ptr{Cvoid}}()
1899+
result_size = Ref{Csize_t}(0)
1900+
1901+
result = MathOptSolve(
1902+
model_proto,
1903+
model_size,
1904+
Int(model.solver_type),
1905+
MathOptNewInterrupter(),
1906+
solve_result_proto,
1907+
result_size,
1908+
status_msg,
1909+
)
1910+
1911+
# A non-null status_msg indicates a failure in executing the solve call.
1912+
if status_msg[] != C_NULL
1913+
failure_status_message = unsafe_string(status_msg[])
1914+
# TODO: b/407544202 - Add error to SolveResult instead of printing it.
1915+
@error "The following failure was encountered when executing the solve call: $failure_status_message"
1916+
return
1917+
end
1918+
1919+
solve_result_proto =
1920+
unsafe_wrap(Vector{UInt8}, Ptr{UInt8}(solve_result_proto[]), result_size[])
1921+
io = IOBuffer(solve_result_proto)
1922+
d = PB.ProtoDecoder(io)
1923+
model.solve_result = PB.decode(d, SolveResultProto)
1924+
18841925
return nothing
18851926
end

ortools/julia/ORTools.jl/src/moi_wrapper/Type_wrappers.jl

+8-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ using ORToolsGenerated
22
const OperationsResearch = ORToolsGenerated.Proto.operations_research
33
const MathOpt = OperationsResearch.math_opt
44
const SolverType = MathOpt.SolverTypeProto
5+
const SolveResultProto = MathOpt.SolveResultProto
6+
const PB = MathOpt.PB
57

68
"""
79
Given the nature of the fields, we are using an alias for the VariablesProto struct.
@@ -379,11 +381,11 @@ function to_proto_struct(model::Model)::MathOpt.ModelProto
379381

380382
return MathOpt.ModelProto(
381383
model.name,
382-
to_proto_struct(model.variables),
384+
model.variables,
383385
to_proto_struct(model.objective),
384386
auxiliary_objectives,
385-
to_proto_struct(model.linear_constraints),
386-
to_proto_struct(model.linear_constraint_matrix),
387+
model.linear_constraints,
388+
model.linear_constraint_matrix,
387389
quadratic_constraints,
388390
second_order_cone_constraints,
389391
sos1_constraints,
@@ -392,6 +394,9 @@ function to_proto_struct(model::Model)::MathOpt.ModelProto
392394
)
393395
end
394396

397+
# The size of the encoded model
398+
encoded_model_size(model::Model) = PB._encoded_size(to_proto_struct(model))
399+
395400
"""
396401
Mutable wrapper struct for the GscipParameters struct.
397402
"""

0 commit comments

Comments
 (0)