Scala is a first class functional programming language which supports among other FP concepts, higher-kind types, functors and monads. This post illustrates the capability of Scale to leverage covariant and contravariant functors for tensor analysis, and implementing vectors and co-vectors.

**Overview**

Most of Scala developers have some experience with the core tenets of functional programming: monads, functors and applicatives. Those concepts are not specific to Scala or even functional programming at large. There are elements of a field in Mathematics known as topology or algebraic topology.

Differential geometry or differential topology makes heavy use of tensors that leverage

*covariant*and*contravariant*functors.
This post introduces the concepts of

- Contravariant functors applied to co-vectors and differential forms
- Projection of higher kind

**Vector fields 101**

Let's consider a 3 dimension Euclidean space with basis vector {e

^{i}} and a vector field**V**(f^{1}, f^{2}, f^{3}) [Note: we follow Einstein tensor indices convention]
The vector field at the point P(x,y,z) as the tuple (f

The vector over a field of k dimension field can be formally. mathematically defined as

\[f: \boldsymbol{x} \,\, \epsilon \,\,\, \mathbb{R}^{k} \mapsto \mathbb{R} \\ f(\mathbf{x})=\sum_{i=1}^{n}{f^{i}}(\mathbf{x}).\mathbf{e}^{i}\]
Example: \[f(x,y,z) = 2x+z^{3}\boldsymbol{\mathbf{\overrightarrow{i}}} + xy+e^{-y}-z^{2}\boldsymbol{\mathbf{\overrightarrow{j}}} + \frac{x^{3}}{y}\boldsymbol{\mathbf{\overrightarrow{k}}}\]
_{1}(x,y,z), f_{2}(x,y,z), f_{3}(x,y,z)).The vector over a field of k dimension field can be formally. mathematically defined as

Now, let's consider the same vector

**V**with a second reference (origin O' and basis vector e_{'i}\[f(\mathbf{x})=\sum_{i=1}^{n}{f'_{i}}(\mathbf{x}).\mathbf{e'}_{i}\]

The transformation matrix S

\[S_{ij}: \begin{Vmatrix} f^{1} \\ f^{2} \\ f^{3} \end{Vmatrix} \mapsto \begin{Vmatrix} f'^{1} \\ f'^{2} \\ f'^{3} \end{Vmatrix}\]
_{ij}convert the coordinates value functions f^{i}and f^{'i}. The tuple**f**=(f^{i}) or more accurately defined as (f_{i}) is the co-vector field for the vector field**V**
The scalar product of the co-vector f' and vector v(f) defined as

\[< f',v> = \sum f'_{i}.f^{i}\]
**is defined as**
Given the scalar product we can define the co-vector field f' as a linear map

\[\alpha (v) = < f',v> (1) \]
**Covariant functors**

I assume the reader has basic understanding of Functor and Monads. Here is short overview:

A

**category**C is composed of object x and**morphism**f defined as**functor F**is a map between two categories C and D that preserves the mapping.

\[x\in C \Rightarrow F(x)\in D \\ x, y\in C \,\,\, F: x \mapsto y => F(x)\mapsto F(y)\]

Let's look at the definition of a functor in Scala with the "preserving" mapping method,

**map**1 2 3 | trait Functor[M[_]] { def map[U, V](m: M[U])(f: U => V): M[V] } |

Let's define the functor for a vector (or tensor) field. A vector field is defined as a sequence or list of fields (i.e. values or function values).

type VField[U] = List[U] trait VField_Ftor extends Functor[VField] { override def map[U, V](vu: VField[U])(f: U => V): VField[V] = vu.map(f) }

This particular implementation relies on the fact that List is a category with its own functor. The next step is to define the implicit class conversion

*VField[U] => Functor[VField[U]]*so the map method is automatically invoked for each*VField*instance.implicit class vField2Functor[U](vu: VField[U]) extends VField_Ftor { final def map[V](f: U => V): VField[V] = super.map(vu)(f) }

By default Covariant Functors (which preserve mapping) are known simply as Functors. Let's look at the case of Covector fields.

**Contravariant functors**

A

\[x, y\in C \,\,\, F: x \mapsto y => F(y)\mapsto F(x)\]
**Contravariant functor**is a map between two categories that reverses the mapping of morphisms.trait CoFunctor[M[_]] { def map[U, V](m: M[U])(f: V => U): M[V] }

The map method of the Cofunctor implements the relation

Let's implement a co-vector field using a contravariant functor. The definition

A morphism on the category V* consists of a morphism of

*M[V->U] => M[U]->M[V]*Let's implement a co-vector field using a contravariant functor. The definition

*(1)*describes a linear map between a vector V over a field X to the scalar product**V*: V => T**.A morphism on the category V* consists of a morphism of

**V => T**or**V => _**where**V**is a vector field and**T**or**_**is a scalar function value.type CoField[V, T] = Function1[V, T]

The co-vector field type,

**CoField**is parameterized on the vector field type**V**which is a input or function parameter. Therefore the functor has to be contravariant.
The higher kind type

**M[_]**takes a single type as parameter (i.e. M[V]) but a co-vector field requires two types:**V**: Vector field**T**: The scalar function is that the result of the inner product**<.>**

Fortunately the contravariant functor

*CoField_Ftor*associated with the co-vector needs to be parameterized only with the vector field V. The solution is to pre-defined (or 'fix') the scalar type T using a higher kind projector for the type*L[V] => CoField[V, T]***T => ({type L[X] = CoField[X,T]})#L**

trait CoField_Ftor[T] extends CoFunctor[({type L[X] = CoField[X,T]})#L ] { override def map[U,V]( vu: CoField[U,T] )(f: V => U): CoField[V,T] = (v: V) => vu(f(v)) }

As you can see the morphism over the type V on the category CoField is defined as

*f: V => U*instead of*f: U => V*. A kind parameterized on the return type (Function1) would require the 'default' (covariant) functor. Once again, we define an implicit class to convert a co-vector field, of type*CoField*to its functor,*CoField2Ftor*implicit class CoField2Ftor[U,T](vu: CoField[U,T]) extends CoField_Ftor[T] { final def map[V](f: V => U): CoField[V,T] = super.map(vu)(f) }

**Evaluation**

Let's consider a field of function values

*FuncD*of two dimension: v(x,y) = f_{1}(x,y).**i**+ f_{2}(x,y.**j**. The Vector field*VField*is defined as a list of two function values.type DVector = Array[Double] type FuncD = Function1[DVector, Double] type VFieldD = VField[FuncD]

The vector is computed by assigning a vector field to a specific point (P(1.5, 2.0). The functor is applied to the vector field,

*vField*to generate a new vector field*vField2*val f1: FuncD = new FuncD((x: DVector) => x(0)*x(1)) val f2: FuncD = new FuncD((x: DVector) => -2.0*x(1)*x(1)) val vfield: VFieldD = List[FuncD](f1, f2) val value: DVector = Array[Double](1.5, 2.0) val vField2: VFieldD = vfield.map( _*(4.0))

A co-vector field,

*coField*is computed as the sum of the fields (function values) (lines 1, 2). Next, we compute the product of co-vector field and vector field (scalar field*product*) (line 6). We simply apply the co-vector*Cofield*(linear map) to the vector field. Once defined, the morphism*_morphism*is used to generate a new co-vector field*coField2*through the contravariant function*CoField2Functor.map*(line 10).
Finally a

*newProduction*is generated by composing the original covariant field*Cofield*and the linear map*coField2*(line 12).1 2 3 4 5 6 7 8 9 10 11 12 | val coField: CoField[VFieldD, FuncD] = (vf: VFieldD) => vf(0) + vf(1) val contraFtor: CoField2Functor[VFieldD, FuncD] = coField val product = coField(vField) val _morphism: VFieldD => VFieldD = (vf: VFieldD) => vf.map( _*(3.0)) val coField2 = contraFtor.map( _morphism ) val newProduct: FuncD = coField2(coField) |

**References**

*Tensor Analysis on Manifolds*- R. Bishop, S. Goldberg - Dover publication 1980*Differential Geometric Structures*- W. Poor - Dover publications 2007*Functors and Natural Transformations*v- A. Tarlecki - Category Theory 2014

The mathematical definition doesn't seem to be right. Does f really map from R^k -> R? The component-wise function f^i does map from R^k to R I guess, but the function f should be f: R^k -> R^k.

ReplyDelete