Minsky
derivative.cc
Go to the documentation of this file.
1 /*
2  @copyright Steve Keen 2014
3  @author Russell Standish
4  This file is part of Minsky.
5 
6  Minsky is free software: you can redistribute it and/or modify it
7  under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  Minsky is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with Minsky. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 // contains implementation of symbolic differentiation
21 
22 #include "minsky.h"
23 #include "equations.h"
24 #include "expr.h"
25 #include "minsky_epilogue.h"
26 #include <regex>
27 
28 using std::regex;
29 using std::smatch;
30 using namespace minsky;
31 
32 namespace MathDAG
33 {
34  NodePtr VariableDAG::derivative(SystemOfEquations& se) const
35  {return se.derivative(*this);}
36  NodePtr ConstantDAG::derivative(SystemOfEquations& se) const
37  {return se.derivative(*this);}
38 
39  string differentiateName(const string& x)
40  {
41  int order=0; // current derivative order
42  string varName=x; // base variable name
43  const regex singleDeriv(R"(d(.*)/dt)"),
44  higherOrderDeriv(R"(d\^\{(\d*)\}(.*)/dt\^\{(\d*)\})");
45  smatch m;
46  if (regex_match(x,m,singleDeriv))
47  {
48  order=1;
49  varName=m[1];
50  }
51  else if(regex_match(x,m,higherOrderDeriv) && m[1]==m[3])
52  {
53  // for some reason, stoi is missing on MXE, so fake it
54  order=atoi(m[3].str().c_str());
55  varName=m[2];
56  }
57  order++;
58  ostringstream r;
59  if (order==1)
60  r<<"d"<<varName<<"/dt";
61  else
62  r<<"d^{"<<order<<"}"<<varName<<"/dt^{"<<order<<"}";
63  return r.str();
64  }
65 
66  // applies the chain rule to expression x
67  template <>
68  NodePtr SystemOfEquations::chainRule(const Expr& x, const Expr& deriv)
69  {
70  const NodePtr dx=x->derivative(*this);
71  // perform some constant optimisation
72  if (dx==zero)
73  return zero;
74  if (dx==one)
75  return deriv;
76  return dx * deriv;
77  }
78 
79  template <>
80  NodePtr SystemOfEquations::derivative(const VariableDAG& expr)
81  {
82  const string name=differentiateName(expr.name);
83  // ensure variable value exists, even if only temporary
84  const VariablePtr tmp(VariableType::tempFlow, name);
85  VariableDAGPtr r(dynamic_pointer_cast<VariableDAG>(makeDAG(tmp->valueId(),tmp->name(),tmp->type())));
86  if (expr.rhs)
87  {
88  if (processingDerivative.contains(expr.name))
89  throw error("definition loop detected in processing derivative of %s",expr.name.c_str());
90  processingDerivative.insert(expr.name);
91  r->rhs=expr.rhs->derivative(*this);
92  processingDerivative.erase(expr.name);
93  }
94  else if (expr.type==VariableType::integral || expr.type==VariableType::stock)
95  {
96  auto ii=expressionCache.getIntegralInput(expr.valueId);
97  if (!ii)
98  throw error("integral input %s not defined",expr.valueId.c_str());
99  if (ii->rhs)
100  r->rhs=ii->rhs; // elide input variable, in case this is a temporary
101  else
102  derivInputs.emplace_back(r, expr.valueId);
103  }
104  else
105  {
106  r->rhs=zero;
107  return zero;
108  }
109  assert(expressionCache.reverseLookup(*r));
110  return r;
111  }
112 
113  template <>
114  NodePtr SystemOfEquations::derivative(const ConstantDAG&)
115  {
116  return zero;
117  }
118 
119  template <>
120  NodePtr SystemOfEquations::derivative
122  {
123  return zero;
124  }
125 
126  template <>
127  NodePtr SystemOfEquations::derivative<>
129  {
130  CachedOp<OperationType::add> r(expressionCache);
131  r->arguments.resize(expr.arguments.size());
132  for (size_t i=0; i<expr.arguments.size(); ++i)
133  for (WeakNodePtr n: expr.arguments[i])
134  {
135  assert(expressionCache.reverseLookup(*n));
136  r->arguments[i].push_back(n->derivative(*this));
137  assert(expressionCache.reverseLookup(*r->arguments[i].back()));
138  }
139  assert(expressionCache.reverseLookup(*r));
140  return r;
141  }
142 
143  template <>
144  NodePtr SystemOfEquations::derivative
146  {
147  CachedOp<OperationType::subtract> r(expressionCache);
148  r->arguments.resize(expr.arguments.size());
149  for (size_t i=0; i<expr.arguments.size(); ++i)
150  for (WeakNodePtr n: expr.arguments[i])
151  r->arguments[i].push_back(n->derivative(*this));
152  assert(expressionCache.reverseLookup(*r));
153  return r;
154  }
155 
156  template <>
157  NodePtr SystemOfEquations::derivative
159  {
160  // unfold arguments into a single list
161  vector<WeakNodePtr> args;
162  for (size_t i=0; i<expr.arguments.size(); ++i)
163  for (const WeakNodePtr n: expr.arguments[i])
164  args.push_back(n);
165 
166  // remember multiplies are n-ary, not binary. eg
167  // (uvw)' = u'(vw)+v'(uw)+w'(uv)
168 
169  CachedOp<OperationType::add> r(expressionCache);
170  assert(!r->arguments.empty());
171 
172  for (size_t i=0; i<args.size(); ++i)
173  {
174  const CachedOp<OperationType::multiply> p(expressionCache);
175  r->arguments[0].push_back(p);
176  assert(!p->arguments.empty());
177  for (size_t j=0; j<args.size(); ++j)
178  if (j!=i)
179  p->arguments[0].push_back(args[j]);
180  p->arguments[0].push_back(args[i]->derivative(*this));
181  assert(expressionCache.reverseLookup(*p->arguments[0].back()));
182  }
183 
184  assert(expressionCache.reverseLookup(*r));
185  return r;
186  }
187 
188  template <>
189  NodePtr SystemOfEquations::derivative
191  {
192  // remember divides are n-ary, not binary. So convert each
193  // argument list into a product expression, and then perform the
194  // standard binary quotient rule. u=numerator, v=denominator
195  // d(u/v) = (vdu-udv)/v^2
196 
197  const CachedOp<OperationType::multiply> u1(expressionCache), v1(expressionCache);
198 
199  assert(expr.arguments.size()==2);
200  assert(!u1->arguments.empty() && !v1->arguments.empty());
201 
202  // check all arguments are cached
203 #ifndef NDEBUG
204  for (auto av: expr.arguments)
205  for (auto a: av)
206  assert(a && expressionCache.reverseLookup(*a));
207 #endif
208 
209  u1->arguments[0]=expr.arguments[0];
210  v1->arguments[0]=expr.arguments[1];
211 
212  const Expr u(expressionCache,u1), v(expressionCache,v1);
213  const Expr du(expressionCache, u->derivative(*this)), dv(expressionCache, v->derivative(*this));
214 
215  NodePtr r = (v*du-u*dv)/(v*v);
216  assert(expressionCache.reverseLookup(*r));
217  return r;
218  }
219 
220  template <>
221  NodePtr SystemOfEquations::derivative(const OperationDAG<OperationType::log>& expr)
222  {
223  // log_b(x) = ln(x)/ln(b)
224  assert(expr.arguments.size()==2);
225  if (expr.arguments[0].empty())
226  return zero;
227  const Expr x(expressionCache, expressionCache.reverseLookup(*expr.arguments[0][0]));
228  if (expr.arguments[1].empty())
229  return x->derivative(*this)/x;
230  const Expr b(expressionCache, expressionCache.reverseLookup(*expr.arguments[1][0]));
231  return (log(x)/log(b))->derivative(*this);
232  }
233 
234 
235  template <>
236  NodePtr SystemOfEquations::derivative
238  {
239  // x^y = exp(y*ln(x))
240  assert(expr.arguments.size()==2);
241  if (expr.arguments[0].empty())
242  return zero;
243  if (expr.arguments[1].empty())
244  {
245  const Expr x(expressionCache, expr.arguments[0][0]);
246  const Expr dx(expressionCache, x->derivative(*this));
247  return dx * x;
248  }
249  const Expr x(expressionCache, expr.arguments[0][0]);
250  const Expr y(expressionCache, expr.arguments[1][0]);
251  return exp(y * log(x))->derivative(*this);
252  }
253 
254  template <>
255  NodePtr SystemOfEquations::derivative
257  {
258  if (expr.arguments[0].empty())
259  return zero;
260  throw error("lt is not differentiable");
261  }
262 
263  template <>
264  NodePtr SystemOfEquations::derivative
266  {
267  if (expr.arguments[0].empty())
268  return zero;
269  throw error("le is not differentiable");
270  }
271 
272  template <>
273  NodePtr SystemOfEquations::derivative
275  {
276  if (expr.arguments[0].empty())
277  return zero;
278  throw error("eq is not differentiable");
279  }
280 
281  template <>
282  NodePtr SystemOfEquations::derivative
284  {
285  return zero;
286  }
287 
288  template <>
289  NodePtr SystemOfEquations::derivative
291  {
292  return zero;
293  }
294 
295  template <>
296  NodePtr SystemOfEquations::derivative
298  {
299  return zero;
300  }
301 
302  // nb strictly speaking, the derivative is undefined at x==y,
303  // unless x(t)==y(t), but shouldn't cause problems on integration
304  template <>
305  NodePtr SystemOfEquations::derivative
307  {
308  assert(expr.arguments.size()==2);
309  auto tmp=make_shared<OperationDAG<OperationType::min>>(expr);
310  // combine all arguments
311  tmp->arguments[1].clear();
312  for (auto i: expr.arguments[1])
313  tmp->arguments[0].push_back(i);
314 
315  switch (tmp->arguments[0].size())
316  {
317  case 0:
318  return zero;
319  case 1:
320  return tmp->arguments[0][0]->derivative(*this);
321  default:
322  {
323  const Expr x(expressionCache,tmp->arguments[0].back());
324  tmp->arguments[0].pop_back();
325  const Expr y(expressionCache,NodePtr(tmp));
326  return (x<=y)*x->derivative(*this) +
327  (1-(x<=y))*y->derivative(*this);
328  }
329  };
330  }
331 
332  // nb strictly speaking, the derivative is undefined at x==y,
333  // unless x(t)==y(t), but shouldn't cause problems on integration
334  template <>
335  NodePtr SystemOfEquations::derivative
337  {
338  assert(expr.arguments.size()==2);
339  auto tmp=make_shared<OperationDAG<OperationType::max>>(expr);
340  // combine all arguments
341  tmp->arguments[1].clear();
342  for (auto i: expr.arguments[1])
343  tmp->arguments[0].push_back(i);
344 
345  switch (tmp->arguments[0].size())
346  {
347  case 0:
348  return zero;
349  case 1:
350  return tmp->arguments[0][0]->derivative(*this);
351  default:
352  {
353  const Expr x(expressionCache,tmp->arguments[0].back());
354  tmp->arguments[0].pop_back();
355  const Expr y(expressionCache,NodePtr(tmp));
356  return (x<=y)*y->derivative(*this) +
357  (1-(x<=y))*x->derivative(*this);
358  }
359  };
360  }
361 
362  template <>
363  NodePtr SystemOfEquations::derivative
365  {
366  return one;
367  }
368 
369  template <>
370  NodePtr SystemOfEquations::derivative
372  {
373  return zero;
374  }
375 
376  template <>
377  NodePtr SystemOfEquations::derivative
379  {
380  return zero;
381  }
382 
383  template <>
384  NodePtr SystemOfEquations::derivative
386  {
387  return zero;
388  }
389 
390  template <>
391  NodePtr SystemOfEquations::derivative
393  {
394  return zero;
395  }
396 
397  template <>
398  NodePtr SystemOfEquations::derivative
400  {
401  return zero;
402  }
403 
404  template <>
405  NodePtr SystemOfEquations::derivative<>
407  {
408  if (expr.arguments[0].empty())
409  return zero;
410  const Expr x(expressionCache, expr.arguments[0][0]);
411  return (100*x)->derivative(*this);
412  }
413 
414  template <>
415  NodePtr SystemOfEquations::derivative
417  {
418  if (!expr.arguments[0].empty() && expr.arguments[0][0])
419  return expr.arguments[0][0]->derivative(*this);
420  return zero;
421  }
422 
423  template <>
424  NodePtr SystemOfEquations::derivative
426  {
427  throw error("shouldn't be executed");
428  }
429 
430  template <>
431  NodePtr SystemOfEquations::derivative
433  {
434  throw error("shouldn't be executed");
435  }
436 
437  template <>
438  NodePtr SystemOfEquations::derivative
440  {
441  throw error("cannot differentiate an empirical curve");
442  }
443 
444  template <>
445  NodePtr SystemOfEquations::derivative
447  {
448  throw error("cannot differentiate an empirical curve");
449  }
450 
451  template <>
452  NodePtr SystemOfEquations::derivative
454  {
455  if (expr.arguments[0].empty())
456  return zero;
457  const Expr x(expressionCache, expr.arguments[0][0]);
458  return chainRule(x, 0.5/sqrt(x));
459  }
460 
461  template <>
462  NodePtr SystemOfEquations::derivative
464  {
465  if (expr.arguments[0].empty())
466  return zero;
467  const Expr x(expressionCache, expr.arguments[0][0]);
468  const Expr expx(expressionCache, expressionCache.reverseLookup(expr));
469  return chainRule(x, exp(x));
470  }
471 
472  template <>
473  NodePtr SystemOfEquations::derivative
475  {
476  if (expr.arguments[0].empty())
477  return zero;
478  const Expr x(expressionCache, expr.arguments[0][0]);
479  return chainRule(x, 1/x);
480  }
481 
482  template <>
483  NodePtr SystemOfEquations::derivative
485  {
486  if (expr.arguments[0].empty())
487  return zero;
488  const Expr x(expressionCache, expr.arguments[0][0]);
489  return chainRule(x,cos(x));
490  }
491 
492  template <>
493  NodePtr SystemOfEquations::derivative<>
495  {
496  if (expr.arguments[0].empty())
497  return zero;
498  const Expr x(expressionCache, expr.arguments[0][0]);
499  return chainRule(x,-1*sin(x));
500  }
501 
502 
503  template <>
504  NodePtr SystemOfEquations::derivative<>
506  {
507  if (expr.arguments[0].empty())
508  return zero;
509  const Expr x(expressionCache, expr.arguments[0][0]);
510  const Expr secx=1/cos(x);
511  return chainRule(x,secx * secx);
512  }
513 
514  template <>
515  NodePtr SystemOfEquations::derivative<>
517  {
518  if (expr.arguments[0].empty())
519  return zero;
520  const Expr x(expressionCache, expr.arguments[0][0]);
521  return chainRule(x, 1/sqrt(1-x*x));
522  }
523 
524  template <>
525  NodePtr SystemOfEquations::derivative<>
527  {
528  if (expr.arguments[0].empty())
529  return zero;
530  const Expr x(expressionCache, expr.arguments[0][0]);
531  return chainRule(x, -1/sqrt(1-x*x));
532  }
533 
534  template <>
535  NodePtr SystemOfEquations::derivative<>
537  {
538  if (expr.arguments[0].empty())
539  return zero;
540  const Expr x(expressionCache, expr.arguments[0][0]);
541  return chainRule(x, 1/(1+x*x));
542  }
543 
544  template <>
545  NodePtr SystemOfEquations::derivative<>
547  {
548  if (expr.arguments[0].empty())
549  return zero;
550  const Expr x(expressionCache, expr.arguments[0][0]);
551  return chainRule(x, cosh(x));
552  }
553 
554  template <>
555  NodePtr SystemOfEquations::derivative<>
557  {
558  if (expr.arguments[0].empty())
559  return zero;
560  const Expr x(expressionCache, expr.arguments[0][0]);
561  return chainRule(x, sinh(x));
562  }
563 
564  template <>
565  NodePtr SystemOfEquations::derivative<>
567  {
568  if (expr.arguments[0].empty())
569  return zero;
570  const Expr x(expressionCache, expr.arguments[0][0]);
571  const Expr sech=1/cosh(x);
572  return chainRule(x, sech*sech);
573  }
574 
575  template <>
576  NodePtr SystemOfEquations::derivative<>
578  {
579  if (expr.arguments[0].empty())
580  return zero;
581  const Expr x(expressionCache, expr.arguments[0][0]);
582  return chainRule(x, (one-2*(x<=zero)));
583  }
584 
585  template <>
586  NodePtr SystemOfEquations::derivative<>
588  {
589  if (expr.arguments[0].empty())
590  return zero;
591  // should really be δ(x-⌊x⌋)
592  throw error("floor is not differentiable");
593  }
594 
595  template <>
596  NodePtr SystemOfEquations::derivative<>
598  {
599  if (expr.arguments[0].empty())
600  return zero;
601  throw error("frac is not differentiable");
602  }
603 
604  template <>
605  NodePtr SystemOfEquations::derivative
607  {
608  if (expr.arguments[0].empty())
609  return zero;
610  const Expr x(expressionCache, expr.arguments[0][0]);
611  return chainRule(x,polygamma(x,Expr(expressionCache,zero))*Gamma(x));
612  }
613 
614  template <>
615  NodePtr SystemOfEquations::derivative
617  {
618  assert(expr.arguments.size()==2);
619  if (expr.arguments[0].empty())
620  return zero;
621  const Expr x(expressionCache, expressionCache.reverseLookup(*expr.arguments[0][0]));
622  if (expr.arguments[1].empty())
623  return chainRule(x,polygamma(x,Expr(expressionCache, one)));
624  return chainRule(x,polygamma(x,1+Expr(expressionCache, expr.arguments[1][0])));
625  }
626 
627  template <>
628  NodePtr SystemOfEquations::derivative
630  {
631  if (expr.arguments[0].empty())
632  return zero;
633  const Expr x(expressionCache, expr.arguments[0][0]);
634  return chainRule(x,polygamma(1+x,Expr(expressionCache,zero))*Gamma(1+x));
635  }
636 
637  template <>
638  NodePtr SystemOfEquations::derivative
640  {throw error("Derivatives of user defined functions not implemented");}
641 
642 #define VECTOR_DERIVATIVE_NOT_IMPLEMENTED(op) \
643  template <> \
644  NodePtr SystemOfEquations::derivative<> \
645  (const OperationDAG<OperationType::op>& expr) \
646  { \
647  throw error("Vector derivatives not implemented"); \
648  }
649 
652  VECTOR_DERIVATIVE_NOT_IMPLEMENTED(linearRegression)
669  VECTOR_DERIVATIVE_NOT_IMPLEMENTED(runningProduct)
671  VECTOR_DERIVATIVE_NOT_IMPLEMENTED(differencePlus)
679 
680 }
Expr sin(const Expr &x)
Definition: expr.h:131
virtual std::shared_ptr< Node > derivative(SystemOfEquations &) const =0
support for the derivative operator.
Expr cos(const Expr &x)
Definition: expr.h:137
Expr polygamma(const Expr &x, const Expr &y)
Definition: expr.h:166
Expr sqrt(const Expr &x)
Definition: expr.h:154
Expr Gamma(const Expr &x)
Definition: expr.h:160
string differentiateName(const string &x)
creates a new name to represent the derivative of a variable
Definition: derivative.cc:39
Creation and access to the minskyTCL_obj object, which has code to record whenever Minsky&#39;s state cha...
Definition: constMap.h:22
Expr exp(const Expr &x)
Definition: expr.h:126
Expr sinh(const Expr &x)
Definition: expr.h:142
Expr cosh(const Expr &x)
Definition: expr.h:148
std::string str(T x)
utility function to create a string representation of a numeric type
Definition: str.h:33
weak reference into subexpression cache
Definition: equations.h:133
std::shared_ptr< Node > NodePtr
Definition: equations.h:131
vector< vector< WeakNodePtr > > arguments
Definition: equations.h:219
#define VECTOR_DERIVATIVE_NOT_IMPLEMENTED(op)
Definition: derivative.cc:642
shared_ptr< VariableDAG > VariableDAGPtr
Definition: equations.h:208
Expr log(const Expr &x)
Definition: expr.h:120
WeakNodePtr rhs
Definition: equations.h:178