| Copyright | (c) Justin Le 2017 |
|---|---|
| License | BSD3 |
| Maintainer | justin@jle.im |
| Stability | experimental |
| Portability | non-portable |
| Safe Haskell | None |
| Language | Haskell2010 |
Numeric.Backprop.Implicit
Description
Offers full functionality for implicit-graph back-propagation. The
intended usage is to write a BPOp, which is a normal Haskell
function from BVars to a result BVar. These BVars can be
manipulated using their Num / Fractional / Floating instances.
The library can then perform back-propagation on the function (using
backprop or grad) by using an implicitly built graph.
This should actually be powerful enough for most use cases, but falls short for a couple of situations:
- If the result of a function on
BVars is used twice (likezinlet z = x * y in z + z), this will allocate a new redundant graph node for every usage site ofz. You can explicitly forcez, but only using an explicit graph description using Numeric.Backprop. - This can't handle sum types, like Numeric.Backprop can. You can
never pattern match on the constructors of a value inside a
BVar. I'm not sure if this is a fundamental limitation (I suspect it might be) or if I just can't figure out how to implement it. Suggestions welcome!
As a comparison, this module offers functionality and an API very similar to Numeric.AD.Mode.Reverse from the ad library, except for the fact that it can handle heterogeneous values.
- type BPOp rs a = forall s. Prod (BVar s rs) rs -> BVar s rs a
- data BVar :: Type -> [Type] -> Type -> Type
- type Op as a = forall m. Monad m => OpM m as a
- type OpB s as a = OpM (ST s) as a
- data Prod k f a :: forall k. (k -> *) -> [k] -> * where
- type Tuple = Prod * I
- newtype I a :: * -> * = I {
- getI :: a
- backprop :: (Known Length rs, Every Num rs, Num a) => BPOp rs a -> Tuple rs -> (a, Tuple rs)
- grad :: (Known Length rs, Every Num rs, Num a) => BPOp rs a -> Tuple rs -> Tuple rs
- eval :: (Known Length rs, Every Num rs, Num a) => BPOp rs a -> Tuple rs -> a
- backprop' :: Prod Summer rs -> Prod Unity rs -> BPOp rs a -> Tuple rs -> (a, Tuple rs)
- grad' :: Prod Summer rs -> Prod Unity rs -> BPOp rs a -> Tuple rs -> Tuple rs
- constVar :: a -> BVar s rs a
- liftB :: OpB s as a -> Prod (BVar s rs) as -> BVar s rs a
- (.$) :: OpB s as a -> Prod (BVar s rs) as -> BVar s rs a
- liftB1 :: OpB s '[a] b -> BVar s rs a -> BVar s rs b
- liftB2 :: OpB s '[a, b] c -> BVar s rs a -> BVar s rs b -> BVar s rs c
- liftB3 :: OpB s '[a, b, c] d -> BVar s rs a -> BVar s rs b -> BVar s rs c -> BVar s rs d
- partsVar :: forall s rs bs a. (Known Length bs, Every Num bs) => Iso' a (Tuple bs) -> BVar s rs a -> Prod (BVar s rs) bs
- withParts :: forall s rs bs a r. (Known Length bs, Every Num bs) => Iso' a (Tuple bs) -> BVar s rs a -> (Prod (BVar s rs) bs -> r) -> r
- splitVars :: forall s rs as. (Known Length as, Every Num as) => BVar s rs (Tuple as) -> Prod (BVar s rs) as
- gSplit :: forall s rs as a. (Generic a, Code a ~ '[as], Known Length as, Every Num as) => BVar s rs a -> Prod (BVar s rs) as
- gTuple :: (Generic a, Code a ~ '[as]) => Iso' a (Tuple as)
- partsVar' :: forall s rs bs a. Prod Summer bs -> Prod Unity bs -> Iso' a (Tuple bs) -> BVar s rs a -> Prod (BVar s rs) bs
- withParts' :: forall s rs bs a r. Prod Summer bs -> Prod Unity bs -> Iso' a (Tuple bs) -> BVar s rs a -> (Prod (BVar s rs) bs -> r) -> r
- splitVars' :: forall s rs as. Prod Summer as -> Prod Unity as -> BVar s rs (Tuple as) -> Prod (BVar s rs) as
- gSplit' :: forall s rs as a. (Generic a, Code a ~ '[as]) => Prod Summer as -> Prod Unity as -> BVar s rs a -> Prod (BVar s rs) as
- op1 :: Num a => (forall s. AD s (Forward a) -> AD s (Forward a)) -> Op '[a] a
- op2 :: Num a => (forall s. Reifies s Tape => Reverse s a -> Reverse s a -> Reverse s a) -> Op '[a, a] a
- op3 :: Num a => (forall s. Reifies s Tape => Reverse s a -> Reverse s a -> Reverse s a -> Reverse s a) -> Op '[a, a, a] a
- opN :: (Num a, Known Nat n) => (forall s. Reifies s Tape => Vec n (Reverse s a) -> Reverse s a) -> Op (Replicate n a) a
- op1' :: (a -> (b, Maybe b -> a)) -> Op '[a] b
- op2' :: (a -> b -> (c, Maybe c -> (a, b))) -> Op '[a, b] c
- op3' :: (a -> b -> c -> (d, Maybe d -> (a, b, c))) -> Op '[a, b, c] d
- pattern (:>) :: forall k f a b. f a -> f b -> Prod k f ((:) k a ((:) k b ([] k)))
- only :: f a -> Prod k f ((:) k a ([] k))
- head' :: Prod k f ((:<) k a as) -> f a
- pattern (::<) :: forall a as. a -> Tuple as -> Tuple ((:<) * a as)
- only_ :: a -> Tuple ((:) * a ([] *))
- newtype Summer a = Summer {
- runSummer :: [a] -> a
- newtype Unity a = Unity {
- getUnity :: a
- summers :: (Every Num as, Known Length as) => Prod Summer as
- unities :: (Every Num as, Known Length as) => Prod Unity as
- summers' :: Every Num as => Length as -> Prod Summer as
- unities' :: Every Num as => Length as -> Prod Unity as
Types
Backprop types
type BPOp rs a = forall s. Prod (BVar s rs) rs -> BVar s rs a Source #
An operation on BVars that can be backpropagated. A value of type:
BPOp rs a
takes a bunch of BVars containg rs and uses them to (purely) produce
a BVar containing an a.
foo ::BPOp'[ Double, Double ] Double foo (x:<y:<'Ø') = x + sqrt y
BPOp here is related to BPOpI from the normal
explicit-graph backprop module Numeric.Backprop.
data BVar :: Type -> [Type] -> Type -> Type Source #
The basic unit of manipulation inside BP (or inside an
implicit-graph backprop function). Instead of directly working with
values, you work with BVars contating those values. When you work
with a BVar, the backprop library can keep track of what values
refer to which other values, and so can perform back-propagation to
compute gradients.
A refers to a value of type BVar s rs aa, with an environment
of values of the types rs. The phantom parameter s is used to
ensure that stray BVars don't leak outside of the backprop process.
(That is, if you're using implicit backprop, it ensures that you interact
with BVars in a polymorphic way. And, if you're using explicit
backprop, it ensures that a never leaves the BVar s rs a
that it was created in.)BP s rs
BVars have Num, Fractional, Floating, etc. instances, so they
can be manipulated using polymorphic functions and numeric functions in
Haskell. You can add them, subtract them, etc., in "implicit" backprop
style.
(However, note that if you directly manipulate BVars using those
instances or using liftB, it delays evaluation, so every usage site
has to re-compute the result/create a new node. If you want to re-use
a BVar you created using + or - or liftB, use
bindVar to force it first. See documentation for
bindVar for more details.)
Instances
| Floating a => Floating (BVar s rs a) Source # | See note for |
| Fractional a => Fractional (BVar s rs a) Source # | See note for |
| Num a => Num (BVar s rs a) Source # | Note that if you use the |
type Op as a = forall m. Monad m => OpM m as a Source #
An describes a differentiable function from Op as aas to a.
For example, a value of type
Op '[Int, Bool] Double
is a function from an Int and a Bool, returning a Double. It can
be differentiated to give a gradient of an Int and a Bool if given
a total derivative for the Double. If we call Bool \(2\), then,
mathematically, it is akin to a:
\[ f : \mathbb{Z} \times 2 \rightarrow \mathbb{R} \]
See runOp, gradOp, and gradOpWith for examples on how to run it,
and Op for instructions on creating it.
This type is abstracted over using the pattern synonym with constructor
Op, so you can create one from scratch with it. However, it's
simplest to create it using op2', op1', op2', and op3' helper
smart constructors And, if your function is a numeric function, they
can even be created automatically using op1, op2, op3, and opN
with a little help from Numeric.AD from the ad library.
Note that this type is a subset or subtype of OpM (and also of
OpB). So, if a function ever expects an (or a OpM m as
aOpB), you can always provide an
instead.Op as a
Many functions in this library will expect an (or
an OpM m as a), and in all of these cases, you can
provide an OpB s as a.Op as a
type OpB s as a = OpM (ST s) as a Source #
A subclass of OpM (and superclass of Op), representing Ops that
the backprop library uses to perform backpropation.
An
OpB s rs a
represents a differentiable function that takes a tuple of rs and
produces an a a, which can be run on s and also inside BVar ss. For example, an BP
s takes an OpB s '[ Int, Double ] BoolInt and
a Double and produces a Bool, and does it in a differentiable way.
OpB is a superset of Op, so, if you see any function
that expects an OpB (like opVar' and
~$, for example), you can give them an Op, as well.
You can think of OpB as a superclass/parent class of Op in this
sense, and of Op as a subclass of OpB.
Tuple types
See Numeric.Backprop for a mini-tutorial on Prod and
Tuple
data Prod k f a :: forall k. (k -> *) -> [k] -> * where #
Instances
| Witness ØC ØC (Prod k f (Ø k)) | |
| IxFunctor1 k [k] (Index k) (Prod k) | |
| IxFoldable1 k [k] (Index k) (Prod k) | |
| IxTraversable1 k [k] (Index k) (Prod k) | |
| Functor1 [k] k (Prod k) | |
| Foldable1 [k] k (Prod k) | |
| Traversable1 [k] k (Prod k) | |
| TestEquality k f => TestEquality [k] (Prod k f) | |
| BoolEquality k f => BoolEquality [k] (Prod k f) | |
| Eq1 k f => Eq1 [k] (Prod k f) | |
| Ord1 k f => Ord1 [k] (Prod k f) | |
| Show1 k f => Show1 [k] (Prod k f) | |
| Read1 k f => Read1 [k] (Prod k f) | |
| (Known [k] (Length k) as, Every k (Known k f) as) => Known [k] (Prod k f) as | |
| (Witness p q (f a1), Witness s t (Prod a f as)) => Witness (p, s) (q, t) (Prod a f ((:<) a a1 as)) | |
| ListC ((<$>) Constraint * Eq ((<$>) * k f as)) => Eq (Prod k f as) | |
| (ListC ((<$>) Constraint * Eq ((<$>) * k f as)), ListC ((<$>) Constraint * Ord ((<$>) * k f as))) => Ord (Prod k f as) | |
| ListC ((<$>) Constraint * Show ((<$>) * k f as)) => Show (Prod k f as) | |
| type WitnessC ØC ØC (Prod k f (Ø k)) | |
| type KnownC [k] (Prod k f) as | |
| type WitnessC (p, s) (q, t) (Prod a f ((:<) a a1 as)) | |
back-propagation
backprop :: (Known Length rs, Every Num rs, Num a) => BPOp rs a -> Tuple rs -> (a, Tuple rs) Source #
Var manipulation
liftB :: OpB s as a -> Prod (BVar s rs) as -> BVar s rs a Source #
Apply OpB over a Prod of BVars, as inputs. Provides
"implicit-graph" back-propagation, with deferred evaluation.
If you had an , this function will expect a 3-Prod
of a OpB s '[a, b, c] d, a BVar s rs a, and a BVar s rs b, and the
result will be a BVar s rs c:BVar s rs d
myOp ::OpBs '[a, b, c] d x ::BVars rs a y ::BVars rs b z ::BVars rs c x :< y :< z :< Ø ::Prod(BVars rs) '[a, b, c]liftBmyOp (x :< y :< z :< Ø) ::BVars rs d
Note that OpB is a superclass of Op, so you can provide any Op
here, as well (like those created by op1, op2, constOp, op0
etc.)
liftB has an infix alias, .$, so the above example can also be
written as:
myOp.$(x :< y :< z :< Ø) ::BVars rs d
to let you pretend that you're applying the myOp function to three
inputs.
The result is a new deferred BVar. This should be fine in most
cases, unless you use the result in more than one location. This will
cause evaluation to be duplicated and multiple redundant graph nodes to
be created. If you need to use it in two locations, you should use
opVar instead of liftB, or use bindVar:
opVaro xs =bindVar(liftBo xs)
liftB can be thought of as a "deferred evaluation" version of opVar.
(.$) :: OpB s as a -> Prod (BVar s rs) as -> BVar s rs a infixr 5 Source #
Infix synonym for liftB, which lets you pretend that you're applying
OpBs as if they were functions:
myOp ::OpBs '[a, b, c] d x ::BVars rs a y ::BVars rs b z ::BVars rs c x :< y :< z :< Ø ::Prod(BVars rs) '[a, b, c] myOp.$(x :< y :< z :< Ø) ::BVars rs d
Note that OpB is a superclass of Op, so you can pass in any Op
here, as well (like those created by op1, op2, constOp, op0
etc.)
See the documentation for liftB for all the caveats of this usage.
.$ can also be thought of as a "deferred evaluation" version of ~$:
o~$xs =bindVar(o.$xs)
liftB1 :: OpB s '[a] b -> BVar s rs a -> BVar s rs b Source #
Convenient wrapper over liftB that takes an OpB with one argument
and a single BVar argument. Lets you not have to type out the entire
Prod.
liftB1o x =liftBo (x:<'Ø') myOp ::Op'[a] b x ::BVars rs aliftB1myOp x ::BVars rs b
Note that OpB is a superclass of Op, so you can pass in an Op here
(like one made with op1) as well.
See the documentation for liftB for caveats and potential problematic
situations with this.
liftB2 :: OpB s '[a, b] c -> BVar s rs a -> BVar s rs b -> BVar s rs c Source #
Convenient wrapper over liftB that takes an OpB with two arguments
and two BVar arguments. Lets you not have to type out the entire
Prod.
liftB2o x y =liftBo (x:<y:<'Ø') myOp ::Op'[a, b] c x ::BVars rs a y ::BVars rs bliftB2myOp x y ::BVars rs c
Note that OpB is a superclass of Op, so you can pass in an Op here
(like one made with op2) as well.
See the documentation for liftB for caveats and potential problematic
situations with this.
liftB3 :: OpB s '[a, b, c] d -> BVar s rs a -> BVar s rs b -> BVar s rs c -> BVar s rs d Source #
Convenient wrapper over liftB that takes an OpB with three arguments
and three BVar arguments. Lets you not have to type out the entire
Prod.
liftB3o x y z =liftBo (x:<y:<z:<'Ø') myOp ::Op'[a, b, c] d x ::BVars rs a y ::BVars rs b z ::BVars rs cliftB3myOp x y z ::BVars rs d
Note that OpB is a superclass of Op, so you can pass in an Op here
(like one made with op3) as well.
See the documentation for liftB for caveats and potential problematic
situations with this.
As Parts
partsVar :: forall s rs bs a. (Known Length bs, Every Num bs) => Iso' a (Tuple bs) -> BVar s rs a -> Prod (BVar s rs) bs Source #
Use an Iso (or compatible Iso from the lens
library) to "pull out" the parts of a data type and work with each part
as a BVar.
If there is an isomorphism between a b and a (that is, if
an Tuple asa is just a container for a bunch of as), then it lets you break
out the as inside and work with those.
data Foo = F Int Bool fooIso ::Iso'Foo (Tuple '[Int, Bool]) fooIso =iso(\(F i b) -> i ::< b ::< Ø) (\(i ::< b ::< Ø) -> F i b )partsVarfooIso ::BVarrs Foo ->Prod(BVars rs) '[Int, Bool] stuff ::BPOps '[Foo] a stuff (foo :< Ø) = casepartsVarfooIso foo of i ::< Ø - -- now, i is aBVarpointing to theIntinside foo -- and b is aBVarpointing to theBoolinside foo -- you can do stuff with the i and b here
You can use this to pass in product types as the environment to a BP,
and then break out the type into its constituent products.
Note that for a type like Foo, fooIso can be generated automatically
with Generic from GHC.Generics and
Generic from Generics.SOP and generics-sop, using the
gTuple iso. See gSplit for more information.
Also, if you are literally passing a tuple (like
) then you can give in the identity
isomorphism (BP s '[Tuple '[Int, Bool]id) or use splitVars.
At the moment, this implicit partsVar is less efficient than the
explicit partsVar, but this might change in the
future.
withParts :: forall s rs bs a r. (Known Length bs, Every Num bs) => Iso' a (Tuple bs) -> BVar s rs a -> (Prod (BVar s rs) bs -> r) -> r Source #
A continuation-based version of partsVar. Instead of binding the
parts and using it in the rest of the block, provide a continuation to
handle do stuff with the parts inside.
Building on the example from partsVar:
data Foo = F Int Bool fooIso ::Iso'Foo (Tuple '[Int, Bool]) fooIso =iso(\(F i b) -> i ::< b ::< Ø) (\(i ::< b ::< Ø) -> F i b ) stuff ::BPOps '[Foo] a stuff (foo :< Ø) =withPartsfooIso foo $ \case i :< b :< Ø -> -- now, i is aBVarpointing to theIntinside foo -- and b is aBVarpointing to theBoolinside foo -- you can do stuff with the i and b here
Mostly just a stylistic alternative to partsVar.
splitVars :: forall s rs as. (Known Length as, Every Num as) => BVar s rs (Tuple as) -> Prod (BVar s rs) as Source #
Split out a BVar of a tuple into a tuple (Prod) of BVars.
-- the environment is a single Int-Bool tuple, tup stuff ::BPOps '[ Tuple '[Int, Bool] ] a stuff (tup :< Ø) = casesplitVartup of i :< b :< Ø <-splitVarstup -- now, i is aBVarpointing to theIntinside tup -- and b is aBVarpointing to theBoolinside tup -- you can do stuff with the i and b here
Note that
splitVars=partsVarid
gSplit :: forall s rs as a. (Generic a, Code a ~ '[as], Known Length as, Every Num as) => BVar s rs a -> Prod (BVar s rs) as Source #
Using Generic from GHC.Generics and
Generic from Generics.SOP, split a BVar containing
a product type into a tuple (Prod) of BVars pointing to each value
inside.
Building on the example from partsVar:
import qualified Generics.SOP as SOP data Foo = F Int Bool deriving Generic instance SOP.Generic FoogSplit::BVarrs Foo ->Prod(BVars rs) '[Int, Bool] stuff ::BPOps '[Foo] a stuff (foo :< Ø) = casegSplitfoo of i ::< Ø - -- now, i is aBVarpointing to theIntinside foo -- and b is aBVarpointing to theBoolinside foo -- you can do stuff with the i and b here
Because Foo is a straight up product type, gSplit can use
GHC.Generics and take out the items inside.
Note that
gSplit=splitVarsgTuple
gTuple :: (Generic a, Code a ~ '[as]) => Iso' a (Tuple as) Source #
An Iso between a type that is a product type, and a tuple that
contains all of its components. Uses Generics.SOP and the
Generic typeclass.
>>>import qualified Generics.SOP as SOP>>>data Foo = A Int Bool deriving Generic>>>instance SOP.Generic Foo>>>view gTuple (A 10 True)10 ::< True ::< Ø>>>review gTuple (15 ::< False ::< Ø)A 15 False
partsVar' :: forall s rs bs a. Prod Summer bs -> Prod Unity bs -> Iso' a (Tuple bs) -> BVar s rs a -> Prod (BVar s rs) bs Source #
withParts' :: forall s rs bs a r. Prod Summer bs -> Prod Unity bs -> Iso' a (Tuple bs) -> BVar s rs a -> (Prod (BVar s rs) bs -> r) -> r Source #
splitVars' :: forall s rs as. Prod Summer as -> Prod Unity as -> BVar s rs (Tuple as) -> Prod (BVar s rs) as Source #
gSplit' :: forall s rs as a. (Generic a, Code a ~ '[as]) => Prod Summer as -> Prod Unity as -> BVar s rs a -> Prod (BVar s rs) as Source #
Op
op2 :: Num a => (forall s. Reifies s Tape => Reverse s a -> Reverse s a -> Reverse s a) -> Op '[a, a] a Source #
op3 :: Num a => (forall s. Reifies s Tape => Reverse s a -> Reverse s a -> Reverse s a -> Reverse s a) -> Op '[a, a, a] a Source #
opN :: (Num a, Known Nat n) => (forall s. Reifies s Tape => Vec n (Reverse s a) -> Reverse s a) -> Op (Replicate n a) a Source #
op1' :: (a -> (b, Maybe b -> a)) -> Op '[a] b Source #
Create an Op of a function taking one input, by giving its explicit
derivative. The function should return a tuple containing the result of
the function, and also a function taking the derivative of the result
and return the derivative of the input.
If we have
\[ \eqalign{ f &: \mathbb{R} \rightarrow \mathbb{R}\cr y &= f(x)\cr z &= g(y) } \]
Then the derivative \( \frac{dz}{dx} \), it would be:
\[ \frac{dz}{dx} = \frac{dz}{dy} \frac{dy}{dx} \]
If our Op represents \(f\), then the second item in the resulting
tuple should be a function that takes \(\frac{dz}{dy}\) and returns
\(\frac{dz}{dx}\).
If the input is Nothing, then \(\frac{dz}{dy}\) should be taken to be
\(1\).
As an example, here is an Op that squares its input:
square :: Num a =>Op'[a] a square =op1'$ \x -> (x*x, \case Nothing -> 2 * x Just d -> 2 * d * x )
Remember that, generally, end users shouldn't directly construct Ops;
they should be provided by libraries or generated automatically.
For numeric functions, single-input Ops can be generated automatically
using op1.
op2' :: (a -> b -> (c, Maybe c -> (a, b))) -> Op '[a, b] c Source #
Create an Op of a function taking two inputs, by giving its explicit
gradient. The function should return a tuple containing the result of
the function, and also a function taking the derivative of the result
and return the derivative of the input.
If we have
\[ \eqalign{ f &: \mathbb{R}^2 \rightarrow \mathbb{R}\cr z &= f(x, y)\cr k &= g(z) } \]
Then the gradient \( \left< \frac{\partial k}{\partial x}, \frac{\partial k}{\partial y} \right> \) would be:
\[ \left< \frac{\partial k}{\partial x}, \frac{\partial k}{\partial y} \right> = \left< \frac{dk}{dz} \frac{\partial z}{dx}, \frac{dk}{dz} \frac{\partial z}{dy} \right> \]
If our Op represents \(f\), then the second item in the resulting
tuple should be a function that takes \(\frac{dk}{dz}\) and returns
\( \left< \frac{\partial k}{dx}, \frac{\partial k}{dx} \right> \).
If the input is Nothing, then \(\frac{dk}{dz}\) should be taken to be
\(1\).
As an example, here is an Op that multiplies its inputs:
mul :: Num a =>Op'[a, a] a mul =op2'$ \x y -> (x*y, \case Nothing -> (y , x ) Just d -> (d*y, x*d) )
Remember that, generally, end users shouldn't directly construct Ops;
they should be provided by libraries or generated automatically.
For numeric functions, two-input Ops can be generated automatically
using op2.
Utility
pattern (:>) :: forall k f a b. f a -> f b -> Prod k f ((:) k a ((:) k b ([] k))) infix 6 #
Construct a two element Prod. Since the precedence of (:>) is higher than (:<), we can conveniently write lists like:
>>>a :< b :> c
Which is identical to:
>>>a :< b :< c :< Ø
Instructions on how to "sum" a list of values of a given type.
Basically used as an explicit witness for a Num instance.
For most types, the only meaningful value of type is
Summer a. However, using Summer sumSummer lets us use BP with types
that are not instances of Num. Any type can be used, as long as you
provide a way to "sum" it!
For most of the functions in this library, you can completely ignore
this, as they will be generated automatically. You only need to work
with this directly if you want to use custom types that aren't
instances of Num with this library.
If 'Num a' is satisfied, one can create the canonical Summer using
.known :: Num a => Summer a
A canonical "unity" (the multiplicative identity) for a given type.
Basically used as an explicit witness for a Num instance.
For most types, the only meaningful value of type is
Unity a. However, using Unity 1'Unity lets us use BP with types
that are not instances of Num. Any type can be used, as long as you
provide a way to get a multiplicative identity in it!
For most of the functions in this library, you can completely ignore
this, as they will be generated automatically. You only need to work
with this directly if you want to use custom types that aren't
instances of Num with this library.
If 'Num a' is satisfied, one can create the canonical Unity using
.known :: Num a => Unity a