def prepare_evaluate_hessian(self, inputs, hessian_inputs, adj_inputs, relevant_dependencies): # First fetch all relevant values fwd_block_variable = self.get_outputs()[0] hessian_input = hessian_inputs[0] tlm_output = fwd_block_variable.tlm_value if hessian_input is None: return if tlm_output is None: return F_form = self._create_F_form() # Using the equation Form we derive dF/du, d^2F/du^2 * du/dm * direction. dFdu_form = backend.derivative(F_form, fwd_block_variable.saved_output) d2Fdu2 = ufl.algorithms.expand_derivatives( backend.derivative(dFdu_form, fwd_block_variable.saved_output, tlm_output)) adj_sol = self.adj_sol if adj_sol is None: raise RuntimeError("Hessian computation was run before adjoint.") bdy = self._should_compute_boundary_adjoint(relevant_dependencies) adj_sol2, adj_sol2_bdy = self._assemble_and_solve_soa_eq( dFdu_form, adj_sol, hessian_input, d2Fdu2, bdy) r = {} r["adj_sol2"] = adj_sol2 r["adj_sol2_bdy"] = adj_sol2_bdy r["form"] = F_form r["adj_sol"] = adj_sol return r
def second_derivative_action(self, dependencies, values, inner_variable, inner_contraction_vector, outer_variable, hermitian, action_vector): if isinstance(self.form, ufl.form.Form): # Find the dolfin Function corresponding to variable. dolfin_inner_variable = values[dependencies.index(inner_variable)].data dolfin_outer_variable = values[dependencies.index(outer_variable)].data dolfin_dependencies = [dep for dep in _extract_function_coeffs(self.form)] dolfin_values = [val.data for val in values] current_form = backend.replace(self.form, dict(zip(dolfin_dependencies, dolfin_values))) trial = backend.TrialFunction(dolfin_outer_variable.function_space()) d_rhs = backend.derivative(current_form, dolfin_inner_variable, inner_contraction_vector.data) d_rhs = ufl.algorithms.expand_derivatives(d_rhs) if len(d_rhs.integrals()) == 0: return None d_rhs = backend.derivative(d_rhs, dolfin_outer_variable, trial) d_rhs = ufl.algorithms.expand_derivatives(d_rhs) if len(d_rhs.integrals()) == 0: return None if hermitian: action = backend.action(backend.adjoint(d_rhs), action_vector.data) else: action = backend.action(d_rhs, action_vector.data) return adjlinalg.Vector(action) else: # RHS is a adjlinalg.Vector. Its derivative is therefore zero. raise exceptions.LibadjointErrorNotImplemented("No derivative method for constant RHS.")
def second_derivative_action(dependencies, values, inner_variable, inner_contraction_vector, outer_variable, outer_contraction_vector, hermitian, input, coefficient, context): dolfin_inner_variable = values[dependencies.index(inner_variable)].data dolfin_outer_variable = values[dependencies.index(outer_variable)].data dolfin_values = [val.data for val in values] expressions.update_expressions(frozen_expressions) constant.update_constants(frozen_constants) current_form = backend.replace(eq_lhs, dict(zip(diag_coeffs, dolfin_values))) deriv = backend.derivative(current_form, dolfin_inner_variable) args = ufl.algorithms.extract_arguments(deriv) deriv = backend.replace(deriv, {args[1]: inner_contraction_vector.data}) # contract over the middle index deriv = backend.derivative(deriv, dolfin_outer_variable) args = ufl.algorithms.extract_arguments(deriv) deriv = backend.replace(deriv, {args[1]: outer_contraction_vector.data}) # contract over the middle index # Assemble the G-matrix now, so that we can apply the Dirichlet BCs to it if len(ufl.algorithms.extract_arguments(ufl.algorithms.expand_derivatives(coefficient*deriv))) == 0: return adjlinalg.Vector(None) G = coefficient * deriv if hermitian: output = backend.action(backend.adjoint(G), input.data) else: output = backend.action(G, input.data) return adjlinalg.Vector(output)
def _assemble_soa_eq_rhs(self, dFdu_form, adj_sol, hessian_input, d2Fdu2): # Start piecing together the rhs of the soa equation b = hessian_input.copy() b_form = d2Fdu2 for bo in self.get_dependencies(): c = bo.output c_rep = bo.saved_output tlm_input = bo.tlm_value if (c == self.func and not self.linear) or tlm_input is None: continue if isinstance(c, compat.MeshType): X = backend.SpatialCoordinate(c) dFdu_adj = backend.action(backend.adjoint(dFdu_form), adj_sol) d2Fdudm = ufl.algorithms.expand_derivatives( backend.derivative(dFdu_adj, X, tlm_input)) if len(d2Fdudm.integrals()) > 0: b -= compat.assemble_adjoint_value(d2Fdudm) elif not isinstance(c, backend.DirichletBC): b_form += backend.derivative(dFdu_form, c_rep, tlm_input) b_form = ufl.algorithms.expand_derivatives(b_form) if len(b_form.integrals()) > 0: b_form = backend.adjoint(b_form) b -= compat.assemble_adjoint_value(backend.action(b_form, adj_sol)) return b
def equation_partial_second_derivative(self, adjointer, adjoint, i, variable, m_dot): form = adjresidual.get_residual(i) if form is not None: form = -form mesh = ufl.algorithms.extract_arguments( form)[0].function_space().mesh() fn_space = backend.FunctionSpace(mesh, "R", 0) dparam = backend.Function(fn_space) dparam.vector()[:] = 1.0 * float(self.coeff) d2param = backend.Function(fn_space) d2param.vector()[:] = 1.0 * float(self.coeff) * m_dot diff_form = ufl.algorithms.expand_derivatives( backend.derivative(form, get_constant(self.a), dparam)) if diff_form is None: return None diff_form = ufl.algorithms.expand_derivatives( backend.derivative(diff_form, get_constant(self.a), d2param)) if diff_form is None: return None # Let's see if the form actually depends on the parameter m if len(diff_form.integrals()) != 0: dFdm = backend.assemble(diff_form) # actually - dF/dm assert isinstance(dFdm, backend.GenericVector) out = dFdm.inner(adjoint.vector()) return out else: return None # dF/dm is zero, return None
def functional_partial_second_derivative(self, adjointer, J, timestep, m_dot): form = J.get_form(adjointer, timestep) if form is None: return None for coeff in ufl.algorithms.extract_coefficients(form): try: mesh = coeff.function_space().mesh() fn_space = backend.FunctionSpace(mesh, "R", 0) break except: pass dparam = backend.Function(fn_space) dparam.vector()[:] = 1.0 * float(self.coeff) d = backend.derivative(form, get_constant(self.a), dparam) d = ufl.algorithms.expand_derivatives(d) d2param = backend.Function(fn_space) d2param.vector()[:] = 1.0 * float(self.coeff) * m_dot d = backend.derivative(d, get_constant(self.a), d2param) d = ufl.algorithms.expand_derivatives(d) if len(d.integrals()) != 0: return backend.assemble(d) else: return None
def __init__(self, form, control): if not isinstance(control.control, backend.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 = backend_types.Function(self.V) self.form = ufl.replace(form, {u: self.u}) self.trial = backend.TrialFunction(self.V) self.dform = backend.derivative(self.form, self.u, self.trial) if len( ufl.algorithms.extract_arguments( ufl.algorithms.expand_derivatives(self.dform))) == 0: raise ValueError("Form must depend on control") self.test = backend.TestFunction(self.V) self.hess = ufl.algorithms.expand_derivatives( backend.derivative(self.dform, self.u, self.test)) if len(ufl.algorithms.extract_arguments(self.hess)) == 0: self.zero_hess = True else: self.zero_hess = False
def equation_partial_second_derivative(self, adjointer, adjoint, i, variable, m_dot): form = adjresidual.get_residual(i) if form is not None: form = -form mesh = ufl.algorithms.extract_arguments(form)[0].function_space().mesh() fn_space = backend.FunctionSpace(mesh, "R", 0) dparam = backend.Function(fn_space) dparam.vector()[:] = 1.0 * float(self.coeff) d2param = backend.Function(fn_space) d2param.vector()[:] = 1.0 * float(self.coeff) * m_dot diff_form = ufl.algorithms.expand_derivatives(backend.derivative(form, get_constant(self.a), dparam)) if diff_form is None: return None diff_form = ufl.algorithms.expand_derivatives(backend.derivative(diff_form, get_constant(self.a), d2param)) if diff_form is None: return None # Let's see if the form actually depends on the parameter m if len(diff_form.integrals()) != 0: dFdm = backend.assemble(diff_form) # actually - dF/dm assert isinstance(dFdm, backend.GenericVector) out = dFdm.inner(adjoint.vector()) return out else: return None # dF/dm is zero, return None
def evaluate_hessian_component(self, inputs, hessian_inputs, adj_inputs, block_variable, idx, relevant_dependencies, prepared=None): form = prepared hessian_input = hessian_inputs[0] adj_input = adj_inputs[0] c1 = block_variable.output c1_rep = block_variable.saved_output if isinstance(c1, backend.Function): dc = backend.TestFunction(c1.function_space()) elif isinstance(c1, compat.ExpressionType): mesh = form.ufl_domain().ufl_cargo() W = c1._ad_function_space(mesh) dc = backend.TestFunction(W) elif isinstance(c1, backend.Constant): mesh = compat.extract_mesh_from_form(form) dc = backend.TestFunction(c1._ad_function_space(mesh)) elif isinstance(c1, compat.MeshType): pass else: return None if isinstance(c1, compat.MeshType): X = backend.SpatialCoordinate(c1) dform = backend.derivative(form, X) else: dform = backend.derivative(form, c1_rep, dc) hessian_outputs = hessian_input * compat.assemble_adjoint_value(dform) for other_idx, bv in relevant_dependencies: c2_rep = bv.saved_output tlm_input = bv.tlm_value if tlm_input is None: continue if isinstance(c2_rep, compat.MeshType): X = backend.SpatialCoordinate(c2_rep) ddform = backend.derivative(dform, X, tlm_input) else: ddform = backend.derivative(dform, c2_rep, tlm_input) hessian_outputs += adj_input * compat.assemble_adjoint_value( ddform) if isinstance(c1, compat.ExpressionType): return [(hessian_outputs, W)] else: return hessian_outputs
def evaluate_tlm_component(self, inputs, tlm_inputs, block_variable, idx, prepared=None): F_form = prepared["form"] dFdu = prepared["dFdu"] V = self.get_outputs()[idx].output.function_space() bcs = [] dFdm = 0. dFdm_shape = 0. for block_variable in self.get_dependencies(): tlm_value = block_variable.tlm_value c = block_variable.output c_rep = block_variable.saved_output if isinstance(c, backend.DirichletBC): if tlm_value is None: bcs.append(compat.create_bc(c, homogenize=True)) else: bcs.append(tlm_value) continue elif isinstance(c, compat.MeshType): X = backend.SpatialCoordinate(c) c_rep = X if tlm_value is None: continue if c == self.func and not self.linear: continue if isinstance(c, compat.MeshType): dFdm_shape += compat.assemble_adjoint_value( backend.derivative(-F_form, c_rep, tlm_value)) else: dFdm += backend.derivative(-F_form, c_rep, tlm_value) if isinstance(dFdm, float): v = dFdu.arguments()[0] dFdm = backend.inner(backend.Constant(numpy.zeros(v.ufl_shape)), v) * backend.dx dFdm = compat.assemble_adjoint_value(dFdm) + dFdm_shape dudm = backend.Function(V) return self._assemble_and_solve_tlm_eq( compat.assemble_adjoint_value(dFdu, bcs=bcs), dFdm, dudm, bcs)
def derivative_action(self, dependencies, values, variable, contraction_vector, hermitian): if contraction_vector.data is None: return adjlinalg.Vector(None) if isinstance(self.form, ufl.form.Form): # Find the dolfin Function corresponding to variable. dolfin_variable = values[dependencies.index(variable)].data dolfin_dependencies = [dep for dep in _extract_function_coeffs(self.form)] dolfin_values = [val.data for val in values] current_form = backend.replace(self.form, dict(zip(dolfin_dependencies, dolfin_values))) trial = backend.TrialFunction(dolfin_variable.function_space()) d_rhs = backend.derivative(current_form, dolfin_variable, trial) if hermitian: action = backend.action(backend.adjoint(d_rhs), contraction_vector.data) else: action = backend.action(d_rhs, contraction_vector.data) return adjlinalg.Vector(action) else: # RHS is a adjlinalg.Vector. Its derivative is therefore zero. return adjlinalg.Vector(None)
def prepare_evaluate_adj(self, inputs, adj_inputs, relevant_dependencies): fwd_block_variable = self.get_outputs()[0] u = fwd_block_variable.output dJdu = adj_inputs[0] F_form = self._create_F_form() dFdu = backend.derivative(F_form, fwd_block_variable.saved_output, backend.TrialFunction(u.function_space())) dFdu_form = backend.adjoint(dFdu) dJdu = dJdu.copy() compute_bdy = self._should_compute_boundary_adjoint( relevant_dependencies) adj_sol, adj_sol_bdy = self._assemble_and_solve_adj_eq( dFdu_form, dJdu, compute_bdy) self.adj_sol = adj_sol if self.adj_cb is not None: self.adj_cb(adj_sol) if self.adj_bdy_cb is not None and compute_bdy: self.adj_bdy_cb(adj_sol_bdy) r = {} r["form"] = F_form r["adj_sol"] = adj_sol r["adj_sol_bdy"] = adj_sol_bdy return r
def __init__(self, form, F, u, bcs, mass, solver_parameters, J): '''form is M.u - F(u). F is the nonlinear equation, F(u) := 0.''' RHS.__init__(self, form) self.F = F self.u = u self.bcs = bcs self.mass = mass self.solver_parameters = solver_parameters self.J = J or backend.derivative(F, u) # We want to mark that the RHS term /also/ depends on # the previous value of u, as that's what we need to initialise # the nonlinear solver. var = adjglobals.adj_variables[self.u] self.ic_var = None if backend.parameters["adjoint"]["fussy_replay"]: can_depend = True try: prev_var = find_previous_variable(var) except: can_depend = False if can_depend: self.ic_var = prev_var self.deps += [self.ic_var] self.coeffs += [u] else: self.ic_copy = backend.Function(u) self.ic_var = None
def derivative_action(self, dependencies, values, variable, contraction_vector, hermitian): if contraction_vector.data is None: return adjlinalg.Vector(None) if isinstance(self.form, ufl.form.Form): # Find the dolfin Function corresponding to variable. dolfin_variable = values[dependencies.index(variable)].data dolfin_dependencies = [ dep for dep in _extract_function_coeffs(self.form) ] dolfin_values = [val.data for val in values] current_form = backend.replace( self.form, dict(zip(dolfin_dependencies, dolfin_values))) trial = backend.TrialFunction(dolfin_variable.function_space()) d_rhs = backend.derivative(current_form, dolfin_variable, trial) if hermitian: action = backend.action(backend.adjoint(d_rhs), contraction_vector.data) else: action = backend.action(d_rhs, contraction_vector.data) return adjlinalg.Vector(action) else: # RHS is a adjlinalg.Vector. Its derivative is therefore zero. return adjlinalg.Vector(None)
def derivative_assembly(self, dependencies, values, variable, hermitian): replace_map = {} for i in range(len(self.deps)): if self.deps[i] == self.ic_var: continue j = dependencies.index(self.deps[i]) replace_map[self.coeffs[i]] = values[j].data diff_var = values[dependencies.index(variable)].data current_form = backend.replace(self.form, replace_map) deriv = backend.derivative(current_form, diff_var) if hermitian: deriv = backend.adjoint(deriv) bcs = [ utils.homogenize(bc) for bc in self.bcs if isinstance(bc, backend.DirichletBC) ] + [ bc for bc in self.bcs if not isinstance(bc, backend.DirichletBC) ] else: bcs = self.bcs return adjlinalg.Matrix(deriv, bcs=bcs)
def equation_partial_derivative(self, adjointer, adjoint, i, variable): form = adjresidual.get_residual(i) if form is None: return None else: form = -form fn_space = ufl.algorithms.extract_arguments(form)[0].function_space() dparam = backend.Function( backend.FunctionSpace(fn_space.mesh(), "R", 0)) dparam.vector()[:] = 1.0 dJdv = numpy.zeros(len(self.v)) for (i, a) in enumerate(self.v): diff_form = ufl.algorithms.expand_derivatives( backend.derivative(form, a, dparam)) dFdm = backend.assemble(diff_form) # actually - dF/dm assert isinstance(dFdm, backend.GenericVector) out = dFdm.inner(adjoint.vector()) dJdv[i] = out return dJdv
def second_derivative(self, adjointer, variable, dependencies, values, contraction): if contraction.data is None: return adjlinalg.Vector(None) functional_value = None for timestep in self._derivative_timesteps(adjointer, variable): functional_value = _add(functional_value, self._substitute_form(adjointer, timestep, dependencies, values)) d = backend.derivative(functional_value, values[dependencies.index(variable)].data) d = ufl.algorithms.expand_derivatives(d) d = backend.derivative(d, values[dependencies.index(variable)].data, contraction.data) if len(d.integrals()) == 0: raise SystemExit, "This isn't supposed to happen -- your functional is supposed to depend on %s" % variable return adjlinalg.Vector(d)
def functional_partial_derivative(self, adjointer, J, timestep): form = J.get_form(adjointer, timestep) if form is None: return None # OK. Now that we have the form for the functional at this timestep, let's differentiate it with respect to # my dear Constant, and be done. for coeff in ufl.algorithms.extract_coefficients(form): try: mesh = coeff.function_space().mesh() fn_space = backend.FunctionSpace(mesh, "R", 0) break except: pass dparam = backend.Function(fn_space) dparam.vector()[:] = 1.0 * float(self.coeff) d = backend.derivative(form, get_constant(self.a), dparam) d = ufl.algorithms.expand_derivatives(d) # Add the derivatives of Expressions wrt to the Constant d = self.expression_derivative(form, d) if len(d.integrals()) != 0: return backend.assemble(d) else: return None
def equation_partial_derivative(self, adjointer, adjoint, i, variable): form = adjresidual.get_residual(i) if form is not None: form = -form mesh = ufl.algorithms.extract_arguments( form)[0].function_space().mesh() fn_space = backend.FunctionSpace(mesh, "R", 0) dparam = backend.Function(fn_space) dparam.vector()[:] = 1.0 * float(self.coeff) diff_form = ufl.algorithms.expand_derivatives( backend.derivative(form, get_constant(self.a), dparam)) # Add the derivatives of Expressions wrt to the Constant diff_form = self.expression_derivative(form, diff_form) # Let's see if the form actually depends on the parameter m if len(diff_form.integrals()) != 0: dFdm = backend.assemble(diff_form) # actually - dF/dm out = adjoint.vector().inner(dFdm) else: out = None # dF/dm is zero, return None return out
def evaluate_adj_component(self, inputs, adj_inputs, block_variable, idx, prepared=None): if not self.linear and self.func == block_variable.output: # We are not able to calculate derivatives wrt initial guess. return None F_form = prepared["form"] adj_sol = prepared["adj_sol"] adj_sol_bdy = prepared["adj_sol_bdy"] c = block_variable.output c_rep = block_variable.saved_output if isinstance(c, backend.Function): trial_function = backend.TrialFunction(c.function_space()) elif isinstance(c, backend.Constant): mesh = compat.extract_mesh_from_form(F_form) trial_function = backend.TrialFunction(c._ad_function_space(mesh)) elif isinstance(c, compat.ExpressionType): mesh = F_form.ufl_domain().ufl_cargo() c_fs = c._ad_function_space(mesh) trial_function = backend.TrialFunction(c_fs) elif isinstance(c, backend.DirichletBC): tmp_bc = compat.create_bc(c, value=extract_subfunction( adj_sol_bdy, c.function_space())) return [tmp_bc] elif isinstance(c, compat.MeshType): # Using CoordianteDerivative requires us to do action before # differentiating, might change in the future. F_form_tmp = backend.action(F_form, adj_sol) X = backend.SpatialCoordinate(c_rep) dFdm = backend.derivative(-F_form_tmp, X) dFdm = compat.assemble_adjoint_value(dFdm, **self.assemble_kwargs) return dFdm dFdm = -backend.derivative(F_form, c_rep, trial_function) dFdm = backend.adjoint(dFdm) dFdm = dFdm * adj_sol dFdm = compat.assemble_adjoint_value(dFdm, **self.assemble_kwargs) if isinstance(c, compat.ExpressionType): return [[dFdm, c_fs]] else: return dFdm
def second_derivative_action(self, dependencies, values, inner_variable, inner_contraction_vector, outer_variable, hermitian, action_vector): if isinstance(self.form, ufl.form.Form): # Find the dolfin Function corresponding to variable. dolfin_inner_variable = values[dependencies.index( inner_variable)].data dolfin_outer_variable = values[dependencies.index( outer_variable)].data dolfin_dependencies = [ dep for dep in _extract_function_coeffs(self.form) ] dolfin_values = [val.data for val in values] current_form = backend.replace( self.form, dict(zip(dolfin_dependencies, dolfin_values))) trial = backend.TrialFunction( dolfin_outer_variable.function_space()) d_rhs = backend.derivative(current_form, dolfin_inner_variable, inner_contraction_vector.data) d_rhs = ufl.algorithms.expand_derivatives(d_rhs) if len(d_rhs.integrals()) == 0: return None d_rhs = backend.derivative(d_rhs, dolfin_outer_variable, trial) d_rhs = ufl.algorithms.expand_derivatives(d_rhs) if len(d_rhs.integrals()) == 0: return None if hermitian: action = backend.action(backend.adjoint(d_rhs), action_vector.data) else: action = backend.action(d_rhs, action_vector.data) return adjlinalg.Vector(action) else: # RHS is a adjlinalg.Vector. Its derivative is therefore zero. raise libadjoint.exceptions.LibadjointErrorNotImplemented( "No derivative method for constant RHS.")
def derivative_action(self, variable, contraction_vector, hermitian, input, coefficient): deriv = backend.derivative(self.current_form, variable) args = ufl.algorithms.extract_arguments(deriv) deriv = backend.replace(deriv, {args[1]: contraction_vector}) if hermitian: deriv = backend.adjoint(deriv) action = coefficient * backend.action(deriv, input) return action
def prepare_evaluate_tlm(self, inputs, tlm_inputs, relevant_outputs): fwd_block_variable = self.get_outputs()[0] u = fwd_block_variable.output F_form = self._create_F_form() # Obtain dFdu. dFdu = backend.derivative(F_form, fwd_block_variable.saved_output, backend.TrialFunction(u.function_space())) return {"form": F_form, "dFdu": dFdu}
def second_derivative(self, adjointer, variable, dependencies, values, contraction): if contraction.data is None: return adjlinalg.Vector(None) functional_value = None for timestep in self._derivative_timesteps(adjointer, variable): functional_value = _add( functional_value, self._substitute_form(adjointer, timestep, dependencies, values)) d = backend.derivative(functional_value, values[dependencies.index(variable)].data) d = ufl.algorithms.expand_derivatives(d) d = backend.derivative(d, values[dependencies.index(variable)].data, contraction.data) if len(d.integrals()) == 0: raise SystemExit( "This isn't supposed to happen -- your functional is supposed to depend on %s" % variable) return adjlinalg.Vector(d)
def __call__(self, adjointer, i, dependencies, values, variable): form = adjresidual.get_residual(i) if form is not None: form = -form fn_space = ufl.algorithms.extract_arguments(form)[0].function_space() dparam = backend.Function(backend.FunctionSpace(fn_space.mesh(), "R", 0)) dparam.vector()[:] = 1.0 * float(self.coeff) diff_form = ufl.algorithms.expand_derivatives(backend.derivative(form, get_constant(self.a), dparam)) return adjlinalg.Vector(diff_form) else: return None
def evaluate_tlm_component(self, inputs, tlm_inputs, block_variable, idx, prepared=None): form = prepared dform = 0. dform_shape = 0. for bv in self.get_dependencies(): c_rep = bv.saved_output tlm_value = bv.tlm_value if tlm_value is None: continue if isinstance(c_rep, compat.MeshType): X = backend.SpatialCoordinate(c_rep) dform_shape += compat.assemble_adjoint_value( backend.derivative(form, X, tlm_value)) else: dform += backend.derivative(form, c_rep, tlm_value) if not isinstance(dform, float): dform = compat.assemble_adjoint_value(dform) return dform + dform_shape
def evaluate_adj_component(self, inputs, adj_inputs, block_variable, idx, prepared=None): form = prepared adj_input = adj_inputs[0] c = block_variable.output c_rep = block_variable.saved_output if isinstance(c, compat.ExpressionType): # Create a FunctionSpace from self.form and Expression. # And then make a TestFunction from this space. mesh = self.form.ufl_domain().ufl_cargo() V = c._ad_function_space(mesh) dc = backend.TestFunction(V) dform = backend.derivative(form, c_rep, dc) output = compat.assemble_adjoint_value(dform) return [[adj_input * output, V]] elif isinstance(c, compat.MeshType): X = backend.SpatialCoordinate(c_rep) dform = backend.derivative(form, X) output = compat.assemble_adjoint_value(dform) return adj_input * output if isinstance(c, backend.Function): dc = backend.TestFunction(c.function_space()) elif isinstance(c, backend.Constant): mesh = compat.extract_mesh_from_form(self.form) dc = backend.TestFunction(c._ad_function_space(mesh)) dform = backend.derivative(form, c_rep, dc) output = compat.assemble_adjoint_value(dform) return adj_input * output
def expression_derivative(self, form, diff_form): """ Applies the chain rule on diff_form to add derivatives of Expressions with respect to the control. """ coeffs = ufl.algorithms.extract_coefficients(form) # Take the derivative of Expressions with respect to Constants only # if the derivative is provided explicitly by the user expr_deriv_coeffs = [] for coeff in coeffs: # Check if the coefficient is an expression with user-defined # derivatives if not hasattr(coeff, "deval"): continue if not hasattr(coeff, "dependencies"): raise ValueError, "An expression with deval() must also \ implement the dependencies() function." if not hasattr(coeff, "copy"): raise ValueError, "An expression with deval() must also \ implement the copy() function." # Check that that expression depends on self.a elif self.a not in coeff.dependencies(): continue else: expr_deriv_coeffs.append(coeff) # Ok, so diff_form has the expression "coeff" which depends on self.a # For the following computation we temporarly change this expression # such that it returns the derivative wrt to self.a instead of # plain evaluation. # Now apply the chain rule to expand the diff_form through these # expressions for c in expr_deriv_coeffs: dc = c.copy() eval_deriv_a = lambda expr, value, x: expr.deval(value, x, self.a) dc.eval = types.MethodType(eval_deriv_a, dc) diff_form += ufl.algorithms.expand_derivatives( backend.derivative(form, c, dc)) return diff_form
def derivative(self, adjointer, variable, dependencies, values): functional_value = None for timestep in self._derivative_timesteps(adjointer, variable): functional_value = _add(functional_value, self._substitute_form(adjointer, timestep, dependencies, values)) if functional_value is None: backend.info_red("Your functional is supposed to depend on %s, but does not?" % variable) raise libadjoint.exceptions.LibadjointErrorInvalidInputs d = backend.derivative(functional_value, values[dependencies.index(variable)].data) d = ufl.algorithms.expand_derivatives(d) if len(d.integrals()) == 0: raise SystemExit, "This isn't supposed to happen -- your functional is supposed to depend on %s" % variable return adjlinalg.Vector(d)
def __call__(self, adjointer, i, dependencies, values, variable): form = adjresidual.get_residual(i) if form is not None: form = -form fn_space = ufl.algorithms.extract_arguments( form)[0].function_space() dparam = backend.Function( backend.FunctionSpace(fn_space.mesh(), "R", 0)) dparam.vector()[:] = 1.0 * float(self.coeff) diff_form = ufl.algorithms.expand_derivatives( backend.derivative(form, get_constant(self.a), dparam)) return adjlinalg.Vector(diff_form) else: return None
def derivative_assembly(self, dependencies, values, variable, hermitian): replace_map = {} for i in range(len(self.deps)): if self.deps[i] == self.ic_var: continue j = dependencies.index(self.deps[i]) replace_map[self.coeffs[i]] = values[j].data diff_var = values[dependencies.index(variable)].data current_form = backend.replace(self.form, replace_map) deriv = backend.derivative(current_form, diff_var) if hermitian: deriv = backend.adjoint(deriv) bcs = [utils.homogenize(bc) for bc in self.bcs if isinstance(bc, backend.DirichletBC)] + [bc for bc in self.bcs if not isinstance(bc, backend.DirichletBC)] else: bcs = self.bcs return adjlinalg.Matrix(deriv, bcs=bcs)
def expression_derivative(self, form, diff_form): """ Applies the chain rule on diff_form to add derivatives of Expressions with respect to the control. """ coeffs = ufl.algorithms.extract_coefficients(form) # Take the derivative of Expressions with respect to Constants only # if the derivative is provided explicitly by the user expr_deriv_coeffs = [] for coeff in coeffs: # Check if the coefficient is an expression with user-defined # derivatives if not hasattr(coeff, "dependencies"): continue if not hasattr(coeff, "user_defined_derivatives"): raise ValueError, "An expression with dependencies must also \ provide the user_defined_derivatives \ dictionary." # Check that that expression depends on self.a elif self.a not in coeff.dependencies: continue else: expr_deriv_coeffs.append(coeff) # Ok, so diff_form has the expression "coeff" which depends on self.a # For the following computation we temporarly change this expression # such that it returns the derivative wrt to self.a instead of # plain evaluation. # Now apply the chain rule to expand the diff_form through these # expressions for c in expr_deriv_coeffs: diff_form += ufl.algorithms.expand_derivatives( backend.derivative(form, c, c.user_defined_derivatives[self.a])) return diff_form
def expression_derivative(self, form, diff_form): """ Applies the chain rule on diff_form to add derivatives of Expressions with respect to the control. """ coeffs = ufl.algorithms.extract_coefficients(form) # Take the derivative of Expressions with respect to Constants only # if the derivative is provided explicitly by the user expr_deriv_coeffs = [] for coeff in coeffs: # Check if the coefficient is an expression with user-defined # derivatives if not hasattr(coeff, "dependencies"): continue if not hasattr(coeff, "user_defined_derivatives"): raise ValueError("An expression with dependencies must also \ provide the user_defined_derivatives \ dictionary.") # Check that that expression depends on self.a elif self.a not in coeff.dependencies: continue else: expr_deriv_coeffs.append(coeff) # Ok, so diff_form has the expression "coeff" which depends on self.a # For the following computation we temporarly change this expression # such that it returns the derivative wrt to self.a instead of # plain evaluation. # Now apply the chain rule to expand the diff_form through these # expressions for c in expr_deriv_coeffs: diff_form += ufl.algorithms.expand_derivatives( backend.derivative(form, c, c.user_defined_derivatives[self.a])) return diff_form
def equation_partial_derivative(self, adjointer, adjoint, i, variable): form = adjresidual.get_residual(i) if form is None: return None else: form = -form fn_space = ufl.algorithms.extract_arguments(form)[0].function_space() dparam = backend.Function(backend.FunctionSpace(fn_space.mesh(), "R", 0)) dparam.vector()[:] = 1.0 dJdv = numpy.zeros(len(self.v)) for (i, a) in enumerate(self.v): diff_form = ufl.algorithms.expand_derivatives(backend.derivative(form, a, dparam)) dFdm = backend.assemble(diff_form) # actually - dF/dm assert isinstance(dFdm, backend.GenericVector) out = dFdm.inner(adjoint.vector()) dJdv[i] = out return dJdv
def __call__(self, adjointer, i, dependencies, values, variable): diff_form = None assert self.dv is not None, "Need a perturbation direction to use in the TLM." form = adjresidual.get_residual(i) if form is None: return None else: form = -form fn_space = ufl.algorithms.extract_arguments(form)[0].function_space() dparam = backend.Function(backend.FunctionSpace(fn_space.mesh(), "R", 0)) dparam.vector()[:] = 1.0 for (a, da) in zip(self.v, self.dv): out_form = da * backend.derivative(form, a, dparam) if diff_form is None: diff_form = out_form else: diff_form += out_form return adjlinalg.Vector(diff_form)
def derivative(self, adjointer, variable, dependencies, values): functional_value = None for timestep in self._derivative_timesteps(adjointer, variable): functional_value = _add( functional_value, self._substitute_form(adjointer, timestep, dependencies, values)) if functional_value is None: backend.info_red( "Your functional is supposed to depend on %s, but does not?" % variable) raise libadjoint.exceptions.LibadjointErrorInvalidInputs d = backend.derivative(functional_value, values[dependencies.index(variable)].data) d = ufl.algorithms.expand_derivatives(d) if len(d.integrals()) == 0: raise SystemExit( "This isn't supposed to happen -- your functional is supposed to depend on %s" % variable) return adjlinalg.Vector(d)
def __call__(self, adjointer, i, dependencies, values, variable): diff_form = None assert self.dv is not None, "Need a perturbation direction to use in the TLM." form = adjresidual.get_residual(i) if form is None: return None else: form = -form fn_space = ufl.algorithms.extract_arguments(form)[0].function_space() dparam = backend.Function( backend.FunctionSpace(fn_space.mesh(), "R", 0)) dparam.vector()[:] = 1.0 for (a, da) in zip(self.v, self.dv): out_form = da * backend.derivative(form, a, dparam) if diff_form is None: diff_form = out_form else: diff_form += out_form return adjlinalg.Vector(diff_form)
def evaluate_hessian_component(self, inputs, hessian_inputs, adj_inputs, block_variable, idx, relevant_dependencies, prepared=None): c = block_variable.output if c == self.func and not self.linear: return None adj_sol2 = prepared["adj_sol2"] adj_sol2_bdy = prepared["adj_sol2_bdy"] F_form = prepared["form"] adj_sol = prepared["adj_sol"] fwd_block_variable = self.get_outputs()[0] tlm_output = fwd_block_variable.tlm_value c_rep = block_variable.saved_output # If m = DirichletBC then d^2F(u,m)/dm^2 = 0 and d^2F(u,m)/dudm = 0, # so we only have the term dF(u,m)/dm * adj_sol2 if isinstance(c, backend.DirichletBC): tmp_bc = compat.create_bc(c, value=extract_subfunction( adj_sol2_bdy, c.function_space())) return [tmp_bc] if isinstance(c_rep, backend.Constant): mesh = compat.extract_mesh_from_form(F_form) W = c._ad_function_space(mesh) elif isinstance(c, compat.ExpressionType): mesh = F_form.ufl_domain().ufl_cargo() W = c._ad_function_space(mesh) elif isinstance(c, compat.MeshType): X = backend.SpatialCoordinate(c) element = X.ufl_domain().ufl_coordinate_element() W = backend.FunctionSpace(c, element) else: W = c.function_space() dc = backend.TestFunction(W) form_adj = backend.action(F_form, adj_sol) form_adj2 = backend.action(F_form, adj_sol2) if isinstance(c, compat.MeshType): dFdm_adj = backend.derivative(form_adj, X, dc) dFdm_adj2 = backend.derivative(form_adj2, X, dc) else: dFdm_adj = backend.derivative(form_adj, c_rep, dc) dFdm_adj2 = backend.derivative(form_adj2, c_rep, dc) # TODO: Old comment claims this might break on split. Confirm if true or not. d2Fdudm = ufl.algorithms.expand_derivatives( backend.derivative(dFdm_adj, fwd_block_variable.saved_output, tlm_output)) hessian_output = 0 # We need to add terms from every other dependency # i.e. the terms d^2F/dm_1dm_2 for _, bv in relevant_dependencies: c2 = bv.output c2_rep = bv.saved_output if isinstance(c2, backend.DirichletBC): continue tlm_input = bv.tlm_value if tlm_input is None: continue if c2 == self.func and not self.linear: continue # TODO: If tlm_input is a Sum, this crashes in some instances? if isinstance(c2_rep, compat.MeshType): X = backend.SpatialCoordinate(c2_rep) d2Fdm2 = ufl.algorithms.expand_derivatives( backend.derivative(dFdm_adj, X, tlm_input)) else: d2Fdm2 = ufl.algorithms.expand_derivatives( backend.derivative(dFdm_adj, c2_rep, tlm_input)) if d2Fdm2.empty(): continue hessian_output -= compat.assemble_adjoint_value(d2Fdm2) if not d2Fdudm.empty(): # FIXME: This can be empty in the multimesh case, ask sebastian hessian_output -= compat.assemble_adjoint_value(d2Fdudm) hessian_output -= compat.assemble_adjoint_value(dFdm_adj2) if isinstance(c, compat.ExpressionType): return [(hessian_output, W)] else: return hessian_output