From 3aaec9dc34c717a3e7120a095297d4416a910ba0 Mon Sep 17 00:00:00 2001 From: pedromxavier Date: Mon, 11 Sep 2023 23:46:59 -0400 Subject: [PATCH 1/9] Modify Function Interface --- Project.toml | 4 +- benchmark/suites/constructors.jl | 2 +- benchmark/suites/operators.jl | 12 +- benchmark/suites/quadratization.jl | 2 +- src/PseudoBooleanOptimization.jl | 28 +- src/function/abstract.jl | 36 --- src/function/dict/function.jl | 2 +- src/function/vector/function.jl | 4 +- src/{interface.jl => interface/function.jl} | 134 ++------ src/interface/quadratization.jl | 60 ++++ src/interface/variable.jl | 27 ++ src/library.jl | 3 +- src/library/function/abstract.jl | 37 +++ src/library/function/function.jl | 7 + src/library/function/setdict.jl | 109 +++++++ src/library/function/vectordict.jl | 0 src/{ => library}/print.jl | 0 src/library/quadratization/abstract.jl | 141 +++++++++ src/library/quadratization/ntr_kzfd.jl | 48 +++ src/library/quadratization/ptr_bg.jl | 56 ++++ .../synthesis/not_all_equal_3sat.jl | 9 +- src/{ => library}/synthesis/regular_xorsat.jl | 13 +- .../synthesis/sherrington_kirkpatrick.jl | 6 +- src/{ => library}/synthesis/wishart.jl | 4 +- src/library/variable.jl | 77 +++++ src/quadratization.jl | 288 ------------------ 26 files changed, 630 insertions(+), 479 deletions(-) rename src/{interface.jl => interface/function.jl} (52%) create mode 100644 src/interface/quadratization.jl create mode 100644 src/interface/variable.jl create mode 100644 src/library/function/abstract.jl create mode 100644 src/library/function/function.jl create mode 100644 src/library/function/setdict.jl create mode 100644 src/library/function/vectordict.jl rename src/{ => library}/print.jl (100%) create mode 100644 src/library/quadratization/abstract.jl create mode 100644 src/library/quadratization/ntr_kzfd.jl create mode 100644 src/library/quadratization/ptr_bg.jl rename src/{ => library}/synthesis/not_all_equal_3sat.jl (79%) rename src/{ => library}/synthesis/regular_xorsat.jl (90%) rename src/{ => library}/synthesis/sherrington_kirkpatrick.jl (74%) rename src/{ => library}/synthesis/wishart.jl (97%) create mode 100644 src/library/variable.jl delete mode 100644 src/quadratization.jl diff --git a/Project.toml b/Project.toml index 3b08c68..7e42edc 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "PseudoBooleanOptimization" uuid = "c8fa9a04-bc42-452d-8558-dc51757be744" authors = ["pedromxavier "] -version = "0.1.1" +version = "0.2.0" [deps] LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" @@ -10,4 +10,4 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [compat] MutableArithmetics = "1.3" -julia = "1.6" +julia = "1.6" diff --git a/benchmark/suites/constructors.jl b/benchmark/suites/constructors.jl index 606694a..ffa85ae 100644 --- a/benchmark/suites/constructors.jl +++ b/benchmark/suites/constructors.jl @@ -1,4 +1,4 @@ -function benchmark_constructors!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T}} +function benchmark_constructors!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractPBF{V,T}} suite["constructors"] = BenchmarkGroup() return nothing diff --git a/benchmark/suites/operators.jl b/benchmark/suites/operators.jl index 54bb730..9c25bbb 100644 --- a/benchmark/suites/operators.jl +++ b/benchmark/suites/operators.jl @@ -1,4 +1,4 @@ -function benchmark_operators!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T}} +function benchmark_operators!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractPBF{V,T}} suite["operators"] = BenchmarkGroup() benchmark_add!(suite, F) @@ -11,7 +11,7 @@ function benchmark_operators!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFuncti return nothing end -function benchmark_add!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T}} +function benchmark_add!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractPBF{V,T}} suite["operators"]["+"] = BenchmarkGroup() suite["operators"]["+"]["small"] = @benchmarkable( f + g; @@ -31,7 +31,7 @@ function benchmark_add!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T return nothing end -function benchmark_sub!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T}} +function benchmark_sub!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractPBF{V,T}} suite["operators"]["-"] = BenchmarkGroup() suite["operators"]["-"]["small"] = @benchmarkable( f - g; @@ -51,7 +51,7 @@ function benchmark_sub!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T return nothing end -function benchmark_mul!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T}} +function benchmark_mul!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractPBF{V,T}} suite["operators"]["*"] = BenchmarkGroup() suite["operators"]["*"]["small"] = @benchmarkable( f * g; @@ -71,7 +71,7 @@ function benchmark_mul!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T return nothing end -function benchmark_dict_evaluation!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T}} +function benchmark_dict_evaluation!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractPBF{V,T}} suite["operators"]["dict-evaluation"] = BenchmarkGroup() suite["operators"]["dict-evaluation"]["small"] = @benchmarkable( f(x); @@ -91,7 +91,7 @@ function benchmark_dict_evaluation!(suite, ::Type{F}) where {V,T,F<:PBO.Abstract return nothing end -function benchmark_set_evaluation!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T}} +function benchmark_set_evaluation!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractPBF{V,T}} suite["operators"]["set-evaluation"] = BenchmarkGroup() suite["operators"]["set-evaluation"]["small"] = @benchmarkable( f(x); diff --git a/benchmark/suites/quadratization.jl b/benchmark/suites/quadratization.jl index 7e1321a..3ed8e94 100644 --- a/benchmark/suites/quadratization.jl +++ b/benchmark/suites/quadratization.jl @@ -1,4 +1,4 @@ -function benchmark_quadratization!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T}} +function benchmark_quadratization!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractPBF{V,T}} suite["quadratization"] = BenchmarkGroup() suite["quadratization"]["automatic"] = BenchmarkGroup() suite["quadratization"]["automatic"]["small"] = @benchmarkable( diff --git a/src/PseudoBooleanOptimization.jl b/src/PseudoBooleanOptimization.jl index 49334ed..2ad36de 100644 --- a/src/PseudoBooleanOptimization.jl +++ b/src/PseudoBooleanOptimization.jl @@ -5,24 +5,20 @@ using LinearAlgebra using MutableArithmetics const MA = MutableArithmetics -include("library.jl") -include("interface.jl") +# Interface Definition +include("interface/variable.jl") +include("interface/function.jl") +include("interface/quadratization.jl") -include("function/abstract.jl") -include("function/term.jl") -include("function/dict/function.jl") -# include("function/vector/function.jl") - -# This selects the implementation onwards -const PBF{V,T} = DictFunction{V,T} - -include("print.jl") -include("quadratization.jl") +# include("library/print.jl") +# include("library/quadratization/abstract.jl") +# include("library/quadratization/ntr_kzfd.jl") +# include("library/quadratization/ptr_bg.jl") # Synthetic PBF generation -include("synthesis/wishart.jl") -include("synthesis/regular_xorsat.jl") -include("synthesis/sherrington_kirkpatrick.jl") -include("synthesis/not_all_equal_3sat.jl") +# include("library/synthesis/wishart.jl") +# include("library/synthesis/regular_xorsat.jl") +# include("library/synthesis/sherrington_kirkpatrick.jl") +# include("library/synthesis/not_all_equal_3sat.jl") end # module PseudoBooleanOptimization diff --git a/src/function/abstract.jl b/src/function/abstract.jl index 27b83c8..8b13789 100644 --- a/src/function/abstract.jl +++ b/src/function/abstract.jl @@ -1,37 +1 @@ -function bounds(f::AbstractFunction) - return (lowerbound(f), upperbound(f)) -end -function discretize(f::AbstractFunction{V,T}; tol::T = 1E-6) where {V,T} - return discretize!(copy(f); tol) -end - -function gradient(f::AbstractFunction{V,T}, x::Vector{V}) where {V,T} - return [derivative(f, xi) for xi in x] -end - -function maxgap(f::F) where {V,T,F<:AbstractFunction{V,T}} - return sum(abs(c) for (ω, c) in f if !isempty(ω); init = zero(T)) -end - -function mingap(f::F; tol::T = 1E-6) where {V,T,F<:AbstractFunction{V,T}} - return relaxedgcd(collect(values(f)); tol)::T -end - -function lowerbound(f::F) where {V,T,F<:AbstractFunction{V,T}} - return sum((c < zero(T) || isempty(ω)) ? c : zero(T) for (ω, c) in f) -end - -function upperbound(f::F) where {V,T,F<:AbstractFunction{V,T}} - return sum((c > zero(T) || isempty(ω)) ? c : zero(T) for (ω, c) in f) -end - -function Base.convert(::Type{U}, f::AbstractFunction{V,T}) where {V,T,U<:T} - if isempty(f) - return zero(U) - elseif degree(f) == 0 - return convert(U, f[nothing]) - else - error("Can't convert non-constant pseudo-Boolean function to scalar type '$U'") - end -end diff --git a/src/function/dict/function.jl b/src/function/dict/function.jl index a229385..b34d9ba 100644 --- a/src/function/dict/function.jl +++ b/src/function/dict/function.jl @@ -1,4 +1,4 @@ -struct DictFunction{V,T} <: AbstractFunction{V,T} +struct DictFunction{V,T} <: AbstractPBF{V,T} Ω::Dict{Set{V},T} function DictFunction{V,T}(Ω::Dict{<:Union{Set{V},Nothing},T}) where {V,T} diff --git a/src/function/vector/function.jl b/src/function/vector/function.jl index 7ff422a..57a4cb3 100644 --- a/src/function/vector/function.jl +++ b/src/function/vector/function.jl @@ -1,7 +1,7 @@ @doc raw""" - VectorFunction{V,T} <: AbstractFunction{V,T} + VectorFunction{V,T} <: AbstractPBF{V,T} """ -struct VectorFunction{V,T} <: AbstractFunction{V,T} +struct VectorFunction{V,T} <: AbstractPBF{V,T} Ω::Vector{Term{V,T}} # standard constructor diff --git a/src/interface.jl b/src/interface/function.jl similarity index 52% rename from src/interface.jl rename to src/interface/function.jl index 65d746d..32200ea 100644 --- a/src/interface.jl +++ b/src/interface/function.jl @@ -1,12 +1,12 @@ @doc raw""" - AbstractTerm{V,T} -In the context of pseudo-Boolean functions, a term is a pair ``(\omega \subset \mathbb{V}, c_{\omega} \in \mathbb{T})``. """ -abstract type AbstractTerm{V,T} end +abstract type AbstractPseudoBooleanTerm{V,T} end + +const AbstractPBT{V,T} = AbstractPseudoBooleanTerm{V,T} @doc raw""" - AbstractFunction{V,T} + AbstractPseudoBooleanFunction{V,T} A pseudo-Boolean Function[^Boros2002] ``f \in \mathscr{F}`` over some field ``\mathbb{T}`` takes the form @@ -22,10 +22,17 @@ Variables ``x_j`` are boolean, thus ``f : \mathbb{B}^{n} \to \mathbb{T}``. [^Boros2002]: Endre Boros, Peter L. Hammer, **Pseudo-Boolean optimization**, *Discrete Applied Mathematics*, 2002 [{doi}](https://doi.org/10.1016/S0166-218X(01)00341-9) """ -abstract type AbstractFunction{V,T} end +abstract type AbstractPseudoBooleanFunction{V,T} end + +const AbstractPBF{V,T} = AbstractPseudoBooleanFunction{V,T} + +@doc raw""" + term +""" +function term end @doc raw""" - maxgap(f::AbstractFunction{V,T}) where {V,T} + maxgap(f::AbstractPBF{V,T}) where {V,T} Computes the least upper bound for the greatest variantion possible of ``f \in \mathscr{F}`` i. e. @@ -46,7 +53,7 @@ function maxgap end const δ = maxgap # \delta [tab] @doc raw""" - mingap(f::AbstractFunction{V,T}, tol::T = T(1e-6)) where {V,T} + mingap(f::AbstractPBF{V,T}, tol::T = T(1e-6)) where {V,T} The ideal minimum gap is the greatest lower bound on the smallest non-zero value taken by ``f \in \mathscr{F}``. """ @@ -55,7 +62,7 @@ function mingap end const ϵ = mingap # \epsilon [tab] @doc raw""" - derivative(f::AbstractFunction{V,T}, x::V) where {V,T} + derivative(f::AbstractPBF{V,T}, x::V) where {V,T} The partial derivate of function ``f \in \mathscr{F}`` with respect to the ``x`` variable. @@ -71,7 +78,7 @@ const Δ = derivative # \Delta [tab] const ∂ = derivative # \partial [tab] @doc raw""" - gradient(f::AbstractFunction) + gradient(f::AbstractPBF) Computes the gradient of ``f \in \mathscr{F}`` where the ``i``-th derivative is given by [`derivative`](@ref). """ @@ -80,7 +87,7 @@ function gradient end const ∇ = gradient # \nabla [tab] @doc raw""" - residual(f::AbstractFunction{V,T}, x::S) where {V,T} + residual(f::AbstractPBF{V,T}, x::S) where {V,T} The residual of ``f \in \mathscr{F}`` with respect to ``x``. @@ -95,7 +102,7 @@ function residual end const Θ = residual # \Theta [tab] @doc raw""" - discretize(f::AbstractFunction{V,T}; tol::T) where {V,T} + discretize(f::AbstractPBF{V,T}; tol::T) where {V,T} For a given function ``f \in \mathscr{F}`` written as @@ -115,128 +122,33 @@ This is done by rationalizing every coefficient ``c_\omega`` according to some t function discretize end @doc raw""" - discretize!(f::AbstractFunction{V,T}; tol::T) where {V,T} + discretize!(f::AbstractPBF{V,T}; tol::T) where {V,T} In-place version of [`discretize`](@ref). """ function discretize! end @doc raw""" - varlt(u::V, v::V) where {V} - -Compares two variables, ``u`` and ``v``, with respect to their length-lexicographic order. - -## Rationale -This function exists to define an arbitrary ordering for a given type and was created to address [^MOI]. -There is no predefined comparison between instances MOI's `VariableIndex` type. - - [^MOI]: MathOptInterface Issue [#1985](https://github.com/jump-dev/MathOptInterface.jl/issues/1985) -""" -function varlt end - -varlt(u::V, v::V) where {V} = isless(u, v) # fallback -varlt(u::V, v::V) where {V<:Symbol} = varlt(string(u), string(v)) - -function varlt(u::V, v::V) where {V<:AbstractString} - if length(u) == length(v) - return u < v - else - return length(u) < length(v) - end -end - -function varlt(u::AbstractVector{V}, v::AbstractVector{V}) where {V} - if length(u) == length(v) - # Vectors are assumed to be sorted! - # @assert issorted(u) && issorted(v) - @inbounds for i in eachindex(u) - if varlt(u[i], v[i]) - return true - elseif varlt(v[i], u[i]) - return false - else - continue - end - end - else - return length(u) < length(v) - end -end - -function varlt(u::Set{V}, v::Set{V}) where {V} - if length(u) == length(v) - x = sort!(collect(u); alg = InsertionSort, lt = varlt) - y = sort!(collect(v); alg = InsertionSort, lt = varlt) - - return varlt(x, y) - else - return length(u) < length(v) - end -end - -const ≺ = varlt # \prec[tab] - -@doc raw""" - varmul(u::V, v::V) where {V} -""" -function varmul end - -const × = varmul # \times[tab] - -function varmul(u::Set{V}, v::Set{V}) where {V} - return u ∪ v -end - -function varmul(u::V, v::Set{V}) where {V} - return u ∪ v -end - -function varmul(u::Set{V}, v::V) where {V} - return u ∪ v -end - -function varmul(u::V, v::V) where {V} - return Set{V}([u, v]) -end - -function varmul(u::AbstractVector{V}, v::AbstractVector{V}) where {V} - # Vectors are assumed to be sorted! - # @assert issorted(u) && issorted(v) - return sortedmergewith(u, v; lt = varlt) -end - -@doc raw""" - varshow(v::V) where {V} - varshow(io::IO, v::V) where {V} -""" -function varshow end - -varshow(io::IO, v::V) where {V} = print(io, varshow(v)) -varshow(v::Integer) = "x$(_subscript(v))" -varshow(v::V) where {V<:Union{Symbol,AbstractString}} = v - -@doc raw""" - degree(f::AbstractTerm) - degree(f::AbstractFunction) + degree(f::AbstractPBF) """ function degree end @doc raw""" - lowerbound(f::AbstractFunction) + lowerbound(f::AbstractPBF) Computes an approximate value for the greatest ``\forall \mathbf{x}. l \le f(\mathbf{x})``. """ function lowerbound end @doc raw""" - upperbound(f::AbstractFunction) + upperbound(f::AbstractPBF) Computes an approximate value for the least ``\forall \mathbf{x}. u \ge f(\mathbf{x})``. """ function upperbound end @doc raw""" - bounds(f::AbstractFunction) + bounds(f::AbstractPBF) Given ``f : \mathbb{B}^{n} \to [a, b]``, returns the approximate extrema for the tightest ``[l, u] \supset [a, b]``. """ diff --git a/src/interface/quadratization.jl b/src/interface/quadratization.jl new file mode 100644 index 0000000..1d95a0a --- /dev/null +++ b/src/interface/quadratization.jl @@ -0,0 +1,60 @@ +# :: Quadratization :: # +abstract type QuadratizationMethod end + +struct Quadratization{Q<:QuadratizationMethod} + method::Q + stable::Bool + + function Quadratization{Q}(method::Q, stable::Bool = false) where {Q<:QuadratizationMethod} + return new{Q}(method, stable) + end +end + +function Quadratization(method::Q; stable::Bool = false) where {Q<:QuadratizationMethod} + return Quadratization{Q}(method, stable) +end + +@doc raw""" + quadratize(aux, f::PBF{V, T}, ::Quadratization{Q}) where {V,T,Q} + +Quadratizes a given PBF, i.e., applies a mapping ``\mathcal{Q} : \mathscr{F}^{k} \to \mathscr{F}^{2}``, where ``\mathcal{Q}`` is the quadratization method. + +## Auxiliary variables +The `aux` function is expected to produce auxiliary variables with the following signatures: + + aux()::V where {V} + +Creates and returns a single variable. + + aux(n::Integer)::Vector{V} where {V} + +Creates and returns a vector with ``n`` variables. + + quadratize(f::PBF{V, T}, ::Quadratization{Q}) where {V,T,Q} + +When `aux` is not specified, uses [`auxgen`](@ref) to generate variables. +""" +function quadratize end + +@doc raw""" + quadratize!(aux, f::PBF{V, T}, ::Quadratization{Q}) where {V,T,Q} + quadratize!(f::PBF{V, T}, ::Quadratization{Q}) where {V,T,Q} + +In-place version of [`quadratize`](@ref). +""" +function quadratize! end + +@doc raw""" + auxgen(::AbstractPBF{V,T}; name::AbstractString = "aux") where {V<:AbstractString,T} + +Creates a function that, when called multiple times, returns the strings `"aux_1"`, `"aux_2"`, ... and so on. + + auxgen(::AbstractPBF{Symbol,T}; name::Symbol = :aux) where {T} + +Creates a function that, when called multiple times, returns the symbols `:aux_1`, `:aux_2`, ... and so on. + + auxgen(::AbstractPBF{V,T}; start::V = V(0), step::V = V(-1)) where {V<:Integer,T} + +Creates a function that, when called multiple times, returns the integers ``-1``, ``-2``, ... and so on. +""" +function auxgen end diff --git a/src/interface/variable.jl b/src/interface/variable.jl new file mode 100644 index 0000000..0ee16f8 --- /dev/null +++ b/src/interface/variable.jl @@ -0,0 +1,27 @@ +@doc raw""" + varlt(u::V, v::V) where {V} + +Compares two variables, ``u`` and ``v``, with respect to their length-lexicographic order. + +## Rationale +This function exists to define an arbitrary ordering for a given type and was created to address [^MOI]. +There is no predefined comparison between instances MOI's `VariableIndex` type. + + [^MOI]: MathOptInterface Issue [#1985](https://github.com/jump-dev/MathOptInterface.jl/issues/1985) +""" +function varlt end + +const ≺ = varlt # \prec[tab] + +@doc raw""" + varmul(u::V, v::V) where {V} +""" +function varmul end + +const × = varmul # \times[tab] + +@doc raw""" + varshow(v::V) where {V} + varshow(io::IO, v::V) where {V} +""" +function varshow end diff --git a/src/library.jl b/src/library.jl index 16fc593..be2ef2f 100644 --- a/src/library.jl +++ b/src/library.jl @@ -107,7 +107,6 @@ function _rowwise_allunique( m, n = size(x) for i = 1:m - u .= zero(S) for j = 1:n @@ -200,4 +199,4 @@ function _mod2_elimination!(A::AbstractMatrix{U}, b::AbstractVector{U}) where {U end return nothing -end \ No newline at end of file +end diff --git a/src/library/function/abstract.jl b/src/library/function/abstract.jl new file mode 100644 index 0000000..5157efd --- /dev/null +++ b/src/library/function/abstract.jl @@ -0,0 +1,37 @@ +function bounds(f::AbstractPBF) + return (lowerbound(f), upperbound(f)) +end + +function discretize(f::AbstractPBF{V,T}; tol::T = 1E-6) where {V,T} + return discretize!(copy(f); tol) +end + +function gradient(f::AbstractPBF{V,T}, x::Vector{V}) where {V,T} + return [derivative(f, xi) for xi in x] +end + +function maxgap(f::F) where {V,T,F<:AbstractPBF{V,T}} + return sum(abs(c) for (ω, c) in f if !isempty(ω); init = zero(T)) +end + +function mingap(f::F; tol::T = 1E-6) where {V,T,F<:AbstractPBF{V,T}} + return relaxedgcd(collect(values(f)); tol)::T +end + +function lowerbound(f::F) where {V,T,F<:AbstractPBF{V,T}} + return sum((c < zero(T) || isempty(ω)) ? c : zero(T) for (ω, c) in f) +end + +function upperbound(f::F) where {V,T,F<:AbstractPBF{V,T}} + return sum((c > zero(T) || isempty(ω)) ? c : zero(T) for (ω, c) in f) +end + +function Base.convert(::Type{U}, f::AbstractPBF{V,T}) where {V,T,U<:Number} + if isempty(f) + return zero(U) + elseif degree(f) == 0 + return convert(U, f[nothing]) + else + error("Can't convert non-constant pseudo-Boolean function to scalar type '$U'") + end +end diff --git a/src/library/function/function.jl b/src/library/function/function.jl new file mode 100644 index 0000000..ac0d429 --- /dev/null +++ b/src/library/function/function.jl @@ -0,0 +1,7 @@ +@doc raw""" + PseudoBooleanFunction{V,T,X} +""" +struct PseudoBooleanFunction{V,T,S} <: AbstractPseudoBooleanFunction{V,T} + Ω::S +end + diff --git a/src/library/function/setdict.jl b/src/library/function/setdict.jl new file mode 100644 index 0000000..e450488 --- /dev/null +++ b/src/library/function/setdict.jl @@ -0,0 +1,109 @@ +const SetDict{V,T} = Dict{Set{V},T} + +const SetDictKey{V} = Union{AbstractSet{V},AbstractVector{V},Nothing} + +function PBF{V,T,S}() where {V,T,S<:SetDict{V,T}} + return PBF{V,T,S}(SetDict{V,T}()) +end + +function PBF{V,T}(Ω::Dict{SetDictKey{V},T}) where {V,T} + return new{V,T,SetDict{V,T}}( + SetDict{V,T}((isnothing(ω) ? Set{V}() : ω) => c for (ω, c) in Ω if !iszero(c)) + ) +end + +struct DictFunction{V,T} <: AbstractPBF{V,T} + Ω::Dict{Set{V},T} + + + + function DictFunction{V,T}(v::AbstractVector) where {V,T} + Ω = Dict{Set{V},T}() + + for x in v + t = Term{V,T}(x) + ω = Set{V}(first(t)) + a = last(t) + + Ω[ω] = get(Ω, ω, zero(T)) + a + end + + return DictFunction{V,T}(Ω) + end + + function DictFunction{V,T}(x::Base.Generator) where {V,T} + return DictFunction{V,T}(collect(x)) + end + + function DictFunction{V,T}(x::Vararg{Any}) where {V,T} + return DictFunction{V,T}(collect(x)) + end + + function DictFunction{V,T}() where {V,T} + return new{V,T}(Dict{Set{V},T}()) + end +end + +# Broadcast as scalar +Base.broadcastable(f::DictFunction) = Ref(f) + +# Copy +function Base.copy!(f::DictFunction{V,T}, g::DictFunction{V,T}) where {V,T} + sizehint!(f, length(g)) + copy!(f.Ω, g.Ω) + + return f +end + +function Base.copy(f::DictFunction{V,T}) where {V,T} + return copy!(DictFunction{V,T}(), f) +end + +# Iterator & Length +Base.keys(f::DictFunction) = keys(f.Ω) +Base.values(f::DictFunction) = values(f.Ω) +Base.length(f::DictFunction) = length(f.Ω) +Base.empty!(f::DictFunction) = empty!(f.Ω) +Base.isempty(f::DictFunction) = isempty(f.Ω) +Base.iterate(f::DictFunction) = iterate(f.Ω) +Base.iterate(f::DictFunction, i::Integer) = iterate(f.Ω, i) + +Base.haskey(f::DictFunction{V}, ω::Set{V}) where {V} = haskey(f.Ω, ω) +Base.haskey(f::DictFunction{V}, ξ::V) where {V} = haskey(f, Set{V}([ξ])) +Base.haskey(f::DictFunction{V}, ::Nothing) where {V} = haskey(f, Set{V}()) + +# Indexing: Get # +Base.getindex(f::DictFunction{V,T}, ω::Set{V}) where {V,T} = get(f.Ω, ω, zero(T)) +Base.getindex(f::DictFunction{V}, η::Vector{V}) where {V} = getindex(f, Set{V}(η)) +Base.getindex(f::DictFunction{V}, ξ::V) where {V} = getindex(f, Set{V}([ξ])) +Base.getindex(f::DictFunction{V}, ::Nothing) where {V} = getindex(f, Set{V}()) + +# Indexing: Set # +function Base.setindex!(f::DictFunction{V,T}, c::T, ω::Set{V}) where {V,T} + if !iszero(c) + setindex!(f.Ω, c, ω) + elseif haskey(f, ω) + delete!(f, ω) + end + + return c +end + +Base.setindex!(f::DictFunction{V,T}, c::T, η::Vector{V}) where {V,T} = setindex!(f, c, Set{V}(η)) +Base.setindex!(f::DictFunction{V,T}, c::T, ξ::V) where {V,T} = setindex!(f, c, Set{V}([ξ])) +Base.setindex!(f::DictFunction{V,T}, c::T, ::Nothing) where {V,T} = setindex!(f, c, Set{V}()) + +# Indexing: Delete # +Base.delete!(f::DictFunction{V}, ω::Set{V}) where {V} = delete!(f.Ω, ω) +Base.delete!(f::DictFunction{V}, η::Vector{V}) where {V} = delete!(f, Set{V}(η)) +Base.delete!(f::DictFunction{V}, k::V) where {V} = delete!(f, Set{V}([k])) +Base.delete!(f::DictFunction{V}, ::Nothing) where {V} = delete!(f, Set{V}()) + +# Properties +Base.size(f::DictFunction{V,T}) where {V,T} = (length(f),) + +function Base.sizehint!(f::DictFunction, n::Integer) + sizehint!(f.Ω, n) + + return f +end \ No newline at end of file diff --git a/src/library/function/vectordict.jl b/src/library/function/vectordict.jl new file mode 100644 index 0000000..e69de29 diff --git a/src/print.jl b/src/library/print.jl similarity index 100% rename from src/print.jl rename to src/library/print.jl diff --git a/src/library/quadratization/abstract.jl b/src/library/quadratization/abstract.jl new file mode 100644 index 0000000..3235a9c --- /dev/null +++ b/src/library/quadratization/abstract.jl @@ -0,0 +1,141 @@ +@doc raw""" + Quadratization{DEFAULT}(stable::Bool = false) + +Employs other methods, specifically [`NTR_KZFD`](@ref) and [`PTR_BG`](@ref). +""" +struct DEFAULT <: QuadratizationMethod end + +function quadratize!( + aux, + f::PBF{V,T}, + ω::Set{V}, + c::T, + quad::Quadratization{DEFAULT}, +) where {V,T} + if c < zero(T) + quadratize!(aux, f, ω, c, Quadratization{NTR_KZFD}(quad.stable)) + else + quadratize!(aux, f, ω, c, Quadratization{PTR_BG}(quad.stable)) + end + + return f +end + +function quadratize!( + aux, + f::PBF{V,T}, + quad::Quadratization, +) where {V,T} + # Collect Terms + Ω = collect(f) + + # Stable Quadratization + quad.stable && sort!(Ω; by = first, lt = varlt) + + for (ω, c) in Ω + quadratize!(aux, f, ω, c, quad) + end + + return f +end + +@doc raw""" + Quadratization{INFER}(stable::Bool = false) +""" +struct INFER <: QuadratizationMethod end + +@doc raw""" + infer_quadratization(f::PBF) + +For a given PBF, returns whether it should be quadratized or not, based on its degree. +""" +function infer_quadratization(f::PBF, stable::Bool = false) + k = degree(f) + + if k <= 2 + return nothing + else + # Without any extra knowledge, it is better to + # quadratize using the default approach + return Quadratization{DEFAULT}(stable) + end +end + +function quadratize!(aux, f::PBF, quad::Quadratization{INFER}) + quadratize!(aux, f, infer_quadratization(f, quad.stable)) + + return f +end + +function quadratize!(::Function, f::PBF, ::Nothing) + return f +end + +@doc raw""" + auxgen(::AbstractPBF{V,T}; name::AbstractString = "aux") where {V<:AbstractString,T} + +Creates a function that, when called multiple times, returns the strings `"aux_1"`, `"aux_2"`, ... and so on. + + auxgen(::AbstractPBF{Symbol,T}; name::Symbol = :aux) where {T} + +Creates a function that, when called multiple times, returns the symbols `:aux_1`, `:aux_2`, ... and so on. + + auxgen(::AbstractPBF{V,T}; start::V = V(0), step::V = V(-1)) where {V<:Integer,T} + +Creates a function that, when called multiple times, returns the integers ``-1``, ``-2``, ... and so on. +""" +function auxgen end + +function auxgen(::AbstractPBF{Symbol,T}; name::Symbol = :aux) where {T} + counter = Int[0] + + function aux(n::Union{Integer,Nothing} = nothing) + if isnothing(n) + return first(aux(1)) + else + return [Symbol("$(name)_$(counter[] += 1)") for _ in 1:n] + end + end + + return aux +end + +function auxgen(::AbstractPBF{V,T}; name::AbstractString = "aux") where {V<:AbstractString,T} + counter = Int[0] + + function aux(n::Union{Integer,Nothing} = nothing) + if isnothing(n) + return first(aux(1)) + else + return ["$(name)_$(counter[] += 1)" for _ in 1:n] + end + end + + return aux +end + +function auxgen(::AbstractPBF{V,T}; start::V = V(0), step::V = V(-1)) where {V<:Integer,T} + counter = [start] + + function aux(n::Union{Integer,Nothing} = nothing) + if isnothing(n) + return first(aux(1)) + else + return [(counter[] += step) for _ in 1:n] + end + end + + return aux +end + +function quadratize!(f::PBF, quad::Union{Quadratization,Nothing} = Quadratization{INFER}) + return quadratize!(auxgen(f), f, quad) +end + +function quadratize(aux, f::PBF, quad::Union{Quadratization,Nothing} = Quadratization{INFER}) + return quadratize!(aux, copy(f), quad) +end + +function quadratize(f::PBF, quad::Union{Quadratization,Nothing} = Quadratization{INFER}) + return quadratize!(copy(f), quad) +end diff --git a/src/library/quadratization/ntr_kzfd.jl b/src/library/quadratization/ntr_kzfd.jl new file mode 100644 index 0000000..50fbe4d --- /dev/null +++ b/src/library/quadratization/ntr_kzfd.jl @@ -0,0 +1,48 @@ +@doc raw""" + Quadratization{NTR_KZFD}(stable::Bool = false) + +Negative term reduction NTR-KZFD (Kolmogorov & Zabih, 2004; Freedman & Drineas, 2005) + +Let ``f(\mathbf{x}) = -x_{1} x_{2} \dots x_{k}``. + +```math +\mathcal{Q}\left\lbrace{f}\right\rbrace(\mathbf{x}; z) = (k - 1) z - \sum_{i = 1}^{k} x_{i} z +``` + +where ``\mathbf{x} \in \mathbb{B}^k`` + +!!! info + Introduces a new variable ``z`` and no non-submodular terms. +""" +struct NTR_KZFD <: QuadratizationMethod end + +function quadratize!( + aux, + f::F, + ω::Set{V}, + c::T, + ::Quadratization{NTR_KZFD}, +) where {V,T,F<:AbstractPBF{V,T}} + # Degree + k = length(ω) + + # Fast-track + k < 3 && return nothing + + # Variables + s = aux()::V + + # Stabilization + # NOTE: This method is stable by construction + + # Quadratization + delete!(f, ω) + + f[s] += -c * (k - 1) + + for i ∈ ω + f[i×s] += c + end + + return f +end diff --git a/src/library/quadratization/ptr_bg.jl b/src/library/quadratization/ptr_bg.jl new file mode 100644 index 0000000..8411793 --- /dev/null +++ b/src/library/quadratization/ptr_bg.jl @@ -0,0 +1,56 @@ +@doc raw""" + Quadratization{PTR_BG}(stable::Bool = false) + +Positive term reduction PTR-BG (Boros & Gruber, 2014) + +Let ``f(\mathbf{x}) = x_{1} x_{2} \dots x_{k}``. + +```math +\mathcal{Q}\left\lbrace{f}\right\rbrace(\mathbf{x}; \mathbf{z}) = \left[{ + \sum_{i = 1}^{k-2} z_{i} \left({ k - i - 1 + x_{i} - \sum_{j = i+1}^{k} x_{j} }\right) +}\right] + x_{k-1} x_{k} +``` +where ``\mathbf{x} \in \mathbb{B}^k`` and ``\mathbf{z} \in \mathbb{B}^{k-2}`` + +!!! info + Introduces ``k - 2`` new variables ``z_{1}, \dots, z_{k-2}`` and ``k - 1`` non-submodular terms. +""" +struct PTR_BG <: QuadratizationMethod end + +function quadratize!( + aux, + f::F, + ω::Set{V}, + c::T, + quad::Quadratization{PTR_BG}, +) where {V,T,F<:AbstractPBF{V,T}} + # Degree + k = length(ω) + + # Fast-track + k < 3 && return nothing + + # Variables + s = aux(k - 2)::Vector{V} + b = collect(ω)::Vector{V} + + # Stabilization + quad.stable && sort!(b; lt = varlt) + + # Quadratization + delete!(f, ω) + + f[b[k]×b[k-1]] += c + + for i = 1:(k-2) + f[s[i]] += c * (k - i - 1) + + f[s[i]×b[i]] += c + + for j = (i+1):k + f[s[i]×b[j]] -= c + end + end + + return f +end diff --git a/src/synthesis/not_all_equal_3sat.jl b/src/library/synthesis/not_all_equal_3sat.jl similarity index 79% rename from src/synthesis/not_all_equal_3sat.jl rename to src/library/synthesis/not_all_equal_3sat.jl index 2bb4e2b..1e9e441 100644 --- a/src/synthesis/not_all_equal_3sat.jl +++ b/src/library/synthesis/not_all_equal_3sat.jl @@ -5,7 +5,7 @@ Generates Not-all-equal 3-SAT problem with ``m`` variables and ``n`` clauses. """ function not_all_equal_3sat end -function not_all_equal_3sat(rng, ::Type{F}, n::Integer, m::Integer) where {V,T,F<:AbstractFunction{V,T}} +function not_all_equal_3sat(rng, ::Type{F}, n::Integer, m::Integer) where {V,T,F<:AbstractPBF{V,T}} J = Dict{Tuple{Int,Int},T}() C = BitSet(1:n) @@ -31,9 +31,12 @@ function not_all_equal_3sat(rng, ::Type{F}, n::Integer, m::Integer) where {V,T,F # Convert to boolean # s = 2x - 1 - return sum([J[i,j] * F(i => T(2), T(-1)) * F(j => T(2), T(-1)) for (i,j) in keys(J)]) + f = sum([J[i,j] * F(i => T(2), T(-1)) * F(j => T(2), T(-1)) for (i,j) in keys(J)]) + x = nothing # no planted solutions + + return (f, x) end -function not_all_equal_3sat(::Type{F}, n::Integer, m::Integer) where {V,T,F<:AbstractFunction{V,T}} +function not_all_equal_3sat(::Type{F}, n::Integer, m::Integer) where {V,T,F<:AbstractPBF{V,T}} return not_all_equal_3sat(Random.GLOBAL_RNG, F, n, m) end diff --git a/src/synthesis/regular_xorsat.jl b/src/library/synthesis/regular_xorsat.jl similarity index 90% rename from src/synthesis/regular_xorsat.jl rename to src/library/synthesis/regular_xorsat.jl index f89d932..4d0a31a 100644 --- a/src/synthesis/regular_xorsat.jl +++ b/src/library/synthesis/regular_xorsat.jl @@ -1,4 +1,5 @@ @doc raw""" + k_regular_k_xorsat Generates a ``k``-regurlar ``k``-XORSAT instance with ``n`` boolean variables. """ @@ -8,7 +9,7 @@ function k_regular_k_xorsat( n::Integer, k::Integer; quad::Union{Quadratization,Nothing} = nothing, -) where {V,T,F<:AbstractFunction{V,T}} +) where {V,T,F<:AbstractPBF{V,T}} idx = zeros(Int, n, k) for j = 1:k, i = 1:n @@ -59,7 +60,7 @@ function k_regular_k_xorsat( n::Integer, k::Integer; quad::Union{Quadratization,Nothing} = nothing, -) where {V,T,F<:AbstractFunction{V,T}} +) where {V,T,F<:AbstractPBF{V,T}} return k_regular_k_xorsat(Random.GLOBAL_RNG, F, n, k; quad) end @@ -77,12 +78,14 @@ function r_regular_k_xorsat( r::Integer, k::Integer; quad::Union{Quadratization,Nothing} = nothing, -) where {V,T,F<:AbstractFunction{V,T}} +) where {V,T,F<:AbstractPBF{V,T}} if r == k return k_regular_k_xorsat(rng, F, n, k; quad) else # NOTE: This might involve Sudoku solving! - error("A method for generating r-regular k-XORSAT where r ≂̸ k is not implemented") + error("A method for generating r-regular k-XORSAT where r ≂̸ k is not implemented (yet).") + + return nothing end end @@ -92,6 +95,6 @@ function r_regular_k_xorsat( r::Integer, k::Integer; quad::Union{Quadratization,Nothing} = nothing, -) where {V,T,F<:AbstractFunction{V,T}} +) where {V,T,F<:AbstractPBF{V,T}} return r_regular_k_xorsat(Random.GLOBAL_RNG, F, n, r, k; quad) end diff --git a/src/synthesis/sherrington_kirkpatrick.jl b/src/library/synthesis/sherrington_kirkpatrick.jl similarity index 74% rename from src/synthesis/sherrington_kirkpatrick.jl rename to src/library/synthesis/sherrington_kirkpatrick.jl index f824ef2..65c1b9b 100644 --- a/src/synthesis/sherrington_kirkpatrick.jl +++ b/src/library/synthesis/sherrington_kirkpatrick.jl @@ -1,5 +1,5 @@ @doc raw""" - sherrington_kirkpatrick(rng, ::Type{F}, n::Integer; μ::T = zero(T), σ::T = one(T)) where {V,T,F<:AbstractFunction{V,T}} + sherrington_kirkpatrick(rng, ::Type{F}, n::Integer; μ::T = zero(T), σ::T = one(T)) where {V,T,F<:AbstractPBF{V,T}} ```math f^{(n)}_{\textrm{SK}}(\mathbf{x}) = \sum_{i = 1}^{n} \sum_{j = i + 1}^{n} J_{i, j} x_i x_j @@ -8,13 +8,13 @@ f^{(n)}_{\textrm{SK}}(\mathbf{x}) = \sum_{i = 1}^{n} \sum_{j = i + 1}^{n} J_{i, where ``J_{i, j} \sim \mathcal{N}(0, 1)``. """ -function sherrington_kirkpatrick(rng, ::Type{F}, n::Integer; μ::T = zero(T), σ::T = one(T)) where {V,T,F<:AbstractFunction{V,T}} +function sherrington_kirkpatrick(rng, ::Type{F}, n::Integer; μ::T = zero(T), σ::T = one(T)) where {V,T,F<:AbstractPBF{V,T}} return sum([ (σ * randn(rng, T) + μ) * F(i => T(2), T(-1)) * F(j => T(2), T(-1)) for i = 1:n for j = (i+1):n ]) end -function sherrington_kirkpatrick(::Type{F}, n::Integer; μ::T = zero(T), σ::T = one(T)) where {V,T,F<:AbstractFunction{V,T}} +function sherrington_kirkpatrick(::Type{F}, n::Integer; μ::T = zero(T), σ::T = one(T)) where {V,T,F<:AbstractPBF{V,T}} return sherrington_kirkpatrick(Random.GLOBAL_RNG, F, n; μ, σ) end diff --git a/src/synthesis/wishart.jl b/src/library/synthesis/wishart.jl similarity index 97% rename from src/synthesis/wishart.jl rename to src/library/synthesis/wishart.jl index 00df7c5..ddd95c5 100644 --- a/src/synthesis/wishart.jl +++ b/src/library/synthesis/wishart.jl @@ -20,7 +20,7 @@ Alternatively, can even replace the Gaussian with a bounded range uniform discre """ function wishart end -function wishart(rng, ::Type{F}, n::Integer, m::Integer = 1; discretize_bonds::Bool = false, precision = nothing) where {V,T,F<:AbstractFunction{V,T}} +function wishart(rng, ::Type{F}, n::Integer, m::Integer = 1; discretize_bonds::Bool = false, precision = nothing) where {V,T,F<:AbstractPBF{V,T}} # Plants the FM GS t = ones(n, 1) @@ -53,6 +53,6 @@ function wishart(rng, ::Type{F}, n::Integer, m::Integer = 1; discretize_bonds::B return sum([J[i,j] * F(i => T(2), T(-1)) * F(j => T(2), T(-1)) for i = 1:n for j = 1:n]) end -function wishart(::Type{F}, n::Integer, m::Integer = 1; discretize_bonds::Bool = false, precision = nothing) where {V,T,F<:AbstractFunction{V,T}} +function wishart(::Type{F}, n::Integer, m::Integer = 1; discretize_bonds::Bool = false, precision = nothing) where {V,T,F<:AbstractPBF{V,T}} return wishart(GLOBAL_RNG, F, n, m; discretize_bonds, precision) end diff --git a/src/library/variable.jl b/src/library/variable.jl new file mode 100644 index 0000000..58aa81c --- /dev/null +++ b/src/library/variable.jl @@ -0,0 +1,77 @@ +varlt(u::V, v::V) where {V} = isless(u, v) # fallback +varlt(u::V, v::V) where {V<:Symbol} = varlt(string(u), string(v)) + +function varlt(u::V, v::V) where {V<:Signed} + # For signed integers, the order should be as follows: + # 0, 1, 2, 3, ..., -1, -2, -3, ... + if sign(u) == sign(v) + return v < u + else + return sign(u) < sign(v) + end +end + +function varlt(u::V, v::V) where {V<:AbstractString} + if length(u) == length(v) + return u < v + else + return length(u) < length(v) + end +end + +function varlt(u::AbstractVector{V}, v::AbstractVector{V}) where {V} + if length(u) == length(v) + # Vectors are assumed to be sorted! + # @assert issorted(u) && issorted(v) + @inbounds for i in eachindex(u) + if varlt(u[i], v[i]) + return true + elseif varlt(v[i], u[i]) + return false + else + continue + end + end + else + return length(u) < length(v) + end +end + +function varlt(u::Set{V}, v::Set{V}) where {V} + if length(u) == length(v) + x = sort!(collect(u); alg = InsertionSort, lt = varlt) + y = sort!(collect(v); alg = InsertionSort, lt = varlt) + + return varlt(x, y) + else + return length(u) < length(v) + end +end + + +function varmul(u::Set{V}, v::Set{V}) where {V} + return u ∪ v +end + +function varmul(u::V, v::Set{V}) where {V} + return u ∪ v +end + +function varmul(u::Set{V}, v::V) where {V} + return u ∪ v +end + +function varmul(u::V, v::V) where {V} + return Set{V}([u, v]) +end + +function varmul(u::AbstractVector{V}, v::AbstractVector{V}) where {V} + # Vectors are assumed to be sorted! + # @assert issorted(u) && issorted(v) + return sortedmergewith(u, v; lt = varlt) +end + + +varshow(io::IO, v::V) where {V} = show(io, varshow(v)) +varshow(v::Integer) = "x$(_subscript(v))" +varshow(v::V) where {V<:Union{Symbol,AbstractString}} = v diff --git a/src/quadratization.jl b/src/quadratization.jl deleted file mode 100644 index e5fbac5..0000000 --- a/src/quadratization.jl +++ /dev/null @@ -1,288 +0,0 @@ -# :: Quadratization :: # -abstract type QuadratizationMethod end - -struct Quadratization{Q<:QuadratizationMethod} - stable::Bool - - function Quadratization{Q}(stable::Bool = false) where {Q<:QuadratizationMethod} - return new{Q}(stable) - end -end - -@doc raw""" - quadratize(aux, f::PBF{V, T}, ::Quadratization{Q}) where {V,T,Q} - -Quadratizes a given PBF, i.e., applies a mapping ``\mathcal{Q} : \mathscr{F}^{k} \to \mathscr{F}^{2}``, where ``\mathcal{Q}`` is the quadratization method. - -## Auxiliary variables -The `aux` function is expected to produce auxiliary variables with the following signatures: - - aux()::V where {V} - -Creates and returns a single variable. - - aux(n::Integer)::Vector{V} where {V} - -Creates and returns a vector with ``n`` variables. - - quadratize(f::PBF{V, T}, ::Quadratization{Q}) where {V,T,Q} - -When `aux` is not specified, uses [`auxgen`](@ref) to generate variables. -""" -function quadratize end - -@doc raw""" - quadratize!(aux, f::PBF{V, T}, ::Quadratization{Q}) where {V,T,Q} - quadratize!(f::PBF{V, T}, ::Quadratization{Q}) where {V,T,Q} - -In-place version of [`quadratize`](@ref). -""" -function quadratize! end - -@doc raw""" - Quadratization{NTR_KZFD}(stable::Bool = false) - -Negative term reduction NTR-KZFD (Kolmogorov & Zabih, 2004; Freedman & Drineas, 2005) - -Let ``f(\mathbf{x}) = -x_{1} x_{2} \dots x_{k}``. - -```math -\mathcal{Q}\left\lbrace{f}\right\rbrace(\mathbf{x}; z) = (k - 1) z - \sum_{i = 1}^{k} x_{i} z -``` - -where ``\mathbf{x} \in \mathbb{B}^k`` - -!!! info - Introduces a new variable ``z`` and no non-submodular terms. -""" -struct NTR_KZFD <: QuadratizationMethod end - -function quadratize!( - aux, - f::PBF{V,T}, - ω::Set{V}, - c::T, - ::Quadratization{NTR_KZFD}, -) where {V,T} - # Degree - k = length(ω) - - # Fast-track - k < 3 && return nothing - - # Variables - s = aux()::V - - # Stabilize - # NOTE: This method is stable by construction - - # Quadratization - delete!(f, ω) - - f[s] += -c * (k - 1) - - for i ∈ ω - f[i×s] += c - end - - return f -end - -@doc raw""" - Quadratization{PTR_BG}(stable::Bool = false) - -Positive term reduction PTR-BG (Boros & Gruber, 2014) - -Let ``f(\mathbf{x}) = x_{1} x_{2} \dots x_{k}``. - -```math -\mathcal{Q}\left\lbrace{f}\right\rbrace(\mathbf{x}; \mathbf{z}) = \left[{ - \sum_{i = 1}^{k-2} z_{i} \left({ k - i - 1 + x_{i} - \sum_{j = i+1}^{k} x_{j} }\right) -}\right] + x_{k-1} x_{k} -``` -where ``\mathbf{x} \in \mathbb{B}^k`` and ``\mathbf{z} \in \mathbb{B}^{k-2}`` - -!!! info - Introduces ``k - 2`` new variables ``z_{1}, \dots, z_{k-2}`` and ``k - 1`` non-submodular terms. -""" -struct PTR_BG <: QuadratizationMethod end - -function quadratize!( - aux, - f::PBF{V,T}, - ω::Set{V}, - c::T, - quad::Quadratization{PTR_BG}, -) where {V,T} - # Degree - k = length(ω) - - # Fast-track - k < 3 && return nothing - - # Variables - s = aux(k - 2)::Vector{V} - b = collect(ω)::Vector{V} - - # Stabilize - quad.stable && sort!(b; lt = varlt) - - # Quadratization - delete!(f, ω) - - f[b[k]×b[k-1]] += c - - for i = 1:(k-2) - f[s[i]] += c * (k - i - 1) - - f[s[i]×b[i]] += c - - for j = (i+1):k - f[s[i]×b[j]] -= c - end - end - - return f -end - -@doc raw""" - Quadratization{DEFAULT}(stable::Bool = false) - -Employs other methods, specifically [`NTR_KZFD`](@ref) and [`PTR_BG`](@ref). -""" -struct DEFAULT <: QuadratizationMethod end - -function quadratize!( - aux, - f::PBF{V,T}, - ω::Set{V}, - c::T, - quad::Quadratization{DEFAULT}, -) where {V,T} - if c < zero(T) - quadratize!(aux, f, ω, c, Quadratization{NTR_KZFD}(quad.stable)) - else - quadratize!(aux, f, ω, c, Quadratization{PTR_BG}(quad.stable)) - end - - return f -end - -function quadratize!( - aux, - f::PBF{V,T}, - quad::Quadratization, -) where {V,T} - # Collect Terms - Ω = collect(f) - - # Stable Quadratization - quad.stable && sort!(Ω; by = first, lt = varlt) - - for (ω, c) in Ω - quadratize!(aux, f, ω, c, quad) - end - - return f -end - -@doc raw""" - Quadratization{INFER}(stable::Bool = false) -""" -struct INFER <: QuadratizationMethod end - -@doc raw""" - infer_quadratization(f::PBF) - -For a given PBF, returns whether it should be quadratized or not, based on its degree. -""" -function infer_quadratization(f::PBF, stable::Bool = false) - k = degree(f) - - if k <= 2 - return nothing - else - # Without any extra knowledge, it is better to - # quadratize using the default approach - return Quadratization{DEFAULT}(stable) - end -end - -function quadratize!(aux, f::PBF, quad::Quadratization{INFER}) - quadratize!(aux, f, infer_quadratization(f, quad.stable)) - - return f -end - -function quadratize!(::Function, f::PBF, ::Nothing) - return f -end - -@doc raw""" - auxgen(::AbstractFunction{V,T}; name::AbstractString = "aux") where {V<:AbstractString,T} - -Creates a function that, when called multiple times, returns the strings `"aux_1"`, `"aux_2"`, ... and so on. - - auxgen(::AbstractFunction{Symbol,T}; name::Symbol = :aux) where {T} - -Creates a function that, when called multiple times, returns the symbols `:aux_1`, `:aux_2`, ... and so on. - - auxgen(::AbstractFunction{V,T}; start::V = V(0), step::V = V(-1)) where {V<:Integer,T} - -Creates a function that, when called multiple times, returns the integers ``-1``, ``-2``, ... and so on. -""" -function auxgen end - -function auxgen(::AbstractFunction{Symbol,T}; name::Symbol = :aux) where {T} - counter = Int[0] - - function aux(n::Union{Integer,Nothing} = nothing) - if isnothing(n) - return first(aux(1)) - else - return [Symbol("$(name)_$(counter[] += 1)") for _ in 1:n] - end - end - - return aux -end - -function auxgen(::AbstractFunction{V,T}; name::AbstractString = "aux") where {V<:AbstractString,T} - counter = Int[0] - - function aux(n::Union{Integer,Nothing} = nothing) - if isnothing(n) - return first(aux(1)) - else - return ["$(name)_$(counter[] += 1)" for _ in 1:n] - end - end - - return aux -end - -function auxgen(::AbstractFunction{V,T}; start::V = V(0), step::V = V(-1)) where {V<:Integer,T} - counter = [start] - - function aux(n::Union{Integer,Nothing} = nothing) - if isnothing(n) - return first(aux(1)) - else - return [(counter[] += step) for _ in 1:n] - end - end - - return aux -end - -function quadratize!(f::PBF, quad::Union{Quadratization,Nothing} = Quadratization{INFER}) - return quadratize!(auxgen(f), f, quad) -end - -function quadratize(aux, f::PBF, quad::Union{Quadratization,Nothing} = Quadratization{INFER}) - return quadratize!(aux, copy(f), quad) -end - -function quadratize(f::PBF, quad::Union{Quadratization,Nothing} = Quadratization{INFER}) - return quadratize!(copy(f), quad) -end From 342e823824ec908fcdc898ca24e782a16b48039a Mon Sep 17 00:00:00 2001 From: pedromxavier Date: Fri, 29 Sep 2023 21:54:53 -0400 Subject: [PATCH 2/9] Update interface --- docs/src/index.md | 1 + src/PseudoBooleanOptimization.jl | 8 +- src/function/abstract.jl | 1 - src/function/dict/function.jl | 102 ----- src/function/dict/interface.jl | 21 - src/function/term.jl | 129 ------ src/function/vector/function.jl | 81 ---- src/function/vector/interface.jl | 1 - src/function/vector/operators.jl | 171 -------- src/interface/function.jl | 22 +- src/library.jl | 200 --------- src/library/function/abstract.jl | 30 +- src/library/function/function.jl | 52 ++- src/library/function/setdict.jl | 109 ----- .../function/setdict}/operators.jl | 17 - src/library/function/setdict/setdict.jl | 127 ++++++ src/library/function/vector/tuplevector.jl | 386 ++++++++++++++++++ .../function/{ => vector}/vectordict.jl | 0 src/library/mod2linsolve.jl | 110 +++++ src/library/print.jl | 14 +- src/library/relaxedgcd.jl | 38 ++ src/library/sortedmerge.jl | 48 +++ .../{variable.jl => variable/abstract.jl} | 2 +- test/runtests.jl | 2 + test/unit/unit.jl | 16 +- 25 files changed, 831 insertions(+), 857 deletions(-) delete mode 100644 src/function/abstract.jl delete mode 100644 src/function/dict/function.jl delete mode 100644 src/function/dict/interface.jl delete mode 100644 src/function/term.jl delete mode 100644 src/function/vector/function.jl delete mode 100644 src/function/vector/interface.jl delete mode 100644 src/function/vector/operators.jl delete mode 100644 src/library/function/setdict.jl rename src/{function/dict => library/function/setdict}/operators.jl (82%) create mode 100644 src/library/function/setdict/setdict.jl create mode 100644 src/library/function/vector/tuplevector.jl rename src/library/function/{ => vector}/vectordict.jl (100%) create mode 100644 src/library/mod2linsolve.jl create mode 100644 src/library/relaxedgcd.jl create mode 100644 src/library/sortedmerge.jl rename src/library/{variable.jl => variable/abstract.jl} (96%) diff --git a/docs/src/index.md b/docs/src/index.md index 99d888a..66aec2c 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -14,6 +14,7 @@ f = PBO.PBF{Symbol,Float64}( :x => 3.0, (:y, :z) => 4.0, (:x, :w) => 1.0, + -100.0, ) g = f^2 - f diff --git a/src/PseudoBooleanOptimization.jl b/src/PseudoBooleanOptimization.jl index 2ad36de..d31d093 100644 --- a/src/PseudoBooleanOptimization.jl +++ b/src/PseudoBooleanOptimization.jl @@ -10,7 +10,13 @@ include("interface/variable.jl") include("interface/function.jl") include("interface/quadratization.jl") -# include("library/print.jl") +# include("library/variable/abstract.jl") +# include("library/variable/mapping.jl") + +include("library/function/abstract.jl") +include("library/function/function.jl") +include("library/function/setdict.jl") + # include("library/quadratization/abstract.jl") # include("library/quadratization/ntr_kzfd.jl") # include("library/quadratization/ptr_bg.jl") diff --git a/src/function/abstract.jl b/src/function/abstract.jl deleted file mode 100644 index 8b13789..0000000 --- a/src/function/abstract.jl +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/function/dict/function.jl b/src/function/dict/function.jl deleted file mode 100644 index b34d9ba..0000000 --- a/src/function/dict/function.jl +++ /dev/null @@ -1,102 +0,0 @@ -struct DictFunction{V,T} <: AbstractPBF{V,T} - Ω::Dict{Set{V},T} - - function DictFunction{V,T}(Ω::Dict{<:Union{Set{V},Nothing},T}) where {V,T} - return new{V,T}( - Dict{Set{V},T}(isnothing(ω) ? Set{V}() : ω => c for (ω, c) in Ω if !iszero(c)), - ) - end - - function DictFunction{V,T}(v::AbstractVector) where {V,T} - Ω = Dict{Set{V},T}() - - for x in v - t = Term{V,T}(x) - ω = Set{V}(first(t)) - a = last(t) - - Ω[ω] = get(Ω, ω, zero(T)) + a - end - - return DictFunction{V,T}(Ω) - end - - function DictFunction{V,T}(x::Base.Generator) where {V,T} - return DictFunction{V,T}(collect(x)) - end - - function DictFunction{V,T}(x::Vararg{Any}) where {V,T} - return DictFunction{V,T}(collect(x)) - end - - function DictFunction{V,T}() where {V,T} - return new{V,T}(Dict{Set{V},T}()) - end -end - -# Broadcast as scalar -Base.broadcastable(f::DictFunction) = Ref(f) - -# Copy -function Base.copy!(f::DictFunction{V,T}, g::DictFunction{V,T}) where {V,T} - sizehint!(f, length(g)) - copy!(f.Ω, g.Ω) - - return f -end - -function Base.copy(f::DictFunction{V,T}) where {V,T} - return copy!(DictFunction{V,T}(), f) -end - -# Iterator & Length -Base.keys(f::DictFunction) = keys(f.Ω) -Base.values(f::DictFunction) = values(f.Ω) -Base.length(f::DictFunction) = length(f.Ω) -Base.empty!(f::DictFunction) = empty!(f.Ω) -Base.isempty(f::DictFunction) = isempty(f.Ω) -Base.iterate(f::DictFunction) = iterate(f.Ω) -Base.iterate(f::DictFunction, i::Integer) = iterate(f.Ω, i) - -Base.haskey(f::DictFunction{V}, ω::Set{V}) where {V} = haskey(f.Ω, ω) -Base.haskey(f::DictFunction{V}, ξ::V) where {V} = haskey(f, Set{V}([ξ])) -Base.haskey(f::DictFunction{V}, ::Nothing) where {V} = haskey(f, Set{V}()) - -# Indexing: Get # -Base.getindex(f::DictFunction{V,T}, ω::Set{V}) where {V,T} = get(f.Ω, ω, zero(T)) -Base.getindex(f::DictFunction{V}, η::Vector{V}) where {V} = getindex(f, Set{V}(η)) -Base.getindex(f::DictFunction{V}, ξ::V) where {V} = getindex(f, Set{V}([ξ])) -Base.getindex(f::DictFunction{V}, ::Nothing) where {V} = getindex(f, Set{V}()) - -# Indexing: Set # -function Base.setindex!(f::DictFunction{V,T}, c::T, ω::Set{V}) where {V,T} - if !iszero(c) - setindex!(f.Ω, c, ω) - elseif haskey(f, ω) - delete!(f, ω) - end - - return c -end - -Base.setindex!(f::DictFunction{V,T}, c::T, η::Vector{V}) where {V,T} = setindex!(f, c, Set{V}(η)) -Base.setindex!(f::DictFunction{V,T}, c::T, ξ::V) where {V,T} = setindex!(f, c, Set{V}([ξ])) -Base.setindex!(f::DictFunction{V,T}, c::T, ::Nothing) where {V,T} = setindex!(f, c, Set{V}()) - -# Indexing: Delete # -Base.delete!(f::DictFunction{V}, ω::Set{V}) where {V} = delete!(f.Ω, ω) -Base.delete!(f::DictFunction{V}, η::Vector{V}) where {V} = delete!(f, Set{V}(η)) -Base.delete!(f::DictFunction{V}, k::V) where {V} = delete!(f, Set{V}([k])) -Base.delete!(f::DictFunction{V}, ::Nothing) where {V} = delete!(f, Set{V}()) - -# Properties -Base.size(f::DictFunction{V,T}) where {V,T} = (length(f),) - -function Base.sizehint!(f::DictFunction, n::Integer) - sizehint!(f.Ω, n) - - return f -end - -include("interface.jl") -include("operators.jl") diff --git a/src/function/dict/interface.jl b/src/function/dict/interface.jl deleted file mode 100644 index 8fa5504..0000000 --- a/src/function/dict/interface.jl +++ /dev/null @@ -1,21 +0,0 @@ -function degree(f::DictFunction) - return maximum(length.(keys(f)); init = 0) -end - -function discretize!(f::DictFunction{V,T}; tol::T = 1E-6) where {V,T} - ε = mingap(f; tol) - - for (ω, c) in f - f[ω] = round(c / ε; digits = 0) - end - - return f -end - -function derivative(f::DictFunction{V,T}, x::V) where {V,T} - return DictFunction{V,T}(ω => f[ω×x] for ω ∈ keys(f) if (x ∉ ω)) -end - -function residual(f::DictFunction{V,T}, x::V) where {V,T} - return DictFunction{V,T}(ω => c for (ω, c) ∈ f if (x ∉ ω)) -end diff --git a/src/function/term.jl b/src/function/term.jl deleted file mode 100644 index a7abed5..0000000 --- a/src/function/term.jl +++ /dev/null @@ -1,129 +0,0 @@ -const TermKey{V} = Union{AbstractVector{V},AbstractSet{V},Tuple{Vararg{V}}} - -@doc raw""" - Term{V,T} <: AbstractTerm{V,T} -""" -struct Term{V,T} <: AbstractTerm{V,T} - ω::Vector{V} - c::T - - # standard constructor - function Term{V,T}(x::K, c::T) where {V,T,K<:TermKey{V}} - ω = unique!(sort!(collect(x); alg = InsertionSort, lt = varlt)) - - return new{V,T}(ω, c) - end - - # shortcut: fast-track - function Term{V,T}(x::K, c::T, ::Nothing) where {V,T,K<:TermKey{V}} - return new{V,T}(x, c) - end - - # shortcut: zero - Term{V,T}() where {V,T} = new{V,T}(V[], zero(T)) - - # shortcut: one - Term{V,T}(::Nothing) where {V,T} = new{V,T}(V[], one(T)) - - # shortcut: constant - Term{V,T}(c::T) where {V,T} = new{V,T}(V[], c) - - # shortcut: variable - Term{V,T}(v::V) where {V,T} = new{V,T}(V[v], one(T)) - - # shortcut: monomial - Term{V,T}(v::V, c::T) where {V,T} = new{V,T}(V[v], c) - - # shortcut: fallback error - Term{V,T}(x::Any) where {V,T} = error("Invalid term '$(x)'") -end - -function Term{V,T}(ω::K) where {V,T,K<:TermKey{V}} - return Term{V,T}(ω, one(T)) -end - -function Term{V,T}((_, c)::Pair{Nothing,T}) where {V,T} - return Term{V,T}(c) -end - -function Term{V,T}((_, c)::Tuple{Nothing,T}) where {V,T} - return Term{V,T}(c) -end - -function Term{V,T}((v, c)::Pair{V,T}) where {V,T} - return Term{V,T}(v, c) -end - -function Term{V,T}( - (ω, c)::Pair{K,T}, -) where {V,T,K<:Union{AbstractVector{V},AbstractSet{V},Tuple{Vararg{V}}}} - return Term{V,T}(ω, c) -end - -function varlt(u::Term{V,T}, v::Term{V,T}) where {V,T} - if length(u.ω) == length(v.ω) - return varlt(u.ω, v.ω) - else - return isless(length(u.ω), length(v.ω)) - end -end - -function varmul(u::Term{V,T}, v::Term{V,T}) where {V,T} - return sortedmergewith(u, v; lt = varlt) -end - -function Base.length(::Term) - return 2 -end - -function Base.iterate(u::Term) - return (first(u), 2) -end - -function Base.iterate(u::Term, i::Integer) - if i == 1 - return (first(u), 2) - elseif i == 2 - return (last(u), 3) - else - return nothing - end -end - -function Base.first(u::Term) - return u.ω -end - -function Base.last(u::Term) - return u.c -end - -function Base.:(+)(u::Term{V,T}, v::Term{V,T}) where {V,T} - @assert u.ω == v.ω - - return Term{V,T}(u.ω, u.c + v.c, true) # use fast-track -end - -function Base.:(-)(u::Term{V,T}, v::Term{V,T}) where {V,T} - @assert u.ω == v.ω - - return Term{V,T}(u.ω, u.c - v.c, true) # use fast-track -end - -function Base.:(*)(u::Term{V,T}, c::T) where {V,T} - return Term{V,T}(u.ω, u.c * c, true) # use fast-track -end - -Base.:(*)(c::T, u::Term{V,T}) where {V,T} = u * c - -function Base.:(*)(u::Term{V,T}, v::Term{V,T}) where {V,T} - if u.ω == v.ω - return Term{V,T}(u.ω, u.c * v.c, true) # use fast-track - else - return Term{V,T}(sortedmergewith(u.ω, v.ω; lt = varlt), u.c * v.c, true) # use fast-track - end -end - -Base.:(/)(u::Term{V,T}, c::T) where {V,T} = u * inv(c) - -degree(t::Term) = length(first(t)) diff --git a/src/function/vector/function.jl b/src/function/vector/function.jl deleted file mode 100644 index 57a4cb3..0000000 --- a/src/function/vector/function.jl +++ /dev/null @@ -1,81 +0,0 @@ -@doc raw""" - VectorFunction{V,T} <: AbstractPBF{V,T} -""" -struct VectorFunction{V,T} <: AbstractPBF{V,T} - Ω::Vector{Term{V,T}} - - # standard constructor - function VectorFunction{V,T}(v::AbstractVector{Term{V,T}}, ready::Bool = false) where {V,T} - if ready - Ω = v - else - Ω = sort!(filter(!iszero ∘ last, v); alg=QuickSort, lt=varlt) - end - - return new{V,T}(Ω) - end - - # heterogeneous list - function VectorFunction{V,T}(x::AbstractVector) where {V,T} - return VectorFunction{V,T}(Term{V,T}.(x)) - end - - # dictionary - function VectorFunction{V,T}(x::AbstractDict) where {V,T} - return VectorFunction{V,T}(Term{V,T}.(pairs(x))) - end - - # fallback - function VectorFunction{V,T}(x::Any) where {V,T} - return VectorFunction{V,T}(Term{V,T}[Term{V,T}(x)]) - end -end - -# Broadcast as scalar ? -Base.broadcastable(f::VectorFunction) = f - -# Copy -function Base.sizehint!(f::VectorFunction, n::Integer) - sizehint!(f.Ω, n) - - return f -end - -function Base.copy!(f::VectorFunction{V,T}, g::VectorFunction{V,T}) where {V,T} - sizehint!(f, length(g)) - - copy!(f.Ω, g.Ω) - - return f -end - -function Base.copy(f::VectorFunction{V,T}) where {V,T} - return copy!(VectorFunction{V,T}(), f) -end - -# Iterator & Length -Base.iterate(f::VectorFunction) = iterate(f.Ω) -Base.iterate(f::VectorFunction, i::Integer) = iterate(f.Ω, i) - -# Properties -Base.size(f::VectorFunction{V,T}) where {V,T} = (length(f),) - -function Base.getindex(f::VectorFunction{V,T}, i::Integer) where {V,T} - return getindex(f.Ω, i) -end - -function Base.getindex(f::VectorFunction{V,T}, ::Nothing) - if isempty(f) || !isempty(first(first(f))) - return zero(T) - else - return last(first(f)) - end -end - -function Base.getindex(f::VectorFunction{V,T}, v::Vector{V}) where {V,T} - t = Term{V,T}(v) - - i = findsorted(f.Ω, t) - - return getindex(f.Ω, i) -end diff --git a/src/function/vector/interface.jl b/src/function/vector/interface.jl deleted file mode 100644 index 7473e3b..0000000 --- a/src/function/vector/interface.jl +++ /dev/null @@ -1 +0,0 @@ -degree(f::VectorFunction) = degree(last(f)) diff --git a/src/function/vector/operators.jl b/src/function/vector/operators.jl deleted file mode 100644 index a8d932a..0000000 --- a/src/function/vector/operators.jl +++ /dev/null @@ -1,171 +0,0 @@ -# Arithmetic: (+) -function Base.:(+)(f::VectorFunction{V,T}, g::VectorFunction{V,T}) where {V,T} - return VectorFunction{V,T}(sortedmergewith(+, f, g; lt = varlt), true) -end - -function Base.:(+)(f::VectorFunction{V,T}, c::T) where {V,T} - g = copy(f) - - g[nothing] += c - - return g -end - -Base.:(+)(f::VectorFunction{V,T}, c) where {V,T} = +(f, convert(T, c)) -Base.:(+)(c, f::VectorFunction) = +(f, c) - -# Arithmetic: (-) -function Base.:(-)(f::VectorFunction{V,T}) where {V,T} - return VectorFunction{V,T}(Dict{Set{S},T}(ω => -c for (ω, c) in f)) -end - -function Base.:(-)(f::VectorFunction{V,T}, g::VectorFunction{V,T}) where {V,T} - h = copy(f) - - for (ω, c) in g - h[ω] -= c - end - - return h -end - -function Base.:(-)(f::VectorFunction{V,T}, c::T) where {V,T} - if iszero(c) - copy(f) - else - g = copy(f) - - g[nothing] -= c - - return g - end -end - -function Base.:(-)(c::T, f::VectorFunction{V,T}) where {V,T} - g = -f - - if !iszero(c) - g[nothing] += c - end - - return g -end - -Base.:(-)(c, f::VectorFunction{V,T}) where {V,T} = -(convert(T, c), f) -Base.:(-)(f::VectorFunction{V,T}, c) where {V,T} = -(f, convert(T, c)) - -# Arithmetic: (*) -function Base.:(*)(f::VectorFunction{V,T}, g::VectorFunction{V,T}) where {V,T} - h = zero(VectorFunction{V,T}) - m = length(f) - n = length(g) - - if iszero(f) || iszero(g) # T(n) = O(1) - return h - elseif f === g # T(n) = O(n) + O(n^2 / 2) - k = collect(f) - - sizehint!(h, n^2 ÷ 2) - - for i = 1:n - ωi, ci = k[i] - - h[ωi] += ci * ci - - for j = (i+1):n - ωj, cj = k[j] - - h[union(ωi, ωj)] += 2 * ci * cj - end - end - - return h - else # T(n) = O(m n) - sizehint!(h, m * n) - - for (ωᵢ, cᵢ) in f, (ωⱼ, cⱼ) in g - h[union(ωᵢ, ωⱼ)] += cᵢ * cⱼ - end - - return h - end -end - -function Base.:(*)(f::VectorFunction{V,T}, a::T) where {V,T} - if iszero(a) - return VectorFunction{V,T}() - else - return VectorFunction{V,T}(ω => c * a for (ω, c) ∈ f) - end -end - -Base.:(*)(f::VectorFunction{V,T}, a) where {V,T} = *(f, convert(T, a)) -Base.:(*)(a, f::VectorFunction) = *(f, a) - -# Arithmetic: (/) -function Base.:(/)(f::VectorFunction{V,T}, a::T) where {V,T} - if iszero(a) - throw(DivideError()) - else - return VectorFunction{V,T}(Dict(ω => c / a for (ω, c) in f)) - end -end - -Base.:(/)(f::VectorFunction{V,T}, a) where {V,T} = /(f, convert(T, a)) - -# Arithmetic: (^) -function Base.:(^)(f::VectorFunction{V,T}, n::Integer) where {V,T} - if n < 0 - throw(DivideError()) - elseif n == 0 - return one(VectorFunction{V,T}) - elseif n == 1 - return copy(f) - elseif n == 2 - return f * f - else - g = f * f - - if iseven(n) - return g^(n ÷ 2) - else - return g^(n ÷ 2) * f - end - end -end - -# Arithmetic: Evaluation -function (f::VectorFunction{V,T})(x::Dict{S,U}) where {V,T,U<:Integer} - g = VectorFunction{V,T}() - - for (ω, c) in f - η = Set{S}() - - for j in ω - if haskey(x, j) - if iszero(x[j]) - c = zero(T) - break - end - else - push!(η, j) - end - end - - g[η] += c - end - - return g -end - -function (f::VectorFunction{V,T})(η::Set{S}) where {V,T} - return sum(c for (ω, c) in f if ω ⊆ η; init = zero(T)) -end - -function (f::VectorFunction{S})(x::Pair{S,U}...) where {S,U<:Integer} - return f(Dict{S,U}(x...)) -end - -function (f::VectorFunction{S})() where {S} - return f(Dict{S,Int}()) -end \ No newline at end of file diff --git a/src/interface/function.jl b/src/interface/function.jl index 32200ea..6b327b2 100644 --- a/src/interface/function.jl +++ b/src/interface/function.jl @@ -1,10 +1,3 @@ -@doc raw""" - -""" -abstract type AbstractPseudoBooleanTerm{V,T} end - -const AbstractPBT{V,T} = AbstractPseudoBooleanTerm{V,T} - @doc raw""" AbstractPseudoBooleanFunction{V,T} @@ -26,11 +19,26 @@ abstract type AbstractPseudoBooleanFunction{V,T} end const AbstractPBF{V,T} = AbstractPseudoBooleanFunction{V,T} +@doc raw""" + AbstractForm{V,T} +""" +abstract type AbstractForm{V,T} end + @doc raw""" term """ function term end +@doc raw""" + term_head +""" +function term_head end + +@doc raw""" + term_tail +""" +function term_tail end + @doc raw""" maxgap(f::AbstractPBF{V,T}) where {V,T} diff --git a/src/library.jl b/src/library.jl index be2ef2f..139597f 100644 --- a/src/library.jl +++ b/src/library.jl @@ -1,202 +1,2 @@ -function sortedmergewith( - combine, - u::AbstractVector{T}, - v::AbstractVector{T}; - lt = isless, -) where {T} - m = length(u) - n = length(v) - w = Vector{T}(undef, m + n) - i = 1 - j = 1 - k = 1 - @inbounds while i <= m && j <= n - if lt(u[i], v[j]) - w[k] = u[i] - k += 1 - i += 1 - elseif lt(v[j], u[i]) - w[k] = v[j] - k += 1 - j += 1 - else - w[k] = combine(u[i], v[j]) - k += 1 - i += 1 - j += 1 - end - end - @inbounds while i <= m - w[k] = u[i] - k += 1 - i += 1 - end - - @inbounds while j <= n - w[k] = v[j] - k += 1 - j += 1 - end - - return resize!(w, k - 1) -end - -function sortedmergewith(u::AbstractVector{T}, v::AbstractVector{T}; lt = isless) where {T} - return sortedmergewith(coalesce, u, v; lt) -end - -# Relaxed Greatest Common Divisor -@doc raw""" - relaxedgcd(x::T, y::T; tol::T = T(1e-6)) where {T} - -We define two real numbers ``x`` and ``y`` to be ``\tau``-comensurable if, for some ``\tau > 0`` there exists a continued fractions convergent ``p_{k} \div q_{k}`` such that - -```math - \left| {q_{k} x - p_{k} y} \right| \le \tau -``` -""" -function relaxedgcd(x::T, y::T; tol::T = 1e-6) where {T} - x_ = abs(x) - y_ = abs(y) - - if x_ < y_ - return relaxedgcd(y_, x_; tol)::T - elseif y_ < tol - return x_ - elseif x_ < tol - return y_ - else - return (x_ / numerator(rationalize(x_ / y_; tol)))::T - end -end - -function relaxedgcd(a::AbstractArray{T}; tol::T = 1e-6) where {T} - if length(a) == 0 - return one(T) - elseif length(a) == 1 - return first(a) - else - return reduce((x, y) -> relaxedgcd(x, y; tol), a) - end -end - -function _subscript(i::Integer) - if i < 0 - return "₋$(_subscript(abs(i)))" - else - return join(reverse(digits(i)) .+ Char(0x2080)) - end -end - -function _colwise_shuffle!(rng, x::AbstractMatrix{U}) where {U<:Integer} - n = size(x, 2) - - @inbounds for j = 1:n - shuffle!(rng, @view(x[:, j])) - end - - return x -end - -function _rowwise_allunique( - x::AbstractMatrix{U}, - u::Vector{S}, -) where {U<:Integer,S<:Integer} - m, n = size(x) - - for i = 1:m - u .= zero(S) - - for j = 1:n - k = x[i, j] - - if isone(u[k]) - return false - else - u[k] = one(S) - end - end - end - - return true -end - -function _swaprows!(x::AbstractMatrix, i::Integer, j::Integer) - n = size(x, 2) - - @inbounds for k = 1:n - x[i, k], x[j, k] = (x[j, k], x[i, k]) - end - - return nothing -end - -function _swaprows!(x::AbstractVector, i::Integer, j::Integer) - x[i], x[j] = (x[j], x[i]) - - return nothing -end - -function _mod2_numsolutions!(A::AbstractMatrix{U}, b::AbstractVector{U}) where {U<:Integer} - _mod2_elimination!(A, b) - - m, n = size(A) - - # start with full rank - rank = m - - @inbounds for i = 1:m - if iszero(@view(A[i, :])) # all-zero row encountered - if !iszero(b[i]) # no solutions - return 0 - end - - rank -= 1 - end - end - - return 2^(n - rank) -end - -function _mod2_elimination!(A::AbstractMatrix{U}, b::AbstractVector{U}) where {U<:Integer} - m, n = size(A) - - i = 1 - j = 1 - - @inbounds while i <= m && j <= n - pivot = i - - for l = i:m - if A[l, j] == 1 - pivot = l - break - end - end - - if A[pivot, j] == 0 - j += 1 - continue - end - - if i != pivot - _swaprows!(A, i, pivot) - _swaprows!(b, i, pivot) - end - - for k = (i+1):m - for l = 1:n - A[k, l] ⊻= A[k, j] & A[i, l] - end - - b[k] ⊻= A[k, j] & b[i] - end - - i += 1 - j += 1 - end - - return nothing -end diff --git a/src/library/function/abstract.jl b/src/library/function/abstract.jl index 5157efd..94bcffc 100644 --- a/src/library/function/abstract.jl +++ b/src/library/function/abstract.jl @@ -2,12 +2,34 @@ function bounds(f::AbstractPBF) return (lowerbound(f), upperbound(f)) end -function discretize(f::AbstractPBF{V,T}; tol::T = 1E-6) where {V,T} - return discretize!(copy(f); tol) +function degree(f::AbstractPBF) + return maximum(length.(keys(f)); init = 0) end -function gradient(f::AbstractPBF{V,T}, x::Vector{V}) where {V,T} - return [derivative(f, xi) for xi in x] +function residual(f::F, x::V) where {V,T,F<:AbstractPBF{V,T}} + return F(ω => c for (ω, c) ∈ f if (x ∉ ω)) +end + +function discretize!(f::AbstractPBF{_,T}; tol::T = T(1E-6)) where {_,T} + ε = mingap(f; tol) + + for (ω, c) in f + f[ω] = round(c / ε; digits = 0) + end + + return f +end + +function discretize(f::AbstractPBF{V,T}; atol::T = 1E-6) where {V,T} + return discretize!(copy(f); atol) +end + +function derivative(f::F, x::V) where {V,T,F<:AbstractPBF{V,T}} + return F(ω => f[ω×x] for ω ∈ keys(f) if (x ∉ ω)) +end + +function gradient(f::F, x::Vector{V}) where {V,T,F<:AbstractPBF{V,T}} + return F[derivative(f, xi) for xi in x] end function maxgap(f::F) where {V,T,F<:AbstractPBF{V,T}} diff --git a/src/library/function/function.jl b/src/library/function/function.jl index ac0d429..b99e996 100644 --- a/src/library/function/function.jl +++ b/src/library/function/function.jl @@ -1,7 +1,53 @@ @doc raw""" - PseudoBooleanFunction{V,T,X} + PseudoBooleanFunction{V,T,S} + +This is a concrete implementation of [`AbstractPBF`](@ref) that uses the `S` data structure to store the terms of the function. """ -struct PseudoBooleanFunction{V,T,S} <: AbstractPseudoBooleanFunction{V,T} - Ω::S +struct PseudoBooleanFunction{V,T,S} <: AbstractPBF{V,T} + Φ::S + + # Constructor: I know what I am doing + function PseudoBooleanFunction{V,T,S}(Φ::S) where {V,T,S} + return new{V,T,S}(Φ) + end +end + +const PBF{V,T,S} = PseudoBooleanFunction{V,T,S} + +# Type promotion +function Base.promote_rule(::Type{PBF{V,Tf,S}}, ::Type{PBF{V,Tg,S}}) where {V,Tf,Tg,S} + T = promote_type(Tf, Tg) + + return PBF{V,T,S} +end + +# Arithmetic: '+', '-', '*', '/' +function Base.:(+)(f::F, g::G) where {V,Tf,Tg,F<:AbstractPBF{V,Tf},G<:AbstractPBF{V,Tg}} + Ω = union(keys(f), keys(g)) + h = sizehint!(zero(F), length(Ω)) + + for ω in Ω + h[ω] = f[ω] + g[ω] + end + + return h +end + +function Base.:(-)(f::Ff, g::Fg) where {V,Tf,Tg,Ff<:AbstractPBF{V,Tf},Fg<:AbstractPBF{V,Tg}} + F = promote_type(Ff, Fg) + Ω = union(keys(f), keys(g)) + h = sizehint!(zero(F), length(Ω)) + + for ω in Ω + h[ω] = f[ω] - g[ω] + end + + return h end +# Comparsion: '==', '≈' +Base.:(==)(f::DictFunction{V,T}, g::DictFunction{V,T}) where {V,T} = (f.Ω == g.Ω) + +function Base.isapprox(f::AbstractPBF{V,Tf}, g::AbstractPBF{V,Tg}; kw...) where {V,Tf,Tg} + return length(f) == length(g) && all(ω -> isapprox(g[ω], f[ω]; kw...), keys(f)) +end diff --git a/src/library/function/setdict.jl b/src/library/function/setdict.jl deleted file mode 100644 index e450488..0000000 --- a/src/library/function/setdict.jl +++ /dev/null @@ -1,109 +0,0 @@ -const SetDict{V,T} = Dict{Set{V},T} - -const SetDictKey{V} = Union{AbstractSet{V},AbstractVector{V},Nothing} - -function PBF{V,T,S}() where {V,T,S<:SetDict{V,T}} - return PBF{V,T,S}(SetDict{V,T}()) -end - -function PBF{V,T}(Ω::Dict{SetDictKey{V},T}) where {V,T} - return new{V,T,SetDict{V,T}}( - SetDict{V,T}((isnothing(ω) ? Set{V}() : ω) => c for (ω, c) in Ω if !iszero(c)) - ) -end - -struct DictFunction{V,T} <: AbstractPBF{V,T} - Ω::Dict{Set{V},T} - - - - function DictFunction{V,T}(v::AbstractVector) where {V,T} - Ω = Dict{Set{V},T}() - - for x in v - t = Term{V,T}(x) - ω = Set{V}(first(t)) - a = last(t) - - Ω[ω] = get(Ω, ω, zero(T)) + a - end - - return DictFunction{V,T}(Ω) - end - - function DictFunction{V,T}(x::Base.Generator) where {V,T} - return DictFunction{V,T}(collect(x)) - end - - function DictFunction{V,T}(x::Vararg{Any}) where {V,T} - return DictFunction{V,T}(collect(x)) - end - - function DictFunction{V,T}() where {V,T} - return new{V,T}(Dict{Set{V},T}()) - end -end - -# Broadcast as scalar -Base.broadcastable(f::DictFunction) = Ref(f) - -# Copy -function Base.copy!(f::DictFunction{V,T}, g::DictFunction{V,T}) where {V,T} - sizehint!(f, length(g)) - copy!(f.Ω, g.Ω) - - return f -end - -function Base.copy(f::DictFunction{V,T}) where {V,T} - return copy!(DictFunction{V,T}(), f) -end - -# Iterator & Length -Base.keys(f::DictFunction) = keys(f.Ω) -Base.values(f::DictFunction) = values(f.Ω) -Base.length(f::DictFunction) = length(f.Ω) -Base.empty!(f::DictFunction) = empty!(f.Ω) -Base.isempty(f::DictFunction) = isempty(f.Ω) -Base.iterate(f::DictFunction) = iterate(f.Ω) -Base.iterate(f::DictFunction, i::Integer) = iterate(f.Ω, i) - -Base.haskey(f::DictFunction{V}, ω::Set{V}) where {V} = haskey(f.Ω, ω) -Base.haskey(f::DictFunction{V}, ξ::V) where {V} = haskey(f, Set{V}([ξ])) -Base.haskey(f::DictFunction{V}, ::Nothing) where {V} = haskey(f, Set{V}()) - -# Indexing: Get # -Base.getindex(f::DictFunction{V,T}, ω::Set{V}) where {V,T} = get(f.Ω, ω, zero(T)) -Base.getindex(f::DictFunction{V}, η::Vector{V}) where {V} = getindex(f, Set{V}(η)) -Base.getindex(f::DictFunction{V}, ξ::V) where {V} = getindex(f, Set{V}([ξ])) -Base.getindex(f::DictFunction{V}, ::Nothing) where {V} = getindex(f, Set{V}()) - -# Indexing: Set # -function Base.setindex!(f::DictFunction{V,T}, c::T, ω::Set{V}) where {V,T} - if !iszero(c) - setindex!(f.Ω, c, ω) - elseif haskey(f, ω) - delete!(f, ω) - end - - return c -end - -Base.setindex!(f::DictFunction{V,T}, c::T, η::Vector{V}) where {V,T} = setindex!(f, c, Set{V}(η)) -Base.setindex!(f::DictFunction{V,T}, c::T, ξ::V) where {V,T} = setindex!(f, c, Set{V}([ξ])) -Base.setindex!(f::DictFunction{V,T}, c::T, ::Nothing) where {V,T} = setindex!(f, c, Set{V}()) - -# Indexing: Delete # -Base.delete!(f::DictFunction{V}, ω::Set{V}) where {V} = delete!(f.Ω, ω) -Base.delete!(f::DictFunction{V}, η::Vector{V}) where {V} = delete!(f, Set{V}(η)) -Base.delete!(f::DictFunction{V}, k::V) where {V} = delete!(f, Set{V}([k])) -Base.delete!(f::DictFunction{V}, ::Nothing) where {V} = delete!(f, Set{V}()) - -# Properties -Base.size(f::DictFunction{V,T}) where {V,T} = (length(f),) - -function Base.sizehint!(f::DictFunction, n::Integer) - sizehint!(f.Ω, n) - - return f -end \ No newline at end of file diff --git a/src/function/dict/operators.jl b/src/library/function/setdict/operators.jl similarity index 82% rename from src/function/dict/operators.jl rename to src/library/function/setdict/operators.jl index e81bcf1..0b61f09 100644 --- a/src/function/dict/operators.jl +++ b/src/library/function/setdict/operators.jl @@ -1,20 +1,3 @@ -# Comparison: (==) -Base.:(==)(f::DictFunction{V,T}, g::DictFunction{V,T}) where {V,T} = (f.Ω == g.Ω) - -function Base.isapprox(f::DictFunction{V,T}, g::DictFunction{V,T}; kw...) where {V,T} - return (length(f) == length(g)) && all(haskey(g, ω) && isapprox(g[ω], f[ω]; kw...) for ω in keys(f)) -end - -function isscalar(f::DictFunction) - return isempty(f) || (length(f) == 1 && haskey(f, nothing)) -end - -Base.zero(::Type{DictFunction{V,T}}) where {V,T} = DictFunction{V,T}() -Base.iszero(f::DictFunction) = isempty(f) -Base.one(::Type{DictFunction{V,T}}) where {V,T} = DictFunction{V,T}(one(T)) -Base.isone(f::DictFunction) = isscalar(f) && isone(f[nothing]) -Base.round(f::DictFunction{V,T}; kw...) where {V,T} = DictFunction{V,T}(ω => round(c; kw...) for (ω, c) in f) - # Arithmetic: (+) function Base.:(+)(f::DictFunction{V,T}, g::DictFunction{V,T}) where {V,T} h = copy(f) diff --git a/src/library/function/setdict/setdict.jl b/src/library/function/setdict/setdict.jl new file mode 100644 index 0000000..1a1c7d5 --- /dev/null +++ b/src/library/function/setdict/setdict.jl @@ -0,0 +1,127 @@ +const SetDict{V,T} = Dict{Set{V},T} + +# Constructors +function PBF(Φ::S) where {V,T,S<:SetDict{V,T}} + return PBF{V,T,S}(SetDict{V,T}(ω => c for (ω, c) in Φ if !iszero(c))) +end + +function Base.zero(::Type{PBF{V,T,S}}) where {V,T,S<:SetDict{V,T}} + return PBF{V,T,S}(SetDict{V,T}()) +end + +function Base.one(::Type{PBF{V,T,S}}) where {V,T,S<:SetDict{V,T}} + return PBF{V,T,S}(SetDict{V,T}(Set{V}() => one(T))) +end + +# Type promotion +function Base.promote_rule(::Type{PBF{V,Tf,SetDict{V,Tf}}}, ::Type{PBF{V,Tg,S}}) where {V,Tf,Tg,S} + T = promote_type(Tf, Tg) + + return PBF{V,T,SetDict{V,T}} +end + +# Term parser +function term(Φ::Type{SetDict{V,T}}, ω, c) where {V,T} + c_ = term_tail(Φ, c) + + if iszero(c_) + return (Set{V}() => zero(T)) + else + ω_ = term_head(Φ, ω) + + return (ω_ => c_) + end +end + +term_head(::Type{PBF{V,_,SetDict{V,_}}}, ω::Set{V}) where {V,_} = ω +term_head(::Type{PBF{V,_,SetDict{V,_}}}, ::Nothing) where {V,_} = Set{V}() +term_head(::Type{PBF{V,_,SetDict{V,_}}}, ω::V) where {V,_} = Set{V}([ω]) +term_head(::Type{PBF{V,_,SetDict{V,_}}}, ω::AbstractSet{V}) where {V,_} = Set{V}(ω) +term_head(::Type{PBF{V,_,SetDict{V,_}}}, ω::AbstractVector{V}) where {V,_} = Set{V}(ω) +term_head(::Type{PBF{V,_,SetDict{V,_}}}, ω::NTuple{N,V}) where {N,V,_} = Set{V}(ω) + +term_tail(::Type{PBF{_,T,SetDict{_,T}}}, c::T) where {_,T} = c +term_tail(::Type{PBF{_,T,SetDict{_,T}}}, c) where {_,T} = convert(T, c) + +# Copy +function Base.copy!(f::PBF{V,T,SetDict{V,T}}, g::PBF{V,T,SetDict{V,T}}) where {V,T} + sizehint!(f, length(g)) + + copy!(f.Φ, g.Φ) + + return f +end + +function Base.copy(f::F) where {V,T,F<:PBF{V,T,SetDict{V,T}}} + return copy!(zero(F), f) +end + +function Base.sizehint!(f::PBF{V,T,SetDict{V,T}}, n::Integer) where {V,T} + sizehint!(f.Φ, n) + + return f +end + +# Iterator & Length +Base.length(f::PBF{V,T,SetDict{V,T}}) where {V,T} = length(f.Φ) +Base.iterate(f::PBF{V,T,SetDict{V,T}}) where {V,T} = iterate(f.Φ) +Base.iterate(f::PBF{V,T,SetDict{V,T}}, i::Integer) where {V,T} = iterate(f.Φ, i) + +# Emptiness +Base.empty!(f::PBF{V,T,SetDict{V,T}}) where {V,T} = empty!(f.Φ) +Base.isempty(f::PBF{V,T,SetDict{V,T}}) where {V,T} = isempty(f.Φ) + +# Dictionary interface +Base.keys(f::PBF{V,T,SetDict{V,T}}) where {V,T} = keys(f.Φ) +Base.values(f::PBF{V,T,SetDict{V,T}}) where {V,T} = values(f.Φ) + +# Base.map!(φ::Function, f::PBF{V,T,SetDict{V,T}}) where {V,T} = map!(φ, values(f)) + +# Broadcast as scalar +Base.broadcastable(f::PBF{V,T,S}) where {V,T,S} = Ref(f) + +# Indexing - in +function Base.haskey(f::F, ω) where {V,_,F<:PBF{V,_,SetDict{V,_}}} + return haskey(f.Φ, term_head(F, ω)) +end + +# Indexing - get +function Base.getindex(f::F, ω) where {V,T,F<:PBF{V,T,SetDict{V,T}}} + return get(f.Φ, term_head(F, ω), zero(T)) +end + +# Indexing - set +function Base.setindex!(f::F, c, ω) where {V,T,F<:PBF{V,T,SetDict{V,T}}} + ω_ = term_head(F, ω) + c_ = term_tail(F, c) + + if !iszero(c_) + setindex!(f.Φ, c_, ω_) + elseif haskey(f, ω_) + delete!(f, ω_) + end + + return c_ +end + +function Base.delete!(f::F, ω) where {V,T,F<:PBF{V,T,SetDict{V,T}}} + delete!(f.Φ, term_head(F, ω)) + + return f +end + +##### + + + + +function isscalar(f::DictFunction) + return isempty(f) || (length(f) == 1 && haskey(f, nothing)) +end + +Base.zero(::Type{DictFunction{V,T}}) where {V,T} = DictFunction{V,T}() +Base.iszero(f::DictFunction) = isempty(f) +Base.one(::Type{DictFunction{V,T}}) where {V,T} = DictFunction{V,T}(one(T)) +Base.isone(f::DictFunction) = isscalar(f) && isone(f[nothing]) +Base.round(f::DictFunction{V,T}; kw...) where {V,T} = DictFunction{V,T}(ω => round(c; kw...) for (ω, c) in f) + diff --git a/src/library/function/vector/tuplevector.jl b/src/library/function/vector/tuplevector.jl new file mode 100644 index 0000000..dc3cc94 --- /dev/null +++ b/src/library/function/vector/tuplevector.jl @@ -0,0 +1,386 @@ +@doc raw""" + VectorFunction{V,T} <: AbstractPBF{V,T} +""" +struct VectorFunction{V,T} <: AbstractPBF{V,T} + Ω::Vector{Term{V,T}} + + # standard constructor + function VectorFunction{V,T}(v::AbstractVector{Term{V,T}}, ready::Bool = false) where {V,T} + if ready + Ω = v + else + Ω = sort!(filter(!iszero ∘ last, v); alg=QuickSort, lt=varlt) + end + + return new{V,T}(Ω) + end + + # heterogeneous list + function VectorFunction{V,T}(x::AbstractVector) where {V,T} + return VectorFunction{V,T}(Term{V,T}.(x)) + end + + # dictionary + function VectorFunction{V,T}(x::AbstractDict) where {V,T} + return VectorFunction{V,T}(Term{V,T}.(pairs(x))) + end + + # fallback + function VectorFunction{V,T}(x::Any) where {V,T} + return VectorFunction{V,T}(Term{V,T}[Term{V,T}(x)]) + end +end + +# Broadcast as scalar ? +Base.broadcastable(f::VectorFunction) = f + +# Copy +function Base.sizehint!(f::VectorFunction, n::Integer) + sizehint!(f.Ω, n) + + return f +end + +function Base.copy!(f::VectorFunction{V,T}, g::VectorFunction{V,T}) where {V,T} + sizehint!(f, length(g)) + + copy!(f.Ω, g.Ω) + + return f +end + +function Base.copy(f::VectorFunction{V,T}) where {V,T} + return copy!(VectorFunction{V,T}(), f) +end + +# Iterator & Length +Base.iterate(f::VectorFunction) = iterate(f.Ω) +Base.iterate(f::VectorFunction, i::Integer) = iterate(f.Ω, i) + +# Properties +Base.size(f::VectorFunction{V,T}) where {V,T} = (length(f),) + +function Base.getindex(f::VectorFunction{V,T}, i::Integer) where {V,T} + return getindex(f.Ω, i) +end + +function Base.getindex(f::VectorFunction{V,T}, ::Nothing) + if isempty(f) || !isempty(first(first(f))) + return zero(T) + else + return last(first(f)) + end +end + +function Base.getindex(f::VectorFunction{V,T}, v::Vector{V}) where {V,T} + t = Term{V,T}(v) + + i = findsorted(f.Ω, t) + + return getindex(f.Ω, i) +end + + +const TermKey{V} = Union{AbstractVector{V},AbstractSet{V},Tuple{Vararg{V}}} + +@doc raw""" + Term{V,T} <: AbstractTerm{V,T} +""" +struct Term{V,T} <: AbstractTerm{V,T} + ω::Vector{V} + c::T + + # standard constructor + function Term{V,T}(x::K, c::T) where {V,T,K<:TermKey{V}} + ω = unique!(sort!(collect(x); alg = InsertionSort, lt = varlt)) + + return new{V,T}(ω, c) + end + + # shortcut: fast-track + function Term{V,T}(x::K, c::T, ::Nothing) where {V,T,K<:TermKey{V}} + return new{V,T}(x, c) + end + + # shortcut: zero + Term{V,T}() where {V,T} = new{V,T}(V[], zero(T)) + + # shortcut: one + Term{V,T}(::Nothing) where {V,T} = new{V,T}(V[], one(T)) + + # shortcut: constant + Term{V,T}(c::T) where {V,T} = new{V,T}(V[], c) + + # shortcut: variable + Term{V,T}(v::V) where {V,T} = new{V,T}(V[v], one(T)) + + # shortcut: monomial + Term{V,T}(v::V, c::T) where {V,T} = new{V,T}(V[v], c) + + # shortcut: fallback error + Term{V,T}(x::Any) where {V,T} = error("Invalid term '$(x)'") +end + +function Term{V,T}(ω::K) where {V,T,K<:TermKey{V}} + return Term{V,T}(ω, one(T)) +end + +function Term{V,T}((_, c)::Pair{Nothing,T}) where {V,T} + return Term{V,T}(c) +end + +function Term{V,T}((_, c)::Tuple{Nothing,T}) where {V,T} + return Term{V,T}(c) +end + +function Term{V,T}((v, c)::Pair{V,T}) where {V,T} + return Term{V,T}(v, c) +end + +function Term{V,T}( + (ω, c)::Pair{K,T}, +) where {V,T,K<:Union{AbstractVector{V},AbstractSet{V},Tuple{Vararg{V}}}} + return Term{V,T}(ω, c) +end + +function varlt(u::Term{V,T}, v::Term{V,T}) where {V,T} + if length(u.ω) == length(v.ω) + return varlt(u.ω, v.ω) + else + return isless(length(u.ω), length(v.ω)) + end +end + +function varmul(u::Term{V,T}, v::Term{V,T}) where {V,T} + return sortedmergewith(u, v; lt = varlt) +end + +function Base.length(::Term) + return 2 +end + +function Base.iterate(u::Term) + return (first(u), 2) +end + +function Base.iterate(u::Term, i::Integer) + if i == 1 + return (first(u), 2) + elseif i == 2 + return (last(u), 3) + else + return nothing + end +end + +function Base.first(u::Term) + return u.ω +end + +function Base.last(u::Term) + return u.c +end + +function Base.:(+)(u::Term{V,T}, v::Term{V,T}) where {V,T} + @assert u.ω == v.ω + + return Term{V,T}(u.ω, u.c + v.c, true) # use fast-track +end + +function Base.:(-)(u::Term{V,T}, v::Term{V,T}) where {V,T} + @assert u.ω == v.ω + + return Term{V,T}(u.ω, u.c - v.c, true) # use fast-track +end + +function Base.:(*)(u::Term{V,T}, c::T) where {V,T} + return Term{V,T}(u.ω, u.c * c, true) # use fast-track +end + +Base.:(*)(c::T, u::Term{V,T}) where {V,T} = u * c + +function Base.:(*)(u::Term{V,T}, v::Term{V,T}) where {V,T} + if u.ω == v.ω + return Term{V,T}(u.ω, u.c * v.c, true) # use fast-track + else + return Term{V,T}(sortedmergewith(u.ω, v.ω; lt = varlt), u.c * v.c, true) # use fast-track + end +end + +Base.:(/)(u::Term{V,T}, c::T) where {V,T} = u * inv(c) + +degree(t::Term) = length(first(t)) + +# Arithmetic: (+) +function Base.:(+)(f::VectorFunction{V,T}, g::VectorFunction{V,T}) where {V,T} + return VectorFunction{V,T}(sortedmergewith(+, f, g; lt = varlt), true) +end + +function Base.:(+)(f::VectorFunction{V,T}, c::T) where {V,T} + g = copy(f) + + g[nothing] += c + + return g +end + +Base.:(+)(f::VectorFunction{V,T}, c) where {V,T} = +(f, convert(T, c)) +Base.:(+)(c, f::VectorFunction) = +(f, c) + +# Arithmetic: (-) +function Base.:(-)(f::VectorFunction{V,T}) where {V,T} + return VectorFunction{V,T}(Dict{Set{S},T}(ω => -c for (ω, c) in f)) +end + +function Base.:(-)(f::VectorFunction{V,T}, g::VectorFunction{V,T}) where {V,T} + h = copy(f) + + for (ω, c) in g + h[ω] -= c + end + + return h +end + +function Base.:(-)(f::VectorFunction{V,T}, c::T) where {V,T} + if iszero(c) + copy(f) + else + g = copy(f) + + g[nothing] -= c + + return g + end +end + +function Base.:(-)(c::T, f::VectorFunction{V,T}) where {V,T} + g = -f + + if !iszero(c) + g[nothing] += c + end + + return g +end + +Base.:(-)(c, f::VectorFunction{V,T}) where {V,T} = -(convert(T, c), f) +Base.:(-)(f::VectorFunction{V,T}, c) where {V,T} = -(f, convert(T, c)) + +# Arithmetic: (*) +function Base.:(*)(f::VectorFunction{V,T}, g::VectorFunction{V,T}) where {V,T} + h = zero(VectorFunction{V,T}) + m = length(f) + n = length(g) + + if iszero(f) || iszero(g) # T(n) = O(1) + return h + elseif f === g # T(n) = O(n) + O(n^2 / 2) + k = collect(f) + + sizehint!(h, n^2 ÷ 2) + + for i = 1:n + ωi, ci = k[i] + + h[ωi] += ci * ci + + for j = (i+1):n + ωj, cj = k[j] + + h[union(ωi, ωj)] += 2 * ci * cj + end + end + + return h + else # T(n) = O(m n) + sizehint!(h, m * n) + + for (ωᵢ, cᵢ) in f, (ωⱼ, cⱼ) in g + h[union(ωᵢ, ωⱼ)] += cᵢ * cⱼ + end + + return h + end +end + +function Base.:(*)(f::VectorFunction{V,T}, a::T) where {V,T} + if iszero(a) + return VectorFunction{V,T}() + else + return VectorFunction{V,T}(ω => c * a for (ω, c) ∈ f) + end +end + +Base.:(*)(f::VectorFunction{V,T}, a) where {V,T} = *(f, convert(T, a)) +Base.:(*)(a, f::VectorFunction) = *(f, a) + +# Arithmetic: (/) +function Base.:(/)(f::VectorFunction{V,T}, a::T) where {V,T} + if iszero(a) + throw(DivideError()) + else + return VectorFunction{V,T}(Dict(ω => c / a for (ω, c) in f)) + end +end + +Base.:(/)(f::VectorFunction{V,T}, a) where {V,T} = /(f, convert(T, a)) + +# Arithmetic: (^) +function Base.:(^)(f::VectorFunction{V,T}, n::Integer) where {V,T} + if n < 0 + throw(DivideError()) + elseif n == 0 + return one(VectorFunction{V,T}) + elseif n == 1 + return copy(f) + elseif n == 2 + return f * f + else + g = f * f + + if iseven(n) + return g^(n ÷ 2) + else + return g^(n ÷ 2) * f + end + end +end + +# Arithmetic: Evaluation +function (f::VectorFunction{V,T})(x::Dict{S,U}) where {V,T,U<:Integer} + g = VectorFunction{V,T}() + + for (ω, c) in f + η = Set{S}() + + for j in ω + if haskey(x, j) + if iszero(x[j]) + c = zero(T) + break + end + else + push!(η, j) + end + end + + g[η] += c + end + + return g +end + +function (f::VectorFunction{V,T})(η::Set{S}) where {V,T} + return sum(c for (ω, c) in f if ω ⊆ η; init = zero(T)) +end + +function (f::VectorFunction{S})(x::Pair{S,U}...) where {S,U<:Integer} + return f(Dict{S,U}(x...)) +end + +function (f::VectorFunction{S})() where {S} + return f(Dict{S,Int}()) +end + +degree(f::VectorFunction) = degree(last(f)) diff --git a/src/library/function/vectordict.jl b/src/library/function/vector/vectordict.jl similarity index 100% rename from src/library/function/vectordict.jl rename to src/library/function/vector/vectordict.jl diff --git a/src/library/mod2linsolve.jl b/src/library/mod2linsolve.jl new file mode 100644 index 0000000..d2e2c5e --- /dev/null +++ b/src/library/mod2linsolve.jl @@ -0,0 +1,110 @@ +function _colwise_shuffle!(rng, x::AbstractMatrix{U}) where {U<:Integer} + n = size(x, 2) + + @inbounds for j = 1:n + shuffle!(rng, @view(x[:, j])) + end + + return x +end + +function _rowwise_allunique( + x::AbstractMatrix{U}, + u::Vector{S}, +) where {U<:Integer,S<:Integer} + m, n = size(x) + + for i = 1:m + u .= zero(S) + + for j = 1:n + k = x[i, j] + + if isone(u[k]) + return false + else + u[k] = one(S) + end + end + end + + return true +end + +function _swaprows!(x::AbstractMatrix, i::Integer, j::Integer) + n = size(x, 2) + + @inbounds for k = 1:n + x[i, k], x[j, k] = (x[j, k], x[i, k]) + end + + return nothing +end + +function _swaprows!(x::AbstractVector, i::Integer, j::Integer) + x[i], x[j] = (x[j], x[i]) + + return nothing +end + +function _mod2_numsolutions!(A::AbstractMatrix{U}, b::AbstractVector{U}) where {U<:Integer} + _mod2_elimination!(A, b) + + m, n = size(A) + + # start with full rank + rank = m + + @inbounds for i = 1:m + if iszero(@view(A[i, :])) # all-zero row encountered + if !iszero(b[i]) # no solutions + return 0 + end + + rank -= 1 + end + end + + return 2^(n - rank) +end + +function _mod2_elimination!(A::AbstractMatrix{U}, b::AbstractVector{U}) where {U<:Integer} + m, n = size(A) + + i = 1 + j = 1 + + @inbounds while i <= m && j <= n + pivot = i + + for l = i:m + if A[l, j] == 1 + pivot = l + break + end + end + + if A[pivot, j] == 0 + j += 1 + continue + end + + if i != pivot + _swaprows!(A, i, pivot) + _swaprows!(b, i, pivot) + end + + for k = (i+1):m + for l = 1:n + A[k, l] ⊻= A[k, j] & A[i, l] + end + + b[k] ⊻= A[k, j] & b[i] + end + + i += 1 + j += 1 + end + + return nothing +end diff --git a/src/library/print.jl b/src/library/print.jl index 713a88e..07e6a5d 100644 --- a/src/library/print.jl +++ b/src/library/print.jl @@ -1,3 +1,11 @@ +function _subscript(i::Integer) + if i < 0 + return "₋$(_subscript(abs(i)))" + else + return join(reverse(digits(i)) .+ Char(0x2080)) + end +end + function Base.show(io::IO, (ω, c)::Term{V,T}) where {V,T} if isempty(ω) print(io, c) @@ -8,8 +16,8 @@ function Base.show(io::IO, (ω, c)::Term{V,T}) where {V,T} end end -function Base.show(io::IO, func::DictFunction{V,T}) where {V,T} - terms = Term{V,T}.(sort!(collect(func); by=first, lt=varlt)) +function Base.show(io::IO, func::AbstractPBF{V,T}) where {V,T} + terms = sort!(map((ω, c) -> (sort(collect(ω)) => c), func); by=first, lt=varlt) if isempty(terms) print(io, zero(T)) @@ -18,6 +26,8 @@ function Base.show(io::IO, func::DictFunction{V,T}) where {V,T} end end + + # function Base.show(io::IO, func::VectorFunction{V,T}) where {V,T} # join(io, func.Ω, " + ") # end diff --git a/src/library/relaxedgcd.jl b/src/library/relaxedgcd.jl new file mode 100644 index 0000000..8f7a8bf --- /dev/null +++ b/src/library/relaxedgcd.jl @@ -0,0 +1,38 @@ +# Relaxed Greatest Common Divisor +@doc raw""" + relaxedgcd(x::T, y::T; tol::T = T(1E-6)) where {T} + +We define two real numbers ``x`` and ``y`` to be ``\tau``-comensurable if, +for some ``\tau > 0`` there exists a continued fractions convergent +``p_{k} \div q_{k}`` such that + +```math + \left| {q_{k} x - p_{k} y} \right| \le \tau +``` +""" +function relaxedgcd end + +function relaxedgcd(x::T, y::T; tol::T = T(1E-6)) where {T} + x_ = abs(x) + y_ = abs(y) + + if x_ < y_ + return relaxedgcd(y_, x_; tol)::T + elseif y_ < tol + return x_ + elseif x_ < tol + return y_ + else + return (x_ / numerator(rationalize(x_ / y_; tol)))::T + end +end + +function relaxedgcd(a::AbstractArray{T}; tol::T = 1e-6) where {T} + if length(a) == 0 + return one(T) + elseif length(a) == 1 + return first(a) + else + return reduce((x, y) -> relaxedgcd(x, y; tol), a) + end +end diff --git a/src/library/sortedmerge.jl b/src/library/sortedmerge.jl new file mode 100644 index 0000000..aec391c --- /dev/null +++ b/src/library/sortedmerge.jl @@ -0,0 +1,48 @@ +function sortedmergewith( + combine, + u::AbstractVector{T}, + v::AbstractVector{T}; + lt = isless, +) where {T} + m = length(u) + n = length(v) + w = Vector{T}(undef, m + n) + i = 1 + j = 1 + k = 1 + + @inbounds while i <= m && j <= n + if lt(u[i], v[j]) + w[k] = u[i] + k += 1 + i += 1 + elseif lt(v[j], u[i]) + w[k] = v[j] + k += 1 + j += 1 + else + w[k] = combine(u[i], v[j]) + k += 1 + i += 1 + j += 1 + end + end + + @inbounds while i <= m + w[k] = u[i] + k += 1 + i += 1 + end + + @inbounds while j <= n + w[k] = v[j] + k += 1 + j += 1 + end + + return resize!(w, k - 1) +end + +function sortedmergewith(u::AbstractVector{T}, v::AbstractVector{T}; lt = isless) where {T} + return sortedmergewith(coalesce, u, v; lt) +end diff --git a/src/library/variable.jl b/src/library/variable/abstract.jl similarity index 96% rename from src/library/variable.jl rename to src/library/variable/abstract.jl index 58aa81c..902790e 100644 --- a/src/library/variable.jl +++ b/src/library/variable/abstract.jl @@ -74,4 +74,4 @@ end varshow(io::IO, v::V) where {V} = show(io, varshow(v)) varshow(v::Integer) = "x$(_subscript(v))" -varshow(v::V) where {V<:Union{Symbol,AbstractString}} = v +varshow(v::V) where {V<:Union{Symbol,AbstractString}} = string(v) diff --git a/test/runtests.jl b/test/runtests.jl index 69a52e9..5ad6a95 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -10,6 +10,8 @@ include("unit/unit.jl") function main() test_unit() + + return nothing end main() # Here we go! diff --git a/test/unit/unit.jl b/test/unit/unit.jl index 082b112..cce6064 100644 --- a/test/unit/unit.jl +++ b/test/unit/unit.jl @@ -7,12 +7,14 @@ include("print.jl") function test_unit() @testset "Pseudo-Boolean Functions" verbose = true begin - test_constructors() - test_operators() - test_evaluation() - test_calculus() - test_discretization() - test_quadratization() - test_print() + # test_constructors() + # test_operators() + # test_evaluation() + # test_calculus() + # test_discretization() + # test_quadratization() + # test_print() end + + return nothing end From d7b1f980b0dba3a97d869fbca872fd5e650bcab4 Mon Sep 17 00:00:00 2001 From: pedromxavier Date: Sat, 30 Sep 2023 18:49:20 -0400 Subject: [PATCH 3/9] Update quadratization interface --- src/PseudoBooleanOptimization.jl | 19 +- src/interface/function.jl | 4 +- src/interface/quadratization.jl | 15 -- src/library.jl | 2 - src/library/function/abstract.jl | 6 +- src/library/function/function.jl | 31 ---- src/library/function/operators.jl | 163 ++++++++++++++++++ src/library/function/setdict/setdict.jl | 17 +- .../vector/{vectordict.jl => operators.jl} | 0 src/library/function/vector/tuplevector.jl | 34 +--- src/library/print.jl | 33 ---- src/library/quadratization/abstract.jl | 83 ++------- src/library/quadratization/ntr_kzfd.jl | 12 +- src/library/quadratization/ptr_bg.jl | 10 +- src/library/variable/abstract.jl | 5 - src/library/variable/auxgen.jl | 56 ++++++ src/library/variable/print.jl | 51 ++++++ 17 files changed, 322 insertions(+), 219 deletions(-) delete mode 100644 src/library.jl create mode 100644 src/library/function/operators.jl rename src/library/function/vector/{vectordict.jl => operators.jl} (100%) delete mode 100644 src/library/print.jl create mode 100644 src/library/variable/auxgen.jl create mode 100644 src/library/variable/print.jl diff --git a/src/PseudoBooleanOptimization.jl b/src/PseudoBooleanOptimization.jl index d31d093..eab62df 100644 --- a/src/PseudoBooleanOptimization.jl +++ b/src/PseudoBooleanOptimization.jl @@ -10,16 +10,23 @@ include("interface/variable.jl") include("interface/function.jl") include("interface/quadratization.jl") -# include("library/variable/abstract.jl") -# include("library/variable/mapping.jl") +# Utility Functions +include("library/mod2linsolve.jl") +include("library/relaxedgcd.jl") +include("library/sortedmerge.jl") + +include("library/variable/abstract.jl") +include("library/variable/auxgen.jl") +include("library/variable/print.jl") include("library/function/abstract.jl") include("library/function/function.jl") -include("library/function/setdict.jl") +include("library/function/operators.jl") +include("library/function/setdict/setdict.jl") -# include("library/quadratization/abstract.jl") -# include("library/quadratization/ntr_kzfd.jl") -# include("library/quadratization/ptr_bg.jl") +include("library/quadratization/abstract.jl") +include("library/quadratization/ntr_kzfd.jl") +include("library/quadratization/ptr_bg.jl") # Synthetic PBF generation # include("library/synthesis/wishart.jl") diff --git a/src/interface/function.jl b/src/interface/function.jl index 6b327b2..855b3fc 100644 --- a/src/interface/function.jl +++ b/src/interface/function.jl @@ -20,9 +20,9 @@ abstract type AbstractPseudoBooleanFunction{V,T} end const AbstractPBF{V,T} = AbstractPseudoBooleanFunction{V,T} @doc raw""" - AbstractForm{V,T} + isscalar(f::AbstractPBF)::Bool """ -abstract type AbstractForm{V,T} end +function isscalar end @doc raw""" term diff --git a/src/interface/quadratization.jl b/src/interface/quadratization.jl index 1d95a0a..0ec111e 100644 --- a/src/interface/quadratization.jl +++ b/src/interface/quadratization.jl @@ -43,18 +43,3 @@ function quadratize end In-place version of [`quadratize`](@ref). """ function quadratize! end - -@doc raw""" - auxgen(::AbstractPBF{V,T}; name::AbstractString = "aux") where {V<:AbstractString,T} - -Creates a function that, when called multiple times, returns the strings `"aux_1"`, `"aux_2"`, ... and so on. - - auxgen(::AbstractPBF{Symbol,T}; name::Symbol = :aux) where {T} - -Creates a function that, when called multiple times, returns the symbols `:aux_1`, `:aux_2`, ... and so on. - - auxgen(::AbstractPBF{V,T}; start::V = V(0), step::V = V(-1)) where {V<:Integer,T} - -Creates a function that, when called multiple times, returns the integers ``-1``, ``-2``, ... and so on. -""" -function auxgen end diff --git a/src/library.jl b/src/library.jl deleted file mode 100644 index 139597f..0000000 --- a/src/library.jl +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/library/function/abstract.jl b/src/library/function/abstract.jl index 94bcffc..c9e2e7f 100644 --- a/src/library/function/abstract.jl +++ b/src/library/function/abstract.jl @@ -49,11 +49,9 @@ function upperbound(f::F) where {V,T,F<:AbstractPBF{V,T}} end function Base.convert(::Type{U}, f::AbstractPBF{V,T}) where {V,T,U<:Number} - if isempty(f) - return zero(U) - elseif degree(f) == 0 + if isscalar(f) return convert(U, f[nothing]) else - error("Can't convert non-constant pseudo-Boolean function to scalar type '$U'") + error("Can't convert non-scalar pseudo-Boolean function to scalar type '$U'") end end diff --git a/src/library/function/function.jl b/src/library/function/function.jl index b99e996..bf90b4d 100644 --- a/src/library/function/function.jl +++ b/src/library/function/function.jl @@ -20,34 +20,3 @@ function Base.promote_rule(::Type{PBF{V,Tf,S}}, ::Type{PBF{V,Tg,S}}) where {V,Tf return PBF{V,T,S} end - -# Arithmetic: '+', '-', '*', '/' -function Base.:(+)(f::F, g::G) where {V,Tf,Tg,F<:AbstractPBF{V,Tf},G<:AbstractPBF{V,Tg}} - Ω = union(keys(f), keys(g)) - h = sizehint!(zero(F), length(Ω)) - - for ω in Ω - h[ω] = f[ω] + g[ω] - end - - return h -end - -function Base.:(-)(f::Ff, g::Fg) where {V,Tf,Tg,Ff<:AbstractPBF{V,Tf},Fg<:AbstractPBF{V,Tg}} - F = promote_type(Ff, Fg) - Ω = union(keys(f), keys(g)) - h = sizehint!(zero(F), length(Ω)) - - for ω in Ω - h[ω] = f[ω] - g[ω] - end - - return h -end - -# Comparsion: '==', '≈' -Base.:(==)(f::DictFunction{V,T}, g::DictFunction{V,T}) where {V,T} = (f.Ω == g.Ω) - -function Base.isapprox(f::AbstractPBF{V,Tf}, g::AbstractPBF{V,Tg}; kw...) where {V,Tf,Tg} - return length(f) == length(g) && all(ω -> isapprox(g[ω], f[ω]; kw...), keys(f)) -end diff --git a/src/library/function/operators.jl b/src/library/function/operators.jl new file mode 100644 index 0000000..1e199b6 --- /dev/null +++ b/src/library/function/operators.jl @@ -0,0 +1,163 @@ +# Mapping: 'map', 'map!' +function Base.map(φ::Function, f::PBF{V,Tf,S}) where {V,Tf,S} + Ω = keys(f) + c = map(ω -> φ(f[ω]), Ω) + T = promote_type(Tf, eltype(c)) + g = zero(PBF{V,T,S}) + + for (i, ω) in enumerate(Ω) + g[ω] = c[i] + end + + return g +end + +function Base.map!(φ::Function, f::Ff, g::Fg) where {V,Tf,Tg,Ff<:AbstractPBF{V,Tf},Fg<:AbstractPBF{V,Tg}} + sizehint!(empty!(f), length(g)) + + for (ω, c) in g + f[ω] = φ(c) + end + + return f +end + +# Arithmetic: '+' +function Base.:(+)(f::PBF{V,Tf,Sf}, g::PBF{V,Tg,Sg}) where {V,Tf,Tg,Sf,Sg} + F = promote_type(PBF{V,Tf,Sf}, PBF{V,Tg,Sg}) + Ω = union(keys(f), keys(g)) + h = sizehint!(zero(F), length(Ω)) + + for ω in Ω + h[ω] = f[ω] + g[ω] + end + + return h +end + +function Base.:(+)(f::PBF{V,Tf,S}, β::Tc) where {V,Tf,Tc,S} + T = promote_type(Tf, Tc) + g = copy!(zero(PBF{V,T,S}), f) + + g[nothing] += β + + return g +end + +function Base.:(+)(β::Any, f::AbstractPBF) + return +(f, β) +end + +# Arithmetic: '-' +function Base.:(-)(f::PBF{V,Tf,Sf}, g::PBF{V,Tg,Sg}) where {V,Tf,Tg,Sf,Sg} + F = promote_type(PBF{V,Tf,Sf}, PBF{V,Tg,Sg}) + Ω = union(keys(f), keys(g)) + h = sizehint!(zero(F), length(Ω)) + + for ω in Ω + h[ω] = f[ω] - g[ω] + end + + return h +end + +function Base.:(-)(f::F) where {V,T,F<:AbstractPBF{V,T}} + return map(c -> -c, zero(F), f) +end + +function Base.:(-)(f::PBF{V,Tf,S}, β::Tc) where {V,Tf,Tc,S} + T = promote_type(Tf, Tc) + g = copy!(zero(PBF{V,T,S}), f) + + g[nothing] -= β + + return g +end + +function Base.:(-)(β::Tc, f::PBF{V,Tf,S}) where {V,Tf,Tc,S} + T = promote_type(Tf, Tc) + g = zero(PBF{V,T,S}) + + map!(c -> -c, g, f) + + g[nothing] += β + + return g +end + +# Arithmetic: '*' +function Base.:(*)(f::PBF{V,Tf,Sf}, g::PBF{V,Tg,Sg}) where {V,Tf,Tg,Sf,Sg} + F = promote_type(PBF{V,Tf,Sf}, PBF{V,Tg,Sg}) + h = zero(F) + m = length(f) + n = length(g) + + if iszero(f) || iszero(g) # T(n) = O(1) + return h + elseif f === g # T(n) = O(n) + O(n^2 / 2) + k = collect(f) + + sizehint!(h, n^2 ÷ 2) + + for i = 1:n + ωi, ci = k[i] + + h[ωi] += ci * ci + + for j = (i+1):n + ωj, cj = k[j] + + h[ωi × ωj] += 2 * ci * cj + end + end + + return h + else # T(n) = O(m n) + sizehint!(h, m * n) + + for (ωᵢ, cᵢ) in f, (ωⱼ, cⱼ) in g + h[ωᵢ × ωⱼ] += cᵢ * cⱼ + end + + return h + end +end + +function Base.:(*)(f::PBF{V,Tf,S}, α::Tc) where {V,Tf,Tc,S} + T = promote_type(Tf, Tc) + g = zero(PBF{V,T,S}) + + if !iszero(α) + map!(c -> c * α, g, f) + end + + return g +end + +function Base.:(*)(α::Any, f::AbstractPBF) + return *(f, α) +end + +# Arithmetic: '/' +function Base.:(/)(f::PBF{V,Tf,S}, α::Tc) where {V,Tf,Tc,S} + if iszero(α) + throw(DivideError()) + end + + T = promote_type(Tf, Tc) + g = zero(PBF{V,T,S}) + + map!(c -> c / α, g, f) + + return g +end + +# Comparsion: '==' +function Base.:(==)(f::AbstractPBF{V,Tf}, g::AbstractPBF{V,Tg}) where {V,Tf,Tg} + return length(f) == length(g) && all(ω -> g[ω] == f[ω], keys(f)) +end + +# Comparison: '≈' +function Base.isapprox(f::AbstractPBF{V,Tf}, g::AbstractPBF{V,Tg}; kw...) where {V,Tf,Tg} + return all(ω -> isapprox(g[ω], f[ω]; kw...), union(keys(f), keys(g))) +end diff --git a/src/library/function/setdict/setdict.jl b/src/library/function/setdict/setdict.jl index 1a1c7d5..fb00973 100644 --- a/src/library/function/setdict/setdict.jl +++ b/src/library/function/setdict/setdict.jl @@ -110,18 +110,15 @@ function Base.delete!(f::F, ω) where {V,T,F<:PBF{V,T,SetDict{V,T}}} return f end -##### - - - -function isscalar(f::DictFunction) +function isscalar(f::PBF{V,T,SetDict{V,T}}) where {V,T} return isempty(f) || (length(f) == 1 && haskey(f, nothing)) end -Base.zero(::Type{DictFunction{V,T}}) where {V,T} = DictFunction{V,T}() -Base.iszero(f::DictFunction) = isempty(f) -Base.one(::Type{DictFunction{V,T}}) where {V,T} = DictFunction{V,T}(one(T)) -Base.isone(f::DictFunction) = isscalar(f) && isone(f[nothing]) -Base.round(f::DictFunction{V,T}; kw...) where {V,T} = DictFunction{V,T}(ω => round(c; kw...) for (ω, c) in f) +function Base.iszero(f::PBF{V,T,SetDict{V,T}}) where {V,T} + return isempty(f) +end +function Base.isone(f::PBF{V,T,SetDict{V,T}}) where {V,T} + return isscalar(f) && isone(f[nothing]) +end diff --git a/src/library/function/vector/vectordict.jl b/src/library/function/vector/operators.jl similarity index 100% rename from src/library/function/vector/vectordict.jl rename to src/library/function/vector/operators.jl diff --git a/src/library/function/vector/tuplevector.jl b/src/library/function/vector/tuplevector.jl index dc3cc94..b6713cd 100644 --- a/src/library/function/vector/tuplevector.jl +++ b/src/library/function/vector/tuplevector.jl @@ -1,38 +1,6 @@ -@doc raw""" - VectorFunction{V,T} <: AbstractPBF{V,T} -""" -struct VectorFunction{V,T} <: AbstractPBF{V,T} - Ω::Vector{Term{V,T}} - - # standard constructor - function VectorFunction{V,T}(v::AbstractVector{Term{V,T}}, ready::Bool = false) where {V,T} - if ready - Ω = v - else - Ω = sort!(filter(!iszero ∘ last, v); alg=QuickSort, lt=varlt) - end - - return new{V,T}(Ω) - end - - # heterogeneous list - function VectorFunction{V,T}(x::AbstractVector) where {V,T} - return VectorFunction{V,T}(Term{V,T}.(x)) - end +const TupleVector{V,T} = Vector{Pair{Tuple{Vararg{V}},T}} - # dictionary - function VectorFunction{V,T}(x::AbstractDict) where {V,T} - return VectorFunction{V,T}(Term{V,T}.(pairs(x))) - end - - # fallback - function VectorFunction{V,T}(x::Any) where {V,T} - return VectorFunction{V,T}(Term{V,T}[Term{V,T}(x)]) - end -end -# Broadcast as scalar ? -Base.broadcastable(f::VectorFunction) = f # Copy function Base.sizehint!(f::VectorFunction, n::Integer) diff --git a/src/library/print.jl b/src/library/print.jl deleted file mode 100644 index 07e6a5d..0000000 --- a/src/library/print.jl +++ /dev/null @@ -1,33 +0,0 @@ -function _subscript(i::Integer) - if i < 0 - return "₋$(_subscript(abs(i)))" - else - return join(reverse(digits(i)) .+ Char(0x2080)) - end -end - -function Base.show(io::IO, (ω, c)::Term{V,T}) where {V,T} - if isempty(ω) - print(io, c) - elseif isone(c) - join(io, varshow.(ω), "*") - else - join(io, [string(c); varshow.(ω)], "*") - end -end - -function Base.show(io::IO, func::AbstractPBF{V,T}) where {V,T} - terms = sort!(map((ω, c) -> (sort(collect(ω)) => c), func); by=first, lt=varlt) - - if isempty(terms) - print(io, zero(T)) - else - join(io, terms, " + ") - end -end - - - -# function Base.show(io::IO, func::VectorFunction{V,T}) where {V,T} -# join(io, func.Ω, " + ") -# end diff --git a/src/library/quadratization/abstract.jl b/src/library/quadratization/abstract.jl index 3235a9c..32639fd 100644 --- a/src/library/quadratization/abstract.jl +++ b/src/library/quadratization/abstract.jl @@ -7,15 +7,15 @@ struct DEFAULT <: QuadratizationMethod end function quadratize!( aux, - f::PBF{V,T}, - ω::Set{V}, + f::AbstractPBF{V,T}, + ω::Any, c::T, quad::Quadratization{DEFAULT}, ) where {V,T} if c < zero(T) - quadratize!(aux, f, ω, c, Quadratization{NTR_KZFD}(quad.stable)) + quadratize!(aux, f, ω, c, Quadratization(NTR_KZFD(); stable=quad.stable)) else - quadratize!(aux, f, ω, c, Quadratization{PTR_BG}(quad.stable)) + quadratize!(aux, f, ω, c, Quadratization(PTR_BG(); stable=quad.stable)) end return f @@ -23,7 +23,7 @@ end function quadratize!( aux, - f::PBF{V,T}, + f::AbstractPBF{V,T}, quad::Quadratization, ) where {V,T} # Collect Terms @@ -45,11 +45,11 @@ end struct INFER <: QuadratizationMethod end @doc raw""" - infer_quadratization(f::PBF) + infer_quadratization(f::AbstractPBF) For a given PBF, returns whether it should be quadratized or not, based on its degree. """ -function infer_quadratization(f::PBF, stable::Bool = false) +function infer_quadratization(f::AbstractPBF, stable::Bool = false) k = degree(f) if k <= 2 @@ -57,85 +57,28 @@ function infer_quadratization(f::PBF, stable::Bool = false) else # Without any extra knowledge, it is better to # quadratize using the default approach - return Quadratization{DEFAULT}(stable) + return Quadratization(DEFAULT(); stable) end end -function quadratize!(aux, f::PBF, quad::Quadratization{INFER}) +function quadratize!(aux, f::AbstractPBF, quad::Quadratization{INFER}) quadratize!(aux, f, infer_quadratization(f, quad.stable)) return f end -function quadratize!(::Function, f::PBF, ::Nothing) +function quadratize!(::Function, f::AbstractPBF, ::Nothing) return f end -@doc raw""" - auxgen(::AbstractPBF{V,T}; name::AbstractString = "aux") where {V<:AbstractString,T} - -Creates a function that, when called multiple times, returns the strings `"aux_1"`, `"aux_2"`, ... and so on. - - auxgen(::AbstractPBF{Symbol,T}; name::Symbol = :aux) where {T} - -Creates a function that, when called multiple times, returns the symbols `:aux_1`, `:aux_2`, ... and so on. - - auxgen(::AbstractPBF{V,T}; start::V = V(0), step::V = V(-1)) where {V<:Integer,T} - -Creates a function that, when called multiple times, returns the integers ``-1``, ``-2``, ... and so on. -""" -function auxgen end - -function auxgen(::AbstractPBF{Symbol,T}; name::Symbol = :aux) where {T} - counter = Int[0] - - function aux(n::Union{Integer,Nothing} = nothing) - if isnothing(n) - return first(aux(1)) - else - return [Symbol("$(name)_$(counter[] += 1)") for _ in 1:n] - end - end - - return aux -end - -function auxgen(::AbstractPBF{V,T}; name::AbstractString = "aux") where {V<:AbstractString,T} - counter = Int[0] - - function aux(n::Union{Integer,Nothing} = nothing) - if isnothing(n) - return first(aux(1)) - else - return ["$(name)_$(counter[] += 1)" for _ in 1:n] - end - end - - return aux -end - -function auxgen(::AbstractPBF{V,T}; start::V = V(0), step::V = V(-1)) where {V<:Integer,T} - counter = [start] - - function aux(n::Union{Integer,Nothing} = nothing) - if isnothing(n) - return first(aux(1)) - else - return [(counter[] += step) for _ in 1:n] - end - end - - return aux -end - -function quadratize!(f::PBF, quad::Union{Quadratization,Nothing} = Quadratization{INFER}) +function quadratize!(f::AbstractPBF, quad::Union{Quadratization,Nothing} = Quadratization(INFER())) return quadratize!(auxgen(f), f, quad) end -function quadratize(aux, f::PBF, quad::Union{Quadratization,Nothing} = Quadratization{INFER}) +function quadratize(aux, f::AbstractPBF, quad::Union{Quadratization,Nothing} = Quadratization(INFER())) return quadratize!(aux, copy(f), quad) end -function quadratize(f::PBF, quad::Union{Quadratization,Nothing} = Quadratization{INFER}) +function quadratize(f::AbstractPBF, quad::Union{Quadratization,Nothing} = Quadratization(INFER())) return quadratize!(copy(f), quad) end diff --git a/src/library/quadratization/ntr_kzfd.jl b/src/library/quadratization/ntr_kzfd.jl index 50fbe4d..16fac08 100644 --- a/src/library/quadratization/ntr_kzfd.jl +++ b/src/library/quadratization/ntr_kzfd.jl @@ -19,12 +19,16 @@ struct NTR_KZFD <: QuadratizationMethod end function quadratize!( aux, f::F, - ω::Set{V}, + ω::Any, c::T, ::Quadratization{NTR_KZFD}, ) where {V,T,F<:AbstractPBF{V,T}} + @assert c < zero(T) + + ω_ = term_head(F, ω) + # Degree - k = length(ω) + k = length(ω_) # Fast-track k < 3 && return nothing @@ -36,11 +40,11 @@ function quadratize!( # NOTE: This method is stable by construction # Quadratization - delete!(f, ω) + delete!(f, ω_) f[s] += -c * (k - 1) - for i ∈ ω + for i ∈ ω_ f[i×s] += c end diff --git a/src/library/quadratization/ptr_bg.jl b/src/library/quadratization/ptr_bg.jl index 8411793..2c21954 100644 --- a/src/library/quadratization/ptr_bg.jl +++ b/src/library/quadratization/ptr_bg.jl @@ -20,25 +20,27 @@ struct PTR_BG <: QuadratizationMethod end function quadratize!( aux, f::F, - ω::Set{V}, + ω::Any, c::T, quad::Quadratization{PTR_BG}, ) where {V,T,F<:AbstractPBF{V,T}} + ω_ = term_head(F, ω) + # Degree - k = length(ω) + k = length(ω_) # Fast-track k < 3 && return nothing # Variables s = aux(k - 2)::Vector{V} - b = collect(ω)::Vector{V} + b = collect(ω_)::Vector{V} # Stabilization quad.stable && sort!(b; lt = varlt) # Quadratization - delete!(f, ω) + delete!(f, ω_) f[b[k]×b[k-1]] += c diff --git a/src/library/variable/abstract.jl b/src/library/variable/abstract.jl index 902790e..1dd10a2 100644 --- a/src/library/variable/abstract.jl +++ b/src/library/variable/abstract.jl @@ -70,8 +70,3 @@ function varmul(u::AbstractVector{V}, v::AbstractVector{V}) where {V} # @assert issorted(u) && issorted(v) return sortedmergewith(u, v; lt = varlt) end - - -varshow(io::IO, v::V) where {V} = show(io, varshow(v)) -varshow(v::Integer) = "x$(_subscript(v))" -varshow(v::V) where {V<:Union{Symbol,AbstractString}} = string(v) diff --git a/src/library/variable/auxgen.jl b/src/library/variable/auxgen.jl new file mode 100644 index 0000000..6d400e4 --- /dev/null +++ b/src/library/variable/auxgen.jl @@ -0,0 +1,56 @@ +@doc raw""" + auxgen(::AbstractPBF{V,T}; name::AbstractString = "aux") where {V<:AbstractString,T} + +Creates a function that, when called multiple times, returns the strings `"aux_1"`, `"aux_2"`, ... and so on. + + auxgen(::AbstractPBF{Symbol,T}; name::Symbol = :aux) where {T} + +Creates a function that, when called multiple times, returns the symbols `:aux_1`, `:aux_2`, ... and so on. + + auxgen(::AbstractPBF{V,T}; start::V = V(0), step::V = V(-1)) where {V<:Integer,T} + +Creates a function that, when called multiple times, returns the integers ``-1``, ``-2``, ... and so on. +""" +function auxgen end + +function auxgen(::AbstractPBF{Symbol,T}; name::Symbol = :aux) where {T} + counter = Ref{Int}(0) + + function aux(n::Union{Integer,Nothing} = nothing) + if isnothing(n) + return first(aux(1)) + else + return [Symbol("$(name)_$(counter[] += 1)") for _ in 1:n] + end + end + + return aux +end + +function auxgen(::AbstractPBF{V,T}; name::AbstractString = "aux") where {V<:AbstractString,T} + counter = Ref{Int}(0) + + function aux(n::Union{Integer,Nothing} = nothing) + if isnothing(n) + return first(aux(1)) + else + return ["$(name)_$(counter[] += 1)" for _ in 1:n] + end + end + + return aux +end + +function auxgen(::AbstractPBF{V,T}; start::V = V(0), step::V = V(-1)) where {V<:Integer,T} + counter = [start] + + function aux(n::Union{Integer,Nothing} = nothing) + if isnothing(n) + return first(aux(1)) + else + return [(counter[] += step) for _ in 1:n] + end + end + + return aux +end diff --git a/src/library/variable/print.jl b/src/library/variable/print.jl new file mode 100644 index 0000000..c6ac1d4 --- /dev/null +++ b/src/library/variable/print.jl @@ -0,0 +1,51 @@ +function _subscript(i::Integer) + if i < 0 + return "₋$(_subscript(abs(i)))" + else + return join(reverse!(digits(i)) .+ Char(0x2080)) + end +end + +varshow(io::IO, v::V) where {V} = show(io, varshow(v)) +varshow(v::Integer, x::AbstractString = "x") = "$(x)$(_subscript(v))" +varshow(v::V) where {V<:Union{Symbol,AbstractString}} = string(v) + +function Base.show(io::IO, ::MIME"text/plain", f::AbstractPBF{V,T}) where {V,T} + if isscalar(f) + print(io, f[nothing]) + + return nothing + end + + terms = sort!(map(((ω, c)::Pair) -> (sort(collect(ω)) => c), pairs(f)); by=first, lt=varlt) + + for (i, (ω, c)) in enumerate(terms) + if i > 1 + if c < zero(T) + print(io, " - ") + else + print(io, " + ") + end + else + if c < zero(T) + print(io, "-") + end + end + + c_ = abs(c) + + if isone(c_) + join(io, varshow.(ω), " ") + else + join(io, [string(c_); varshow.(ω)], " ") + end + end + + return nothing +end + + + +# function Base.show(io::IO, f::VectorFunction{V,T}) where {V,T} +# join(io, f.Ω, " + ") +# end From 90a1621e92330a76c862a1b62f3c5e1e37ef0506 Mon Sep 17 00:00:00 2001 From: pedromxavier Date: Mon, 2 Oct 2023 18:39:58 -0400 Subject: [PATCH 4/9] Evolve interface --- src/PseudoBooleanOptimization.jl | 16 ++++--- src/interface/variable.jl | 25 ++++++++++ src/library/function/setdict/operators.jl | 46 ------------------- src/library/synthesis/not_all_equal_3sat.jl | 14 +++++- src/library/synthesis/regular_xorsat.jl | 9 +++- .../synthesis/sherrington_kirkpatrick.jl | 22 +++++++-- src/library/synthesis/wishart.jl | 37 +++++++++++---- src/library/variable/{auxgen.jl => vargen.jl} | 15 ------ .../variable/{abstract.jl => varlt.jl} | 23 ---------- src/library/variable/varmap.jl | 12 +++++ src/library/variable/varmul.jl | 25 ++++++++++ src/library/variable/{print.jl => varshow.jl} | 0 test/unit/unit.jl | 12 ++--- 13 files changed, 142 insertions(+), 114 deletions(-) rename src/library/variable/{auxgen.jl => vargen.jl} (62%) rename src/library/variable/{abstract.jl => varlt.jl} (73%) create mode 100644 src/library/variable/varmap.jl create mode 100644 src/library/variable/varmul.jl rename src/library/variable/{print.jl => varshow.jl} (100%) diff --git a/src/PseudoBooleanOptimization.jl b/src/PseudoBooleanOptimization.jl index eab62df..e23af50 100644 --- a/src/PseudoBooleanOptimization.jl +++ b/src/PseudoBooleanOptimization.jl @@ -15,9 +15,11 @@ include("library/mod2linsolve.jl") include("library/relaxedgcd.jl") include("library/sortedmerge.jl") -include("library/variable/abstract.jl") -include("library/variable/auxgen.jl") -include("library/variable/print.jl") +include("library/variable/varlt.jl") +include("library/variable/vargen.jl") +include("library/variable/varmap.jl") +include("library/variable/varmul.jl") +include("library/variable/varshow.jl") include("library/function/abstract.jl") include("library/function/function.jl") @@ -29,9 +31,9 @@ include("library/quadratization/ntr_kzfd.jl") include("library/quadratization/ptr_bg.jl") # Synthetic PBF generation -# include("library/synthesis/wishart.jl") -# include("library/synthesis/regular_xorsat.jl") -# include("library/synthesis/sherrington_kirkpatrick.jl") -# include("library/synthesis/not_all_equal_3sat.jl") +include("library/synthesis/wishart.jl") +include("library/synthesis/regular_xorsat.jl") +include("library/synthesis/sherrington_kirkpatrick.jl") +include("library/synthesis/not_all_equal_3sat.jl") end # module PseudoBooleanOptimization diff --git a/src/interface/variable.jl b/src/interface/variable.jl index 0ee16f8..148871c 100644 --- a/src/interface/variable.jl +++ b/src/interface/variable.jl @@ -25,3 +25,28 @@ const × = varmul # \times[tab] varshow(io::IO, v::V) where {V} """ function varshow end + +@doc raw""" + varmap(::Type{V}, i::Integer) where {V} +""" +function varmap end + +@doc raw""" + vargen(::Type{V}, n::Union{Integer,Nothing} = nothing) where {V} +""" +function vargen end + +@doc raw""" + auxgen(::AbstractPBF{V,T}; name::AbstractString = "aux") where {V<:AbstractString,T} + +Creates a function that, when called multiple times, returns the strings `"aux_1"`, `"aux_2"`, ... and so on. + + auxgen(::AbstractPBF{Symbol,T}; name::Symbol = :aux) where {T} + +Creates a function that, when called multiple times, returns the symbols `:aux_1`, `:aux_2`, ... and so on. + + auxgen(::AbstractPBF{V,T}; start::V = V(0), step::V = V(-1)) where {V<:Integer,T} + +Creates a function that, when called multiple times, returns the integers ``-1``, ``-2``, ... and so on. +""" +function auxgen end \ No newline at end of file diff --git a/src/library/function/setdict/operators.jl b/src/library/function/setdict/operators.jl index 0b61f09..d9d48cf 100644 --- a/src/library/function/setdict/operators.jl +++ b/src/library/function/setdict/operators.jl @@ -64,53 +64,7 @@ end Base.:(-)(c, f::DictFunction{V,T}) where {V,T} = -(convert(T, c), f) Base.:(-)(f::DictFunction{V,T}, c) where {V,T} = -(f, convert(T, c)) -# Arithmetic: (*) -function Base.:(*)(f::DictFunction{V,T}, g::DictFunction{V,T}) where {V,T} - h = zero(DictFunction{V,T}) - m = length(f) - n = length(g) - if iszero(f) || iszero(g) # T(n) = O(1) - return h - elseif f === g # T(n) = O(n) + O(n^2 / 2) - k = collect(f) - - sizehint!(h, n^2 ÷ 2) - - for i = 1:n - ωi, ci = k[i] - - h[ωi] += ci * ci - - for j = (i+1):n - ωj, cj = k[j] - - h[ωi × ωj] += 2 * ci * cj - end - end - - return h - else # T(n) = O(m n) - sizehint!(h, m * n) - - for (ωᵢ, cᵢ) in f, (ωⱼ, cⱼ) in g - h[ωᵢ × ωⱼ] += cᵢ * cⱼ - end - - return h - end -end - -function Base.:(*)(f::DictFunction{V,T}, a::T) where {V,T} - if iszero(a) - return DictFunction{V,T}() - else - return DictFunction{V,T}(ω => c * a for (ω, c) ∈ f) - end -end - -Base.:(*)(f::DictFunction{V,T}, a) where {V,T} = *(f, convert(T, a)) -Base.:(*)(a, f::DictFunction) = *(f, a) # Arithmetic: (/) function Base.:(/)(f::DictFunction{V,T}, a::T) where {V,T} diff --git a/src/library/synthesis/not_all_equal_3sat.jl b/src/library/synthesis/not_all_equal_3sat.jl index 1e9e441..092140c 100644 --- a/src/library/synthesis/not_all_equal_3sat.jl +++ b/src/library/synthesis/not_all_equal_3sat.jl @@ -30,8 +30,18 @@ function not_all_equal_3sat(rng, ::Type{F}, n::Integer, m::Integer) where {V,T,F end # Convert to boolean - # s = 2x - 1 - f = sum([J[i,j] * F(i => T(2), T(-1)) * F(j => T(2), T(-1)) for (i,j) in keys(J)]) + # let s_i = 2x_i - 1 + # ⟹ s_i s_j = (2x_i - 1) (2x_j - 1) = 4x_i x_j - 2x_i - 2x_j + 1 + # ⟹ Jij s_i s_j = 4Jij x_i x_j - 2Jij x_i - 2Jij x_j + Jij + f = sizehint!(zero(F), length(J) + n) + + for ((i, j), c) in J + f[i, j] += 4c + f[i] -= 2c + f[j] -= 2c + f[nothing] += c + end + x = nothing # no planted solutions return (f, x) diff --git a/src/library/synthesis/regular_xorsat.jl b/src/library/synthesis/regular_xorsat.jl index 4d0a31a..35c4f0a 100644 --- a/src/library/synthesis/regular_xorsat.jl +++ b/src/library/synthesis/regular_xorsat.jl @@ -46,9 +46,14 @@ function k_regular_k_xorsat( if num_solutions > 0 # Convert to boolean # s = 2x - 1 + # ∑ᵢ (-1)^cᵢ ∏ⱼ (2xᵢⱼ - 1) = ∑ᵢ (-1)^c[i] (2xᵢ₁ - 1) ⋯ (2xᵢₖ - 1) = 2^ f = sum((-1.0)^c[i] * prod([F(idx[i,j] => 2.0, -1.0) for j = 1:k]) for i = 1:n) - return quadratize!(f, quad) + quadratize!(f, quad) + + x = nothing # no planted solutions + + return (f, x) end end @@ -82,7 +87,7 @@ function r_regular_k_xorsat( if r == k return k_regular_k_xorsat(rng, F, n, k; quad) else - # NOTE: This might involve Sudoku solving! + # NOTE: This might involve solving Sudoku! error("A method for generating r-regular k-XORSAT where r ≂̸ k is not implemented (yet).") return nothing diff --git a/src/library/synthesis/sherrington_kirkpatrick.jl b/src/library/synthesis/sherrington_kirkpatrick.jl index 65c1b9b..556f9ea 100644 --- a/src/library/synthesis/sherrington_kirkpatrick.jl +++ b/src/library/synthesis/sherrington_kirkpatrick.jl @@ -9,10 +9,24 @@ where ``J_{i, j} \sim \mathcal{N}(0, 1)``. """ function sherrington_kirkpatrick(rng, ::Type{F}, n::Integer; μ::T = zero(T), σ::T = one(T)) where {V,T,F<:AbstractPBF{V,T}} - return sum([ - (σ * randn(rng, T) + μ) * F(i => T(2), T(-1)) * F(j => T(2), T(-1)) - for i = 1:n for j = (i+1):n - ]) + J = Dict{Tuple{Int,Int},T}((i, j) => (σ * randn(rng, T) + μ) for i = 1:n for j = (i+1):n) + + # Convert to boolean + # let s_i = 2x_i - 1 + # ⟹ s_i s_j = (2x_i - 1) (2x_j - 1) = 4x_i x_j - 2x_i - 2x_j + 1 + # ⟹ Jij s_i s_j = 4Jij x_i x_j - 2Jij x_i - 2Jij x_j + Jij + f = sizehint!(zero(F), length(J) + n) + + for ((i, j), c) in J + f[i, j] += 4c + f[i] -= 2c + f[j] -= 2c + f[nothing] += c + end + + x = nothing # no planted solutions + + return (f, x) end function sherrington_kirkpatrick(::Type{F}, n::Integer; μ::T = zero(T), σ::T = one(T)) where {V,T,F<:AbstractPBF{V,T}} diff --git a/src/library/synthesis/wishart.jl b/src/library/synthesis/wishart.jl index ddd95c5..698a94d 100644 --- a/src/library/synthesis/wishart.jl +++ b/src/library/synthesis/wishart.jl @@ -11,7 +11,7 @@ Hamiltonian is zero field, i.e: E(\mathbf{s}) = -\frac{1}{2} \mathbf{s}' J \mathbf{s} ``` -- ``M`` specifies the number of columns in ``W`` (for ``M \ge n``, FM and easy.) +- ``m`` specifies the number of columns in ``W`` (for ``m \ge n``, FM and easy.) - `precision`: number of decimal points to round the uncorrelated Gaussian used to generate the w elements. This is to avoid numerical issues where a spurious state takes over as the GS. @@ -34,23 +34,42 @@ function wishart(rng, ::Type{F}, n::Integer, m::Integer = 1; discretize_bonds::B else R = randn(rng, n, m) - if precision !== nothing - R = round.(R; digits = precision) + if !isnothing(precision) + map!(x -> round(x; digits = precision), R, R) end end W = s'R - J̃ = -(W * W') / n - J = J̃ - diagm(diag(J̃)) + J = W * W' + + for i = 1:n + J[i,i] = zero(T) + end if discretize_bonds - J *= n^2 * (n - 1) - J = round.(J) + map!(x -> round((n^2 - n^3) * x; digits = precision), J, J) + else + map!(x -> round((n - n^2) * x; digits = precision), J, J) end # Convert to boolean - # s = 2x - 1 - return sum([J[i,j] * F(i => T(2), T(-1)) * F(j => T(2), T(-1)) for i = 1:n for j = 1:n]) + # let s_i = 2x_i - 1 + # ⟹ s_i s_j = (2x_i - 1) (2x_j - 1) = 4x_i x_j - 2x_i - 2x_j + 1 + # ⟹ Jij s_i s_j = 4Jij x_i x_j - 2Jij x_i - 2Jij x_j + Jij + f = sizehint!(zero(F), n^2 ÷ 2) + + for i = 1:n, j = 1:n + c = J[i, j] + + f[i, j] += 4c + f[i] -= 2c + f[j] -= 2c + f[nothing] += c + end + + x = [t] # planted solution + + return (f, x) end function wishart(::Type{F}, n::Integer, m::Integer = 1; discretize_bonds::Bool = false, precision = nothing) where {V,T,F<:AbstractPBF{V,T}} diff --git a/src/library/variable/auxgen.jl b/src/library/variable/vargen.jl similarity index 62% rename from src/library/variable/auxgen.jl rename to src/library/variable/vargen.jl index 6d400e4..2d6589c 100644 --- a/src/library/variable/auxgen.jl +++ b/src/library/variable/vargen.jl @@ -1,18 +1,3 @@ -@doc raw""" - auxgen(::AbstractPBF{V,T}; name::AbstractString = "aux") where {V<:AbstractString,T} - -Creates a function that, when called multiple times, returns the strings `"aux_1"`, `"aux_2"`, ... and so on. - - auxgen(::AbstractPBF{Symbol,T}; name::Symbol = :aux) where {T} - -Creates a function that, when called multiple times, returns the symbols `:aux_1`, `:aux_2`, ... and so on. - - auxgen(::AbstractPBF{V,T}; start::V = V(0), step::V = V(-1)) where {V<:Integer,T} - -Creates a function that, when called multiple times, returns the integers ``-1``, ``-2``, ... and so on. -""" -function auxgen end - function auxgen(::AbstractPBF{Symbol,T}; name::Symbol = :aux) where {T} counter = Ref{Int}(0) diff --git a/src/library/variable/abstract.jl b/src/library/variable/varlt.jl similarity index 73% rename from src/library/variable/abstract.jl rename to src/library/variable/varlt.jl index 1dd10a2..e25c458 100644 --- a/src/library/variable/abstract.jl +++ b/src/library/variable/varlt.jl @@ -47,26 +47,3 @@ function varlt(u::Set{V}, v::Set{V}) where {V} return length(u) < length(v) end end - - -function varmul(u::Set{V}, v::Set{V}) where {V} - return u ∪ v -end - -function varmul(u::V, v::Set{V}) where {V} - return u ∪ v -end - -function varmul(u::Set{V}, v::V) where {V} - return u ∪ v -end - -function varmul(u::V, v::V) where {V} - return Set{V}([u, v]) -end - -function varmul(u::AbstractVector{V}, v::AbstractVector{V}) where {V} - # Vectors are assumed to be sorted! - # @assert issorted(u) && issorted(v) - return sortedmergewith(u, v; lt = varlt) -end diff --git a/src/library/variable/varmap.jl b/src/library/variable/varmap.jl new file mode 100644 index 0000000..f9a6edd --- /dev/null +++ b/src/library/variable/varmap.jl @@ -0,0 +1,12 @@ +# Fallback implementation of varmap for types that do not implement it +function varmap(::Type{V}, i::Integer) where {V} + return V(i) +end + +function varmap(::Type{V}, i::Integer, x::AbstractString = "x") where {V<:AbstractString} + return V("$(x)_$(i)") +end + +function varmap(::Type{Symbol}, i::Integer, x::Symbol = :x) + return Symbol("$(x)_$(i)") +end diff --git a/src/library/variable/varmul.jl b/src/library/variable/varmul.jl new file mode 100644 index 0000000..21fac6a --- /dev/null +++ b/src/library/variable/varmul.jl @@ -0,0 +1,25 @@ +function varmul(u::Set{V}, v::Set{V}) where {V} + return u ∪ v +end + +function varmul(u::V, v::Set{V}) where {V} + return u ∪ v +end + +function varmul(u::Set{V}, v::V) where {V} + return u ∪ v +end + +function varmul(u::V, v::V) where {V} + if varlt(u, v) + return (u, v) + else + return (v, u) + end +end + +function varmul(u::AbstractVector{V}, v::AbstractVector{V}) where {V} + # Vectors are assumed to be sorted! + # @assert issorted(u) && issorted(v) + return sortedmergewith(u, v; lt = varlt) +end diff --git a/src/library/variable/print.jl b/src/library/variable/varshow.jl similarity index 100% rename from src/library/variable/print.jl rename to src/library/variable/varshow.jl diff --git a/test/unit/unit.jl b/test/unit/unit.jl index cce6064..7587559 100644 --- a/test/unit/unit.jl +++ b/test/unit/unit.jl @@ -7,12 +7,12 @@ include("print.jl") function test_unit() @testset "Pseudo-Boolean Functions" verbose = true begin - # test_constructors() - # test_operators() - # test_evaluation() - # test_calculus() - # test_discretization() - # test_quadratization() + test_constructors() + test_operators() + test_evaluation() + test_calculus() + test_discretization() + test_quadratization() # test_print() end From e0d069547811a5db447867e10e9ce07c0411ff9d Mon Sep 17 00:00:00 2001 From: pedromxavier Date: Thu, 5 Oct 2023 22:46:16 -0400 Subject: [PATCH 5/9] Add tests --- docs/make.jl | 30 ++-- docs/src/api.md | 10 ++ docs/src/index.md | 5 +- docs/src/quadratization.md | 18 --- src/PseudoBooleanOptimization.jl | 2 + src/interface/variable.jl | 15 +- src/library/function/abstract.jl | 22 ++- src/library/function/default.jl | 13 ++ src/library/function/function.jl | 19 +++ src/library/function/operators.jl | 76 +++++++++- src/library/function/setdict/operators.jl | 137 ------------------ src/library/function/setdict/setdict.jl | 97 ++++++++----- src/library/mod2linsolve.jl | 6 +- src/library/quadratization/abstract.jl | 2 +- src/library/subscript.jl | 7 + src/library/synthesis/not_all_equal_3sat.jl | 30 ++-- src/library/synthesis/regular_xorsat.jl | 13 +- .../synthesis/sherrington_kirkpatrick.jl | 32 ++-- src/library/synthesis/wishart.jl | 59 +++++--- src/library/variable/vargen.jl | 45 +++--- src/library/variable/varmap.jl | 10 +- src/library/variable/varset.jl | 3 + src/library/variable/varshow.jl | 10 -- test/runtests.jl | 2 +- test/unit/constructors.jl | 86 +++++++++++ test/unit/function.jl | 9 -- test/unit/term_parser.jl | 33 +++++ test/unit/unit.jl | 26 ++-- 28 files changed, 483 insertions(+), 334 deletions(-) create mode 100644 docs/src/api.md delete mode 100644 docs/src/quadratization.md create mode 100644 src/library/function/default.jl delete mode 100644 src/library/function/setdict/operators.jl create mode 100644 src/library/subscript.jl create mode 100644 src/library/variable/varset.jl create mode 100644 test/unit/constructors.jl delete mode 100644 test/unit/function.jl create mode 100644 test/unit/term_parser.jl diff --git a/docs/make.jl b/docs/make.jl index 1417467..cc17944 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -3,28 +3,36 @@ using PseudoBooleanOptimization const PBO = PseudoBooleanOptimization # Set up to run docstrings with jldoctest -DocMeta.setdocmeta!(PseudoBooleanOptimization, :DocTestSetup, :(using PseudoBooleanOptimization); recursive = true) +DocMeta.setdocmeta!( + PseudoBooleanOptimization, + :DocTestSetup, + :(using PseudoBooleanOptimization); + recursive = true, +) makedocs(; modules = [PseudoBooleanOptimization], doctest = true, - clean = true, - format = Documenter.HTML( - assets = ["assets/extra_styles.css", "assets/favicon.ico"], - mathengine = Documenter.KaTeX(), + clean = true, + format = Documenter.HTML( + assets = ["assets/extra_styles.css", "assets/favicon.ico"], + mathengine = Documenter.KaTeX(), sidebar_sitename = false, ), sitename = "PseudoBooleanOptimization.jl", - authors = "Pedro Maciel Xavier", - pages = [ - "Home" => "index.md", - "Quadratization" => "quadratization.md" + authors = "Pedro Maciel Xavier", + pages = [ # + "Home" => "index.md", + "API Reference" => "api.md", ], - workdir = @__DIR__, + workdir = @__DIR__ ) if "--skip-deploy" ∈ ARGS @warn "Skipping deployment" else - deploydocs(repo = raw"github.com/pedromxavier/PseudoBooleanOptimization.jl.git", push_preview = true) + deploydocs( + repo = raw"github.com/pedromxavier/PseudoBooleanOptimization.jl.git", + push_preview = true, + ) end diff --git a/docs/src/api.md b/docs/src/api.md new file mode 100644 index 0000000..c922011 --- /dev/null +++ b/docs/src/api.md @@ -0,0 +1,10 @@ +# API Reference + +## Variables + +```@docs +PBO.varlt +PBO.varmul +PBO.varmap +PBO.vargen +``` diff --git a/docs/src/index.md b/docs/src/index.md index 66aec2c..a432c1a 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -7,8 +7,7 @@ f(\mathbf{x}) = \sum_{\omega \subseteq [n]} c_{\omega} \prod_{j \in \omega} x_{j ## Getting Started ```@example -using PseudoBooleanOptimization -const PBO = PseudoBooleanOptimization +import PseudoBooleanOptimization as PBO f = PBO.PBF{Symbol,Float64}( :x => 3.0, @@ -17,5 +16,5 @@ f = PBO.PBF{Symbol,Float64}( -100.0, ) -g = f^2 - f +g = f^2 - 2f ``` diff --git a/docs/src/quadratization.md b/docs/src/quadratization.md deleted file mode 100644 index b21c478..0000000 --- a/docs/src/quadratization.md +++ /dev/null @@ -1,18 +0,0 @@ -# Quadratization - -```@docs -PBO.quadratize -PBO.quadratize! -``` - -```@docs -PBO.auxgen -``` - -## Methods - -```@docs -PBO.NTR_KZFD -PBO.PTR_BG -PBO.DEFAULT -``` diff --git a/src/PseudoBooleanOptimization.jl b/src/PseudoBooleanOptimization.jl index e23af50..a93bfda 100644 --- a/src/PseudoBooleanOptimization.jl +++ b/src/PseudoBooleanOptimization.jl @@ -11,6 +11,7 @@ include("interface/function.jl") include("interface/quadratization.jl") # Utility Functions +include("library/subscript.jl") include("library/mod2linsolve.jl") include("library/relaxedgcd.jl") include("library/sortedmerge.jl") @@ -25,6 +26,7 @@ include("library/function/abstract.jl") include("library/function/function.jl") include("library/function/operators.jl") include("library/function/setdict/setdict.jl") +include("library/function/default.jl") include("library/quadratization/abstract.jl") include("library/quadratization/ntr_kzfd.jl") diff --git a/src/interface/variable.jl b/src/interface/variable.jl index 148871c..0984ed9 100644 --- a/src/interface/variable.jl +++ b/src/interface/variable.jl @@ -32,21 +32,16 @@ function varshow end function varmap end @doc raw""" - vargen(::Type{V}, n::Union{Integer,Nothing} = nothing) where {V} -""" -function vargen end - -@doc raw""" - auxgen(::AbstractPBF{V,T}; name::AbstractString = "aux") where {V<:AbstractString,T} + vargen(::AbstractPBF{V,T}; name::AbstractString = "x") where {V<:AbstractString,T} Creates a function that, when called multiple times, returns the strings `"aux_1"`, `"aux_2"`, ... and so on. - auxgen(::AbstractPBF{Symbol,T}; name::Symbol = :aux) where {T} + vargen(::AbstractPBF{Symbol,T}; name::Symbol = :x) where {T} -Creates a function that, when called multiple times, returns the symbols `:aux_1`, `:aux_2`, ... and so on. +Creates a function that, when called multiple times, returns the symbols `:x₋₁`, `:x₋₂`, ... and so on. - auxgen(::AbstractPBF{V,T}; start::V = V(0), step::V = V(-1)) where {V<:Integer,T} + vargen(::AbstractPBF{V,T}; start::V = V(0), step::V = V(-1)) where {V<:Integer,T} Creates a function that, when called multiple times, returns the integers ``-1``, ``-2``, ... and so on. """ -function auxgen end \ No newline at end of file +function vargen end diff --git a/src/library/function/abstract.jl b/src/library/function/abstract.jl index c9e2e7f..981c4a2 100644 --- a/src/library/function/abstract.jl +++ b/src/library/function/abstract.jl @@ -1,3 +1,11 @@ +function Base.zero(::F) where {V,T,F<:AbstractPBF{V,T}} + return zero(F) +end + +function Base.one(::F) where {V,T,F<:AbstractPBF{V,T}} + return one(F) +end + function bounds(f::AbstractPBF) return (lowerbound(f), upperbound(f)) end @@ -10,18 +18,22 @@ function residual(f::F, x::V) where {V,T,F<:AbstractPBF{V,T}} return F(ω => c for (ω, c) ∈ f if (x ∉ ω)) end +function Base.round(f::AbstractPBF; kws...) + map!(c -> round(c; kws...), f) + + return f +end + function discretize!(f::AbstractPBF{_,T}; tol::T = T(1E-6)) where {_,T} ε = mingap(f; tol) - for (ω, c) in f - f[ω] = round(c / ε; digits = 0) - end + map!(c -> round(c / ε; digits = 0), f) return f end -function discretize(f::AbstractPBF{V,T}; atol::T = 1E-6) where {V,T} - return discretize!(copy(f); atol) +function discretize(f::AbstractPBF{V,T}; tol::T = 1E-6) where {V,T} + return discretize!(copy(f); tol) end function derivative(f::F, x::V) where {V,T,F<:AbstractPBF{V,T}} diff --git a/src/library/function/default.jl b/src/library/function/default.jl new file mode 100644 index 0000000..cec110f --- /dev/null +++ b/src/library/function/default.jl @@ -0,0 +1,13 @@ +const DefaultPBF{V,T} = PBF{V,T,SetDict{V,T}} + +function PBF{V,T}(args...; kwargs...) where {V,T} + return DefaultPBF{V,T}(args...; kwargs...) +end + +function Base.zero(::Type{PBF{V,T}}) where {V,T} + return zero(DefaultPBF{V,T}) +end + +function Base.one(::Type{PBF{V,T}}) where {V,T} + return one(DefaultPBF{V,T}) +end diff --git a/src/library/function/function.jl b/src/library/function/function.jl index bf90b4d..1ace2ad 100644 --- a/src/library/function/function.jl +++ b/src/library/function/function.jl @@ -20,3 +20,22 @@ function Base.promote_rule(::Type{PBF{V,Tf,S}}, ::Type{PBF{V,Tg,S}}) where {V,Tf return PBF{V,T,S} end + +# Constructors +function PBF{V,T,S}(items) where {V,T,S} + @assert Base.haslength(items) + + f = sizehint!(zero(PBF{V,T,S}), length(items)) + + for x in items + ω, c = term(PBF{V,T,S}, x) + + f[ω] += c + end + + return f +end + +function PBF{V,T,S}(args...) where {V,T,S} + return PBF{V,T,S}(args) +end diff --git a/src/library/function/operators.jl b/src/library/function/operators.jl index 1e199b6..a37eac6 100644 --- a/src/library/function/operators.jl +++ b/src/library/function/operators.jl @@ -12,7 +12,19 @@ function Base.map(φ::Function, f::PBF{V,Tf,S}) where {V,Tf,S} return g end -function Base.map!(φ::Function, f::Ff, g::Fg) where {V,Tf,Tg,Ff<:AbstractPBF{V,Tf},Fg<:AbstractPBF{V,Tg}} +function Base.map!(φ::Function, f::F) where {V,T,F<:AbstractPBF{V,T}} + for (ω, c) in f + f[ω] = φ(c) + end + + return f +end + +function Base.map!( + φ::Function, + f::Ff, + g::Fg, +) where {V,Tf,Tg,Ff<:AbstractPBF{V,Tf},Fg<:AbstractPBF{V,Tg}} sizehint!(empty!(f), length(g)) for (ω, c) in g @@ -62,7 +74,7 @@ function Base.:(-)(f::PBF{V,Tf,Sf}, g::PBF{V,Tg,Sg}) where {V,Tf,Tg,Sf,Sg} end function Base.:(-)(f::F) where {V,T,F<:AbstractPBF{V,T}} - return map(c -> -c, zero(F), f) + return map!(c -> -c, zero(F), f) end function Base.:(-)(f::PBF{V,Tf,S}, β::Tc) where {V,Tf,Tc,S} @@ -107,7 +119,7 @@ function Base.:(*)(f::PBF{V,Tf,Sf}, g::PBF{V,Tg,Sg}) where {V,Tf,Tg,Sf,Sg} for j = (i+1):n ωj, cj = k[j] - h[ωi × ωj] += 2 * ci * cj + h[ωi×ωj] += 2 * ci * cj end end @@ -116,7 +128,7 @@ function Base.:(*)(f::PBF{V,Tf,Sf}, g::PBF{V,Tg,Sg}) where {V,Tf,Tg,Sf,Sg} sizehint!(h, m * n) for (ωᵢ, cᵢ) in f, (ωⱼ, cⱼ) in g - h[ωᵢ × ωⱼ] += cᵢ * cⱼ + h[ωᵢ×ωⱼ] += cᵢ * cⱼ end return h @@ -152,6 +164,62 @@ function Base.:(/)(f::PBF{V,Tf,S}, α::Tc) where {V,Tf,Tc,S} return g end +# Arithmetic: '^' +function Base.:(^)(f::F, n::Integer) where {V,T,F<:AbstractPBF{V,T}} + if n < 0 + throw(DivideError()) + elseif n == 0 + return one(F) + elseif n == 1 + return copy(f) + elseif n == 2 + return f * f + else + g = f * f + + if iseven(n) + return g^(n ÷ 2) + else + return g^(n ÷ 2) * f + end + end +end + +# Arithmetic: Evaluation +function (f::AbstractPBF{V,T})(x::Dict{V,U}) where {V,T,U<:Integer} + g = zero(f) + + for (ω, c) in f + η = Set{V}() + + for j in ω + if haskey(x, j) + if iszero(x[j]) + c = zero(T) + break + end + else + push!(η, j) + end + end + + if !iszero(c) + g[η] += c + end + end + + return g +end + +function (f::AbstractPBF{V,T})(η::Set{V}) where {V,T} + return sum(c for (ω, c) in f if ω ⊆ η; init = zero(f)) +end + +function (f::AbstractPBF{V})(ps::Pair{V,U}...) where {V,U<:Integer} + return f(Dict{V,U}(ps...)) +end + + # Comparsion: '==' function Base.:(==)(f::AbstractPBF{V,Tf}, g::AbstractPBF{V,Tg}) where {V,Tf,Tg} return length(f) == length(g) && all(ω -> g[ω] == f[ω], keys(f)) diff --git a/src/library/function/setdict/operators.jl b/src/library/function/setdict/operators.jl deleted file mode 100644 index d9d48cf..0000000 --- a/src/library/function/setdict/operators.jl +++ /dev/null @@ -1,137 +0,0 @@ -# Arithmetic: (+) -function Base.:(+)(f::DictFunction{V,T}, g::DictFunction{V,T}) where {V,T} - h = copy(f) - - for (ω, c) in g - h[ω] += c - end - - return h -end - -function Base.:(+)(f::DictFunction{V,T}, c::T) where {V,T} - if iszero(c) - return copy(f) - else - g = copy(f) - - g[nothing] += c - - return g - end -end - -Base.:(+)(f::DictFunction{V,T}, c) where {V,T} = +(f, convert(T, c)) -Base.:(+)(c, f::DictFunction) = +(f, c) - -# Arithmetic: (-) -function Base.:(-)(f::DictFunction{V,T}) where {V,T} - return DictFunction{V,T}(Dict{Set{V},T}(ω => -c for (ω, c) in f)) -end - -function Base.:(-)(f::DictFunction{V,T}, g::DictFunction{V,T}) where {V,T} - h = copy(f) - - for (ω, c) in g - h[ω] -= c - end - - return h -end - -function Base.:(-)(f::DictFunction{V,T}, c::T) where {V,T} - if iszero(c) - return copy(f) - else - g = copy(f) - - g[nothing] -= c - - return g - end -end - -function Base.:(-)(c::T, f::DictFunction{V,T}) where {V,T} - g = -f - - if !iszero(c) - g[nothing] += c - end - - return g -end - -Base.:(-)(c, f::DictFunction{V,T}) where {V,T} = -(convert(T, c), f) -Base.:(-)(f::DictFunction{V,T}, c) where {V,T} = -(f, convert(T, c)) - - - -# Arithmetic: (/) -function Base.:(/)(f::DictFunction{V,T}, a::T) where {V,T} - if iszero(a) - throw(DivideError()) - else - return DictFunction{V,T}(Dict(ω => c / a for (ω, c) in f)) - end -end - -Base.:(/)(f::DictFunction{V,T}, a) where {V,T} = /(f, convert(T, a)) - -# Arithmetic: (^) -function Base.:(^)(f::DictFunction{V,T}, n::Integer) where {V,T} - if n < 0 - throw(DivideError()) - elseif n == 0 - return one(DictFunction{V,T}) - elseif n == 1 - return copy(f) - elseif n == 2 - return f * f - else - g = f * f - - if iseven(n) - return g^(n ÷ 2) - else - return g^(n ÷ 2) * f - end - end -end - -# Arithmetic: Evaluation -function (f::DictFunction{V,T})(x::Dict{V,U}) where {V,T,U<:Integer} - g = DictFunction{V,T}() - - for (ω, c) in f - η = Set{V}() - - for j in ω - if haskey(x, j) - if iszero(x[j]) - c = zero(T) - break - end - else - push!(η, j) - end - end - - if !iszero(c) - g[η] += c - end - end - - return g -end - -function (f::DictFunction{V,T})(η::Set{V}) where {V,T} - return sum(c for (ω, c) in f if ω ⊆ η; init = zero(T)) -end - -function (f::DictFunction{V})(x::Pair{V,U}...) where {V,U<:Integer} - return f(Dict{V,U}(x...)) -end - -function (f::DictFunction{V})() where {V} - return f(Dict{V,Int}()) -end diff --git a/src/library/function/setdict/setdict.jl b/src/library/function/setdict/setdict.jl index fb00973..79d7b6a 100644 --- a/src/library/function/setdict/setdict.jl +++ b/src/library/function/setdict/setdict.jl @@ -1,27 +1,38 @@ -const SetDict{V,T} = Dict{Set{V},T} +const SetDict{V,T} = Dict{Set{V},T} +const SetDictPBF{V,T} = PBF{V,T,SetDict{V,T}} # Constructors function PBF(Φ::S) where {V,T,S<:SetDict{V,T}} return PBF{V,T,S}(SetDict{V,T}(ω => c for (ω, c) in Φ if !iszero(c))) end -function Base.zero(::Type{PBF{V,T,S}}) where {V,T,S<:SetDict{V,T}} - return PBF{V,T,S}(SetDict{V,T}()) +function Base.zero(::Type{SetDictPBF{V,T}}) where {V,T} + return SetDictPBF{V,T}(SetDict{V,T}()) end -function Base.one(::Type{PBF{V,T,S}}) where {V,T,S<:SetDict{V,T}} - return PBF{V,T,S}(SetDict{V,T}(Set{V}() => one(T))) +function Base.one(::Type{SetDictPBF{V,T}}) where {V,T} + return SetDictPBF{V,T}(SetDict{V,T}(Set{V}() => one(T))) end # Type promotion -function Base.promote_rule(::Type{PBF{V,Tf,SetDict{V,Tf}}}, ::Type{PBF{V,Tg,S}}) where {V,Tf,Tg,S} +function Base.promote_rule( + ::Type{PBF{V,Tf,SetDict{V,Tf}}}, + ::Type{PBF{V,Tg,S}}, +) where {V,Tf,Tg,S} T = promote_type(Tf, Tg) - return PBF{V,T,SetDict{V,T}} + return SetDictPBF{V,T} end # Term parser -function term(Φ::Type{SetDict{V,T}}, ω, c) where {V,T} +term(Φ::Type{SetDictPBF{V,T}}, v::V) where {V,T} = (term_head(Φ, v) => one(T)) +term(Φ::Type{SetDictPBF{V,T}}, v::Nothing) where {V,T} = (term_head(Φ, v) => one(T)) +term(Φ::Type{SetDictPBF{V,T}}, v::Tuple{Vararg{V}}) where {V,T} = (term_head(Φ, v) => one(T)) +term(Φ::Type{SetDictPBF{V,T}}, v::AbstractVector{V}) where {V,T} = (term_head(Φ, v) => one(T)) +term(Φ::Type{SetDictPBF{V,T}}, v::AbstractSet{V}) where {V,T} = (term_head(Φ, v) => one(T)) +term(Φ::Type{SetDictPBF{V,_}}, x::Any) where {V,_} = (Set{V}() => term_tail(Φ, x)) + +function term(Φ::Type{SetDictPBF{V,T}}, (ω, c)::Pair{<:Any,<:Any}) where {V,T} c_ = term_tail(Φ, c) if iszero(c_) @@ -33,18 +44,18 @@ function term(Φ::Type{SetDict{V,T}}, ω, c) where {V,T} end end -term_head(::Type{PBF{V,_,SetDict{V,_}}}, ω::Set{V}) where {V,_} = ω -term_head(::Type{PBF{V,_,SetDict{V,_}}}, ::Nothing) where {V,_} = Set{V}() -term_head(::Type{PBF{V,_,SetDict{V,_}}}, ω::V) where {V,_} = Set{V}([ω]) -term_head(::Type{PBF{V,_,SetDict{V,_}}}, ω::AbstractSet{V}) where {V,_} = Set{V}(ω) -term_head(::Type{PBF{V,_,SetDict{V,_}}}, ω::AbstractVector{V}) where {V,_} = Set{V}(ω) -term_head(::Type{PBF{V,_,SetDict{V,_}}}, ω::NTuple{N,V}) where {N,V,_} = Set{V}(ω) +term_head(::Type{SetDictPBF{V,_}}, ω::Set{V}) where {V,_} = ω +term_head(::Type{SetDictPBF{V,_}}, ::Nothing) where {V,_} = Set{V}() +term_head(::Type{SetDictPBF{V,_}}, ω::V) where {V,_} = Set{V}([ω]) +term_head(::Type{SetDictPBF{V,_}}, ω::AbstractSet{V}) where {V,_} = Set{V}(ω) +term_head(::Type{SetDictPBF{V,_}}, ω::AbstractVector{V}) where {V,_} = Set{V}(ω) +term_head(::Type{SetDictPBF{V,_}}, ω::NTuple{N,V}) where {N,V,_} = Set{V}(ω) -term_tail(::Type{PBF{_,T,SetDict{_,T}}}, c::T) where {_,T} = c -term_tail(::Type{PBF{_,T,SetDict{_,T}}}, c) where {_,T} = convert(T, c) +term_tail(::Type{SetDictPBF{_,T}}, x::T) where {_,T} = x +term_tail(::Type{SetDictPBF{_,T}}, x) where {_,T} = convert(T, x) # Copy -function Base.copy!(f::PBF{V,T,SetDict{V,T}}, g::PBF{V,T,SetDict{V,T}}) where {V,T} +function Base.copy!(f::SetDictPBF{V,T}, g::SetDictPBF{V,T}) where {V,T} sizehint!(f, length(g)) copy!(f.Φ, g.Φ) @@ -52,46 +63,56 @@ function Base.copy!(f::PBF{V,T,SetDict{V,T}}, g::PBF{V,T,SetDict{V,T}}) where {V return f end -function Base.copy(f::F) where {V,T,F<:PBF{V,T,SetDict{V,T}}} +function Base.copy(f::F) where {V,T,F<:SetDictPBF{V,T}} return copy!(zero(F), f) end -function Base.sizehint!(f::PBF{V,T,SetDict{V,T}}, n::Integer) where {V,T} +function Base.sizehint!(f::SetDictPBF{V,T}, n::Integer) where {V,T} sizehint!(f.Φ, n) return f end # Iterator & Length -Base.length(f::PBF{V,T,SetDict{V,T}}) where {V,T} = length(f.Φ) -Base.iterate(f::PBF{V,T,SetDict{V,T}}) where {V,T} = iterate(f.Φ) -Base.iterate(f::PBF{V,T,SetDict{V,T}}, i::Integer) where {V,T} = iterate(f.Φ, i) +Base.length(f::SetDictPBF{V,T}) where {V,T} = length(f.Φ) +Base.iterate(f::SetDictPBF{V,T}) where {V,T} = iterate(f.Φ) +Base.iterate(f::SetDictPBF{V,T}, i::Integer) where {V,T} = iterate(f.Φ, i) + +Base.collect(f::SetDictPBF{V,T}) where {V,T} = collect(f.Φ) # Emptiness -Base.empty!(f::PBF{V,T,SetDict{V,T}}) where {V,T} = empty!(f.Φ) -Base.isempty(f::PBF{V,T,SetDict{V,T}}) where {V,T} = isempty(f.Φ) +Base.empty!(f::SetDictPBF{V,T}) where {V,T} = empty!(f.Φ) +Base.isempty(f::SetDictPBF{V,T}) where {V,T} = isempty(f.Φ) # Dictionary interface -Base.keys(f::PBF{V,T,SetDict{V,T}}) where {V,T} = keys(f.Φ) -Base.values(f::PBF{V,T,SetDict{V,T}}) where {V,T} = values(f.Φ) +Base.keys(f::SetDictPBF{V,T}) where {V,T} = keys(f.Φ) +Base.values(f::SetDictPBF{V,T}) where {V,T} = values(f.Φ) -# Base.map!(φ::Function, f::PBF{V,T,SetDict{V,T}}) where {V,T} = map!(φ, values(f)) +# Base.map!(φ::Function, f::SetDictPBF{V,T}) where {V,T} = map!(φ, values(f)) # Broadcast as scalar Base.broadcastable(f::PBF{V,T,S}) where {V,T,S} = Ref(f) # Indexing - in -function Base.haskey(f::F, ω) where {V,_,F<:PBF{V,_,SetDict{V,_}}} +function Base.haskey(f::F, ω) where {V,_,F<:SetDictPBF{V,_}} return haskey(f.Φ, term_head(F, ω)) end +function Base.haskey(f::F, ω...) where {V,_,F<:SetDictPBF{V,_}} + return haskey(f, reduce(varmul, term_head.(F, ω))) +end + # Indexing - get -function Base.getindex(f::F, ω) where {V,T,F<:PBF{V,T,SetDict{V,T}}} +function Base.getindex(f::F, ω) where {V,T,F<:SetDictPBF{V,T}} return get(f.Φ, term_head(F, ω), zero(T)) end +function Base.getindex(f::F, ω...) where {V,T,F<:SetDictPBF{V,T}} + return getindex(f, reduce(varmul, term_head.(F, ω))) +end + # Indexing - set -function Base.setindex!(f::F, c, ω) where {V,T,F<:PBF{V,T,SetDict{V,T}}} +function Base.setindex!(f::F, c, ω) where {V,T,F<:SetDictPBF{V,T}} ω_ = term_head(F, ω) c_ = term_tail(F, c) @@ -104,21 +125,29 @@ function Base.setindex!(f::F, c, ω) where {V,T,F<:PBF{V,T,SetDict{V,T}}} return c_ end -function Base.delete!(f::F, ω) where {V,T,F<:PBF{V,T,SetDict{V,T}}} +function Base.setindex!(f::F, c, ω...) where {V,T,F<:SetDictPBF{V,T}} + return setindex!(f, c, reduce(varmul, term_head.(F, ω))) +end + +function Base.delete!(f::F, ω) where {V,T,F<:SetDictPBF{V,T}} delete!(f.Φ, term_head(F, ω)) return f end +function Base.delete!(f::F, ω...) where {V,T,F<:SetDictPBF{V,T}} + return delete!(f, reduce(varmul, term_head.(F, ω))) +end + -function isscalar(f::PBF{V,T,SetDict{V,T}}) where {V,T} +function isscalar(f::SetDictPBF{V,T}) where {V,T} return isempty(f) || (length(f) == 1 && haskey(f, nothing)) end -function Base.iszero(f::PBF{V,T,SetDict{V,T}}) where {V,T} +function Base.iszero(f::SetDictPBF{V,T}) where {V,T} return isempty(f) end -function Base.isone(f::PBF{V,T,SetDict{V,T}}) where {V,T} +function Base.isone(f::SetDictPBF{V,T}) where {V,T} return isscalar(f) && isone(f[nothing]) end diff --git a/src/library/mod2linsolve.jl b/src/library/mod2linsolve.jl index d2e2c5e..608438f 100644 --- a/src/library/mod2linsolve.jl +++ b/src/library/mod2linsolve.jl @@ -50,6 +50,10 @@ end function _mod2_numsolutions!(A::AbstractMatrix{U}, b::AbstractVector{U}) where {U<:Integer} _mod2_elimination!(A, b) + return _mod2_numsolutions(A, b) +end + +function _mod2_numsolutions(A::AbstractMatrix{U}, b::AbstractVector{U}) where {U<:Integer} m, n = size(A) # start with full rank @@ -57,7 +61,7 @@ function _mod2_numsolutions!(A::AbstractMatrix{U}, b::AbstractVector{U}) where { @inbounds for i = 1:m if iszero(@view(A[i, :])) # all-zero row encountered - if !iszero(b[i]) # no solutions + if !iszero(b[i]) # no solutions return 0 end diff --git a/src/library/quadratization/abstract.jl b/src/library/quadratization/abstract.jl index 32639fd..be81a23 100644 --- a/src/library/quadratization/abstract.jl +++ b/src/library/quadratization/abstract.jl @@ -72,7 +72,7 @@ function quadratize!(::Function, f::AbstractPBF, ::Nothing) end function quadratize!(f::AbstractPBF, quad::Union{Quadratization,Nothing} = Quadratization(INFER())) - return quadratize!(auxgen(f), f, quad) + return quadratize!(vargen(f; start = -1, step = -1), f, quad) end function quadratize(aux, f::AbstractPBF, quad::Union{Quadratization,Nothing} = Quadratization(INFER())) diff --git a/src/library/subscript.jl b/src/library/subscript.jl new file mode 100644 index 0000000..d228742 --- /dev/null +++ b/src/library/subscript.jl @@ -0,0 +1,7 @@ +function _subscript(i::Integer) + if i < 0 + return "₋$(_subscript(abs(i)))" + else + return join(reverse!(digits(i)) .+ Char(0x2080)) + end +end diff --git a/src/library/synthesis/not_all_equal_3sat.jl b/src/library/synthesis/not_all_equal_3sat.jl index 092140c..1845cd9 100644 --- a/src/library/synthesis/not_all_equal_3sat.jl +++ b/src/library/synthesis/not_all_equal_3sat.jl @@ -5,7 +5,12 @@ Generates Not-all-equal 3-SAT problem with ``m`` variables and ``n`` clauses. """ function not_all_equal_3sat end -function not_all_equal_3sat(rng, ::Type{F}, n::Integer, m::Integer) where {V,T,F<:AbstractPBF{V,T}} +function not_all_equal_3sat( + rng, + ::Type{F}, + n::Integer, + m::Integer, +) where {V,T,F<:AbstractPBF{V,T}} J = Dict{Tuple{Int,Int},T}() C = BitSet(1:n) @@ -19,8 +24,8 @@ function not_all_equal_3sat(rng, ::Type{F}, n::Integer, m::Integer) where {V,T,F for j = 1:3 c[j] = pop!(C, rand(rng, C)) end - - s .= rand(rng, (-1,+1), 3) + + s .= rand(rng, (-1, +1), 3) for i = 1:3, j = (i+1):3 # i < j x = (c[i], c[j]) @@ -36,17 +41,22 @@ function not_all_equal_3sat(rng, ::Type{F}, n::Integer, m::Integer) where {V,T,F f = sizehint!(zero(F), length(J) + n) for ((i, j), c) in J - f[i, j] += 4c - f[i] -= 2c - f[j] -= 2c + xi = varmap(V, i) + xj = varmap(V, j) + + f[xi, xj] += 4c + f[xi] -= 2c + f[xj] -= 2c f[nothing] += c end - x = nothing # no planted solutions - - return (f, x) + return (f, Dict{V,Int}[]) # no planted solutions end -function not_all_equal_3sat(::Type{F}, n::Integer, m::Integer) where {V,T,F<:AbstractPBF{V,T}} +function not_all_equal_3sat( + ::Type{F}, + n::Integer, + m::Integer, +) where {V,T,F<:AbstractPBF{V,T}} return not_all_equal_3sat(Random.GLOBAL_RNG, F, n, m) end diff --git a/src/library/synthesis/regular_xorsat.jl b/src/library/synthesis/regular_xorsat.jl index 35c4f0a..3d2060c 100644 --- a/src/library/synthesis/regular_xorsat.jl +++ b/src/library/synthesis/regular_xorsat.jl @@ -13,7 +13,7 @@ function k_regular_k_xorsat( idx = zeros(Int, n, k) for j = 1:k, i = 1:n - idx[i,j] = i + idx[i, j] = i end A = Matrix{Int}(undef, n, n) @@ -47,13 +47,12 @@ function k_regular_k_xorsat( # Convert to boolean # s = 2x - 1 # ∑ᵢ (-1)^cᵢ ∏ⱼ (2xᵢⱼ - 1) = ∑ᵢ (-1)^c[i] (2xᵢ₁ - 1) ⋯ (2xᵢₖ - 1) = 2^ - f = sum((-1.0)^c[i] * prod([F(idx[i,j] => 2.0, -1.0) for j = 1:k]) for i = 1:n) + f = sum((-1.0)^c[i] * prod([F(idx[i, j] => 2.0, -1.0) for j = 1:k]) for i = 1:n) quadratize!(f, quad) - x = nothing # no planted solutions - - return (f, x) + # TODO: Return planted solution + return (f, Dict{V,Int}[]) # no planted solutions end end @@ -88,7 +87,9 @@ function r_regular_k_xorsat( return k_regular_k_xorsat(rng, F, n, k; quad) else # NOTE: This might involve solving Sudoku! - error("A method for generating r-regular k-XORSAT where r ≂̸ k is not implemented (yet).") + error( + "A method for generating r-regular k-XORSAT where r ≂̸ k is not implemented (yet).", + ) return nothing end diff --git a/src/library/synthesis/sherrington_kirkpatrick.jl b/src/library/synthesis/sherrington_kirkpatrick.jl index 556f9ea..89ad1fe 100644 --- a/src/library/synthesis/sherrington_kirkpatrick.jl +++ b/src/library/synthesis/sherrington_kirkpatrick.jl @@ -8,8 +8,16 @@ f^{(n)}_{\textrm{SK}}(\mathbf{x}) = \sum_{i = 1}^{n} \sum_{j = i + 1}^{n} J_{i, where ``J_{i, j} \sim \mathcal{N}(0, 1)``. """ -function sherrington_kirkpatrick(rng, ::Type{F}, n::Integer; μ::T = zero(T), σ::T = one(T)) where {V,T,F<:AbstractPBF{V,T}} - J = Dict{Tuple{Int,Int},T}((i, j) => (σ * randn(rng, T) + μ) for i = 1:n for j = (i+1):n) +function sherrington_kirkpatrick( + rng, + ::Type{F}, + n::Integer; + μ::T = zero(T), + σ::T = one(T), +) where {V,T,F<:AbstractPBF{V,T}} + J = Dict{Tuple{Int,Int},T}( + (i, j) => (σ * randn(rng, T) + μ) for i = 1:n for j = (i+1):n + ) # Convert to boolean # let s_i = 2x_i - 1 @@ -18,17 +26,23 @@ function sherrington_kirkpatrick(rng, ::Type{F}, n::Integer; μ::T = zero(T), σ f = sizehint!(zero(F), length(J) + n) for ((i, j), c) in J - f[i, j] += 4c - f[i] -= 2c - f[j] -= 2c + xi = varmap(V, i) + xj = varmap(V, j) + + f[xi, xj] += 4c + f[xi] -= 2c + f[xj] -= 2c f[nothing] += c end - x = nothing # no planted solutions - - return (f, x) + return (f, Dict{V,Int}[]) # no planted solutions end -function sherrington_kirkpatrick(::Type{F}, n::Integer; μ::T = zero(T), σ::T = one(T)) where {V,T,F<:AbstractPBF{V,T}} +function sherrington_kirkpatrick( + ::Type{F}, + n::Integer; + μ::T = zero(T), + σ::T = one(T), +) where {V,T,F<:AbstractPBF{V,T}} return sherrington_kirkpatrick(Random.GLOBAL_RNG, F, n; μ, σ) end diff --git a/src/library/synthesis/wishart.jl b/src/library/synthesis/wishart.jl index 698a94d..c6df9b0 100644 --- a/src/library/synthesis/wishart.jl +++ b/src/library/synthesis/wishart.jl @@ -1,11 +1,12 @@ @doc raw""" wishart(rng, n::Integer, m::Integer) -Generate a ``K_{n}`` (complete graph) Ising weight matrix ``J`` with the ``(+)^{n}`` state as a planted GS. +Generate a ``K_{n}`` (complete graph) Ising weight matrix ``J`` with the +``\mathbf{1} \in {\pm 1}^{n}`` state as a planted ground state. -Diagonal of ``J`` is zero. +The main diagonal of ``J`` is zero. -Hamiltonian is zero field, i.e: +The Hamiltonian is zero field, i.e, ```math E(\mathbf{s}) = -\frac{1}{2} \mathbf{s}' J \mathbf{s} @@ -20,17 +21,24 @@ Alternatively, can even replace the Gaussian with a bounded range uniform discre """ function wishart end -function wishart(rng, ::Type{F}, n::Integer, m::Integer = 1; discretize_bonds::Bool = false, precision = nothing) where {V,T,F<:AbstractPBF{V,T}} - # Plants the FM GS - t = ones(n, 1) +function wishart( + rng, + ::Type{F}, + n::Integer, + m::Integer = 1; + discretize_bonds::Bool = false, + precision = nothing, +) where {V,T,F<:AbstractPBF{V,T}} + # Plants the FM ground state + s = ones(Int, n) # Sample correlated Gaussian with covariance matrix sigma # NOTE: rank(sigma) = n - 1 - σ = (n * I - (t * t')) / (n - 1) - s = √((n - 1) / n) * σ + σ = (n * I - (s * s')) / (n - 1) + θ = √((n - 1) / n) * σ if discretize_bonds - R = rand(rng, (-1,+1), n, m) + R = rand(rng, (-1, +1), n, m) else R = randn(rng, n, m) @@ -39,17 +47,17 @@ function wishart(rng, ::Type{F}, n::Integer, m::Integer = 1; discretize_bonds::B end end - W = s'R + W = θ'R J = W * W' - + for i = 1:n - J[i,i] = zero(T) + J[i, i] = zero(T) end if discretize_bonds - map!(x -> round((n^2 - n^3) * x; digits = precision), J, J) + map!(x -> round((n^2 - n^3) * x; digits = something(precision, 0)), J, J) else - map!(x -> round((n - n^2) * x; digits = precision), J, J) + map!(x -> round((n - n^2) * x; digits = something(precision, 0)), J, J) end # Convert to boolean @@ -59,19 +67,28 @@ function wishart(rng, ::Type{F}, n::Integer, m::Integer = 1; discretize_bonds::B f = sizehint!(zero(F), n^2 ÷ 2) for i = 1:n, j = 1:n + xi = varmap(V, i) + xj = varmap(V, j) + c = J[i, j] - f[i, j] += 4c - f[i] -= 2c - f[j] -= 2c + f[xi, xj] += 4c + f[xi] -= 2c + f[xj] -= 2c f[nothing] += c end - x = [t] # planted solution + x = Dict{V,Int}[Dict{V,Int}(varmap(V, i) => (s[i] + 1) ÷ 2 for i = 1:n)] - return (f, x) + return (f, x) # one planted solution! Yeah! end -function wishart(::Type{F}, n::Integer, m::Integer = 1; discretize_bonds::Bool = false, precision = nothing) where {V,T,F<:AbstractPBF{V,T}} - return wishart(GLOBAL_RNG, F, n, m; discretize_bonds, precision) +function wishart( + ::Type{F}, + n::Integer, + m::Integer = 1; + discretize_bonds::Bool = false, + precision = nothing, +) where {V,T,F<:AbstractPBF{V,T}} + return wishart(Random.GLOBAL_RNG, F, n, m; discretize_bonds, precision) end diff --git a/src/library/variable/vargen.jl b/src/library/variable/vargen.jl index 2d6589c..a8bd76d 100644 --- a/src/library/variable/vargen.jl +++ b/src/library/variable/vargen.jl @@ -1,41 +1,36 @@ -function auxgen(::AbstractPBF{Symbol,T}; name::Symbol = :aux) where {T} - counter = Ref{Int}(0) +struct VariableGenerator{V} + counter::Ref{Int} + step::Int - function aux(n::Union{Integer,Nothing} = nothing) - if isnothing(n) - return first(aux(1)) - else - return [Symbol("$(name)_$(counter[] += 1)") for _ in 1:n] - end + function VariableGenerator{V}(start::Integer, step::Integer) where {V} + return new{V}(Ref(start), step) end +end - return aux +function VariableGenerator(::Type{V}; start::Integer = 1, step::Integer = 1) where {V} + return VariableGenerator{V}(start, step) end -function auxgen(::AbstractPBF{V,T}; name::AbstractString = "aux") where {V<:AbstractString,T} - counter = Ref{Int}(0) +function __next(vg::VariableGenerator{V}) where {V} + i = vg.counter[] - function aux(n::Union{Integer,Nothing} = nothing) - if isnothing(n) - return first(aux(1)) - else - return ["$(name)_$(counter[] += 1)" for _ in 1:n] - end - end + vg.counter[] += vg.step - return aux + return varmap(V, i) end -function auxgen(::AbstractPBF{V,T}; start::V = V(0), step::V = V(-1)) where {V<:Integer,T} - counter = [start] +function vargen(::Type{V}; start::Integer = 1, step::Integer = 1) where {V} + vg = VariableGenerator{V}(start, step) - function aux(n::Union{Integer,Nothing} = nothing) + return (n::Union{Integer,Nothing} = nothing) -> begin if isnothing(n) - return first(aux(1)) + return __next(vg)::V else - return [(counter[] += step) for _ in 1:n] + return [__next(vg) for _ = 1:n]::Vector{V} end end +end - return aux +function vargen(::AbstractPBF{V}; start::Integer = 1, step::Integer = 1) where {V} + return vargen(V; start, step) end diff --git a/src/library/variable/varmap.jl b/src/library/variable/varmap.jl index f9a6edd..7ac9044 100644 --- a/src/library/variable/varmap.jl +++ b/src/library/variable/varmap.jl @@ -1,12 +1,8 @@ -# Fallback implementation of varmap for types that do not implement it +# Fallback implementation function varmap(::Type{V}, i::Integer) where {V} return V(i) end -function varmap(::Type{V}, i::Integer, x::AbstractString = "x") where {V<:AbstractString} - return V("$(x)_$(i)") -end - -function varmap(::Type{Symbol}, i::Integer, x::Symbol = :x) - return Symbol("$(x)_$(i)") +function varmap(::Type{V}, i::Integer) where {V<:Union{AbstractString,Symbol}} + return V("x$(_subscript(i))") end diff --git a/src/library/variable/varset.jl b/src/library/variable/varset.jl new file mode 100644 index 0000000..27ca752 --- /dev/null +++ b/src/library/variable/varset.jl @@ -0,0 +1,3 @@ +struct VarSet{V,S<:Set{V}} <: AbstractSet{V} + set::S +end diff --git a/src/library/variable/varshow.jl b/src/library/variable/varshow.jl index c6ac1d4..82dfd1c 100644 --- a/src/library/variable/varshow.jl +++ b/src/library/variable/varshow.jl @@ -1,11 +1,3 @@ -function _subscript(i::Integer) - if i < 0 - return "₋$(_subscript(abs(i)))" - else - return join(reverse!(digits(i)) .+ Char(0x2080)) - end -end - varshow(io::IO, v::V) where {V} = show(io, varshow(v)) varshow(v::Integer, x::AbstractString = "x") = "$(x)$(_subscript(v))" varshow(v::V) where {V<:Union{Symbol,AbstractString}} = string(v) @@ -44,8 +36,6 @@ function Base.show(io::IO, ::MIME"text/plain", f::AbstractPBF{V,T}) where {V,T} return nothing end - - # function Base.show(io::IO, f::VectorFunction{V,T}) where {V,T} # join(io, f.Ω, " + ") # end diff --git a/test/runtests.jl b/test/runtests.jl index 5ad6a95..2cba1af 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,7 +3,7 @@ using PseudoBooleanOptimization const PBO = PseudoBooleanOptimization # Test assets, i.e., test examples and mockups -include("assets/assets.jl") +# include("assets/assets.jl") # Unit tests include("unit/unit.jl") diff --git a/test/unit/constructors.jl b/test/unit/constructors.jl new file mode 100644 index 0000000..cce7cb3 --- /dev/null +++ b/test/unit/constructors.jl @@ -0,0 +1,86 @@ +function test_constructors() + @testset "⊛ Constructors" verbose = true begin + test_setdict_constructors() + end + + return nothing +end + +function test_setdict_constructors() + @testset "∴ SetDict" begin + let S = Symbol + T = Float64 + F = PBO.SetDictPBF{S,T} + + @test F(0) == F(Dict{Set{S},T}()) + @test zero(F) == F(Dict{Set{S},T}()) + + let f = F( + nothing => 0.5, + :x => 1.0, + :y => 1.0, + :z => 1.0, + [:x, :y] => -2.0, + [:x, :z] => -2.0, + [:y, :z] => -2.0, + [:x, :y, :z] => 3.0, + ) + g = F( + Dict{Set{S},T}( + Set{S}() => 0.5, + Set{S}([:x]) => 1.0, + Set{S}([:y]) => 1.0, + Set{S}([:z]) => 1.0, + Set{S}([:x, :y]) => -2.0, + Set{S}([:x, :z]) => -2.0, + Set{S}([:y, :z]) => -2.0, + Set{S}([:x, :y, :z]) => 3.0, + ), + ) + @test f == g + end + + @test F(1.0) == F(Dict{Set{S},T}(Set{S}() => 1.0)) + @test one(F) == F(Dict{Set{S},T}(Set{S}() => 1.0)) + + let f = F([:x, :y, :z, :w, nothing]) + g = F( + Dict{Set{S},T}( + Set{S}([:x]) => 1.0, + Set{S}([:y]) => 1.0, + Set{S}([:z]) => 1.0, + Set{S}([:w]) => 1.0, + Set{S}() => 1.0, + ), + ) + @test f == g + end + + let f = F((nothing => 0.5), :x, [:x, :y] => -2.0) + g = F( + Dict{Set{S},T}(Set{S}() => 0.5, Set{S}([:x]) => 1.0, Set{S}([:x, :y]) => -2.0), + ) + @test f == g + end + + let f = F(nothing => 0.5, :y, [:x, :y] => 2.0) + g = F( + Dict{Set{S},T}(Set{S}() => 0.5, Set{S}([:y]) => 1.0, Set{S}([:x, :y]) => 2.0), + ) + @test f == g + end + + let f = F(nothing, :z => -1.0) + g = F(Set{S}() => 1.0, Set{S}([:z]) => -1.0) + @test f == g + end + + let f = F(S[] => 0.0, Set{S}([:x, :y, :z]) => 3.0) + g = F(Set{S}() => 0.0, Set{S}([:x, :y, :z]) => 3.0) + @test f == g + end + end + end + + return nothing +end \ No newline at end of file diff --git a/test/unit/function.jl b/test/unit/function.jl deleted file mode 100644 index a8962bd..0000000 --- a/test/unit/function.jl +++ /dev/null @@ -1,9 +0,0 @@ -function test_constructors() - @testset "Constructors" begin - for (x, y) in Assets.PBF_CONSTRUCTOR_LIST - @test x == y - end - end - - return nothing -end \ No newline at end of file diff --git a/test/unit/term_parser.jl b/test/unit/term_parser.jl new file mode 100644 index 0000000..e565bd5 --- /dev/null +++ b/test/unit/term_parser.jl @@ -0,0 +1,33 @@ +function test_term_parser() + @testset "⊛ Term Parser" verbose = true begin + @testset "∴ SetDict" begin + let F = PBO.PBF{Symbol,Float64,PBO.SetDict{Symbol,Float64}} + @test PBO.term(F, 1) == (Set{Symbol}() => 1.0) + @test PBO.term(F, 1.0) == (Set{Symbol}() => 1.0) + @test PBO.term(F, nothing => 1.0) == (Set{Symbol}() => 1.0) + @test PBO.term(F, 1 // 1) == (Set{Symbol}() => 1.0) + @test PBO.term(F, :x) == (Set{Symbol}([:x]) => 1.0) + @test PBO.term(F, (:x, :x)) == (Set{Symbol}([:x]) => 1.0) + @test PBO.term(F, (:x, :y)) == (Set{Symbol}([:x, :y]) => 1.0) + @test PBO.term(F, [:x, :y, :z]) == (Set{Symbol}([:x, :y, :z]) => 1.0) + @test PBO.term(F, [:x, :y, :z]) == (Set{Symbol}([:x, :y, :z]) => 1.0) + @test PBO.term(F, [:x, :z] => -3.0) == (Set{Symbol}([:x, :z]) => -3.0) + @test PBO.term(F, [:x, :z] => 0.0) == (Set{Symbol}() => 0.0) + @test_throws Exception PBO.term(F, 1.0 => 1.0) + end + + let F = PBO.PBF{Int,Int,PBO.SetDict{Int,Int}} + @test PBO.term(F, 1) == (Set{Int}([1]) => 1) + @test PBO.term(F, 2) == (Set{Int}([2]) => 1) + @test PBO.term(F, 1.0) == (Set{Int}() => 1) + @test PBO.term(F, 2.0) == (Set{Int}() => 2) + @test PBO.term(F, nothing => 1.0) == (Set{Int}() => 1) + @test PBO.term(F, 1 // 1) == (Set{Int}() => 1) + @test_throws Exception PBO.term(F, :x) + @test_throws Exception PBO.term(F, 1.0 => 1) + end + end + end + + return nothing +end diff --git a/test/unit/unit.jl b/test/unit/unit.jl index 7587559..847cd20 100644 --- a/test/unit/unit.jl +++ b/test/unit/unit.jl @@ -1,18 +1,20 @@ -include("function.jl") -include("operators.jl") -include("calculus.jl") -include("discretization.jl") -include("quadratization.jl") -include("print.jl") +include("term_parser.jl") +include("constructors.jl") +# include("operators.jl") +# include("calculus.jl") +# include("discretization.jl") +# include("quadratization.jl") +# include("print.jl") function test_unit() - @testset "Pseudo-Boolean Functions" verbose = true begin + @testset "□ Unit Tests" verbose = true begin + test_term_parser() test_constructors() - test_operators() - test_evaluation() - test_calculus() - test_discretization() - test_quadratization() + # test_operators() + # test_evaluation() + # test_calculus() + # test_discretization() + # test_quadratization() # test_print() end From feeb5b55c92d8a2668e8e01f5c072c7cd4038103 Mon Sep 17 00:00:00 2001 From: pedromxavier Date: Sun, 15 Oct 2023 16:46:29 -0400 Subject: [PATCH 6/9] Update PBO interface --- Project.toml | 13 +-- src/PseudoBooleanOptimization.jl | 19 +++- src/interface/function.jl | 17 +++- src/library/function/abstract.jl | 2 +- src/library/function/function.jl | 5 + src/library/function/setdict/setdict.jl | 4 +- src/library/{variable => variables}/vargen.jl | 0 src/library/{variable => variables}/varlt.jl | 10 +- src/library/{variable => variables}/varmap.jl | 0 src/library/{variable => variables}/varmul.jl | 0 src/library/{variable => variables}/varset.jl | 0 .../{variable => variables}/varshow.jl | 2 +- test/Project.toml | 1 + test/assets/function.jl | 23 +---- test/integration/integration.jl | 32 ++++++ test/runtests.jl | 14 ++- test/unit/constructors.jl | 79 ++++++++------- test/unit/operators.jl | 98 ++++++++++++++++--- test/unit/unit.jl | 8 +- test/unit/variables.jl | 29 ++++++ 20 files changed, 252 insertions(+), 104 deletions(-) rename src/library/{variable => variables}/vargen.jl (100%) rename src/library/{variable => variables}/varlt.jl (83%) rename src/library/{variable => variables}/varmap.jl (100%) rename src/library/{variable => variables}/varmul.jl (100%) rename src/library/{variable => variables}/varset.jl (100%) rename src/library/{variable => variables}/varshow.jl (98%) create mode 100644 test/integration/integration.jl create mode 100644 test/unit/variables.jl diff --git a/Project.toml b/Project.toml index 7e42edc..4d69648 100644 --- a/Project.toml +++ b/Project.toml @@ -1,13 +1,14 @@ -name = "PseudoBooleanOptimization" -uuid = "c8fa9a04-bc42-452d-8558-dc51757be744" +name = "PseudoBooleanOptimization" +uuid = "c8fa9a04-bc42-452d-8558-dc51757be744" authors = ["pedromxavier "] -version = "0.2.0" +version = "0.1.1" [deps] -LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MutableArithmetics = "d8a4904e-b15c-11e9-3269-09a3773c0cb0" -Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" [compat] MutableArithmetics = "1.3" -julia = "1.6" +julia = "1.6" diff --git a/src/PseudoBooleanOptimization.jl b/src/PseudoBooleanOptimization.jl index a93bfda..a8f59f1 100644 --- a/src/PseudoBooleanOptimization.jl +++ b/src/PseudoBooleanOptimization.jl @@ -1,10 +1,19 @@ module PseudoBooleanOptimization +const PBO = PseudoBooleanOptimization + using Random using LinearAlgebra using MutableArithmetics const MA = MutableArithmetics +# Versioning +using TOML +const __PROJECT__ = abspath(dirname(pathof(PBO)), "..") +const __VERSION__ = VersionNumber( + getindex(TOML.parsefile(joinpath(__PROJECT__, "Project.toml")), "version"), +) + # Interface Definition include("interface/variable.jl") include("interface/function.jl") @@ -16,11 +25,11 @@ include("library/mod2linsolve.jl") include("library/relaxedgcd.jl") include("library/sortedmerge.jl") -include("library/variable/varlt.jl") -include("library/variable/vargen.jl") -include("library/variable/varmap.jl") -include("library/variable/varmul.jl") -include("library/variable/varshow.jl") +include("library/variables/varlt.jl") +include("library/variables/vargen.jl") +include("library/variables/varmap.jl") +include("library/variables/varmul.jl") +include("library/variables/varshow.jl") include("library/function/abstract.jl") include("library/function/function.jl") diff --git a/src/interface/function.jl b/src/interface/function.jl index 855b3fc..1a87e40 100644 --- a/src/interface/function.jl +++ b/src/interface/function.jl @@ -15,14 +15,23 @@ Variables ``x_j`` are boolean, thus ``f : \mathbb{B}^{n} \to \mathbb{T}``. [^Boros2002]: Endre Boros, Peter L. Hammer, **Pseudo-Boolean optimization**, *Discrete Applied Mathematics*, 2002 [{doi}](https://doi.org/10.1016/S0166-218X(01)00341-9) """ -abstract type AbstractPseudoBooleanFunction{V,T} end +abstract type AbstractFunction{V,T} end -const AbstractPBF{V,T} = AbstractPseudoBooleanFunction{V,T} +const AbstractPBF{V,T} = AbstractFunction{V,T} @doc raw""" - isscalar(f::AbstractPBF)::Bool + data(f::AbstractPBF{V,T}) + +Returns the internal representation of ``f \in \mathscr{F}``. +""" +function data end + +@doc raw""" + isconstant(f::AbstractPBF)::Bool + +Check if the given Pseudo-Boolean function `f` is a constant, i.e., if it has no variables. """ -function isscalar end +function isconstant end @doc raw""" term diff --git a/src/library/function/abstract.jl b/src/library/function/abstract.jl index 981c4a2..a5b417c 100644 --- a/src/library/function/abstract.jl +++ b/src/library/function/abstract.jl @@ -61,7 +61,7 @@ function upperbound(f::F) where {V,T,F<:AbstractPBF{V,T}} end function Base.convert(::Type{U}, f::AbstractPBF{V,T}) where {V,T,U<:Number} - if isscalar(f) + if isconstant(f) return convert(U, f[nothing]) else error("Can't convert non-scalar pseudo-Boolean function to scalar type '$U'") diff --git a/src/library/function/function.jl b/src/library/function/function.jl index 1ace2ad..fb97edb 100644 --- a/src/library/function/function.jl +++ b/src/library/function/function.jl @@ -14,6 +14,11 @@ end const PBF{V,T,S} = PseudoBooleanFunction{V,T,S} +# Internal representation +function data(f::PBF{V,T,S})::S where {V,T,S} + return f.Φ +end + # Type promotion function Base.promote_rule(::Type{PBF{V,Tf,S}}, ::Type{PBF{V,Tg,S}}) where {V,Tf,Tg,S} T = promote_type(Tf, Tg) diff --git a/src/library/function/setdict/setdict.jl b/src/library/function/setdict/setdict.jl index 79d7b6a..a6559ca 100644 --- a/src/library/function/setdict/setdict.jl +++ b/src/library/function/setdict/setdict.jl @@ -140,7 +140,7 @@ function Base.delete!(f::F, ω...) where {V,T,F<:SetDictPBF{V,T}} end -function isscalar(f::SetDictPBF{V,T}) where {V,T} +function isconstant(f::SetDictPBF{V,T}) where {V,T} return isempty(f) || (length(f) == 1 && haskey(f, nothing)) end @@ -149,5 +149,5 @@ function Base.iszero(f::SetDictPBF{V,T}) where {V,T} end function Base.isone(f::SetDictPBF{V,T}) where {V,T} - return isscalar(f) && isone(f[nothing]) + return isconstant(f) && isone(f[nothing]) end diff --git a/src/library/variable/vargen.jl b/src/library/variables/vargen.jl similarity index 100% rename from src/library/variable/vargen.jl rename to src/library/variables/vargen.jl diff --git a/src/library/variable/varlt.jl b/src/library/variables/varlt.jl similarity index 83% rename from src/library/variable/varlt.jl rename to src/library/variables/varlt.jl index e25c458..6ddd5f3 100644 --- a/src/library/variable/varlt.jl +++ b/src/library/variables/varlt.jl @@ -5,9 +5,9 @@ function varlt(u::V, v::V) where {V<:Signed} # For signed integers, the order should be as follows: # 0, 1, 2, 3, ..., -1, -2, -3, ... if sign(u) == sign(v) - return v < u + return isless(abs(u), abs(v)) else - return sign(u) < sign(v) + return isless(sign(v), sign(u)) end end @@ -33,17 +33,17 @@ function varlt(u::AbstractVector{V}, v::AbstractVector{V}) where {V} end end else - return length(u) < length(v) + return isless(length(u), length(v)) end end -function varlt(u::Set{V}, v::Set{V}) where {V} +function varlt(u::AbstractSet{V}, v::AbstractSet{V}) where {V} if length(u) == length(v) x = sort!(collect(u); alg = InsertionSort, lt = varlt) y = sort!(collect(v); alg = InsertionSort, lt = varlt) return varlt(x, y) else - return length(u) < length(v) + return isless(length(u), length(v)) end end diff --git a/src/library/variable/varmap.jl b/src/library/variables/varmap.jl similarity index 100% rename from src/library/variable/varmap.jl rename to src/library/variables/varmap.jl diff --git a/src/library/variable/varmul.jl b/src/library/variables/varmul.jl similarity index 100% rename from src/library/variable/varmul.jl rename to src/library/variables/varmul.jl diff --git a/src/library/variable/varset.jl b/src/library/variables/varset.jl similarity index 100% rename from src/library/variable/varset.jl rename to src/library/variables/varset.jl diff --git a/src/library/variable/varshow.jl b/src/library/variables/varshow.jl similarity index 98% rename from src/library/variable/varshow.jl rename to src/library/variables/varshow.jl index 82dfd1c..94f726c 100644 --- a/src/library/variable/varshow.jl +++ b/src/library/variables/varshow.jl @@ -3,7 +3,7 @@ varshow(v::Integer, x::AbstractString = "x") = "$(x)$(_subscript(v))" varshow(v::V) where {V<:Union{Symbol,AbstractString}} = string(v) function Base.show(io::IO, ::MIME"text/plain", f::AbstractPBF{V,T}) where {V,T} - if isscalar(f) + if isconstant(f) print(io, f[nothing]) return nothing diff --git a/test/Project.toml b/test/Project.toml index 0c36332..319b670 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,2 +1,3 @@ [deps] +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/assets/function.jl b/test/assets/function.jl index c342284..cc2c07d 100644 --- a/test/assets/function.jl +++ b/test/assets/function.jl @@ -59,28 +59,7 @@ const PBF_LIST = [ ] const PBF_CONSTRUCTOR_LIST = [ - (PBO.PBF{S,T}(0.0), PBO.PBF{S,T}()), - (zero(PBO.PBF{S,T}), PBO.PBF{S,T}()), - ( - f, - PBO.PBF{S,T}( - nothing => 0.5, - :x => 1.0, - :y => 1.0, - :z => 1.0, - [:x, :y] => -2.0, - [:x, :z] => -2.0, - [:y, :z] => -2.0, - [:x, :y, :z] => 3.0, - ), - ), - (g, PBO.PBF{S,T}(1.0)), - (g, one(PBO.PBF{S,T})), - (h, PBO.PBF{S,T}([:x, :y, :z, :w, nothing])), - (p, PBO.PBF{S,T}((nothing, 0.5), :x, [:x, :y] => -2.0)), - (q, PBO.PBF{S,T}(nothing => 0.5, :y, [:x, :y] => 2.0)), - (r, PBO.PBF{S,T}(nothing, :z => -1.0)), - (s, PBO.PBF{S,T}(S[] => 0.0, Set{S}([:x, :y, :z]) => 3.0)), + ] const PBF_OPERATOR_LIST = [ diff --git a/test/integration/integration.jl b/test/integration/integration.jl new file mode 100644 index 0000000..4854a11 --- /dev/null +++ b/test/integration/integration.jl @@ -0,0 +1,32 @@ +function integration_tests() + @testset "□ Integration Tests" verbose = false begin + test_qubotools() + end + + return nothing +end + +function test_dependant(pkg_name::AbstractString, dep_path::AbstractString = PBO.__PROJECT__) + Pkg.activate(; temp = true) + + Pkg.develop(; path = dep_path) + Pkg.add(pkg_name) + + Pkg.status() + + try + Pkg.test(pkg_name) + catch e + return false + end + + return true +end + +function test_qubotools() + @testset "⋆ QUBOTools.jl" begin + @test test_dependant("QUBOTools") + end + + return nothing +end diff --git a/test/runtests.jl b/test/runtests.jl index 2cba1af..384ca92 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,3 +1,4 @@ +using Pkg using Test using PseudoBooleanOptimization const PBO = PseudoBooleanOptimization @@ -8,8 +9,17 @@ const PBO = PseudoBooleanOptimization # Unit tests include("unit/unit.jl") -function main() - test_unit() +# Integration tests +include("integration/integration.jl") + +function main(; run_itegration_tests::Bool = false) + @testset "♠ PseudoBooleanOptimization.jl $(PBO.__VERSION__) Test Suite ♠" verbose = true begin + unit_tests() + + if run_itegration_tests + integration_tests() + end + end return nothing end diff --git a/test/unit/constructors.jl b/test/unit/constructors.jl index cce7cb3..84a310e 100644 --- a/test/unit/constructors.jl +++ b/test/unit/constructors.jl @@ -1,6 +1,6 @@ function test_constructors() @testset "⊛ Constructors" verbose = true begin - test_setdict_constructors() + test_setdict_constructors() end return nothing @@ -12,8 +12,10 @@ function test_setdict_constructors() T = Float64 F = PBO.SetDictPBF{S,T} - @test F(0) == F(Dict{Set{S},T}()) - @test zero(F) == F(Dict{Set{S},T}()) + @test PBO.data(F()) == Dict{Set{S},T}() + @test PBO.data(F(0)) == Dict{Set{S},T}() + @test PBO.data(F(0.0)) == Dict{Set{S},T}() + @test PBO.data(zero(F)) == Dict{Set{S},T}() let f = F( nothing => 0.5, @@ -25,62 +27,63 @@ function test_setdict_constructors() [:y, :z] => -2.0, [:x, :y, :z] => 3.0, ) - g = F( - Dict{Set{S},T}( - Set{S}() => 0.5, - Set{S}([:x]) => 1.0, - Set{S}([:y]) => 1.0, - Set{S}([:z]) => 1.0, - Set{S}([:x, :y]) => -2.0, - Set{S}([:x, :z]) => -2.0, - Set{S}([:y, :z]) => -2.0, - Set{S}([:x, :y, :z]) => 3.0, - ), + Φ = Dict{Set{S},T}( + Set{S}() => 0.5, + Set{S}([:x]) => 1.0, + Set{S}([:y]) => 1.0, + Set{S}([:z]) => 1.0, + Set{S}([:x, :y]) => -2.0, + Set{S}([:x, :z]) => -2.0, + Set{S}([:y, :z]) => -2.0, + Set{S}([:x, :y, :z]) => 3.0, ) - @test f == g + @test PBO.data(f) == Φ end - @test F(1.0) == F(Dict{Set{S},T}(Set{S}() => 1.0)) - @test one(F) == F(Dict{Set{S},T}(Set{S}() => 1.0)) + @test PBO.data(F(1.0)) == Dict{Set{S},T}(Set{S}() => 1.0) + @test PBO.data(one(F)) == Dict{Set{S},T}(Set{S}() => 1.0) + @test PBO.data(F(2.0)) == Dict{Set{S},T}(Set{S}() => 2.0) let f = F([:x, :y, :z, :w, nothing]) - g = F( - Dict{Set{S},T}( - Set{S}([:x]) => 1.0, - Set{S}([:y]) => 1.0, - Set{S}([:z]) => 1.0, - Set{S}([:w]) => 1.0, - Set{S}() => 1.0, - ), + Φ = Dict{Set{S},T}( + Set{S}([:x]) => 1.0, + Set{S}([:y]) => 1.0, + Set{S}([:z]) => 1.0, + Set{S}([:w]) => 1.0, + Set{S}() => 1.0, ) - @test f == g + @test PBO.data(f) == Φ end - + let f = F((nothing => 0.5), :x, [:x, :y] => -2.0) - g = F( - Dict{Set{S},T}(Set{S}() => 0.5, Set{S}([:x]) => 1.0, Set{S}([:x, :y]) => -2.0), + Φ = Dict{Set{S},T}( + Set{S}() => 0.5, + Set{S}([:x]) => 1.0, + Set{S}([:x, :y]) => -2.0, ) - @test f == g + @test PBO.data(f) == Φ end let f = F(nothing => 0.5, :y, [:x, :y] => 2.0) - g = F( - Dict{Set{S},T}(Set{S}() => 0.5, Set{S}([:y]) => 1.0, Set{S}([:x, :y]) => 2.0), + Φ = Dict{Set{S},T}( + Set{S}() => 0.5, + Set{S}([:y]) => 1.0, + Set{S}([:x, :y]) => 2.0, ) - @test f == g + @test PBO.data(f) == Φ end let f = F(nothing, :z => -1.0) - g = F(Set{S}() => 1.0, Set{S}([:z]) => -1.0) - @test f == g + Φ = Dict{Set{S},T}(Set{S}() => 1.0, Set{S}([:z]) => -1.0) + @test PBO.data(f) == Φ end let f = F(S[] => 0.0, Set{S}([:x, :y, :z]) => 3.0) - g = F(Set{S}() => 0.0, Set{S}([:x, :y, :z]) => 3.0) - @test f == g + Φ = Dict{Set{S},T}(Set{S}([:x, :y, :z]) => 3.0) + @test PBO.data(f) == Φ end end end return nothing -end \ No newline at end of file +end diff --git a/test/unit/operators.jl b/test/unit/operators.jl index dc79bc9..ae5f1cc 100644 --- a/test/unit/operators.jl +++ b/test/unit/operators.jl @@ -1,25 +1,93 @@ function test_operators() @testset "Operators" verbose = true begin - @testset "$op" for (op::Function, data) in Assets.PBF_OPERATOR_LIST - for (x, y) in data - if y isa Type{<:Exception} - @test_throws y op(x...) - else - @test op(x...) == y - end - end - end + test_setdict_operators() end return nothing end -function test_evaluation() - @testset "Evaluation" verbose = true begin - @testset "$tag" for (tag::String, data) in Assets.PBF_EVALUATION_LIST - for ((f, x), y) in data - @test f(x) == y +function test_setdict_operators() + @testset "∴ SetDict" begin + let S = Symbol + T = Float64 + F = PBO.SetDictPBF{S,T} + + @testset "+" begin + let a = -1.0 + f = F(:x => 1.0, (:x, :y) => -2.0, 0.5) + g = F(:y => -1.0, (:x, :y) => 2.0, 1.0) + + @test f + g == g + f == F(:x => 1.0, :y => -1.0, 1.5) + @test f + a == a + f == F(:x => 1.0, (:x, :y) => -2.0, -0.5) + @test g + a == a + g == F(:y => -1.0, (:x, :y) => 2.0) + end + end + + @testset "-" begin + let a = 2.0 + f = F(:x => 1.0, (:x, :y) => -2.0, 0.5) + g = F(:y => -1.0, (:x, :y) => 2.0, 1.0) + + @test -f == F(:x => -1.0, (:x, :y) => 2.0, -0.5) + @test -g == F(:y => 1.0, (:x, :y) => -2.0, -1.0) + + @test iszero(f - f) + @test iszero(g - g) + + @test f - g == F(:x => 1.0, :y => 1.0, (:x, :y) => -4.0, -0.5) + @test g - f == F(:x => -1.0, :y => -1.0, (:x, :y) => 4.0, 0.5) + + @test f - a == F(:x => 1.0, (:x, :y) => -2.0, -1.5) + @test a - f == F(:x => -1.0, (:x, :y) => 2.0, 1.5) + @test g - a == F(:y => -1.0, (:x, :y) => 2.0, -1.0) + @test a - g == F(:y => 1.0, (:x, :y) => -2.0, 1.0) + end + end + + @testset "*" begin + let a = 0.25 + f = F(:x => 1.0, (:x, :y) => -2.0, 0.5) + g = F(:z => -1.0, (:y, :z) => 2.0, 1.0) + + @test f * g == g * f == F( + 0.5, + :x => 1.0, + :z => -0.5, + (:x, :z) => -1.0, + (:x, :y) => -2.0, + (:y, :z) => 1.0, + ) + + @test f * a == a * f == F(0.125, :x => 0.25, (:x, :y) => -0.5) + @test g * a == a * g == F(0.25, :z => -0.25, (:y, :z) => 0.5) + end + end + + @testset "/" begin + let a = 2.0 + f = F(:x => 1.0, (:x, :y) => -2.0, 0.5) + + @test f / a == F(:x => 0.5, (:x, :y) => -1.0, 0.25) + end + end + + @testset "^" begin + let f = F(:x => 1.0, (:x, :y) => -2.0, 0.5) + @test f^1 == f + @test f^2 == f * f == F( + :x => 2.0, + (:x, :y) => -2.0, + 0.25, + ) + @test f^3 == f * f * f == F( + :x => 3.25, + (:x, :y) => -3.5, + 0.125, + ) + end end end end -end + + return nothing +end \ No newline at end of file diff --git a/test/unit/unit.jl b/test/unit/unit.jl index 847cd20..1426fb8 100644 --- a/test/unit/unit.jl +++ b/test/unit/unit.jl @@ -1,16 +1,18 @@ +include("variables.jl") include("term_parser.jl") include("constructors.jl") -# include("operators.jl") +include("operators.jl") # include("calculus.jl") # include("discretization.jl") # include("quadratization.jl") # include("print.jl") -function test_unit() +function unit_tests() @testset "□ Unit Tests" verbose = true begin + test_variable_system() test_term_parser() test_constructors() - # test_operators() + test_operators() # test_evaluation() # test_calculus() # test_discretization() diff --git a/test/unit/variables.jl b/test/unit/variables.jl new file mode 100644 index 0000000..d8ceb16 --- /dev/null +++ b/test/unit/variables.jl @@ -0,0 +1,29 @@ +function test_variable_system() + @testset "⊛ Variable System" verbose = true begin + @testset "→ Ordering" begin + @testset "∴ Symbol" begin + @test PBO.varlt(:x, :x) === false + @test PBO.varlt(:x, :y) === true + @test PBO.varlt(:y, :x) === false + + @test PBO.varlt(:xx, :y) === false + @test PBO.varlt(:y, :xx) === true + end + + @testset "∴ Integer" begin + @test PBO.varlt(1, 1) === false + @test PBO.varlt(1, 2) === true + @test PBO.varlt(2, 1) === false + + @test PBO.varlt(1, -1) === true + @test PBO.varlt(-1, 1) === false + + @test PBO.varlt(-1, -1) === false + @test PBO.varlt(-1, -2) === true + @test PBO.varlt(-2, -1) === false + end + end + end + + return nothing +end \ No newline at end of file From 4178c18898a49dc100306168b1099fe4c8073845 Mon Sep 17 00:00:00 2001 From: pedromxavier Date: Sun, 15 Oct 2023 16:58:31 -0400 Subject: [PATCH 7/9] Fix name --- benchmark/suites/constructors.jl | 2 +- benchmark/suites/operators.jl | 12 ++++++------ benchmark/suites/quadratization.jl | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/benchmark/suites/constructors.jl b/benchmark/suites/constructors.jl index ffa85ae..606694a 100644 --- a/benchmark/suites/constructors.jl +++ b/benchmark/suites/constructors.jl @@ -1,4 +1,4 @@ -function benchmark_constructors!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractPBF{V,T}} +function benchmark_constructors!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T}} suite["constructors"] = BenchmarkGroup() return nothing diff --git a/benchmark/suites/operators.jl b/benchmark/suites/operators.jl index 9c25bbb..54bb730 100644 --- a/benchmark/suites/operators.jl +++ b/benchmark/suites/operators.jl @@ -1,4 +1,4 @@ -function benchmark_operators!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractPBF{V,T}} +function benchmark_operators!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T}} suite["operators"] = BenchmarkGroup() benchmark_add!(suite, F) @@ -11,7 +11,7 @@ function benchmark_operators!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractPBF{V, return nothing end -function benchmark_add!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractPBF{V,T}} +function benchmark_add!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T}} suite["operators"]["+"] = BenchmarkGroup() suite["operators"]["+"]["small"] = @benchmarkable( f + g; @@ -31,7 +31,7 @@ function benchmark_add!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractPBF{V,T}} return nothing end -function benchmark_sub!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractPBF{V,T}} +function benchmark_sub!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T}} suite["operators"]["-"] = BenchmarkGroup() suite["operators"]["-"]["small"] = @benchmarkable( f - g; @@ -51,7 +51,7 @@ function benchmark_sub!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractPBF{V,T}} return nothing end -function benchmark_mul!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractPBF{V,T}} +function benchmark_mul!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T}} suite["operators"]["*"] = BenchmarkGroup() suite["operators"]["*"]["small"] = @benchmarkable( f * g; @@ -71,7 +71,7 @@ function benchmark_mul!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractPBF{V,T}} return nothing end -function benchmark_dict_evaluation!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractPBF{V,T}} +function benchmark_dict_evaluation!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T}} suite["operators"]["dict-evaluation"] = BenchmarkGroup() suite["operators"]["dict-evaluation"]["small"] = @benchmarkable( f(x); @@ -91,7 +91,7 @@ function benchmark_dict_evaluation!(suite, ::Type{F}) where {V,T,F<:PBO.Abstract return nothing end -function benchmark_set_evaluation!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractPBF{V,T}} +function benchmark_set_evaluation!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T}} suite["operators"]["set-evaluation"] = BenchmarkGroup() suite["operators"]["set-evaluation"]["small"] = @benchmarkable( f(x); diff --git a/benchmark/suites/quadratization.jl b/benchmark/suites/quadratization.jl index 3ed8e94..7e1321a 100644 --- a/benchmark/suites/quadratization.jl +++ b/benchmark/suites/quadratization.jl @@ -1,4 +1,4 @@ -function benchmark_quadratization!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractPBF{V,T}} +function benchmark_quadratization!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T}} suite["quadratization"] = BenchmarkGroup() suite["quadratization"]["automatic"] = BenchmarkGroup() suite["quadratization"]["automatic"]["small"] = @benchmarkable( From 1328ab57ee39377501e63b8c96df0e6856c9181e Mon Sep 17 00:00:00 2001 From: pedromxavier Date: Sun, 15 Oct 2023 18:53:02 -0400 Subject: [PATCH 8/9] Fix docs --- docs/Project.toml | 3 + docs/make.jl | 16 +++-- docs/src/api.md | 72 +++++++++++++++++++++ docs/src/assets/README.md | 3 +- docs/src/index.md | 12 ++++ docs/src/manual/1-intro.md | 3 + docs/src/manual/2-function.md | 9 +++ docs/src/manual/3-operators.md | 5 ++ docs/src/manual/4-quadratization.md | 7 ++ docs/src/manual/5-synthesis.md | 7 ++ src/PseudoBooleanOptimization.jl | 2 +- src/interface/function.jl | 6 +- src/interface/quadratization.jl | 9 ++- src/interface/{variable.jl => variables.jl} | 0 src/library/function/function.jl | 8 +-- src/library/quadratization/abstract.jl | 5 -- 16 files changed, 147 insertions(+), 20 deletions(-) create mode 100644 docs/src/manual/1-intro.md create mode 100644 docs/src/manual/2-function.md create mode 100644 docs/src/manual/3-operators.md create mode 100644 docs/src/manual/4-quadratization.md create mode 100644 docs/src/manual/5-synthesis.md rename src/interface/{variable.jl => variables.jl} (100%) diff --git a/docs/Project.toml b/docs/Project.toml index ae73795..c9dcb99 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -2,3 +2,6 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" DocumenterDiagrams = "a106ebf2-4182-4cba-90d4-44cd3cc36e85" PseudoBooleanOptimization = "c8fa9a04-bc42-452d-8558-dc51757be744" + +[compat] +Documenter = "1" diff --git a/docs/make.jl b/docs/make.jl index cc17944..fa51681 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -11,10 +11,11 @@ DocMeta.setdocmeta!( ) makedocs(; - modules = [PseudoBooleanOptimization], - doctest = true, - clean = true, - format = Documenter.HTML( + modules = [PseudoBooleanOptimization], + doctest = true, + clean = true, + warnonly = [:missing_docs], + format = Documenter.HTML( assets = ["assets/extra_styles.css", "assets/favicon.ico"], mathengine = Documenter.KaTeX(), sidebar_sitename = false, @@ -23,6 +24,13 @@ makedocs(; authors = "Pedro Maciel Xavier", pages = [ # "Home" => "index.md", + "Manual" => [ + "manual/1-intro.md", + "manual/2-function.md", + "manual/3-operators.md", + "manual/4-quadratization.md", + "manual/5-synthesis.md", + ], "API Reference" => "api.md", ], workdir = @__DIR__ diff --git a/docs/src/api.md b/docs/src/api.md index c922011..91fb335 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -7,4 +7,76 @@ PBO.varlt PBO.varmul PBO.varmap PBO.vargen +PBO.varshow +``` + +## Terms + +```@docs +PBO.term +PBO.term_head +PBO.term_tail +``` + +## Functions + +```@docs +PBO.AbstractPBF +PBO.PBF +PBO.data +``` + +## Analysis + +```@docs +PBO.isconstant +PBO.degree +``` + +```@docs +PBO.lowerbound +PBO.upperbound +PBO.bounds +PBO.mingap +PBO.maxgap +``` + +```@docs +PBO.derivative +PBO.gradient +PBO.residual +``` + +```@docs +PBO.discretize +PBO.discretize! +``` + +```@docs +PBO.relaxedgcd +``` + +## Quadratization + +```@docs +PBO.quadratize +PBO.quadratize! +PBO.infer_quadratization +``` + +```@docs +PBO.INFER +PBO.DEFAULT +PBO.PTR_BG +PBO.NTR_KZFD +``` + +## Synthesis + +```@docs +PBO.wishart +PBO.sherrington_kirkpatrick +PBO.not_all_equal_3sat +PBO.k_regular_k_xorsat +PBO.r_regular_k_xorsat ``` diff --git a/docs/src/assets/README.md b/docs/src/assets/README.md index b5a9088..c0930ee 100644 --- a/docs/src/assets/README.md +++ b/docs/src/assets/README.md @@ -12,6 +12,7 @@ The colors were chosen according to *Julia's Reference for logo graphics*[^Julia Text color matches renders fairly well in both light and dark background themes. ## Typography + The *MADETYPE Sunflower*[^Sunflower] font was chosen. It was converted to a SVG path using the *Google Font to Svg Path*[^DanMarshall] online tool. @@ -19,7 +20,7 @@ It was converted to a SVG path using the *Google Font to Svg Path*[^DanMarshall] [github.com/JuliaLang/julia-logo-graphics](https://github.com/JuliaLang/julia-logo-graphics/) [^Sunflower]: - [Licensed](/docs/src/assets/fonts/Sunflower-LICENSE.txt) by the authors for use in this project + [Licensed](./fonts/Sunflower-LICENSE.txt) by the authors for use in this project [^DanMarshall]: [danmarshall.github.io/google-font-to-svg-path](https://danmarshall.github.io/google-font-to-svg-path/) \ No newline at end of file diff --git a/docs/src/index.md b/docs/src/index.md index a432c1a..5b77dbf 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -18,3 +18,15 @@ f = PBO.PBF{Symbol,Float64}( g = f^2 - 2f ``` + +## Table of Contents + +```@contents +Pages = [ + "manual/1-intro.md", + "manual/2-function.md", + "manual/3-operators.md", + "manual/4-quadratization.md", +] +Depth = 2 +``` diff --git a/docs/src/manual/1-intro.md b/docs/src/manual/1-intro.md new file mode 100644 index 0000000..19b1763 --- /dev/null +++ b/docs/src/manual/1-intro.md @@ -0,0 +1,3 @@ +# PseudoBooleanOptimization.jl Manual + +## Introduction diff --git a/docs/src/manual/2-function.md b/docs/src/manual/2-function.md new file mode 100644 index 0000000..b21c992 --- /dev/null +++ b/docs/src/manual/2-function.md @@ -0,0 +1,9 @@ +# Pseudo-Boolean functions + +Let `f: \mathbb{B}^{n} \to \mathbb{R}`. Then `f` can be written as + +```math +f(\mathbf{x}) = \sum_{\omega \subseteq [n]} c_{\omega} \prod_{j \in \omega} x_{j} +``` + +where ``c_{\omega} \in \mathbb{R}`` and ``x_{j} \in \{ 0, 1 \}`` for all ``j \in [n] = \{ 1, \dots, n \}``. diff --git a/docs/src/manual/3-operators.md b/docs/src/manual/3-operators.md new file mode 100644 index 0000000..95d0da4 --- /dev/null +++ b/docs/src/manual/3-operators.md @@ -0,0 +1,5 @@ +# Operations with pseudo-Boolean functions + +## Arithmetic operations + +The arithmetic operations `+`, `-`, `*`, `/`, `^` are defined for pseudo-Boolean functions. The result of an arithmetic operation is a pseudo-Boolean function. diff --git a/docs/src/manual/4-quadratization.md b/docs/src/manual/4-quadratization.md new file mode 100644 index 0000000..36066d2 --- /dev/null +++ b/docs/src/manual/4-quadratization.md @@ -0,0 +1,7 @@ +# Quadratization + +A Quadratization is a mapping ``\mathcal{Q}\{f\}: \mathsrc{F} \to \mathsrc{F}^{2}`` such that + +```math +\min_{\mathbf{y}} \mathsrc{Q}\{f\}(\mathbf{x}, \mathbf{y}) = f(\mathbf{x}) ~\forall \mathbf{x} \in \mathbb{B}^{n}.~\forall f \in \mathsrc{F}. +``` diff --git a/docs/src/manual/5-synthesis.md b/docs/src/manual/5-synthesis.md new file mode 100644 index 0000000..892a0ed --- /dev/null +++ b/docs/src/manual/5-synthesis.md @@ -0,0 +1,7 @@ +# Synthetic Problem Generation + +```@example synthesis +import PseudoBooleanOptimization as PBO + +f = PBO.wishart(PBO.PBF{Int,Float64}, 3, 5) +``` diff --git a/src/PseudoBooleanOptimization.jl b/src/PseudoBooleanOptimization.jl index a8f59f1..085da04 100644 --- a/src/PseudoBooleanOptimization.jl +++ b/src/PseudoBooleanOptimization.jl @@ -15,7 +15,7 @@ const __VERSION__ = VersionNumber( ) # Interface Definition -include("interface/variable.jl") +include("interface/variables.jl") include("interface/function.jl") include("interface/quadratization.jl") diff --git a/src/interface/function.jl b/src/interface/function.jl index 1a87e40..db62e84 100644 --- a/src/interface/function.jl +++ b/src/interface/function.jl @@ -1,5 +1,5 @@ @doc raw""" - AbstractPseudoBooleanFunction{V,T} + AbstractPBF{V,T} A pseudo-Boolean Function[^Boros2002] ``f \in \mathscr{F}`` over some field ``\mathbb{T}`` takes the form @@ -15,9 +15,9 @@ Variables ``x_j`` are boolean, thus ``f : \mathbb{B}^{n} \to \mathbb{T}``. [^Boros2002]: Endre Boros, Peter L. Hammer, **Pseudo-Boolean optimization**, *Discrete Applied Mathematics*, 2002 [{doi}](https://doi.org/10.1016/S0166-218X(01)00341-9) """ -abstract type AbstractFunction{V,T} end +abstract type AbstractPBF{V,T} end -const AbstractPBF{V,T} = AbstractFunction{V,T} +const AbstractFunction{V,T} = AbstractPBF{V,T} @doc raw""" data(f::AbstractPBF{V,T}) diff --git a/src/interface/quadratization.jl b/src/interface/quadratization.jl index 0ec111e..5bc486b 100644 --- a/src/interface/quadratization.jl +++ b/src/interface/quadratization.jl @@ -14,6 +14,13 @@ function Quadratization(method::Q; stable::Bool = false) where {Q<:Quadratizatio return Quadratization{Q}(method, stable) end +@doc raw""" + infer_quadratization(f::AbstractPBF) + +For a given PBF, returns whether it should be quadratized or not, based on its degree. +""" +function infer_quadratization end + @doc raw""" quadratize(aux, f::PBF{V, T}, ::Quadratization{Q}) where {V,T,Q} @@ -32,7 +39,7 @@ Creates and returns a vector with ``n`` variables. quadratize(f::PBF{V, T}, ::Quadratization{Q}) where {V,T,Q} -When `aux` is not specified, uses [`auxgen`](@ref) to generate variables. +When `aux` is not specified, uses [`vargen`](@ref) to generate variables. """ function quadratize end diff --git a/src/interface/variable.jl b/src/interface/variables.jl similarity index 100% rename from src/interface/variable.jl rename to src/interface/variables.jl diff --git a/src/library/function/function.jl b/src/library/function/function.jl index fb97edb..cf3d9df 100644 --- a/src/library/function/function.jl +++ b/src/library/function/function.jl @@ -1,19 +1,17 @@ @doc raw""" - PseudoBooleanFunction{V,T,S} + PBF{V,T,S} This is a concrete implementation of [`AbstractPBF`](@ref) that uses the `S` data structure to store the terms of the function. """ -struct PseudoBooleanFunction{V,T,S} <: AbstractPBF{V,T} +struct PBF{V,T,S} <: AbstractPBF{V,T} Φ::S # Constructor: I know what I am doing - function PseudoBooleanFunction{V,T,S}(Φ::S) where {V,T,S} + function PBF{V,T,S}(Φ::S) where {V,T,S} return new{V,T,S}(Φ) end end -const PBF{V,T,S} = PseudoBooleanFunction{V,T,S} - # Internal representation function data(f::PBF{V,T,S})::S where {V,T,S} return f.Φ diff --git a/src/library/quadratization/abstract.jl b/src/library/quadratization/abstract.jl index be81a23..dc5d3ca 100644 --- a/src/library/quadratization/abstract.jl +++ b/src/library/quadratization/abstract.jl @@ -44,11 +44,6 @@ end """ struct INFER <: QuadratizationMethod end -@doc raw""" - infer_quadratization(f::AbstractPBF) - -For a given PBF, returns whether it should be quadratized or not, based on its degree. -""" function infer_quadratization(f::AbstractPBF, stable::Bool = false) k = degree(f) From 1458a6051a62a2b5c0c3608145df962d6baed07d Mon Sep 17 00:00:00 2001 From: pedromxavier Date: Sun, 15 Oct 2023 20:03:49 -0400 Subject: [PATCH 9/9] Disable automatic benchmarking --- .github/workflows/benchmark.yml | 4 +-- benchmark/benchmark.jl | 2 ++ benchmark/suites/operators.jl | 40 +++++++++++++++--------------- benchmark/suites/quadratization.jl | 4 +-- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 0f28916..daf6d63 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -1,7 +1,7 @@ name: Benchmark on: - pull_request: - types: [opened, synchronize, reopened] + workflow_dispatch: + jobs: test: name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} diff --git a/benchmark/benchmark.jl b/benchmark/benchmark.jl index 6007efd..360a577 100644 --- a/benchmark/benchmark.jl +++ b/benchmark/benchmark.jl @@ -15,6 +15,8 @@ const PBO = PseudoBooleanOptimization Random.seed!(0) +erste(f) = f isa Tuple ? first(f) : f + const SUITE = BenchmarkGroup() include("suites/constructors.jl") diff --git a/benchmark/suites/operators.jl b/benchmark/suites/operators.jl index 54bb730..5347ae8 100644 --- a/benchmark/suites/operators.jl +++ b/benchmark/suites/operators.jl @@ -16,15 +16,15 @@ function benchmark_add!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T suite["operators"]["+"]["small"] = @benchmarkable( f + g; setup = begin - f = PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 10) - g = PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 10) + f = erste(PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 10)) + g = erste(PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 10)) end ) suite["operators"]["+"]["large"] = @benchmarkable( f + g; setup = begin - f = PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 100) - g = PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 100) + f = erste(PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 100)) + g = erste(PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 100)) end ) @@ -36,15 +36,15 @@ function benchmark_sub!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T suite["operators"]["-"]["small"] = @benchmarkable( f - g; setup = begin - f = PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 10) - g = PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 10) + f = erste(PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 10)) + g = erste(PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 10)) end ) suite["operators"]["-"]["large"] = @benchmarkable( f - g; setup = begin - f = PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 100) - g = PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 100) + f = erste(PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 100)) + g = erste(PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 100)) end ) @@ -56,15 +56,15 @@ function benchmark_mul!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractFunction{V,T suite["operators"]["*"]["small"] = @benchmarkable( f * g; setup = begin - f = PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 10) - g = PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 10) + f = erste(PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 10)) + g = erste(PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 10)) end ) suite["operators"]["*"]["large"] = @benchmarkable( f * g; setup = begin - f = PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 32) - g = PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 32) + f = erste(PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 32)) + g = erste(PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 32)) end ) @@ -76,15 +76,15 @@ function benchmark_dict_evaluation!(suite, ::Type{F}) where {V,T,F<:PBO.Abstract suite["operators"]["dict-evaluation"]["small"] = @benchmarkable( f(x); setup = begin - f = PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 10) - x = Dict{Int,Int}(i => rand((0, 1)) for i = 1:10) + f = erste(PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 10)) + x = erste(Dict{Int,Int}(i => rand((0, 1)) for i = 1:10)) end ) suite["operators"]["dict-evaluation"]["large"] = @benchmarkable( f(x); setup = begin - f = PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 100) - x = Dict{Int,Int}(i => rand((0, 1)) for i = 1:100) + f = erste(PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 100)) + x = erste(Dict{Int,Int}(i => rand((0, 1)) for i = 1:100)) end ) @@ -96,15 +96,15 @@ function benchmark_set_evaluation!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractF suite["operators"]["set-evaluation"]["small"] = @benchmarkable( f(x); setup = begin - f = PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 10) - x = Set{Int}(i for i = 1:10 if rand(Bool)) + f = erste(PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 10)) + x = erste(Set{Int}(i for i = 1:10 if rand(Bool))) end ) suite["operators"]["set-evaluation"]["large"] = @benchmarkable( f(x); setup = begin - f = PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 100) - x = Set{Int}(i for i = 1:100 if rand(Bool)) + f = erste(PBO.sherrington_kirkpatrick(Random.GLOBAL_RNG, $F, 100)) + x = erste(Set{Int}(i for i = 1:100 if rand(Bool))) end ) diff --git a/benchmark/suites/quadratization.jl b/benchmark/suites/quadratization.jl index 7e1321a..956ec73 100644 --- a/benchmark/suites/quadratization.jl +++ b/benchmark/suites/quadratization.jl @@ -4,13 +4,13 @@ function benchmark_quadratization!(suite, ::Type{F}) where {V,T,F<:PBO.AbstractF suite["quadratization"]["automatic"]["small"] = @benchmarkable( PBO.quadratize(f, PBO.Quadratization{PBO.INFER}()); setup = begin - f = PBO.k_regular_k_xorsat(Random.GLOBAL_RNG, $F, 10, 3; quad = nothing) + f = erste(PBO.k_regular_k_xorsat(Random.GLOBAL_RNG, $F, 10, 3; quad = nothing)) end ) suite["quadratization"]["automatic"]["large"] = @benchmarkable( PBO.quadratize(f, PBO.Quadratization{PBO.INFER}()); setup = begin - f = PBO.k_regular_k_xorsat(Random.GLOBAL_RNG, $F, 100, 3; quad = nothing) + f = erste(PBO.k_regular_k_xorsat(Random.GLOBAL_RNG, $F, 100, 3; quad = nothing)) end )