8#include <casadi/casadi.hpp>
30 switch (parallelization) {
38 throw InvalidArgument(
"Function::map: unsupported map parallelization mode");
55 Function(
const std::string &name,
const std::vector<SymbolicArg> &inputs,
56 const std::vector<SymbolicArg> &outputs)
57 : fn_(name, convert_args(inputs), convert_args(outputs)) {}
64 Function(
const std::vector<SymbolicArg> &inputs,
const std::vector<SymbolicArg> &outputs)
65 : fn_(generate_unique_name(), convert_args(inputs), convert_args(outputs)) {}
68 static std::string generate_unique_name() {
69 static std::atomic<uint64_t> counter{0};
70 return "janus_fn_" + std::to_string(counter.fetch_add(1));
73 static std::vector<SymbolicScalar> convert_args(
const std::vector<SymbolicArg> &args) {
74 std::vector<SymbolicScalar> ret;
75 ret.reserve(args.size());
76 for (
const auto &arg : args) {
77 ret.push_back(arg.get());
85 template <
typename T>
struct is_symbolic_type : std::false_type {};
86 template <>
struct is_symbolic_type<casadi::MX> : std::true_type {};
89 template <
typename S,
int R,
int C,
int O,
int MR,
int MC>
90 struct is_symbolic_type<Eigen::Matrix<S, R, C, O, MR, MC>>
91 : std::conditional_t<std::is_same_v<S, casadi::MX>, std::true_type, std::false_type> {};
94 template <
typename T> casadi::MX normalize_arg(
const T &val)
const {
95 if constexpr (std::is_arithmetic_v<std::decay_t<T>> ||
96 std::is_same_v<std::decay_t<T>, casadi::MX> ||
97 std::is_same_v<std::decay_t<T>, casadi::DM>) {
98 return casadi::MX(val);
105 template <
typename T> casadi::DM normalize_arg_dm(
const T &val)
const {
106 using DecayT = std::decay_t<T>;
107 if constexpr (std::is_arithmetic_v<DecayT>) {
108 return casadi::DM(
double(val));
109 }
else if constexpr (std::is_same_v<DecayT, std::vector<double>>) {
110 return casadi::DM(val);
113 casadi::DM m(val.rows(), val.cols());
114 for (
int i = 0; i < val.rows(); ++i)
115 for (
int j = 0; j < val.cols(); ++j)
116 m(i, j) = double(val(i, j));
130 template <
typename... Args>
auto operator()(Args &&...args)
const {
131 constexpr bool is_symbolic = (is_symbolic_type<std::decay_t<Args>>::value || ...);
133 if constexpr (is_symbolic) {
134 std::vector<casadi::MX> mx_args;
135 mx_args.reserve(
sizeof...(args));
136 (mx_args.push_back(normalize_arg(std::forward<Args>(args))), ...);
138 std::vector<casadi::MX> res_mx = fn_(mx_args);
139 return to_eigen_vector<casadi::MX>(res_mx);
141 std::vector<casadi::DM> dm_args;
142 dm_args.reserve(
sizeof...(args));
143 (dm_args.push_back(normalize_arg_dm(std::forward<Args>(args))), ...);
145 std::vector<casadi::DM> res_dm = fn_(dm_args);
146 return to_eigen_vector<double>(res_dm);
156 template <
typename... Args>
auto eval(Args &&...args)
const {
157 auto results =
operator()(std::forward<Args>(args)...);
164 std::vector<NumericMatrix>
operator()(std::vector<double> &args)
const {
165 return operator()(
const_cast<const std::vector<double> &
>(args));
171 std::vector<NumericMatrix>
operator()(
const std::vector<double> &args)
const {
172 std::vector<casadi::DM> dm_args;
173 dm_args.reserve(args.size());
174 for (
double val : args) {
175 dm_args.push_back(casadi::DM(val));
178 auto res_dm = fn_(dm_args);
179 return to_eigen_vector<double>(res_dm);
220 return Function(fn_.map(n, parallelization));
242 Function map(
int n,
const std::string ¶llelization,
int max_num_threads)
const {
246 if (max_num_threads <= 0) {
247 throw InvalidArgument(
"Function::map: max_num_threads must be positive");
249 return Function(fn_.map(n, parallelization, max_num_threads));
253 explicit Function(casadi::Function fn) : fn_(std::move(fn)) {}
255 casadi::Function fn_;
257 template <
typename Scalar,
typename CasadiType>
258 std::vector<JanusMatrix<Scalar>> to_eigen_vector(
const std::vector<CasadiType> &dms)
const {
259 std::vector<JanusMatrix<Scalar>> ret;
260 ret.reserve(dms.size());
261 for (
const auto &dm : dms) {
263 if constexpr (std::is_same_v<CasadiType, casadi::MX>) {
267 MatType mat(dm.size1(), dm.size2());
268 std::vector<double> elements =
static_cast<std::vector<double>
>(dm);
269 for (Eigen::Index j = 0; j < mat.cols(); ++j) {
270 for (Eigen::Index i = 0; i < mat.rows(); ++i) {
271 mat(i, j) = elements[j * mat.rows() + i];
290template <
typename T>
struct is_tuple : std::false_type {};
291template <
typename... Ts>
struct is_tuple<std::tuple<Ts...>> : std::true_type {};
297template <
typename Tuple, std::size_t... Is>
299 return {std::get<Is>(t)...};
302template <
typename... Ts> std::vector<SymbolicArg>
tuple_to_outputs(
const std::tuple<Ts...> &t) {
309template <
typename Func, std::size_t... Is>
311 std::index_sequence<Is...>) {
312 return fn(syms[Is]...);
315template <
int NInputs,
typename Func>
318 std::make_index_sequence<NInputs>{});
348template <
int NInputs,
int NOutputs,
typename Func>
350 static_assert(NInputs > 0,
"NInputs must be positive");
351 static_assert(NOutputs > 0,
"NOutputs must be positive");
354 std::vector<SymbolicScalar> inputs;
355 inputs.reserve(NInputs);
356 for (
int i = 0; i < NInputs; ++i) {
357 inputs.push_back(
sym(
"_x" + std::to_string(i)));
364 std::vector<SymbolicArg> outputs;
373 std::vector<SymbolicArg> input_args(inputs.begin(), inputs.end());
375 return Function(name, input_args, outputs);
394template <
int NInputs,
typename Func>
397 static_assert(NInputs > 0,
"NInputs must be positive");
398 if (
static_cast<int>(input_names.size()) != NInputs) {
399 throw InvalidArgument(
"make_function: input_names.size() must equal NInputs");
403 std::vector<SymbolicScalar> inputs;
404 inputs.reserve(NInputs);
405 for (
const auto &iname : input_names) {
406 inputs.push_back(
sym(iname));
413 std::vector<SymbolicArg> outputs;
421 std::vector<SymbolicArg> input_args(inputs.begin(), inputs.end());
423 return Function(name, input_args, outputs);
Custom exception hierarchy for Janus framework.
Core type aliases for numeric and symbolic Eigen/CasADi interop.
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
Function map(int n, MapParallelization parallelization=MapParallelization::Parallel) const
Create a batched version of this function using CasADi's map primitive.
Definition Function.hpp:206
Function map(int n, MapParallelization parallelization, int max_num_threads) const
Create a batched version of this function with an explicit thread cap.
Definition Function.hpp:231
std::vector< NumericMatrix > operator()(const std::vector< double > &args) const
Evaluate with a const vector of doubles.
Definition Function.hpp:171
auto eval(Args &&...args) const
Evaluate function and return first output.
Definition Function.hpp:156
auto operator()(Args &&...args) const
Evaluate function with arbitrary arguments (scalars or Eigen matrices) Handles both numeric (double/M...
Definition Function.hpp:130
Function(const std::string &name, const std::vector< SymbolicArg > &inputs, const std::vector< SymbolicArg > &outputs)
Construct a new Function object.
Definition Function.hpp:55
Function map(int n, const std::string ¶llelization) const
Create a batched version using a direct CasADi backend name.
Definition Function.hpp:216
Function map(int n, const std::string ¶llelization, int max_num_threads) const
Create a batched version with explicit backend name and thread cap.
Definition Function.hpp:242
std::vector< NumericMatrix > operator()(std::vector< double > &args) const
Evaluate with a mutable vector of doubles.
Definition Function.hpp:164
Function(const std::vector< SymbolicArg > &inputs, const std::vector< SymbolicArg > &outputs)
Constructor with auto-generated name.
Definition Function.hpp:64
Input validation failed (e.g., mismatched sizes, invalid parameters).
Definition JanusError.hpp:31
Smooth approximation of ReLU function: softplus(x) = (1/beta) * log(1 + exp(beta * x)).
Definition Diagnostics.hpp:131
auto invoke_with_symbols_impl(Func &&fn, const std::vector< SymbolicScalar > &syms, std::index_sequence< Is... >)
Invoke lambda with unpacked symbolic arguments.
Definition Function.hpp:310
std::string to_casadi_parallelization(MapParallelization parallelization)
Definition Function.hpp:29
auto invoke_with_symbols(Func &&fn, const std::vector< SymbolicScalar > &syms)
Definition Function.hpp:316
std::vector< SymbolicArg > tuple_to_outputs(const std::tuple< Ts... > &t)
Definition Function.hpp:302
constexpr bool is_tuple_v
Definition Function.hpp:292
std::vector< SymbolicArg > tuple_to_outputs_impl(const Tuple &t, std::index_sequence< Is... >)
Convert a tuple of symbolic scalars to a vector of SymbolicArg.
Definition Function.hpp:298
Definition Diagnostics.hpp:19
Function make_function(const std::string &name, Func &&fn)
Create a Function from a lambda expression.
Definition Function.hpp:349
Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic > JanusMatrix
Dynamic-size matrix for both numeric and symbolic backends.
Definition JanusTypes.hpp:43
casadi::MX to_mx(const Eigen::MatrixBase< Derived > &e)
Convert Eigen matrix of MX (or numeric) to CasADi MX.
Definition JanusTypes.hpp:189
MapParallelization
Batch mapping backend for janus::Function::map().
Definition Function.hpp:22
@ Unroll
CasADi unrolled map backend.
Definition Function.hpp:25
@ Serial
CasADi serial map backend.
Definition Function.hpp:24
@ Parallel
CasADi OpenMP map backend (falls back to serial if unavailable).
Definition Function.hpp:23
SymbolicScalar sym(const std::string &name)
Create a named symbolic scalar variable.
Definition JanusTypes.hpp:90
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
Helper to detect if a type is a std::tuple.
Definition Function.hpp:290