ToC

Form fields

A form-field of rank n is an operator that takes n vector fields to a real-valued function on the manifold. A one-form field takes a single vector field.

(derive ::oneform-field ::form-field)
nil
(derive ::form-field ::o/operator)
nil
(defn ff:zero
"Returns a form field that returns, for any supplied vector field `vf`, a
manifold function [[manifold/zero-manifold-function]] that maps every input
manifold `point` to the scalar value 0."
[_]
m/zero-manifold-function)
#object[emmy.calculus.form_field$ff_COLON_zero 0x1b16ef08 "
emmy.calculus.form_field$ff_COLON_zero@1b16ef08"
]
(defn get-rank
"Returns the rank of the supplied differential form `f`. Functions are treated
as differential forms of rank 0.
Throws for any non differential form supplied."
[f]
(cond (o/operator? f)
(or (:rank (o/context f))
(u/illegal
(str "operator, but not a differential form: " f)))
(f/function? f) 0
:else (u/illegal
(str "not a differential form: " f))))
#object[emmy.calculus.form_field$get_rank 0x24f1729b "
emmy.calculus.form_field$get_rank@24f1729b"
]
(defn form-field?
"Returns true if the supplied `f` is a form field operator, false otherwise."
[ff]
(and (o/operator? ff)
(let [subtype (:subtype
(o/context ff))]
(isa? subtype ::form-field))))
#object[emmy.calculus.form_field$form_field_QMARK_ 0x7441df8e "
emmy.calculus.form_field$form_field_QMARK_@7441df8e"
]
(defn nform-field?
"Returns true if the supplied `f` is an [form field of rank
n](https://en.wikipedia.org/wiki/Differential_form), false otherwise.
A form-field of rank n is an operator that takes n vector fields to a
real-valued function on the manifold."
[f n]
(and (form-field? f)
(= n (get-rank f))))
#object[emmy.calculus.form_field$nform_field_QMARK_ 0x129f43bc "
emmy.calculus.form_field$nform_field_QMARK_@129f43bc"
]
(defn oneform-field?
"Returns true if the supplied `f` is
a [One-form](https://en.wikipedia.org/wiki/One-form), false
otherwise.
A [One-form](https://en.wikipedia.org/wiki/One-form) takes a single vector
field to a real-valued function on the manifold."
[f]
(nform-field? f 1))
#object[emmy.calculus.form_field$oneform_field_QMARK_ 0x3edd8048 "
emmy.calculus.form_field$oneform_field_QMARK_@3edd8048"
]
(defn- ff:zero-like
"Given some form field `op`, returns a form field with the same context and
its procedure replaced by `ff:zero`.
The returned form field responds `true` to `g/zero?`."
[op]
{:pre [(form-field? op)]}
(o/make-operator ff:zero
'ff:zero
(o/context op)))
#object[emmy.calculus.form_field$ff_COLON_zero_like 0x119748a2 "
emmy.calculus.form_field$ff_COLON_zero_like@119748a2"
]
(defn- ff:zero?
"Returns true if the supplied form field `op` is a form field with a procedure
equal to `ff:zero`, false otherwise."
[op]
(and (form-field? op)
(= (o/procedure op) ff:zero)))
#object[emmy.calculus.form_field$ff_COLON_zero_QMARK_ 0x6bd058b1 "
emmy.calculus.form_field$ff_COLON_zero_QMARK_@6bd058b1"
]
(letfn [(one-like [_]
(u/unsupported
"form fields don't have an identity."))
(id-like [_]
(u/unsupported
"form fields don't have a multiplicative identity."))
(identity? [_] false)]
(let [defaults {:zero? ff:zero?
:zero-like ff:zero-like
:one-like one-like
:identity? identity?
:identity-like id-like}]
(defn- ff-context [m]
(merge defaults m))))
#'emmy.calculus.form-field/ff-context
(defn ^:no-doc procedure->nform-field
"Accepts a function `f` and an optional symbolic `name`, and returns an n-form
field, i.e., a subtype of [[emmy.operator/Operator]].
`f` should be a function from n vector field arguments to a smooth real-valued
function `g` of a manifold.
If `n` is 0, the function will be called immediately and its return value
returned."
([f n]
(procedure->nform-field f n 'unnamed-nform-field))
([f n name]
(if (= n 0)
(f)
(let [args (into [] (repeat n ::vf/vector-field))]
(o/make-operator f name
(ff-context
{:subtype ::form-field
:arity [:exactly n]
:rank n
:arguments args}))))))
#object[emmy.calculus.form_field$procedure__GT_nform_field 0x3ba64d4 "
emmy.calculus.form_field$procedure__GT_nform_field@3ba64d4"
]
(defn ^:no-doc procedure->oneform-field
"Accepts a function `f` and an optional symbolic `name`, and returns a one-form
field, i.e., a subtype of [[emmy.operator/Operator]].
`f` should be a function from a vector field to a smooth real-valued function
`g` of a manifold."
([f]
(let [name 'unnamed-1form-field]
(procedure->oneform-field f name)))
([f name]
(o/make-operator f name
(ff-context
{:subtype ::oneform-field
:arity [:exactly 1]
:rank 1
:arguments [::vf/vector-field]}))))
#object[emmy.calculus.form_field$procedure__GT_oneform_field 0x51b77b2b "
emmy.calculus.form_field$procedure__GT_oneform_field@51b77b2b"
]
(defn ^:no-doc oneform-field-procedure
"Takes:
- a `down` tuple of `components` of the one-form field relative to
`coordinate-system`
- the `coordinate-system`
And returns a procedure (not yet an operator!) that takes a structure of vector fields
and produces a new structure of functions of manifold points."
[components coordinate-system]
(fn [vf-components]
(s/mapr (fn [vf]
{:pre [(vf/vector-field? vf)]}
(f/compose
(g/* components
(vf/vector-field->components vf coordinate-system))
(m/chart coordinate-system)))
vf-components)))
#object[emmy.calculus.form_field$oneform_field_procedure 0x7aceebe7 "
emmy.calculus.form_field$oneform_field_procedure@7aceebe7"
]
(defn components->oneform-field
"Takes:
- a `down` tuple of `components` of the one-form field relative to
`coordinate-system`
- the `coordinate-system`
And returns a full one-form field.
A one-field field is an operator that takes a vector field to a real-valued
function on the manifold."
([components coordinate-system]
(let [name `(~'oneform-field
~(g/freeze components))]
(components->oneform-field
components coordinate-system name)))
([components coordinate-system name]
(-> (oneform-field-procedure components coordinate-system)
(procedure->oneform-field name))))
#object[emmy.calculus.form_field$components__GT_oneform_field 0x13b221df "
emmy.calculus.form_field$components__GT_oneform_field@13b221df"
]

We can extract the components function for a form, given a coordinate system.

(defn oneform-field->components
"Given a one-form field `form` and a `coordinate-system`, returns a function
from the coordinate representation of a manifold point to a coordinate
representation of the coordinatized components of the form field at that
point.
For example:
```clojure
(let-coordinates [[x y] R2-rect]
(let [f (literal-oneform-field 'f R2-rect)]
((oneform-field->components f R2-rect)
(up 'x0 'y0))))
;;=> (down (f_0 (up x0 y0))
;; (f_1 (up x0 y0)))
```"
[form coordinate-system]
{:pre [(form-field? form)]}
(let [basis (vf/coordinate-system->vector-basis coordinate-system)]
(f/compose (form basis)
(m/point coordinate-system))))
#object[emmy.calculus.form_field$oneform_field__GT_components 0x43fe7bb9 "
emmy.calculus.form_field$oneform_field__GT_components@43fe7bb9"
]

API

(defn literal-oneform-field
"Given a symbolic name `sym` and a `coordinate-system`, returns a one-form field
consisting of literal real-valued functions from the coordinate system's
dimension for each coordinate component.
These functions are passed to [[components->oneform-field]], along with the
supplied `coordinate-system` and symbolic name `sym`.
For coordinate systems of dimension 1, `literal-form-field`'s component
functions will accept a single non-structural argument."
[name coordinate-system]
(let [n (:dimension (m/manifold coordinate-system))
domain (if (= n 1) 0 (s/up* (repeat n 0)))
range (s/down* (repeat n 0))]
(-> (af/literal-function name domain range)
(components->oneform-field coordinate-system name))))
#object[emmy.calculus.form_field$literal_oneform_field 0x29fa2ece "
emmy.calculus.form_field$literal_oneform_field@29fa2ece"
]

To get the elements of a coordinate basis for the 1-form fields:

(defn- coordinate-basis-oneform-field-procedure
[coordinate-system & indices]
(fn [vf-structure]
(letfn [(internal [vf]
{:pre [(vf/vector-field? vf)]}
(vf
(f/compose (apply s/component indices)
(m/chart coordinate-system))))]
(s/mapr internal vf-structure))))
#object[emmy.calculus.form_field$coordinate_basis_oneform_field_procedure 0x246679c5 "
emmy.calculus.form_field$coordinate_basis_oneform_field_procedure@246679c5"
]
(defn coordinate-basis-oneform-field
"Given some `coordinate-system`, a symbolic `name` and a sequence of indices
into the structure of the coordinate system's representation, returns a
one-form field.
The returned one-form field at each structural spot takes a vector field and
returns a function that takes the directional derivative in that coordinate's
direction using the vector field."
[coordinate-system name & indices]
(let [ofp (apply coordinate-basis-oneform-field-procedure
coordinate-system indices)]
(-> (f/memoize
(comp f/memoize ofp))
(procedure->oneform-field name))))
#object[emmy.calculus.form_field$coordinate_basis_oneform_field 0x2658084f "
emmy.calculus.form_field$coordinate_basis_oneform_field@2658084f"
]
(defn ^:no-doc coordinate-name->ff-name
"From the name of a coordinate, produce the name of the coordinate basis
one-form field (as a symbol)"
[n]
(symbol (str \d n)))
#object[emmy.calculus.form_field$coordinate_name__GT_ff_name 0x164b9c8e "
emmy.calculus.form_field$coordinate_name__GT_ff_name@164b9c8e"
]
(defn coordinate-system->oneform-basis
"Given some `coordinate-system`, returns a structure of
`coordinate-basis-oneform-field` instances.
The one-form field at each structural spot takes a vector field and returns a
function that takes the directional derivative in that coordinate's direction
using the vector field.
When applied as a function, the structure behaves equivalently to
```clojure
(coordinate-basis-oneform-field <coordinate-system> 'ignored-name)
```
With no indices supplied."
[coordinate-system]
(s/map-chain
(fn [c-name chain _]
(let [ff-name (coordinate-name->ff-name c-name)]
(apply coordinate-basis-oneform-field
coordinate-system ff-name chain)))
(m/coordinate-prototype coordinate-system)))
#object[emmy.calculus.form_field$coordinate_system__GT_oneform_basis 0x6ebf4cd7 "
emmy.calculus.form_field$coordinate_system__GT_oneform_basis@6ebf4cd7"
]

Given component functions defined on manifold points and a 1-form basis, to produce the 1-form field as a linear combination.

(defn basis-components->oneform-field
"Given a structure of `components` functions defined on manifold points and and
a matching `oneform-basis` (of identical structure),
Returns a new one-form field that
- passes its vector-field argument to `oneform-basis`, returning a new
equivalent structure with each slot populated by functions from a manifold
point to the directional derivative (using the vector field) in that
coordinate direction
- contracts the result of that operation with the result of applying each
component in `components` to the manifold point.
NOTE:
- This is for any basis, not just a coordinate basis
- The `components` are evaluated at a manifold point, not its coordinates
- Given a dual basis, you can retrieve the original components
with [[oneform-field->basis-components]]"
[components oneform-basis]
(procedure->oneform-field
(fn [vf]
(g/* components (oneform-basis vf)))))
#object[emmy.calculus.form_field$basis_components__GT_oneform_field 0x7980b894 "
emmy.calculus.form_field$basis_components__GT_oneform_field@7980b894"
]
(defn oneform-field->basis-components
"Given a structure `w` of and a vector field basis `vector-basis`, returns a new
structure generated by applying the full vector basis to each element of `w`.
Here's an example of how to use this function to round trip a structure of
basis components:
```clojure
(let [vb (vf/coordinate-system->vector-basis coordsys)
basis (coordinate-system->oneform-basis coordsys)
components (down d:dx d:dy)]
(= components
(-> components
(basis-components->oneform-field basis)
(oneform-field->basis-components vb))))
```"
[w vector-basis]
(s/mapr w vector-basis))
#object[emmy.calculus.form_field$oneform_field__GT_basis_components 0x1f7ccd9b "
emmy.calculus.form_field$oneform_field__GT_basis_components@1f7ccd9b"
]
(defn function->oneform-field
"One of the two incompatible definitions of differential.
This differential is a special case of exterior derivative. The other one
lives at [[map/differential]]."
[f]
{:pre [(f/function? f)]}
(let [op (fn [vf-structure]
(s/mapr (fn [vf]
{:pre [(vf/vector-field? vf)]}
(fn [m] ((vf f) m)))
vf-structure))
name `(~'d ~(g/freeze f))]
(procedure->oneform-field op name)))
#object[emmy.calculus.form_field$function__GT_oneform_field 0x55998d16 "
emmy.calculus.form_field$function__GT_oneform_field@55998d16"
]
(def differential-of-function
"Alias for [[function->oneform-field]].
One of the two incompatible definitions of differential.
This differential is a special case of exterior derivative. The other one
lives at [[map/differential]]."
function->oneform-field)
#object[emmy.calculus.form_field$function__GT_oneform_field 0x55998d16 "
emmy.calculus.form_field$function__GT_oneform_field@55998d16"
]

Wedge Product (from Wedge.scm)

Now we transition to wedge.

Higher rank forms can be constructed from 1forms by wedging them together. This antisymmetric tensor product is computed as a determinant. The purpose of this is to allow us to use the construction dx^dy to compute the area described by the vectors that are given to it.

(defn- wedge2
"Binary and unary cases of the wedge product."
([form1] form1)
([form1 form2]
(let [n1 (get-rank form1)
n2 (get-rank form2)]
(if (or (zero? n1) (zero? n2))
(g/* form1 form2)
(let [n (+ n1 n2)
k (/ 1
(* (factorial n1)
(factorial n2)))
w (fn [& args]
(assert (= (count args) n)
(str "Wrong number of args to wedge product: "
(count args) " vs required " n))
;; "Error in Singer" comment from GJS.
(g/* k (apply
g/+ (map (fn [permutation parity]
(let [[a1 a2] (split-at n1 permutation)]
(g/* parity
(apply form1 a1)
(apply form2 a2))))
(permute/permutation-sequence args)
(cycle [1 -1])))))
name `(~'wedge
~(g/freeze form1)
~(g/freeze form2))]
(procedure->nform-field w n name))))))
#object[emmy.calculus.form_field$wedge2 0x41d4aa86 "
emmy.calculus.form_field$wedge2@41d4aa86"
]
(defn wedge
"Computes the wedge product of the sequence `fs` of one-forms.
Higher rank forms can be constructed from one-forms by wedging them together.
This antisymmetric tensor product is computed as a determinant. The purpose of
this is to allow us to use the construction dx^dy to compute the area
described by the vectors that are given to it.
See Spivak p275 v1 of 'Differential Geometry' to see the correct definition.
The key is that the wedge of the coordinate basis forms had better be the
volume element."
([] (constantly 1))
([f] f)
([f & fs]
(reduce (fn [r l]
(wedge2 l r))
(reverse (cons f fs)))))
#object[emmy.calculus.form_field$wedge 0x3d6cee30 "
emmy.calculus.form_field$wedge@3d6cee30"
]

One-form fields multiply by [[wedge]].

(defmethod g/mul [::form-field ::form-field] [a b]
(wedge2 a b))
#object[clojure.lang.MultiFn 0x4485ec13 "
clojure.lang.MultiFn@4485ec13"
]

Alternative definition in terms of alternation.

(defn Alt
"Returns the alternation of the supplied differential `form`."
[form]
(let [n (get-rank form)]
(if (zero? n)
form
(letfn [(alternation [& args]
(assert (= (count args) n)
"Wrong number of args to alternation")
(g/* (/ 1 (factorial n))
(ua/generic-sum
(map (fn [permutation parity]
(g/* parity (apply form permutation)))
(permute/permutation-sequence args)
(cycle [1 -1])))))]
(procedure->nform-field
alternation n `(~'Alt ~(g/freeze form)))))))
#object[emmy.calculus.form_field$Alt 0x5ded38a8 "
emmy.calculus.form_field$Alt@5ded38a8"
]
(defn- tensor-product2
([t1] t1)
([t1 t2]
(let [n1 (get-rank t1)
n2 (get-rank t2)]
(if (or (zero? n1) (zero? n2))
(g/* t1 t2)
(let [n (+ n1 n2)
tp (fn [& args]
(assert (= (count args) n)
"Wrong number of args to tensor product")
(let [[a1 a2] (split-at n1 args)]
(g/* (apply t1 a1)
(apply t2 a2))))]
(procedure->nform-field
tp n `(~'tensor-product
~(g/freeze t1)
~(g/freeze t2))))))))
#object[emmy.calculus.form_field$tensor_product2 0x39092909 "
emmy.calculus.form_field$tensor_product2@39092909"
]
(defn- w2
([form1] form1)
([form1 form2]
(let [n1 (get-rank form1)
n2 (get-rank form2)]
(g/* (/ (factorial (+ n1 n2))
(* (factorial n1)
(factorial n2)))
(Alt (tensor-product2 form1 form2))))))
#object[emmy.calculus.form_field$w2 0x634e5d76 "
emmy.calculus.form_field$w2@634e5d76"
]
(defn alt-wedge
"Alternative definition of [[wedge]] in terms of alternation."
[& args]
(reduce w2 (constantly 1) args))
#object[emmy.calculus.form_field$alt_wedge 0x840c078 "
emmy.calculus.form_field$alt_wedge@840c078"
]

Exterior Derivative

A form field is a function of a place and a number of vector fields. The exterior derivative of this form field is a derivative of the form with respect to the place, removing any dependence on place of the vector fields.

Consider w(v)(x), where b is the coefficient function for w in coordinates X:

v1(w(v2))(x) - v2(w(v1))(x) = v1(b v2(X))(x) - v2(b v1(X))(x) = v1(b)(x) v2(X)(x) + b(x) v1(v2(X))(x)

  • v2(b)(x) v1(X)(x) - b(x) v2(v1(X))(x) = v1(b)(x) v2(X)(x) - v2(b)(x) v1(X)(x) + b(x)v1, v2(x) = v1(b)(x) v2(X)(x) - v2(b)(x) v1(X)(x) + w([v1, v2])(x)

We define exterior derivative as follows:

dw(v1, v2)(x) = v1(b)(x) v2(X)(x) - v2(b)(x) v1(X)(x) = v1(w(v2))(x) - v2(w(v1))(x) - w([v1, v2])(x)

It is not obvious that this is equivalent to the standard definition. See page 91 in Spivak.

Another way to think about this is that if we were able to define constant vector fields (v1_bar, v2_bar) that have constant coefficient functions equal to the value of the coefficient function at the point, then dw(v1, v2)(x) would be just v1_bar(w(v2_bar))(x) - v2_bar(w(v1_bar))(x).

This definition is a generalization to k-forms, by recursion on the vector argument list.

The test for k<n is best if the n is the dimension of the manifold under study. However, if the manifold is embedded in a higher dimensional manifold n will be the dimension of the bigger manifold, making this test less effective (cutting off fewer branches).

Formula is from Spivak Vol. 1 p289.

NOTE: This is an excessively complicated program. Another, more elementary program would, for a k-form, extract the cofficient functions relative to a literal basis, by applying it to the basis vectors, do the derivatives of the coefficients, to make one forms, and form the sum of the wedges of the new 1-forms with the wedges of the corresponding dual basis elements.

(defn- exterior-derivative-procedure [kform]
(let [k (get-rank kform)]
(if (= k 0)
(differential-of-function kform)
(let [without #(concat (take %1 %2)
(drop (inc %1) %2))
k+1form (fn [& vectors]
(assert (= (count vectors) (inc k)))
(fn [point]
(let [n (:dimension
(m/point->manifold point))]
(if (< k n)
(ua/generic-sum
(fn [i]
(let [rest (without i vectors)]
(g/+ (g/* (if (even? i) 1 -1)
(((nth vectors i) (apply kform rest))
point))
(ua/generic-sum
(fn [j]
(g/* (if (even? (+ i j)) 1 -1)
((apply kform
(cons
(o/commutator (nth vectors i)
(nth vectors j))
;; (dec j) because
;; already deleted i.
(without (dec j) rest)))
point)))
(inc i)
(inc k)))))
0 (inc k))
0))))]
(procedure->nform-field
k+1form (inc k) `(~'d ~(g/freeze kform)))))))
#object[emmy.calculus.form_field$exterior_derivative_procedure 0x26aec9d2 "
emmy.calculus.form_field$exterior_derivative_procedure@26aec9d2"
]
(def exterior-derivative
(o/make-operator
exterior-derivative-procedure 'd))
d
(def d exterior-derivative)
d