Janus 2.0.0
High-performance C++20 dual-mode numerical framework
Loading...
Searching...
No Matches
Diagnostics.hpp
Go to the documentation of this file.
1
3#pragma once
4
5#include "Function.hpp"
6#include "JanusError.hpp"
7#include "Sparsity.hpp"
8
9#include <algorithm>
10#include <casadi/casadi.hpp>
11#include <numeric>
12#include <optional>
13#include <queue>
14#include <sstream>
15#include <string>
16#include <utility>
17#include <vector>
18
19namespace janus {
20
28
35 std::vector<int> output_indices;
36};
37
42 int local_index = -1;
43 std::string label;
44};
45
50 int flat_row = -1;
51 int output_idx = -1;
52 int local_row = -1;
53 int local_col = -1;
54 std::string label;
55};
56
61 std::vector<int> input_local_indices;
62 std::vector<int> output_rows;
65};
66
71 std::string message;
72 std::vector<int> input_local_indices;
73 std::vector<int> output_rows;
74};
75
81 int input_idx = -1;
82 std::string input_name;
83 std::vector<int> output_indices;
84 std::vector<std::string> output_names;
90 std::vector<DiagnosticInputRef> inputs;
91 std::vector<DiagnosticOutputRef> outputs;
92 std::vector<int> deficient_local_indices;
94 std::vector<StructuralDeficiencyGroup> deficiency_groups;
95 std::vector<StructuralDiagnosticIssue> issues;
96
99 bool full_rank() const { return rank_deficiency == 0; }
100};
101
113
118 std::optional<StructuralSensitivityReport>
120 std::optional<StructuralSensitivityReport>
122
125 bool has_deficiency() const {
126 return (observability.has_value() && !observability->full_rank()) ||
127 (identifiability.has_value() && !identifiability->full_rank());
128 }
129};
130
131namespace detail {
132
133inline std::vector<int> sort_unique(std::vector<int> values) {
134 std::sort(values.begin(), values.end());
135 values.erase(std::unique(values.begin(), values.end()), values.end());
136 return values;
137}
138
139inline std::vector<casadi_int> to_casadi_int(const std::vector<int> &values) {
140 return std::vector<casadi_int>(values.begin(), values.end());
141}
142
143inline std::string join_labels(const std::vector<std::string> &labels) {
144 std::ostringstream oss;
145 for (std::size_t i = 0; i < labels.size(); ++i) {
146 if (i > 0) {
147 oss << ", ";
148 }
149 oss << labels[i];
150 }
151 return oss.str();
152}
153
154inline std::string property_name(StructuralProperty property) {
155 switch (property) {
157 return "observability";
159 return "identifiability";
160 }
161 throw InvalidArgument("Unsupported structural property");
162}
163
164inline std::string property_subject_plural(StructuralProperty property) {
165 switch (property) {
167 return "states";
169 return "parameters";
170 }
171 throw InvalidArgument("Unsupported structural property");
172}
173
174inline std::string property_subject_singular(StructuralProperty property) {
175 switch (property) {
177 return "state";
179 return "parameter";
180 }
181 throw InvalidArgument("Unsupported structural property");
182}
183
184inline std::string property_fix_hint(StructuralProperty property) {
185 switch (property) {
187 return "add sensors that depend on them or constrain/fix them";
189 return "add measurements that separate them or constrain/fix them";
190 }
191 throw InvalidArgument("Unsupported structural property");
192}
193
194inline std::string input_label(const std::string &input_name, int local_index) {
195 return input_name + "[" + std::to_string(local_index) + "]";
196}
197
198inline std::string output_label(const std::string &output_name, int rows, int cols,
199 int flat_index) {
200 if (rows == 1 && cols == 1) {
201 return output_name;
202 }
203 const int row = flat_index % rows;
204 const int col = flat_index / rows;
205 if (cols == 1) {
206 return output_name + "[" + std::to_string(row) + "]";
207 }
208 if (rows == 1) {
209 return output_name + "[" + std::to_string(col) + "]";
210 }
211 return output_name + "(" + std::to_string(row) + "," + std::to_string(col) + ")";
212}
213
214inline std::vector<int> canonical_output_indices(const casadi::Function &cfn,
215 const std::vector<int> &requested,
216 const std::string &context) {
217 if (requested.empty()) {
218 std::vector<int> all(static_cast<std::size_t>(cfn.n_out()));
219 std::iota(all.begin(), all.end(), 0);
220 return all;
221 }
222
223 std::vector<int> indices;
224 indices.reserve(requested.size());
225 for (int output_idx : requested) {
226 if (output_idx < 0 || output_idx >= cfn.n_out()) {
227 throw InvalidArgument(context + ": output index out of range");
228 }
229 indices.push_back(output_idx);
230 }
231 indices = sort_unique(std::move(indices));
232 if (indices.size() != requested.size()) {
233 throw InvalidArgument(context + ": output_indices must not contain duplicates");
234 }
235 return indices;
236}
237
238inline void validate_input_block(const casadi::Function &cfn, int input_idx,
239 const std::string &context) {
240 if (input_idx < 0 || input_idx >= cfn.n_in()) {
241 throw InvalidArgument(context + ": input_idx out of range");
242 }
243
244 const casadi::Sparsity input_sp = cfn.sparsity_in(input_idx);
245 if (!input_sp.is_dense() || !input_sp.is_column()) {
246 throw InvalidArgument(context + ": selected input must be a dense column vector");
247 }
248}
249
250inline void validate_output_blocks(const casadi::Function &cfn,
251 const std::vector<int> &output_indices,
252 const std::string &context) {
253 for (int output_idx : output_indices) {
254 const casadi::Sparsity output_sp = cfn.sparsity_out(output_idx);
255 if (!output_sp.is_dense()) {
256 throw InvalidArgument(context + ": selected outputs must be dense");
257 }
258 }
259}
260
261inline casadi::MX flatten_output(const casadi::MX &output) {
262 return casadi::MX::reshape(output, output.numel(), 1);
263}
264
265inline std::vector<DiagnosticInputRef> make_input_refs(const casadi::Function &cfn, int input_idx) {
266 std::vector<DiagnosticInputRef> refs;
267 refs.reserve(static_cast<std::size_t>(cfn.nnz_in(input_idx)));
268 for (int local_index = 0; local_index < cfn.nnz_in(input_idx); ++local_index) {
269 refs.push_back(DiagnosticInputRef{
270 local_index,
271 input_label(cfn.name_in(input_idx), local_index),
272 });
273 }
274 return refs;
275}
276
277inline std::vector<DiagnosticOutputRef> make_output_refs(const casadi::Function &cfn,
278 const std::vector<int> &output_indices) {
279 std::vector<DiagnosticOutputRef> refs;
280 int flat_row = 0;
281 for (int output_idx : output_indices) {
282 const int rows = cfn.size1_out(output_idx);
283 const int cols = cfn.size2_out(output_idx);
284 const std::string &name = cfn.name_out(output_idx);
285 for (int linear = 0; linear < rows * cols; ++linear) {
286 refs.push_back(DiagnosticOutputRef{
287 flat_row,
288 output_idx,
289 linear % rows,
290 linear / rows,
291 output_label(name, rows, cols, linear),
292 });
293 flat_row += 1;
294 }
295 }
296 return refs;
297}
298
299inline casadi::MX collect_selected_outputs(const std::vector<casadi::MX> &outputs,
300 const std::vector<int> &output_indices) {
301 std::vector<casadi::MX> flattened;
302 flattened.reserve(output_indices.size());
303 for (int output_idx : output_indices) {
304 flattened.push_back(flatten_output(outputs.at(static_cast<std::size_t>(output_idx))));
305 }
306 if (flattened.empty()) {
307 return casadi::MX(0, 1);
308 }
309 return casadi::MX::vertcat(flattened);
310}
311
313 std::vector<int> rows;
314 std::vector<int> cols;
315};
316
317inline std::vector<BipartiteComponent> connected_components(const casadi::Sparsity &sp) {
318 const int n_rows = static_cast<int>(sp.size1());
319 const int n_cols = static_cast<int>(sp.size2());
320
321 std::vector<casadi_int> row_indices_ci;
322 std::vector<casadi_int> col_indices_ci;
323 sp.get_triplet(row_indices_ci, col_indices_ci);
324
325 std::vector<std::vector<int>> row_to_cols(static_cast<std::size_t>(n_rows));
326 std::vector<std::vector<int>> col_to_rows(static_cast<std::size_t>(n_cols));
327 for (std::size_t k = 0; k < row_indices_ci.size(); ++k) {
328 const int row = static_cast<int>(row_indices_ci[k]);
329 const int col = static_cast<int>(col_indices_ci[k]);
330 row_to_cols.at(static_cast<std::size_t>(row)).push_back(col);
331 col_to_rows.at(static_cast<std::size_t>(col)).push_back(row);
332 }
333
334 std::vector<bool> row_visited(static_cast<std::size_t>(n_rows), false);
335 std::vector<bool> col_visited(static_cast<std::size_t>(n_cols), false);
336 std::vector<BipartiteComponent> components;
337
338 for (int start_col = 0; start_col < n_cols; ++start_col) {
339 if (col_visited.at(static_cast<std::size_t>(start_col))) {
340 continue;
341 }
342
343 BipartiteComponent component;
344 std::queue<std::pair<bool, int>> frontier;
345 frontier.push({false, start_col});
346 col_visited.at(static_cast<std::size_t>(start_col)) = true;
347
348 while (!frontier.empty()) {
349 const auto [is_row, index] = frontier.front();
350 frontier.pop();
351
352 if (is_row) {
353 component.rows.push_back(index);
354 for (int col : row_to_cols.at(static_cast<std::size_t>(index))) {
355 if (!col_visited.at(static_cast<std::size_t>(col))) {
356 col_visited.at(static_cast<std::size_t>(col)) = true;
357 frontier.push({false, col});
358 }
359 }
360 } else {
361 component.cols.push_back(index);
362 for (int row : col_to_rows.at(static_cast<std::size_t>(index))) {
363 if (!row_visited.at(static_cast<std::size_t>(row))) {
364 row_visited.at(static_cast<std::size_t>(row)) = true;
365 frontier.push({true, row});
366 }
367 }
368 }
369 }
370
371 component.rows = sort_unique(std::move(component.rows));
372 component.cols = sort_unique(std::move(component.cols));
373 components.push_back(std::move(component));
374 }
375
376 return components;
377}
378
379inline int structural_rank(const casadi::Sparsity &sp) {
380 return static_cast<int>(casadi::Sparsity::sprank(sp));
381}
382
383inline int structural_rank_of_component(const casadi::Sparsity &sp,
384 const BipartiteComponent &component) {
385 const std::vector<casadi_int> rows = to_casadi_int(component.rows);
386 const std::vector<casadi_int> cols = to_casadi_int(component.cols);
387 std::vector<casadi_int> mapping;
388 const casadi::Sparsity sub = sp.sub(rows, cols, mapping);
389 return structural_rank(sub);
390}
391
392inline std::vector<int> zero_sensitivity_columns(const casadi::Sparsity &sp) {
393 std::vector<int> zeros;
394 zeros.reserve(static_cast<std::size_t>(sp.size2()));
395 for (int col = 0; col < sp.size2(); ++col) {
396 bool has_nonzero = false;
397 for (int nz = sp.colind(col); nz < sp.colind(col + 1); ++nz) {
398 if (sp.row(nz) >= 0) {
399 has_nonzero = true;
400 break;
401 }
402 }
403 if (!has_nonzero) {
404 zeros.push_back(col);
405 }
406 }
407 return zeros;
408}
409
410inline std::vector<std::string> labels_for_inputs(const std::vector<DiagnosticInputRef> &inputs,
411 const std::vector<int> &indices) {
412 std::vector<std::string> labels;
413 labels.reserve(indices.size());
414 for (int index : indices) {
415 labels.push_back(inputs.at(static_cast<std::size_t>(index)).label);
416 }
417 return labels;
418}
419
420inline std::vector<std::string> labels_for_outputs(const std::vector<DiagnosticOutputRef> &outputs,
421 const std::vector<int> &rows) {
422 std::vector<std::string> labels;
423 labels.reserve(rows.size());
424 for (int row : rows) {
425 labels.push_back(outputs.at(static_cast<std::size_t>(row)).label);
426 }
427 return labels;
428}
429
430inline std::vector<StructuralDiagnosticIssue>
431build_issues(StructuralProperty property, const std::vector<DiagnosticInputRef> &inputs,
432 const std::vector<DiagnosticOutputRef> &outputs,
433 const std::vector<int> &zero_sensitivity_local_indices,
434 const std::vector<StructuralDeficiencyGroup> &deficiency_groups) {
435 std::vector<StructuralDiagnosticIssue> issues;
436
437 if (!zero_sensitivity_local_indices.empty()) {
438 issues.push_back(StructuralDiagnosticIssue{
439 "Selected outputs have no structural dependence on " +
440 join_labels(labels_for_inputs(inputs, zero_sensitivity_local_indices)) + "; " +
441 property_fix_hint(property) + ".",
442 zero_sensitivity_local_indices,
443 {},
444 });
445 }
446
447 for (const auto &group : deficiency_groups) {
448 if (group.output_rows.empty()) {
449 continue;
450 }
451 issues.push_back(StructuralDiagnosticIssue{
452 "Selected outputs only provide structural rank " +
453 std::to_string(group.structural_rank) + " for " +
454 std::to_string(group.input_local_indices.size()) + " " +
455 property_subject_plural(property) + " in {" +
456 join_labels(labels_for_inputs(inputs, group.input_local_indices)) +
457 "}. Add "
458 "independent measurements involving {" +
459 join_labels(labels_for_outputs(outputs, group.output_rows)) + "} or " +
460 property_fix_hint(property) + ".",
461 group.input_local_indices,
462 group.output_rows,
463 });
464 }
465
466 return issues;
467}
468
470 StructuralProperty property,
471 const StructuralSensitivityOptions &opts) {
472 const std::string context = "analyze_structural_" + property_name(property);
473 const casadi::Function &cfn = fn.casadi_function();
474
475 validate_input_block(cfn, input_idx, context);
476 const std::vector<int> output_indices =
477 canonical_output_indices(cfn, opts.output_indices, context);
478 validate_output_blocks(cfn, output_indices, context);
479
480 const std::vector<casadi::MX> inputs = cfn.mx_in();
481 const std::vector<casadi::MX> outputs = cfn(inputs);
482
483 const casadi::MX selected_input = inputs.at(static_cast<std::size_t>(input_idx));
484 const casadi::MX selected_outputs = collect_selected_outputs(outputs, output_indices);
485 const casadi::Sparsity jac_sp =
486 casadi::MX::jacobian(selected_outputs, selected_input).sparsity();
487
489 report.property = property;
490 report.input_idx = input_idx;
491 report.input_name = cfn.name_in(input_idx);
492 report.output_indices = output_indices;
493 report.variable_dimension = static_cast<int>(selected_input.numel());
494 report.output_dimension = static_cast<int>(selected_outputs.numel());
495 report.structural_rank = structural_rank(jac_sp);
496 report.rank_deficiency = report.variable_dimension - report.structural_rank;
497 report.sensitivity_sparsity = SparsityPattern(jac_sp);
498 report.inputs = make_input_refs(cfn, input_idx);
499 report.outputs = make_output_refs(cfn, output_indices);
500 report.output_names.reserve(output_indices.size());
501 for (int output_idx : output_indices) {
502 report.output_names.push_back(cfn.name_out(output_idx));
503 }
504
506
507 const std::vector<BipartiteComponent> components = connected_components(jac_sp);
508 for (const auto &component : components) {
509 if (component.cols.empty()) {
510 continue;
511 }
512
513 const int component_rank = structural_rank_of_component(jac_sp, component);
514 if (component_rank < static_cast<int>(component.cols.size())) {
516 component.cols,
517 component.rows,
518 component_rank,
519 static_cast<int>(component.cols.size()) - component_rank,
520 });
521 }
522 }
523
524 std::vector<int> deficient = report.zero_sensitivity_local_indices;
525 for (const auto &group : report.deficiency_groups) {
526 deficient.insert(deficient.end(), group.input_local_indices.begin(),
527 group.input_local_indices.end());
528 }
529 report.deficient_local_indices = sort_unique(std::move(deficient));
530
531 report.issues = build_issues(property, report.inputs, report.outputs,
533 return report;
534}
535
536} // namespace detail
537
547analyze_structural_observability(const Function &fn, int state_input_idx = 0,
548 const StructuralSensitivityOptions &opts = {}) {
549 return detail::analyze_property(fn, state_input_idx, StructuralProperty::Observability, opts);
550}
551
561analyze_structural_identifiability(const Function &fn, int parameter_input_idx,
562 const StructuralSensitivityOptions &opts = {}) {
564 opts);
565}
566
576 if (opts.state_input_idx < 0 && opts.parameter_input_idx < 0) {
577 throw InvalidArgument("analyze_structural_diagnostics: at least one of state_input_idx or "
578 "parameter_input_idx must be non-negative");
579 }
580
582 StructuralSensitivityOptions sensitivity_opts;
583 sensitivity_opts.output_indices = opts.output_indices;
584
585 if (opts.state_input_idx >= 0) {
586 report.observability =
587 analyze_structural_observability(fn, opts.state_input_idx, sensitivity_opts);
588 }
589 if (opts.parameter_input_idx >= 0) {
590 report.identifiability =
592 }
593 return report;
594}
595
596} // namespace janus
Symbolic function wrapper around CasADi with Eigen-native IO.
Custom exception hierarchy for Janus framework.
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
Definition Sparsity.hpp:38
Smooth approximation of ReLU function: softplus(x) = (1/beta) * log(1 + exp(beta * x)).
Definition Diagnostics.hpp:131
std::vector< BipartiteComponent > connected_components(const casadi::Sparsity &sp)
Definition Diagnostics.hpp:317
void validate_input_block(const casadi::Function &cfn, int input_idx, const std::string &context)
Definition Diagnostics.hpp:238
void validate_output_blocks(const casadi::Function &cfn, const std::vector< int > &output_indices, const std::string &context)
Definition Diagnostics.hpp:250
std::string output_label(const std::string &output_name, int rows, int cols, int flat_index)
Definition Diagnostics.hpp:198
std::string property_subject_singular(StructuralProperty property)
Definition Diagnostics.hpp:174
casadi::MX collect_selected_outputs(const std::vector< casadi::MX > &outputs, const std::vector< int > &output_indices)
Definition Diagnostics.hpp:299
std::string input_label(const std::string &input_name, int local_index)
Definition Diagnostics.hpp:194
std::vector< std::string > labels_for_inputs(const std::vector< DiagnosticInputRef > &inputs, const std::vector< int > &indices)
Definition Diagnostics.hpp:410
std::vector< DiagnosticOutputRef > make_output_refs(const casadi::Function &cfn, const std::vector< int > &output_indices)
Definition Diagnostics.hpp:277
std::vector< int > sort_unique(std::vector< int > values)
Definition Diagnostics.hpp:133
std::string property_fix_hint(StructuralProperty property)
Definition Diagnostics.hpp:184
std::string property_subject_plural(StructuralProperty property)
Definition Diagnostics.hpp:164
int structural_rank(const casadi::Sparsity &sp)
Definition Diagnostics.hpp:379
std::vector< DiagnosticInputRef > make_input_refs(const casadi::Function &cfn, int input_idx)
Definition Diagnostics.hpp:265
int structural_rank_of_component(const casadi::Sparsity &sp, const BipartiteComponent &component)
Definition Diagnostics.hpp:383
std::vector< std::string > labels_for_outputs(const std::vector< DiagnosticOutputRef > &outputs, const std::vector< int > &rows)
Definition Diagnostics.hpp:420
std::string join_labels(const std::vector< std::string > &labels)
Definition Diagnostics.hpp:143
std::vector< casadi_int > to_casadi_int(const std::vector< int > &values)
Definition Diagnostics.hpp:139
std::vector< int > canonical_output_indices(const casadi::Function &cfn, const std::vector< int > &requested, const std::string &context)
Definition Diagnostics.hpp:214
std::vector< StructuralDiagnosticIssue > build_issues(StructuralProperty property, const std::vector< DiagnosticInputRef > &inputs, const std::vector< DiagnosticOutputRef > &outputs, const std::vector< int > &zero_sensitivity_local_indices, const std::vector< StructuralDeficiencyGroup > &deficiency_groups)
Definition Diagnostics.hpp:431
std::vector< int > zero_sensitivity_columns(const casadi::Sparsity &sp)
Definition Diagnostics.hpp:392
casadi::MX flatten_output(const casadi::MX &output)
Definition Diagnostics.hpp:261
StructuralSensitivityReport analyze_property(const Function &fn, int input_idx, StructuralProperty property, const StructuralSensitivityOptions &opts)
Definition Diagnostics.hpp:469
std::string property_name(StructuralProperty property)
Definition Diagnostics.hpp:154
Definition Diagnostics.hpp:19
StructuralProperty
Structural property being analyzed from a symbolic sensitivity pattern.
Definition Diagnostics.hpp:24
@ Identifiability
Parameter identifiability from outputs.
Definition Diagnostics.hpp:26
@ Observability
State observability from outputs.
Definition Diagnostics.hpp:25
StructuralDiagnosticsReport analyze_structural_diagnostics(const Function &fn, const StructuralDiagnosticsOptions &opts)
Run structural observability and identifiability checks together.
Definition Diagnostics.hpp:575
StructuralSensitivityReport analyze_structural_identifiability(const Function &fn, int parameter_input_idx, const StructuralSensitivityOptions &opts={})
Analyze which parameters are structurally identifiable from selected outputs.
Definition Diagnostics.hpp:561
StructuralSensitivityReport analyze_structural_observability(const Function &fn, int state_input_idx=0, const StructuralSensitivityOptions &opts={})
Analyze which states are structurally observable from selected outputs.
Definition Diagnostics.hpp:547
auto all(const Eigen::MatrixBase< Derived > &a)
Returns true if all elements are true (non-zero).
Definition Logic.hpp:504
Structural rank analysis of selected outputs with respect to one input block.
Definition Diagnostics.hpp:79
One scalarized element of the selected input block.
Definition Diagnostics.hpp:41
int local_index
Scalar index within the input block.
Definition Diagnostics.hpp:42
std::string label
Human-readable label (e.g. "x[0]").
Definition Diagnostics.hpp:43
One scalarized row in the selected output stack.
Definition Diagnostics.hpp:49
int output_idx
Index of the output block.
Definition Diagnostics.hpp:51
int local_col
Column within the output block.
Definition Diagnostics.hpp:53
int local_row
Row within the output block.
Definition Diagnostics.hpp:52
int flat_row
Row in the flattened output stack.
Definition Diagnostics.hpp:50
std::string label
Human-readable label.
Definition Diagnostics.hpp:54
One structurally deficient connected component in the sensitivity graph.
Definition Diagnostics.hpp:60
int structural_rank
Rank of this sub-block.
Definition Diagnostics.hpp:63
int rank_deficiency
Number of structurally deficient inputs.
Definition Diagnostics.hpp:64
std::vector< int > output_rows
Outputs in this connected component.
Definition Diagnostics.hpp:62
std::vector< int > input_local_indices
Inputs in this connected component.
Definition Diagnostics.hpp:61
One user-facing structural diagnostic with an attached remediation hint.
Definition Diagnostics.hpp:70
std::vector< int > output_rows
Affected output rows.
Definition Diagnostics.hpp:73
std::vector< int > input_local_indices
Affected input indices.
Definition Diagnostics.hpp:72
std::string message
Human-readable diagnostic message.
Definition Diagnostics.hpp:71
Combined observability and identifiability analysis options.
Definition Diagnostics.hpp:108
int state_input_idx
State input block index (-1 to skip observability).
Definition Diagnostics.hpp:110
int parameter_input_idx
Parameter input block index (-1 to skip identifiability).
Definition Diagnostics.hpp:111
std::vector< int > output_indices
Output indices to analyze (empty = all).
Definition Diagnostics.hpp:109
Combined structural diagnostics report.
Definition Diagnostics.hpp:117
std::optional< StructuralSensitivityReport > identifiability
Identifiability result (if requested).
Definition Diagnostics.hpp:121
std::optional< StructuralSensitivityReport > observability
Observability result (if requested).
Definition Diagnostics.hpp:119
bool has_deficiency() const
Check if any analysis found a rank deficiency.
Definition Diagnostics.hpp:125
Output-selection options shared by the structural diagnostics helpers.
Definition Diagnostics.hpp:34
std::vector< int > output_indices
Indices of outputs to analyze (empty = all).
Definition Diagnostics.hpp:35
Structural rank analysis of selected outputs with respect to one input block.
Definition Diagnostics.hpp:79
std::vector< StructuralDeficiencyGroup > deficiency_groups
Connected deficient components.
Definition Diagnostics.hpp:94
std::vector< std::string > output_names
Names of selected outputs.
Definition Diagnostics.hpp:84
int rank_deficiency
variable_dimension - structural_rank
Definition Diagnostics.hpp:88
std::vector< StructuralDiagnosticIssue > issues
User-facing diagnostic messages.
Definition Diagnostics.hpp:95
StructuralProperty property
Analysis type.
Definition Diagnostics.hpp:80
std::vector< DiagnosticInputRef > inputs
Per-element input metadata.
Definition Diagnostics.hpp:90
int output_dimension
Number of scalar outputs.
Definition Diagnostics.hpp:86
bool full_rank() const
Check if the Jacobian has full structural rank.
Definition Diagnostics.hpp:99
std::vector< int > output_indices
Selected output indices.
Definition Diagnostics.hpp:83
std::vector< int > deficient_local_indices
All structurally deficient inputs.
Definition Diagnostics.hpp:92
std::vector< DiagnosticOutputRef > outputs
Per-element output metadata.
Definition Diagnostics.hpp:91
SparsityPattern sensitivity_sparsity
Jacobian sparsity pattern.
Definition Diagnostics.hpp:89
int input_idx
Analyzed input block index.
Definition Diagnostics.hpp:81
int structural_rank
Structural rank of Jacobian.
Definition Diagnostics.hpp:87
int variable_dimension
Number of scalar variables.
Definition Diagnostics.hpp:85
std::string input_name
Name of the input block.
Definition Diagnostics.hpp:82
std::vector< int > zero_sensitivity_local_indices
Inputs with zero Jacobian columns.
Definition Diagnostics.hpp:93
Definition Diagnostics.hpp:312
std::vector< int > cols
Definition Diagnostics.hpp:314
std::vector< int > rows
Definition Diagnostics.hpp:313