1
2 from __future__ import division
3
4 from allimports import *
5 from PyDSTool.utils import *
6 from PyDSTool.common import *
7 from PyDSTool.Symbolic import ensureStrArgDict, Quantity, QuantSpec
8 from PyDSTool.Trajectory import Trajectory
9 from PyDSTool.parseUtils import symbolMapClass, readArgs
10 from PyDSTool.Variable import Variable, iscontinuous
11 from PyDSTool.Points import Pointset
12 import PyDSTool.Events as Events
13
14
15 from numpy import Inf, NaN, isfinite, sometrue, alltrue
16 import math, random
17 import os
18 from copy import copy, deepcopy
19 try:
20
21 import psyco
22 HAVE_PSYCO = True
23 except ImportError:
24 HAVE_PSYCO = False
25
26
27
28 __all__ = ['ctsGen', 'discGen', 'theGenSpecHelper', 'Generator',
29 'genDB', 'auxfn_container', '_pollInputs']
30
31
32
34 """This class keeps a record of which non-Python Generators have been
35 created in a session. A single global instance of this class is created,
36 and prevents the user from re-using the name of a low-level
37 DLL (such as that for a Dopri_ODEsystem vector field) unless the
38 system is identical (according to its FuncSpec)."""
39
42
44 """Look up generator instance and return Boolean of whether
45 it is already registered. Only non-python based generators
46 are registered, so all others will return False.
47 """
48 if gen.funcspec.targetlang == 'python':
49
50 return False
51 try:
52 if gen._solver.rhs in self.database:
53 entry = self.database[gen._solver.rhs]
54 return className(gen) == entry['class'] \
55 and hash(gen.funcspec) == entry['hash']
56 else:
57 return False
58 except AttributeError:
59
60 return False
61
63 try:
64 del self.database[gen._solver.rhs]
65 except (AttributeError, KeyError):
66
67
68 return
69
71 if gen.funcspec.targetlang == 'python':
72
73 return
74
75 try:
76 if gen._solver.rhs not in self.database:
77 self.database[gen._solver.rhs] = {'name': gen.name,
78 'class': className(gen),
79 'hash': hash(gen.funcspec)}
80 else:
81 if hash(gen.funcspec) != self.database[gen._solver.rhs]['hash']:
82 raise PyDSTool_KeyError("Generator named %s already exists"%gen.name)
83
84 except AttributeError:
85
86 return
87
89 s = "Generator internal database class: "
90 s += str(self.database.keys())
91 return s
92
93 __str__ = __repr__
94
95
98
99
100
101 global genDB
102 genDB = genDBClass()
103
104
105
106
108 """
109 Auxiliary function interface for python user
110 """
112 self.genref = genref
113 self.map_ixs = ixmap
114
116 try:
117 return self.__dict__[k]
118 except KeyError:
119 raise KeyError("Function %s not present"%k)
120
122 return [k for k in self.__dict__.keys() \
123 if k not in ['genref', 'map_ixs']]
124
126 return k in self.keys()
127
129 return [self.__dict__[k] for k in self.__dict__.keys() \
130 if k not in ['genref', 'map_ixs']]
131
134
136 return "Aux functions: " + ", ".join(self.keys())
137
138
141 self.parixmap = {}
142 self.pars = genref.pars
143 i = genref.inputs.keys()
144 i.sort()
145 p = genref.pars.keys()
146 p.sort()
147 allnames = p + i
148 for pair in enumerate(allnames):
149 self.parixmap[pair[0]] = pair[1]
150
152 try:
153 return self.pars[self.parixmap[k]]
154 except:
155 raise "Cannot access external input values using ixmap class"
156
158 return "Index mapping: " + str(self.parixmap)
159
160
162 """
163 Trajectory Generator abstract class.
164 """
165
166 _querykeys = ['pars', 'parameters', 'events', 'abseps',
167 'ics', 'initialconditions', 'vars', 'variables',
168 'auxvariables', 'auxvars', 'vardomains', 'pardomains']
169
170 _needKeys = ['name']
171 _optionalKeys = ['globalt0', 'checklevel', 'model', 'abseps',
172 'eventPars', 'FScompatibleNames',
173 'FScompatibleNamesInv']
174
176
177
178
179
180
181 self._funcreg = {}
182 try:
183
184
185
186 dummy = self.foundKeys
187
188 except:
189 self.foundKeys = 0
190 try:
191 self.name = kw['name']
192 self.foundKeys += 1
193 except KeyError:
194 raise PyDSTool_KeyError("name must be supplied as a keyword in arguments")
195
196 self.dimension = None
197
198
199
200
201
202
203
204
205 self.variables = {}
206
207 self.initialconditions = {}
208
209
210 self.funcspec = None
211
212 self.pars = {}
213
214 self.eventstruct = None
215
216 self.trajevents = None
217
218 self.inputs = {}
219
220 self.indepvariable = None
221
222 self._callnames = []
223
224 self._registry = {}
225
226
227
228
229 self._for_hybrid_DS = False
230
231 if 'abseps' in kw:
232 self._abseps = kw['abseps']
233 self.foundKeys += 1
234 else:
235 self._abseps = 1e-13
236
237 if 'checklevel' in kw:
238 if kw['checklevel'] not in range(4):
239 raise ValueError('Invalid interval endpoint checking option')
240 self.checklevel = kw['checklevel']
241 self.foundKeys += 1
242 else:
243
244 self.checklevel = 0
245
246 self.diagnostics = Diagnostics(errmessages, errorfields, warnmessages,
247 warnfields, propagate_dict=self.inputs)
248
249
250
251 if 'globalt0' in kw:
252 v = kw['globalt0']
253 assert isinstance(v, _num_types), 'Incorrect type of globalt0'
254 self.globalt0 = v
255 self.foundKeys += 1
256 else:
257 self.globalt0 = 0
258
259
260
261 if 'model' in kw:
262 assert isinstance(kw['model'], str), "model tag must be a string"
263 self._modeltag = kw['model']
264 self.foundKeys += 1
265 else:
266 self._modeltag = None
267 if 'FScompatibleNames' in kw:
268 sm = kw['FScompatibleNames']
269 if sm is None:
270 sm = symbolMapClass()
271 self._FScompatibleNames = sm
272 self.foundKeys += 1
273 else:
274 self._FScompatibleNames = symbolMapClass()
275 if 'FScompatibleNamesInv' in kw:
276 sm = kw['FScompatibleNamesInv']
277 if sm is None:
278 sm = symbolMapClass()
279 self._FScompatibleNamesInv = sm
280 self.foundKeys += 1
281 else:
282 self._FScompatibleNamesInv = symbolMapClass()
283
284
285 self._eventPars = []
286 if 'eventPars' in kw:
287 if isinstance(kw['eventPars'], list):
288 self._eventPars = kw['eventPars']
289 elif isinstance(kw['eventPars'], str):
290 self._eventPars.append(kw['eventPars'])
291 self.foundKeys += 1
292 self._eventPars = self._FScompatibleNames(self._eventPars)
293
294 self.defined = False
295
296
298 """Register parameter names as event specific parameters."""
299 if isinstance(eventPars, list):
300 self._eventPars.extend(self._FScompatibleNames(eventPars))
301 elif isinstance(eventPars, str):
302 self._eventPars.append(self._FScompatibleNames(eventPars))
303
304 - def getEvents(self, evnames=None, asGlobalTime=True):
305 """Produce dictionary of pointsets of all flagged events' independent
306 and dependent variable values, for each event (whether terminal or not).
307 Times will be globalized if optional asGlobalTime argument is True
308 (default behavior). If a single event name is passed, only the pointset
309 is returned (not a dictionary).
310
311 evnames may be a singleton string or list of strings, or left blank to
312 return data for all events.
313
314 The events are not guaranteed to be ordered by the value of the
315 independent variable.
316 """
317 compat_evnames = self._FScompatibleNamesInv(self.trajevents.keys())
318 if evnames is None:
319 evnames = compat_evnames
320 if asGlobalTime:
321 t_offset = self.globalt0
322 else:
323 t_offset = 0
324 if isinstance(evnames, str):
325
326 assert evnames in compat_evnames, "Invalid event name provided: %s"%evnames
327 try:
328 result = self.trajevents[self._FScompatibleNames(evnames)]
329 except AttributeError:
330
331 return None
332 else:
333 result.indepvararray += t_offset
334 return result
335 else:
336
337 assert all([ev in compat_evnames for ev in evnames]), \
338 "Invalid event name(s) provided: %s"%str(evnames)
339 result = {}
340 for (evname, evptset) in self.trajevents.iteritems():
341 compat_evname = self._FScompatibleNamesInv(evname)
342 if compat_evname not in evnames:
343 continue
344 result[compat_evname] = copy(evptset)
345 try:
346 result[compat_evname].indepvararray += t_offset
347 except AttributeError:
348
349 pass
350 return result
351
353 """Produce dictionary of lists of all flagged events' independent
354 variable values, for each event (whether terminal or not).
355 Times will be globalized if optional asGlobalTime argument is True
356 (default behavior). If a single event name is passed, only the pointset
357 is returned (not a dictionary).
358
359 evnames may be a singleton string or list of strings, or left blank to
360 return data for all events.
361
362 The events are guaranteed to be ordered by the value of the
363 independent variable.
364 """
365 result = {}
366 if asGlobalTime:
367 t_offset = self.globalt0
368 else:
369 t_offset = 0
370 compat_evnames = self._FScompatibleNamesInv(self.trajevents.keys())
371 if evnames is None:
372 evnames = compat_evnames
373 if isinstance(evnames, str):
374
375 assert evnames in compat_evnames, "Invalid event name provided: %s"%evnames
376 try:
377 return self.trajevents[self._FScompatibleNames(evnames)].indepvararray \
378 + t_offset
379 except AttributeError:
380
381 return []
382 else:
383
384 assert all([ev in compat_evnames for ev in evnames]), \
385 "Invalid event name(s) provided: %s"%str(evnames)
386 for (evname, evptset) in self.trajevents.iteritems():
387 compat_evname = self._FScompatibleNamesInv(evname)
388 if compat_evname not in compat_evnames:
389 continue
390 try:
391 result[compat_evname] = evptset.indepvararray + t_offset
392 except AttributeError:
393
394 result[compat_evname] = []
395 return result
396
397
398 - def query(self, querykey=''):
399 """Return info about Generator set-up.
400 Valid query key: 'pars', 'parameters', 'pardomains', 'events',
401 'ics', 'initialconditions', 'vars', 'variables',
402 'auxvars', 'auxvariables', 'vardomains'
403 """
404 assert isinstance(querykey, str), \
405 ("Query argument must be a single string")
406 if querykey not in self._querykeys:
407 print 'Valid query keys are:', self._querykeys
408 print "('events' key only queries model-level events, not those"
409 print " inside sub-models)"
410 raise ValueError('Query key '+querykey+' is not valid')
411 if querykey in ['pars', 'parameters']:
412 result = self._FScompatibleNamesInv(self.pars)
413 elif querykey in ['ics', 'initialconditions']:
414 try:
415 result = self._FScompatibleNamesInv(self.initialconditions)
416 except AttributeError:
417 result = None
418 elif querykey == 'events':
419 result = self.eventstruct.events
420 elif querykey in ['vars', 'variables']:
421 result = self._FScompatibleNamesInv(self.funcspec.vars)
422 elif querykey in ['auxvars', 'auxvariables']:
423 result = self._FScompatibleNamesInv(self.funcspec.auxvars)
424 elif querykey == 'vardomains':
425 result = {}
426 for varname, var in self.variables.iteritems():
427 result[self._FScompatibleNamesInv(varname)] = \
428 var.depdomain
429 elif querykey == 'pardomains':
430 result = {}
431 for parname, pardom in self.parameterDomains.iteritems():
432 result[self._FScompatibleNamesInv(parname)] = \
433 pardom
434 elif querykey == 'abseps':
435 result = self._abseps
436 return result
437
438 - def get(self, key):
439 """For API compatibility with ModelInterface: get will make a copy of
440 the key and pass it through the inverse FuncSpec-compatible name map.
441 """
442 return self._FScompatibleNamesInv(getattr(self, key))
443
444
446 """Default method. Can be overridden by subclasses."""
447 return False
448
449
451 """Default method. Can be overridden by subclasses."""
452 return False
453
454
455 - def info(self, verbose=1):
457
458
460
461
462
463 fs_args = {'name': self.name,
464 'ignorespecial': []}
465
466 self._kw_process_varspecs(kw, fs_args)
467 for key in keys:
468 if key == 'varspecs':
469
470 continue
471 f = getattr(self, '_kw_process_'+key)
472
473 f(kw, fs_args)
474 return fs_args
475
477 if 'varspecs' in kw:
478 for varname, varspec in kw['varspecs'].items():
479 assert isinstance(varname, (str, QuantSpec, Quantity)), "Invalid type for Variable name: %s"%str(varname)
480 assert isinstance(varspec, (str, QuantSpec, Quantity)), "Invalid type for Variable %s's specification"%varname
481 self.foundKeys += 1
482 fs_args['varspecs'] = \
483 self._FScompatibleNames(ensureStrArgDict(kw['varspecs']))
484 else:
485 raise PyDSTool_KeyError("Keyword 'varspecs' missing from "
486 "argument")
487 all_vars = []
488
489 fs_args['_for_macro_info'] = args(totforvars=0, numfors=0, varsbyforspec={})
490 for specname, specstr in fs_args['varspecs'].items():
491 if not '[' in specname:
492 all_vars.append(specname)
493 fs_args['_for_macro_info'].varsbyforspec[specname] = [specname]
494 continue
495
496 assert specstr[:4] == 'for(', ('Expected `for` macro when '
497 'square brackets used in name definition')
498
499 ok, arglist, nargs = readArgs(specstr[3:])
500 if not ok:
501 raise ValueError('Error finding '
502 'arguments applicable to `for` '
503 'macro')
504 rootstr = specname[:specname.find('[')]
505 assert len(arglist) == 4, ('Wrong number of arguments passed '
506 'to `for` macro. Expected 4')
507 ilo = int(arglist[1])
508 ihi = int(arglist[2])
509 new_vars = [rootstr+str(i) for i in range(ilo,ihi+1)]
510 all_vars.extend(new_vars)
511 fs_args['_for_macro_info'].numfors += 1
512 fs_args['_for_macro_info'].totforvars += (ihi-ilo+1)
513 fs_args['_for_macro_info'].varsbyforspec[specname] = new_vars
514
515
516 self.__all_vars = all_vars
517
518 - def _kw_process_tdomain(self, kw, fs_args):
519 if 'tdomain' in kw:
520 self.tdomain = kw['tdomain']
521 if self.tdomain[0] >= self.tdomain[1]:
522 print "Time domain specified: [%s, %s]"%(self.tdomain[0],
523 self.tdomain[1])
524 raise PyDSTool_ValueError("tdomain values must be in order of "
525 "increasing size")
526 self.foundKeys += 1
527 else:
528 self.tdomain = [-Inf, Inf]
529
531
532 if 'ttype' in kw:
533 try:
534 self.indepvartype = _num_equivtype[kw['ttype']]
535 except KeyError:
536 raise TypeError('Invalid ttype: %s'%str(kw['ttype']))
537 self.foundKeys += 1
538 else:
539 self.indepvartype = float
540
542
543 if 'tdata' in kw:
544 self.tdata = kw['tdata']
545 if self.tdata[0] >= self.tdata[1]:
546 raise PyDSTool_ValueError("tdata values must be in order of "
547 "increasing size")
548
549
550 if self.tdomain[0] > self.tdata[0]:
551 raise ValueError('tdata cannot be specified below smallest '\
552 'value in tdomain\n (possibly due to uncertain'\
553 'bounding)')
554 if self.tdomain[1] < self.tdata[1]:
555 raise ValueError('tdata cannot be specified above largest '\
556 'value in tdomain\n (possibly due to uncertain '\
557 'bounding)')
558 self.foundKeys += 1
559 else:
560 self.tdata = self.tdomain
561
563
564 if 'tstep' in kw:
565 self.tstep = kw['tstep']
566 if self.tstep > self.tdata[1]-self.tdata[0]:
567 raise PyDSTool_ValueError('tstep too large')
568 if compareNumTypes(self.indepvartype, _all_int) and round(self.tstep) != self.tstep:
569 raise PyDSTool_ValueError('tstep must be an integer for integer ttype')
570 self.foundKeys += 1
571 else:
572 if compareNumTypes(self.indepvartype, _all_int):
573
574 self.tstep = 1
575 else:
576
577 raise PyDSTool_KeyError('tstep key needed for float ttype')
578
620
622 if 'ics' in kw:
623 self._xdatadict = {}
624 for k, v in dict(kw['ics']).iteritems():
625 self._xdatadict[self._FScompatibleNames(str(k))] = ensurefloat(v)
626 self.initialconditions = self._xdatadict.copy()
627 unspecd = remain(self._xdatadict.keys(), self.__all_vars)
628 if unspecd != []:
629
630 raise ValueError("Missing varspec entries for declared ICs: " + str(unspecd))
631 for name in remain(self.__all_vars,
632 self._xdatadict.keys()):
633 self.initialconditions[name] = NaN
634 self.foundKeys += 1
635 else:
636 self._xdatadict = {}
637 for name in self.__all_vars:
638 self.initialconditions[name] = NaN
639
641 if 'auxvars' in kw:
642 assert 'vars' not in kw, ("Cannot use both 'auxvars' and 'vars' "
643 "keywords")
644 if isinstance(kw['auxvars'], list):
645 auxvars = self._FScompatibleNames([str(v) for v in kw['auxvars']])
646 else:
647 auxvars = self._FScompatibleNames([str(kw['auxvars'])])
648 vars = remain(self.__all_vars, auxvars)
649 self.foundKeys += 1
650 elif 'vars' in kw:
651 assert 'auxvars' not in kw, \
652 "Cannot use both 'auxvars' and 'vars' keywords"
653 if isinstance(kw['vars'], list):
654 vars = self._FScompatibleNames([str(v) for v in kw['vars']])
655 else:
656 vars = self._FScompatibleNames([str(kw['vars'])])
657 auxvars = remain(self.__all_vars, vars)
658 self.foundKeys += 1
659 else:
660
661 auxvars = []
662 vars = self.__all_vars
663
664 fs_args['vars'] = vars
665 self.dimension = len(vars)
666 if auxvars != []:
667 fs_args['auxvars'] = auxvars
668
670
671
672 self.xtype = {}
673 if 'xtype' in kw:
674 xts = kw['xtype']
675 for name_temp, xt in dict(xts).iteritems():
676 if compareNumTypes(xt, _all_int):
677 xt_actual = int
678 elif compareNumTypes(xt, _all_float):
679 xt_actual = float
680 else:
681 raise TypeError("Invalid variable type %s"%str(xt))
682 name = self._FScompatibleNames(name_temp)
683 if name[-1] == ']':
684
685 base = name[:name.index('[')]
686
687 for_spec = fs_args['varspecs'][name][4:-1].replace(' ', '').split(',')
688 for name_i in range(int(for_spec[1]), int(for_spec[2])+1):
689 self.xtype[base+str(name_i)] = xt_actual
690 else:
691 self.xtype[name] = xt_actual
692 for name in remain(fs_args['varspecs'].keys(), self.xtype.keys()):
693 self.xtype[name] = float
694 self.foundKeys += 1
695 else:
696 for name in fs_args['varspecs']:
697 if name[-1] == ']':
698
699 base = name[:name.index('[')]
700
701 for_spec = fs_args['varspecs'][name][4:-1].replace(' ', '').split(',')
702 for name_i in range(int(for_spec[1]), int(for_spec[2])+1):
703 self.xtype[base+str(name_i)] = float
704 else:
705 self.xtype[name] = float
706
707 - def _kw_process_xdomain(self, kw, fs_args):
708 if 'xdomain' in kw:
709 self.xdomain = {}
710 for k, v in dict(kw['xdomain']).iteritems():
711 name = self._FScompatibleNames(str(k))
712 if isinstance(v, _seq_types):
713 assert len(v) == 2, \
714 "Invalid size of domain specification for "+name
715 if v[0] >= v[1]:
716 raise PyDSTool_ValueError('xdomain values must be in'
717 'order of increasing size')
718 else:
719 self.xdomain[name] = copy(v)
720 elif isinstance(v, _num_types):
721 self.xdomain[name] = [v, v]
722 else:
723 raise PyDSTool_TypeError('Invalid type for xdomain spec'
724 ' '+name)
725 for name in remain(fs_args['varspecs'].keys(), self.xdomain.keys()):
726 if name[-1] == ']':
727
728 base = name[:name.index('[')]
729
730 for_spec = fs_args['varspecs'][name][4:-1].replace(' ', '').split(',')
731 for name_i in range(int(for_spec[1]), int(for_spec[2])+1):
732 self.xdomain[base+str(name_i)] = [-Inf, Inf]
733 else:
734 self.xdomain[name] = [-Inf, Inf]
735 self.foundKeys += 1
736 else:
737 self.xdomain = {}
738 for name in fs_args['varspecs']:
739 if name[-1] == ']':
740
741 base = name[:name.index('[')]
742
743 for_spec = fs_args['varspecs'][name][4:-1].replace(' ', '').split(',')
744 for name_i in range(int(for_spec[1]), int(for_spec[2])+1):
745 self.xdomain[base+str(name_i)] = [-Inf, Inf]
746 else:
747 self.xdomain[name] = [-Inf, Inf]
748
750 if 'reuseterms' in kw:
751 self.foundKeys += 1
752 fs_args['reuseterms'] = kw['reuseterms']
753
755 if 'ignorespecial' in kw:
756 self.foundKeys += 1
757 fs_args['ignorespecial'].extend(kw['ignorespecial'])
758
760 if 'algparams' in kw:
761 self.algparams = copy(kw['algparams'])
762 self.foundKeys += 1
763 else:
764 self.algparams = {}
765
767 if 'pars' in kw:
768 self.pars = {}
769 if isinstance(kw['pars'], list):
770
771 for p in kw['pars']:
772 try:
773 self.pars[self._FScompatibleNames(p.name)] = p.tonumeric()
774 except (AttributeError, TypeError):
775 raise TypeError("Invalid parameter symbolic definition")
776 else:
777 for k, v in dict(kw['pars']).iteritems():
778 self.pars[self._FScompatibleNames(str(k))] = ensurefloat(v)
779 fs_args['pars'] = self.pars.keys()
780 self._register(self.pars)
781 self.foundKeys += 1
782 self.numpars = len(self.pars)
783
784 - def _kw_process_pdomain(self, kw, fs_args):
785 if 'pdomain' in kw:
786 if self.pars:
787 self.pdomain = {}
788 for k, v in dict(kw['pdomain']).iteritems():
789 assert len(v) == 2, \
790 "Invalid size of domain specification for "+k
791 self.pdomain[self._FScompatibleNames(str(k))] = v
792 for name in self.pdomain:
793 if self.pdomain[name][0] >= self.pdomain[name][1]:
794 raise PyDSTool_ValueError('pdomain values must be in order of increasing size')
795 for name in remain(self.pars.keys(), self.pdomain.keys()):
796 self.pdomain[name] = [-Inf, Inf]
797 self.foundKeys += 1
798 else:
799 raise ValueError('Cannot specify pdomain because no pars declared')
800 else:
801 if self.pars:
802 self.pdomain = {}
803 for pname in self.pars:
804 self.pdomain[pname] = [-Inf, Inf]
805 if self.pars:
806 self.parameterDomains = {}
807 for pname in self.pdomain:
808 self.parameterDomains[pname] = Interval(pname, float,
809 self.pdomain[pname],
810 self._abseps)
811 try:
812 cval = self.parameterDomains[pname].contains(self.pars[pname])
813 except KeyError:
814 raise ValueError("Parameter %s is missing a value"%pname)
815 if self.checklevel < 3:
816 if cval is not notcontained:
817 if cval is uncertain and self.checklevel == 2:
818 print 'Warning: Parameter value at bound'
819 else:
820 print self.pars[pname], "not in", self.parameterDomains[pname].get()
821 raise PyDSTool_ValueError('Parameter %s: value out of bounds'%pname)
822 else:
823 if cval is uncertain:
824 raise PyDSTool_UncertainValueError('Parameter %s: value at bound'%pname)
825 elif cval is notcontained:
826 raise PyDSTool_ValueError('Parameter %s: value out of bounds'%pname)
827
829 if 'fnspecs' in kw:
830 fs_args['fnspecs'] = ensureStrArgDict(kw['fnspecs'])
831 self.foundKeys += 1
832
834 fs_args['targetlang'] = theGenSpecHelper(self).lang
835 if 'compiler' in kw:
836 if fs_args['targetlang'] == 'python':
837 print "Warning: redundant option 'compiler' for python target"
838 self._compiler = kw['compiler']
839 self.foundKeys += 1
840 elif fs_args['targetlang'] != 'python':
841 osname = os.name
842
843 if osname == 'nt':
844 self._compiler = 'mingw32'
845 elif osname == 'mac':
846 self._compiler = 'mwerks'
847 elif osname == 'posix' or osname == 'unix':
848 self._compiler = 'unix'
849 elif osname == 'os2emx':
850 self._compiler = 'emx'
851 else:
852 self._compiler = ''
853 else:
854 self._compiler = ''
855
857 if 'vfcodeinsert_start' in kw:
858 fs_args['codeinsert_start'] = kw['vfcodeinsert_start']
859 self.foundKeys += 1
860 if 'vfcodeinsert_end' in kw:
861 fs_args['codeinsert_end'] = kw['vfcodeinsert_end']
862 self.foundKeys += 1
863
865
866 if 'system' in kw:
867 self._solver = kw['system']
868 try:
869 fs_args['ignorespecial'].append(self._solver.name)
870 except:
871 raise TypeError("Invalid solver system provided")
872 self.foundKeys += 1
873 if self.pars:
874
875
876 parlist = self.pars.keys()
877 parstr = "".join(["'%s': %s, "%(parname,parname) \
878 for parname in parlist])
879 if 'codeinsert_start' in fs_args:
880 fs_args['codeinsert_start'] = \
881 ' %s.set(pars={%s})\n'%(self._solver.name, parstr) \
882 + fs_args['codeinsert_start']
883 else:
884 fs_args['codeinsert_start'] = \
885 ' %s.set(pars={%s})\n'%(self._solver.name, parstr)
886 else:
887 self._solver = None
888
889
891 """Return detailed information about the Generator
892 specification."""
893
894 if verbose == 0:
895 outputStr = "Generator "+self.name
896 else:
897 outputStr = '**************************************************'
898 outputStr += '\n Generator '+self.name
899 outputStr +='\n**************************************************'
900 outputStr +='\nType : ' + className(self)
901 outputStr +='\nIndependent variable interval: ' + str(self.indepvariable.depdomain)
902 outputStr +='\nGlobal t0 = ' + str(self.globalt0)
903 outputStr +='\nInterval endpoint check level = ' + str(self.checklevel)
904 outputStr +='\nDimension = ' + str(self.dimension)
905 outputStr +='\n'
906 if isinstance(self.funcspec, FuncSpec):
907 outputStr += self.funcspec._infostr(verbose)
908 if verbose == 2:
909 outputStr += '\nVariables` validity intervals:'
910 for v in self.variables.values():
911 outputStr += '\n ' + str(v.depdomain)
912 if self.eventstruct is not None:
913 outputStr += '\nEvents defined:'
914 outputStr += '\n ' + str(self.eventstruct.events.keys())
915 if self._modeltag is not None:
916 outputStr += '\nAssociated Model: ' + self._modeltag.name
917 if verbose > 0:
918 outputStr += '\n'
919 return outputStr
920
921
923 if self.eventstruct is not None:
924 for evname, ev in self.eventstruct.events.iteritems():
925 print evname + ":\n" + ev._funcstr
926 print "\n"
927
928
930 print self.funcspec.spec[0]
931
932
934 print self.funcspec.auxspec[0]
935
936
938 if auxfnname is None:
939 retdict = {}
940 for aname, aspec in self.funcspec.auxfns.iteritems():
941 retdict[aname] = aspec[0]
942 info(retdict)
943 else:
944 try:
945 print self.funcspec.auxfns[auxfnname][0]
946 except KeyError:
947 raise NameError("Aux function %s not found"%auxfnname)
948
949
952
953
954 __str__ = __repr__
955
956
958 try:
959 assert self.dimension > 0
960 assert len(self.variables) == self.dimension
961
962 assert self.indepvariable.name == 't'
963 assert self.funcspec
964 assert self.checklevel in range(4)
965
966 if self.pars:
967 for name in self.pars:
968 assert isinstance(self.pars[name], _num_types)
969 assert type(self.pars[name]) == self._registry[name]
970 if self.inputs:
971 for subjectname, obj in self.inputs.iteritems():
972
973
974
975 assert self.contains(obj.indepdomain,
976 self.indepvariable.indepdomain, 0)
977
978 assert type(obj) == self._registry[subjectname]
979 for name in self.variables:
980 assert self.variables[name].__class__ == self._registry[name]
981 dummy = self.indepvariable(self.tdata[0])
982
983
984 if isinstance(self.funcspec, FuncSpec):
985 varnames = self.variables.keys()
986 fsvars = self.funcspec.vars
987 if len(varnames) > 1:
988 varnames.sort()
989 fsvars.sort()
990 assert varnames == fsvars, ('Inconsistency with funcspec '
991 'variable names')
992 else:
993 assert varnames == fsvars
994 parnames = self.pars.keys()
995 fspars = self.funcspec.pars
996 if len(parnames) > 1:
997 parnames.sort()
998 fspars.sort()
999 assert parnames == fspars, ('Inconsistency with funcspec '
1000 'parameter names')
1001 else:
1002 assert parnames == fspars
1003 if self.inputs:
1004 inputnames = self.inputs.keys()
1005 fsinputs = self.funcspec.inputs
1006 if len(inputnames) > 1:
1007 inputnames.sort()
1008 fsinputs.sort()
1009 assert inputnames == fsinputs, ('Inconsistency with funcspec'
1010 ' input names')
1011 else:
1012 assert inputnames == fsinputs
1013 else:
1014 assert len(self.funcspec) == self.dimension
1015 except:
1016 print 'Invalid system specification'
1017 raise
1018
1019
1020
1036
1037
1039 """Internal method for indicating whether this Generator is currently
1040 being used as part of a hybrid dybnamical system calculation"""
1041 self._for_hybrid_DS = state
1042
1043
1045 """_register names and types of sub-system variables (including
1046 Generator variables), pars and external inputs.
1047
1048 Names must be unique for the Generator.
1049 """
1050
1051 if isinstance(items, dict):
1052
1053 for name, v in items.iteritems():
1054 if isinstance(self.funcspec, FuncSpec):
1055 assert name in self.funcspec.vars \
1056 or name in self.funcspec.auxvars \
1057 or name in self.funcspec.pars \
1058 or name in self.funcspec.inputs, \
1059 ("Generator = '"
1060 +self.name+"': name "+name+" not found in "
1061 "functional specification declaration")
1062 if name not in self._registry:
1063 self._registry[name] = type(v)
1064 else:
1065 raise ValueError('The name `' + name + '` of type `'
1066 + type(v).__name__ +
1067 '` already exists in the registry')
1068 if isinstance(v, Variable) and \
1069 name in self.variables or name in self.inputs:
1070 self._callnames.append(name)
1071 self._callnames.sort()
1072 elif isinstance(items, Variable) and items.name == 't':
1073
1074 if items.name not in self._registry:
1075 self._registry[items.name] = type(items)
1076 else:
1077 raise ValueError('The reserved name `t` has already'
1078 ' been declared to the registry')
1079 else:
1080 raise TypeError('Expected dictionary or independent variable in '
1081 'argument to _register()')
1082
1084
1085
1086
1087 self.eventstruct = EventStruct()
1088 if 'enforcebounds' in kw:
1089 if 'activatedbounds' in kw:
1090 ab = kw['activatedbounds']
1091 self.foundKeys += 1
1092 else:
1093 ab = None
1094 if kw['enforcebounds']:
1095 self._makeBoundsEvents(precise=True, activatedbounds=ab)
1096 self.foundKeys += 1
1097 if 'events' in kw:
1098 self._addEvents(kw['events'])
1099 self.foundKeys += 1
1100
1101
1103 if isinstance(evs, list):
1104 map(self.eventstruct.add, copy(evs))
1105 elif isinstance(evs, Event):
1106
1107 self.eventstruct.add(evs)
1108 else:
1109 raise TypeError('Unsupported type of argument for event '
1110 'structure')
1111
1112
1113 - def _makeBoundsEvents(self, precise=True, eventtol=1e-6,
1114 activatedbounds=None):
1115 events = []
1116
1117 alldoms = copy(self.pdomain)
1118 alldoms.update(self.xdomain)
1119 if self._eventPars != []:
1120
1121
1122 nonEvtPars = self.funcspec.pars
1123 else:
1124 nonEvtPars = self.funcspec.pars
1125 allnames = self.funcspec.vars + nonEvtPars
1126 if activatedbounds in (None,{}):
1127 activatedbounds = {}.fromkeys(allnames, (False,False))
1128 for xname, xdom in alldoms.iteritems():
1129 if xname not in allnames:
1130
1131 continue
1132 xdlo = xdom[0]
1133 xdhi = xdom[1]
1134 evname = xname+"_domlo"
1135 evargs = {'term': True,
1136 'precise': precise,
1137 'name': evname,
1138 'eventtol': eventtol,
1139 'eventdelay': eventtol*10,
1140 'eventinterval': eventtol*20,
1141 'xdomain': self.xdomain,
1142 'pdomain': self.pdomain}
1143 try:
1144 evargs['active'] = activatedbounds[xname][0] and isfinite(xdlo)
1145 except KeyError:
1146 evargs['active'] = False
1147 evstr = xname + '-' + 'getbound("%s", 0)'%xname
1148 ev = Events.makeZeroCrossEvent(evstr, -1, evargs, [xname],
1149 targetlang=self.funcspec.targetlang,
1150 reuseterms=self.funcspec.reuseterms)
1151 events.append(ev)
1152 evname = xname+"_domhi"
1153 evargs = {'term': True,
1154 'precise': precise,
1155 'name': evname,
1156 'eventtol': eventtol,
1157 'eventdelay': eventtol*10,
1158 'eventinterval': eventtol*20,
1159 'xdomain': self.xdomain,
1160 'pdomain': self.pdomain}
1161 try:
1162 evargs['active'] = activatedbounds[xname][1] and isfinite(xdhi)
1163 except KeyError:
1164 evargs['active'] = False
1165 evstr = xname + '-' + 'getbound("%s", 1)'%xname
1166 ev = Events.makeZeroCrossEvent(evstr, 1, evargs, [xname],
1167 targetlang=self.funcspec.targetlang,
1168 reuseterms=self.funcspec.reuseterms)
1169 events.append(ev)
1170 if events != []:
1171 self._addEvents(events)
1172
1173
1174 - def set(self, **kw):
1175 """Set generic parameters."""
1176
1177 if len(kw) > 0:
1178 if 'globalt0' in kw:
1179 self.globalt0 = kw['globalt0']
1180 try:
1181 self.eventstruct.setglobalt0(self.globalt0)
1182 except AttributeError:
1183
1184 pass
1185 if 'checklevel' in kw:
1186 self.checklevel = kw['checklevel']
1187 if hasattr(self, 'algparams'):
1188 self.algparams['checkBounds'] = kw['checklevel']
1189 if 'abseps' in kw:
1190 self._abseps = kw['abseps']
1191 if remain(kw.keys(), ['globalt0', 'checklevel', 'abseps']) != []:
1192 raise PyDSTool_KeyError('Invalid keywords passed')
1193
1194
1196 """Set initialconditions attribute of all generator's events, in
1197 case event uses auxiliary functions that access this information."""
1198 try:
1199 evs = self.eventstruct.events.values()
1200 except AttributeError:
1201
1202 pass
1203 else:
1204 for ev in evs:
1205 ev.initialconditions = self._FScompatibleNames(ics)
1206 ev.globalt0 = gt0
1207
1208
1210 try:
1211 self.eventstruct.resetEvtimes()
1212 except AttributeError:
1213
1214 pass
1215
1217 """Reset any high level (Python) events in Generator"""
1218 try:
1219
1220
1221 self.eventstruct.resetHighLevelEvents(0, state=state)
1222 except AttributeError:
1223
1224 pass
1225
1226
1227
1229 return self.globalt0 + t
1230
1232 return self.initialconditions[varname]
1233
1235 if x>0:
1236 return 1
1237 else:
1238 return 0
1239
1241 if c:
1242 return e1
1243 else:
1244 return e2
1245
1247 return self._var_namemap[varname]
1248
1249
1251 """Generate indices mapping.
1252
1253 This creates a mapping from the names of variables,
1254 pars and inputs, to indices in the arrays used for
1255 refering to the internal (dynamic) call methods."""
1256
1257 if gentypes is not None:
1258 if isinstance(gentypes, str):
1259 gentypes = [gentypes]
1260 for s in gentypes:
1261 assert s in ['variables', 'inputs', 'pars'], \
1262 ('Incorrect type string for _generate_ixmaps')
1263 else:
1264
1265 gentypes = ['variables', 'inputs', 'pars']
1266
1267
1268 if 'variables' in gentypes:
1269 self._var_ixmap = sortedDictKeys(self.variables,
1270 self.funcspec.vars)
1271 self._var_namemap = invertMap(self._var_ixmap)
1272 if 'pars' in gentypes:
1273 if self.pars:
1274 self._parameter_ixmap = sortedDictKeys(self.pars)
1275 self._parameter_namemap = invertMap(self._parameter_ixmap)
1276 else:
1277 self._parameter_ixmap = []
1278 self._parameter_namemap = {}
1279 if 'inputs' in gentypes:
1280 if self.inputs:
1281 self._inputs_ixmap = \
1282 sortedDictKeys(self.inputs)
1283 self._inputs_namemap = invertMap(self._inputs_ixmap)
1284 else:
1285 self._inputs_ixmap = []
1286 self._inputs_namemap = {}
1287
1288
1289 - def contains(self, interval, val, checklevel=2):
1290 """Interval containment test"""
1291
1292 if checklevel == 0:
1293
1294
1295
1296
1297 return True
1298 elif checklevel == 2:
1299
1300 testresult = interval.contains(val)
1301 if testresult is contained:
1302 return True
1303 elif testresult is uncertain:
1304 self.diagnostics.warnings.append((W_UNCERTVAL, (val,interval)))
1305 return True
1306 else:
1307 return False
1308 elif checklevel == 1:
1309
1310 if interval.contains(val) is not notcontained:
1311 return True
1312 else:
1313 return False
1314 else:
1315
1316 if val in interval:
1317 return True
1318 else:
1319 return False
1320
1321
1322
1324 d = copy(self.__dict__)
1325 for fname, finfo in self._funcreg.iteritems():
1326 try:
1327 del d[fname]
1328 except KeyError:
1329 pass
1330 return d
1331
1332
1334 self.__dict__.update(state)
1335 if self._funcreg != {}:
1336 self.addMethods()
1337
1338
1340
1341
1342 try:
1343 for fname, finfo in self._funcreg.iteritems():
1344 try:
1345 delattr(eval(finfo[0]), fname)
1346 except AttributeError:
1347 pass
1348 except NameError:
1349
1350
1351
1352 pass
1353 if hasattr(self, 'eventstruct'):
1354 if self.eventstruct is not None:
1355 self.eventstruct.__del__()
1356 if self.indepvariable is not None:
1357 del self.indepvariable
1358 for v in self.variables.values():
1359 v.__del__()
1360 if hasattr(self, 'inputs'):
1361 for v in self.inputs.values():
1362 v.__del__()
1363 except AttributeError:
1364
1365 pass
1366 except NameError:
1367
1368 pass
1369
1370
1372 pickledself = pickle.dumps(self)
1373 return pickle.loads(pickledself)
1374
1375
1377 pickledself = pickle.dumps(self)
1378 return pickle.loads(pickledself)
1379
1380
1381
1382
1383
1385 "Abstract class for continuously-parameterized trajectory generators."
1386
1388
1389 assert isinputcts(self.indepvariable), ("self.indepvariable must be continuously-"
1390 "defined for this class")
1391
1394
1395
1396
1398 "Abstract class for discretely-parameterized trajectory generators."
1399
1401 assert isdiscrete(self.indepvariable), ("self.indepvariable must be discretely-"
1402 "defined for this class")
1403
1406
1407
1408
1409
1410
1414
1415
1417 """Generator specification helper - abstract class.
1418
1419 Used to help ModelConstructor translate abstract model specifications
1420 into concrete specifications specific to individual Generators."""
1421
1424
1425 - def add(self, genClass, symbolMapDict, lang, specType='RHSfuncSpec'):
1426 genName = className(genClass)
1427 if genName in self.gshDB:
1428 raise ValueError("Generator %s has already been declared"%genName)
1429 else:
1430 infoObj = GenSpecInfoObj()
1431 infoObj.genClass = genClass
1432 infoObj.symbolMap = symbolMapClass(symbolMapDict)
1433 infoObj.lang = lang
1434 infoObj.specType = specType
1435 if issubclass(genClass, ctsGen):
1436 infoObj.domain = Continuous
1437 elif issubclass(genClass, discGen):
1438 infoObj.domain = Discrete
1439 else:
1440 raise TypeError("Invalid Generator class")
1441 self.gshDB[genName] = infoObj
1442
1444 try:
1445 if isinstance(subject, str):
1446 return self.gshDB[subject]
1447 else:
1448 return self.gshDB[className(subject)]
1449 except KeyError:
1450 raise KeyError("Generator %s was not found in database"%str(subject))
1451
1453 return subject in self.gshDB or className(subject) in self.gshDB
1454
1455
1477
1478 global theGenSpecHelper
1479 theGenSpecHelper = GenSpecHelper()
1480