Open
Description
When using disk checkpointing with tape.timestepper()
(i.e., with SingleDiskStorageSchedule
), we observe inconsistent behavior in the forward solve even before invoking any functional or derivative evaluation. This appears specifically when updating a DirichletBC
field during a time-stepping loop.
Consider the following reproducer:
from firedrake import *
from firedrake.adjoint import *
from checkpoint_schedules import SingleDiskStorageSchedule as scheduler
# from checkpoint_schedules import SingleMemoryStorageSchedule as scheduler
continue_annotation()
enable_disk_checkpointing()
tape = get_working_tape()
tape.enable_checkpointing(scheduler())
mesh = checkpointable_mesh(UnitSquareMesh(5, 5))
V = FunctionSpace(mesh, "CG", 2)
T = Function(V).interpolate(0.0)
u = TrialFunction(V)
v = TestFunction(V)
a = inner(grad(u), grad(v)) * dx
x = SpatialCoordinate(mesh)
F = Function(V)
control = Control(F)
F.interpolate(sin(x[0] * pi) * sin(2 * x[1] * pi))
L = F * v * dx
bcs = [DirichletBC(V, T, (1,))]
uu = Function(V)
for i in tape.timestepper(iter(range(3))):
T.assign(T + 1.0)
solve(a == L, uu, bcs=bcs, solver_parameters={"ksp_type": "preonly", "pc_type": "lu"})
obj = assemble(uu * uu * dx)
pause_annotation()
print(f"Objective at the end of taping:{obj}")
rf = ReducedFunctional(obj, control)
print(f"Objective by calling functional: {rf(F)}")
would produce inconsistent values:
Objective at the end of taping:4.000901957850139
Objective by calling functional: 1.0009019578501521
whereas NOT checkpointing to disk, by using SingleMemoryStorageSchedule
as scheduler (line 4) gives consistent AND different values:
Objective at the end of taping:9.000901957850123
Objective by calling functional: 9.000901957850061