From fe06efd533e37431e885540dadff7a80db9607f3 Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Mon, 27 Jun 2022 03:20:44 -0400 Subject: [PATCH] change maxcut to spinglass --- Project.toml | 2 +- README.md | 2 +- docs/make.jl | 4 +- docs/serve.jl | 2 + docs/src/performancetips.md | 4 +- docs/src/ref.md | 3 +- examples/{MaxCut.jl => SpinGlass.jl} | 44 ++++++++------ src/GenericTensorNetworks.jl | 4 +- src/networks/MaxCut.jl | 62 ------------------- src/networks/SpinGlass.jl | 90 ++++++++++++++++++++++++++++ src/networks/networks.jl | 6 +- test/networks/MaxCut.jl | 53 ---------------- test/networks/SpinGlass.jl | 73 ++++++++++++++++++++++ test/networks/networks.jl | 2 +- 14 files changed, 207 insertions(+), 144 deletions(-) rename examples/{MaxCut.jl => SpinGlass.jl} (54%) delete mode 100644 src/networks/MaxCut.jl create mode 100644 src/networks/SpinGlass.jl delete mode 100644 test/networks/MaxCut.jl create mode 100644 test/networks/SpinGlass.jl diff --git a/Project.toml b/Project.toml index 46a3d617..e96263da 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "GenericTensorNetworks" uuid = "3521c873-ad32-4bb4-b63d-f4f178f42b49" authors = ["GiggleLiu and contributors"] -version = "1.0.6" +version = "1.1.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" diff --git a/README.md b/README.md index 2a04896c..b667451e 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ The *solution space properties* include * The enumeration of solutions at certain sizes. * The direct sampling of solutions at certain sizes. -The solvable problems include [Independent set problem](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/generated/IndependentSet/), [Maximal independent set problem](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/generated/MaximalIS/), [Cutting problem (Spin-glass problem)](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/generated/MaxCut/), [Vertex matching problem](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/generated/Matching/), [Binary paint shop problem](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/generated/PaintShop/), [Coloring problem](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/generated/Coloring/), [Dominating set problem](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/generated/DominatingSet/), [Set packing problem](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/generated/SetPacking/), [Satisfiability problem](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/generated/Satisfiability/) and [Set covering problem](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/generated/SetCovering/). +The solvable problems include [Independent set problem](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/generated/IndependentSet/), [Maximal independent set problem](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/generated/MaximalIS/), [Cutting problem (Spin-glass problem)](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/generated/SpinGlass/), [Vertex matching problem](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/generated/Matching/), [Binary paint shop problem](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/generated/PaintShop/), [Coloring problem](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/generated/Coloring/), [Dominating set problem](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/generated/DominatingSet/), [Set packing problem](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/generated/SetPacking/), [Satisfiability problem](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/generated/Satisfiability/) and [Set covering problem](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/generated/SetCovering/). ## Installation

diff --git a/docs/make.jl b/docs/make.jl index 5027979e..5b9c39ca 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -48,8 +48,8 @@ makedocs(; "Problems" => [ "Independent set problem" => "generated/IndependentSet.md", "Maximal independent set problem" => "generated/MaximalIS.md", - "Cutting problem" => "generated/MaxCut.md", - "Vertex Matching problem" => "generated/Matching.md", + "Spin glass problem" => "generated/SpinGlass.md", + "Vertex matching problem" => "generated/Matching.md", "Binary paint shop problem" => "generated/PaintShop.md", "Coloring problem" => "generated/Coloring.md", "Dominating set problem" => "generated/DominatingSet.md", diff --git a/docs/serve.jl b/docs/serve.jl index 6d7a561e..5861c572 100644 --- a/docs/serve.jl +++ b/docs/serve.jl @@ -11,6 +11,8 @@ function serve(;host::String="0.0.0.0", port::Int=8000) skip_dirs=[ joinpath("docs", "src", "assets"), joinpath("docs", "src", "generated"), + joinpath("docs", "src", "notebooks"), + joinpath("docs", "build"), ], literate="examples", host=\"$host\", diff --git a/docs/src/performancetips.md b/docs/src/performancetips.md index e3017419..91f44447 100644 --- a/docs/src/performancetips.md +++ b/docs/src/performancetips.md @@ -191,9 +191,9 @@ The graphs in all benchmarks are random three-regular graphs, which have treewid Panel (a) shows the time and space complexity of tensor network contraction for different graph sizes. The contraction order is obtained using the `TreeSA` algorithm that implemented in [OMEinsumContractionOrders](https://github.com/TensorBFS/OMEinsumContractionOrders.jl). If we assume our contraction-order finding program has found the optimal treewidth, which is very likely to be true, the space complexity is the same as the treewidth of the problem graph. Slicing technique has been used for graphs with space complexity greater than ``2^{27}`` (above the yellow dashed line) to fit the computation into a 16GB memory. One can see that all the computation times in panels (b), (c), and (d) have a strong correlation with the predicted time and space complexity. While in panel (d), the computation time of configuration enumeration also strongly correlates with other factors such as the configuration space size. -Among these benchmarks, computational tasks with data types real numbers, complex numbers, or [Tropical](@ref) numbers (CPU only) can utilize fast basic linear algebra subprograms (BLAS) functions. These tasks usually compute much faster than ones with other element types in the same category. +Among these benchmarks, computational tasks with data types real numbers, complex numbers, or [`Tropical`](@ref) numbers (CPU only) can utilize fast basic linear algebra subprograms (BLAS) functions. These tasks usually compute much faster than ones with other element types in the same category. Immutable data types with no reference to other values can be compiled to GPU devices that run much faster than CPUs in all cases when the problem scale is big enough. -These data types do not include those defined in [Polynomial](@ref), [ConfigEnumerator](@ref), [ExtendedTropical](@ref) and [SumProductTree](@ref) or a data type containing them as a part. +These data types do not include those defined in [`Polynomial`](@ref), [`ConfigEnumerator`](@ref), [`ExtendedTropical`](@ref) and [`SumProductTree`](@ref) or a data type containing them as a part. In panel (c), one can see the Fourier transformation-based method is the fastest in computing the independence polynomial, but it may suffer from round-off errors. The finite field (GF(p)) approach is the only method that does not have round-off errors and can be run on a GPU. In panel (d), one can see the technique to bound the enumeration space (see paper) improves the performance for more than one order of magnitude in enumerating the MISs. The bounding technique can also reduce the memory usage significantly, without which the largest computable graph size is only ``\sim150`` on a device with 32GB main memory. diff --git a/docs/src/ref.md b/docs/src/ref.md index 2b87d67a..11799317 100644 --- a/docs/src/ref.md +++ b/docs/src/ref.md @@ -8,7 +8,7 @@ MaximalIS Matching Coloring DominatingSet -MaxCut +SpinGlass PaintShop Satisfiability SetCovering @@ -43,6 +43,7 @@ is_set_covering is_set_packing cut_size +spinglass_energy num_paint_shop_color_switch paint_shop_coloring_from_config mis_compactify! diff --git a/examples/MaxCut.jl b/examples/SpinGlass.jl similarity index 54% rename from examples/MaxCut.jl rename to examples/SpinGlass.jl index e262a0cd..7242d24d 100644 --- a/examples/MaxCut.jl +++ b/examples/SpinGlass.jl @@ -1,17 +1,23 @@ -# # Cutting problem (Spin-glass problem) +# # Spin-glass problem (Cutting problem) # !!! note # It is highly recommended to read the [Independent set problem](@ref) chapter before reading this one. # ## Problem definition -# In graph theory, a [cut](https://en.wikipedia.org/wiki/Cut_(graph_theory)) is a partition of the vertices of a graph into two disjoint subsets. -# It is closely related to the [spin-glass](https://en.wikipedia.org/wiki/Spin_glass) problem in physics. -# Finding the maximum cut is NP-Hard, where a maximum cut is a cut whose size is at least the size of any other cut, +# Let ``G=(V, E)`` be a graph, the [spin-glass](https://en.wikipedia.org/wiki/Spin_glass) problem in physics is characterized by the following energy function +# ```math +# H = - \sum_{ij \in E} J_{ij} s_i s_j + \sum_{i \in V} h_i s_i, +# ``` +# where ``h_i`` is an onsite energy term associated with spin ``s_i \in \{0, 1\}``, and ``J_{ij}`` is the coupling strength between spins ``s_i`` and ``s_j``. +# +# The spin glass problem very close related to the cutting problem in graph theory. +# A [cut](https://en.wikipedia.org/wiki/Cut_(graph_theory)) is a partition of the vertices of a graph into two disjoint subsets. +# Finding the maximum cut (the spin glass maximum energy) is NP-Hard, where a maximum cut is a cut whose size is at least the size of any other cut, # where the size of a cut is the number of edges (or the sum of weights on edges) crossing the cut. using GenericTensorNetworks, Graphs -# In the following, we are going to defined an cutting problem for the Petersen graph. +# In the following, we are going to defined an spin glass problem for the Petersen graph. graph = Graphs.smallgraph(:petersen) @@ -23,52 +29,52 @@ locations = [[rot15(0.0, 2.0, i) for i=0:4]..., [rot15(0.0, 1.0, i) for i=0:4].. show_graph(graph; locs=locations, format=:svg) # ## Generic tensor network representation -# We define the cutting problem as -problem = MaxCut(graph); +# We define the spin glass problem as +problem = SpinGlass(graph); # ### Theory (can skip) # # For a vertex ``v\in V``, we define a boolean degree of freedom ``s_v\in\{0, 1\}``. -# Then the maximum cutting problem can be encoded to tensor networks by mapping an edge ``(i,j)\in E`` to an edge matrix labelled by ``s_is_j`` +# Then the spin glass problem can be encoded to tensor networks by mapping an edge ``(i,j)\in E`` to an edge matrix labelled by ``s_is_j`` # ```math # B(x_{\langle i, j\rangle}) = \left(\begin{matrix} # 1 & x_{\langle i, j\rangle}^{w_{\langle i,j \rangle}}\\ # x_{\langle i, j\rangle}^{w_{\langle i,j \rangle}} & 1 # \end{matrix}\right), # ``` -# If and only if there is a cut on edge ``(i, j)``, +# If and only if the spin configuration is anti-parallel on edge ``(i, j)``, # this tensor contributes a factor ``x_{\langle i, j\rangle}^{w_{\langle i,j \rangle}}``, # where ``w_{\langle i,j\rangle}`` is the weight of this edge. # Similar to other problems, we can define a polynomial about edges variables by setting ``x_{\langle i, j\rangle} = x``, -# where its k-th coefficient is two times the number of configurations of cut size k. +# where its k-th coefficient is two times the number of configurations with energy (cut size) k. # Its contraction time space complexity is ``2^{{\rm tw}(G)}``, where ``{\rm tw(G)}`` is the [tree-width](https://en.wikipedia.org/wiki/Treewidth) of ``G``. # ## Solving properties -# ### Maximum cut size ``\gamma(G)`` -max_cut_size = solve(problem, SizeMax())[] +# ### Maximum energy ``E^*(G)`` +Emax = solve(problem, SizeMax())[] # ### Counting properties # ##### graph polynomial -# The graph polynomial defined for the cutting problem is +# The graph polynomial defined for the spin glass problem is # ```math -# C(G, x) = \sum_{k=0}^{\gamma(G)} c_k x^k, +# C(G, x) = \sum_{k=0}^{E^*(G)} c_k x^k, # ``` # where ``\alpha(G)`` is the maximum independent set size, -# ``c_k/2`` is the number of cuts of size ``k`` in graph ``G=(V,E)``. +# ``c_k/2`` is the number of anti-parallel edges (cuts) of size ``k`` in graph ``G=(V,E)``. # Since the variable ``x`` is defined on edges, # the coefficients of the polynomial is the number of configurations having different number of anti-parallel edges. max_config = solve(problem, GraphPolynomial())[] # ### Configuration properties -# ##### finding one max cut solution +# ##### finding one solution with highest energy max_vertex_config = solve(problem, SingleConfigMax())[].c.data -max_cut_size_verify = cut_size(graph, max_vertex_config) +Emax_verify = spinglass_energy(graph, max_vertex_config) -# You should see a consistent result as above `max_cut_size`. +# You should see a consistent result as above `Emax`. show_graph(graph; locs=locations, vertex_colors=[ iszero(max_vertex_config[i]) ? "white" : "red" for i=1:nv(graph)], format=:svg) -# where red vertices and white vertices are separated by the cut. +# where a red vertice and a white vertice correspond to a spin having value 1 and 0 respectively. diff --git a/src/GenericTensorNetworks.jl b/src/GenericTensorNetworks.jl index abfbbca8..9ab3c813 100644 --- a/src/GenericTensorNetworks.jl +++ b/src/GenericTensorNetworks.jl @@ -34,7 +34,7 @@ export GraphProblem, optimize_code, NoWeight export flavors, labels, terms, nflavor, get_weights, fixedvertices export IndependentSet, mis_compactify!, is_independent_set export MaximalIS, is_maximal_independent_set -export MaxCut, cut_size +export cut_size, spinglass_energy, SpinGlass export PaintShop, paintshop_from_pairs, num_paint_shop_color_switch, paint_shop_coloring_from_config export Coloring, is_vertex_coloring export Satisfiability, CNF, CNFClause, BoolVar, satisfiable, @bools, ∨, ¬, ∧ @@ -69,6 +69,8 @@ include("deprecate.jl") include("multiprocessing.jl") include("visualize.jl") +Base.@deprecate MaxCut(g::SimpleGraph; weights=NoWeight(), openvertices=(), fixedvertices=Dict{Int,Int}(), optimizer=GreedyMethod(), simplifier=nothing) SpinGlass(g; edge_weights=weights, openvertices, fixedvertices, optimizer, simplifier) + using Requires function __init__() @require CUDA="052768ef-5323-5732-b1bb-66c8b64840ba" include("cuda.jl") diff --git a/src/networks/MaxCut.jl b/src/networks/MaxCut.jl deleted file mode 100644 index d8ef0cd3..00000000 --- a/src/networks/MaxCut.jl +++ /dev/null @@ -1,62 +0,0 @@ -""" - MaxCut{CT<:AbstractEinsum,WT<:Union{NoWieght, Vector}} <: GraphProblem - MaxCut(graph; weights=NoWeight(), openvertices=(), - optimizer=GreedyMethod(), simplifier=nothing, - fixedvertices=Dict() - ) - -The [cutting](https://psychic-meme-f4d866f8.pages.github.io/dev/generated/MaxCut.html) problem (or spin glass problem). - -Positional arguments -------------------------------- -* `graph` is the problem graph. - -Keyword arguments -------------------------------- -* `weights` are associated with the edges of the `graph`. -* `optimizer` and `simplifier` are for tensor network optimization, check [`optimize_code`](@ref) for details. -* `fixedvertices` is a dict to specify the values of degree of freedoms, where a value can be `0` (in one side of the cut) or `1` (in the other side of the cut). -* `openvertices` is a tuple of labels to specify the output tensor. Theses degree of freedoms will not be contracted. -""" -struct MaxCut{CT<:AbstractEinsum,WT<:Union{NoWeight, Vector}} <: GraphProblem - code::CT - graph::SimpleGraph{Int} - weights::WT - fixedvertices::Dict{Int,Int} -end -function MaxCut(g::SimpleGraph; weights=NoWeight(), openvertices=(), fixedvertices=Dict{Int,Int}(), optimizer=GreedyMethod(), simplifier=nothing) - @assert weights isa NoWeight || length(weights) == ne(g) - rawcode = EinCode([[minmax(e.src,e.dst)...] for e in Graphs.edges(g)], collect(Int, openvertices)) # labels for edge tensors - MaxCut(_optimize_code(rawcode, uniformsize_fix(rawcode, 2, fixedvertices), optimizer, simplifier), g, weights, Dict{Int,Int}(fixedvertices)) -end - -flavors(::Type{<:MaxCut}) = [0, 1] -get_weights(gp::MaxCut, i::Int) = [0, gp.weights[i]] -terms(gp::MaxCut) = getixsv(gp.code) -labels(gp::MaxCut) = [1:nv(gp.graph)...] -fixedvertices(gp::MaxCut) = gp.fixedvertices - -function generate_tensors(x::T, gp::MaxCut) where T - ixs = getixsv(gp.code) - tensors = map(enumerate(ixs)) do (i, ix) - maxcutb((Ref(x) .^ get_weights(gp, i)) ...) - end - return select_dims(add_labels!(tensors, ixs, labels(gp)), ixs, fixedvertices(gp)) -end - -function maxcutb(a, b) - return [a b; b a] -end - -""" - cut_size(g::SimpleGraph, config; weights=NoWeight()) - -Compute the cut size from vertex `config` (an iterator). -""" -function cut_size(g::SimpleGraph, config; weights=NoWeight()) - size = zero(eltype(weights)) * false - for (i, e) in enumerate(edges(g)) - size += (config[e.src] != config[e.dst]) * weights[i] - end - return size -end \ No newline at end of file diff --git a/src/networks/SpinGlass.jl b/src/networks/SpinGlass.jl new file mode 100644 index 00000000..318cf7be --- /dev/null +++ b/src/networks/SpinGlass.jl @@ -0,0 +1,90 @@ +""" + SpinGlass{CT<:AbstractEinsum,WT<:Union{NoWieght, Vector}} <: GraphProblem + SpinGlass(graph; edge_weights=NoWeight(), vertex_weights=NoWeight(), openvertices=(), + optimizer=GreedyMethod(), simplifier=nothing, + fixedvertices=Dict() + ) + +The [spin glass](https://psychic-meme-f4d866f8.pages.github.io/dev/generated/SpinGlass.html) problem (or cutting problem). + +Positional arguments +------------------------------- +* `graph` is the problem graph. + +Keyword arguments +------------------------------- +* `edge_weights` are associated with the edges of the `graph`, also known as the coupling strengths in spin glasses. +* `vertex_weights` are associated with the vertices of the `graph`, also known the onsite energy term in spin glasses. +* `optimizer` and `simplifier` are for tensor network optimization, check [`optimize_code`](@ref) for details. +* `fixedvertices` is a dict to specify the values of degree of freedoms, where a value can be `0` (in one side of the cut) or `1` (in the other side of the cut). +* `openvertices` is a tuple of labels to specify the output tensor. Theses degree of freedoms will not be contracted. +""" +struct SpinGlass{CT<:AbstractEinsum,WT1<:Union{NoWeight, Vector},WT2<:Union{ZeroWeight, Vector}} <: GraphProblem + code::CT + graph::SimpleGraph{Int} + edge_weights::WT1 + vertex_weights::WT2 + fixedvertices::Dict{Int,Int} +end +function SpinGlass(g::SimpleGraph; edge_weights=NoWeight(), vertex_weights=ZeroWeight(), openvertices=(), fixedvertices=Dict{Int,Int}(), optimizer=GreedyMethod(), simplifier=nothing) + @assert edge_weights isa NoWeight || length(edge_weights) == ne(g) + @assert vertex_weights isa ZeroWeight || length(vertex_weights) == nv(g) + rawcode = EinCode([ + map(e->[minmax(e.src,e.dst)...], Graphs.edges(g))..., + map(v->[v], Graphs.vertices(g))..., + ], collect(Int, openvertices)) # labels for edge tensors + SpinGlass(_optimize_code(rawcode, uniformsize_fix(rawcode, 2, fixedvertices), optimizer, simplifier), g, edge_weights, vertex_weights, Dict{Int,Int}(fixedvertices)) +end + +flavors(::Type{<:SpinGlass}) = [0, 1] +# first `ne` indices are for edge weights, last `nv` indices are for vertex weights. +get_weights(gp::SpinGlass, i::Int) = i <= ne(gp.graph) ? [0, gp.edge_weights[i]] : [0, gp.vertex_weights[i-ne(gp.graph)]] +terms(gp::SpinGlass) = getixsv(gp.code) +labels(gp::SpinGlass) = [1:nv(gp.graph)...] +fixedvertices(gp::SpinGlass) = gp.fixedvertices + +function generate_tensors(x::T, gp::SpinGlass) where T + ixs = getixsv(gp.code) + l = ne(gp.graph) + tensors = [ + Array{T}[spinglassb((Ref(x) .^ get_weights(gp, i)) ...) for i=1:l]..., + add_labels!(Array{T}[Ref(x) .^ get_weights(gp, i+l) for i=1:nv(gp.graph)], ixs[l+1:end], labels(gp))... + ] + return select_dims(tensors, ixs, fixedvertices(gp)) +end + +function spinglassb(a, b) + return [a b; b a] +end + +""" + spinglass_energy(g::SimpleGraph, config; edge_weights=NoWeight(), vertex_weights=ZeroWeight()) + +Compute the spin glass state energy for the vertex configuration `config` (an iterator). +""" +function spinglass_energy(g::SimpleGraph, config; edge_weights=NoWeight(), vertex_weights=ZeroWeight()) + size = zero(eltype(edge_weights)) * false + # coupling terms + for (i, e) in enumerate(edges(g)) + size += (config[e.src] != config[e.dst]) * edge_weights[i] + end + # onsite terms + for (i, v) in enumerate(vertices(g)) + size += config[v] * vertex_weights[i] + end + return size +end + + +""" + cut_size(g::SimpleGraph, config; weights=NoWeight()) + +Compute the cut size for the vertex configuration `config` (an iterator). +""" +function cut_size(g::SimpleGraph, config; weights=NoWeight()) + size = zero(eltype(weights)) * false + for (i, e) in enumerate(edges(g)) + size += (config[e.src] != config[e.dst]) * weights[i] + end + return size +end \ No newline at end of file diff --git a/src/networks/networks.jl b/src/networks/networks.jl index c0433a18..106c8612 100644 --- a/src/networks/networks.jl +++ b/src/networks/networks.jl @@ -9,6 +9,10 @@ struct NoWeight end Base.getindex(::NoWeight, i) = 1 Base.eltype(::NoWeight) = Int +struct ZeroWeight end +Base.getindex(::ZeroWeight, i) = 0 +Base.eltype(::ZeroWeight) = Int + ######## Interfaces for graph problems ########## """ get_weights(problem::GraphProblem, sym) -> Vector @@ -109,7 +113,7 @@ generate_tensors(::Type{GT}) where GT = length(flavors(GT)) include("IndependentSet.jl") include("MaximalIS.jl") -include("MaxCut.jl") +include("SpinGlass.jl") include("Matching.jl") include("Coloring.jl") include("PaintShop.jl") diff --git a/test/networks/MaxCut.jl b/test/networks/MaxCut.jl deleted file mode 100644 index 52d1be25..00000000 --- a/test/networks/MaxCut.jl +++ /dev/null @@ -1,53 +0,0 @@ -using GenericTensorNetworks, Test, Graphs -using GenericTensorNetworks: max_size - -@testset "graph utils" begin - g2 = SimpleGraph(3) - add_edge!(g2, 1,2) - for g in [smallgraph(:petersen), g2] - gp = MaxCut(g) - mc = max_size(gp) - config = solve(gp, SingleConfigMax())[].c.data - @test cut_size(g, config) == mc - end - g = smallgraph(:petersen) - # weighted - weights = collect(1:ne(g)) - gp = MaxCut(g; weights=weights) - mc = max_size(gp) - config = solve(gp, SingleConfigMax())[].c.data - @test solve(gp, CountingMax())[].c == 2 - @test cut_size(g, config; weights=weights) == mc -end - -@testset "spinglass" begin - g = SimpleGraph(5) - for (i,j) in [(1,2),(2,3),(3,4),(4,1),(1,5),(2,4)] - add_edge!(g, i, j) - end - @test graph_polynomial(MaxCut(g), Val(:polynomial))[] == Polynomial([2,2,4,12,10,2]) - @test graph_polynomial(MaxCut(g), Val(:finitefield))[] == Polynomial([2,2,4,12,10,2]) -end - -@testset "enumerating - max cut" begin - g = SimpleGraph(5) - for (i,j) in [(1,2),(2,3),(3,4),(4,1),(1,5),(2,4)] - add_edge!(g, i, j) - end - code = MaxCut(g; optimizer=GreedyMethod()) - res = GenericTensorNetworks.best_solutions(code; all=true)[] - @test length(res.c.data) == 2 - @test cut_size(g, res.c.data[1]) == 5 -end - -@testset "fix vertices - max vut" begin - g = SimpleGraph(5) - for (i,j) in [(1,2),(2,3),(3,4),(4,1),(1,5),(2,4)] - add_edge!(g, i, j) - end - fixedvertices = Dict(1=>1, 4=>0) - problem = MaxCut(g, fixedvertices=fixedvertices) - optimal_config = solve(problem, ConfigsMax())[].c - @test length(optimal_config) == 1 - @test optimal_config[1] == StaticBitVector(Array{Bool, 1}([1, 0, 1, 0, 0])) -end diff --git a/test/networks/SpinGlass.jl b/test/networks/SpinGlass.jl new file mode 100644 index 00000000..6475b6f4 --- /dev/null +++ b/test/networks/SpinGlass.jl @@ -0,0 +1,73 @@ +using GenericTensorNetworks, Test, Graphs +using GenericTensorNetworks: max_size, graph_polynomial + +@testset "graph utils" begin + g2 = SimpleGraph(3) + add_edge!(g2, 1,2) + for g in [smallgraph(:petersen), g2] + gp = SpinGlass(g) + mc = max_size(gp) + config = solve(gp, SingleConfigMax())[].c.data + @test spinglass_energy(g, config) == mc + end + g = smallgraph(:petersen) + # weighted + weights = collect(1:ne(g)) + gp = SpinGlass(g; edge_weights=weights) + mc = max_size(gp) + config = solve(gp, SingleConfigMax())[].c.data + @test solve(gp, CountingMax())[].c == 2 + @test spinglass_energy(g, config; edge_weights=weights) == mc + + # weighted Max-Cut + weights = collect(1:ne(g)) + gp = MaxCut(g; weights) + mc = max_size(gp) + config = solve(gp, SingleConfigMax())[].c.data + @test solve(gp, CountingMax())[].c == 2 + @test cut_size(g, config; weights) == mc +end + +@testset "SpinGlass" begin + g = SimpleGraph(5) + for (i,j) in [(1,2),(2,3),(3,4),(4,1),(1,5),(2,4)] + add_edge!(g, i, j) + end + @test graph_polynomial(SpinGlass(g), Val(:polynomial))[] == Polynomial([2,2,4,12,10,2]) + @test graph_polynomial(SpinGlass(g), Val(:finitefield))[] == Polynomial([2,2,4,12,10,2]) +end + +@testset "enumerating - max cut" begin + g = SimpleGraph(5) + for (i,j) in [(1,2),(2,3),(3,4),(4,1),(1,5),(2,4)] + add_edge!(g, i, j) + end + code = SpinGlass(g; optimizer=GreedyMethod()) + res = GenericTensorNetworks.best_solutions(code; all=true)[] + @test length(res.c.data) == 2 + @test spinglass_energy(g, res.c.data[1]) == 5 +end + +@testset "fix vertices - max vut" begin + g = SimpleGraph(5) + for (i,j) in [(1,2),(2,3),(3,4),(4,1),(1,5),(2,4)] + add_edge!(g, i, j) + end + fixedvertices = Dict(1=>1, 4=>0) + problem = SpinGlass(g, fixedvertices=fixedvertices) + optimal_config = solve(problem, ConfigsMax())[].c + @test length(optimal_config) == 1 + @test optimal_config[1] == StaticBitVector(Array{Bool, 1}([1, 0, 1, 0, 0])) +end + +@testset "vertex weights" begin + g = smallgraph(:petersen) + edge_weights = collect(1:ne(g)) + vertex_weights = collect(1:nv(g)) + gp = SpinGlass(g; edge_weights, vertex_weights) + + mc = max_size(gp) + config = solve(gp, SingleConfigMax())[].c.data + @test solve(gp, CountingMax())[].c == 1 + @test spinglass_energy(g, config; edge_weights, vertex_weights) == mc +end \ No newline at end of file diff --git a/test/networks/networks.jl b/test/networks/networks.jl index f6a57d51..ef2c470b 100644 --- a/test/networks/networks.jl +++ b/test/networks/networks.jl @@ -15,7 +15,7 @@ end include("IndependentSet.jl") include("MaximalIS.jl") -include("MaxCut.jl") +include("SpinGlass.jl") include("PaintShop.jl") include("Coloring.jl") include("Matching.jl")