80 opts[
"fsens_err_con"] =
true;
86 opts[
"interpolation_type"] = std::string(
"polynomial");
88 opts[
"interpolation_type"] = std::string(
"hermite");
108template <
typename Expr,
typename... Vars>
109auto jacobian(
const Expr &expression,
const Vars &...variables) {
110 if constexpr (std::is_same_v<Expr, SymbolicScalar>) {
112 std::vector<SymbolicScalar> vars;
113 (vars.push_back(variables), ...);
116 return SymbolicScalar::jacobian(expression, v_cat);
120 static_assert(std::is_same_v<Expr, casadi::MX>,
121 "Automatic Jacobian only supported for Symbolic types currently.");
131 return cas_fn.size1_in(input_idx) * cas_fn.size2_in(input_idx);
136 return cas_fn.size1_out(output_idx) * cas_fn.size2_out(output_idx);
141 throw InvalidArgument(context +
": selected function output must be scalar");
146 const std::string &context,
const std::string &rhs_name) {
147 if (lhs.size1() != rhs.size1() || lhs.size2() != rhs.size2()) {
149 " must have the same shape as the differentiation variables");
158 return casadi::MX::zeros(rows, cols * directions);
162 const int directions = rows * cols;
163 std::vector<casadi::MX> blocks;
164 blocks.reserve(
static_cast<std::size_t
>(directions));
166 for (
int direction = 0; direction < directions; ++direction) {
167 casadi::DM e = casadi::DM::zeros(directions, 1);
169 blocks.push_back(casadi::MX::reshape(casadi::MX(e), rows, cols));
172 return casadi::MX::horzcat(blocks);
176 int block_cols,
int n_blocks) {
177 std::vector<casadi::MX> cols;
178 cols.reserve(
static_cast<std::size_t
>(n_blocks));
180 for (
int block = 0; block < n_blocks; ++block) {
181 casadi::MX block_mx =
182 stacked(casadi::Slice(), casadi::Slice(block * block_cols, (block + 1) * block_cols));
183 cols.push_back(casadi::MX::reshape(block_mx, block_rows * block_cols, 1));
186 return casadi::MX::horzcat(cols);
190 int output_idx,
int input_idx) {
191 return fn.
casadi_function().name() +
"_" + suffix +
"_o" + std::to_string(output_idx) +
"_i" +
192 std::to_string(input_idx);
196 int objective_output_idx,
int constraint_output_idx,
199 std::to_string(objective_output_idx) +
"_con" + std::to_string(constraint_output_idx) +
200 "_i" + std::to_string(input_idx);
204 const std::string &name) {
205 if (expr.numel() != 1) {
211 const SymbolicArg &multipliers,
const std::string &context) {
212 const casadi::MX objective_mx = objective.
get();
213 const casadi::MX constraints_mx = constraints.
get();
214 const casadi::MX multipliers_mx = multipliers.
get();
217 if (constraints_mx.numel() != multipliers_mx.numel()) {
218 throw InvalidArgument(context +
": multipliers must have the same number of entries as "
219 "the constraint output");
222 const casadi::MX constraints_vec =
223 casadi::MX::reshape(constraints_mx, constraints_mx.numel(), 1);
224 const casadi::MX multipliers_vec =
225 casadi::MX::reshape(multipliers_mx, multipliers_mx.numel(), 1);
226 return objective_mx + casadi::MX::dot(multipliers_vec, constraints_vec);
232 const int out_rows = cas_fn.size1_out(output_idx);
233 const int out_cols = cas_fn.size2_out(output_idx);
236 std::vector<casadi::MX> outputs = cas_fn(inputs);
238 casadi::Function fwd = cas_fn.forward(nfwd);
240 std::vector<casadi::MX> args;
241 args.reserve(
static_cast<std::size_t
>(cas_fn.n_in() * 2 + cas_fn.n_out()));
242 args.insert(args.end(), inputs.begin(), inputs.end());
243 args.insert(args.end(), outputs.begin(), outputs.end());
245 for (
int i = 0; i < cas_fn.n_in(); ++i) {
246 if (i == input_idx) {
247 args.push_back(
make_basis_seed(cas_fn.size1_in(i), cas_fn.size2_in(i)));
249 args.push_back(
make_zero_seed(cas_fn.size1_in(i), cas_fn.size2_in(i), nfwd));
253 std::vector<casadi::MX> sensitivities = fwd(args);
260 const int in_rows = cas_fn.size1_in(input_idx);
261 const int in_cols = cas_fn.size2_in(input_idx);
264 std::vector<casadi::MX> outputs = cas_fn(inputs);
266 casadi::Function adj = cas_fn.reverse(nadj);
268 std::vector<casadi::MX> args;
269 args.reserve(
static_cast<std::size_t
>(cas_fn.n_in() + cas_fn.n_out() * 2));
270 args.insert(args.end(), inputs.begin(), inputs.end());
271 args.insert(args.end(), outputs.begin(), outputs.end());
273 for (
int i = 0; i < cas_fn.n_out(); ++i) {
274 if (i == output_idx) {
275 args.push_back(
make_basis_seed(cas_fn.size1_out(i), cas_fn.size2_out(i)));
277 args.push_back(
make_zero_seed(cas_fn.size1_out(i), cas_fn.size2_out(i), nadj));
281 std::vector<casadi::MX> sensitivities = adj(args);
292inline SensitivityRecommendation
296 if (parameter_count <= 0) {
297 throw InvalidArgument(
"select_sensitivity_regime: parameter_count must be positive");
299 if (output_count <= 0) {
300 throw InvalidArgument(
"select_sensitivity_regime: output_count must be positive");
302 if (horizon_length <= 0) {
303 throw InvalidArgument(
"select_sensitivity_regime: horizon_length must be positive");
305 if (opts.forward_parameter_threshold <= 0 || opts.adjoint_parameter_threshold <= 0 ||
306 opts.checkpoint_horizon_threshold <= 0 || opts.very_long_horizon_threshold <= 0 ||
307 opts.checkpoint_steps <= 0 || opts.checkpoint_steps_very_long <= 0) {
308 throw InvalidArgument(
"select_sensitivity_regime: thresholds must be positive");
310 if (opts.forward_parameter_threshold > opts.adjoint_parameter_threshold) {
312 "select_sensitivity_regime: forward_parameter_threshold must not exceed "
313 "adjoint_parameter_threshold");
315 if (opts.checkpoint_horizon_threshold > opts.very_long_horizon_threshold) {
317 "select_sensitivity_regime: checkpoint_horizon_threshold must not exceed "
318 "very_long_horizon_threshold");
325 recommendation.
stiff = stiff;
327 const bool forward_friendly =
328 parameter_count <= opts.forward_parameter_threshold && output_count >= parameter_count;
329 const bool long_horizon = horizon_length >= opts.checkpoint_horizon_threshold;
330 const bool output_limited = parameter_count > output_count;
331 const bool parameter_heavy = parameter_count >= opts.adjoint_parameter_threshold;
333 if (forward_friendly) {
335 return recommendation;
338 if (long_horizon && output_limited) {
343 ? opts.checkpoint_steps_very_long
344 : opts.checkpoint_steps;
345 return recommendation;
350 return recommendation;
356inline SensitivityRecommendation
358 int horizon_length = 1,
bool stiff =
false,
374 int horizon_length = 1,
bool stiff =
false,
387 fn, recommendation.
uses_forward_mode() ?
"fwd_jac" :
"adj_jac", output_idx, input_idx),
389 std::vector<SymbolicArg>{SymbolicArg(jacobian_expr)});
399inline auto jacobian(
const std::vector<SymbolicArg> &expressions,
400 const std::vector<SymbolicArg> &variables) {
403 return SymbolicScalar::jacobian(expr_cat, var_cat);
422 if (g.size2() > 1 && g.size1() == 1) {
479 const casadi::MX expr_mx = expr.
get();
480 const casadi::MX vars_mx = vars.
get();
481 const casadi::MX direction_mx = direction.
get();
486 const casadi::MX gradient_mx = casadi::MX::gradient(expr_mx, vars_mx);
487 const casadi::MX hvp_mx = casadi::MX::jtimes(gradient_mx, vars_mx, direction_mx,
false);
488 return to_eigen(casadi::MX::reshape(hvp_mx, vars_mx.size1(), vars_mx.size2()));
509 "lagrangian_hessian_vector_product"),
526 std::vector<casadi::MX> outputs = cas_fn(inputs);
527 casadi::MX direction =
528 casadi::MX::sym(
"hvp_direction", cas_fn.size1_in(input_idx), cas_fn.size2_in(input_idx));
530 casadi::MX hvp_expr =
as_mx(
534 args.emplace_back(direction);
548 int constraint_output_idx,
int input_idx = 0) {
550 "lagrangian_hessian_vector_product");
552 "lagrangian_hessian_vector_product");
557 std::vector<casadi::MX> outputs = cas_fn(inputs);
558 casadi::MX multipliers =
559 casadi::MX::sym(
"lagrange_multipliers", cas_fn.size1_out(constraint_output_idx),
560 cas_fn.size2_out(constraint_output_idx));
561 casadi::MX direction = casadi::MX::sym(
"lagrangian_hvp_direction", cas_fn.size1_in(input_idx),
562 cas_fn.size2_in(input_idx));
565 outputs.at(objective_output_idx), outputs.at(constraint_output_idx), inputs.at(input_idx),
566 multipliers, direction));
569 args.emplace_back(multipliers);
570 args.emplace_back(direction);
573 constraint_output_idx, input_idx),
574 args, std::vector<SymbolicArg>{
SymbolicArg(hvp_expr)});
600 const std::vector<SymbolicArg> &vars,
610 const std::vector<SymbolicArg> &vars,
621 const std::vector<SymbolicArg> &vars,
Symbolic function wrapper around CasADi with Eigen-native IO.
C++20 concepts constraining valid Janus scalar types.
Core type aliases for numeric and symbolic Eigen/CasADi interop.
Sparsity pattern analysis, graph coloring, and sparse derivative evaluators.
Wrapper around casadi::Function providing Eigen-native IO.
Definition Function.hpp:46
const casadi::Function & casadi_function() const
Access the underlying CasADi function.
Definition Function.hpp:186
Input validation failed (e.g., mismatched sizes, invalid parameters).
Definition JanusError.hpp:31
Universal symbolic argument wrapper for Function inputs/outputs.
Definition JanusTypes.hpp:250
SymbolicScalar get() const
Get underlying CasADi MX object.
Definition JanusTypes.hpp:286
void validate_scalar_expression(const casadi::MX &expr, const std::string &context, const std::string &name)
Definition AutoDiff.hpp:203
std::vector< SymbolicScalar > to_mx_vector(const std::vector< SymbolicArg > &args)
Definition Sparsity.hpp:592
int output_numel(const Function &fn, int output_idx)
Definition AutoDiff.hpp:134
std::string lagrangian_function_name(const Function &fn, const std::string &suffix, int objective_output_idx, int constraint_output_idx, int input_idx)
Definition AutoDiff.hpp:195
casadi::MX lagrangian_scalar(const SymbolicArg &objective, const SymbolicArg &constraints, const SymbolicArg &multipliers, const std::string &context)
Definition AutoDiff.hpp:210
std::string sensitivity_function_name(const Function &fn, const std::string &suffix, int output_idx, int input_idx)
Definition AutoDiff.hpp:189
casadi::MX reverse_block_jacobian(const Function &fn, int output_idx, int input_idx)
Definition AutoDiff.hpp:257
casadi::MX forward_block_jacobian(const Function &fn, int output_idx, int input_idx)
Definition AutoDiff.hpp:229
void validate_same_shape(const casadi::MX &lhs, const casadi::MX &rhs, const std::string &context, const std::string &rhs_name)
Definition AutoDiff.hpp:145
void validate_scalar_output(const Function &fn, int output_idx, const std::string &context)
Definition AutoDiff.hpp:139
casadi::MX make_basis_seed(int rows, int cols)
Definition AutoDiff.hpp:161
casadi::MX stacked_blocks_to_columns(const casadi::MX &stacked, int block_rows, int block_cols, int n_blocks)
Definition AutoDiff.hpp:175
std::vector< SymbolicScalar > symbolic_inputs_like(const Function &fn, const std::string &prefix)
Definition Sparsity.hpp:629
std::vector< SymbolicArg > to_symbolic_args(const std::vector< SymbolicScalar > &args)
Definition Sparsity.hpp:601
casadi::MX make_zero_seed(int rows, int cols, int directions)
Definition AutoDiff.hpp:157
int input_numel(const Function &fn, int input_idx)
Definition AutoDiff.hpp:129
void validate_function_indices(const Function &fn, int output_idx, int input_idx, const char *where)
Definition Sparsity.hpp:618
Definition Diagnostics.hpp:19
JanusVector< SymbolicScalar > SymbolicVector
Eigen vector of MX elements.
Definition JanusTypes.hpp:72
@ Forward
Forward-mode (column compression).
Definition Sparsity.hpp:586
SensitivityRecommendation select_sensitivity_regime(int parameter_count, int output_count, int horizon_length=1, bool stiff=false, const SensitivitySwitchOptions &opts=SensitivitySwitchOptions())
Recommend a sensitivity regime from parameter/output counts.
Definition AutoDiff.hpp:293
Function sensitivity_jacobian(const Function &fn, int output_idx=0, int input_idx=0, int horizon_length=1, bool stiff=false, const SensitivitySwitchOptions &opts=SensitivitySwitchOptions())
Build a Jacobian function for one output/input block using the recommended regime.
Definition AutoDiff.hpp:373
SymbolicMatrix lagrangian_hessian_vector_product(const SymbolicArg &objective, const SymbolicArg &constraints, const SymbolicArg &vars, const SymbolicArg &multipliers, const SymbolicArg &direction)
Hessian-vector product of a Lagrangian, i.e. a second-order adjoint action.
Definition AutoDiff.hpp:503
SymbolicMatrix hessian_lagrangian(const SymbolicArg &objective, const SymbolicArg &constraints, const SymbolicArg &vars, const SymbolicArg &multipliers)
Hessian of Lagrangian for constrained optimization.
Definition AutoDiff.hpp:459
JanusMatrix< SymbolicScalar > SymbolicMatrix
Eigen matrix of MX elements.
Definition JanusTypes.hpp:71
CheckpointInterpolation
Checkpoint interpolation recommendation for long-horizon adjoints.
Definition AutoDiff.hpp:29
@ Polynomial
Definition AutoDiff.hpp:32
@ None
Definition AutoDiff.hpp:30
@ Hermite
Definition AutoDiff.hpp:31
SensitivityRegime
Sensitivity regime selected for a Jacobian-like derivative workload.
Definition AutoDiff.hpp:20
@ Adjoint
Few outputs, many parameters.
Definition AutoDiff.hpp:22
@ Forward
Many outputs, relatively few parameters.
Definition AutoDiff.hpp:21
@ CheckpointedAdjoint
Adjoint with checkpoint recommendations for long horizons.
Definition AutoDiff.hpp:23
Eigen::Matrix< casadi::MX, Eigen::Dynamic, Eigen::Dynamic > to_eigen(const casadi::MX &m)
Convert CasADi MX to Eigen matrix of MX.
Definition JanusTypes.hpp:213
auto jacobian(const Expr &expression, const Vars &...variables)
Computes Jacobian of an expression with respect to variables.
Definition AutoDiff.hpp:109
SymbolicVector sym_gradient(const SymbolicArg &expr, const SymbolicArg &vars)
Symbolic gradient (for scalar-output functions).
Definition AutoDiff.hpp:416
SymbolicMatrix hessian_vector_product(const SymbolicArg &expr, const SymbolicArg &vars, const SymbolicArg &direction)
Hessian-vector product for a scalar expression without forming the dense Hessian.
Definition AutoDiff.hpp:477
casadi::MX SymbolicScalar
CasADi MX symbolic scalar.
Definition JanusTypes.hpp:70
SymbolicScalar as_mx(const SymbolicVector &v)
Get the underlying MX representation of a SymbolicVector.
Definition JanusTypes.hpp:173
SymbolicMatrix hessian(const SymbolicArg &expr, const SymbolicArg &vars)
Hessian matrix (second-order derivatives).
Definition AutoDiff.hpp:437
Result of Janus sensitivity regime selection.
Definition AutoDiff.hpp:50
int parameter_count
Definition AutoDiff.hpp:53
bool uses_checkpointing() const
Definition AutoDiff.hpp:63
bool uses_forward_mode() const
Definition AutoDiff.hpp:59
int horizon_length
Definition AutoDiff.hpp:55
SensitivityRegime regime
Definition AutoDiff.hpp:51
int steps_per_checkpoint
Definition AutoDiff.hpp:56
int output_count
Definition AutoDiff.hpp:54
casadi::Dict integrator_options() const
Convert the recommendation into SUNDIALS/CasADi integrator options.
Definition AutoDiff.hpp:76
CheckpointInterpolation checkpoint_interpolation
Definition AutoDiff.hpp:52
bool stiff
Definition AutoDiff.hpp:57
int casadi_direction_count() const
Definition AutoDiff.hpp:65
bool uses_reverse_mode() const
Definition AutoDiff.hpp:61
Heuristics controlling automatic forward-vs-adjoint selection.
Definition AutoDiff.hpp:38
int forward_parameter_threshold
Definition AutoDiff.hpp:39
int adjoint_parameter_threshold
Definition AutoDiff.hpp:40
int very_long_horizon_threshold
Definition AutoDiff.hpp:42
int checkpoint_steps
Definition AutoDiff.hpp:43
int checkpoint_steps_very_long
Definition AutoDiff.hpp:44
int checkpoint_horizon_threshold
Definition AutoDiff.hpp:41