| Safe Haskell | None |
|---|---|
| Language | GHC2024 |
Circuit.Diff.Circuit
Description
Diff as a circuits arrow.
This module gives the instances that turn the differentiable carrier
Diff into a Category with tracing,
channels, strength, tensor products, and bimonoid structure. Keeping the
instances in the same package as the Diff type avoids the orphan
instances that would arise if 'circuits-ad' defined them for a carrier
living elsewhere.
Synopsis
- newtype Diff (p :: k) a b = Diff {
- runDiff :: a -> (b, b -> a)
- type Diff' = Diff ()
- traceNFrom :: forall {k} a (p :: k) b c. MergeZero (->) a => a -> Int -> Diff p (a, b) (a, c) -> Diff p b c
- traceStarFrom :: forall {k} j c (p :: k) b. (StarSemiring j, MergeZero (->) c) => j -> Int -> Diff p (j, b) (j, c) -> Diff p b c
- traceStar :: forall {k} (p :: k) j b c. Diff p (j, b) (j, c) -> Diff p b c
- quadD :: forall {k} (p :: k). Diff p Double Double
Re-exports from the carrier
newtype Diff (p :: k) a b Source #
A reverse-mode differentiable function tagged by a phantom type p.
The phantom tag prevents perturbation confusion: values of type
Diff p a b can only be composed with other Diff p values. Nested
AD introduces a fresh tag for each level.
runDiff f a returns a pair (b, pullback) where b = f a and pullback
maps a cotangent db on the output to a cotangent da on the input.
Constructors
| Diff | |
Fields
| |
Instances
| Channel Either (Diff p :: Type -> Type -> Type) Source # | Cocartesian channel plumbing for 'Diff. |
| Channel (,) (Diff p :: Type -> Type -> Type) Source # | Cartesian channel plumbing for 'Diff. |
| Strength Either (Diff p :: Type -> Type -> Type) Source # | Cocartesian tensorial strength for 'Diff. |
| Strength (,) (Diff p :: Type -> Type -> Type) Source # | Cartesian tensorial strength for 'Diff. |
Defined in Circuit.Diff.Circuit | |
| Traced Either (Diff p :: Type -> Type -> Type) Source # | Trace for 'Diff with the The The number of iterations is treated as locally constant by the derivative: small perturbations of the input do not change the branch sequence. This is the standard reverse-mode treatment of data-dependent control flow. Proof obligation (joins the linearity obligation on the other
traces): a cotangent on a sum is represented as the same sum, and
its tag must match the primal trajectory — the cotangent space at a
point of |
| Traced (,) (Diff p :: Type -> Type -> Type) Source # |
The forward pass ties the standard lazy knot: let (a, c) = body (a, b) in c The backward pass ties the same shape of knot, transposed. Given let bd = backward (fst bd, dc) in snd bd The knot flows through the pair rather than through the channel cotangent
alone —
|
Defined in Circuit.Diff.Circuit | |
| Action (,) (Diff p :: Type -> Type -> Type) Source # | |
Defined in Circuit.Diff.Circuit | |
| Tensor (,) (Diff p :: Type -> Type -> Type) Source # | |
| Unital (,) (Diff p :: Type -> Type -> Type) Source # | Monoidal product for Diff: independent wires, no additive constraint.
|
| Category (Diff p :: Type -> Type -> Type) Source # | 'Circuit.Diff still provides |
| Category (Diff p :: Type -> Type -> Type) Source # | |
| Zero (->) a => Discard (Diff p :: Type -> Type -> Type) (a :: Type) Source # | |
Defined in Circuit.Diff.Circuit | |
| Zero (->) a => Zero (Diff p :: Type -> Type -> Type) (a :: Type) Source # | |
Defined in Circuit.Diff.Circuit | |
| Merge (->) a => Copy (Diff p) a Source # | Copy in D: the pullback is
|
Defined in Circuit.Diff.Circuit | |
| Merge (->) a => Merge (Diff p) a Source # | Add in D: the pullback is
|
Defined in Circuit.Diff.Circuit | |
| Lit (Diff p Double Double) Source # | |
| (Num s, Num b) => Num (Diff p s b) Source # | Mechanical |
Defined in Circuit.Diff | |
| (Additive s, Additive b) => Additive (Diff p s b) Source # | Additive structure: sum rule.
|
| (Additive s, Subtractive s, Subtractive b) => Subtractive (Diff p s b) Source # | Subtractive structure: negation pushes through the pullback. |
| (ExpField b, Additive s, Subtractive s, Multiplicative b) => ExpField (Diff p s b) Source # | Exponential field: |
| (TrigField b, ExpField b, Additive s, Subtractive s, Multiplicative b, Divisive b) => TrigField (Diff p s b) Source # | Trigonometric field: the elementary transcendental family. |
Defined in Circuit.Diff Methods sin :: Diff p s b -> Diff p s b # cos :: Diff p s b -> Diff p s b # tan :: Diff p s b -> Diff p s b # asin :: Diff p s b -> Diff p s b # acos :: Diff p s b -> Diff p s b # atan :: Diff p s b -> Diff p s b # atan2 :: Diff p s b -> Diff p s b -> Diff p s b # sinh :: Diff p s b -> Diff p s b # cosh :: Diff p s b -> Diff p s b # tanh :: Diff p s b -> Diff p s b # asinh :: Diff p s b -> Diff p s b # | |
| (Additive s, Subtractive b, Multiplicative b, Divisive b) => Divisive (Diff p s b) Source # | Divisive structure: reciprocal rule. Division inherits the product rule via the default |
| (Additive s, Multiplicative b) => Multiplicative (Diff p s b) Source # | Multiplicative structure: product rule.
|
The untagged differentiable arrow. Existing code can continue to use
this; it is simply Diff ().
Traced variants
traceNFrom :: forall {k} a (p :: k) b c. MergeZero (->) a => a -> Int -> Diff p (a, b) (a, c) -> Diff p b c Source #
Iterated trace for strict carriers.
The lazy trace diverges on strict cotangent types (Double, etc.) when
the feedback channel has nonzero self-coupling (∂a_out/∂a_in ≠ 0).
traceNFrom replaces the lazy knot with truncated fixed-point iteration.
- Forward — iterate from caller-supplied seed
x0, N steps. There is no canonical seed for the forward pass (the fixpoint is arbitrary nonlinear), so the caller provides one. - Backward — iterate from
zero, N steps, extractsndonce. The backward equation is guaranteed affine (calculus promises linearity in cotangents), sozerois the principled seed. The Neumann summation happens inside the iteration — no double-counting, andplusretreats to where the theory says it lives: inside prims andCopy.
Lives beside the lawful-but-lazy instance, not replacing it.
Arguments
| :: forall {k} j c (p :: k) b. (StarSemiring j, MergeZero (->) c) | |
| => j | forward seed |
| -> Int | forward iteration count |
| -> Diff p (j, b) (j, c) | |
| -> Diff p b c |
Trace with a closed-form backward pass via the Kleene star.
The integer n in traceNFrom truncates a series on both passes.
But only the forward fixpoint is genuinely nonlinear; the backward
channel equation is affine — calculus promises linearity in
cotangents:
da = A·da + C·dc solution: da = star A · C·dc
with star a = one + a·star a — the Neumann series as algebra,
1/(1−a) over a field. Because the pullback is linear, the
blocks A and C·dc are extractable by probing:
backward (dj, dc) = (A·dj + C·dc, B·dj + D·dc) A = fst (backward (one, 0)) -- channel self-coupling C·dc = fst (backward (zero, dc))
and the trace's pullback is the Schur complement
D·dc + B·star A·C·dc, recovered with one more probe at the
backward fixpoint:
db = snd (backward (star A · C·dc, dc))
(Check: A·(star A·C·dc) + C·dc = (A·star A + one)·C·dc
= star A·C·dc — the star law discharges the fixpoint.)
So: forward still iterates from the caller's seed (no closed form
exists for an arbitrary nonlinear fixpoint), but the backward pass
is exact in three calls to backward — no Neumann index at all.
The star probe is computed once per forward point and shared
across all cotangents.
The closed form is the truncated iteration's limit; pure-Prelude
witness at a = 0.3, c = 2:
>>>let daIter = iterate (\d -> 0.3 * d + 2.0 * 1.0) 0 !! 200>>>abs (daIter - 1.0 / (1.0 - 0.3) * 2.0) < 1e-12True
Caveat: numhask declares StarSemiring but ships no
instances; the only carriers in the tower are
FieldStar (star a = recip (1−a)),
Warshall, and MinPlus. For bare Double channels and for vector
channels solved by starMatrix, see
Circuit.Diff.Star — the Schur-complement bridge proper.
Proof obligation: the probes assume the pullback is linear. Every honestly-constructed 'Diff primitive satisfies this (a pullback is a linear map); a primitive whose backward closure is affine-with-offset is a bug that this function will silently misread.
traceStar :: forall {k} (p :: k) j b c. Diff p (j, b) (j, c) -> Diff p b c Source #
Trace via the Kleene star — the execution formula, lazy form.
For a knot body with channel self-coupling block A and cross-blocks B, C, D, the trace is the Schur complement:
traceStar f = D + B · star A · C
The lazy trace instance for 'Diff computes exactly this via a
lazy fixpoint rather than closed form, so this alias is definable
without using star at all. Note that numhask ships no
StarSemiring instances, so for concrete carriers prefer
traceStarFrom (scalar channel, closed-form backward) or
Circuit.Diff.Star.traceStarMatrix (vector channel, solved by
starMatrix — the bridge made literal).
Smoke test
Orphan instances
| Channel Either (Diff p :: Type -> Type -> Type) Source # | Cocartesian channel plumbing for 'Diff. |
| Channel (,) (Diff p :: Type -> Type -> Type) Source # | Cartesian channel plumbing for 'Diff. |
| Strength Either (Diff p :: Type -> Type -> Type) Source # | Cocartesian tensorial strength for 'Diff. |
| Strength (,) (Diff p :: Type -> Type -> Type) Source # | Cartesian tensorial strength for 'Diff. |
| Traced Either (Diff p :: Type -> Type -> Type) Source # | Trace for 'Diff with the The The number of iterations is treated as locally constant by the derivative: small perturbations of the input do not change the branch sequence. This is the standard reverse-mode treatment of data-dependent control flow. Proof obligation (joins the linearity obligation on the other
traces): a cotangent on a sum is represented as the same sum, and
its tag must match the primal trajectory — the cotangent space at a
point of |
| Traced (,) (Diff p :: Type -> Type -> Type) Source # |
The forward pass ties the standard lazy knot: let (a, c) = body (a, b) in c The backward pass ties the same shape of knot, transposed. Given let bd = backward (fst bd, dc) in snd bd The knot flows through the pair rather than through the channel cotangent
alone —
|
| Action (,) (Diff p :: Type -> Type -> Type) Source # | |
| Tensor (,) (Diff p :: Type -> Type -> Type) Source # | |
| Unital (,) (Diff p :: Type -> Type -> Type) Source # | Monoidal product for Diff: independent wires, no additive constraint.
|
| Category (Diff p :: Type -> Type -> Type) Source # | 'Circuit.Diff still provides |
| Zero (->) a => Discard (Diff p :: Type -> Type -> Type) (a :: Type) Source # | |
| Zero (->) a => Zero (Diff p :: Type -> Type -> Type) (a :: Type) Source # | |
| Merge (->) a => Copy (Diff p) a Source # | Copy in D: the pullback is
|
| Merge (->) a => Merge (Diff p) a Source # | Add in D: the pullback is
|