diff --git a/README.md b/README.md index 6e6db5d0..6d56cb9c 100644 --- a/README.md +++ b/README.md @@ -50,15 +50,15 @@ julia> using GraphTensorNetworks, Random, Graphs julia> graph = (Random.seed!(2); Graphs.smallgraph(:petersen)) {10, 15} undirected simple Int64 graph -julia> problem = Independence(graph; optimizer=TreeSA(sc_target=0, sc_weight=1.0, ntrials=10, βs=0.01:0.1:15.0, niters=20, rw_weight=0.2)); +julia> problem = IndependentSet(graph; optimizer=TreeSA(sc_target=0, sc_weight=1.0, ntrials=10, βs=0.01:0.1:15.0, niters=20, rw_weight=0.2)); ┌ Warning: target space complexity not found, got: 4.0, with time complexity 7.965784284662087, read-right complexity 8.661778097771988. └ @ OMEinsumContractionOrders ~/.julia/dev/OMEinsumContractionOrders/src/treesa.jl:71 time/space complexity is (7.965784284662086, 4.0) ``` -Here, the `problem` is a `Independence` instance, it contains the tensor network contraction tree for the target graph. +Here, the `problem` is a `IndependentSet` instance, it contains the tensor network contraction tree for the target graph. Here, we choose the `TreeSA` optimizer to optimize the tensor network contraciton tree, it is a local search based algorithm, check [arXiv: 2108.05665](https://arxiv.org/abs/2108.05665). You will see some warnings, do not panic, this is because we set `sc_target` (target space complex) to 1 for agressive optimization of space complexity. Type `?TreeSA` in a Julia REPL for more information about the key word arguments. -Similarly, one can select tensor network structures for solving other problems like `MaximalIndependence`, `MaxCut`, `Matching`, `Coloring{K}`, `PaintShop` and `set_packing`. +Similarly, one can select tensor network structures for solving other problems like `MaximalIS`, `MaxCut`, `Matching`, `Coloring{K}`, `PaintShop` and `set_packing`. #### 1. find MIS size, count MISs and count ISs ```julia diff --git a/docs/make.jl b/docs/make.jl index 2833342d..4bda2da6 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -6,13 +6,12 @@ using DocThemeIndigo using Literate for each in readdir(pkgdir(GraphTensorNetworks, "examples")) - project_dir = pkgdir(GraphTensorNetworks, "examples", each) - isdir(project_dir) || continue - @info "building" project_dir - input_file = pkgdir(GraphTensorNetworks, "examples", each, "main.jl") + input_file = pkgdir(GraphTensorNetworks, "examples", each) + endswith(input_file, ".jl") || continue + @info "building" input_file output_dir = pkgdir(GraphTensorNetworks, "docs", "src", "tutorials") @info "executing" input_file - Literate.markdown(input_file, output_dir; name=each, execute=false) + Literate.markdown(input_file, output_dir; name=each[1:end-3], execute=false) end indigo = DocThemeIndigo.install(GraphTensorNetworks) @@ -24,17 +23,17 @@ makedocs(; repo="https://github.com/Happy-Diode/GraphTensorNetworks.jl/blob/{commit}{path}#{line}", sitename="GraphTensorNetworks.jl", format=Documenter.HTML(; - prettyurls=get(ENV, "CI", "false") == "true", + prettyurls=false, canonical="https://Happy-Diode.github.io/GraphTensorNetworks.jl", assets=String[indigo], ), pages=[ "Home" => "index.md", "Tutorials" => [ - "Independent set problem" => "tutorials/Independence.md", - "Maximal independent set problem" => "tutorials/MaximalIndependence.md", + "Independent set problem" => "tutorials/IndependentSet.md", + "Maximal independent set problem" => "tutorials/MaximalIS.md", "Cutting problem" => "tutorials/MaxCut.md", - "Matching problem" => "tutorials/Coloring.md", + "Matching problem" => "tutorials/Matching.md", "Binary paint shop problem" => "tutorials/PaintShop.md", "Coloring problem" => "tutorials/Coloring.md", "Other problems" => "tutorials/Others.md", diff --git a/docs/src/ref.md b/docs/src/ref.md index 19d3eaa9..d2eca105 100644 --- a/docs/src/ref.md +++ b/docs/src/ref.md @@ -3,8 +3,8 @@ ```@docs solve GraphProblem -Independence -MaximalIndependence +IndependentSet +MaximalIS Matching Coloring MaxCut diff --git a/examples/Coloring.jl b/examples/Coloring.jl new file mode 100644 index 00000000..a9efce3e --- /dev/null +++ b/examples/Coloring.jl @@ -0,0 +1,50 @@ +# # Coloring problem + +# !!! note +# This tutorial only covers the coloring problem specific features, +# It is recommended to read the [Independent set problem](@ref) tutorial too to know more about +# * how to optimize the tensor network contraction order, +# * what are the other graph properties computable, +# * how to select correct method to compute graph properties, +# * how to compute weighted graphs and handle open vertices. + +# ## Problem definition +# A [vertex coloring](https://en.wikipedia.org/wiki/Graph_coloring) is an assignment of labels or colors to each vertex of a graph such that no edge connects two identically colored vertices. +# In the following, we are going to defined a 3-coloring problem for the Petersen graph. + +using GraphTensorNetworks, Graphs + +graph = Graphs.smallgraph(:petersen) + +# We can visualize this graph using the following function +rot15(a, b, i::Int) = cos(2i*π/5)*a + sin(2i*π/5)*b, cos(2i*π/5)*b - sin(2i*π/5)*a + +locations = [[rot15(0.0, 1.0, i) for i=0:4]..., [rot15(0.0, 0.6, i) for i=0:4]...] + +show_graph(graph; locs=locations) + +# ## Tensor network representation +# Type [`Coloring`](@ref) can be used for constructing the tensor network with optimized contraction order for a coloring problem. +# Let us use 3-colouring problem defined on vertices as an example. +# For a vertex ``v``, we define the degree of freedoms ``c_v\in\{1,2,3\}`` and a vertex tensor labelled by it as +# ```math +# W(v) = \left(\begin{matrix} +# r_v\\ +# g_v\\ +# b_v +# \end{matrix}\right). +# ``` +# For an edge ``(u, v)``, we define an edge tensor as a matrix labelled by ``(c_u, c_v)`` to specify the constraint +# ```math +# B = \left(\begin{matrix} +# 0 & 1 & 1\\ +# 1 & 0 & 1\\ +# 1 & 1 & 0 +# \end{matrix}\right). +# ``` +# The number of possible colouring can be obtained by contracting this tensor network by setting vertex tensor elements ``r_v, g_v`` and ``b_v`` to 1. +# +# We construct the tensor network for the 3-coloring problem as +problem = Coloring{3}(graph); + +# ## Solving properties \ No newline at end of file diff --git a/examples/Coloring/main.jl b/examples/Coloring/main.jl deleted file mode 100644 index 3c28aedf..00000000 --- a/examples/Coloring/main.jl +++ /dev/null @@ -1,31 +0,0 @@ -# # Coloring problem - -# !!! note -# This tutorial only covers the coloring problem specific features, -# It is recommended to read the [Independent set problem](@ref) tutorial too to know more about -# * how to optimize the tensor network contraction order, -# * what are the other graph properties computable, -# * how to select correct method to compute graph properties, -# * how to compute weighted graphs and handle open vertices. - -# ## Introduction -using GraphTensorNetworks, Graphs - -# Please check the docstring of [`Coloring`](@ref) for the definition of the vertex cutting problem. -@doc Coloring - -# In the following, we are going to defined a 3-coloring problem for the Petersen graph. - -graph = Graphs.smallgraph(:petersen) - -# We can visualize this graph using the following function -rot15(a, b, i::Int) = cos(2i*π/5)*a + sin(2i*π/5)*b, cos(2i*π/5)*b - sin(2i*π/5)*a - -locations = [[rot15(0.0, 1.0, i) for i=0:4]..., [rot15(0.0, 0.6, i) for i=0:4]...] - -show_graph(graph; locs=locations) - -# Then we define the cutting problem as -problem = Coloring{3}(graph); - -# ## Solving properties \ No newline at end of file diff --git a/examples/Independence/main.jl b/examples/IndependentSet.jl similarity index 70% rename from examples/Independence/main.jl rename to examples/IndependentSet.jl index ef9b88b6..426b034a 100644 --- a/examples/Independence/main.jl +++ b/examples/IndependentSet.jl @@ -1,13 +1,11 @@ # # Independent set problem -# ## Introduction -using GraphTensorNetworks, Graphs - -# Please check the docstring of [`Independence`](@ref) for the definition of independence problem. -@doc Independence - +# ## Problem definition +# In graph theory, an [independent set](https://en.wikipedia.org/wiki/Independent_set_(graph_theory)) is a set of vertices in a graph, no two of which are adjacent. # In the following, we are going to defined an independent set problem for the Petersen graph. +using GraphTensorNetworks, Graphs + graph = Graphs.smallgraph(:petersen) # We can visualize this graph using the following function @@ -17,17 +15,38 @@ locations = [[rot15(0.0, 1.0, i) for i=0:4]..., [rot15(0.0, 0.6, i) for i=0:4].. show_graph(graph; locs=locations) +# ## Tensor network representation +# Type [`IndependentSet`](@ref) can be used for constructing the tensor network with optimized contraction order for solving an independent set problem. +# we map a vertex ``i\in V`` to a label ``s_i \in \{0, 1\}`` of dimension 2, +# where we use 0 (1) to denote a vertex is absent (present) in the set. +# For each label ``s_i``, we defined a parametrized rank-one vertex tensor ``W(x_i)`` as +# ```math +# W(x_i)_{s_i} = \left(\begin{matrix} +# 1 \\ +# x_i +# \end{matrix}\right)_{s_i} +# ``` +# We use subscripts to index tensor elements, e.g.``W(x_i)_0=1`` is the first element associated +# with ``s_i=0`` and ``W(x_i)_1=x_i`` is the second element associated with ``s_i=1``. +# Similarly, on each edge ``(u, v)``, we define a matrix ``B`` indexed by ``s_u`` and ``s_v`` as +# ```math +# B_{s_i s_j} = \left(\begin{matrix} +# 1 & 1\\ +# 1 & 0 +# \end{matrix}\right)_{s_is_j} +# ``` # Let us contruct the problem instance with optimized tensor network contraction order as bellow. -problem = Independence(graph; optimizer=TreeSA(sc_weight=1.0, ntrials=10, +problem = IndependentSet(graph; optimizer=TreeSA(sc_weight=1.0, ntrials=10, βs=0.01:0.1:15.0, niters=20, rw_weight=0.2), simplifier=MergeGreedy()); -# The `optimizer` is for optimizing the contraction orders. +# In the input arguments of [`IndependentSet`](@ref), the `optimizer` is for optimizing the contraction orders. # Here we use the local search based optimizer in [arXiv:2108.05665](https://arxiv.org/abs/2108.05665). # If no optimizer is specified, the default fast (in terms of the speed of searching contraction order) # but worst (in term of contraction complexity) [`GreedyMethod`](@ref) will be used. # `simplifier` is a preprocessing routine to speed up the `optimizer`. -# Please check section [Tensor Network](@ref) for more details. +# The returned instance `problem` contains a field `code` that specifies the tensor network contraction order. +# 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``. # One can check the time, space and read-write complexity with the following function. timespacereadwrite_complexity(problem) @@ -48,7 +67,13 @@ count_all_independent_sets = solve(problem, CountingAll())[] count_max2_independent_sets = solve(problem, CountingMax(2))[] # ##### independence polynomial -# For the definition of independence polynomial, please check the docstring of [`Independence`](@ref) or this [wiki page](https://mathworld.wolfram.com/IndependencePolynomial.html). +# The graph polynomial defined for the independence problem is known as the independence polynomial. +# ```math +# I(G, x) = \sum_{k=0}^{\alpha(G)} a_k x^k, +# ``` +# where ``\alpha(G)`` is the maximum independent set size, +# ``a_k`` is the number of independent sets of size ``k`` in graph ``G=(V,E)``. +# The total number of independent sets is thus equal to ``I(G, 1)``. # There are 3 methods to compute a graph polynomial, `:finitefield`, `:fft` and `:polynomial`. # These methods are introduced in the docstring of [`GraphPolynomial`](@ref). independence_polynomial = solve(problem, GraphPolynomial(; method=:finitefield))[] @@ -98,9 +123,9 @@ loaded_sets = load_configs(filename; format=:binary, bitlength=10) # Because the bitstring length is not stored. # ## Weights and open vertices -# [`Independence`] accepts weights as a key word argument. +# [`IndependentSet`] accepts weights as a key word argument. # The following code computes the weighted MIS problem. -problem = Independence(graph; weights=collect(1:10)) +problem = IndependentSet(graph; weights=collect(1:10)) max_config_weighted = solve(problem, SingleConfigMax())[] @@ -108,7 +133,7 @@ show_graph(graph; locs=locations, colors= [iszero(max_config_weighted.c.data[i]) ? "white" : "red" for i=1:nv(graph)]) # The following code computes the MIS tropical tensor (reference to be added) with open vertices 1 and 2. -problem = Independence(graph; openvertices=[1,2,3]) +problem = IndependentSet(graph; openvertices=[1,2,3]) mis_tropical_tensor = solve(problem, SizeMax()) diff --git a/examples/Matching.jl b/examples/Matching.jl new file mode 100644 index 00000000..c765ab32 --- /dev/null +++ b/examples/Matching.jl @@ -0,0 +1,56 @@ +# # Vertex matching problem + +# !!! note +# This tutorial only covers the vertex matching problem specific features, +# It is recommended to read the [Independent set problem](@ref) tutorial too to know more about +# * how to optimize the tensor network contraction order, +# * what are the other graph properties computable, +# * how to select correct method to compute graph properties, +# * how to compute weighted graphs and handle open vertices. + +# ## Problem definition +# A ``k``-matching in a graph ``G`` is a set of k edges, no two of which have a vertex in common. + +using GraphTensorNetworks, Graphs + +# In the following, we are going to defined a matching problem for the Petersen graph. + +graph = Graphs.smallgraph(:petersen) + +# We can visualize this graph using the following function +rot15(a, b, i::Int) = cos(2i*π/5)*a + sin(2i*π/5)*b, cos(2i*π/5)*b - sin(2i*π/5)*a + +locations = [[rot15(0.0, 1.0, i) for i=0:4]..., [rot15(0.0, 0.6, i) for i=0:4]...] + +show_graph(graph; locs=locations) + +# ## Tensor network representation +# Type [`Matching`](@ref) can be used for constructing the tensor network with optimized contraction order for a matching problem. +# We map an edge ``(u, v) \in E`` to a label ``\langle u, v\rangle \in \{0, 1\}`` in a tensor network, +# where 1 means two vertices of an edge are matched, 0 means otherwise. +# Then we define a tensor of rank ``d(v) = |N(v)|`` on vertex ``v`` such that, +# ```math +# W_{\langle v, n_1\rangle, \langle v, n_2 \rangle, \ldots, \langle v, n_{d(v)}\rangle} = \begin{cases} +# 1, & \sum_{i=1}^{d(v)} \langle v, n_i \rangle \leq 1,\\ +# 0, & \text{otherwise}, +# \end{cases} +# ``` +# and a tensor of rank 1 on the bond +# ```math +# B_{\langle v, w\rangle} = \begin{cases} +# 1, & \langle v, w \rangle = 0 \\ +# x, & \langle v, w \rangle = 1, +# \end{cases} +# ``` +# where label ``\langle v, w \rangle`` is equivalent to ``\langle w,v\rangle``. +# +# We construct the tensor network for the matching problem by typing +problem = Matching(graph); + +# ## Solving properties +# The graph polynomial defined for the independence problem is known as the matching polynomial. +# Here, we adopt the first definition in the [wiki page](https://en.wikipedia.org/wiki/Matching_polynomial). +# ```math +# M(G, x) = \sum\limits_{k=1}^{|V|/2} c_k x^k, +# ``` +# where ``k`` is the number of matches, and coefficients ``c_k`` are the corresponding counting. \ No newline at end of file diff --git a/examples/Matching/main.jl b/examples/Matching/main.jl deleted file mode 100644 index af7a5f53..00000000 --- a/examples/Matching/main.jl +++ /dev/null @@ -1,31 +0,0 @@ -# # Vertex matching problem - -# !!! note -# This tutorial only covers the vertex matching problem specific features, -# It is recommended to read the [Independent set problem](@ref) tutorial too to know more about -# * how to optimize the tensor network contraction order, -# * what are the other graph properties computable, -# * how to select correct method to compute graph properties, -# * how to compute weighted graphs and handle open vertices. - -# ## Introduction -using GraphTensorNetworks, Graphs - -# Please check the docstring of [`Matching`](@ref) for the definition of the vertex matching problem. -@doc Matching - -# In the following, we are going to defined a matching problem for the Petersen graph. - -graph = Graphs.smallgraph(:petersen) - -# We can visualize this graph using the following function -rot15(a, b, i::Int) = cos(2i*π/5)*a + sin(2i*π/5)*b, cos(2i*π/5)*b - sin(2i*π/5)*a - -locations = [[rot15(0.0, 1.0, i) for i=0:4]..., [rot15(0.0, 0.6, i) for i=0:4]...] - -show_graph(graph; locs=locations) - -# Then we define the matching problem as -problem = Matching(graph); - -# ## Solving properties \ No newline at end of file diff --git a/examples/MaxCut.jl b/examples/MaxCut.jl new file mode 100644 index 00000000..449354c1 --- /dev/null +++ b/examples/MaxCut.jl @@ -0,0 +1,75 @@ +# # Cutting problem (Spin-glass problem) + +# !!! note +# This tutorial only covers the cutting problem specific features, +# It is recommended to read the [Independent set problem](@ref) tutorial too to know more about +# * how to optimize the tensor network contraction order, +# * what are the other graph properties computable, +# * how to select correct method to compute graph properties, +# * how to compute weighted graphs and handle open vertices. + +# ## 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, +# where the size of a cut is the number of edges (or the sum of weights on edges) crossing the cut. + +using GraphTensorNetworks, Graphs + +# In the following, we are going to defined an cutting problem for the Petersen graph. + +graph = Graphs.smallgraph(:petersen) + +# We can visualize this graph using the following function +rot15(a, b, i::Int) = cos(2i*π/5)*a + sin(2i*π/5)*b, cos(2i*π/5)*b - sin(2i*π/5)*a + +locations = [[rot15(0.0, 1.0, i) for i=0:4]..., [rot15(0.0, 0.6, i) for i=0:4]...] + +show_graph(graph; locs=locations) + +# ## Tensor network representation +# 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`` +# ```math +# B(x_{\langle i, j\rangle}) = \left(\begin{matrix} +# 1 & x_{\langle i, j\rangle}\\ +# x_{\langle i, j\rangle} & 1 +# \end{matrix}\right), +# ``` +# where variable ``x_{\langle i, j\rangle}`` represents a cut on edge ``(i, j)`` or a domain wall of an Ising spin glass. +# 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. +# We define the cutting problem as +problem = MaxCut(graph); + +# 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())[] + +# ### Counting properties +# ##### graph polynomial +# The graph polynomial defined for the cutting problem is +# ```math +# C(G, x) = \sum_{k=0}^{\gamma(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)``. +# 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 +max_edge_config = solve(problem, SingleConfigMax())[] + +# These configurations are defined on edges, we need to find a valid assignment on vertices +max_vertex_config = cut_assign(graph, max_edge_config.c.data) + +max_cut_size_verify = cut_size(graph, max_vertex_config) + +# You should see a consistent result as above `max_cut_size`. + +show_graph(graph; locs=locations, colors=[ + iszero(max_vertex_config[i]) ? "white" : "red" for i=1:nv(graph)]) \ No newline at end of file diff --git a/examples/MaxCut/main.jl b/examples/MaxCut/main.jl deleted file mode 100644 index 25e47db5..00000000 --- a/examples/MaxCut/main.jl +++ /dev/null @@ -1,54 +0,0 @@ -# # Cutting problem (Spin-glass problem) - -# !!! note -# This tutorial only covers the cutting problem specific features, -# It is recommended to read the [Independent set problem](@ref) tutorial too to know more about -# * how to optimize the tensor network contraction order, -# * what are the other graph properties computable, -# * how to select correct method to compute graph properties, -# * how to compute weighted graphs and handle open vertices. - -# ## Introduction -using GraphTensorNetworks, Graphs - -# Please check the docstring of [`MaxCut`](@ref) for the definition of the vertex cutting problem. -@doc MaxCut - -# In the following, we are going to defined an cutting problem for the Petersen graph. - -graph = Graphs.smallgraph(:petersen) - -# We can visualize this graph using the following function -rot15(a, b, i::Int) = cos(2i*π/5)*a + sin(2i*π/5)*b, cos(2i*π/5)*b - sin(2i*π/5)*a - -locations = [[rot15(0.0, 1.0, i) for i=0:4]..., [rot15(0.0, 0.6, i) for i=0:4]...] - -show_graph(graph; locs=locations) - -# Then we define the cutting problem as -problem = MaxCut(graph); - -# ## Solving properties - -# ### Maximum cut size ``\gamma(G)`` -max_cut_size = solve(problem, SizeMax())[] - -# ### Counting properties -# ##### graph polynomial -# Since the variable ``x`` is defined on edges, -# hence 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 -max_edge_config = solve(problem, SingleConfigMax())[] - -# These configurations are defined on edges, we need to find a valid assignment on vertices -max_vertex_config = cut_assign(graph, max_edge_config.c.data) - -max_cut_size_verify = cut_size(graph, max_vertex_config) - -# You should see a consistent result as above `max_cut_size`. - -show_graph(graph; locs=locations, colors=[ - iszero(max_vertex_config[i]) ? "white" : "red" for i=1:nv(graph)]) \ No newline at end of file diff --git a/examples/MaximalIS.jl b/examples/MaximalIS.jl new file mode 100644 index 00000000..aa7e572f --- /dev/null +++ b/examples/MaximalIS.jl @@ -0,0 +1,66 @@ +# # Maximal independent set problem + +# !!! note +# This tutorial only covers the maximal independent set problem specific features, +# It is recommended to read the [Independent set problem](@ref) tutorial too to know more about +# * how to optimize the tensor network contraction order, +# * what are the other graph properties computable, +# * how to select correct method to compute graph properties, +# * how to compute weighted graphs and handle open vertices. + +# ## Problem definition +using GraphTensorNetworks, Graphs + +# In graph theory, a [maximal independent set](https://en.wikipedia.org/wiki/Maximal_independent_set) is an independent set that is not a subset of any other independent set. +# It is different from maximum independent set because it does not require the set to have the max size. + +# In the following, we are going to solve the maximal independent set problem for the Petersen graph. + +graph = Graphs.smallgraph(:petersen) + +# We can visualize this graph using the following function +rot15(a, b, i::Int) = cos(2i*π/5)*a + sin(2i*π/5)*b, cos(2i*π/5)*b - sin(2i*π/5)*a + +locations = [[rot15(0.0, 1.0, i) for i=0:4]..., [rot15(0.0, 0.6, i) for i=0:4]...] + +show_graph(graph; locs=locations) + +# ## Tensor network representation +# For a vertex ``v\in V``, we define a boolean degree of freedom ``s_v\in\{0, 1\}``. +# We defined the restriction on its neighbourhood ``N[v]``: +# ```math +# T(x_v)_{s_1,s_2,\ldots,s_{|N(v)|},s_v} = \begin{cases} +# s_vx_v & s_1=s_2=\ldots=s_{|N(v)|}=0,\\ +# 1-s_v& \text{otherwise}. +# \end{cases} +# ``` +# Intuitively, it means if all the neighbourhood vertices are not in ``I_{m}``, i.e., ``s_1=s_2=\ldots=s_{|N(v)|}=0``, then ``v`` should be in ``I_{m}`` and contribute a factor ``x_{v}``, +# otherwise, if any of the neighbourhood vertices is in ``I_{m}``, then ``v`` cannot be in ``I_{m}``. +# We construct the tensor network for the maximal independent set problem as +problem = MaximalIS(graph); + +# Its contraction time space complexity of a [`MaximalIS`](@ref) instance is no longer determined by the tree-width of the original graph ``G``. +# It is often harder to contract this tensor network than to contract the one for regular independent set problem. + +# ## Solving properties + +# ### Counting properties +# ##### maximal independence polynomial +# The graph polynomial defined for the maximal independent set problem is +# ```math +# I_{\rm max}(G, x) = \sum_{k=0}^{\alpha(G)} b_k x^k, +# ``` +# where ``b_k`` is the number of maximal independent sets of size ``k`` in graph ``G=(V, E)``. + +max_config = solve(problem, GraphPolynomial())[] + +# Since it only counts the maximal independent sets, the first several coefficients are 0. + +# ### Configuration properties +# ##### finding all maximal independent set +max_edge_config = solve(problem, ConfigsAll())[] + +# This result should be consistent with that given by the [Bron Kerbosch algorithm](https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm) on the complement of Petersen graph. +maximal_cliques = maximal_cliques(complement(graph)) + +# For sparse graphs, the generic tensor network approach is usually much faster and memory efficient. \ No newline at end of file diff --git a/examples/MaximalIndependence/main.jl b/examples/MaximalIndependence/main.jl deleted file mode 100644 index 7502f3ec..00000000 --- a/examples/MaximalIndependence/main.jl +++ /dev/null @@ -1,50 +0,0 @@ -# # Maximal independent set problem - -# !!! note -# This tutorial only covers the maximal independent set problem specific features, -# It is recommended to read the [Independent set problem](@ref) tutorial too to know more about -# * how to optimize the tensor network contraction order, -# * what are the other graph properties computable, -# * how to select correct method to compute graph properties, -# * how to compute weighted graphs and handle open vertices. - -# ## Introduction -using GraphTensorNetworks, Graphs - -# Please check the docstring of [`MaximalIndependence`](@ref) for the definition of the maximal independence problem. -@doc MaximalIndependence - -# In the following, we are going to defined an cutting problem for the Petersen graph. - -graph = Graphs.smallgraph(:petersen) - -# We can visualize this graph using the following function -rot15(a, b, i::Int) = cos(2i*π/5)*a + sin(2i*π/5)*b, cos(2i*π/5)*b - sin(2i*π/5)*a - -locations = [[rot15(0.0, 1.0, i) for i=0:4]..., [rot15(0.0, 0.6, i) for i=0:4]...] - -show_graph(graph; locs=locations) - -# Then we define the maximal independent set problem as -problem = MaximalIndependence(graph); - -# The tensor network structure is different from that of [`Independence`](@ref), -# Its tensor is defined on a vertex and its neighbourhood, -# and it makes the contraction of [`MaximalIndependence`](@ref) much harder. - -# ## Solving properties - -# ### Counting properties -# ##### maximal independence polynomial -max_config = solve(problem, GraphPolynomial())[] - -# Since it only counts the maximal independent sets, the first several coefficients are 0. - -# ### Configuration properties -# ##### finding all maximal independent set -max_edge_config = solve(problem, ConfigsAll())[] - -# This result should be consistent with that given by the [Bron Kerbosch algorithm](https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm) on the complement of Petersen graph. -maximal_cliques = maximal_cliques(complement(graph)) - -# For sparse graphs, the generic tensor network approach is usually much faster and memory efficient. \ No newline at end of file diff --git a/examples/Others/main.jl b/examples/Others.jl similarity index 100% rename from examples/Others/main.jl rename to examples/Others.jl diff --git a/examples/PaintShop/main.jl b/examples/PaintShop.jl similarity index 81% rename from examples/PaintShop/main.jl rename to examples/PaintShop.jl index 38a3da8a..4a9c90ba 100644 --- a/examples/PaintShop/main.jl +++ b/examples/PaintShop.jl @@ -8,12 +8,11 @@ # * how to select correct method to compute graph properties, # * how to compute weighted graphs and handle open vertices. -# ## Introduction -using GraphTensorNetworks, Graphs - -# Please check the docstring of [`PaintShop`](@ref) for the definition of the binary paint shop problem. -@doc PaintShop +# ## Problme Definition +# The [binary paint shop problem](http://m-hikari.com/ams/ams-2012/ams-93-96-2012/popovAMS93-96-2012-2.pdf). # In the following, we are going to defined a binary paint shop problem for the following string +using GraphTensorNetworks, Graphs + sequence = "abaccb" \ No newline at end of file diff --git a/notebooks/tropicalwidget_simple.jl b/notebooks/tropicalwidget_simple.jl index 758a13ed..fe6b924b 100644 --- a/notebooks/tropicalwidget_simple.jl +++ b/notebooks/tropicalwidget_simple.jl @@ -50,7 +50,7 @@ graph = unit_disk_graph(locs, 0.23) # SimpleGraph vizconfig(graph; locs=locs, config=rand(Bool, 12), graphsize=8cm) # ╔═╡ e068f0b8-7b2c-49b2-94d1-83dead406326 -gp = Independence(graph; outputs=(1,2,3,4)) +gp = IndependentSet(graph; outputs=(1,2,3,4)) # ╔═╡ 90c88c7f-e0c7-4845-a8b9-7f7562bd256f solve(gp, :config_single) diff --git a/notebooks/tutorial.jl b/notebooks/tutorial.jl index c66c60cf..1d0829b4 100644 --- a/notebooks/tutorial.jl +++ b/notebooks/tutorial.jl @@ -24,7 +24,7 @@ g = unit_disk_graph(locs, 0.2) vizconfig(g; locs=locs, unit=0.5) # ╔═╡ b6e916e2-7d43-4085-a3c8-5c57069e8384 -gp = Independence(g, optimizer=TreeSA()); +gp = IndependentSet(g, optimizer=TreeSA()); # ╔═╡ 1a58dea3-14fa-4be7-b087-45ed9bad2407 gp diff --git a/src/GraphTensorNetworks.jl b/src/GraphTensorNetworks.jl index 863299b7..3257eae3 100644 --- a/src/GraphTensorNetworks.jl +++ b/src/GraphTensorNetworks.jl @@ -27,7 +27,7 @@ export random_regular_graph, diagonal_coupled_graph, is_independent_set export square_lattice_graph, unit_disk_graph, random_diagonal_coupled_graph, random_square_lattice_graph # Tensor Networks (Graph problems) -export GraphProblem, Independence, MaximalIndependence, Matching, Coloring, optimize_code, set_packing, MaxCut, PaintShop, paintshop_from_pairs, UnWeighted +export GraphProblem, IndependentSet, MaximalIS, Matching, Coloring, optimize_code, set_packing, MaxCut, PaintShop, paintshop_from_pairs, UnWeighted export flavors, symbols, nflavor, get_weights export mis_compactify!, cut_assign, cut_size @@ -52,6 +52,7 @@ include("graphs.jl") include("bounding.jl") include("visualize.jl") include("interfaces.jl") +include("deprecate.jl") using Requires function __init__() diff --git a/src/configurations.jl b/src/configurations.jl index 886bb459..49ba6e8c 100644 --- a/src/configurations.jl +++ b/src/configurations.jl @@ -70,7 +70,7 @@ end all_solutions(problem) Finding all solutions grouped by size. -e.g. when the problem is `MaximalIndependence`, it computes all maximal independent sets, or the maximal cliques of it complement. +e.g. when the problem is [`MaximalIS`](@ref), it computes all maximal independent sets, or the maximal cliques of it complement. """ all_solutions(gp::GraphProblem) = solutions(gp, Polynomial{Float64,:x}, all=true, usecuda=false) diff --git a/src/deprecate.jl b/src/deprecate.jl new file mode 100644 index 00000000..c0e50ee1 --- /dev/null +++ b/src/deprecate.jl @@ -0,0 +1,2 @@ +@deprecate Independence(args...; kwargs...) IndependentSet(args...; kwargs...) +@deprecate MaximalIndependence(args...; kwargs...) MaximalIS(args...; kwargs...) \ No newline at end of file diff --git a/src/graph_polynomials.jl b/src/graph_polynomials.jl index 92c47d62..085858f7 100644 --- a/src/graph_polynomials.jl +++ b/src/graph_polynomials.jl @@ -9,8 +9,8 @@ using Graphs Computing the graph polynomial for specific problem. * `problem` can be one of the following instances, - * `Independence` for the independence polynomial, - * `MaximalIndependence` for the maximal independence polynomial, + * `IndependentSet` for the independence polynomial, + * `MaximalIS` for the maximal independence polynomial, * `Matching` for the matching polynomial, * `method` can be one of the following inputs, @@ -96,6 +96,6 @@ function improved_counting(ys::AbstractArray...) end improved_counting(ys::Mod...) = Mods.CRT(ys...) -for GP in [:Independence, :Matching, :MaxCut, :MaximalIndependence, :PaintShop] +for GP in [:IndependentSet, :Matching, :MaxCut, :MaximalIS, :PaintShop] @eval contractx(gp::$GP, x::T; usecuda=false) where T = contractf(l->Ref(x) .^ get_weights(gp, l), gp; usecuda=usecuda) end \ No newline at end of file diff --git a/src/interfaces.jl b/src/interfaces.jl index 2178ae38..e5e916e5 100644 --- a/src/interfaces.jl +++ b/src/interfaces.jl @@ -16,7 +16,7 @@ struct SizeMax <: AbstractProperty end CountingAll <: AbstractProperty CountingAll() -Counting the total number of sets. e.g. for [`Independence`](@ref) problem, it counts the independent sets. +Counting the total number of sets. e.g. for [`IndependentSet`](@ref) problem, it counts the independent sets. * The corresponding tensor element type is `Base.Real`. * The weights on graph does not have effect. @@ -28,7 +28,7 @@ struct CountingAll <: AbstractProperty end CountingMax{K} <: AbstractProperty CountingMax(K=1) -Counting the number of sets with `K` largest size. e.g. for [`Independence`](@ref) problem, +Counting the number of sets with `K` largest size. e.g. for [`IndependentSet`](@ref) problem, it counts independent sets of size ``\\alpha(G), \\alpha(G)-1, \\ldots, \\alpha(G)-K+1``. * The corresponding tensor element type is [`CountingTropical`](@ref) for `K == 1`, and [`TruncatedPoly`](@ref)`{K}` for `K > 1`. @@ -43,14 +43,14 @@ max_k(::CountingMax{K}) where K = K GraphPolynomial{METHOD} <: AbstractProperty GraphPolynomial(; method=:finitefield, kwargs...) -Compute the graph polynomial, e.g. for [`Independence`](@ref) problem, it is the independence polynomial. +Compute the graph polynomial, e.g. for [`IndependentSet`](@ref) problem, it is the independence polynomial. The `METHOD` type parameter can be one of the following symbols * `:finitefield`, it uses finite field algebra to fit the polynomial. * The corresponding tensor element type is [`Mods.Mod`](@ref), * It does not have round-off error, * GPU is supported, - * It accepts keyword arguments `maxorder` (optional, e.g. the MIS size in the [`Independence`](@ref) problem). + * It accepts keyword arguments `maxorder` (optional, e.g. the MIS size in the [`IndependentSet`](@ref) problem). * `:polynomial`, the program uses polynomial numbers to solve the polynomial directly. * The corresponding tensor element type is [`Polynomials.Polynomial`](@ref). * It might have small round-off error depending on the data type for storing the counting. @@ -75,7 +75,7 @@ graph_polynomial_method(::GraphPolynomial{METHOD}) where METHOD = METHOD SingleConfigMax{BOUNDED} <: AbstractProperty SingleConfigMax(; bounded=false) -Finding single best solution, e.g. for [`Independence`](@ref) problem, it is one of the maximum independent sets. +Finding single best solution, e.g. for [`IndependentSet`](@ref) problem, it is one of the maximum independent sets. * The corresponding data type is [`CountingTropical{Float64,<:ConfigSampler}`](@ref) if `BOUNDED` is `true`, [`Tropical`](@ref) otherwise. * Weighted graph problems is supported. @@ -88,7 +88,7 @@ SingleConfigMax(; bounded::Bool=false) = SingleConfigMax{bounded}() ConfigsAll <:AbstractProperty ConfigsAll() -Find all valid configurations, e.g. for [`Independence`](@ref) problem, it is finding all independent sets. +Find all valid configurations, e.g. for [`IndependentSet`](@ref) problem, it is finding all independent sets. * The corresponding data type is [`ConfigEnumerator`](@ref). * Weights do not take effect. @@ -99,7 +99,7 @@ struct ConfigsAll <:AbstractProperty end ConfigsMax{K, BOUNDED} <:AbstractProperty ConfigsMax(K=1; bounded=true) -Find configurations with largest sizes, e.g. for [`Independence`](@ref) problem, +Find configurations with largest sizes, e.g. for [`IndependentSet`](@ref) problem, it is finding all independent sets of sizes ``\\alpha(G), \\alpha(G)-1, \\ldots, \\alpha(G)-K+1``. * The corresponding data type is [`CountingTropical`](@ref)`{Float64,<:ConfigEnumerator}` for `K == 1` and [`TruncatedPoly`](@ref)`{K,<:ConfigEnumerator}` for `K > 1`. @@ -181,7 +181,7 @@ Returns the maximum size and the counting of the graph problem. It is a shorthand of `solve(problem, CountingMax(); usecuda=false)`. """ function max_size_count end -for TP in [:MaximalIndependence, :Independence, :Matching, :MaxCut, :PaintShop] +for TP in [:MaximalIS, :IndependentSet, :Matching, :MaxCut, :PaintShop] @eval max_size(m::$TP; usecuda=false) = Int(sum(solve(m, SizeMax(); usecuda=usecuda)).n) # floating point number is faster (BLAS) @eval max_size_count(m::$TP; usecuda=false) = (r = sum(solve(m, CountingMax(); usecuda=usecuda)); (Int(r.n), Int(r.c))) end diff --git a/src/networks/Coloring.jl b/src/networks/Coloring.jl index 87d1ae8f..edbff2fb 100644 --- a/src/networks/Coloring.jl +++ b/src/networks/Coloring.jl @@ -2,33 +2,8 @@ Coloring{K,CT<:AbstractEinsum} <: GraphProblem Coloring{K}(graph; openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) -[Vertex Coloring](https://en.wikipedia.org/wiki/Graph_coloring) problem. +[Vertex Coloring](https://psychic-meme-f4d866f8.pages.github.io/dev/tutorials/Coloring/) problem. `optimizer` and `simplifier` are for tensor network optimization, check `optimize_code` for details. - -Problem definition ---------------------------- -A vertex coloring is an assignment of labels or colors to each vertex of a graph such that no edge connects two identically colored vertices. - -Tensor network ---------------------------- -Let us use 3-colouring problem defined on vertices as an example. -For a vertex ``v``, we define the degree of freedoms ``c_v\\in\\{1,2,3\\}`` and a vertex tensor labelled by it as -```math -W(v) = \\left(\\begin{matrix} - r_v\\\\ - g_v\\\\ - b_v -\\end{matrix}\\right). -``` -For an edge ``(u, v)``, we define an edge tensor as a matrix labelled by ``(c_u, c_v)`` to specify the constraint -```math -B = \\left(\\begin{matrix} - 0 & 1 & 1\\\\ - 1 & 0 & 1\\\\ - 1 & 1 & 0 -\\end{matrix}\\right). -``` -The number of possible colouring can be obtained by contracting this tensor network by setting vertex tensor elements ``r_v, g_v`` and ``b_v`` to 1. """ struct Coloring{K,CT<:AbstractEinsum} <: GraphProblem code::CT @@ -36,7 +11,12 @@ struct Coloring{K,CT<:AbstractEinsum} <: GraphProblem end Coloring{K}(code::ET, nv::Int) where {K,ET<:AbstractEinsum} = Coloring{K,ET}(code, nv) # same network layout as independent set. -Coloring{K}(g::SimpleGraph; openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) where K = Coloring{K}(Independence(g; openvertices=openvertices, optimizer=optimizer, simplifier=simplifier).code, nv(g)) +function Coloring{K}(g::SimpleGraph; openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) where K + rawcode = EinCode(([[i] for i in Graphs.vertices(g)]..., # labels for vertex tensors + [[minmax(e.src,e.dst)...] for e in Graphs.edges(g)]...), collect(Int, openvertices)) # labels for edge tensors + code = _optimize_code(rawcode, uniformsize(rawcode, 2), optimizer, simplifier) + Coloring{K}(code, nv(g)) +end flavors(::Type{<:Coloring{K}}) where K = collect(0:K-1) symbols(c::Coloring{K}) where K = [i for i=1:c.nv] diff --git a/src/networks/Independence.jl b/src/networks/IndependentSet.jl similarity index 55% rename from src/networks/Independence.jl rename to src/networks/IndependentSet.jl index e7c2de1c..6b07717d 100644 --- a/src/networks/Independence.jl +++ b/src/networks/IndependentSet.jl @@ -1,71 +1,33 @@ """ - Independence{CT<:AbstractEinsum,WT<:Union{UnWeighted, Vector}} <: GraphProblem - Independence(graph; weights=UnWeighted(), openvertices=(), + IndependentSet{CT<:AbstractEinsum,WT<:Union{UnWeighted, Vector}} <: GraphProblem + IndependentSet(graph; weights=UnWeighted(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) -The [Independent set problem](https://en.wikipedia.org/wiki/Independent_set_(graph_theory)). +The [independent set problem](https://psychic-meme-f4d866f8.pages.github.io/dev/tutorials/IndependentSet/) in graph theory. In the constructor, `weights` are the weights of vertices. `openvertices` specifies labels for the output tensor. `optimizer` and `simplifier` are for tensor network optimization, check `optimize_code` for details. - -Problem definition ---------------------------- -In graph theory, an independent set is a set of vertices in a graph, no two of which are adjacent. - -Graph polynomial ---------------------------- -The graph polynomial defined for the independence problem is known as the independence polynomial. -```math -I(G, x) = \\sum_{k=0}^{\\alpha(G)} a_k x^k, -``` -where ``\\alpha(G)`` is the maximum independent set size, -``a_k`` is the number of independent sets of size ``k`` in graph ``G=(V,E)``. -The total number of independent sets is thus equal to ``I(G, 1)``. - -Tensor network ---------------------------- -In tensor network representation of the independent set problem, -we map a vertex ``i\\in V`` to a label ``s_i \\in \\{0, 1\\}`` of dimension 2, -where we use 0 (1) to denote a vertex is absent (present) in the set. -For each label ``s_i``, we defined a parametrized rank-one vertex tensor ``W(x_i)`` as -```math -W(x_i)_{s_i} = \\left(\\begin{matrix} - 1 \\\\ - x_i -\\end{matrix}\\right)_{s_i} -``` -We use subscripts to index tensor elements, e.g.``W(x_i)_0=1`` is the first element associated -with ``s_i=0`` and ``W(x_i)_1=x_i`` is the second element associated with ``s_i=1``. -Similarly, on each edge ``(u, v)``, we define a matrix ``B`` indexed by ``s_u`` and ``s_v`` as -```math -B_{s_i s_j} = \\left(\\begin{matrix} - 1 & 1\\\\ - 1 & 0 -\\end{matrix}\\right)_{s_is_j} -``` - -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``. """ -struct Independence{CT<:AbstractEinsum,WT<:Union{UnWeighted, Vector}} <: GraphProblem +struct IndependentSet{CT<:AbstractEinsum,WT<:Union{UnWeighted, Vector}} <: GraphProblem code::CT nv::Int weights::WT end -function Independence(g::SimpleGraph; weights=UnWeighted(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) +function IndependentSet(g::SimpleGraph; weights=UnWeighted(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) @assert weights isa UnWeighted || length(weights) == nv(g) rawcode = EinCode(([[i] for i in Graphs.vertices(g)]..., # labels for vertex tensors [[minmax(e.src,e.dst)...] for e in Graphs.edges(g)]...), collect(Int, openvertices)) # labels for edge tensors code = _optimize_code(rawcode, uniformsize(rawcode, 2), optimizer, simplifier) - Independence(code, nv(g), weights) + IndependentSet(code, nv(g), weights) end -flavors(::Type{<:Independence}) = [0, 1] -symbols(gp::Independence) = [i for i in 1:gp.nv] -get_weights(gp::Independence, label) = [0, gp.weights[findfirst(==(label), symbols(gp))]] +flavors(::Type{<:IndependentSet}) = [0, 1] +symbols(gp::IndependentSet) = [i for i in 1:gp.nv] +get_weights(gp::IndependentSet, label) = [0, gp.weights[findfirst(==(label), symbols(gp))]] # generate tensors -function generate_tensors(fx, gp::Independence) +function generate_tensors(fx, gp::IndependentSet) syms = symbols(gp) isempty(syms) && return [] ixs = getixsv(gp.code) @@ -94,7 +56,7 @@ misv(vals) = vals set_packing(sets; openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) Set packing is a generalization of independent set problem to hypergraphs. -Calling this function will return you an `Independence` instance. +Calling this function will return you an `IndependentSet` instance. `sets` are a vector of vectors, each element being a vertex in the independent set problem. `optimizer` and `simplifier` are for tensor network optimization, check `optimize_code` for details. @@ -112,7 +74,7 @@ julia> res = best_solutions(gp; all=true)[] function set_packing(sets; weights=UnWeighted(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) n = length(sets) code = EinCode(vcat([[i] for i=1:n], [[i,j] for i=1:n,j=1:n if j>i && !isempty(sets[i] ∩ sets[j])]), collect(Int,openvertices)) - Independence(_optimize_code(code, uniformsize(code, 2), optimizer, simplifier), n, weights) + IndependentSet(_optimize_code(code, uniformsize(code, 2), optimizer, simplifier), n, weights) end """ diff --git a/src/networks/Matching.jl b/src/networks/Matching.jl index a4768367..8bc63537 100644 --- a/src/networks/Matching.jl +++ b/src/networks/Matching.jl @@ -4,38 +4,6 @@ [Vertex matching](https://mathworld.wolfram.com/Matching.html) problem. `optimizer` and `simplifier` are for tensor network optimization, check `optimize_code` for details. - -Problem definition ---------------------------- -A ``k``-matching in a graph ``G`` is a set of k edges, no two of which have a vertex in common. - -Graph polynomial ---------------------------- -The matching polynomial adopts the first definition in [wiki page](https://en.wikipedia.org/wiki/Matching_polynomial) -```math -M(G, x) = \\sum\\limits_{k=1}^{|V|/2} c_k x^k, -``` -where ``k`` is the number of matches, and coefficients ``c_k`` are the corresponding counting. - -Tensor network ---------------------------- -We map an edge ``(u, v) \\in E`` to a label ``\\langle u, v\\rangle \\in \\{0, 1\\}`` in a tensor network, -where 1 means two vertices of an edge are matched, 0 means otherwise. -Then we define a tensor of rank ``d(v) = |N(v)|`` on vertex ``v`` such that, -```math -W_{\\langle v, n_1\\rangle, \\langle v, n_2 \\rangle, \\ldots, \\langle v, n_{d(v)}\\rangle} = \\begin{cases} - 1, & \\sum_{i=1}^{d(v)} \\langle v, n_i \\rangle \\leq 1,\\\\ - 0, & \\text{otherwise}, -\\end{cases} -``` -and a tensor of rank 1 on the bond -```math -B_{\\langle v, w\\rangle} = \\begin{cases} -1, & \\langle v, w \\rangle = 0 \\\\ -x, & \\langle v, w \\rangle = 1, -\\end{cases} -``` -where label ``\\langle v, w \\rangle`` is equivalent to ``\\langle w,v\\rangle``. """ struct Matching{CT<:AbstractEinsum} <: GraphProblem symbols::Vector{Tuple{Int,Int}} diff --git a/src/networks/MaxCut.jl b/src/networks/MaxCut.jl index 61da01eb..ccb8ff94 100644 --- a/src/networks/MaxCut.jl +++ b/src/networks/MaxCut.jl @@ -3,40 +3,9 @@ MaxCut(graph; weights=UnWeighted(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) -[Cut](https://en.wikipedia.org/wiki/Maximum_cut) problem (or spin glass problem). +[Cut](https://psychic-meme-f4d866f8.pages.github.io/dev/tutorials/MaxCut/) problem (or spin glass problem). In the constructor, `weights` are the weights of edges. `optimizer` and `simplifier` are for tensor network optimization, check `optimize_code` for details. - -Problem definition ---------------------------- -In graph theory, a cut is a partition of the vertices of a graph into two disjoint subsets. -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. - -Graph polynomial ---------------------------- -The graph polynomial defined for the cut problem is -```math -C(G, x) = \\sum_{k=0}^{\\gamma(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)``. - -Tensor network ---------------------------- -For a vertex ``v\\in V``, we define a boolean degree of freedom ``s_v\\in\\{0, 1\\}``. -Then the maximum cut 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}\\\\ - x_{\\langle i, j\\rangle} & 1 -\\end{matrix}\\right), -``` -where variable ``x_{\\langle i, j\\rangle}`` represents a cut on edge ``(i, j)`` or a domain wall of an Ising spin glass. -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. - -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``. """ struct MaxCut{CT<:AbstractEinsum,WT<:Union{UnWeighted, Vector}} <: GraphProblem code::CT diff --git a/src/networks/MaximalIS.jl b/src/networks/MaximalIS.jl new file mode 100644 index 00000000..ea9da6bd --- /dev/null +++ b/src/networks/MaximalIS.jl @@ -0,0 +1,41 @@ +""" + MaximalIS{CT<:AbstractEinsum,WT<:Union{UnWeighted, Vector}} <: GraphProblem + MaximalIS(graph; weights=UnWeighted(), openvertices=(), + optimizer=GreedyMethod(), simplifier=nothing) + +[Maximal independent set](https://psychic-meme-f4d866f8.pages.github.io/dev/tutorials/MaximalIS/) problem. In the constructor, `weights` are the weights of vertices. +`optimizer` and `simplifier` are for tensor network optimization, check `optimize_code` for details. +""" +struct MaximalIS{CT<:AbstractEinsum,WT<:Union{UnWeighted, Vector}} <: GraphProblem + code::CT + weights::WT +end + +function MaximalIS(g::SimpleGraph; weights=UnWeighted(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) + @assert weights isa UnWeighted || length(weights) == nv(g) + rawcode = EinCode(([[Graphs.neighbors(g, v)..., v] for v in Graphs.vertices(g)]...,), collect(Int, openvertices)) + MaximalIS(_optimize_code(rawcode, uniformsize(rawcode, 2), optimizer, simplifier), weights) +end + +flavors(::Type{<:MaximalIS}) = [0, 1] +symbols(gp::MaximalIS) = [i for i in 1:length(getixsv(gp.code))] +get_weights(gp::MaximalIS, label) = [0, gp.weights[findfirst(==(label), symbols(gp))]] + +function generate_tensors(fx, mi::MaximalIS) + ixs = getixsv(mi.code) + isempty(ixs) && return [] + T = eltype(fx(ixs[1][end])) + return map(ixs) do ix + neighbortensor(fx(ix[end])..., length(ix)) + end +end +function neighbortensor(a::T, b::T, d::Int) where T + t = zeros(T, fill(2, d)...) + for i = 2:1<<(d-1) + t[i] = one(T) + end + t[1<<(d-1)+1] = a + t[1<<(d-1)+1] = b + return t +end + diff --git a/src/networks/MaximalIndependence.jl b/src/networks/MaximalIndependence.jl deleted file mode 100644 index af1ac03d..00000000 --- a/src/networks/MaximalIndependence.jl +++ /dev/null @@ -1,70 +0,0 @@ -""" - MaximalIndependence{CT<:AbstractEinsum,WT<:Union{UnWeighted, Vector}} <: GraphProblem - MaximalIndependence(graph; weights=UnWeighted(), openvertices=(), - optimizer=GreedyMethod(), simplifier=nothing) - -[Maximal independent set](https://en.wikipedia.org/wiki/Maximal_independent_set) problem. In the constructor, `weights` are the weights of vertices. -`optimizer` and `simplifier` are for tensor network optimization, check `optimize_code` for details. - -Problem definition ---------------------------- -In graph theory, a maximal independent set is an independent set that is not a subset of any other independent set. -It is different from maximum independent set because it does not require the set to have the max size. - -Graph polynomial ---------------------------- -The graph polynomial defined for the maximal independent set problem is -```math -I_{\\rm max}(G, x) = \\sum_{k=0}^{\\alpha(G)} b_k x^k, -``` -where ``b_k`` is the number of maximal independent sets of size ``k`` in graph ``G=(V, E)``. - -Tensor network ---------------------------- -For a vertex ``v\\in V``, we define a boolean degree of freedom ``s_v\\in\\{0, 1\\}``. -We defined the restriction on its neighbourhood ``N[v]``: -```math -T(x_v)_{s_1,s_2,\\ldots,s_{|N(v)|},s_v} = \\begin{cases} - s_vx_v & s_1=s_2=\\ldots=s_{|N(v)|}=0,\\\\ - 1-s_v& \\text{otherwise}.\\ -\\end{cases} -``` -Intuitively, it means if all the neighbourhood vertices are not in ``I_{m}``, i.e., ``s_1=s_2=\\ldots=s_{|N(v)|}=0``, then ``v`` should be in ``I_{m}`` and contribute a factor ``x_{v}``, -otherwise, if any of the neighbourhood vertices is in ``I_{m}``, then ``v`` cannot be in ``I_{m}``. - -Its contraction time space complexity is no longer determined by the tree-width of the original graph ``G``. -It is often harder to contract this tensor network than to contract the one for regular independent set problem. -""" -struct MaximalIndependence{CT<:AbstractEinsum,WT<:Union{UnWeighted, Vector}} <: GraphProblem - code::CT - weights::WT -end - -function MaximalIndependence(g::SimpleGraph; weights=UnWeighted(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) - @assert weights isa UnWeighted || length(weights) == nv(g) - rawcode = EinCode(([[Graphs.neighbors(g, v)..., v] for v in Graphs.vertices(g)]...,), collect(Int, openvertices)) - MaximalIndependence(_optimize_code(rawcode, uniformsize(rawcode, 2), optimizer, simplifier), weights) -end - -flavors(::Type{<:MaximalIndependence}) = [0, 1] -symbols(gp::MaximalIndependence) = [i for i in 1:length(getixsv(gp.code))] -get_weights(gp::MaximalIndependence, label) = [0, gp.weights[findfirst(==(label), symbols(gp))]] - -function generate_tensors(fx, mi::MaximalIndependence) - ixs = getixsv(mi.code) - isempty(ixs) && return [] - T = eltype(fx(ixs[1][end])) - return map(ixs) do ix - neighbortensor(fx(ix[end])..., length(ix)) - end -end -function neighbortensor(a::T, b::T, d::Int) where T - t = zeros(T, fill(2, d)...) - for i = 2:1<<(d-1) - t[i] = one(T) - end - t[1<<(d-1)+1] = a - t[1<<(d-1)+1] = b - return t -end - diff --git a/src/networks/networks.jl b/src/networks/networks.jl index 47b733c9..0b079ffa 100644 --- a/src/networks/networks.jl +++ b/src/networks/networks.jl @@ -54,7 +54,7 @@ The following code gives your the maximum independent set size ```jldoctest julia> using Graphs, GraphTensorNetworks -julia> gp = Independence(smallgraph(:petersen)); +julia> gp = IndependentSet(smallgraph(:petersen)); julia> f(x) = Tropical.([0, 1.0]) f (generic function with 1 method) @@ -91,8 +91,8 @@ generate_tensors(::Type{GT}) where GT = length(flavors(GT)) # requires field `code` -include("Independence.jl") -include("MaximalIndependence.jl") +include("IndependentSet.jl") +include("MaximalIS.jl") include("MaxCut.jl") include("Matching.jl") include("Coloring.jl") diff --git a/test/bounding.jl b/test/bounding.jl index afe575a2..d50aa8f3 100644 --- a/test/bounding.jl +++ b/test/bounding.jl @@ -22,8 +22,8 @@ end y2 = bounding_contract(AllConfigs{1}(), code, xs, BitArray(ones(Bool,2,2,2)), xs) @test y1 ≈ y2 end - rawcode = Independence(random_regular_graph(10, 3); optimizer=nothing).code - optcode = Independence(random_regular_graph(10, 3); optimizer=GreedyMethod()).code + rawcode = IndependentSet(random_regular_graph(10, 3); optimizer=nothing).code + optcode = IndependentSet(random_regular_graph(10, 3); optimizer=GreedyMethod()).code xs = map(OMEinsum.getixs(rawcode)) do ix length(ix)==1 ? GraphTensorNetworks.misv([one(TropicalF64), TropicalF64(1.0)]) : GraphTensorNetworks.misb(TropicalF64) end diff --git a/test/configurations.jl b/test/configurations.jl index c24ccec0..87dfc274 100644 --- a/test/configurations.jl +++ b/test/configurations.jl @@ -31,8 +31,8 @@ using GraphTensorNetworks: _onehotv, sampler_type, set_type end @testset "enumerating" begin - rawcode = Independence(random_regular_graph(10, 3); optimizer=nothing) - optcode = Independence(optimize_code(rawcode.code, uniformsize(rawcode.code, 2), GreedyMethod()), 10, UnWeighted()) + rawcode = IndependentSet(random_regular_graph(10, 3); optimizer=nothing) + optcode = IndependentSet(optimize_code(rawcode.code, uniformsize(rawcode.code, 2), GreedyMethod()), 10, UnWeighted()) for code in [rawcode, optcode] res0 = max_size(code) _, res1 = max_size_count(code) diff --git a/test/graph_polynomials.jl b/test/graph_polynomials.jl index e2e2ad96..ebaefb9a 100644 --- a/test/graph_polynomials.jl +++ b/test/graph_polynomials.jl @@ -13,17 +13,17 @@ end @test ne(g) == 20 g = diagonal_coupled_graph((x = trues(3, 3); x[2,2]=0; x)) @test ne(g) == 12 - @test length(GraphTensorNetworks.labels(Independence(g).code)) == 8 + @test length(GraphTensorNetworks.labels(IndependentSet(g).code)) == 8 end @testset "independence_polynomial" begin Random.seed!(2) g = random_regular_graph(10, 3) - p1 = graph_polynomial(Independence(g), Val(:fitting))[] - p2 = graph_polynomial(Independence(g), Val(:polynomial))[] - p3 = graph_polynomial(Independence(g), Val(:fft))[] - p4 = graph_polynomial(Independence(g), Val(:finitefield))[] - p5 = graph_polynomial(Independence(g), Val(:finitefield); max_iter=1)[] + p1 = graph_polynomial(IndependentSet(g), Val(:fitting))[] + p2 = graph_polynomial(IndependentSet(g), Val(:polynomial))[] + p3 = graph_polynomial(IndependentSet(g), Val(:fft))[] + p4 = graph_polynomial(IndependentSet(g), Val(:finitefield))[] + p5 = graph_polynomial(IndependentSet(g), Val(:finitefield); max_iter=1)[] @test p1 ≈ p2 @test p1 ≈ p3 @test p1 ≈ p4 @@ -31,32 +31,12 @@ end # test overflow g = random_regular_graph(120, 3) - gp = Independence(g, optimizer=TreeSA(; ntrials=1); simplifier=MergeGreedy()) + gp = IndependentSet(g, optimizer=TreeSA(; ntrials=1); simplifier=MergeGreedy()) p6 = graph_polynomial(gp, Val(:polynomial))[] p7 = graph_polynomial(gp, Val(:finitefield))[] @test p6 ≈ p7 end -@testset "counting maximal IS" begin - g = random_regular_graph(20, 3) - gp = MaximalIndependence(g, optimizer=KaHyParBipartite(sc_target=20)) - cs = graph_polynomial(gp, Val(:fft); r=1.0)[] - gp = MaximalIndependence(g, optimizer=SABipartite(sc_target=20)) - cs2 = graph_polynomial(gp, Val(:polynomial))[] - gp = MaximalIndependence(g, optimizer=GreedyMethod()) - cs3 = graph_polynomial(gp, Val(:finitefield))[] - cg = complement(g) - cliques = maximal_cliques(cg) - for i=1:20 - c = count(x->length(x)==i, cliques) - if c > 0 - @test cs2.coeffs[i+1] == c - @test cs3.coeffs[i+1] == c - @test cs.coeffs[i+1] ≈ c - end - end -end - @testset "match polynomial" begin g = SimpleGraph(7) for (i,j) in [(1,2),(2,3),(3,4),(4,5),(5,6),(6,1),(1,7)] diff --git a/test/interfaces.jl b/test/interfaces.jl index 6d0fe0a6..3a21b68f 100644 --- a/test/interfaces.jl +++ b/test/interfaces.jl @@ -4,7 +4,7 @@ using Graphs, Test @testset "independence problem" begin g = Graphs.smallgraph("petersen") for optimizer in (GreedyMethod(), TreeSA(ntrials=1)) - gp = Independence(g; optimizer=optimizer) + gp = IndependentSet(g; optimizer=optimizer) res1 = solve(gp, SizeMax())[] res2 = solve(gp, CountingAll())[] res3 = solve(gp, CountingMax(1))[] @@ -85,7 +85,7 @@ end @testset "slicing" begin g = Graphs.smallgraph("petersen") - gp = Independence(g; optimizer=TreeSA(nslices=5, ntrials=1)) + gp = IndependentSet(g; optimizer=TreeSA(nslices=5, ntrials=1)) res1 = solve(gp, SizeMax())[] res2 = solve(gp, CountingAll())[] res3 = solve(gp, CountingMax(1))[] @@ -126,7 +126,7 @@ end @testset "Weighted" begin g = Graphs.smallgraph("petersen") - gp = Independence(g; weights=collect(1:10)) + gp = IndependentSet(g; weights=collect(1:10)) res1 = solve(gp, SizeMax())[] @test res1 == Tropical(24.0) res2 = solve(gp, CountingMax(1))[] diff --git a/test/networks/Independence.jl b/test/networks/IndependentSet.jl similarity index 87% rename from test/networks/Independence.jl rename to test/networks/IndependentSet.jl index 4755dcca..9567c84f 100644 --- a/test/networks/Independence.jl +++ b/test/networks/IndependentSet.jl @@ -5,7 +5,7 @@ using GraphTensorNetworks, Test, Graphs for (i,j) in [(1,2), (2,3), (4,5), (5,6), (1,6)] add_edge!(g, i, j) end - g = Independence(g, openvertices=[1,4,6,3]) + g = IndependentSet(g, openvertices=[1,4,6,3]) m = solve(g, SizeMax()) @test m isa Array{Tropical{Float64}, 4} @test count(!iszero, m) == 12 diff --git a/test/networks/MaximalIS.jl b/test/networks/MaximalIS.jl new file mode 100644 index 00000000..a48527fa --- /dev/null +++ b/test/networks/MaximalIS.jl @@ -0,0 +1,22 @@ +using GraphTensorNetworks, Test, Graphs + +@testset "counting maximal IS" begin + g = random_regular_graph(20, 3) + gp = MaximalIS(g, optimizer=KaHyParBipartite(sc_target=20)) + cs = graph_polynomial(gp, Val(:fft); r=1.0)[] + gp = MaximalIS(g, optimizer=SABipartite(sc_target=20)) + cs2 = graph_polynomial(gp, Val(:polynomial))[] + gp = MaximalIS(g, optimizer=GreedyMethod()) + cs3 = graph_polynomial(gp, Val(:finitefield))[] + cg = complement(g) + cliques = maximal_cliques(cg) + for i=1:20 + c = count(x->length(x)==i, cliques) + if c > 0 + @test cs2.coeffs[i+1] == c + @test cs3.coeffs[i+1] == c + @test cs.coeffs[i+1] ≈ c + end + end +end + diff --git a/test/networks/networks.jl b/test/networks/networks.jl index 885a2097..8a93e38f 100644 --- a/test/networks/networks.jl +++ b/test/networks/networks.jl @@ -1,2 +1,3 @@ -include("Independence.jl") +include("IndependentSet.jl") +include("MaximalIS.jl") include("MaxCut.jl") \ No newline at end of file