26 #include "function.hpp"
27 #include "casadi_meta.hpp"
28 #include "function_internal.hpp"
29 #include "sx_function.hpp"
31 #include "serializer.hpp"
32 #include "mx_function.hpp"
33 #include "filesystem_impl.hpp"
34 #include <casadi/core/resource_casadi_viz.hpp>
45 std::string graph_string(
const std::string& value) {
48 for (
unsigned char c : value) {
49 if (c ==
'"' || c ==
'\\') {
51 }
else if (c < 32 || c ==
'<' || c ==
'>' || c ==
'&') {
52 s <<
"\\u" << std::hex << std::setfill(
'0') << std::setw(4)
53 <<
static_cast<unsigned int>(c) << std::dec;
62 void graph_sparsity(std::ostream& s,
const Sparsity& sp) {
63 s <<
"{\"shape\":[" << sp.size1() <<
"," << sp.size2() <<
"],\"colind\":[";
64 for (casadi_int i = 0; i <= sp.size2(); ++i) {
69 for (casadi_int i = 0; i < sp.nnz(); ++i) {
77 casadi_int id, op, io = -1, offset = 0;
79 std::string kind, display, symbol;
80 std::vector<std::string> input_names, output_names, constants;
81 std::vector<Sparsity> inputs, outputs;
83 struct GraphEdge { casadi_int from, output, to, input; };
85 std::vector<GraphNode> nodes;
86 std::vector<GraphEdge> edges;
89 std::string graph_model(Function
function,
const std::string& direction,
90 std::vector<Function>& functions, std::map<const FunctionInternal*, casadi_int>& indices,
91 bool include_functions, GraphModel& model) {
92 auto sx =
dynamic_cast<const SXFunction*
>(
function.get());
93 auto mx =
dynamic_cast<const MXFunction*
>(
function.get());
94 const auto sx_inputs = sx ?
function.sx_in() : std::vector<SX>{};
95 const auto mx_inputs = mx ?
function.mx_in() : std::vector<MX>{};
96 std::ostringstream data, edges;
97 data <<
"{\"version\":1,\"name\":" << graph_string(
function.name())
98 <<
",\"type\":" << graph_string(
function.class_name())
99 <<
",\"direction\":" << graph_string(direction) <<
",\"inputs\":[";
100 for (casadi_int i = 0; i <
function.n_in(); ++i) {
102 data <<
"{\"name\":" << graph_string(
function.name_in(i)) <<
",\"sparsity\":";
103 graph_sparsity(data,
function.sparsity_in(i));
106 data <<
"],\"outputs\":[";
107 for (casadi_int i = 0; i <
function.n_out(); ++i) {
109 data <<
"{\"name\":" << graph_string(
function.name_out(i)) <<
",\"sparsity\":";
110 graph_sparsity(data,
function.sparsity_out(i));
113 data <<
"],\"nodes\":[";
116 std::map<casadi_int, std::pair<casadi_int, casadi_int>> producer;
117 bool first_edge =
true;
118 for (casadi_int k = 0; k <
function.n_instructions(); ++k) {
119 casadi_int op =
function.instruction_id(k);
120 auto arg =
function.instruction_input(k), res =
function.instruction_output(k);
122 std::string expression = sx ? sx->print(sx->algorithm_.at(k))
123 : mx->print(mx->algorithm_.at(k));
125 node.id = k; node.op = op;
126 std::string kind =
"operation", io_metadata, call_metadata;
129 casadi_int io = (input ? arg : res).at(0);
130 casadi_int offset = sx ? (input ? arg : res).at(1)
131 : mx->algorithm_.at(k).data->offset();
132 node.io = io; node.offset = offset;
133 io_metadata =
",\"io_index\":" +
str(io) +
",\"io_offset\":" +
str(offset);
135 std::string symbol = sx ?
str(sx_inputs.at(io).nonzeros().at(offset))
136 :
str(mx_inputs.at(io));
137 node.symbol = symbol;
138 io_metadata +=
",\"symbol\":" + graph_string(symbol);
140 label = (input ?
function.name_in(io) :
function.name_out(io));
141 if ((sx && (input ?
function.nnz_in(io) :
function.nnz_out(io)) != 1) || offset != 0) {
142 label +=
"[" +
str(offset) +
"]";
144 kind = input ?
"input" :
"output";
147 label = sx ?
str(sx->free_vars_.at(sx->algorithm_.at(k).i1))
148 : mx->algorithm_.at(k).data.name();
153 label = (sx ? sx->call_.el.at(sx->algorithm_.at(k).i1).f.name()
154 : mx->algorithm_.at(k).data.which_function().name());
158 std::vector<std::string> input_names, output_names, constants;
159 for (casadi_int i = 0; i < arg.size(); ++i) input_names.push_back(
"arg" +
str(i));
160 for (casadi_int i = 0; i < res.size(); ++i) output_names.push_back(
"out" +
str(i));
162 const Function& f = sx ? sx->call_.el.at(sx->algorithm_.at(k).i1).f
163 : mx->algorithm_.at(k).data.which_function();
164 call_metadata =
",\"callee_type\":" + graph_string(f.class_name());
165 Dict info = f.info();
166 auto source = info.find(
"model_path");
167 if (source != info.end()) {
168 std::string
path = source->second.to_string();
170 call_metadata +=
",\"model_path\":" + graph_string(
path);
171 label +=
" [" +
path.substr(
path.find_last_of(
"/\\") + 1) +
"]";
174 if (include_functions && (f.is_a(
"SXFunction") || f.is_a(
"MXFunction"))) {
175 auto found = indices.find(f.get());
176 if (found == indices.end()) {
177 found = indices.emplace(f.get(), functions.size()).first;
178 functions.push_back(f);
180 call_metadata +=
",\"callee\":" +
str(found->second);
182 input_names.clear(); output_names.clear();
183 for (casadi_int side = 0; side < 2; ++side) {
184 auto& names = side == 0 ? input_names : output_names;
185 for (casadi_int i = 0; i < (side == 0 ? f.n_in() : f.n_out()); ++i) {
186 std::string name = side == 0 ? f.name_in(i) : f.name_out(i);
187 casadi_int count = side == 0 ? f.nnz_in(i) : f.nnz_out(i);
189 for (casadi_int j = 0; j < count; ++j) {
190 names.push_back(name + (count == 1 ?
"" :
"[" +
str(j) +
"]"));
193 names.push_back(name);
198 input_names = {
"accumulator",
"left",
"right"};
200 input_names = {
"source"};
202 input_names = {
"base",
"values"};
204 std::string display = label, formula = label;
205 if (kind ==
"operation") {
210 for (casadi_int i = 0; i < input_names.size(); ++i) {
212 formula += input_names[i];
217 case OP_ADD: display =
"+";
break;
218 case OP_SUB: display =
"-";
break;
219 case OP_MUL: display =
"*";
break;
220 case OP_DIV: display =
"/";
break;
221 case OP_NEG: display =
"-";
break;
222 case OP_SQ: display =
"(.)^2";
break;
224 case OP_TWICE: display =
"2*(.)";
break;
225 case OP_INV: display =
"1/(.)";
break;
229 std::vector<double> values = sx ? std::vector<double> {sx->algorithm_.at(k).d}
230 :
static_cast<DM>(mx->algorithm_.at(k).data).nonzeros();
231 for (
double value : values) {
232 std::ostringstream number;
233 number << std::setprecision(17) << value;
234 constants.push_back(number.str());
236 display = constants.size() == 1 ? constants.front() :
"constant";
239 node.kind = kind; node.display = display;
241 node.ordered = arg.size() > 1 && !operation_checker<CommChecker>(op);
242 node.input_names = input_names; node.output_names = output_names;
243 node.constants = constants;
245 data <<
"{\"id\":" << k <<
",\"op\":" << op <<
",\"label\":"
246 << graph_string(label) <<
",\"kind\":" << graph_string(kind)
247 <<
",\"expression\":" << graph_string(expression)
248 <<
",\"display\":" << graph_string(display)
249 <<
",\"formula\":" << graph_string(formula)
251 <<
",\"ordered\":" << (arg.size() > 1 && !operation_checker<CommChecker>(op)
253 for (
const auto& entry : std::map<std::string, std::vector<std::string>>{
254 {
"input_names", input_names}, {
"output_names", output_names}, {
"constants", constants}}) {
255 data <<
"," << graph_string(entry.first) <<
":[";
256 for (casadi_int i = 0; i < entry.second.size(); ++i) {
258 data << graph_string(entry.second[i]);
263 data <<
",\"mapping_kind\":" << graph_string(op ==
OP_GETNONZEROS ?
"extract"
265 const auto mapping = mx->algorithm_.at(k).data.mapping().nonzeros();
266 for (casadi_int i = 0; i < mapping.size(); ++i) {
272 data << io_metadata << call_metadata <<
",\"inputs\":[";
273 for (casadi_int i = 0; i < arg.size(); ++i) {
275 node.inputs.push_back(sx ?
Sparsity::scalar() : mx->algorithm_.at(k).data->dep(i).sparsity());
276 graph_sparsity(data, node.inputs.back());
277 if (arg[i] < 0)
continue;
278 auto p = producer.find(arg[i]);
279 casadi_assert(p != producer.end(),
"Missing graph producer for instruction " +
str(k));
280 model.edges.push_back({p->second.first, p->second.second, k, i});
281 if (!first_edge) edges <<
",";
283 edges <<
"{\"from\":" << p->second.first <<
",\"output\":" << p->second.second
284 <<
",\"to\":" << k <<
",\"input\":" << i <<
"}";
286 data <<
"],\"outputs\":[";
287 for (casadi_int i = 0; i < res.size(); ++i) {
289 node.outputs.push_back(sx ?
Sparsity::scalar() : mx->algorithm_.at(k).data->sparsity(i));
290 graph_sparsity(data, node.outputs.back());
291 if (res[i] >= 0) producer[res[i]] = {k, i};
294 model.nodes.push_back(std::move(node));
296 data <<
"],\"edges\":[" << edges.str() <<
"]}";
302 std::string dot_quote(
const std::string& value) {
303 std::string out =
"\"";
304 for (
char c : value) {
307 }
else if (c ==
'\r') {
310 if (c ==
'\\' || c ==
'"') out +=
'\\';
316 std::string dot_html(
const std::string& value) {
318 for (
char c : value) {
320 case '&': out +=
"&";
break;
321 case '<': out +=
"<";
break;
322 case '>': out +=
">";
break;
323 case '"': out +=
""";
break;
329 std::string graph_size(
const Sparsity& sp) {
330 return str(sp.size1()) +
"-by-" +
str(sp.size2());
332 void dot_matrix(std::ostream& s,
const std::string&
id,
const Sparsity& sp,
333 const std::string& title,
const std::vector<std::string>* constants =
nullptr,
334 const std::string& color =
"#666666",
bool show_sizes =
true) {
335 s <<
id <<
" [shape=plain, fontcolor=\"#666666\", fontsize=10, label=<"
336 <<
"<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"3\">";
337 if (show_sizes || !title.empty()) s <<
"<TR><TD COLSPAN=\""
338 << std::max<casadi_int>(1, std::min<casadi_int>(8, sp.size2()))
339 <<
"\"><FONT COLOR=\"" << color <<
"\">"
340 << dot_html(show_sizes ? (title.empty() ? graph_size(sp) : title +
" : " + graph_size(sp))
342 <<
"</FONT></TD></TR>";
343 std::map<std::pair<casadi_int, casadi_int>, casadi_int> lookup;
344 for (casadi_int c = 0; c < std::min<casadi_int>(8, sp.size2()); ++c) {
345 for (casadi_int k = sp.colind()[c]; k < sp.colind()[c+1]; ++k) {
346 if (sp.row()[k] < 8) lookup[{sp.row()[k], c}] = k;
349 for (casadi_int r = 0; r < std::min<casadi_int>(8, sp.size1()); ++r) {
351 for (casadi_int c = 0; c < std::min<casadi_int>(8, sp.size2()); ++c) {
352 auto nz = lookup.find({r, c});
353 s <<
"<TD BGCOLOR=\"white\"";
354 if (nz != lookup.end()) s <<
" PORT=\"nz" << nz->second <<
"\"";
356 if (constants) s << (nz == lookup.end() ?
"." : dot_html(constants->at(nz->second)));
358 s <<
"<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">"
359 <<
"<TR><TD FIXEDSIZE=\"TRUE\" WIDTH=\"9\" HEIGHT=\"9\" BGCOLOR=\""
360 << (nz == lookup.end() ?
"white" :
"#111111") <<
"\"></TD></TR></TABLE>";
363 if (!sp.size2()) s <<
"<TD>empty</TD>";
366 if (!sp.size1()) s <<
"<TR><TD>empty</TD></TR>";
367 if (sp.size1() > 8 || sp.size2() > 8) s <<
"<TR><TD>...</TD></TR>";
368 s <<
"</TABLE>>];\n";
370 bool dot_record(
const GraphNode& n) {
371 return (n.ordered && !n.binary) || n.kind ==
"call";
373 bool dot_has_table(
const GraphNode& n,
const Sparsity& sp,
bool show_contents) {
374 if (!show_contents || sp.numel() == 1)
return false;
375 if (n.kind ==
"call" || n.inputs.empty() || n.op ==
OP_GETNONZEROS
377 for (
const auto& input : n.inputs)
if (input != sp)
return true;
380 void dot_ports(std::ostream& s,
const GraphNode& n,
381 const std::vector<std::string>& names,
const std::string& prefix,
bool show_sizes) {
382 if (names.empty())
return;
383 s <<
"<TR><TD><TABLE BORDER=\"0\" CELLBORDER=\"1\" COLOR=\"#d26666\"><TR>";
384 for (casadi_int i = 0; i < names.size(); ++i) {
385 s <<
"<TD PORT=\"" << prefix << i <<
"\"><FONT POINT-SIZE=\"10\">"
386 << dot_html(names[i]);
387 const auto& sp = prefix ==
"in" ? n.inputs.at(i) : n.outputs.at(i);
388 if (show_sizes && sp.numel() != 1) s <<
" " << graph_size(sp);
391 s <<
"</TR></TABLE></TD></TR>";
393 void graph_dot(std::ostream& s,
const Function& f,
const GraphModel& model,
394 const std::string& direction,
const std::string& view,
bool show_contents,
bool show_sizes) {
395 bool function_view = view ==
"function";
396 std::map<std::pair<casadi_int, casadi_int>, std::string> sources;
397 std::map<casadi_int, std::string> targets;
398 s <<
"digraph G {\ngraph [rankdir=" << (function_view ?
"TB" : direction)
399 <<
", bgcolor=\"white\", pad=0.4, nodesep=0.55, ranksep=0.65];\n"
400 <<
"node [shape=ellipse, style=filled, color=\"#b00000\", fillcolor=\"#b00000\", "
401 <<
"fontcolor=white, fontname=Helvetica, fontsize=14, margin=\"0.15,0.08\", "
402 <<
"width=0.5, height=0.5];\nedge [color=\"#34658b\", penwidth=1.5, arrowsize=0.65];\n";
403 for (
const auto& n : model.nodes) {
404 if (n.kind ==
"output" || (function_view && n.kind ==
"input"))
continue;
405 const std::string
id =
"n" +
str(n.id);
406 std::string display = !function_view && n.kind ==
"input" ? n.symbol : n.display;
407 std::string color = n.kind ==
"input" || n.kind ==
"symbol" ?
"#34658b"
408 : n.kind ==
"constant" ?
"#38754d" :
"#b00000";
409 s <<
id <<
" [color=" << dot_quote(color) <<
", fillcolor=" << dot_quote(color);
411 s <<
", shape=plain, label=<<TABLE BORDER=\"0\" CELLBORDER=\"0\" "
412 <<
"CELLSPACING=\"0\" CELLPADDING=\"6\" BGCOLOR=\"#b00000\">";
413 dot_ports(s, n, n.input_names,
"in", show_sizes);
414 s <<
"<TR><TD>" << dot_html(display) <<
"</TD></TR>";
415 if (n.kind ==
"call") dot_ports(s, n, n.output_names,
"out", show_sizes);
418 const auto& ports = n.outputs.empty() ? n.inputs : n.outputs;
420 for (
const auto& sp : n.outputs) table = table || dot_has_table(n, sp, show_contents);
421 if (show_sizes && !table && !ports.empty() && ports.front().numel() != 1) {
422 display +=
"\n" + graph_size(ports.front());
424 s <<
", label=" << dot_quote(display);
427 for (casadi_int i = 0; i < n.outputs.size(); ++i) {
428 const auto& sp = n.outputs[i];
429 if (!dot_has_table(n, sp, show_contents))
continue;
430 std::string value =
"v" +
str(n.id) +
"_" +
str(i);
431 dot_matrix(s, value, sp, n.kind ==
"call" ? n.output_names.at(i) :
"",
432 n.kind ==
"constant" ? &n.constants :
nullptr,
"#666666", show_sizes);
433 s <<
id << (n.kind ==
"call" ?
":out" +
str(i) :
"") <<
" -> " << value <<
";\n";
434 sources[{n.id, i}] = value;
437 for (
bool input : {
true,
false}) {
438 std::vector<std::string> row;
439 for (casadi_int i = 0; i < (input ? f.n_in() : f.n_out()); ++i) {
440 const auto& sp = input ? f.sparsity_in(i) : f.sparsity_out(i);
441 if (!function_view && (input || !show_contents || !f.is_a(
"SXFunction")
442 || sp.numel() == 1))
continue;
443 std::string kind = input ?
"input" :
"output",
id =
"b" + kind +
"_" +
str(i);
444 std::string name = function_view ? (input ? f.name_in(i) : f.name_out(i)) :
"";
445 if (show_contents && sp.numel() != 1) dot_matrix(s,
id, sp, name,
nullptr,
446 function_view ? (input ?
"#34658b" :
"#b00000") :
"#666666", show_sizes);
448 s <<
id <<
" [label=" << dot_quote(name
449 + (show_sizes && sp.numel() != 1 ?
"\n" + graph_size(sp) :
"")) <<
", color=\""
450 << (input ?
"#34658b" :
"#b00000") <<
"\", fillcolor=\""
451 << (input ?
"#34658b" :
"#b00000") <<
"\"];\n";
452 for (
const auto& n : model.nodes) {
453 if (n.kind != kind || n.io != i)
continue;
454 std::string endpoint = id;
455 if (show_contents && f.is_a(
"SXFunction") && sp.numel() != 1 && n.offset < sp.nnz()) {
456 const auto col = std::upper_bound(sp.colind(), sp.colind()+sp.size2()+1, n.offset)
458 if (col < 8 && sp.row()[n.offset] < 8) endpoint +=
":nz" +
str(n.offset);
460 if (input) sources[{n.id, 0}] = endpoint;
462 targets[n.id] = endpoint;
466 if (function_view && !row.empty()) {
467 s <<
"{rank=" << (input ?
"source" :
"sink") <<
";";
468 for (
const auto&
id : row) s <<
id <<
";";
470 for (casadi_int i = 1; i < row.size(); ++i) {
471 s << row[i-1] <<
" -> " << row[i] <<
" [style=invis, weight=100];\n";
475 for (
const auto& e : model.edges) {
476 const auto& from = model.nodes.at(e.from);
477 const auto& to = model.nodes.at(e.to);
478 auto source = sources.find({e.from, e.output});
479 auto target = targets.find(e.to);
480 if (to.kind ==
"output" && target == targets.end())
continue;
481 s << (source != sources.end() ? source->second :
"n" +
str(e.from)
482 + (from.kind ==
"call" ?
":out" +
str(e.output) :
"")) <<
" -> ";
483 if (target != targets.end()) s << target->second;
485 s <<
"n" << e.to << (to.binary ? (e.input == 0 ?
":w" :
":e")
486 : dot_record(to) ?
":in" +
str(e.input) :
"");
492 struct GraphOptions {
493 std::string direction =
"TB", viz_js, view =
"function";
494 std::string viewer_url =
"https://unpkg.com/@casadi/casadi-viz@"
498 bool include_functions =
true, show_matrix_contents =
true, show_matrix_sizes =
true;
499 explicit GraphOptions(
const Dict& opts) {
500 for (
const auto& opt : opts) {
501 if (opt.first ==
"direction") {
502 direction = opt.second.to_string();
503 }
else if (opt.first ==
"view") {
504 view = opt.second.to_string();
505 }
else if (opt.first ==
"include_functions") {
506 include_functions = opt.second.to_bool();
507 }
else if (opt.first ==
"show_matrix_contents") {
508 show_matrix_contents = opt.second.to_bool();
509 }
else if (opt.first ==
"show_matrix_sizes") {
510 show_matrix_sizes = opt.second.to_bool();
511 }
else if (opt.first ==
"viewer_url") {
512 viewer_url = opt.second.to_string();
513 }
else if (opt.first ==
"viz_js") {
514 viz_js = opt.second.to_string();
516 casadi_error(
"Unknown export_graph option: " + opt.first);
519 casadi_assert(direction ==
"LR" || direction ==
"RL" || direction ==
"TB"
520 || direction ==
"BT",
"Invalid export_graph direction: " + direction);
522 casadi_assert(view ==
"function" || view ==
"expression",
523 "Invalid export_graph view: " + view);
528 std::string graph_bundle(
const Function& f,
const GraphOptions& opts,
bool dot =
false) {
529 casadi_assert(f.is_a(
"SXFunction") || f.is_a(
"MXFunction"),
530 "export_graph requires an SXFunction or MXFunction");
531 std::vector<Function> functions{f};
532 std::map<const FunctionInternal*, casadi_int> indices{{f.get(), 0}};
533 std::vector<std::string> models;
534 for (casadi_int i = 0; i < functions.size(); ++i) {
536 models.push_back(graph_model(functions[i], opts.direction, functions, indices,
537 opts.include_functions, model));
539 std::ostringstream output;
540 graph_dot(output, f, model, opts.direction, opts.view,
541 opts.show_matrix_contents, opts.show_matrix_sizes);
545 std::ostringstream data;
546 data << models.front().substr(0, models.front().size()-1)
547 <<
",\"format\":\"casadi_viz\",\"view\":"
548 << graph_string(opts.view) <<
",\"casadi_version\":"
550 for (casadi_int i = 1; i < models.size(); ++i) {
551 if (i > 1) data <<
",";
559 void write_graph(
const std::string& fname,
const std::string& data,
560 const GraphOptions& options) {
561 if (fname.size() < 5 || fname.substr(fname.size()-5) !=
".html") {
563 *output << data <<
"\n";
565 casadi_assert(output->good(),
"Failed to write graph to '" + fname +
"'");
569 std::ostringstream config;
570 config <<
"{\"viewer_url\":" << graph_string(options.viewer_url) <<
",\"runtime\":";
571 if (options.viz_js.empty()) {
575 std::ostringstream contents;
576 contents << input->rdbuf();
577 config <<
"{\"source\":" << graph_string(contents.str()) <<
"}";
582 const std::string html = resource_casadi_viz;
583 const std::string graph_marker =
"@@GRAPH_DATA@@", viz_marker =
"@@VIZ_CONFIG@@";
584 size_t graph_pos = html.find(graph_marker), viz_pos = html.find(viz_marker);
585 casadi_assert_dev(graph_pos != std::string::npos && viz_pos > graph_pos);
587 *output << html.substr(0, graph_pos) << data
588 << html.substr(graph_pos + graph_marker.size(), viz_pos-graph_pos-graph_marker.size())
589 << config.str() << html.substr(viz_pos + viz_marker.size());
591 casadi_assert(output->good(),
"Failed to write graph to '" + fname +
"'");
594 std::string graph_extension(
const std::string& fname) {
595 const auto dot = fname.find_last_of(
'.');
596 const std::string extension =
dot == std::string::npos ?
"" : fname.substr(
dot);
597 casadi_assert(extension ==
".html" || extension ==
".dot" || extension ==
".casadi_viz",
598 "Unsupported export_graph extension '" + extension
599 +
"'. Supported extensions: .html, .dot, .casadi_viz");
603 template<
typename MatType>
604 std::string expression_bundle(
const std::vector<MatType>& expressions,
const Dict& opts) {
605 Dict defaults = opts;
606 if (!defaults.count(
"view")) defaults[
"view"] =
"expression";
607 const GraphOptions options(defaults);
608 StringSerializer serializer;
609 serializer.pack(expressions);
610 std::ostringstream data;
611 data <<
"{\"format\":\"casadi_viz\",\"version\":1,\"source\":"
612 << graph_string(serializer.encode()) <<
",\"view\":" << graph_string(options.view)
613 <<
",\"direction\":" << graph_string(options.direction)
615 <<
",\"include_functions\":" << (options.include_functions ?
"true" :
"false")
616 <<
",\"show_matrix_contents\":" << (options.show_matrix_contents ?
"true" :
"false")
617 <<
",\"show_matrix_sizes\":" << (options.show_matrix_sizes ?
"true" :
"false") <<
"}";
621 template<
typename MatType>
622 void export_expressions(
const std::vector<MatType>& expressions,
const std::string& fname,
624 if (graph_extension(fname) ==
".dot") {
625 Dict defaults = opts;
626 if (!defaults.count(
"view")) defaults[
"view"] =
"expression";
627 Function(
"expression", symvar(veccat(expressions)), expressions).export_graph(fname, defaults);
629 write_graph(fname, expression_bundle(expressions, opts), GraphOptions(opts));
636 return graph_bundle(*
this, GraphOptions(opts));
640 const std::string extension = graph_extension(fname);
641 const GraphOptions options(opts);
642 write_graph(fname, graph_bundle(*
this, options, extension ==
".dot"), options);
646 export_expressions(std::vector<SX>{expression}, fname, opts);
649 void export_graph(
const std::vector<SX>& expressions,
const std::string& fname,
const Dict& opts) {
650 export_expressions(expressions, fname, opts);
654 export_expressions(std::vector<MX>{expression}, fname, opts);
657 void export_graph(
const std::vector<MX>& expressions,
const std::string& fname,
const Dict& opts) {
658 export_expressions(expressions, fname, opts);
662 return expression_bundle(expressions, opts);
670 return expression_bundle(expressions, opts);
static std::unique_ptr< std::ostream > ofstream_ptr(const std::string &path, std::ios_base::openmode mode=std::ios_base::out)
static std::unique_ptr< std::istream > ifstream_ptr(const std::string &path, std::ios_base::openmode mode=std::ios_base::in, bool fail=true)
void export_graph(const std::string &fname, const Dict &opts=Dict()) const
Export an SX/MX instruction graph as .html, .dot, or .casadi_viz.
static std::string print_operator(const MX &x, const std::vector< std::string > &args)
Sparse matrix class. SX and DM are specializations.
static Sparsity scalar(bool dense_scalar=true)
Create a scalar sparsity pattern *.
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
CASADI_EXPORT std::string export_graph(const MX &expression, const Dict &opts=Dict())
Export expressions as .html, .dot, or .casadi_viz, defaulting to expression view.
T dot(const std::vector< T > &a, const std::vector< T > &b)
std::vector< casadi_int > path(const std::vector< casadi_int > &map, casadi_int i_start)
static bool is_binary(unsigned char op)
Is binary operation?
static std::string post(unsigned char op)
static std::string pre(unsigned char op)
static std::string name(unsigned char op)
static std::string sep(unsigned char op)