Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dualspace update #25

Merged
merged 47 commits into from
Feb 1, 2022
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
a4b7ab0
Allow variant hints to be passed into VectorElement and TensorElement…
mscroggs Oct 28, 2021
6b804eb
Check trivial case for Action and Adjoint
nbouziani Sep 30, 2021
621036a
Update possible right term for Action
nbouziani Nov 5, 2021
e986988
Take into account various BaseForm objects in expand_derivatives
nbouziani May 11, 2021
8c6b5b0
Add BaseForm in __init__
nbouziani Nov 5, 2021
fba3d29
Add BaseForm in as_ufl
nbouziani Oct 1, 2021
e06c834
Update action
nbouziani Nov 5, 2021
d062079
Update Cofunction and Coargument for when they take in a primal space
nbouziani Oct 1, 2021
10df11f
Add equals to FormSum
nbouziani Nov 6, 2021
08c9a80
Add __eq__ to Action
nbouziani Nov 6, 2021
746764a
Add __eq__ to Adjoint
nbouziani Nov 6, 2021
178d27c
Add __eq__ and __hash__ to Coargument
nbouziani Nov 6, 2021
dcd73f1
Fix typos
nbouziani Nov 6, 2021
8594808
Refactor analysis.py
nbouziani Nov 6, 2021
777c1aa
Add BaseFormDerivative
nbouziani Nov 9, 2021
449dbd6
Fix for arguments, coefficients and function spaces
nbouziani Nov 10, 2021
1a75d6b
Fix hash for coefficients, arguments and function spaces + some more …
nbouziani Nov 10, 2021
404d690
Draft BaseForm diff
nbouziani Nov 12, 2021
9dac91e
Draft: Refactor UFL types using UFLType
nbouziani Nov 17, 2021
685abcc
Fix __eq__ for Coargument
nbouziani Nov 20, 2021
22c21bd
Add set of continuous families not requiring a restriction (#73)
jorgensd Nov 22, 2021
ba71380
Add UFLType handler as the default handler + Move UFLType to ufl_type.py
nbouziani Nov 23, 2021
c9df1ce
Add ufl_operands and _ufl_compute_hash to BaseForm objects for MultiF…
nbouziani Nov 23, 2021
03f60e8
Add Matrix/Cofunction/Coargument differentiation + Add some ufl_type …
nbouziani Nov 23, 2021
c8ac12c
Push arguments analysis through BaseFormDerivative
nbouziani Nov 23, 2021
6b2a590
Add Action differentiation
nbouziani Nov 23, 2021
e21af2c
Add tests for BaseForm differentiation
nbouziani Nov 23, 2021
19f12be
Add Adjoint(Adjoint(.)) = Id
nbouziani Nov 23, 2021
fd9e3aa
Fix Action differentiation
nbouziani Nov 24, 2021
7ad7a1a
Matrix derivative is always 0 (since we can't differentiate wrt a Mat…
nbouziani Nov 24, 2021
6d59dfe
Update _handle_derivative_arguments
nbouziani Nov 24, 2021
1b44c18
Fix _analyze_form_arguments for FormSum
nbouziani Nov 24, 2021
81504fd
Update FormSum and tests
nbouziani Nov 24, 2021
2da7243
Fix ExprList
nbouziani Nov 24, 2021
d53a4bf
Add Adjoint differentiation
nbouziani Nov 24, 2021
2fdd180
Add handlers for Action/Adjoint/FormSum in map_integrands
nbouziani Nov 26, 2021
b9964d9
Use pytest.raises
nbouziani Nov 29, 2021
99347a3
Change __eq__ to equals for dual objects
nbouziani Nov 30, 2021
1de7f12
Create FUNDING.yml (#75)
jhale Dec 8, 2021
3d801f3
Update traversal.py
nbouziani Dec 8, 2021
dce6968
Replace expr handler by ufl_type in Replacer
nbouziani Dec 8, 2021
db3b50d
Refactor UFL type system
nbouziani Dec 17, 2021
31d8bfc
Address comments from the PR
nbouziani Dec 19, 2021
7a4303f
Add function to get the `ufl.Cell` corresponding to a cell's facet (#76)
jpdean Jan 10, 2022
539f999
Remove cellname2facetname (#77)
jpdean Jan 10, 2022
645814b
Wence/reconstruct tp (#78)
wence- Jan 11, 2022
ca66b3c
Merge remote-tracking branch 'origin/master' into dualspace_update
nbouziani Jan 16, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 70 additions & 9 deletions test/test_duals.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from ufl import FiniteElement, FunctionSpace, MixedFunctionSpace, \
Coefficient, Matrix, Cofunction, FormSum, Argument, Coargument,\
TestFunction, TrialFunction, Adjoint, Action, \
action, adjoint, tetrahedron, triangle, interval, dx
action, adjoint, derivative, tetrahedron, triangle, interval, dx

__authors__ = "India Marsden"
__date__ = "2020-12-28 -- 2020-12-28"
Expand All @@ -13,6 +13,7 @@

from ufl.domain import default_domain
from ufl.duals import is_primal, is_dual
from ufl.algorithms.ad import expand_derivatives


def test_mixed_functionspace(self):
Expand Down Expand Up @@ -58,7 +59,6 @@ def test_dual_coefficients():
v = Coefficient(V, count=1)
u = Coefficient(V_dual, count=1)
w = Cofunction(V_dual)
x = Cofunction(V)

assert is_primal(v)
assert not is_dual(v)
Expand All @@ -69,8 +69,11 @@ def test_dual_coefficients():
assert is_dual(w)
assert not is_primal(w)

assert is_primal(x)
assert not is_dual(x)
try:
x = Cofunction(V)
assert False
except ValueError:
pass
nbouziani marked this conversation as resolved.
Show resolved Hide resolved


def test_dual_arguments():
Expand All @@ -82,7 +85,6 @@ def test_dual_arguments():
v = Argument(V, 1)
u = Argument(V_dual, 2)
w = Coargument(V_dual, 3)
x = Coargument(V, 4)

assert is_primal(v)
assert not is_dual(v)
Expand All @@ -93,8 +95,11 @@ def test_dual_arguments():
assert is_dual(w)
assert not is_primal(w)

assert is_primal(x)
assert not is_dual(x)
try:
x = Coargument(V, 4)
assert False
except ValueError:
pass
nbouziani marked this conversation as resolved.
Show resolved Hide resolved


def test_addition():
Expand Down Expand Up @@ -153,6 +158,9 @@ def test_adjoint():
assert isinstance(res, FormSum)
assert isinstance(res.components()[0], Adjoint)

# Adjoint(Adjoint(.)) = Id
assert adjoint(adj) == a


def test_action():
domain_2d = default_domain(triangle)
Expand All @@ -167,7 +175,7 @@ def test_action():
u = Coefficient(U)
u_a = Argument(U, 0)
v = Coefficient(V)
u_star = Cofunction(U.dual())
ustar = Cofunction(U.dual())
u_form = u_a * dx

res = action(a, u)
Expand All @@ -191,4 +199,57 @@ def test_action():
res = action(a, v)

with pytest.raises(TypeError):
res = action(a, u_star)
res = action(a, ustar)


def test_differentiation():
domain_2d = default_domain(triangle)
f_2d = FiniteElement("CG", triangle, 1)
V = FunctionSpace(domain_2d, f_2d)
domain_1d = default_domain(interval)
f_1d = FiniteElement("CG", interval, 1)
U = FunctionSpace(domain_1d, f_1d)

u = Coefficient(U)
v = Argument(U, 0)
vstar = Argument(U.dual(), 0)

# -- Cofunction -- #
w = Cofunction(U.dual())
dwdu = expand_derivatives(derivative(w, u))
assert dwdu == 0

dwdw = expand_derivatives(derivative(w, w, vstar))
assert dwdw == vstar

dudw = expand_derivatives(derivative(u, w))
assert dudw == 0

# -- Coargument -- #
dvstardu = expand_derivatives(derivative(vstar, u))
assert dvstardu == 0

# -- Matrix -- #
M = Matrix(V, U)
dMdu = expand_derivatives(derivative(M, u))
assert dMdu == 0

# -- Action -- #
Ac = Action(M, u)
dAcdu = expand_derivatives(derivative(Ac, u))

# Action(dM/du, u) + Action(M, du/du) = Action(M, uhat) since dM/du = 0.
# Multiply by 1 to get a FormSum (type compatibility).
assert dAcdu == 1 * Action(M, v)

# -- Adjoint -- #
Ad = Adjoint(M)
dAddu = expand_derivatives(derivative(Ad, u))
# Push differentiation through Adjoint
assert dAddu == 0

# -- Form sum -- #
Fs = M + Ac
dFsdu = expand_derivatives(derivative(Fs, u))
# Distribute differentiation over FormSum components
assert dFsdu == 1 * Action(M, v)
2 changes: 1 addition & 1 deletion ufl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@
from ufl.measure import Measure, register_integral_type, integral_types, custom_integral_types

# Form class
from ufl.form import Form, FormSum, replace_integral_domains
from ufl.form import Form, BaseForm, FormSum, replace_integral_domains

# Integral classes
from ufl.integral import Integral
Expand Down
28 changes: 24 additions & 4 deletions ufl/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
# SPDX-License-Identifier: LGPL-3.0-or-later

from ufl.form import BaseForm, FormSum, Form
from ufl.coefficient import Coefficient
from ufl.argument import Argument
from ufl.coefficient import Coefficient, Cofunction
from ufl.differentiation import CoefficientDerivative

# --- The Action class represents the action of a numerical object that needs
# to be computed at assembly time ---
Expand All @@ -29,6 +31,7 @@ class Action(BaseForm):
__slots__ = (
"_left",
"_right",
"ufl_operands",
"_repr",
"_arguments",
"_hash")
Expand All @@ -39,6 +42,10 @@ def __getnewargs__(self):
def __new__(cls, *args, **kw):
left, right = args

# Check trivial case
if left == 0 or right == 0:
return 0
nbouziani marked this conversation as resolved.
Show resolved Hide resolved

if isinstance(left, FormSum):
# Action distributes over sums on the LHS
return FormSum(*[(Action(component, right), 1)
Expand All @@ -55,19 +62,25 @@ def __init__(self, left, right):

self._left = left
self._right = right
self.ufl_operands = (self._left, self._right)

if isinstance(right, CoefficientDerivative):
# Action differentiation pushes differentiation through
# right as a consequence of Leibniz formula.
right, *_ = right.ufl_operands

if isinstance(right, Form):
if isinstance(right, (Form, Action)):
if (left.arguments()[-1].ufl_function_space().dual()
!= right.arguments()[0].ufl_function_space()):

raise TypeError("Incompatible function spaces in Action")
elif isinstance(right, Coefficient):
elif isinstance(right, (Coefficient, Cofunction, Argument)):
if (left.arguments()[-1].ufl_function_space()
!= right.ufl_function_space()):

raise TypeError("Incompatible function spaces in Action")
else:
raise TypeError("Incompatible argument in Action")
raise TypeError("Incompatible argument in Action: %s" % type(right))

self._repr = "Action(%s, %s)" % (repr(self._left), repr(self._right))
self._hash = None
Expand Down Expand Up @@ -101,6 +114,13 @@ def _analyze_form_arguments(self):
else:
raise TypeError

def __eq__(self, other):
if not isinstance(other, Action):
nbouziani marked this conversation as resolved.
Show resolved Hide resolved
return False
if self is other:
return True
return (self._left == other._left and self._right == other._right)

def __str__(self):
return "Action(%s, %s)" % (repr(self._left), repr(self._right))

Expand Down
17 changes: 16 additions & 1 deletion ufl/adjoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,21 @@ class Adjoint(BaseForm):
"_form",
"_repr",
"_arguments",
"ufl_operands",
"_hash")

def __getnewargs__(self):
return (self._form)

def __new__(cls, *args, **kw):
form = args[0]
if isinstance(form, FormSum):
# Check trivial case
if form == 0:
return 0

if isinstance(form, Adjoint):
return form._form
elif isinstance(form, FormSum):
# Adjoint distributes over sums
return FormSum(*[(Adjoint(component), 1)
for component in form.components()])
Expand All @@ -44,6 +51,7 @@ def __init__(self, form):
raise ValueError("Can only take Adjoint of a 2-form.")

self._form = form
self.ufl_operands = (self._form,)
self._hash = None
self._repr = "Adjoint(%s)" % repr(self._form)

Expand All @@ -58,6 +66,13 @@ def _analyze_form_arguments(self):
"""The arguments of adjoint are the reverse of the form arguments."""
self._arguments = self._form.arguments()[::-1]

def __eq__(self, other):
if not isinstance(other, Adjoint):
nbouziani marked this conversation as resolved.
Show resolved Hide resolved
return False
if self is other:
return True
return (self._form == other._form)

def __str__(self):
return "Adjoint(%s)" % self._form

Expand Down
14 changes: 14 additions & 0 deletions ufl/algorithms/ad.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
# Modified by Anders Logg, 2009.

from ufl.log import warning
from ufl.form import FormSum
from ufl.action import Action
from ufl.adjoint import Adjoint
from ufl.algorithms.apply_algebra_lowering import apply_algebra_lowering
from ufl.algorithms.apply_derivatives import apply_derivatives

Expand All @@ -27,6 +30,17 @@ def expand_derivatives(form, **kwargs):
if kwargs:
warning("Deprecation: expand_derivatives no longer takes any keyword arguments")

if isinstance(form, FormSum):
return FormSum(*[(expand_derivatives(component), 1) for component in form.components()])
if isinstance(form, Action):
return Action(expand_derivatives(form._left), expand_derivatives(form._right))
if isinstance(form, Adjoint):
dform = expand_derivatives(form._form)
if not dform:
# If dform == 0
return dform
raise NotImplementedError('Adjoint derivative is not supported.')

# Lower abstractions for tensor-algebra types into index notation
form = apply_algebra_lowering(form)

Expand Down
Loading