1 """Functional specification classes.
2
3 Robert Clewley, August 2005.
4
5 This module aids in building internal representations of ODEs, etc.,
6 particularly for the benefit of Automatic Differentiation
7 and for manipulation of abstraction digraphs.
8 """
9
10
11 from __future__ import division
12 from utils import *
13 from common import *
14 from parseUtils import *
15 from errors import *
16 from utils import info as utils_info
17 from Symbolic import QuantSpec
18
19
20 from copy import copy, deepcopy
21 import math, random, numpy, scipy, scipy.special
22 from numpy import any
23
24 __all__ = ['RHSfuncSpec', 'ImpFuncSpec', 'ExpFuncSpec', 'FuncSpec',
25 'getSpecFromFile', 'resolveClashingAuxFnPars', 'makePartialJac']
26
27
28
30 """Functional specification of dynamics: abstract class.
31
32 NOTES ON BUILT-IN AUX FUNCTIONS (WITH SYNTAX AS USED IN SPEC STRING):
33
34 globalindepvar(t) -> global independent variable (time) reference
35
36 initcond(varname) -> initial condition of that variable in this DS
37
38 heav(x) = 1 if x > 0, 0 otherwise
39
40 getindex(varname) -> index of varname in internal representation of
41 variables as array
42
43 getbound(name, which_bd) -> value of user-defined bound on the named
44 variable or parameter, either the lower (which_bd=0) or higher
45 (which_bd=1)
46
47 if(condition, expr1, expr2) -> if condition as a function of state,
48 parameters and time is true, then evaluate <expr1>, else evaluate
49 <expr2>.
50
51 MACRO `for` SYNTAX:
52
53 for(i, ilo, ihi, expr_in_i) -> list of expressions where each
54 occurrence of `[i]` is replaced with the appropriate integer.
55 The letter i can be replaced with any other single character.
56
57 MACRO `sum` SYNTAX:
58
59 sum(i, ilo, ihi, expr_in_i) -> an expression that sums
60 over the expression replacing any occurrence of `[i]` with
61 the appropriate integer.
62 """
63
65
66 self._protected_mathnames = protected_mathnames
67 self._protected_randomnames = protected_randomnames
68 self._protected_scipynames = protected_scipynames
69 self._protected_specialfns = protected_specialfns
70
71
72 self._builtin_auxnames = builtin_auxnames
73 self._protected_macronames = protected_macronames
74 self._protected_auxnames = copy(self._builtin_auxnames)
75 self._protected_reusenames = []
76 needKeys = ['name', 'vars']
77 optionalKeys = ['pars', 'inputs', 'varspecs', 'spec', '_for_macro_info',
78 'targetlang', 'fnspecs', 'auxvars', 'reuseterms',
79 'codeinsert_start', 'codeinsert_end', 'ignorespecial']
80 self._initargs = deepcopy(kw)
81
82 try:
83
84 if 'name' in kw:
85 self.name = kw['name']
86 else:
87 self.name = 'untitled'
88
89 if isinstance(kw['vars'], list):
90 vars = kw['vars'][:]
91 else:
92 assert isinstance(kw['vars'], str), 'Invalid variable name'
93 vars = [kw['vars']]
94 except KeyError:
95 raise PyDSTool_KeyError('Necessary keys missing from argument dict')
96 foundKeys = len(needKeys)
97
98
99 if 'pars' in kw:
100 if isinstance(kw['pars'], list):
101 pars = kw['pars'][:]
102 else:
103 assert isinstance(kw['pars'], str), 'Invalid parameter name'
104 pars = [kw['pars']]
105 foundKeys += 1
106 else:
107 pars = []
108
109 if 'inputs' in kw:
110 if isinstance(kw['inputs'], list):
111 inputs = kw['inputs'][:]
112 else:
113 assert isinstance(kw['inputs'], str), 'Invalid input name'
114 inputs = [kw['inputs']]
115 foundKeys += 1
116 else:
117 inputs = []
118 if 'targetlang' in kw:
119 try:
120 tlang = kw['targetlang'].lower()
121 except AttributeError:
122 raise TypeError("Expected string type for target language")
123 if tlang not in targetLangs:
124 raise ValueError('Invalid specification for targetlang')
125 self.targetlang = tlang
126 foundKeys += 1
127 else:
128 self.targetlang = 'python'
129 if self.targetlang == 'c':
130 self._defstr = "#define"
131 self._undefstr = "#undef"
132 else:
133 self._defstr = ""
134 self._undefstr = ""
135 if 'ignorespecial' in kw:
136 self._ignorespecial = kw['ignorespecial']
137 foundKeys += 1
138 else:
139 self._ignorespecial = []
140
141
142 if 'reuseterms' in kw:
143 if isinstance(kw['reuseterms'], dict):
144 self.reuseterms = deepcopy(kw['reuseterms'])
145 else:
146 raise ValueError('reuseterms must be a dictionary of strings ->'
147 ' replacement strings')
148 ignore_list = []
149 for term, repterm in self.reuseterms.iteritems():
150 assert isinstance(term, str), \
151 "terms in 'reuseterms' dictionary must be strings"
152
153
154 if isNumericToken(term):
155
156
157
158 ignore_list.append(term)
159
160
161 if term[0] in '+/*':
162 print "Error in term:", term
163 raise ValueError('terms to be substituted must not begin '
164 'with arithmetic operators')
165 if term[0] == '-':
166 term = '(' + term + ')'
167 if term[-1] in '+-/*':
168 print "Error in term:", term
169 raise ValueError('terms to be substituted must not end with '
170 'arithmetic operators')
171 for s in term:
172 if self.targetlang == 'python':
173 if s in '[]{}~@#$%&\|?^':
174 print "Error in term:", term
175 raise ValueError('terms to be substituted must be '
176 'alphanumeric or contain arithmetic operators '
177 '+ - / *')
178 else:
179 if s in '[]{}~!@#$%&\|?><':
180 print "Error in term:", term
181 raise ValueError('terms to be substituted must be alphanumeric or contain arithmetic operators + - / *')
182 if repterm[0] in num_chars:
183 print "Error in replacement term:", repterm
184 raise ValueError('replacement terms must not begin with numbers')
185 for s in repterm:
186 if s in '+-/*.()[]{}~!@#$%^&\|?><,':
187 print "Error in replacement term:", repterm
188 raise ValueError('replacement terms must be alphanumeric')
189 for t in ignore_list:
190 del self.reuseterms[t]
191 foundKeys += 1
192 else:
193 self.reuseterms = {}
194
195 if 'auxvars' in kw:
196 if isinstance(kw['auxvars'], list):
197 auxvars = kw['auxvars'][:]
198 else:
199 assert isinstance(kw['auxvars'], str), 'Invalid variable name'
200 auxvars = [kw['auxvars']]
201 foundKeys += 1
202 else:
203 auxvars = []
204
205
206
207
208 self.auxfns = {}
209 if 'fnspecs' in kw:
210 self._auxfnspecs = deepcopy(kw['fnspecs'])
211 foundKeys += 1
212 else:
213 self._auxfnspecs = {}
214
215
216 assert 'varspecs' in kw or 'spec' in kw, ("Require a functional "
217 "specification key -- 'spec' or 'varspecs'")
218 if '_for_macro_info' in kw:
219 foundKeys += 1
220 self._varsbyforspec = kw['_for_macro_info'].varsbyforspec
221 else:
222 self._varsbyforspec = {}
223 if 'varspecs' in kw:
224 if auxvars == []:
225 numaux = 0
226 else:
227 numaux = len(auxvars)
228 if '_for_macro_info' in kw:
229 if kw['_for_macro_info'].numfors > 0:
230 num_varspecs = numaux + len(vars) - kw['_for_macro_info'].totforvars + \
231 kw['_for_macro_info'].numfors
232 else:
233 num_varspecs = numaux + len(vars)
234 else:
235 num_varspecs = numaux + len(vars)
236 if len(kw['varspecs']) != len(self._varsbyforspec) and \
237 len(kw['varspecs']) != num_varspecs:
238 print "# state variables: ", len(vars)
239 print "# auxiliary variables: ", numaux
240 print "# of variable specs: ", len(kw['varspecs'])
241 raise ValueError('Incorrect size of varspecs')
242 self.varspecs = deepcopy(kw['varspecs'])
243 foundKeys += 1
244 else:
245 self.varspecs = {}
246 self.codeinserts = {'start': '', 'end': ''}
247 if 'codeinsert_start' in kw:
248 codestr = kw['codeinsert_start']
249 assert isinstance(codestr, str), 'code insert must be a string'
250 if self.targetlang == 'python':
251
252
253 if codestr[:4] != _indentstr:
254 codestr = _indentstr+codestr
255
256 if codestr[-1] != '\n':
257 addnl = '\n'
258 else:
259 addnl = ''
260 self.codeinserts['start'] = codestr+addnl
261 foundKeys += 1
262 if 'codeinsert_end' in kw:
263 codestr = kw['codeinsert_end']
264 assert isinstance(codestr, str), 'code insert must be a string'
265 if self.targetlang == 'python':
266
267
268 assert codestr[:4] == " ", ("First line of inserted "
269 "python code at start of spec was "
270 "wrongly indented")
271
272 if codestr[-1] != '\n':
273 addnl = '\n'
274 else:
275 addnl = ''
276 self.codeinserts['end'] = codestr+addnl
277 foundKeys += 1
278
279
280
281 if 'spec' in kw:
282 if 'varspecs' in kw:
283 raise PyDSTool_KeyError, \
284 "Cannot provide both 'spec' and 'varspecs' keys"
285 assert isinstance(kw['spec'], tuple), ("'spec' must be a pair:"
286 " (spec body, spec name)")
287 assert len(kw['spec'])==2, ("'spec' must be a pair:"
288 " (spec body, spec name)")
289 self.spec = deepcopy(kw['spec'])
290
291
292 self.auxspec = {}
293 if 'dependencies' in kw:
294 self.dependencies = kw['dependencies']
295 else:
296 raise PyDSTool_KeyError("Dependencies must be provided "
297 "explicitly when using 'spec' form of initialization")
298 foundKeys += 2
299 else:
300 self.spec = {}
301 self.auxspec = {}
302 self.dependencies = []
303 if len(kw) > foundKeys:
304 raise PyDSTool_KeyError('Invalid keys passed in argument dict')
305 self.defined = False
306 self.validateDef(vars, pars, inputs, auxvars, self._auxfnspecs.keys())
307
308
309
310 vars.sort()
311 pars.sort()
312 inputs.sort()
313 auxvars.sort()
314 self.vars = vars
315 self.pars = pars
316 self.inputs = inputs
317 self.auxvars = auxvars
318
319
320 self.doPreMacros()
321
322
323
324 self.generateAuxFns()
325 if self.spec == {}:
326 assert self.varspecs != {}, \
327 'No functional specification provided!'
328 self.generateSpec()
329
330 self.validateDependencies(self.dependencies)
331
332
333
334 self.algparams = {}
335 self.defined = True
336
338 """Unique identifier for this specification."""
339 deflist = [self.name, self.targetlang]
340
341 for l in [self.pars, self.vars, self.auxvars, self.inputs,
342 self.spec, self.auxspec]:
343 deflist.append(tuple(l))
344
345 for d in [self.auxfns, self.codeinserts]:
346 deflist.append(tuple(sortedDictItems(d, byvalue=False)))
347 return hash(tuple(deflist))
348
350 if targetlang == self.targetlang:
351
352 return deepcopy(self)
353 fs = FuncSpec.__new__(self.__class__)
354 new_args = deepcopy(self._initargs)
355 if self.codeinserts['start'] != '':
356 del new_args['codeinsert_start']
357 print "Warning: code insert (start) ignored for new target"
358 if self.codeinserts['end'] != '':
359 del new_args['codeinsert_end']
360 print "Warning: code insert (end) ignored for new target"
361 new_args['targetlang'] = targetlang
362 fs.__init__(new_args)
363 return fs
364
365
367
368 utils_info(self.__dict__, "FuncSpec " + self.name)
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392 - def validateDef(self, vars, pars, inputs, auxvars, auxfns):
393 """Validate definition of the functional specification."""
394
395 assert not intersect(vars, pars), 'variable and param names overlap'
396 assert not intersect(vars, inputs), 'variable and input names overlap'
397 assert not intersect(pars, inputs), 'param and input names overlap'
398 assert not intersect(vars, auxfns), ('variable and auxiliary function '
399 'names overlap')
400 assert not intersect(pars, auxfns), ('param and auxiliary function '
401 'names overlap')
402 assert not intersect(inputs, auxfns), ('input and auxiliary function '
403 'names overlap')
404 assert not intersect(vars, auxvars), ('variable and auxiliary variable '
405 'names overlap')
406 assert not intersect(pars, auxvars), ('param and auxiliary variable '
407 'names overlap')
408 assert not intersect(inputs, auxvars), ('input and auxiliary variable '
409 'names overlap')
410
411 assert isUniqueSeq(vars), 'variable names are repeated'
412 assert isUniqueSeq(pars), 'parameter names are repeated'
413 assert isUniqueSeq(inputs), 'input names are repeated'
414 if auxvars != []:
415 assert isUniqueSeq(auxvars), 'auxiliary variable names are repeated'
416 if auxfns != []:
417 assert isUniqueSeq(auxfns), 'auxiliary function names are repeated'
418 allnames = vars+pars+inputs+auxvars
419 allprotectednames = self._protected_mathnames + \
420 self._protected_scipynames + \
421 self._protected_specialfns + \
422 self._protected_randomnames + \
423 self._protected_auxnames + \
424 ['abs', 'min', 'max', 'and', 'or', 'not',
425 'True', 'False']
426
427 assert reduce(bool.__and__, [name_chars_RE.match(n[0]) \
428 is not None for n in allnames]), \
429 ('variable, parameter, and input names must not '
430 'begin with non-alphabetic chars')
431 assert reduce(bool.__and__, [n not in allnames for n in \
432 allprotectednames]), \
433 ('variable, parameter, and input names must not '
434 'overlap with protected math / aux function names')
435
436
437
438
439
441 """Validate the stored dependency pairs for self-consistency."""
442
443
444
445 assert isinstance(dependencies, list), ('dependencies must be a list '
446 'of unique ordered pairs')
447
448
449 for d in dependencies:
450 assert len(d) == 2, 'dependencies must be ordered pairs'
451 i = d[0]
452 o = d[1]
453 firstpos = dependencies.index(d)
454 assert d not in dependencies[firstpos+1:], \
455 'dependency pairs must be unique'
456 assert i in self.vars+self.auxvars, 'unknown variable name in dependencies'
457 assert o in self.vars or o in self.inputs, \
458 'unknown variable name in dependencies'
459
460
461
462
464
465
466 if self.targetlang == 'python':
467 self._genAuxFnPy(pytarget=True)
468 elif self.targetlang == 'c':
469 self._genAuxFnC()
470 self._genAuxFnPy()
471 elif self.targetlang == 'matlab':
472 self._genAuxFnMatlab()
473 self._genAuxFnPy()
474 elif self.targetlang == 'dstool':
475 raise NotImplementedError
476 elif self.targetlang == 'xpp':
477 raise NotImplementedError
478 else:
479 raise ValueError('targetlang attribute must be in '+str(targetLangs))
480
481
483 """Automatically generate callable target-language functions from
484 the user-defined specification strings."""
485 if self.targetlang == 'python':
486 self._genSpecPy()
487 elif self.targetlang == 'c':
488 self._genSpecC()
489 elif self.targetlang == 'matlab':
490 self._genSpecMatlab()
491 elif self.targetlang == 'odetools':
492 raise NotImplementedError
493 elif self.targetlang == 'xpp':
494 raise NotImplementedError
495 else:
496 raise ValueError('targetlang attribute must be in '+str(targetLangs))
497
498
500 """Pre-process any macro spec definitions (e.g. `for` loops)."""
501
502 assert self.varspecs != {}, 'varspecs attribute must be defined'
503 specnames_unsorted = self.varspecs.keys()
504 _vbfs_inv = invertMap(self._varsbyforspec)
505
506 if len(_vbfs_inv) > 0:
507 specname_vars = []
508 specname_auxvars = []
509 for varname in self.vars:
510
511 specname = _vbfs_inv[varname]
512 if specname not in specname_vars:
513 specname_vars.append(specname)
514 for varname in self.auxvars:
515
516 specname = _vbfs_inv[varname]
517 if specname not in specname_auxvars:
518 specname_auxvars.append(specname)
519 else:
520 specname_vars = intersect(self.vars, specnames_unsorted)
521 specname_auxvars = intersect(self.auxvars, specnames_unsorted)
522 specname_vars.sort()
523 specname_auxvars.sort()
524 specnames = specname_vars + specname_auxvars
525 specnames_temp = copy(specnames)
526 for specname in specnames_temp:
527 leftbrack_ix = specname.find('[')
528 rightbrack_ix = specname.find(']')
529 test_sum = leftbrack_ix + rightbrack_ix
530 if test_sum > 0:
531
532 assert rightbrack_ix - leftbrack_ix == 2, ('Misuse of square '
533 'brackets in spec definition. Expected single'
534 ' character between left and right brackets.')
535
536
537
538
539 rootstr = specname[:leftbrack_ix]
540 istr = specname[leftbrack_ix+1]
541 specstr = self.varspecs[specname]
542 assert specstr[:4] == 'for(', ('Expected `for` macro when '
543 'square brackets used in name definition')
544
545 arginfo = readArgs(specstr[3:])
546 if not arginfo[0]:
547 raise ValueError('Error finding '
548 'arguments applicable to `for` '
549 'macro')
550 arglist = arginfo[1]
551 assert len(arglist) == 4, ('Wrong number of arguments passed '
552 'to `for` macro. Expected 4')
553 istr = arglist[0]
554 allnames = self.vars + self.pars + self.inputs + self.auxvars \
555 + self._protected_mathnames \
556 + self._protected_randomnames \
557 + self._protected_auxnames \
558 + self._protected_scipynames \
559 + self._protected_specialfns \
560 + self._protected_macronames \
561 + ['abs', 'and', 'or', 'not', 'True', 'False']
562 assert istr not in allnames, ('loop index in `for` macro '
563 'must not be a reserved name')
564 for ichar in istr:
565 assert name_chars_RE.match(ichar) is not None, \
566 ('loop index in `for` macro '
567 'must be alphanumeric')
568 ilo = int(arglist[1])
569 ihi = int(arglist[2])
570
571 expr = arglist[3]
572
573 varspecs = self._macroFor(rootstr, istr, ilo, ihi, expr)
574 specnames_gen = varspecs.keys()
575
576
577 specnames.remove(specname)
578
579
580
581
582
583
584
585
586
587
588 del(self.varspecs[specname])
589 for sname in specnames_gen:
590 self.varspecs[sname] = varspecs[sname]
591 specnames.append(sname)
592
593
594
595
596 elif test_sum == -2:
597 pass
598
599 else:
600 raise AssertionError('Misuse of square brackets in spec '
601 'definition. Expected single'
602 ' character between left and right brackets.')
603
604
605 - def _macroFor(self, rootstr, istr, ilo, ihi, expr_in_i):
606 """Internal utility function to build multiple instances of expression
607 'expr_in_i' where integer i has been substituted for values from ilo to ihi.
608 Returns dictionary keyed by rootstr+str(i) for each i.
609 """
610
611 retdict = {}
612 q = QuantSpec('__temp__', expr_in_i)
613 eval_pieces = {}
614 avoid_toks = []
615 for ix, tok in enumerate(q):
616 if tok[0] == '[':
617 eval_str = tok[1:-1]
618 if istr in eval_str:
619 eval_pieces[ix] = eval_str
620
621
622 keys = eval_pieces.keys()
623 keys.sort()
624 ranges = remove_indices_from_range(keys, len(q.parser.tokenized)-1)
625
626 pieces = []
627 eval_ixs = []
628 for ri, r in enumerate(ranges):
629 if len(r) == 1:
630 pieces.append(q[r[0]])
631 else:
632
633 pieces.append(''.join(q[r[0]:r[1]]))
634 if ri+1 == len(ranges):
635
636 if len(keys) > 0 and keys[-1] == r[-1]:
637 pieces.append('')
638 eval_ixs.append(len(pieces)-1)
639
640 else:
641
642 pieces.append('')
643 eval_ixs.append(len(pieces)-1)
644 for i in range(ilo, ihi+1):
645 for k, ei in zip(keys, eval_ixs):
646 s = eval_pieces[k].replace(istr, str(i))
647 try:
648 pieces[ei] = str(int(eval(s)))
649 except NameError:
650
651 pieces[ei] = s
652 retdict[rootstr+str(i)] = ''.join(pieces)+'\n'
653 return retdict
654
655 - def _macroSum(self, istr, ilo, ihi, expr_in_i):
656 def_dict = self._macroFor('', istr, int(ilo), int(ihi), expr_in_i)
657 retstr = '(' + "+".join([term.strip() for term in def_dict.values()]) + ')'
658 return retstr
659
660
661
663 if pytarget:
664 assert self.targetlang == 'python', \
665 'Wrong target language for this call'
666 auxnames = self._auxfnspecs.keys()
667
668 uafi = {}
669
670
671
672
673
674
675
676
677
678 auxfns = {}
679 auxfns['globalindepvar'] = \
680 ("def _auxfn_globalindepvar(ds, parsinps, t):\n" \
681 + _indentstr \
682 + "return ds.globalt0 + t", '_auxfn_globalindepvar')
683 auxfns['initcond'] = \
684 ("def _auxfn_initcond(ds, parsinps, varname):\n" \
685 + _indentstr \
686 + "return ds.initialconditions[varname]",'_auxfn_initcond')
687 auxfns['heav'] = \
688 ("def _auxfn_heav(ds, parsinps, x):\n" + _indentstr \
689 + "if x>0:\n" + 2*_indentstr \
690 + "return 1\n" + _indentstr + "else:\n" \
691 + 2*_indentstr + "return 0", '_auxfn_heav')
692 auxfns['if'] = \
693 ("def _auxfn_if(ds, parsinps, c, e1, e2):\n" \
694 + _indentstr + "if c:\n" + 2*_indentstr \
695 + "return e1\n" + _indentstr \
696 + "else:\n" + 2*_indentstr + "return e2", '_auxfn_if')
697 auxfns['getindex'] = \
698 ("def _auxfn_getindex(ds, parsinps, varname):\n" \
699 + _indentstr \
700 + "return ds._var_namemap[varname]", '_auxfn_getindex')
701 auxfns['getbound'] = \
702 ("def _auxfn_getbound(ds, parsinps, name, bd):\n" \
703 + _indentstr + "try:\n" \
704 + 2*_indentstr + "return ds.xdomain[name][bd]\n" \
705 + _indentstr + "except KeyError:\n" + 2*_indentstr \
706 + "try:\n" + 3*_indentstr \
707 + "return ds.pdomain[name][bd]\n" + 2*_indentstr \
708 + "except KeyError, e:\n" + 3*_indentstr \
709 + "print 'Invalid var / par name %s'%name,\n" \
710 + 3*_indentstr + "print 'or bounds not well defined:'\n" \
711 + 3*_indentstr + "print ds.xdomain, ds.pdomain\n" \
712 + 3*_indentstr + "raise (RuntimeError, e)",
713 '_auxfn_getbound')
714
715
716 self._pyauxfns = auxfns
717
718
719 for auxname in auxnames:
720 self._pyauxfns[auxname] = None
721
722
723
724 self._protected_auxnames.extend(['Jacobian','Jacobian_pars'])
725
726
727 protectednames = self.pars + self.inputs \
728 + ['abs', 'pow', 'and', 'or', 'not', 'True', 'False'] \
729 + self._protected_auxnames + auxnames \
730 + self._protected_scipynames + self._protected_specialfns \
731 + self._protected_macronames + self._protected_mathnames \
732 + self._protected_randomnames + self._protected_reusenames
733
734
735 auxfn_namemap = {}
736 specials_base = self.pars + self._protected_auxnames \
737 + ['abs', 'pow', 'and', 'or', 'not', 'True', 'False'] \
738 + auxnames + self._protected_scipynames \
739 + self._protected_specialfns \
740 + self._protected_macronames + self._protected_mathnames \
741 + self._protected_randomnames + self._protected_reusenames
742 for auxname in auxnames:
743 auxinfo = self._auxfnspecs[auxname]
744 try:
745 if len(auxinfo) != 2:
746 raise ValueError('auxinfo tuple must be of length 2')
747 except TypeError:
748 raise TypeError('fnspecs argument must contain pairs')
749
750
751 assert isinstance(auxinfo[0], list), ('aux function arguments '
752 'must be given as a list')
753 assert isinstance(auxinfo[1], str), ('aux function specification '
754 'must be a string '
755 'of the function code')
756
757 if auxname == 'Jacobian':
758 if not compareList(auxinfo[0],['t']+self.vars):
759 print ['t']+self.vars
760 print "Auxinfo =", auxinfo[0]
761 raise ValueError("Invalid argument list given in Jacobian.")
762 auxparlist = ["t","x","parsinps"]
763
764 specials = ["t","x"]
765 auxstr = auxinfo[1]
766 if any([pt in auxstr for pt in ('^', '**')]):
767 auxstr = convertPowers(auxstr, 'pow')
768 specvars = self.vars
769 specvars.sort()
770 specdict = {}.fromkeys(specvars)
771 if len(specvars) == 1:
772 assert '[' not in auxstr, \
773 "'[' character invalid in Jacobian for 1D system"
774 assert ']' not in auxstr, \
775 "']' character invalid in Jacobian for 1D system"
776 specdict[specvars[0]] = auxstr
777 else:
778 specdict = parseMatrixStrToDictStr(auxstr, specvars)
779 reusestr, body_processed_dict = self._processReusedPy(specvars,
780 specdict,
781 specials=specials+specials_base)
782 body_processed = self._specStrParse(specvars,
783 body_processed_dict, 'xjac',
784 specials=specials+specials_base)
785 auxstr_py = self._genSpecFnPy('_auxfn_Jac',
786 reusestr+body_processed,
787 'xjac', specvars)
788
789 m = n = len(specvars)
790 specdict_check = {}.fromkeys(specvars)
791 for specname in specvars:
792 temp = body_processed_dict[specname]
793 specdict_check[specname] = \
794 count_sep(temp.replace("[","").replace("]",""))+1
795 body_processed = ""
796 for row in range(m):
797 if specdict_check[specvars[row]] != n:
798 print "Row %i: "%m, specdict[specvars[row]]
799 print "Found length %i"%specdict_check[specvars[row]]
800 raise ValueError("Jacobian should be %sx%s"%(m,n))
801 elif auxname == 'Jacobian_pars':
802 if not compareList(auxinfo[0],['t']+self.vars):
803 print ['t']+self.vars
804 print "Auxinfo =", auxinfo[0]
805 raise ValueError("Invalid argument list given in Jacobian.")
806 auxparlist = ["t","x","parsinps"]
807
808 specials = ["t","x"]
809 auxstr = auxinfo[1]
810 if any([pt in auxstr for pt in ('^', '**')]):
811 auxstr = convertPowers(auxstr, 'pow')
812 specvars = self.vars
813 specvars.sort()
814 specdict = {}.fromkeys(self.vars)
815 if len(specvars) == len(self.vars) == 1:
816 assert '[' not in auxstr, \
817 "'[' character invalid in Jacobian for 1D system"
818 assert ']' not in auxstr, \
819 "']' character invalid in Jacobian for 1D system"
820 specdict[specvars[0]] = auxstr
821 else:
822 specdict = parseMatrixStrToDictStr(auxstr, self.vars)
823 reusestr, body_processed_dict = self._processReusedPy(self.vars,
824 specdict,
825 specials=specials+specials_base)
826 body_processed = self._specStrParse(self.vars,
827 body_processed_dict, 'pjac',
828 specials=specials+specials_base)
829 auxstr_py = self._genSpecFnPy('_auxfn_Jac_p',
830 reusestr+body_processed,
831 'pjac', self.vars)
832
833 n = len(specvars)
834 m = len(self.vars)
835 specdict_check = {}.fromkeys(self.vars)
836 for specname in self.vars:
837 temp = body_processed_dict[specname]
838 specdict_check[specname] = \
839 count_sep(temp.replace("[","").replace("]",""))+1
840 body_processed = ""
841 for row in range(m):
842 try:
843 if specdict_check[self.vars[row]] != n:
844 print "Row %i: "%m, specdict[self.vars[row]]
845 print "Found length %i"%specdict_check[self.vars[row]]
846 raise ValueError("Jacobian w.r.t. pars should be %sx%s"%(m,n))
847 except IndexError:
848 print "\nFound:\n"
849 info(specdict)
850 raise ValueError("Jacobian w.r.t. pars should be %sx%s"%(m,n))
851 elif auxname == 'massMatrix':
852 if not compareList(auxinfo[0],['t']+self.vars):
853 print ['t']+self.vars
854 print "Auxinfo =", auxinfo[0]
855 raise ValueError("Invalid argument list given in Mass Matrix.")
856 auxparlist = ["t","x","parsinps"]
857
858 specials = ["t","x"]
859 auxstr = auxinfo[1]
860 if any([pt in auxstr for pt in ('^', '**')]):
861 auxstr = convertPowers(auxstr, 'pow')
862 specvars = self.vars
863 specvars.sort()
864 specdict = {}.fromkeys(specvars)
865 if len(specvars) == 1:
866 assert '[' not in auxstr, \
867 "'[' character invalid in mass matrix for 1D system"
868 assert ']' not in auxstr, \
869 "']' character invalid in mass matrix for 1D system"
870 specdict[specvars.values()[0]] = auxstr
871 else:
872 specdict = parseMatrixStrToDictStr(auxstr, specvars)
873 reusestr, body_processed_dict = self._processReusedPy(specvars,
874 specdict,
875 specials=specials+specials_base)
876 body_processed = self._specStrParse(specvars,
877 body_processed_dict, 'xmat',
878 specials=specials+specials_base)
879 auxstr_py = self._genSpecFnPy('_auxfn_massMatrix',
880 reusestr+body_processed,
881 'xmat', specvars)
882
883 m = n = len(specvars)
884 specdict_check = {}.fromkeys(specvars)
885 for specname in specvars:
886 specdict_check[specname] = 1 + \
887 count_sep(body_processed_dict[specname].replace("[","").replace("]",""))
888 body_processed = ""
889 for row in range(m):
890 if specdict_check[specvars[row]] != n:
891 print "Row %i: "%m, specdict[specvars[row]]
892 print "Found length %i"%specdict_check[specvars[row]]
893 raise ValueError("Mass matrix should be %sx%s"%(m,n))
894 else:
895 user_parstr = makeParList(auxinfo[0])
896
897
898 if user_parstr == '':
899
900 auxparstr = 'parsinps'
901 else:
902 auxparstr = 'parsinps, ' + user_parstr
903 auxstr_py = 'def _auxfn_' + auxname + '(ds, ' + auxparstr \
904 +'):\n'
905 auxparlist = auxparstr.replace(" ","").split(",")
906 badparnames = intersect(auxparlist,
907 remain(protectednames,auxnames))
908 if badparnames != []:
909 print "Bad parameter names in auxiliary function", \
910 auxname, ":", badparnames
911
912
913 raise ValueError("Cannot use protected names for auxiliary "
914 "function parameters")
915
916 specials = auxparlist
917 specials.remove('parsinps')
918 illegalterms = remain(self.vars + self.auxvars, specials)
919 auxstr = auxinfo[1]
920 if any([pt in auxstr for pt in ('^', '**')]):
921 auxstr = convertPowers(auxstr, 'pow')
922 reusestr, body_processed_dict = self._processReusedPy([auxname],
923 {auxname:auxstr},
924 specials=specials+specials_base,
925 dovars=False,
926 illegal=illegalterms)
927 body_processed = self._specStrParse([auxname],
928 body_processed_dict,
929 specials=specials+specials_base,
930 dovars=False,
931 noreturndefs=True,
932 illegal=illegalterms)
933 auxstr_py += reusestr + _indentstr + 'return ' \
934 + body_processed
935
936 try:
937 auxfns[auxname] = makeUniqueFn(auxstr_py)
938
939 except:
940 print 'Error in supplied auxiliary spec dictionary code'
941 raise
942 auxfn_namemap['ds.'+auxname] = 'ds.'+auxfns[auxname][1]
943
944 if specials == [''] or specials == []:
945 fn_args = ''
946 else:
947 fn_args = ','+','.join(specials)
948 fn_elts = ['def ', auxname, '(self', fn_args,
949 ',__parsinps__=None):\n\t', 'if __parsinps__ is None:\n\t\t',
950 '__parsinps__=self.map_ixs(self.genref)\n\t',
951 'return self.genref.', auxfns[auxname][1],
952 '(__parsinps__', fn_args, ')\n']
953 uafi[auxname] = ''.join(fn_elts)
954
955 for auxname, auxspec in auxfns.iteritems():
956 dummyQ = QuantSpec('dummy', auxspec[0], preserveSpace=True,
957 treatMultiRefs=False)
958 dummyQ.mapNames(auxfn_namemap)
959 auxfns[auxname] = (dummyQ(), auxspec[1])
960 if pytarget:
961 self.auxfns = auxfns
962
963
964
965
966
967
968 self._user_auxfn_interface = uafi
969 self._protected_auxnames.extend(auxnames)
970
971
972
973 - def _genSpecFnPy(self, name, specstr, resname, specnames,
974 docodeinserts=False):
975
976 retstr = 'def '+name+'(ds, t, x, parsinps):\n'
977
978
979 lstart = len(self.codeinserts['start'])
980 lend = len(self.codeinserts['end'])
981 if docodeinserts:
982 if lstart>0:
983 start_code = self._specStrParse(['inserts'],
984 {'inserts':self.codeinserts['start']}, '',
985 noreturndefs=True, ignoreothers=True,
986 doing_inserts=True)
987 else:
988 start_code = ''
989 if lend > 0:
990 end_code = self._specStrParse(['inserts'],
991 {'inserts':self.codeinserts['end']}, '',
992 noreturndefs=True, ignoreothers=True,
993 doing_inserts=True)
994 else:
995 end_code = ''
996 else:
997 start_code = end_code = ''
998 retstr += start_code + specstr + end_code
999
1000 if len(specnames) == 1:
1001 retstr += _indentstr + 'return array([' + resname + '0])\n'
1002 else:
1003 retstr += _indentstr + 'return array([' \
1004 + makeParList(range(len(specnames)), resname) + '])\n'
1005 return retstr
1006
1007
1009 assert self.targetlang == 'python', ('Wrong target language for this'
1010 ' call')
1011 assert self.varspecs != {}, 'varspecs attribute must be defined'
1012 specnames_unsorted = self.varspecs.keys()
1013 _vbfs_inv = invertMap(self._varsbyforspec)
1014
1015 if len(_vbfs_inv) > 0:
1016 specname_vars = []
1017 specname_auxvars = []
1018 for varname in self.vars:
1019
1020 specname = _vbfs_inv[varname]
1021 if varname not in specname_vars:
1022 specname_vars.append(varname)
1023 for varname in self.auxvars:
1024
1025 specname = _vbfs_inv[varname]
1026 if varname not in specname_auxvars:
1027 specname_auxvars.append(varname)
1028 else:
1029 specname_vars = intersect(self.vars, specnames_unsorted)
1030 specname_auxvars = intersect(self.auxvars, specnames_unsorted)
1031 specname_vars.sort()
1032 for vn, vs in self.varspecs.items():
1033 if any([pt in vs for pt in ('^', '**')]):
1034 self.varspecs[vn] = convertPowers(vs, 'pow')
1035 self.vars.sort()
1036 reusestr, specupdated = self._processReusedPy(specname_vars,
1037 self.varspecs)
1038 self.varspecs.update(specupdated)
1039 temp = self._specStrParse(specname_vars, self.varspecs, 'xnew')
1040 specstr_py = self._genSpecFnPy('_specfn', reusestr+temp, 'xnew',
1041 specname_vars, docodeinserts=True)
1042
1043 specname_auxvars.sort()
1044 assert self.auxvars == specname_auxvars, \
1045 ('Mismatch between declared auxiliary'
1046 ' variable names and varspecs keys')
1047 reusestraux, specupdated = self._processReusedPy(specname_auxvars,
1048 self.varspecs)
1049 self.varspecs.update(specupdated)
1050 tempaux = self._specStrParse(specname_auxvars, self.varspecs, 'auxvals')
1051 auxspecstr_py = self._genSpecFnPy('_auxspecfn', reusestraux+tempaux,
1052 'auxvals', specname_auxvars)
1053 try:
1054 spec_info = makeUniqueFn(specstr_py)
1055 except SyntaxError:
1056 print "Syntax error in specification:\n", specstr_py
1057 raise
1058 try:
1059 auxspec_info = makeUniqueFn(auxspecstr_py)
1060 except SyntaxError:
1061 print "Syntax error in auxiliary spec:\n", auxspecstr_py
1062 raise
1063 self.spec = spec_info
1064 self.auxspec = auxspec_info
1065
1066
1067 - def _processReusedPy(self, specnames, specdict, specials=[],
1068 dovars=True, dopars=True, doinps=True, illegal=[]):
1069 """Process reused subexpression terms for Python code."""
1070
1071 reused, specupdated, new_protected, order = _processReused(specnames,
1072 specdict,
1073 self.reuseterms,
1074 _indentstr)
1075 self._protected_reusenames = new_protected
1076
1077 reusedParsed = self._parseReusedTermsPy(reused, [2,4],
1078 specials=specials, dovars=dovars,
1079 dopars=dopars, doinps=doinps,
1080 illegal=illegal)
1081 reusedefs = {}.fromkeys(new_protected)
1082 for vname, deflist in reusedParsed.iteritems():
1083 for d in deflist:
1084 reusedefs[d[2]] = d
1085 return (concatStrDict(reusedefs, intersect(order,reusedefs.keys())),
1086 specupdated)
1087
1088
1089 - def _parseReusedTermsPy(self, d, symbol_ixs, specials=[],
1090 dovars=True, dopars=True, doinps=True, illegal=[]):
1091 """Process dictionary of reused term definitions (in spec syntax)."""
1092
1093
1094
1095 allnames = self.vars + self.pars + self.inputs + self.auxvars \
1096 + ['abs'] + self._protected_auxnames \
1097 + self._protected_scipynames + self._protected_specialfns \
1098 + self._protected_macronames + self._protected_mathnames \
1099 + self._protected_randomnames + self._protected_reusenames
1100 allnames = remain(allnames, illegal)
1101 if dovars:
1102 var_arrayixstr = dict(zip(self.vars, map(lambda i: str(i), \
1103 range(len(self.vars))) ))
1104 aux_arrayixstr = dict(zip(self.auxvars, map(lambda i: str(i), \
1105 range(len(self.auxvars))) ))
1106 else:
1107 var_arrayixstr = {}
1108 aux_arrayixstr = {}
1109 if dopars:
1110 if doinps:
1111
1112
1113 parsinps_names = self.pars+self.inputs
1114 else:
1115 parsinps_names = self.pars
1116 parsinps_arrayixstr = dict(zip(parsinps_names,
1117 map(lambda i: str(i), \
1118 range(len(parsinps_names))) ))
1119 else:
1120 parsinps_names = []
1121 parsinps_arrayixstr = {}
1122 specialtokens = remain(allnames,specials) + ['(', 't'] + specials
1123 for specname, itemlist in d.iteritems():
1124 listix = -1
1125 for strlist in itemlist:
1126 listix += 1
1127 if strlist == []:
1128 continue
1129 if len(strlist) < max(symbol_ixs):
1130 raise ValueError("Symbol indices out of range in "
1131 "call to _parseReusedTermsPy")
1132 for ix in symbol_ixs:
1133 symbol = strlist[ix]
1134 parsedsymbol = self.__processTokens(allnames,
1135 specialtokens, symbol,
1136 var_arrayixstr, aux_arrayixstr,
1137 parsinps_names, parsinps_arrayixstr,
1138 specname)
1139
1140 d[specname][listix][ix] = parsedsymbol.strip()
1141 return d
1142
1143
1144 - def _specStrParse(self, specnames, specdict, resname='', specials=[],
1145 dovars=True, dopars=True, doinps=True,
1146 noreturndefs=False, forexternal=False, illegal=[],
1147 ignoreothers=False, doing_inserts=False):
1148
1149
1150
1151 assert isinstance(specnames, list), "specnames must be a list"
1152 if noreturndefs or forexternal:
1153 assert len(specnames) == 1, ("can only pass a single specname for "
1154 "'forexternal' or 'noreturndefs' options")
1155 allnames = self.vars + self.pars + self.inputs + self.auxvars \
1156 + ['abs', 'and', 'or', 'not', 'True', 'False'] \
1157 + self._protected_auxnames \
1158 + self._protected_scipynames + self._protected_specialfns \
1159 + self._protected_macronames + self._protected_mathnames \
1160 + self._protected_randomnames + self._protected_reusenames
1161 allnames = remain(allnames, illegal)
1162 if dovars:
1163 if forexternal:
1164 var_arrayixstr = dict(zip(self.vars,
1165 ["'"+v+"'" for v in self.vars]))
1166 aux_arrayixstr = dict(zip(self.auxvars,
1167 ["'"+v+"'" for v in self.auxvars]))
1168 else:
1169 var_arrayixstr = dict(zip(self.vars, map(lambda i: str(i), \
1170 range(len(self.vars))) ))
1171 aux_arrayixstr = dict(zip(self.auxvars, map(lambda i: str(i),\
1172 range(len(self.auxvars))) ))
1173 else:
1174 var_arrayixstr = {}
1175 aux_arrayixstr = {}
1176
1177
1178
1179 if dopars:
1180 if forexternal:
1181 if doinps:
1182
1183
1184 parsinps_names = self.pars+self.inputs
1185 else:
1186 parsinps_names = self.pars
1187
1188 parsinps_arrayixstr = dict(zip(parsinps_names,
1189 ["'"+pn+"'" for pn in parsinps_names]))
1190 else:
1191 if doinps:
1192
1193
1194 parsinps_names = self.pars+self.inputs
1195 else:
1196 parsinps_names = self.pars
1197 parsinps_arrayixstr = dict(zip(parsinps_names,
1198 map(lambda i: str(i), \
1199 range(len(parsinps_names))) ))
1200 else:
1201 parsinps_names = []
1202 parsinps_arrayixstr = {}
1203 specialtokens = remain(allnames,specials) + ['(', 't'] \
1204 + remain(specials,['t'])
1205 specstr_lang = ''
1206 specname_count = 0
1207 for specname in specnames:
1208 specstr = specdict[specname]
1209 assert type(specstr)==str, "Specification for %s was not a string"%specname
1210 if not noreturndefs:
1211 specstr_lang += _indentstr + resname+str(specname_count)+' = '
1212 specname_count += 1
1213 specstr_lang += self.__processTokens(allnames, specialtokens,
1214 specstr, var_arrayixstr,
1215 aux_arrayixstr, parsinps_names,
1216 parsinps_arrayixstr, specname, ignoreothers,
1217 doing_inserts)
1218 if not noreturndefs or not forexternal:
1219 specstr_lang += '\n'
1220 return specstr_lang
1221
1222
1223 - def __processTokens(self, allnames, specialtokens, specstr,
1224 var_arrayixstr, aux_arrayixstr, parsinps_names,
1225 parsinps_arrayixstr, specname, ignoreothers=False,
1226 doing_inserts=False):
1227
1228
1229
1230
1231
1232
1233 returnstr = ''
1234 if specstr[-1] != ')':
1235
1236
1237 specstr += ' '
1238 scount = 0
1239 speclen = len(specstr)
1240 valid_depnames = self.vars+self.auxvars
1241 s = ''
1242 ignore_list = ['', ' ', '\n'] + allnames
1243 foundtoken = False
1244
1245
1246 strname_arg_imminent = False
1247 auxfn_args_imminent = False
1248 while scount < speclen:
1249 stemp = specstr[scount]
1250 scount += 1
1251 if name_chars_RE.match(stemp) is None:
1252
1253
1254
1255
1256 if not ignoreothers and s not in ignore_list:
1257
1258
1259
1260 print "Error in specification `" + specname + \
1261 "` with token `"+s+"` :\n", specstr
1262 raise ValueError('Undeclared or illegal token `'+s+'` in'
1263 ' spec string `'+specname+'`')
1264 if stemp == '^' and self.targetlang == 'python':
1265 raise ValueError('Character `^` is not allowed. '
1266 'Please use the pow() call')
1267 if stemp == '(':
1268 returnstr += s
1269 s = stemp
1270 else:
1271 returnstr += s
1272 if len(returnstr)>1 and stemp == returnstr[-1] == "*":
1273
1274 raise ValueError('Operator ** is not allowed. '
1275 'Please use the pow() call')
1276 returnstr += stemp
1277 s = ''
1278 continue
1279 else:
1280 if s == '' and stemp not in num_chars:
1281 s += stemp
1282 elif s != '':
1283 s += stemp
1284 else:
1285 returnstr += stemp
1286 continue
1287 if s in specialtokens + self._ignorespecial:
1288 if s != '(':
1289 if scount < speclen - 1:
1290 if name_chars_RE.match(specstr[scount]) is None:
1291 foundtoken = True
1292 else:
1293 if s in ['e','E'] and \
1294 name_chars_RE.match(specstr[scount]).group() \
1295 in num_chars+['-']:
1296
1297
1298 foundtoken = True
1299 else:
1300 foundtoken = True
1301 else:
1302 foundtoken = True
1303 if foundtoken:
1304 if s == '(':
1305 if auxfn_args_imminent:
1306 returnstr += s+'parsinps, '
1307 auxfn_args_imminent = False
1308 else:
1309 returnstr += s
1310 elif s == 'abs':
1311 returnstr += s
1312 elif s in var_arrayixstr and \
1313 (len(returnstr)==0 or len(returnstr)>0 and \
1314 returnstr[-1] not in ["'", '"']):
1315 if strname_arg_imminent:
1316 returnstr += "'"+s+"'"
1317 strname_arg_imminent = False
1318 else:
1319 if specname in valid_depnames \
1320 and (specname, s) not in self.dependencies:
1321 self.dependencies.append((specname,s))
1322 returnstr += 'x['+var_arrayixstr[s]+']'
1323 elif s in aux_arrayixstr:
1324 if strname_arg_imminent:
1325 returnstr += "'"+s+"'"
1326 strname_arg_imminent = False
1327 else:
1328 print "Spec name:", specname
1329 print "Spec string:", specstr
1330 print "Problem symbol:", s
1331 raise NameError('auxiliary variables cannot '
1332 'appear on any right-hand side '
1333 'except their initial value')
1334 elif s in parsinps_arrayixstr and \
1335 (len(returnstr)==0 or len(returnstr)>0 and \
1336 returnstr[-1] not in ["'", '"']):
1337 if s in self.inputs:
1338 if specname in valid_depnames and \
1339 (specname, s) not in self.dependencies:
1340 self.dependencies.append((specname,s))
1341 if strname_arg_imminent:
1342 returnstr += "'"+s+"'"
1343 strname_arg_imminent = False
1344 else:
1345 returnstr += 'parsinps[' + \
1346 parsinps_arrayixstr[s] + ']'
1347 elif s in self._protected_mathnames:
1348 if s in ['e','E']:
1349
1350
1351 if len(returnstr)>0:
1352 if returnstr[-1] not in num_chars+['.']:
1353 returnstr += 'math.'+s.lower()
1354 else:
1355 returnstr += s
1356 else:
1357 returnstr += 'math.'+s.lower()
1358 else:
1359 returnstr += 'math.'+s
1360 elif s in self._protected_randomnames:
1361 if len(returnstr) > 0:
1362 if returnstr[-1] == '.':
1363
1364
1365 returnstr += s
1366 else:
1367 returnstr += 'random.'+s
1368 else:
1369 returnstr += 'random.'+s
1370 elif s in self._protected_scipynames:
1371 if len(returnstr) > 0:
1372 if returnstr[-1] == '.':
1373
1374
1375 returnstr += s
1376 else:
1377 returnstr += 'scipy.'+s
1378 else:
1379 returnstr += 'scipy.'+s
1380 elif s in self._protected_specialfns:
1381 if self.targetlang != 'python':
1382 print "Function %s is currently not supported "%s, \
1383 "outside of python target language definitions"
1384 raise ValueError("Invalid special function for "
1385 "non-python target definition")
1386
1387
1388 returnstr += 'scipy.'+s.replace('_','.')
1389 elif s in self._protected_macronames:
1390 if doing_inserts:
1391
1392
1393 returnstr += s
1394 else:
1395 if specname in self._pyauxfns:
1396
1397 to_remove = self.vars + self.auxvars + self.inputs
1398 filtfunc = lambda n: n not in to_remove
1399 specialtokens_temp = filter(filtfunc,
1400 specialtokens+self._ignorespecial)
1401 else:
1402 specialtokens_temp = specialtokens+self._ignorespecial
1403 if s == 'if':
1404
1405
1406 endargbrace = findEndBrace(specstr[scount:]) \
1407 + scount + 1
1408 argstr = specstr[scount:endargbrace]
1409 procstr = self.__processTokens(allnames,
1410 specialtokens_temp, argstr,
1411 var_arrayixstr,
1412 aux_arrayixstr, parsinps_names,
1413 parsinps_arrayixstr, specname)
1414 arginfo = readArgs(procstr)
1415 if not arginfo[0]:
1416 raise ValueError('Error finding '
1417 'arguments applicable to `if` '
1418 'macro')
1419
1420
1421
1422 scount += len(argstr)
1423 arglist = arginfo[1]
1424 assert len(arglist) == 3, ('Wrong number of'
1425 ' arguments passed to `if`'
1426 ' macro. Expected 3')
1427 returnstr += 'ds.' + self._pyauxfns[s][1] + \
1428 '(parsinps, '+procstr[1:]
1429 elif s == 'for':
1430 raise ValueError('Macro '+s+' cannot '
1431 'be used here')
1432 elif s == 'sum':
1433 endargbrace = findEndBrace(specstr[scount:]) \
1434 + scount + 1
1435 argstr = specstr[scount:endargbrace]
1436 arginfo = readArgs(argstr)
1437 if not arginfo[0]:
1438 raise ValueError('Error finding '
1439 'arguments applicable to `sum` '
1440 'macro')
1441 arglist = arginfo[1]
1442 assert len(arglist) == 4, ('Wrong number of'
1443 ' arguments passed to `sum`'
1444 ' macro. Expected 4')
1445
1446
1447
1448 scount += len(argstr)
1449
1450 returnstr += self.__processTokens(allnames,
1451 specialtokens_temp,
1452 self._macroSum(*arglist), var_arrayixstr,
1453 aux_arrayixstr, parsinps_names,
1454 parsinps_arrayixstr, specname)
1455 else:
1456
1457 returnstr += s
1458 elif s in self._protected_auxnames:
1459 if s in ['initcond', 'getbound']:
1460
1461
1462
1463 strname_arg_imminent = True
1464
1465
1466 returnstr += 'ds.' + self._pyauxfns[s][1]
1467 auxfn_args_imminent = True
1468 elif s in self._pyauxfns:
1469
1470
1471
1472
1473
1474 if s in ['initcond', 'getbound']:
1475
1476
1477
1478 strname_arg_imminent = True
1479
1480
1481 returnstr += 'ds.' + s
1482 auxfn_args_imminent = True
1483 elif s in self._protected_reusenames:
1484 returnstr += s
1485 else:
1486
1487
1488 returnstr += s
1489
1490 s = ''
1491 foundtoken = False
1492
1493 return returnstr
1494
1495
1496
1497
1499 """Process reused subexpression terms for C code."""
1500
1501 if self.auxfns:
1502 def addParToCall(s):
1503 return addArgToCalls(self._processSpecialC(s),
1504 self.auxfns.keys(), "p_, wk_, xv_")
1505 parseFunc = addParToCall
1506 else:
1507 parseFunc = self._processSpecialC
1508 reused, specupdated, new_protected, order = _processReused(specnames,
1509 specdict,
1510 self.reuseterms,
1511 '', 'double', ';',
1512 parseFunc)
1513 self._protected_reusenames = new_protected
1514 reusedefs = {}.fromkeys(new_protected)
1515 for vname, deflist in reused.iteritems():
1516 for d in deflist:
1517 reusedefs[d[2]] = d
1518 return (concatStrDict(reusedefs, intersect(order, reusedefs.keys())),
1519 specupdated)
1520
1521
1523 auxnames = self._auxfnspecs.keys()
1524
1525
1526
1527 vnames = self.vars
1528 pnames = self.pars
1529 vnames.sort()
1530 pnames.sort()
1531 for auxname in auxnames:
1532 assert auxname not in ['auxvars', 'vfieldfunc'], \
1533 ("auxiliary function name '" +auxname+ "' clashes with internal"
1534 " names")
1535
1536
1537
1538
1539
1540
1541 for auxname in auxnames:
1542 auxspec = self._auxfnspecs[auxname]
1543 assert len(auxspec) == 2, 'auxspec tuple must be of length 2'
1544 if not isinstance(auxspec[0], list):
1545 print "Found type ", type(auxspec[0])
1546 print "Containing: ", auxspec[0]
1547 raise TypeError('aux function arguments '
1548 'must be given as a list')
1549 if not isinstance(auxspec[1], str):
1550 print "Found type ", type(auxspec[1])
1551 print "Containing: ", auxspec[1]
1552 raise TypeError('aux function specification '
1553 'must be a string of the function code')
1554
1555 if auxname == 'Jacobian':
1556 sig = "void jacobian("
1557 if not compareList(auxspec[0],['t']+self.vars):
1558 print ['t']+self.vars
1559 print "Auxspec =", auxspec[0]
1560 raise ValueError("Invalid argument list given in Jacobian.")
1561 if any([pt in auxspec[1] for pt in ('^', '**')]):
1562 auxstr = convertPowers(auxspec[1], 'pow')
1563 else:
1564 auxstr = auxspec[1]
1565 parlist = "unsigned n_, unsigned np_, double t, double *Y_,"
1566 ismat = True
1567 sig += parlist + " double *p_, double **f_, unsigned wkn_, double *wk_, unsigned xvn_, double *xv_)"
1568 specvars = self.vars
1569 specvars.sort()
1570 n = len(specvars)
1571 m = n
1572 specdict_temp = {}.fromkeys(specvars)
1573 if m == 1:
1574 assert '[' not in auxstr, \
1575 "'[' character invalid in Jacobian for 1D system"
1576 assert ']' not in auxstr, \
1577 "']' character invalid in Jacobian for 1D system"
1578 specdict_temp[specvars[0]] = auxstr
1579 else:
1580 specdict_temp = parseMatrixStrToDictStr(auxstr, specvars)
1581 reusestr, body_processed_dict = self._processReusedC(specvars,
1582 specdict_temp)
1583 specdict = {}.fromkeys(specvars)
1584 for specname in specvars:
1585 temp = body_processed_dict[specname]
1586 specdict[specname] = splitargs(temp.replace("[","").replace("]",""))
1587 body_processed = ""
1588
1589 for col in range(n):
1590 for row in range(m):
1591 try:
1592 body_processed += "f_[" + str(col) + "][" + str(row) \
1593 + "] = " + specdict[specvars[row]][col] + ";\n"
1594 except IndexError:
1595 raise ValueError("Jacobian should be %sx%s"%(m,n))
1596 body_processed += "\n"
1597 auxspec_processedDict = {auxname: body_processed}
1598 elif auxname == 'Jacobian_pars':
1599 sig = "void jacobianParam("
1600 if not compareList(auxspec[0],['t']+self.vars):
1601 print ['t']+self.vars
1602 print "Auxspec =", auxspec[0]
1603 raise ValueError("Invalid argument list given in Jacobian.")
1604 parlist = "unsigned n_, unsigned np_, double t, double *Y_,"
1605 if any([pt in auxspec[1] for pt in ('^', '**')]):
1606 auxstr = convertPowers(auxspec[1], 'pow')
1607 else:
1608 auxstr = auxspec[1]
1609 ismat = True
1610
1611 sig += parlist + " double *p_, double **f_, unsigned wkn_, double *wk_, unsigned xvn_, double *xv_)"
1612 specvars = self.vars
1613 specvars.sort()
1614 n = len(specvars)
1615 if n == 0:
1616 raise ValueError("Cannot have a Jacobian w.r.t. pars"
1617 " because no pars are defined")
1618 m = len(self.vars)
1619 specdict_temp = {}.fromkeys(self.vars)
1620 if m == n == 1:
1621 assert '[' not in auxstr, \
1622 "'[' character invalid in Jacobian for 1D system"
1623 assert ']' not in auxstr, \
1624 "']' character invalid in Jacobian for 1D system"
1625 specdict_temp[self.vars.values()[0]] = auxstr
1626 else:
1627 specdict_temp = parseMatrixStrToDictStr(auxstr, self.vars, m)
1628 reusestr, body_processed_dict = self._processReusedC(self.vars,
1629 specdict_temp)
1630 specdict = {}.fromkeys(self.vars)
1631 for specname in self.vars:
1632 temp = body_processed_dict[specname]
1633 specdict[specname] = splitargs(temp.replace("[","").replace("]",""))
1634 body_processed = ""
1635
1636 for col in range(n):
1637 for row in range(m):
1638 try:
1639 body_processed += "f_[" + str(col) + "][" + str(row) \
1640 + "] = " + specdict[self.vars[row]][col] + ";\n"
1641 except (IndexError, KeyError):
1642 print "\nFound matrix:\n"
1643 info(specdict)
1644 raise ValueError("Jacobian should be %sx%s"%(m,n))
1645 body_processed += "\n"
1646 auxspec_processedDict = {auxname: body_processed}
1647 elif auxname == 'massMatrix':
1648 sig = "void massMatrix("
1649 if not compareList(auxspec[0],['t']+self.vars):
1650 raise ValueError("Invalid argument list given in Mass Matrix.")
1651 if any([pt in auxspec[1] for pt in ('^', '**')]):
1652 auxstr = convertPowers(auxspec[1], 'pow')
1653 else:
1654 auxstr = auxspec[1]
1655 parlist = "unsigned n_, unsigned np_,"
1656 ismat = True
1657
1658 sig += parlist + " double t, double *Y_, double *p_, double **f_, unsigned wkn_, double *wk_, unsigned xvn_, double *xv_)"
1659 specvars = self.vars
1660 specvars.sort()
1661 n = len(specvars)
1662 m = n
1663 specdict_temp = {}.fromkeys(specvars)
1664 if m == 1:
1665 assert '[' not in auxstr, \
1666 "'[' character invalid in mass matrix for 1D system"
1667 assert ']' not in auxstr, \
1668 "']' character invalid in mass matrix for 1D system"
1669 specdict_temp[specvars.values()[0]] = auxstr
1670 else:
1671 specdict_temp = parseMatrixStrToDictStr(auxstr, specvars, m)
1672 reusestr, body_processed_dict = self._processReusedC(specvars,
1673 specdict_temp)
1674 specdict = {}.fromkeys(specvars)
1675 for specname in specvars:
1676 temp = body_processed_dict[specname].replace("[","").replace("]","")
1677 specdict[specname] = splitargs(temp)
1678 body_processed = ""
1679
1680 for col in range(n):
1681 for row in range(m):
1682 try:
1683 body_processed += "f_[" + str(col) + "][" + str(row) \
1684 + "] = " + specdict[specvars[row]][col] + ";\n"
1685 except KeyError:
1686 raise ValueError("Mass matrix should be %sx%s"%(m,n))
1687 body_processed += "\n"
1688 auxspec_processedDict = {auxname: body_processed}
1689 else:
1690 ismat = False
1691 sig = "double " + auxname + "("
1692 parlist = ""
1693 namemap = {}
1694 for parname in auxspec[0]:
1695 if parname == '':
1696 continue
1697 parlist += "double " + "__" + parname + "__, "
1698 namemap[parname] = '__'+parname+'__'
1699 sig += parlist + "double *p_, double *wk_, double *xv_)"
1700 auxstr = auxspec[1]
1701 if any([pt in auxspec[1] for pt in ('^', '**')]):
1702 auxstr = convertPowers(auxstr, 'pow')
1703 prep_auxstr = self._processSpecialC(auxstr)
1704 prep_auxstr_quant = QuantSpec('prep_q',
1705 prep_auxstr.replace(' ','').replace('\n',''),
1706 treatMultiRefs=False, preserveSpace=True)
1707
1708
1709
1710 prep_auxstr_quant.mapNames(namemap)
1711 auxspec = (auxspec[0], prep_auxstr_quant())
1712 reusestr, auxspec_processedDict = self._processReusedC([auxname],
1713 {auxname:auxspec[1]})
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737 dummyQ = QuantSpec('dummy', auxspec_processedDict[auxname],
1738 treatMultiRefs=False, preserveSpace=True)
1739 body_processed = "return "*(not ismat) + dummyQ() + ";\n\n"
1740
1741 auxspecstr = sig + " {\n\n" \
1742 + "\n" + (len(reusestr)>0)*"/* reused term definitions */\n" \
1743 + reusestr + (len(reusestr)>0)*"\n" + body_processed \
1744 + "}"
1745
1746
1747
1748 self.auxfns[auxname] = (auxspecstr, sig)
1749
1750 self.auxfns['heav'] = ("int heav(double x_, double *p_, double *wk_, double *xv_) {\n" \
1751 + " if (x_>0.0) {return 1;} else {return 0;}\n}",
1752 "int heav(double x_, double *p_, double *wk_, double *xv_)")
1753 self.auxfns['__rhs_if'] = ("double __rhs_if(int cond_, double e1_, " \
1754 + "double e2_, double *p_, double *wk_, double *xv_) {\n" \
1755 + " if (cond_) {return e1_;} else {return e2_;};\n}",
1756 "double __rhs_if(int cond_, double e1_, double e2_, double *p_, double *wk_, double *xv_)")
1757 self.auxfns['__maxof2'] = ("double __maxof2(double e1_, double e2_, double *p_, double *wk_, double *xv_) {\n" \
1758 + "if (e1_ > e2_) {return e1_;} else {return e2_;};\n}",
1759 "double __maxof2(double e1_, double e2_, double *p_, double *wk_, double *xv_)")
1760 self.auxfns['__minof2'] = ("double __minof2(double e1_, double e2_, double *p_, double *wk_, double *xv_) {\n" \
1761 + "if (e1_ < e2_) {return e1_;} else {return e2_;};\n}",
1762 "double __minof2(double e1_, double e2_, double *p_, double *wk_, double *xv_)")
1763 self.auxfns['__maxof3'] = ("double __maxof3(double e1_, double e2_, double e3_, double *p_, double *wk_, double *xv_) {\n" \
1764 + "double temp_;\nif (e1_ > e2_) {temp_ = e1_;} else {temp_ = e2_;};\n" \
1765 + "if (e3_ > temp_) {return e3_;} else {return temp_;};\n}",
1766 "double __maxof3(double e1_, double e2_, double e3_, double *p_, double *wk_, double *xv_)")
1767 self.auxfns['__minof3'] = ("double __minof3(double e1_, double e2_, double e3_, double *p_, double *wk_, double *xv_) {\n" \
1768 + "double temp_;\nif (e1_ < e2_) {temp_ = e1_;} else {temp_ = e2_;};\n" \
1769 + "if (e3_ < temp_) {return e3_;} else {return temp_;};\n}",
1770 "double __minof3(double e1_, double e2_, double e3_, double *p_, double *wk_, double *xv_)")
1771 self.auxfns['__maxof4'] = ("double __maxof4(double e1_, double e2_, double e3_, double e4_, double *p_, double *wk_, double *xv_) {\n" \
1772 + "double temp_;\nif (e1_ > e2_) {temp_ = e1_;} else {temp_ = e2_;};\n" \
1773 + "if (e3_ > temp_) {temp_ = e3_;};\nif (e4_ > temp_) {return e4_;} else {return temp_;};\n}",
1774 "double __maxof4(double e1_, double e2_, double e3_, double e4_, double *p_, double *wk_, double *xv_)")
1775 self.auxfns['__minof4'] = ("double __minof4(double e1_, double e2_, double e3_, double e4_, double *p_, double *wk_, double *xv_) {\n" \
1776 + "double temp_;\nif (e1_ < e2_) {temp_ = e1_;} else {temp_ = e2_;};\n" \
1777 + "if (e3_ < temp_) {temp_ = e3_;};\nif (e4_ < temp_) {return e4_;} else {return temp_;};\n}",
1778 "double __minof4(double e1_, double e2_, double e3_, double e4_, double *p_, double *wk_, double *xv_)")
1779
1780 cases_ic = ""
1781 cases_index = ""
1782 for i in xrange(len(self.vars)):
1783 if i == 0:
1784 command = 'if'
1785 else:
1786 command = 'else if'
1787 vname = self.vars[i]
1788 cases_ic += " " + command + " (strcmp(varname, " + '"' + vname + '"'\
1789 + ")==0)\n\treturn gICs[" + str(i) + "];\n"
1790 cases_index += " " + command + " (strcmp(name, " + '"' + vname + '"'\
1791 + ")==0)\n\treturn " + str(i) + ";\n"
1792
1793 for i in xrange(len(self.pars)):
1794 pname = self.pars[i]
1795 cases_index += " else if" + " (strcmp(name, " + '"' + pname + '"'\
1796 +")==0)\n\treturn " + str(i+len(self.vars)) + ";\n"
1797 cases_ic += """ else {\n\tfprintf(stderr, "Invalid variable name %s for """ \
1798 + """initcond call\\n", varname);\n\treturn 0.0/0.0;\n\t}\n"""
1799 cases_index += """ else {\n\tfprintf(stderr, "Invalid name %s for """ \
1800 + """getindex call\\n", name);\n\treturn 0.0/0.0;\n\t}\n"""
1801 self.auxfns['initcond'] = ("double initcond(char *varname, double *p_, double *wk_, double *xv_) {\n" \
1802 + "\n" + cases_ic + "}",
1803 'double initcond(char *varname, double *p_, double *wk_, double *xv_)')
1804 self.auxfns['getindex'] = ("int getindex(char *name, double *p_, double *wk_, double *xv_) {\n" \
1805 + "\n" + cases_index + "}",
1806 'int getindex(char *name, double *p_, double *wk_, double *xv_)')
1807 self.auxfns['globalindepvar'] = ("double globalindepvar(double t, double *p_, double *wk_, double *xv_)" \
1808 + " {\n return globalt0+t;\n}",
1809 'double globalindepvar(double t, double *p_, double *wk_, double *xv_)')
1810 self.auxfns['getbound'] = \
1811 ("double getbound(char *name, int which_bd, double *p_, double *wk_, double *xv_) {\n" \
1812 + " return gBds[which_bd][getindex(name)];\n}",
1813 'double getbound(char *name, int which_bd, double *p_, double *wk_, double *xv_)')
1814
1815
1817 assert self.targetlang == 'c', ('Wrong target language for this'
1818 ' call')
1819 assert self.varspecs != {}, 'varspecs attribute must be defined'
1820 specnames_unsorted = self.varspecs.keys()
1821 _vbfs_inv = invertMap(self._varsbyforspec)
1822
1823 if len(_vbfs_inv) > 0:
1824 specname_vars = []
1825 specname_auxvars = []
1826 for varname in self.vars:
1827
1828 specname = _vbfs_inv[varname]
1829 if varname not in specname_vars:
1830 specname_vars.append(varname)
1831 for varname in self.auxvars:
1832
1833 specname = _vbfs_inv[varname]
1834 if varname not in specname_auxvars:
1835 specname_auxvars.append(varname)
1836 else:
1837 specname_vars = intersect(self.vars, specnames_unsorted)
1838 specname_auxvars = intersect(self.auxvars, specnames_unsorted)
1839 specname_vars.sort()
1840
1841 vnames = specname_vars
1842 pnames = self.pars
1843 inames = self.inputs
1844 pnames.sort()
1845 inames.sort()
1846 pardefines = ""
1847 vardefines = ""
1848 inpdefines = ""
1849 parundefines = ""
1850 varundefines = ""
1851 inpundefines = ""
1852
1853 assert self.vars == specname_vars, ('Mismatch between declared '
1854 ' variable names and varspecs keys')
1855 valid_depTargNames = self.inputs+self.vars+self.auxvars
1856 for specname, specstr in self.varspecs.iteritems():
1857 assert type(specstr)==str, "Specification for %s was not a string"%specname
1858 if any([pt in specstr for pt in ('^', '**')]):
1859 specstr = convertPowers(specstr, 'pow')
1860 specQS = QuantSpec('__spectemp__', specstr)
1861 for s in specQS:
1862 if s in valid_depTargNames and (specname, s) not in \
1863 self.dependencies:
1864 self.dependencies.append((specname, s))
1865
1866
1867 reusestr, specupdated = self._processReusedC(specname_vars,
1868 self.varspecs)
1869 self.varspecs.update(specupdated)
1870 specstr_C = self._genSpecFnC('vfieldfunc', reusestr, specname_vars,
1871 pardefines, vardefines, inpdefines,
1872 parundefines, varundefines, inpundefines,
1873 True)
1874 self.spec = specstr_C
1875
1876 specname_auxvars.sort()
1877 assert self.auxvars == specname_auxvars, \
1878 ('Mismatch between declared auxiliary'
1879 ' variable names and varspecs keys')
1880 if self.auxvars != []:
1881 reusestraux, specupdated = self._processReusedC(specname_auxvars,
1882 self.varspecs)
1883 self.varspecs.update(specupdated)
1884 if self.auxvars == []:
1885 auxspecstr_C = self._genSpecFnC('auxvars', '',
1886 specname_auxvars,
1887 '', '', '',
1888 '', '', '', False)
1889 else:
1890 auxspecstr_C = self._genSpecFnC('auxvars', reusestraux,
1891 specname_auxvars, pardefines,
1892 vardefines, inpdefines, parundefines,
1893 varundefines, inpundefines,
1894 False)
1895 self.auxspec = auxspecstr_C
1896
1897
1898 - def _genSpecFnC(self, funcname, reusestr, specnames, pardefines,
1899 vardefines, inpdefines, parundefines, varundefines,
1900 inpundefines, docodeinserts):
1901 sig = "void " + funcname + "(unsigned n_, unsigned np_, double t, double *Y_, " \
1902 + "double *p_, double *f_, unsigned wkn_, double *wk_, unsigned xvn_, double *xv_)"
1903
1904 specstr = sig + "{" + pardefines + vardefines + inpundefines + "\n"
1905 if docodeinserts and self.codeinserts['start'] != '':
1906 specstr += '/* Verbose code insert -- begin */\n' \
1907 + self.codeinserts['start'] \
1908 + '/* Verbose code insert -- end */\n\n'
1909 specstr += (len(reusestr)>0)*"/* reused term definitions */\n" \
1910 + reusestr + "\n"
1911 auxdefs_parsed = {}
1912
1913 for i in xrange(len(specnames)):
1914 xname = specnames[i]
1915 fbody = self.varspecs[xname]
1916 fbody_parsed = self._processSpecialC(fbody)
1917 if self.auxfns:
1918 fbody_parsed = addArgToCalls(fbody_parsed,
1919 self.auxfns.keys(),
1920 "p_, wk_, xv_")
1921 if 'initcond' in self.auxfns:
1922
1923
1924 fbody_parsed = wrapArgInCall(fbody_parsed,
1925 'initcond', '"')
1926 specstr += "f_[" + str(i) + "] = " + fbody_parsed + ";\n"
1927 auxdefs_parsed[xname] = fbody_parsed
1928 if docodeinserts and self.codeinserts['end'] != '':
1929 specstr += '\n/* Verbose code insert -- begin */\n' \
1930 + self.codeinserts['end'] \
1931 + '/* Verbose code insert -- end */\n'
1932 specstr += "\n" + parundefines + varundefines + inpundefines + "}\n\n"
1933 self._auxdefs_parsed = auxdefs_parsed
1934 return (specstr, funcname)
1935
1936
1941
1942
1944 """Pre-process 'if' statements and names of 'abs' and 'sign' functions,
1945 as well as logical operators.
1946 """
1947 qspec = QuantSpec('spec', specStr, treatMultiRefs=False)
1948 qspec.mapNames({'abs': 'fabs', 'sign': 'signum', 'mod': 'fmod',
1949 'and': '&&', 'or': '||', 'not': '!',
1950 'True': 1, 'False': 0,
1951 'max': '__maxof', 'min': '__minof'})
1952 qtoks = qspec.parser.tokenized
1953
1954 new_specStr = str(qspec)
1955 if 'if' in qtoks:
1956 new_specStr = ""
1957 num_ifs = qtoks.count('if')
1958 if_ix = -1
1959 ix_continue = 0
1960 for ifstmt in range(num_ifs):
1961 if_ix = qtoks[if_ix+1:].index('if')+if_ix+1
1962 new_specStr += "".join(qtoks[ix_continue:if_ix]) + "__rhs_if("
1963 rbrace_ix = findEndBrace(qtoks[if_ix+1:])+if_ix+1
1964 ix_continue = rbrace_ix+1
1965 new_specStr += "".join(qtoks[if_ix+2:ix_continue])
1966 new_specStr += "".join(qtoks[ix_continue:])
1967 qspec = QuantSpec('spec', new_specStr)
1968 qtoks = qspec.parser.tokenized
1969 if '__minof' in qtoks:
1970 new_specStr = ""
1971 num = qtoks.count('__minof')
1972 n_ix = -1
1973 ix_continue = 0
1974 for stmt in range(num):
1975 n_ix = qtoks[n_ix+1:].index('__minof')+n_ix+1
1976 new_specStr += "".join(qtoks[ix_continue:n_ix])
1977 rbrace_ix = findEndBrace(qtoks[n_ix+1:])+n_ix+1
1978 ix_continue = rbrace_ix+1
1979
1980
1981
1982 num_args = qtoks[n_ix+2:ix_continue].count(',') + 1
1983 if num_args > 4:
1984 raise NotImplementedError("Max of more than 4 arguments not currently supported in C")
1985 new_specStr += '__minof%s(' % str(num_args)
1986 new_specStr += "".join([q for q in qtoks[n_ix+2:ix_continue] if q not in ('[',']')])
1987 new_specStr += "".join(qtoks[ix_continue:])
1988 qspec = QuantSpec('spec', new_specStr)
1989 qtoks = qspec.parser.tokenized
1990 if '__maxof' in qtoks:
1991 new_specStr = ""
1992 num = qtoks.count('__maxof')
1993 n_ix = -1
1994 ix_continue = 0
1995 for stmt in range(num):
1996 n_ix = qtoks[n_ix+1:].index('__maxof')+n_ix+1
1997 new_specStr += "".join(qtoks[ix_continue:n_ix])
1998 rbrace_ix = findEndBrace(qtoks[n_ix+1:])+n_ix+1
1999 ix_continue = rbrace_ix+1
2000
2001
2002
2003 num_args = qtoks[n_ix+2:ix_continue].count(',') + 1
2004 if num_args > 4:
2005 raise NotImplementedError("Min of more than 4 arguments not currently supported in C")
2006 new_specStr += '__maxof%s(' % str(num_args)
2007 new_specStr += "".join([q for q in qtoks[n_ix+2:ix_continue] if q not in ('[',']')])
2008 new_specStr += "".join(qtoks[ix_continue:])
2009 qspec = QuantSpec('spec', new_specStr)
2010 qtoks = qspec.parser.tokenized
2011 return new_specStr
2012
2013
2014
2016 auxnames = self.auxfns.keys()
2017
2018
2019
2020
2021 vnames = self.vars
2022 pnames = self.pars
2023 vnames.sort()
2024 pnames.sort()
2025
2026 for auxname in auxnames:
2027 assert auxname not in ['auxvars', 'vfield'], \
2028 ("auxiliary function name '" +auxname+ "' clashes with internal"
2029 " names")
2030
2031
2032
2033
2034
2035
2036 for auxname, auxspec in self._auxfnspecs.iteritems():
2037 assert len(auxspec) == 2, 'auxspec tuple must be of length 2'
2038 if not isinstance(auxspec[0], list):
2039 print "Found type ", type(auxspec[0])
2040 print "Containing: ", auxspec[0]
2041 raise TypeError('aux function arguments '
2042 'must be given as a list')
2043 if not isinstance(auxspec[1], str):
2044 print "Found type ", type(auxspec[1])
2045 print "Containing: ", auxspec[1]
2046 raise TypeError('aux function specification '
2047 'must be a string of the function code')
2048
2049
2050
2051
2052 if auxname == 'Jacobian':
2053 raise NotImplementedError
2054 elif auxname == 'Jacobian_pars':
2055 raise NotImplementedError
2056 elif auxname == 'massMatrix':
2057 raise NotImplementedError
2058 else:
2059 ismat = False
2060 topstr = "function y_ = " + auxname + "("
2061 commentstr = "% Auxilliary function " + auxname + " for model " + self.name + "\n% Generated by PyDSTool for ADMC++ target\n\n"
2062 parlist = ""
2063 namemap = {}
2064 for parname in auxspec[0]:
2065 parlist += parname + "__, "
2066 namemap[parname] = parname+'__'
2067 topstr += parlist + " p_)\n"
2068 sig = topstr + commentstr
2069 pardefines = self._prepareMatlabPDefines(pnames)
2070 auxstr = auxspec[1]
2071 if any([pt in auxstr for pt in ('pow', '**')]):
2072 auxstr = convertPowers(auxstr, '^')
2073 reusestr, auxspec_processedDict = self._processReusedMatlab([auxname],
2074 {auxname:auxstr.replace(' ','').replace('\n','')})
2075
2076
2077 dummyQ = QuantSpec('dummy', auxspec_processedDict[auxname],
2078 treatMultiRefs=False, preserveSpace=True)
2079 if not ismat:
2080 dummyQ.mapNames(namemap)
2081 body_processed = "y_ = "*(not ismat) + dummyQ() + ";\n\n"
2082
2083 auxspecstr = sig + pardefines + " \n\n" \
2084 + "\n" + (len(reusestr)>0)*"% reused term definitions \n" \
2085 + reusestr + (len(reusestr)>0)*"\n" + body_processed
2086
2087
2088 self.auxfns[auxname] = (auxspecstr, sig)
2089 self._protected_auxnames.extend(auxnames)
2090
2091
2092
2094 assert self.targetlang == 'matlab', ('Wrong target language for this'
2095 ' call')
2096 assert self.varspecs != {}, 'varspecs attribute must be defined'
2097 specnames_unsorted = self.varspecs.keys()
2098 specname_vars = intersect(self.vars, specnames_unsorted)
2099 specname_vars.sort()
2100
2101
2102 vnames = specname_vars
2103 pnames = self.pars
2104 pnames.sort()
2105 pardefines = self._prepareMatlabPDefines(pnames)
2106 vardefines = self._prepareMatlabVDefines(vnames)
2107
2108 assert self.vars == specname_vars, ('Mismatch between declared '
2109 ' variable names and varspecs keys')
2110 valid_depTargNames = self.inputs+self.vars+self.auxvars
2111 for specname, specstr in self.varspecs.iteritems():
2112 assert type(specstr)==str, "Specification for %s was not a string"%specname
2113 if any([pt in specstr for pt in ('pow', '**')]):
2114 specstr = convertPowers(specstr, '^')
2115 specQS = QuantSpec('__spectemp__', specstr)
2116 for s in specQS:
2117 if s in valid_depTargNames and (specname, s) not in \
2118 self.dependencies:
2119 self.dependencies.append((specname, s))
2120
2121
2122 reusestr, specupdated = self._processReusedMatlab(specname_vars,
2123 self.varspecs)
2124 self.varspecs.update(specupdated)
2125 specstr_Matlab = self._genSpecFnMatlab('vfield', reusestr, specname_vars,
2126 pardefines, vardefines, True)
2127 self.spec = specstr_Matlab
2128
2129
2130
2131 - def _genSpecFnMatlab(self, funcname, reusestr, specnames, pardefines,
2132 vardefines, docodeinserts):
2133 topstr = "function [vf_, y_] = " + funcname + "(vf_, t_, x_, p_)\n"
2134 commentstr = "% Vector field definition for model " + self.name + "\n% Generated by PyDSTool for ADMC++ target\n\n"
2135
2136 specstr = topstr + commentstr + pardefines + vardefines + "\n"
2137 if docodeinserts and self.codeinserts['start'] != '':
2138 specstr += '% Verbose code insert -- begin \n' \
2139 + self.codeinserts['start'] \
2140 + '% Verbose code insert -- end \n\n'
2141 specstr += (len(reusestr)>0)*"% reused term definitions \n" \
2142 + reusestr + "\n"
2143
2144 for i in xrange(len(specnames)):
2145 xname = specnames[i]
2146 fbody = self.varspecs[xname]
2147 fbody_parsed = self._processIfMatlab(fbody)
2148 if self.auxfns:
2149 fbody_parsed = addArgToCalls(fbody_parsed,
2150 self.auxfns.keys(),
2151 "p_")
2152
2153
2154
2155
2156
2157 specstr += "y_(" + str(i+1) + ") = " + fbody_parsed + ";\n"
2158 if docodeinserts and self.codeinserts['end'] != '':
2159 specstr += '\n% Verbose code insert -- begin \n' \
2160 + self.codeinserts['end'] \
2161 + '% Verbose code insert -- end \n'
2162 specstr += "\n\n"
2163 return (specstr, funcname)
2164
2165
2167 """Process reused subexpression terms for Matlab code."""
2168
2169 if self.auxfns:
2170 def addParToCall(s):
2171 return addArgToCalls(s, self.auxfns.keys(), "p_")
2172 parseFunc = addParToCall
2173 else:
2174 parseFunc = idfn
2175 reused, specupdated, new_protected, order = _processReused(specnames,
2176 specdict,
2177 self.reuseterms,
2178 '', '', ';',
2179 parseFunc)
2180 self._protected_reusenames = new_protected
2181 reusedefs = {}.fromkeys(new_protected)
2182 for vname, deflist in reused.iteritems():
2183 for d in deflist:
2184 reusedefs[d[2]] = d
2185 return (concatStrDict(reusedefs, intersect(order, reusedefs.keys())),
2186 specupdated)
2187
2188
2190 qspec = QuantSpec('spec', specStr)
2191 qtoks = qspec[:]
2192 if 'if' in qtoks:
2193 raise NotImplementedError
2194 else:
2195 new_specStr = specStr
2196 return new_specStr
2197
2198
2200 pardefines = ""
2201 for i in xrange(len(pnames)):
2202 p = pnames[i]
2203 pardefines += "\t" + p + " = p_(" + str(i+1) + ");\n"
2204
2205 alldefines = "\n% Parameter definitions\n\n" + pardefines
2206 return alldefines
2207
2208
2210 vardefines = ""
2211 for i in xrange(len(vnames)):
2212 v = vnames[i]
2213 vardefines += "\t" + v + " = x_(" + str(i+1) + ");\n"
2214 alldefines = "\n% Variable definitions\n\n" + vardefines
2215 return alldefines
2216
2217
2218
2219
2221 if verbose == 0:
2222 outputStr = "FuncSpec " + self.name
2223 else:
2224 outputStr = '*********** FuncSpec: '+self.name + ' ***********'
2225 outputStr += '\nTarget lang: '+ self.targetlang
2226 outputStr += '\nVariables: '
2227 for v in self.vars:
2228 outputStr += v+' '
2229 outputStr += '\nParameters: '
2230 if len(self.pars):
2231 for p in self.pars:
2232 outputStr += p+' '
2233 else:
2234 outputStr += '[]'
2235 outputStr += '\nExternal inputs: '
2236 if len(self.inputs):
2237 for i in self.inputs:
2238 outputStr += i+' '
2239 else:
2240 outputStr += '[]'
2241 if verbose == 2:
2242 outputStr += "\nSpecification functions (in target language):"
2243 outputStr += "\n (ignore any arguments `ds` and `parsinps`," \
2244 + "\n which are for internal use only)\n"
2245 if self.spec == {}:
2246 outputStr += "\n None\n"
2247 else:
2248 outputStr += "\n "+self.spec[0]+"\n"
2249 if len(self.auxvars) and self.auxspec != {}:
2250 outputStr += " "+self.auxspec[0]
2251 if self._protected_auxnames != []:
2252 outputStr += '\n\nUser-defined auxiliary variables: '
2253 for v in self.auxvars:
2254 outputStr += v+' '
2255 outputStr += '\n\nUser-defined auxiliary functions (in target ' + \
2256 'language):'
2257 for auxname in self.auxfns:
2258
2259 if auxname not in self._builtin_auxnames or verbose>0:
2260 outputStr += '\n '+self.auxfns[auxname][0]+'\n'
2261 outputStr += "\n\nDependencies in specification functions - pair (i, o)"\
2262 " means i depends on o:\n " + str(self.dependencies)
2263 return outputStr
2264
2265 - def info(self, verbose=0):
2267
2270
2271 __str__ = __repr__
2272
2273
2274
2275
2276
2277
2278
2280 """Right-hand side definition for vars defined."""
2281
2284
2285
2286
2288 """Explicit definition of vars defined."""
2289
2291 assert 'codeinsert_start' not in kw, ('code inserts invalid for '
2292 'explicit function specification')
2293 assert 'codeinsert_end' not in kw, ('code inserts invalid for '
2294 'explicit function specification')
2295 FuncSpec.__init__(self, kw)
2296
2297
2298
2300 """Assumes this will be set to equal zero when solving for vars defined."""
2301
2302
2303
2304
2306 assert 'codeinsert_start' not in kw, ('code inserts invalid for '
2307 'implicit function specification')
2308 assert 'codeinsert_end' not in kw, ('code inserts invalid for '
2309 'implicit function specification')
2310 FuncSpec.__init__(self, kw)
2311
2312
2313
2314 -def _processReused(specnames, specdict, reuseterms, indentstr='',
2315 typestr='', endstatementchar='', parseFunc=idfn):
2316 """Process substitutions of reused terms."""
2317
2318 seenrepterms = []
2319 reused = {}.fromkeys(specnames)
2320 reuseterms_inv = invertMap(reuseterms)
2321
2322 are_dependent = []
2323 deps = {}
2324 for origterm, rterm in reuseterms.iteritems():
2325 for ot, rt in reuseterms.iteritems():
2326 if proper_match(origterm, rt):
2327 if rterm not in are_dependent:
2328 are_dependent.append(rterm)
2329 try:
2330 deps[rterm].append(rt)
2331 except KeyError:
2332
2333 deps[rterm] = [rt]
2334 order = remain(reuseterms.values(), are_dependent) + are_dependent
2335 for specname in specnames:
2336 reused[specname] = []
2337 specstr = specdict[specname]
2338 repeatkeys = []
2339 for origterm, repterm in reuseterms.iteritems():
2340
2341 if proper_match(specstr, origterm):
2342 specstr = specstr.replace(origterm, repterm)
2343 if repterm not in seenrepterms:
2344 reused[specname].append([indentstr,
2345 typestr+' '*(len(typestr)>0),
2346 repterm, " = ",
2347 parseFunc(origterm),
2348 endstatementchar, "\n"])
2349 seenrepterms.append(repterm)
2350 else:
2351
2352 repeatkeys.append(origterm)
2353 if len(seenrepterms) > 0:
2354
2355 for origterm in repeatkeys:
2356
2357 repterm = reuseterms[origterm]
2358 if proper_match(specstr, origterm):
2359 specstr = specstr.replace(origterm, repterm)
2360 if repterm not in seenrepterms:
2361 seenrepterms.append(repterm)
2362 reused[specname].append([indentstr,
2363 typestr+' '*(len(typestr)>0),
2364 repterm, " = ",
2365 parseFunc(origterm),
2366 endstatementchar, "\n"])
2367
2368
2369
2370
2371 if reused[specname] == [] and len(reuseterms) > 0:
2372 for origterm, repterm in reuseterms.iteritems():
2373
2374 if proper_match(specstr, repterm) and repterm not in seenrepterms:
2375 reused[specname].append([indentstr,
2376 typestr+' '*(len(typestr)>0),
2377 repterm, " = ",
2378 parseFunc(origterm),
2379 endstatementchar, "\n"])
2380 seenrepterms.append(repterm)
2381 specdict[specname] = specstr
2382
2383
2384 add_reps = []
2385 for r in seenrepterms:
2386 if r in are_dependent:
2387 for repterm in deps[r]:
2388 if repterm not in seenrepterms:
2389 reused[specname].append([indentstr,
2390 typestr+' '*(len(typestr)>0),
2391 repterm, " = ",
2392 parseFunc(reuseterms_inv[repterm]),
2393 endstatementchar, "\n"])
2394 seenrepterms.append(repterm)
2395
2396
2397
2398
2399
2400
2401
2402
2403 return (reused, specdict, seenrepterms, order)
2404
2405
2406
2407
2408
2409
2410
2411
2413 """Use this when parameters have been added to a modified Generator which
2414 might clash with aux fn argument names. (E.g., used by find_nullclines).
2415
2416 'select' option (list of varnames) selects those entries from the Jac of the varnames,
2417 e.g. for constructing Jacobian w.r.t. 'parameters' using a parameter formerly
2418 a variable (e.g. for find_nullclines).
2419 """
2420 fargs, fspec = spec_pair
2421 J = QuantSpec('J', fspec)
2422
2423
2424 dim = len(varnames)
2425 if J.dim == dim:
2426
2427 return (fargs, fspec)
2428 assert J.dim > dim, "Cannot add variable names to system while using its old Jacobian aux function"
2429 assert remain(varnames, fargs) == [], "Invalid variable names to resolve Jacobian aux function"
2430 assert fargs[0] == 't'
2431
2432 vixs = [fargs.index(v)-1 for v in varnames]
2433 vixs.sort()
2434 if select is None:
2435 select = varnames
2436 sixs = vixs
2437 else:
2438 sixs = [fargs.index(v)-1 for v in select]
2439 if dim == 1:
2440 fspec = str(J.fromvector(vixs[0]).fromvector(sixs[0]))
2441 else:
2442 terms = []
2443 for i in vixs:
2444 Ji = J.fromvector(i)
2445 subterms = []
2446 for j in sixs:
2447 subterms.append( str(Ji.fromvector(j)) )
2448 terms.append( "[" + ",".join(subterms) + "]" )
2449 fspec = "[" + ",".join(terms) + "]"
2450
2451 fargs_new = ['t'] + [fargs[ix+1] for ix in vixs]
2452 return (fargs_new, fspec)
2453
2454
2456 """Use this when parameters have been added to a modified Generator which
2457 might clash with aux fn argument names. (E.g., used by find_nullclines).
2458 Will remove arguments that are now considered parameters by the system,
2459 in both the function definitions and their use in specs for the variables.
2460 """
2461 changed_fns = []
2462 new_fnspecs = {}
2463 for fname, (fargs, fspec) in fnspecs.iteritems():
2464 common_names = intersect(fargs, parnames)
2465 if fname in parnames:
2466 print "Problem with function definition", fname
2467 raise ValueError("Unrecoverable clash between parameter names and aux fn name")
2468 if common_names == []:
2469 new_fnspecs[fname] = (fargs, fspec)
2470 else:
2471 changed_fns.append(fname)
2472 new_fnspecs[fname] = (remain(fargs, parnames), fspec)
2473
2474 new_varspecs = {}
2475 for vname, vspec in varspecs.iteritems():
2476 q = QuantSpec('__temp__', vspec)
2477
2478 used_fns = intersect(q.parser.tokenized, changed_fns)
2479 for f in used_fns:
2480 ix = q.parser.tokenized.index(f)
2481
2482 rest = ''.join(q.parser.tokenized[ix+1:])
2483 end_ix = findEndBrace(rest)
2484
2485 argstr = rest[:end_ix+1]
2486
2487 success, args_list, arglen = readArgs(argstr)
2488 assert success, "Parsing arguments failed"
2489 new_args_list = []
2490
2491 for arg in args_list:
2492 qarg = QuantSpec('a', arg)
2493
2494
2495 if len(qarg.parser.tokenized) > 1:
2496 if any([p in qarg for p in parnames]):
2497
2498
2499 print "Warning: some auxiliary function parameters clash in function %s" %f
2500 new_args_list.append(arg)
2501 elif arg not in parnames:
2502
2503 new_args_list.append(arg)
2504 new_argstr = ','.join(new_args_list)
2505
2506 vspec = ''.join(q[:ix+1]) + '(' + new_argstr + ')' + rest[end_ix+1:]
2507 q = QuantSpec('__temp__', vspec)
2508 new_varspecs[vname] = vspec
2509 return new_fnspecs, new_varspecs
2510
2511
2512
2514 """Read text specs from a file"""
2515 try:
2516 f = open(specfilename, 'r')
2517 s = f.read()
2518 except IOError, e:
2519 print 'File error:', str(e)
2520 raise
2521 f.close()
2522 return s
2523