Skip to content

Commit

Permalink
Apply collages to intermediate variables (#82)
Browse files Browse the repository at this point in the history
Co-authored-by: Matt <[email protected]>
  • Loading branch information
lukem12345 and quffaro authored Oct 16, 2024
1 parent 8e10fc5 commit e36dbd0
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 46 deletions.
49 changes: 31 additions & 18 deletions src/collages.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,17 @@ end

collate(c::Collage) = collate(c.src, c.tgt, c.uwd, c.symbols)

# TODO: This is assuming only "restriction"-type morphisms.
""" function collate(equations, boundaries, uwd, symbols)
Create a collage of two Decapodes that simulates with boundary conditions.
```
"""
function collate(equations, boundaries, uwd, symbols)
# TODO: This is assuming only "restriction"-type morphisms.

f = SummationDecapode{Any, Any, Symbol}()
# TODO: Double-check
copy_parts!(f, equations, (:Var, :TVar, :Op1, :Op2, , :Summand))

# TODO: Throw an error if the user tries to use a boundary value differential
# form that is of a different type of the thing that we are applying the bound
# to. i.e. Form1 but target is a Form0.

# TODO: This sets restrictions as Op1s. They are actually Op2s. i.e. Use `bv`.
for b in boxes(uwd)
# Set up pointers.
ps = incident(uwd, b, :box)
ev = first(ps)
bv = last(ps)
Expand All @@ -33,15 +26,35 @@ function collate(equations, boundaries, uwd, symbols)
en = symbols[en_key]
bn = symbols[bn_key]
var = only(incident(f, en, :name))
b_var = add_part!(f, :Var, type=f[var, :type], name=f[var, :name])
f[var, :name] = Symbol("r$(b)_" * string(f[var, :name]))
s_var = add_part!(f, :Var, type=boundaries[only(incident(boundaries, bn, :name)), :type], name=bn)
add_part!(f, :Op2, proj1=b_var, proj2=s_var, res=var, op2=uwd[b, :name])

# Update tangent variable pointers, if any.
tangent_op1s = filter(x -> f[x, :op1]==:∂ₜ, incident(f, var, :src))
isempty(tangent_op1s) && continue
f[only(tangent_op1s), :src] = b_var
en_type = equations[only(incident(equations, en, :name)), :type]
bn_type = boundaries[only(incident(boundaries, bn, :name)), :type]
if en_type != bn_type
error("Cannot use $(string(bn)) of type $(string(bn_type)) to bound $(string(en)) of type $(string(en_type)).")
end
# Add a new variable and transfer the children of the original variable to it.
b_var = add_part!(f, :Var, type=f[var, :type], name=Symbol("r$(b)_" * string(f[var, :name])))
transfer_children!(f, var, b_var)
# Transfer ∂ₜ morphisms back to the original variable, if any.
transferred_partials = filter(x -> f[x, :op1]==:∂ₜ, incident(f, b_var, :src))
if !isempty(transferred_partials)
f[only(transferred_partials), :src] = var
end
# Transfer ∂ₜ morphisms to the "masked"/ bounded variable, if any.
untransferred_partials = filter(x -> f[x, :op1]==:∂ₜ, incident(f, var, :tgt))
if !isempty(untransferred_partials)
f[untransferred_partials, :tgt] = b_var
end
# Insert the "masking" operation.
gettype(xs, n) = xs[only(incident(xs, n, :name)), :type]
newtype = @match (gettype(equations, en), gettype(boundaries, bn)) begin
(:infer, _) => :infer
(_, :infer) => :infer
(:Form0, :Constant) => :Form0
(x, y) && if x == y end => x
(x, y) => error("Type mismatch between $x and $y")
end
s_var = add_part!(f, :Var, type=newtype, name=bn)
add_part!(f, :Op2, proj1=var, proj2=s_var, res=b_var, op2=uwd[b, :name])
end

f
Expand Down
125 changes: 97 additions & 28 deletions test/collages.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,36 @@ using Catlab

# TODO: Add test with empty boundary Decapode.

# Note: Since the order does not matter in which rb1 and rb2 are applied, it
# seems informal to state that one goes before the other.
# It might be better to provide a semantics for incident edges a la:
#Diffusion = @Decapode begin
# C::Form0
# ∂ₜ(C) == rb3(∘(d,⋆,d,⋆)(rb1(C)))
# ∂ₜ(C) == rb3(∘(d,⋆,d,⋆)(rb2(C)))
#end
# Such a technique would preserve the technical definition of "collage".


# Test simple boundary masks.
DiffusionDynamics = @decapode begin
DiffusionDynamics = infer_types!(@decapode begin
K::Form0
∂ₜ(K) == (d,,d,)(K)
end
end)
DiffusionBoundaries = @decapode begin
(Kb1, Kb2, Null)::Form0
end

DiffusionMorphism = @relation () begin
rb1_leftwall(C, Cb1)
rb2_rightwall(C, Cb2)
rb3(Ċ, Zero)
end

DiffusionSymbols = Dict(
:C => :K,
:Ċ => :K̇,
:Cb1 => :Kb1,
:Cb2 => :Kb2,
:Zero => :Null)

DiffusionCollage = DiagrammaticEquations.collate(
DiffusionDynamics,
DiffusionBoundaries,
Expand All @@ -45,30 +53,91 @@ DiffusionCollage = DiagrammaticEquations.collate(
TVar = 1
Op1 = 2
Op2 = 3
src = [5, 1]
tgt = [2, 2]
proj1 = [3, 5, 7]
proj2 = [4, 6, 8]
res = [1, 3, 2]
incl = [2]
op1 = Any[:∂ₜ, [:d, :, :d, :]]
op2 = [:rb1_leftwall, :rb2_rightwall, :rb3]
type = [:Form0, :infer, :Form0, :Form0, :Form0, :Form0, :infer, :Form0]
name = [:r1_K, :r3_K̇, :r2_K, :Kb1, :K, :Kb2, :, :Null]
src = [1, 3]
tgt = [7, 2]
proj1 = [5, 1, 2]
proj2 = [4, 6, 8]
res = [3, 5, 7]
incl = [2]
op1 = Any[:∂ₜ, [:d, :, :d, :]]
op2 = [:rb1_leftwall, :rb2_rightwall, :rb3]
type = [:Form0, :Form0, :Form0, :Form0, :Form0, :Form0, :Form0, :Form0]
name = [:K, :, :r1_K, :Kb1, :r2_K, :Kb2, :r3_K̇, :Null]
end

# Note: Since the order does not matter in which rb1 and rb2 are applied, it
# seems informal to state that one goes before the other.
# It might be better to provide a semantics for incident edges a la:
#Diffusion = @Decapode begin
# C::Form0
# ∂ₜ(C) == rb3(∘(d,⋆,d,⋆)(rb1(C)))
# ∂ₜ(C) == rb3(∘(d,⋆,d,⋆)(rb2(C)))
#end
# Such a technique would preserve the technical definition of "collage".
# Test boundary condition applications on intermediate variables.
IntermediateDynamics = infer_types!(@decapode begin
K::Form0
J == Δ(K)
∂ₜ(K) == Δ(J)
end)
IntermediateBoundaries = @decapode begin
(Jb1)::Form0
end
IntermediateMorphism = @relation () begin
rb1_leftwall(C, Cb1)
end
IntermediateSymbols = Dict(
:C => :J,
:Cb1 => :Jb1)
IntermediateCollage = DiagrammaticEquations.collate(
IntermediateDynamics,
IntermediateBoundaries,
IntermediateMorphism,
IntermediateSymbols)
@test IntermediateCollage == @acset SummationDecapode{Any, Any, Symbol} begin
Var = 5
TVar = 1
Op1 = 3
Op2 = 1
src = [1, 1, 4]
tgt = [2, 3, 3]
proj1 = [2]
proj2 = [5]
res = [4]
incl = [3]
op1 = [, :∂ₜ, ]
op2 = [:rb1_leftwall]
type = [:Form0, :Form0, :Form0, :Form0, :Form0]
name = [:K, :J, :K̇, :r1_J, :Jb1]
end

# Test gensim on a collage.
c = Collage(DiffusionDynamics, DiffusionBoundaries,
DiffusionMorphism, DiffusionSymbols)
# Test twice-applied boundary condition applications on intermediate variables.
TwiceDynamics = infer_types!(@decapode begin
K::Form0
J == Δ(K)
∂ₜ(K) == Δ(J)
end)
TwiceBoundaries = @decapode begin
(Jb1, Jb2)::Form0
end
TwiceMorphism = @relation () begin
rb1_leftwall(C, Cb1)
rb2_rightwall(C, Cb2)
end
TwiceSymbols = Dict(
:C => :J,
:Cb1 => :Jb1,
:Cb2 => :Jb2)
TwiceCollage = DiagrammaticEquations.collate(
TwiceDynamics,
TwiceBoundaries,
TwiceMorphism,
TwiceSymbols)
@test TwiceCollage == @acset SummationDecapode{Any, Any, Symbol} begin
Var = 7
TVar = 1
Op1 = 3
Op2 = 2
src = [1, 1, 4]
tgt = [2, 3, 3]
proj1 = [6, 2]
proj2 = [5, 7]
res = [4, 6]
incl = [3]
op1 = [, :∂ₜ, ]
op2 = [:rb1_leftwall, :rb2_rightwall]
type = [:Form0, :Form0, :Form0, :Form0, :Form0, :Form0, :Form0]
name = [:K, :J, :K̇, :r1_J, :Jb1, :r2_J, :Jb2]
end

# @test gensim(c) == gensim(DiffusionCollage)

0 comments on commit e36dbd0

Please sign in to comment.