Ejemplo n.º 1
0
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
Ejemplo n.º 6
0
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.
Ejemplo n.º 7
0
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
Ejemplo n.º 8
0
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            
Ejemplo n.º 9
0
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)
Ejemplo n.º 10
0
    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
Ejemplo n.º 12
0
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)
Ejemplo n.º 14
0
 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"
Ejemplo n.º 15
0
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))
Ejemplo n.º 16
0
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)
Ejemplo n.º 18
0
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))
Ejemplo n.º 19
0
    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
Ejemplo n.º 20
0
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]
Ejemplo n.º 21
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)