def localInputAndChecks(self, xmlNode): """ Local method for additional reading. @ In, xmlNode, xml.etree.ElementTree.Element, Xml element node @ Out, None """ GradientBasedOptimizer.localInputAndChecks(self, xmlNode) self.paramDict['alpha'] = float(self.paramDict.get('alpha', 0.602)) self.paramDict['gamma'] = float(self.paramDict.get('gamma', 0.101)) self.paramDict['A'] = float( self.paramDict.get('A', self.limit['mdlEval'] / 10.)) self.paramDict['a'] = self.paramDict.get('a', None) self.paramDict['c'] = float(self.paramDict.get('c', 0.005)) #FIXME the optimization parameters should probably all operate ONLY on normalized data! # -> perhaps the whole optimizer should only work on optimized data. #FIXME normalizing doesn't seem to have the desired effect, currently; it makes the step size very small (for large scales) #if "a" was defaulted, use the average scale of the input space. #This is the suggested value from the paper, missing a 1/gradient term since we don't know it yet. if self.paramDict['a'] is None: self.paramDict['a'] = mathUtils.hyperdiagonal( np.ones(len( self.getOptVars()))) # the features are always normalized self.raiseAMessage('Defaulting "a" gradient parameter to', self.paramDict['a']) else: self.paramDict['a'] = float(self.paramDict['a']) self.constraintHandlingPara['innerBisectionThreshold'] = float( self.paramDict.get('innerBisectionThreshold', 1e-2)) self.constraintHandlingPara['innerLoopLimit'] = float( self.paramDict.get('innerLoopLimit', 1000)) self.gradDict['pertNeeded'] = self.gradDict['numIterForAve'] * 2 stochDist = self.paramDict.get('stochasticDistribution', 'Hypersphere') if stochDist == 'Bernoulli': self.stochasticDistribution = Distributions.returnInstance( 'Bernoulli', self) self.stochasticDistribution.p = 0.5 self.stochasticDistribution.initializeDistribution() # Initialize bernoulli distribution for random perturbation. Add artificial noise to avoid that specular loss functions get false positive convergence # FIXME there has to be a better way to get two random numbers self.stochasticEngine = lambda: [ (0.5 + randomUtils.random() * (1. + randomUtils.random( ) / 1000. * randomUtils.randomIntegers(-1, 1, self))) if self.stochasticDistribution.rvs() == 1 else -1. * (0.5 + randomUtils.random() * (1. + randomUtils.random( ) / 1000. * randomUtils.randomIntegers(-1, 1, self))) for _ in range(len(self.getOptVars())) ] elif stochDist == 'Hypersphere': self.stochasticEngine = lambda: randomUtils.randPointsOnHypersphere( len(self.getOptVars())) else: self.raiseAnError( IOError, self.paramDict['stochasticEngine'] + 'is currently not supported for SPSA')
def chooseEvaluationPoints(self, opt, stepSize, constraints=None): """ Determines new point(s) needed to evaluate gradient @ In, opt, dict, current opt point (normalized) @ In, stepSize, float, distance from opt point to sample neighbors @ In, constraints, dict, optional, boundary and functional constraints to respect when choosing new sampling points @ Out, evalPoints, list(dict), list of points that need sampling @ Out, evalInfo, list(dict), identifying information about points """ dh = self._proximity * stepSize evalPoints = [] evalInfo = [] directions = np.atleast_1d(randomUtils.random(self.N) < 0.5) * 2 - 1 for o, optVar in enumerate(self._optVars): # pick a new grad eval point optValue = opt[optVar] new = copy.deepcopy(opt) delta = dh * directions[o] # note this is NORMALIZED space delta new[optVar] = optValue + delta # constraint handling if constraints is not None: # all constraints speak DENORM space, not NORM space denormed = constraints['denormalize'](new) altPoint = self._handleConstraints( denormed, constraints['denormalize'](opt), optVar, constraints) # need NORM point, delta new = constraints['normalize'](altPoint) delta = new[optVar] - opt[optVar] # store as samplable point evalPoints.append(new) evalInfo.append({'type': 'grad', 'optVar': optVar, 'delta': delta}) return evalPoints, evalInfo
def uniformCrossover(parents,**kwargs): """ Method designed to perform crossover by swapping genes one by one @ In, parents, xr.DataArray, parents involved in the mating process. @ In, kwargs, dict, dictionary of parameters for this mutation method: parents, 2D array, parents in the current mating process. Shape is nParents x len(chromosome) i.e, number of Genes/Vars @ Out, children, xr.DataArray, children resulting from the crossover. Shape is nParents x len(chromosome) i.e, number of Genes/Vars """ nParents,nGenes = np.shape(parents) children = xr.DataArray(np.zeros((int(2*comb(nParents,2)),np.shape(parents)[1])), dims=['chromosome','Gene'], coords={'chromosome': np.arange(int(2*comb(nParents,2))), 'Gene':parents.coords['Gene'].values}) if (kwargs['crossoverProb'] == None) or ('crossoverProb' not in kwargs.keys()): crossoverProb = randomUtils.random(dim=1, samples=1) else: crossoverProb = kwargs['crossoverProb'] index = 0 parentsPairs = list(combinations(parents,2)) for parentPair in parentsPairs: parent1 = parentPair[0].values parent2 = parentPair[1].values children1,children2 = uniformCrossoverMethod(parent1,parent2,crossoverProb) children[index] = copy.deepcopy(children1) children[index+1] = copy.deepcopy(children2) index = index + 1 return children
def onePointCrossover(parents,**kwargs): """ Method designed to perform crossover by swapping chromosome portions before/after specified or sampled location @ In, parents, xr.DataArray, parents involved in the mating process. @ In, kwargs, dict, dictionary of parameters for this mutation method: crossoverProb, float, crossoverProb determines when child takes genes from a specific parent, default is random points, integer, point at which the cross over happens, default is random variables, list, variables names. @ Out, children, np.array, children resulting from the crossover. Shape is nParents x len(chromosome) i.e, number of Genes/Vars """ nParents,nGenes = np.shape(parents) # Number of children = 2* (nParents choose 2) children = xr.DataArray(np.zeros((int(2*comb(nParents,2)),nGenes)), dims=['chromosome','Gene'], coords={'chromosome': np.arange(int(2*comb(nParents,2))), 'Gene':kwargs['variables']}) # defaults if (kwargs['crossoverProb'] == None) or ('crossoverProb' not in kwargs.keys()): crossoverProb = randomUtils.random(dim=1, samples=1) else: crossoverProb = kwargs['crossoverProb'] # create children parentsPairs = list(combinations(parents,2)) for ind,parent in enumerate(parentsPairs): parent = np.array(parent).reshape(2,-1) # two parents at a time if randomUtils.random(dim=1,samples=1) <= crossoverProb: if (kwargs['points'] == None) or ('points' not in kwargs.keys()): point = list([randomUtils.randomIntegers(1,nGenes-1,None)]) elif (any(i>=nGenes-1 for i in kwargs['points'])): raise ValueError('Crossover point cannot be larger than number of Genes (variables)') else: point = kwargs['points'] for i in range(nGenes): if len(point)>1: raise ValueError('In one Point Crossover a single crossover location should be provided!') children[2*ind:2*ind+2,i] = copy.deepcopy(parent[np.arange(0,2)*(i<point[0])+np.arange(-1,-3,-1)*(i>=point[0]),i]) else: # Each child is just a copy of the parents children[2*ind:2*ind+2,:] = copy.deepcopy(parent) return children
def localInputAndChecks(self, xmlNode, paramInput): """ Local method for additional reading. @ In, xmlNode, xml.etree.ElementTree.Element, Xml element node @ In, paramInput, InputData.ParameterInput, the parsed parameters @ Out, None """ GradientBasedOptimizer.localInputAndChecks(self, xmlNode, paramInput) self.currentDirection = None numValues = self._numberOfSamples() # set the initial step size ## use the hyperdiagonal of a unit hypercube with a side length equal to the user's provided initialStepSize * 1.0 stepPercent = float(self.paramDict.get('initialStepSize', 0.05)) self.paramDict['initialStepSize'] = mathUtils.hyperdiagonal(np.ones(numValues)*stepPercent) self.raiseADebug('Based on initial step size factor of "{:1.5e}", initial step size is "{:1.5e}"' .format(stepPercent, self.paramDict['initialStepSize'])) # set the perturbation distance ## if not given, default to 10% of the step size self.paramDict['pertDist'] = float(self.paramDict.get('perturbationDistance',0.01)) self.raiseADebug('Perturbation distance is "{:1.5e}" percent of the step size' .format(self.paramDict['pertDist'])) self.constraintHandlingPara['innerBisectionThreshold'] = float(self.paramDict.get('innerBisectionThreshold', 1e-2)) if not 0 < self.constraintHandlingPara['innerBisectionThreshold'] < 1: self.raiseAnError(IOError,'innerBisectionThreshold must be between 0 and 1; got',self.constraintHandlingPara['innerBisectionThreshold']) self.constraintHandlingPara['innerLoopLimit'] = float(self.paramDict.get('innerLoopLimit', 1000)) self.gradDict['pertNeeded'] = self.gradDict['numIterForAve'] * (self.paramDict['pertSingleGrad']+1) # determine the number of indpendent variables (scalar and vectors included) stochDist = self.paramDict.get('stochasticDistribution', 'Hypersphere') if stochDist == 'Bernoulli': self.stochasticDistribution = Distributions.returnInstance('Bernoulli',self) self.stochasticDistribution.p = 0.5 self.stochasticDistribution.initializeDistribution() # Initialize bernoulli distribution for random perturbation. Add artificial noise to avoid that specular loss functions get false positive convergence # FIXME there has to be a better way to get two random numbers self.stochasticEngine = lambda: [(0.5+randomUtils.random()*(1.+randomUtils.random()/1000.*randomUtils.randomIntegers(-1, 1, self))) if self.stochasticDistribution.rvs() == 1 else -1.*(0.5+randomUtils.random()*(1.+randomUtils.random()/1000.*randomUtils.randomIntegers(-1, 1, self))) for _ in range(numValues)] elif stochDist == 'Hypersphere': # TODO assure you can't get a "0" along any dimension! Need to be > 1e-15. Right now it's just highly unlikely. self.stochasticEngine = lambda: randomUtils.randPointsOnHypersphere(numValues) if numValues > 1 else [randomUtils.randPointsOnHypersphere(numValues)] else: self.raiseAnError(IOError, self.paramDict['stochasticEngine']+'is currently not supported for SPSA')
def _checkAcceptability(self, traj, opt, optVal, info): """ Check if new opt point is acceptably better than the old one @ In, traj, int, identifier @ In, opt, dict, new opt point @ In, optVal, float, new optimization value @ In, info, dict, meta information about the opt point @ Out, acceptable, str, acceptability condition for point @ Out, old, dict, old opt point @ Out, rejectReason, str, reject reason of opt point, or return None if accepted """ # Check acceptability # NOTE: if self._optPointHistory[traj]: -> faster to use "try" for all but the first time try: old, _ = self._optPointHistory[traj][-1] oldVal = old[self._objectiveVar] # check if same point self.raiseADebug( ' ... change: {d: 1.3e} new objective: {n: 1.6e} old objective: {o: 1.6e}' .format(d=opt[self._objectiveVar] - oldVal, o=oldVal, n=opt[self._objectiveVar])) # if this is an opt point rerun, accept it without checking. if self._acceptRerun[traj]: acceptable = 'rerun' self._acceptRerun[traj] = False elif all(opt[var] == old[var] for var in self.toBeSampled): # this is the classic "same point" trap; we accept the same point, and check convergence later acceptable = 'accepted' else: if self._acceptabilityCriterion( oldVal, opt[self._objectiveVar]) > randomUtils.random( dim=1, samples=1): # TODO replace it back acceptable = 'accepted' else: acceptable = 'rejected' except IndexError: # if first sample, simply assume it's better! acceptable = 'first' old = None self._acceptHistory[traj].append(acceptable) self.raiseADebug(' ... {a}!'.format(a=acceptable)) return acceptable, old, 'None'
def inversionMutator(offSprings, **kwargs): """ This method is designed mirror a sequence of genes in each chromosome with probability = mutationProb. The sequence of genes to be mirrored is completely random. E.g. given chromosome C = [0,1,2,3,4,5,6,7,8,9] and sampled locL=2 locU=6; New chromosome C' = [0,1,6,5,4,3,2,7,8,9] @ In, offSprings, xr.DataArray, children resulting from the crossover process @ In, kwargs, dict, dictionary of parameters for this mutation method: mutationProb, float, probability that governs the mutation process, i.e., if prob < random number, then the mutation will occur @ Out, offSprings, xr.DataArray, children resulting from the crossover process """ for child in offSprings: # the mutation is performed for each child independently if randomUtils.random(dim=1, samples=1) < kwargs['mutationProb']: # sample gene locations: i.e., determine loc1 and loc2 locRangeList = list(range(0, child.values.shape[0])) index1 = randomUtils.randomIntegers(0, len(locRangeList), caller=None, engine=None) loc1 = locRangeList[index1] locRangeList.pop(loc1) index2 = randomUtils.randomIntegers(0, len(locRangeList), caller=None, engine=None) loc2 = locRangeList[index2] if loc1 > loc2: locL = loc2 locU = loc1 elif loc1 < loc2: locL = loc1 locU = loc2 ############## # select sequence to be mirrored and mirror it seq = child.values[locL:locU + 1] mirrSeq = seq[::-1] ############## # insert mirrored sequence into child child.values[locL:locU + 1] = mirrSeq return offSprings
def rouletteWheel(population,**kwargs): """ Roulette Selection mechanism for parent selection @ In, population, xr.DataArray, populations containing all chromosomes (individuals) candidate to be parents, i.e. population.values.shape = populationSize x nGenes. @ In, kwargs, dict, dictionary of parameters for this mutation method: fitness, xr.DataArray, fitness of each chromosome (individual) in the population, i.e., np.shape(fitness) = 1 x populationSize variables, list, variable names. nParents, int, number of required parents. @ Out, selectedParents, xr.DataArray, selected parents, i.e. np.shape(selectedParents) = nParents x nGenes. """ # Arguments pop = copy.deepcopy(population) fitness = copy.deepcopy(kwargs['fitness']) nParents= kwargs['nParents'] # if nparents = population size then do nothing (whole population are parents) if nParents == pop.shape[0]: return population elif nParents > pop.shape[0]: raise IOError('Number of parents is greater than population size') # begin the roulette selection algorithm selectedParent = xr.DataArray( np.zeros((nParents,np.shape(pop)[1])), dims=['chromosome','Gene'], coords={'chromosome':np.arange(nParents), 'Gene': kwargs['variables']}) # imagine a wheel that is partitioned according to the selection probabilities for i in range(nParents): # set a random pointer roulettePointer = randomUtils.random(dim=1, samples=1) # initialize Probability counter = 0 selectionProb = fitness.data/np.sum(fitness.data) # Share of the pie (rouletteWheel) sumProb = selectionProb[counter] while sumProb < roulettePointer : counter += 1 sumProb += selectionProb[counter] selectedParent[i,:] = pop.values[counter,:] pop = np.delete(pop, counter, axis=0) fitness = np.delete(fitness,counter,axis=0) return selectedParent
def scrambleMutator(offSprings, **kwargs): """ This method performs the scramble mutator. For each child, a subset of genes is chosen and their values are shuffled randomly. @ In, offSprings, xr.DataArray, offsprings after crossover @ In, kwargs, dict, dictionary of parameters for this mutation method: chromosome, numpy.array, the chromosome that will mutate to the new child locs, list, the locations of the genes to be randomly scrambled mutationProb, float, probability that governs the mutation process, i.e., if prob < random number, then the mutation will occur variables, list, variables names. @ Out, child, np.array, the mutated chromosome, i.e., the child. """ locs = kwargs['locs'] if locs == None: nLocs = randomUtils.randomIntegers(0, offSprings.sizes['Gene'] - 1, None) locs = [] for i in range(nLocs): l = randomUtils.randomIntegers(0, offSprings.sizes['Gene'] - 1, None) locs.append(l) locs = list(set(locs)) nMutable = len(locs) # initializing children children = xr.DataArray(np.zeros((np.shape(offSprings))), dims=['chromosome', 'Gene'], coords={ 'chromosome': np.arange(np.shape(offSprings)[0]), 'Gene': kwargs['variables'] }) for i in range(np.shape(offSprings)[0]): children[i] = copy.deepcopy(offSprings[i]) new = list(itemgetter(*locs)(offSprings[i].values)) for ind, element in enumerate(locs): if randomUtils.random(dim=1, samples=1) < kwargs['mutationProb']: children[i, locs[0]:locs[-1] + 1] = randomUtils.randomPermutation( list(offSprings.data[i, locs[0]:locs[-1] + 1]), None) return children
def uniformCrossoverMethod(parent1,parent2,crossoverProb): """ Method designed to perform a uniform crossover on 2 arrays @ In, parent1: first array @ In, parent2: second array @ In, crossoverProb: crossover probability for each gene @ Out, children1: first generated array @ Out, children2: second generated array """ children1 = np.zeros(parent1.size) children2 = np.zeros(parent2.size) for pos in range(parent1.size): if randomUtils.random(dim=1,samples=1)<crossoverProb: children1[pos] = parent1[pos] children2[pos] = parent2[pos] else: children1[pos] = parent2[pos] children2[pos] = parent1[pos] return children1,children2
def swapMutator(offSprings, **kwargs): """ This method performs the swap mutator. For each child, two genes are sampled and switched E.g.: child=[a,b,c,d,e] --> b and d are selected --> child = [a,d,c,b,e] @ In, offSprings, xr.DataArray, children resulting from the crossover process @ In, kwargs, dict, dictionary of parameters for this mutation method: locs, list, the 2 locations of the genes to be swapped mutationProb, float, probability that governs the mutation process, i.e., if prob < random number, then the mutation will occur variables, list, variables names. @ Out, children, xr.DataArray, the mutated chromosome, i.e., the child. """ if kwargs['locs'] == None: locs = list( set( randomUtils.randomChoice(list( np.arange(offSprings.data.shape[1])), size=2, replace=False))) loc1 = locs[0] loc2 = locs[1] else: loc1 = kwargs['locs'][0] loc2 = kwargs['locs'][1] # initializing children children = xr.DataArray(np.zeros((np.shape(offSprings))), dims=['chromosome', 'Gene'], coords={ 'chromosome': np.arange(np.shape(offSprings)[0]), 'Gene': kwargs['variables'] }) for i in range(np.shape(offSprings)[0]): children[i] = copy.deepcopy(offSprings[i]) ## TODO What happens if loc1 or 2 is out of range?! should we raise an error? if randomUtils.random(dim=1, samples=1) <= kwargs['mutationProb']: children[i, loc1] = offSprings[i, loc2] children[i, loc2] = offSprings[i, loc1] return children
def chooseEvaluationPoints(self, opt, stepSize): """ Determines new point(s) needed to evaluate gradient @ In, opt, dict, current opt point (normalized) @ In, stepSize, float, distance from opt point to sample neighbors @ Out, evalPoints, list(dict), list of points that need sampling @ Out, evalInfo, list(dict), identifying information about points """ dh = self._proximity * stepSize evalPoints = [] evalInfo = [] directions = np.asarray(randomUtils.random(self.N) < 0.5) * 2 - 1 for o, optVar in enumerate(self._optVars): optValue = opt[optVar] new = copy.deepcopy(opt) delta = dh * directions[o] new[optVar] = optValue + delta evalPoints.append(new) evalInfo.append({'type': 'grad', 'optVar': optVar, 'delta': delta}) return evalPoints, evalInfo
def bitFlipMutator(offSprings, **kwargs): """ This method is designed to flip a single gene in each chromosome with probability = mutationProb. E.g. gene at location loc is flipped from current value to newValue The gene to be flipped is completely random. The new value of the flipped gene is is completely random. @ In, offSprings, xr.DataArray, children resulting from the crossover process @ In, kwargs, dict, dictionary of parameters for this mutation method: mutationProb, float, probability that governs the mutation process, i.e., if prob < random number, then the mutation will occur @ Out, offSprings, xr.DataArray, children resulting from the crossover process """ for child in offSprings: # the mutation is performed for each child independently if randomUtils.random(dim=1, samples=1) < kwargs['mutationProb']: # sample gene location to be flipped: i.e., determine loc chromosomeSize = child.values.shape[0] loc = randomUtils.randomIntegers(0, chromosomeSize, caller=None, engine=None) ############## # sample value: i.e., determine newValue if kwargs['sampleRange'] == 'local': rangeValues = list(set(offSprings[:, loc].values)) else: #kwargs['sampleRange']=='global' rangeValues = offSprings.values.ravel().tolist() rangeValues.pop(child.values[loc]) newValuePos = randomUtils.randomIntegers(0, len(rangeValues), caller=None, engine=None) newValue = rangeValues[newValuePos] ############## # gene at location loc is flipped from current value to newValue child.values[loc] = newValue return offSprings
def _nextNeighbour(self, rlz, fraction=1): """ Perturbs the state to find the next random neighbour based on the cooling schedule @ In, rlz, dict, current realization @ In, fraction, float, optional, the current iteration divided by the iteration limit i.e., $\frac{iter}{Limit}$ @ Out, nextNeighbour, dict, the next random state for exponential cooling: .. math:: fraction = \\frac{iter}{Limit} amp = 1-fraction delta = \\frac{-amp}{2} + amp * r where :math: `r \sim \mathcal{U}(0,1)` for boltzmann cooling: .. math:: amp = min(\\sqrt(T), \\frac{1}{3*alpha} delta = r * alpha * amp where :math: `r \\sim \\mathcal{N}(0,1)` for cauchy cooling: .. math:: amp = r delta = alpha * T * tan(amp) where :math: `r \\sim \\mathcal{U}(-\\pi,\\pi)` for veryfast cooling: .. math:: amp = r delta = \\sign(amp-0.5)*T*((1.0+\\frac{1.0}{T})^{\\abs{2*amp-1}-1.0} where :math: `r \\sim \\mathcal{U}(0,1)` """ nextNeighbour = {} D = len(self.toBeSampled.keys()) alpha = 0.94 if self._coolingMethod in ['exponential', 'geometric']: amp = ((fraction)**-1) / 20 r = randomUtils.random(dim=D, samples=1) delta = (-amp / 2.) + amp * r elif self._coolingMethod == 'boltzmann': amp = min(np.sqrt(self.T), 1 / 3.0 / alpha) delta = randomUtils.randomNormal(dim=D, size=1) * alpha * amp elif self._coolingMethod == 'veryfast': amp = randomUtils.random(dim=D, samples=1) delta = np.sign(amp - 0.5) * self.T * ( (1 + 1.0 / self.T)**abs(2 * amp - 1) - 1.0) elif self._coolingMethod == 'cauchy': amp = (np.pi - (-np.pi)) * randomUtils.random(dim=D, samples=1) - np.pi delta = alpha * self.T * np.tan(amp) for i, var in enumerate(self.toBeSampled.keys()): nextNeighbour[var] = rlz[var] + delta[i] self.info['amp_' + var] = amp self.info['delta_' + var] = delta[i] self.info['fraction'] = fraction return nextNeighbour
return True ### BEGIN TESTS # NOTE that due to seeding, this test relies HEAVILY on not changing the orders of calls to randomUtils! # Reseed at the beginning of sections and add new tests to the end of sections. # set the stochastic environment TODO check both someday? # cannot pass the numpy as the stochasticEnv randomUtils.stochasticEnv = 'crow' eng = randomUtils.newRNG() # randomSeed(), setting the random seed randomUtils.randomSeed(42,engine=None) randomUtils.randomSeed(42,engine=eng) # check that seed is set checkAnswer('First float from first seed for engine not provided',randomUtils.random(engine=None),0.374540118847) checkAnswer('First float from first seed for local engine provided',randomUtils.random(engine=eng),0.374540118847) # check resetting seed randomUtils.randomSeed(12345,engine=None) #next float would be 0.95071430641 if seed didn't change randomUtils.randomSeed(12345,engine=eng) #next float would be 0.95071430641 if seed didn't change checkAnswer('First float from second seed for engine not provided',randomUtils.random(engine=None),0.929616092817) checkAnswer('First float from second seed for local engine provided',randomUtils.random(engine=eng),0.929616092817) ### random(), sampling on [0,1] ## single sampling randomUtils.randomSeed(42,engine=None) randomUtils.randomSeed(42,engine=eng) vals = np.array([randomUtils.random(engine=None) for _ in range(int(1e5))]) mean = np.average(vals)
def localGenerateInput(self, model, myInput): """ Function to select the next most informative point for refining the limit surface search. After this method is called, the self.inputInfo should be ready to be sent to the model @ In, model, model instance, an instance of a model @ In, myInput, list, a list of the original needed inputs for the model (e.g. list of files, etc.) @ Out, None """ varCount = 0 self.inputInfo['distributionName'] = { } #Used to determine which distribution to change if needed. self.inputInfo['distributionType'] = { } #Used to determine which distribution type is used weight = 1.0 for varName in self.axisName: # new implementation for ND LHS if not "<distribution>" in varName: if self.variables2distributionsMapping[varName][ 'totDim'] > 1 and self.variables2distributionsMapping[ varName]['reducedDim'] == 1: # to avoid double count of weight for ND distribution; I need to count only one variable instaed of N distName = self.variables2distributionsMapping[varName][ 'name'] if self.variablesTransformationDict: for distVarName in self.distributions2variablesMapping[ distName]: for subVar in utils.first( distVarName.keys()).strip().split(','): self.inputInfo['distributionName'][ subVar] = self.toBeSampled[varName] self.inputInfo['distributionType'][ subVar] = self.distDict[varName].type ndCoordinate = np.zeros( len(self.distributions2variablesMapping[distName])) dxs = np.zeros( len(self.distributions2variablesMapping[distName])) centerCoordinate = np.zeros( len(self.distributions2variablesMapping[distName])) positionList = self.distributions2variablesIndexList[ distName] sorted_mapping = sorted([ (utils.first(var.keys()).strip(), utils.first(var.values())) for var in self.distributions2variablesMapping[distName] ]) for var in sorted_mapping: # if the varName is a comma separated list of strings the user wants to sample the comma separated variables with the same sampled value => link the value to all comma separated variables variable, position = var upper = self.gridEntity.returnShiftedCoordinate( self.gridEntity.returnIteratorIndexes(), { variable: self.sampledCoordinate[self.counter - 1][varCount] + 1 })[variable] lower = self.gridEntity.returnShiftedCoordinate( self.gridEntity.returnIteratorIndexes(), { variable: self.sampledCoordinate[self.counter - 1][varCount] })[variable] varCount += 1 if self.gridInfo[variable] == 'CDF': coordinate = lower + ( upper - lower) * randomUtils.random() ndCoordinate[positionList.index( position)] = self.distDict[ variable].inverseMarginalDistribution( coordinate, variable) dxs[positionList.index( position )] = self.distDict[ variable].inverseMarginalDistribution( max(upper, lower), variable ) - self.distDict[ variable].inverseMarginalDistribution( min(upper, lower), variable) centerCoordinate[positionList.index( position)] = (self.distDict[variable]. inverseMarginalDistribution( upper, variable) + self.distDict[variable]. inverseMarginalDistribution( lower, variable)) / 2.0 for subVar in variable.strip().split(','): self.values[subVar] = ndCoordinate[ positionList.index(position)] self.inputInfo['upper'][ subVar] = self.distDict[ variable].inverseMarginalDistribution( max(upper, lower), variable) self.inputInfo['lower'][ subVar] = self.distDict[ variable].inverseMarginalDistribution( min(upper, lower), variable) elif self.gridInfo[variable] == 'value': dxs[positionList.index(position)] = max( upper, lower) - min(upper, lower) centerCoordinate[positionList.index( position)] = (upper + lower) / 2.0 coordinateCdf = self.distDict[ variable].marginalCdf(lower) + ( self.distDict[variable].marginalCdf( upper) - self.distDict[variable].marginalCdf( lower)) * randomUtils.random() coordinate = self.distDict[ variable].inverseMarginalDistribution( coordinateCdf, variable) ndCoordinate[positionList.index( position)] = coordinate for subVar in variable.strip().split(','): self.values[subVar] = coordinate self.inputInfo['upper'][subVar] = max( upper, lower) self.inputInfo['lower'][subVar] = min( upper, lower) gridsWeight = self.distDict[varName].cellIntegral( centerCoordinate, dxs) self.inputInfo['SampledVarsPb'][ varName] = self.distDict[varName].pdf(ndCoordinate) else: if self.gridInfo[varName] == 'CDF': upper = self.gridEntity.returnShiftedCoordinate( self.gridEntity.returnIteratorIndexes(), { varName: self.sampledCoordinate[self.counter - 1][varCount] + 1 })[varName] lower = self.gridEntity.returnShiftedCoordinate( self.gridEntity.returnIteratorIndexes(), { varName: self.sampledCoordinate[self.counter - 1][varCount] })[varName] varCount += 1 coordinate = lower + ( upper - lower) * randomUtils.random() gridCoordinate, distName = self.distDict[ varName].ppf( coordinate ), self.variables2distributionsMapping[ varName]['name'] for distVarName in self.distributions2variablesMapping[ distName]: for subVar in utils.first( distVarName.keys()).strip().split(','): self.inputInfo['distributionName'][ subVar], self.inputInfo[ 'distributionType'][ subVar], self.values[ subVar] = self.toBeSampled[ varName], self.distDict[ varName].type, np.atleast_1d( gridCoordinate )[utils.first( distVarName. values()) - 1] # coordinate stores the cdf values, we need to compute the pdf for SampledVarsPb self.inputInfo['SampledVarsPb'][ varName] = self.distDict[varName].pdf( np.atleast_1d(gridCoordinate).tolist()) gridsWeight = max(upper, lower) - min(upper, lower) else: self.raiseAnError( IOError, "Since the globalGrid is defined, the Stratified Sampler is only working when the sampling is performed on a grid on a CDF. However, the user specifies the grid on " + self.gridInfo[varName]) weight *= gridsWeight self.inputInfo['ProbabilityWeight-' + distName] = gridsWeight if ( "<distribution>" in varName ) or self.variables2distributionsMapping[varName]['totDim'] == 1: # 1D variable # if the varName is a comma separated list of strings the user wants to sample the comma separated variables with the same sampled value => link the value to all comma separated variables upper = self.gridEntity.returnShiftedCoordinate( self.gridEntity.returnIteratorIndexes(), { varName: self.sampledCoordinate[self.counter - 1][varCount] + 1 })[varName] lower = self.gridEntity.returnShiftedCoordinate( self.gridEntity.returnIteratorIndexes(), { varName: self.sampledCoordinate[self.counter - 1][varCount] })[varName] varCount += 1 if self.gridInfo[varName] == 'CDF': coordinate = lower + (upper - lower) * randomUtils.random() ppfValue = self.distDict[varName].ppf(coordinate) ppfLower = self.distDict[varName].ppf(min(upper, lower)) ppfUpper = self.distDict[varName].ppf(max(upper, lower)) gridWeight = self.distDict[varName].cdf( ppfUpper) - self.distDict[varName].cdf(ppfLower) self.inputInfo['SampledVarsPb'][varName] = self.distDict[ varName].pdf(ppfValue) elif self.gridInfo[varName] == 'value': coordinateCdf = self.distDict[varName].cdf( min(upper, lower)) + (self.distDict[varName].cdf( max(upper, lower)) - self.distDict[varName].cdf( min(upper, lower))) * randomUtils.random() if coordinateCdf == 0.0: self.raiseAWarning( IOError, "The grid lower bound and upper bound in value will generate ZERO cdf value!!!" ) coordinate = self.distDict[varName].ppf(coordinateCdf) gridWeight = self.distDict[varName].cdf(max( upper, lower)) - self.distDict[varName].cdf( min(upper, lower)) self.inputInfo['SampledVarsPb'][varName] = self.distDict[ varName].pdf(coordinate) # compute the weight and ProbabilityWeight-varName weight *= gridWeight self.inputInfo['ProbabilityWeight-' + varName] = gridWeight for subVar in varName.strip().split(','): self.inputInfo['distributionName'][ subVar] = self.toBeSampled[varName] self.inputInfo['distributionType'][subVar] = self.distDict[ varName].type if self.gridInfo[varName] == 'CDF': self.values[subVar] = ppfValue self.inputInfo['upper'][subVar] = ppfUpper self.inputInfo['lower'][subVar] = ppfLower elif self.gridInfo[varName] == 'value': self.values[subVar] = coordinate self.inputInfo['upper'][subVar] = max(upper, lower) self.inputInfo['lower'][subVar] = min(upper, lower) self.inputInfo['PointProbability'] = reduce( mul, self.inputInfo['SampledVarsPb'].values()) self.inputInfo['ProbabilityWeight'] = weight self.inputInfo['SamplerType'] = 'Stratified'
### BEGIN TESTS # NOTE that due to seeding, this test relies HEAVILY on not changing the orders of calls to randomUtils! # Reseed at the beginning of sections and add new tests to the end of sections. # set the stochastic environment TODO check both someday? # cannot pass the numpy as the stochasticEnv randomUtils.stochasticEnv = 'crow' eng = randomUtils.newRNG() # randomSeed(), setting the random seed randomUtils.randomSeed(42, engine=None) randomUtils.randomSeed(42, engine=eng) # check that seed is set checkAnswer('First float from first seed for engine not provided', randomUtils.random(engine=None), 0.374540118847) checkAnswer('First float from first seed for local engine provided', randomUtils.random(engine=eng), 0.374540118847) # check resetting seed randomUtils.randomSeed( 12345, engine=None) #next float would be 0.95071430641 if seed didn't change randomUtils.randomSeed( 12345, engine=eng) #next float would be 0.95071430641 if seed didn't change checkAnswer('First float from second seed for engine not provided', randomUtils.random(engine=None), 0.929616092817) checkAnswer('First float from second seed for local engine provided', randomUtils.random(engine=eng), 0.929616092817)
def localGenerateInput(self, model, myInput): """ Function to select the next most informative point for refining the limit surface search. After this method is called, the self.inputInfo should be ready to be sent to the model @ In, model, model instance, an instance of a model @ In, myInput, list, a list of the original needed inputs for the model (e.g. list of files, etc.) @ Out, None """ # create values dictionary weight = 1.0 for key in self.distDict: # check if the key is a comma separated list of strings # in this case, the user wants to sample the comma separated variables with the same sampled value => link the value to all comma separated variables dim = self.variables2distributionsMapping[key]['dim'] totDim = self.variables2distributionsMapping[key]['totDim'] dist = self.variables2distributionsMapping[key]['name'] reducedDim = self.variables2distributionsMapping[key]['reducedDim'] weight = 1.0 if totDim == 1: for var in self.distributions2variablesMapping[dist]: varID = utils.first(var.keys()) if self.samplingType == 'uniform': distData = self.distDict[key].getCrowDistDict() if ('xMin' not in distData.keys()) or ( 'xMax' not in distData.keys()): self.raiseAnError( IOError, "In the Monte-Carlo sampler a uniform sampling type has been chosen; however, one or more distributions have not specified either the lowerBound or the upperBound" ) lower = distData['xMin'] upper = distData['xMax'] rvsnum = lower + (upper - lower) * randomUtils.random() epsilon = (upper - lower) / self.limit midPlusCDF = self.distDict[key].cdf(rvsnum + epsilon) midMinusCDF = self.distDict[key].cdf(rvsnum - epsilon) weight *= midPlusCDF - midMinusCDF else: rvsnum = self.distDict[key].rvs() self.inputInfo['SampledVarsPb'][key] = self.distDict[ key].pdf(rvsnum) for kkey in varID.strip().split(','): self.values[kkey] = np.atleast_1d(rvsnum)[0] self.inputInfo['ProbabilityWeight-' + varID] = 1. elif totDim > 1: if reducedDim == 1: if self.samplingType is None: rvsnum = self.distDict[key].rvs() coordinate = np.atleast_1d(rvsnum).tolist() else: coordinate = np.zeros(totDim) for i in range(totDim): lower = self.distDict[key].returnLowerBound(i) upper = self.distDict[key].returnUpperBound(i) coordinate[i] = lower + ( upper - lower) * randomUtils.random() if reducedDim > len(coordinate): self.raiseAnError( IOError, "The dimension defined for variables drew from the multivariate normal distribution is exceeded by the dimension used in Distribution (MultivariateNormal) " ) probabilityValue = self.distDict[key].pdf(coordinate) self.inputInfo['SampledVarsPb'][key] = probabilityValue for var in self.distributions2variablesMapping[dist]: varID = utils.first(var.keys()) varDim = var[varID] for kkey in varID.strip().split(','): self.values[kkey] = np.atleast_1d(rvsnum)[varDim - 1] self.inputInfo['ProbabilityWeight-' + dist] = 1. else: self.raiseAnError( IOError, "Total dimension for given distribution should be >= 1") if len(self.inputInfo['SampledVarsPb'].keys()) > 0: self.inputInfo['PointProbability'] = reduce( mul, self.inputInfo['SampledVarsPb'].values()) else: self.inputInfo['PointProbability'] = 1.0 if self.samplingType == 'uniform': self.inputInfo['ProbabilityWeight'] = weight else: self.inputInfo[ 'ProbabilityWeight'] = 1.0 #MC weight is 1/N => weight is one self.inputInfo['SamplerType'] = 'MonteCarlo'
def initialize(self, externalSeeding=None, solutionExport=None): """ This function should be called every time a clean optimizer is needed. Called before takeAstep in <Step> @ In, externalSeeding, int, optional, external seed @ In, solutionExport, DataObject, optional, a PointSet to hold the solution @ Out, None """ for entry in self.assemblerDict.get('Preconditioner', []): cls, typ, name, model = entry if cls != 'Models' or typ != 'ExternalModel': self.raiseAnError( IOError, 'Currently only "ExternalModel" models can be used as preconditioners! Got "{}.{}" for "{}".' .format(cls, typ, name)) self.preconditioners[name] = model model.initialize({}, []) for entry in self.assemblerDict.get('Sampler', []): cls, typ, name, sampler = entry forwardSampler = False for baseClass in sampler.__class__.__bases__: if "ForwardSampler" in baseClass.__name__: forwardSampler = True break if not forwardSampler: self.raiseAnError( IOError, 'Only "ForwardSampler"s (e.g. MonteCarlo, Grid, etc.) can be used for initializing the trajectories in the Optimizer! Got "{}.{}" for "{}".' .format(cls, typ, name)) self.initializationSampler = sampler initDict = {} for entity in ['Distributions', 'Functions', 'DataObjects']: initDict[entity] = dict( (entry[2], entry[3]) for entry in self.assemblerDict.get(entity, [])) self.initializationSampler._localGenerateAssembler(initDict) for key in self.initializationSampler.getInitParams().keys(): if key.startswith("sampled variable:"): var = key.replace("sampled variable:", "").strip() # check if the sampled variables are among the optimization parameters if var not in self.getOptVars(): self.raiseAnError( IOError, 'The variable "' + var + '" sampled by the initialization Sampler "' + self.initializationSampler.name + '" is not among the optimization parameters!') # check if the sampled variables have been already initialized in the optimizer (i.e. <initial>) if self.optVarsInitialized[var]: self.raiseAnError( IOError, 'The variable "' + var + '" sampled by the initialization Sampler "' + self.initializationSampler.name + '" has been already initialized in the Optimizer block. Remove <initial> XML node in Optimizer or the <variable> XML node in the Sampler!' ) # generate the initial coordinates by the sampler and check if they are inside the boundaries self.initializationSampler.initialize(externalSeeding) # check the number of trajectories (i.e. self.initializationSample.limit in the Sampler) currentNumberTrajectories = len(self.optTraj) if currentNumberTrajectories > 1: if currentNumberTrajectories != self.initializationSampler.limit: self.raiseAnError( IOError, "The number of samples generated by the initialization Sampler are different " + "than the one inputted in the Optimizer (from the variables where the <initial> XML block has been inputted)" ) else: self.optTraj = list(range(self.initializationSampler.limit)) for varName in self.optVarsInit['initial'].keys(): self.optVarsInit['initial'][varName] = dict.fromkeys( self.optTraj, self.optVarsInit['initial'][varName][0]) while self.initializationSampler.amIreadyToProvideAnInput(): self.initializationSampler.localGenerateInput(None, None) self.initializationSampler.inputInfo[ 'prefix'] = self.initializationSampler.counter sampledVars = self.initializationSampler.inputInfo[ 'SampledVars'] for varName, value in sampledVars.items(): self.optVarsInit['initial'][varName][ self.initializationSampler.counter] = np.atleast_1d( value) self.initializationSampler.counter += 1 # NOTE: counter['varsUpdate'] needs to be set AFTER self.optTraj length is set by the sampler (if used exclusively) self.counter['mdlEval'] = 0 self.counter['varsUpdate'] = [0] * len(self.optTraj) self.optTrajLive = copy.deepcopy(self.optTraj) self.mdlEvalHist = self.assemblerDict['TargetEvaluation'][0][3] # check if the TargetEvaluation feature and target spaces are consistent ins = self.mdlEvalHist.getVars("input") outs = self.mdlEvalHist.getVars("output") for varName in self.fullOptVars: if varName not in ins: self.raiseAnError( RuntimeError, "the optimization variable " + varName + " is not contained in the TargetEvaluation object " + self.mdlEvalHist.name) if self.objVar not in outs: self.raiseAnError( RuntimeError, "the optimization objective variable " + self.objVar + " is not contained in the TargetEvaluation object " + self.mdlEvalHist.name) self.objSearchingROM = SupervisedLearning.returnInstance( 'SciKitLearn', self, **{ 'SKLtype': 'neighbors|KNeighborsRegressor', 'Features': ','.join(list(self.fullOptVars)), 'Target': self.objVar, 'n_neighbors': 1, 'weights': 'distance' }) self.solutionExport = solutionExport if self.solutionExport is None: self.raiseAnError( IOError, 'The results of optimization cannot be obtained without a SolutionExport defined in the Step!' ) if type(solutionExport).__name__ not in ["PointSet", "DataSet"]: self.raiseAnError(IOError,'solutionExport type must be a PointSet or DataSet. Got '+\ type(solutionExport).__name__+ '!') if 'Function' in self.assemblerDict.keys(): self.constraintFunction = self.assemblerDict['Function'][0][3] if 'constrain' not in self.constraintFunction.availableMethods(): self.raiseAnError( IOError, 'the function provided to define the constraints must have an implemented method called "constrain"' ) # initialize dictionary entries # TODO a bunch of the gradient-level trajectory initializations should be moved here. for traj in self.optTraj: self.optVars[traj] = self.getOptVars() self.submissionQueue[traj] = deque() #check initial point array consistency rightLen = len(self.optTraj) #the hypothetical correct length for var in self.getOptVars(): haveLen = len(self.optVarsInit['initial'][var]) if haveLen != rightLen: self.raiseAnError( RuntimeError, 'The number of trajectories for variable "{}" is incorrect! Got {} but expected {}! Check the <initial> block.' .format(var, haveLen, rightLen)) # check the constraint here to check if the initial values violate it varK = {} for trajInd in self.optTraj: for varName in self.getOptVars(): varK[varName] = self.optVarsInit['initial'][varName][trajInd] satisfied, _ = self.checkConstraint(varK) if not satisfied: # get a random value between the the lower and upper bounds self.raiseAWarning( "the initial values specified for trajectory " + str(trajInd) + " do not satify the contraints. Picking random ones!") randomGuessesCnt = 0 while not satisfied and randomGuessesCnt < self.constraintHandlingPara[ 'innerLoopLimit']: for varName in self.getOptVars(): varK[varName] = self.optVarsInit['lowerBound'][ varName] + randomUtils.random( ) * self.optVarsInit['ranges'][varName] self.optVarsInit['initial'][varName][trajInd] = varK[ varName] satisfied, _ = self.checkConstraint(varK) if not satisfied: self.raiseAnError( Exception, "It was not possible to find any initial values that could satisfy the constraints for trajectory " + str(trajInd)) # extend multivalue variables (aka vector variables, or variables with "shape") ## TODO someday take array of initial values from a DataSet for var, shape in self.variableShapes.items(): if np.prod(shape) > 1: for traj in self.optTraj: baseVal = self.optVarsInit['initial'][var][traj] newVal = np.ones(shape) * baseVal self.optVarsInit['initial'][var][traj] = newVal if self.initSeed is not None: randomUtils.randomSeed(self.initSeed) self.localInitialize(solutionExport=solutionExport)
if updateResults: results["pass"] += 1 return True ### BEGIN TESTS # NOTE that due to seeding, this test relies HEAVILY on not changing the orders of calls to randomUtils! # Reseed at the beginning of sections and add new tests to the end of sections. # set the stochastic environment TODO check both someday? randomUtils.stochasticEnv = 'crow' # randomSeed(), setting the random seed randomUtils.randomSeed(42) # check that seed is set checkAnswer('First float from first seed', randomUtils.random(), 0.374540118847) # check resetting seed randomUtils.randomSeed( 12345) #next float would be 0.95071430641 if seed didn't change checkAnswer('First float from second seed', randomUtils.random(), 0.929616092817) ### random(), sampling on [0,1] ## single sampling randomUtils.randomSeed(42) vals = np.array([randomUtils.random() for _ in range(int(1e5))]) mean = np.average(vals) stdv = np.std(vals) checkAnswer('mean of 1e5 single samples', mean, 0.5, tol=1e-3) checkAnswer('stdv of 1e5 single samples', stdv, np.sqrt(1. / 12.), tol=1e-3)
def localGenerateInput(self,model,oldInput): """ Function to select the next most informative point for refining the limit surface search. After this method is called, the self.inputInfo should be ready to be sent to the model @ In, model, model instance, an instance of a model @ In, oldInput, list, a list of the original needed inputs for the model (e.g. list of files, etc.) @ Out, None """ # Alternatively, though I don't think we do this yet: # compute the direction normal to the surface, compute the derivative # normal to the surface of the probability, check the points where the # derivative probability is the lowest # create values dictionary self.inputInfo['distributionName'] = {} #Used to determine which distribution to change if needed. self.inputInfo['distributionType'] = {} #Used to determine which distribution type is used self.raiseADebug('generating input') varSet=False # DM: This sequence gets used repetitively, so I am promoting it to its own # variable axisNames = [key.replace('<distribution>','') for key in self.axisName] if self.surfPoint is not None and len(self.surfPoint) > 0: if self.batchStrategy == 'none': self.__scoreCandidates() maxDistance, maxGridId, maxId = 0.0, "", 0 for key, value in sorted(self.invPointPersistence.items()): if key != self.exceptionGrid and self.surfPoint[key] is not None: localMax = np.max(self.scores[key]) if localMax > maxDistance: maxDistance, maxGridId, maxId = localMax, key, np.argmax(self.scores[key]) if maxDistance > 0.0: for varIndex, _ in enumerate([key.replace('<distribution>','') for key in self.axisName]): self.values[self.axisName[varIndex]] = copy.copy(float(self.surfPoint[maxGridId][maxId,varIndex])) self.inputInfo['SampledVarsPb'][self.axisName[varIndex]] = self.distDict[self.axisName[varIndex]].pdf(self.values[self.axisName[varIndex]]) self.inputInfo['ProbabilityWeight-'+self.axisName[varIndex]] = self.distDict[self.axisName[varIndex]].pdf(self.values[self.axisName[varIndex]]) varSet=True else: self.raiseADebug('Maximum score is 0.0') elif self.batchStrategy.startswith('max'): ######################################################################## ## Initialize the queue with as many points as requested or as many as ## possible if len(self.toProcess) == 0: self.__scoreCandidates() edges = [] flattenedSurfPoints = list() flattenedBandPoints = list() flattenedScores = list() for key in self.bandIndices.keys(): flattenedSurfPoints = flattenedSurfPoints + list(self.surfPoint[key]) flattenedScores = flattenedScores + list(self.scores[key]) flattenedBandPoints = flattenedBandPoints + self.listSurfPoint[key] + self.bandIndices[key] flattenedSurfPoints = np.array(flattenedSurfPoints) for i,iCoords in enumerate(flattenedBandPoints): for j in range(i+1, len(flattenedBandPoints)): jCoords = flattenedBandPoints[j] ijValidNeighbors = True for d in range(len(jCoords)): if abs(iCoords[d] - jCoords[d]) > 1: ijValidNeighbors = False break if ijValidNeighbors: edges.append((i,j)) edges.append((j,i)) names = axisNames[:] #make copy names.append('score') amsc = AMSC_Object(X=flattenedSurfPoints, Y=flattenedScores, w=None, names=names, graph='none', gradient='steepest', normalization='feature', persistence='difference', edges=edges, debug=False) plevel = self.simplification*(max(flattenedScores)-min(flattenedScores)) partitions = amsc.StableManifolds(plevel) mergeSequence = amsc.GetMergeSequence() maxIdxs = list(set(partitions.keys())) thresholdLevel = self.threshold*(max(flattenedScores)-min(flattenedScores))+min(flattenedScores) # Sort the maxima based on decreasing function value, thus the top # candidate is the first element. if self.batchStrategy.endswith('V'): sortedMaxima = sorted(maxIdxs, key=lambda idx: flattenedScores[idx], reverse=True) else: # Sort the maxima based on decreasing persistence value, thus the top # candidate is the first element. sortedMaxima = sorted(maxIdxs, key=lambda idx: mergeSequence[idx][1], reverse=True) B = min(self.maxBatchSize,len(sortedMaxima)) for idx in sortedMaxima[0:B]: if flattenedScores[idx] >= thresholdLevel: self.toProcess.append(flattenedSurfPoints[idx,:]) if len(self.toProcess) == 0: self.toProcess.append(flattenedSurfPoints[np.argmax(flattenedScores),:]) ######################################################################## ## Select one sample selectedPoint = self.toProcess.pop() for varIndex, varName in enumerate(axisNames): self.values[self.axisName[varIndex]] = float(selectedPoint[varIndex]) self.inputInfo['SampledVarsPb'][self.axisName[varIndex]] = self.distDict[self.axisName[varIndex]].pdf(self.values[self.axisName[varIndex]]) self.inputInfo['ProbabilityWeight-'+self.axisName[varIndex]] = self.distDict[self.axisName[varIndex]].pdf(self.values[self.axisName[varIndex]]) varSet=True elif self.batchStrategy == 'naive': ######################################################################## ## Initialize the queue with as many points as requested or as many as ## possible if len(self.toProcess) == 0: self.__scoreCandidates() sortedIndices = sorted(range(len(self.scores)), key=lambda k: self.scores[k],reverse=True) B = min(self.maxBatchSize,len(sortedIndices)) for idx in sortedIndices[0:B]: self.toProcess.append(self.surfPoint[idx,:]) if len(self.toProcess) == 0: self.toProcess.append(self.surfPoint[np.argmax(self.scores),:]) ######################################################################## ## Select one sample selectedPoint = self.toProcess.pop() for varIndex, varName in enumerate(axisNames): self.values[self.axisName[varIndex]] = float(selectedPoint[varIndex]) self.inputInfo['SampledVarsPb'][self.axisName[varIndex]] = self.distDict[self.axisName[varIndex]].pdf(self.values[self.axisName[varIndex]]) self.inputInfo['ProbabilityWeight-'+self.axisName[varIndex]] = self.distDict[self.axisName[varIndex]].pdf(self.values[self.axisName[varIndex]]) varSet=True if not varSet: #here we are still generating the batch for key in sorted(self.distDict.keys()): if self.toleranceWeight=='cdf': self.values[key] = self.distDict[key].ppf(float(randomUtils.random())) else: self.values[key] = self.distDict[key].lowerBound+(self.distDict[key].upperBound-self.distDict[key].lowerBound)*float(randomUtils.random()) self.inputInfo['distributionName'][key] = self.toBeSampled[key] self.inputInfo['distributionType'][key] = self.distDict[key].type self.inputInfo['SampledVarsPb' ][key] = self.distDict[key].pdf(self.values[key]) self.inputInfo['ProbabilityWeight-'+key] = self.distDict[key].pdf(self.values[key]) self.addMetaKeys(['ProbabilityWeight-'+key]) self.inputInfo['PointProbability' ] = reduce(mul, self.inputInfo['SampledVarsPb'].values()) # the probability weight here is not used, the post processor is going to recreate the grid associated and use a ROM for the probability evaluation self.inputInfo['ProbabilityWeight'] = self.inputInfo['PointProbability'] self.hangingPoints = np.vstack((self.hangingPoints,copy.copy(np.array([self.values[axis] for axis in self.axisName])))) self.raiseADebug('At counter '+str(self.counter)+' the generated sampled variables are: '+str(self.values)) self.inputInfo['SamplerType'] = 'LimitSurfaceSearch' self.inputInfo['subGridTol' ] = self.subGridTol
else: if updateResults: results["pass"] += 1 return True ### BEGIN TESTS # NOTE that due to seeding, this test relies HEAVILY on not changing the orders of calls to randomUtils! # Reseed at the beginning of sections and add new tests to the end of sections. # set the stochastic environment TODO check both someday? randomUtils.stochasticEnv = 'crow' # randomSeed(), setting the random seed randomUtils.randomSeed(42) # check that seed is set checkAnswer('First float from first seed',randomUtils.random(),0.374540118847) # check resetting seed randomUtils.randomSeed(12345) #next float would be 0.95071430641 if seed didn't change checkAnswer('First float from second seed',randomUtils.random(),0.929616092817) ### random(), sampling on [0,1] ## single sampling randomUtils.randomSeed(42) vals = np.array([randomUtils.random() for _ in range(int(1e5))]) mean = np.average(vals) stdv = np.std(vals) checkAnswer('mean of 1e5 single samples',mean,0.5, tol=1e-3) checkAnswer('stdv of 1e5 single samples',stdv,np.sqrt(1./12.), tol=1e-3) ## 1d batch sampling randomUtils.randomSeed(42) vals = randomUtils.random(1e5)