Skip to content

Commit eb75e6b

Browse files
committed
Merge branch 'feature/20250625-IntegrationWithJuMP' into develop
2 parents d848779 + fcb5e97 commit eb75e6b

File tree

21 files changed

+1686
-3
lines changed

21 files changed

+1686
-3
lines changed

.vscode/settings.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"CUDA",
2525
"CUSPARSE",
2626
"deploydocs",
27+
"diagind",
2728
"eigmax",
2829
"ENDATA",
2930
"eprint",
@@ -43,10 +44,15 @@
4344
"lenstr",
4445
"lualatex",
4546
"Lyutsarev",
47+
"Maciel",
4648
"makedocs",
49+
"mathbb",
50+
"mathbf",
4751
"mathengine",
52+
"maxcut",
4853
"Mersenne",
4954
"Mohseni",
55+
"MOIU",
5056
"Mourgias",
5157
"Naeimeh",
5258
"nargs",
@@ -72,13 +78,15 @@
7278
"Sobol",
7379
"sprintf",
7480
"stdlib",
81+
"subseteq",
7582
"testset",
7683
"Tidyverse",
7784
"timedout",
7885
"toplevel",
7986
"triu",
8087
"Vassily",
8188
"versioninfo",
89+
"vmap",
8290
"weakdeps",
8391
"writeheader",
8492
"xchg",

Project.toml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
name = "AOCoptimizer"
22
uuid = "ba4aa9bd-6938-48c2-966f-258481ba1c4a"
3-
authors = ["Kirill Kalinin <kkalinin@microsoft.com>", "Christos Gkantsidis <chrisgk@microsoft.com>"]
3+
authors = [
4+
"Kirill Kalinin <kkalinin@microsoft.com>",
5+
"Christos Gkantsidis <chrisgk@microsoft.com>",
6+
"Pedro Maciel Xavier"
7+
]
48
version = "0.2.1"
59

610
[deps]
@@ -27,9 +31,12 @@ TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
2731

2832
[weakdeps]
2933
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
34+
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
35+
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
3036

3137
[extensions]
3238
CUDAExt = "CUDA"
39+
JuMPExt = ["JuMP", "MathOptInterface"]
3340

3441
[compat]
3542
Adapt = "4.3"
@@ -46,6 +53,7 @@ Distributions = "0.25"
4653
IntervalSets = "0.7"
4754
JET = "0.9"
4855
JSON = "0.21"
56+
JuMP = "1"
4957
KernelAbstractions = "0.9"
5058
LinearAlgebra = "1.11"
5159
OrderedCollections = "1.8"

docs/src/manual/installation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ If the above does not work with the error
4545
please try the following:
4646

4747
```julia
48-
Pkg.dev(url="https://github.com/microsoft/AOCoptimizer.jl#main")
48+
Pkg.dev(url="https://github.com/microsoft/AOCoptimizer.jl")
4949
```
5050

5151
If you also want to use `CUDA` or `JuMP`,

docs/src/reference/reference.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ CurrentModule = AOCoptimizer
33
DocTestSetup = quote
44
import AOCoptimizer as AOC
55
import AOCoptimizer.Solver as Solver
6+
import AOCoptimizer.api as API
67
end
78
DocTestFilters = [r"AOCoptimizer|AOC"]
89
```
@@ -355,3 +356,40 @@ Solver._optimal_batch_size
355356
```@docs
356357
Solver.Collector._default_best_assignment_collector
357358
```
359+
360+
## `API`
361+
362+
The `AOCoptimizer.api` module contains simplified (and rigid) interfaces to the solver.
363+
They're easier to use than the normal interfaces, but they don't give access to most configuration parameters.
364+
365+
```@docs
366+
api.adjust_inputs_to_engine
367+
```
368+
369+
```@docs
370+
api.GraphCutResult
371+
```
372+
373+
```@docs
374+
api.compute_max_cut
375+
```
376+
377+
```@docs
378+
api.IsingResult
379+
```
380+
381+
```@docs
382+
api.compute_ising
383+
```
384+
385+
```@docs
386+
api.compute_mixed_ising
387+
```
388+
389+
```@docs
390+
api.compute_qumo_positive
391+
```
392+
393+
```@docs
394+
api.compute_qumo
395+
```

ext/CUDAExt/CUDAExt.jl

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,4 +157,30 @@ AOCoptimizer.Solver._similar_vector(x::CuSparseMatrix, l) = CuVector{eltype(x)}(
157157

158158
include("engine.jl")
159159

160+
function AOCoptimizer.api.adjust_inputs_to_engine(
161+
::AOCoptimizer.Solver.EngineCuda,
162+
matrix::AbstractMatrix{T},
163+
linear::Union{Nothing,AbstractVector{T}} = nothing,
164+
) where T<:Real
165+
version = CUDA.capability(dev.dev)
166+
167+
if version < v"5.3" && T === Float16
168+
@warn "Computing with Float16 on a GPU with compute capability less than 5.3 is not supported. Switching to Float32."
169+
if linear !== nothing
170+
linear = Float32.(linear)
171+
end
172+
return Float32, Float32.(matrix), linear
173+
end
174+
175+
if version < v"8.0" && T === BFloat16
176+
@warn "Computing with BFloat16 on a GPU with compute capability less than 8.0 is not supported. Switching to Float32."
177+
if linear !== nothing
178+
linear = Float32.(linear)
179+
end
180+
return Float32, Float32.(matrix), linear
181+
end
182+
183+
return T, matrix, linear
184+
end
185+
160186
end # module CUDAExt

ext/JuMPExt/JuMPExt.jl

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#=
2+
JuMPExt.jl
3+
4+
Extensions to allow the integration of the AOC optimizer with the JuMP framework.
5+
In reality, the extensions depend only on MathOptInterface, but will mostly be
6+
used through JuMP.
7+
8+
The code implemented allows the user to use the AOC optimizer to solve "almost"-QUMO problems.
9+
This extension will transform the "almost"-QUMO problems to QUMO, and then invoke the
10+
AOCoptimizer solver to solve them. Below we implement very simple transformations.
11+
12+
The code below is adapted by the work of Pedro
13+
=#
14+
15+
module JuMPExt
16+
17+
using LinearAlgebra
18+
import MathOptInterface as MOI
19+
import MathOptInterface: is_empty, empty!, optimize!
20+
import AOCoptimizer as AOC
21+
22+
export Optimizer
23+
24+
const MOIU = MOI.Utilities
25+
const VI = MOI.VariableIndex
26+
const CI{S,F} = MOI.ConstraintIndex{S,F}
27+
const EQ{T} = MOI.EqualTo{T}
28+
const LT{T} = MOI.LessThan{T}
29+
const GT{T} = MOI.GreaterThan{T}
30+
const SAT{T} = MOI.ScalarAffineTerm{T}
31+
const SAF{T} = MOI.ScalarAffineFunction{T}
32+
const SQT{T} = MOI.ScalarQuadraticTerm{T}
33+
const SQF{T} = MOI.ScalarQuadraticFunction{T}
34+
35+
const Engine = AOC.Solver.Engine
36+
const aoc_api = AOC.api
37+
38+
include("variables.jl")
39+
include("wrapper.jl")
40+
41+
end # module

ext/JuMPExt/attributes.jl

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#=
2+
attributes.jl
3+
4+
Specifies the attributes supported by the optimizer.
5+
Handles, setting and retrieving those attributes.
6+
=#
7+
8+
#=
9+
Custom attributes specific to the optimizer
10+
=#
11+
12+
"""Root type for all AOC attributes"""
13+
abstract type AOCAttribute <: MOI.AbstractOptimizerAttribute end
14+
15+
"""Specifies the random seed to use"""
16+
struct Seed <: AOCAttribute end
17+
18+
"""Specifies the working directory where
19+
the optimizer should store temporary files and updates (where applicable)"""
20+
struct WorkDir <: AOCAttribute end
21+
22+
"""Specifies the numeric type to use (Float64, Float32, Float16, BFloat16),
23+
when running locally (if applicable);
24+
Float64 may not be available when running on the GPU"""
25+
struct NumericType <: AOCAttribute end
26+
27+
"""Specifies the numeric type to use (Float64, Float32, Float16, BFloat16),
28+
when running remotely;
29+
Float64 may not be available when running on the GPU"""
30+
struct Precision <: AOCAttribute end
31+
32+
"""Specifies the backend to use.
33+
By default it is the online service (Service);
34+
other backends are available (e.g., [`RandomAssignment`](@ref) for quick testing)"""
35+
struct Backend <: AOCAttribute end
36+
37+
38+
const AOC_RAW_ATTRIBUTES = Dict{String,Any}(
39+
"seed" => Seed(),
40+
"work_dir" => WorkDir(),
41+
"numeric_type" => NumericType(),
42+
"backend" => Backend(),
43+
)
44+
45+
MOI.supports(::Optimizer, ::AOCAttribute) = true
46+
47+
MOI.get(optimizer::Optimizer, ::Seed) = get(optimizer.aim_attributes, :seed, nothing)
48+
function MOI.set(optimizer::Optimizer, ::Seed, value::Integer)
49+
optimizer.aim_attributes[:seed] = value
50+
return nothing
51+
end
52+
function MOI.set(optimizer::Optimizer, ::Seed, ::Nothing)
53+
delete!(optimizer.aim_attributes, :seed)
54+
return nothing
55+
end
56+
57+
MOI.get(optimizer::Optimizer, ::WorkDir) = get(optimizer.aim_attributes, :work_dir, nothing)
58+
function MOI.set(optimizer::Optimizer, ::WorkDir, value::AbstractString)
59+
optimizer.aim_attributes[:work_dir] = String(value)
60+
return nothing
61+
end
62+
function MOI.set(optimizer::Optimizer, ::WorkDir, ::Nothing)
63+
delete!(optimizer.aim_attributes, :work_dir)
64+
return nothing
65+
end
66+
67+
MOI.get(optimizer::Optimizer{T}, ::NumericType) where {T<:Real} =
68+
get(optimizer.aim_attributes, :numeric_type, T)
69+
70+
function MOI.set(optimizer::Optimizer, ::NumericType, ::Type{T}) where {T<:Real}
71+
optimizer.aim_attributes[:numeric_type] = T
72+
return nothing
73+
end
74+
75+
MOI.get(optimizer::Optimizer, ::Precision) = get(optimizer.aim_attributes, :precision, "Float32")
76+
function MOI.set(optimizer::Optimizer, ::Precision, value::String)
77+
@assert value ("BFloat16", "Float16", "Float32", "Float64")
78+
optimizer.aim_attributes[:precision] = value
79+
return nothing
80+
end
81+
82+
MOI.get(optimizer::Optimizer, ::Backend) = get(optimizer.aim_attributes, :backend, AOC.Solver.best_engine())
83+
function MOI.set(optimizer::Optimizer, ::Backend, value::B) where {B<:Engine}
84+
optimizer.aim_attributes[:backend] = value
85+
return nothing
86+
end
87+
88+
89+
#=
90+
Attributes to interface with the MOI backend
91+
=#
92+
93+
MOI.get(::Optimizer, ::MOI.SolverName) = "AOC Optimizer"
94+
95+
# TODO: This should be AOC's version instead!
96+
MOI.get(::Optimizer, ::MOI.SolverVersion) = AOC.__VERSION__
97+
98+
MOI.get(optimizer::Optimizer, ::MOI.RawSolver) = optimizer
99+
100+
MOI.supports(::Optimizer, attr::MOI.RawOptimizerAttribute) = true # haskey(AOC_RAW_ATTRIBUTES, attr.name)
101+
function MOI.get(optimizer::Optimizer, attr::MOI.RawOptimizerAttribute)
102+
if haskey(AOC_RAW_ATTRIBUTES, attr.name)
103+
return MOI.get(optimizer, AOC_RAW_ATTRIBUTES[attr.name])
104+
else
105+
return optimizer.raw_attributes[attr.name]
106+
end
107+
end
108+
function MOI.set(optimizer::Optimizer, attr::MOI.RawOptimizerAttribute, value::Any)
109+
if haskey(AOC_RAW_ATTRIBUTES, attr.name)
110+
MOI.set(optimizer, AOC_RAW_ATTRIBUTES[attr.name], value)
111+
else
112+
optimizer.raw_attributes[attr.name] = value
113+
end
114+
115+
return nothing
116+
end
117+
118+
MOI.supports(::Optimizer, ::MOI.Name) = true
119+
MOI.get(optimizer::Optimizer, ::MOI.Name) = get(optimizer.moi_attributes, :name, "")
120+
function MOI.set(optimizer::Optimizer, ::MOI.Name, value::AbstractString)
121+
optimizer.moi_attributes[:name] = String(value)
122+
return nothing
123+
end
124+
125+
MOI.supports(::Optimizer, ::MOI.Silent) = true
126+
MOI.get(optimizer::Optimizer, ::MOI.Silent) = get(optimizer.moi_attributes, :silent, false)
127+
function MOI.set(optimizer::Optimizer, ::MOI.Silent, value::Bool)
128+
optimizer.moi_attributes[:silent] = value
129+
return nothing
130+
end
131+
132+
MOI.supports(::Optimizer, ::MOI.TimeLimitSec) = true
133+
MOI.get(optimizer::Optimizer, ::MOI.TimeLimitSec) = get(optimizer.moi_attributes, :time_limit_sec, nothing)
134+
function MOI.set(optimizer::Optimizer, ::MOI.TimeLimitSec, value::Real)
135+
@assert value >= 0
136+
optimizer.moi_attributes[:time_limit_sec] = Float64(value)
137+
return nothing
138+
end
139+
function MOI.set(optimizer::Optimizer, ::MOI.TimeLimitSec, ::Nothing)
140+
delete!(optimizer.moi_attributes, :time_limit_sec)
141+
return nothing
142+
end
143+
144+
MOI.supports(::Optimizer, ::MOI.NumberOfThreads) = true
145+
MOI.get(optimizer::Optimizer, ::MOI.NumberOfThreads) = get(optimizer.moi_attributes, :number_of_threads, 2)
146+
147+
function MOI.set(optimizer::Optimizer, ::MOI.NumberOfThreads, value::Integer)
148+
@assert value >= 1
149+
150+
optimizer.moi_attributes[:number_of_threads] = value
151+
return nothing
152+
end
153+
154+
155+
#=
156+
Unsupported attributes
157+
=#
158+
159+
MOI.supports(::Optimizer, ::MOI.ObjectiveLimit) = false
160+
MOI.supports(::Optimizer, ::MOI.SolutionLimit) = false
161+
MOI.supports(::Optimizer, ::MOI.AbsoluteGapTolerance) = false
162+
MOI.supports(::Optimizer, ::MOI.RelativeGapTolerance) = false
163+
MOI.supports(::Optimizer, ::MOI.AbstractModelAttribute) = false
164+
MOI.supports(::Optimizer, ::MOI.AbstractOptimizerAttribute) = false

0 commit comments

Comments
 (0)