CppADCodeGen 2.4.3
A C++ Algorithmic Differentiation Package with Source Code Generation
Loading...
Searching...
No Matches
language_mathml.hpp
1#ifndef CPPAD_CG_LANGUAGE_MATHML_INCLUDED
2#define CPPAD_CG_LANGUAGE_MATHML_INCLUDED
3/* --------------------------------------------------------------------------
4 * CppADCodeGen: C++ Algorithmic Differentiation with Source Code Generation:
5 * Copyright (C) 2015 Ciengis
6 * Copyright (C) 2018 Joao Leal
7 *
8 * CppADCodeGen is distributed under multiple licenses:
9 *
10 * - Eclipse Public License Version 1.0 (EPL1), and
11 * - GNU General Public License Version 3 (GPL3).
12 *
13 * EPL1 terms and conditions can be found in the file "epl-v10.txt", while
14 * terms and conditions for the GPL3 can be found in the file "gpl3.txt".
15 * ----------------------------------------------------------------------------
16 * Author: Joao Leal
17 */
18
19namespace CppAD {
20namespace cg {
21
27template<class Base>
28class LanguageMathML : public Language<Base> {
29public:
31 using Arg = Argument<Base>;
32protected:
33 static const std::string _C_STATIC_INDEX_ARRAY;
34 static const std::string _C_SPARSE_INDEX_ARRAY;
35 static const std::string _ATOMIC_TX;
36 static const std::string _ATOMIC_TY;
37 static const std::string _ATOMIC_PX;
38 static const std::string _ATOMIC_PY;
39protected:
40 // information from the code handler (not owned)
42 // current indentation
43 size_t _indentationLevel;
44 // css style
45 std::string _style;
46 // javascript source code
47 std::string _javascript;
48 // additional markup for the head
49 std::string _headExtra;
50 std::string _startEq;
51 std::string _endEq;
52 std::string _forStart;
53 std::string _forEnd;
54 std::string _forBodyStart;
55 std::string _forBodyEnd;
56 std::string _ifStart;
57 std::string _ifEnd;
58 std::string _elseIfStart;
59 std::string _elseIfEnd;
60 std::string _elseStart;
61 std::string _elseEnd;
62 std::string _condBodyStart;
63 std::string _condBodyEnd;
64 std::string _assignStr;
65 std::string _assignAddStr;
66 // markup for multiplications
67 std::string _multOpStr;
68 // markup for multiplications with parameters
69 std::string _multValOpStr;
70 // new line characters
71 std::string _endline;
72 // output stream for the generated source code
73 std::ostringstream _code;
74 // creates the variable names
76 // auxiliary string stream
77 std::ostringstream _ss;
78 //
79 size_t _independentSize;
80 //
81 size_t _minTemporaryVarID;
82 // maps the variable IDs to the their position in the dependent vector
83 // (some IDs may be the same as the independent variables when dep = indep)
84 std::map<size_t, size_t> _dependentIDs;
85 // the dependent variable vector
86 const ArrayView<CG<Base> >* _dependent;
87 // whether or not to ignore assignment of constant zero values to dependent variables
88 bool _ignoreZeroDepAssign;
89 // the name of the file to be created without the extension
90 std::string _filename;
91 // the maximum number of assignment (~lines) per local file
92 size_t _maxAssignmentsPerFile;
93 // maps filenames to their content
94 std::map<std::string, std::string>* _sources;
95 // the values in the temporary array
96 std::vector<const Arg*> _tmpArrayValues;
97 // the values in the temporary sparse array
98 std::vector<const Arg*> _tmpSparseArrayValues;
99 //
100 std::vector<const LoopStartOperationNode<Base>*> _currentLoops;
101 // the maximum precision used to print values
102 size_t _parameterPrecision;
103 // whether or not to always enclose the base of a power within parenthesis
104 bool _powBaseEnclose;
105 // whether or not to save dependencies among variables in javascript
106 bool _saveVariableRelations;
107private:
108 std::string auxArrayName_;
109 std::vector<int> varIds_;
110 std::vector<std::string> depConstIds_;
111 std::vector<std::string> depIsIndepIds_;
112public:
113
118 _info(nullptr),
119 _indentationLevel(0),
120 _style(".loop{}\n"
121 ".loopBody{padding-left: 2em;}\n"
122 ".condIf{}\n"
123 ".condElseIf{}\n"
124 ".condElse{}\n"
125 ".condBody{padding-left: 2em;}\n"
126 ".dep{color:#600;}\n"
127 ".indep{color:#060;}\n"
128 ".tmp{color:#006;}\n"
129 ".index{color:#00f;}\n"),
130 _startEq(R"(<math display="block" class="equation">)"),
131 _endEq("</math>"),
132 _forStart("<div class='loop'>"),
133 _forEnd("</div>"),
134 _forBodyStart("<div class='loopBody'>"),
135 _forBodyEnd("</div>"),
136 _ifStart("<div class='condIf'>"),
137 _ifEnd("</div>"),
138 _elseIfStart("<div class='condElseIf'>"),
139 _elseIfEnd("</div>"),
140 _elseStart("<div class='condElse'>"),
141 _elseEnd("</div>"),
142 _condBodyStart("<div class='condBody'>"),
143 _condBodyEnd("</div>"),
144 _assignStr("<mo>=</mo>"),
145 _assignAddStr("<mo>+=</mo>"),
146 _multOpStr("<mo>&it;</mo>"),
147 _multValOpStr("<mo>&times;</mo>"),
148 _endline("\n"),
149 _nameGen(nullptr),
150 _independentSize(0), // not really required (but it avoids warnings)
151 _minTemporaryVarID(0), // not really required (but it avoids warnings)
152 _dependent(nullptr),
153 _ignoreZeroDepAssign(false),
154 _filename("algorithm"),
155 _maxAssignmentsPerFile(0),
156 _sources(nullptr),
157 _parameterPrecision(std::numeric_limits<Base>::digits10),
158 _powBaseEnclose(false),
159 _saveVariableRelations(false) {
160 }
161
162 inline virtual ~LanguageMathML() = default;
163
164 inline const std::string& getAssignMarkup() const {
165 return _assignStr;
166 }
167
168 inline void setAssignMarkup(const std::string& assign) {
169 _assignStr = assign;
170 }
171
172 inline const std::string& getAddAssignMarkup() const {
173 return _assignAddStr;
174 }
175
176 inline void setAddAssignMarkup(const std::string& assignAdd) {
177 _assignAddStr = assignAdd;
178 }
179
187 inline const std::string& getMultiplicationMarkup() const {
188 return _multOpStr;
189 }
190
199 inline void setMultiplicationMarkup(const std::string& multOpStr) {
200 _multOpStr = multOpStr;
201 }
202
208 inline const std::string& getMultiplicationConstParMarkup() const {
209 return _multValOpStr;
210 }
211
220 inline void setMultiplicationConstParMarkup(const std::string& multValOpStr) {
221 _multValOpStr = multValOpStr;
222 }
223
224 inline bool isIgnoreZeroDepAssign() const {
225 return _ignoreZeroDepAssign;
226 }
227
228 inline void setIgnoreZeroDepAssign(bool ignore) {
229 _ignoreZeroDepAssign = ignore;
230 }
231
232 void setFilename(const std::string& name) {
233 _filename = name;
234 }
235
241 void setStyle(const std::string& style) {
242 _style = style;
243 }
244
245 const std::string& getStyle() const {
246 return _style;
247 }
248
254 void setJavascript(const std::string& javascript) {
255 _javascript = javascript;
256 }
257
258 const std::string& getJavascript() const {
259 return _javascript;
260 }
261
267 void setHeadExtraMarkup(const std::string& headExtra) {
268 _headExtra = headExtra;
269 }
270
271 const std::string& getHeadExtraMarkup() const {
272 return _headExtra;
273 }
274
281 virtual void setEquationMarkup(const std::string& begin,
282 const std::string& end) {
283 _startEq = begin;
284 _endEq = end;
285 }
286
290 virtual const std::string& getEquationStartMarkup() const {
291 return _startEq;
292 }
293
297 virtual const std::string& getEquationEndMarkup() const {
298 return _endEq;
299 }
300
307 virtual void setForMarkup(const std::string& begin,
308 const std::string& end) {
309 _forStart = begin;
310 _forEnd = end;
311 }
312
316 virtual const std::string& getForStartMarkup() const {
317 return _forStart;
318 }
319
323 virtual const std::string& getForEndMarkup() const {
324 return _forEnd;
325 }
326
333 virtual void setIfMarkup(const std::string& begin,
334 const std::string& end) {
335 _ifStart = begin;
336 _ifEnd = end;
337 }
338
342 virtual const std::string& getIfStartMarkup() const {
343 return _ifStart;
344 }
345
349 virtual const std::string& getIfEndMarkup() const {
350 return _ifEnd;
351 }
352
359 virtual void setElseIfMarkup(const std::string& begin,
360 const std::string& end) {
361 _elseIfStart = begin;
362 _elseIfEnd = end;
363 }
364
368 virtual const std::string& getElseIfStartMarkup() const {
369 return _elseIfStart;
370 }
371
375 virtual const std::string& getElseIfEndMarkup() const {
376 return _elseIfEnd;
377 }
378
385 virtual void setElseMarkup(const std::string& begin,
386 const std::string& end) {
387 _elseStart = begin;
388 _elseEnd = end;
389 }
390
394 virtual const std::string& getElseStartMarkup() const {
395 return _elseStart;
396 }
397
401 virtual const std::string& getElseEndMarkup() const {
402 return _elseEnd;
403 }
404
411 virtual size_t getParameterPrecision() const {
412 return _parameterPrecision;
413 }
414
421 virtual void setParameterPrecision(size_t p) {
422 _parameterPrecision = p;
423 }
424
433 virtual void setAlwaysEnclosePowBase(bool enclose) {
434 _powBaseEnclose = enclose;
435 }
436
443 virtual bool isAlwaysEnclosePowBase() const {
444 return _powBaseEnclose;
445 }
446
447 virtual void setMaxAssignmentsPerFunction(size_t maxAssignmentsPerFunction,
448 std::map<std::string, std::string>* sources) {
449 _maxAssignmentsPerFile = maxAssignmentsPerFunction;
450 _sources = sources;
451 }
452
453 inline void setSaveVariableRelations(bool save) {
454 _saveVariableRelations = save;
455 }
456
457 bool requiresVariableDependencies() const override {
458 return _saveVariableRelations;
459 }
460
461 /***************************************************************************
462 * STATIC
463 **************************************************************************/
464 static inline void printIndexCondExpr(std::ostringstream& out,
465 const std::vector<size_t>& info,
466 const std::string& index) {
467 CPPADCG_ASSERT_KNOWN(info.size() > 1 && info.size() % 2 == 0, "Invalid number of information elements for an index condition expression operation")
468
469 size_t infoSize = info.size();
470 for (size_t e = 0; e < infoSize; e += 2) {
471 if (e > 0) {
472 out << "<mo>&or;</mo>"; // or
473 }
474 size_t min = info[e];
475 size_t max = info[e + 1];
476 if (min == max) {
477 out << "<mi class='index'>" << index << "</mi><mo>==</mo><mn>" << min << "</nm>";
478 } else if (min == 0) {
479 out << "<mi class='index'>" << index << "</mi><mo>&le;</mo><mn>" << max << "</mn>";
480 } else if (max == (std::numeric_limits<size_t>::max)()) {
481 out << "<mn>" << min << "</mn><mo>&le;</mo><mi class='index'>" << index << "</mi>";
482 } else {
483 if (infoSize != 2)
484 out << "<mfenced><mrow>";
485
486 if (max - min == 1)
487 out << "<mn>" << min << "</mn><mo>==</mo><mi class='index'>" << index << "</mi><mo>&or;</mo><mi class='index'>" << index << "</mi><mo>==</mo><mn>" << max << "</mn>";
488 else
489 out << "<mn>" << min << "</mn><mo>&le;</mo><mi class='index'>" << index << "</mi><mo>&and;</mo><mi class='index'>" << index << "</mi><mo>&le;</mo><mn>" << max << "</mn";
490
491 if (infoSize != 2)
492 out << "</mrow></mfenced>";
493 }
494 }
495 }
496
497 /***************************************************************************
498 *
499 **************************************************************************/
500
501 inline void printStaticIndexArray(std::ostringstream& os,
502 const std::string& name,
503 const std::vector<size_t>& values);
504
505 inline void printStaticIndexMatrix(std::ostringstream& os,
506 const std::string& name,
507 const std::map<size_t, std::map<size_t, size_t> >& values);
508
509 /***************************************************************************
510 * index patterns
511 **************************************************************************/
512 static inline void generateNames4RandomIndexPatterns(const std::set<RandomIndexPattern*>& randomPatterns);
513
514 inline void printRandomIndexPatternDeclaration(std::ostringstream& os,
515 const std::string& identation,
516 const std::set<RandomIndexPattern*>& randomPatterns);
517
518 static void indexPattern2String(std::ostream& os,
519 const IndexPattern& ip,
520 const Node& index);
521
522 static void indexPattern2String(std::ostream& os,
523 const IndexPattern& ip,
524 const std::vector<const Node*>& indexes);
525
526 static inline void linearIndexPattern2String(std::ostream& os,
527 const LinearIndexPattern& lip,
528 const Node& index);
529
530 /***************************************************************************
531 * protected
532 **************************************************************************/
533protected:
534
535 void generateSourceCode(std::ostream& out,
536 std::unique_ptr<LanguageGenerationData<Base> > info) override {
537
538 const bool multiFile = _maxAssignmentsPerFile > 0 && _sources != nullptr;
539
540 // clean up
541 _code.str("");
542 _ss.str("");
543 _indentationLevel = 0;
544 auxArrayName_ = "";
545 _currentLoops.clear();
546 depConstIds_.clear();
547 depIsIndepIds_.clear();
548 _dependentIDs.clear();
549
550
551 // save some info
552 _info = info.get();
553 _independentSize = info->independent.size();
554 _dependent = &info->dependent;
555 _nameGen = &info->nameGen;
556 _minTemporaryVarID = info->minTemporaryVarID;
557 const ArrayView<CG<Base> >& dependent = info->dependent;
558 const std::vector<Node*>& variableOrder = info->variableOrder;
559
560 _tmpArrayValues.resize(_nameGen->getMaxTemporaryArrayVariableID());
561 std::fill(_tmpArrayValues.begin(), _tmpArrayValues.end(), nullptr);
562 _tmpSparseArrayValues.resize(_nameGen->getMaxTemporarySparseArrayVariableID());
563 std::fill(_tmpSparseArrayValues.begin(), _tmpSparseArrayValues.end(), nullptr);
564
565 varIds_.resize(_minTemporaryVarID + variableOrder.size());
566 std::fill(varIds_.begin(), varIds_.end(), 0);
567
571 generateNames4RandomIndexPatterns(info->indexRandomPatterns);
572
576 //generate names for the independent variables
577 for (size_t j = 0; j < _independentSize; j++) {
578 Node& op = *info->independent[j];
579 if (op.getName() == nullptr) {
580 op.setName(_nameGen->generateIndependent(op, getVariableID(op)));
581 }
582 }
583
584 // generate names for the dependent variables (must be after naming independents)
585 for (size_t i = 0; i < dependent.size(); i++) {
586 Node* node = dependent[i].getOperationNode();
587 if (node != nullptr && node->getOperationType() != CGOpCode::LoopEnd && node->getName() == nullptr) {
588 if (node->getOperationType() == CGOpCode::LoopIndexedDep) {
589 size_t pos = node->getInfo()[0];
590 const IndexPattern* ip = info->loopDependentIndexPatterns[pos];
591 node->setName(_nameGen->generateIndexedDependent(*node, getVariableID(*node), *ip));
592
593 } else {
594 node->setName(_nameGen->generateDependent(i));
595 }
596 }
597 }
598
602 const std::vector<FuncArgument>& indArg = _nameGen->getIndependent();
603 const std::vector<FuncArgument>& depArg = _nameGen->getDependent();
604 const std::vector<FuncArgument>& tmpArg = _nameGen->getTemporary();
605 CPPADCG_ASSERT_KNOWN(!indArg.empty() && !depArg.empty(),
606 "There must be at least one dependent and one independent argument")
607 CPPADCG_ASSERT_KNOWN(tmpArg.size() == 3,
608 "There must be three temporary variables")
609
610 auxArrayName_ = tmpArg[1].name + "p";
611
615 // dependent variables indexes that are copies of other dependent variables
616 std::set<size_t> dependentDuplicates;
617
618 for (size_t i = 0; i < dependent.size(); i++) {
619 Node* node = dependent[i].getOperationNode();
620 if (node != nullptr) {
621 CGOpCode type = node->getOperationType();
622 if (type != CGOpCode::Inv && type != CGOpCode::LoopEnd) {
623 size_t varID = getVariableID(*node);
624 if (varID > 0) {
625 auto it2 = _dependentIDs.find(varID);
626 if (it2 == _dependentIDs.end()) {
627 _dependentIDs[getVariableID(*node)] = i;
628 } else {
629 // there can be several dependent variables with the same ID
630 dependentDuplicates.insert(i);
631 }
632 }
633 }
634 }
635 }
636
637 // the names of local functions
638 std::vector<std::string> mathMLFiles;
639 if (multiFile) {
640 mathMLFiles.reserve(variableOrder.size() / _maxAssignmentsPerFile);
641 }
642
646 if (variableOrder.size() > 0) {
647 // generate names for temporary variables
648 for (Node* node : variableOrder) {
649 CGOpCode op = node->getOperationType();
650 if (!isDependent(*node) && op != CGOpCode::IndexDeclaration) {
651 // variable names for temporaries must always be created since they might have been used before with a different name/id
652 if (requiresVariableName(*node) && op != CGOpCode::ArrayCreation && op != CGOpCode::SparseArrayCreation) {
653 node->setName(_nameGen->generateTemporary(*node, getVariableID(*node)));
654 } else if (op == CGOpCode::ArrayCreation) {
655 node->setName(_nameGen->generateTemporaryArray(*node, getVariableID(*node)));
656 } else if (op == CGOpCode::SparseArrayCreation) {
657 node->setName(_nameGen->generateTemporarySparseArray(*node, getVariableID(*node)));
658 }
659 }
660 }
661
665 if (info->zeroDependents) {
666 // zero initial values
667 const std::vector<FuncArgument>& depArg = _nameGen->getDependent();
668 for (size_t i = 0; i < depArg.size(); i++) {
669 _code << _startEq;
670 const FuncArgument& a = depArg[i];
671 if (a.array) {
672 _code << a.name;
673 } else {
674 _code << "<mrow id='" << createHtmlID(*(*_dependent)[i].getOperationNode()) << "' class='dep'>" << _nameGen->generateDependent(i) << "</mrow>";
675 }
676 _code << _assignStr;
677 printParameter(Base(0.0));
678 _code << _endEq << _endline;
679 }
680 }
681
682 size_t assignCount = 0;
683 for (Node* it : variableOrder) {
684 // check if a new function should start
685 if (assignCount >= _maxAssignmentsPerFile && multiFile && _currentLoops.empty()) {
686 assignCount = 0;
687 saveLocalFunction(mathMLFiles, mathMLFiles.empty() && info->zeroDependents);
688 }
689
690 Node& node = *it;
691
692 // a dependent variable assigned by a loop does require any source code (its done inside the loop)
693 if (node.getOperationType() == CGOpCode::DependentRefRhs) {
694 continue; // nothing to do (this operation is right hand side only)
695 } else if (node.getOperationType() == CGOpCode::TmpDcl) { // temporary variable declaration does not need any source code here
696 continue; // nothing to do (bogus operation)
697 }
698
699 assignCount += printAssignment(node);
700 }
701
702 if (!mathMLFiles.empty() && assignCount > 0) {
703 assignCount = 0;
704 saveLocalFunction(mathMLFiles, false);
705 }
706 }
707
708 if (!mathMLFiles.empty()) {
712 CPPADCG_ASSERT_KNOWN(tmpArg[0].array,
713 "The temporary variables must be saved in an array in order to generate multiple functions")
714 printAlgorithmFileStart(_code);
715 for (size_t i = 0; i < mathMLFiles.size(); i++) {
716 _code << "<a href='" << mathMLFiles[i] << ".html'>part " << (i + 1) << "</a><br/>" << _endline;
717 }
718 printAlgorithmFileEnd(_code);
719 }
720
721 // dependent duplicates
722 if (!dependentDuplicates.empty()) {
723 _code << "<!-- variable duplicates: " << dependentDuplicates.size() << " -->" << _endline;
724
725 for (size_t index : dependentDuplicates) {
726 const CG<Base>& dep = dependent[index];
727 std::string varName = _nameGen->generateDependent(index);
728 Node* depNode = dep.getOperationNode();
729 const std::string& origVarName = *depNode->getName();
730
731 _code << _startEq
732 << "<mrow id='" << createHtmlID(depNode) << "' class='dep'>" << varName << "</mrow>"
733 << _assignStr
734 << "<mrow id='" << createHtmlID(depNode) << "' class='dep'>" << origVarName << "</mrow>";
735 printAssignmentEnd();
736 }
737 }
738
739 // constant dependent variables
740 bool commentWritten = false;
741 for (size_t i = 0; i < dependent.size(); i++) {
742 if (dependent[i].isParameter()) {
743 if (!_ignoreZeroDepAssign || !dependent[i].isIdenticalZero()) {
744 if (!commentWritten) {
745 _code << "<!-- dependent variables without operations -->" << _endline;
746 commentWritten = true;
747 }
748
749 std::string depId = "d" + std::to_string(i);
750 if (_saveVariableRelations) {
751 depConstIds_.push_back(depId);
752 }
753
754 std::string varName = _nameGen->generateDependent(i);
755 _code << _startEq
756 << "<mrow id='" << depId << "' class='dep'>" << varName << "</mrow>" << _assignStr; // id='" << createID(??)
757 printParameter(dependent[i].getValue());
758 printAssignmentEnd();
759 }
760 } else if (dependent[i].getOperationNode()->getOperationType() == CGOpCode::Inv) {
761 if (!commentWritten) {
762 _code << "<!-- dependent variables without operations -->" << _endline;
763 commentWritten = true;
764 }
765
766 std::string varName = _nameGen->generateDependent(i);
767 const std::string& indepName = *dependent[i].getOperationNode()->getName();
768 std::string depId = createHtmlID(dependent[i].getOperationNode());
769 if (_saveVariableRelations) {
770 depIsIndepIds_.push_back(depId);
771 }
772 _code << _startEq
773 << "<mrow id='" << depId << "' class='dep'>" << varName << "</mrow>"
774 << _assignStr
775 << "<mrow id='" << createHtmlID(dependent[i].getOperationNode()) << "' class='indep'>" << indepName << "</mrow>";
776 printAssignmentEnd(*dependent[i].getOperationNode());
777 }
778 }
779
783 if (mathMLFiles.empty()) {
784 // a single source file
785 printAlgorithmFileStart(_ss);
786 _ss << _code.str();
787 printAlgorithmFileEnd(_ss);
788
789 out << _ss.str();
790
791 if (_sources != nullptr) {
792 (*_sources)[_filename + ".html"] = _ss.str();
793 }
794 } else {
795 // there are multiple source files (this last one is the master)
796 (*_sources)[_filename + ".html"] = _code.str();
797 }
798
799 }
800
801 inline size_t getVariableID(const Node& node) const {
802 return _info->varId[node]; // some of these values are 0 and (std::numeric_limits<size_t>::max)()
803 }
804
805 inline virtual void printAlgorithmFileStart(std::ostream& out) {
806 out << "<!DOCTYPE html>" << _endline <<
807 "<html lang=\"en\">" << _endline <<
808 "<head>" << _endline <<
809 "<meta charset=\"utf-8\">" << _endline <<
810 "<title>" << _filename << "</title>" << _endline;
811
812 if (!_headExtra.empty()) {
813 out << _headExtra << _endline;
814 }
815
816 if (!_style.empty()) {
817 out << "<style>" << _endline
818 << _style << _endline
819 << "</style>" << _endline;
820 }
821
822 if (_saveVariableRelations) {
823 CPPADCG_ASSERT_UNKNOWN(_info->variableDependencies.size() == _info->variableOrder.size())
824
825 out << "<script type=\"text/javascript\">" << _endline;
826 out << " var depConst = [];" << _endline;
827 out << " var depIsVar = [];" << _endline;
828 out << " var var2dep = {" << _endline;
829
830 const auto& varDeps = _info->variableDependencies;
831 const auto& varOrder = _info->variableOrder;
832 bool firstI = true;
833 for (size_t i = 0; i < varDeps.size(); ++i) {
834 if (!varDeps[i].empty()) {
835 if (firstI) {
836 firstI = false;
837 } else {
838 out << "," << _endline;
839 }
840 out << " \"" << getHtmlID(*varOrder[i]) << "\": [";
841 for (const auto* n: varDeps[i]) {
842 if (n != *varDeps[i].begin()) out << ", ";
843 out << getHtmlID(*n);
844 }
845 out << "]";
846 }
847 }
848 out << _endline << " };" << _endline;
849
850 // transpose of varDeps
851 out << " var dep2var = {" << _endline;
852
853 std::map<size_t, std::set<size_t> > deps2Var;
854 for (size_t i = 0; i < varDeps.size(); ++i) {
855 size_t idi = getHtmlID(*varOrder[i]);
856 for (const auto* n: varDeps[i]) {
857 size_t idj = getHtmlID(*n);
858 deps2Var[idj].insert(idi);
859 }
860 }
861
862 for (const auto& pair: deps2Var) {
863 if (pair.first != deps2Var.begin()->first) {
864 out << "," << _endline;
865 }
866 out << " \"" << pair.first << "\": [";
867 for (size_t j: pair.second) {
868 if (j != *pair.second.begin()) out << ", ";
869 out << j;
870 }
871 out << "]";
872 }
873 out << _endline << " };" << _endline;
874
875 out << "</script>" << _endline;
876 }
877
878 if (!_javascript.empty()) {
879 out << "<script type=\"text/javascript\">" << _endline <<
880 _javascript << _endline
881 << "</script>" << _endline;
882 }
883
884 out << "</head>" << _endline <<
885 "" << _endline <<
886 "<body>" << _endline <<
887 "<!-- source file for '" << _filename << "' (automatically generated by CppADCodeGen) -->" << _endline <<
888 "<div id='algorithm'>" << _endline;
889 }
890
891 inline virtual void printAlgorithmFileEnd(std::ostream& out) {
892
893 if(_saveVariableRelations) {
894 out << "<script type=\"text/javascript\">" << _endline;
895 out << " depConst = [";
896 bool first = true;
897 for (const auto& d: depConstIds_) {
898 if (first)
899 first = false;
900 else
901 out << ", ";
902 out << "\'" << d << "\'";
903 }
904 out << "];" << _endline;
905 out << " depIsVar = [";
906 first = true;
907 for (const auto& d: depIsIndepIds_) {
908 if (first)
909 first = false;
910 else
911 out << ", ";
912 out << "\'" << d << "\'";
913 }
914 out << "];" << _endline;
915 out << "</script>" << _endline;
916 }
917
918 out << "</div>" << _endline <<
919 "</body>" << _endline <<
920 "</html>";
921 }
922
923 inline unsigned printAssignment(Node& node) {
924 return printAssignment(node, node);
925 }
926
927 inline unsigned printAssignment(Node& nodeName,
928 const Arg& nodeRhs) {
929 if (nodeRhs.getOperation() != nullptr) {
930 return printAssignment(nodeName, *nodeRhs.getOperation());
931 } else {
932 printAssignmentStart(nodeName);
933 printParameter(*nodeRhs.getParameter());
934 printAssignmentEnd(nodeName);
935 return 1;
936 }
937 }
938
939 inline unsigned printAssignment(Node& nodeName,
940 Node& nodeRhs) {
941 bool createsVar = directlyAssignsVariable(nodeRhs); // do we need to do the assignment here?
942 if (!createsVar) {
943 printAssignmentStart(nodeName);
944 }
945 unsigned lines = printExpressionNoVarCheck(nodeRhs);
946 if (!createsVar) {
947 printAssignmentEnd(nodeRhs);
948 }
949
950 if (nodeRhs.getOperationType() == CGOpCode::ArrayElement) {
951 Node* array = nodeRhs.getArguments()[0].getOperation();
952 size_t arrayId = getVariableID(*array);
953 size_t pos = nodeRhs.getInfo()[0];
954 if (array->getOperationType() == CGOpCode::ArrayCreation)
955 _tmpArrayValues[arrayId - 1 + pos] = nullptr; // this could probably be removed!
956 else
957 _tmpSparseArrayValues[arrayId - 1 + pos] = nullptr; // this could probably be removed!
958 }
959
960 return lines;
961 }
962
963 inline virtual void printAssignmentStart(Node& op) {
964 printAssignmentStart(op, createVariableName(op), isDependent(op));
965 }
966
967 inline virtual void printAssignmentStart(Node& node, const std::string& varName, bool isDep) {
968 _code << _startEq;
969 _code << "<mrow id='" << createHtmlID(node) << "' class='" << (isDep ? "dep" : "tmp") << "'>" << varName << "</mrow>";
970
971 CGOpCode op = node.getOperationType();
972 if (op == CGOpCode::DependentMultiAssign || (op == CGOpCode::LoopIndexedDep && node.getInfo()[1] == 1)) {
973 _code << _assignAddStr; // +=
974 } else {
975 _code << _assignStr; // =
976 }
977 }
978
979 inline virtual void printAssignmentEnd() {
980 _code << _endEq << _endline;
981 }
982
983 inline virtual void printAssignmentEnd(Node& op) {
984 printAssignmentEnd();
985 }
986
987 virtual void saveLocalFunction(std::vector<std::string>& localFuncNames,
988 bool zeroDependentArray) {
989 _ss << _filename << "__part_" << (localFuncNames.size() + 1);
990 std::string funcName = _ss.str();
991 _ss.str("");
992
993 // loop indexes
994 _nameGen->prepareCustomFunctionVariables(_ss);
995 _ss << _code.str();
996 _nameGen->finalizeCustomFunctionVariables(_ss);
997
998 (*_sources)[funcName + ".html"] = _ss.str();
999 localFuncNames.push_back(funcName);
1000
1001 _code.str("");
1002 _ss.str("");
1003 }
1004
1005 bool createsNewVariable(const Node& var,
1006 size_t totalUseCount,
1007 size_t opCount) const override {
1008 CGOpCode op = var.getOperationType();
1009 if (totalUseCount > 1) {
1010 return op != CGOpCode::ArrayElement && op != CGOpCode::Index && op != CGOpCode::IndexDeclaration && op != CGOpCode::Tmp;
1011 } else {
1012 return ( op == CGOpCode::ArrayCreation ||
1013 op == CGOpCode::SparseArrayCreation ||
1014 op == CGOpCode::AtomicForward ||
1015 op == CGOpCode::AtomicReverse ||
1016 op == CGOpCode::ComLt ||
1017 op == CGOpCode::ComLe ||
1018 op == CGOpCode::ComEq ||
1019 op == CGOpCode::ComGe ||
1020 op == CGOpCode::ComGt ||
1021 op == CGOpCode::ComNe ||
1022 op == CGOpCode::LoopIndexedDep ||
1023 op == CGOpCode::LoopIndexedTmp ||
1024 op == CGOpCode::IndexAssign ||
1025 op == CGOpCode::Assign) &&
1026 op != CGOpCode::CondResult;
1027 }
1028 }
1029
1030 virtual bool requiresVariableName(const Node& var) const {
1031 CGOpCode op = var.getOperationType();
1032 return (_info->totalUseCount.get(var) > 1 &&
1033 op != CGOpCode::AtomicForward &&
1034 op != CGOpCode::AtomicReverse &&
1035 op != CGOpCode::LoopStart &&
1036 op != CGOpCode::LoopEnd &&
1037 op != CGOpCode::Index &&
1038 op != CGOpCode::IndexAssign &&
1039 op != CGOpCode::StartIf &&
1040 op != CGOpCode::ElseIf &&
1041 op != CGOpCode::Else &&
1042 op != CGOpCode::EndIf &&
1043 op != CGOpCode::CondResult &&
1044 op != CGOpCode::LoopIndexedTmp &&
1045 op != CGOpCode::Tmp);
1046 }
1047
1055 virtual bool directlyAssignsVariable(const Node& var) const {
1056 CGOpCode op = var.getOperationType();
1057 return isCondAssign(op) ||
1058 op == CGOpCode::ArrayCreation ||
1059 op == CGOpCode::SparseArrayCreation ||
1060 op == CGOpCode::AtomicForward ||
1061 op == CGOpCode::AtomicReverse ||
1062 op == CGOpCode::DependentMultiAssign ||
1063 op == CGOpCode::LoopStart ||
1064 op == CGOpCode::LoopEnd ||
1065 op == CGOpCode::IndexAssign ||
1066 op == CGOpCode::StartIf ||
1067 op == CGOpCode::ElseIf ||
1068 op == CGOpCode::Else ||
1069 op == CGOpCode::EndIf ||
1070 op == CGOpCode::CondResult ||
1071 op == CGOpCode::IndexDeclaration;
1072 }
1073
1074 bool requiresVariableArgument(enum CGOpCode op, size_t argIndex) const override {
1075 return op == CGOpCode::CondResult;
1076 }
1077
1078 inline const std::string& createVariableName(Node& var) {
1079 CGOpCode op = var.getOperationType();
1080 CPPADCG_ASSERT_UNKNOWN(getVariableID(var) > 0)
1081 CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::AtomicForward)
1082 CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::AtomicReverse)
1083 CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::LoopStart)
1084 CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::LoopEnd)
1085 CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::Index)
1086 CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::IndexAssign)
1087 CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::IndexDeclaration)
1088
1089 if (var.getName() == nullptr) {
1090 if (op == CGOpCode::ArrayCreation) {
1091 var.setName(_nameGen->generateTemporaryArray(var, getVariableID(var)));
1092
1093 } else if (op == CGOpCode::SparseArrayCreation) {
1094 var.setName(_nameGen->generateTemporarySparseArray(var, getVariableID(var)));
1095
1096 } else if (op == CGOpCode::LoopIndexedDep) {
1097 size_t pos = var.getInfo()[0];
1098 const IndexPattern* ip = _info->loopDependentIndexPatterns[pos];
1099 var.setName(_nameGen->generateIndexedDependent(var, getVariableID(var), *ip));
1100
1101 } else if (op == CGOpCode::LoopIndexedIndep) {
1102 size_t pos = var.getInfo()[1];
1103 const IndexPattern* ip = _info->loopIndependentIndexPatterns[pos];
1104 var.setName(_nameGen->generateIndexedIndependent(var, getVariableID(var), *ip));
1105
1106 } else if (getVariableID(var) <= _independentSize) {
1107 // independent variable
1108 var.setName(_nameGen->generateIndependent(var, getVariableID(var)));
1109
1110 } else if (getVariableID(var) < _minTemporaryVarID) {
1111 // dependent variable
1112 auto it = _dependentIDs.find(getVariableID(var));
1113 CPPADCG_ASSERT_UNKNOWN(it != _dependentIDs.end())
1114
1115 size_t index = it->second;
1116 var.setName(_nameGen->generateDependent(index));
1117
1118 } else if (op == CGOpCode::LoopIndexedTmp || op == CGOpCode::Tmp) {
1119 CPPADCG_ASSERT_KNOWN(var.getArguments().size() >= 1, "Invalid number of arguments for loop indexed temporary operation")
1120 Node* tmpVar = var.getArguments()[0].getOperation();
1121 CPPADCG_ASSERT_KNOWN(tmpVar != nullptr && tmpVar->getOperationType() == CGOpCode::TmpDcl, "Invalid arguments for loop indexed temporary operation")
1122 return createVariableName(*tmpVar);
1123
1124 } else {
1125 // temporary variable
1126 var.setName(_nameGen->generateTemporary(var, getVariableID(var)));
1127 }
1128 }
1129
1130
1131 return *var.getName();
1132 }
1133
1137 inline size_t getHtmlID(const Node& var) const {
1138 return var.getHandlerPosition(); // always unique and higher than zero
1139 }
1140
1144 inline std::string createHtmlID(const Node* var) {
1145 return createHtmlID(*var);
1146 }
1147
1151 virtual std::string createHtmlID(const Node& var) {
1152 size_t id = getHtmlID(var);
1153 if (varIds_.size() <= id) {
1154 varIds_.resize(id + 1 + varIds_.size() * 3 / 2, 0);
1155 }
1156
1157 int n = varIds_[id];
1158 varIds_[id]++;
1159
1160 if (n == 0)
1161 return "v" + std::to_string(id);
1162 else
1163 return "v" + std::to_string(id) + "_" + std::to_string(n);
1164 }
1165
1166 virtual void printIndependentVariableName(Node& op) {
1167 CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 0, "Invalid number of arguments for independent variable")
1168 _code << "<mrow id='" << createHtmlID(op) << "' class='indep'>" << _nameGen->generateIndependent(op, getVariableID(op)) << "</mrow>";
1169 }
1170
1171 virtual unsigned print(const Arg& arg) {
1172 if (arg.getOperation() != nullptr) {
1173 // expression
1174 return printExpression(*arg.getOperation());
1175 } else {
1176 // parameter
1177 printParameter(*arg.getParameter());
1178 return 1;
1179 }
1180 }
1181
1182 virtual unsigned printExpression(Node& node) {
1183 if (getVariableID(node) > 0) {
1184 const std::string& name = createVariableName(node); // use variable name
1185
1186 CGOpCode op = node.getOperationType();
1187 if (getVariableID(node) >= _minTemporaryVarID || op == CGOpCode::ArrayCreation || op == CGOpCode::SparseArrayCreation || op == CGOpCode::LoopIndexedDep || op == CGOpCode::LoopIndexedIndep) {
1188
1189 _code << "<mrow id='" << createHtmlID(node) << "' class='tmp'>" << name << "</mrow>"; // TODO!!!!!!!!!!!!!!!!!!!!!!!
1190
1191 } else if (getVariableID(node) <= _independentSize) {
1192 // independent variable
1193 _code << "<mrow id='" << createHtmlID(node) << "' class='indep'>" << name << "</mrow>";
1194
1195 } else {
1196 // dependent variable
1197 _code << "<mrow id='" << createHtmlID(node) << "' class='dep'>" << name << "</mrow>";
1198
1199 }
1200
1201 return 1;
1202 } else {
1203 // print expression code
1204 return printExpressionNoVarCheck(node);
1205 }
1206 }
1207
1208 virtual unsigned printExpressionNoVarCheck(Node& node) {
1209 CGOpCode op = node.getOperationType();
1210 switch (op) {
1211 case CGOpCode::ArrayCreation:
1212 printArrayCreationOp(node);
1213 break;
1214 case CGOpCode::SparseArrayCreation:
1215 printSparseArrayCreationOp(node);
1216 break;
1217 case CGOpCode::ArrayElement:
1218 printArrayElementOp(node);
1219 break;
1220 case CGOpCode::Assign:
1221 return printAssignOp(node);
1222
1223 case CGOpCode::Abs:
1224 case CGOpCode::Acos:
1225 case CGOpCode::Asin:
1226 case CGOpCode::Atan:
1227 case CGOpCode::Cosh:
1228 case CGOpCode::Cos:
1229 case CGOpCode::Exp:
1230 case CGOpCode::Log:
1231 case CGOpCode::Sign:
1232 case CGOpCode::Sinh:
1233 case CGOpCode::Sin:
1234 case CGOpCode::Sqrt:
1235 case CGOpCode::Tanh:
1236 case CGOpCode::Tan:
1237#if CPPAD_USE_CPLUSPLUS_2011
1238 case CGOpCode::Erf:
1239 case CGOpCode::Erfc:
1240 case CGOpCode::Asinh:
1241 case CGOpCode::Acosh:
1242 case CGOpCode::Atanh:
1243 case CGOpCode::Expm1:
1244 case CGOpCode::Log1p:
1245#endif
1246 printUnaryFunction(node);
1247 break;
1248 case CGOpCode::AtomicForward: // atomicFunction.forward(q, p, vx, vy, tx, ty)
1249 printAtomicForwardOp(node);
1250 break;
1251 case CGOpCode::AtomicReverse: // atomicFunction.reverse(p, tx, ty, px, py)
1252 printAtomicReverseOp(node);
1253 break;
1254 case CGOpCode::Add:
1255 printOperationAdd(node);
1256 break;
1257 case CGOpCode::Alias:
1258 return printOperationAlias(node);
1259
1260 case CGOpCode::ComLt:
1261 case CGOpCode::ComLe:
1262 case CGOpCode::ComEq:
1263 case CGOpCode::ComGe:
1264 case CGOpCode::ComGt:
1265 case CGOpCode::ComNe:
1266 printConditionalAssignment(node);
1267 break;
1268 case CGOpCode::Div:
1269 printOperationDiv(node);
1270 break;
1271 case CGOpCode::Inv:
1272 printIndependentVariableName(node);
1273 break;
1274 case CGOpCode::Mul:
1275 printOperationMul(node);
1276 break;
1277 case CGOpCode::Pow:
1278 printPowFunction(node);
1279 break;
1280 case CGOpCode::Pri:
1281 // do nothing
1282 break;
1283 case CGOpCode::Sub:
1284 printOperationMinus(node);
1285 break;
1286
1287 case CGOpCode::UnMinus:
1288 printOperationUnaryMinus(node);
1289 break;
1290
1291 case CGOpCode::DependentMultiAssign:
1292 return printDependentMultiAssign(node);
1293
1294 case CGOpCode::Index:
1295 return 0; // nothing to do
1296 case CGOpCode::IndexAssign:
1297 printIndexAssign(node);
1298 break;
1299 case CGOpCode::IndexDeclaration:
1300 return 0; // already done
1301
1302 case CGOpCode::LoopStart:
1303 printLoopStart(node);
1304 break;
1305 case CGOpCode::LoopIndexedIndep:
1306 printLoopIndexedIndep(node);
1307 break;
1308 case CGOpCode::LoopIndexedDep:
1309 printLoopIndexedDep(node);
1310 break;
1311 case CGOpCode::LoopIndexedTmp:
1312 printLoopIndexedTmp(node);
1313 break;
1314 case CGOpCode::TmpDcl:
1315 // nothing to do
1316 return 0;
1317 case CGOpCode::Tmp:
1318 printTmpVar(node);
1319 break;
1320 case CGOpCode::LoopEnd:
1321 printLoopEnd(node);
1322 break;
1323 case CGOpCode::IndexCondExpr:
1324 printIndexCondExprOp(node);
1325 break;
1326 case CGOpCode::StartIf:
1327 printStartIf(node);
1328 break;
1329 case CGOpCode::ElseIf:
1330 printElseIf(node);
1331 break;
1332 case CGOpCode::Else:
1333 printElse(node);
1334 break;
1335 case CGOpCode::EndIf:
1336 printEndIf(node);
1337 break;
1338 case CGOpCode::CondResult:
1339 printCondResult(node);
1340 break;
1341 case CGOpCode::UserCustom:
1342 printUserCustom(node);
1343 break;
1344 default:
1345 throw CGException("Unknown operation code '", op, "'.");
1346 }
1347 return 1;
1348 }
1349
1350 virtual unsigned printAssignOp(Node& node) {
1351 CPPADCG_ASSERT_KNOWN(node.getArguments().size() == 1, "Invalid number of arguments for assign operation")
1352
1353 return print(node.getArguments()[0]);
1354 }
1355
1356 virtual void printUnaryFunction(Node& op) {
1357 CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 1, "Invalid number of arguments for unary function")
1358
1359 switch (op.getOperationType()) {
1360 case CGOpCode::Abs:
1361 _code << "<mi>abs</mi>";
1362 break;
1363 case CGOpCode::Acos:
1364 _code << "<mi>arccos</mi>";
1365 break;
1366 case CGOpCode::Asin:
1367 _code << "<mi>arcsin</mi>";
1368 break;
1369 case CGOpCode::Atan:
1370 _code << "<mi>arctan</mi>";
1371 break;
1372 case CGOpCode::Cosh:
1373 _code << "<mi>cosh</mi>";
1374 break;
1375 case CGOpCode::Cos:
1376 _code << "<mi>cos</mi>";
1377 break;
1378 case CGOpCode::Exp:
1379 _code << "<mi>exp</mi>";
1380 break;
1381 case CGOpCode::Log:
1382 _code << "<mi>ln</mi>";
1383 break;
1384 case CGOpCode::Sinh:
1385 _code << "<mi>sinh</mi>";
1386 break;
1387 case CGOpCode::Sign:
1388 _code << "<mi>sgn</mi>";
1389 break;
1390 case CGOpCode::Sin:
1391 _code << "<mi>sin</mi>";
1392 break;
1393 case CGOpCode::Sqrt:
1394 _code << "<msqrt><mrow>";
1395 print(op.getArguments()[0]);
1396 _code << "</mrow></msqrt>";
1397 return;
1398 case CGOpCode::Tanh:
1399 _code << "<mi>tanh</mi>";
1400 break;
1401 case CGOpCode::Tan:
1402 _code << "<mi>tan</mi>";
1403 break;
1404#if CPPAD_USE_CPLUSPLUS_2011
1405 case CGOpCode::Erf:
1406 _code << "<mi>erf</mi>";
1407 break;
1408 case CGOpCode::Erfc:
1409 _code << "<mi>erfc</mi>";
1410 break;
1411 case CGOpCode::Asinh:
1412 _code << "<mi>asinh</mi>";
1413 break;
1414 case CGOpCode::Acosh:
1415 _code << "<mi>acosh</mi>";
1416 break;
1417 case CGOpCode::Atanh:
1418 _code << "<mi>atanh</mi>";
1419 break;
1420 case CGOpCode::Expm1:
1421 _code << "<mi>expm1</mi>";
1422 break;
1423 case CGOpCode::Log1p:
1424 _code << "<mi>log1p</mi>";
1425 break;
1426#endif
1427 default:
1428 throw CGException("Unknown function name for operation code '", op.getOperationType(), "'.");
1429 }
1430
1431 _code << "<mo>&ApplyFunction;</mo>"
1432 "<mfenced><mrow>";
1433 print(op.getArguments()[0]);
1434 _code << "</mrow></mfenced>";
1435 }
1436
1437 virtual void printPowFunction(Node& op) {
1438 CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 2, "Invalid number of arguments for pow() function")
1439
1440 auto encloseInParentheses = [this](const Node* node) {
1441 while (node != nullptr) {
1442 if (getVariableID(*node) != 0)
1443 return false;
1444 if (node->getOperationType() == CGOpCode::Alias)
1445 node = node->getArguments()[0].getOperation();
1446 else
1447 break;
1448 }
1449 return node != nullptr &&
1450 getVariableID(*node) == 0 &&
1451 !isFunction(node->getOperationType());
1452 };
1453
1454
1455 bool encloseBase = _powBaseEnclose || encloseInParentheses(op.getArguments()[0].getOperation());
1456 bool encloseExpo = encloseInParentheses(op.getArguments()[1].getOperation());
1457
1458 _code << "<msup>";
1459 if (encloseBase)
1460 _code << "<mfenced>";
1461 _code << "<mrow>";
1462 print(op.getArguments()[0]);
1463 _code << "</mrow>";
1464 if (encloseBase)
1465 _code << "</mfenced>";
1466 if (encloseExpo)
1467 _code << "<mfenced>";
1468 _code << "<mrow>";
1469 print(op.getArguments()[1]);
1470 _code << "</mrow>";
1471 if (encloseExpo)
1472 _code << "</mfenced>";
1473 _code << "</msup>";
1474 }
1475
1476 virtual unsigned printOperationAlias(Node& op) {
1477 CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 1, "Invalid number of arguments for alias")
1478 return print(op.getArguments()[0]);
1479 }
1480
1481 virtual void printOperationAdd(Node& op) {
1482 CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 2, "Invalid number of arguments for addition")
1483
1484 const Arg& left = op.getArguments()[0];
1485 const Arg& right = op.getArguments()[1];
1486
1487 if(right.getParameter() == nullptr || (*right.getParameter() >= 0)) {
1488 print(left);
1489 _code << "<mo>+</mo>";
1490 print(right);
1491 } else {
1492 // right has a negative parameter so we would get v0 + -v1
1493 print(left);
1494 _code << "<mo>-</mo>";
1495 printParameter(-*right.getParameter()); // make it positive
1496 }
1497 }
1498
1499 virtual void printOperationMinus(Node& op) {
1500 CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 2, "Invalid number of arguments for subtraction")
1501
1502 const Arg& left = op.getArguments()[0];
1503 const Arg& right = op.getArguments()[1];
1504
1505 // whether or not to flip + to - and change sign
1506 if(right.getParameter() == nullptr || (*right.getParameter() >= 0)) {
1507 bool encloseRight = encloseInParenthesesMul(right);
1508
1509 print(left);
1510 _code << "<mo>-</mo>";
1511 if (encloseRight) {
1512 _code << "<mfenced><mrow>";
1513 }
1514 print(right);
1515 if (encloseRight) {
1516 _code << "</mrow></mfenced>";
1517 }
1518 } else {
1519 // right has a negative parameter so we would get v0 - -v1
1520 print(left);
1521 _code << "<mo>+</mo>";
1522 printParameter(-*right.getParameter()); // make it positive
1523 }
1524 }
1525
1526 virtual void printOperationDiv(Node& op) {
1527 CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 2, "Invalid number of arguments for division")
1528
1529 const Arg& left = op.getArguments()[0];
1530 const Arg& right = op.getArguments()[1];
1531
1532 _code << "<mfrac>";
1533 _code << "<mrow>";
1534 print(left);
1535 _code << "</mrow>";
1536 _code << "<mrow>";
1537 print(right);
1538 _code << "</mrow>";
1539 _code << "</mfrac>";
1540 }
1541
1542 inline bool encloseInParenthesesMul(const Arg& arg) const {
1543 if (arg.getParameter() != nullptr) {
1544 return ((*arg.getParameter()) < 0);
1545 } else {
1546 return encloseInParenthesesMul(arg.getOperation());
1547 }
1548 }
1549
1550 inline bool encloseInParenthesesMul(const Node* node) const {
1551 while (node != nullptr) {
1552 if (getVariableID(*node) != 0) {
1553 return false;
1554 } else if (node->getOperationType() == CGOpCode::Alias) {
1555 node = node->getArguments()[0].getOperation();
1556 } else {
1557 break;
1558 }
1559 }
1560 return node != nullptr &&
1561 getVariableID(*node) == 0 &&
1562 node->getOperationType() != CGOpCode::Div &&
1563 node->getOperationType() != CGOpCode::Mul &&
1564 !isFunction(node->getOperationType());
1565 }
1566
1567 virtual void printOperationMul(Node& op) {
1568 CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 2, "Invalid number of arguments for multiplication")
1569
1570 const Arg& left = op.getArguments()[0];
1571 const Arg& right = op.getArguments()[1];
1572
1573 bool encloseLeft = encloseInParenthesesMul(left);
1574 bool encloseRight = encloseInParenthesesMul(right);
1575
1576 auto isNumber = [this](const Node* node, int pos) -> bool {
1577 while (node != nullptr) {
1578 if (getVariableID(*node) != 0) {
1579 return false;
1580 }
1581 CGOpCode op = node->getOperationType();
1582 if (op == CGOpCode::Alias) {
1583 node = node->getArguments()[0].getOperation();
1584 continue;
1585 } else if (op == CGOpCode::Mul) {
1586 node = node->getArguments()[pos].getOperation();
1587 } else if (pos == 0 && op == CGOpCode::Pow) {
1588 node = node->getArguments()[0].getOperation();
1589 } else {
1590 return false;
1591 }
1592 }
1593 return true; // a constant number
1594 };
1595
1596 if (encloseLeft) {
1597 _code << "<mfenced><mrow>";
1598 }
1599 print(left);
1600 if (encloseLeft) {
1601 _code << "</mrow></mfenced>";
1602 }
1603
1604 if (isNumber(left.getOperation(), 1) && isNumber(right.getOperation(), 0))
1605 _code << _multValOpStr; // numbers too close together are difficult to distinguish
1606 else
1607 _code << _multOpStr; // e.g. invisible times
1608
1609 if (encloseRight) {
1610 _code << "<mfenced><mrow>";
1611 }
1612 print(right);
1613 if (encloseRight) {
1614 _code << "</mrow></mfenced>";
1615 }
1616 }
1617
1618 virtual void printOperationUnaryMinus(Node& op) {
1619 CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 1, "Invalid number of arguments for unary minus")
1620
1621 const Arg& arg = op.getArguments()[0];
1622
1623 bool enclose = encloseInParenthesesMul(arg);
1624
1625 _code << "<mo>-</mo>";
1626 if (enclose) {
1627 _code << "<mfenced><mrow>";
1628 }
1629 print(arg);
1630 if (enclose) {
1631 _code << "</mrow></mfenced>";
1632 }
1633 }
1634
1635 virtual void printConditionalAssignment(Node& node) {
1636 CPPADCG_ASSERT_UNKNOWN(getVariableID(node) > 0)
1637
1638 const std::vector<Arg>& args = node.getArguments();
1639 const Arg &left = args[0];
1640 const Arg &right = args[1];
1641 const Arg &trueCase = args[2];
1642 const Arg &falseCase = args[3];
1643
1644 bool isDep = isDependent(node);
1645 const std::string& varName = createVariableName(node);
1646
1647 if ((trueCase.getParameter() != nullptr && falseCase.getParameter() != nullptr && *trueCase.getParameter() == *falseCase.getParameter()) ||
1648 (trueCase.getOperation() != nullptr && falseCase.getOperation() != nullptr && trueCase.getOperation() == falseCase.getOperation())) {
1649 // true and false cases are the same
1650 printAssignmentStart(node, varName, isDep);
1651 print(trueCase);
1652 printAssignmentEnd(node);
1653 } else {
1654
1655 _code << _ifStart << _startEq << "<mi>if</mi>"
1656 "<mfenced><mrow>";
1657 print(left);
1658 _code << "<mo>";
1659 getComparison(_code, node.getOperationType());
1660 _code << "</mo>";
1661 print(right);
1662 _code << "</mrow></mfenced>" << _endEq << _endline
1663 << _condBodyStart << _endline;
1664
1665 //checkEquationEnvStart(); // no need
1666 printAssignmentStart(node, varName, isDep);
1667 print(trueCase);
1668 printAssignmentEnd(node);
1669 _code << _condBodyEnd << _endline << _ifEnd << _endline;
1670
1671 // else
1672 _code << _elseStart << _startEq << "<mi>else</mi>" << _endEq << _endline
1673 << _condBodyStart << _endline;
1674 //checkEquationEnvStart(); // no need
1675 printAssignmentStart(node, varName, isDep);
1676 print(falseCase);
1677 printAssignmentEnd(node);
1678 _code << _condBodyEnd << _endline << _elseEnd << _endline; // end if
1679 }
1680 }
1681
1682 inline bool isSameArgument(const Arg& newArg,
1683 const Arg* oldArg) {
1684 if (oldArg != nullptr) {
1685 if (oldArg->getParameter() != nullptr) {
1686 if (newArg.getParameter() != nullptr) {
1687 return (*newArg.getParameter() == *oldArg->getParameter());
1688 }
1689 } else {
1690 return (newArg.getOperation() == oldArg->getOperation());
1691 }
1692 }
1693 return false;
1694 }
1695
1696 virtual void printArrayCreationOp(Node& op);
1697
1698 virtual void printSparseArrayCreationOp(Node& op);
1699
1700 inline void printArrayStructInit(const std::string& dataArrayName,
1701 size_t pos,
1702 const std::vector<Node*>& arrays,
1703 size_t k);
1704
1705 inline void printArrayStructInit(const std::string& dataArrayName,
1706 Node& array);
1707
1708 inline void markArrayChanged(Node& ty);
1709
1710 inline size_t printArrayCreationUsingLoop(size_t startPos,
1711 Node& array,
1712 size_t startj,
1713 std::vector<const Arg*>& tmpArrayValues);
1714
1715 inline std::string getTempArrayName(const Node& op);
1716
1717 virtual void printArrayElementOp(Node& op);
1718
1719 virtual void printAtomicForwardOp(Node& atomicFor) {
1720 CPPADCG_ASSERT_KNOWN(atomicFor.getInfo().size() == 3, "Invalid number of information elements for atomic forward operation")
1721 int q = atomicFor.getInfo()[1];
1722 int p = atomicFor.getInfo()[2];
1723 size_t p1 = p + 1;
1724 const std::vector<Arg>& opArgs = atomicFor.getArguments();
1725 CPPADCG_ASSERT_KNOWN(opArgs.size() == p1 * 2, "Invalid number of arguments for atomic forward operation")
1726
1727 size_t id = atomicFor.getInfo()[0];
1728 std::vector<Node*> tx(p1), ty(p1);
1729 for (size_t k = 0; k < p1; k++) {
1730 tx[k] = opArgs[0 * p1 + k].getOperation();
1731 ty[k] = opArgs[1 * p1 + k].getOperation();
1732 }
1733
1734 CPPADCG_ASSERT_KNOWN(tx[0]->getOperationType() == CGOpCode::ArrayCreation, "Invalid array type")
1735 CPPADCG_ASSERT_KNOWN(p == 0 || tx[1]->getOperationType() == CGOpCode::SparseArrayCreation, "Invalid array type")
1736 CPPADCG_ASSERT_KNOWN(ty[p]->getOperationType() == CGOpCode::ArrayCreation, "Invalid array type")
1737
1738 // tx
1739 for (size_t k = 0; k < p1; k++) {
1740 printArrayStructInit(_ATOMIC_TX, k, tx, k);
1741 }
1742 // ty
1743 printArrayStructInit(_ATOMIC_TY, *ty[p]);
1744 _ss.str("");
1745
1746 _code << _startEq
1747 << _info->atomicFunctionId2Name.at(id) << "<mo>.</mo><mo>forward</mo>"
1748 "<mfenced separators=','>"
1749 "<mn>" << q << "</mn>"
1750 "<mn>" << p << "</mn>"
1751 "<mrow class='tmp'>" << _ATOMIC_TX << "</mrow>"
1752 "<mrow><mo>&amp;</mo><mrow class='tmp'>" << _ATOMIC_TY << "</mrow></mrow>"
1753 "</mfenced>"
1754 << _endEq << _endline;
1755
1759 markArrayChanged(*ty[p]);
1760 }
1761
1762 virtual void printAtomicReverseOp(Node& atomicRev) {
1763 CPPADCG_ASSERT_KNOWN(atomicRev.getInfo().size() == 2, "Invalid number of information elements for atomic reverse operation")
1764 int p = atomicRev.getInfo()[1];
1765 size_t p1 = p + 1;
1766 const std::vector<Arg>& opArgs = atomicRev.getArguments();
1767 CPPADCG_ASSERT_KNOWN(opArgs.size() == p1 * 4, "Invalid number of arguments for atomic reverse operation")
1768
1769 size_t id = atomicRev.getInfo()[0];
1770 std::vector<Node*> tx(p1), px(p1), py(p1);
1771 for (size_t k = 0; k < p1; k++) {
1772 tx[k] = opArgs[0 * p1 + k].getOperation();
1773 px[k] = opArgs[2 * p1 + k].getOperation();
1774 py[k] = opArgs[3 * p1 + k].getOperation();
1775 }
1776
1777 CPPADCG_ASSERT_KNOWN(tx[0]->getOperationType() == CGOpCode::ArrayCreation, "Invalid array type")
1778 CPPADCG_ASSERT_KNOWN(p == 0 || tx[1]->getOperationType() == CGOpCode::SparseArrayCreation, "Invalid array type")
1779
1780 CPPADCG_ASSERT_KNOWN(px[0]->getOperationType() == CGOpCode::ArrayCreation, "Invalid array type")
1781
1782 CPPADCG_ASSERT_KNOWN(py[0]->getOperationType() == CGOpCode::SparseArrayCreation, "Invalid array type")
1783 CPPADCG_ASSERT_KNOWN(p == 0 || py[1]->getOperationType() == CGOpCode::ArrayCreation, "Invalid array type")
1784
1785 // tx
1786 for (size_t k = 0; k < p1; k++) {
1787 printArrayStructInit(_ATOMIC_TX, k, tx, k);
1788 }
1789 // py
1790 for (size_t k = 0; k < p1; k++) {
1791 printArrayStructInit(_ATOMIC_PY, k, py, k);
1792 }
1793 // px
1794 printArrayStructInit(_ATOMIC_PX, *px[0]);
1795 _ss.str("");
1796
1797 _code << _startEq
1798 << _info->atomicFunctionId2Name.at(id) << "<mo>.</mo><mo>reverse</mo>"
1799 "<mfenced separators=','>"
1800 "<mn>" << p << "</mn>"
1801 "<mrow class='tmp'>" << _ATOMIC_TX << "</mrow>"
1802 "<mrow><mo>&amp;</mo><mrow class='tmp'>" << _ATOMIC_PX << "</mrow></mrow>"
1803 "<mrow class='tmp'>" << _ATOMIC_PY << "</mrow>"
1804 "</mfenced>"
1805 << _endEq << _endline;
1806
1810 markArrayChanged(*px[0]);
1811 }
1812
1813 virtual unsigned printDependentMultiAssign(Node& node) {
1814 CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::DependentMultiAssign, "Invalid node type")
1815 CPPADCG_ASSERT_KNOWN(node.getArguments().size() > 0, "Invalid number of arguments")
1816
1817 const std::vector<Arg>& args = node.getArguments();
1818 for (size_t a = 0; a < args.size(); a++) {
1819 bool useArg;
1820 const Arg& arg = args[a];
1821 if (arg.getParameter() != nullptr) {
1822 useArg = true;
1823 } else {
1824 CGOpCode op = arg.getOperation()->getOperationType();
1825 useArg = op != CGOpCode::DependentRefRhs && op != CGOpCode::LoopEnd && op != CGOpCode::EndIf;
1826 }
1827
1828 if (useArg) {
1829 printAssignment(node, arg); // ignore other arguments!
1830 return 1;
1831 }
1832 }
1833 return 0;
1834 }
1835
1836 virtual void printLoopStart(Node& node) {
1837 CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::LoopStart, "Invalid node type")
1838
1839 auto& lnode = static_cast<LoopStartOperationNode<Base>&> (node);
1840 _currentLoops.push_back(&lnode);
1841
1842 const std::string& jj = *lnode.getIndex().getName();
1843 std::string lastIt;
1844 if (lnode.getIterationCountNode() != nullptr) {
1845 lastIt = *lnode.getIterationCountNode()->getIndex().getName() + " <mo>-</mo> <mn>1</mn>";
1846 } else {
1847 lastIt = "<mn>" + std::to_string(lnode.getIterationCount() - 1) + "</mn>";
1848 }
1849
1850 _code << _forStart << _startEq << "<mi>for</mi>"
1851 "<mfenced><mrow><mi class='index'>"
1852 << jj << "</mi><mo>&isin;</mo>"
1853 "<mfenced open='[' close='[' separators=';'>"
1854 "<mn>0</mn>" << lastIt <<
1855 "</mfenced>"
1856 "</mrow></mfenced>" << _endEq << _endline
1857 << _forBodyStart;
1858 _indentationLevel++;
1859 }
1860
1861 virtual void printLoopEnd(Node& node) {
1862 CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::LoopEnd, "Invalid node type")
1863
1864 _indentationLevel--;
1865
1866 _code << _forBodyEnd << _forEnd << _endline;
1867
1868 _currentLoops.pop_back();
1869 }
1870
1871 virtual void printLoopIndexedDep(Node& node) {
1872 CPPADCG_ASSERT_KNOWN(node.getArguments().size() >= 1, "Invalid number of arguments for loop indexed dependent operation")
1873
1874 // LoopIndexedDep
1875 print(node.getArguments()[0]);
1876 }
1877
1878 virtual void printLoopIndexedIndep(Node& node) {
1879 CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::LoopIndexedIndep, "Invalid node type")
1880 CPPADCG_ASSERT_KNOWN(node.getInfo().size() == 1, "Invalid number of information elements for loop indexed independent operation")
1881
1882 // CGLoopIndexedIndepOp
1883 size_t pos = node.getInfo()[1];
1884 const IndexPattern* ip = _info->loopIndependentIndexPatterns[pos];
1885 _code << _nameGen->generateIndexedIndependent(node, getVariableID(node), *ip);
1886 }
1887
1888 virtual void printLoopIndexedTmp(Node& node) {
1889 CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::LoopIndexedTmp, "Invalid node type")
1890 CPPADCG_ASSERT_KNOWN(node.getArguments().size() == 2, "Invalid number of arguments for loop indexed temporary operation")
1891 Node* tmpVar = node.getArguments()[0].getOperation();
1892 CPPADCG_ASSERT_KNOWN(tmpVar != nullptr && tmpVar->getOperationType() == CGOpCode::TmpDcl, "Invalid arguments for loop indexed temporary operation")
1893
1894 print(node.getArguments()[1]);
1895 }
1896
1897 virtual void printTmpVar(Node& node) {
1898 CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::Tmp, "Invalid node type")
1899 CPPADCG_ASSERT_KNOWN(node.getArguments().size() > 0, "Invalid number of arguments for temporary variable usage operation")
1900 Node* tmpVar = node.getArguments()[0].getOperation();
1901 CPPADCG_ASSERT_KNOWN(tmpVar != nullptr && tmpVar->getOperationType() == CGOpCode::TmpDcl, "Invalid arguments for loop indexed temporary operation")
1902
1903 _code << "<mrow id='" << createHtmlID(tmpVar) << "' class='tmp'>" << *tmpVar->getName() << "</mrow>";
1904 }
1905
1906 virtual void printIndexAssign(Node& node) {
1907 CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::IndexAssign, "Invalid node type")
1908 CPPADCG_ASSERT_KNOWN(node.getArguments().size() > 0, "Invalid number of arguments for an index assignment operation")
1909
1910 auto& inode = static_cast<IndexAssignOperationNode<Base>&> (node);
1911
1912 const IndexPattern& ip = inode.getIndexPattern();
1913 _code << _startEq
1914 << "<mi class='index'>"<< (*inode.getIndex().getName()) << "</mi>"
1915 << _assignStr;
1916 indexPattern2String(_code, ip, inode.getIndexPatternIndexes());
1917 _code << _endEq << _endline;
1918 }
1919
1920 virtual void printIndexCondExprOp(Node& node) {
1921 CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::IndexCondExpr, "Invalid node type")
1922 CPPADCG_ASSERT_KNOWN(node.getArguments().size() == 1, "Invalid number of arguments for an index condition expression operation")
1923 CPPADCG_ASSERT_KNOWN(node.getArguments()[0].getOperation() != nullptr, "Invalid argument for an index condition expression operation")
1924 CPPADCG_ASSERT_KNOWN(node.getArguments()[0].getOperation()->getOperationType() == CGOpCode::Index, "Invalid argument for an index condition expression operation")
1925
1926 const std::vector<size_t>& info = node.getInfo();
1927
1928 auto& iterationIndexOp = static_cast<IndexOperationNode<Base>&> (*node.getArguments()[0].getOperation());
1929 const std::string& index = *iterationIndexOp.getIndex().getName();
1930
1931 printIndexCondExpr(_code, info, index);
1932 }
1933
1934 virtual void printStartIf(Node& node) {
1939 CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::StartIf, "Invalid node type")
1940 CPPADCG_ASSERT_KNOWN(node.getArguments().size() >= 1, "Invalid number of arguments for an 'if start' operation")
1941 CPPADCG_ASSERT_KNOWN(node.getArguments()[0].getOperation() != nullptr, "Invalid argument for an 'if start' operation")
1942
1943 _code << _ifStart << _startEq << "<mi>if</mi>"
1944 "<mfenced><mrow>";
1945 printIndexCondExprOp(*node.getArguments()[0].getOperation());
1946 _code << "</mrow></mfenced>" << _endEq << _endline
1947 << _condBodyStart << _endline;
1948
1949 _indentationLevel++;
1950 }
1951
1952 virtual void printElseIf(Node& node) {
1958 CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::ElseIf, "Invalid node type")
1959 CPPADCG_ASSERT_KNOWN(node.getArguments().size() >= 2, "Invalid number of arguments for an 'else if' operation")
1960 CPPADCG_ASSERT_KNOWN(node.getArguments()[0].getOperation() != nullptr, "Invalid argument for an 'else if' operation")
1961 CPPADCG_ASSERT_KNOWN(node.getArguments()[1].getOperation() != nullptr, "Invalid argument for an 'else if' operation")
1962
1963 _indentationLevel--;
1964
1965 // close previous environment
1966 _code << _condBodyEnd << _endline;
1967 CGOpCode nType = node.getArguments()[0].getOperation()->getOperationType();
1968 if (nType == CGOpCode::StartIf) {
1969 _code << _ifEnd << _endline;
1970 } else if (nType == CGOpCode::ElseIf) {
1971 _code << _elseIfEnd << _endline;
1972 }
1973
1974 // start new else if
1975 _code << _elseIfStart << _startEq << "<mi>else if</mi>"
1976 "<mfenced><mrow>";
1977 printIndexCondExprOp(*node.getArguments()[1].getOperation());
1978 _code << "</mrow></mfenced>" << _endEq << _endline
1979 << _condBodyStart << _endline;
1980
1981 _indentationLevel++;
1982 }
1983
1984 virtual void printElse(Node& node) {
1989 CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::Else, "Invalid node type")
1990 CPPADCG_ASSERT_KNOWN(node.getArguments().size() >= 1, "Invalid number of arguments for an 'else' operation")
1991
1992 _indentationLevel--;
1993
1994 // close previous environment
1995 _code << _condBodyEnd << _endline;
1996 CGOpCode nType = node.getArguments()[0].getOperation()->getOperationType();
1997 if (nType == CGOpCode::StartIf) {
1998 _code << _ifEnd << _endline;
1999 } else if (nType == CGOpCode::ElseIf) {
2000 _code << _elseIfEnd << _endline;
2001 }
2002
2003 // start else
2004 _code << _elseStart << _startEq << "<mi>else</mi>" << _endEq << _endline
2005 << _condBodyStart << _endline;
2006 _code << _elseStart;
2007
2008 _indentationLevel++;
2009 }
2010
2011 virtual void printEndIf(Node& node) {
2012 CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::EndIf, "Invalid node type for an 'end if' operation")
2013
2014 _indentationLevel--;
2015
2016 // close previous environment
2017 _code << _condBodyEnd << _endline;
2018 CGOpCode nType = node.getArguments()[0].getOperation()->getOperationType();
2019 if (nType == CGOpCode::StartIf) {
2020 _code << _ifEnd << _endline;
2021 } else if (nType == CGOpCode::ElseIf) {
2022 _code << _elseIfEnd << _endline;
2023 } else {
2024 assert(nType == CGOpCode::Else);
2025 _code << _elseEnd << _endline;
2026 }
2027 }
2028
2029 virtual void printCondResult(Node& node) {
2030 CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::CondResult, "Invalid node type")
2031 CPPADCG_ASSERT_KNOWN(node.getArguments().size() == 2, "Invalid number of arguments for an assignment inside an if/else operation")
2032 CPPADCG_ASSERT_KNOWN(node.getArguments()[0].getOperation() != nullptr, "Invalid argument for an an assignment inside an if/else operation")
2033 CPPADCG_ASSERT_KNOWN(node.getArguments()[1].getOperation() != nullptr, "Invalid argument for an an assignment inside an if/else operation")
2034
2035 // just follow the argument
2036 Node& nodeArg = *node.getArguments()[1].getOperation();
2037 printAssignment(nodeArg);
2038 }
2039
2040 virtual void printUserCustom(Node& node) {
2041 CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::UserCustom, "Invalid node type")
2042
2043 throw CGException("Unable to generate MathML for user custom operation nodes.");
2044 }
2045
2046 inline bool isDependent(const Node& arg) const {
2047 if (arg.getOperationType() == CGOpCode::LoopIndexedDep) {
2048 return true;
2049 }
2050 size_t id = getVariableID(arg);
2051 return id > _independentSize && id < _minTemporaryVarID;
2052 }
2053
2054 virtual void printParameter(const Base& value) {
2055 // make sure all digits of floating point values are printed
2056 std::ostringstream os;
2057 os << std::setprecision(_parameterPrecision) << value;
2058
2059 std::string number = os.str();
2060 size_t pos = number.find('e');
2061 if (pos != std::string::npos) {
2062 _code << "<mn>" << number.substr(0, pos) << "</mn><mo>&times;</mo>";
2063 _code << "<msup><mn>10</mn><mn>";
2064 pos++;
2065 if (number[pos] == '-') {
2066 _code << "-";
2067 pos++;
2068 } else if (number[pos] == '+') {
2069 pos++;
2070 }
2071 while (pos < number.size() - 1 && number[pos] == '0')
2072 pos++; // remove zeros
2073
2074 _code << number.substr(pos) << "</mn></msup>";
2075
2076 } else {
2077 _code << "<mn>" << number << "</mn>";
2078 }
2079
2080
2081 }
2082
2083 virtual void getComparison(std::ostream& os, enum CGOpCode op) const {
2084 switch (op) {
2085 case CGOpCode::ComLt:
2086 os << "&lt;";
2087 return;
2088
2089 case CGOpCode::ComLe:
2090 os << "&le;";
2091 return;
2092
2093 case CGOpCode::ComEq:
2094 os << "==";
2095 return;
2096
2097 case CGOpCode::ComGe:
2098 os << "&ge;";
2099 return;
2100
2101 case CGOpCode::ComGt:
2102 os << "&gt;";
2103 return;
2104
2105 case CGOpCode::ComNe:
2106 os << "&ne;";
2107 return;
2108
2109 default:
2110 CPPAD_ASSERT_UNKNOWN(0)
2111 break;
2112 }
2113 throw CGException("Invalid comparison operator code"); // should never get here
2114 }
2115
2116 static bool isFunction(enum CGOpCode op) {
2117 return isUnaryFunction(op) || op == CGOpCode::Pow;
2118 }
2119
2120 static bool isUnaryFunction(enum CGOpCode op) {
2121 switch (op) {
2122 case CGOpCode::Abs:
2123 case CGOpCode::Acos:
2124 case CGOpCode::Asin:
2125 case CGOpCode::Atan:
2126 case CGOpCode::Cosh:
2127 case CGOpCode::Cos:
2128 case CGOpCode::Exp:
2129 case CGOpCode::Log:
2130 case CGOpCode::Sign:
2131 case CGOpCode::Sinh:
2132 case CGOpCode::Sin:
2133 case CGOpCode::Sqrt:
2134 case CGOpCode::Tanh:
2135 case CGOpCode::Tan:
2136#if CPPAD_USE_CPLUSPLUS_2011
2137 case CGOpCode::Erf:
2138 case CGOpCode::Erfc:
2139 case CGOpCode::Asinh:
2140 case CGOpCode::Acosh:
2141 case CGOpCode::Atanh:
2142 case CGOpCode::Expm1:
2143 case CGOpCode::Log1p:
2144#endif
2145 return true;
2146 default:
2147 return false;
2148 }
2149 }
2150
2151 static bool isCondAssign(enum CGOpCode op) {
2152 switch (op) {
2153 case CGOpCode::ComLt:
2154 case CGOpCode::ComLe:
2155 case CGOpCode::ComEq:
2156 case CGOpCode::ComGe:
2157 case CGOpCode::ComGt:
2158 case CGOpCode::ComNe:
2159 return true;
2160 default:
2161 return false;
2162 }
2163 }
2164};
2165
2166template<class Base>
2167const std::string LanguageMathML<Base>::_C_STATIC_INDEX_ARRAY = "index"; // NOLINT(cert-err58-cpp)
2168
2169template<class Base>
2170const std::string LanguageMathML<Base>::_C_SPARSE_INDEX_ARRAY = "idx"; // NOLINT(cert-err58-cpp)
2171
2172template<class Base>
2173const std::string LanguageMathML<Base>::_ATOMIC_TX = "atx"; // NOLINT(cert-err58-cpp)
2174
2175template<class Base>
2176const std::string LanguageMathML<Base>::_ATOMIC_TY = "aty"; // NOLINT(cert-err58-cpp)
2177
2178template<class Base>
2179const std::string LanguageMathML<Base>::_ATOMIC_PX = "apx"; // NOLINT(cert-err58-cpp)
2180
2181template<class Base>
2182const std::string LanguageMathML<Base>::_ATOMIC_PY = "apy"; // NOLINT(cert-err58-cpp)
2183
2184} // END cg namespace
2185} // END CppAD namespace
2186
2187#endif
size_t size() const noexcept
const CodeHandlerVector< Base, size_t > & totalUseCount
Definition language.hpp:95
const std::vector< Node * > & variableOrder
Definition language.hpp:53
const CodeHandlerVector< Base, size_t > & varId
Definition language.hpp:49
const std::vector< std::set< Node * > > & variableDependencies
Definition language.hpp:57
const std::vector< Node * > & independent
Definition language.hpp:36
const std::map< size_t, std::string > & atomicFunctionId2Name
Definition language.hpp:69
virtual void printElseIf(Node &node)
void setHeadExtraMarkup(const std::string &headExtra)
virtual size_t getParameterPrecision() const
void setMultiplicationConstParMarkup(const std::string &multValOpStr)
const std::string & getMultiplicationConstParMarkup() const
virtual const std::string & getIfStartMarkup() const
bool requiresVariableDependencies() const override
virtual void setEquationMarkup(const std::string &begin, const std::string &end)
void printRandomIndexPatternDeclaration(std::ostringstream &os, const std::string &identation, const std::set< RandomIndexPattern * > &randomPatterns)
virtual const std::string & getElseIfEndMarkup() const
virtual const std::string & getElseIfStartMarkup() const
std::string createHtmlID(const Node *var)
virtual void printAtomicForwardOp(Node &atomicFor)
void setJavascript(const std::string &javascript)
virtual const std::string & getElseEndMarkup() const
virtual std::string createHtmlID(const Node &var)
void generateSourceCode(std::ostream &out, std::unique_ptr< LanguageGenerationData< Base > > info) override
virtual bool directlyAssignsVariable(const Node &var) const
virtual void printElse(Node &node)
virtual const std::string & getForEndMarkup() const
virtual const std::string & getEquationStartMarkup() const
bool createsNewVariable(const Node &var, size_t totalUseCount, size_t opCount) const override
virtual const std::string & getEquationEndMarkup() const
virtual void printAtomicReverseOp(Node &atomicRev)
virtual bool isAlwaysEnclosePowBase() const
virtual void setAlwaysEnclosePowBase(bool enclose)
virtual const std::string & getForStartMarkup() const
void printStaticIndexMatrix(std::ostringstream &os, const std::string &name, const std::map< size_t, std::map< size_t, size_t > > &values)
size_t getHtmlID(const Node &var) const
virtual void setParameterPrecision(size_t p)
const std::string & getMultiplicationMarkup() const
void setMultiplicationMarkup(const std::string &multOpStr)
virtual void setElseMarkup(const std::string &begin, const std::string &end)
virtual void setIfMarkup(const std::string &begin, const std::string &end)
virtual void setElseIfMarkup(const std::string &begin, const std::string &end)
virtual void setForMarkup(const std::string &begin, const std::string &end)
virtual const std::string & getIfEndMarkup() const
void setStyle(const std::string &style)
virtual const std::string & getElseStartMarkup() const
void setName(const std::string &name)
const std::vector< size_t > & getInfo() const
size_t getHandlerPosition() const
const std::vector< Argument< Base > > & getArguments() const
CGOpCode getOperationType() const
const std::string * getName() const
virtual std::string generateTemporary(const OperationNode< Base > &variable, size_t id)=0
virtual const std::vector< FuncArgument > & getDependent() const
virtual size_t getMaxTemporarySparseArrayVariableID() const =0
virtual std::string generateIndexedIndependent(const OperationNode< Base > &var, size_t id, const IndexPattern &ip)=0
virtual std::string generateTemporaryArray(const OperationNode< Base > &variable, size_t id)=0
virtual const std::vector< FuncArgument > & getIndependent() const
virtual const std::vector< FuncArgument > & getTemporary() const
virtual std::string generateTemporarySparseArray(const OperationNode< Base > &variable, size_t id)=0
virtual std::string generateIndependent(const OperationNode< Base > &variable, size_t id)=0
virtual std::string generateIndexedDependent(const OperationNode< Base > &var, size_t id, const IndexPattern &ip)=0
virtual size_t getMaxTemporaryArrayVariableID() const =0
virtual std::string generateDependent(size_t index)=0