Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

conegation rewriting #31

Merged
merged 4 commits into from
Oct 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Luxor = "ae8d54c2-7ccd-5906-9d76-62fc9837b5bc"
AlgebraicRewritingLuxorExt = "Luxor"

[compat]
ACSets = "0.2.3"
ACSets = "0.2.9"
Catlab = "0.15.4"
DataStructures = "0.17, 0.18"
Reexport = "^1"
Expand Down
3 changes: 2 additions & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ end
makedocs(
sitename = "AlgebraicRewriting",
format = Documenter.HTML(),
modules = [AlgebraicRewriting]
modules = [AlgebraicRewriting],
warnonly = true
)


Expand Down
2 changes: 0 additions & 2 deletions docs/src/full_demo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ This is a regular julia file that can be run interactively.
Importantly:
- use Julia 1.9 (not yet official released)
- ]activate the environment in AlgebraicRewriting.jl/docs
- check (via ]status) that Catlab branch "varacsets4" is used
- check that AlgebraicRewriting.jl branch "compat_varacsets" is used
- check that graphviz is installed locally (test via "which dot" in terminal)
Table of contents:
Expand Down
1 change: 1 addition & 0 deletions src/categorical_algebra/CSets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ end
function can_pushout_complement(pair::ComposablePair{<:ACSet})
S = acset_schema(dom(pair))
Ts = datatypes(dom(pair))
all(is_natural, pair) || error("Unnatural inputs")

all(can_pushout_complement, unpack_diagram(pair; S=S, Ts=Ts)) &&
isempty(dangling_condition(pair))
Expand Down
48 changes: 48 additions & 0 deletions src/rewrite/CoNeg.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
module CoNeg
using Catlab.CategoricalAlgebra

using ...CategoricalAlgebra.CSets
using ..Utils
import ..Utils: rewrite_match_maps
import Catlab.CategoricalAlgebra: Subobject

Subobject(X::ACSet, f::ACSetTransformation) =
Subobject(X; Dict([k=>collect(vs) for (k,vs) in pairs(components(f))])...)

""" rewrite_match_maps(r::Rule{:CoNeg}, m)
Apply a CoNegation rewrite rule (given as a span, L↩I->R) to a ACSet
using a monic match morphism `m` which indicates where to apply the rewrite.
l r
L <- I -> R
m ↓ ↓ ↓
G <- K -> H where K = ~L ∨ I

This works for any type that implements bi-Heyting logic operators ~ and ∨.

This is described [here](https://topos.site/blog/2023/04/conegation-rewriting).
Essentially, it is partway between DPO and SPO. Suppose the rule tries to delete
two things, one of which satisfies the dangling condition, the other violates it.
While DPO would fail to apply at all, and SPO would delete both things (cascading
the deletion for the latter), co-negation rewriting would simply delete the item
which can be deleted without cascading and ignore the other element.

It includes a quote which indicates that this method should work even when the
match morphism isn't monic, if it satisfies the identification condition.
Supporting this is not yet implemented.

Match morphisms which bind attribute variables are not monic, hence we this
form of rewriting doesn't support VarACSets. Intuitively, it feels like this
restriction could be relaxed.
"""
function rewrite_match_maps(r::Rule{:CoNeg}, m; check::Bool=false)
!check || is_monic(m) || error("Can only use CoNeg rewriting with monic matches: $m")
L = codom(m)
L′ = Subobject(L, m)
I′ = Subobject(L, compose(left(r), m))
K′ = ~L′ ∨ I′
ik = hom(Subobject(dom(hom(K′)), hom(I′)))
rh, kh = pushout(right(r), ik)
Dict(:ik => ik, :kg => hom(K′), :rh => rh, :kh => kh)
end

end # module
2 changes: 2 additions & 0 deletions src/rewrite/Rewrite.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ using Reexport
include("Constraints.jl")
include("Utils.jl")
include("DPO.jl")
include("CoNeg.jl")
include("SPO.jl")
include("SqPO.jl")
include("PBPO.jl")
Expand All @@ -14,6 +15,7 @@ include("Representable.jl")
@reexport using .Utils
@reexport using .Representable
@reexport using .DPO
@reexport using .CoNeg
@reexport using .SPO
@reexport using .SqPO
@reexport using .PBPO
Expand Down
7 changes: 4 additions & 3 deletions src/rewrite/Utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ The L structure encodes a pattern to be matched.
The R morphism encodes a replacement pattern to be substituted in.
They are related to each other by an interface I with maps: L ⟵ I ⟶ R

A semantics (DPO, SPO, or SqPO) must be chosen.
A semantics (DPO, SPO, CoNeg, or SqPO) must be chosen.

Control the match-finding process by specifying whether the match is
intended to be monic or not, as well as an optional application
Expand Down Expand Up @@ -94,11 +94,12 @@ right(r::Rule{T}) where T = r.R

# Extracting specific maps from rewriting output data
#####################################################
DPO′ = [:DPO, :CoNeg] # these have identical diagrams

"""Extract the map from the R to the result from the full output data"""
function get_rmap(sem::Symbol, maps)
if isnothing(maps) nothing
elseif sem == :DPO maps[:rh]
elseif sem DPO maps[:rh]
elseif sem == :SPO invert_hom(maps[:rmono], epic=false) ⋅ maps[:rmap]
elseif sem == :SqPO maps[:r]
elseif sem == :PBPO maps[:w]
Expand All @@ -111,7 +112,7 @@ get_result(sem::Symbol, maps) = codom(get_rmap(sem, maps))
"""Extract the partial map (derived rule) from full output data"""
function get_pmap(sem::Symbol, maps)
if isnothing(maps) nothing
elseif sem == :DPO Span(maps[:kg], maps[:kh])
elseif sem DPO Span(maps[:kg], maps[:kh])
elseif sem == :SPO Span(maps[:gmono], maps[:gmap])
elseif sem == :SqPO Span(maps[:i], maps[:o])
elseif sem == :PBPO Span(maps[:gl], maps[:gr])
Expand Down
18 changes: 18 additions & 0 deletions test/rewrite/CoNeg.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module TestCoNeg

using Test
using AlgebraicRewriting, Catlab

# Graphs
########
L = Graph(2)
R = ob(terminal(Graph))
r = Rule{:CoNeg}(create.([L,R])...)

G = path_graph(Graph, 2) Graph(1)
m = ACSetTransformation(L, G; V=[1,3]);

expected = path_graph(Graph, 2) R
@test is_isomorphic(expected, rewrite_match(r, m))

end # module
11 changes: 3 additions & 8 deletions test/rewrite/DPO.jl
Original file line number Diff line number Diff line change
Expand Up @@ -117,21 +117,16 @@ end
# Graphs with attributes
########################

@present TheoryDecGraph(FreeSchema) begin
E::Ob
V::Ob
src::Hom(E,V)
tgt::Hom(E,V)

@present SchDecGraph <: SchGraph begin
X::AttrType
dec::Attr(E,X)
end

@present TheoryLabeledDecGraph <: TheoryDecGraph begin
@present SchLabeledDecGraph <: SchDecGraph begin
label::Attr(V,X)
end

@acset_type LabeledDecGraph(TheoryLabeledDecGraph, index=[:src,:tgt])
@acset_type LabeledDecGraph(SchLabeledDecGraph, index=[:src,:tgt])

aI2 = @acset LabeledDecGraph{String} begin
V = 2; label = ["a","b"]
Expand Down
4 changes: 4 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ end
include("rewrite/DPO.jl")
end

@testset "CoNeg" begin
include("rewrite/CoNeg.jl")
end

@testset "SPO" begin
include("rewrite/SPO.jl")
end
Expand Down