1 """Structured model specification classes, and associated utilities.
2
3 Robert Clewley, October 2005.
4
5
6 Technical notes for ModelSpec attributes:
7 _componentNameMap: object -> full (hierarchical) objname
8 _registry: objname -> regObject:
9 obj, objLocalName, objClassName, objParentName
10 _componentTypeMap: classname -> [objlist]
11 """
12
13
14
15 from __future__ import division
16 from common import *
17 from utils import info as utils_info
18 from parseUtils import *
19 from errors import *
20 from Symbolic import *
21
22 from math import *
23 from utils import *
24 from numpy import Inf, NaN, isfinite, mod, sum
25 from numpy import sometrue, alltrue
26 from copy import copy, deepcopy
27 import math, random
28
29
30
31
32
33 _classes = ['ModelSpec', 'Component', 'LeafComponent',
34 'nameResolverClass']
35
36 _functions = ['searchModelSpec', 'matchSubName', 'processMacro',
37 'resolveMacroTargets']
38
39 _objects = ['nameResolver']
40
41
42 __all__ = _classes + _functions + _objects
43
44
45
47 """Model specification abstract class.
48 Sub-classes can override targetLangs, compatibleSubcomponents,
49 compatibleContainers, and compatibleGens."""
50
51 targetLangs = ()
52 compatibleSubcomponents = ()
53 compatibleContainers = ()
54 compatibleGens = ()
55
57 """Called by all sub-classes - do not override"""
58 self._registry = {}
59 self._componentNameMap = {}
60 self._componentTypeMap = {}
61 self.multiDefRefs = {}
62 self.funcSpecDict = {}
63 self.flatSpec = {'FScompatibleNames': symbolMapClass(),
64 'FScompatibleNamesInv': symbolMapClass()}
65 self.variables = {}
66 self.pars = {}
67 self.inputs = {}
68 self.auxfns = {}
69 self.components = {}
70 self.name = name
71 self.eventPars = []
72
73
74
75 self._allSubcomponentTypes = allCompTypes(accrueCompTypes(self.__class__))
76
77
78
79 self.connxnTargets = []
80
81 self.freeSymbols = []
82
83 self.library_tag = ''
84
85
86
87 nameResolver.clear(self)
88 self.validate()
89
90
92 oldName = self.name
93 for robj in self._registry.values():
94 if robj.objParentName == oldName:
95 robj.objParentName = newName
96 self.name = newName
97
98
100 raise NotImplementedError("Only call this method on a concrete "
101 "sub-class")
102
103
104 - def flattenSpec(self, multiDefUnravel=True, globalRefs=None,
105 ignoreInputs=False, force=False):
106 """Flatten structured model specification to dictionary compatible with
107 FuncSpec instantiation.
108
109 Use globalRefs option to declare global variables used in the
110 definitions that are not defined in them (e.g. time as 't', although
111 this one is included as globalRef by default).
112
113 Default for multiple quantity definitions is to unravel them.
114 Use multiDefUnravel=False to override this.
115
116 Use force option to rebuild flat spec if an existing one is
117 out of date.
118 """
119
120
121
122
123
124
125
126
127
128
129
130
131
132 if not self.isDefined(True, ignoreInputs):
133 raise AssertionError("Model spec not fully defined")
134 if globalRefs is None:
135 globalRefs = ['t']
136 elif 't' not in globalRefs:
137 globalRefs.append('t')
138 if not self.isComplete(globalRefs):
139
140
141
142 print "Unresolved symbols:", remain(self.freeSymbols, globalRefs)
143 raise ValueError("Cannot flatten incomplete functional "
144 "specification")
145 if self.funcSpecDict == {} or force:
146 try:
147 self.compileFuncSpec(ignoreInputs=ignoreInputs)
148 except KeyboardInterrupt:
149 raise
150 except:
151 print "compileFuncSpec() failed"
152 raise
153 fs = self.funcSpecDict
154
155
156 FScompatibleNames = {}
157 FScompatibleNamesInv = {}
158 for name in self._registry.keys():
159 FScompatibleNames[name] = replaceSep(name)
160 FScompatibleNamesInv[replaceSep(name)] = name
161
162 self._FScompatibleNames = FScompatibleNames
163 self._FScompatibleNamesInv = FScompatibleNamesInv
164
165
166 if self.multiDefRefs != {}:
167 unravelDict = {}.fromkeys(self.multiDefRefs)
168 if multiDefUnravel:
169 for k in unravelDict:
170
171 infotuple = self.multiDefRefs[k]
172 unravelDict[k] = [(infotuple[1], infotuple[2])]
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200 outfs = {}
201 quants = ['vars', 'pars', 'inputs']
202 outfs['domains'] = {}
203 outfs['spectypes'] = {}
204
205 mathNameMap = dict(zip(allmathnames_symbolic,allmathnames))
206
207 def add_to_fs(k, name, carg, i=None):
208
209
210 c = copy(carg)
211 if 'for' in c.usedSymbols:
212
213
214
215 c, dummy1, dummy2 = processMacro(c, self)
216
217 c.mapNames(mathNameMap)
218 fsname = FScompatibleNames[name]
219 if i is None:
220 try:
221 outfs[k][fsname] = replaceSep(c.spec)
222 except KeyError:
223 outfs[k] = {fsname: replaceSep(c.spec)}
224 else:
225
226 try:
227 outfs[k][fsname] = replaceSep(c[i])
228 except KeyError:
229 outfs[k] = {fsname: replaceSep(c[i])}
230 outfs['domains'][fsname] = c.domain
231 if k not in ['pars', 'inputs']:
232
233
234 outfs['spectypes'][fsname] = c.spec.specType
235
236 full_mref_names = {}
237 if multiDefUnravel:
238
239
240 for k, v in self.multiDefRefs.iteritems():
241 try:
242 ksubs = FScompatibleNames[k]
243 except KeyError:
244 ksubs = replaceSep(k)
245 FScompatibleNames[k] = ksubs
246 FScompatibleNamesInv[ksubs] = k
247
248
249
250 for ix in range(v[1], v[2]+1):
251 FScompatibleNames[k+str(ix)] = ksubs+str(ix)
252 FScompatibleNamesInv[ksubs+str(ix)] = k+str(ix)
253 full_mref_names[ksubs+"["+v[0]+","+str(v[1])+","+str(v[2])+"]"]\
254 = ksubs
255 for k, v in fs.iteritems():
256 if k in quants:
257 for c in v:
258 if c.name in full_mref_names.keys():
259
260 root = full_mref_names[c.name]
261 for p in unravelDict[root]:
262 for i in xrange(p[0],p[1]+1):
263 name = root + str(i)
264 try:
265 if name not in outfs[k]:
266 add_to_fs(k, name, c, i)
267 except KeyError:
268 add_to_fs(k, name, c, i)
269 else:
270 add_to_fs(k, c.name, c)
271 elif k == 'auxfns':
272 for a in v:
273 afsname = FScompatibleNames[a.name]
274 acopy = copy(a)
275 acopy.mapNames(mathNameMap)
276 aspec = acopy.spec
277 try:
278 outfs[k][afsname] = (a.signature, replaceSep(aspec))
279 except KeyError:
280 outfs[k] = {afsname: (a.signature, replaceSep(aspec))}
281 elif k == 'complete':
282
283 pass
284 else:
285 outfs[k] = v
286 outfs['FScompatibleNames'] = symbolMapClass(FScompatibleNames)
287 outfs['FScompatibleNamesInv'] = symbolMapClass(FScompatibleNamesInv)
288 self.flatSpec = outfs
289 return outfs
290
291 - def isDefined(self, verbose=False, ignoreInputs=False):
292 defined = True
293 if len(self.compatibleGens) == 0:
294 if verbose:
295 print "'%s' ill defined: empty compatibleGens"%self.name
296 return False
297 if len(self.targetLangs) == 0:
298 if verbose:
299 print "'%s' ill defined: empty targetLangs"%self.name
300 return False
301 if self.isEmpty():
302 if verbose:
303 print "'%s' ill defined: empty contents"%self.name
304 return False
305 for v in self.variables.values():
306 if not v.isDefined(verbose):
307 if verbose:
308 print "... in '%s' (type %s)"%(self.name,str(self.__class__))
309 return False
310 for p in self.pars.values():
311 if not p.isDefined(verbose):
312 if verbose:
313 print "... in '%s' (type %s)"%(self.name,str(self.__class__))
314 return False
315 if not ignoreInputs:
316 for i in self.inputs.values():
317 if not i.isDefined(verbose):
318 if verbose:
319 print "... in '%s' (type %s)"%(self.name,str(self.__class__))
320 return False
321 for a in self.auxfns.values():
322 if not a.isDefined(verbose):
323 if verbose:
324 print "... in '%s' (type %s)"%(self.name,str(self.__class__))
325 return False
326 return defined
327
328
330 if isinstance(targ, str):
331 if targ not in self.connxnTargets:
332 self.connxnTargets.append(targ)
333 elif isinstance(targ, list):
334 for t in targ:
335 if t not in self.connxnTargets:
336 self.connxnTargets.append(t)
337 else:
338 raise TypeError("Invalid ModelSpec type for targ argument")
339
340
342 if isinstance(targ, str):
343 try:
344 self.connxnTargets.remove(targ)
345 except ValueError:
346 raise ValueError("Connection target %s not found"%targ)
347 elif isinstance(targ, list):
348 for t in targ:
349 try:
350 self.connxnTargets.remove(t)
351 except ValueError:
352 raise ValueError("Connection target %s not found"%t)
353
354
356 try:
357 if obj.name in self._registry:
358 return obj == self._registry[obj.name].obj
359 elif self.multiDefInfo[0]:
360
361
362
363 return isMultiDefClash(obj, self.multiDefRefs)
364 else:
365 return False
366 except AttributeError:
367 return False
368
369
370 - def __eq__(self, other, diff=False):
371 results = []
372 try:
373 results.append(type(self) == type(other))
374 results.append(self.name == other.name)
375 results.append(self._registry == other._registry)
376 except AttributeError, e:
377 if diff:
378 print "Type:", className(self), results
379 print " " + e
380 return False
381 if diff:
382 print "Type:", className(self), results
383 return alltrue(results)
384
385
387 return not self.__eq__(other)
388
389
391 """Print the difference between two ModelSpecs to screen."""
392 self.__eq__(other, diff=True)
393
394
396 assert isinstance(self.name, str), "ModelSpec name must be a string"
397 assert len(remain(self.targetLangs, targetLangs)) == 0, \
398 "Invalid target language for '" + self.name + "'"
399 if hasattr(self, 'components'):
400
401 for c in self.components.values():
402 assert c in self._componentNameMap, "Component name map incomplete"
403
404
405 for v in self.flatSpec['FScompatibleNamesInv'](self.variables.values()):
406 assert v in self._componentNameMap, "Variable name map incomplete"
407 for p in self.flatSpec['FScompatibleNamesInv'](self.pars.values()):
408 if p not in self._componentNameMap and p.name not in self.eventPars:
409 print "\n", self.name, self
410 print "Pars:", p
411 print p.name in self.flatSpec['FScompatibleNames']
412 print p.name in self.flatSpec['FScompatibleNamesInv']
413 print self.eventPars
414 print self.flatSpec['FScompatibleNamesInv'](p)
415 print "Component name map:", self._componentNameMap
416 raise AssertionError("Parameter name map incomplete")
417 i = None
418 for i in self.flatSpec['FScompatibleNamesInv'](self.inputs.values()):
419 assert i in self._componentNameMap, "External input name map incomplete"
420 for a in self.flatSpec['FScompatibleNamesInv'](self.auxfns.values()):
421 assert a in self._componentNameMap, "Auxiliary function name map incomplete"
422
423
424
425
426 - def add(self, arg, tosubcomponent=None):
427 """Add object to registry, into a specified sub component if
428 provided.
429 """
430 if isinstance(arg, list):
431 for obj in arg:
432 self.add(obj, tosubcomponent)
433 elif tosubcomponent is not None:
434 if tosubcomponent in self.components:
435 c = deepcopy(self[tosubcomponent])
436 self.remove(tosubcomponent)
437 c.add(arg)
438 self.add(c)
439 elif tosubcomponent == '':
440
441 self.add(arg)
442 else:
443 raise ValueError("Unknown sub-component %s"%tosubcomponent)
444 elif isHierarchicalName(arg.name):
445
446 obj = deepcopy(arg)
447 hier_name_split = obj.name.split(NAMESEP)
448 comp_name = hier_name_split[0]
449 rest_name = ".".join(hier_name_split[1:])
450 obj.rename(rest_name)
451 self.add(obj, tosubcomponent=comp_name)
452 else:
453 obj = deepcopy(arg)
454 objname = self._register(obj)
455 if isinstance(obj, Var):
456 self.variables[objname] = obj
457 elif isinstance(obj, Par):
458 self.pars[objname] = obj
459 elif isinstance(obj, Input):
460 self.inputs[objname] = obj
461 elif isinstance(obj, Fun):
462 self.auxfns[objname] = obj
463 elif isinstance(obj, ModelSpec):
464 if not issubclass(self.__class__, obj.compatibleContainers):
465 print "Component " + self.name + ": " + self.__class__.__name__
466 print "Invalid sub-component " + objname \
467 + " (type " + className(obj) + ") to add,"
468 print " with compatible container types:", obj.compatibleContainers
469 print "Compatible sub-component types: ", self._allSubcomponentTypes
470 raise ValueError("Incompatible sub-component type"
471 " for object '" + objname + "'")
472 else:
473 self.components[objname] = obj
474 else:
475 raise TypeError("Invalid Quantity object to add")
476
477
479 raise NotImplementedError("Only call this method on a concrete "
480 "sub-class")
481
482
483 - def _register(self, obj, depth=0, parent_obj=None):
484
485
486 if len(obj.compatibleGens) == 0:
487
488 obj.compatibleGens = self.compatibleGens
489 elif len(remain(obj.compatibleGens, self.compatibleGens)) > 0:
490 print remain(obj.compatibleGens, self.compatibleGens)
491 raise ValueError("Incompatible generators found in component"
492 " '" + obj.name + "'")
493 if len(obj.targetLangs) == 0:
494
495 obj.targetLangs = self.targetLangs
496 elif len(remain(obj.targetLangs, self.targetLangs)) > 0:
497 print remain(obj.targetLangs, self.targetLangs)
498 raise ValueError("Incompatible target language in component"
499 " '" + obj.name + "'")
500 if obj.name in protected_allnames:
501 raise ValueError("Name '" + obj.name + "' is a protected name")
502 if not compareClassAndBases(obj, self._allSubcomponentTypes):
503 print "Valid sub-component types that have been declared:", \
504 self._allSubcomponentTypes
505 raise TypeError("Invalid type for object '" + obj.name + \
506 "' in component '" + self.name + "'")
507 if parent_obj is None:
508 parentname = None
509 else:
510 parentname = parent_obj.name
511 if isMultiDefClash(obj, self.multiDefRefs, parentname):
512 raise ValueError("Object %s defines names that already exist in"
513 " registry"%obj.name)
514
515
516
517 if hasattr(obj, '_registry'):
518
519
520 do_obj_register = False
521
522
523
524
525
526
527 namemap = {}
528 tracebacknames = []
529 for sub_regObj in obj._registry.values():
530 subobj = sub_regObj.obj
531 subobjname = self._register(subobj, depth+1, obj)
532
533 namemap[subobj.name] = subobjname
534 if isMultiRef(subobjname):
535
536
537
538 rootname = subobj.multiDefInfo[1]
539 ixname = subobj.multiDefInfo[2]
540 namemap[rootname] = obj.name+NAMESEP+rootname
541 tracebacknames.append(subobjname)
542
543
544
545
546
547
548
549 mapper = symbolMapClass(namemap)
550 globalized_newdefs = mapper(obj._registry.keys())
551 self.freeSymbols = remain(self.freeSymbols,
552 globalized_newdefs)
553 obj_freeSymbols = mapper(obj.freeSymbols)
554
555
556
557
558
559
560
561 if obj_freeSymbols != obj.freeSymbols:
562 print "mapped object free symbols not same as originals"
563
564
565
566
567 for s in obj.freeSymbols:
568 if s not in namemap:
569
570
571 if parentname is None:
572 snew = self.name+NAMESEP+s
573 else:
574 snew = parentname+NAMESEP+s
575
576
577
578 for sub_regObj in obj._registry.values():
579 subobjname = namemap[sub_regObj.obj.name]
580 subsplit = subobjname.split(NAMESEP)
581
582
583 if len(subsplit) > 1:
584 actual_parentname = subsplit[-2]
585 if actual_parentname in obj.components:
586 actual_parent = obj.components[actual_parentname]
587
588
589
590
591 else:
592
593
594
595
596
597
598 actual_parent = obj
599
600
601
602 else:
603 actual_parent = obj
604 new_regObj = deepcopy(sub_regObj)
605 if isinstance(new_regObj.obj, Quantity):
606 if new_regObj.obj.typestr == 'auxfn':
607
608 func_namemap = copy(namemap)
609 for s in new_regObj.obj.signature:
610 try:
611 del func_namemap[s]
612 except KeyError:
613 pass
614 new_regObj.obj.mapNames(func_namemap)
615 else:
616 new_regObj.obj.mapNames(namemap)
617 self._registry[subobjname] = regObject(new_regObj.obj,
618 actual_parent)
619 self._registry[subobjname].namemap = namemap
620
621
622 localName = self._registry[subobjname].objLocalName
623 temp = localName.split(NAMESEP)
624 if len(temp) > 1:
625 localName = NAMESEP.join(temp[1:])
626 self._registry[subobjname].objLocalName = localName
627 objname = obj.name
628 self._componentNameMap[obj] = objname
629 oclasses = getSuperClasses(obj, ModelSpec)
630 for oclass in oclasses:
631 try:
632 if obj not in self._componentTypeMap[oclass]:
633 self._componentTypeMap[oclass].append(obj)
634 except KeyError:
635 self._componentTypeMap[oclass] = [obj]
636 do_free_symbs = True
637 else:
638
639 do_obj_register = (depth == 0)
640 do_free_symbs = do_obj_register
641 try:
642 if depth > 0:
643 objname = nameResolver(obj, self, parentname)
644 else:
645 objname = obj.name
646 except AttributeError:
647 raise TypeError("Invalid object type in _register(): %s (type"
648 " %s)"%(obj.name, className(obj)))
649
650
651 if hasattr(obj, 'multiDefInfo'):
652
653
654 if obj.multiDefInfo[0]:
655
656
657
658
659
660 if parentname is None:
661 rootname = obj.multiDefInfo[1]
662 else:
663 rootname = parentname+NAMESEP+obj.multiDefInfo[1]
664 self.multiDefRefs[rootname] = obj.multiDefInfo[2:]
665 self._componentNameMap[obj] = objname
666 oclasses = getSuperClasses(obj)
667 for oclass in oclasses:
668 try:
669 if obj not in self._componentTypeMap[oclass]:
670 self._componentTypeMap[oclass].append(obj)
671 except KeyError:
672 self._componentTypeMap[oclass] = [obj]
673 if objname in self._registry:
674 raise ValueError("Name '%s' already exists in "%objname \
675 + "registry of object '%s'"%self.name)
676
677
678 for fs in copy(self.freeSymbols):
679 fs_split = fs.split(NAMESEP)
680 if len(fs_split) > 1:
681 checkname = "".join(fs_split[1:])
682 else:
683 checkname = fs
684 if objname == checkname:
685 self.freeSymbols.remove(fs)
686 obj_freeSymbols = obj.freeSymbols
687 if do_obj_register:
688 self._registry[objname] = regObject(deepcopy(obj),
689 parent_obj or self)
690
691
692 if do_free_symbs:
693
694
695
696 self.freeSymbols.extend(remain(obj_freeSymbols,
697 self._registry.keys()+self.freeSymbols))
698
699
700 return objname
701
702
704 """Delete named object (Var, Par, Fun, Input, or sub-component)
705 """
706 self.remove(name)
707
709 """Return object named using the hierarchical naming format."""
710 try:
711 return self._registry[name].obj
712 except KeyError:
713
714
715 name_list = name.split('.')
716 try:
717 c = self.components[name_list[0]]
718 except KeyError:
719 if name == '':
720
721 return self
722 else:
723 raise ValueError("Object %s not found in registry"%name)
724 else:
725 rest = name_list[1:]
726 if len(rest) > 0:
727 return c['.'.join(rest)]
728 else:
729 return c
730
731
733 """Remove target component from specification.
734
735 Use global hierarchical names for components if specifying a string."""
736
737 if hasattr(target, 'name'):
738 objname = target.name
739 elif isinstance(target, str):
740 objname = target
741 elif isinstance(target, list):
742 for t in target: self.remove(t)
743 return
744 else:
745 raise TypeError("Invalid type to remove from ModelSpec "
746 "'%s'"%self.name)
747 try:
748 del self._registry[objname]
749 except KeyError:
750
751 pass
752 if isHierarchicalName(objname):
753 tempname = objname.split(NAMESEP)
754 parentname = tempname[0]
755 try:
756 self.components[parentname].remove("".join(tempname[1:]))
757 except ValueError, e:
758 print e
759 raise ValueError("Error recognizing parent object '%s' in "
760 "sub-component '%s'"%(parentname,objname))
761 else:
762 if objname in self.variables:
763 del self.variables[objname]
764 elif objname in self.pars:
765 del self.pars[objname]
766 elif objname in self.inputs:
767 del self.inputs[objname]
768 elif objname in self.components:
769 del self.components[objname]
770 elif objname in self.auxfns:
771 del self.auxfns[objname]
772 protected_auxnamesDB.removeAuxFn(objname)
773 else:
774 raise ValueError("%s: Error recognizing object '%s'"%(self.name,objname))
775
776
777 self.freeSymbols = []
778 self.multiDefRefs = {}
779 self._registry = {}
780 self._componentNameMap = {}
781 self._componentTypeMap = {}
782 nameResolver.clear(self)
783 for v in self.variables.values():
784 self._register(v)
785 for p in self.pars.values():
786 self._register(p)
787 for i in self.inputs.values():
788 self._register(i)
789 for a in self.auxfns.values():
790 self._register(a)
791
792
793 if hasattr(self, 'components'):
794
795 for c in self.components.values():
796 self._register(c)
797 try:
798 self.validate()
799 except AssertionError, e:
800 raise RuntimeError("Model spec structure inconsistent: "+str(e))
801
802
811
812
814
815 utils_info(self.__dict__, "ModelSpec " + self.name)
816
817
818 - def info(self, verboselevel=1):
819 if verboselevel > 0:
820
821 utils_info(self.__dict__, "ModelSpec " + self.name,
822 recurseDepthLimit=1+verboselevel)
823 else:
824 print self.__repr__()
825
826
829
831 base_str = "Component " + self.name
832 if verbose > 0:
833 if len(self.components) > 0:
834 base_str += "\n Components: ( "
835 base_str += ", ".join([c._infostr(verbose-1) for c in self.components.values()]) + " )"
836 if len(self.variables) > 0:
837 base_str += "\n Variables: ( "
838 base_str += ", ".join([str(c) for c in self.variables.values()]) + " )"
839 if len(self.pars) > 0:
840 base_str += "\n Parameters: ( "
841 base_str += ", ".join([str(c) for c in self.pars.values()]) + " )"
842 if len(self.inputs) > 0:
843 base_str += "\n Inputs: ( "
844 base_str += ", ".join([str(c) for c in self.inputs.values()]) + " )"
845 if len(self.auxfns) > 0:
846 base_str += "\n Functions: ( "
847 base_str += ", ".join([str(c) for c in self.auxfns.values()]) + " )"
848 return base_str
849
850
851 - def search(self, name, component_type_order=None):
852 """Find Quantity objects containing a component named <name>,
853 of type given by the hierarchical name <comptype1.comptype2. ... .name>,
854 where component_type_order = [<comptype1>, <comptype2>, ... ],
855 and <comptypeN> may be a component type (as a wildcard) or a specific
856 component name.
857 """
858 return searchModelSpec(self, name, component_type_order)
859
860
861 __str__ = __repr__
862
863
865 pickledself = pickle.dumps(self)
866 return pickle.loads(pickledself)
867
868
870 pickledself = pickle.dumps(self)
871 return pickle.loads(pickledself)
872
873
874
875
877 """Non-leaf node sub-class of ModelSpec abstract class."""
878
880 self.validate()
881 assert self.isDefined(True, ignoreInputs=ignoreInputs), \
882 "Node '" + self.name + "' is not completely defined"
883 fsdict = {}
884 fsdict['inputs'] = []
885 fsdict['vars'] = []
886 fsdict['pars'] = []
887 fsdict['auxfns'] = []
888 for objname, regObj in self._registry.iteritems():
889 objtype = regObj.obj.typestr+'s'
890 add_obj = regObj.obj.renderForCode()
891 if regObj.namemap != {}:
892 if isinstance(add_obj, Fun):
893
894
895
896 for name in regObj.namemap:
897 mappedName = regObj.namemap[name]
898 if mappedName in add_obj.freeSymbols and not \
899 isinstance(self._registry[mappedName].obj, (Par, Fun)):
900 print "Problem with registry object:", \
901 self._registry[mappedName].obj
902 print "in auxiliary function:", add_obj.name
903 print " that has free symbols:", \
904 add_obj.freeSymbols
905 raise TypeError("Fun '" + add_obj.name + "' "
906 "cannot bind symbols to non-"
907 "pars (info printed above)")
908
909
910 fsdict[objtype].append(add_obj)
911 fsdict['complete'] = self.isComplete()
912 self.funcSpecDict = fsdict
913
914
916 return self.components == {}
917
918
919 - def isDefined(self, verbose=False, ignoreInputs=False):
920
921
922 defined = ModelSpec.isDefined(self, verbose, ignoreInputs)
923 for c in self.components.values():
924 if not c.isDefined(verbose, ignoreInputs):
925 return False
926 return defined
927
928
929
931 """Leaf node sub-class of ModelSpec abstract class."""
932
933 - def _register(self, obj, depth=0, parent_obj=None):
934 if not compareBaseClass(obj, Quantity):
935 print "Bad argument %s (type %s) to register"%(str(obj), type(obj))
936 raise TypeError("Not a valid Quantity type")
937 return ModelSpec._register(self, obj, 0, parent_obj)
938
939
941 return self.variables == {} and self.pars == {} \
942 and self.inputs == {}
943
944
946 self.validate()
947 assert self.isDefined(ignoreInputs=ignoreInputs), \
948 "Node '" + self.name + "' is not completely defined"
949 fsdict = {}
950 fsdict['vars'] = [deepcopy(v) for v in self.variables.values()]
951 fsdict['pars'] = [deepcopy(p) for p in self.pars.values()]
952 fsdict['inputs'] = [deepcopy(i) for i in self.inputs.values()]
953 fsdict['auxfns'] = [deepcopy(a) for a in self.auxfns.values()]
954 self.funcSpecDict = fsdict
955
956
957 - def add(self, arg):
958 if isinstance(arg, list):
959 for obj in arg:
960 self.add(obj)
961 else:
962 if isinstance(arg, ModelSpec):
963 raise TypeError("Cannot add sub-components to a leaf node")
964 else:
965 ModelSpec.add(self, arg)
966
967
968 Component.compatibleSubcomponents=(Par, Var, Input, Fun,
969 Component, LeafComponent)
970 Component.compatibleContainers=(Component,)
971
972 LeafComponent.compatibleSubcomponents=(Par, Var, Input, Fun)
973 LeafComponent.compatibleContainers=(Component,LeafComponent)
974
975
976
977
979 """Filter full list of all super-classes' compatible sub-component
980 types to remove any classes that are not over-ridden by sub-classes.
981
982 e.g. If Par was allowed for Component type, but a sub-class of
983 Component specifies only a sub-class of Par may be used, then
984 Par is removed from the original list of valid component types.
985 """
986 filter_list = []
987 for c in class_list:
988 filter_list.extend([d for d in class_list if issubclass(c, d) and c!=d])
989 return [c for c in class_list if c not in filter_list]
990
991
992
994 """Collect all super-class component types (string names) and accumulate
995 recursively.
996 """
997 cs = list(c.compatibleSubcomponents)
998 for cb in c.__bases__:
999 if cb is ModelSpec:
1000
1001
1002 break
1003 else:
1004 try:
1005 cs.extend(accrueCompTypes(cb))
1006 except AttributeError:
1007
1008
1009 pass
1010 return makeSeqUnique(cs)
1011
1012
1013 -def matchSubName(nameList, subName, position, level=0, invert=False):
1014 """Return a list of names from nameList matching subName at the given
1015 position (at the hierarchical name level given, if the name is
1016 hierarchical).
1017
1018 e.g. For a hierarchical variable name such as 'sRM.s_cell1_cell2',
1019 level=0 will cause a search for a match with subName in the top-level name,
1020 i.e. 'sRM', at the given position. Whereas, level=1 will search for a match
1021 in 's_cell1_cell2' at the given position.
1022
1023 invert=True will return names that did not match."""
1024 matchList = []
1025 for name in nameList:
1026 if isHierarchicalName(name):
1027 hier_parts = name.split('.')
1028 searchName = hier_parts[level]
1029 else:
1030 searchName = name
1031 subName_parts = searchName.split('_')
1032 if invert:
1033 if subName != subName_parts[position]:
1034 matchList.append(name)
1035 else:
1036 if subName == subName_parts[position]:
1037 matchList.append(name)
1038 return matchList
1039
1040
1042 """Find Quantity or sub-component objects containing named <name>
1043 of type given by the hierarchical name <comptype1.comptype2. ... .name>,
1044 where component_type_order = [<comptype1>, <comptype2>, ... ],
1045 and <comptypeN> may be a component type or a specific component name."""
1046 if isHierarchicalName(name):
1047
1048 if component_type_order is None or component_type_order == []:
1049
1050 parts = name.split(NAMESEP)
1051 return searchModelSpec(mspec, parts[-1], parts[:-1])
1052 else:
1053 raise ValueError("Cannot pass both a hierarchical name to search"
1054 " and a non-empty component_type_order argument")
1055 if component_type_order is None or component_type_order == []:
1056
1057 if name in mspec._registry:
1058 return [name]
1059 elif name in mspec.components:
1060 return [name]
1061 elif name in mspec._componentTypeMap:
1062 return [mspec._componentNameMap[o] for o in \
1063 mspec._componentTypeMap[name]]
1064 else:
1065 return []
1066 else:
1067 ct = component_type_order[0]
1068 try:
1069 complist = mspec._componentTypeMap[ct]
1070 except KeyError:
1071
1072
1073 try:
1074 complist = [mspec.components[ct]]
1075 except KeyError:
1076 return []
1077 result = []
1078
1079
1080 if name in ['RHSfuncSpec', 'ExpFuncSpec'] and component_type_order[1:] == []:
1081 for comp in complist:
1082 compname = mspec._componentNameMap[comp]
1083 try:
1084 test = mspec._registry[compname].obj.specType == name
1085 except (KeyError, AttributeError):
1086
1087 test = False
1088 if test:
1089 result.append(compname)
1090 else:
1091 for comp in complist:
1092 comp_result = searchModelSpec(comp, name, component_type_order[1:])
1093 result.extend([comp.name + NAMESEP + r for r in comp_result])
1094 return result
1095
1096
1097
1098
1100 """Process 'for' macro occurrences in a Quantity specification.
1101
1102 forSubs = True causes any target names to be resolved to their
1103 global names and returned to caller so that they can be substituted
1104 textually into the Quantity's specification."""
1105
1106 ctypestrlist = []
1107 targList = []
1108 toks = c.spec.parser.tokenized
1109 num_fors = toks.count('for')
1110 specStr = ""
1111 forpos = 0
1112 for for_ix in range(num_fors):
1113 old_forpos = forpos
1114 forpos += toks[forpos:].index('for')
1115 if old_forpos == 0:
1116 specStr += "".join(toks[:forpos])
1117 else:
1118 specStr += "".join(toks[old_forpos+1:forpos])
1119 qtemp = QuantSpec('macrospec', toks[forpos+1])
1120
1121
1122 parentName = ".".join(c.name.split('.')[:-1])
1123 sourceList = searchModelSpec(mspec, parentName)
1124 if len(sourceList) == 1:
1125 sourceName = sourceList[0]
1126 elif len(sourceList) == 0:
1127 sourceName = ""
1128 else:
1129 print "Found: ", sourceList
1130 raise ValueError("source list for macro resolution should have no "
1131 "more than one entry")
1132 targList_new, ctStr, opStr = resolveMacroTargets(qtemp.parser.tokenized,
1133 mspec, sourceName)
1134 ctypestrlist.append(ctStr)
1135 if forSubs:
1136 for t in targList_new:
1137 if t not in targList: targList.append(t)
1138 if targList_new == []:
1139 specStr += "0"
1140 else:
1141 specStr += "(" + opStr.join(targList_new) + ")"
1142 forpos += 1
1143
1144 specStr += "".join(toks[forpos+1:])
1145 c.spec = QuantSpec(replaceSep(c.spec.subjectToken), specStr.strip(),
1146 c.spec.specType)
1147
1148
1149 c.freeSymbols = remain(c.freeSymbols, ctypestrlist)
1150 c.usedSymbols = remain(c.usedSymbols, ctypestrlist+['for'])
1151 return (c, targList, ctypestrlist)
1152
1153
1155
1156 opStr = toks[5]
1157 localTarget = toks[3]
1158 componentTypeStr = toks[1]
1159 if componentTypeStr in ['Var', 'Par', 'Quantity']:
1160 raise ValueError("Invalid component type for macro in"
1161 " %s"%targParent)
1162 if targParent == "":
1163 target = componentTypeStr+'.'+localTarget
1164 else:
1165 target = targParent+'.'+componentTypeStr+'.'+localTarget
1166 targList = searchModelSpec(mspec, target)
1167 return (targList, componentTypeStr, opStr)
1168
1169
1170
1171
1173 """Registry object container."""
1174
1175 - def __init__(self, obj, parent, namemap={}):
1176 self.obj = obj
1177 self.objClass = obj.__class__
1178 self.objParentClass = parent.__class__
1179 self.objParentName = parent.name
1180 self.objLocalName = obj.name
1181
1182 self.namemap = copy(namemap)
1183
1185 tests = []
1186 try:
1187 tests.append(self.objClass == other.objClass)
1188 tests.append(self.objParentClass == other.objParentClass)
1189 tests.append(self.objParentName == other.objParentName)
1190 tests.append(self.objLocalName == other.objLocalName)
1191 tests.append(self.obj == other.obj)
1192 tests.append(self.namemap == other.namemap)
1193 except AttributeError:
1194 return False
1195 return alltrue(tests)
1196
1198 return "regObject %s (%s)"%(self.objLocalName, className(self.obj))
1199
1201 return self.obj.spec()
1202
1204 return (self.objLocalName, self.objClass, self.namemap, self.obj)
1205
1207 if self.namemap == {}:
1208 return self.objLocalName
1209 else:
1210 try:
1211 return self.namemap[self.objLocalName]
1212 except KeyError:
1213 print "Namemap for '%s' is: "%self.__repr__(), self.namemap
1214 raise ValueError("Namemap for registry object didn't "
1215 "contain object's name "
1216 "'%s'"%self.objLocalName)
1217
1218
1221 self.varcount = self.parcount = self.funcount = self.inpcount = 0
1222
1224 if typelabel == 'var':
1225 return self.varcount
1226 elif typelabel == 'par':
1227 return self.parcount
1228 elif typelabel == 'input':
1229 return self.inpcount
1230 elif typelabel == 'auxfn':
1231 return self.funcount
1232 else:
1233 raise KeyError("Invalid key")
1234
1236 if typelabel == 'var':
1237 self.varcount = value
1238 elif typelabel == 'par':
1239 self.parcount = value
1240 elif typelabel == 'input':
1241 self.inpcount = value
1242 elif typelabel == 'auxfn':
1243 self.funcount = value
1244 else:
1245 raise KeyError("Invalid key")
1246
1247
1248
1250 """This class keeps a tab of how many times a local name has been
1251 used for a given specfication type ('var', 'par', 'input' or
1252 'auxfn'), and renames it with an appropriate globalized name
1253 (hierarchical according to declared parent object, with possible
1254 numbered suffix for multiple name declarations).
1255
1256 Only one instance of this class is needed for a session."""
1257
1260
1261 - def __call__(self, obj, fspec, parentname=None):
1262 typelabel = className(obj).lower()
1263 if parentname is None:
1264 fullname = obj.name
1265 else:
1266
1267 fullname = parentname + NAMESEP + obj.name
1268 if fspec.name not in self.database:
1269 self.database[fspec.name] = {}
1270 if obj.name in self.database[fspec.name]:
1271 self.database[fspec.name][fullname][typelabel] += 1
1272 namecount = self.database[fspec.name][fullname][typelabel]
1273 else:
1274 self.database[fullname] = typeCounter()
1275 namecount = 0
1276 if namecount == 0:
1277 globalname = fullname
1278 else:
1279
1280
1281 globalname = fullname + NAMESEP + str(namecount)
1282 return globalname
1283
1285 return "ModelSpec internal helper class: nameResolver object"
1286
1287 __str__ = __repr__
1288
1289 - def clear(self, fspec):
1290 if fspec.name in self.database:
1291 del self.database[fspec.name]
1292
1295
1296
1297
1298 global nameResolver
1299 nameResolver = nameResolverClass()
1300