ufl
ufl copied to clipboard
Dualspace
This PR implements Dual function spaces, cofunctions in dual function spaces, arguments, and all the consequential objects needed to support dual operations and operators in UFL. This is documented in this thesis: https://drive.google.com/file/d/1hH_ntqZYThicpkStVwkBR-CrQ185bb95/view
It is intended to be backwards compatible so packages which don't currently use dual spaces will never see any dual objects.
This is an unfortunately big diff, but is claimed to be the minimal change which implements a closed set of functionality. The interpolation operator and external operators use this to provide actual functionality. They will follow in a subsequent PR.
Thanks for the contribution. I had a quick look.
Why is it needed to distinguish primal and dual Coefficients with a new name "Cofunction"? I'd say
V_dual = V.dual()
v_dual = Coefficient(V_dual)
(and the same for Argument) should have the same meaning. No need for that extra code which check if you ask for "cofunction" on primal or dual space. One can add the parent class runtime based on if FS is primal or dual. Also the word cofunction seems to be reserved a different meaning, https://en.wikipedia.org/wiki/Cofunction.
I like you've added BaseForm
for a linear functional. The old Form
should not have been reserved for "integral" forms, but that would require a larger braking change...
Can you explain why is an extra class FormSum
required to handle the sum of BaseForms
?
Also the word cofunction seems to be reserved a different meaning, https://en.wikipedia.org/wiki/Cofunction.
Here it's being used to refer to the category theory meaning (hence the link at the top of the wikipedia article to https://en.wikipedia.org/wiki/Dual_(category_theory). But googling for "Cofunction" definitely gives you results that are all about angles!
Sorry for taking a while to get back, I'm catching up after being on strike.
The reason why dual coefficients and dual arguments need to be different types to primal ones is that they are fundamentally different objects with different algebraic properties. Cofunctions
and Coarguments
are FormBase
objects they can be used where other 1-forms could be used, and they can't be used where UFL.expr
is needed. For example, suppose u
is an Argument
and v
is a Cofunction
. The following makes mathematical sense and can be supported in code:
u*dx + v
However, v*dx
does not make sense and should be an error. Similarly, if w
is a Coefficient
then:
u*dx + w
must be an error. The clean way to achieve this is through different types. What complicates things in this case is that the inheritance relationships work in two ways. Coefficient
and Cofunction
are both known values in vector spaces (primal and dual spaces respectively). Similarly, Argument
and Coargument
are both unknown values in vector spaces. The class inheritance relations reflect the commonality and differences between these cases cleanly. The alternative would be an enormous number of hand-coded dispatches on the type of the underlying function space. We should make the OO features of Python work for us and not fight against them.
On the name Cofunction
, I am open to alternatives but I haven't heard a good one yet. Sure, we're overloading a term which is used in a different bit of maths, but the category-theoretic use of the prefix "co-" does make sense. The strictly UFL name would be Cocoefficient
, however the confusion that that would cause is easily predictable.
The reason that we need FormSum
is the same as the reason that we need Action
and Adjoint
. Every operation which is legal on a FormBase
either has to evaluate eagerly or has to return a symbolic expression which can be evaluated at assembly time. Since taking the action, adjoint or a linear combination of FormBase
objects can be a large numerical operation, it needs to be delayed to assembly time. It should be noted that FormSum
is quite similar to Form
in this respect: Form
stores a sum of integrals. As you say, if we were doing this from scratch then we might implement and name Form
differently. I think the design we have here is a good balance of keeping things as clean as we can without breaking what we already have.