예제 #1
0
 def _ad_assign_numpy(dst, src, offset):
     dst.assign(
         backend.Constant(
             numpy.reshape(src[offset:offset + dst.value_size()],
                           dst.ufl_shape)))
     offset += dst.value_size()
     return dst, offset
예제 #2
0
def test_scalar_parameters_adjoint(J, a, dJda, seed=0.1):
    info_blue("Running Taylor remainder convergence analysis for the adjoint model ... ")

    functional_values = []
    f_direct = J(a)

    a = numpy.array([float(x) for x in a])
    dJda = numpy.array(dJda)

    perturbation_direction = a/5.0
    perturbation_sizes = [seed / (2**i) for i in range(5)]
    perturbations = [a * i for i in perturbation_sizes]
    for x in perturbations:
        da = [backend.Constant(a[i] + x[i]) for i in range(len(a))]
        functional_values.append(J(da))

    # First-order Taylor remainders (not using adjoint)
    no_gradient = [abs(perturbed_f - f_direct) for perturbed_f in functional_values]

    info("Taylor remainder without adjoint information: " + str(no_gradient))
    info("Convergence orders for Taylor remainder without adjoint information (should all be 1): " + str(convergence_order(no_gradient)))

    with_gradient = []
    for i in range(len(perturbations)):
        remainder = abs(functional_values[i] - f_direct - numpy.dot(dJda, perturbations[i]))
        with_gradient.append(remainder)

    info("Taylor remainder with adjoint information: " + str(with_gradient))
    info("Convergence orders for Taylor remainder with adjoint information (should all be 2): " + str(convergence_order(with_gradient)))

    return min(convergence_order(with_gradient))
예제 #3
0
 def __copy_data(self, m):
     """Returns a deep copy of the given Function/Constant."""
     if hasattr(m, "vector"):
         return backend.Function(m.function_space())
     elif hasattr(m, "value_size"):
         return backend.Constant(m(()))
     else:
         raise TypeError('Unknown control type %s.' % str(type(m)))
예제 #4
0
        def make_mdot(vec):
            if isinstance(self.m, FunctionControl):
                mdot = self.m.set_perturbation(
                    backend.Function(self.m.data().function_space(), vec))
            elif isinstance(self.m, ConstantControl):
                mdot = self.m.set_perturbation(backend.Constant(vec))

            return mdot
예제 #5
0
def postprocess(dJdparam, project, list_type):
    if isinstance(dJdparam, list):
        return delist([postprocess(x, project, list_type) for x in dJdparam],
                      list_type=list_type)
    else:
        if project:
            dJdparam = project_test(dJdparam)

        if isinstance(dJdparam, float):
            dJdparam = backend.Constant(dJdparam)

    return dJdparam
예제 #6
0
    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)
예제 #7
0
def _taylor_test_multi_control(J, m, Jm, dJdm, HJm, seed, perturbation_direction, value, size=None):
    if perturbation_direction is None:
        perturbation_direction = [None] * len(m.controls)
    perturbation_direction = enlist(perturbation_direction)

    if value is None:
        value = [None] * len(m.controls)
    value = enlist(value)

    # Create a deep copy of the initial control values
    m_cpy = []
    for c in m:
        if isinstance(c.data(), backend.Function):
            m_cpy.append(c.data().copy(deepcopy=True))
        elif isinstance(c.data(), backend.MultiMeshFunction):
            m_cpy.append(c.data().copy(deepcopy=True))
        else:
            m_cpy.append(backend.Constant(c.data()))

    # Build a objective version restricted to the i'th control
    def J_cmp(J, i):
        m_values = list(m_cpy)
        def out(x):
            m_values[i] = x
            return J(m_values)
        return out

    # A Hessian version restricted to the i'th control
    if HJm is not None:
        HJm_cmp = lambda i: lambda x: HJm.__class__(HJm.J, m[i])(x)
    else:
        HJm_cmp = lambda i: None

    # Perform the Taylor tests for each control
    min_conv = 1e10
    for i in range(len(m.controls)):
        print("\nRunning Taylor test for control {}".format(i))
        conv = _taylor_test_single_control(J_cmp(J, i), m[i], Jm, dJdm[i],
                                           HJm_cmp(i), seed, perturbation_direction[i], value[i], size)
        min_conv = min(min_conv, conv)

    return min_conv
예제 #8
0
def scale(obj, factor):
    """ A generic function to scale Functions,
        Constants and lists, numpy arrays, ...
    """

    if hasattr(obj, "function_space"):
        # dolfin.Function of dolfin.MultiMeshFunctionSpace
        if isinstance(obj.function_space(), compatibility.multi_mesh_function_space_type):
            scaled_obj = backend.MultiMeshFunction(obj.function_space(), factor * obj.vector())
        else:
            scaled_obj = backend.Function(obj.function_space(), factor * obj.vector())
    elif isinstance(obj, backend.Constant):
        # dolfin.Constant
        scaled_obj = backend.Constant(factor * constant_to_array(obj))
    else:
        # Lists, numpy arrays, ...
        try:
            scaled_obj = factor * obj
        except TypeError:
            # Lists of dolfin objects?
            scaled_obj = [scale(o, factor) for o in obj]

    return scaled_obj
예제 #9
0
def test_scalar_parameter_adjoint(J, a, dJda, seed=None):
    info_blue("Running Taylor remainder convergence analysis for the adjoint model ... ")

    functional_values = []
    f_direct = J(a)

    if seed is None:
        seed = float(a)/5.0
        if seed == 0.0:
            seed = 0.1

    perturbations = [seed / (2**i) for i in range(5)]

    for da in (backend.Constant(float(a) + x) for x in perturbations):
        functional_values.append(J(da))

    # First-order Taylor remainders (not using adjoint)
    no_gradient = [abs(perturbed_f - f_direct) for perturbed_f in functional_values]

    info("Taylor remainder without adjoint information: " + str(no_gradient))
    info("Convergence orders for Taylor remainder without adjoint information (should all be 1): " + str(convergence_order(no_gradient)))

    with_gradient = []
    gradient_fd   = []
    for i in range(len(perturbations)):
        gradient_fd.append((functional_values[i] - f_direct)/perturbations[i])

        remainder = abs(functional_values[i] - f_direct - float(dJda)*perturbations[i])
        with_gradient.append(remainder)

    info("Taylor remainder with adjoint information: " + str(with_gradient))
    info("Convergence orders for Taylor remainder with adjoint information (should all be 2): " + str(convergence_order(with_gradient)))

    info("Gradients (finite differencing): " + str(gradient_fd))
    info("Gradient (adjoint): " + str(dJda))

    return min(convergence_order(with_gradient))
예제 #10
0
def compute_gradient(J,
                     param,
                     forget=True,
                     ignore=[],
                     callback=lambda var, output: None,
                     project=False):
    if not isinstance(J, Functional):
        raise ValueError("J must be of type dolfin_adjoint.Functional.")

    flag = misc.pause_annotation()

    enlisted_controls = enlist(param)
    param = ListControl(enlisted_controls)

    if backend.parameters["adjoint"]["allow_zero_derivatives"]:
        dJ_init = []
        for c in enlisted_controls:
            if isinstance(c.data(), backend.Constant):
                dJ_init.append(backend.Constant(0))
            elif isinstance(c.data(), backend.Function):
                space = c.data().function_space()
                dJ_init.append(backend.Function(space))

    else:
        dJ_init = [None] * len(enlisted_controls)

    dJdparam = enlisted_controls.__class__(dJ_init)

    last_timestep = adjglobals.adjointer.timestep_count

    ignorelist = []
    for fn in ignore:
        if isinstance(fn, backend.Function):
            ignorelist.append(adjglobals.adj_variables[fn])
        elif isinstance(fn, str):
            ignorelist.append(libadjoint.Variable(fn, 0, 0))
        else:
            ignorelist.append(fn)

    for i in range(adjglobals.adjointer.timestep_count):
        adjglobals.adjointer.set_functional_dependencies(J, i)

    for i in range(adjglobals.adjointer.equation_count)[::-1]:
        fwd_var = adjglobals.adjointer.get_forward_variable(i)
        if fwd_var in ignorelist:
            info("Ignoring the adjoint equation for %s" % fwd_var)
            continue

        (adj_var, output) = adjglobals.adjointer.get_adjoint_solution(i, J)

        callback(adj_var, output.data)

        storage = libadjoint.MemoryStorage(output)
        storage.set_overwrite(True)
        adjglobals.adjointer.record_variable(adj_var, storage)
        fwd_var = libadjoint.Variable(adj_var.name, adj_var.timestep,
                                      adj_var.iteration)

        out = param.equation_partial_derivative(adjglobals.adjointer,
                                                output.data, i, fwd_var)
        dJdparam = _add(dJdparam, out)

        if last_timestep > adj_var.timestep:
            # We have hit a new timestep, and need to compute this timesteps' \partial J/\partial m contribution
            out = param.functional_partial_derivative(adjglobals.adjointer, J,
                                                      adj_var.timestep)
            dJdparam = _add(dJdparam, out)

        last_timestep = adj_var.timestep

        if forget is None:
            pass
        elif forget:
            adjglobals.adjointer.forget_adjoint_equation(i)
        else:
            adjglobals.adjointer.forget_adjoint_values(i)

    rename(J, dJdparam, param)

    misc.continue_annotation(flag)

    return postprocess(dJdparam, project, list_type=enlisted_controls)
예제 #11
0
def update_constants(d):
    for constant in d:
        name = constant.adj_name
        if name not in scalar_parameters:
            backend.Constant.assign(constant_objects[name],
                                    backend.Constant(d[constant]))
예제 #12
0
 def make_const(arr):
     return [backend.Constant(x) for x in arr]
예제 #13
0
def _taylor_test_single_control(J, m, Jm, dJdm, HJm, seed, perturbation_direction, value, size=None):
    from . import function, controls

    # Default to five runs/perturbations is none given
    if size is None:
        size = 5
    
    # Check inputs
    if not isinstance(m, libadjoint.Parameter):
        raise ValueError("m must be a valid control instance.")

    def get_const(val):
        if isinstance(val, str):
            return float(constant.constant_values[val])
        else:
            return float(val)

    def get_value(param, value):
        if value is not None:
            return value
        else:
            try:
                return param.data()
            except libadjoint.exceptions.LibadjointErrorNeedValue:
                info_red("Do you need to pass forget=False to compute_gradient?")
                raise

    # First, compute perturbation sizes.
    seed_default = 0.01
    if seed is None:
        if isinstance(m, controls.ConstantControl):
            seed = get_const(m.a) / 5.0

            if seed == 0.0: seed = 0.1
        elif isinstance(m, controls.FunctionControl):
            ic = get_value(m, value)
            if len(ic.vector()) == 1: # our control is in R
                seed = float(ic) / 5.0
            else:
                seed = seed_default
        else:
            seed = seed_default

    perturbation_sizes = [seed/(2.0**i) for i in range(size)]

    # Next, compute the perturbation direction.
    if perturbation_direction is None:
        if isinstance(m, controls.ConstantControl):
            perturbation_direction = 1
        elif isinstance(m, controls.ConstantControls):
            perturbation_direction = numpy.array([get_const(x)/5.0 for x in m.v])
        elif isinstance(m, controls.FunctionControl):
            ic = get_value(m, value)

            # Check for MultiMeshFunction_space
            if isinstance(ic.function_space(), compatibility.multi_mesh_function_space_type):
                perturbation_direction = backend.MultiMeshFunction(ic.function_space())
            else:
                perturbation_direction = function.Function(ic.function_space())

            compatibility.randomise(perturbation_direction)

        else:
            raise libadjoint.exceptions.LibadjointErrorNotImplemented("Don't know how to compute a perturbation direction")
    else:
        if isinstance(m, controls.FunctionControl):
            ic = get_value(m, value)
        elif isinstance(m, controls.ConstantControl):
            perturbation_direction = float(perturbation_direction)

    # So now compute the perturbations:
    if not isinstance(perturbation_direction, (backend.Function, backend.MultiMeshFunction)):
        perturbations = [x*perturbation_direction for x in perturbation_sizes]
    else:
        perturbations = []
        for x in perturbation_sizes:
            perturbation = perturbation_direction.copy(deepcopy=True)
            vec = perturbation.vector()
            vec *= x
            perturbations.append(perturbation)

    # And now the perturbed inputs:
    if isinstance(m, controls.ConstantControl):
        pinputs = [backend.Constant(get_const(m.a) + x) for x in perturbations]
    elif isinstance(m, controls.ConstantControls):
        a = numpy.array([get_const(x) for x in m.v])

        def make_const(arr):
            return [backend.Constant(x) for x in arr]

        pinputs = [make_const(a + x) for x in perturbations]
    elif isinstance(m, controls.FunctionControl):
        pinputs = []
        for x in perturbations:
            pinput = x.copy(deepcopy=True)
            vec = pinput.vector()
            vec += ic.vector()
            pinputs.append(pinput)

    # Issue 34: We must evaluate HJm before we evaluate the tape at the
    # perturbed controls below.
    if HJm is not None:
        HJm_values = []
        for perturbation in perturbations:
            HJmp = HJm(perturbation)
            HJm_values.append(HJmp)

    # At last: the common bit!
    functional_values = []
    for pinput in pinputs:
        Jp = J(pinput)
        functional_values.append(Jp)

    # First-order Taylor remainders (not using adjoint)
    no_gradient = [abs(perturbed_J - Jm) for perturbed_J in functional_values]

    info("Taylor remainder without gradient information: " + str(no_gradient))
    info("Convergence orders for Taylor remainder without gradient information (should all be 1): " + str(convergence_order(no_gradient)))

    with_gradient = []
    for i in range(len(perturbations)):
        if isinstance(m, controls.ConstantControl) or isinstance(m, controls.ConstantControls):
            remainder = taylor_remainder_with_gradient(m, Jm, dJdm, functional_values[i], perturbations[i])
        else:
            remainder = taylor_remainder_with_gradient(m, Jm, dJdm, functional_values[i], perturbations[i], ic=ic)
        with_gradient.append(remainder)

    if min(with_gradient + no_gradient) < 1e-16:
        warning("Warning: The Taylor remainders are close to machine precision (< %s). Try increasing the seed value in case the Taylor remainder test fails." % min(with_gradient + no_gradient))

    info("Taylor remainder with gradient information: " + str(with_gradient))
    info("Convergence orders for Taylor remainder with gradient information (should all be 2): " + str(convergence_order(with_gradient)))

    if HJm is not None:
        with_hessian = []
        if isinstance(m, controls.ConstantControl):
            for i in range(len(perturbations)):
                remainder = abs(functional_values[i] - Jm - float(dJdm)*perturbations[i] - 0.5*perturbations[i]*HJm_values[i])
                with_hessian.append(remainder)
        elif isinstance(m, controls.ConstantControls):
            for i in range(len(perturbations)):
                remainder = abs(functional_values[i] - Jm - numpy.dot(dJdm, perturbations[i]) - 0.5*numpy.dot(perturbations[i], HJm_values[i]))
                with_hessian.append(remainder)
        elif isinstance(m, controls.FunctionControl):
            for i in range(len(perturbations)):
                remainder = abs(functional_values[i] - Jm - dJdm.vector().inner(perturbations[i].vector()) - 0.5*perturbations[i].vector().inner(HJm_values[i].vector()))
                with_hessian.append(remainder)

        info("Taylor remainder with Hessian information: " + str(with_hessian))
        info("Convergence orders for Taylor remainder with Hessian information (should all be 3): " + str(convergence_order(with_hessian)))
        return min(convergence_order(with_hessian))
    else:
        return min(convergence_order(with_gradient))
예제 #14
0
    def derivative(self, adjointer, variable, dependencies, values):
        if self.verbose:
            for dep in dependencies:
                print(variable.timestep, "derive wrt ", dep.name)
        if self.regform is not None and variable.name == self.regform.coefficients(
        )[0].name():  # derivative wrt the contorl
            if self.verbose: " derivatives wrt the controls "
            raise RuntimeError("""The derivative of a regularisation term
                                  doesn't work properly and shouldn't be used"""
                               )
            return self.regfunc.derivative(adjointer, variable, dependencies,
                                           values)
        else:
            # transate finish_time: UGLY!!
            if "FINISH_TIME" in self.times:
                final_time = _time_levels(adjointer,
                                          adjointer.timestep_count - 1)[1]
                self.times[self.times.index("FINISH_TIME")] = final_time

            if self.verbose:
                print("derive ", variable.timestep, " num values ",
                      len(values))
            timesteps = self._derivative_timesteps(adjointer, variable)

            ff = [backend.Constant(0.0)] * len(self.coords)
            for i in range(len(self.coords)):
                if self.skip[i]:
                    if self.verbose: print("skipped")
                else:
                    if len(timesteps
                           ) is 1:  # only occurs at start and finish time
                        tsoi = timesteps[-1]
                        if tsoi is 0:
                            toi = _time_levels(adjointer, tsoi)[0]
                            ind = -1
                        else:
                            toi = _time_levels(adjointer, tsoi)[-1]
                            ind = 0
                    else:
                        if len(values) is 1:  # one value (easy)
                            tsoi = timesteps[-1]
                            toi = _time_levels(adjointer, tsoi)[0]
                            ind = 0
                        elif len(values) is 2:  # two values (hard)
                            tsoi = timesteps[-1]
                            toi = _time_levels(adjointer, tsoi)[0]
                            if _time_levels(adjointer, tsoi)[1] in self.times:
                                ind = 0
                            else:
                                ind = 1
                        else:  # three values (easy)
                            tsoi = timesteps[1]
                            toi = _time_levels(adjointer, tsoi)[0]
                            ind = 1
                    coef = values[ind].data
                    ref = self.refs[i][self.times.index(toi)]
                    if self.index[i] is None: solu = coef(self.coords[i])
                    else: solu = coef(self.coords[i])[self.index[i]]
                    ff[i] = backend.Constant(self.alpha * 2.0 *
                                             (solu - float(ref)))

            # Set up linear combinations to be projected
            form = ff[0] * self.basis[0]
            for i in range(1, len(self.coords)):
                form += ff[i] * self.basis[i]

            v = backend.project(form, self.func.function_space())

            return adjlinalg.Vector(v)