Minsky
variableValue.cc
Go to the documentation of this file.
1 /*
2  @copyright Steve Keen 2012
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 #include "minsky.h"
20 #include "variableValues.h"
21 #include "flowCoef.h"
22 #include "str.h"
23 #include "valueId.h"
24 
25 #include "constMap.rcd"
26 #include "dimension.rcd"
27 #include "hypercube.rcd"
28 #include "hypercube.xcd"
29 #include "index.rcd"
30 #include "index.xcd"
31 #include "nobble.h"
32 #include "slider.rcd"
33 #include "tensorInterface.rcd"
34 #include "tensorInterface.xcd"
35 #include "tensorVal.rcd"
36 #include "tensorVal.xcd"
37 #include "units.rcd"
38 #include "userFunction.h"
39 #include "variableValue.rcd"
40 #include "variableValues.rcd"
41 #include "variableValues.xcd"
42 #include "xvector.rcd"
43 #include "minsky_epilogue.h"
44 
45 #include <iomanip>
46 #include <error.h>
47 
48 using namespace ecolab;
49 using namespace std;
50 
51 #ifdef WIN32
52 // std::quoted not supported (yet) on MXE
53 string quoted(const string& x)
54 {
55  string r="\"";
56  for (auto& i: x)
57  if (i=='"')
58  r+=R"(\")";
59  else
60  r+=i;
61  return r+"\"";
62 }
63 #endif
64 
65 namespace minsky
66 {
67  std::vector<double,CIVITA_ALLOCATOR<double>> ValueVector::stockVars(1);
68  std::vector<double,CIVITA_ALLOCATOR<double>> ValueVector::flowVars(1);
69 
70  namespace {
71  // special scalar constants
72  struct SpecialConst: public VariableValue
73  {
74  using ITensorVal::operator=;
75  SpecialConst(const string& name, const string& init):
76  VariableValue(VariableType::constant,name) {m_init=init;}
77  };
78  }
79 
80  VariableValuePtr& VariableValues::zero() {
81  static VariableValuePtr s_zero(make_shared<SpecialConst>("constant:zero","0"));
82  return s_zero;
83  }
84  VariableValuePtr& VariableValues::one() {
85  static VariableValuePtr s_one(make_shared<SpecialConst>("constant:one","1"));
86  return s_one;
87  }
88 
89  bool VariableValue::idxInRange() const
90  {return m_type==undefined || idx()+size()<=
91  (isFlowVar()?ValueVector::flowVars.size(): ValueVector::stockVars.size());}
92 
93 
94 
95  double& VariableValue::operator[](size_t i)
96  {
97  assert(i<size() && idxInRange());
98  return *(&valRef()+i);
99  }
100 
101  VariableValue& VariableValue::operator=(minsky::TensorVal const& x)
102  {
103  index(x.index());
104  hypercube(x.hypercube());
105  assert(idxInRange());
106  if (x.size())
107  memcpy(&valRef(), x.begin(), x.size()*sizeof(x[0]));
108  return *this;
109  }
110 
111  VariableValue& VariableValue::operator=(const ITensor& x)
112  {
113  index(x.index());
114  hypercube(x.hypercube());
115  assert(idxInRange());
116  for (size_t i=0; i<x.size(); ++i)
117  (*this)[i]=x[i];
118  return *this;
119  }
120 
121  VariableValue& VariableValue::allocValue()
122  {
123  switch (m_type)
124  {
125  case undefined:
126  m_idx=-1;
127  break;
128  case flow:
129  case tempFlow:
130  case constant:
131  case parameter:
132  m_idx=ValueVector::flowVars.size();
133  assert(size());
134  // return a more meaningful error message to the user than what STL does
135  if (ValueVector::flowVars.max_size()-ValueVector::flowVars.size()<size())
136  throw runtime_error("Maximum processing data exceeded.");
137  ValueVector::flowVars.resize(ValueVector::flowVars.size()+size());
138  break;
139  case stock:
140  case integral:
141  m_idx=ValueVector::stockVars.size();
142  assert(size());
143  // return a more meaningful error message to the user than what STL does
144  if (ValueVector::stockVars.max_size()-ValueVector::stockVars.size()<size())
145  throw runtime_error("Maximum processing data exceeded.");
146  ValueVector::stockVars.resize(ValueVector::stockVars.size()+size());
147  break;
148  default: break;
149  }
150  return *this;
151  }
152 
153  const double& VariableValue::valRef() const
154  {
155  switch (m_type)
156  {
157  case flow:
158  case tempFlow:
159  case constant:
160  case parameter:
161  //assert(idxInRange()); // assertions fail after cancelled reset()
162  if (size_t(m_idx)<ValueVector::flowVars.size())
163  return ValueVector::flowVars[m_idx];
164  break;
165  case stock:
166  case integral:
167  //assert(idxInRange()); // assertions fail after cancelled reset()
168  if (size_t(m_idx)<ValueVector::stockVars.size())
169  return ValueVector::stockVars[m_idx];
170  break;
171  default: break;
172  }
173  static const double zero=0;
174  return zero;
175  }
176 
177  double& VariableValue::valRef()
178  {
179  if (m_idx==-1)
180  allocValue();
181  switch (m_type)
182  {
183  case flow:
184  case tempFlow:
185  case constant:
186  case parameter:
187  assert(idxInRange());
188  if (size_t(m_idx+size())<=ValueVector::flowVars.size())
189  return ValueVector::flowVars[m_idx];
190  case stock:
191  case integral:
192  assert(idxInRange());
193  if (size_t(m_idx+size())<=ValueVector::stockVars.size())
194  return ValueVector::stockVars[m_idx];
195  break;
196  default: break;
197  }
198  throw error("invalid access of variable value reference: %s",name.c_str());
199  }
200 
201  const std::string& VariableValue::init(const std::string& x) {
202  m_init=x;
203  // don't reallocate if initialisation will be ignored (eg wired flow)
204  if (x.empty() || (m_type!=parameter && isFlowVar() && cminsky().definingVar(valueId())))
205  return m_init;
206  auto tensorInit=cminsky().variableValues.initValue(*this);
207  if (tensorInit.size()>size()) m_idx=-1; // force reallocation
208  index(tensorInit.index());
209  hypercube(tensorInit.hypercube());
210  return m_init;
211  }
212 
213  const Hypercube& VariableValue::hypercube() const
214  {
215  if (rhs) return rhs->hypercube();
216  // return the initialisation hypercube if not a defined flowVar
217  if (tensorInit.rank()>0 && (!isFlowVar() || m_type==parameter || !cminsky().definingVar(valueId())))
218  return tensorInit.hypercube();
219  return m_hypercube;
220  }
221 
222  void VariableValue::sliderSet(double x)
223  {
224  if (!isfinite(x)) return;
225  if (x<sliderMin) x=sliderMin;
226  if (x>sliderMax) x=sliderMax;
227  sliderStep=maxSliderSteps();
228  init(to_string(x));
229  setValue(x);
230  }
231 
232  void VariableValue::incrSlider(double step)
233  {
234  sliderSet(value()+step*(sliderStepRel? value(): 1)*sliderStep);
235  }
236 
237  void VariableValue::adjustSliderBounds()
238  {
239  if (size()==1 && !isnan(value()))
240  {
241  if (!isfinite(sliderMax) ||sliderMax<value())
242  sliderMax=value()? abs(10*value()):1;
243  if (!isfinite(sliderMin) || sliderMin>=value())
244  sliderMin=value()? -abs(10*value()):-1;
245  assert(sliderMin<sliderMax);
246  sliderStep=maxSliderSteps();
247  }
248  }
249 
250 
251  TensorVal VariableValues::initValue
252  (const VariableValue& v, set<string>& visited) const
253  {
254  if (v.tensorInit.rank()>0)
255  return v.tensorInit;
256 
257  const FlowCoef fc(v.init());
258  if (trimWS(fc.name).empty())
259  return fc.coef;
260 
261  // special generator functions handled here
262  auto p=fc.name.find('(');
263  if (p!=string::npos)
264  {
265  // const string fn=fc.name.substr(0,p);
266  // unpack args
267  const char* x=fc.name.c_str()+p+1;
268  char* e;
269  vector<unsigned> dims;
270  for (;;)
271  {
272  auto tmp=strtol(x,&e,10);
273  if (tmp>0 && e>x && *e)
274  {
275  x=e+1;
276  dims.push_back(tmp);
277  }
278  else
279  break;
280  }
281  TensorVal r(dims);
282  r.allocVal();
283 
284  if (fc.name.starts_with("iota"))
285  for (size_t i=0; i<r.size(); ++i)
286  r[i]=i;
287  else if (fc.name.starts_with("one"))
288  for (size_t i=0; i<r.size(); ++i)
289  r[i]=1;
290  else if (fc.name.starts_with("zero") || fc.name.starts_with("eye"))
291  {
292  for (size_t i=0; i<r.size(); ++i)
293  r[i]=0;
294  if (fc.name.starts_with("eye"))
295  {
296  // diagonal elements set to 1
297  // find minimum dimension, and stride of diagonal elements
298  size_t mind=r.size(), stride=1;
299  for (auto i: dims)
300  mind=min(mind, size_t(i));
301  for (size_t i=0; i<dims.size()-1; ++i)
302  stride*=(dims[i]+1);
303  for (size_t i=0; i<mind; ++i)
304  r[stride*i]=1;
305  }
306  }
307  else if (fc.name.starts_with("rand"))
308  {
309  for (size_t i=0; i<r.size(); ++i)
310  r[i]=double(rand())/RAND_MAX; // NOLINT
311  }
312  return r;
313  }
314 
315  // resolve name
316  auto valueId=minsky::valueId(v.m_scope.lock(), fc.name);
317  if (visited.contains(valueId))
318  throw error("circular definition of initial value for %s",
319  fc.name.c_str());
320  const VariableValues::const_iterator vv=find(valueId);
321  if (vv==end())
322  throw error("Unknown variable %s in initialisation of %s",fc.name.c_str(), v.name.c_str());
323 
324  visited.insert(valueId);
325  return fc.coef*initValue(*vv->second, visited);
326  }
327 
328  void VariableValues::resetValue(VariableValue& v) const
329  {
330  if (v.idx()<0) v.allocValue();
331  // initialise variable only if its variable is not defined or it is a stock
332  if (!v.isFlowVar() || !cminsky().definingVar(v.valueId()))
333  {
334  if (v.tensorInit.rank())
335  {
336  // ensure dimensions are correct
337  auto hc=v.tensorInit.hypercube();
338  for (auto& xv: hc.xvectors)
339  {
340  auto dim=cminsky().dimensions.find(xv.name);
341  if (dim!=cminsky().dimensions.end())
342  xv.dimension=dim->second;
343  }
344  v.tensorInit.hypercube(hc);
345  }
346  if (v.tensorInit.rank())
347  v=v.tensorInit;
348  else
349  v=initValue(v);
350  }
351  assert(v.idxInRange());
352  }
353 
354 
355  string VariableValues::newName(const string& name) const
356  {
357  int i=1;
358  string trialName;
359  do
360  trialName=utf_to_utf<char>(name)+to_string(i++);
361  while (count(valueId(trialName)));
362  return trialName;
363  }
364 
366  {
367  // reallocate all variables
368  ValueVector::stockVars.clear();
369  ValueVector::flowVars.clear();
370  for (auto& v: *this) {
371  v.second->reset_idx(); // Set idx of all flowvars and stockvars to -1 on reset. For ticket 1049
372  resetValue(v.second->allocValue());
373  assert(v.second->idxInRange());
374  }
375  }
376 
377  bool VariableValues::validEntries() const
378  {
379  return all_of(begin(), end(), [](const value_type& v){return isValueId(v.first);});
380  }
381 
382 
383  EngNotation engExp(double v)
384  {
385  EngNotation r;
386  r.sciExp=(v!=0)? floor(log(fabs(v))/log(10)): 0;
387  if (r.sciExp==3) // special case for dates
388  r.engExp=0;
389  else
390  r.engExp=r.sciExp>=0? 3*(r.sciExp/3): 3*((r.sciExp+1)/3-1);
391  return r;
392  }
393 
394  string mantissa(double value, const EngNotation& e, int digits)
395  {
396  int width, decimal_places;
397  digits=std::max(digits, 3);
398  switch (e.sciExp-e.engExp)
399  {
400  case -3: width=digits+4; decimal_places=digits+1; break;
401  case -2: width=digits+3; decimal_places=digits; break;
402  case 0: case -1: width=digits+2; decimal_places=digits-1; break;
403  case 1: width=digits+2; decimal_places=digits-2; break;
404  case 2: case 3: width=digits+2; decimal_places=digits-3; break;
405  default: return ""; // shouldn't be here...
406  }
407  char val[80];
408  const char conv[]="%*.*f";
409  snprintf(val,sizeof(val),conv,width,decimal_places,value*pow(10,-e.engExp));
410  return val;
411  }
412 
413  string expMultiplier(int exp)
414  {return exp!=0? "×10<sup>"+std::to_string(exp)+"</sup>": "";}
415 
416 
417  void VariableValue::exportAsCSV(const string& filename, const string& comment, bool tabular) const
418  {
419  ofstream of(filename);
420  if (!comment.empty())
421  of<<R"(""")"<<comment<<R"(""")"<<endl;
422 
423  // calculate longest dimension
424  auto dims=hypercube().dims();
425  auto longestDim=max_element(dims.begin(),dims.end())-dims.begin();
426 
427  const auto& xv=hypercube().xvectors;
428 
429  ostringstream os;
430  for (const auto& i: xv)
431  {
432  if (&i>xv.data()) os<<",";
433  os<<json(static_cast<const NamedDimension&>(i));
434  }
435  of<<quoted("RavelHypercube=["+os.str()+"]")<<endl;
436  if (tabular)
437  of<<"HorizontalDimension="<<quoted(xv[longestDim].name)<<endl;
438 
439  for (size_t i=0; i<xv.size(); ++i)
440  if (!tabular || i!=longestDim)
441  of<<CSVQuote(xv[i].name,',')<<",";
442 
443  if (tabular)
444  for (size_t k=0; k<dims[longestDim]; ++k)
445  {
446  if (k>0) of<<",";
447  of<<CSVQuote(str(xv[longestDim][k],xv[longestDim].dimension.units),',');
448  }
449  else
450  of<<"value$";
451  of<<"\n";
452 
453  auto idxv=index();
454 
455  if (tabular)
456  {
457  size_t stride=1;
458  for (size_t i=0; i<longestDim; ++i)
459  stride*=dims[i];
460  for (size_t i=0; i<hypercube().numElements(); i+=stride*dims[longestDim])
461  for (size_t j=0; j<stride; ++j)
462  {
463  // collect elements in a buffer, which can be discarded if no data on line
464  bool isData=false;
465  vector<double> data;
466  data.reserve(dims[longestDim]);
467  for (size_t k=0; k<stride*dims[longestDim]; k+=stride)
468  {
469  data.push_back(this->atHCIndex(i+j+k));
470  if (isfinite(data.back())) isData=true;
471  }
472  if (isData)
473  {
474  auto idx=i+j;
475  for (size_t k=0; k<rank(); ++k)
476  {
477  auto div=std::div(idx, ssize_t(dims[k]));
478  if (k!=longestDim)
479  {
480  if (k>1 || (k>0 && longestDim>0)) of<<",";
481  of << "\""<<str(xv[k][div.rem], xv[k].dimension.units) << "\"";
482  }
483  idx=div.quot;
484  }
485  for (auto d: data)
486  {
487  of<<",";
488  if (isfinite(d)) of<<d;
489  }
490  of<<"\n";
491  }
492  }
493  }
494  else
495  {
496  size_t i=0;
497  for (auto d=begin(); d!=end(); ++i, ++d)
498  if (isfinite(*d))
499  {
500  ssize_t idx=idxv.empty()? i: idxv[i];
501  for (size_t j=0; j<rank(); ++j)
502  {
503  auto div=std::div(idx, ssize_t(dims[j]));
504  of << "\""<<str(xv[j][div.rem], xv[j].dimension.units) << "\",";
505  idx=div.quot;
506  }
507  of << *d << "\n";
508  }
509  }
510  }
511 
512  Summary VariableValue::summary() const
513  {
515  MathDAG::VariableDAGPtr varNode;
516  switch (type())
517  {
518  case integral:
519  case stock:
520  varNode=system.getNodeFromIntVar(valueId());
521  break;
522  default:
523  varNode=system.getNodeFromValueId(valueId());
524  break;
525  }
526 
527  string scopeName=":";
528  if (auto scope=m_scope.lock())
529  if (scope!=cminsky().model)
530  scopeName=scope->title.empty()? scope->id(): scope->title;
531 
532  string godleyName;
533  string definition=varNode && varNode->rhs? varNode->rhs->latexStr(): "";
534  string udfDefinition;
535  try
536  {
537  udfDefinition=varNode && varNode->rhs? varNode->rhs->matlabStr():"";
538  }
539  catch (const std::exception& ex)
540  {
541  udfDefinition=ex.what(); // if matlabStr fails, insert error message
542  }
543  if (auto var=cminsky().definingVar(valueId()))
544  {
545  if (auto controller=dynamic_pointer_cast<GodleyIcon>(var->controller.lock()))
546  godleyName=controller->table.title.empty()? controller->id(): controller->table.title;
547  if (auto p=var->ports(1).lock())
548  if (!p->wires().empty())
549  if (auto udf=dynamic_cast<UserFunction*>(&p->wires().front()->from()->item()))
550  {
551  definition="\\text{"+udf->expression+"}";
552  udfDefinition=udf->expression;
553  }
554  }
555 
556 
557  return Summary{
558  valueId(),
559  name,
560  type(),
561  definition,
562  udfDefinition,
563  init(),
564  sliderStep, sliderMin, sliderMax,
565  value(),
566  scopeName,
567  godleyName,
568  hypercube().dims(),
569  units.latexStr()
570  };
571 
572  }
573 
574 }
575 
std::string expMultiplier(int exp)
TensorVal initValue(const VariableValue &, std::set< std::string > &visited) const
evaluates the initial value of a given variableValue in the context given by this. visited is used to check for circular definitions
EngNotation engExp(double value)
return formatted mantissa and exponent in engineering format
classdesc::Exclude< std::weak_ptr< Group > > m_scope
Definition: variableValue.h:64
virtual const Hypercube & hypercube() const
information describing the axes, types and labels of this tensor
bool isFlowVar() const
returns true if variable&#39;s data is allocated on the flowVariables vector
Definition: variableValue.h:97
UnitsExpressionWalker pow(const UnitsExpressionWalker &x, const UnitsExpressionWalker &y)
SpecialConst(const string &name, const string &init)
reset
Definition: minsky.tcl:1325
VariableValues variableValues
Definition: minsky.h:200
virtual const Index & index() const
the index vector - assumed to be ordered and unique
STL namespace.
size_t scope(const string &name)
extract scope from a qualified variable name
Definition: valueId.cc:83
summary for the variable tab (aka summary tab).
bool idxInRange() const
string valueId(const string &name)
construct a valueId from fully qualified name @ name should not be canonicalised
Definition: valueId.cc:75
a shared_ptr that default constructs a default target, and is always valid
const std::string & init() const
VariableDAGPtr getNodeFromIntVar(const std::string &valueId)
Definition: equations.h:423
struct TCLcmd::trap::init_t init
virtual std::size_t size() const
return number of elements in tensor - maybe less than hypercube.numElements if sparse ...
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
TensorVal tensorInit
when init is a tensor of values, this overrides the init string
Definition: variableValue.h:50
std::string trimWS(const std::string &s)
Definition: str.h:49
represents the units (in sense of dimensional analysis) of a variable.
Definition: units.h:34
std::string str(T x)
utility function to create a string representation of a numeric type
Definition: str.h:33
VariablePtr definingVar(const std::string &valueId) const
returns reference to variable defining (ie input wired) for valueId
Definition: minsky.cc:205
const Minsky & cminsky()
const version to help in const correctness
Definition: minsky.h:549
std::string mantissa(double value, const EngNotation &, int digits=3)
step
Definition: minsky.tcl:1289
represents a numerical coefficient times a variable (a "flow")
Definition: flowCoef.h:27
bool isValueId(const string &name)
check that name is a valid valueId (useful for assertions)
Definition: valueId.cc:33
VariableDAGPtr getNodeFromValueId(const std::string &v)
Definition: equations.h:421
std::string valueId() const
VariableValue & allocValue()
allocate space in the variable vector.
std::string CSVQuote(const std::string &x, char sep)
quotes a string if it contains a separator character, and double quotes quotes
Definition: str.h:162
GroupPtr model
Definition: minsky.h:255
string to_string(CONST84 char *x)
Definition: minskyTCLObj.h:33
shared_ptr< VariableDAG > VariableDAGPtr
Definition: equations.h:208
CLASSDESC_ACCESS_EXPLICIT_INSTANTIATION(minsky::Units)
Expr log(const Expr &x)
Definition: expr.h:120
Dimensions dimensions
Definition: minsky.h:201