Expand description
Flat-tape reverse-mode AD for .nl expression trees.
Replaces the FD-based Hessian path with a port of the tape AD used
in ripopt::nl::autodiff. The tape is a Vec<TapeOp> where each op
refers to its operands by tape-slot index; forward evaluation runs
through the slice once filling a parallel Vec<f64> of values, and
reverse-mode adjoints walk the same buffer backwards.
Sparse Hessians are computed by forward-over-reverse: for each
variable j that the tape depends on, run a forward tangent sweep
seeded with e_j, then a second-order reverse sweep that produces
column j of the Hessian. The caller supplies a (row, col) -> nnz position map (lower triangle, row >= col), and contributions are
accumulated in place — the outer loop in eval_h calls the same
map for the objective and every active constraint, so every
Lagrangian term lands in the right slot.
Common subexpressions are tape-emitted once: when the recursive
builder hits Expr::Cse(rc) it keys on the Arc pointer identity,
emitting the body the first time and returning the cached
result-slot index on subsequent references. The forward pass then
computes each CSE once and the reverse pass folds adjoints from
every reference into a single slot — exact chain-rule behaviour.
Structs§
- Funcall
Data - Boxed payload of
TapeOp::Funcall. The library is kept alive by theArc;nameis the registered function name;argscarries positional arguments where real-valued args reference earlier tape slots and string args are inline literals. - Hybrid
Tape - Summand
- Tape
- A flattened expression tape. The result of evaluation is the value
at slot
ops.len() - 1(i.e. the last op).
Enums§
- Summand
Op - One slot in a per-summand local tape.
- Tape
Funcall Arg - One argument of a
TapeOp::Funcall. Real arguments are tape-slot indices (their values come from the runningvals[]during forward); string arguments are owned literals (AMPLh<len>:<chars>tokens). - TapeOp
- One operation in the flattened tape. Operand fields are tape-slot
indices into the same tape;
Var(i)references problem variable indexi(read from the inputxslice during forward).
Functions§
- hybrid_
supported - Pass-1 helper: per-root walk that increments
counts[ptr]the first time a Cse pointer is encountered in this root. Recursing into the body is gated on the first visit to avoid quadratic blowup on heavily shared CSE DAGs. True whenexpr(or any subexpression) is an AMPL external function call. The hybrid summand path rejects funcalls outright, but the promoted-CSE branch emits a shared CSE body viabuild_recursivewith an emptyExternalResolver::default()— it has no resolver of its own. Without this pre-scan a funcall buried in a promoted CSE would reachbuild_recursive’sExpr::Funcallarm and panic with the misleadingunresolved AMPL funcall id <n>message, instead of the clear “not supported on the hybrid path” message the non-promoted summand path raises. Pre-scanning makes both paths report the same reason. (Funcalls are unsupported on the hybrid path regardless of whether the id would resolve, so this never rejects a buildable tape.) WhetherHybridTape::build_multican buildexprs, i.e. none of them uses an opcode the hybrid (partial-separability) path rejects: comparisons, AND/OR/NOT, if-then-else, min/max lists, or AMPL external function calls.build_into_summandpanics on those, so a caller choosing between the hybrid path and the flatTapepath has to ask first — there is nothing to catch.