Skip to content
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
2 changes: 1 addition & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ makedocs(;
repo="https://github.com/Happy-Diode/GraphTensorNetworks.jl/blob/{commit}{path}#{line}",
sitename="GraphTensorNetworks.jl",
format=Documenter.HTML(;
prettyurls=false,
prettyurls=get(ENV, "CI", "false") == "true",
canonical="https://Happy-Diode.github.io/GraphTensorNetworks.jl",
assets=String[indigo],
),
Expand Down
7 changes: 7 additions & 0 deletions docs/src/ref.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ mis_compactify!

cut_size
cut_assign

num_paint_shop_color_switch
paint_shop_coloring_from_config

is_good_vertex_coloring
```

## Properties
Expand Down Expand Up @@ -90,10 +95,12 @@ MergeGreedy
#### Graph
```@docs
show_graph
spring_layout

diagonal_coupled_graph
square_lattice_graph
unit_disk_graph
line_graph

random_diagonal_coupled_graph
random_square_lattice_graph
Expand Down
25 changes: 24 additions & 1 deletion examples/Coloring.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,27 @@ show_graph(graph; locs=locations)
# We construct the tensor network for the 3-coloring problem as
problem = Coloring{3}(graph);

# ## Solving properties
# ## Solving properties
# ##### counting all possible coloring
num_of_coloring = solve(problem, CountingAll())[]

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

is_good_vertex_coloring(graph, single_solution.c.data)

vertex_color_map = Dict(0=>"red", 1=>"green", 2=>"blue")

show_graph(graph; locs=locations, vertex_colors=[vertex_color_map[Int(c)] for c in single_solution.c.data])

# Let us try to solve the same issue on its line graph, a graph that generated by mapping an edge to a vertex and two edges sharing a common vertex will be connected.
linegraph = line_graph(graph)

show_graph(linegraph; locs=[0.5 .* (locations[e.src] .+ locations[e.dst]) for e in edges(graph)])

# Let us construct the tensor network and see if there are solutions.
lineproblem = Coloring{3}(linegraph);

num_of_coloring = solve(lineproblem, CountingAll())[]

# You will see a zero printed, meaning no solution for the 3-coloring on edges of a Petersen graph.
12 changes: 6 additions & 6 deletions examples/IndependentSet.jl
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ max_config = solve(problem, SingleConfigMax(; bounded=false))[]
# The return value contains a bit string, and one should read this bit string from left to right.
# Having value 1 at i-th bit means vertex ``i`` is in the maximum independent set.
# One can visualize this MIS with the following function.
show_graph(graph; locs=locations, colors=[iszero(max_config.c.data[i]) ? "white" : "red"
show_graph(graph; locs=locations, vertex_colors=[iszero(max_config.c.data[i]) ? "white" : "red"
for i=1:nv(graph)])

# ##### enumeration of all MISs
Expand All @@ -101,12 +101,12 @@ using Compose

m = length(all_max_configs.c)

imgs = ntuple(k->(context((k-1)/m, 0.0, 1.2/m, 1.0), show_graph(graph;
imgs = ntuple(k->show_graph(graph;
locs=locations, scale=0.25,
colors=[iszero(all_max_configs.c[k][i]) ? "white" : "red"
for i=1:nv(graph)])), m)
vertex_colors=[iszero(all_max_configs.c[k][i]) ? "white" : "red"
for i=1:nv(graph)]), m);

Compose.set_default_graphic_size(18cm, 4cm); Compose.compose(context(), imgs...)
Compose.set_default_graphic_size(18cm, 4cm); Compose.compose(context(), ntuple(k->(context((k-1)/m, 0.0, 1.2/m, 1.0), imgs[k]), m)...)

# ##### enumeration of all IS configurations
all_independent_sets = solve(problem, ConfigsAll())[]
Expand All @@ -129,7 +129,7 @@ problem = IndependentSet(graph; weights=collect(1:10))

max_config_weighted = solve(problem, SingleConfigMax())[]

show_graph(graph; locs=locations, colors=
show_graph(graph; locs=locations, vertex_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.
Expand Down
18 changes: 17 additions & 1 deletion examples/Matching.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,25 @@ show_graph(graph; locs=locations)
problem = Matching(graph);

# ## Solving properties
# ### Maximum matching
# ### Configuration properties
max_matching = solve(problem, SizeMax())[]
# The largest number of matching is 5, which means we have a perfect matching (vertices are all paired).

# ##### matching polynomial
# 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.
# where ``k`` is the number of matches, and coefficients ``c_k`` are the corresponding counting.

matching_poly = solve(problem, GraphPolynomial())[]

# ## Configuration properties

# ##### one of the perfect matches
match_config = solve(problem, SingleConfigMax())[]

# Let us show the result by coloring the matched edges to red
show_graph(graph; locs=locations, edge_colors=[isone(match_config.c.data[i]) ? "red" : "black" for i=1:ne(graph)])
2 changes: 1 addition & 1 deletion examples/MaxCut.jl
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,5 @@ 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=[
show_graph(graph; locs=locations, vertex_colors=[
iszero(max_vertex_config[i]) ? "white" : "red" for i=1:nv(graph)])
14 changes: 11 additions & 3 deletions examples/MaximalIS.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# * how to compute weighted graphs and handle open vertices.

# ## Problem definition
using GraphTensorNetworks, Graphs
using GraphTensorNetworks, Graphs, Compose

# 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.
Expand All @@ -26,6 +26,7 @@ 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 [`MaximalIS`](@ref) can be used for constructing the tensor network with optimized contraction order for solving a maximal independent set problem.
# 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
Expand Down Expand Up @@ -58,9 +59,16 @@ max_config = solve(problem, GraphPolynomial())[]

# ### Configuration properties
# ##### finding all maximal independent set
max_edge_config = solve(problem, ConfigsAll())[]
maximal_configs = solve(problem, ConfigsAll())[]

imgs = ntuple(k->show_graph(graph;
locs=locations, scale=0.25,
vertex_colors=[iszero(maximal_configs[k][i]) ? "white" : "red"
for i=1:nv(graph)]), length(maximal_configs));

Compose.set_default_graphic_size(18cm, 12cm); Compose.compose(context(), ntuple(k->(context((mod1(k,5)-1)/5, ((k-1)÷5)/3, 1.2/5, 1.0/3), imgs[k]), 15)...)

# 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))
cliques = maximal_cliques(complement(graph))

# For sparse graphs, the generic tensor network approach is usually much faster and memory efficient.
92 changes: 89 additions & 3 deletions examples/PaintShop.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,96 @@
# * how to compute weighted graphs and handle open vertices.

# ## Problme Definition
# The [binary paint shop problem](http://m-hikari.com/ams/ams-2012/ams-93-96-2012/popovAMS93-96-2012-2.pdf).
# The [binary paint shop problem](http://m-hikari.com/ams/ams-2012/ams-93-96-2012/popovAMS93-96-2012-2.pdf) is defined as follows:
# we are given a ``2m`` length sequence containing ``m`` cars, where each car appears twice.
# Each car need to be colored red in one occurrence, and blue in the other.
# We need to choose which occurrence for each car to color with which color — the goal is to minimize the number of times we need to change the current color.

# In the following, we are going to defined a binary paint shop problem for the following string
# 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 GraphTensorNetworks, Graphs

sequence = "abaccb"
sequence = collect("iadgbeadfcchghebif")

# We can visualize this paint shop problem as a graph
rot(a, b, θ) = cos(θ)*a + sin(θ)*b, cos(θ)*b - sin(θ)*a

locations = [rot(0.0, 1.0, -0.25π - 1.5*π*(i-0.5)/length(sequence)) for i=1:length(sequence)]

graph = path_graph(length(sequence))
for i=1:length(sequence)
j = findlast(==(sequence[i]), sequence)
i != j && add_edge!(graph, i, j)
end

show_graph(graph; locs=locations, texts=string.(sequence), edge_colors=[sequence[e.src] == sequence[e.dst] ? "blue" : "black" for e in edges(graph)])

# Vertices connected by blue edges must have different colors,
# and the goal becomes a min-cut problem defined on black edges.

# ## Tensor network representation
# Type [`PaintShop`](@ref) can be used for constructing the tensor network with optimized contraction order for solving a binary paint shop problem.
# To obtain its tensor network representation, we associating car ``c_i`` (the ``i``-th character in our example) with a degree of freedom ``s_{c_i} \in \{0, 1\}``,
# where we use ``0`` to denote the first appearance of a car is colored red and ``1`` to denote the first appearance of a car is colored blue.
# For each black edges ``(i, i+1)``, we define an edge tensor labeld by ``(s_{c_i}, s_{c_{i+1}})`` as follows:
# If both cars on this edge are their first or second appearance
# ```math
# B^{\rm parallel} = \begin{matrix}
# x & 1 \\
# 1 & x \\
# \end{matrix},
#
# otherwise,
# B^{\rm anti-parallel} = B^{\rm 10} = \begin{matrix}
# 1 & x \\
# x & 1 \\
# \end{matrix}.
# ```
# It can be understood as, when both cars are their first appearance,
# they tend to have the same configuration so that the color is not changed.
# Otherwise, they tend to have different configuration to keep the color unchanged.

# Let us contruct the problem instance as bellow.
problem = PaintShop(sequence);

# ### 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.

# ### Counting properties
# ##### graph polynomial
# The graph polynomial of the binary paint shop problem in our convension is defined as
# ```math
# D(G, x) = \sum_{k=0}^{\delta(G)} d_k x^k
# ```
# where ``2d_k`` is the number of possible coloring with number of color changes ``2m-1-k``.
paint_polynomial = solve(problem, GraphPolynomial())[]

# ### Configuration properties
# ##### finding best solutions
best_configs = solve(problem, ConfigsMax())[]

painting1 = paint_shop_coloring_from_config(best_configs.c.data[1]; initial=false)

show_graph(graph; locs=locations, texts=string.(sequence), edge_colors=[sequence[e.src] == sequence[e.dst] ? "blue" : "black" for e in edges(graph)],
vertex_colors=[isone(c) ? "red" : "black" for c in painting1], vertex_text_color="white")

#

painting2 = paint_shop_coloring_from_config(best_configs.c.data[2]; initial=false)

show_graph(graph; locs=locations, texts=string.(sequence), edge_colors=[sequence[e.src] == sequence[e.dst] ? "blue" : "black" for e in edges(graph)],
vertex_colors=[isone(c) ? "red" : "black" for c in painting2], vertex_text_color="white")

# Since we have different choices of initial color, the number of best solution is 4.
# The following function will check the solution and return you the number of color switchs
num_paint_shop_color_switch(sequence, painting2)
7 changes: 5 additions & 2 deletions src/GraphTensorNetworks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export GreedyMethod, TreeSA, SABipartite, KaHyParBipartite, MergeVectors, MergeG
export StaticBitVector, StaticElementVector, @bv_str
export is_commutative_semiring
export Max2Poly, TruncatedPoly, Polynomial, Tropical, CountingTropical, StaticElementVector, Mod, ConfigEnumerator, onehotv, ConfigSampler
export CountingTropicalF64, CountingTropicalF32, TropicalF64, TropicalF32

# Lower level APIs
export AllConfigs, SingleConfig
Expand All @@ -25,11 +26,13 @@ export contractx, contractf, graph_polynomial, max_size, max_size_count
# Graphs
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
export line_graph

# Tensor Networks (Graph problems)
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
export mis_compactify!, cut_assign, cut_size, num_paint_shop_color_switch, paint_shop_coloring_from_config
export is_good_vertex_coloring

# Interfaces
export solve, SizeMax, CountingAll, CountingMax, GraphPolynomial, SingleConfigMax, ConfigsAll, ConfigsMax
Expand All @@ -38,7 +41,7 @@ export solve, SizeMax, CountingAll, CountingMax, GraphPolynomial, SingleConfigMa
export save_configs, load_configs

# Visualization
export show_graph
export show_graph, spring_layout

project_relative_path(xs...) = normpath(joinpath(dirname(dirname(pathof(@__MODULE__))), xs...))

Expand Down
9 changes: 6 additions & 3 deletions src/arithematics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -220,15 +220,18 @@ end
Base.:(==)(x::ConfigSampler{N,S,C}, y::ConfigSampler{N,S,C}) where {N,S,C} = x.data == y.data

function Base.:+(x::ConfigSampler{N,S,C}, y::ConfigSampler{N,S,C}) where {N,S,C} # biased sampling: return `x`, maybe using random sampler is better.
return x
return randn() > 0.5 ? x : y
end

function Base.:*(x::ConfigSampler{L,S,C}, y::ConfigSampler{L,S,C}) where {L,S,C}
ConfigSampler(x.data | y.data)
end

Base.zero(::Type{ConfigSampler{N,S,C}}) where {N,S,C} = ConfigSampler{N,S,C}(statictrues(StaticElementVector{N,S,C}))
Base.one(::Type{ConfigSampler{N,S,C}}) where {N,S,C} = ConfigSampler{N,S,C}(staticfalses(StaticElementVector{N,S,C}))
@generated function Base.zero(::Type{ConfigSampler{N,S,C}}) where {N,S,C}
ex = Expr(:call, :(StaticElementVector{$N,$S,$C}), Expr(:tuple, fill(typemax(UInt64), C)...))
:(ConfigSampler{N,S,C}($ex))
end
Base.one(::Type{ConfigSampler{N,S,C}}) where {N,S,C} = ConfigSampler{N,S,C}(zero(StaticElementVector{N,S,C}))
Base.zero(::ConfigSampler{N,S,C}) where {N,S,C} = zero(ConfigSampler{N,S,C})
Base.one(::ConfigSampler{N,S,C}) where {N,S,C} = one(ConfigSampler{N,S,C})

Expand Down
2 changes: 1 addition & 1 deletion src/configurations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ e.g. when the problem is [`MaximalIS`](@ref), it computes all maximal independen
all_solutions(gp::GraphProblem) = solutions(gp, Polynomial{Float64,:x}, all=true, usecuda=false)

# return a mapping from label to onehot bitstrings (degree of freedoms).
function fx_solutions(gp::GraphProblem, ::Type{BT}, all::Bool) where {K,BT}
function fx_solutions(gp::GraphProblem, ::Type{BT}, all::Bool) where {BT}
syms = symbols(gp)
T = (all ? set_type : sampler_type)(BT, length(syms), nflavor(gp))
vertex_index = Dict([s=>i for (i, s) in enumerate(syms)])
Expand Down
2 changes: 1 addition & 1 deletion src/graph_polynomials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,6 @@ function improved_counting(ys::AbstractArray...)
end
improved_counting(ys::Mod...) = Mods.CRT(ys...)

for GP in [:IndependentSet, :Matching, :MaxCut, :MaximalIS, :PaintShop]
for GP in [:IndependentSet, :Matching, :MaxCut, :MaximalIS, :PaintShop, :Coloring]
@eval contractx(gp::$GP, x::T; usecuda=false) where T = contractf(l->Ref(x) .^ get_weights(gp, l), gp; usecuda=usecuda)
end
19 changes: 19 additions & 0 deletions src/graphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,22 @@ function unit_disk_graph(locs::AbstractVector, unit::Real)
end
return g
end

"""
line_graph(g::SimpleGraph)

Returns the line graph of `g`.
The line graph is generated by mapping an edge to a vertex and two edges sharing a common vertex will be connected.
"""
function line_graph(graph::SimpleGraph)
edge_list = collect(edges(graph))
linegraph = SimpleGraph(length(edge_list))
for (i, ei) in enumerate(edge_list)
for (j, ej) in enumerate(edge_list)
if ei.src ∈ (ej.src, ej.dst) || ei.dst ∈ (ej.src, ej.dst)
add_edge!(linegraph, i, j)
end
end
end
return linegraph
end
14 changes: 13 additions & 1 deletion src/networks/Coloring.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Coloring{K,CT<:AbstractEinsum} <: GraphProblem
Coloring{K}(graph; openvertices=(), optimizer=GreedyMethod(), simplifier=nothing)

[Vertex Coloring](https://psychic-meme-f4d866f8.pages.github.io/dev/tutorials/Coloring/) problem.
[Vertex Coloring](https://psychic-meme-f4d866f8.pages.github.io/dev/tutorials/Coloring.html) problem.
`optimizer` and `simplifier` are for tensor network optimization, check `optimize_code` for details.
"""
struct Coloring{K,CT<:AbstractEinsum} <: GraphProblem
Expand Down Expand Up @@ -42,3 +42,15 @@ end
# coloring vertex tensor
coloringv(vals::Vector{T}) where T = vals

# utilities
"""
is_good_vertex_coloring(graph::SimpleGraph, config)

Returns true if the coloring specified by config is a valid one, i.e. does not violate the contraints of vertices of an edges having different colors.
"""
function is_good_vertex_coloring(graph::SimpleGraph, config)
for e in edges(graph)
config[e.src] == config[e.dst] && return false
end
return true
end
Loading