(adjoint_options, shapes, state_norm)
In-place modifies the adjoint options to choose or wrap the norm function.
In-place modifies the adjoint options to choose or wrap the norm function.
In - place modifies the adjoint options to choose or wrap the norm function .
def handle_adjoint_norm_(adjoint_options, shapes, state_norm):
    """In-place modifies the adjoint options to choose or wrap the norm function."""

    # This is the default adjoint norm on the backward pass: a mixed norm over the tuple of inputs.
    def default_adjoint_norm(tensor_tuple):
        t, y, adj_y, *adj_params = tensor_tuple
        # (If the state is actually a flattened tuple then this will be unpacked again in state_norm.)
        return max(t.abs(), state_norm(y), state_norm(adj_y), _mixed_norm(adj_params))

    if "norm" not in adjoint_options:
        # `adjoint_options` was not explicitly specified by the user. Use the default norm.
        adjoint_options["norm"] = default_adjoint_norm
    else:
        # `adjoint_options` was explicitly specified by the user...
        try:
            adjoint_norm = adjoint_options['norm']
        except KeyError:
            # ...but they did not specify the norm argument. Back to plan A: use the default norm.
            adjoint_options['norm'] = default_adjoint_norm
        else:
            # ...and they did specify the norm argument.
            if adjoint_norm == 'seminorm':
                # They told us they want to use seminorms. Slight modification to plan A: use the default norm,
                # but ignore the parameter state
                def adjoint_seminorm(tensor_tuple):
                    t, y, adj_y, *adj_params = tensor_tuple
                    # (If the state is actually a flattened tuple then this will be unpacked again in state_norm.)
                    return max(t.abs(), state_norm(y), state_norm(adj_y))
                adjoint_options['norm'] = adjoint_seminorm
            else:
                # And they're using their own custom norm.
                if shapes is None:
                    # The state on the forward pass was a tensor, not a tuple. We don't need to do anything, they're
                    # already going to get given the full adjoint state as (t, y, adj_y, adj_params)
                    pass  # this branch included for clarity
                else:
                    # This is the bit that is tuple/tensor abstraction-breaking, because the odeint machinery
                    # doesn't know about the tupled nature of the forward state. We need to tell the user's adjoint
                    # norm about that ourselves.

                    def _adjoint_norm(tensor_tuple):
                        t, y, adj_y, *adj_params = tensor_tuple
                        y = _flat_to_shape(y, (), shapes)
                        adj_y = _flat_to_shape(adj_y, (), shapes)
                        return adjoint_norm((t, *y, *adj_y, *adj_params))
                    adjoint_options['norm'] = _adjoint_norm
def handle_adjoint_norm_ ( adjoint_options , shapes , state_norm ) : # This is the default adjoint norm on the backward pass: a mixed norm over the tuple of inputs. def default_adjoint_norm ( tensor_tuple ) : t , y , adj_y , * adj_params = tensor_tuple # (If the state is actually a flattened tuple then this will be unpacked again in state_norm.) return max ( t . abs ( ) , state_norm ( y ) , state_norm ( adj_y ) , _mixed_norm ( adj_params ) ) if "norm" not in adjoint_options : # `adjoint_options` was not explicitly specified by the user. Use the default norm. adjoint_options [ "norm" ] = default_adjoint_norm else : # `adjoint_options` was explicitly specified by the user... try : adjoint_norm = adjoint_options [ 'norm' ] except KeyError : # ...but they did not specify the norm argument. Back to plan A: use the default norm. adjoint_options [ 'norm' ] = default_adjoint_norm else : # ...and they did specify the norm argument. if adjoint_norm == 'seminorm' : # They told us they want to use seminorms. Slight modification to plan A: use the default norm, # but ignore the parameter state def adjoint_seminorm ( tensor_tuple ) : t , y , adj_y , * adj_params = tensor_tuple # (If the state is actually a flattened tuple then this will be unpacked again in state_norm.) return max ( t . abs ( ) , state_norm ( y ) , state_norm ( adj_y ) ) adjoint_options [ 'norm' ] = adjoint_seminorm else : # And they're using their own custom norm. if shapes is None : # The state on the forward pass was a tensor, not a tuple. We don't need to do anything, they're # already going to get given the full adjoint state as (t, y, adj_y, adj_params) pass # this branch included for clarity else : # This is the bit that is tuple/tensor abstraction-breaking, because the odeint machinery # doesn't know about the tupled nature of the forward state. We need to tell the user's adjoint # norm about that ourselves. def _adjoint_norm ( tensor_tuple ) : t , y , adj_y , * adj_params = tensor_tuple y = _flat_to_shape ( y , ( ) , shapes ) adj_y = _flat_to_shape ( adj_y , ( ) , shapes ) return adjoint_norm ( ( t , * y , * adj_y , * adj_params ) ) adjoint_options [ 'norm' ] = _adjoint_norm