Source code for firedrake.adjoint.ufl_constraints

import ufl
import ufl.algorithms
from pyadjoint.optimization.constraints import (
    Constraint, EqualityConstraint, InequalityConstraint,
)

import firedrake


[docs] class UFLConstraint(Constraint): """ Easily implement scalar constraints using UFL. The form must be a 0-form that depends on a Function control. """ def __init__(self, form, control): if not isinstance(control.control, firedrake.Function): raise NotImplementedError("Only implemented for Function controls") args = ufl.algorithms.extract_arguments(form) if len(args) != 0: raise ValueError("Must be a rank-zero form, i.e. a functional") u = control.control self.V = u.function_space() # We want to make a copy of the control purely for use # in the constraint, so that our writing it isn't # bothering anyone else self.u = firedrake.Function(self.V) self.form = ufl.replace(form, {u: self.u}) self.test = firedrake.TestFunction(self.V) self.dform = firedrake.derivative(self.form, self.u, self.test) if len(ufl.algorithms.extract_arguments( ufl.algorithms.expand_derivatives(self.dform) )) == 0: raise ValueError("Form must depend on control") self.trial = firedrake.TrialFunction(self.V) self.hess = ufl.algorithms.expand_derivatives( firedrake.derivative(self.dform, self.u, self.trial) ) self.zero_hess = len(ufl.algorithms.extract_arguments(self.hess)) == 0
[docs] def update_control(self, m): if isinstance(m, list): assert len(m) == 1 m = m[0] if isinstance(m, firedrake.Function): self.u.assign(m) else: self.u._ad_assign_numpy(self.u, m, 0)
[docs] def function(self, m): self.update_control(m) b = firedrake.assemble(self.form) return firedrake.Constant(b)
[docs] def jacobian(self, m): if isinstance(m, list): assert len(m) == 1 m = m[0] self.update_control(m) out = [firedrake.assemble(self.dform)] return out
[docs] def jacobian_action(self, m, dm, result): """Computes the Jacobian action of c(m) in direction dm. Stores the result in result. """ if isinstance(m, list): assert len(m) == 1 m = m[0] self.update_control(m) form = firedrake.action(self.dform, dm) result.assign(firedrake.assemble(form))
[docs] def jacobian_adjoint_action(self, m, dp, result): """Computes the Jacobian adjoint action of c(m) in direction dp. Stores the result in result. """ if isinstance(m, list): assert len(m) == 1 m = m[0] self.update_control(m) asm = firedrake.assemble( dp * ufl.replace(self.dform, {self.trial: self.test}) ) if isinstance(result, firedrake.Cofunction): result.assign(asm) else: raise NotImplementedError("Do I need to untangle all controls?")
[docs] def hessian_action(self, m, dm, dp, result): """Computes the Hessian action of c(m) in direction dm and dp. Stores the result in result. """ if isinstance(m, list): assert len(m) == 1 m = m[0] self.update_control(m) H = dm * ufl.replace(self.hess, {self.trial: dp}) if isinstance(result, firedrake.Function): if self.zero_hess: result.assign(0) else: result.assign(firedrake.assemble(H)) else: raise NotImplementedError("Do I need to untangle all controls?")
[docs] def output_workspace(self): """Return an object like the output of c(m) for calculations.""" return firedrake.Constant(firedrake.assemble(self.form))
def _get_constraint_dim(self): """Returns the number of constraint components.""" return 1
[docs] class UFLEqualityConstraint(UFLConstraint, EqualityConstraint): pass
[docs] class UFLInequalityConstraint(UFLConstraint, InequalityConstraint): pass