Skip to content
This repository has been archived by the owner on Apr 4, 2023. It is now read-only.

Commit

Permalink
Merge pull request #33 from psrenergy/px/v0.6.0
Browse files Browse the repository at this point in the history
Upgrade Interface
  • Loading branch information
pedromxavier authored Jan 7, 2023
2 parents 4aa492a + 9b18a3d commit bc48f29
Show file tree
Hide file tree
Showing 19 changed files with 181 additions and 115 deletions.
6 changes: 2 additions & 4 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "Anneal"
uuid = "e4d9eb7f-b088-426e-aeb5-1c0dae3d8abb"
version = "0.5.7"
version = "0.6.0"
authors = [
"pedromxavier <pedroxavier@psr-inc.com>",
"pedroripper <pedroripper@psr-inc.com>",
Expand All @@ -10,16 +10,14 @@ authors = [
]

[deps]
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
QUBOTools = "60eb5b62-0a39-4ddc-84c5-97d2adff9319"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[compat]
BenchmarkTools = "1.3"
JuMP = "1"
MathOptInterface = "1"
QUBOTools = "0.5.4"
QUBOTools = "0.6.1"
julia = "1.6"
5 changes: 3 additions & 2 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
[deps]
Anneal = "e4d9eb7f-b088-426e-aeb5-1c0dae3d8abb"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
QUBOTools = "60eb5b62-0a39-4ddc-84c5-97d2adff9319"

[compat]
Documenter = "0.27"
JuMP = "1"
MathOptInterface = "1"
QUBOTools = "0.5"
Plots = "1.38"
QUBOTools = "0.6.1"
35 changes: 29 additions & 6 deletions docs/src/examples.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# Examples

## Solving Simple QUBO Model with Anneal's [`ExactSampler`](@ref exact-sampler)
```@example
## Solving Simple QUBO Model with Anneal's [`RandomSampler`](@ref random-sampler)

```@example simple-workflow
using JuMP
using Anneal
model = Model(ExactSampler.Optimizer)
model = Model(RandomSampler.Optimizer)
Q = [
-1.0 2.0 2.0
Expand All @@ -17,10 +18,32 @@ Q = [
@objective(model, Min, x' * Q * x)
optimize!(model)
```

### Recover Results

```@example simple-workflow
for i = 1:result_count(model)
xᵢ = value.(x; result=i)
yᵢ = objective_value(model; result=i)
println("f($xᵢ) = $yᵢ")
# State vector
xi = value.(x; result=i)
# Energy
yi = objective_value(model; result=i)
# Sampling Frequency
ri = reads(model; result=i)
println("f($xi) = $(yi)\t×$(ri)")
end
```

### Plot: Sampling distribution

```@example simple-workflow
using Plots
# Extract SampleSet
ω = sampleset(model)
plot(ω)
```
13 changes: 8 additions & 5 deletions docs/src/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Anneal.@anew
Inside a module scope for the new interface, one should call the [`Anneal.@anew`](@ref anew) macro, specifying the solver's attributes as described in the macro's docs.
The second and last step is to define the `Anneal.sample(::Optimizer)` method, that must return a [`Anneal.SampleSet`](@ref sampleset).

Using it might be somehow restrictive in comparison to the regular [JuMP/MOI Solver Interface workflow]((https://jump.dev/MathOptInterface.jl/stable/tutorials/implementing/)).
Using it might be somehow restrictive in comparison to the regular [JuMP/MOI Solver Interface workflow](https://jump.dev/MathOptInterface.jl/stable/tutorials/implementing/).
Yet, our guess is that most of this package's users are not considering going deeper into the MOI internals that soon.

### [`@anew`](@ref anew-macro) example
Expand Down Expand Up @@ -73,12 +73,10 @@ module SuperSampler
function Anneal.sample(sampler::Optimizer{T}) where {T}
# ~ Is your annealer running on the Ising Model? Have this:
h, J, u, v = Anneal.ising(
sampler,
Vector, # Here we opt for a sparse, vector representation
T, # The coefficient type
sampler
)


n = MOI.get(sampler, MOI.NumberOfVariables())

# ~ Retrieve Attributes ~ #
Expand Down Expand Up @@ -186,7 +184,7 @@ Their values must be either `:min` or `:max` and `:boll` or `:spin`, respectivel
Strings, symbols and literals are supported as input for these fields.

```julia
sense = :max
sense = :max
domain = :spin
```

Expand All @@ -198,3 +196,8 @@ version = v"1.0.2"
```

# Model Mapping

# Automatic Tests
```@docs
Anneal.test
```
6 changes: 3 additions & 3 deletions docs/src/samplers.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ Anneal.AbstractSampler
## [Identity Sampler](@id identity-sampler)

```@docs
Anneal.IdentitySampler
Anneal.IdentitySampler.Optimizer
```

## [Exact Sampler](@id exact-sampler)

```@docs
Anneal.ExactSampler
Anneal.ExactSampler.Optimizer
```

## [Random Sampler](@id random-sampler)

```@docs
Anneal.RandomSampler
Anneal.RandomSampler.Optimizer
```
7 changes: 3 additions & 4 deletions src/Anneal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,16 @@ import QUBOTools: ↑, ↓

# ~*~ Expots: QUBOTools Backend ~*~ #
export QUBOTools, SampleSet, Sample
export ising, qubo, adjacency, reads, state, value
export ising, qubo, reads
export sampleset, variables, indices
export ,

# ~*~ See:
# https://github.com/jump-dev/MathOptInterface.jl/issues/1985
QUBOTools.varcmp(x::VI, y::VI) = isless(x.value, y.value)
QUBOTools.varlt(x::VI, y::VI) = isless(x.value, y.value)

# ~*~ Imports: Tests + Benchmarking ~*~ #
import Test
import BenchmarkTools

# -*- Includes: Library -*- #
include("lib/error.jl")
Expand Down Expand Up @@ -62,7 +61,7 @@ export @anew
include("test/test.jl")

# -*- Includes: Benchmark -*-
include("benchmark/benchmark.jl")
# include("benchmark/benchmark.jl")

# -*- Includes: Submodules -*-
include("samplers/IdentitySampler.jl")
Expand Down
2 changes: 1 addition & 1 deletion src/interface/automatic/interface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Anneal.model_sense(sampler::AutomaticSampler) = QUBOTools.sense(sampler.source):
model_domain(sampler)::Union{Symbol,Nothing}
""" function model_domain end

Anneal.model_domain(sampler::AutomaticSampler) = QUBOTools.domain(sampler.source)::QUBOTools.VariableDomain
Anneal.model_domain(sampler::AutomaticSampler) = QUBOTools.domain(sampler.source)::QUBOTools.Domain

@doc raw"""
solver_sense(sampler)::Union{Symbol,Nothing}
Expand Down
11 changes: 9 additions & 2 deletions src/interface/automatic/macros.jl
Original file line number Diff line number Diff line change
Expand Up @@ -331,8 +331,8 @@ macro anew(raw_args...)

mutable struct $(esc(id)){T} <: Anneal.AutomaticSampler{T}
# ~*~ QUBOTools Backend model ~*~ #
source::Union{QUBOTools.Model,Nothing}
target::Union{QUBOTools.Model,Nothing}
source::Union{QUBOTools.Model{VI,T,Int},Nothing}
target::Union{QUBOTools.Model{VI,T,Int},Nothing}
# ~*~ Attributes ~*~ #
attrs::Anneal._SamplerAttributeData{T}

Expand Down Expand Up @@ -365,5 +365,12 @@ macro anew(raw_args...)

return nothing
end

# `do` block version
function $(esc(:test))(config!::Function; examples::Bool = false)
Anneal.test(config!, $(esc(id)); examples = examples)

return nothing
end
end
end
4 changes: 2 additions & 2 deletions src/interface/automatic/sample.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ function Anneal.sample!(sampler::AutomaticSampler)
results = @timed Anneal.sample(sampler)

target_results = _parse_results(results)
source_results = QUBOTools.format(
source_results = QUBOTools.cast(
QUBOTools.sense(backend(sampler)),
QUBOTools.domain(backend(sampler)),
QUBOTools.sense(frontend(sampler)),
QUBOTools.domain(backend(sampler)),
QUBOTools.domain(frontend(sampler)),
target_results,
)
Expand Down
48 changes: 26 additions & 22 deletions src/interface/automatic/wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,17 @@ function MOI.copy_to(sampler::AutomaticSampler{T}, model::MOI.ModelLike) where {
# Collect warm-start values
for vi in MOI.get(model, MOI.ListOfVariableIndices())
xi = MOI.get(model, MOI.VariablePrimalStart(), vi)

MOI.set(sampler, MOI.VariablePrimalStart(), vi, xi)
end

sampler.source = Anneal.parse_qubo_model(T, model)::QUBOTools.Model
sampler.target = QUBOTools.format(
sampler.target = QUBOTools.cast(
Anneal.model_sense(sampler),
Anneal.model_domain(sampler),
Anneal.solver_sense(sampler),
Anneal.model_domain(sampler),
Anneal.solver_domain(sampler),
sampler.source
sampler.source,
)::QUBOTools.Model

return MOIU.identity_index_map(model)
Expand Down Expand Up @@ -86,7 +86,7 @@ end

function MOI.get(sampler::AutomaticSampler, ::MOI.RawStatusString)
metadata = QUBOTools.metadata(QUBOTools.sampleset(frontend(sampler)))::Dict{String,Any}

if !haskey(metadata, "status")
return ""
else
Expand Down Expand Up @@ -188,19 +188,23 @@ function MOI.get(sampler::AutomaticSampler, ::MOI.NumberOfVariables)
end

# ~*~ File IO: Base API ~*~ #
function Base.write(filename::AbstractString, sampler::AutomaticSampler, fmt::QUBOTools.AbstractFormat = QUBOTools.infer_format(filename))
return QUBOTools.write_model(filename, frontend(sampler), fmt)
end

function Base.read!(filename::AbstractString, sampler::AutomaticSampler, fmt::QUBOTools.AbstractFormat = QUBOTools.infer_format(filename))
sampler.source = QUBOTools.read_model(filename, fmt)
sampler.target = __transpose_model(sampler, sampler.source)

return sampler
end

MOI.supports(
::AbstractSampler,
::MOI.VariablePrimalStart,
::MOI.VariableIndex,
) = true
# function Base.write(
# filename::AbstractString,
# sampler::AutomaticSampler,
# fmt::QUBOTools.AbstractFormat = QUBOTools.infer_format(filename),
# )
# return QUBOTools.write_model(filename, frontend(sampler), fmt)
# end

# function Base.read!(
# filename::AbstractString,
# sampler::AutomaticSampler,
# fmt::QUBOTools.AbstractFormat = QUBOTools.infer_format(filename),
# )
# sampler.source = QUBOTools.read_model(filename, fmt)
# sampler.target = QUBOTools.format(sampler, sampler.source)

# return sampler
# end

MOI.supports(::AbstractSampler, ::MOI.VariablePrimalStart, ::MOI.VariableIndex) = true
14 changes: 7 additions & 7 deletions src/lib/tools.jl
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,7 @@ end
function parse_qubo_model(T::Type, model::MOI.ModelLike)
# ~*~ Check for emptiness ~*~ #
if MOI.is_empty(model)
@warn "The given model is empty"
return QUBOTools.StandardQUBOModel{VI,Int,T,QUBOTools.BoolDomain}(
return QUBOTools.Model{VI,Int,T}(
Dict{VI,T}(),
Dict{Tuple{VI,VI},T}(),
)
Expand Down Expand Up @@ -191,7 +190,7 @@ function parse_qubo_model(T::Type, model::MOI.ModelLike)
# ~*~ Retrieve Variable Domain ~*~ #
# Assuming:
# - 𝕊, 𝔹 ⊆ Ω
D = if !isempty(𝕊) && !isempty(𝔹)
domain = if !isempty(𝕊) && !isempty(𝔹)
@error "The given model contains both boolean and spin variables"
flag = true

Expand All @@ -203,7 +202,7 @@ function parse_qubo_model(T::Type, model::MOI.ModelLike)

nothing
else
QUBOTools.BoolDomain
QUBOTools.BoolDomain()
end
elseif isempty(𝔹) # Ising model?
if 𝕊 != Ω
Expand All @@ -212,7 +211,7 @@ function parse_qubo_model(T::Type, model::MOI.ModelLike)

nothing
else
QUBOTools.SpinDomain
QUBOTools.SpinDomain()
end
end

Expand All @@ -223,17 +222,18 @@ function parse_qubo_model(T::Type, model::MOI.ModelLike)
end

# ~*~ Retrieve Model ~*~ #
L, Q, offset = __extract_qubo_model(T, Ω, model, D())
L, Q, offset = __extract_qubo_model(T, Ω, model, domain)
scale = one(T)

# ~*~ Objective Sense ~*~ #
sense = QUBOTools.Sense(MOI.get(model, MOI.ObjectiveSense()))

# ~*~ Return Model ~*~ #
return QUBOTools.Model{D,VI,T,Int}(
return QUBOTools.Model{VI,T,Int}(
L, Q;
scale = scale,
offset = offset,
sense = sense,
domain = domain,
)
end
12 changes: 11 additions & 1 deletion src/samplers/ExactSampler.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,19 @@ Anneal.@anew Optimizer begin
domain = :bool
end

@doc raw"""
ExactSampler.Optimizer{T}
This sampler performs an exhaustive search over all ``2^{n}`` possible states.
!!! warn
Due to the exponetially large amount of visited states, it is not possible
to use this sampler for problems any larger than ``20`` variables big.
""" Optimizer

function Anneal.sample(sampler::Optimizer{T}) where {T}
# ~*~ Retrieve Model ~*~ #
Q, α, β = Anneal.qubo(sampler, Dict, T)
Q, α, β = Anneal.qubo(sampler, Dict)

# ~*~ Retrieve Attributes ~*~ #
n = MOI.get(sampler, MOI.NumberOfVariables())
Expand Down
Loading

2 comments on commit bc48f29

@pedromxavier
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/75275

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.6.0 -m "<description of version>" bc48f2948913e7b43e83ce2cfafbfd1e23582028
git push origin v0.6.0

Please sign in to comment.