MoDeNa  1.0
Software framework facilitating sequential multi-scale modelling
SurrogateModel.py
Go to the documentation of this file.
1 
31 
32 
42 
43 import os
44 import six
45 import abc
46 import hashlib
47 import modena
48 from modena.Strategy import *
49 import weakref
50 import re
51 import random
52 from mongoengine import *
53 from mongoengine.document import TopLevelDocumentMetaclass
54 from mongoengine.base import BaseField
55 import pymongo
56 from fireworks import Firework, FireTaskBase
57 from collections import defaultdict
58 import jinja2
59 
60 
61 # Create connection to database
62 MODENA_URI = os.environ.get('MODENA_URI', 'mongodb://localhost:27017/test')
63 (uri, database) = MODENA_URI.rsplit('/', 1)
64 connect(database, host=MODENA_URI)
65 
66 MODENA_PARSED_URI = pymongo.uri_parser.parse_uri(
67  MODENA_URI,
68  default_port=27017
69 )
70 MODENA_PARSED_URI['host'], MODENA_PARSED_URI['port'] = \
71  MODENA_PARSED_URI.pop('nodelist')[0]
72 MODENA_PARSED_URI['name'] = MODENA_PARSED_URI.pop('database')
73 del MODENA_PARSED_URI['collection'], MODENA_PARSED_URI['options']
74 
75 
78 
79 class ArgPosNotFound(Exception):
80  pass
81 
82 
88 def existsAndHasArgPos(i, name):
89  if name not in i or 'argPos' not in i[name]:
90  raise Exception('[%s][\'argPos\'] not found' % name)
91  return i[name]['argPos']
92 
93 
94 
102 def checkAndConvertType(kwargs, name, cls):
103  try:
104  if not isinstance(kwargs[name], cls):
105  raise TypeError('%s must be of type %s' % (name, cls))
106  kwargs['meth_' + name] = kwargs[name].to_dict()
107  del kwargs[name]
108  except:
109  raise Exception('%s not found' % name)
110 
111 
112 
122 def loadType(obj, name, cls):
123  #print 'In loadType ' + name
124  n = '___' + name
125  if hasattr(obj, n):
126  return getattr(obj, n)
127  else:
128  var = getattr(obj, 'meth_' + name) # get strategy dictionary
129  #print obj._get_changed_fields()
130  var = load_object(var) # de-serialise object from dictionary
131  #print obj._get_changed_fields()
132  setattr(obj, n, var)
133  return var
134 
135 
136 
138 class EmbDoc(DynamicEmbeddedDocument):
139  meta = {'allow_inheritance': False}
140 
141 
142 
144 class GrowingList(list):
145  def __setitem__(self, index, value):
146  if index >= len(self):
147  self.extend([None]*(index + 1 - len(self)))
148  list.__setitem__(self, index, value)
149 
150 
151 
160 class IndexSet(Document):
161  # Database definition
162  name = StringField(primary_key=True)
163  names = ListField(StringField(required=True))
164  meta = {'allow_inheritance': True}
165 
166  @abc.abstractmethod
167 
172  def __init__(self, *args, **kwargs):
173  self.___index___ = {j: i for i, j in enumerate(kwargs['names'])}
174  super(IndexSet, self).__init__(*args, **kwargs)
175  self.save()
176 
177 
178 
184  def get_name(self, index):
185  try:
186  return self.names[index]
187  except:
188  raise Exception('%i is not in index set %s' % (index, self.name))
189 
190 
191 
197  def get_index(self, name):
198  try:
199  return self.___index___[name]
200  except:
201  raise Exception('%s is not in index set %s' % (name, self.name))
202 
203 
204 
206  def iterator_end(self):
207  return len(self.names)
208 
209 
210 
212  def iterator_size(self):
213  return len(self.names)
214 
215 
216  @classmethod
217 
221  def exceptionLoad(self, indexSetId):
222  return 401
223 
224 
225  @classmethod
226 
230  def load(self, indexSetId):
231  return self.objects.get(name=indexSetId)
232 
233 
234 # Fitting data is not stored here to allow excluding it in load since it
235 # is not possible to exclude inputs.*.fitData
236 
246 class MinMax(EmbeddedDocument):
247  min = FloatField(required=True)
248  max = FloatField(required=True)
249 
250 
251 
261 class MinMaxOpt(EmbeddedDocument):
262  min = FloatField()
263  max = FloatField()
264 
265 
266 
278 class MinMaxArgPos(EmbeddedDocument):
279  min = FloatField(required=True, default=None)
280  max = FloatField(required=True, default=None)
281  argPos = IntField(required=True)
282  index = ReferenceField(IndexSet)
283 
284  def __init__(self, *args, **kwargs):
285  super(MinMaxArgPos, self).__init__(*args, **kwargs)
286 
287 
288  def printIndex(self):
289  print str(self.index)
290 
291 
292 
304 class MinMaxArgPosOpt(EmbeddedDocument):
305  min = FloatField()
306  max = FloatField()
307  argPos = IntField()
308  index = ReferenceField(IndexSet)
309 
310  def __init__(self, *args, **kwargs):
311  super(MinMaxArgPosOpt, self).__init__(*args, **kwargs)
312 
313 
314  def printIndex(self):
315  print str(self.index)
316 
317 '''
318 Currently not working
319 '''
320 class IOP(DictField):
321 
322  def __init__(self, field=None, *args, **kwargs):
323  #if not isinstance(field, BaseField):
324  # self.error('Argument to MapField constructor must be a valid '
325  # 'field')
326  super(IOP, self).__init__(field=field, *args, **kwargs)
327 
328 
329  def size(self):
330  size = 0
331  for k in self._fields.keys():
332  if 'index' in v:
333  size += v.index.iterator_size()
334  else:
335  size += 1
336 
337  return size
338 
339 
340  def iteritems(self):
341  for k in self._fields.keys():
342  if 'index' in v:
343  for idx in v.index.names:
344  yield '%s[%s]' % (k, idx), v
345  else:
346  yield k, v
347 
348 
349  def keys(self):
350  for k, v in self._fields.iteritems():
351  if 'index' in v:
352  for idx in v.index.names:
353  yield '%s[%s]' % (k, idx)
354  else:
355  yield k
356 
357 
358 
373 class SurrogateFunction(DynamicDocument):
374  # Database definition
375  name = StringField(primary_key=True)
376  inputs = MapField(EmbeddedDocumentField(MinMaxArgPosOpt))
377  outputs = MapField(EmbeddedDocumentField(MinMaxArgPos))
378  parameters = MapField(EmbeddedDocumentField(MinMaxArgPos))
379  functionName = StringField(required=True)
380  libraryName = StringField(required=True)
381  indices = MapField(ReferenceField(IndexSet))
382  meta = {'allow_inheritance': True}
383 
384  @abc.abstractmethod
385 
388  def __init__(self, *args, **kwargs):
389  if kwargs.has_key('_cls'):
390  super(SurrogateFunction, self).__init__(*args, **kwargs)
391  else:
392  super(SurrogateFunction, self).__init__()
393 
394  argPos = kwargs.pop('argPos', False)
395  if not argPos:
396  nInp = 0;
397  for k, v in kwargs['inputs'].iteritems():
398  if 'argPos' in v:
399  raise Exception(
400  'argPos in function for inputs %s (old format)' % k +
401  ' -- delete argPos from function'
402  )
403  if not 'index' in v:
404  v['argPos'] = nInp
405  nInp += 1
406 
407  for k, v in kwargs['inputs'].iteritems():
408  if 'index' in v:
409  v['argPos'] = nInp
410  nInp += v['index'].iterator_size()
411 
412  for k, v in kwargs['inputs'].iteritems():
413  if not isinstance(v, MinMaxArgPosOpt):
414  self.inputs[k] = MinMaxArgPosOpt(**v)
415 
416  for k, v in kwargs['outputs'].iteritems():
417  if not isinstance(v, MinMaxArgPos):
418  self.outputs[k] = MinMaxArgPos(**v)
419 
420  for k, v in kwargs['parameters'].iteritems():
421  if not isinstance(v, MinMaxArgPos):
422  self.parameters[k] = MinMaxArgPos(**v)
423 
424  if 'indices' in kwargs:
425  for k, v in kwargs['indices'].iteritems():
426  self.indices[k] = kwargs['indices'][k]
427 
428  self.initKwargs(kwargs)
429 
430  for k in self.inputs.keys():
431  self.checkVariableName(k)
432 
433  for k in self.outputs.keys():
434  self.checkVariableName(k)
435 
436  for k in self.parameters.keys():
437  self.checkVariableName(k)
438 
439  self.save()
440 
441 
442  @abc.abstractmethod
443 
445  def initKwargs(self, kwargs):
446  raise NotImplementedError('initKwargs not implemented!')
447 
448 
449 
454  def indexSet(self, name):
455  return self.indices[name]
456 
457 
458 
460  def checkVariableName(self, name):
461  m = re.search(r'[(.*)]', name)
462  if m and not m.group(1) in self.indices:
463  raise Exception('Index %s not defined' % m.group(1))
464 
465 
466 
468  def inputs_iterAll(self):
469  for k, v in self.inputs.iteritems():
470  if 'index' in v:
471  for idx in v.index.names:
472  yield '%s[%s]' % (k, idx), v
473  else:
474  yield k, v
475 
476 
477 
479  def inputs_size(self):
480  size = 0
481  for k, v in self.inputs.iteritems():
482  if 'index' in v:
483  size += v.index.iterator_size()
484  else:
485  size += 1
486 
487  return size
488 
489 
490  @classmethod
491 
495  def exceptionLoad(self, surrogateFunctionId):
496  return 201
497 
498 
499  @classmethod
500 
506  def load(self, surrogateFunctionId):
507  return self.objects.get(_id=surrogateFunctionId)
508 
509 
510 
515  def __init__(self, *args, **kwargs):
516  super(CFunction, self).__init__(*args, **kwargs)
517 
518 
519 
528  def initKwargs(self, kwargs):
529  if not kwargs.has_key('Ccode'):
530  raise Exception('Need Ccode')
531 
532  ln = self.compileCcode(kwargs)
533  fn = re.search(
534  'void\s*(.*)\s*\('
535  '\s*const\s*modena_model_t\s*\*\s*model\s*,'
536  '\s*const\s*double\s*\*\s*inputs\s*,'
537  '\s*double\s*\*\s*outputs\s*\)',
538  kwargs['Ccode']
539  ).group(1)
540  fn = fn.strip(' \t\n\r')
541 
542  self.name = fn
543  self.libraryName = ln
544  self.functionName = fn
545 
546 
547 
549  def compileCcode(self, kwargs):
550 
551  m = hashlib.md5()
552  m.update(kwargs['Ccode'])
553  h = m.hexdigest()
554  d = 'func_' + h
555  ln = '%s/%s/lib%s.so' % (os.getcwd(), d, h)
556 
557  if(True or not os.path.exists(ln)):
558  if(not os.path.isdir(d)): os.mkdir(d)
559  os.chdir(d)
560 
561  env = jinja2.Environment(lstrip_blocks=True, trim_blocks=True)
562 
563  child = env.from_string(r'''
564  {% extends Ccode %}
565  {% block variables %}
566  const double* parameters = model->parameters;
567  {% for k, v in pFunction.inputs.iteritems() %}
568  {% if 'index' in v %}
569  const size_t {{k}}_argPos = {{v.argPos}};
570  const double* {{k}} = &inputs[{{k}}_argPos];
571  const size_t {{k}}_size = {{ v.index.iterator_size() }};
572  {% else %}
573  const size_t {{k}}_argPos = {{v['argPos']}};
574  const double {{k}} = inputs[{{k}}_argPos];
575  {% endif %}
576  {% endfor %}
577  {% endblock %}
578  ''')
579 
580  parent = env.from_string(kwargs['Ccode'])
581 
582  #print child.render(pFunction=kwargs, Ccode=parent)
583  child.stream(pFunction=kwargs, Ccode=parent).dump('%s.c' % h)
584 
585  f = open('CMakeLists.txt', 'w')
586  f.write("""
587 cmake_minimum_required (VERSION 2.8)
588 project (%(h)s C)
589 
590 #set(CMAKE_BUILD_TYPE Debug)
591 
592 find_package(MODENA REQUIRED)
593 find_package(LTDL REQUIRED)
594 
595 add_library(%(h)s MODULE %(h)s.c)
596 target_link_libraries(%(h)s MODENA::modena ${LTDL_LIBRARIES})
597 
598 install(TARGETS %(h)s DESTINATION ${CMAKE_INSTALL_PREFIX}/lib )
599 """ % {'h': h})
600  f.close()
601 
602  from subprocess import call
603  call(['cmake', '.'])
604  call(['make'])
605  os.chdir('..')
606 
607  return ln
608 
609 
610 
615 class Function(CFunction):
616  def __init__(self, *args, **kwargs):
617  if kwargs.has_key('_cls'):
618  super(Function, self).__init__(*args, **kwargs)
619  if kwargs.has_key('libraryName'):
620  super(Function, self).__init__(*args, **kwargs)
621  else:
622  # This is a bad check, make a better one...
623  if not kwargs.has_key('function'):
624  raise Exception('Algebraic representation not found')
625 
626  # lambda function writing inputs, parameters
627  cDouble = lambda VAR: '\n'.join(
628  [
629  'const double %s = %s[%s];' % (
630  V, VAR, kwargs[VAR][V]['argPos']
631  )
632  for V in kwargs[VAR]
633  ]
634  )
635 
636  # lambda function parsing 'function' and writing outputs
637  outPut = lambda OUT: '\n'.join(
638  [
639  'outputs[%s] = %s;' % (
640  kwargs['outputs'][O]['argPos'],
641  self.Parse(kwargs['function'][O])
642  )
643  for O in kwargs[OUT]
644  ]
645  )
646 
647  # Main body of the Ccode
648  Ccode='''
649 #include "modena.h"
650 #include "math.h"
651 
652 void {name}
653 (
654  const modena_model* model,
655  const double* inputs,
656  double *outputs
657 )
658 {{
659 {inputs}
660 {parameters}
661 {outputs}
662 }}
663 '''
664  kwargs['Ccode'] = Ccode.format(
665  name=kwargs['function']['name'],
666  inputs=cDouble('inputs'),
667  parameters=cDouble('parameters'),
668  outputs=outPut('outputs')
669  )
670 
671  super(Function, self).__init__(*args, **kwargs)
672 
673 
674  def Parse(self, formula, debug=False, model='', stack={}, delim=0, \
675  var=r'[A-Za-z]+\d*',add=r'\+',sub=r'-',mul=r'\*',\
676  div=r'/',pow=r'\^',dig=r'\d+\.?\d*'\
677  ):
678  operators=r'%s|%s|%s|%s|%s' %(add,sub,mul,div,pow)
679  ldel=r'\('
680  rdel=r'\)'
681 
682  #Test explicitly for empty string. Returning error.
683  empty = re.match('\s',formula)
684  if empty:
685  print 'Error: The string is empty'
686  return
687 
688  formula = re.sub(r'\s+','',formula)
689 
690  # Initialise a dictionary stack.
691  stack = stack or {}
692 
693  # Python has no switch - case construct. Match all possibilities first and
694  # test afterwards:
695  re_var = re.match(var,formula)
696  re_dig = re.match(dig,formula)
697  re_ldel = re.match(ldel,formula)
698  re_rdel = re.match(rdel,formula)
699  re_oper = re.match(operators,formula)
700 
701  # Parameter followed by an optional number. Allow 'p' or 'p0' as variable names
702  if re_var:
703  tail = formula[len(re_var.group(0)):]
704  head = re_var.group(0)
705 
706  elif re_dig:
707  tail = formula[len(re_dig.group(0)):]
708  head = re_dig.group(0)
709 
710  elif re_oper:
711  head = re_oper.group(0)
712  tail = formula[1:]
713 
714  # Left delimiter.
715  elif re_ldel:
716  head = re_ldel.group(0)
717  tail = formula[1:]
718  delim += 1
719 
720  # Right delimiter followed by an optional number (default is 1).
721  elif re_rdel:
722  head = re_rdel.group(0)
723  tail = formula[len(re_rdel.group(0)):]
724  delim -= 1
725 
726  # Testing if there is a parenthesis imbalance.
727  if delim < 0:
728  raise Exception('Unmatched parenthesis.')
729 
730  # Wrong syntax. Returning an error message.
731  else:
732  raise Exception('The expression syntax is not suported.')
733 
734  model += head
735 
736  # The formula has not been consumed yet. Continue recursive parsing.
737  if len(tail) > 0:
738  return self.Parse(tail,debug,model,stack,delim)
739 
740  # Nothing left to parse. Stop recursion.
741  else:
742  return model
743 
744 
745 
765 class SurrogateModel(DynamicDocument):
766  # List of all instances (for initialisation)
767  ___refs___ = []
768 
769  # Database definition
770  _id = StringField(primary_key=True)
771  surrogateFunction = ReferenceField(SurrogateFunction, required=True)
772  parameters = ListField(FloatField())
773  meta = {'allow_inheritance': True}
774 
775 
777  def __init__(self, *args, **kwargs):
778  self.___refs___.append(weakref.ref(self))
779 
780  if kwargs.has_key('_cls'):
781  super(SurrogateModel, self).__init__(*args, **kwargs)
782  self.___indices___ = self.parseIndices(self._id)
783  #print '--- Loaded model', self._id
784 
785  if hasattr(self, 'importFrom'):
786  __import__(self.importFrom)
787 
788  else:
789  if not kwargs.has_key('_id'):
790  raise Exception('Need _id')
791 
792  #print '--- Initialising model', kwargs['_id']
793 
794  if not kwargs.has_key('surrogateFunction'):
795  raise Exception('Need surrogateFunction')
796  if not isinstance(kwargs['surrogateFunction'], SurrogateFunction):
797  raise TypeError('Need surrogateFunction')
798 
799  self.___indices___ = self.parseIndices(kwargs['_id'])
800 
801  kwargs['fitData'] = {}
802  kwargs['inputs'] = {}
803  for k, v in kwargs['surrogateFunction'].inputs_iterAll():
804  kwargs['inputs'][k] = v.to_mongo()
805  if 'index' in kwargs['inputs'][k]:
806  del kwargs['inputs'][k]['index']
807  if 'argPos' in kwargs['inputs'][k]:
808  del kwargs['inputs'][k]['argPos']
809 
810  kwargs['outputs'] = {}
811  for k, v in kwargs['surrogateFunction'].outputs.iteritems():
812  k = self.expandIndices(k)
813  kwargs['fitData'][k] = []
814  kwargs['outputs'][k] = MinMaxArgPosOpt(**{})
815 
816  for k, v in kwargs['inputs'].iteritems():
817  kwargs['fitData'][k] = []
818  kwargs['inputs'][k] = MinMaxArgPosOpt(**v)
819 
820  for k, v in kwargs['inputs'].iteritems():
821  if 'argPos' in v and not v['argPos'] == kwargs['surrogateFunction'].inputs[k].argPos:
822  raise Exception('argPos in function and model must be the same -- delete argPos from model')
823 
824  self.initKwargs(kwargs)
825 
827  kwargs,
828  'initialisationStrategy',
829  InitialisationStrategy
830  )
831 
832  super(SurrogateModel, self).__init__(*args, **kwargs)
833 
834  subOutputs = {}
835  for m in self.substituteModels:
836  if not isinstance(m, SurrogateModel):
837  raise TypeError(
838  'Elements of substituteModels '
839  'must be derived from SurrogateModel'
840  )
841  subOutputs.update(m.outputsToModels())
842 
843  #print 'inputs for', self._id
844  #print 'subOutputs=', subOutputs.keys()
845  #print 'inputs =', self.inputs.keys()
846 
847  nInp = len(self.inputs)
848  for o in subOutputs.keys():
849  try:
850  self.inputs_argPos(o)
851  del self.inputs[o]
852  del self.fitData[o]
853 
854  for k, v in subOutputs[o].inputs.iteritems():
855  try:
856  self.inputs_argPos(k)
857  except ArgPosNotFound:
858  self.inputs[k] = subOutputs[o].inputs[k]
859  self.inputs[k].argPos = nInp
860  nInp += 1
861 
862  except ArgPosNotFound:
863  pass
864 
865  self.save()
866 
867  #print 'model =', self._id, len(self.inputs)
868  #for k, v in self.inputs.iteritems():
869  # print 'inputs in model', k, self.inputs_argPos(k)
870  #for k, v in self.surrogateFunction.inputs_iterAll():
871  # print 'inputs in function', k, v.argPos
872  #print('parameters = [%s]' % ', '.join('%g' % v for v in self.parameters))
873 
874 
875  @abc.abstractmethod
876  def initKwargs(self, kwargs):
877  raise NotImplementedError('initKwargs not implemented!')
878 
879 
880 
886  def parseIndices(self, name):
887  indices = {}
888  m = re.search('\[(.*)\]', name)
889  if m:
890  for exp in m.group(1).split(','):
891  m = re.search('(.*)=(.*)', exp)
892  if m:
893  indices[m.group(1)] = m.group(2)
894  else:
895  raise Exception('Unable to parse %s' % exp)
896 
897  return indices
898 
899 
900 
906  def expandIndices(self, name):
907  m = re.search('\[(.*)\]', name)
908  if m:
909  try:
910  return re.sub(
911  '\[(.*)\]',
912  '[%s]' % ','.join(
913  '%s' % self.___indices___[exp]
914  for exp in m.group(1).split(',')
915  ),
916  name
917  )
918  except ArgPosNotFound:
919  raise Exception('Unable to expand indices in %s' % name)
920 
921  return name
922 
923 
924  def expandIndicesWithName(self, name):
925  m = re.search('\[(.*)\]', name)
926  if m:
927  try:
928  return re.sub(
929  '\[(.*)\]',
930  '[%s]' % ','.join(
931  '%s=%s' % (exp, self.___indices___[exp])
932  for exp in m.group(1).split(',')
933  ),
934  name
935  )
936  except ArgPosNotFound:
937  raise Exception('Unable to expand indices in %s' % name)
938 
939  return name
940 
941 
942 
946  def outputsToModels(self):
947  o = { k: self for k in self.outputs.keys() }
948  for m in self.substituteModels:
949  o.update(m.outputsToModels())
950  return o
951 
952 
953 
958  def inputsMinMax(self):
959 
960 
964  def new(Min, Max):
965  obj = type('MinMax', (object,), {})
966  obj.min = Min
967  obj.max = Max
968  return obj
969 
970  i = { k: new(v.min, v.max) for k, v in self.surrogateFunction.inputs_iterAll() }
971 
972  for m in self.substituteModels:
973  for k, v in m.inputsMinMax().iteritems():
974  if k in i:
975  v.min = max(v.min, i[k].min)
976  v.max = min(v.max, i[k].max)
977  else:
978  i[k] = new(v.min, v.max)
979 
980  return i
981 
982 
983 
986  def inputs_argPos(self, name):
987  m = re.search('(.*)\[(.*=)?(.*)]', name)
988  if m:
989  try:
990  base = m.group(1)
991  return existsAndHasArgPos(self.surrogateFunction.inputs, base) \
992  + self.surrogateFunction.inputs[base].index.get_index(m.group(3))
993  except:
994  raise ArgPosNotFound('argPos for ' + name + ' not found in inputs')
995  else:
996  try:
997  return existsAndHasArgPos(self.inputs, name)
998  except:
999  try:
1000  return existsAndHasArgPos(self.surrogateFunction.inputs, name)
1001  except:
1002  raise ArgPosNotFound('argPos for ' + name + ' not found in inputs')
1003 
1004 
1005 
1007  def outputs_argPos(self, name):
1008  try:
1009  return existsAndHasArgPos(self.outputs, name)
1010  except:
1011  try:
1012  return existsAndHasArgPos(
1013  self.surrogateFunction.outputs,
1014  name
1015  )
1016  except:
1017  raise ArgPosNotFound('argPos for ' + name + ' not found in outputs')
1018 
1019 
1020 
1022  def parameters_argPos(self, name):
1023  try:
1024  return existsAndHasArgPos(self.parameters, name)
1025  except:
1026  try:
1027  return existsAndHasArgPos(
1028  self.surrogateFunction.parameters,
1029  name
1030  )
1031  except:
1032  raise ArgPosNotFound('argPos for ' + name + ' not found in parameters')
1033 
1034 
1035 
1037  def calculate_maps(self, sm):
1038  map_outputs = []
1039  map_inputs = []
1040 
1041  for k in self.inputs:
1042  try:
1043  map_inputs.extend([self.inputs_argPos(k), sm.inputs_argPos(k)])
1044  except ArgPosNotFound:
1045  pass
1046 
1047  for k, v in sm.surrogateFunction.outputs.iteritems():
1048  try:
1049  map_outputs.extend(
1050  [v.argPos, self.inputs_argPos(sm.expandIndices(k))]
1051  )
1052  except ArgPosNotFound:
1053  pass
1054 
1055  #print 'maps: output =', map_outputs, 'input =', map_inputs
1056  return map_outputs, map_inputs
1057 
1058 
1059 
1061  def minMax(self):
1062  l = self.surrogateFunction.inputs_size()
1063  minValues = [-9e99] * l
1064  maxValues = [9e99] * l
1065 
1066  for k, v in self.inputs.iteritems():
1067  minValues[self.inputs_argPos(k)] = v.min
1068  maxValues[self.inputs_argPos(k)] = v.max
1069 
1070  #print 'min =', minValues, 'max =', maxValues,
1071  return minValues, maxValues, \
1072  self.inputs.keys(), \
1073  self.outputs.keys(), \
1074  self.surrogateFunction.parameters.keys()
1075 
1076 
1077 
1079  def updateMinMax(self):
1080  if not self.nSamples:
1081  for v in self.inputs.values():
1082  v.min = 9e99
1083  v.max = -9e99
1084 
1085  for v in self.outputs.values():
1086  v.min = 9e99
1087  v.max = -9e99
1088 
1089  for k, v in self.inputs.iteritems():
1090  v.min = min(self.fitData[k])
1091  v.max = max(max(self.fitData[k]), v.min*1.000001)
1092 
1093  for k, v in self.outputs.iteritems():
1094  v.min = min(self.fitData[k])
1095  v.max = max(self.fitData[k])
1096 
1097 
1098 
1105  def error(self, cModel, **kwargs):
1106  idxGenerator = kwargs.pop('idxGenerator', xrange(self.nSamples))
1107  checkBounds = kwargs.pop('checkBounds', True)
1108 
1109  i = [0] * self.surrogateFunction.inputs_size()
1110 
1111  # TODO: Deal with multivalued functions
1112  output = self.fitData[six.next(six.iterkeys(self.outputs))]
1113 
1114  for idx in idxGenerator:
1115  # Load inputs
1116  for k, v in self.inputs.iteritems():
1117  i[self.inputs_argPos(k)] = self.fitData[k][idx]
1118 
1119  #print 'i = {', ', '.join('%s: %g' % (
1120  # k, self.fitData[k][idx]
1121  #) for k in self.inputs.keys()), '}'
1122  #print 'i =', str(i)
1123 
1124  # Call the surrogate model
1125  out = cModel(i, checkBounds= checkBounds)
1126 
1127  #print '%i %g - %g = %g' % (
1128  # idx, out[0], output[idx], out[0] - output[idx]
1129  #)
1130  yield out[0] - output[idx]
1131 
1132 
1133 
1139  def __getattribute__(self, name):
1140  if name.startswith( '___' ):
1141  return object.__getattribute__(self, name)
1142  else:
1143  return super(SurrogateModel, self).__getattribute__(name)
1144 
1145 
1146 
1152  def __setattribute__(self, name, value):
1153  if name.startswith( '___' ):
1154  object.__setattribute__(self, name, value)
1155  else:
1156  super(SurrogateModel, self).__setattribute__(name, value)
1157 
1158 
1159 
1165  def exceptionOutOfBounds(self, oPoint):
1166  oPointDict = {
1167  k: oPoint[self.inputs_argPos(k)] for k in self.inputs.keys()
1168  }
1169  self.outsidePoint = EmbDoc(**oPointDict)
1170  self.save()
1171  return 200
1172 
1173 
1174  @classmethod
1175 
1185  def exceptionLoad(self, surrogateModelId):
1186  collection = self._get_collection()
1187  collection.update(
1188  { '_id': surrogateModelId },
1189  { '_id': surrogateModelId },
1190  upsert=True
1191  )
1192  return 201
1193 
1194 
1195  @classmethod
1196 
1200  def exceptionParametersNotValid(self, surrogateModelId):
1201  return 202
1202 
1203 
1204 
1209  def callModel(self, inputs):
1210  #print 'In callModel', self._id
1211  # Instantiate the surrogate model
1212  cModel = modena.libmodena.modena_model_t(model=self)
1213 
1214  i = [0] * self.surrogateFunction.inputs_size()
1215 
1216  for m in self.substituteModels:
1217  inputs.update(m.callModel(inputs))
1218 
1219  #print 'inputs', inputs.keys()
1220 
1221  # Set inputs
1222  for k, v in self.inputs.iteritems():
1223  i[self.inputs_argPos(k)] = inputs[k]
1224 
1225  # Call the surrogate model
1226  out = cModel(i)
1227 
1228  outputs = {
1229  self.expandIndices(k): out[v.argPos]
1230  for k, v in self.surrogateFunction.outputs.iteritems()
1231  }
1232 
1233  #print 'outputs', outputs.keys()
1234 
1235  return outputs
1236 
1237 
1238 
1240  def updateFitDataFromFwSpec(self, fw_spec):
1241  # Load the fitting data
1242  # Removed temporarily, probably bug in mongo engine
1243  #self.reload('fitData')
1244 
1245  for k, v in self.inputs.iteritems():
1246  if fw_spec[k][0].__class__ == list:
1247  self.fitData[k].extend(fw_spec[k][0])
1248  else:
1249  self.fitData[k].extend(fw_spec[k])
1250 
1251  for k in self.outputs:
1252  if fw_spec[k][0].__class__ == list:
1253  self.fitData[k].extend(fw_spec[k][0])
1254  else:
1255  self.fitData[k].extend(fw_spec[k])
1256 
1257  # Get first set
1258  firstSet = six.next(six.itervalues(self.fitData))
1259  self.nSamples = len(firstSet)
1260 
1261 
1262 
1264  def initialisationStrategy(self):
1265  return loadType(
1266  self,
1267  'initialisationStrategy',
1268  InitialisationStrategy
1269  )
1270 
1271 
1272  @classmethod
1273 
1275  def load(self, surrogateModelId):
1276  # Removed temporarily, probably bug in mongo engine
1277  #return self.objects.exclude('fitData').get(_id=surrogateModelId)
1278 
1279  #print self.objects.get(_id=surrogateModelId).nix
1280  return self.objects.get(_id=surrogateModelId)
1281 
1282 
1283  @classmethod
1284 
1286  def loadFailing(self):
1287  # Removed temporarily, probably bug in mongo engine
1288  #return self.objects(
1289  # __raw__={'outsidePoint': { '$exists': True}}
1290  #).exclude('fitData').first()
1291  return self.objects(
1292  __raw__={'outsidePoint': { '$exists': True}}
1293  ).first()
1294 
1295 
1296  @classmethod
1297 
1299  def loadFromModule(self):
1300  collection = self._get_collection()
1301  doc = collection.find_one({ '_cls': { '$exists': False}})
1302  modName = re.search('(.*)(\[.*\])?', doc['_id']).group(1)
1303  # TODO:
1304  # Give a better name to the variable a model is imported from
1305  try:
1306  mod = __import__(modName)
1307  return (m for m in modena.BackwardMappingModel.get_instances() if m._id == modName).next()
1308  except ImportError:
1309  print "MoDeNa framework error: could not find '%s' " %(modName)
1310 
1311 
1312  @classmethod
1313 
1315  def loadParametersNotValid(self):
1316  return self.objects(
1317  __raw__={ 'parameters': { '$size': 0 } }
1318  ).first()
1319 
1320 
1321  @classmethod
1322 
1324  def get_instances(self):
1325  for inst_ref in self.___refs___:
1326  inst = inst_ref()
1327  if inst is not None:
1328  yield inst
1329 
1330  def __repr__(self):
1331  return self._id
1332 
1333 
1334 
1348  # Database definition
1349  inputs = MapField(EmbeddedDocumentField(MinMaxArgPosOpt))
1350  outputs = MapField(EmbeddedDocumentField(MinMaxArgPosOpt))
1351  substituteModels = ListField(ReferenceField(SurrogateModel))
1352  meta = {'allow_inheritance': True}
1353 
1354  def __init__(self, *args, **kwargs):
1355  super(ForwardMappingModel, self).__init__(*args, **kwargs)
1356 
1357 
1358  def initKwargs(self, kwargs):
1359  if 'initialisationStrategy' not in kwargs:
1360  kwargs['initialisationStrategy'] = \
1361  EmptyInitialisationStrategy()
1362 
1363 
1364 
1368  def exactTasks(self, points):
1369  return Workflow([])
1370 
1371 
1372 
1376  # Database definition
1377  inputs = IOP(EmbeddedDocumentField(MinMaxArgPosOpt))
1378  outputs = MapField(EmbeddedDocumentField(MinMaxArgPosOpt))
1379  fitData = MapField(ListField(FloatField(required=True)))
1380  substituteModels = ListField(ReferenceField(SurrogateModel))
1381  outsidePoint = EmbeddedDocumentField(EmbDoc)
1382  meta = {'allow_inheritance': True}
1383 
1384 
1385  def __init__(self, *args, **kwargs):
1386  super(BackwardMappingModel, self).__init__(*args, **kwargs)
1387 
1388 
1389  def initKwargs(self, kwargs):
1390  checkAndConvertType(kwargs, 'exactTask', FireTaskBase)
1391 
1393  kwargs,
1394  'outOfBoundsStrategy',
1395  OutOfBoundsStrategy
1396  )
1397 
1399  kwargs,
1400  'parameterFittingStrategy',
1401  ParameterFittingStrategy
1402  )
1403 
1404 
1405 
1409  def exactTasks(self, points):
1410 
1411  # De-serialise the exact task from dict
1412  et = load_object(self.meth_exactTask)
1413 
1414  tl = []
1415  e = six.next(six.itervalues(points))
1416  for i in xrange(len(e)):
1417  p = { k: points[k][i] for k in points }
1418 
1419  t = et
1420  t['point'] = p
1421  t['indices'] = self.___indices___
1422  t['modelId'] = self._id
1423  fw = Firework(t)
1424 
1425  tl.append(fw)
1426 
1427  return Workflow(tl, name='exact tasks for new points')
1428 
1429 
1430  def parameterFittingStrategy(self):
1431  pfs = loadType(
1432  self,
1433  'parameterFittingStrategy',
1434  ParameterFittingStrategy
1435  )
1436 
1437  # Nasty hack to work around a bug somewhere in mongoengine or fireworks
1438  self._changed_fields = filter(
1439  lambda a: a != u'improveErrorStrategy._fw_name', self._changed_fields
1440  )
1441 
1442  return pfs
1443 
1444 
1445  def outOfBoundsStrategy(self):
1446  return loadType(
1447  self,
1448  'outOfBoundsStrategy',
1449  OutOfBoundsStrategy
1450  )
1451 
1452 
1453 
1480  def extendedRange(self, outsidePoint, expansion_factor=1.2):
1481 
1482  sampleRange = {}
1483  limitPoint = {}
1484 
1485  for k, v in self.inputs.iteritems():
1486  sampleRange[k] = {}
1487  outsideValue = outsidePoint[k]
1488  inputsMinMax = self.inputsMinMax()
1489 
1490  # If the value outside point is outside the range, set the
1491  # "localdict" max to the outside point value
1492 
1493  if outsideValue > v['max']:
1494  if outsideValue > inputsMinMax[k].max:
1495  raise OutOfBounds(
1496  'new value is larger than function min for %s' % k
1497  )
1498 
1499  value = min(
1500  outsideValue*expansion_factor,
1501  inputsMinMax[k].max
1502  )
1503 
1504  sampleRange[k]['min'] = v['max']
1505  sampleRange[k]['max'] = value
1506  limitPoint[k] = value
1507 
1508  elif outsideValue < v['min']:
1509  if outsideValue < inputsMinMax[k].min:
1510  raise OutOfBounds(
1511  'new value is smaller than function max for %s' % k
1512  )
1513 
1514  value = max(
1515  outsideValue/expansion_factor,
1516  inputsMinMax[k].min
1517  )
1518 
1519  sampleRange[k]['min'] = value
1520  sampleRange[k]['max'] = v['min']
1521  limitPoint[k] = value
1522 
1523  else:
1524  sampleRange[k]['min'] = v['min']
1525  sampleRange[k]['max'] = v['max']
1526  limitPoint[k] = random.uniform(v['min'], v['max'])
1527 
1528  return sampleRange, limitPoint
1529 
1530 
1531 
def checkAndConvertType(kwargs, name, cls)
Function checking if the type of the strategy "name" provided by the user is correct, i.e.
Class for defining &#39;backward mapping&#39; models.
Class list that is automatically extended when index is out of range.
def existsAndHasArgPos(i, name)
Function checking whether the model inputs corresponds to the arguments.
def loadType(obj, name, cls)
Function that helps loading strategy "name" from model "obj".
Class for defining &#39;forward mapping&#39; models.
Class wrapper for DynamicEmbeddedDocument from MongeEngine.
Class for defining Surrogate Functions where the executable code is a C- function.