Skip to content

Changes based on new ProblemReductions #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Dec 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Updates in v3.0

1. The solution size of `Coloring`/`Satisfiability` is now defined as the number of violations of colors/clauses. The smaller the better now.
2. Rename `best_solutions` to `largest_solutions`, `best2_solutions` to `largest2_solutions` and `bestk_solutions` to `largestk_solutions`.
3. Remove the weights from `PaintShop`.
4. Remove the weights on vertices from `MaxCut`.
5. `SpinGlass` is no longer specified by cliques. It is now specified by graphs or hypergraphs. Weights can be defined on both edges and vertices.
6. Remove `unit_disk_graph`, replace it with `UnitDiskGraph` from `ProblemReductions`.
4 changes: 2 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ LuxorGraphPlot = "1f49bdf2-22a7-4bc4-978b-948dc219fbbc"
OMEinsum = "ebe7aa44-baf0-506c-a96f-8464559b3922"
Polynomials = "f27b6e38-b328-58d1-80ce-0feddd5e7a45"
Primes = "27ebfcd6-29c5-5fa9-bf4b-fb8fc14df3ae"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
ProblemReductions = "899c297d-f7d2-4ebf-8815-a35996def416"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SIMDTypes = "94e857df-77ce-4151-89e5-788b33177be4"
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
Expand All @@ -41,7 +41,7 @@ LuxorGraphPlot = "0.5"
OMEinsum = "0.8"
Polynomials = "4"
Primes = "0.5"
Printf = "1"
ProblemReductions = "0.2"
Random = "1"
SIMDTypes = "0.1"
Serialization = "1"
Expand Down
5 changes: 2 additions & 3 deletions docs/make.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Pkg
using GenericTensorNetworks
using GenericTensorNetworks: TropicalNumbers, Polynomials, OMEinsum, OMEinsum.OMEinsumContractionOrders, LuxorGraphPlot
using GenericTensorNetworks: TropicalNumbers, Polynomials, OMEinsum, OMEinsum.OMEinsumContractionOrders, LuxorGraphPlot, ProblemReductions
using Documenter
using DocThemeIndigo
using Literate
Expand All @@ -17,7 +17,7 @@ indigo = DocThemeIndigo.install(GenericTensorNetworks)
DocMeta.setdocmeta!(GenericTensorNetworks, :DocTestSetup, :(using GenericTensorNetworks); recursive=true)

makedocs(;
modules=[GenericTensorNetworks, TropicalNumbers, OMEinsum, OMEinsumContractionOrders, LuxorGraphPlot],
modules=[GenericTensorNetworks, ProblemReductions, TropicalNumbers, OMEinsum, OMEinsumContractionOrders, LuxorGraphPlot],
authors="Jinguo Liu",
repo="https://github.com/QuEraComputing/GenericTensorNetworks.jl/blob/{commit}{path}#{line}",
sitename="GenericTensorNetworks.jl",
Expand All @@ -40,7 +40,6 @@ makedocs(;
"Satisfiability problem" => "generated/Satisfiability.md",
"Set covering problem" => "generated/SetCovering.md",
"Set packing problem" => "generated/SetPacking.md",
#"Other problems" => "generated/Others.md",
],
"Topics" => [
"Gist" => "gist.md",
Expand Down
45 changes: 26 additions & 19 deletions docs/src/ref.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# References
## Graph problems
## Constraint Satisfaction Problems
```@docs
solve
GenericTensorNetwork
GraphProblem
ConstraintSatisfactionProblem
IndependentSet
MaximalIS
Matching
Expand All @@ -15,28 +15,35 @@ PaintShop
Satisfiability
SetCovering
SetPacking
OpenPitMining
```

#### Graph Problem Interfaces
#### Constraint Satisfaction Problem Interfaces

To subtype [`GraphProblem`](@ref), a new type must contain a `code` field to represent the (optimized) tensor network.
Interfaces [`GenericTensorNetworks.generate_tensors`](@ref), [`labels`](@ref), [`flavors`](@ref) and [`get_weights`](@ref) are required.
[`nflavor`](@ref) is optional.
To subtype [`ConstraintSatisfactionProblem`](@ref), a new type must contain a `code` field to represent the (optimized) tensor network.
Interfaces [`GenericTensorNetworks.generate_tensors`](@ref), [`flavors`](@ref) and [`weights`](@ref) are required.
[`num_flavors`](@ref) is optional.

```@docs
GenericTensorNetworks.generate_tensors
labels
energy_terms
flavors
get_weights
chweights
nflavor
weights
set_weights
is_weighted
num_flavors
fixedvertices
```

#### Graph Problem Utilities
#### Constraint Satisfaction Problem Utilities
```@docs
hard_constraints
is_satisfied
local_solution_spec
solution_size
energy_mode
LargerSizeIsBetter
SmallerSizeIsBetter
energy

is_independent_set
is_maximal_independent_set
is_dominating_set
Expand All @@ -46,10 +53,7 @@ is_set_covering
is_set_packing

cut_size
spinglass_energy
num_paint_shop_color_switch
paint_shop_coloring_from_config
mis_compactify!

CNF
CNFClause
Expand All @@ -60,8 +64,7 @@ satisfiable
¬

is_valid_mining
print_mining
mis_compactify!
```

## Properties
Expand Down Expand Up @@ -145,7 +148,12 @@ MergeGreedy

## Others
#### Graph
Except the `SimpleGraph` defined in [Graphs](https://github.com/JuliaGraphs/Graphs.jl), `GenericTensorNetworks` also defines the following types and functions.

```@docs
HyperGraph
UnitDiskGraph

show_graph
show_configs
show_einsum
Expand All @@ -162,7 +170,6 @@ render_locs

diagonal_coupled_graph
square_lattice_graph
unit_disk_graph
line_graph

random_diagonal_coupled_graph
Expand Down
102 changes: 102 additions & 0 deletions docs/src/tensornetwork.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# An introduction to tensor networks

Let $G = (V, E)$ be a hypergraph, where $V$ is the set of vertices and $E$ is the set of hyperedges. Each vertex $v \in V$ is associated with a local variable, e.g. "spin" and "bit". A hyperedge $e \in E$ is a subset of vertices $e \subseteq V$. On top of which, we can define a local Hamiltonian $H$ as a sum of local terms $h_e$ over all hyperedges $e \in E$:

```math
H(\sigma) = \sum_{e \in E} h_e(\sigma_e)
```

where $\sigma_e$ is the restriction of the configuration $\sigma$ to the vertices in $e$.

The following solution space properties are of interest:

* The partition function,
```math
Z = \sum_{\sigma} e^{-\beta H(\sigma)}
```
where $\beta$ is the inverse temperature.
* The maximum/minimum solution sizes,
```math
\max_{\sigma} H(\sigma), \min_{\sigma} H(\sigma)
```
* The number of solutions at certain sizes,
```math
N(k) = \sum_{\sigma} \delta(k, H(\sigma))
```
* The enumeration of solutions at certain sizes.
```math
S = \{ \sigma | H(\sigma) = k \}
```
* The direct sampling of solutions at certain sizes.
```math
\sigma \sim S
```

## Tensor network representation

### Partition function
It is well known that the partition function of an energy model can be represented as a tensor network[^Levin2007]. The partition function can be written in a sum-product form as
```math
Z = \sum_{\sigma} e^{-\beta H(\sigma)} = \sum_{\sigma} \prod_{e \in E} T_e(\sigma_e)
```
where $T_e(\sigma_e) = e^{-\beta h_e(\sigma_e)}$ is a tensor associated with the hyperedge $e$.

This sum-product form is directly related to a tensor network $(V, \{T_{\sigma_e} \mid e\in E\}, \emptyset)$, where $T_{\sigma_e}$ is a tensor labeled by $\sigma_e \subseteq V$, and its elements are defined by $T_{\sigma_e}= T_e(\sigma_e)$. $\emptyset$ is the set of open vertices in a tensor network, which are not summed over.

### Maximum/minimum solution sizes
The maximum/minimum solution sizes can be represented as a tensor network as well. The maximum solution size can be written as
```math
\max_{\sigma} H(\sigma) = \max_{\sigma} \sum_{e \in E} h_e(\sigma_e)
```
which can be represented as a tropical tensor network[^Liu2021] $(V, \{h_{\sigma_e} \mid e\in E\}, \emptyset)$, where $h_{\sigma_e}$ is a tensor labeled by $\sigma_e \subseteq V$, and its elements are defined by $h_{\sigma_e}= h_e(\sigma_e)$.

## Problems
The independent set problem on graph $G=(V, E)$ is characterized by the Hamiltonian
```math
H(\sigma) = U \sum_{(i, j) \in E} n_i n_j - \sum_{i \in V} n_i
```
where $n_i \in \{0, 1\}$ is a binary variable associated with vertex $i$, and $U\rightarrow \infty$ is a large constant. The goal is to find the maximum independent set, i.e. the maximum number of vertices such that no two vertices are connected by an edge.
The partition function for an independent set problem is
```math
Z = \sum_{\sigma} e^{-\beta H(\sigma)} = \sum_{\sigma} \prod_{(i, j) \in E} e^{-\beta U n_in_j} \prod_{i \in V} e^{\beta n_i}
```

Let $x = e^{\beta}$, the partition function can be written as
```math
Z = \sum_{\sigma} \prod_{(i, j) \in E} B_{n_in_j} \prod_{i \in V} W_{n_i}
```
where $B_{n_in_j} = \lim_{U \rightarrow \infty} e^{-U \beta n_in_j}=\begin{cases}0, \quad n_in_j = 1\\1,\quad n_in_j = 0\end{cases}$ and $W_{n_i} = x^{n_i}$ are tensors associated with the hyperedge $(i, j)$ and the vertex $i$, respectively.

The tensor network representation for the partition function is
```math
\mathcal{N}_{IS} = (\Lambda, \{B_{n_in_j} \mid (i, j)\in E\} \cup \{W_{n_i} \mid i\in \Lambda\}, \emptyset)
```
where $\Lambda = \{n_i \mid i \in V\}$ is the set of binary variables, $B_{n_in_j}$ is a tensor associated with the hyperedge $(i, j)$ and $W_{n_i}$ is a tensor associated with the vertex $i$. The tensors are defined as
```math
W = \left(\begin{matrix}
1 \\
x
\end{matrix}\right)
```
where $x$ is a variable associated with $v$.
```math
B = \left(\begin{matrix}
1 & 1\\
1 & 0
\end{matrix}\right).
```

The contraction of the tensor network $\mathcal{N}_{IS}$ gives the partition function $Z$. It is implicitly assumed that the tensor elements are real numbers.

However, by replacing the tensor elements with tropical numbers, the tensor network $\mathcal{N}_{IS}$ can be used to compute the maximum independent set size and its degeneracy[^Liu2021].

An algebra can be defined by
```math
\begin{align*}
\oplus &= \max\\
\otimes &= +
\end{align*}
```

[^Levin2007]: Levin, M., Nave, C.P., 2007. Tensor renormalization group approach to two-dimensional classical lattice models. Physical Review Letters 99, 1–4. https://doi.org/10.1103/PhysRevLett.99.120601
[^Liu2021]: Liu, J.-G., Wang, L., Zhang, P., 2021. Tropical Tensor Network for Ground States of Spin Glasses. Phys. Rev. Lett. 126, 090506. https://doi.org/10.1103/PhysRevLett.126.090506
7 changes: 4 additions & 3 deletions examples/Coloring.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@ problem = GenericTensorNetwork(coloring)

# ## Solving properties
# ##### counting all possible coloring
num_of_coloring = solve(problem, CountingMax())[]
# The size of a coloring problem is the number of violations of the coloring constraint.
num_of_coloring = solve(problem, CountingMin())[]

# ##### finding one best coloring
single_solution = solve(problem, SingleConfigMax())[]
single_solution = solve(problem, SingleConfigMin())[]
read_config(single_solution)

is_vertex_coloring(graph, read_config(single_solution))
Expand All @@ -68,7 +69,7 @@ show_graph(linegraph, [(locations[e.src] .+ locations[e.dst])
# Let us construct the tensor network and see if there are solutions.
lineproblem = Coloring{3}(linegraph);

num_of_coloring = solve(GenericTensorNetwork(lineproblem), CountingMax())[]
num_of_coloring = solve(GenericTensorNetwork(lineproblem), CountingMin())[]
read_size_count(num_of_coloring)

# You will see the maximum size 28 is smaller than the number of edges in the `linegraph`,
Expand Down
4 changes: 2 additions & 2 deletions examples/PaintShop.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# In the following, we use a character to represent a car,
# and defined a binary paint shop problem as a string that each character appear exactly twice.

using GenericTensorNetworks, Graphs
using GenericTensorNetworks, Graphs, GenericTensorNetworks.ProblemReductions

sequence = collect("iadgbeadfcchghebif")

Expand Down Expand Up @@ -88,7 +88,7 @@ best_configs = solve(problem, ConfigsMin())[]

# One can see to identical bitstrings corresponding two different vertex configurations, they are related to bit-flip symmetry.

painting1 = paint_shop_coloring_from_config(pshop, read_config(best_configs)[1])
painting1 = ProblemReductions.paint_shop_coloring_from_config(pshop, read_config(best_configs)[1])

show_graph(graph, locations; format=:svg, texts=string.(sequence),
edge_colors=[sequence[e.src] == sequence[e.dst] ? "blue" : "black" for e in edges(graph)],
Expand Down
10 changes: 5 additions & 5 deletions examples/Satisfiability.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# One can specify a satisfiable problem in the [conjunctive normal form](https://en.wikipedia.org/wiki/Conjunctive_normal_form).
# In boolean logic, a formula is in conjunctive normal form (CNF) if it is a conjunction (∧) of one or more clauses,
# where a clause is a disjunction (∨) of literals.
using GenericTensorNetworks
using GenericTensorNetworks, GenericTensorNetworks.ProblemReductions

@bools a b c d e f g

Expand Down Expand Up @@ -48,15 +48,15 @@ problem = GenericTensorNetwork(sat)

# ## Solving properties
# #### Satisfiability and its counting
# The size of a satisfiability problem is defined by the number of satisfiable clauses.
num_satisfiable = solve(problem, SizeMax())[]
# The size of a satisfiability problem is defined by the number of unsatisfied clauses.
num_satisfiable = solve(problem, SizeMin())[]

# The [`GraphPolynomial`](@ref) of a satisfiability problem counts the number of solutions that `k` clauses satisfied.
num_satisfiable_count = read_size_count(solve(problem, GraphPolynomial())[])

# #### Find one of the solutions
single_config = read_config(solve(problem, SingleConfigMax())[])
single_config = read_config(solve(problem, SingleConfigMin())[])

# One will see a bit vector printed.
# One can create an assignment and check the validity with the following statement:
satisfiable(cnf, Dict(zip(labels(problem), single_config)))
satisfiable(cnf, Dict(zip(ProblemReductions.symbols(problem.problem), single_config)))
2 changes: 1 addition & 1 deletion examples/SetCovering.jl
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ min_configs = read_config(solve(problem, ConfigsMin())[])
# Hence the two optimal solutions are ``\{z_1, z_3, z_5, z_6\}`` and ``\{z_2, z_3, z_4, z_5\}``.
# The correctness of this result can be checked with the [`is_set_covering`](@ref) function.

all(c->is_set_covering(sets, c), min_configs)
all(c->is_set_covering(problem.problem, c), min_configs)

# Similarly, if one is only interested in computing one of the minimum set coverings,
# one can use the graph property [`SingleConfigMin`](@ref).
2 changes: 1 addition & 1 deletion examples/SetPacking.jl
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ max_configs = read_config(solve(problem, ConfigsMax())[])
# Hence the only optimal solution is ``\{z_1, z_3, z_6\}`` that has size 3.
# The correctness of this result can be checked with the [`is_set_packing`](@ref) function.

all(c->is_set_packing(sets, c), max_configs)
all(c->is_set_packing(problem.problem, c), max_configs)

# Similarly, if one is only interested in computing one of the maximum set packing,
# one can use the graph property [`SingleConfigMax`](@ref).
14 changes: 8 additions & 6 deletions examples/SpinGlass.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ show_graph(graph, locations; format=:svg)

# ## Generic tensor network representation
# An anti-ferromagnetic spin glass problem can be defined with the [`SpinGlass`](@ref) type as
spinglass = SpinGlass(graph, fill(1, ne(graph)))
spinglass = SpinGlass(graph, fill(1, ne(graph)), zeros(Int, nv(graph)))

# The tensor network representation of the set packing problem can be obtained by
problem = GenericTensorNetwork(spinglass)
Expand Down Expand Up @@ -81,8 +81,10 @@ partition_function = solve(problem, GraphPolynomial())[]
# The ground state of the spin glass problem can be found by the [`SingleConfigMin`](@ref) solver.
ground_state = read_config(solve(problem, SingleConfigMin())[])

# The energy of the ground state can be verified by the [`spinglass_energy`](@ref) function.
Emin_verify = spinglass_energy(spinglass, ground_state)
# The energy of the ground state can be verified by the [`energy`](@ref) function.
# Note the output of the ground state can not be directly used as the input of the `energy` function.
# It needs to be converted to the spin configurations.
Emin_verify = energy(problem.problem, 1 .- 2 .* Int.(ground_state))

# You should see a consistent result as above `Emin`.

Expand Down Expand Up @@ -117,7 +119,7 @@ weights = [-1, 1, -1, 1, -1, 1, -1, 1];
# \end{align*}
# ```
# A spin glass problem can be defined with the [`SpinGlass`](@ref) type as
hyperspinglass = SpinGlass(num_vertices, hyperedges, weights)
hyperspinglass = SpinGlass(HyperGraph(num_vertices, hyperedges), weights, zeros(Int, num_vertices))

# The tensor network representation of the set packing problem can be obtained by
hyperproblem = GenericTensorNetwork(hyperspinglass)
Expand Down Expand Up @@ -155,8 +157,8 @@ poly = solve(hyperproblem, GraphPolynomial())[]
# The ground state of the spin glass problem can be found by the [`SingleConfigMin`](@ref) solver.
ground_state = read_config(solve(hyperproblem, SingleConfigMin())[])

# The energy of the ground state can be verified by the [`spinglass_energy`](@ref) function.
# The energy of the ground state can be verified by the [`energy`](@ref) function.

Emin_verify = spinglass_energy(hyperspinglass, ground_state)
Emin_verify = energy(hyperproblem.problem, 1 .- 2 .* Int.(ground_state))

# You should see a consistent result as above `Emin`.
Loading