def interpolate(v, V, annotate=None, name=None): '''The interpolate call changes Function data, and so it too must be annotated so that the adjoint and tangent linear models may be constructed automatically by libadjoint. To disable the annotation of this function, just pass :py:data:`annotate=False`. This is useful in cases where the interpolation is known to be irrelevant or diagnostic for the purposes of the adjoint computation (such as interpolating fields to other function spaces for the purposes of visualisation).''' out = backend.interpolate(v, V) if name is not None: out.adj_name = name to_annotate = utils.to_annotate(annotate) if isinstance(v, backend.Function) and to_annotate: rhsdep = adjglobals.adj_variables[v] if adjglobals.adjointer.variable_known(rhsdep): rhs = InterpolateRHS(v, V) identity_block = utils.get_identity_block(V) solving.register_initial_conditions(zip(rhs.coefficients(),rhs.dependencies()), linear=True) dep = adjglobals.adj_variables.next(out) if backend.parameters["adjoint"]["record_all"]: adjglobals.adjointer.record_variable(dep, libadjoint.MemoryStorage(adjlinalg.Vector(out))) initial_eq = libadjoint.Equation(dep, blocks=[identity_block], targets=[dep], rhs=rhs) cs = adjglobals.adjointer.register_equation(initial_eq) solving.do_checkpoint(cs, dep, rhs) return out
def interpolate(*args, **kwargs): """Interpolation is overloaded to ensure that the returned Function object is overloaded. We are not able to annotate the interpolation call at the moment. """ output = backend.interpolate(*args, **kwargs) return create_overloaded_object(output)
def evaluate_adj_component(self, inputs, adj_inputs, block_variable, idx, prepared=None): adj_inputs = adj_inputs[0] c = block_variable.output if c not in self.expression.user_defined_derivatives: return None for key in self.expression._ad_attributes_dict: if key not in self.expression.ad_ignored_attributes: setattr(self.expression.user_defined_derivatives[c], key, self.expression._ad_attributes_dict[key]) adj_output = None for adj_pair in adj_inputs: adj_input = adj_pair[0] V = adj_pair[1] if adj_output is None: adj_output = 0.0 interp = backend.interpolate( self.expression.user_defined_derivatives[c], V) if isinstance(c, (backend.Constant, AdjFloat)): adj_output += adj_input.inner(interp.vector()) else: vec = adj_input * interp.vector() adj_func = backend.Function(V, vec) num_sub_spaces = V.num_sub_spaces() if num_sub_spaces > 1: for i in range(num_sub_spaces): adj_output += backend.interpolate( adj_func.sub(i), c.function_space()).vector() else: adj_output += backend.interpolate( adj_func, c.function_space()).vector() return adj_output
def interpolate(v, V, annotate=None, name=None): '''The interpolate call changes Function data, and so it too must be annotated so that the adjoint and tangent linear models may be constructed automatically by libadjoint. To disable the annotation of this function, just pass :py:data:`annotate=False`. This is useful in cases where the interpolation is known to be irrelevant or diagnostic for the purposes of the adjoint computation (such as interpolating fields to other function spaces for the purposes of visualisation).''' out = backend.interpolate(v, V) out = utils.function_to_da_function(out) if name is not None: out.adj_name = name to_annotate = utils.to_annotate(annotate) if to_annotate: if isinstance(v, backend.Function): rhsdep = adjglobals.adj_variables[v] if adjglobals.adjointer.variable_known(rhsdep): rhs = InterpolateRHS(v, V) identity_block = utils.get_identity_block(V) solving.register_initial_conditions(zip( rhs.coefficients(), rhs.dependencies()), linear=True) dep = adjglobals.adj_variables.next(out) if backend.parameters["adjoint"]["record_all"]: adjglobals.adjointer.record_variable( dep, libadjoint.MemoryStorage(adjlinalg.Vector(out))) initial_eq = libadjoint.Equation(dep, blocks=[identity_block], targets=[dep], rhs=rhs) cs = adjglobals.adjointer.register_equation(initial_eq) solving.do_checkpoint(cs, dep, rhs) elif annotate is not None: outvar = adjglobals.adj_variables[out] solving.register_initial_conditions([ [out, outvar], ], linear=True) return out
def derivative_action(self, dependencies, values, variable, contraction_vector, hermitian): if not hermitian: return adjlinalg.Vector(backend.interpolate(contraction_vector.data, self.V)) else: # For future reference, the transpose action of the interpolation operator # is (in pseudocode!): # # for target_dof in target: # figure out what element it lives in, to compute src_dofs # for src_dof in src_dofs: # basis = the value of the basis function of src_dof at the node of target_dof # # # all of the above is exactly the same as the forward interpolation. # # forward interpolation would do: # # target_coefficients[target_dof] += basis * src_coefficients[src_dof] # # # but the adjoint action is: # src_coefficients[src_dof] += basis * target_coefficients[target_dof] raise libadjoint.exceptions.LibadjointErrorNotImplemented("Can't transpose an interpolation operator yet, sorry!")
def derivative_action(self, dependencies, values, variable, contraction_vector, hermitian): if not hermitian: return adjlinalg.Vector( backend.interpolate(contraction_vector.data, self.V)) else: # For future reference, the transpose action of the interpolation operator # is (in pseudocode!): # # for target_dof in target: # figure out what element it lives in, to compute src_dofs # for src_dof in src_dofs: # basis = the value of the basis function of src_dof at the node of target_dof # # # all of the above is exactly the same as the forward interpolation. # # forward interpolation would do: # # target_coefficients[target_dof] += basis * src_coefficients[src_dof] # # # but the adjoint action is: # src_coefficients[src_dof] += basis * target_coefficients[target_dof] raise libadjoint.exceptions.LibadjointErrorNotImplemented( "Can't transpose an interpolation operator yet, sorry!")
def evaluate_hessian_component(self, inputs, hessian_inputs, adj_inputs, block_variable, idx, relevant_dependencies, prepared=None): hessian_inputs = hessian_inputs[0] adj_inputs = adj_inputs[0] c1 = block_variable.output if c1 not in self.expression.user_defined_derivatives: return None first_deriv = self.expression.user_defined_derivatives[c1] for key in self.expression._ad_attributes_dict: if key not in self.expression.ad_ignored_attributes: setattr(first_deriv, key, self.expression._ad_attributes_dict[key]) hessian_output = None for _, bo2 in relevant_dependencies: c2 = bo2.output tlm_input = bo2.tlm_value if tlm_input is None: continue if c2 not in first_deriv.user_defined_derivatives: continue second_deriv = first_deriv.user_defined_derivatives[c2] for key in self.expression._ad_attributes_dict: if key not in self.expression.ad_ignored_attributes: setattr(second_deriv, key, self.expression._ad_attributes_dict[key]) for adj_pair in adj_inputs: adj_input = adj_pair[0] V = adj_pair[1] if hessian_output is None: hessian_output = 0.0 # TODO: Seems we can only project and not interpolate ufl.algebra.Product in dolfin. # Consider the difference and which actually makes sense here. interp = backend.project(tlm_input * second_deriv, V) if isinstance(c1, (backend.Constant, AdjFloat)): hessian_output += adj_input.inner(interp.vector()) else: vec = adj_input * interp.vector() hessian_func = backend.Function(V, vec) num_sub_spaces = V.num_sub_spaces() if num_sub_spaces > 1: for i in range(num_sub_spaces): hessian_output += backend.interpolate( hessian_func.sub(i), c1.function_space()).vector() else: hessian_output += backend.interpolate( hessian_func, c1.function_space()).vector() for hessian_pair in hessian_inputs: if hessian_output is None: hessian_output = 0.0 hessian_input = hessian_pair[0] V = hessian_pair[1] interp = backend.interpolate(first_deriv, V) if isinstance(c1, (backend.Constant, AdjFloat)): hessian_output += hessian_input.inner(interp.vector()) else: vec = hessian_input * interp.vector() hessian_func = backend.Function(V, vec) num_sub_spaces = V.num_sub_spaces() if num_sub_spaces > 1: for i in range(num_sub_spaces): hessian_output += backend.interpolate( hessian_func.sub(i), c1.function_space()).vector() else: hessian_output += backend.interpolate( hessian_func, c1.function_space()).vector() return hessian_output
def __call__(self, dependencies, values): return adjlinalg.Vector(backend.interpolate(values[0].data, self.V))