Skip to main content

Module nl_tape

Module nl_tape 

Source
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§

FuncallData
Boxed payload of TapeOp::Funcall. The library is kept alive by the Arc; name is the registered function name; args carries positional arguments where real-valued args reference earlier tape slots and string args are inline literals.
HybridTape
Summand
Tape
A flattened expression tape. The result of evaluation is the value at slot ops.len() - 1 (i.e. the last op).

Enums§

SummandOp
One slot in a per-summand local tape.
TapeFuncallArg
One argument of a TapeOp::Funcall. Real arguments are tape-slot indices (their values come from the running vals[] during forward); string arguments are owned literals (AMPL h<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 index i (read from the input x slice 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 when expr (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 via build_recursive with an empty ExternalResolver::default() — it has no resolver of its own. Without this pre-scan a funcall buried in a promoted CSE would reach build_recursive’s Expr::Funcall arm and panic with the misleading unresolved 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.) Whether HybridTape::build_multi can build exprs, 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_summand panics on those, so a caller choosing between the hybrid path and the flat Tape path has to ask first — there is nothing to catch.