From 9e212a5d56fbfe42b600188a35f805eeab1f03b5 Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Wed, 25 Feb 2026 17:29:57 -0500 Subject: [PATCH 1/2] [CPPAD] Enhance tracing functions to support selected output indices --- include/pyoptinterface/cppad_interface.hpp | 7 +++- lib/cppad_interface.cpp | 49 ++++++++++++++++++---- lib/cppad_interface_ext.cpp | 5 ++- 3 files changed, 49 insertions(+), 12 deletions(-) diff --git a/include/pyoptinterface/cppad_interface.hpp b/include/pyoptinterface/cppad_interface.hpp index b3b020e..c76e46f 100644 --- a/include/pyoptinterface/cppad_interface.hpp +++ b/include/pyoptinterface/cppad_interface.hpp @@ -32,8 +32,11 @@ ADFunDouble sparse_hessian(const ADFunDouble &f, const sparsity_pattern_t &patte const std::vector &p_values); // Transform ExpressionGraph to CppAD function -ADFunDouble cppad_trace_graph_constraints(const ExpressionGraph &graph); -ADFunDouble cppad_trace_graph_objective(const ExpressionGraph &graph, bool aggregate = true); +// selected_outputs: indices of outputs to trace, empty means all outputs +ADFunDouble cppad_trace_graph_constraints(const ExpressionGraph &graph, + const std::vector &selected_outputs = {}); +ADFunDouble cppad_trace_graph_objective(const ExpressionGraph &graph, bool aggregate = true, + const std::vector &selected_outputs = {}); struct CppADAutodiffGraph { diff --git a/lib/cppad_interface.cpp b/lib/cppad_interface.cpp index 26cd2b4..7dc93b2 100644 --- a/lib/cppad_interface.cpp +++ b/lib/cppad_interface.cpp @@ -427,7 +427,8 @@ CppAD::AD cppad_trace_expression( return result; } -ADFunDouble cppad_trace_graph_constraints(const ExpressionGraph &graph) +ADFunDouble cppad_trace_graph_constraints(const ExpressionGraph &graph, + const std::vector &selected_outputs) { ankerl::unordered_dense::map> seen_expressions; @@ -453,13 +454,29 @@ ADFunDouble cppad_trace_graph_constraints(const ExpressionGraph &graph) } auto &outputs = graph.m_constraint_outputs; - auto N_outputs = outputs.size(); + + std::vector indices; + if (selected_outputs.empty()) + { + indices.reserve(outputs.size()); + for (size_t i = 0; i < outputs.size(); i++) + { + indices.push_back(i); + } + } + else + { + indices = selected_outputs; + } + + auto N_outputs = indices.size(); std::vector> y(N_outputs); - // Trace the outputs + // Trace the selected outputs for (size_t i = 0; i < N_outputs; i++) { - auto &output = outputs[i]; + auto idx = indices[i]; + auto &output = outputs[idx]; y[i] = cppad_trace_expression(graph, output, x, p, seen_expressions); } @@ -469,7 +486,8 @@ ADFunDouble cppad_trace_graph_constraints(const ExpressionGraph &graph) return f; } -ADFunDouble cppad_trace_graph_objective(const ExpressionGraph &graph, bool aggregate) +ADFunDouble cppad_trace_graph_objective(const ExpressionGraph &graph, bool aggregate, + const std::vector &selected_outputs) { ankerl::unordered_dense::map> seen_expressions; @@ -493,13 +511,28 @@ ADFunDouble cppad_trace_graph_objective(const ExpressionGraph &graph, bool aggre } auto &outputs = graph.m_objective_outputs; - auto N_outputs = outputs.size(); + + std::vector indices; + if (selected_outputs.empty()) + { + indices.reserve(outputs.size()); + for (size_t i = 0; i < outputs.size(); i++) + { + indices.push_back(i); + } + } + else + { + indices = selected_outputs; + } + + auto N_outputs = indices.size(); std::vector> y(N_outputs); - // Trace the outputs for (size_t i = 0; i < N_outputs; i++) { - auto &output = outputs[i]; + auto idx = indices[i]; + auto &output = outputs[idx]; y[i] = cppad_trace_expression(graph, output, x, p, seen_expressions); } diff --git a/lib/cppad_interface_ext.cpp b/lib/cppad_interface_ext.cpp index ff49b44..60d64ab 100644 --- a/lib/cppad_interface_ext.cpp +++ b/lib/cppad_interface_ext.cpp @@ -182,8 +182,9 @@ NB_MODULE(cppad_interface_ext, m) .def_ro("jacobian", &CppADAutodiffGraph::jacobian_graph) .def_ro("hessian", &CppADAutodiffGraph::hessian_graph); - m.def("cppad_trace_graph_constraints", cppad_trace_graph_constraints); + m.def("cppad_trace_graph_constraints", cppad_trace_graph_constraints, nb::arg("graph"), + nb::arg("selected_outputs") = std::vector{}); m.def("cppad_trace_graph_objective", cppad_trace_graph_objective, nb::arg("graph"), - nb::arg("aggregate") = true); + nb::arg("aggregate") = true, nb::arg("selected_outputs") = std::vector{}); m.def("cppad_autodiff", &cppad_autodiff); } From d3b358f2df09c0e28b95f2fc3b059d6b36204b44 Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Wed, 25 Feb 2026 17:30:31 -0500 Subject: [PATCH 2/2] Refactor callback implementation to use outputs in trace functions --- include/pyoptinterface/knitro_model.hpp | 33 +++++++++---------------- lib/knitro_model.cpp | 12 +++++---- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/include/pyoptinterface/knitro_model.hpp b/include/pyoptinterface/knitro_model.hpp index d810cbe..c3b1711 100644 --- a/include/pyoptinterface/knitro_model.hpp +++ b/include/pyoptinterface/knitro_model.hpp @@ -147,8 +147,6 @@ struct CallbackEvaluator std::vector indexCons; CppAD::ADFun fun; - - std::vector fun_rows; CppAD::sparse_rc> jac_pattern_; CppAD::sparse_rcv, std::vector> jac_; CppAD::sparse_jac_work jac_work_; @@ -163,11 +161,10 @@ struct CallbackEvaluator void setup() { fun.optimize(); - CppAD::sparse_rc> jac_pattern_in(fun.Range(), fun_rows.size(), - fun_rows.size()); - for (size_t k = 0; k < fun_rows.size(); k++) + CppAD::sparse_rc> jac_pattern_in(fun.Range(), fun.Range(), fun.Range()); + for (size_t k = 0; k < fun.Range(); k++) { - jac_pattern_in.set(k, fun_rows[k], fun_rows[k]); + jac_pattern_in.set(k, k, k); } fun.rev_jac_sparsity(jac_pattern_in, false, false, true, jac_pattern_); jac_pattern_in.resize(fun.Domain(), fun.Domain(), fun.Domain()); @@ -177,11 +174,7 @@ struct CallbackEvaluator } CppAD::sparse_rc> jac_pattern_out; fun.for_jac_sparsity(jac_pattern_in, false, false, true, jac_pattern_out); - std::vector select_rows(fun.Range(), false); - for (size_t k = 0; k < fun_rows.size(); k++) - { - select_rows[fun_rows[k]] = true; - } + std::vector select_rows(fun.Range(), true); fun.rev_hes_sparsity(select_rows, false, true, hess_pattern_); for (size_t k = 0; k < hess_pattern_.nnz(); k++) { @@ -205,15 +198,15 @@ struct CallbackEvaluator x[i] = req_x[indexVars[i]]; } auto y = fun.Forward(0, x); - for (size_t k = 0; k < fun_rows.size(); k++) + for (size_t k = 0; k < fun.Range(); k++) { if (aggregate) { - res_y[0] += y[fun_rows[k]]; + res_y[0] += y[k]; } else { - res_y[k] = y[fun_rows[k]]; + res_y[k] = y[k]; } } } @@ -238,15 +231,15 @@ struct CallbackEvaluator { x[i] = req_x[indexVars[i]]; } - for (size_t k = 0; k < fun_rows.size(); k++) + for (size_t k = 0; k < fun.Range(); k++) { if (aggregate) { - w[fun_rows[k]] = req_w[0]; + w[k] = req_w[0]; } else { - w[fun_rows[k]] = req_w[indexCons[k]]; + w[k] = req_w[indexCons[k]]; } } fun.sparse_hes(x, w, hess_, hess_pattern_, hess_coloring_, hess_work_); @@ -696,14 +689,12 @@ class KNITROModel : public OnesideLinearConstraintMixin, } template - void _add_callback_impl(const ExpressionGraph &graph, const std::vector &rows, - const std::vector cons, const T &trace, const F f, - const G g, const H h) + void _add_callback_impl(const ExpressionGraph &graph, const std::vector cons, + const T &trace, const F f, const G g, const H h) { auto evaluator_ptr = std::make_unique>(); auto *evaluator = evaluator_ptr.get(); evaluator->fun = trace(graph); - evaluator->fun_rows = rows; evaluator->indexVars.resize(graph.n_variables()); for (size_t i = 0; i < graph.n_variables(); i++) { diff --git a/lib/knitro_model.cpp b/lib/knitro_model.cpp index 72eae83..b1bc3c4 100644 --- a/lib/knitro_model.cpp +++ b/lib/knitro_model.cpp @@ -856,8 +856,10 @@ void KNITROModel::_add_constraint_callback(ExpressionGraph *graph, const Outputs evaluator->eval_hess(req->x, req->lambda, res->hess); return 0; }; - auto trace = cppad_trace_graph_constraints; - _add_callback_impl(*graph, outputs.con_idxs, outputs.cons, trace, f, g, h); + auto trace = [outputs](const ExpressionGraph &graph) { + return cppad_trace_graph_constraints(graph, outputs.con_idxs); + }; + _add_callback_impl(*graph, outputs.cons, trace, f, g, h); } void KNITROModel::_add_objective_callback(ExpressionGraph *graph, const Outputs &outputs) @@ -881,10 +883,10 @@ void KNITROModel::_add_objective_callback(ExpressionGraph *graph, const Outputs evaluator->eval_hess(req->x, req->sigma, res->hess, true); return 0; }; - auto trace = [](const ExpressionGraph &graph) { - return cppad_trace_graph_objective(graph, false); + auto trace = [outputs](const ExpressionGraph &graph) { + return cppad_trace_graph_objective(graph, true, outputs.obj_idxs); }; - _add_callback_impl(*graph, outputs.obj_idxs, {}, trace, f, g, h); + _add_callback_impl(*graph, {}, trace, f, g, h); } void KNITROModel::_add_callbacks()