firedrakeproject/firedrake

BUG: Adjoint inconsistency with self-assignment via projection

Closed this issue · 1 comments

Describe the bug
An adjoint inconsistency when adding an extra calculation which should not change the derivative.

Steps to Reproduce

from firedrake import *
from firedrake.adjoint import *

import numpy as np

mesh = UnitIntervalMesh(10)
X = SpatialCoordinate(mesh)
space = FunctionSpace(mesh, "Lagrange", 1)
test = TestFunction(space)

continue_annotation()
u = Function(space, name="u").interpolate(X[0] - 0.5)
u_ref = u.copy(deepcopy=True)
v = Function(space).assign(u)
u.project(u)
J = assemble(v * v * dx)
pause_annotation()

_ = compute_gradient(J, Control(u))
adj_value = u.block_variable.adj_value

assert np.allclose(adj_value.dat.data_ro,
                   assemble(2 * inner(u_ref, test) * dx).dat.data_ro)

leads to

WARNING:root:Adjoint value is None, is the functional independent of the control variable?
Traceback (most recent call last):
  File "/home/maddison/build/tlm_adjoint/firedrake_bugs/adjoint_self_assignment.py", line 22, in <module>
    assert np.allclose(adj_value.dat.data_ro,
AttributeError: 'NoneType' object has no attribute 'dat'

Commenting out the u.project(u) line leads to a correct derivative.

Expected behavior
The derivative should be unaffected.

Environment:
Ubuntu 22.04, recent Firedrake build.

Additional Info

Changing to an Expr, u.project(2 * u), leads to the same error.

This looks I've misunderstood the behaviour. Here Control(u) refers to the last version, which is the result of the project, and not the first version, which is the input to project. The derivative with respect to the last version is, correctly, zero.