Icarus
Vehicle Simulation as a Transformable Computational Graph, built on Vulcan and Janus
Loading...
Searching...
No Matches
SymbolicStager.hpp
Go to the documentation of this file.
1#pragma once
2
14
18
19#include <janus/core/Function.hpp>
20#include <janus/math/AutoDiff.hpp>
21
22#include <string>
23#include <vector>
24
25namespace icarus::staging {
26
31 bool generate_dynamics = true;
32 bool generate_jacobian = true;
33 bool generate_step = false;
34 double step_dt = 0.01;
35 std::string function_name = "dynamics";
36 bool include_time = true;
37 std::vector<std::string> control_signals;
38};
39
51 public:
52 using Scalar = janus::SymbolicScalar;
53
59 explicit SymbolicStager(SymbolicSimulatorCore &sym_sim) : sym_sim_(sym_sim) {}
60
70 SymbolicDynamics result;
71
72 const std::size_t n_states = sym_sim_.GetStateSize();
73 const int n_controls = static_cast<int>(config.control_signals.size());
74
75 // Create symbolic variables
76 auto [x_vec, x_mx] = janus::sym_vec_pair("x", static_cast<int>(n_states));
77 Scalar t_sym = janus::sym("t");
78
79 // Create symbolic controls if specified
80 std::vector<Scalar> u_elements;
81 Scalar u_mx;
82 if (n_controls > 0) {
83 auto [u_v, u_m] = janus::sym_vec_pair("u", n_controls);
84 for (int i = 0; i < n_controls; ++i) {
85 u_elements.push_back(u_v(i));
86 }
87 u_mx = u_m;
88 }
89
90 // Set symbolic state and time
91 sym_sim_.SetState(x_vec);
92 sym_sim_.SetTime(t_sym);
93
94 // Apply symbolic controls
95 for (int i = 0; i < n_controls; ++i) {
96 sym_sim_.SetSignal(config.control_signals[i], u_elements[i]);
97 }
98
99 // Compute derivatives symbolically
100 JanusVector<Scalar> xdot_vec = sym_sim_.ComputeDerivatives();
101
102 // Convert to single MX column vector
103 std::vector<Scalar> xdot_elements;
104 xdot_elements.reserve(n_states);
105 for (Eigen::Index i = 0; i < static_cast<Eigen::Index>(n_states); ++i) {
106 xdot_elements.push_back(xdot_vec(i));
107 }
108 Scalar xdot_mx = Scalar::vertcat(xdot_elements);
109
110 // Build dynamics function inputs
111 std::vector<janus::SymbolicArg> inputs;
112 if (config.include_time) {
113 inputs.push_back(t_sym);
114 }
115 inputs.push_back(x_mx);
116 if (n_controls > 0) {
117 inputs.push_back(u_mx);
118 }
119
120 // Create dynamics function: f(t, x, [u]) -> xdot
121 if (config.generate_dynamics) {
122 result.dynamics = janus::Function(config.function_name, inputs, {xdot_mx});
123 }
124
125 // Create Jacobian function: J(t, x, [u]) -> df/dx
126 if (config.generate_jacobian) {
127 Scalar J_sym = janus::jacobian({xdot_mx}, {x_mx});
128 result.jacobian_x = janus::Function("jacobian_x", inputs, {J_sym});
129 }
130
131 // Create control Jacobian if controls specified
132 if (n_controls > 0 && config.generate_jacobian) {
133 Scalar Ju_sym = janus::jacobian({xdot_mx}, {u_mx});
134 result.jacobian_u = janus::Function("jacobian_u", inputs, {Ju_sym});
135 }
136
137 // Store metadata
138 result.state_names = GetStateNames();
139 result.control_names = config.control_signals;
140
141 return result;
142 }
143
152 janus::Function GenerateStepFunction(double dt) {
153 const std::size_t n_states = sym_sim_.GetStateSize();
154
155 // Create symbolic variables
156 auto [x_vec, x_mx] = janus::sym_vec_pair("x", static_cast<int>(n_states));
157 Scalar t_sym = janus::sym("t");
158 Scalar dt_sym(dt);
159
160 // RK4 integration
161 auto compute_xdot = [this](const JanusVector<Scalar> &x, Scalar t) {
162 sym_sim_.SetState(x);
163 sym_sim_.SetTime(t);
164 return sym_sim_.ComputeDerivatives();
165 };
166
167 // k1 = f(t, x)
168 JanusVector<Scalar> k1 = compute_xdot(x_vec, t_sym);
169
170 // k2 = f(t + dt/2, x + dt/2 * k1)
171 JanusVector<Scalar> x_mid1 = x_vec + dt_sym * Scalar(0.5) * k1;
172 JanusVector<Scalar> k2 = compute_xdot(x_mid1, t_sym + dt_sym * Scalar(0.5));
173
174 // k3 = f(t + dt/2, x + dt/2 * k2)
175 JanusVector<Scalar> x_mid2 = x_vec + dt_sym * Scalar(0.5) * k2;
176 JanusVector<Scalar> k3 = compute_xdot(x_mid2, t_sym + dt_sym * Scalar(0.5));
177
178 // k4 = f(t + dt, x + dt * k3)
179 JanusVector<Scalar> x_end = x_vec + dt_sym * k3;
180 JanusVector<Scalar> k4 = compute_xdot(x_end, t_sym + dt_sym);
181
182 // x_next = x + dt/6 * (k1 + 2*k2 + 2*k3 + k4)
183 JanusVector<Scalar> x_next =
184 x_vec + dt_sym * (Scalar(1.0 / 6.0) * k1 + Scalar(1.0 / 3.0) * k2 +
185 Scalar(1.0 / 3.0) * k3 + Scalar(1.0 / 6.0) * k4);
186
187 // Convert to single MX column vector
188 std::vector<Scalar> x_next_elements;
189 x_next_elements.reserve(n_states);
190 for (Eigen::Index i = 0; i < static_cast<Eigen::Index>(n_states); ++i) {
191 x_next_elements.push_back(x_next(i));
192 }
193 Scalar x_next_mx = Scalar::vertcat(x_next_elements);
194
195 return janus::Function("step", {t_sym, x_mx}, {x_next_mx});
196 }
197
201 [[nodiscard]] std::vector<std::string> GetStateNames() const {
202 std::vector<std::string> names;
203 const auto &bindings = sym_sim_.GetStateBindings();
204 names.reserve(bindings.size());
205 for (const auto &binding : bindings) {
206 names.push_back(binding.name);
207 }
208 return names;
209 }
210
214 [[nodiscard]] std::size_t GetStateSize() const { return sym_sim_.GetStateSize(); }
215
216 private:
217 SymbolicSimulatorCore &sym_sim_;
218};
219
220// =============================================================================
221// Convenience Functions
222// =============================================================================
223
233 SymbolicSimulatorCore sym_sim(config);
234 SymbolicStager stager(sym_sim);
235 return stager.GenerateDynamics();
236}
237
246 const SymbolicStagerConfig &stager_config) {
247 SymbolicSimulatorCore sym_sim(config);
248 SymbolicStager stager(sym_sim);
249 return stager.GenerateDynamics(stager_config);
250}
251
252} // namespace icarus::staging
Core type definitions, concepts, and configuration for Icarus.
Core types for staging subsystem (trim, linearization, symbolic).
Lightweight symbolic simulator for graph extraction.
Lightweight symbolic simulator for graph extraction.
Definition SymbolicSimulatorCore.hpp:48
void SetTime(Scalar t)
Set time (symbolic).
Definition SymbolicSimulatorCore.hpp:90
void SetState(const JanusVector< Scalar > &x)
Set state vector (symbolic).
Definition SymbolicSimulatorCore.hpp:80
void SetSignal(const std::string &name, Scalar value)
Write signal value (symbolic).
Definition SymbolicSimulatorCore.hpp:177
std::size_t GetStateSize() const
Get total state size.
Definition SymbolicSimulatorCore.hpp:149
Symbolic graph generator.
Definition SymbolicStager.hpp:50
janus::Function GenerateStepFunction(double dt)
Generate discrete-time step function.
Definition SymbolicStager.hpp:152
std::vector< std::string > GetStateNames() const
Get state variable names in order.
Definition SymbolicStager.hpp:201
SymbolicDynamics GenerateDynamics(const SymbolicStagerConfig &config={})
Generate symbolic dynamics representation.
Definition SymbolicStager.hpp:69
SymbolicStager(SymbolicSimulatorCore &sym_sim)
Construct stager with symbolic simulator.
Definition SymbolicStager.hpp:59
janus::SymbolicScalar Scalar
Definition SymbolicStager.hpp:52
std::size_t GetStateSize() const
Get total state size.
Definition SymbolicStager.hpp:214
Definition Simulator.hpp:53
SymbolicDynamics GenerateSymbolicDynamics(const SimulatorConfig &config)
Generate dynamics graph from simulator config.
Definition SymbolicStager.hpp:232
Complete simulation configuration.
Definition SimulatorConfig.hpp:673
Symbolic dynamics representation.
Definition StagingTypes.hpp:54
std::optional< janus::Function > jacobian_u
df/du (if controls specified)
Definition StagingTypes.hpp:57
std::optional< janus::Function > jacobian_x
df/dx
Definition StagingTypes.hpp:56
std::vector< std::string > state_names
Definition StagingTypes.hpp:59
std::optional< janus::Function > dynamics
f(t, x) -> xdot
Definition StagingTypes.hpp:55
std::vector< std::string > control_names
Definition StagingTypes.hpp:60
Configuration for symbolic graph generation.
Definition SymbolicStager.hpp:30
std::vector< std::string > control_signals
Signals to treat as controls (inputs).
Definition SymbolicStager.hpp:37
bool generate_dynamics
Generate dynamics function f(t, x) -> xdot.
Definition SymbolicStager.hpp:31
double step_dt
Step size for discrete step function.
Definition SymbolicStager.hpp:34
bool include_time
Include time as explicit input.
Definition SymbolicStager.hpp:36
std::string function_name
Definition SymbolicStager.hpp:35
bool generate_jacobian
Generate state Jacobian df/dx.
Definition SymbolicStager.hpp:32
bool generate_step
Generate discrete step function.
Definition SymbolicStager.hpp:33