para#
The category of parametric maps over a symmetric underlying category.
A parametric map from x to y with parameter space p is a morphism x @ p -> y in the underlying category. Composition tensors the parameters and tensor routes them to the right with a swap. Parametric maps first appeared in the study of supervised learning [FSTuyeras19].
Summary#
A parametric map from dom to cod with parameter space param is a morphism inside : dom @ param -> cod in an underlying category, optionally with a coparameter space copar on the codomain, i.e. inside : dom @ param -> cod @ copar. |
|
Parametric maps over a traced symmetric underlying category form a traced category, with the parameters swapped out of the way. |
|
Parametric maps over a Markov underlying category form a Markov category, with the copy of the underlying category as |
|
Parametric maps over a closed underlying category form a closed category, currying with the parameters swapped out of the way. |
|
Parametric maps over a feedback underlying category form a feedback category, with |
|
Parametric maps over a compact underlying category form a compact category, with the cups and caps of the underlying category. |
|
Parametric maps over a hypergraph underlying category form a hypergraph category, with the spiders of the underlying category. |
Axioms#
Composition tensors the parameter spaces:
>>> from discopy.symmetric import Ty, Box, Diagram
>>> x, y, z, w, p, q = map(Ty, "xyzwpq")
>>> f = Symmetric(x, y, Box('f', x @ p, y), p)
>>> g = Symmetric(y, z, Box('g', y @ q, z), q)
>>> assert (f >> g).param == p @ q
>>> assert (f >> g).inside == f.inside @ q >> g.inside
>>> (f >> g).inside.draw(doctest="docs/_static/para/then.svg")
So does the tensor, with a swap routing the parameters to the right:
>>> h = Symmetric(z, w, Box('h', z @ q, w), q)
>>> assert (f @ h).param == p @ q
>>> assert (f @ h).inside\
... == x @ Diagram.swap(z, p) @ q >> f.inside @ h.inside
>>> (f @ h).inside.draw(doctest="docs/_static/para/tensor.svg")
Reparametrisation precomposes the parameters, contravariantly:
>>> p_, p__ = Ty("p'"), Ty("p''")
>>> r, s = Box('r', p_, p), Box('s', p__, p_)
>>> assert f.reparam(r).param == p_
>>> assert f.reparam(s >> r) == f.reparam(r).reparam(s)
The identity and swap of Symmetric are those of the underlying
category, with the empty parameter space:
>>> assert Symmetric.id(x) == Symmetric.lift(Diagram.id(x))
>>> assert Symmetric.swap(x, y) == Symmetric.lift(Diagram.swap(x, y))
>>> t = Traced(x @ y, z @ y, Box('t', x @ y @ p, z @ y), p)
>>> assert t.trace().dom == x and t.trace().param == p
The construction preserves each level of the hierarchy below symmetric:
Traced, Markov, Closed, Feedback,
Compact and Hypergraph lift the extra structure of their
underlying category with the empty parameter space, e.g.
>>> from discopy import frobenius
>>> X = frobenius.Ty('x')
>>> assert Hypergraph.spiders(1, 2, X)\
... == Hypergraph.lift(frobenius.Diagram.spiders(1, 2, X))
while the operations on morphisms swap the parameters out of the way,
the same as Traced.trace():
>>> from discopy import closed
>>> a, b, c, P = map(closed.Ty, "abcP")
>>> k = Closed(a @ b, c, closed.Box('k', a @ b @ P, c), P)
>>> assert k.curry(left=True).cod == c << b
>>> assert k.curry(left=False).cod == a >> c
A map may also carry a coparameter space copar on the codomain, i.e.
inside : dom @ param -> cod @ copar, empty by default — the type of one
time step of a stateful morphism sequence [DLdeFeliceRoman22], i.e. of
a Stream with the delay forgotten.
Composition and tensor accumulate the hidden objects on both sides:
>>> m, n = Ty('m'), Ty('n')
>>> t = Symmetric(x, y, Box('t', x @ p, y @ m), p, m)
>>> u = Symmetric(y, z, Box('u', y @ q, z @ n), q, n)
>>> assert (t >> u).param == p @ q and (t >> u).copar == m @ n
>>> (t >> u).inside.draw(doctest="docs/_static/para/stateful-then.svg")
Coparametric maps, studied in categorical cybernetics [CGavranovicHR22], are the case of an empty param, composed by accumulating the coparameters in forward order:
>>> f_ = Symmetric(x, y, Box("f'", x, y @ m), copar=m)
>>> g_ = Symmetric(y, z, Box("g'", y, z @ n), copar=n)
>>> assert (f_ >> g_).copar == m @ n
>>> (f_ >> g_).inside.draw(doctest="docs/_static/para/copara-then.svg")
Recoparametrisation post-composes the coparameters, covariantly where
Symmetric.reparam() is contravariant:
>>> assert t.recopar(Box('c', m, n)).copar == n
and the diagonal param == copar is closed under composition: it is the free category with feedback of Katis et al. [KSW02].
>>> s = Ty('s')
>>> v = Symmetric(x, y, Box('v', x @ s, y @ s), s, s)
>>> w = Symmetric(y, z, Box('w', y @ s, z @ s), s, s)
>>> assert (v >> w).param == (v >> w).copar == s @ s
Example
Parametric maps compose like layers of a neural network, e.g. over
Function with weight and bias parameters:
>>> from discopy.python import Function
>>> layer = Symmetric[Function]((float, ), (float, ),
... Function(lambda x, w, b: w * x + b, (float, ) * 3, (float, )),
... param=(float, float))
>>> network = layer >> layer
>>> assert network.param == (float, ) * 4
>>> network.inside(2., 3., 1., .5, 0.)
3.5