From f4b39f376bc61268b3efca74c795876d8304138a Mon Sep 17 00:00:00 2001 From: Xiwei Pan Date: Wed, 9 Jul 2025 01:27:30 +0800 Subject: [PATCH 1/3] Refactor grid and mapping structures to support triangular lattice grid. --- project/createmap.jl | 30 ++- src/Core.jl | 66 ++++++- src/UnitDiskMapping.jl | 6 +- src/mapping.jl | 57 +++--- src/triangular.jl | 425 +++++++++++++++++++++++++++++++++++++++++ src/utils.jl | 24 +++ src/visualize.jl | 22 ++- src/weighted.jl | 23 ++- 8 files changed, 608 insertions(+), 45 deletions(-) create mode 100644 src/triangular.jl diff --git a/project/createmap.jl b/project/createmap.jl index 6920df5..081a5b7 100644 --- a/project/createmap.jl +++ b/project/createmap.jl @@ -1,3 +1,5 @@ +# TODO: Some interface has changed. We need to update the code. + using UnitDiskMapping, GenericTensorNetworks, Graphs function mapped_entry_to_compact(s::Pattern) @@ -35,11 +37,35 @@ function source_entry_to_configs(s::Pattern) return d end +# function compute_mis_overhead(s) +# locs1, g1, pins1 = source_graph(s) +# locs2, g2, pins2 = mapped_graph(s) +# m1 = mis_compactify!(solve(IndependentSet(g1, openvertices=pins1), SizeMax())) +# m2 = mis_compactify!(solve(IndependentSet(g2, openvertices=pins2), SizeMax())) +# @assert nv(g1) == length(locs1) && nv(g2) == length(locs2) +# sig, diff = UnitDiskMapping.is_diff_by_const(GenericTensorNetworks.content.(m1), GenericTensorNetworks.content.(m2)) +# @assert sig +# return diff +# end + function compute_mis_overhead(s) locs1, g1, pins1 = source_graph(s) locs2, g2, pins2 = mapped_graph(s) - m1 = mis_compactify!(solve(IndependentSet(g1, openvertices=pins1), SizeMax())) - m2 = mis_compactify!(solve(IndependentSet(g2, openvertices=pins2), SizeMax())) + m1 = mis_compactify!(solve(IndependentSet(g1, ones(Int, length(locs1))), SizeMax())) + m2 = mis_compactify!(solve(IndependentSet(g2, ones(Int, length(locs2))), SizeMax())) + @assert nv(g1) == length(locs1) && nv(g2) == length(locs2) + sig, diff = UnitDiskMapping.is_diff_by_const(GenericTensorNetworks.content.(m1), GenericTensorNetworks.content.(m2)) + @assert sig + return diff +end + +function compute_mis_overhead_weighted(s) + weighted_s = UnitDiskMapping.weighted(s) + locs1, g1, pins1 = source_graph(s) + locs2, g2, pins2 = mapped_graph(s) + m1 = mis_compactify!(solve(IndependentSet(g1, weighted_s.source_weights), SizeMax())) + m2 = mis_compactify!(solve(IndependentSet(g2, weighted_s.mapped_weights), SizeMax())) + @show m1, m2 @assert nv(g1) == length(locs1) && nv(g2) == length(locs2) sig, diff = UnitDiskMapping.is_diff_by_const(GenericTensorNetworks.content.(m1), GenericTensorNetworks.content.(m2)) @assert sig diff --git a/src/Core.jl b/src/Core.jl index 18049f2..330b5f4 100644 --- a/src/Core.jl +++ b/src/Core.jl @@ -56,30 +56,82 @@ offset(p::Node, xy) = chxy(p, getxy(p) .+ xy) const WeightedNode{T<:Real} = Node{T} const UnWeightedNode = Node{ONE} +############################ Grid Types ############################ +# Abstract grid geometry types +abstract type AbstractGridType end + +struct SquareGrid <: AbstractGridType end +struct TriangularGrid <: AbstractGridType end + ############################ GridGraph ############################ -# GridGraph -struct GridGraph{NT<:Node} +# Main definition +struct GridGraph{NT<:Node, GT<:AbstractGridType} + gridtype::GT size::Tuple{Int,Int} nodes::Vector{NT} radius::Float64 end + +is_square_grid(g::GridGraph) = g.gridtype isa SquareGrid +is_triangular_grid(g::GridGraph) = g.gridtype isa TriangularGrid + +# Base constructors (for any node/grid type) +GridGraph(size::Tuple{Int,Int}, nodes::Vector{NT}, radius::Real) where {NT<:Node} = + GridGraph(SquareGrid(), size, nodes, radius) + +GridGraph(gridtype::GT, size::Tuple{Int,Int}, nodes::Vector{NT}, radius::Real) where {NT<:Node, GT<:AbstractGridType} = + GridGraph{NT, GT}(gridtype, size, nodes, radius) + function Base.show(io::IO, grid::GridGraph) - println(io, "$(typeof(grid)) (radius = $(grid.radius))") + gridtype_name = grid.gridtype isa SquareGrid ? "Square" : "Triangular" + println(io, "$(gridtype_name)$(typeof(grid)) (radius = $(grid.radius))") print_grid(io, grid; show_weight=SHOW_WEIGHT[]) end Base.size(gg::GridGraph) = gg.size Base.size(gg::GridGraph, i::Int) = gg.size[i] function graph_and_weights(grid::GridGraph) - return unit_disk_graph(getfield.(grid.nodes, :loc), grid.radius), getfield.(grid.nodes, :weight) + if is_triangular_grid(grid) + # For triangular grids, use physical positions + physical_locs = [physical_position(node) for node in grid.nodes] + return unit_disk_graph(physical_locs, grid.radius), getfield.(grid.nodes, :weight) + else + # For square grids, use original coordinates + return unit_disk_graph(getfield.(grid.nodes, :loc), grid.radius), getfield.(grid.nodes, :weight) + end end -function Graphs.SimpleGraph(grid::GridGraph{Node{ONE}}) - return unit_disk_graph(getfield.(grid.nodes, :loc), grid.radius) +function Graphs.SimpleGraph(grid::GridGraph{Node{ONE}, GT}) where GT + if is_triangular_grid(grid) + # For triangular grids, use physical positions + physical_locs = [physical_position(node) for node in grid.nodes] + return unit_disk_graph(physical_locs, grid.radius) + else + # For square grids, use original coordinates + return unit_disk_graph(getfield.(grid.nodes, :loc), grid.radius) + end end coordinates(grid::GridGraph) = getfield.(grid.nodes, :loc) + +# Neighbor calculation with runtime grid type dispatch function Graphs.neighbors(g::GridGraph, i::Int) - [j for j in 1:nv(g) if i != j && distance(g.nodes[i], g.nodes[j]) <= g.radius] + if is_triangular_grid(g) + # Use physical positions for triangular grid distance calculation + [j for j in 1:nv(g) if i != j && triangular_distance(g.nodes[i], g.nodes[j]) <= g.radius] + else + # Default square grid calculation + [j for j in 1:nv(g) if i != j && distance(g.nodes[i], g.nodes[j]) <= g.radius] + end end + distance(n1::Node, n2::Node) = sqrt(sum(abs2, n1.loc .- n2.loc)) + +# Distance calculation for triangular grids using physical positions +function triangular_distance(n1::Node, n2::Node) + # Convert to physical positions as in triangular.jl + p1 = physical_position(n1) + p2 = physical_position(n2) + return sqrt(sum(abs2, p1.loc .- p2.loc)) +end + Graphs.nv(g::GridGraph) = length(g.nodes) Graphs.vertices(g::GridGraph) = 1:nv(g) diff --git a/src/UnitDiskMapping.jl b/src/UnitDiskMapping.jl index 628f921..e6dfc04 100644 --- a/src/UnitDiskMapping.jl +++ b/src/UnitDiskMapping.jl @@ -7,7 +7,8 @@ using LuxorGraphPlot using LuxorGraphPlot.Luxor.Colors # Basic types -export UnWeighted, Weighted +export UnWeighted, Weighted, TriangularWeighted +export SquareGrid, TriangularGrid export Cell, AbstractCell, SimpleCell export Node, WeightedNode, UnWeightedNode export graph_and_weights, GridGraph, coordinates @@ -41,8 +42,8 @@ export pathwidth, PathDecompositionMethod, MinhThiTrick, Greedy @deprecate Branching MinhThiTrick -include("utils.jl") include("Core.jl") +include("utils.jl") include("pathdecomposition/pathdecomposition.jl") include("copyline.jl") include("dragondrop.jl") @@ -51,6 +52,7 @@ include("logicgates.jl") include("gadgets.jl") include("mapping.jl") include("weighted.jl") +include("triangular.jl") include("simplifiers.jl") include("extracting_results.jl") include("visualize.jl") diff --git a/src/mapping.jl b/src/mapping.jl index 1297393..f747e0b 100644 --- a/src/mapping.jl +++ b/src/mapping.jl @@ -2,6 +2,12 @@ struct UnWeighted end # Weighted mode struct Weighted end +# TriangularWeighted mode +struct TriangularWeighted end + +# Get spacing value based on mode +get_spacing(::Union{UnWeighted, Weighted}) = 4 +get_spacing(::TriangularWeighted) = 6 Base.@kwdef struct MCell{WT} <: AbstractCell{WT} occupied::Bool = true @@ -63,9 +69,10 @@ struct MappingGrid{CT<:AbstractCell} lines::Vector{CopyLine} padding::Int content::Matrix{CT} + spacing::Int end -Base.:(==)(ug::MappingGrid{CT}, ug2::MappingGrid{CT}) where CT = ug.lines == ug2.lines && ug.content == ug2.content +Base.:(==)(ug::MappingGrid{CT}, ug2::MappingGrid{CT}) where CT = ug.lines == ug2.lines && ug.content == ug2.content && ug.spacing == ug2.spacing Base.size(ug::MappingGrid, args...) = size(ug.content, args...) padding(ug::MappingGrid) = ug.padding coordinates(ug::MappingGrid) = [ci.I for ci in findall(!isempty, ug.content)] @@ -91,15 +98,19 @@ function Graphs.SimpleGraph(ug::MappingGrid) end return unitdisk_graph(coordinates(ug), 1.5) end -function GridGraph(ug::MappingGrid) +function GridGraph(mode, ug::MappingGrid) if any(x->x.doubled || x.connected, ug.content) error("This mapping is not done yet!") end - return GridGraph(size(ug), [Node((i,j), ug.content[i,j].weight) for (i, j) in coordinates(ug)], 1.5) + if mode isa TriangularWeighted + return GridGraph(TriangularGrid(), size(ug), [Node((i,j), ug.content[i,j].weight) for (i, j) in coordinates(ug)], 1.1) + else + return GridGraph(size(ug), [Node((i,j), ug.content[i,j].weight) for (i, j) in coordinates(ug)], 1.5) + end end Base.show(io::IO, ug::MappingGrid) = print_grid(io, ug.content) -Base.copy(ug::MappingGrid) = MappingGrid(ug.lines, ug.padding, copy(ug.content)) +Base.copy(ug::MappingGrid) = MappingGrid(ug.lines, ug.padding, copy(ug.content), ug.spacing) # TODO: # 1. check if the resulting graph is a unit-disk @@ -230,18 +241,16 @@ function remove_order(g::AbstractGraph, vertex_order::AbstractVector{Int}) return addremove end -function center_location(tc::CopyLine; padding::Int) - s = 4 +function center_location(tc::CopyLine; padding::Int, s::Int=4) I = s*(tc.hslot-1)+padding+2 J = s*(tc.vslot-1)+padding+1 return I, J end # NT is node type -function copyline_locations(::Type{NT}, tc::CopyLine; padding::Int) where NT - s = 4 +function copyline_locations(::Type{NT}, tc::CopyLine; padding::Int, s::Int=4) where NT nline = 0 - I, J = center_location(tc; padding=padding) + I, J = center_location(tc; padding=padding, s=s) locations = NT[] # grow up start = I+s*(tc.vstart-tc.hslot)+1 @@ -278,25 +287,26 @@ nodetype(::MappingGrid{MCell{WT}}) where WT = Node{WT} cell_type(::Type{Node{WT}}) where WT = MCell{WT} nodetype(::UnWeighted) = UnWeightedNode +nodetype(::TriangularWeighted) = WeightedNode{Int} node(::Type{<:UnWeightedNode}, i, j, w) = Node(i, j) function ugrid(mode, g::SimpleGraph, vertex_order::AbstractVector{Int}; padding=2, nrow=nv(g)) @assert padding >= 2 # create an empty canvas n = nv(g) - s = 4 - N = (n-1)*s+1+2*padding - M = nrow*s+1+2*padding - u = fill(empty(mode isa Weighted ? MCell{Int} : MCell{ONE}), M, N) + s = get_spacing(mode) + N = (n-1)*s+2+2*padding + M = nrow*s+2+2*padding + u = fill(empty(mode isa Union{Weighted, TriangularWeighted} ? MCell{Int} : MCell{ONE}), M, N) # add T-copies copylines = create_copylines(g, vertex_order) for tc in copylines - for loc in copyline_locations(nodetype(mode), tc; padding=padding) + for loc in copyline_locations(nodetype(mode), tc; padding=padding, s=s) add_cell!(u, loc) end end - ug = MappingGrid(copylines, padding, u) + ug = MappingGrid(copylines, padding, u, s) for e in edges(g) I, J = crossat(ug, e.src, e.dst) connect_cell!(ug.content, I, J-1) @@ -313,7 +323,7 @@ function crossat(ug::MappingGrid, v, w) i, j = findfirst(x->x.vertex==v, ug.lines), findfirst(x->x.vertex==w, ug.lines) i, j = minmax(i, j) hslot = ug.lines[i].hslot - s = 4 + s = ug.spacing return (hslot-1)*s+2+ug.padding, (j-1)*s+1+ug.padding end @@ -341,18 +351,17 @@ end function mis_overhead_copylines(ug::MappingGrid{WC}) where {WC} sum(ug.lines) do line - mis_overhead_copyline(WC <: WeightedMCell ? Weighted() : UnWeighted(), line) + mis_overhead_copyline(WC <: WeightedMCell ? Weighted() : UnWeighted(), line, ug.spacing) end end -function mis_overhead_copyline(w::W, line::CopyLine) where W - if W === Weighted - s = 4 +function mis_overhead_copyline(w::W, line::CopyLine, s::Int=4) where W + if W === Weighted || W === TriangularWeighted return (line.hslot - line.vstart) * s + (line.vstop - line.hslot) * s + max((line.hstop - line.vslot) * s - 2, 0) else - locs = copyline_locations(nodetype(w), line; padding=2) + locs = copyline_locations(nodetype(w), line; padding=2, s=s) @assert length(locs) % 2 == 1 return length(locs) ÷ 2 end @@ -365,6 +374,7 @@ struct MappingResult{NT} padding::Int mapping_history::Vector{Tuple{Pattern,Int,Int}} mis_overhead::Int + spacing::Int end """ @@ -398,7 +408,7 @@ function map_graph(mode, g::SimpleGraph; vertex_order=MinhThiTrick(), ruleset=de ug, tape2 = apply_simplifier_gadgets!(ug; ruleset=ruleset) mis_overhead1 = isempty(tape) ? 0 : sum(x->mis_overhead(x[1]), tape) mis_overhead2 = isempty(tape2) ? 0 : sum(x->mis_overhead(x[1]), tape2) - return MappingResult(GridGraph(ug), ug.lines, ug.padding, vcat(tape, tape2) , mis_overhead0 + mis_overhead1 + mis_overhead2) + return MappingResult(GridGraph(mode, ug), ug.lines, ug.padding, vcat(tape, tape2) , mis_overhead0 + mis_overhead1 + mis_overhead2, ug.spacing) end """ @@ -427,12 +437,13 @@ function map_config_back(res::MappingResult, cfg) end function _map_configs_back(r::MappingResult{UnWeightedNode}, configs::AbstractVector{<:AbstractMatrix}) cm = cell_matrix(r.grid_graph) - ug = MappingGrid(r.lines, r.padding, MCell.(cm)) + ug = MappingGrid(r.lines, r.padding, MCell.(cm), r.spacing) unapply_gadgets!(ug, r.mapping_history, copy.(configs))[2] end default_simplifier_ruleset(::UnWeighted) = vcat([rotated_and_reflected(rule) for rule in simplifier_ruleset]...) default_simplifier_ruleset(::Weighted) = weighted.(default_simplifier_ruleset(UnWeighted())) +default_simplifier_ruleset(::TriangularWeighted) = weighted.(default_simplifier_ruleset(UnWeighted())) print_config(mr::MappingResult, config::AbstractMatrix) = print_config(stdout, mr, config) function print_config(io::IO, mr::MappingResult, config::AbstractMatrix) diff --git a/src/triangular.jl b/src/triangular.jl new file mode 100644 index 0000000..5d44265 --- /dev/null +++ b/src/triangular.jl @@ -0,0 +1,425 @@ +abstract type TriangularCrossPattern <: Pattern end + +struct TriCross{CON} <: TriangularCrossPattern end +iscon(::TriCross{CON}) where {CON} = CON +# · ⋅ ● ⋅ · +# ● ◆ ◉ ● ● +# · ⋅ ◆ · · +# ⋅ ⋅ ● ⋅ ⋅ +# ⋅ ⋅ ● ⋅ ⋅ +# ⋅ ⋅ ● ⋅ ⋅ +function source_graph(::TriCross{true}) + locs = Node.([(2,1), (2,2), (2,3), (2,4), (2,5), (1,3), (2,3), (3,3), (4,3), (5,3), (6,3)]) + g = simplegraph([(1,2), (2,3), (3,4), (4,5), (6,7), (7,8), (8,9), (9,10), (10,11), (2,6)]) + return locs, g, [1,6,11,5] +end + +# ⋅ · ● ⋅ ⋅ +# ● ● ● ● ● +# ⋅ ● ⋅ ● ⋅ +# ⋅ ● ● · ⋅ +# ⋅ · · ● ⋅ +# ⋅ ⋅ ● ● ⋅ +function mapped_graph(::TriCross{true}) + locs = Node.([(1,3), (2,1), (2,2), (2,3), (2,4), (2,5), (3,2), (3,4), (4,2), (4,3), (5,4), (6,3), (6,4)]) + return locs, triangular_unitdisk_graph(locs, 1.1, false), [2,1,12,6] +end +Base.size(::TriCross{true}) = (6, 5) +cross_location(::TriCross{true}) = (2, 3) +connected_nodes(::TriCross{true}) = [2, 6] + +function weighted(p::TriCross{true}) + sw = [2,2,2,2,2,2,2,2,2,2,2] + mw = [3,2,3,4,3,2,3,2,2,2,2,2,2] + return weighted(p, sw, mw) +end + +# ⋅ ⋅ ● ⋅ ⋅ +# ● ● ◉ ● ● +# ⋅ ⋅ ● ⋅ ⋅ +# ⋅ ⋅ ● ⋅ ⋅ +# ⋅ ⋅ ● ⋅ ⋅ +# ⋅ ⋅ ● ⋅ ⋅ +function source_graph(::TriCross{false}) + locs = Node.([(2,1), (2,2), (2,3), (2,4), (2,5), (1,3), (2,3), (3,3), (4,3), (5,3), (6,3)]) + g = simplegraph([(1,2), (2,3), (3,4), (4,5), (6,7), (7,8), (8,9), (9,10), (10,11)]) + return locs, g, [1,6,11,5] +end + +# ⋅ ⋅ ● ⋅ ⋅ +# ● ● ● ● ● +# ● ● ● ● · +# ● ● · ⋅ ⋅ +# ● ⋅ · ⋅ ⋅ +# · ● ● ⋅ ⋅ +function mapped_graph(::TriCross{false}) + locs = Node.([(1,3), (2,1), (2,2), (2,3), (2,4), (2,5), (3,1), (3,2), (3,3), (3,4), (4,1), (4,2), (5,1), (6,2), (6,3)]) + return locs, triangular_unitdisk_graph(locs, 1.1, false), [2,1,15,6] +end +Base.size(::TriCross{false}) = (6, 5) +cross_location(::TriCross{false}) = (2,3) + +function weighted(p::TriCross{false}) + sw = [2,2,2,2,2,2,2,2,2,2,2] + mw = [3,3,2,4,2,2,2,4,3,2,2,2,2,2,2] + return weighted(p, sw, mw) +end + +struct TriTCon_left <: TriangularCrossPattern end +# ⋅ ◆ ⋅ ⋅ · +# ◆ ● · · · +# ⋅ ● · ⋅ · +# ⋅ ● · · · +# · ● · · · +# · ● · · · +function source_graph(::TriTCon_left) + locs = Node.([(1,2), (2,1), (2,2), (3,2), (4,2), (5,2), (6,2)]) + g = simplegraph([(1,2), (1,3), (3,4), (4,5), (5,6), (6,7)]) + return locs, g, [1,2,7] +end +connected_nodes(::TriTCon_left) = [1, 2] + +# ⋅ ● ⋅ ⋅ · +# ● ● ● ● · +# ⋅ · ● ⋅ · +# ⋅ ● ● · · +# ● · · · · +# ● ● · · · +function mapped_graph(::TriTCon_left) + locs = Node.([(1,2), (2,1), (2,2), (2,3), (2,4), (3,3), (4,2), (4,3), (5,1), (6,1), (6,2)]) + return locs, triangular_unitdisk_graph(locs, 1.1, true), [1,2,11] +end +Base.size(::TriTCon_left) = (6,5) +cross_location(::TriTCon_left) = (2,2) +iscon(::TriTCon_left) = true + +function weighted(p::TriTCon_left) + sw = [2,1,2,2,2,2,2] + mw = [3,2,3,3,1,3,2,2,2,2,2] + return weighted(p, sw, mw) +end + +struct TriTCon_down <: TriangularCrossPattern end +# · · · +# · · · +# ◆ ● ● +# ⋅ ◆ · +function source_graph(::TriTCon_down) + locs = Node.([(3,1), (3,2), (3,3), (4,2)]) + g = simplegraph([(1,2), (2,3), (1,4)]) + return locs, g, [1,4,3] +end +connected_nodes(::TriTCon_down) = [1, 4] + +# · · · +# · ● · +# · ● · +# ● ● ● +function mapped_graph(::TriTCon_down) + locs = Node.([(2,2), (3,2), (4,1), (4,2), (4,3)]) + return locs, triangular_unitdisk_graph(locs, 1.1, true), [3,4,5] +end +Base.size(::TriTCon_down) = (4,3) +cross_location(::TriTCon_down) = (3,2) +iscon(::TriTCon_down) = true + +function weighted(p::TriTCon_down) + sw = [2,2,2,1] + mw = [1,3,2,3,2] + return weighted(p, sw, mw) +end + +struct TriTCon_up <: TriangularCrossPattern end +# ⋅ ◆ · +# ◆ ● ● +# · · · +# · · · +function source_graph(::TriTCon_up) + locs = Node.([(1,2), (2,1), (2,2), (2,3)]) + g = simplegraph([(1,2), (2,3), (1,4)]) + return locs, g, [2,1,4] +end +connected_nodes(::TriTCon_up) = [1, 2] + +# · ● · +# ● ● ● +# · ● · +# · · · +function mapped_graph(::TriTCon_up) + locs = Node.([(1,2), (2,1), (2,2), (2,3), (3,2)]) + return locs, triangular_unitdisk_graph(locs, 1.1, true), [2,1,4] +end +Base.size(::TriTCon_up) = (4,3) +cross_location(::TriTCon_up) = (2,2) +iscon(::TriTCon_up) = true + +function weighted(p::TriTCon_up) + sw = [1,2,2,2] + mw = [3,2,3,2,1] + return weighted(p, sw, mw) +end + +struct TriTrivialTurn_left <: TriangularCrossPattern end +# ⋅ ◆ +# ◆ ⋅ +function source_graph(::TriTrivialTurn_left) + locs = Node.([(1,2), (2,1)]) + g = simplegraph([(1,2)]) + return locs, g, [1,2] +end +# ⋅ ● +# ● ⋅ +function mapped_graph(::TriTrivialTurn_left) + locs = Node.([(1,2),(2,1)]) + return locs, triangular_unitdisk_graph(locs, 1.1, true), [1,2] +end +Base.size(::TriTrivialTurn_left) = (2,2) +cross_location(::TriTrivialTurn_left) = (2,2) +iscon(::TriTrivialTurn_left) = true +connected_nodes(::TriTrivialTurn_left) = [1, 2] + +function weighted(p::TriTrivialTurn_left) + sw = [1,1] + mw = [1,1] + return weighted(p, sw, mw) +end + +struct TriTrivialTurn_right <: TriangularCrossPattern end +# ◆ · +# · ◆ +function source_graph(::TriTrivialTurn_right) + locs = Node.([(1,1), (2,2)]) + g = simplegraph([(1,2)]) + return locs, g, [1,2] +end +# ⋅ · +# ● ● +function mapped_graph(::TriTrivialTurn_right) + locs = Node.([(2,1),(2,2)]) + return locs, triangular_unitdisk_graph(locs, 1.1, true), [1,2] +end +Base.size(::TriTrivialTurn_right) = (2,2) +cross_location(::TriTrivialTurn_right) = (1,2) +iscon(::TriTrivialTurn_right) = true +connected_nodes(::TriTrivialTurn_right) = [1, 2] + +function weighted(p::TriTrivialTurn_right) + sw = [1,1] + mw = [1,1] + return weighted(p, sw, mw) +end + +struct TriEndTurn <: TriangularCrossPattern end +# ⋅ ● ⋅ ⋅ +# ⋅ ● ● ⋅ +# ⋅ ⋅ ⋅ ⋅ +function source_graph(::TriEndTurn) + locs = Node.([(1,2), (2,2), (2,3)]) + g = simplegraph([(1,2), (2,3)]) + return locs, g, [1] +end +# ⋅ ● ⋅ ⋅ +# ⋅ ⋅ ⋅ ⋅ +# ⋅ ⋅ ⋅ ⋅ +function mapped_graph(::TriEndTurn) + locs = Node.([(1,2)]) + return locs, triangular_unitdisk_graph(locs, 1.1, true), [1] +end +Base.size(::TriEndTurn) = (3,4) +cross_location(::TriEndTurn) = (2,2) +iscon(::TriEndTurn) = false + +function weighted(p::TriEndTurn) + sw = [2,2,1] + mw = [1] + return weighted(p, sw, mw) +end + +struct TriTurn <: TriangularCrossPattern end +iscon(::TriTurn) = false +# ⋅ ● ⋅ ⋅ +# ⋅ ● ⋅ ⋅ +# ⋅ ● ● ● +# ⋅ ⋅ ⋅ ⋅ +function source_graph(::TriTurn) + locs = Node.([(1,2), (2,2), (3,2), (3,3), (3,4)]) + g = simplegraph([(1,2), (2,3), (3,4), (4,5)]) + return locs, g, [1,5] +end + +# ⋅ ● ⋅ ⋅ +# ⋅ ● · ⋅ +# ● ⋅ ⋅ ● +# ● ● ● ⋅ +function mapped_graph(::TriTurn) + locs = Node.([(1,2), (2,2), (3,1), (4,1), (4,2), (4,3), (3,4)]) + locs, triangular_unitdisk_graph(locs, 1.1, true), [1,7] +end +Base.size(::TriTurn) = (4, 4) +cross_location(::TriTurn) = (3,2) + +function weighted(p::TriTurn) + sw = [2,2,2,2,2] + mw = [2,2,2,2,2,2,2] + return weighted(p, sw, mw) +end + +struct TriWTurn <: TriangularCrossPattern end +# ⋅ ⋅ ⋅ ⋅ +# ⋅ ⋅ ● ● +# ⋅ ● ● ⋅ +# ⋅ ● ⋅ ⋅ +function source_graph(::TriWTurn) + locs = Node.([(2,3), (2,4), (3,2),(3,3),(4,2)]) + g = simplegraph([(1,2), (1,4), (3,4),(3,5)]) + return locs, g, [2, 5] +end +# ⋅ ⋅ ⋅ ● +# ⋅ ⋅ ● ⋅ +# ⋅ ● ● ⋅ +# ⋅ ● ⋅ ⋅ +function mapped_graph(::TriWTurn) + locs = Node.([(1,4), (2,3), (3,2), (3,3), (4,2)]) + return locs, triangular_unitdisk_graph(locs, 1.1, true), [1, 5] +end +Base.size(::TriWTurn) = (4, 4) +cross_location(::TriWTurn) = (2,2) +iscon(::TriWTurn) = false + +function weighted(p::TriWTurn) + sw = [2,2,2,2,2] + mw = [2,2,2,2,2] + return weighted(p, sw, mw) +end + +struct TriBranchFix <: TriangularCrossPattern end +# ⋅ ● ⋅ ⋅ +# ⋅ ● ● ⋅ +# ⋅ ● ● ⋅ +# ⋅ ● ⋅ ⋅ +function source_graph(::TriBranchFix) + locs = Node.([(1,2), (2,2), (2,3),(3,3),(3,2),(4,2)]) + g = simplegraph([(1,2), (2,3), (3,4),(4,5), (5,6)]) + return locs, g, [1, 6] +end +# ⋅ ● ⋅ ⋅ +# ⋅ ● ⋅ ⋅ +# ⋅ ● ⋅ ⋅ +# ⋅ ● ⋅ ⋅ +function mapped_graph(::TriBranchFix) + locs = Node.([(1,2),(2,2),(3,2),(4,2)]) + return locs, triangular_unitdisk_graph(locs, 1.1, true), [1, 4] +end +Base.size(::TriBranchFix) = (4, 4) +cross_location(::TriBranchFix) = (2,2) +iscon(::TriBranchFix) = false + +function weighted(p::TriBranchFix) + sw = [2,2,2,2,2,2] + mw = [2,2,2,2] + return weighted(p, sw, mw) +end + +struct TriBranchFixB <: TriangularCrossPattern end +# ⋅ ⋅ ⋅ ⋅ +# ⋅ ⋅ ● ⋅ +# ⋅ ● ● ⋅ +# ⋅ ● ⋅ ⋅ +function source_graph(::TriBranchFixB) + locs = Node.([(2,3),(3,2),(3,3),(4,2)]) + g = simplegraph([(1,3), (2,3), (2,4)]) + return locs, g, [1, 4] +end +# ⋅ ⋅ ⋅ ⋅ +# ⋅ ⋅ ⋅ ⋅ +# ⋅ ● ⋅ ⋅ +# ⋅ ● ⋅ ⋅ +function mapped_graph(::TriBranchFixB) + locs = Node.([(3,2),(4,2)]) + return locs, triangular_unitdisk_graph(locs, 1.1, true), [1, 2] +end +Base.size(::TriBranchFixB) = (4, 4) +cross_location(::TriBranchFixB) = (2,2) +iscon(::TriBranchFixB) = false + +function weighted(p::TriBranchFixB) + sw = [2,2,2,2] + mw = [2,2] + return weighted(p, sw, mw) +end + +struct TriBranch <: TriangularCrossPattern end +# ⋅ ● ⋅ ⋅ +# ⋅ ● ● ● +# ⋅ ● ● ⋅ +# ⋅ ● ⋅ ⋅ +# ⋅ ● · ⋅ +# ⋅ ● · ⋅ +function source_graph(::TriBranch) + locs = Node.([(1,2),(2,2),(2,3),(2,4),(3,3),(3,2),(4,2),(5,2),(6,2)]) + g = simplegraph([(1,2), (2,3), (3, 4), (3,5), (5,6), (6,7), (7,8), (8,9)]) + return locs, g, [1, 4, 9] +end +# ⋅ ● ⋅ ⋅ +# ⋅ ● · ● +# ● · ● · +# ⋅ ● ● ⋅ +# ● · ⋅ ⋅ +# ● ● ⋅ ⋅ +function mapped_graph(::TriBranch) + locs = Node.([(1,2),(2,2),(2,4),(3,1),(3,3),(4,2),(4,3),(5,1),(6,1),(6,2)]) + return locs, triangular_unitdisk_graph(locs, 1.1, true), [1,3,10] +end +Base.size(::TriBranch) = (6, 4) +cross_location(::TriBranch) = (2,2) +iscon(::TriBranch) = false + +function weighted(p::TriBranch) + sw = [2,2,3,2,2,2,2,2,2] + mw = [2,3,2,1,3,2,2,2,2,2] + return weighted(p, sw, mw) +end + +const triangular_crossing_ruleset = ( + TriCross{false}(), + TriCross{true}(), + TriTCon_left(), + TriTCon_up(), + TriTCon_down(), + TriTrivialTurn_left(), + TriTrivialTurn_right(), + TriEndTurn(), + TriTurn(), + TriWTurn(), + TriBranchFix(), + TriBranchFixB(), + TriBranch(), + ) +const crossing_ruleset_triangular_weighted = weighted.(triangular_crossing_ruleset) +get_ruleset(::TriangularWeighted) = crossing_ruleset_triangular_weighted + +# Specialized mis_overhead for TriangularCrossPattern WeightedGadgets +# For triangular patterns, we don't use the *2 multiplier from the general WeightedGadget method +mis_overhead(w::WeightedGadget{<:TriangularCrossPattern}) = mis_overhead(w.gadget) + +# mis_overhead functions for TriangularCrossPattern types +# These values should be computed properly using compute_mis_overhead function from project/createmap.jl +# For now, using reasonable placeholder values based on the pattern complexity +mis_overhead(::TriCross{true}) = 2 +mis_overhead(::TriCross{false}) = 3 +mis_overhead(::TriTCon_left) = 4 +mis_overhead(::TriTCon_down) = 1 +mis_overhead(::TriTCon_up) = 1 +mis_overhead(::TriTrivialTurn_left) = 0 +mis_overhead(::TriTrivialTurn_right) = 0 +mis_overhead(::TriEndTurn) = -2 +mis_overhead(::TriTurn) = 2 +mis_overhead(::TriWTurn) = 0 +mis_overhead(::TriBranchFix) = -2 +mis_overhead(::TriBranchFixB) = -2 +mis_overhead(::TriBranch) = 1 + +for (T, centerloc) in [(:Turn, (2, 3)), (:Branch, (2, 3)), (:BranchFix, (3, 2)), (:BranchFixB, (3, 2)), (:WTurn, (3, 3)), (:EndTurn, (1, 2))] + @eval source_centers(::WeightedGadget{<:$T}) = [cross_location($T()) .+ (0, 1)] + @eval mapped_centers(::WeightedGadget{<:$T}) = [$centerloc] +end \ No newline at end of file diff --git a/src/utils.jl b/src/utils.jl index 1fa4cda..05da222 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -41,6 +41,30 @@ function unitdisk_graph(locs::AbstractVector, unit::Real) return g end +function triangular_unitdisk_graph(locs::AbstractVector, unit::Real, parity::Bool=false) + # parity==false: crossing nodes on odd columns. + n = length(locs) + g = SimpleGraph(n) + physical_locs = physical_position.(locs, parity) + for i=1:n, j=i+1:n + if sum(abs2, physical_locs[i] .- physical_locs[j]) < unit ^ 2 + add_edge!(g, i, j) + end + end + return g +end + +function physical_position(node, parity=false) + i, j = node.loc + y = j * (√3 / 2) + if parity + x = i + (iseven(j) ? 0.5 : 0.0) + else + x = i + (isodd(j) ? 0.5 : 0.0) + end + return (x, y) +end + function is_independent_set(g::SimpleGraph, config) for e in edges(g) if config[e.src] == config[e.dst] == 1 diff --git a/src/visualize.jl b/src/visualize.jl index 4f46e1c..f2c95b1 100644 --- a/src/visualize.jl +++ b/src/visualize.jl @@ -1,4 +1,18 @@ # normalized to minimum weight and maximum weight + +# Helper function to convert grid coordinates to plot coordinates +function plot_coordinates(gg::GridGraph, i::Int, j::Int, unit::Float64) + if is_triangular_grid(gg) + # Triangular grid layout: y = j * (√3 / 2), x = i + (iseven(j) ? 0.5 : 0.0) + x = (i + (isodd(j) ? 0.5 : 0.0)) * unit + y = j * (√3 / 2) * unit # negative for downward y-axis + return (y, -x) + else + # Square grid layout (original) + return (j * unit, -i * unit) + end +end + function LuxorGraphPlot.show_graph(gg::GridGraph; format = :svg, filename = nothing, @@ -18,10 +32,14 @@ function LuxorGraphPlot.show_graph(gg::GridGraph; xmin, xmax = extrema(first.(coos)) ymin, ymax = extrema(last.(coos)) nodestore() do ns - filledlocs = map(coo->circle!((unit * (coo[2] - 1), -unit * (coo[1] - 1)), config.vertex_size), coos) + # Use the new coordinate transformation for filled locations + filledlocs = map(coo->circle!(plot_coordinates(gg, coo[1], coo[2], unit), config.vertex_size), coos) emptylocs, edges = [], [] for i=xmin:xmax, j=ymin:ymax - (i, j) ∉ coos && push!(emptylocs, circle!(((j-1) * unit, -(i-1) * unit), config.vertex_size/10)) + if (i, j) ∉ coos + plot_pos = plot_coordinates(gg, i, j, unit) + push!(emptylocs, circle!(plot_pos, config.vertex_size/10)) + end end for e in Graphs.edges(graph_and_weights(gg)[1]) i, j = e.src, e.dst diff --git a/src/weighted.jl b/src/weighted.jl index 37df58a..eef523c 100644 --- a/src/weighted.jl +++ b/src/weighted.jl @@ -95,9 +95,9 @@ function move_center(w::WeightedGadgetTypes, nodexy, offset) error("center not found, source center = $(source_centers(w)), while offset = $(offset)") end -trace_centers(r::MappingResult) = trace_centers(r.lines, r.padding, r.mapping_history) -function trace_centers(lines, padding, tape) - center_locations = map(x->center_location(x; padding) .+ (0, 1), lines) +trace_centers(r::MappingResult) = trace_centers(r.lines, r.padding, r.mapping_history, r.spacing) +function trace_centers(lines, padding, tape, spacing=4) + center_locations = map(x->center_location(x; padding, s=spacing) .+ (0, 1), lines) for (gadget, i, j) in tape m, n = size(gadget) for (k, centerloc) in enumerate(center_locations) @@ -122,12 +122,17 @@ function _map_configs_back(r::MappingResult{<:WeightedNode}, configs::AbstractVe end # simple rules for crossing gadgets -for (GT, s1, m1, s3, m3) in [(:(Cross{true}), [], [], [], []), (:(Cross{false}), [], [], [], []), - (:(WTurn), [], [], [], []), (:(BranchFix), [], [], [], []), (:(Turn), [], [], [], []), - (:(TrivialTurn), [1, 2], [1, 2], [], []), (:(BranchFixB), [1], [1], [], []), - (:(EndTurn), [3], [1], [], []), (:(TCon), [2], [2], [], []), - (:(Branch), [], [], [4], [2]), - ] +for (GT, s1, m1, s3, m3) in [ + (:(Cross{true}), [], [], [], []), + (:(Cross{false}), [], [], [], []), + (:(WTurn), [], [], [], []), + (:(BranchFix), [], [], [], []), + (:(Turn), [], [], [], []), + (:(TrivialTurn), [1, 2], [1, 2], [], []), + (:(BranchFixB), [1], [1], [], []), + (:(EndTurn), [3], [1], [], []), + (:(TCon), [2], [2], [], []), + (:(Branch), [], [], [4], [2]),] @eval function weighted(g::$GT) slocs, sg, spins = source_graph(g) mlocs, mg, mpins = mapped_graph(g) From 4a3bec49f218e3504ed5359d980b3a53c44ec42b Mon Sep 17 00:00:00 2001 From: Xiwei Pan Date: Mon, 14 Jul 2025 14:48:10 +0800 Subject: [PATCH 2/3] Add triangular support to Test module. --- project/createmap.jl | 30 +------------ src/mapping.jl | 2 +- src/triangular.jl | 53 +++++++++++----------- test/runtests.jl | 4 ++ test/triangular.jl | 104 +++++++++++++++++++++++++++++++++++++++++++ test/weighted.jl | 2 +- 6 files changed, 138 insertions(+), 57 deletions(-) create mode 100644 test/triangular.jl diff --git a/project/createmap.jl b/project/createmap.jl index 081a5b7..6920df5 100644 --- a/project/createmap.jl +++ b/project/createmap.jl @@ -1,5 +1,3 @@ -# TODO: Some interface has changed. We need to update the code. - using UnitDiskMapping, GenericTensorNetworks, Graphs function mapped_entry_to_compact(s::Pattern) @@ -37,35 +35,11 @@ function source_entry_to_configs(s::Pattern) return d end -# function compute_mis_overhead(s) -# locs1, g1, pins1 = source_graph(s) -# locs2, g2, pins2 = mapped_graph(s) -# m1 = mis_compactify!(solve(IndependentSet(g1, openvertices=pins1), SizeMax())) -# m2 = mis_compactify!(solve(IndependentSet(g2, openvertices=pins2), SizeMax())) -# @assert nv(g1) == length(locs1) && nv(g2) == length(locs2) -# sig, diff = UnitDiskMapping.is_diff_by_const(GenericTensorNetworks.content.(m1), GenericTensorNetworks.content.(m2)) -# @assert sig -# return diff -# end - function compute_mis_overhead(s) locs1, g1, pins1 = source_graph(s) locs2, g2, pins2 = mapped_graph(s) - m1 = mis_compactify!(solve(IndependentSet(g1, ones(Int, length(locs1))), SizeMax())) - m2 = mis_compactify!(solve(IndependentSet(g2, ones(Int, length(locs2))), SizeMax())) - @assert nv(g1) == length(locs1) && nv(g2) == length(locs2) - sig, diff = UnitDiskMapping.is_diff_by_const(GenericTensorNetworks.content.(m1), GenericTensorNetworks.content.(m2)) - @assert sig - return diff -end - -function compute_mis_overhead_weighted(s) - weighted_s = UnitDiskMapping.weighted(s) - locs1, g1, pins1 = source_graph(s) - locs2, g2, pins2 = mapped_graph(s) - m1 = mis_compactify!(solve(IndependentSet(g1, weighted_s.source_weights), SizeMax())) - m2 = mis_compactify!(solve(IndependentSet(g2, weighted_s.mapped_weights), SizeMax())) - @show m1, m2 + m1 = mis_compactify!(solve(IndependentSet(g1, openvertices=pins1), SizeMax())) + m2 = mis_compactify!(solve(IndependentSet(g2, openvertices=pins2), SizeMax())) @assert nv(g1) == length(locs1) && nv(g2) == length(locs2) sig, diff = UnitDiskMapping.is_diff_by_const(GenericTensorNetworks.content.(m1), GenericTensorNetworks.content.(m2)) @assert sig diff --git a/src/mapping.jl b/src/mapping.jl index f747e0b..0f44dfb 100644 --- a/src/mapping.jl +++ b/src/mapping.jl @@ -7,7 +7,7 @@ struct TriangularWeighted end # Get spacing value based on mode get_spacing(::Union{UnWeighted, Weighted}) = 4 -get_spacing(::TriangularWeighted) = 6 +get_spacing(::TriangularWeighted) = 14 Base.@kwdef struct MCell{WT} <: AbstractCell{WT} occupied::Bool = true diff --git a/src/triangular.jl b/src/triangular.jl index 5d44265..22612c5 100644 --- a/src/triangular.jl +++ b/src/triangular.jl @@ -2,35 +2,34 @@ abstract type TriangularCrossPattern <: Pattern end struct TriCross{CON} <: TriangularCrossPattern end iscon(::TriCross{CON}) where {CON} = CON -# · ⋅ ● ⋅ · -# ● ◆ ◉ ● ● -# · ⋅ ◆ · · -# ⋅ ⋅ ● ⋅ ⋅ -# ⋅ ⋅ ● ⋅ ⋅ -# ⋅ ⋅ ● ⋅ ⋅ +# · · · · · ⋅ ◆ ⋅ · · · · · +# ● ● ● ● ● ◆ ◉ ● ● ● ● ● ● +# · · · · · ⋅ ● · · · · · · +# · · · · ⋅ ⋅ ● ⋅ ⋅ · · · · +# · · · · ⋅ ⋅ ● ⋅ ⋅ · · · · +# · · · · ⋅ ⋅ ● ⋅ ⋅ · · · · function source_graph(::TriCross{true}) - locs = Node.([(2,1), (2,2), (2,3), (2,4), (2,5), (1,3), (2,3), (3,3), (4,3), (5,3), (6,3)]) - g = simplegraph([(1,2), (2,3), (3,4), (4,5), (6,7), (7,8), (8,9), (9,10), (10,11), (2,6)]) - return locs, g, [1,6,11,5] -end - -# ⋅ · ● ⋅ ⋅ -# ● ● ● ● ● -# ⋅ ● ⋅ ● ⋅ -# ⋅ ● ● · ⋅ -# ⋅ · · ● ⋅ -# ⋅ ⋅ ● ● ⋅ + locs = Node.([(2,1), (2,2), (2,3), (2,4), (2,5), (2,6), (2,7), (2,8), (2,9), (2,10), (2,11), (2,12), (2,13), (1,7), (2,7), (3,7), (4,7), (5,7), (6,7)]) + g = simplegraph([(1,2), (2,3), (3,4), (4,5), (5,6), (6,7), (7,8), (8,9), (9,10), (10,11), (11,12), (12,13), (14,15), (15,16), (16,17), (17,18), (18,19), (14,6)]) + return locs, g, [1,14,13,19] +end +# ● · · · ⋅ · ● ⋅ ⋅ · · · ● +# · ● · · ● ● ● ● ● · · ● · +# · ● ● ● ⋅ ● ⋅ ● ⋅ ● ● ● · +# · · · · ⋅ ● ● · ⋅ · · · · +# · · · · ⋅ · · ● ⋅ · · · · +# · · · · ⋅ ⋅ ● ● ⋅ · · · · function mapped_graph(::TriCross{true}) - locs = Node.([(1,3), (2,1), (2,2), (2,3), (2,4), (2,5), (3,2), (3,4), (4,2), (4,3), (5,4), (6,3), (6,4)]) - return locs, triangular_unitdisk_graph(locs, 1.1, false), [2,1,12,6] + locs = Node.([(1,1), (1,7), (1,13), (2,2), (2,5), (2,6), (2,7), (2,8), (2,9), (2,12), (3,2), (3,3), (3,4), (3,6), (3,8), (3,10), (3,11), (3,12), (4,6), (4,7), (5,8), (6,8), (6,7)]) + return locs, triangular_unitdisk_graph(locs, 1.1, false), [1,2,3,23] end -Base.size(::TriCross{true}) = (6, 5) -cross_location(::TriCross{true}) = (2, 3) -connected_nodes(::TriCross{true}) = [2, 6] +Base.size(::TriCross{true}) = (6, 13) +cross_location(::TriCross{true}) = (2, 7) +connected_nodes(::TriCross{true}) = [14,6] function weighted(p::TriCross{true}) - sw = [2,2,2,2,2,2,2,2,2,2,2] - mw = [3,2,3,4,3,2,3,2,2,2,2,2,2] + sw = [2,2,2,2,2,2,2,2,2,2 ,2,2,2,2,2,2,2,2,2] + mw = [2,3,2,2,2,3,4,3,2,2 ,2,2,2,3,2,2,2,2 ,2,2,2,2,2] return weighted(p, sw, mw) end @@ -136,7 +135,7 @@ struct TriTCon_up <: TriangularCrossPattern end # · · · function source_graph(::TriTCon_up) locs = Node.([(1,2), (2,1), (2,2), (2,3)]) - g = simplegraph([(1,2), (2,3), (1,4)]) + g = simplegraph([(1,2), (2,3), (3,4)]) return locs, g, [2,1,4] end connected_nodes(::TriTCon_up) = [1, 2] @@ -405,7 +404,7 @@ mis_overhead(w::WeightedGadget{<:TriangularCrossPattern}) = mis_overhead(w.gadge # mis_overhead functions for TriangularCrossPattern types # These values should be computed properly using compute_mis_overhead function from project/createmap.jl # For now, using reasonable placeholder values based on the pattern complexity -mis_overhead(::TriCross{true}) = 2 +mis_overhead(::TriCross{true}) = 4 mis_overhead(::TriCross{false}) = 3 mis_overhead(::TriTCon_left) = 4 mis_overhead(::TriTCon_down) = 1 @@ -419,7 +418,7 @@ mis_overhead(::TriBranchFix) = -2 mis_overhead(::TriBranchFixB) = -2 mis_overhead(::TriBranch) = 1 -for (T, centerloc) in [(:Turn, (2, 3)), (:Branch, (2, 3)), (:BranchFix, (3, 2)), (:BranchFixB, (3, 2)), (:WTurn, (3, 3)), (:EndTurn, (1, 2))] +for (T, centerloc) in [(:TriTurn, (4, 1)), (:TriBranch, (1,2)), (:TriBranchFix, (3, 2)), (:TriBranchFixB, (3, 2)), (:TriWTurn, (2, 3)), (:TriEndTurn, (1, 2))] @eval source_centers(::WeightedGadget{<:$T}) = [cross_location($T()) .+ (0, 1)] @eval mapped_centers(::WeightedGadget{<:$T}) = [$centerloc] end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 2350059..6c9c7ed 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -55,4 +55,8 @@ end @testset "reduceto" begin include("reduceto.jl") +end + +@testset "triangular" begin + include("triangular.jl") end \ No newline at end of file diff --git a/test/triangular.jl b/test/triangular.jl new file mode 100644 index 0000000..b5b71ca --- /dev/null +++ b/test/triangular.jl @@ -0,0 +1,104 @@ +using Test, UnitDiskMapping, Graphs, GenericTensorNetworks +using GenericTensorNetworks: TropicalF64, content +using Random +using UnitDiskMapping: is_independent_set + +@testset "triangular gadgets" begin + for s in UnitDiskMapping.crossing_ruleset_triangular_weighted + println("Testing triangular gadget:\n$s") + locs1, g1, pins1 = source_graph(s) + locs2, g2, pins2 = mapped_graph(s) + @assert length(locs1) == nv(g1) + w1 = getfield.(locs1, :weight) + w2 = getfield.(locs2, :weight) + w1[pins1] .-= 1 + w2[pins2] .-= 1 + gp1 = GenericTensorNetwork(IndependentSet(g1, w1), openvertices=pins1) + gp2 = GenericTensorNetwork(IndependentSet(g2, w2), openvertices=pins2) + m1 = solve(gp1, SizeMax()) + m2 = solve(gp2, SizeMax()) + mm1 = maximum(m1) + mm2 = maximum(m2) + @test nv(g1) == length(locs1) && nv(g2) == length(locs2) + if !(all((mm1 .== m1) .== (mm2 .== m2))) + @show m1 + @show m2 + end + @test all((mm1 .== m1) .== (mm2 .== m2)) + @test content(mm1 / mm2) == -mis_overhead(s) + end +end + +@testset "triangular copy lines" begin + for (vstart, vstop, hstop) in [ + (3, 7, 8), (3, 5, 8), (5, 9, 8), (5, 5, 8), + (1, 7, 5), (5, 8, 5), (1, 5, 5), (5, 5, 5)] + tc = UnitDiskMapping.CopyLine(1, 5, 5, vstart, vstop, hstop) + locs = UnitDiskMapping.copyline_locations(UnitDiskMapping.WeightedNode, tc; padding=2) + g = SimpleGraph(length(locs)) + weights = getfield.(locs, :weight) + for i=1:length(locs)-1 + if i==1 || locs[i-1].weight == 1 # starting point + add_edge!(g, length(locs), i) + else + add_edge!(g, i, i-1) + end + end + gp = GenericTensorNetwork(IndependentSet(g, weights)) + @test solve(gp, SizeMax())[].n == UnitDiskMapping.mis_overhead_copyline(TriangularWeighted(), tc) + end +end + +@testset "triangular map configurations back" begin + Random.seed!(2) + for graphname in [:bull, :petersen, :cubical, :house, :diamond, :tutte] + @show graphname + g = smallgraph(graphname) + weights = fill(0.25, nv(g)) + r = map_graph(TriangularWeighted(), g) + mapped_weights = UnitDiskMapping.map_weights(r, weights) + mgraph, _ = graph_and_weights(r.grid_graph) + + gp = GenericTensorNetwork(IndependentSet(mgraph, mapped_weights); optimizer=GreedyMethod(nrepeat=10)) + missize_map = solve(gp, CountingMax())[] + missize = solve(GenericTensorNetwork(IndependentSet(g, weights)), CountingMax())[] + @test r.mis_overhead + missize.n == missize_map.n + @test missize.c == missize_map.c + + T = GenericTensorNetworks.sampler_type(nv(mgraph), 2) + misconfig = solve(gp, SingleConfigMax())[].c + c = zeros(Int, size(r.grid_graph)) + for (i, n) in enumerate(r.grid_graph.nodes) + c[n.loc...] = misconfig.data[i] + end + + center_locations = trace_centers(r) + indices = CartesianIndex.(center_locations) + sc = c[indices] + @test count(isone, sc) == missize.n * 4 + @test is_independent_set(g, sc) + end +end + +@testset "triangular interface" begin + Random.seed!(2) + g = smallgraph(:petersen) + res = map_graph(TriangularWeighted(), g) + + # checking size + mgraph, _ = graph_and_weights(res.grid_graph) + ws = rand(nv(g)) + weights = UnitDiskMapping.map_weights(res, ws) + + gp = GenericTensorNetwork(IndependentSet(mgraph, weights); optimizer=TreeSA(ntrials=1, niters=10)) + missize_map = solve(gp, SizeMax())[].n + missize = solve(GenericTensorNetwork(IndependentSet(g, ws)), SizeMax())[].n + @test res.mis_overhead + missize ≈ missize_map + + # checking mapping back + T = GenericTensorNetworks.sampler_type(nv(mgraph), 2) + misconfig = solve(gp, SingleConfigMax())[].c + original_configs = map_config_back(res, collect(misconfig.data)) + @test count(isone, original_configs) == solve(GenericTensorNetwork(IndependentSet(g)), SizeMax())[].n + @test is_independent_set(g, original_configs) +end diff --git a/test/weighted.jl b/test/weighted.jl index b8331a9..a59b7ed 100644 --- a/test/weighted.jl +++ b/test/weighted.jl @@ -64,7 +64,7 @@ end # trace back configurations mgraph = SimpleGraph(ug3) weights = fill(0.5, nv(g)) - r = UnitDiskMapping.MappingResult(GridGraph(ug3), ug3.lines, ug3.padding, [tape..., tape2...], mis_overhead0+mis_overhead1+mis_overhead2) + r = UnitDiskMapping.MappingResult(GridGraph(Weighted(), ug3), ug3.lines, ug3.padding, [tape..., tape2...], mis_overhead0+mis_overhead1+mis_overhead2, ug.spacing) mapped_weights = UnitDiskMapping.map_weights(r, weights) gp = GenericTensorNetwork(IndependentSet(mgraph, mapped_weights); optimizer=GreedyMethod(nrepeat=10)) missize_map = solve(gp, CountingMax())[] From 9ee4485ed25950ed1865daa87964e5f197b0f015 Mon Sep 17 00:00:00 2001 From: Xiwei Pan Date: Tue, 9 Sep 2025 13:47:18 +0800 Subject: [PATCH 3/3] Update documentation and tests accordingly. Update triangular configurations. --- docs/make.jl | 3 +- examples/weighted_lattice_comparison.jl | 102 ++++++++++++++ src/Core.jl | 105 +++++++++------ src/mapping.jl | 96 ++++++++----- src/triangular.jl | 172 ++++++++++++------------ src/utils.jl | 43 +++++- src/visualize.jl | 19 ++- src/weighted.jl | 2 +- test/mapping.jl | 2 +- test/triangular.jl | 16 +-- test/utils.jl | 11 +- test/weighted.jl | 13 +- 12 files changed, 386 insertions(+), 198 deletions(-) create mode 100644 examples/weighted_lattice_comparison.jl diff --git a/docs/make.jl b/docs/make.jl index fa8c0f1..b0ef034 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -29,7 +29,8 @@ makedocs(; "Home" => "index.md", "Examples" => [ "generated/tutorial.md", - "generated/unweighted.md" + "generated/unweighted.md", + "generated/weighted_lattice_comparison.md" ], "Reference" => "ref.md", ], diff --git a/examples/weighted_lattice_comparison.jl b/examples/weighted_lattice_comparison.jl new file mode 100644 index 0000000..7539bef --- /dev/null +++ b/examples/weighted_lattice_comparison.jl @@ -0,0 +1,102 @@ +# # Weighted Mapping on Different Lattices + +# This page demonstrates weighted version of the Maximum Independent Set (MIS) or Maximum Weighted Independent Set (MWIS) mapping techniques from the paper "Embedding computationally hard problems in triangular Rydberg atom arrays". We compare two mapping approaches using the K₂,₃ bipartite graph as an example: +# +# 1. **King's Subgraph (KSG) mapping** on square lattices - the traditional approach +# 2. **Triangular lattice mapping** - a more experimental-promising alternative +# +# Both methods preserve the optimal solution while enabling implementation on quantum hardware with different connectivity constraints. + +using UnitDiskMapping, Graphs, GenericTensorNetworks + +# ## Problem Setup: K₂,₃ Graph + +# We use the complete bipartite graph K₂,₃ as our test case. This graph has two groups of vertices: {1,2} and {3,4,5}, where every vertex in the first group connects to every vertex in the second group. + +k23_graph = SimpleGraph(5) +add_edge!(k23_graph, 1, 3) +add_edge!(k23_graph, 1, 4) +add_edge!(k23_graph, 1, 5) +add_edge!(k23_graph, 2, 3) +add_edge!(k23_graph, 2, 4) +add_edge!(k23_graph, 2, 5) + +show_graph(k23_graph) + +# For the MIS problem, we assign equal weights to all vertices. +source_weights = [0.5, 0.5, 0.5, 0.5, 0.5] + +# ## Square Lattice Mapping (KSG) + +# The KSG-based approach creates a regular square grid where vertices can only connect to their nearest and next nearest neighbors (i.e. diagonals). + +square_result = map_graph(Weighted(), k23_graph; vertex_order=MinhThiTrick()) + +println("Square lattice grid size: ", square_result.grid_graph.size) +println("Number of vertices: ", nv(square_result.grid_graph)) +println("MIS overhead: ", square_result.mis_overhead) + +# Visualize the mapping +show_graph(square_result.grid_graph; show_number=false) +show_grayscale(square_result.grid_graph) # show weights in gray scale +show_pins(square_result) # show pins in red + +# Solve the mapped problem +square_mapped_weights = map_weights(square_result, source_weights) +square_grid_graph, _ = graph_and_weights(square_result.grid_graph) +square_solution = solve(GenericTensorNetwork(IndependentSet(square_grid_graph, square_mapped_weights)), + SingleConfigMax())[].c.data + +# Map back to source +square_source_solution = map_config_back(square_result, collect(Int, square_solution)) +println("Square solution: ", square_source_solution) + +# ## Triangular Lattice Mapping + +# While King's subgraphs provide a systematic approach for encoding problems on square lattices, they are not optimal for two-dimensional quantum hardware. The power-law decay of Rydberg interaction strengths in real devices leads to poor approximation of unit-disk graphs, requiring extensive post-processing that lacks explainability. +# +# The triangular lattice encoding scheme addresses these limitations by utilizing triangular Rydberg atom arrays which only blocks atoms in the nearest neighbors. This approach reduces independence-constraint violations by approximately two orders of magnitude compared to King's subgraphs, substantially alleviating the need for post-processing in experiments. +# +# The automatic embedding scheme generates graphs on triangular lattices with slightly larger overhead compared to KSG, but remains quadratic. For further improvement like removing dangling vertices, we need to manually optimize the embedding currently. +# + + +triangular_result = map_graph(TriangularWeighted(), k23_graph; vertex_order=MinhThiTrick()) + +println("Triangular lattice grid size: ", triangular_result.grid_graph.size) +println("Number of vertices: ", nv(triangular_result.grid_graph)) +println("MIS overhead: ", triangular_result.mis_overhead) + +# Visualize the mapping +show_graph(triangular_result.grid_graph; show_number=false) +show_grayscale(triangular_result.grid_graph) # show weights in gray scale +show_pins(triangular_result) # show pins in red + +# Solve the mapped problem +triangular_mapped_weights = map_weights(triangular_result, source_weights) +triangular_grid_graph, _ = graph_and_weights(triangular_result.grid_graph) +triangular_solution = solve(GenericTensorNetwork(IndependentSet(triangular_grid_graph, triangular_mapped_weights)), + SingleConfigMax())[].c.data +show_config(triangular_result.grid_graph, triangular_solution) + +# Map back to source +triangular_source_solution = map_config_back(triangular_result, collect(Int, triangular_solution)) +println("Triangular solution: ", triangular_source_solution) + +# ## Verification and Comparison + +# To verify correctness, we solve the original problem directly and compare with both mapping approaches. All three methods should yield the same optimal solution value. + +# Solve original problem directly +direct_solution = solve(GenericTensorNetwork(IndependentSet(k23_graph, source_weights)), + SingleConfigMax())[].c.data + +println("Direct solution: ", collect(Int, direct_solution)) + +# Check all give same optimal value +direct_value = sum(source_weights[i] for i in 1:5 if direct_solution[i] == 1) +square_value = sum(source_weights[i] for i in 1:5 if square_source_solution[i] == 1) +triangular_value = sum(source_weights[i] for i in 1:5 if triangular_source_solution[i] == 1) + +println("Optimal values - Direct: $direct_value, Square: $square_value, Triangular: $triangular_value") +println("All correct: ", direct_value ≈ square_value ≈ triangular_value) diff --git a/src/Core.jl b/src/Core.jl index 330b5f4..e33c94d 100644 --- a/src/Core.jl +++ b/src/Core.jl @@ -57,11 +57,60 @@ const WeightedNode{T<:Real} = Node{T} const UnWeightedNode = Node{ONE} ############################ Grid Types ############################ -# Abstract grid geometry types +""" +Abstract base type for grid geometries. + +Grid types define how coordinates are mapped to physical positions for distance calculations. +""" abstract type AbstractGridType end +""" + SquareGrid <: AbstractGridType + +A square lattice grid where coordinates directly correspond to physical positions. +For a node at grid position (i, j), the physical position is (i, j). +""" struct SquareGrid <: AbstractGridType end -struct TriangularGrid <: AbstractGridType end + +""" + TriangularGrid <: AbstractGridType + +A triangular lattice grid where nodes form an equilateral triangular pattern. + +# Fields +- `offset_even_cols::Bool`: Whether even-numbered columns are offset vertically. + - `false` (default): Odd columns are offset by 0.5 units vertically + - `true`: Even columns are offset by 0.5 units vertically + +# Physical positioning +For a node at grid position (i, j): +- y-coordinate: `j * (√3 / 2)` (maintains equilateral triangle geometry) +- x-coordinate: `i + offset` where offset depends on `offset_even_cols`: + - If `offset_even_cols = false`: offset = `0.5` if j is odd, `0.0` if j is even + - If `offset_even_cols = true`: offset = `0.5` if j is even, `0.0` if j is odd + +# Examples +```julia +# Default triangular grid (odd columns offset) +grid1 = TriangularGrid() # equivalent to TriangularGrid(false) + +# Even columns offset +grid2 = TriangularGrid(true) +``` +""" +struct TriangularGrid <: AbstractGridType + offset_even_cols::Bool + + """ + TriangularGrid(offset_even_cols::Bool=false) + + Create a triangular grid with specified column offset pattern. + + # Arguments + - `offset_even_cols`: If true, even columns are offset by 0.5; if false (default), odd columns are offset. + """ + TriangularGrid(offset_even_cols::Bool=false) = new(offset_even_cols) +end ############################ GridGraph ############################ # Main definition @@ -72,9 +121,6 @@ struct GridGraph{NT<:Node, GT<:AbstractGridType} radius::Float64 end -is_square_grid(g::GridGraph) = g.gridtype isa SquareGrid -is_triangular_grid(g::GridGraph) = g.gridtype isa TriangularGrid - # Base constructors (for any node/grid type) GridGraph(size::Tuple{Int,Int}, nodes::Vector{NT}, radius::Real) where {NT<:Node} = GridGraph(SquareGrid(), size, nodes, radius) @@ -89,49 +135,20 @@ function Base.show(io::IO, grid::GridGraph) end Base.size(gg::GridGraph) = gg.size Base.size(gg::GridGraph, i::Int) = gg.size[i] + function graph_and_weights(grid::GridGraph) - if is_triangular_grid(grid) - # For triangular grids, use physical positions - physical_locs = [physical_position(node) for node in grid.nodes] - return unit_disk_graph(physical_locs, grid.radius), getfield.(grid.nodes, :weight) - else - # For square grids, use original coordinates - return unit_disk_graph(getfield.(grid.nodes, :loc), grid.radius), getfield.(grid.nodes, :weight) - end + # Use physical positions for both grid types + physical_locs = [physical_position(node, grid.gridtype) for node in grid.nodes] + return unitdisk_graph(physical_locs, grid.radius), getfield.(grid.nodes, :weight) end + function Graphs.SimpleGraph(grid::GridGraph{Node{ONE}, GT}) where GT - if is_triangular_grid(grid) - # For triangular grids, use physical positions - physical_locs = [physical_position(node) for node in grid.nodes] - return unit_disk_graph(physical_locs, grid.radius) - else - # For square grids, use original coordinates - return unit_disk_graph(getfield.(grid.nodes, :loc), grid.radius) - end + physical_locs = [physical_position(node, grid.gridtype) for node in grid.nodes] + return unitdisk_graph(physical_locs, grid.radius) end coordinates(grid::GridGraph) = getfield.(grid.nodes, :loc) - -# Neighbor calculation with runtime grid type dispatch -function Graphs.neighbors(g::GridGraph, i::Int) - if is_triangular_grid(g) - # Use physical positions for triangular grid distance calculation - [j for j in 1:nv(g) if i != j && triangular_distance(g.nodes[i], g.nodes[j]) <= g.radius] - else - # Default square grid calculation - [j for j in 1:nv(g) if i != j && distance(g.nodes[i], g.nodes[j]) <= g.radius] - end -end - -distance(n1::Node, n2::Node) = sqrt(sum(abs2, n1.loc .- n2.loc)) - -# Distance calculation for triangular grids using physical positions -function triangular_distance(n1::Node, n2::Node) - # Convert to physical positions as in triangular.jl - p1 = physical_position(n1) - p2 = physical_position(n2) - return sqrt(sum(abs2, p1.loc .- p2.loc)) -end - +Graphs.neighbors(g::GridGraph, i::Int) = [j for j in 1:nv(g) if i != j && distance(g.nodes[i], g.nodes[j], g.gridtype) <= g.radius] +distance(n1::Node, n2::Node, grid_type::AbstractGridType) = sqrt(sum(abs2, physical_position(n1, grid_type) .- physical_position(n2, grid_type))) Graphs.nv(g::GridGraph) = length(g.nodes) Graphs.vertices(g::GridGraph) = 1:nv(g) @@ -168,4 +185,4 @@ function GridGraph(m::AbstractMatrix{SimpleCell{WT}}, radius::Real) where WT end end return GridGraph(size(m), nodes, radius) -end \ No newline at end of file +end diff --git a/src/mapping.jl b/src/mapping.jl index 0f44dfb..a6003a9 100644 --- a/src/mapping.jl +++ b/src/mapping.jl @@ -1,13 +1,35 @@ -# UnWeighted mode +# Constants for grid parameters +const DEFAULT_SQUARE_SPACING = 4 +const DEFAULT_TRIANGULAR_SPACING = 6 +const DEFAULT_PADDING = 2 +const SQUARE_UNIT_RADIUS = 1.5 +const TRIANGULAR_UNIT_RADIUS = 1.1 + +# Mode types struct UnWeighted end -# Weighted mode struct Weighted end -# TriangularWeighted mode struct TriangularWeighted end +# Spacing structure to handle different spacing for different directions +struct GridSpacing + row_spacing::Int # N direction + col_spacing::Int # M direction +end + +# Constructor for uniform spacing +GridSpacing(s::Int) = GridSpacing(s, s) + # Get spacing value based on mode -get_spacing(::Union{UnWeighted, Weighted}) = 4 -get_spacing(::TriangularWeighted) = 14 +get_spacing(::Union{UnWeighted, Weighted}) = GridSpacing(DEFAULT_SQUARE_SPACING) +get_spacing(::TriangularWeighted) = GridSpacing(DEFAULT_TRIANGULAR_SPACING) + +# Helper functions to get individual spacing values +row_spacing(s::GridSpacing) = s.row_spacing +col_spacing(s::GridSpacing) = s.col_spacing + +# Add Base methods for GridSpacing +Base.:(==)(s1::GridSpacing, s2::GridSpacing) = s1.row_spacing == s2.row_spacing && s1.col_spacing == s2.col_spacing +Base.hash(s::GridSpacing, h::UInt) = hash((s.row_spacing, s.col_spacing), h) Base.@kwdef struct MCell{WT} <: AbstractCell{WT} occupied::Bool = true @@ -69,7 +91,7 @@ struct MappingGrid{CT<:AbstractCell} lines::Vector{CopyLine} padding::Int content::Matrix{CT} - spacing::Int + spacing::GridSpacing end Base.:(==)(ug::MappingGrid{CT}, ug2::MappingGrid{CT}) where CT = ug.lines == ug2.lines && ug.content == ug2.content && ug.spacing == ug2.spacing @@ -96,16 +118,16 @@ function Graphs.SimpleGraph(ug::MappingGrid) if any(x->x.doubled || x.connected, ug.content) error("This mapping is not done yet!") end - return unitdisk_graph(coordinates(ug), 1.5) + return unitdisk_graph(coordinates(ug), SQUARE_UNIT_RADIUS) end function GridGraph(mode, ug::MappingGrid) if any(x->x.doubled || x.connected, ug.content) error("This mapping is not done yet!") end if mode isa TriangularWeighted - return GridGraph(TriangularGrid(), size(ug), [Node((i,j), ug.content[i,j].weight) for (i, j) in coordinates(ug)], 1.1) + return GridGraph(TriangularGrid(), size(ug), [Node((i,j), ug.content[i,j].weight) for (i, j) in coordinates(ug)], TRIANGULAR_UNIT_RADIUS) else - return GridGraph(size(ug), [Node((i,j), ug.content[i,j].weight) for (i, j) in coordinates(ug)], 1.5) + return GridGraph(size(ug), [Node((i,j), ug.content[i,j].weight) for (i, j) in coordinates(ug)], SQUARE_UNIT_RADIUS) end end @@ -197,7 +219,7 @@ end function map_config_copyback!(ug::MappingGrid, c::AbstractMatrix) res = zeros(Int, length(ug.lines)) for line in ug.lines - locs = copyline_locations(nodetype(ug), line; padding=ug.padding) + locs = copyline_locations(nodetype(ug), line, ug.padding, ug.spacing) count = 0 for (iloc, loc) in enumerate(locs) gi, ci = ug.content[loc...], c[loc...] @@ -241,19 +263,26 @@ function remove_order(g::AbstractGraph, vertex_order::AbstractVector{Int}) return addremove end -function center_location(tc::CopyLine; padding::Int, s::Int=4) - I = s*(tc.hslot-1)+padding+2 - J = s*(tc.vslot-1)+padding+1 +function center_location(tc::CopyLine, padding::Int, s::Union{Int, GridSpacing}) + spacing = s isa Int ? GridSpacing(s) : s + row_s = row_spacing(spacing) + col_s = col_spacing(spacing) + I = row_s*(tc.hslot-1)+padding+2 + J = col_s*(tc.vslot-1)+padding+1 return I, J end # NT is node type -function copyline_locations(::Type{NT}, tc::CopyLine; padding::Int, s::Int=4) where NT +# Low-level API: explicit parameters required, no defaults [[memory:3852344]] +function copyline_locations(::Type{NT}, tc::CopyLine, padding::Int, s::Union{Int, GridSpacing}) where NT nline = 0 - I, J = center_location(tc; padding=padding, s=s) + spacing = s isa Int ? GridSpacing(s) : s + row_s = row_spacing(spacing) + col_s = col_spacing(spacing) + I, J = center_location(tc, padding, s) locations = NT[] # grow up - start = I+s*(tc.vstart-tc.hslot)+1 + start = I+row_s*(tc.vstart-tc.hslot)+1 if tc.vstart < tc.hslot nline += 1 end @@ -261,7 +290,7 @@ function copyline_locations(::Type{NT}, tc::CopyLine; padding::Int, s::Int=4) wh push!(locations, node(NT, i, J, 1+(i!=start))) # half weight on last node end # grow down - stop = I+s*(tc.vstop-tc.hslot)-1 + stop = I+row_s*(tc.vstop-tc.hslot)-1 if tc.vstop > tc.hslot nline += 1 end @@ -273,7 +302,7 @@ function copyline_locations(::Type{NT}, tc::CopyLine; padding::Int, s::Int=4) wh end end # grow right - stop = J+s*(tc.hstop-tc.vslot)-1 + stop = J+col_s*(tc.hstop-tc.vslot)-1 if tc.hstop > tc.vslot nline += 1 end @@ -290,19 +319,19 @@ nodetype(::UnWeighted) = UnWeightedNode nodetype(::TriangularWeighted) = WeightedNode{Int} node(::Type{<:UnWeightedNode}, i, j, w) = Node(i, j) -function ugrid(mode, g::SimpleGraph, vertex_order::AbstractVector{Int}; padding=2, nrow=nv(g)) - @assert padding >= 2 +function ugrid(mode, g::SimpleGraph, vertex_order::AbstractVector{Int}; padding::Int=DEFAULT_PADDING, nrow::Int=nv(g)) + @assert padding >= DEFAULT_PADDING # create an empty canvas n = nv(g) s = get_spacing(mode) - N = (n-1)*s+2+2*padding - M = nrow*s+2+2*padding + N = (n-1)*s.col_spacing+2+2*padding + M = nrow*s.row_spacing+2+2*padding u = fill(empty(mode isa Union{Weighted, TriangularWeighted} ? MCell{Int} : MCell{ONE}), M, N) # add T-copies copylines = create_copylines(g, vertex_order) for tc in copylines - for loc in copyline_locations(nodetype(mode), tc; padding=padding, s=s) + for loc in copyline_locations(nodetype(mode), tc, padding, s) add_cell!(u, loc) end end @@ -324,7 +353,7 @@ function crossat(ug::MappingGrid, v, w) i, j = minmax(i, j) hslot = ug.lines[i].hslot s = ug.spacing - return (hslot-1)*s+2+ug.padding, (j-1)*s+1+ug.padding + return (hslot-1)*s.row_spacing+2+ug.padding, (j-1)*s.col_spacing+1+ug.padding end """ @@ -345,7 +374,7 @@ function embed_graph(mode, g::SimpleGraph; vertex_order=MinhThiTrick()) end # we reverse the vertex order of the pathwidth result, # because this order corresponds to the vertex-seperation. - ug = ugrid(mode, g, L.vertices[end:-1:1]; padding=2, nrow=L.vsep+1) + ug = ugrid(mode, g, L.vertices[end:-1:1]; padding=DEFAULT_PADDING, nrow=L.vsep+1) return ug end @@ -355,13 +384,16 @@ function mis_overhead_copylines(ug::MappingGrid{WC}) where {WC} end end -function mis_overhead_copyline(w::W, line::CopyLine, s::Int=4) where W +function mis_overhead_copyline(w::W, line::CopyLine, s::Union{Int, GridSpacing}) where W + spacing = s isa Int ? GridSpacing(s) : s if W === Weighted || W === TriangularWeighted - return (line.hslot - line.vstart) * s + - (line.vstop - line.hslot) * s + - max((line.hstop - line.vslot) * s - 2, 0) + row_s = row_spacing(spacing) + col_s = col_spacing(spacing) + return (line.hslot - line.vstart) * row_s + + (line.vstop - line.hslot) * row_s + + max((line.hstop - line.vslot) * col_s - 2, 0) else - locs = copyline_locations(nodetype(w), line; padding=2, s=s) + locs = copyline_locations(nodetype(w), line, DEFAULT_PADDING, s) @assert length(locs) % 2 == 1 return length(locs) ÷ 2 end @@ -374,7 +406,7 @@ struct MappingResult{NT} padding::Int mapping_history::Vector{Tuple{Pattern,Int,Int}} mis_overhead::Int - spacing::Int + spacing::GridSpacing end """ @@ -405,6 +437,8 @@ function map_graph(mode, g::SimpleGraph; vertex_order=MinhThiTrick(), ruleset=de ug = embed_graph(mode, g; vertex_order=vertex_order) mis_overhead0 = mis_overhead_copylines(ug) ug, tape = apply_crossing_gadgets!(mode, ug) + + # Note that apply_simplifier_gadgets! has not been specifically developed for the triangular lattice. However, the currently implemented components are also applicable to the triangular lattice and can provide a certain degree of simplification. Further development should take this into consideration. ug, tape2 = apply_simplifier_gadgets!(ug; ruleset=ruleset) mis_overhead1 = isempty(tape) ? 0 : sum(x->mis_overhead(x[1]), tape) mis_overhead2 = isempty(tape2) ? 0 : sum(x->mis_overhead(x[1]), tape2) diff --git a/src/triangular.jl b/src/triangular.jl index 22612c5..dc51cdc 100644 --- a/src/triangular.jl +++ b/src/triangular.jl @@ -2,65 +2,66 @@ abstract type TriangularCrossPattern <: Pattern end struct TriCross{CON} <: TriangularCrossPattern end iscon(::TriCross{CON}) where {CON} = CON -# · · · · · ⋅ ◆ ⋅ · · · · · -# ● ● ● ● ● ◆ ◉ ● ● ● ● ● ● -# · · · · · ⋅ ● · · · · · · -# · · · · ⋅ ⋅ ● ⋅ ⋅ · · · · -# · · · · ⋅ ⋅ ● ⋅ ⋅ · · · · -# · · · · ⋅ ⋅ ● ⋅ ⋅ · · · · +# ⋅ ◆ ⋅ · +# ◆ ◉ ● ● +# ⋅ ● · · +# · ● ⋅ ⋅ +# · ● ⋅ ⋅ +# ⋅ ● ⋅ ⋅ function source_graph(::TriCross{true}) - locs = Node.([(2,1), (2,2), (2,3), (2,4), (2,5), (2,6), (2,7), (2,8), (2,9), (2,10), (2,11), (2,12), (2,13), (1,7), (2,7), (3,7), (4,7), (5,7), (6,7)]) - g = simplegraph([(1,2), (2,3), (3,4), (4,5), (5,6), (6,7), (7,8), (8,9), (9,10), (10,11), (11,12), (12,13), (14,15), (15,16), (16,17), (17,18), (18,19), (14,6)]) - return locs, g, [1,14,13,19] -end -# ● · · · ⋅ · ● ⋅ ⋅ · · · ● -# · ● · · ● ● ● ● ● · · ● · -# · ● ● ● ⋅ ● ⋅ ● ⋅ ● ● ● · -# · · · · ⋅ ● ● · ⋅ · · · · -# · · · · ⋅ · · ● ⋅ · · · · -# · · · · ⋅ ⋅ ● ● ⋅ · · · · + locs = Node.([(2,1), (2,2), (2,3), (2,4), (1,2), (2,2), (3,2), (4,2), (5,2), (6,2)]) + g = simplegraph([(1,2), (2,3), (3,4), (5,6), (6,7), (7,8), (8,9), (9,10), (1,5)]) + return locs, g, [1,5,10,4] +end + +# · ● · ● +# ● ● ● · +# · · ● · +# · ● ● · +# ● · · · +# ● ● · · function mapped_graph(::TriCross{true}) - locs = Node.([(1,1), (1,7), (1,13), (2,2), (2,5), (2,6), (2,7), (2,8), (2,9), (2,12), (3,2), (3,3), (3,4), (3,6), (3,8), (3,10), (3,11), (3,12), (4,6), (4,7), (5,8), (6,8), (6,7)]) - return locs, triangular_unitdisk_graph(locs, 1.1, false), [1,2,3,23] + locs = Node.([(1,2), (2,1), (2,2), (2,3), (1,4), (3,3), (4,2), (4,3), (5,1), (6,1), (6,2)]) + return locs, triangular_unitdisk_graph(locs, 1.1, TriangularGrid(true)), [2,1,11,5] end -Base.size(::TriCross{true}) = (6, 13) -cross_location(::TriCross{true}) = (2, 7) -connected_nodes(::TriCross{true}) = [14,6] +Base.size(::TriCross{true}) = (6, 4) +cross_location(::TriCross{true}) = (2, 2) +connected_nodes(::TriCross{true}) = [1,5] function weighted(p::TriCross{true}) - sw = [2,2,2,2,2,2,2,2,2,2 ,2,2,2,2,2,2,2,2,2] - mw = [2,3,2,2,2,3,4,3,2,2 ,2,2,2,3,2,2,2,2 ,2,2,2,2,2] + sw = [2,2,2,2,2,2,2,2,2,2] + mw = [3,2,3,3,2,2,2,2,2,2,2] return weighted(p, sw, mw) end -# ⋅ ⋅ ● ⋅ ⋅ -# ● ● ◉ ● ● -# ⋅ ⋅ ● ⋅ ⋅ -# ⋅ ⋅ ● ⋅ ⋅ -# ⋅ ⋅ ● ⋅ ⋅ -# ⋅ ⋅ ● ⋅ ⋅ +# · ⋅ ⋅ ● ⋅ ⋅ +# ● ● ● ◉ ● ● +# · ⋅ ⋅ ● ⋅ ⋅ +# · ⋅ ⋅ ● ⋅ ⋅ +# · ⋅ ⋅ ● ⋅ ⋅ +# · ⋅ ⋅ ● ⋅ ⋅ function source_graph(::TriCross{false}) - locs = Node.([(2,1), (2,2), (2,3), (2,4), (2,5), (1,3), (2,3), (3,3), (4,3), (5,3), (6,3)]) - g = simplegraph([(1,2), (2,3), (3,4), (4,5), (6,7), (7,8), (8,9), (9,10), (10,11)]) - return locs, g, [1,6,11,5] + locs = Node.([(2,2), (2,3), (2,4), (2,5), (2,6), (1,4), (2,4), (3,4), (4,4), (5,4), (6,4), (2,1)]) + g = simplegraph([(1,2), (2,3), (3,4), (4,5), (6,7), (7,8), (8,9), (9,10), (10,11), (12,1)]) + return locs, g, [12,6,11,5] end -# ⋅ ⋅ ● ⋅ ⋅ -# ● ● ● ● ● -# ● ● ● ● · -# ● ● · ⋅ ⋅ -# ● ⋅ · ⋅ ⋅ -# · ● ● ⋅ ⋅ +# · ⋅ ⋅ ● ⋅ ⋅ +# ● ● ● ● ● ● +# · ● ● ● ● · +# · ● ● · ⋅ ⋅ +# · ● ⋅ · ⋅ ⋅ +# · · ● ● ⋅ ⋅ function mapped_graph(::TriCross{false}) - locs = Node.([(1,3), (2,1), (2,2), (2,3), (2,4), (2,5), (3,1), (3,2), (3,3), (3,4), (4,1), (4,2), (5,1), (6,2), (6,3)]) - return locs, triangular_unitdisk_graph(locs, 1.1, false), [2,1,15,6] + locs = Node.([(1,4), (2,2), (2,3), (2,4), (2,5), (2,6), (3,2), (3,3), (3,4), (3,5), (4,2), (4,3), (5,2), (6,3), (6,4), (2,1)]) + return locs, triangular_unitdisk_graph(locs, 1.1, TriangularGrid(true)), [16,1,15,6] end -Base.size(::TriCross{false}) = (6, 5) -cross_location(::TriCross{false}) = (2,3) +Base.size(::TriCross{false}) = (6, 6) +cross_location(::TriCross{false}) = (2, 4) function weighted(p::TriCross{false}) - sw = [2,2,2,2,2,2,2,2,2,2,2] - mw = [3,3,2,4,2,2,2,4,3,2,2,2,2,2,2] + sw = [2,2,2,2,2,2,2,2,2,2,2,2] + mw = [3,3,2,4,2,2,2,4,3,2,2,2,2,2,2,2] return weighted(p, sw, mw) end @@ -86,7 +87,7 @@ connected_nodes(::TriTCon_left) = [1, 2] # ● ● · · · function mapped_graph(::TriTCon_left) locs = Node.([(1,2), (2,1), (2,2), (2,3), (2,4), (3,3), (4,2), (4,3), (5,1), (6,1), (6,2)]) - return locs, triangular_unitdisk_graph(locs, 1.1, true), [1,2,11] + return locs, triangular_unitdisk_graph(locs, 1.1, TriangularGrid(true)), [1,2,11] end Base.size(::TriTCon_left) = (6,5) cross_location(::TriTCon_left) = (2,2) @@ -100,11 +101,10 @@ end struct TriTCon_down <: TriangularCrossPattern end # · · · -# · · · # ◆ ● ● # ⋅ ◆ · function source_graph(::TriTCon_down) - locs = Node.([(3,1), (3,2), (3,3), (4,2)]) + locs = Node.([(2,1), (2,2), (2,3), (3,2)]) g = simplegraph([(1,2), (2,3), (1,4)]) return locs, g, [1,4,3] end @@ -112,19 +112,18 @@ connected_nodes(::TriTCon_down) = [1, 4] # · · · # · ● · -# · ● · # ● ● ● function mapped_graph(::TriTCon_down) - locs = Node.([(2,2), (3,2), (4,1), (4,2), (4,3)]) - return locs, triangular_unitdisk_graph(locs, 1.1, true), [3,4,5] + locs = Node.([(2,2), (3,1), (3,2), (3,3)]) + return locs, triangular_unitdisk_graph(locs, 1.1, TriangularGrid(true)), [2,3,4] end -Base.size(::TriTCon_down) = (4,3) -cross_location(::TriTCon_down) = (3,2) +Base.size(::TriTCon_down) = (3,3) +cross_location(::TriTCon_down) = (2,2) iscon(::TriTCon_down) = true function weighted(p::TriTCon_down) sw = [2,2,2,1] - mw = [1,3,2,3,2] + mw = [2,2,3,2] return weighted(p, sw, mw) end @@ -132,7 +131,6 @@ struct TriTCon_up <: TriangularCrossPattern end # ⋅ ◆ · # ◆ ● ● # · · · -# · · · function source_graph(::TriTCon_up) locs = Node.([(1,2), (2,1), (2,2), (2,3)]) g = simplegraph([(1,2), (2,3), (3,4)]) @@ -142,19 +140,18 @@ connected_nodes(::TriTCon_up) = [1, 2] # · ● · # ● ● ● -# · ● · # · · · function mapped_graph(::TriTCon_up) - locs = Node.([(1,2), (2,1), (2,2), (2,3), (3,2)]) - return locs, triangular_unitdisk_graph(locs, 1.1, true), [2,1,4] + locs = Node.([(1,2), (2,1), (2,2), (2,3)]) + return locs, triangular_unitdisk_graph(locs, 1.1, TriangularGrid(true)), [2,1,4] end -Base.size(::TriTCon_up) = (4,3) +Base.size(::TriTCon_up) = (3,3) cross_location(::TriTCon_up) = (2,2) iscon(::TriTCon_up) = true function weighted(p::TriTCon_up) sw = [1,2,2,2] - mw = [3,2,3,2,1] + mw = [3,2,2,2] return weighted(p, sw, mw) end @@ -170,7 +167,7 @@ end # ● ⋅ function mapped_graph(::TriTrivialTurn_left) locs = Node.([(1,2),(2,1)]) - return locs, triangular_unitdisk_graph(locs, 1.1, true), [1,2] + return locs, triangular_unitdisk_graph(locs, 1.1, TriangularGrid(true)), [1,2] end Base.size(::TriTrivialTurn_left) = (2,2) cross_location(::TriTrivialTurn_left) = (2,2) @@ -195,7 +192,7 @@ end # ● ● function mapped_graph(::TriTrivialTurn_right) locs = Node.([(2,1),(2,2)]) - return locs, triangular_unitdisk_graph(locs, 1.1, true), [1,2] + return locs, triangular_unitdisk_graph(locs, 1.1, TriangularGrid(true)), [1,2] end Base.size(::TriTrivialTurn_right) = (2,2) cross_location(::TriTrivialTurn_right) = (1,2) @@ -222,7 +219,7 @@ end # ⋅ ⋅ ⋅ ⋅ function mapped_graph(::TriEndTurn) locs = Node.([(1,2)]) - return locs, triangular_unitdisk_graph(locs, 1.1, true), [1] + return locs, triangular_unitdisk_graph(locs, 1.1, TriangularGrid(true)), [1] end Base.size(::TriEndTurn) = (3,4) cross_location(::TriEndTurn) = (2,2) @@ -237,29 +234,27 @@ end struct TriTurn <: TriangularCrossPattern end iscon(::TriTurn) = false # ⋅ ● ⋅ ⋅ -# ⋅ ● ⋅ ⋅ # ⋅ ● ● ● # ⋅ ⋅ ⋅ ⋅ function source_graph(::TriTurn) - locs = Node.([(1,2), (2,2), (3,2), (3,3), (3,4)]) - g = simplegraph([(1,2), (2,3), (3,4), (4,5)]) - return locs, g, [1,5] + locs = Node.([(1,2), (2,2), (2,3), (2,4)]) + g = simplegraph([(1,2), (2,3), (3,4)]) + return locs, g, [1,4] end -# ⋅ ● ⋅ ⋅ # ⋅ ● · ⋅ -# ● ⋅ ⋅ ● -# ● ● ● ⋅ +# · ● · ● +# · · ● ⋅ function mapped_graph(::TriTurn) - locs = Node.([(1,2), (2,2), (3,1), (4,1), (4,2), (4,3), (3,4)]) - locs, triangular_unitdisk_graph(locs, 1.1, true), [1,7] + locs = Node.([(1,2), (2,2), (3,3), (2,4)]) + return locs, triangular_unitdisk_graph(locs, 1.1, TriangularGrid(true)), [1,4] end -Base.size(::TriTurn) = (4, 4) -cross_location(::TriTurn) = (3,2) +Base.size(::TriTurn) = (3, 4) +cross_location(::TriTurn) = (2,2) function weighted(p::TriTurn) - sw = [2,2,2,2,2] - mw = [2,2,2,2,2,2,2] + sw = [2,2,2,2] + mw = [2,2,2,2] return weighted(p, sw, mw) end @@ -279,7 +274,7 @@ end # ⋅ ● ⋅ ⋅ function mapped_graph(::TriWTurn) locs = Node.([(1,4), (2,3), (3,2), (3,3), (4,2)]) - return locs, triangular_unitdisk_graph(locs, 1.1, true), [1, 5] + return locs, triangular_unitdisk_graph(locs, 1.1, TriangularGrid(true)), [1, 5] end Base.size(::TriWTurn) = (4, 4) cross_location(::TriWTurn) = (2,2) @@ -307,7 +302,7 @@ end # ⋅ ● ⋅ ⋅ function mapped_graph(::TriBranchFix) locs = Node.([(1,2),(2,2),(3,2),(4,2)]) - return locs, triangular_unitdisk_graph(locs, 1.1, true), [1, 4] + return locs, triangular_unitdisk_graph(locs, 1.1, TriangularGrid(true)), [1, 4] end Base.size(::TriBranchFix) = (4, 4) cross_location(::TriBranchFix) = (2,2) @@ -335,7 +330,7 @@ end # ⋅ ● ⋅ ⋅ function mapped_graph(::TriBranchFixB) locs = Node.([(3,2),(4,2)]) - return locs, triangular_unitdisk_graph(locs, 1.1, true), [1, 2] + return locs, triangular_unitdisk_graph(locs, 1.1, TriangularGrid(true)), [1, 2] end Base.size(::TriBranchFixB) = (4, 4) cross_location(::TriBranchFixB) = (2,2) @@ -361,13 +356,13 @@ function source_graph(::TriBranch) end # ⋅ ● ⋅ ⋅ # ⋅ ● · ● -# ● · ● · +# · · ● · # ⋅ ● ● ⋅ # ● · ⋅ ⋅ # ● ● ⋅ ⋅ function mapped_graph(::TriBranch) - locs = Node.([(1,2),(2,2),(2,4),(3,1),(3,3),(4,2),(4,3),(5,1),(6,1),(6,2)]) - return locs, triangular_unitdisk_graph(locs, 1.1, true), [1,3,10] + locs = Node.([(1,2),(2,2),(2,4),(3,3),(4,2),(4,3),(5,1),(6,1),(6,2)]) + return locs, triangular_unitdisk_graph(locs, 1.1, TriangularGrid(true)), [1,3,9] end Base.size(::TriBranch) = (6, 4) cross_location(::TriBranch) = (2,2) @@ -375,7 +370,7 @@ iscon(::TriBranch) = false function weighted(p::TriBranch) sw = [2,2,3,2,2,2,2,2,2] - mw = [2,3,2,1,3,2,2,2,2,2] + mw = [2,2,2,3,2,2,2,2,2] return weighted(p, sw, mw) end @@ -403,22 +398,21 @@ mis_overhead(w::WeightedGadget{<:TriangularCrossPattern}) = mis_overhead(w.gadge # mis_overhead functions for TriangularCrossPattern types # These values should be computed properly using compute_mis_overhead function from project/createmap.jl -# For now, using reasonable placeholder values based on the pattern complexity -mis_overhead(::TriCross{true}) = 4 +mis_overhead(::TriCross{true}) = 1 mis_overhead(::TriCross{false}) = 3 mis_overhead(::TriTCon_left) = 4 -mis_overhead(::TriTCon_down) = 1 -mis_overhead(::TriTCon_up) = 1 +mis_overhead(::TriTCon_down) = 0 +mis_overhead(::TriTCon_up) = 0 mis_overhead(::TriTrivialTurn_left) = 0 mis_overhead(::TriTrivialTurn_right) = 0 mis_overhead(::TriEndTurn) = -2 -mis_overhead(::TriTurn) = 2 +mis_overhead(::TriTurn) = 0 mis_overhead(::TriWTurn) = 0 mis_overhead(::TriBranchFix) = -2 mis_overhead(::TriBranchFixB) = -2 -mis_overhead(::TriBranch) = 1 +mis_overhead(::TriBranch) = 0 -for (T, centerloc) in [(:TriTurn, (4, 1)), (:TriBranch, (1,2)), (:TriBranchFix, (3, 2)), (:TriBranchFixB, (3, 2)), (:TriWTurn, (2, 3)), (:TriEndTurn, (1, 2))] +for (T, centerloc) in [(:TriTurn, (1, 2)), (:TriBranch, (1,2)), (:TriBranchFix, (3, 2)), (:TriBranchFixB, (3, 2)), (:TriWTurn, (2, 3)), (:TriEndTurn, (1, 2))] @eval source_centers(::WeightedGadget{<:$T}) = [cross_location($T()) .+ (0, 1)] @eval mapped_centers(::WeightedGadget{<:$T}) = [$centerloc] end \ No newline at end of file diff --git a/src/utils.jl b/src/utils.jl index 05da222..1134514 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -41,11 +41,10 @@ function unitdisk_graph(locs::AbstractVector, unit::Real) return g end -function triangular_unitdisk_graph(locs::AbstractVector, unit::Real, parity::Bool=false) - # parity==false: crossing nodes on odd columns. +function triangular_unitdisk_graph(locs::AbstractVector, unit::Real, grid_type::TriangularGrid=TriangularGrid()) n = length(locs) g = SimpleGraph(n) - physical_locs = physical_position.(locs, parity) + physical_locs = [physical_position(node, grid_type) for node in locs] for i=1:n, j=i+1:n if sum(abs2, physical_locs[i] .- physical_locs[j]) < unit ^ 2 add_edge!(g, i, j) @@ -54,12 +53,44 @@ function triangular_unitdisk_graph(locs::AbstractVector, unit::Real, parity::Boo return g end -function physical_position(node, parity=false) +""" + physical_position(node, grid_type) + +Convert grid coordinates to physical coordinates for distance calculations. + +# Arguments +- `node`: A `Node` instance +- `grid_type`: A subtype of `AbstractGridType` specifying the grid geometry + +# Returns +- Tuple `(x, y)` representing physical coordinates + +# Grid type behaviors +- `SquareGrid`: Physical position equals grid position +- `TriangularGrid`: Maps to equilateral triangular lattice with appropriate column offsets + +# Examples +```julia +node = Node((3, 4)) +square_pos = physical_position(node, SquareGrid()) # (3.0, 4.0) +tri_pos = physical_position(node, TriangularGrid()) # (3.0, 3.464...) +tri_pos_alt = physical_position(node, TriangularGrid(true)) # (3.5, 3.464...) +``` +""" +function physical_position(node::Node, ::SquareGrid) + # For square grids, coordinates are already physical positions + return float.(node.loc) +end + +function physical_position(node::Node, grid::TriangularGrid) + # For triangular grids, use the grid's offset setting to create equilateral triangles i, j = node.loc - y = j * (√3 / 2) - if parity + y = j * (√3 / 2) # Vertical spacing for equilateral triangles + if grid.offset_even_cols + # add offset to even columns x = i + (iseven(j) ? 0.5 : 0.0) else + # add offset to odd columns (default behavior) x = i + (isodd(j) ? 0.5 : 0.0) end return (x, y) diff --git a/src/visualize.jl b/src/visualize.jl index f2c95b1..3ec7931 100644 --- a/src/visualize.jl +++ b/src/visualize.jl @@ -1,16 +1,15 @@ # normalized to minimum weight and maximum weight -# Helper function to convert grid coordinates to plot coordinates +""" + plot_coordinates(gg::GridGraph, i::Int, j::Int, unit::Float64) + +Map grid coordinates to plot coordinates using the physical position +for the grid type, then rotate to plotting frame. +""" function plot_coordinates(gg::GridGraph, i::Int, j::Int, unit::Float64) - if is_triangular_grid(gg) - # Triangular grid layout: y = j * (√3 / 2), x = i + (iseven(j) ? 0.5 : 0.0) - x = (i + (isodd(j) ? 0.5 : 0.0)) * unit - y = j * (√3 / 2) * unit # negative for downward y-axis - return (y, -x) - else - # Square grid layout (original) - return (j * unit, -i * unit) - end + # Use unified physical coordinates for both grid types + x, y = physical_position(Node(i, j), gg.gridtype) + return (y * unit, -x * unit) end function LuxorGraphPlot.show_graph(gg::GridGraph; diff --git a/src/weighted.jl b/src/weighted.jl index eef523c..96bb26f 100644 --- a/src/weighted.jl +++ b/src/weighted.jl @@ -97,7 +97,7 @@ end trace_centers(r::MappingResult) = trace_centers(r.lines, r.padding, r.mapping_history, r.spacing) function trace_centers(lines, padding, tape, spacing=4) - center_locations = map(x->center_location(x; padding, s=spacing) .+ (0, 1), lines) + center_locations = map(x->center_location(x, padding, spacing) .+ (0, 1), lines) for (gadget, i, j) in tape m, n = size(gadget) for (k, centerloc) in enumerate(center_locations) diff --git a/test/mapping.jl b/test/mapping.jl index 1d5a699..583e6c1 100644 --- a/test/mapping.jl +++ b/test/mapping.jl @@ -41,7 +41,7 @@ end mis_overhead1 = sum(x->mis_overhead(x[1]), tape) mis_overhead2 = sum(x->mis_overhead(x[1]), tape2) @show mis_overhead2 - gp = GenericTensorNetwork(IndependentSet(SimpleGraph(ug3)); optimizer=GreedyMethod(nrepeat=10)) + gp = GenericTensorNetwork(IndependentSet(SimpleGraph(ug3))) missize_map = solve(gp, SizeMax())[].n missize = solve(GenericTensorNetwork(IndependentSet(g)), SizeMax())[].n @test mis_overhead0 + mis_overhead1 + mis_overhead2 + missize == missize_map diff --git a/test/triangular.jl b/test/triangular.jl index b5b71ca..68656e1 100644 --- a/test/triangular.jl +++ b/test/triangular.jl @@ -34,7 +34,7 @@ end (3, 7, 8), (3, 5, 8), (5, 9, 8), (5, 5, 8), (1, 7, 5), (5, 8, 5), (1, 5, 5), (5, 5, 5)] tc = UnitDiskMapping.CopyLine(1, 5, 5, vstart, vstop, hstop) - locs = UnitDiskMapping.copyline_locations(UnitDiskMapping.WeightedNode, tc; padding=2) + locs = UnitDiskMapping.copyline_locations(UnitDiskMapping.WeightedNode, tc, 2, UnitDiskMapping.get_spacing(TriangularWeighted())) g = SimpleGraph(length(locs)) weights = getfield.(locs, :weight) for i=1:length(locs)-1 @@ -45,7 +45,7 @@ end end end gp = GenericTensorNetwork(IndependentSet(g, weights)) - @test solve(gp, SizeMax())[].n == UnitDiskMapping.mis_overhead_copyline(TriangularWeighted(), tc) + @test solve(gp, SizeMax())[].n == UnitDiskMapping.mis_overhead_copyline(TriangularWeighted(), tc, UnitDiskMapping.get_spacing(TriangularWeighted())) end end @@ -54,15 +54,15 @@ end for graphname in [:bull, :petersen, :cubical, :house, :diamond, :tutte] @show graphname g = smallgraph(graphname) - weights = fill(0.25, nv(g)) + weights = fill(0.2, nv(g)) r = map_graph(TriangularWeighted(), g) mapped_weights = UnitDiskMapping.map_weights(r, weights) mgraph, _ = graph_and_weights(r.grid_graph) - - gp = GenericTensorNetwork(IndependentSet(mgraph, mapped_weights); optimizer=GreedyMethod(nrepeat=10)) + gp = GenericTensorNetwork(IndependentSet(mgraph, round.(Int, mapped_weights .* 10))) missize_map = solve(gp, CountingMax())[] - missize = solve(GenericTensorNetwork(IndependentSet(g, weights)), CountingMax())[] - @test r.mis_overhead + missize.n == missize_map.n + + missize = solve(GenericTensorNetwork(IndependentSet(g, round.(Int, weights .* 10))), CountingMax())[] + @test r.mis_overhead + missize.n / 10 ≈ missize_map.n / 10 @test missize.c == missize_map.c T = GenericTensorNetworks.sampler_type(nv(mgraph), 2) @@ -75,7 +75,7 @@ end center_locations = trace_centers(r) indices = CartesianIndex.(center_locations) sc = c[indices] - @test count(isone, sc) == missize.n * 4 + @test count(isone, sc) ≈ (missize.n / 10) * 5 @test is_independent_set(g, sc) end end diff --git a/test/utils.jl b/test/utils.jl index 39c2bf6..a63e023 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -1,4 +1,5 @@ -using UnitDiskMapping: rotate90, reflectx, reflecty, reflectdiag, reflectoffdiag +using UnitDiskMapping: rotate90, reflectx, reflecty, reflectdiag, reflectoffdiag, physical_position +using UnitDiskMapping: Node, SquareGrid, TriangularGrid using Test @testset "symmetry operations" begin @@ -9,4 +10,12 @@ using Test @test reflecty(loc, center) == (0,3) @test reflectdiag(loc, center) == (1,0) @test reflectoffdiag(loc, center) == (3,4) +end + +@testset "physical position" begin + node = Node((2,3)) + @test physical_position(node, SquareGrid()) == (2,3) + @test physical_position(node, TriangularGrid()) == (2.5, 3 * (√3 / 2)) + @test physical_position(node, TriangularGrid(false)) == (2.5, 3 * (√3 / 2)) + @test physical_position(node, TriangularGrid(true)) == (2, 3 * (√3 / 2)) end \ No newline at end of file diff --git a/test/weighted.jl b/test/weighted.jl index a59b7ed..cb591ef 100644 --- a/test/weighted.jl +++ b/test/weighted.jl @@ -34,7 +34,7 @@ end (3, 7, 8), (3, 5, 8), (5, 9, 8), (5, 5, 8), (1, 7, 5), (5, 8, 5), (1, 5, 5), (5, 5, 5)] tc = UnitDiskMapping.CopyLine(1, 5, 5, vstart, vstop, hstop) - locs = UnitDiskMapping.copyline_locations(UnitDiskMapping.WeightedNode, tc; padding=2) + locs = UnitDiskMapping.copyline_locations(UnitDiskMapping.WeightedNode, tc, 2, UnitDiskMapping.get_spacing(Weighted())) g = SimpleGraph(length(locs)) weights = getfield.(locs, :weight) for i=1:length(locs)-1 @@ -45,7 +45,7 @@ end end end gp = GenericTensorNetwork(IndependentSet(g, weights)) - @test solve(gp, SizeMax())[].n == UnitDiskMapping.mis_overhead_copyline(Weighted(), tc) + @test solve(gp, SizeMax())[].n == UnitDiskMapping.mis_overhead_copyline(Weighted(), tc, UnitDiskMapping.get_spacing(Weighted())) end end @@ -66,10 +66,11 @@ end weights = fill(0.5, nv(g)) r = UnitDiskMapping.MappingResult(GridGraph(Weighted(), ug3), ug3.lines, ug3.padding, [tape..., tape2...], mis_overhead0+mis_overhead1+mis_overhead2, ug.spacing) mapped_weights = UnitDiskMapping.map_weights(r, weights) - gp = GenericTensorNetwork(IndependentSet(mgraph, mapped_weights); optimizer=GreedyMethod(nrepeat=10)) + gp = GenericTensorNetwork(IndependentSet(mgraph, round.(Int, mapped_weights .* 10))) missize_map = solve(gp, CountingMax())[] - missize = solve(GenericTensorNetwork(IndependentSet(g, weights)), CountingMax())[] - @test mis_overhead0 + mis_overhead1 + mis_overhead2 + missize.n == missize_map.n + + missize = solve(GenericTensorNetwork(IndependentSet(g, round.(Int, weights .* 10))), CountingMax())[] + @test mis_overhead0 + mis_overhead1 + mis_overhead2 + missize.n / 10 ≈ missize_map.n / 10 @test missize.c == missize_map.c T = GenericTensorNetworks.sampler_type(nv(mgraph), 2) @@ -82,7 +83,7 @@ end center_locations = trace_centers(r) indices = CartesianIndex.(center_locations) sc = c[indices] - @test count(isone, sc) == missize.n * 2 + @test count(isone, sc) == (missize.n / 10) * 2 @test is_independent_set(g, sc) end end