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

Visualize constraints #28

Merged
merged 1 commit 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
4 changes: 4 additions & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ Luxor = "ae8d54c2-7ccd-5906-9d76-62fc9837b5bc"
PrettyTables = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d"
Revise = "295af30f-e4ad-537b-8983-00126c2a3abe"
StructEquality = "6ec83bb0-ed9f-11e9-3b4c-2b04cb4e219c"

[compat]
ACSets = "0.2.6"
Catlab = "0.15.5"
21 changes: 0 additions & 21 deletions src/categorical_algebra/CSets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -376,27 +376,6 @@ function sub_vars(X::ACSet, subs::AbstractDict)
return first(legs(pushout(ox, oc)))
end

# """
# For any ACSet, X, a canonical map A→X where A has distinct variables for all
# subparts.
# """
# function abstract(X::StructACSet{S,Ts}; check::Bool=false) where {S,Ts}
# A = deepcopy(X);
# comps = Dict{Any,Any}(map(attrtypes(S)) do at
# rem_parts!(A, at, parts(A,at))
# comp = Union{AttrVar,attrtype_instantiation(S,Ts,at)}[]
# for (f, d, _) in attrs(S; to=at)
# append!(comp, A[f])
# A[f] = AttrVar.(add_parts!(A, at, nparts(A,d)))
# end
# at => comp
# end)
# for o in ob(S) comps[o]=parts(X,o) end
# res = ACSetTransformation(A,X; comps...)
# !check || is_natural(res) || error("bad abstract $comps")
# return res
# end


"""
Take an ACSet pullback combinatorially and freely add variables for all
Expand Down
100 changes: 91 additions & 9 deletions src/rewrite/Constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
export apply_constraint, Constraint, CGraph, arity,
∀, ∃, ∃!, True, False, Commutes,
AppCond, LiftCond, Trivial
using Catlab, Catlab.CategoricalAlgebra, Catlab.Graphs
using Catlab
import Catlab.Theories: ⊗, ⊕
import Catlab.CategoricalAlgebra: ¬
import Catlab.Graphics: to_graphviz
using StructEquality
import ...CategoricalAlgebra.CSets: Migrate

Expand Down Expand Up @@ -43,7 +44,7 @@
Ints are explicit arguments provided when apply_constraint is called
"""
const CGraph = VELabeledGraph{Union{Nothing, ACSet},
Union{Nothing,Int, ACSetTransformation}}
Union{Nothing, Int, ACSetTransformation}}

"""Number of variables in a constraint graph"""
arity(g::CGraph) = maximum(filter(v->v isa Int, g[:elabel]); init=0)
Expand Down Expand Up @@ -113,13 +114,15 @@
# Interpreter for boolean algebra
#################################
const Assgn = Vector{Union{Nothing, <:ACSetTransformation}} # partial assignment
paren(x) = "($x)"

"""Something that, in a context, can be evaluated to a bool"""
abstract type BoolExpr end
check_expr(::CGraph, ::BoolExpr) = error("Method undefined")
bound(::BoolExpr) = error("Method undefined")
eval_boolexpr(::BoolExpr, ::CGraph, ::Assgn) = error("Method undefined")
map_edges(f,c::BoolExpr) = error("Method undefined")
subexprs(::BoolExpr) = error("Method undefined")

Check warning on line 125 in src/rewrite/Constraints.jl

View check run for this annotation

Codecov / codecov/patch

src/rewrite/Constraints.jl#L125

Added line #L125 was not covered by tests

"""
A commutative diagram with multiple parallel paths, asserted to either
Expand All @@ -134,10 +137,17 @@
end
end

subexprs(::Commutes) = BoolExpr[]
Base.show(io::IO, c::Commutes) =
join(io, [join(p, "⋅") for p in c.pths], c.commutes ? " = " : " ≠ ")

"""Constant, independent of context"""
@struct_hash_equal struct BoolConst <: BoolExpr
val::Bool
end
Base.show(io::IO, c::BoolConst) = print(io, c.val ? "⊤" : "⊥")

Check warning on line 148 in src/rewrite/Constraints.jl

View check run for this annotation

Codecov / codecov/patch

src/rewrite/Constraints.jl#L148

Added line #L148 was not covered by tests
subexprs(::BoolConst) = BoolExpr[]

const True = BoolConst(true)
const False = BoolConst(false)

Expand All @@ -159,6 +169,27 @@
k ∈ [:Exists,:Forall,:Exists!] ? new(e,k,x,st,monic) : error("$k not supported")
end

quantifier_symbol(c::Quantifier) = if c.kind == :Exists "∃"
elseif c.kind == :Forall "∀"
elseif c.kind == :Exists! "∃!"

Check warning on line 174 in src/rewrite/Constraints.jl

View check run for this annotation

Codecov / codecov/patch

src/rewrite/Constraints.jl#L174

Added line #L174 was not covered by tests
end

function Base.show(io::IO, c::Quantifier)
q = quantifier_symbol(c)
m = if c.monic isa AbstractVector
"monic{$(join(c.monic, ","))}, "

Check warning on line 180 in src/rewrite/Constraints.jl

View check run for this annotation

Codecov / codecov/patch

src/rewrite/Constraints.jl#L180

Added line #L180 was not covered by tests
elseif c.monic
"monic, "
else
""
end
st = c.st == True ? "" : paren(c.st)
mst = isempty(m*st) ? "" : " $(paren(m*st))"
x = c.expr == True && c.kind ∈ [:Exists, :Exists!] ? "" : ": $(paren(c.expr))"
print(io, join([q,c.e,mst,x]))
end
subexprs(q::Quantifier) = [q.expr, q.st]

Exists!(e,x;st=True,monic=false) = Quantifier(e,:Exists,x;st=st,monic=monic)
Forall(e,x;st=True,monic=false) = Quantifier(e,:Forall,x;st=st,monic=monic)
Exists(e,x;st=True,monic=false) = Quantifier(e,:Exists,x;st=st,monic=monic)
Expand All @@ -169,23 +200,42 @@
"""Disjunction of multiple expressions"""
@struct_hash_equal struct BoolOr <: BoolExpr
exprs::Vector{BoolExpr}
BoolOr(x...) = new(collect(x))
end
BoolOr(x...) = new(collect(filter(!=(False),x)))
end

Base.show(io::IO, b::BoolOr) =
if isempty(b.exprs)
print(io, False)

Check warning on line 208 in src/rewrite/Constraints.jl

View check run for this annotation

Codecov / codecov/patch

src/rewrite/Constraints.jl#L208

Added line #L208 was not covered by tests
else
join(io, paren.(b.exprs), " ∨ ")
end

subexprs(b::BoolOr) = b.exprs

⊕(xs::BoolExpr...) = BoolOr(xs...)

"""Conjunction of multiple expressions"""
@struct_hash_equal struct BoolAnd <: BoolExpr
exprs::Vector{BoolExpr}
BoolAnd(x...) = new(collect(x))
end
BoolAnd(x...) = new(collect(filter(!=(True),x)))
end

Base.show(io::IO, b::BoolAnd) =
if isempty(b.exprs)
show(io, True)

Check warning on line 225 in src/rewrite/Constraints.jl

View check run for this annotation

Codecov / codecov/patch

src/rewrite/Constraints.jl#L225

Added line #L225 was not covered by tests
else
join(io, paren.(b.exprs), " ∧ ")
end
subexprs(b::BoolAnd) = b.exprs

Check warning on line 229 in src/rewrite/Constraints.jl

View check run for this annotation

Codecov / codecov/patch

src/rewrite/Constraints.jl#L229

Added line #L229 was not covered by tests

⊗(xs::BoolExpr...) = BoolAnd(xs...)

"""Negation of an expression"""
@struct_hash_equal struct BoolNot <: BoolExpr
expr::BoolExpr
end
subexprs(b::BoolNot) = [b.expr]

Check warning on line 237 in src/rewrite/Constraints.jl

View check run for this annotation

Codecov / codecov/patch

src/rewrite/Constraints.jl#L237

Added line #L237 was not covered by tests
Base.show(io::IO, b::BoolNot) = print(io, "¬$(b.expr isa Quantifier ? b.expr : paren(b.expr))")

¬(x::BoolExpr) = BoolNot(x)

Expand Down Expand Up @@ -340,10 +390,11 @@
end
new_g = merge_graphs(c1.g, c2.g)
l1, l2 = legs(new_g)
Constraint(apex(new_g), map_edges(l1,c1.d) ⊕ map_edges(l2,c2.d))
new_d = map_edges(l1,c1.d) ⊕ map_edges(l2,c2.d)
Constraint(apex(new_g), new_d)
end

⊕(cs::Constraint...) = reduce(, cs; init=Trivial′)
⊕(cs::Constraint...) = reduce(, cs; init=Trivial′)

Check warning on line 397 in src/rewrite/Constraints.jl

View check run for this annotation

Codecov / codecov/patch

src/rewrite/Constraints.jl#L397

Added line #L397 was not covered by tests

¬(c::Constraint) = Constraint(c.g, ¬c.d)

Expand Down Expand Up @@ -421,4 +472,35 @@
return Constraint(cg, expr)
end

end
# Visualize constraints
#######################

function to_graphviz(c::Constraint)
pg = to_graphviz_property_graph(c.g; node_labels=true,
graph_attrs=Dict(:label=>sprint(show, c.d)))

for v in vertices(c.g)
x = isnothing(c.g[v, :vlabel]) ? "λ" : ""
set_vprop!(pg, v, :label, "$x$v")
end

for e in edges(c.g)
x = if c.g[e, :elabel] isa Int "(λ$(c.g[e, :elabel]))"
elseif isnothing(c.g[e, :elabel]) getquantifier(c.d, e)
else ""
end
set_eprop!(pg, e, :label, "$x$e")
end
to_graphviz(pg)
end

function getquantifier(d::BoolExpr, e::Int)
if d isa Quantifier && d.e == e
quantifier_symbol(d)
else
qs = collect(filter(!isnothing, getquantifier.(subexprs(d), Ref(e))))
isempty(qs) ? nothing : only(qs)
end
end

end # module
4 changes: 4 additions & 0 deletions test/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Revise = "295af30f-e4ad-537b-8983-00126c2a3abe"
StructEquality = "6ec83bb0-ed9f-11e9-3b4c-2b04cb4e219c"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[compat]
ACSets = "0.2.6"
Catlab = "0.15.5"
6 changes: 5 additions & 1 deletion test/rewrite/Constraints.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module TestConstraints
using AlgebraicRewriting
using Catlab, Catlab.CategoricalAlgebra, Catlab.Graphs
using Catlab
using Test

# Testing arity 2 constraints
Expand All @@ -12,6 +12,7 @@ c = Constraint(cg, ∃(3, Commutes([1],[2,3])))
h1, hid, hnot, _ = homomorphisms(Graph.([2,2])...)
@test !apply_constraint(c, hid, h1)
@test apply_constraint(c, hid, hnot)
@test arity(c) == 2

# AppCond
#########
Expand All @@ -33,6 +34,7 @@ f = homomorphism(p2, G)

@test apply_constraint(constr, f)
@test !apply_constraint(constr, id(p2))
to_graphviz(constr)

# LiftCond
##########
Expand Down Expand Up @@ -78,6 +80,7 @@ h1,h2,h3,h4 = homomorphisms(G, loop_csp; initial=(V=Dict(1=>1),))
@test !apply_constraint(constr,h2)
@test !apply_constraint(constr,h3)
@test apply_constraint(constr,h4)
to_graphviz(constr)

# Combining constraints
#######################
Expand All @@ -89,5 +92,6 @@ three_loops = @acset Graph begin V=1; E=3; src=1; tgt=1 end
c2 = AppCond(homomorphism(Graph(1), two_loops); monic=true)
c3 = AppCond(homomorphism(Graph(1), three_loops); monic=true)
constr = c2 ⊕ c3
to_graphviz(constr)

end # module