class DOEdriver(CaseIterDriverBase): """ Driver for Design of Experiments. """ # pylint: disable-msg=E1101 DOEgenerator = Slot(IDOEgenerator, iotype='in', required=True, desc='Iterator supplying normalized DOE values.') record_doe = Bool(True, iotype='in', desc='Record normalized DOE values to CSV file.') doe_filename = Str('', iotype='in', desc='Name of CSV file to record to' ' (default is <driver-name>.csv)') case_outputs = ListStr([], iotype='in', desc='A list of outputs to be saved with each case.') case_filter = Slot(ICaseFilter, iotype='in', desc='Selects cases to be run.') def execute(self): """Generate and evaluate cases.""" self._csv_file = None try: super(DOEdriver, self).execute() finally: if self._csv_file is not None: self._csv_file.close() def get_case_iterator(self): """Returns a new iterator over the Case set.""" return self._get_cases() def _get_cases(self): """Generate each case.""" params = self.get_parameters().values() self.DOEgenerator.num_parameters = len(params) record_doe = self.record_doe events = self.get_events() outputs = self.case_outputs case_filter = self.case_filter if record_doe: if not self.doe_filename: self.doe_filename = '%s.csv' % self.name self._csv_file = open(self.doe_filename, 'wb') csv_writer = csv.writer(self._csv_file) for i, row in enumerate(self.DOEgenerator): if record_doe: csv_writer.writerow(['%.16g' % val for val in row]) vals = [p.low+(p.high-p.low)*val for p,val in zip(params,row)] case = self.set_parameters(vals, Case(parent_uuid=self._case_id)) # now add events for varname in events: case.add_input(varname, True) case.add_outputs(outputs) if case_filter is None or case_filter.select(i, case): yield case
class SimpleComp(Component): cont_in = Slot(DumbVT, iotype='in') cont_out = Slot(DumbVT, iotype='out') def __init__(self): super(SimpleComp, self).__init__() self.add('cont_in', DumbVT()) self.add('cont_out', DumbVT())
class DumbVT(VariableTree): vt2 = Slot(DumbVT2, iotype='in') def __init__(self): super(DumbVT, self).__init__() self.add('vt2', DumbVT2()) self.add('v1', Float(1., desc='vv1')) self.add('v2', Float(2., desc='vv2'))
class DumbVT2(VariableTree): vt3 = Slot(DumbVT3, iotype='in') def __init__(self): super(DumbVT2, self).__init__() self.add('x', Float(-1.)) self.add('y', Float(-2.)) self.add('vt3', DumbVT3())
class DistributionCaseDriver(CaseIterDriverBase): """ Driver for evaluating models at point distributions. """ implements(IHasParameters) distribution_generator = Slot( IDistributionGenerator, iotype='in', required=True, desc='Iterator supplying values of point distribitions.') case_outputs = List(Str, iotype='in', desc='A list of outputs to be saved with each case.') def get_case_iterator(self): """Returns a new iterator over the Case set.""" return self._get_cases() def _get_cases(self): """Iterator over the cases""" params = self.get_parameters().values() self.distribution_generator.num_parameters = len(params) for row in self.distribution_generator: case = self.set_parameters(row, Case(parent_uuid=self._case_id)) case.add_outputs(self.case_outputs) yield case
class HAWC2PostPowerCurve(Component): cases = Slot(ICaseIterator, iotype='in') rotor_loads = VarTree(RotorLoadsArrayVT(), iotype='out') nraverage = Int(200) def execute(self): wsp = [] P = [] T = [] Q = [] for case in self.cases: wsp.append(case.get_input('builder.Ps.wind.wsp')) out = case.get_output('wrapper.output') data = out.datasets[0].get_channels([10, 11, 12]) Q.append(np.average(data[-self.nraverage:, 0])) P.append(np.average(data[-self.nraverage:, 1])) T.append(np.average(data[-self.nraverage:, 2])) # sort the cases to make sure they are stored with increasing wsp zipped = zip(wsp, T, Q, P) zipped.sort() wsp, T, Q, P = zip(*zipped) self.rotor_loads.wsp = wsp self.rotor_loads.T = np.array(T) * 1000. self.rotor_loads.Q = np.array(Q) * 1000. self.rotor_loads.P = np.array(P) * 1000.
class DOEdriver(CaseIterDriverBase): """ Driver for Design of Experiments """ # pylint: disable-msg=E1101 DOEgenerator = Slot(IDOEgenerator, iotype='in', required=True, desc='Iterator supplying normalized DOE values.') case_outputs = ListStr([], iotype='in', desc='A list of outputs to be saved with each case.') def get_case_iterator(self): """Returns a new iterator over the Case set.""" return self._get_cases() def _get_cases(self): params = self.get_parameters().values() self.DOEgenerator.num_parameters = len(params) for row in self.DOEgenerator: vals = [p.low+(p.high-p.low)*val for p,val in zip(params,row)] case = self.set_parameters(vals, Case(parent_uuid=self._case_id)) # now add events for varname in self.get_events(): case.add_input(varname,True) case.add_outputs(self.case_outputs) yield case
class NeighborhoodDOEdriver(CaseIterDriverBase): """Driver for Design of Experiments within a specified neighborhood around a point.""" # pylint: disable-msg=E1101 DOEgenerator = Slot(IDOEgenerator, iotype='in', required=True, desc='Iterator supplying normalized DOE values.') case_outputs = ListStr([], iotype='in', desc='A list of outputs to be saved with each case.') alpha = Float(.3, low=.01, high =1.0, iotype='in', desc='Multiplicative factor for neighborhood DOE Driver.') beta = Float(.01, low=.001, high=1.0, iotype='in', desc='Another factor for neighborhood DOE Driver.') def get_case_iterator(self): """Returns a new iterator over the Case set.""" return self._get_cases() def _get_cases(self): params = self.get_parameters().values() self.DOEgenerator.num_parameters = len(params) M = [] P = [] for p in params: temp = p.evaluate() P.append(temp) M.append((temp-p.low)/(p.high-p.low)) for row in list(self.DOEgenerator)+[tuple(M)]: vals = [] for p,val,curval in zip(params, row, P): delta_low = curval-p.low k_low = 1.0/(1.0+(1-self.beta)*delta_low) new_low = curval - self.alpha*k_low*delta_low#/(self.exec_count+1) delta_high = p.high-curval k_high = 1.0/(1.0+(1-self.beta)*delta_high) new_high = curval + self.alpha*k_high*delta_high#/(self.exec_count+1) newval = new_low+(new_high-new_low)*val vals.append(newval) case = self.set_parameters(vals, Case(parent_uuid=self._case_id)) # now add events for varname in self.get_events(): case.add_input(varname, True) case.add_outputs(self.case_outputs) yield case
class DOE_Maker(Component): cases = List([], iotype='in', desc='list of integers of maximum sample size') DOEgen = Slot(IDOEgenerator, iotype='out') def __init__(self, doc=None): super(DOE_Maker, self).__init__(doc) #self.force_execute = True def execute(self): n = self.cases.pop() print 'Number of training cases = ', n self.DOEgen = Uniform(num_samples=n)
def _add_output(self, name): """Adds the specified output variable and its associated surrogate Slot.""" sur_name = __surrogate_prefix__ + name self.add_trait(sur_name, Slot(ISurrogate, allow_none=True)) self.on_trait_change(self._surrogate_updated, sur_name) if self.default_surrogate is not None: surrogate = deepcopy(self.default_surrogate) self._default_surrogate_copies[sur_name] = surrogate self._add_var_for_surrogate(surrogate, name) else: self.add_trait(name, _clone_trait(self.model.trait(name))) self._training_data[name] = []
class Verifier(Component): """ Verifies evaluated cases. """ cases = Slot(ICaseIterator, iotype='in') def execute(self): """ Verify evaluated cases. """ for case in self.cases: i = int(case.label) # Correlation key. self._logger.critical('verifying case %d', i) assert case.msg is None assert case['driven.rosen_suzuki'] == rosen_suzuki( case['driven.x']) assert case['driven.sum_y'] == sum(case['driven.y']) assert case['driven.extra'] == 2.5
class AutoAssemb(Assembly): d2 = Slot(Dummy) def configure(self): self.add('d1', Dummy()) self.add('d2', Dummy()) self.add('d3', Dummy()) self.driver.workflow.add(['d1', 'd2', 'd3']) self.connect('d1.output', 'd2.input') self.connect('d2.output', 'd3.input') self.create_passthrough('d1.input') self.create_passthrough('d3.output')
class Generator(Component): """ Generates cases to be evaluated. """ cases = Slot(ICaseIterator, iotype='out') def execute(self): """ Generate some cases to be evaluated. """ cases = [] for i in range(10): inputs = [('driven.x', numpy_random.normal(size=4)), ('driven.y', numpy_random.normal(size=10)), ('driven.raise_error', False), ('driven.stop_exec', False)] outputs = ['driven.rosen_suzuki', 'driven.sum_y'] cases.append(Case(inputs, outputs, label=str(i))) self.cases = ListCaseIterator(cases)
def setUp(self): self.top = top = set_as_top(Assembly()) driver = top.add('driver', SimpleCaseIterDriver()) top.add('comp1', ExecComp(exprs=['z=x+y'])) top.add('comp2', ExecComp(exprs=['z=x+1'])) top.connect('comp1.z', 'comp2.x') top.comp1.add('a_string', Str("Hello',;','", iotype='out')) top.comp1.add('a_array', Array(array([1.0, 3.0, 5.5]), iotype='out')) top.comp1.add('x_array', Array(array([1.0, 1.0, 1.0]), iotype='in')) top.comp1.add('vt', Slot(DumbVT, iotype='out')) top.comp1.vt = DumbVT() driver.workflow.add(['comp1', 'comp2']) # now create some Cases outputs = ['comp1.z', 'comp2.z', 'comp1.a_string', 'comp1.a_array[2]'] cases = [] for i in range(10): inputs = [('comp1.x', i+0.1), ('comp1.y', i*2 + .1), ('comp1.x_array[1]', 99.88)] cases.append(Case(inputs=inputs, outputs=outputs, label='case%s'%i)) driver.iterator = ListCaseIterator(cases) self.filename = "openmdao_test_csv_case_iterator.csv"
class MetaModel(Component): # pylint: disable-msg=E1101 model = Slot(IComponent, allow_none=True, desc='Slot for the Component or Assembly being ' 'encapsulated.') includes = ListStr(iotype='in', desc='A list of names of variables to be included ' 'in the public interface.') excludes = ListStr(iotype='in', desc='A list of names of variables to be excluded ' 'from the public interface.') warm_start_data = Slot(ICaseIterator, iotype="in", desc="CaseIterator containing cases to use as " "initial training data. When this is set, all " "previous training data is cleared and replaced " "with data from this CaseIterator") surrogate = Dict( key_trait=Str, value_trait=Slot(ISurrogate), allow_none=True, desc='Dictionary that provides a mapping between variables and ' 'surrogate models for each output. The "default" ' 'key must be given. It is the default surrogate model for all ' 'outputs. Any specific surrogate models can be ' 'specifed by a key with the desired variable name.') surrogate_args = Dict( key_trait=Str, allow_none=True, desc='Dictionary that provides mapping between variables and ' 'arguments that should be passed to the surrogate model. Keys should ' 'match those in the surrogate dictionary. Values can be a list of ordered ' 'arguments, a dictionary of named arguments, or a two-tuple of a list and a dictionary.' ) report_errors = Bool( True, iotype="in", desc= "If True, metamodel will report errors reported from the component. " "If False, metamodel will swallow the errors but log that they happened and exclude the case" "from the training set") recorder = Slot(ICaseRecorder, desc='Records training cases') # when fired, the next execution will train the metamodel train_next = Event() #when fired, the next execution will reset all training data reset_training_data = Event() def __init__(self, *args, **kwargs): super(MetaModel, self).__init__(*args, **kwargs) self._current_model_traitnames = set() self._surrogate_info = {} self._surrogate_input_names = [] self._training_input_history = [] self._const_inputs = { } # dict of constant training inputs indices and their values self._train = False self._new_train_data = False self._failed_training_msgs = [] # the following line will work for classes that inherit from MetaModel # as long as they declare their traits in the class body and not in # the __init__ function. If they need to create traits dynamically # during initialization they'll have to provide the value of # _mm_class_traitnames self._mm_class_traitnames = set(self.traits(iotype=not_none).keys()) def _train_next_fired(self): self._train = True self._new_train_data = True def _reset_training_data_fired(self): self._training_input_history = [] self._const_inputs = {} self._failed_training_msgs = [] # remove output history from surrogate_info for name, tup in self._surrogate_info.items(): surrogate, output_history = tup self._surrogate_info[name] = (surrogate, []) def _warm_start_data_changed(self, oldval, newval): self.reset_training_data = True #build list of inputs for case in newval: if self.recorder: self.recorder.record(case) inputs = [] for inp_name in self._surrogate_input_names: var_name = '.'.join([self.name, inp_name]) inp_val = case[var_name] if inp_val is not None: inputs.append(inp_val) else: self.raise_exception( 'The variable "%s" was not ' 'found as an input in one of the cases provided ' 'for warm_start_data.' % var_name, ValueError) #print "inputs", inputs self._training_input_history.append(inputs) for output_name in self.list_outputs_from_model(): #grab value from case data var_name = '.'.join([self.name, output_name]) try: val = case.get_output(var_name) except KeyError: self.raise_exception( 'The output "%s" was not found ' 'in one of the cases provided for ' 'warm_start_data' % var_name, ValueError) else: # save to training output history self._surrogate_info[output_name][1].append(val) self._new_train_data = True def execute(self): """If the training flag is set, train the metamodel. Otherwise, predict outputs. """ if self._train: if self.model is None: self.raise_exception("MetaModel object must have a model!", RuntimeError) try: inputs = self.update_model_inputs() #print '%s training with inputs: %s' % (self.get_pathname(), inputs) self.model.run(force=True) except Exception as err: if self.report_errors: raise err else: self._failed_training_msgs.append(str(err)) else: #if no exceptions are generated, save the data self._training_input_history.append(inputs) self.update_outputs_from_model() case_outputs = [] for name, tup in self._surrogate_info.items(): surrogate, output_history = tup case_outputs.append(('.'.join([self.name, name]), output_history[-1])) # save the case, making sure to add out name to the local input name since # this Case is scoped to our parent Assembly case_inputs = [ ('.'.join([self.name, name]), val) for name, val in zip(self._surrogate_input_names, inputs) ] if self.recorder: self.recorder.record( Case(inputs=case_inputs, outputs=case_outputs)) self._train = False else: #print '%s predicting' % self.get_pathname() if self._new_train_data: if len(self._training_input_history) < 2: self.raise_exception( "ERROR: need at least 2 training points!", RuntimeError) # figure out if we have any constant training inputs tcases = self._training_input_history in_hist = tcases[0][:] # start off assuming every input is constant idxlist = range(len(in_hist)) self._const_inputs = dict(zip(idxlist, in_hist)) for i in idxlist: val = in_hist[i] for case in range(1, len(tcases)): if val != tcases[case][i]: del self._const_inputs[i] break if len(self._const_inputs) == len(in_hist): self.raise_exception( "ERROR: all training inputs are constant.") elif len(self._const_inputs) > 0: # some inputs are constant, so we have to remove them from the training set training_input_history = [] for inputs in self._training_input_history: training_input_history.append([ val for i, val in enumerate(inputs) if i not in self._const_inputs ]) else: training_input_history = self._training_input_history for name, tup in self._surrogate_info.items(): surrogate, output_history = tup surrogate.train(training_input_history, output_history) self._new_train_data = False inputs = [] for i, name in enumerate(self._surrogate_input_names): val = getattr(self, name) cval = self._const_inputs.get(i, _missing) if cval is _missing: inputs.append(val) elif val != cval: self.raise_exception( "ERROR: training input '%s' was a constant value of (%s) but the value has changed to (%s)." % (name, cval, val), ValueError) for name, tup in self._surrogate_info.items(): surrogate = tup[0] # copy output to boudary setattr(self, name, surrogate.predict(inputs))
class FUSEDWindIO(Component): """ Master IO class for reading and writing JSON formatted files. For writing an input file the class relies on the modified VariableTree class FusedVariableTree that has the method print_vars() which returns its parameters in a nicely printed nested dictionary containing only keys and variables. To read an input file each section in the file has to have a key named "type" which is the name of the VarTree to be instantiated and set with the entries in the input section. """ master_input_file = Str('fusedwind_master.json',desc='') master_output_file = Str('fusedwind_master_out.json',desc='') vtrees_in = Slot(iotype='out') vtrees_out = Slot(iotype='in') def load_master(self): f = open(self.master_input_file,'r') loaded = json.load(f) self.add('vtrees_in',Component()) for name, comp in loaded.iteritems(): decoded = self.decode_json(comp) print 'loading', name vt = self.load_vartree(decoded) self.vtrees_in.add(name, vt) def write_master(self): """serialize and write object hierarchy to a JSON file""" f = open(self.master_output_file,'wb') master = {} master[self.vtrees_out.name] = self.print_vars(self.vtrees_out) # for comp in self.vtrees_out.list_vars(): # obj = getattr(self.vtrees_out, comp) # # obj = copy.deepcopy(obj) # if isinstance(obj, VariableTree): # print 'dumping', comp # master[comp] = self.print_vars(obj) self._master = master # ipdb.set_trace() json.dump(master, f, indent = 4) def print_vars(self, obj, parent=None): attrs = {} attrs['type'] = type(obj).__name__ parameters = {} for name in obj.list_vars(): trait = obj.get_trait(name) meta = obj.get_metadata(name) value = getattr(obj, name) ttype = trait.trait_type # Each variable type provides its own basic attributes attr, slot_attr = ttype.get_attribute(name, value, trait, meta) # Let the GUI know that this var is the top element of a # variable tree if attr.get('ttype') in ['vartree', 'slot']: vartable = obj.get(name) # if hasattr(vartable, 'print_vars'): attr['vt'] = 'vt' # For variables trees only: recursively add the inputs and outputs # into this variable list if 'vt' in attr: vt_attrs = self.print_vars(vartable) parameters[name] = vt_attrs else: if meta['vartypename'] == 'Array': parameters[name] = value.tolist() elif meta['vartypename'] == 'List': newvalues = [] for i, item in enumerate(value): if isinstance(item, VariableTree): newvalues.append(self.print_vars(item)) elif isinstance(item, np.ndarray): newvalues.append(item.tolist()) else: newvalues.append(item) parameters[name] = newvalues else: parameters[name] = value # ipdb.set_trace() attrs['parameters'] = parameters class_dict = {} if obj.name is '': name = type(obj).__name__ else: name = obj.name class_dict[name] = attrs return attrs def decode_json(self, vartree): """decode unicode keys to ascii strings""" outtree = {} for key, val in vartree.iteritems(): key = key.encode('ascii') if isinstance(val, dict): outtree[key] = self.decode_json(val) else: if type(val) == unicode: outtree[key] = val.encode('ascii') else: outtree[key] = val return outtree def load_vartree(self, vartree): """convert an input dictionary to a FusedIOVariableTree""" obj = None if 'type' in vartree: name = vartree['type'] try: klass = getattr(api, name) obj = klass() except: raise RuntimeError('unknown variable tree type %s'%name) for key, val in vartree['parameters'].iteritems(): if key == 'type': pass if isinstance(val, dict): print 'loading', key child = self.load_vartree(val) setattr(obj, key, child) elif isinstance(val, list): for i, item in enumerate(val): if isinstance(item, dict): child = self.load_vartree(item) val[i] = child setattr(obj, key, val) else: if hasattr(obj,key): meta = obj.get_metadata(key) if meta['vartypename'] == 'Array': val = np.array(val) setattr(obj,key,val) return obj
class MultiObjExpectedImprovement(Component): best_cases = Slot( CaseSet, iotype="in", desc="CaseIterator which contains only Pareto optimal cases \ according to criteria.") criteria = Array( iotype="in", desc="Names of responses to maximize expected improvement around. \ Must be NormalDistribution type.") predicted_values = Array( [0, 0], iotype="in", dtype=NormalDistribution, desc="CaseIterator which contains NormalDistributions for each \ response at a location where you wish to calculate EI." ) n = Int(1000, iotype="in", desc="Number of Monte Carlo Samples with \ which to calculate probability of improvement.") calc_switch = Enum("PI", ["PI", "EI"], iotype="in", desc="Switch to use either \ probability (PI) or expected (EI) improvement.") PI = Float(0.0, iotype="out", desc="The probability of improvement of the next_case.") EI = Float(0.0, iotype="out", desc="The expected improvement of the next_case.") reset_y_star = Event(desc='Reset Y* on next execution') def __init__(self): super(MultiObjExpectedImprovement, self).__init__() self.y_star = None def _reset_y_star_fired(self): self.y_star = None def get_y_star(self): criteria_count = len(self.criteria) flat_crit = self.criteria.ravel() try: y_star = zip(*[self.best_cases[crit] for crit in self.criteria]) except KeyError: self.raise_exception( 'no cases in the provided case_set had output ' 'matching the provided criteria, %s' % self.criteria, ValueError) #sort list on first objective y_star = array(y_star)[array([i[0] for i in y_star]).argsort()] return y_star def _2obj_PI(self, mu, sigma): """Calculates the multi-objective probability of improvement for a new point with two responses. Takes as input a pareto frontier, mean and sigma of new point.""" y_star = self.y_star PI1 = (0.5 + 0.5 * erf( (1 / (2**0.5)) * ((y_star[0][0] - mu[0]) / sigma[0]))) PI3 = (1-(0.5+0.5*erf((1/(2**0.5))*((y_star[-1][0]-mu[0])/sigma[0]))))\ *(0.5+0.5*erf((1/(2**0.5))*((y_star[-1][1]-mu[1])/sigma[1]))) PI2 = 0 if len(y_star) > 1: for i in range(len(y_star) - 1): PI2=PI2+((0.5+0.5*erf((1/(2**0.5))*((y_star[i+1][0]-mu[0])/sigma[0])))\ -(0.5+0.5*erf((1/(2**0.5))*((y_star[i][0]-mu[0])/sigma[0]))))\ *(0.5+0.5*erf((1/(2**0.5))*((y_star[i+1][1]-mu[1])/sigma[1]))) mcpi = PI1 + PI2 + PI3 return mcpi def _2obj_EI(self, mu, sigma): """Calculates the multi-criteria expected improvement for a new point with two responses. Takes as input a pareto frontier, mean and sigma of new point.""" y_star = self.y_star ybar11 = mu[0]*(0.5+0.5*erf((1/(2**0.5))*((y_star[0][0]-mu[0])/sigma[0])))\ -sigma[0]*(1/((2*pi)**0.5))*exp(-0.5*((y_star[0][0]-mu[0])**2/sigma[0]**2)) ybar13 = (mu[0]*(0.5+0.5*erf((1/(2**0.5))*((y_star[-1][0]-mu[0])/sigma[0])))\ -sigma[0]*(1/((2*pi)**0.5))*exp(-0.5*((y_star[-1][0]-mu[0])**2/sigma[0]**2)))\ *(0.5+0.5*erf((1/(2**0.5))*((y_star[-1][1]-mu[1])/sigma[1]))) ybar12 = 0 if len(y_star) > 1: for i in range(len(y_star) - 1): ybar12 = ybar12+((mu[0]*(0.5+0.5*erf((1/(2**0.5))*((y_star[i+1][0]-mu[0])/sigma[0])))\ -sigma[0]*(1/((2*pi)**0.5))*exp(-0.5*((y_star[i+1][0]-mu[0])**2/sigma[0]**2)))\ -(mu[0]*(0.5+0.5*erf((1/(2**0.5))*((y_star[i][0]-mu[0])/sigma[0])))\ -sigma[0]*(1/((2*pi)**0.5))*exp(-0.5*((y_star[i][0]-mu[0])**2/sigma[0]**2))))\ *(0.5+0.5*erf((1/(2**0.5))*((y_star[i+1][1]-mu[1])/sigma[1]))) ybar1 = (ybar11 + ybar12 + ybar13) / self.PI ybar21 = mu[1]*(0.5+0.5*erf((1/(2**0.5))*((y_star[0][1]-mu[1])/sigma[1])))\ -sigma[1]*(1/((2*pi)**0.5))*exp(-0.5*((y_star[0][1]-mu[1])**2/sigma[1]**2)) ybar23 = (mu[1]*(0.5+0.5*erf((1/(2**0.5))*((y_star[-1][1]-mu[1])/sigma[1])))\ -sigma[1]*(1/((2*pi)**0.5))*exp(-0.5*((y_star[-1][1]-mu[1])**2/sigma[1]**2)))\ *(0.5+0.5*erf((1/(2**0.5))*((y_star[-1][0]-mu[0])/sigma[0]))) ybar22 = 0 if len(y_star) > 1: for i in range(len(y_star) - 1): ybar22 = ybar22+((mu[1]*(0.5+0.5*erf((1/(2**0.5))*((y_star[i+1][1]-mu[1])/sigma[1])))\ -sigma[1]*(1/((2*pi)**0.5))*exp(-0.5*((y_star[i+1][1]-mu[1])**2/sigma[1]**2)))\ -(mu[1]*(0.5+0.5*erf((1/(2**0.5))*((y_star[i][1]-mu[1])/sigma[1])))\ -sigma[1]*(1/((2*pi)**0.5))*exp(-0.5*((y_star[i][1]-mu[1])**2/sigma[1]**2))))\ *(0.5+0.5*erf((1/(2**0.5))*((y_star[i+1][0]-mu[0])/sigma[0]))) ybar2 = (ybar21 + ybar22 + ybar23) / self.PI dists = [((ybar1 - point[0])**2 + (ybar2 - point[1])**2)**0.5 for point in y_star] mcei = self.PI * min(dists) if isnan(mcei): mcei = 0 return mcei def _dom(self, a, b): """determines if a completely dominates b returns True is if does """ comp = [c1 < c2 for c1, c2 in zip(a, b)] if sum(comp) == len(self.criteria): return True return False def _nobj_PI(self, mu, sigma): cov = diag(array(sigma)**2) rands = random.multivariate_normal(mu, cov, self.n) num = 0 # number of cases that dominate the current Pareto set for random_sample in rands: for par_point in self.y_star: #par_point = [p[2] for p in par_point.outputs] if self._dom(par_point, random_sample): num = num + 1 break pi = (self.n - num) / float(self.n) return pi def execute(self): """ Calculates the expected improvement or probability of improvement of a candidate point given by a normal distribution. """ mu = [objective.mu for objective in self.predicted_values] sig = [objective.sigma for objective in self.predicted_values] if self.y_star == None: self.y_star = self.get_y_star() n_objs = len(self.criteria) if n_objs == 2: """biobjective optimization""" self.PI = self._2obj_PI(mu, sig) if self.calc_switch == 'EI': """execute EI calculations""" self.EI = self._2obj_EI(mu, sig) if n_objs > 2: """n objective optimization""" self.PI = self._nobj_PI(mu, sig) if self.calc_switch == 'EI': """execute EI calculations""" self.raise_exception( "EI calculations not supported" " for more than 2 objectives", ValueError)
class MetaModel(Component): # pylint: disable-msg=E1101 model = Slot(IComponent, allow_none=True, desc='Slot for the Component or Assembly being ' 'encapsulated.') includes = List(Str, iotype='in', desc='A list of names of variables to be included ' 'in the public interface.') excludes = List(Str, iotype='in', desc='A list of names of variables to be excluded ' 'from the public interface.') warm_start_data = Slot(ICaseIterator, iotype="in", desc="CaseIterator containing cases to use as " "initial training data. When this is set, all " "previous training data is cleared and replaced " "with data from this CaseIterator.") default_surrogate = Slot( ISurrogate, allow_none=True, desc="This surrogate will be used for all " "outputs that don't have a specific surrogate assigned " "to them in their sur_<name> slot.") report_errors = Bool( True, iotype="in", desc= "If True, metamodel will report errors reported from the component. " "If False, metamodel will swallow the errors but log that they happened and " "exclude the case from the training set.") recorder = Slot(ICaseRecorder, desc='Records training cases') # when fired, the next execution will train the metamodel train_next = Event() #when fired, the next execution will reset all training data reset_training_data = Event() def __init__(self, *args, **kwargs): super(MetaModel, self).__init__(*args, **kwargs) self._surrogate_input_names = None self._surrogate_output_names = None self._surrogate_overrides = set( ) # keeps track of which sur_<name> slots are full self._training_data = {} self._training_input_history = [] self._const_inputs = { } # dict of constant training inputs indices and their values self._train = False self._new_train_data = False self._failed_training_msgs = [] self._default_surrogate_copies = { } # need to maintain separate copy of default surrogate for each sur_* that doesn't # have a surrogate defined # the following line will work for classes that inherit from MetaModel # as long as they declare their traits in the class body and not in # the __init__ function. If they need to create traits dynamically # during initialization they'll have to provide the value of # _mm_class_traitnames self._mm_class_traitnames = set(self.traits(iotype=not_none).keys()) def _train_next_fired(self): self._train = True self._new_train_data = True def _reset_training_data_fired(self): self._training_input_history = [] self._const_inputs = {} self._failed_training_msgs = [] # remove output history from training_data for name in self._training_data: self._training_data[name] = [] def _warm_start_data_changed(self, oldval, newval): self.reset_training_data = True # build list of inputs for case in newval: if self.recorder: self.recorder.record(case) inputs = [] for inp_name in self.surrogate_input_names(): var_name = '.'.join([self.name, inp_name]) try: inp_val = case[var_name] except KeyError: pass #self.raise_exception('The variable "%s" was not ' #'found as an input in one of the cases provided ' #'for warm_start_data.' % var_name, ValueError) else: if inp_val is not None: inputs.append(inp_val) #print "inputs", inputs self._training_input_history.append(inputs) for output_name in self.surrogate_output_names(): #grab value from case data var_name = '.'.join([self.name, output_name]) try: val = case.get_output(var_name) except KeyError: self.raise_exception( 'The output "%s" was not found ' 'in one of the cases provided for ' 'warm_start_data' % var_name, ValueError) else: # save to training output history self._training_data[output_name].append(val) self._new_train_data = True def execute(self): """If the training flag is set, train the metamodel. Otherwise, predict outputs. """ if self._train: if self.model is None: self.raise_exception("MetaModel object must have a model!", RuntimeError) try: inputs = self.update_model_inputs() #print '%s training with inputs: %s' % (self.get_pathname(), inputs) self.model.run(force=True) except Exception as err: if self.report_errors: raise err else: self._failed_training_msgs.append(str(err)) else: # if no exceptions are generated, save the data self._training_input_history.append(inputs) self.update_outputs_from_model() case_outputs = [] for name, output_history in self._training_data.items(): case_outputs.append(('.'.join([self.name, name]), output_history[-1])) # save the case, making sure to add out name to the local input name since # this Case is scoped to our parent Assembly case_inputs = [ ('.'.join([self.name, name]), val) for name, val in zip(self.surrogate_input_names(), inputs) ] if self.recorder: self.recorder.record( Case(inputs=case_inputs, outputs=case_outputs)) self._train = False else: if self.default_surrogate is None and not self._surrogate_overrides: # NO surrogates defined. just run model and get outputs inputs = self.update_model_inputs() self.model.run() self.update_outputs_from_model() return #print '%s predicting' % self.get_pathname() if self._new_train_data: if len(self._training_input_history) < 2: self.raise_exception( "ERROR: need at least 2 training points!", RuntimeError) # figure out if we have any constant training inputs tcases = self._training_input_history in_hist = tcases[0][:] # start off assuming every input is constant idxlist = range(len(in_hist)) self._const_inputs = dict(zip(idxlist, in_hist)) for i in idxlist: val = in_hist[i] for case in range(1, len(tcases)): if val != tcases[case][i]: del self._const_inputs[i] break if len(self._const_inputs) == len(in_hist): self.raise_exception( "ERROR: all training inputs are constant.") elif len(self._const_inputs) > 0: # some inputs are constant, so we have to remove them from the training set training_input_history = [] for inputs in self._training_input_history: training_input_history.append([ val for i, val in enumerate(inputs) if i not in self._const_inputs ]) else: training_input_history = self._training_input_history for name, output_history in self._training_data.items(): surrogate = self._get_surrogate(name) if surrogate is not None: surrogate.train(training_input_history, output_history) self._new_train_data = False inputs = [] for i, name in enumerate(self.surrogate_input_names()): val = getattr(self, name) cval = self._const_inputs.get(i, _missing) if cval is _missing: inputs.append(val) elif val != cval: self.raise_exception( "ERROR: training input '%s' was a constant value of (%s) but the value has changed to (%s)." % (name, cval, val), ValueError) for name in self._training_data: surrogate = self._get_surrogate(name) # copy output to boundary if surrogate is None: setattr(self, name, getattr( self.model, name)) # no surrogate. use outputs from model else: setattr(self, name, surrogate.predict(inputs))
def update_model(self, oldmodel, newmodel): """called whenever the model variable is set or when includes/excludes change.""" # TODO: check for pre-connected traits on the new model # TODO: disconnect traits corresponding to old model (or leave them if the new model has the same ones?) # TODO: check for nested MMs? Is this a problem? # TODO: check for name collisions between MetaModel class traits and traits from model if newmodel is not None and not has_interface(newmodel, IComponent): self.raise_exception( 'model of type %s does not implement the IComponent interface' % type(newmodel).__name__, TypeError) if not self.surrogate: self.raise_exception( "surrogate must be set before the model or any includes/excludes of variables", RuntimeError) new_model_traitnames = set() self._surrogate_input_names = [] self._training_input_history = [] self._surrogate_info = {} self._failed_training_msgs = [] # remove traits promoted from the old model for name in self._current_model_traitnames: if self.parent: self.parent.disconnect('.'.join([self.name, name])) self.remove_trait(name) if newmodel: # query for inputs traitdict = newmodel._alltraits(iotype='in') for name, trait in traitdict.items(): if self._eligible(name): self._surrogate_input_names.append(name) if name not in self._mm_class_traitnames: self.add_trait(name, _clone_trait(trait)) new_model_traitnames.add(name) setattr(self, name, getattr(newmodel, name)) # now outputs traitdict = newmodel._alltraits(iotype='out') for name, trait in traitdict.items(): if self._eligible(name): try: surrogate = self.surrogate[name] args = self.surrogate_args.get(name, []) except KeyError: try: surrogate = self.surrogate['default'] args = self.surrogate_args.get('default', []) except KeyError: self.raise_exception( "No default surrogate model was" " specified. Either specify a default, or specify a " "surrogate model for all outputs", ValueError) if isinstance(args, dict): kwargs = args args = [] elif isinstance(args, tuple): args = args[0] kwargs = args[1] else: kwargs = {} trait_type = surrogate.get_uncertain_value(1.0).__class__ self.add(name, Slot(trait_type, iotype='out', desc=trait.desc)) self._surrogate_info[name] = (surrogate.__class__( *args, **kwargs), []) # (surrogate,output_history) new_model_traitnames.add(name) setattr( self, name, surrogate.get_uncertain_value(getattr(newmodel, name))) newmodel.parent = self newmodel.name = 'model' self._current_model_traitnames = new_model_traitnames
class Brent(Driver): """Root finding using Brent's method.""" implements(IHasParameters, IHasEqConstraints, ISolver) lower_bound = Float(0., iotype="in", desc="lower bound for the root search") upper_bound = Float(100., iotype="in", desc="upper bound for the root search") xtol = Float(0.0, iotype="in", desc='The routine converges when a root is known to lie within xtol of the value return. Should be >= 0. ' 'The routine modifies this to take into account the relative precision of doubles.') rtol = Float(0.0, iotype="in", desc='The routine converges when a root is known to lie within rtol times the value returned of ' 'the value returned. Should be >= 0. Defaults to np.finfo(float).eps * 2.') maxiter = Int(100, iotype="in", desc='if convergence is not achieved in maxiter iterations, and error is raised. Must be >= 0.') iprint = Int(0, iotype='in', desc='Set to 1 to print out itercount and residual.') f_resize_bracket = Slot(object, desc='user supplied function to handle resizing bracket. Form of function is: \ lower_new, upper_new, continue = f_resize_bracket(lower, upper, iteration) \ inputs include the current lower and upper bracket and the current iteration \ count starting from 1. Outputs include a new lower and upper bracket, and a \ boolean flag on whether or not to terminate calling resize bracket') invalid_bracket_return = Float(-1, iotype='in', desc='user supplied value to handle what value should be returned \ when a suitable bracket cannot be found. sets the "zero" as a \ linear combination of the lower and upper brackets. \ Must be between 0 and 1 or an error will be thrown. \ root = lower + invalid_bracket_return*(upper-lower)') def __init__(self): super(Brent, self).__init__() self.workflow = CyclicWorkflow() self.xstar = self._param = None def _eval(self, x): """Callback function for evaluating f(x)""" self._param.set(x) self.run_iteration() return self.eval_eq_constraints(self.parent)[0] def execute(self): bracket_invalid = self._eval(self.lower_bound)*self._eval(self.upper_bound) > 0 # check if user supplied function to handle resizing bracket if bracket_invalid and self.f_resize_bracket: # try to resize bracket to find a valid bracket. iteration = 1 should_continue = True bracket_invalid = True while bracket_invalid and should_continue: self.lower_bound, self.upper_bound, should_continue = \ self.f_resize_bracket(self.lower_bound, self.upper_bound, iteration) bracket_invalid = self._eval(self.lower_bound)*self._eval(self.upper_bound) > 0 iteration += 1 if bracket_invalid: # if bracket is still invalid, see if user has specified what to return if self.invalid_bracket_return >= 0.0 and self.invalid_bracket_return <= 1.0: xstar = self.lower_bound + self.invalid_bracket_return*(self.upper_bound-self.lower_bound) brent_iterations = 'valid bracket not found. returning user specified value' else: self.raise_exception('bounds (low=%s, high=%s) do not bracket a root' % (self.lower_bound, self.upper_bound)) else: kwargs = {'maxiter': self.maxiter, 'a': self.lower_bound, 'b': self.upper_bound, 'full_output': True} if self.xtol > 0: kwargs['xtol'] = self.xtol if self.rtol > 0: kwargs['rtol'] = self.rtol # Brent's method xstar, r = brentq(self._eval, **kwargs) brent_iterations = r.iterations # Propagate solution back into the model self._param.set(xstar) self.run_iteration() if self.iprint == 1: print 'iterations:', brent_iterations print 'residual:', self.eval_eq_constraints() def check_config(self, strict=False): '''Make sure we have 1 parameter and 1 constraint''' super(Brent, self).check_config(strict=strict) params = self.get_parameters().values() if len(params) != 1: self.raise_exception("Brent driver must have 1 parameter, " "but instead it has %d" % len(params)) constraints = self.get_eq_constraints() if len(constraints) != 1: self.raise_exception("Brent driver must have 1 equality constraint, " "but instead it has %d" % len(constraints)) self._param = params[0]
class MidFidelity(Assembly): """ Wrapper for M4 variable fidelity capability. """ # Slots. lofi_model = Slot(Component, desc='Low fidelity model', required=True) hifi_model = Slot(Component, desc='High fidelity model', required=True) # Inputs. # No 'Option' variables yet. doe_type = Str('lhs', iotype='in', desc='Type of DOE used to generate response surface.') rs_type = Str('quadratic', iotype='in', desc='Type of response surface.') n_samples = Int(value=1, low=1, iotype='in', desc='Number of samples.') tolerance = Float(1.0e10, iotype='in', desc='?') correction_function = Int(1, iotype='in', desc='Type of correction function.') w_h = Float(0.5, iotype='in', desc='?') accuracy_test_type = Int(2, iotype='in', desc='Method for testing accuracy of response.') n_samples_test = Int( value=10, low=1, iotype='in', desc='Number of additional samples for additional-points test.') ntheta = Int(3, iotype='in', desc='For Kriging method, ntheta=1(SA),2(Cobyla),3(BFGS)') # TODO: change these to delegates or passthroughs sample_points = Array(iotype='out', desc='Points used to make response', ref_name='sample_points', ref_parent='midfi_model') lofi_results = Array(iotype='out', desc='Points used to make response', ref_name='lofi_results', ref_parent='midfi_model') hifi_results = Array(iotype='out', desc='Points used to make response', ref_name='hifi_results', ref_parent='midfi_model') #name='M4_MidFi', def configure(self): self.need_updated_corrections = True self.input_mappings = [] self.output_mappings = [] self._lofi_m4model = None self._hifi_m4model = None self._midfi_model = mool.Optimization.MidFiModel.Mid_Fi_Model() # pylint: disable-msg=E1101 # "Instance of <class> has no <attr> member" def set_hifi_model(self, hifi): """ Set high fidelity model. """ self.hifi_model = hifi self._hifi_m4model = None self.need_updated_corrections = True def set_lofi_model(self, lofi): """ Set low fidelity model. """ self.lofi_model = lofi self._lofi_m4model = None self.need_updated_corrections = True def add_input_mapping(self, mid, low, high): """ Add mapping for input variable. """ self.input_mappings.append((mid, low, high)) self.need_updated_corrections = True def add_output_mapping(self, mid, low, high): """ Add mapping for output variable. """ self.output_mappings.append((mid, low, high)) self.need_updated_corrections = True def execute(self): """ Compute results based on mid-fidelity approximation. """ if self.lofi_model is None: self.raise_exception('No lofi model plugin', ValueError) if self.hifi_model is None: self.raise_exception('No hifi model plugin', ValueError) # Wrap low fidelity model. if self._lofi_m4model is None: inputs = [] for mid, low, high in self.input_mappings: inputs.append(low) outputs = [] for mid, low, high in self.output_mappings: outputs.append(low) self._lofi_m4model = \ wrapper.M4Model(self.lofi_model, inputs, outputs) # Wrap high fidelity model. if self._hifi_m4model is None: inputs = [] for mid, low, high in self.input_mappings: inputs.append(high) outputs = [] for mid, low, high in self.output_mappings: outputs.append(high) self._hifi_m4model = \ wrapper.M4Model(self.hifi_model, inputs, outputs) # If needed, update correction functions. if self.need_updated_corrections: nvars = len(self.input_mappings) # Check for sufficient DOE samples. if self.doe_type == 'ccd': # CCD requires an exact value if nvars == 1: min_samples = 3 else: min_samples = 2**nvars + 2 * nvars + 1 if self.n_samples != min_samples: self._logger.warning( 'Setting n_samples to CCD required value: %d', min_samples) self.n_samples = min_samples elif self.doe_type == 'lhs': min_samples = nvars elif self.doe_type == 'rand_lhs': min_samples = nvars elif self.doe_type == 'oa2': min_samples = (nvars - 1)**2 elif self.doe_type == 'oa3': min_samples = (nvars - 1)**3 else: msg = "Unknown DOE type '%s'" % self.doe_type self.raise_exception(msg, ValueError) if self.n_samples < min_samples: self._logger.warning( 'Updating n_samples to minimum for DOE: %d', min_samples) self.n_samples = min_samples # Check for sufficient response surface samples. if self.rs_type == 'linear': min_samples = nvars + 1 elif self.rs_type == 'quadratic': min_samples = (nvars**2 + 3 * nvars + 2) / 2 elif self.rs_type == 'cubic': min_samples = (nvars**3 + 6 * nvars**2 + 11 * nvars + 6) / 6 elif self.rs_type == 'rbf': min_samples = self.n_samples elif self.rs_type == 'kriging': min_samples = nvars else: msg = "Unknown RS type '%s'" % self.rs_type self.raise_exception(msg, ValueError) if self.n_samples < min_samples: self._logger.warning( 'Updating n_samples to minimum for RS: %d', min_samples) self.n_samples = min_samples # Collect upper and lower bounds. xlb = [] xub = [] for i, mapping in enumerate(self.input_mappings): trait = self.get_trait(mapping[0]).trait_type if isinstance(trait, (Int, Float)): low = trait.low high = trait.high else: msg = 'Unexpected input %d trait type %r' % (i, trait) self.raise_exception(msg, ValueError) xlb.append(low) xub.append(high) self._midfi_model.Set(hifi=self._hifi_m4model, lofi=self._lofi_m4model, xlb=xlb, xub=xub, nd=nvars, nsamples=self.n_samples, nsamples_test=self.n_samples_test, nf=len(self.output_mappings), n_th_func=None, accu_test_type=self.accuracy_test_type, rs_type=self.rs_type, correct_func_type=self.correction_function, w_h=self.w_h, doe_type=self.doe_type, tolerance=self.tolerance, ntheta=self.ntheta) self._midfi_model.Construct_set_run_type(run_type=('doe', 'rs_fast'), iflag_doe=3, iflag_accu=1) self.need_updated_corrections = False vec = [] for mapping in self.input_mappings: vec.append(self.get(mapping[0])) for i, mapping in enumerate(self.output_mappings): self._logger.debug('Running mid-fidelity model %s %d %s', str(vec), i, str(mapping[0])) result = self._midfi_model.RunModel(vec, i) self._logger.debug(' result %s', str(result)) setattr(self, mapping[0], result)