diff --git a/src/UnitDiskMapping.jl b/src/UnitDiskMapping.jl index ee3a7ab..fa2c0c2 100644 --- a/src/UnitDiskMapping.jl +++ b/src/UnitDiskMapping.jl @@ -12,9 +12,9 @@ export UNode, contract_graph, compress_graph include("utils.jl") include("gadgets.jl") -include("simplifiers.jl") include("mapping.jl") include("weighted.jl") +include("simplifiers.jl") include("extracting_results.jl") include("pathdecomposition/pathdecomposition.jl") #include("shrinking/compressUDG.jl") diff --git a/src/gadgets.jl b/src/gadgets.jl index ad6c589..59c8366 100644 --- a/src/gadgets.jl +++ b/src/gadgets.jl @@ -386,16 +386,17 @@ for T in [:RotatedGadget, :ReflectedGadget] @eval mis_overhead(p::$T) = mis_overhead(p.gadget) end -function _apply_transform(r::RotatedGadget, node::Node, center) - loc = getxy(node) +for T in [:RotatedGadget, :ReflectedGadget] + @eval _apply_transform(r::$T, node::Node, center) = chxy(node, _apply_transform(r, getxy(node), center)) +end +function _apply_transform(r::RotatedGadget, loc::Tuple{Int,Int}, center) for _=1:r.n loc = rotate90(loc, center) end - return chxy(node, loc) + return loc end -function _apply_transform(r::ReflectedGadget, node::Node, center) - loc = getxy(node) +function _apply_transform(r::ReflectedGadget, loc::Tuple{Int,Int}, center) loc = if r.mirror == "x" reflectx(loc, center) elseif r.mirror == "y" @@ -407,7 +408,7 @@ function _apply_transform(r::ReflectedGadget, node::Node, center) else throw(ArgumentError("reflection direction $(r.direction) is not defined!")) end - chxy(node, loc) + return loc end export vertex_overhead @@ -429,3 +430,17 @@ function _boundary_config(pins, config) end return res end + +export rotated_and_reflected +function rotated_and_reflected(p::Pattern) + patterns = Pattern[p] + source_matrices = [source_matrix(p)] + for pi in [[RotatedGadget(p, i) for i=1:3]..., [ReflectedGadget(p, axis) for axis in ["x", "y", "diag", "offdiag"]]...] + m = source_matrix(pi) + if m ∉ source_matrices + push!(patterns, pi) + push!(source_matrices, m) + end + end + return patterns +end \ No newline at end of file diff --git a/src/mapping.jl b/src/mapping.jl index 2497db4..b6e1383 100644 --- a/src/mapping.jl +++ b/src/mapping.jl @@ -373,8 +373,10 @@ It can be a vector or one of the following inputs Returns a `MappingResult` instance. """ -map_graph(g::SimpleGraph; vertex_order=Greedy(), ruleset=[RotatedGadget(DanglingLeg(), n) for n=0:3]) = map_graph(UnWeighted(), g; ruleset=ruleset, vertex_order=vertex_order) -function map_graph(mode, g::SimpleGraph; vertex_order=Greedy(), ruleset=[RotatedGadget(DanglingLeg(), n) for n=0:3]) +function map_graph(g::SimpleGraph; vertex_order=Greedy(), ruleset=default_simplifier_ruleset(UnWeighted())) + map_graph(UnWeighted(), g; ruleset=ruleset, vertex_order=vertex_order) +end +function map_graph(mode, g::SimpleGraph; vertex_order=Greedy(), ruleset=default_simplifier_ruleset(mode)) ug = embed_graph(mode, g; vertex_order=vertex_order) mis_overhead0 = mis_overhead_copylines(ug) ug, tape = apply_crossing_gadgets!(mode, ug) @@ -385,3 +387,5 @@ function map_graph(mode, g::SimpleGraph; vertex_order=Greedy(), ruleset=[Rotated end map_configs_back(r::MappingResult{<:Cell}, configs::AbstractVector) = unapply_gadgets!(copy(r.grid_graph), r.mapping_history, copy.(configs))[2] +default_simplifier_ruleset(::UnWeighted) = vcat([rotated_and_reflected(rule) for rule in simplifier_ruleset]...) +default_simplifier_ruleset(::Weighted) = weighted.(default_simplifier_ruleset(UnWeighted())) \ No newline at end of file diff --git a/src/simplifiers.jl b/src/simplifiers.jl index 0542ade..cbca309 100644 --- a/src/simplifiers.jl +++ b/src/simplifiers.jl @@ -32,29 +32,36 @@ struct GridGraph{NT<:Node} end vertices_on_boundary(gg::GridGraph) = vertices_on_boundary(gg.nodes, gg.size...) -function gridgraphfromstring(str::String) - item_array = Vector{Bool}[] +function gridgraphfromstring(mode::Union{Weighted, UnWeighted}, str::String) + item_array = Vector{Int}[] for line in split(str, "\n") - list = [item ∈ ("o", "●") ? true : (@assert item ∈ (".", "⋅"); false) for item in split(line, " ") if !isempty(item)] + items = [item for item in split(line, " ") if !isempty(item)] + list = if mode isa Weighted # TODO: the weighted version need to be tested! Consider removing it! + @assert all(item->item ∈ (".", "⋅", "@", "●", "o", "◯"), items) + [item ∈ ("@", "●") ? 2 : (item ∈ ("o", "◯") ? 1 : 0) for item in items] + else + @assert all(item->item ∈ (".", "⋅", "@", "●"), items) + [item ∈ ("@", "●") ? 1 : 0 for item in items] + end if !isempty(list) push!(item_array, list) end end @assert all(==(length(item_array[1])), length.(item_array)) mat = hcat(item_array...)' - locs = [SimpleNode(ci.I) for ci in findall(mat)] + locs = [_to_node(mode, ci.I, mat[ci]) for ci in findall(!iszero, mat)] return GridGraph(size(mat), locs) end +_to_node(::UnWeighted, loc::Tuple{Int,Int}, w::Int) = SimpleNode(loc...) +_to_node(::Weighted, loc::Tuple{Int,Int}, w::Int) = WeightedNode(loc..., w) -const simplifier_ruleset = SimplifyPattern[] - -macro gg(expr) +function gg_func(mode, expr) @assert expr.head == :(=) name = expr.args[1] pair = expr.args[2] @assert pair.head == :(call) && pair.args[1] == :(=>) - g1 = gridgraphfromstring(pair.args[2]) - g2 = gridgraphfromstring(pair.args[3]) + g1 = gridgraphfromstring(mode, pair.args[2]) + g2 = gridgraphfromstring(mode, pair.args[3]) @assert g1.size == g2.size @assert g1.nodes[vertices_on_boundary(g1)] == g2.nodes[vertices_on_boundary(g2)] return quote @@ -62,11 +69,14 @@ macro gg(expr) Base.size(::$(esc(name))) = $(g1.size) $UnitDiskMapping.source_locations(::$(esc(name))) = $(g1.nodes) $UnitDiskMapping.mapped_locations(::$(esc(name))) = $(g2.nodes) - push!($(simplifier_ruleset), $(esc(name))()) $(esc(name)) end end +macro gg(expr) + gg_func(UnWeighted(), expr) +end + # # How to add a new simplification rule # 1. specify a gadget like the following. Use either `o` and `●` to specify a vertex, # either `.` or `⋅` to specify a placeholder. @@ -83,5 +93,11 @@ end ⋅ ● ⋅ """ -# 2. run the script `project/createmap` to generate `mis_overhead` and other informations required +# 2. add your gadget to simplifier ruleset. +const simplifier_ruleset = SimplifyPattern[DanglingLeg()] +# set centers (vertices with weight 1) for the weighted version +source_centers(::WeightedGadget{DanglingLeg}) = [(2,2)] +mapped_centers(::WeightedGadget{DanglingLeg}) = [(4,2)] + +# 3. run the script `project/createmap` to generate `mis_overhead` and other informations required # for mapping back. (Note: will overwrite the source file `src/extracting_results.jl`) \ No newline at end of file diff --git a/src/weighted.jl b/src/weighted.jl index b831d3f..2d934d6 100644 --- a/src/weighted.jl +++ b/src/weighted.jl @@ -1,3 +1,4 @@ +export WeightedCell, WeightedGadget, WeightedNode # TODO: # * add path decomposition struct WeightedCell{RT} <: AbstractCell @@ -7,13 +8,10 @@ struct WeightedCell{RT} <: AbstractCell weight::RT end -abstract type WeightedCrossPattern <: Pattern end -abstract type WeightedSimplifyPattern <:Pattern end struct WeightedGadget{GT} <: Pattern gadget::GT - factor::Int end -const WeightedPattern = Union{WeightedCrossPattern, WeightedSimplifyPattern, WeightedGadget} +const WeightedGadgetTypes = Union{WeightedGadget, RotatedGadget{<:WeightedGadget}, ReflectedGadget{<:WeightedGadget}} Base.isempty(cell::WeightedCell) = !cell.occupied Base.empty(::Type{WeightedCell{RT}}) where RT = WeightedCell(false, false, false,0) @@ -62,126 +60,49 @@ _weight2(::CopyLine{Weighted}, i, j) = WeightedNode(i, j, 2) _weight1(::CopyLine{Weighted}, i, j) = WeightedNode(i, j, 1) _cell_type(::Type{<:WeightedNode}) = WeightedCell{Int} -function source_graph(r::WeightedGadget) - locs, g, pins = source_graph(r.gadget) - _mul_weight.(locs, r.factor), g, pins -end -function mapped_graph(r::WeightedGadget) - locs, g, pins = mapped_graph(r.gadget) - _mul_weight.(locs, r.factor), g, pins -end -_mul_weight(node::SimpleNode, factor) = WeightedNode(node..., factor) -mis_overhead(p::WeightedGadget) = 2*mis_overhead(p.gadget) - -# new gadgets -struct WeightedWTurn <: WeightedCrossPattern end -# ⋅ ⋅ ⋅ ⋅ -# ⋅ ⋅ ◯ ● -# ⋅ ● ● ⋅ -# ⋅ ● ⋅ ⋅ - -# ⋅ ⋅ ⋅ ⋅ -# ⋅ ⋅ ⋅ ● -# ⋅ ⋅ ◯ ⋅ -# ⋅ ● ⋅ ⋅ - -struct WeightedBranchFix <: WeightedCrossPattern end -# ⋅ ● ⋅ ⋅ -# ⋅ ● ◯ ⋅ -# ⋅ ● ● ⋅ -# ⋅ ● ⋅ ⋅ - -# ⋅ ● ⋅ ⋅ -# ⋅ ● ⋅ ⋅ -# ⋅ ◯ ⋅ ⋅ -# ⋅ ● ⋅ ⋅ - -struct WeightedTurn <: WeightedCrossPattern end -# ⋅ ● ⋅ ⋅ -# ⋅ ● ⋅ ⋅ -# ⋅ ● ◯ ● -# ⋅ ⋅ ⋅ ⋅ - -# ⋅ ● ⋅ ⋅ -# ⋅ ⋅ ◯ ⋅ -# ⋅ ⋅ ⋅ ● -# ⋅ ⋅ ⋅ ⋅ - -struct WeightedBranch <: WeightedCrossPattern end -# ⋅ ● ⋅ ⋅ -# ⋅ ● ⋅ ⋅ -# ⋅ ● ◯ ● -# ⋅ ● ● ⋅ -# ⋅ ● ⋅ ⋅ - - -# ⋅ ● ⋅ ⋅ ? -# ⋅ ⋅ ◯ ⋅ -# ⋅ ● ⋅ ● -# ⋅ ⋅ ● ⋅ -# ⋅ ● ⋅ ⋅ - -struct WeightedBranchFixB <: WeightedCrossPattern end -# ⋅ ⋅ ⋅ ⋅ -# ⋅ ⋅ ◯ ⋅ -# ⋅ ● ● ⋅ -# ⋅ ● ⋅ ⋅ - -# ⋅ ⋅ ⋅ ⋅ -# ⋅ ⋅ ⋅ ⋅ -# ⋅ ◯ ⋅ ⋅ -# ⋅ ● ⋅ ⋅ - -struct WeightedEndTurn <: WeightedCrossPattern end -# ⋅ ● ⋅ ⋅ -# ⋅ ● ◯ ⋅ -# ⋅ ⋅ ⋅ ⋅ - -# ⋅ ◯ ⋅ ⋅ -# ⋅ ⋅ ⋅ ⋅ -# ⋅ ⋅ ⋅ ⋅ - -for T in [:Cross, :TrivialTurn, :TCon] - @eval weighted(c::$T) = WeightedGadget(c, 2) -end +weighted(c::Pattern) = WeightedGadget(c) unweighted(w::WeightedGadget) = w.gadget weighted(r::RotatedGadget) = RotatedGadget(weighted(r.gadget), r.n) weighted(r::ReflectedGadget) = ReflectedGadget(weighted(r.gadget), r.mirror) unweighted(r::RotatedGadget) = RotatedGadget(unweighted(r.gadget), r.n) unweighted(r::ReflectedGadget) = ReflectedGadget(unweighted(r.gadget), r.mirror) +mis_overhead(w::WeightedGadget) = mis_overhead(w.gadget) * 2 -for T in [:Turn, :Branch, :BranchFix, :BranchFixB, :WTurn, :EndTurn] - WT = Symbol(:Weighted, T) - @eval weighted(::$T) = $WT() - @eval unweighted(::$WT) = $T() - @eval mis_overhead(::$WT) = mis_overhead($T()) * 2 +function source_graph(r::WeightedGadget) + raw = unweighted(r) + locs, g, pins = source_graph(raw) + return map(loc->_mul_weight(loc, getxy(loc) ∈ source_centers(r) ? 1 : 2), locs), g, pins +end +function mapped_graph(r::WeightedGadget) + raw = unweighted(r) + locs, g, pins = mapped_graph(raw) + return map(loc->_mul_weight(loc, getxy(loc) ∈ mapped_centers(r) ? 1 : 2), locs), g, pins end +_mul_weight(node::SimpleNode, factor) = WeightedNode(node..., factor) for (T, centerloc) in [(:Turn, (2, 3)), (:Branch, (2, 3)), (:BranchFix, (3, 2)), (:BranchFixB, (3, 2)), (:WTurn, (3, 3)), (:EndTurn, (1, 2))] - WT = Symbol(:Weighted, T) - @eval function source_graph(r::$WT) - raw = unweighted(r) - locs, g, pins = source_graph(raw) - return map(loc->_mul_weight(loc, loc == SimpleNode(cross_location(raw) .+ (0, 1)) ? 1 : 2), locs), g, pins - end - @eval function mapped_graph(r::$WT) - raw = unweighted(r) - locs, g, pins = mapped_graph(raw) - return map(loc->_mul_weight(loc, loc == SimpleNode($centerloc) ? 1 : 2), locs), g, pins + @eval source_centers(::WeightedGadget{<:$T}) = [cross_location($T()) .+ (0, 1)] + @eval mapped_centers(::WeightedGadget{<:$T}) = [$centerloc] +end +# default to having no source center! +source_centers(::WeightedGadget) = Tuple{Int,Int}[] +mapped_centers(::WeightedGadget) = Tuple{Int,Int}[] +for T in [:(RotatedGadget{<:WeightedGadget}), :(ReflectedGadget{<:WeightedGadget})] + @eval function source_centers(r::$T) + cross = cross_location(r.gadget) + return map(loc->loc .+ _get_offset(r), _apply_transform.(Ref(r), source_centers(r.gadget), Ref(cross))) end - @eval function move_center(::$WT, nodexy) - nodexy .+ $centerloc .- (cross_location($WT()) .+ (0, 1)) + @eval function mapped_centers(r::$T) + cross = cross_location(r.gadget) + return map(loc->loc .+ _get_offset(r), _apply_transform.(Ref(r), mapped_centers(r.gadget), Ref(cross))) end end -move_center(::Pattern, nodexy) = nodexy -for T in [:WeightedCrossPattern, :WeightedGadget] - @eval Base.size(r::$T) = size(unweighted(r)) - @eval cross_location(r::$T) = cross_location(unweighted(r)) - @eval iscon(r::$T) = iscon(unweighted(r)) - @eval connected_nodes(r::$T) = connected_nodes(unweighted(r)) - @eval vertex_overhead(r::$T) = vertex_overhead(unweighted(r)) -end +Base.size(r::WeightedGadget) = size(unweighted(r)) +cross_location(r::WeightedGadget) = cross_location(unweighted(r)) +iscon(r::WeightedGadget) = iscon(unweighted(r)) +connected_nodes(r::WeightedGadget) = connected_nodes(unweighted(r)) +vertex_overhead(r::WeightedGadget) = vertex_overhead(unweighted(r)) const crossing_ruleset_weighted = weighted.(crossing_ruleset) get_ruleset(::Weighted) = crossing_ruleset_weighted @@ -189,16 +110,26 @@ get_ruleset(::Weighted) = crossing_ruleset_weighted export get_weights get_weights(ug::UGrid) = [ug.content[ci...].weight for ci in coordinates(ug)] +# mapping configurations back export trace_centers +function move_center(w::WeightedGadgetTypes, nodexy, offset) + for (sc, mc) in zip(source_centers(w), mapped_centers(w)) + if offset == sc + return nodexy .+ mc .- sc # found + end + end + error("center not found, source center = $(source_centers(w)), while offset = $(offset)") +end + trace_centers(r::MappingResult) = trace_centers(r.grid_graph, r.mapping_history) function trace_centers(ug::UGrid, tape) center_locations = map(x->center_location(x; padding=ug.padding) .+ (0, 1), ug.lines) for (gadget, i, j) in tape m, n = size(gadget) for (k, centerloc) in enumerate(center_locations) - offset = centerloc .- (i,j) - if 0<=offset[1] <= m-1 && 0<=offset[2] <= n-1 - center_locations[k] = move_center(gadget, centerloc) + offset = centerloc .- (i-1,j-1) + if 1<=offset[1] <= m && 1<=offset[2] <= n + center_locations[k] = move_center(gadget, centerloc, offset) end end end diff --git a/test/gadgets.jl b/test/gadgets.jl index db965f0..a7185ff 100644 --- a/test/gadgets.jl +++ b/test/gadgets.jl @@ -16,4 +16,11 @@ using Graphs @test diff == -mis_overhead(s) @test sig end +end + +@testset "rotated_and_reflected" begin + @test length(rotated_and_reflected(UnitDiskMapping.DanglingLeg())) == 4 + @test length(rotated_and_reflected(Cross{false}())) == 4 + @test length(rotated_and_reflected(Cross{true}())) == 4 + @test length(rotated_and_reflected(BranchFixB())) == 8 end \ No newline at end of file diff --git a/test/simplifiers.jl b/test/simplifiers.jl index d4b2751..cf56565 100644 --- a/test/simplifiers.jl +++ b/test/simplifiers.jl @@ -1,8 +1,31 @@ -using UnitDiskMapping, Test +using UnitDiskMapping, Test, Graphs @testset "constructor" begin p = UnitDiskMapping.DanglingLeg() @test size(p) == (4, 3) @test UnitDiskMapping.source_locations(p) == UnitDiskMapping.SimpleNode.([(2,2), (3,2), (4,2)]) @test UnitDiskMapping.mapped_locations(p) == UnitDiskMapping.SimpleNode.([(4,2)]) +end + +@testset "macros" begin + @gg Test1 = """ + . . . . + . . @ . + . @ . . + . @ . . + """ => """ + . . . . + . . . . + . . . . + . @ . . + """ + sl, sg, sp = source_graph(Test1()) + ml, mg, mp = mapped_graph(Test1()) + @show sg, collect(edges(sg)) + @test sl == UnitDiskMapping.SimpleNode.([(3, 2), (4,2), (2, 3)]) + @test sg == UnitDiskMapping.simplegraph([(1,2), (1,3)]) + @test sp == [2] + @test ml == [UnitDiskMapping.SimpleNode(4,2)] + @test mg == UnitDiskMapping.SimpleGraph(1) + @test mp == [1] end \ No newline at end of file