1 """Model Constructor classes.
2
3 Instantiate abstract Model Specifications into concrete simulation code.
4
5 Robert Clewley, September 2005.
6
7
8 Overview of steps that ModelConstructor takes:
9
10 1. Build Generators for each vector field
11 * Take flattened spec and select a compatible Generator type and language
12 * include check that compatibleGen selection actually exists,
13 and is compatible with specType/domain, and with cts/discrete
14 time variables)
15 * presently, each v.f. must have all the same Gen type
16 * Map things like x^y and x**y power specs -> pow(x,y)
17 and abs() function -> fabs() in C.
18
19 2. Given the specific target lang/Generator with a ModelSpec vector field
20 * Associate additional events, code inserts, reused vars
21 * Choose which domains map to domain verification events (certainly none that
22 are infinite -- although semi-infinite can be checked in one direction)
23 * All non-infinite domain ends would have an event created for them, but
24 default is for them to be inactive.
25
26 The 'mspec' argument to the GeneratorConstructor class must be a complete
27 ModelSpec (up to introduction of global references including definitions of
28 external inputs).
29
30 The resulting model output from getModel contains the ModelSpec mspec in
31 order to use its structure in resolving information about the relationship
32 between variables.
33 """
34
35
36 from errors import *
37 from common import *
38 from utils import info, remain, intersect
39 import Model, Generator, ModelSpec, Symbolic, Events, MProject
40 from parseUtils import symbolMapClass, NAMESEP, isNumericToken
41
42
43 from numpy import Inf, NaN, isfinite, array, \
44 arange, zeros, ones, concatenate, swapaxes, take, \
45 sometrue, alltrue, any, all
46 import numpy, scipy, math
47 import sys, types, copy
48
49
50 __all__ = ['GeneratorConstructor', 'ModelConstructor', 'makeModelInfo',
51 'makeModelInfoEntry', 'embed', 'EvMapping', 'makeEvMapping',
52 'GDescriptor', 'MDescriptor']
53
54
55
56 mathNameMap = dict(zip(Symbolic.allmathnames_symbolic,
57 Symbolic.allmathnames))
58
59
86
87
88
90 """All-in-one descriptor class for single Generators, and information
91 necessary to be able to build a Model object using a ModelConstructor call
92 -- i.e. for forming a valid 'generatorspecs' field.
93 """
94 _validKeys = ('changelog', 'orig_name', 'modelspec', 'description',
95 'algparams', 'target', 'withStdEvts', 'stdEvtArgs', 'withJac',
96 'withJacP', 'reuseTerms', 'eventPars', 'unravelInfo',
97 'userEvents', 'userFunctions', 'userEventMaps', 'options')
98 _defaults = {'description': '', 'withStdEvts': False, 'withJac': False,
99 'withJacP': False, 'unravelInfo': True, 'options': {}}
100 _checkKeys = ('target')
101
102
103
104
105
107 """Return object in model spec named using the hierarchical
108 naming format.
109 """
110 return self.modelspec[hier_name]
111
113 return self.modelspec.search(hier_name)
114
116 validated = isinstance(self.modelspec, ModelSpec.ModelSpec) and \
117 self.modelspec.isComplete() and \
118 self.modelspec.isDefined(ignoreInputs=True)
119 freeSymbols = self.modelspec.freeSymbols
120 return (validated, freeSymbols)
121
123 return self.validate()[0] and self.target is not None
124
125
126
128 """All-in-one descriptor class for hybrid model definitions and information
129 necessary to be able to build a Model object using a ModelConstructor call.
130
131 generatorspecs should be a dictionary of gen modelspec names -> modelspecs.
132 """
133 _validKeys = ('changelog', 'orig_name', 'name', 'generatorspecs',
134 'description', 'abseps', 'activateAllBounds',
135 'checklevel', 'tdata', 'indepvar', 'icvalues', 'parvalues',
136 'inputs', 'unravelInfo',
137 'userevents', 'userfns', 'reuseTerms',
138 'withJac', 'withJacP',
139 'eventtol', 'eventPars',
140 'withStdEvts', 'stdEvtArgs')
141 _defaults = {'description': '', 'indepvar': ('t', [-Inf,Inf]),
142 'checklevel': 2, 'activateAllBounds': False,
143 'generatorspecs': {}, 'icvalues': {}, 'parvalues': {},
144 'inputs': {}, 'unravelInfo': True}
145 _checkKeys = ('icvalues', 'parvalues', 'inputs')
146
148 assert hasattr(self.generatorspecs, 'values') and \
149 hasattr(self.generatorspecs, 'keys')
150 validated = alltrue([isinstance(gd, GDescriptor) for \
151 gd in self.generatorspecs.values()])
152
153
154 inconsistencies = []
155 return (validated, inconsistencies)
156
158 valid = self.validate()[0]
159 vars_i_all = True
160 pars_i_all = True
161 inps_i_all = True
162 for ms in self.generatorspecs.values():
163 all_vars = ms.modelspec.search('Var')
164 dyn_vars = [v for v in all_vars if ms.modelspec._registry[v].obj.specType == 'RHSfuncSpec']
165 vars_i = alltrue([varname in self.icvalues for \
166 varname in dyn_vars])
167 pars_i = alltrue([(parname in self.parvalues or \
168 par.spec.specStr !='') \
169 for parname, par in ms.modelspec.pars.items()])
170 inps_i = alltrue([inpname in self.inputs for \
171 inpname in ms.modelspec.inputs])
172 if verbose:
173 if not vars_i:
174 print ms.modelspec.name, "Some ICs missing"
175 if not pars_i:
176 print ms.modelspec.name, "Some param values missing"
177 if not inps_i:
178 print ms.modelspec.name, "Some input values missing"
179 vars_i_all = vars_i_all and vars_i
180 pars_i_all = pars_i_all and pars_i
181 inps_i_all = inps_i_all and inps_i
182 return valid and vars_i_all and pars_i_all and inps_i_all
183
185 if name in self.generatorspecs:
186 return self.generatorspecs[name]
187 else:
188 raise KeyError('Generator %s does not exist in registry'%name)
189
191 self.generatorspecs[gd.modelspec.name] = gd
192
193
194
195
196
198 - def __init__(self, mspec=None, userevents=None, userfns=None,
199 unravelInfo=True, inputs=None, checklevel=2,
200 activateAllBounds=False, activatedBounds=None,
201 targetGen="", algparams=None, indepvar=('t',[-Inf,Inf]),
202 tdata=None, parvalues=None, icvalues=None, reuseTerms=None,
203 options=None, abseps=None, eventPars=None, preReuse=False,
204 preReuseTerms=None, preFlat=False):
205 """Notes for initialization arguments:
206
207 mspec : corresponding ModelSpec, for reference
208
209 userevents : list of Event objects
210 userfns : dictionary of named user functions specs
211 inputs : dictionary of Variable objects
212 algparams : dictionary of algorithmic parameters for Generator
213 parvalues : dictionary of parameter values
214 icvalues : dictionary of initial condition values
215 reuseterms : dictionary of reused terms in specifications
216
217 targetGen : STRING name of any compatible Generator class, e.g. 'Vode_ODEsystem'
218
219 eventPars : list of parameter names associated solely with events
220
221 options : Internal use by ModelConstructor
222 preReuse : Internal use
223 preFlat : Internal use
224
225 RETURNS: getGenerator method returns a Generator of the specified class
226 """
227 self.mspec = mspec
228
229
230 if userevents is None:
231 self.userevents = []
232 else:
233 self.userevents = copy.copy(userevents)
234 if userfns is None:
235 self.userfns = {}
236 else:
237
238
239 self.userfns = Symbolic.ensureStrArgDict(copy.copy(userfns))
240 self.unravelInfo = unravelInfo
241 if isinstance(targetGen, str):
242 self.targetGen = targetGen
243 else:
244 raise TypeError("targetGen argument must be a string")
245 if algparams is None:
246 self.algparams = {}
247 else:
248 self.algparams = copy.copy(algparams)
249 self.indepvarname = indepvar[0]
250 self.indepvardomain = indepvar[1]
251 self.tdata = tdata
252 if inputs is None:
253 self.inputs = {}
254 else:
255 self.inputs = copy.copy(inputs)
256 if parvalues is None:
257 self.parvalues = {}
258 else:
259 self.parvalues = copy.copy(parvalues)
260 if icvalues is None:
261 self.icvalues = {}
262 else:
263 self.icvalues = copy.copy(icvalues)
264 self.checklevel = checklevel
265 self.forcedAuxVars = []
266 if options is None:
267 self.optDict = {}
268 else:
269 self.optDict = copy.copy(options)
270 if reuseTerms is None:
271 self.reuseTerms = {}
272 else:
273 self.reuseTerms = copy.copy(reuseTerms)
274 self.vfcodeinsert_start = ""
275 self.vfcodeinsert_end = ""
276 if activatedBounds is None:
277 self.activatedBounds = {}
278 else:
279 self.activatedBounds = copy.copy(activatedBounds)
280 self.activateAllBounds = activateAllBounds
281 if abseps is None:
282 self.abseps = 1e-13
283 else:
284 self.abseps = abseps
285
286 if eventPars is None:
287 self.eventPars = []
288 else:
289 self.eventPars = copy.copy(eventPars)
290 self.preReuse = preReuse
291 if preReuseTerms is None:
292 self.preReuseTerms = {}
293 else:
294 self.preReuseTerms = copy.copy(preReuseTerms)
295 self.preFlat = preFlat
296
297
299 self.forcedAuxVars = vlist
300
302 self.reuseTerms = rdict
303 self.preReuse = False
304
306 self.vfcodeinsert_start = codestr
307
309 self.vfcodeinsert_end = codestr
310
312
313 self.optDict = optDict
314
315 - def addEvents(self, evtarg, eventPars=None):
316 if isinstance(evtarg, list):
317 self.userevents.extend(evtarg)
318 elif isinstance(evtarg, Events.Event):
319 self.userevents.append(evtarg)
320 else:
321 raise TypeError("Invalid event or event list")
322
323 if eventPars is not None and eventPars != [] and eventPars != '':
324 if isinstance(eventPars, list):
325 self.eventPars.extend(eventPars)
326 elif isinstance(eventPars, str):
327 self.eventPars.append(eventPars)
328
329
332
334 """which_bounds argument is either 'lo', 'hi', or a pair ('lo', 'hi').
335 Calling with no arguments activates all bounds."""
336 if varname is None and which_bounds=='all':
337 self.activateAllBounds = True
338 else:
339 entry = [False,False]
340 if 'hi' in which_bounds:
341 entry[1] = True
342 if 'lo' in which_bounds:
343 entry[0] = True
344 self.activatedBounds[varname] = entry
345
346
348 """Build and return a Generator instance from an abstract
349 specification."""
350
351
352
353 globalRefs = [self.indepvarname] + self.inputs.keys()
354 self.mspec.eventPars = copy.copy(self.eventPars)
355
356 if not self.preFlat:
357 try:
358 flatspec = self.mspec.flattenSpec(multiDefUnravel=self.unravelInfo,
359 globalRefs=globalRefs,
360 ignoreInputs=True)
361 except KeyboardInterrupt:
362 raise
363 except:
364 print "Problem flattening Model Spec '%s'"%self.mspec.name
365 print "Global refs: ", globalRefs
366 raise
367 else:
368 flatspec = self.mspec.flatSpec
369
370 FScompatibleNames = flatspec['FScompatibleNames']
371 FScompatibleNamesInv = flatspec['FScompatibleNamesInv']
372
373 if self.targetGen in self.mspec.compatibleGens \
374 and self.targetGen in Generator.theGenSpecHelper:
375 gsh = Generator.theGenSpecHelper(self.targetGen)
376 if gsh.lang not in self.mspec.targetLangs:
377 raise ValueError("Incompatible target language between supplied"
378 " ModelSpec and target Generator")
379 else:
380 print "ModelSpec's compatible Generators:", \
381 ", ".join(self.mspec.compatibleGens)
382 print "ModelConstructor target Generator:", self.targetGen
383 raise ValueError("Target Generator mismatch during generator "
384 "construction")
385 self.targetLang = gsh.lang
386
387 a = args(abseps=self.abseps)
388 a.pars = {}
389 parnames = flatspec['pars'].keys()
390 for p, valstr in flatspec['pars'].iteritems():
391 if valstr == '':
392 if FScompatibleNamesInv(p) not in self.parvalues:
393 raise ValueError("Parameter %s is missing a value"%FScompatibleNamesInv(p))
394 else:
395 if valstr == p:
396
397 a.pars[p] = 0
398
399 else:
400 try:
401 a.pars[p] = float(valstr)
402 except ValueError:
403 raise ValueError("Invalid parameter value set in ModelSpec"
404 " for '%s', value: %s"%(p,valstr))
405
406
407 for p, val in self.parvalues.iteritems():
408 try:
409 pr = FScompatibleNames(p)
410 except KeyError:
411 raise NameError("Parameter '%s' missing from ModelSpec"%p)
412 if pr not in flatspec['pars']:
413 raise NameError("Parameter '%s' missing from ModelSpec"%p)
414 a.pars[pr] = val
415 if self.icvalues != {}:
416 a.ics = {}
417 for v, val in self.icvalues.iteritems():
418 try:
419 vr = FScompatibleNames(v)
420 except KeyError:
421 raise NameError("Variable '%s' missing from ModelSpec"%v)
422 if vr not in flatspec['vars']:
423 raise NameError("Variable '%s' missing from ModelSpec"%v)
424 a.ics[vr] = val
425 a.tdomain = self.indepvardomain
426 if self.tdata is not None:
427 a.tdata = self.tdata
428
429 a.inputs = self.inputs
430 a.name = self.mspec.name
431 xdomain = {}
432 xtype = {}
433 pdomain = {}
434 for k, d in flatspec['domains'].iteritems():
435
436 if k in flatspec['vars']:
437 if isinstance(d[2], _num_types):
438 xdomain[k] = d[2]
439 elif len(d[2]) == 2:
440 xdomain[k] = d[2]
441 else:
442 raise ValueError("Domain spec must be a valid interval")
443 xtype[k] = d[0]
444 elif k in flatspec['pars']:
445 assert len(d[2]) == 2, "Domain spec must be a valid interval"
446 pdomain[k] = d[2]
447
448
449
450
451 a.xdomain = xdomain
452 a.pdomain = pdomain
453 a.xtype = xtype
454 exp_vars = [v for (v,t) in flatspec['spectypes'].items() \
455 if t == 'ExpFuncSpec']
456 rhs_vars = [v for (v,t) in flatspec['spectypes'].items() \
457 if t == 'RHSfuncSpec']
458 imp_vars = [v for (v,t) in flatspec['spectypes'].items() \
459 if t == 'ImpFuncSpec']
460 if gsh.specType == 'RHSfuncSpec':
461 assert imp_vars == [], "Cannot use implicitly defined variables"
462
463 varnames = rhs_vars
464 auxvarnames = exp_vars
465 elif gsh.specType == 'ExpFuncSpec':
466 assert imp_vars == [], "Cannot use implicitly defined variables"
467 assert rhs_vars == [], "Cannot use RHS-type variables"
468 varnames = exp_vars
469 invalid_auxvars = remain(self.forcedAuxVars, varnames)
470 if invalid_auxvars == []:
471
472
473 varnames = remain(varnames, self.forcedAuxVars)
474 auxvarnames = self.forcedAuxVars
475 else:
476 print "Invalid auxiliary variable names:"
477 print invalid_auxvars
478
479 elif gsh.specType == 'ImpFuncSpec':
480 assert rhs_vars == [], "Cannot use RHS-type variables"
481 varnames = imp_vars
482 auxvarnames = exp_vars
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502 if not self.preReuse:
503 reuseTerms, subsExpr = processReused(varnames+auxvarnames, auxvarnames,
504 flatspec, self.mspec._registry,
505 FScompatibleNames, FScompatibleNamesInv)
506
507 clash_reused = intersect(reuseTerms.keys(), self.reuseTerms.keys())
508 if clash_reused != []:
509 print "Clashing terms:", clash_reused
510 raise ValueError("User-supplied reused terms clash with auto-"
511 "generated terms")
512
513 for v in subsExpr:
514 flatspec['vars'][v] = subsExpr[v]
515 reuseTerms.update(self.reuseTerms)
516 a.reuseterms = reuseTerms
517
518 else:
519 a.reuseterms = self.preReuseTerms
520
521 a.varspecs = dict(zip(varnames+auxvarnames, [flatspec['vars'][v] \
522 for v in varnames+auxvarnames]))
523 a.auxvars = auxvarnames
524 a.fnspecs = self.userfns
525 try:
526 a.fnspecs.update(FScompatibleNames(flatspec['auxfns']))
527 except KeyError:
528
529 pass
530 a.checklevel = self.checklevel
531 a.algparams = self.algparams
532 if self.vfcodeinsert_start != "":
533 a.vfcodeinsert_start = self.vfcodeinsert_start
534 if self.vfcodeinsert_end != "":
535 a.vfcodeinsert_end = self.vfcodeinsert_end
536
537 events = []
538
539
540 nonEvtParnames = remain(parnames, self.eventPars)
541 domnames = varnames+nonEvtParnames
542 for xname in domnames:
543 hier_name_lo = FScompatibleNamesInv(xname)+"_domlo"
544 FScompatibleNames[hier_name_lo] = xname+"_domlo"
545 FScompatibleNamesInv[xname+"_domlo"] = hier_name_lo
546 hier_name_hi = FScompatibleNamesInv(xname)+"_domhi"
547 FScompatibleNames[hier_name_hi] = xname+"_domhi"
548 FScompatibleNamesInv[xname+"_domhi"] = hier_name_hi
549 if self.activateAllBounds:
550 a.activatedbounds = {}.fromkeys(domnames,(True,True))
551 else:
552 a.activatedbounds = self.activatedBounds
553 a.enforcebounds = True
554
555 for e in self.userevents:
556 if e not in events:
557 events.append(e)
558 else:
559 raise ValueError("Repeated event definition!")
560
561 a.events = events
562
563 for k,v in self.optDict.iteritems():
564 if hasattr(a, k):
565 raise KeyError("'%s' already exists as a Generator argument"%k)
566 a.k = v
567 a.FScompatibleNames = FScompatibleNames
568 a.FScompatibleNamesInv = FScompatibleNamesInv
569
570
571 a.eventPars = self.eventPars
572
573
574 self.conargs = copy.copy(a)
575
576
577
578
579
580 try:
581 return gsh.genClass(args(**filteredDict(a.__dict__,
582 gsh.genClass._needKeys+gsh.genClass._optionalKeys)))
583 except:
584 print "Problem initializing target Generator '%s'"%self.targetGen
585 raise
586
587
589 del self.userevents
590 if hasattr(self, 'conargs'):
591 try:
592 del self.conargs.events
593 except AttributeError:
594 pass
595
596
597
598
599
601 - def __init__(self, name, userevents=None, userfns=None, unravelInfo=True,
602 inputs=None, checklevel=2, activateAllBounds=False,
603 generatorspecs=None, indepvar=('t',[-Inf,Inf]),
604 parvalues=None, icvalues=None, tdata=None, reuseTerms=None,
605 withJac=None, withJacP=None, featureDicts=None,
606 abseps=None, eventtol=None, eventPars=None,
607 withStdEvts=None, stdEvtArgs=None):
608 """Notes for initialization arguments.
609
610 name : string name of this ModelConstructor
611
612 ** The following are applied to all Generators
613
614 activateAllBounds : Boolean
615 checklevel : integer
616 indepvar : pair of (independent var name, pair giving domain interval)
617
618 ** The following are dictionaries keyed by Generator name, with values:
619
620 generatorspecs : ModelSpecs
621 userevents : list of Event objects
622 userfns : dictionary of named user functions specs
623 inputs : dictionary of Variable objects
624 algparams : dictionary of algorithmic parameters for Generator
625 parvalues : dictionary of parameter values
626 icvalues : dictionary of initial condition values
627 reuseterms : dictionary of reused terms in specifications
628 eventPars : list of parameter names associated solely with events
629 withStdEvts : Boolean for making standard events (bounds & turning points)
630 stdEvtArgs : arguments for the standard events
631 featureDicts : dictionary of Features for making each Model Interface
632 withJac : Boolean for making Jacobian
633 withJacP : Boolean for making Jacobian with respect to parameters
634
635 RETURNS: Nothing, but getModel method returns an instantiated Model
636 object when the specifications are complete and consistent.
637 """
638 self.name = name
639 self.forcedIntVars = []
640 if generatorspecs is None:
641 self._generators = {}
642 else:
643 self._generators = copy.copy(generatorspecs)
644 if userevents is None:
645 self._events = {}
646 else:
647 self._events = copy.copy(userevents)
648 if userfns is None:
649 self._funcs = {}
650 else:
651 self._funcs = copy.copy(userfns)
652 self.indepvar = indepvar
653 self.indepvarname = self.indepvar[0]
654 self.eventmaps = {}
655 if reuseTerms is None:
656 self.reuseTerms = {}
657 else:
658 self.reuseTerms = copy.copy(reuseTerms)
659 if inputs is None:
660 self.inputs = {}
661 else:
662 self.inputs = copy.copy(inputs)
663 if parvalues is None:
664 self.parvalues = {}
665 else:
666 self.parvalues = copy.copy(parvalues)
667 if icvalues is None:
668 self.icvalues = {}
669 else:
670 self.icvalues = copy.copy(icvalues)
671 self.tdata = tdata
672 self.checklevel = checklevel
673 self.unravelInfo = unravelInfo
674 self.activatedBounds = {}
675 self.activateAllBounds = activateAllBounds
676 if abseps is None:
677 abseps = 1e-13
678 self.abseps = abseps
679 if eventtol is None:
680 eventtol = abseps * 1e3
681 self.eventtol = eventtol
682 if withJac is None:
683 self.withJac = {}
684 else:
685 self.withJac = copy.copy(withJac)
686 if withJacP is None:
687 self.withJacP = {}
688 else:
689 self.withJacP = copy.copy(withJacP)
690 if withStdEvts is None:
691 self.withStdEvts = {}
692 else:
693 self.withStdEvts = copy.copy(withStdEvts)
694 if stdEvtArgs is None:
695 self.stdEvtArgs = {}
696 else:
697 self.stdEvtArgs = copy.copy(stdEvtArgs)
698 if featureDicts is None:
699 self.featureDicts = {}
700 else:
701 self.featureDicts = copy.copy(featureDicts)
702
703
704
705 self.preFlat = {}
706 self.preReuse = {}
707 self.preReuseTerms = {}
708 for g in self._generators:
709 self.preFlat[g] = False
710 self.preReuse[g] = False
711 self.preReuseTerms[g] = {}
712
713 if self.withJac != {}:
714 self.createJac()
715 if self.withJacP != {}:
716 self.createJacP()
717
718
719
720 if eventPars is None:
721 self._eventPars = {}
722 else:
723 self._eventPars = copy.copy(eventPars)
724
725 if self.withStdEvts != {}:
726 self.preprocessFlatten()
727 self.preprocessReuseTerms()
728 self.createStdEvts()
729
730
732 return "ModelConstructor %s"%self.name
733
735 globalRefs = [self.indepvarname] + self.inputs.keys()
736 for g in self._generators:
737 gspec = self._generators[g]['modelspec']
738 try:
739 flatspec = gspec.flattenSpec(multiDefUnravel=self.unravelInfo, globalRefs=globalRefs,
740 ignoreInputs=True)
741 except KeyboardInterrupt:
742 raise
743 except:
744 print "Problem flattening Model Spec %s"%self.mspec.name
745 print "Global refs: ", globalRefs
746 raise
747 self.preFlat[g] = True
748
750 for g in self._generators:
751 gspec = self._generators[g]['modelspec']
752 assert self.preFlat[g]
753 flatspec = gspec.flatSpec
754 gsh = Generator.theGenSpecHelper(self._generators[g]['target'])
755
756 FScompatibleNames = flatspec['FScompatibleNames']
757 FScompatibleNamesInv = flatspec['FScompatibleNamesInv']
758
759 exp_vars = [v for (v,t) in flatspec['spectypes'].items() \
760 if t == 'ExpFuncSpec']
761 rhs_vars = [v for (v,t) in flatspec['spectypes'].items() \
762 if t == 'RHSfuncSpec']
763 imp_vars = [v for (v,t) in flatspec['spectypes'].items() \
764 if t == 'ImpFuncSpec']
765 if gsh.specType == 'RHSfuncSpec':
766 assert imp_vars == [], "Cannot use implicitly defined variables"
767
768 varnames = rhs_vars
769 auxvarnames = exp_vars
770 elif gsh.specType == 'ExpFuncSpec':
771 assert imp_vars == [], "Cannot use implicitly defined variables"
772 assert rhs_vars == [], "Cannot use RHS-type variables"
773 varnames = exp_vars
774
775
776
777
778
779
780
781
782
783
784 elif gsh.specType == 'ImpFuncSpec':
785 assert rhs_vars == [], "Cannot use RHS-type variables"
786 varnames = imp_vars
787 auxvarnames = exp_vars
788
789 reuseTerms, subsExpr = processReused(varnames+auxvarnames, auxvarnames,
790 flatspec, gspec._registry,
791 FScompatibleNames, FScompatibleNamesInv)
792
793 clash_reused = intersect(reuseTerms.keys(), self.reuseTerms.keys())
794 if clash_reused != []:
795 print "Clashing terms:", clash_reused
796 raise ValueError("User-supplied reused terms clash with auto-"
797 "generated terms")
798
799
800 for v in subsExpr:
801 flatspec['vars'][v] = subsExpr[v]
802 reuseTerms.update(self.reuseTerms)
803
804
805 self.preReuse[g] = True
806 self.preReuseTerms[g] = reuseTerms
807
808 - def addModelInfo(self, genSpec, genTarg, genAlgPars={}, unravelInfo={},
809 genOpts={}):
810 """genSpec can either be a complete ModelSpec description or a
811 string-based dictionary of definitions.
812 """
813 if isinstance(genSpec, dict):
814 genSpec = args(**genSpec)
815 if len(genAlgPars)==0:
816
817
818 if hasattr(genSpec, 'algparams'):
819 genAlgPars = genSpec['algparams']
820 if hasattr(genSpec, 'events'):
821 self.addEvents(genSpec.name, genSpec.events)
822 self._generators[genSpec.name] = args(modelspec=genSpec,
823 target=genTarg,
824 algparams=copy.copy(genAlgPars),
825 unravelInfo=copy.copy(unravelInfo),
826 options=copy.copy(genOpts))
827
829 evtArgsDefaults = {'eventtol': self.eventtol,
830 'eventdelay': self.eventtol*1e4,
831 'starttime': 0,
832 'term': False,
833 'active': False}
834
835 rhsEvtTypeList = ['val', 'deriv', 'stat']
836 expEvtTypeList = ['val']
837 withEvtParList = ['val', 'deriv']
838 evtDirList = [('inc', 1), ('dec', -1), ('neut', 0)]
839 specList = ['auxfns', 'vars']
840 evtParList = []
841 for g in self._generators:
842 targetLang = Generator.theGenSpecHelper(self._generators[g]['target']).lang
843 evtList = []
844 try:
845 makeEvts = self.withStdEvts[g]
846 except KeyError:
847 makeEvts = False
848 if makeEvts:
849 gspec = self._generators[g]['modelspec']
850 if not self.preFlat[g]:
851 print "Flattening"
852 gspec.flattenSpec()
853 fspec = gspec.flatSpec
854
855 FScNM = fspec['FScompatibleNames']
856 FScNMInv = fspec['FScompatibleNamesInv']
857
858 FSc_update_dict = {}
859 FScInv_update_dict = {}
860 try:
861 stdEvtArgs = self.stdEvtArgs[g]
862 except KeyError:
863 stdEvtArgs = evtArgsDefaults
864
865
866 evtTypeList = expEvtTypeList
867 for s in specList:
868 if s not in fspec.keys():
869 continue
870
871
872 if s == 'auxfns':
873 evtTypeList = expEvtTypeList
874 checkEvtType = False
875 else:
876 evtTypeList = []
877 checkEvtType = True
878
879 for f in fspec[s].keys():
880 if checkEvtType:
881 if fspec['spectypes'][f] == 'ExpFuncSpec':
882 evtTypeList = expEvtTypeList
883 elif fspec['spectypes'][f] == 'RHSfuncSpec':
884 evtTypeList = rhsEvtTypeList
885 else:
886 raise PyDSTool_ValueError("Don't know this "
887 "spec type.")
888
889
890 for evtType in evtTypeList:
891
892 for i in range(len(evtDirList)):
893
894 evtName = f + '_'+ evtType + '_' + evtDirList[i][0] + '_evt'
895 evtNameFSInv = FScNMInv(f) + '_'+ evtType + '_' + evtDirList[i][0] + '_evt'
896
897 if evtType in withEvtParList:
898 parname = evtName+'_p'
899 FScInv_update_dict[parname] = evtNameFSInv+'_p'
900 FSc_update_dict[evtNameFSInv+'_p'] = parname
901
902 par = Symbolic.Par(str(0), parname)
903 par.compatibleGens = gspec.compatibleGens
904
905
906 gspec.pars[parname] = par
907 fspec['pars'][parname] = 0
908
909
910
911 if s == 'vars' and evtType == 'val':
912 evtStr = f + ' - ' + parname
913 elif evtType in withEvtParList:
914 evtStr = fspec[s][f] + '-' + parname
915 else:
916 evtStr = fspec[s][f]
917
918 evtDir = evtDirList[i][1]
919 evtArgs = stdEvtArgs
920 evtArgs['name'] = evtName
921 evtSuccess = True
922
923
924 try:
925 if self.preReuse[g]:
926
927
928
929 reuseterms = {}
930 else:
931 reuseterms = {}
932 theEvt = Events.makeZeroCrossEvent(expr=evtStr,
933 dircode=evtDir,
934 argDict=evtArgs,
935 targetlang=targetLang,
936 flatspec=fspec,
937 reuseterms=reuseterms)
938 except ValueError, errinfo:
939 evtSuccess = False
940
941
942
943 if evtSuccess:
944 evtList.append(theEvt)
945
946
947
948 if evtType in withEvtParList:
949 evtParList.append(parname)
950
951
952 if evtList != []:
953 self.addEvents(g, evtList)
954
955
956 if evtParList != []:
957
958 FScNM.update(FSc_update_dict)
959 FScNMInv.update(FScInv_update_dict)
960 if g in self._eventPars.keys():
961 self._eventPars[g].extend(evtParList)
962 else:
963 self._eventPars[g] = evtParList
964
966 for g in self._generators:
967 if self.withJac[g]:
968 gspec = self._generators[g]['modelspec']
969
970
971 candidate_vars = gspec.funcSpecDict['vars']
972 vars = {}
973 auxvars = {}
974 for v in candidate_vars:
975 vname = str(v).replace('.','_')
976 if v.specType == 'RHSfuncSpec':
977 vars[vname] = gspec.flatSpec['vars'][vname]
978 elif v.specType == 'ExpFuncSpec':
979 auxvars[vname] = gspec.flatSpec['vars'][vname]
980 varnames = vars.keys()
981 varnames.sort()
982
983
984 varspecs = {}
985 for vn in varnames:
986 q = ModelSpec.QuantSpec('__temp__', vars[vn])
987 varspecs[vn] = str(q.eval(auxvars))
988
989
990 candidate_pars = gspec.funcSpecDict['pars']
991 parnames = []
992
993 try:
994 evtPars = self._eventPars[g]
995 except KeyError:
996 evtPars = []
997
998 for p in candidate_pars:
999 pname_with_dot = str(p)
1000 pname_no_dot = str(p).replace('.','_')
1001 if pname_with_dot in evtPars or pname_no_dot in evtPars:
1002 pass
1003 else:
1004 parnames.append(pname_no_dot)
1005 parnames.sort()
1006
1007 jacP = Symbolic.Diff([varspecs[vn] for vn in varnames],
1008 parnames).renderForCode()
1009 self.addFunctions(g, Symbolic.Fun(jacP, ['t'] + varnames,
1010 'Jacobian_pars'))
1012 for g in self._generators:
1013 if self.withJac[g]:
1014 gspec = self._generators[g].modelspec
1015
1016
1017 candidate_vars = gspec.funcSpecDict['vars']
1018 vars = {}
1019 auxvars = {}
1020 for v in candidate_vars:
1021 vname = str(v).replace('.','_')
1022 if v.specType == 'RHSfuncSpec':
1023 vars[vname] = gspec.flatSpec['vars'][vname]
1024 elif v.specType == 'ExpFuncSpec':
1025 auxvars[vname] = gspec.flatSpec['vars'][vname]
1026 varnames = vars.keys()
1027 varnames.sort()
1028
1029
1030 varspecs = {}
1031 for vn in varnames:
1032 q = ModelSpec.QuantSpec('__temp__', vars[vn])
1033 varspecs[vn] = str(q.eval(auxvars))
1034 jac = Symbolic.Diff([varspecs[vn] for vn in varnames],
1035 varnames).renderForCode()
1036 self.addFunctions(g, Symbolic.Fun(jac, ['t'] + varnames,
1037 'Jacobian'))
1038
1040 """Create the generators from the source specs, either in the form
1041 of dicts or args objects, or as a GDescriptor.
1042
1043 Still some teething trouble getting expected types neat and tidy.
1044 """
1045
1046
1047 FScompatibleNames = {}
1048 FScompatibleNamesInv = {}
1049 genObjs = {}
1050 assert len(self._generators) > 0, "No Generator descriptions found"
1051 for gname, geninfo in self._generators.iteritems():
1052 if isinstance(geninfo, args):
1053 if isinstance(geninfo.modelspec, args):
1054
1055 gen = self._genFromStrings(geninfo)
1056 else:
1057
1058 gen = self._genFromMSpec(GDescriptor(**geninfo.__dict__))
1059 elif isinstance(geninfo, dict):
1060 gen = self._genFromMSpec(GDescriptor(**geninfo))
1061 else:
1062
1063 gen = self._genFromMSpec(geninfo)
1064 if gname != gen.name:
1065 print gname, " vs.", gen.name
1066 raise ValueError("Generator name mismatch in gen descriptor")
1067 genObjs[gen.name] = gen
1068
1069 FScompatibleNames.update(gen._FScompatibleNames.lookupDict)
1070 FScompatibleNamesInv.update(gen._FScompatibleNamesInv.lookupDict)
1071 return genObjs, genObjs.keys(), FScompatibleNames, FScompatibleNamesInv
1072
1074 genStrings = geninfodesc['modelspec']
1075
1076
1077 attrs = [self.preReuse, self.preReuseTerms, self._funcs,
1078 self.preFlat, self.parvalues, self.icvalues]
1079 if sometrue([len(a) > 0 for a in attrs]):
1080 raise ValueError("Can't mix string-based generator specs "
1081 "with spec info added to ModelConstructor "
1082 "object")
1083 gsh = Generator.theGenSpecHelper(geninfodesc['target'])
1084 return gsh.genClass(genStrings)
1085
1087 genSpec = geninfodesc.modelspec
1088 genTarg = geninfodesc.target
1089 genAlgPars = geninfodesc.algparams
1090 if self.inputs is not None:
1091 genInputs = self.inputs
1092 else:
1093 genInputs = {}
1094 genUnravelInfo = geninfodesc.unravelInfo
1095 genOpts = geninfodesc.options
1096 try:
1097 genEvents = self._events[genSpec.name]
1098 except KeyError:
1099 genEvents = []
1100 else:
1101 if not isinstance(genEvents, list):
1102 genEvents = [genEvents]
1103 try:
1104 genEventPars = self._eventPars[genSpec.name]
1105 except KeyError:
1106 genEventPars = []
1107 else:
1108 if not isinstance(genEventPars, list):
1109 genEventPars = [genEventPars]
1110
1111 try:
1112 genFns = self._funcs[genSpec.name]
1113 except KeyError:
1114 genFns = None
1115 try:
1116 preReuse = self.preReuse[genSpec.name]
1117 except KeyError:
1118 self.preReuse[genSpec.name] = False
1119 preReuse = False
1120 try:
1121 preReuseTerms = self.preReuseTerms[genSpec.name]
1122 except KeyError:
1123 self.preReuseTerms[genSpec.name] = {}
1124 preReuseTerms = {}
1125 try:
1126 preFlat = self.preFlat[genSpec.name]
1127 except KeyError:
1128 self.preFlat[genSpec.name] = False
1129 preFlat = False
1130
1131
1132 genPars = {}
1133 for p, val in self.parvalues.iteritems():
1134
1135
1136
1137 if p in genSpec._registry:
1138 genPars[p] = val
1139 genICs = {}
1140 for v, val in self.icvalues.iteritems():
1141
1142
1143
1144 if v in genSpec._registry:
1145 genICs[v] = val
1146 genCon = GeneratorConstructor(genSpec, checklevel=self.checklevel,
1147 userevents=genEvents,
1148 userfns=genFns,
1149 targetGen=genTarg,
1150 algparams=genAlgPars,
1151 tdata=self.tdata,
1152 indepvar=self.indepvar,
1153 parvalues=genPars,
1154 inputs=genInputs,
1155 icvalues=genICs,
1156 options=genOpts,
1157 unravelInfo=genUnravelInfo,
1158 reuseTerms=self.reuseTerms,
1159 abseps=self.abseps,
1160 activateAllBounds=self.activateAllBounds,
1161 activatedBounds=self.activatedBounds,
1162 eventPars=genEventPars,
1163 preReuse=preReuse,
1164 preReuseTerms=preReuseTerms,
1165 preFlat=preFlat)
1166 return genCon.getGenerator()
1167
1169 """Build and return (hybrid) model made up of declared Generators and
1170 the mappings between events used to change vector fields in a hybrid
1171 system.
1172 """
1173
1174 genObjs, allGenNames, FScompatibleNames, FScompatibleNamesInv \
1175 = self.createGenerators()
1176
1177 modelInfoEntries = {}
1178 modelInterfaces = {}
1179 allModelNames = allGenNames
1180
1181
1182
1183
1184 test_trajs = {}
1185 for genname, gen in genObjs.iteritems():
1186 test_trajs[genname] = None
1187 if len(genObjs)==1:
1188
1189
1190 useMI = False
1191 genname = genObjs.keys()[0]
1192 if genname in self.eventmaps:
1193 for emap in self.eventmaps[genname]:
1194 if emap[1] != 'terminate':
1195
1196 useMI = True
1197 break
1198 if useMI and genname not in self.featureDicts:
1199
1200
1201
1202 self.featureDicts = {genname: {MProject.always_feature('always'): True}}
1203
1204
1205 test_trajs[genname] = 1
1206 else:
1207 useMI = True
1208 for hostGen, genObj in genObjs.iteritems():
1209 if useMI:
1210 m = embed(genObj,
1211 tdata=genObj.indepvariable.depdomain.get())
1212 try:
1213 DSi = MProject.intModelInterface(m,
1214 MProject.condition(self.featureDicts[hostGen]),
1215 test_traj=test_trajs[hostGen])
1216 except KeyError:
1217
1218 DSi = MProject.intModelInterface(m)
1219 allDSnames = allModelNames
1220 else:
1221 DSi = MProject.GeneratorInterface(genObj)
1222 allDSnames = allGenNames
1223 allGenTermEvents = genObj.eventstruct.getTermEvents()
1224 allGenTermEvNames = [e[0] for e in allGenTermEvents]
1225 if hostGen in self.eventmaps:
1226 genMaps = self.eventmaps[hostGen]
1227 genMapNames = []
1228 for gmix, gmtuple in enumerate(genMaps):
1229 genMapNames.append(gmtuple[0])
1230 if isinstance(gmtuple[1], tuple):
1231
1232 genMaps[gmix] = (gmtuple[0],
1233 (gmtuple[1][0],
1234 gmtuple[1][1]))
1235 for evname in remain(allGenTermEvNames, genMapNames):
1236 genMaps.append((evname, 'terminate'))
1237 modelInfoEntries[hostGen] = makeModelInfoEntry(DSi,
1238 allDSnames,
1239 genMaps)
1240 else:
1241
1242
1243 genMaps = [('time', 'terminate')]
1244 for evname in allGenTermEvNames:
1245 genMaps.append((evname, 'terminate'))
1246 if not isfinite(genObj.indepvariable.depdomain[1]):
1247 print "Warning: Generator %s has no termination event"%genObj.name
1248 print "because it has an non-finite end computation time..."
1249 modelInfoEntries[hostGen] = makeModelInfoEntry(DSi,
1250 allDSnames,
1251 genMaps)
1252 modelInfoDict = makeModelInfo(modelInfoEntries.values())
1253
1254 mod_args = {'name': self.name,
1255 'modelInfo': modelInfoDict,
1256 'mspecdict': copy.copy(self._generators),
1257 'eventPars': copy.copy(self._eventPars)}
1258 if self.tdata is not None:
1259 mod_args['tdata'] = self.tdata
1260 if useMI:
1261 model = Model.HybridModel(mod_args)
1262 else:
1263 model = Model.NonHybridModel(mod_args)
1264 if self.forcedIntVars != []:
1265 model.forceIntVars(self.forcedIntVars)
1266 if self.icvalues != {}:
1267 model.set(ics=self.icvalues)
1268 if self.parvalues != {}:
1269 model.set(pars=self.parvalues)
1270 del genObjs
1271 del modelInfoEntries
1272 del modelInfoDict
1273 return model
1274
1276 """Update with feature -> Bool mapping dictionaries
1277 for a host generator.
1278 """
1279 if hostGen not in self.featureDicts:
1280 self.featureDicts[hostGen] = {}
1281 if isinstance(featDict, dict):
1282 self.featureDicts[hostGen].update(featDict)
1283 else:
1284 raise TypeError("Invalid feature dictionary")
1285
1286 - def addEvents(self, hostGen, evTarg, eventPars=None):
1287 if hostGen not in self._events:
1288 self._events[hostGen] = []
1289 if hostGen not in self._eventPars:
1290 self._eventPars[hostGen] = []
1291 if isinstance(evTarg, (list, tuple)):
1292 self._events[hostGen].extend(evTarg)
1293 elif isinstance(evTarg, Events.Event):
1294 self._events[hostGen].append(evTarg)
1295 else:
1296 raise TypeError("Invalid event or event list")
1297
1298 if eventPars is not None and eventPars != [] and eventPars != '':
1299 if isinstance(eventPars, list):
1300 self._eventPars[hostGen].extend(eventPars)
1301 elif isinstance(eventPars, str):
1302 self._eventPars[hostGen].append(eventPars)
1303 self._generators[hostGen].addEvtPars(eventPars)
1304
1305
1307 if hostGen not in self._funcs:
1308 self._funcs[hostGen] = []
1309 if isinstance(fnTarg, list):
1310 self._funcs[hostGen].extend(fnTarg)
1311 elif isinstance(fnTarg, dict):
1312
1313
1314 for k, v in fnTarg.items():
1315 self._funcs[hostGen].append(Symbolic.Fun(v[1], v[0], k))
1316 else:
1317 self._funcs[hostGen].append(fnTarg)
1318
1319
1321 self.reuseTerms = rdict
1322 for g in self._generators:
1323 self.preReuse[g] = False
1324
1326 """which_bounds argument is either 'all', 'lo', 'hi', or a pair ('lo', 'hi').
1327 Calling with no arguments defaults to activating all bounds."""
1328 if varname is None and which_bounds=='all':
1329 self.activateAllBounds = True
1330 else:
1331 entry = [0,0]
1332 if 'hi' in which_bounds:
1333 entry[1] = 1
1334 if 'lo' in which_bounds:
1335 entry[0] = 1
1336 self.activatedBounds[varname] = entry
1337
1339 if isinstance(arg, list):
1340 self.forcedIntVars = arg
1341 elif isinstance(arg, str):
1342 self.forcedIntVars = [arg]
1343
1344
1345 - def mapEvent(self, hostGen, eventname, target, eventmapping=None):
1346 """eventmapping may be a dictionary or an EvMapping product.
1347 You must have declared all generators before calling this function!
1348 """
1349 allGenNames = []
1350 for gname, geninfo in self._generators.iteritems():
1351
1352 if isinstance(geninfo, GDescriptor):
1353 allGenNames.append(geninfo.modelspec.name)
1354 else:
1355 allGenNames.append(geninfo['modelspec'].name)
1356 if target not in allGenNames and target != 'terminate':
1357 raise ValueError("Unknown target Generator %s"%target)
1358 if hostGen not in self._generators:
1359 raise ValueError("Unknown host Generator %s"%hostGen)
1360 try:
1361 genEvs = self._events[hostGen]
1362 except KeyError:
1363 genEvs = []
1364
1365
1366 is_domev = eventname[-6:] in ['_domlo', '_domhi'] and len(eventname) > 6
1367 evNames = [ev.name for ev in genEvs]
1368 if eventname not in evNames and eventname != 'time' and not is_domev:
1369 raise ValueError("Unknown event '%s' for host Generator"
1370 " '%s'"%(eventname, hostGen))
1371 if eventmapping is None:
1372 evm = EvMapping()
1373 elif isinstance(eventmapping, dict):
1374 try:
1375 pars = geninfo['modelspec'].pars
1376 except AttributeError:
1377 pars = []
1378 evm = EvMapping(eventmapping,
1379 infodict={'vars': geninfo['modelspec'].vars,
1380 'pars': pars})
1381 else:
1382 evm = eventmapping
1383 if hostGen in self.eventmaps:
1384 self.eventmaps[hostGen].append((eventname, (target, evm)))
1385 else:
1386 self.eventmaps[hostGen] = [(eventname, (target, evm))]
1387
1388
1389
1390
1391
1392 -def embed(gen, icdict=None, name=None, tdata=None,
1393 make_copy=True):
1394 """Only use this function for building non-hybrid models with single
1395 Generators. Otherwise, use the ModelConstructor class.
1396
1397 NB The supplied Generator is *copied* into the model unless
1398 optional make_copy argument is False."""
1399 assert isinstance(gen, Generator.Generator), ("gen argument "
1400 "must be a Generator object")
1401 if name is None:
1402 name = gen.name
1403 if make_copy:
1404 g = copy.copy(gen)
1405 else:
1406 g = gen
1407 modelInfoEntry = makeModelInfoEntry(MProject.GeneratorInterface(g),
1408 [g.name])
1409 modelArgs = {'name': name,
1410 'modelInfo': makeModelInfo([modelInfoEntry])}
1411 modelArgs['ics'] = g.get('initialconditions')
1412 if icdict is not None:
1413
1414 modelArgs['ics'].update(icdict)
1415 if tdata is not None:
1416 modelArgs['tdata'] = tdata
1417 elif g.tdata is not None:
1418 modelArgs['tdata'] = g.tdata
1419 return Model.NonHybridModel(modelArgs)
1420
1421
1423 if len(arg) == 1 and isinstance(arg, dict):
1424 dsList = [arg]
1425 else:
1426 dsList = arg
1427 allDSNames = []
1428 returnDict = {}
1429 for infodict in dsList:
1430 assert len(infodict) == 1, \
1431 "Incorrect length of info dictionary"
1432 dsName = infodict.keys()[0]
1433 if dsName not in allDSNames:
1434 allDSNames.append(dsName)
1435 returnDict.update(infodict)
1436 else:
1437 raise ValueError("clashing DS names in info "
1438 "dictionaries")
1439 try:
1440 assert remain(infodict.values()[0].keys(), ['dsi',
1441 'swRules', 'globalConRules', 'domainTests']) == []
1442 except AttributeError:
1443 raise TypeError("Expected dictionary in modelInfo entry")
1444 except AssertionError:
1445 raise ValueError("Invalid keys in modelInfo entry")
1446 return returnDict
1447
1448
1450 """Event mapping class, for use by makeModelInfoEntry and, when
1451 instantiated, the Model class.
1452
1453 assignDict maps its values onto the variable or parameter named by the
1454 key. To use the simple syntax in these assignments, either the 'model'
1455 argument or the 'infodict' argument must be provided, the first taking
1456 preference if both are provided. An instantiated Model object must be
1457 provided with the 'model' argument in order to name these variables and
1458 parameters without further qualification. A dictionary with keys 'vars'
1459 and 'pars' must provide lists of variable and parameter names for the
1460 'infodict' argument. Use this argument with ModelConstructor when an
1461 instantiated model is not available. With either of these arguments,
1462 assignments must be given in the (key, value) form: "a", "1 + a*k/2"
1463
1464 Without the model argument, assignments must be given in the (key, value) form:
1465 "xdict['a']", "1+xdict['a']*pdict['k']/2"
1466
1467 defStrings (list of valid python statements) overrides assignDict if supplied at
1468 initialization, to permit full flexibility in the contents of the
1469 event mapping function. These strings must use "xdict", "pdict", and "idict"
1470 to reference the variables, parameters, and inputs, respectively. Time is 't'.
1471 Any other special arguments can be accessed by adding them to this object as
1472 an attribute after its creation, and referring to it with the prefix 'self.'
1473 in the defString.
1474
1475 Use activeDict to map named events to a given setting for 'active' (Boolean).
1476 """
1477
1478 - def __init__(self, assignDict=None, defString="",
1479 activeDict=None, model=None, infodict=None):
1480 if assignDict is None:
1481 assignDict = {}
1482 else:
1483
1484 new_assignDict = {}
1485 if model is None:
1486 try:
1487 vars = infodict['vars']
1488 pars = infodict['pars']
1489 except:
1490 raise ValueError("Must pass dictionary of 'vars' and 'pars'")
1491 else:
1492 try:
1493 vars = model.query('vars')
1494 pars = model.query('pars')
1495 except:
1496 raise ValueError("Must pass instantiated Model")
1497 for key, value in assignDict.items():
1498 rhs = ModelSpec.QuantSpec('rhs', value)
1499 rhs_str = ''
1500 for tok in rhs.parser.tokenized:
1501 if tok in vars:
1502 rhs_str += "xdict['%s']"%tok
1503 elif tok in pars:
1504 rhs_str += "pdict['%s']"%tok
1505 else:
1506 rhs_str += tok
1507 if key in vars:
1508 new_assignDict["xdict['%s']"%key] = rhs_str
1509 elif key in pars:
1510 new_assignDict["pdict['%s']"%key] = rhs_str
1511 else:
1512 raise ValueError("Invalid LHS for event mapping")
1513 assignDict = new_assignDict
1514 if activeDict is None:
1515 activeDict = {}
1516 self.assignDict = assignDict.copy()
1517 self.defString = defString
1518 self.activeDict = activeDict.copy()
1519 self.makeCallFn()
1520
1521
1523 try:
1524 return alltrue([self.assignDict==other.assignDict,
1525 self.defString==other.defString,
1526 self.activeDict==other.activeDict])
1527 except AttributeError:
1528 return False
1529
1531 indent = " "
1532 fnString = """def evmapping(self, xdict, pdict, idict, estruct, t):"""
1533 if self.defString == "" and self.assignDict == {} and self.activeDict == {}:
1534
1535 fnString += indent + "pass\n"
1536 elif len(self.defString) >= 13 and self.defString[:13] == "def evmapping":
1537
1538 fnString = self.defString
1539 else:
1540 if len(self.assignDict) > 0:
1541 for lhs, rhs in self.assignDict.iteritems():
1542 if not(type(lhs)==type(rhs)==str):
1543 raise TypeError("Assignment dictionary for event "
1544 "mapping must consist of strings for "
1545 "both keys and values")
1546 fnString += "\n" + indent + ("\n"+indent).join(["%s = %s"%(l,r) \
1547 for l, r in self.assignDict.items()])
1548 if len(self.defString) > 0:
1549 fnString += "\n" + indent + ("\n"+indent).join(self.defString.split("\n"))
1550 if len(self.activeDict) > 0:
1551 for evname, state in self.activeDict.iteritems():
1552 if not(type(evname)==str and type(state)==bool):
1553 raise TypeError("Invalid types given for setting "
1554 "active events")
1555 fnString += "\n" + indent + \
1556 ("\n"+indent).join(["estruct.setActiveFlag('%s',%s)"%(evname,str(state)) \
1557 for evname, state in self.activeDict.items()])
1558 self.defString = fnString
1559 try:
1560 exec fnString
1561 except:
1562 print 'Invalid function definition for event mapping:'
1563 print fnString
1564 raise
1565 setattr(self, 'evmapping', types.MethodType(locals()['evmapping'],
1566 self, self.__class__))
1567
1569 d = copy.copy(self.__dict__)
1570 try:
1571 del d['evmapping']
1572 except KeyError:
1573 print "'evmapping' local function not in self.__dict__"
1574 return d
1575
1579
1580
1582 raise NotImplementedError("Use EvMapping directly now with infodict argument of 'vars' and 'pars' keys")
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1596 if sometrue([name == r for r in special_reasons + ['time', 'terminate']]):
1597 raise ValueError("Name %s is reserved:\n"%name + \
1598 "Cannot use variable names or internal names 'time' and 'terminate'")
1599
1600 -def makeModelInfoEntry(dsi, allModelNames=None, swmap_list=None,
1601 globcon_list=None, nonevent_reasons=None):
1602 """Create an entry for the modelInfo attribute of a Model or Generator,
1603 already wrapped in a dsInterface object. Specify the list of non-event based
1604 reasons which can be generated by global consistency checks."""
1605
1606 if allModelNames is None:
1607 allModelNames = []
1608 if swmap_list is None:
1609 swmap_list = []
1610 if globcon_list is None:
1611 globcon_list = []
1612 if nonevent_reasons is None:
1613 nonevent_reasons = []
1614 assert isinstance(allModelNames, list), \
1615 "'allModelNames' argument must be a list"
1616 assert isinstance(swmap_list, list), \
1617 "'swmap_list' argument must be a list"
1618 assert isinstance(globcon_list, list), \
1619 "'globcon_list' argument must be a list of ModelInterfaces"
1620 assert isinstance(nonevent_reasons, list), \
1621 "'nonevent_reasons' argument must be a list"
1622
1623
1624 doms = {}
1625 if isinstance(dsi, MProject.GeneratorInterface):
1626 assert allModelNames == [dsi.model.name], \
1627 "Cannot use non-embedded Generators in hybrid system"
1628 if swmap_list != []:
1629 for (name, target) in swmap_list:
1630 if isinstance(target, str):
1631 if target != 'terminate':
1632 print name, target
1633 raise AssertionError("Generators can only be used "
1634 "directly for non-hybrid systems")
1635 else:
1636
1637 try:
1638 assert target[0] != name
1639 except (TypeError, AssertionError):
1640
1641 print name, target
1642 raise AssertionError("Generators can only be used "
1643 "directly for non-hybrid systems")
1644 for vname, var in dsi.model.variables.iteritems():
1645 if alltrue(var.depdomain.isfinite()):
1646 doms[vname] = Model.domain_test(vname+'_domtest',
1647 pars=args(coordname=vname,
1648 derivname='D_'+vname,
1649 interval=var.depdomain, verbose_level=0))
1650
1651 return {dsi.model.name: {'dsi': dsi, 'domainTests': doms,
1652 'swRules': {}, 'globalConRules': globcon_list}}
1653 elif isinstance(dsi, MProject.ModelInterface):
1654 model = dsi.model
1655 else:
1656 raise TypeError("Invalid type for DS interface: "
1657 "must be a GeneratorInterface or ModelInterface")
1658
1659 for vname, dom in model.query('vardomains').iteritems():
1660 if alltrue(dom.isfinite()):
1661
1662 doms[vname] = Model.domain_test(vname+'_domtest',
1663 pars=args(coordname=vname,
1664 derivname='D_'+vname,
1665 interval=dom, verbose_level=0))
1666
1667 special_reasons = ['time'] + model.query('variables') + nonevent_reasons
1668 validateTransitionName(model.name, special_reasons)
1669 try:
1670
1671 allEndReasonNames = model.query('events').keys() \
1672 + special_reasons
1673 except AttributeError:
1674
1675 allEndReasonNames = special_reasons
1676 if model.name not in allModelNames:
1677 print model.name, allModelNames
1678 raise ValueError('Sub-model`s name not in list of all '
1679 'available names!')
1680 if not alltrue([name not in allEndReasonNames for name in allModelNames]):
1681 print model.name, allModelNames
1682 raise ValueError('Sub-model names overlapped with event or '
1683 'variable names')
1684 allTargNames = allModelNames + ['terminate']
1685
1686 seenReasons = []
1687 swmap_pairs = []
1688 if swmap_list == []:
1689 raise ValueError("There must be an event mapping "
1690 "specified when the model is hybrid")
1691 for mapentry in swmap_list:
1692
1693
1694
1695 reason = mapentry[0]
1696 mapping_info = mapentry[1]
1697 if len(mapentry) > 2:
1698 raise ValueError("mapping entry must be (reason, info-pair) tuple")
1699 if isinstance(mapping_info, tuple):
1700 targetName = mapping_info[0]
1701 numargs = len(mapping_info)
1702 elif isinstance(mapping_info, str):
1703 targetName = mapentry[1]
1704 numargs = 1
1705 else:
1706 raise TypeError("Invalid event mapping entry")
1707 if numargs == 2:
1708 epmap = mapping_info[1]
1709 assert isinstance(epmap, EvMapping), "Must supply EvMapping class"
1710 swmap_pairs.append((reason, mapping_info))
1711 elif numargs == 1:
1712
1713
1714 swmap_pairs.append((reason, (targetName, EvMapping())))
1715 else:
1716 raise ValueError("Expected 2 or 3 arguments to model "
1717 "switch map entry")
1718 assert reason not in seenReasons, ('reason cannot appear more than'
1719 ' once in map domain')
1720 seenReasons.append(reason)
1721 if reason not in allEndReasonNames:
1722 print "Model %s:"%model.name
1723 print allEndReasonNames
1724 raise ValueError("name '"+reason+"' in map "
1725 "domain is missing")
1726 if targetName not in allTargNames:
1727 print "Model %s:"%model.name
1728 print allTargNames
1729 raise ValueError("name '"+targetName+"' in "
1730 "map range is missing")
1731 unseen_sr = remain(allEndReasonNames, seenReasons)
1732 if unseen_sr != []:
1733
1734
1735
1736 for r in unseen_sr:
1737 swmap_pairs.append((r, ('terminate', EvMapping())))
1738 if len(swmap_pairs) != len(allEndReasonNames):
1739 info(dict(swmap_pairs))
1740 print "(%i in total), versus:"%len(swmap_pairs)
1741 print allEndReasonNames, "(%i in total)"%len(allEndReasonNames)
1742 sw_keys = dict(swmap_pairs).keys()
1743 print remain(sw_keys, allEndReasonNames)
1744 print remain(allEndReasonNames, sw_keys)
1745 raise ValueError('Incorrect number of map pairs given in argument')
1746 return {model.name: {'dsi': dsi, 'domainTests': doms,
1747 'swRules': dict(swmap_pairs), 'globalConRules': globcon_list}}
1748
1749
1750 -def processReused(sourcenames, auxvarnames, flatspec, registry,
1751 FScompatibleNames, FScompatibleNamesInv):
1752 """Find and process reused terms in abstract specification. To avoid
1753 RHS specs depending on auxiliary variables, temp variables will be declared
1754 in FuncSpec.py and used in both the RHS and auxiliary variables in the
1755 target language specification.
1756 """
1757 reuseTerms={}
1758 subsExpr={}
1759 num_reused=0
1760
1761
1762
1763 u_subsMap = {}
1764 for auxtok in auxvarnames:
1765 tokobj = registry[FScompatibleNamesInv(auxtok)].obj
1766 addtokbraces = tokobj.spec.isCompound()
1767
1768 FScompat_spec = "".join(FScompatibleNames(tokobj.spec[:]))
1769 u_subsMap[auxtok] = "("*addtokbraces + \
1770 FScompat_spec + ")"*addtokbraces
1771
1772
1773
1774
1775
1776 loopCount = 0
1777 loopMaxDepth = 15
1778 purgeDone = {}.fromkeys(auxvarnames, False)
1779 while not all(purgeDone.values()) and loopCount < loopMaxDepth:
1780 loopCount += 1
1781
1782 tempMap = {}
1783 for auxtok, sx in u_subsMap.iteritems():
1784
1785 if purgeDone[auxtok]:
1786
1787 continue
1788 dummyQ = Symbolic.QuantSpec('dummy', sx)
1789 if not any([auxname in dummyQ \
1790 for auxname in auxvarnames]):
1791
1792 purgeDone[auxtok] = True
1793
1794 continue
1795 dummyQ.mapNames(u_subsMap)
1796 tempMap[auxtok] = dummyQ()
1797
1798
1799
1800 u_subsMap.update(tempMap)
1801 if not purgeDone and len(auxvarnames)>0:
1802
1803 print "Declared auxilary variables:", auxvarnames
1804 raise RuntimeError("You probably have an infinite loop of auxiliary "
1805 "variable inter-dependencies: recursion depth of "
1806 "more than %i encountered during model build"%loopCount)
1807 for v in sourcenames:
1808 if v not in flatspec['vars']:
1809
1810
1811 continue
1812 subsMap = {}
1813 dummyQ = Symbolic.QuantSpec('dummy', flatspec['vars'][v])
1814 for u in dummyQ.usedSymbols:
1815 if u in auxvarnames:
1816 new_reusedname = "__"+u
1817 if new_reusedname in reuseTerms.values():
1818
1819 new_reusedname += '_'+str(num_reused)
1820 num_reused += 1
1821 spec_text = flatspec['vars'][u]
1822 testQ = Symbolic.QuantSpec('dummy', spec_text)
1823 testQ.mapNames(mathNameMap)
1824
1825
1826
1827
1828 addbraces = testQ.isCompound() or testQ()[0] == '-'
1829
1830 noSubsExpr = not addbraces and \
1831 (FScompatibleNamesInv(spec_text) in registry \
1832 or isNumericToken(spec_text))
1833
1834
1835 testQ.mapNames(u_subsMap)
1836
1837 addbraces = testQ.isCompound() or testQ()[0] == '-'
1838
1839 spec_text_new = "("*addbraces + testQ() + ")"*addbraces
1840
1841 if not noSubsExpr:
1842 if u in subsExpr:
1843
1844
1845
1846
1847 pass
1848
1849
1850
1851
1852 else:
1853 subsExpr[u] = spec_text_new
1854 if testQ()[0] == '-':
1855 reuse_term = spec_text_new
1856 else:
1857 reuse_term = testQ()
1858 if reuse_term not in reuseTerms:
1859 reuseTerms[reuse_term] = new_reusedname
1860 if u in subsMap:
1861 raise RuntimeError("%s already in subsMap!"%u)
1862 else:
1863 subsMap[u] = spec_text_new
1864
1865
1866 dummyQ.mapNames(subsMap)
1867
1868
1869
1870
1871 dummyQ.mapNames(mathNameMap)
1872 subsExpr[v] = dummyQ()
1873 return reuseTerms, subsExpr
1874