1 #ifndef CPPAD_CG_BIPARTITE_GRAPH_INCLUDED 2 #define CPPAD_CG_BIPARTITE_GRAPH_INCLUDED 18 #include <cppad/cg/dae_index_reduction/bipartite_nodes.hpp> 19 #include <cppad/cg/dae_index_reduction/dae_equation_info.hpp> 20 #include <cppad/cg/dae_index_reduction/time_diff.hpp> 48 std::vector<Vnode<Base>*> vnodes_;
49 std::vector<Enode<Base>*> enodes_;
65 int timeOrigVarIndex_;
77 const std::vector<DaeVarInfo>& varInfo,
78 const std::vector<std::string>& eqName,
82 origMaxTimeDivOrder_(0),
83 origTimeDependentCount_(0),
84 preserveNames_(false),
85 timeOrigVarIndex_(-1),
91 CPPADCG_ASSERT_UNKNOWN(fun_ !=
nullptr);
92 const size_t m = fun.Range();
93 const size_t n = fun.Domain();
95 CPPADCG_ASSERT_UNKNOWN(varInfo_.size() == n);
96 for (
size_t j = 0; j < n; ++j) {
97 varInfo_[j].setOriginalIndex(j);
101 for (
size_t j = 0; j < n; ++j) {
102 int deriv = varInfo_[j].getAntiDerivative();
103 CPPADCG_ASSERT_UNKNOWN(deriv <
int(varInfo_.size()));
105 varInfo_[deriv].setDerivative(j);
109 for (
size_t j = 0; j < n; ++j) {
110 determineVariableOrder(varInfo_[j]);
114 enodes_.reserve(1.2 * m + 1);
116 for (
size_t i = 0; i < m; i++) {
117 if (i < eqName.size())
124 for (
size_t dj = 0; dj < n; dj++) {
125 if (varInfo_[dj].isIntegratedVariable()) {
126 if (timeOrigVarIndex_ >= 0) {
127 throw CGException(
"More than one time variable (integrated variable) defined");
129 timeOrigVarIndex_ = dj;
134 vector<int> derivOrder = determineVariableDiffOrder(varInfo_);
135 map<int, vector<size_t> > order2Tape;
136 for (
size_t tape = 0; tape < derivOrder.size(); ++tape) {
137 order2Tape[derivOrder[tape]].push_back(tape);
139 origMaxTimeDivOrder_ = *std::max_element(derivOrder.begin(), derivOrder.end());
144 std::string timeVarName;
145 if (timeOrigVarIndex_ < 0) {
148 if (varInfo_[timeOrigVarIndex_].getName().empty()) {
149 varInfo_[timeOrigVarIndex_].setName(
"t");
151 timeVarName = varInfo_[timeOrigVarIndex_].getName();
159 for (
size_t p = 0; p < tapeIndexes.size(); ++p) {
169 }
else if (order == 0) {
170 for (
size_t p = 0; p < tapeIndexes.size(); ++p) {
179 }
else if (order > 0) {
180 for (
size_t p = 0; p < tapeIndexes.size(); ++p) {
196 for (
size_t p = 0; p < tapeIndexes.size(); ++p) {
197 size_t tapeIndex = tapeIndexes[p];
198 tape2New[tapeIndex] = new2Tape.size();
199 new2Tape.push_back(tapeIndex);
204 origTimeDependentCount_ = new2Tape.size();
205 vnodes_.resize(origTimeDependentCount_);
206 for (
size_t j = 0; j < vnodes_.size(); j++) {
207 size_t tapeIndex = new2Tape[j];
208 int tapeIndex0 = varInfo_[tapeIndex].getAntiDerivative();
209 const std::string& name = varInfo_[tapeIndex].getName();
211 CPPADCG_ASSERT_UNKNOWN(varInfo_[tapeIndex].isFunctionOfIntegrated());
213 if (tapeIndex0 < 0) {
217 Vnode<Base>* derivativeOf = vnodes_[tape2New[tapeIndex0]];
218 vnodes_[j] =
new Vnode<Base>(j, tapeIndex, derivativeOf, name);
223 sparsity_ = jacobianSparsity<vector<bool>,
CGBase>(fun);
225 for (
size_t i = 0; i < m; i++) {
226 for (
size_t p = 0; p < n; p++) {
228 if (j >= 0 && sparsity_[i * n + p]) {
229 enodes_[i]->addVariable(vnodes_[j]);
236 for (
size_t j = 0; j < vnodes_.size(); j++) {
238 if (!jj->isParameter() &&
247 throw CGException(
"The system is not well determined. " 248 "The of number of equations (", enodes_.size(),
")" 249 " does not match the number of unknown variables " 259 for (
size_t i = 0; i < enodes_.size(); i++)
262 for (
size_t j = 0; j < vnodes_.size(); j++)
267 inline std::vector<Vnode<Base>*>& variables() {
271 inline const std::vector<Vnode<Base>*>& variables()
const {
275 inline std::vector<Enode<Base>*>& equations() {
279 inline const std::vector<Enode<Base>*>& equations()
const {
283 const std::vector<DaeVarInfo>& getOriginalVariableInfo()
const {
287 inline size_t getOrigTimeDependentCount()
const {
315 size_t origM = this->fun_->Range();
316 if (origM == enodes_.size()) {
319 for (
size_t j = 0; j < varInfo_.size(); j++) {
334 for (
size_t i = origM; i < enodes_.size(); i++) {
339 while (eq->derivativeOf() !=
nullptr) {
340 eq = eq->derivativeOf();
351 inline void printResultInfo(
const std::string& method) {
352 logger_.log() <<
"\n" << method <<
" DAE differentiation/structural index reduction:\n\n" 353 " Equations count: " << enodes_.size() <<
"\n";
355 logger_.log() <<
" " << ii->index() <<
" - " << *ii <<
"\n";
358 logger_.log() <<
"\n Variable count: " << vnodes_.size() <<
"\n";
361 logger_.log() <<
" " << jj->index() <<
" - " << *jj;
362 if (jj->assignmentEquation() !=
nullptr) {
363 logger_.log() <<
" assigned to " << *jj->assignmentEquation() <<
"\n";
364 }
else if (jj->isParameter()) {
365 logger_.log() <<
" is a parameter (time independent)\n";
367 logger_.log() <<
" NOT assigned to any equation\n";
371 logger_.log() <<
"\n Degrees of freedom: " << vnodes_.size() - enodes_.size() << std::endl;
374 inline void uncolorAll() {
390 size_t tapeIndex = varInfo_.size() + newVarCount;
393 vnodes_.push_back(jDiff);
395 if (logger_.getVerbosity() >= Verbosity::High)
396 logger_.log() <<
"Created " << *jDiff <<
"\n";
402 bool addOrigVars =
true) {
407 enodes_.push_back(iDiff);
412 if (logger_.getVerbosity() >= Verbosity::High)
413 logger_.log() <<
"Created " << *iDiff <<
"\n";
428 CPPADCG_ASSERT_UNKNOWN(enodes_[i.index()] == &i);
429 CPPADCG_ASSERT_UNKNOWN(i.
derivative() ==
nullptr);
433 auto& eqs = j->equations();
434 auto it = std::find(eqs.begin(), eqs.end(), &i);
435 CPPADCG_ASSERT_UNKNOWN(it != eqs.end());
441 while(j->equations().empty()) {
442 CPPADCG_ASSERT_UNKNOWN(vnodes_[j->index()] == j);
445 vnodes_.erase(vnodes_.cbegin() + j->index());
448 for (
size_t jj = j->index(); jj < vnodes_.size(); ++jj) {
449 vnodes_[jj]->setTapeIndex(vnodes_[jj]->tapeIndex() - 1);
450 vnodes_[jj]->setIndex(vnodes_[jj]->index() - 1);
454 CPPADCG_ASSERT_UNKNOWN(jOrig !=
nullptr);
455 jOrig->setDerivative(
nullptr);
465 for (
size_t ii = i.index() + 1; ii < enodes_.size(); ++ii) {
466 CPPADCG_ASSERT_UNKNOWN(enodes_[ii]->index() > 0);
467 CPPADCG_ASSERT_UNKNOWN(enodes_[ii]->index() == ii);
468 enodes_[ii]->setIndex(enodes_[ii]->index() - 1);
471 if(i.derivativeOf() !=
nullptr) {
472 i.derivativeOf()->setDerivative(
nullptr);
475 auto it = std::find(enodes_.begin(), enodes_.end(), &i);
476 CPPADCG_ASSERT_UNKNOWN(it != enodes_.end());
500 bool addOrigVars =
true) {
503 iDiff.addVariable(jj);
506 if (jj->derivative() !=
nullptr) {
507 iDiff.addVariable(jj->derivative());
508 }
else if(!jj->isParameter()) {
509 iDiff.addVariable(createDerivate(*jj));
517 inline std::unique_ptr<ADFun<CGBase>>
generateNewModel(std::vector<DaeVarInfo>& newVarInfo,
518 std::vector<DaeEquationInfo>& equationInfo,
519 const std::vector<Base>& x) {
522 std::unique_ptr<ADFun<CGBase> > reducedFun;
528 size_t origM = this->fun_->Range();
529 for (
size_t i = 0; i < origM; i++) {
530 if (enodes_[i]->derivative() !=
nullptr) {
531 CPPADCG_ASSERT_UNKNOWN(enodes_[i]->derivativeOf() ==
nullptr);
532 newEqs.push_back(enodes_[i]->derivative());
536 while (newEqs.size() > 0) {
537 newEquations.push_back(newEqs);
540 for (
size_t i = 0; i < eqs.size(); i++) {
541 if (eqs[i]->derivative() !=
nullptr) {
542 newEqs.push_back(eqs[i]->derivative());
547 if (newEquations.empty()) {
561 newVarInfo.reserve(varInfo_.size() + newVars);
562 for (
size_t j = origTimeDependentCount_; j < vnodes_.size(); j++) {
567 size_t id = newVarInfo.size();
568 newVarInfo.push_back(
DaeVarInfo(antiDeriv, jj->name(), id));
570 DaeVarInfo& newAntiDeriv = newVarInfo[antiDeriv];
581 for (
size_t j = 0; j < vnodes_.size(); j++) {
583 if (jj->assignmentEquation() !=
nullptr) {
584 assignments[jj->assignmentEquation()] = jj;
588 equationInfo.resize(enodes_.size());
589 for (
size_t i = 0; i < enodes_.size(); i++) {
591 int derivativeOf = ii->derivativeOf() !=
nullptr ? ii->derivativeOf()->index() : -1;
592 int origIndex = ii->derivativeOf() ==
nullptr ? i : -1;
593 int assignedVarIndex = assignments.count(ii) > 0 ? assignments[ii]->tapeIndex() : -1;
595 equationInfo[i] =
DaeEquationInfo(i, origIndex, derivativeOf, assignedVarIndex);
598 size_t timeTapeIndex;
612 if (timeOrigVarIndex_ >= 0) {
614 timeTapeIndex = timeOrigVarIndex_;
617 timeTapeIndex = indepNew.size() - 1;
621 for (
size_t j = 0; j < x.size(); j++) {
624 Independent(indepNew);
628 indep2.resize(indep0.size());
631 evaluator0.setPrintFor(preserveNames_);
633 depNew.resize(enodes_.size());
637 }
catch (
const std::exception& ex) {
638 throw CGException(
"Failed to create ADFun: ", ex.what());
641 if (logger_.getVerbosity() >= Verbosity::High) {
642 logger_.log() <<
"Original model:\n";
643 printModel(logger_.log(), *reducedFun, newVarInfo, equationInfo);
652 for (
size_t d = 0; d < newEquations.size(); d++) {
655 size_t m = reducedFun->Domain();
672 reverseTimeDiff(*reducedFun, equations, dep, timeTapeIndex);
680 if (d < newEquations.size() - 1) {
682 }
else if (timeOrigVarIndex_ < 0) {
684 indepNew.resize(m - 1);
690 for (
size_t j = 0; j < x.size(); j++) {
693 Independent(indepNew);
695 if (d < newEquations.size() - 1) {
704 evaluator.setPrintFor(preserveNames_);
709 }
catch (
const std::exception& ex) {
710 throw CGException(
"Failed to create ADFun: ", ex.what());
713 if (logger_.getVerbosity() >= Verbosity::High) {
714 logger_.log() << equations.size() <<
" new equations:\n";
715 printModel(logger_.log(), *reducedFun, newVarInfo, equationInfo);
722 inline static void forwardTimeDiff(
ADFun<CGBase>& reducedFun,
725 size_t tapeTimeIndex) {
727 size_t m = reducedFun.Domain();
729 std::vector<CGBase> u(m,
CGBase(0));
730 u[tapeTimeIndex] =
CGBase(1);
731 std::vector<CGBase> v;
733 v = reducedFun.Forward(1, u);
734 }
catch (
const std::exception& ex) {
735 throw CGException(
"Failed to determine model Jacobian (forward mode): ", ex.what());
738 for (
size_t e = 0; e < equations.size(); e++) {
739 dep[equations[e]->index()] = v[equations[e]->derivativeOf()->index()];
743 inline static void reverseTimeDiff(
ADFun<CGBase>& reducedFun,
746 size_t tapeTimeIndex) {
747 size_t m = reducedFun.Domain();
748 size_t n = reducedFun.Range();
749 std::vector<CGBase> u(m);
750 std::vector<CGBase> v(n);
752 for (
size_t e = 0; e < equations.size(); e++) {
753 size_t i = equations[e]->derivativeOf()->index();
754 if (reducedFun.Parameter(i)) {
755 dep[equations[e]->index()] = 0;
762 u = reducedFun.Reverse(1, v);
763 }
catch (
const std::exception& ex) {
764 throw CGException(
"Failed to determine model Jacobian (reverse mode): ", ex.what());
771 dep[equations[e]->index()] = u[tapeTimeIndex];
785 const std::vector<DaeVarInfo>& newVarInfo,
786 size_t timeTapeIndex)
const {
787 CPPADCG_ASSERT_UNKNOWN(timeTapeIndex < indepOrig.size());
796 ax[2] = indepOrig[timeTapeIndex];
798 for (
size_t j = 0; j < newVarInfo.size(); j++) {
801 ax[0] = indepOrig[j];
806 indepOut[j] = indepOrig[j];
810 if (newVarInfo.size() < indepOrig.size()) {
811 indepOut[indepOut.size() - 1] = indepOrig[timeTapeIndex];
817 inline void printModel(std::ostream& out,
819 printModel(out, fun, varInfo_);
829 const std::vector<DaeVarInfo>& varInfo,
830 const std::vector<DaeEquationInfo>& eqInfo)
const {
831 std::vector<std::string> vnames(varInfo.size());
832 for (
size_t i = 0; i < varInfo.size(); ++i) {
833 vnames[i] = varInfo[i].getName();
835 std::vector<std::string> eqnames(eqInfo.size());
836 for (
size_t i = 0; i < eqInfo.size(); ++i) {
837 if(eqInfo[i].isExplicit()) {
838 CPPADCG_ASSERT_UNKNOWN(eqInfo[i].getAssignedVarIndex() >= 0);
839 eqnames[i] =
"d" + varInfo[eqInfo[i].getAssignedVarIndex()].getName() +
"dt";
841 eqnames[i] =
"res[" + std::to_string(i) +
"]";
845 printModel(out, fun, vnames, eqnames);
857 const std::vector<std::string>& indepNames,
858 const std::vector<std::string>& depNames = std::vector<std::string>())
const {
861 CPPADCG_ASSERT_UNKNOWN(fun.Domain() == indepNames.size() || fun.Domain() == indepNames.size() + 1);
866 handler.makeVariables(indep0);
877 std::ostringstream code;
878 handler.generateCode(code, langC, dep0, nameGen);
879 out <<
"\n" << code.str() << std::endl;
882 inline void printDot(std::ostream& out)
const {
883 out <<
"digraph {\n";
884 out <<
" overlap=false\n";
885 out <<
" rankdir=LR\n";
886 out <<
" node [style=filled, fillcolor=\"#bdcef5\", color=\"#17128e\"]\n";
887 out <<
" edge [splines=false, dir=none]\n";
890 out <<
" subgraph variables {\n";
891 out <<
" rank=min\n";
893 if(!j->isDeleted()) {
894 out <<
" v" << j->index() <<
" [label=\"" << j->name() <<
"\"";
896 out <<
", color=\"#17c68e\"";
903 out <<
" subgraph equations {\n";
904 out <<
" rank=max\n";
906 out <<
" e" << i->index() <<
" [label=\"" << i->name() <<
"\"";
908 out <<
", color=\"#17c68e\"";
914 out <<
" subgraph eq_derivatives {\n";
915 out <<
" edge[dir=forward, color=grey]\n";
917 if (i->
derivative() !=
nullptr && i->derivativeOf() ==
nullptr) {
919 out <<
" e" << i->index() <<
":e -> e" << i->
derivative()->index() <<
":e\n";
927 out <<
" subgraph var_derivatives {\n";
928 out <<
" edge[dir=forward, color=grey]\n";
933 out <<
" v" << j->index() <<
":w -> v" << j->
derivative()->index() <<
":w\n";
944 for (
const Vnode<Base>* j : i->originalVariables()) {
945 if (!j->isDeleted() && j->assignmentEquation() != i) {
950 out <<
"e" << i->index() <<
" -> v" << j->index() <<
" ";
957 out <<
" subgraph assigned {\n";
958 out <<
" edge[color=blue,penwidth=3.0,style=dashed]\n";
962 for (
const Vnode<Base>* j : i->originalVariables()) {
963 if (!j->isDeleted() && j->assignmentEquation() == i) {
968 out <<
"e" << i->index() <<
" -> v" << j->index() <<
" ";
980 template<
class VectorCGB>
982 const VectorCGB& indep0)
const {
984 if (preserveNames_) {
987 std::ostream out(&b);
989 return fun.Forward(0, indep0, out);
991 return fun.Forward(0, indep0);
995 static inline std::vector<int> determineVariableDiffOrder(
const std::vector<DaeVarInfo>& varInfo) {
996 size_t n = varInfo.size();
998 std::vector<int> derivOrder(n, 0);
999 for (
size_t dj = 0; dj < n; dj++) {
1001 derivOrder[dj] = determineVariableDiffOrder(varInfo, dj, j0);
1007 static inline int determineVariableDiffOrder(
const std::vector<DaeVarInfo>& varInfo,
size_t index,
size_t& j0) {
1008 int derivOrder = -1;
1010 if (varInfo[index].isFunctionOfIntegrated()) {
1012 while (varInfo[j0].getAntiDerivative() >= 0) {
1013 CPPADCG_ASSERT_UNKNOWN(j0 < varInfo.size());
1014 CPPADCG_ASSERT_UNKNOWN(varInfo[j0].isFunctionOfIntegrated());
1016 j0 = varInfo[j0].getAntiDerivative();
1024 inline void determineVariableOrder(
DaeVarInfo& var) {
1028 determineVariableOrder(antiD);
1036 template<
class Base>
1037 inline std::ostream& operator<<(std::ostream& os,
1040 for (
const Vnode<Base>* j : i->originalVariables()) {
1042 if (j->isDeleted()) {
1044 }
else if (j->assignmentEquation() == i) {
1049 os << j->name() <<
" ";
std::vector< ActiveOut > evaluate(ArrayView< const ActiveOut > indepNew, ArrayView< const CG< ScalarIn > > depOld)
void setOriginalAntiDerivative(int originalAntiDerivative)
ADFun< CG< Base > > *const fun_
bool isFunctionOfIntegrated() const
Vnode< Base > * derivative() const
void printModel(std::ostream &out, ADFun< CG< Base > > &fun, const std::vector< std::string > &indepNames, const std::vector< std::string > &depNames=std::vector< std::string >()) const
int getOriginalAntiDerivative() const
void setPreserveNames(bool p)
void dirtyDifferentiateEq(Enode< Base > &i, Enode< Base > &iDiff, bool addOrigVars=true)
size_t origTimeDependentCount_
void printModel(std::ostream &out, ADFun< CG< Base > > &fun, const std::vector< DaeVarInfo > &varInfo, const std::vector< DaeEquationInfo > &eqInfo) const
void makeVariables(VectorCG &variables)
BipartiteGraph(ADFun< CG< Base > > &fun, const std::vector< DaeVarInfo > &varInfo, const std::vector< std::string > &eqName, SimpleLogger &logger)
const std::string & getName() const
void setName(const std::string &name)
Enode< Base > * derivative() const
int getOriginalIndex() const
size_t getStructuralIndex() const
Vnode< Base > * antiDerivative() const
int getDerivative() const
std::vector< bool > sparsity_
void setDerivative(int derivative)
int getAntiDerivative() const
bool isPreserveNames() const
bool isIntegratedVariable() const
std::vector< DaeVarInfo > varInfo_
std::unique_ptr< ADFun< CGBase > > generateNewModel(std::vector< DaeVarInfo > &newVarInfo, std::vector< DaeEquationInfo > &equationInfo, const std::vector< Base > &x)
std::vector< CppAD::AD< CG< Base > > > prepareTimeDependentVariables(const std::vector< ADCG > &indepOrig, const std::vector< DaeVarInfo > &newVarInfo, size_t timeTapeIndex) const