From 6cbafe4a6cd8b59b7ed0c0ad0eb4a283a9372e90 Mon Sep 17 00:00:00 2001 From: Yusheng Zhao Date: Fri, 6 Oct 2023 22:21:22 +0800 Subject: [PATCH] Implementation of ZW Diagram (#99) * minimize exports to avoid naming collision * layout structure for zw diagram and adt * add more zw diagram constructor * finish implementing zw diagram * fixing naming conflict in past tests * correct half edge src and dst * add some feature of zw diagram * finish porting non graph modifying actions * device implementation plan * implement adding spider to ZW Diagram * add test for insert_spider! * save test for insert_spider! * fix split_edge * fix insert_spider! * add test except add_spider! * add test for adding fswap * add rem_spider * finish test for rem_spider! * check prev tests * temp fix doc, need to split * sort function according to modules * improve code quality * improve docs --- README.md | 2 + docs/make.jl | 3 +- docs/src/api.md | 100 +++++++- src/ZXCalculus.jl | 70 ++++-- src/parameter.jl | 2 + src/planar_multigraph.jl | 159 +++++++++++-- src/zw_adt.jl | 33 +++ src/zw_diagram.jl | 191 ++++++++++++++++ src/zw_utils.jl | 325 ++++++++++++++++++++++++++ test/parameter.jl | 1 + test/planar_multigraph.jl | 20 +- test/runtests.jl | 10 +- test/utils.jl | 56 ++--- test/zw_diagram.jl | 141 ++++++++++++ test/zw_utils.jl | 470 ++++++++++++++++++++++++++++++++++++++ test/zxw_diagram.jl | 4 +- 16 files changed, 1501 insertions(+), 86 deletions(-) create mode 100644 src/zw_adt.jl create mode 100644 src/zw_diagram.jl create mode 100644 src/zw_utils.jl create mode 100644 test/zw_diagram.jl create mode 100644 test/zw_utils.jl diff --git a/README.md b/README.md index 3d504e1..05280c9 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ pkg> add ZXCalculus This project is highly inpsired by [PyZX](https://github.com/Quantomatic/pyzx) +The `ZXWDiagram` and `ZWDiagram` part is supported by the [OSPP 2023](https://summer-ospp.ac.cn/) + ## License MIT License diff --git a/docs/make.jl b/docs/make.jl index e56d61e..531d4fc 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -2,11 +2,12 @@ using Documenter using DocThemeIndigo using ZXCalculus +using ZXCalculus: ZXW, ZW using Multigraphs indigo = DocThemeIndigo.install(ZXCalculus) makedocs(; - modules = [ZXCalculus], + modules = [ZXCalculus, ZXW, ZW], format=Documenter.HTML(; # ... # put your indigo css in assets diff --git a/docs/src/api.md b/docs/src/api.md index ddf4f43..9090665 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -13,7 +13,6 @@ ZXCalculus.ZXGraph ZXCalculus.ZXGraph(::ZXDiagram) ZXCalculus.ZXLayout ZXCalculus.qubit_loc -ZXCalculus.tcount(zxd::AbstractZXDiagram) spider_type(zxd::ZXDiagram{T, P}, v::T) where {T<:Integer, P} ZXCalculus.phase(zxd::ZXDiagram{T, P}, v::T) where {T<:Integer, P} Graphs.nv(zxd::ZXDiagram) @@ -48,3 +47,102 @@ ZXCalculus.Match ```@docs ZXCalculus.circuit_extraction(zxg::ZXGraph{T, P}) where {T, P} ``` + +## ZXW-diagram +```@docs +ZXCalculus.ZXW.add_inout! +ZXCalculus.ZXW.substitute_variables! +ZXCalculus.biadjacency +ZXCalculus.ZXW.rem_spider! +ZXCalculus.continued_fraction +ZXCalculus.ZXW.symbol_vertices +ZXCalculus.ZXW.dagger +ZXCalculus.ZXW.add_spider! +ZXCalculus.tcount +ZXCalculus.Phase +ZXCalculus.round_phases! +ZXCalculus.gaussian_elimination +ZXCalculus.Scalar +ZXCalculus.ZXW.expval_circ! +Graphs.SimpleGraphs.rem_edge! +ZXCalculus.ZXW.parameter +ZXCalculus.ZXW.int_prep! +ZXCalculus.update_frontier! +ZXCalculus.ZXW.integrate! +ZXCalculus.set_column! +ZXCalculus.set_phase! +ZXCalculus.prev +ZXCalculus.ancilla_extraction +Graphs.SimpleGraphs.add_edge! +ZXCalculus.ZXW.stack_zxwd! +ZXCalculus.ZXW.get_outputs +ZXCalculus.scalar +ZXCalculus.get_inputs +ZXCalculus.ZW.get_inputs +ZXCalculus.column_loc +ZXCalculus.GEStep +ZXCalculus.ZXW.spider_type +ZXCalculus.ZXW.print_spider +ZXCalculus.ZXW.Parameter +ZXCalculus.ZXW.nqubits +ZXCalculus.set_qubit! +ZXCalculus.ZXW.insert_wtrig! +ZXCalculus.ZXW.concat! +ZXCalculus.ZXW.rem_spiders! +ZXCalculus.ZXW.insert_spider! +ZXCalculus.set_loc! +ZXCalculus.spiders +ZXCalculus.split_edge! +ZXCalculus.ZXW.import_edges! +ZXCalculus.get_outputs +ZXCalculus.ZXW.get_inputs +ZXCalculus.ZXW.import_non_in_out! +ZXCalculus.ZXW.set_phase! +ZXCalculus.ZXW.nout +``` + +# Planar Multigraph +```@docs +ZXCalculus.PlanarMultigraph +ZXCalculus.is_boundary +ZXCalculus.trace_face +ZXCalculus.new_edge +ZXCalculus.ϕ +ZXCalculus.nqubits +ZXCalculus.erase_facet! +ZXCalculus.create_face! +ZXCalculus.surrounding_half_edge +ZXCalculus.add_facet_to_boarder! +ZXCalculus.split_facet! +ZXCalculus.split_vertex! +ZXCalculus.add_vertex_and_facet_to_boarder! +ZXCalculus.create_edge! +ZXCalculus.join_facet! +ZXCalculus.create_vertex! +ZXCalculus.σ_inv +ZXCalculus.σ +ZXCalculus.make_hole! +ZXCalculus.gc_vertex! +ZXCalculus.HalfEdge +ZXCalculus.out_half_edge +ZXCalculus.n_conn_comp +ZXCalculus.join_vertex! +``` + +# ZW-diagrams +```@docs +ZXCalculus.ZW.ZWDiagram +ZXCalculus.ZW.insert_spider! +ZXCalculus.ZW.get_output_idx +ZXCalculus.ZW.nqubits +ZXCalculus.ZW.round_phases! +ZXCalculus.ZW.nout +ZXCalculus.ZW.get_input_idx +ZXCalculus.ZW.set_phase! +ZXCalculus.ZW.get_outputs +ZXCalculus.ZW.add_spider! +ZXCalculus.ZW.parameter +ZXCalculus.ZW.print_spider +ZXCalculus.ZW.spider_type +``` + diff --git a/src/ZXCalculus.jl b/src/ZXCalculus.jl index e616f51..0d418fe 100644 --- a/src/ZXCalculus.jl +++ b/src/ZXCalculus.jl @@ -63,7 +63,7 @@ using OMEinsum import Multigraphs: has_vertex using ..ZXCalculus using ..ZXCalculus: safe_convert, add_phase! -import ..pushfirst_gate!, ..push_gate!, ..rewrite +import ..pushfirst_gate!, ..push_gate! import ..rewrite!, ..add_power!, ..add_edge!, ..vertices, ..nv, ..round_phases! @@ -73,30 +73,58 @@ include("zxw_rules.jl") include("to_eincode.jl") include("utils.jl") - -export ZXWDiagram, CalcRule end # module ZXW -using .ZXW: - ZXWDiagram, - ZXWSpiderType, - Parameter, - CalcRule, - PiUnit, - Factor, - Input, - Output, - W, - H, - D, - Z, - X, - rewrite! -export ZXWSpiderType, - ZXWDiagram, Parameter, PiUnit, Factor, Input, Output, W, H, D, Z, X, CalcRule -export substitute_variables!, expval_circ!, stack_zxwd!, concat! +using .ZXW: ZXWDiagram, CalcRule + +export ZXWDiagram, CalcRule include("parameter.jl") include("planar_multigraph.jl") + +module ZW +using Expronicon.ADT: @adt, @const_use +using MLStyle, Graphs +using ..ZXCalculus +using ..ZXCalculus.ZXW: _round_phase, Parameter +# these will be changed to using PlanarMultigraph: vertices after we split out package +using ..ZXCalculus: + vertices, + nv, + has_vertex, + ne, + neighbors, + rem_edge!, + add_edge!, + degree, + next, + split_vertex!, + split_edge!, + face, + trace_face, + make_hole!, + add_vertex_and_facet_to_boarder!, + split_facet!, + twin, + prev, + add_multiedge!, + join_facet!, + trace_vertex, + join_vertex! + + + + +# these remains +using ..ZXCalculus: add_phase! +import ..ZXCalculus: add_power!, add_global_phase!, scalar, spiders, rem_spider! +import Graphs.rem_edge! + + +include("zw_adt.jl") +include("zw_diagram.jl") +include("zw_utils.jl") +end # module ZW + end # module diff --git a/src/parameter.jl b/src/parameter.jl index 68f67f6..5f5331f 100644 --- a/src/parameter.jl +++ b/src/parameter.jl @@ -1,3 +1,5 @@ +using .ZXW: Parameter, PiUnit, Factor + """ Parameter Constructors for `Parameter` type. diff --git a/src/planar_multigraph.jl b/src/planar_multigraph.jl index a83d4c1..0238494 100644 --- a/src/planar_multigraph.jl +++ b/src/planar_multigraph.jl @@ -1,5 +1,5 @@ import Graphs: AbstractEdge, src, dst, nv, ne, neighbors -import Graphs.SimpleGraphs: rem_edge!, rem_vertex!, add_edge!, add_vertex!, vertices +import Graphs.SimpleGraphs: vertices export HalfEdge, src, dst, new_edge, PlanarMultigraph """ @@ -119,30 +119,147 @@ Base.copy(g::PlanarMultigraph) = PlanarMultigraph( ) function Base.:(==)(pmg1::PlanarMultigraph{T}, pmg2::PlanarMultigraph{T}) where {T<:Integer} - if nv(pmg1) != nv(pmg2) + return pmg_equiv(pmg1, pmg2, false) +end + +# """ +# print_nonoverlaping(dict1, dict2) + +# Helper function to print nonoverlaping elements in two dictionaries. +# """ +function print_nonoverlaping(dict1, dict2) + # Print elements in dict1 not in dict2 + println("Elements in dict1 not in dict2:") + for (key, value) in dict1 + if !haskey(dict2, key) || dict2[key] != value + println("Key: ", key, ", Value: ", value) + end + end + + println() + + # Print elements in dict2 not in dict1 + println("Elements in dict2 not in dict1:") + for (key, value) in dict2 + if !haskey(dict1, key) || dict1[key] != value + println("Key: ", key, ", Value: ", value) + end + end + +end + +# """ +# equiv_v2he(pmg1::PlanarMultigraph, pmg2::PlanarMultigraph) + +# Checking if the vertex to half edge mapping is equivalent. +# """ +function equiv_v2he(pmg1::PlanarMultigraph, pmg2::PlanarMultigraph) + if length(keys(pmg1.v2he)) != length(keys(pmg2.v2he)) + return false + end + + for (v, _) in pmg1.v2he + if !haskey(pmg2.v2he, v) + return false + end + if pmg2.v2he[v] ∉ trace_vertex(pmg1, v) + return false + end + end + return true +end + +# """ +# equiv_f2he(pmg1::PlanarMultigraph, pmg2::PlanarMultigraph) + +# Checking if the face to half edge mapping is equivalent. +# """ +function equiv_f2he(pmg1::PlanarMultigraph, pmg2::PlanarMultigraph) + if length(keys(pmg1.f2he)) != length(keys(pmg2.f2he)) + return false + end + + for (f_id, _) in pmg1.f2he + if !haskey(pmg2.f2he, f_id) + return false + end + if pmg2.f2he[f_id] ∉ trace_face(pmg1, f_id) + return false + end + end + return true +end + +# """ +# pmg_equiv( +# pmg1::PlanarMultigraph{T}, +# pmg2::PlanarMultigraph{T}, +# verbose::Bool, +# ) where {T<:Integer} + +# Checking if two PlanarMultigraphs are equivalent. + +# If verbose is true, then print out the nonoverlaping elements in the two. +# """ +function pmg_equiv( + pmg1::PlanarMultigraph{T}, + pmg2::PlanarMultigraph{T}, + verbose::Bool, +) where {T<:Integer} + if !equiv_v2he(pmg1, pmg2) + if verbose + println("v2he") + print_nonoverlaping(pmg1.v2he, pmg2.v2he) + end return false end - if nhe(pmg1) != nhe(pmg2) + + if !equiv_f2he(pmg1, pmg2) + if verbose + println("f2he") + print_nonoverlaping(pmg1.f2he, pmg2.f2he) + end return false end - if nf(pmg1) != nf(pmg2) + + if pmg1.half_edges != pmg2.half_edges + if verbose + println("HalfEdges") + print_nonoverlaping(pmg1.half_edges, pmg2.half_edges) + end return false end - # could be relaxed, idx might be different but content needs to be the same for HalfEdges if pmg1.next != pmg2.next + if verbose + println("next") + print_nonoverlaping(pmg1.next, pmg2.next) + end return false end if pmg1.twin != pmg2.twin + if verbose + println("twin") + print_nonoverlaping(pmg1.twin, pmg2.twin) + end return false end if pmg1.he2f != pmg2.he2f + if verbose + println("he2f") + print_nonoverlaping(pmg1.he2f, pmg2.he2f) + end return false end if sort(pmg1.boundary) != sort(pmg2.boundary) + if verbose + println("boundary") + println(pmg1.boundary) + println(pmg2.boundary) + end return false end return true @@ -252,11 +369,11 @@ function trace_face(g::PlanarMultigraph{T}, f::T; safe_trace = false) where {T} return hes_f end -trace_vertex(g::PlanarMultigraph{T}, v::T) where {T} = - trace_orbit(h -> σ_inv(g, h), out_half_edge(g, v); rev = true) +trace_vertex(pmg::PlanarMultigraph{T}, v::T) where {T} = + trace_orbit(h -> σ_inv(pmg, h), out_half_edge(pmg, v); rev = true) -neighbors(g::PlanarMultigraph{T}, v::T) where {T} = - [dst(g, he) for he in trace_vertex(g, v)] +neighbors(pmg::PlanarMultigraph{T}, v::T) where {T} = + [dst(pmg, he) for he in trace_vertex(pmg, v)] """ is_boundary(g::PlanarMultigraph{T}, he_id::T) where {T} @@ -434,14 +551,14 @@ end Join two facets incident to h and it's twin into one. -The facet incident to h is removed. +The facet incident to h's twin is removed. """ function join_facet!(pmg::PlanarMultigraph{T}, h::T) where {T} vs = src(pmg, h) vd = dst(pmg, h) - length(trace_vertex(pmg, vs)) <= 3 && error("Src vtx must have degree 3 or above") - length(trace_vertex(pmg, vd)) <= 3 && error("Dst vtx must have degree 3 or above") + length(trace_vertex(pmg, vs)) < 3 && error("Src vtx must have degree 3 or above") + length(trace_vertex(pmg, vd)) < 3 && error("Dst vtx must have degree 3 or above") twin_h = twin(pmg, h) hp = prev(pmg, h) @@ -452,12 +569,16 @@ function join_facet!(pmg::PlanarMultigraph{T}, h::T) where {T} f1_id = face(pmg, h) f2_id = face(pmg, twin_h) hes_f = trace_face(pmg, f2_id; safe_trace = false) + circshift!(hes_f, -findfirst(he -> he == twin_h, hes_f)) set_next!(pmg, [thp, hp], [hn, thn]) - set_face!(pmg, hes_f, f1_id; both = true) + set_face!(pmg, hes_f[1:end-1], f1_id; both = true) delete!(pmg.f2he, f2_id) + if f2_id in pmg.boundary + pmg.boundary[findfirst(x -> x == f2_id, pmg.boundary)] = f1_id + end destroy_edge!(pmg, h) return hp @@ -479,7 +600,6 @@ After splitting, h points to the newly added vertex - [CGAL](https://doc.cgal.org/latest/Polyhedron/classCGAL_1_1Polyhedron__3.html#a2b17d7bd2045397167b00616f3b4d622) """ function split_vertex!(pmg::PlanarMultigraph{T}, h::T, g::T) where {T<:Integer} - # Preconditions dst(pmg, h) == dst(pmg, g) || error("h and g don't have the same destination") h == g && error("h and g can't be the same half edge") @@ -505,9 +625,8 @@ function split_vertex!(pmg::PlanarMultigraph{T}, h::T, g::T) where {T<:Integer} # add new half edges from v2 to v1 hes_id, _ = create_edge!(pmg, v2, v1) - for he in he_vec - set_dst!(pmg, he, v2) + set_dst!(pmg, twin(pmg, he), v2) (he == tg) && break end @@ -590,7 +709,8 @@ function add_vertex_and_facet_to_boarder!( # preconditions is_boundary(pmg, h) && is_boundary(pmg, g) || error("Can't add facet on top of facet") h != g || error("Can't add a loop as a facet!") - hes_f = trace_face(pmg, face(pmg, h); safe_trace = false) + old_f = face(pmg, h) + hes_f = trace_face(pmg, old_f; safe_trace = false) hes_f = circshift(hes_f, -findfirst(he -> he == h, hes_f)) g ∉ hes_f && error("Can't add edge across facet") @@ -617,6 +737,7 @@ function add_vertex_and_facet_to_boarder!( (he == g) && break end set_face!(pmg, [hes_hv2newv[2], hes_gv2newv[1]], new_f; both = true) + set_face!(pmg, [hes_hv2newv[1], hes_gv2newv[2]], old_f; both = false) return hes_gv2newv[1] end @@ -639,9 +760,11 @@ function erase_facet!(pmg::PlanarMultigraph{T}, h::T) where {T<:Integer} is_bry = [is_boundary(pmg, he) for he in hes_ft] + f_id = face(pmg, h) + # modify old graph set_face!(pmg, hes_f, 0; both = false) - delete!(pmg.f2he, face(pmg, h)) + delete!(pmg.f2he, f_id) for idx = 1:length(hes_ft) if is_bry[idx] he_f_prev = prev(pmg, hes_f[idx]) diff --git a/src/zw_adt.jl b/src/zw_adt.jl new file mode 100644 index 0000000..30cc112 --- /dev/null +++ b/src/zw_adt.jl @@ -0,0 +1,33 @@ +@adt ZWSpiderType begin + # the following spiders form a complete set of + # generators for pW fragment + + W # W Spider + + fSWAP # fermonic SWAP spider + + # binary Z Spider + struct binZ + r::Parameter + end + + # adding SWAP gives you the ferminoic ZW fragment + SWAP + + # adding 1-nary Z spider gives you the entire ZW calculus + # TODO: proof of it is not in the paper, need to do + struct monoZ + r::Parameter + end + + struct Input + qubit::Int + end + + struct Output + qubit::Int + end + +end + +@const_use ZWSpiderType:W, fSWAP, binZ, SWAP, monoZ, Input, Output diff --git a/src/zw_diagram.jl b/src/zw_diagram.jl new file mode 100644 index 0000000..d9b7a64 --- /dev/null +++ b/src/zw_diagram.jl @@ -0,0 +1,191 @@ +struct ZWDiagram{T<:Integer,P} + pmg::PlanarMultigraph{T} + + st::Dict{T,ZWSpiderType} + + scalar::Scalar{P} + inputs::Vector{T} + outputs::Vector{T} + + function ZWDiagram{T,P}( + pmg::PlanarMultigraph{T}, + st::Dict{T,ZWSpiderType}, + s::Scalar{P} = Scalar{P}(), + inputs::Vector{T} = Vector{T}(), + outputs::Vector{T} = Vector{T}(), + ) where {T<:Integer,P} + nv(pmg) != length(st) && error("There should be a type for each spider!") + + if length(inputs) == 0 + inputs = sort!([@match st[v] begin + Input(_) => v + _ => -1 + end for v in vertices(pmg)]) + inputs = [i for i in inputs if i != -1] + end + + if length(outputs) == 0 + outputs = sort!([@match st[v] begin + Output(_) => v + _ => -1 + end for v in vertices(pmg)]) + outputs = [i for i in outputs if i != -1] + + + zwd = new{T,P}(pmg, st, s, inputs, outputs) + round_phases!(zwd) + return zwd + end + end +end + +ZWDiagram( + pmg::PlanarMultigraph{T}, + st::Dict{T,ZWSpiderType}, + scalar::Scalar{P}, +) where {T,P} = ZWDiagram{T,P}(pmg, st, scalar) + +ZWDiagram( + pmg::PlanarMultigraph{T}, + st::Vector{ZWSpiderType}, + scalar::Scalar{P}, +) where {T,P} = ZWDiagram{T,P}(pmg, Dict(zip(sort!(vertices(pmg)), st)), scalar) + +""" + ZWDiagram(nbits::T, ::Type{P}) where {T<:Integer} + +Create a ZW-diagram with `nbits` input and output qubits. + +Scalar is parameterized to be `P`. +""" +function ZWDiagram(nbits::T, ::Type{P} = Rational) where {T<:Integer,P} + nbits < 1 && error("We need to have at least one qubit in the system!") + + half_edges = Dict{T,HalfEdge}() + for i = 1:2*nbits + half_edges[i] = HalfEdge(i, iseven(i) ? (i - 1) : (i + 1)) + end + for i = 1:(2*nbits-2) + # edges connecting input qubits + jj = i + 2 * nbits + # edges connecting output qubits + kk = i + 4 * nbits - 2 + if iseven(i) + half_edges[jj] = HalfEdge(i - 1, i + 1) + half_edges[kk] = HalfEdge(i, i + 2) + else + half_edges[jj] = HalfEdge(i + 2, i) + half_edges[kk] = HalfEdge(i + 3, i + 1) + end + end + + he2f = Dict{T,T}() + for i = 1:(6*nbits-4) + if i == 2 || i == 2 * nbits - 1 + he2f[i] = 0 + continue + end + + if i <= 2 * nbits + if iseven(i) + he2f[i] = div(i, 2) - 1 + else + he2f[i] = div(i + 1, 2) + end + continue + end + + if i <= 4 * nbits - 2 + if iseven(i) + he2f[i] = 0 + else + he2f[i] = (i - 2 * nbits + 1) ÷ 2 + end + continue + end + + if iseven(i) + he2f[i] = div(i - 4 * nbits + 2, 2) + else + he2f[i] = 0 + end + end + + next = Dict{T,T}() + + if nbits < 2 + next[1] = 2 + next[2] = 1 + else + for i = 1:(6*nbits-4) + if i <= 2 * nbits + if isodd(i) + if i == 2 * nbits - 1 + next[i] = 6 * nbits - 5 + else + next[i] = i + (4 * nbits - 1) + end + else + if i == 2 + next[i] = 2 * nbits + 2 + else + next[i] = i + 2 * nbits - 3 + end + end + continue + end + + if i <= 4 * nbits - 2 + if iseven(i) + if i == 4 * nbits - 2 + next[i] = 2 * nbits - 1 + else + next[i] = i + 2 + end + else + if i == 2 * nbits + 1 + next[i] = 1 + else + next[i] = i - 2 * nbits + end + end + continue + end + + if iseven(i) + next[i] = i - 4 * nbits + 4 + else + if i == 4 * nbits - 1 + next[i] = 2 + else + next[i] = i - 2 + end + end + end + end + + pmg = PlanarMultigraph{T}( + Dict([i => i for i = 1:2*nbits]), # v2he + half_edges, # he_id -> HalfEdge + Dict([[0 => 2]; [i => 2 * i - 1 for i = 1:nbits-1]]), # f2he + he2f, # he_id -> f_id + next, + Dict([i => iseven(i) ? (i - 1) : (i + 1) for i = 1:(6*nbits-4)]), + 2 * nbits, # v_max + 6 * nbits - 4, # he_max + nbits - 1, + [0], + ) + + st = [i % 2 == 1 ? Input(div(i, 2) + 1) : Output(div(i, 2)) for i = 1:2*nbits] + + return ZWDiagram(pmg, st, Scalar{P}()) +end + +Base.copy(zwd::ZWDiagram{T,P}) where {T,P} = ZWDiagram{T,P}( + copy(zwd.pmg), + copy(zwd.st), + copy(zwd.scalar), + copy(zwd.inputs), + copy(zwd.outputs), +) diff --git a/src/zw_utils.jl b/src/zw_utils.jl new file mode 100644 index 0000000..210e2ee --- /dev/null +++ b/src/zw_utils.jl @@ -0,0 +1,325 @@ +""" + round_phases!(zwd) + +Round phases between [0, 2π). +""" +function round_phases!(zwd::ZWDiagram{T,P}) where {T<:Integer,P} + st = zwd.st + for v in keys(st) + st[v] = @match st[v] begin + binZ(p) => binZ(_round_phase(p)) + monoZ(p) => monoZ(_round_phase(p)) + _ => st[v] + end + end + return +end + +""" + spider_type(zwd, v) + +Returns the spider type of a spider if it exists. +""" +function spider_type(zwd::ZWDiagram{T,P}, v::T) where {T<:Integer,P} + if has_vertex(zwd.pmg, v) + return zwd.st[v] + else + error("Spider $v does not exist!") + end +end + +""" + parameter(zwd, v) + +Returns the parameter of a spider. If the spider is not a monoZ or binZ spider, then return 0. +""" +function parameter(zwd::ZWDiagram{T,P}, v::T) where {T<:Integer,P} + @match spider_type(zwd, v) begin + binZ(p) => p + monoZ(p) => p + Input(q) || Output(q) => q + _ => nothing + end +end + +""" + set_phase!(zwd, v, p) + +Set the phase of `v` in `zwd` to `p`. If `v` is not a monoZ or binZ spider, then do nothing. +If `v` is not in `zwd`, then return false to indicate failure. +""" +function set_phase!(zwd::ZWDiagram{T,P}, v::T, p::Parameter) where {T,P} + if has_vertex(zwd.pmg, v) + zwd.st[v] = @match zwd.st[v] begin + monoZ(_) => monoZ(_round_phase(p)) + binZ(_) => binZ(_round_phase(p)) + _ => zxwd.st[v] + end + return true + end + return false +end + +""" + nqubits(zwd) + +Returns the qubit number of a ZW-diagram. +""" +nqubits(zwd::ZWDiagram{T,P}) where {T,P} = length(zwd.inputs) + +""" + nin(zwd) +Returns the number of inputs of a ZW-diagram. +""" + +nin(zwd::ZWDiagram{T,P}) where {T,P} = sum([@match spy begin + Input(_) => 1 + _ => 0 +end for spy in values(zwd.st)]) + +""" + nout(zwd) +Returns the number of outputs of a ZW-diagram +""" +nout(zwd::ZWDiagram{T,P}) where {T,P} = sum([@match spy begin + Output(_) => 1 + _ => 0 +end for spy in values(zwd.st)]) + +""" + print_spider(io, zwd, v) + +Print a spider to `io`. +""" +function print_spider(io::IO, zwd::ZWDiagram{T,P}, v::T) where {T<:Integer,P} + @match zwd.st[v] begin + monoZ(p) => printstyled(io, "S_$(v){phase = $(p)}"; color = :green) + binZ(p) => printstyled(io, "S_$(v){phase = $(p)}"; color = :green) + Input(q) => printstyled(io, "S_$(v){input = $(q)}"; color = :blue) + Output(q) => printstyled(io, "S_$(v){output = $(q)}"; color = :blue) + SWAP => printstyled(io, "S_$(v){SWAP}"; color = :black) + fSWAP => printstyled(io, "S_$(v){fSWAP}"; color = :black) + W => printstyled(io, "S_$(v){W}"; color = :black) + _ => print(io, "S_$(v)") + end +end + +function Base.show(io::IO, zwd::ZWDiagram{T,P}) where {T<:Integer,P} + println(io, "$(typeof(zwd)) with $(nv(zwd.pmg)) vertices and $(ne(zwd.pmg)) edges:") + for v1 in sort!(vertices(zwd.pmg)) + for v2 in neighbors(zwd.pmg, v1) + if v2 >= v1 + print(io, "(") + print_spider(io, zwd, v1) + print(io, " <- -> ") + print_spider(io, zwd, v2) + print(io, ")\n") + end + end + end +end + +""" + nv(zwd) + +Returns the number of vertices (spiders) of a ZW-diagram. +""" +Graphs.nv(zwd::ZWDiagram) = nv(zwd.pmg) + +""" + ne(zwd) + +Returns the number of edges of a ZW-diagram. +""" +Graphs.ne(zwd::ZWDiagram) = ne(zwd.pmg) + +Graphs.outneighbors(zwd::ZWDiagram, v) = neighbors(zwd.pmg, v) +Graphs.inneighbors(zwd::ZWDiagram, v) = neighbors(zwd.pmg, v) +Graphs.degree(zwd::ZWDiagram, v::Integer) = length(neighbors(zwd.pmg, v)) +Graphs.indegree(zwd::ZWDiagram, v::Integer) = degree(zwd, v) +Graphs.outdegree(zwd::ZWDiagram, v::Integer) = degree(zwd, v) + +""" + neighbors(zwd, v) + +Returns a vector of vertices connected to `v`. +""" +Graphs.neighbors(zwd::ZWDiagram, v) = neighbors(zwd.pmg, v) + +""" + rem_edge!(zwd::ZWDiagram, x) + +Remove Edge with indices `x`. +""" +function rem_edge!(zwd::ZWDiagram, x) + vs = src(zwd.pmg, x) + vd = dst(zwd.pmg, x) + if degree(zwd, vs) < 3 || degree(zwd, vd) < 3 + return join_vertex!(zwd.pmg, x) + else + return join_facet!(zwd.pmg, x) + end +end + +""" + Graphs.add_edge!(zwd::ZWDiagram, he, mul) + +Add `mul` of edges that connects vertices with already connected with edge`x`. + +""" +function Graphs.add_edge!(zwd::ZWDiagram, he, mul::Int) + return add_multiedge!(zwd.pmg, he, mul) +end + +function join_spider!(zwd::ZWDiagram{T,P}, he1::T, he2::T) where {T<:Integer,P} + face(zwd.pmg, he1) != face(zwd.pmg, he2) && + error("The two half edges must be on the same face!") + return split_facet!(zwd.pmg, he1, he2) +end + +""" + add_spider!(zwd, spider, connect = []) + +Add a new spider `spider` with appropriate parameter +connected to the half edges `connect`. + +Had to make halfedge class 1 citizen because there will be ambiguity +Consider A to B and there are multiple edges to A and from A to B +""" +function add_spider!( + zwd::ZWDiagram{T,P}, + spider::ZWSpiderType, + connect::Vector{T}, +) where {T<:Integer,P} + + length(connect) < 1 && error("The new vertex must be connect to something!") + + f_id = face(zwd.pmg, src(zwd.pmg, connect[1])) + he_on_face = trace_face(zwd.pmg, f_id) + if !all(x -> x ∈ he_on_face, connect) + error("You must connect to vertices on the same face!") + end + + + make_hole!(zwd.pmg, f_id) + connect = connect[sortperm([findfirst(x -> x == i, he_on_face) for i in connect])] + he_new = + add_vertex_and_facet_to_boarder!(zwd.pmg, prev(zwd.pmg, connect[1]), connect[1]) + v_new = dst(zwd.pmg, he_new) + zwd.st[v_new] = spider + + he_on_trig = twin(zwd.pmg, next(zwd.pmg, he_new)) + + for next_he in connect[2:end] + join_spider!(zwd, he_on_trig, next_he) + end + return v_new +end + +""" + insert_spider!(zwd, he1, spider) + +Insert a spider `spider` with appropriate parameter on the half-edge prior to `he1`. +v1 <- he1 - v2 becomes +v1 <- he1 - v2 <- he_new - v_new +""" +function insert_spider!( + zwd::ZWDiagram{T,P}, + he1::T, + spider::ZWSpiderType, +) where {T<:Integer,P} + he_new = split_edge!(zwd.pmg, he1) + v_new = dst(zwd.pmg, he_new) + zwd.st[v_new] = spider + return v_new +end + +function rem_spiders!(zwd::ZWDiagram{T,P}, vs::Vector{T}) where {T<:Integer,P} + + for v in vs + if !has_vertex(zwd.pmg, v) || !haskey(zwd.st, v) + error("Spider $v does not exist!") + end + end + + for v in vs + out_hes = trace_vertex(zwd.pmg, v) + for he in out_hes + rem_edge!(zwd, he) + end + delete!(zwd.st, v) + end + return +end + +function rem_spider!(zwd::ZWDiagram{T,P}, v::T) where {T<:Integer,P} + return rem_spiders!(zwd, [v]) +end + + +""" + spiders(zwd::ZWDiagram) + +Returns a vector of spider idxs. +""" +spiders(zwd::ZWDiagram) = vertices(zwd.pmg) + +""" + get_inputs(zwd) + +Returns a vector of input ids. +""" +get_inputs(zwd::ZWDiagram) = zwd.inputs + +""" + get_input_idx(zwd::ZXWDiagram{T,P}, q::T) where {T,P} + +Get spider index of input qubit q. +""" +function get_input_idx(zwd::ZWDiagram{T,P}, q::T) where {T,P} + for (i, v) in enumerate(get_inputs(zwd)) + res = @match spider_type(zwd, v) begin + Input(q2) && if q2 == q + end => v + _ => nothing + end + !isnothing(res) && return res + end + return -1 +end + +""" + get_outputs(zwd) + +Returns a vector of output ids. +""" +get_outputs(zwd::ZWDiagram) = zwd.outputs + +""" + get_output_idx(zwd::ZWDiagram{T,P}, q::T) where {T,P} + +Get spider index of output qubit q. +""" +function get_output_idx(zwd::ZWDiagram{T,P}, q::T) where {T,P} + for (i, v) in enumerate(get_outputs(zwd)) + res = @match spider_type(zwd, v) begin + Output(q2) && if q2 == q + end => v + _ => nothing + end + !isnothing(res) && return res + end + return -1 +end + +scalar(zwd::ZWDiagram) = zwd.scalar + +function add_global_phase!(zwd::ZWDiagram{T,P}, p::P) where {T,P} + add_phase!(zwd.scalar, p) + return zwd +end + +function add_power!(zwd::ZWDiagram, n) + add_power!(zwd.scalar, n) + return zwd +end diff --git a/test/parameter.jl b/test/parameter.jl index 3799bbf..4893bc8 100644 --- a/test/parameter.jl +++ b/test/parameter.jl @@ -1,4 +1,5 @@ using Base: CodegenParams +using ZXCalculus.ZXW: Parameter @testset "Constructor" begin p1 = Parameter(Val(:PiUnit)) diff --git a/test/planar_multigraph.jl b/test/planar_multigraph.jl index d81a8a0..0a13f19 100644 --- a/test/planar_multigraph.jl +++ b/test/planar_multigraph.jl @@ -28,7 +28,7 @@ end Dict(1 => HalfEdge(1, 2), 2 => HalfEdge(2, 1)), Dict(0 => 1), Dict(1 => 0, 2 => 0), - Dict(1 => 0, 2 => 0), + Dict(1 => 2, 2 => 1), Dict(1 => 2, 2 => 1), 2, 2, @@ -334,8 +334,8 @@ end pmg2 = PlanarMultigraph( Dict(1 => 1, 2 => 3, 3 => 4, 4 => 5), Dict( - 1 => HalfEdge(1, 2), - 2 => HalfEdge(2, 1), + 1 => HalfEdge(1, 4), + 2 => HalfEdge(4, 1), 3 => HalfEdge(2, 3), 4 => HalfEdge(3, 2), 5 => HalfEdge(4, 2), @@ -516,8 +516,8 @@ end 4 => HalfEdge(3, 2), 5 => HalfEdge(3, 4), 6 => HalfEdge(4, 3), - 7 => HalfEdge(4, 2), - 8 => HalfEdge(2, 4), + 7 => HalfEdge(2, 4), + 8 => HalfEdge(4, 2), ), Dict(0 => 1, 1 => 8), Dict(1 => 0, 2 => 0, 3 => 1, 4 => 0, 5 => 1, 6 => 0, 7 => 0, 8 => 1), @@ -764,12 +764,12 @@ end Dict( 1 => HalfEdge(1, 2), 2 => HalfEdge(2, 1), - 3 => HalfEdge(1, 2), - 4 => HalfEdge(2, 1), - 5 => HalfEdge(1, 2), - 6 => HalfEdge(2, 1), + 3 => HalfEdge(2, 1), + 4 => HalfEdge(1, 2), + 5 => HalfEdge(2, 1), + 6 => HalfEdge(1, 2), ), - Dict(0 => 1, 1 => 3, 2 => 5), + Dict(0 => 1, 1 => 2, 2 => 3), Dict(1 => 0, 2 => 1, 4 => 1, 3 => 2, 6 => 2, 5 => 0), Dict(1 => 5, 5 => 1, 2 => 4, 4 => 2, 3 => 6, 6 => 3), Dict(1 => 2, 2 => 1, 3 => 4, 4 => 3, 5 => 6, 6 => 5), diff --git a/test/runtests.jl b/test/runtests.jl index 3037922..294ec60 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,8 +1,16 @@ -using ZXCalculus, Graphs, Multigraphs, SparseArrays, ZXCalculus.ZXW +using ZXCalculus, Graphs, Multigraphs, SparseArrays, ZXCalculus.ZXW, ZXCalculus.ZW using YaoHIR: Chain using Documenter using Test +@testset "ZW Diagram with Planar Multigraph" begin + include("zw_diagram.jl") +end + +@testset "ZW Diagram Utilities" begin + include("zw_utils.jl") +end + @testset "planar multigraphs.jl" begin include("planar_multigraph.jl") end diff --git a/test/utils.jl b/test/utils.jl index ea28117..d9444c1 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -5,22 +5,12 @@ using ZXCalculus.ZXW: print_spider, push_gate!, pushfirst_gate!, - add_spider!, insert_wtrig!, expval_circ!, substitute_variables!, - set_phase!, - add_spider!, - spider_type, rem_spiders!, - parameter, - add_inout!, - get_inputs, - get_outputs, - scalar, - nin, - nout, - nqubits + add_inout! + using MLStyle: @match @@ -59,20 +49,20 @@ end zxwd = ZXWDiagram(3) - @test @match spider_type(zxwd, 1) begin - Input(_) => true + @test @match ZXW.spider_type(zxwd, 1) begin + ZXW.Input(_) => true _ => false end - @test_throws ErrorException("Spider 10 does not exist!") spider_type(zxwd, 10) + @test_throws ErrorException("Spider 10 does not exist!") ZXW.spider_type(zxwd, 10) - @test parameter(zxwd, 1) == 1 - @test set_phase!(zxwd, 1, Parameter(Val(:PiUnit), 2 // 3)) - @test !set_phase!(zxwd, 10, Parameter(Val(:PiUnit), 2 // 3)) + @test ZXW.parameter(zxwd, 1) == 1 + @test ZXW.set_phase!(zxwd, 1, Parameter(Val(:PiUnit), 2 // 3)) + @test !ZXW.set_phase!(zxwd, 10, Parameter(Val(:PiUnit), 2 // 3)) - @test nqubits(zxwd) == 3 - @test nin(zxwd) == 3 && nout(zxwd) == 3 - @test scalar(zxwd) == Scalar{Number}() + @test ZXW.nqubits(zxwd) == 3 + @test ZXW.nin(zxwd) == 3 && ZXW.nout(zxwd) == 3 + @test ZXW.scalar(zxwd) == Scalar{Number}() @test nv(zxwd) == 6 && ne(zxwd) == 3 @test rem_edge!(zxwd, 5, 6) @@ -83,29 +73,29 @@ end @test neighbors(zxwd, 5) == [6] - @test_throws ErrorException("The vertex to connect does not exist.") add_spider!( + @test_throws ErrorException("The vertex to connect does not exist.") ZXW.add_spider!( zxwd, W, [10, 15], ) - new_v = add_spider!(zxwd, W, [2, 3]) + new_v = ZXW.add_spider!(zxwd, W, [2, 3]) @test @match zxwd.st[new_v] begin W => true _ => false end - @test parameter(zxwd, new_v) == Parameter(Val(:PiUnit), 0) + @test ZXW.parameter(zxwd, new_v) == Parameter(Val(:PiUnit), 0) - new_v2 = add_spider!(zxwd, Z(Parameter(Val(:PiUnit), 1 // 2)), [2, 3]) + new_v2 = ZXW.add_spider!(zxwd, Z(Parameter(Val(:PiUnit), 1 // 2)), [2, 3]) @test @match zxwd.st[new_v] begin Z => true _ => false end - @test set_phase!(zxwd, new_v2, Parameter(Val(:PiUnit), 3 // 2)) && - parameter(zxwd, new_v2) == Parameter(Val(:PiUnit), 3 // 2) + @test ZXW.set_phase!(zxwd, new_v2, Parameter(Val(:PiUnit), 3 // 2)) && + ZXW.parameter(zxwd, new_v2) == Parameter(Val(:PiUnit), 3 // 2) io = IOBuffer() @@ -116,7 +106,7 @@ end @test String(take!(io)) == "S_8{phase = Parameter.PiUnit(pu=3//2, pu_type=Rational{Int64})}" - new_v3 = add_spider!(zxwd, Z(Parameter(Val(:Factor), 1)), [2, 3]) + new_v3 = ZXW.add_spider!(zxwd, Z(Parameter(Val(:Factor), 1)), [2, 3]) print_spider(io, zxwd, new_v3) @test String(take!(io)) == "S_9{phase = Parameter.Factor(f=1, f_type=Int64)}" @@ -127,13 +117,13 @@ end @test nv(zxwd) == 6 && ne(zxwd) == 1 zxwd = ZXWDiagram(3) - nqubits_prior = nqubits(zxwd) + nqubits_prior = ZXW.nqubits(zxwd) add_inout!(zxwd, 3) - @test nqubits(zxwd) == nqubits_prior + 3 - @test nin(zxwd) == nqubits_prior + 3 - @test nout(zxwd) == nqubits_prior + 3 + @test ZXW.nqubits(zxwd) == nqubits_prior + 3 + @test ZXW.nin(zxwd) == nqubits_prior + 3 + @test ZXW.nout(zxwd) == nqubits_prior + 3 nspiders = nv(zxwd) - @test sort!([get_inputs(zxwd)[end-2:end]; get_outputs(zxwd)[end-2:end]]) == + @test sort!([ZXW.get_inputs(zxwd)[end-2:end]; ZXW.get_outputs(zxwd)[end-2:end]]) == collect(nspiders-5:nspiders) diff --git a/test/zw_diagram.jl b/test/zw_diagram.jl new file mode 100644 index 0000000..f2aba2c --- /dev/null +++ b/test/zw_diagram.jl @@ -0,0 +1,141 @@ +using ZXCalculus.ZW: ZWDiagram, Input, Output +@testset "ZWDiagrm Creation" begin + zx1 = ZWDiagram(1) + pmg1 = PlanarMultigraph( + Dict([1 => 1, 2 => 2]), + Dict([1 => HalfEdge(1, 2), 2 => HalfEdge(2, 1)]), + Dict(0 => 1), + Dict(1 => 0, 2 => 0), + Dict(1 => 2, 2 => 1), + Dict(1 => 2, 2 => 1), + 2, + 2, + 0, + [0], + ) + + + @test zx1.st == Dict([1 => Input(1), 2 => Output(1)]) + @test zx1.inputs == [1] + @test zx1.outputs == [2] + @test zx1.pmg == pmg1 + @test zx1.scalar == Scalar{Rational}() + + zx2 = ZWDiagram(2, Float64) + pmg2 = PlanarMultigraph( + Dict([1 => 1, 2 => 2, 3 => 3, 4 => 4]), + Dict([ + 1 => HalfEdge(1, 2), + 2 => HalfEdge(2, 1), + 3 => HalfEdge(3, 4), + 4 => HalfEdge(4, 3), + 5 => HalfEdge(3, 1), + 6 => HalfEdge(1, 3), + 7 => HalfEdge(4, 2), + 8 => HalfEdge(2, 4), + ]), + Dict([0 => 2, 1 => 1]), + Dict([1 => 1, 8 => 1, 4 => 1, 5 => 1, 2 => 0, 3 => 0, 6 => 0, 7 => 0]), + Dict([1 => 8, 8 => 4, 4 => 5, 5 => 1, 2 => 6, 6 => 3, 3 => 7, 7 => 2]), + Dict([1 => 2, 2 => 1, 3 => 4, 4 => 3, 5 => 6, 6 => 5, 7 => 8, 8 => 7]), + 4, + 8, + 1, + [0], + ) + + @test zx2.st == Dict([1 => Input(1), 2 => Output(1), 3 => Input(2), 4 => Output(2)]) + @test zx2.inputs == [1, 3] + @test zx2.outputs == [2, 4] + @test zx2.pmg == pmg2 + @test zx2.pmg.half_edges == pmg2.half_edges + @test zx2.scalar == Scalar{Float64}() + + zx3 = ZWDiagram(3) + + pmg3 = PlanarMultigraph( + Dict([1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6]), + Dict([ + 1 => HalfEdge(1, 2), + 2 => HalfEdge(2, 1), + 3 => HalfEdge(3, 4), + 4 => HalfEdge(4, 3), + 5 => HalfEdge(5, 6), + 6 => HalfEdge(6, 5), + 7 => HalfEdge(3, 1), + 8 => HalfEdge(1, 3), + 9 => HalfEdge(5, 3), + 10 => HalfEdge(3, 5), + 11 => HalfEdge(4, 2), + 12 => HalfEdge(2, 4), + 13 => HalfEdge(6, 4), + 14 => HalfEdge(4, 6), + ]), + Dict([0 => 2, 1 => 1, 2 => 3]), + Dict([ + 1 => 1, + 12 => 1, + 4 => 1, + 7 => 1, + 3 => 2, + 14 => 2, + 6 => 2, + 9 => 2, + 2 => 0, + 5 => 0, + 8 => 0, + 11 => 0, + 10 => 0, + 13 => 0, + ]), + Dict([ + 1 => 12, + 12 => 4, + 4 => 7, + 7 => 1, + 3 => 14, + 14 => 6, + 6 => 9, + 9 => 3, + 2 => 8, + 8 => 10, + 10 => 5, + 5 => 13, + 13 => 11, + 11 => 2, + ]), + Dict([ + 1 => 2, + 2 => 1, + 3 => 4, + 4 => 3, + 5 => 6, + 6 => 5, + 7 => 8, + 8 => 7, + 9 => 10, + 10 => 9, + 11 => 12, + 12 => 11, + 13 => 14, + 14 => 13, + ]), + 6, + 14, + 2, + [0], + ) + + @test zx3.st == Dict([ + 1 => Input(1), + 2 => Output(1), + 3 => Input(2), + 4 => Output(2), + 5 => Input(3), + 6 => Output(3), + ]) + @test zx3.inputs == [1, 3, 5] + @test zx3.outputs == [2, 4, 6] + @test zx3.pmg == pmg3 + @test zx3.pmg.half_edges == pmg3.half_edges +end diff --git a/test/zw_utils.jl b/test/zw_utils.jl new file mode 100644 index 0000000..81d6ce3 --- /dev/null +++ b/test/zw_utils.jl @@ -0,0 +1,470 @@ +using ZXCalculus.ZW: + ZWDiagram, + ZWSpiderType, + spider_type, + set_phase!, + parameter, + nin, + nout, + nqubits, + nv, + ne, + degree, + indegree, + outdegree, + outneighbors, + inneighbors, + neighbors, + spiders, + scalar, + get_inputs, + get_outputs, + get_input_idx, + get_output_idx, + add_power!, + add_global_phase!, + insert_spider!, + neighbors, + join_spider!, + add_edge!, + add_spider!, + rem_edge!, + rem_spider! + + +using ZXCalculus: trace_vertex +using ZXCalculus.ZXW: Parameter + +@testset "utils" begin + zw = ZWDiagram(3) + + @test spider_type(zw, 1) == ZW.Input(1) + @test parameter(zw, 2) == 1 + @test nqubits(zw) == 3 + @test nin(zw) == 3 + @test nout(zw) == 3 + @test nv(zw) == 6 + @test ne(zw) == 7 + @test sort(outneighbors(zw, 1)) == [2, 3] + @test sort(inneighbors(zw, 1)) == [2, 3] + @test sort(neighbors(zw, 3)) == [1, 4, 5] + @test degree(zw, 1) == 2 + @test indegree(zw, 1) == 2 + @test outdegree(zw, 1) == 2 + + @test sort(spiders(zw)) == [1, 2, 3, 4, 5, 6] + @test sort(get_inputs(zw)) == [1, 3, 5] + @test sort(get_outputs(zw)) == [2, 4, 6] + + @test get_input_idx(zw, 2) == 3 + @test get_output_idx(zw, 2) == 4 + + sc = scalar(zw) + @test sc == Scalar{Rational}() + add_power!(zw, 2) + add_global_phase!(zw, 1 // 2) + sc = scalar(zw) + @test sc == Scalar{Rational}(2, 1 // 2) + + # TODO + # set_phase! +end + +@testset "Add and Rem Spiders" begin + + zw = ZWDiagram(3) + + pmg2 = PlanarMultigraph( + Dict(1 => 1, 7 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 2 => 16), + Dict( + 1 => HalfEdge(1, 7), + 2 => HalfEdge(7, 1), + 3 => HalfEdge(3, 4), + 4 => HalfEdge(4, 3), + 5 => HalfEdge(5, 6), + 6 => HalfEdge(6, 5), + 7 => HalfEdge(3, 1), + 8 => HalfEdge(1, 3), + 9 => HalfEdge(5, 3), + 10 => HalfEdge(3, 5), + 11 => HalfEdge(4, 2), + 12 => HalfEdge(2, 4), + 13 => HalfEdge(6, 4), + 14 => HalfEdge(4, 6), + 15 => HalfEdge(7, 2), + 16 => HalfEdge(2, 7), + ), + Dict(0 => 2, 1 => 1, 2 => 3), + Dict( + 1 => 1, + 16 => 0, + 12 => 1, + 4 => 1, + 7 => 1, + 3 => 2, + 14 => 2, + 6 => 2, + 9 => 2, + 2 => 0, + 15 => 1, + 11 => 0, + 5 => 0, + 8 => 0, + 13 => 0, + 10 => 0, + ), + Dict( + 1 => 15, + 15 => 12, + 12 => 4, + 4 => 7, + 7 => 1, + 3 => 14, + 14 => 6, + 6 => 9, + 9 => 3, + 2 => 8, + 8 => 10, + 10 => 5, + 5 => 13, + 13 => 11, + 11 => 16, + 16 => 2, + ), + Dict( + 1 => 2, + 2 => 1, + 3 => 4, + 4 => 3, + 5 => 6, + 6 => 5, + 7 => 8, + 8 => 7, + 9 => 10, + 10 => 9, + 11 => 12, + 12 => 11, + 13 => 14, + 14 => 13, + 15 => 16, + 16 => 15, + ), + 7, + 16, + 2, + [0], + ) + + insert_spider!(zw, 12, ZW.binZ(Parameter(Val(:Factor), 2.0))) + @test zw.pmg == pmg2 + + set_phase!(zw, 7, Parameter(Val(:PiUnit), 1)) + @test zw.st[7] == ZW.binZ(Parameter(Val(:PiUnit), 1)) + + join_spider!(zw, 1, 4) + pmg3 = PlanarMultigraph( + Dict(1 => 1, 7 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 2 => 16), + Dict( + 1 => HalfEdge(1, 7), + 2 => HalfEdge(7, 1), + 3 => HalfEdge(3, 4), + 4 => HalfEdge(4, 3), + 5 => HalfEdge(5, 6), + 6 => HalfEdge(6, 5), + 7 => HalfEdge(3, 1), + 8 => HalfEdge(1, 3), + 9 => HalfEdge(5, 3), + 10 => HalfEdge(3, 5), + 11 => HalfEdge(4, 2), + 12 => HalfEdge(2, 4), + 13 => HalfEdge(6, 4), + 14 => HalfEdge(4, 6), + 15 => HalfEdge(7, 2), + 16 => HalfEdge(2, 7), + 17 => HalfEdge(7, 3), + 18 => HalfEdge(3, 7), + ), + Dict(0 => 2, 1 => 17, 2 => 3, 3 => 18), + Dict( + 1 => 1, + 16 => 0, + 12 => 3, + 4 => 3, + 7 => 1, + 3 => 2, + 14 => 2, + 6 => 2, + 9 => 2, + 2 => 0, + 15 => 3, + 11 => 0, + 5 => 0, + 8 => 0, + 13 => 0, + 10 => 0, + 17 => 1, + 18 => 3, + ), + Dict( + 1 => 17, + 17 => 7, + 7 => 1, + 2 => 8, + 8 => 10, + 10 => 5, + 5 => 13, + 13 => 11, + 11 => 16, + 16 => 2, + 15 => 12, + 12 => 4, + 4 => 18, + 18 => 15, + 3 => 14, + 14 => 6, + 6 => 9, + 9 => 3, + ), + Dict( + 1 => 2, + 2 => 1, + 3 => 4, + 4 => 3, + 5 => 6, + 6 => 5, + 7 => 8, + 8 => 7, + 9 => 10, + 10 => 9, + 11 => 12, + 12 => 11, + 13 => 14, + 14 => 13, + 15 => 16, + 16 => 15, + 17 => 18, + 18 => 17, + ), + 7, + 18, + 3, + [0], + ) + @test pmg3 == zw.pmg + + rem_edge!(zw, 17) + @test pmg2 == zw.pmg + + zw2 = ZWDiagram(3) + insert_spider!(zw2, 12, ZW.binZ(Parameter(Val(:Factor), 2.0))) + + add_spider!(zw2, ZW.fSWAP, [1, 7, 4]) + st2 = Dict( + 5 => ZWSpiderType.Input(qubit = 3), + 4 => ZWSpiderType.Output(qubit = 2), + 6 => ZWSpiderType.Output(qubit = 3), + 7 => ZWSpiderType.binZ(r = Parameter.Factor(f = 2.0, f_type = Float64)), + 2 => ZWSpiderType.Output(qubit = 1), + 3 => ZWSpiderType.Input(qubit = 2), + 8 => ZWSpiderType.fSWAP, + 1 => ZWSpiderType.Input(qubit = 1), + ) + pmg4 = PlanarMultigraph( + Dict(1 => 1, 2 => 16, 3 => 7, 4 => 4, 5 => 5, 6 => 6, 7 => 2, 8 => 18), + Dict( + 1 => HalfEdge(1, 7), + 2 => HalfEdge(7, 1), + 3 => HalfEdge(3, 4), + 4 => HalfEdge(4, 3), + 5 => HalfEdge(5, 6), + 6 => HalfEdge(6, 5), + 7 => HalfEdge(3, 1), + 8 => HalfEdge(1, 3), + 9 => HalfEdge(5, 3), + 10 => HalfEdge(3, 5), + 11 => HalfEdge(4, 2), + 12 => HalfEdge(2, 4), + 13 => HalfEdge(6, 4), + 14 => HalfEdge(4, 6), + 15 => HalfEdge(7, 2), + 16 => HalfEdge(2, 7), + 17 => HalfEdge(4, 8), + 18 => HalfEdge(8, 4), + 19 => HalfEdge(3, 8), + 20 => HalfEdge(8, 3), + 21 => HalfEdge(8, 1), + 22 => HalfEdge(1, 8), + 23 => HalfEdge(8, 7), + 24 => HalfEdge(7, 8), + ), + Dict(1 => 15, 2 => 3, 3 => 4, 4 => 7, 5 => 1, 0 => 2), + Dict( + 18 => 3, + 4 => 3, + 19 => 3, + 3 => 2, + 14 => 2, + 6 => 2, + 9 => 2, + 22 => 4, + 20 => 4, + 7 => 4, + 1 => 5, + 24 => 5, + 21 => 5, + 15 => 1, + 12 => 1, + 17 => 1, + 23 => 1, + 2 => 0, + 8 => 0, + 10 => 0, + 5 => 0, + 13 => 0, + 11 => 0, + 16 => 0, + ), + Dict( + 1 => 24, + 24 => 21, + 21 => 1, + 2 => 8, + 8 => 10, + 10 => 5, + 5 => 13, + 13 => 11, + 11 => 16, + 16 => 2, + 15 => 12, + 12 => 17, + 17 => 23, + 23 => 15, + 18 => 4, + 4 => 19, + 19 => 18, + 22 => 20, + 20 => 7, + 7 => 22, + 3 => 14, + 14 => 6, + 6 => 9, + 9 => 3, + ), + Dict( + 1 => 2, + 2 => 1, + 3 => 4, + 4 => 3, + 5 => 6, + 6 => 5, + 7 => 8, + 8 => 7, + 9 => 10, + 10 => 9, + 11 => 12, + 12 => 11, + 13 => 14, + 14 => 13, + 15 => 16, + 16 => 15, + 17 => 18, + 18 => 17, + 19 => 20, + 20 => 19, + 21 => 22, + 22 => 21, + 23 => 24, + 24 => 23, + ), + 8, + 24, + 5, + [0, 1], + ) + @test zw2.pmg == pmg4 + @test zw2.st == st2 + + rem_spider!(zw2, 8) + + pmg5 = PlanarMultigraph( + Dict(1 => 1, 7 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 2 => 16), + Dict( + 1 => HalfEdge(1, 7), + 2 => HalfEdge(7, 1), + 3 => HalfEdge(3, 4), + 4 => HalfEdge(4, 3), + 5 => HalfEdge(5, 6), + 6 => HalfEdge(6, 5), + 7 => HalfEdge(3, 1), + 8 => HalfEdge(1, 3), + 9 => HalfEdge(5, 3), + 10 => HalfEdge(3, 5), + 11 => HalfEdge(4, 2), + 12 => HalfEdge(2, 4), + 13 => HalfEdge(6, 4), + 14 => HalfEdge(4, 6), + 15 => HalfEdge(7, 2), + 16 => HalfEdge(2, 7), + ), + Dict(0 => 2, 4 => 1, 2 => 3), + Dict( + 1 => 4, + 16 => 0, + 12 => 4, + 4 => 4, + 7 => 4, + 3 => 2, + 14 => 2, + 6 => 2, + 9 => 2, + 2 => 0, + 15 => 4, + 11 => 0, + 5 => 0, + 8 => 0, + 13 => 0, + 10 => 0, + ), + Dict( + 1 => 15, + 15 => 12, + 12 => 4, + 4 => 7, + 7 => 1, + 3 => 14, + 14 => 6, + 6 => 9, + 9 => 3, + 2 => 8, + 8 => 10, + 10 => 5, + 5 => 13, + 13 => 11, + 11 => 16, + 16 => 2, + ), + Dict( + 1 => 2, + 2 => 1, + 3 => 4, + 4 => 3, + 5 => 6, + 6 => 5, + 7 => 8, + 8 => 7, + 9 => 10, + 10 => 9, + 11 => 12, + 12 => 11, + 13 => 14, + 14 => 13, + 15 => 16, + 16 => 15, + ), + 8, + 24, + 5, + [0, 4], + ) + @test zw2.pmg == pmg5 +end diff --git a/test/zxw_diagram.jl b/test/zxw_diagram.jl index c6126ec..2ab4fb6 100644 --- a/test/zxw_diagram.jl +++ b/test/zxw_diagram.jl @@ -1,3 +1,5 @@ +using ZXCalculus.ZXW: ZXWSpiderType, Parameter, PiUnit, Factor, Input, Output, W, H, D, Z, X + @testset "ZXWSpiderType" begin spider_vec = @@ -33,7 +35,7 @@ end @test zxwd_vec.mg == zxwd_dic.mg && zxwd_vec.st == zxwd_dic.st g = Multigraph([0 1 0 0; 1 0 0 0; 0 0 0 1; 0 0 1 0]) - v_t = [Input(1) Output(1) Input(2) Output(2)] + v_t = [ZXW.Input(1) ZXW.Output(1) ZXW.Input(2) ZXW.Output(2)] zxwd_empty = ZXWDiagram(2) @test zxwd_empty.mg.adjlist == g.adjlist && zxwd_empty.st == Dict(zip(1:4, v_t))