Exemple #1
0
    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')
Exemple #2
0
 def _getSegmentIndexFromClusterIndex(self,
                                      cluster,
                                      labelMap,
                                      clusterIndex=None,
                                      chooseRandom=False):
     """
   Given the index of a rom WITHIN a cluster, get the index of that rom's segment in the full history
   @ In, cluster, int, label of cluster that ROM is within
   @ In, labelMap, list(int), map of where clusters appear in order of full history
   @ In, clusterIndex, int, optional, index of ROM within the cluster
   @ In, chooseRandom, bool, optional, if True then choose randomly from eligible indices
   @ Out, segmentIndex, int, position of rom's segment in full history
   @ Out, clusterIndex, int, position of rom within cluster (returned in event of random)
 """
     # Need to either provide the index, or let it be random, but not both
     ## TODO modify to use internal RNG from randUtils
     assert not (clusterIndex is None and chooseRandom is False)
     assert not (clusterIndex is not None and chooseRandom is True)
     # indices of all segments
     indices = np.arange(len(labelMap))
     # indices who belong to this cluster
     eligible = indices[labelMap == cluster]
     # if random, choose now
     if chooseRandom:
         i = randomUtils.randomIntegers(0, len(eligible) - 1, self)
         clusterIndex = eligible[i]
     # global index
     segmentIndex = eligible[clusterIndex]
     return segmentIndex, clusterIndex
Exemple #3
0
def twoPointsCrossover(parents, parentIndexes,**kwargs):
  """
    Method designed to perform a two point crossover on 2 parents:
    Partition each parents in three sequences (A,B,C):
    parent1 = A1 B1 C1
    parent2 = A2 B2 C2
    Then:
    children1 = A1 B2 C1
    children2 = A2 B1 C2
    @ In, parents, xr.DataArray, parents involved in the mating process
    @ In, parentIndexes, list, list containing pairs of parents
    @ 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
          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
    @ 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})
  index = 0
  for couples in parentIndexes:
    locRangeList = list(range(0,nGenes))
    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

    parent1 = parents[couples[0]].values
    parent2 = parents[couples[1]].values
    children1,children2 = twoPointsCrossoverMethod(parent1,parent2,locL,locU)

    children[index]=copy.deepcopy(children1)
    children[index+1]=copy.deepcopy(children2)
    index = index + 2

  return children
Exemple #4
0
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
Exemple #5
0
 def readSamplerInit(self, xmlNode):
     """
   This method is responsible to read only the samplerInit block in the .xml file.
   This method has been moved from the base sampler class since the samplerInit block is needed only for the MC and stratified (LHS) samplers
   @ In, xmlNode, xml.etree.ElementTree.Element, Xml element node
   @ Out, None
 """
     for child in xmlNode:
         if child.tag == "samplerInit":
             self.initSeed = randomUtils.randomIntegers(0, 2**31, self)
             for childChild in child:
                 if childChild.tag == "limit":
                     try:
                         self.limit = int(childChild.text)
                     except ValueError:
                         self.raiseAnError(
                             IOError,
                             'reading the attribute for the sampler ' +
                             self.name +
                             ' it was not possible to perform the conversion to integer for the attribute limit with value '
                             + str(childChild.text))
                 if childChild.tag == "initialSeed":
                     try:
                         self.initSeed = int(childChild.text)
                     except ValueError:
                         self.raiseAnError(
                             IOError,
                             'reading the attribute for the sampler ' +
                             self.name +
                             ' it was not possible to perform the conversion to integer for the attribute initialSeed with value '
                             + str(childChild.text))
                 elif childChild.tag == "reseedEachIteration":
                     if childChild.text.lower(
                     ) in utils.stringsThatMeanTrue():
                         self.reseedAtEachIteration = True
                 elif childChild.tag == "distInit":
                     for childChildChild in childChild:
                         NDdistData = {}
                         for childChildChildChild in childChildChild:
                             if childChildChildChild.tag == 'initialGridDisc':
                                 NDdistData[childChildChildChild.tag] = int(
                                     childChildChildChild.text)
                             elif childChildChildChild.tag == 'tolerance':
                                 NDdistData[
                                     childChildChildChild.tag] = float(
                                         childChildChildChild.text)
                             else:
                                 self.raiseAnError(
                                     IOError, 'Unknown tag ' +
                                     childChildChildChild.tag +
                                     ' .Available are: initialGridDisc and tolerance!'
                                 )
                         self.NDSamplingParams[
                             childChildChild.attrib['name']] = NDdistData
Exemple #6
0
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
Exemple #7
0
 def __getstate__(self):
     """
   Obtains state of object for pickling.
   @ In, None
   @ Out, d, dict, stateful dictionary
 """
     d = copy.copy(self.__dict__)
     # set up a seed for the next pickled iteration
     if self.reseedCopies:
         rand = randomUtils.randomIntegers(1, int(2**20), self)
         d['random seed'] = rand
     return d
Exemple #8
0
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
Exemple #9
0
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
Exemple #10
0
 def handleInput(self, paramInput):
     """
   Read input specs
   @ In, paramInput, InputData.ParameterInput, parameter specs interpreted
   @ Out, None
 """
     likelihood = paramInput.findFirst('likelihood')
     if likelihood is not None:
         self._likelihood = likelihood.value
         self._logLikelihood = likelihood.parameterValues.get('log', False)
     else:
         self.raiseAnError(IOError,
                           "likelihood is required, but not provided!")
     init = paramInput.findFirst('samplerInit')
     if init is not None:
         # limit
         limit = init.findFirst('limit')
         if limit is not None:
             self.limit = limit.value
         else:
             self.raiseAnError(
                 IOError, 'MCMC', self.name,
                 'needs the limit block (number of samples) in the samplerInit block'
             )
         # initialSeed
         seed = init.findFirst('initialSeed')
         if seed is not None:
             self.initSeed = seed.value
         else:
             self.initSeed = randomUtils.randomIntegers(0, 2**31, self)
         burnIn = init.findFirst('burnIn')
         if burnIn is not None:
             self._burnIn = burnIn.value
     else:
         self.raiseAnError(IOError, 'MCMC', self.name,
                           'needs the samplerInit block')
     if self._burnIn >= self.limit:
         self.raiseAnError(
             IOError,
             'Provided "burnIn" value must be less than "limit" value!')
     # TargetEvaluation Node (Required)
     targetEval = paramInput.findFirst('TargetEvaluation')
     self._targetEvaluation = targetEval.value
     self._updateValues = copy.copy(self._initialValues)
Exemple #11
0
  def readSamplerInit(self,xmlNode):
    """
      This method is responsible to read only the samplerInit block in the .xml file.
      This method has been moved from the base sampler class since the samplerInit block is needed only for the MC and stratified (LHS) samplers
      @ In, xmlNode, xml.etree.ElementTree.Element, Xml element node
      @ Out, None
    """
    #TODO, this is redundant and paramInput should be directly passed in.
    paramInput = self.getInputSpecification()()
    paramInput.parseNode(xmlNode)

    for child in paramInput.subparts:
      if child.getName() == "samplerInit":
        self.initSeed = randomUtils.randomIntegers(0,2**31,self)
        for childChild in child.subparts:
          if childChild.getName() == "limit":
            try:
              self.limit = int(childChild.value)
            except ValueError:
              self.raiseAnError(IOError,'reading the attribute for the sampler '+self.name+' it was not possible to perform the conversion to integer for the attribute limit with value ' + str(childChild.value))
          if childChild.getName() == "initialSeed":
            try:
              self.initSeed = int(childChild.value)
            except ValueError:
              self.raiseAnError(IOError,'reading the attribute for the sampler '+self.name+' it was not possible to perform the conversion to integer for the attribute initialSeed with value ' + str(childChild.value))
          elif childChild.getName() == "reseedEachIteration":
            if childChild.value.lower() in utils.stringsThatMeanTrue():
              self.reseedAtEachIteration = True
          elif childChild.getName() == "distInit":
            for childChildChild in childChild.subparts:
              NDdistData = {}
              for childChildChildChild in childChildChild.subparts:
                if childChildChildChild.getName() == 'initialGridDisc':
                  NDdistData[childChildChildChild.getName()] = int(childChildChildChild.value)
                elif childChildChildChild.getName() == 'tolerance':
                  NDdistData[childChildChildChild.getName()] = float(childChildChildChild.value)
                else:
                  self.raiseAnError(IOError,'Unknown tag '+childChildChildChild.getName()+' .Available are: initialGridDisc and tolerance!')
              self.NDSamplingParams[childChildChild.parameterValues['name']] = NDdistData
Exemple #12
0
    def initialize(self, externalSeeding=None, solutionExport=None):
        """
      This function should be called every time a clean sampler is needed. Called before takeAstep in <Step>
      @ In, externalSeeding, int, optional, external seed
      @ In, solutionExport, DataObject, optional, in goal oriented sampling (a.k.a. adaptive sampling this is where the space/point satisfying the constrains)
      @ Out, None
    """
        if self.initSeed == None:
            self.initSeed = randomUtils.randomIntegers(0, 2**31, self)
        self.counter = 0
        if not externalSeeding:
            randomUtils.randomSeed(
                self.initSeed)  #use the sampler initialization seed
            self.auxcnt = self.initSeed
        elif externalSeeding == 'continue':
            pass  #in this case the random sequence needs to be preserved
        else:
            randomUtils.randomSeed(
                externalSeeding)  #the external seeding is used
            self.auxcnt = externalSeeding
        #grab restart dataobject if it's available, then in localInitialize the sampler can deal with it.
        if 'Restart' in self.assemblerDict.keys():
            self.raiseADebug('Restart object: ' +
                             str(self.assemblerDict['Restart']))
            self.restartData = self.assemblerDict['Restart'][0][3]
            self.raiseAMessage('Restarting from ' + self.restartData.name)
            #check consistency of data
            try:
                rdata = self.restartData.getAllMetadata()['crowDist']
                sdata = self.inputInfo['crowDist']
                self.raiseAMessage('sampler inputs:')
                for sk, sv in sdata.items():
                    self.raiseAMessage('|   ' + str(sk) + ': ' + str(sv))
                for i, r in enumerate(rdata):
                    if type(r) != dict:
                        continue
                    if not r == sdata:
                        self.raiseAMessage('restart inputs %i:' % i)
                        for rk, rv in r.items():
                            self.raiseAMessage('|   ' + str(rk) + ': ' +
                                               str(rv))
                        self.raiseAnError(
                            IOError,
                            'Restart "%s" data[%i] does not have same inputs as sampler!'
                            % (self.restartData.name, i))
            except KeyError as e:
                self.raiseAWarning(
                    "No CROW distribution available in restart -", e)
        else:
            self.raiseAMessage('No restart for ' + self.printTag)

        #load restart data into existing points
        if self.restartData is not None:
            if not self.restartData.isItEmpty():
                inps = self.restartData.getInpParametersValues()
                outs = self.restartData.getOutParametersValues()
                #FIXME there is no guarantee ordering is accurate between restart data and sampler
                inputs = list(v for v in inps.values())
                existingInps = zip(*inputs)
                outVals = zip(*list(v for v in outs.values()))
                self.existing = dict(zip(existingInps, outVals))

        #specializing the self.localInitialize() to account for adaptive sampling
        if solutionExport != None:
            self.localInitialize(solutionExport=solutionExport)
        else:
            self.localInitialize()

        for distrib in self.NDSamplingParams:
            if distrib in self.distributions2variablesMapping:
                params = self.NDSamplingParams[distrib]
                temp = utils.first(
                    self.distributions2variablesMapping[distrib][0].keys())
                self.distDict[temp].updateRNGParam(params)
            else:
                self.raiseAnError(
                    IOError,
                    'Distribution "%s" specified in distInit block of sampler "%s" does not exist!'
                    % (distrib, self.name))

        # Store the transformation matrix in the metadata
        if self.variablesTransformationDict:
            self.entitiesToRemove = []
            for variable in self.variables2distributionsMapping.keys():
                distName = self.variables2distributionsMapping[variable][
                    'name']
                dim = self.variables2distributionsMapping[variable]['dim']
                totDim = self.variables2distributionsMapping[variable][
                    'totDim']
                if totDim > 1 and dim == 1:
                    transformDict = {}
                    transformDict['type'] = self.distDict[
                        variable.strip()].type
                    transformDict['transformationMatrix'] = self.distDict[
                        variable.strip()].transformationMatrix()
                    self.inputInfo['transformation-' +
                                   distName] = transformDict
                    self.entitiesToRemove.append('transformation-' + distName)
Exemple #13
0
  def _readMoreXMLbase(self,xmlNode):
    """
      Function to read the portion of the xml input that belongs to the base sampler only
      and initialize some stuff based on the inputs got
      The text is supposed to contain the info where and which variable to change.
      In case of a code the syntax is specified by the code interface itself
      @ In, xmlNode, xml.etree.ElementTree.Element, Xml element node1
      @ Out, paramInput, InputData.ParameterInput the parsed paramInput
    """
    paramInput = self.getInputSpecification()()
    paramInput.parseNode(xmlNode)

    for child in paramInput.subparts:
      prefix = ""

      if child.getName() == 'Distribution':
        for childChild in child.subparts:
          if childChild.getName() =='distribution':
            prefix = "<distribution>"
            toBeSampled = childChild.value
        self.toBeSampled[prefix+child.parameterValues['name']] = toBeSampled

      elif child.getName() == 'variable':
        # variable for tracking if distributions or functions have been declared
        foundDistOrFunc = False
        # store variable name for re-use
        varName = child.parameterValues['name']
        # set shape if present
        if 'shape' in child.parameterValues:
          self.variableShapes[varName] = child.parameterValues['shape']
        # read subnodes
        for childChild in child.subparts:
          if childChild.getName() =='distribution':
            # can only have a distribution if doesn't already have a distribution or function
            if not foundDistOrFunc:
              foundDistOrFunc = True
            else:
              self.raiseAnError(IOError,'A sampled variable cannot have both a distribution and a function, or more than one of either!')
            # name of the distribution to sample
            toBeSampled = childChild.value
            varData={}
            varData['name']=childChild.value
            # variable dimensionality
            if 'dim' not in childChild.parameterValues:
              dim=1
            else:
              dim=childChild.parameterValues['dim']
            varData['dim']=dim
            # set up mapping for variable to distribution
            self.variables2distributionsMapping[varName] = varData
            # flag distribution as needing to be sampled
            self.toBeSampled[prefix+varName] = toBeSampled
          elif childChild.getName() == 'function':
            # can only have a function if doesn't already have a distribution or function
            if not foundDistOrFunc:
              foundDistOrFunc = True
            else:
              self.raiseAnError(IOError,'A sampled variable cannot have both a distribution and a function!')
            # function name
            toBeSampled = childChild.value
            # track variable as a functional sample
            self.dependentSample[prefix+varName] = toBeSampled

        if not foundDistOrFunc:
          self.raiseAnError(IOError,'Sampled variable',varName,'has neither a <distribution> nor <function> node specified!')

      elif child.getName() == "variablesTransformation":
        transformationDict = {}
        listIndex = None
        for childChild in child.subparts:
          if childChild.getName() == "latentVariables":
            transformationDict[childChild.getName()] = list(childChild.value)
          elif childChild.getName() == "manifestVariables":
            transformationDict[childChild.getName()] = list(childChild.value)
          elif childChild.getName() == "manifestVariablesIndex":
            # the index provided by the input file starts from 1, but the index used by the code starts from 0.
            listIndex = list(int(inp) - 1  for inp in childChild.value)
          elif childChild.getName() == "method":
            self.transformationMethod[child.parameterValues['distribution']] = childChild.value
        if listIndex == None:
          self.raiseAWarning('Index is not provided for manifestVariables, default index will be used instead!')
          listIndex = range(len(transformationDict["manifestVariables"]))
        transformationDict["manifestVariablesIndex"] = listIndex
        self.variablesTransformationDict[child.parameterValues['distribution']] = transformationDict

      elif child.getName() == "constant":
        name,value = self._readInConstant(child)
        self.constants[name] = value

      elif child.getName() == "restartTolerance":
        self.restartTolerance = child.value

    if len(self.constants) > 0:
      # check if constant variables are also part of the sampled space. In case, error out
      if not set(self.toBeSampled.keys()).isdisjoint(self.constants.keys()):
        self.raiseAnError(IOError,"Some constant variables are also in the sampling space:" +
                                  ' '.join([i if i in self.toBeSampled.keys() else "" for i in self.constants.keys()])  )

    if self.initSeed == None:
      self.initSeed = randomUtils.randomIntegers(0,2**31,self)
    # Creation of the self.distributions2variablesMapping dictionary: {'distName': [{'variable_name1': dim1}, {'variable_name2': dim2}]}
    for variable in self.variables2distributionsMapping.keys():
      distName = self.variables2distributionsMapping[variable]['name']
      dim      = self.variables2distributionsMapping[variable]['dim']
      listElement={}
      listElement[variable] = dim
      if (distName in self.distributions2variablesMapping.keys()):
        self.distributions2variablesMapping[distName].append(listElement)
      else:
        self.distributions2variablesMapping[distName]=[listElement]

    # creation of the self.distributions2variablesIndexList dictionary:{'distName':[dim1,dim2,...,dimN]}
    self.distributions2variablesIndexList = {}
    for distName in self.distributions2variablesMapping.keys():
      positionList = []
      for var in self.distributions2variablesMapping[distName]:
        position = utils.first(var.values())
        positionList.append(position)
      if sum(set(positionList)) > 1 and len(positionList) != len(set(positionList)):
        dups = set(str(var) for var in positionList if positionList.count(var) > 1)
        self.raiseAnError(IOError,'Each of the following dimensions are assigned to multiple variables in Samplers: "{}"'.format(', '.join(dups)),
                ' associated to ND distribution ', distName, '. This is currently not allowed!')
      positionList = list(set(positionList))
      positionList.sort()
      self.distributions2variablesIndexList[distName] = positionList

    for key in self.variables2distributionsMapping.keys():
      distName = self.variables2distributionsMapping[key]['name']
      dim      = self.variables2distributionsMapping[key]['dim']
      reducedDim = self.distributions2variablesIndexList[distName].index(dim) + 1
      self.variables2distributionsMapping[key]['reducedDim'] = reducedDim  # the dimension of variable in the transformed space
      self.variables2distributionsMapping[key]['totDim'] = max(self.distributions2variablesIndexList[distName]) # We will reset the value if the node <variablesTransformation> exist in the raven input file
      if not self.variablesTransformationDict and self.variables2distributionsMapping[key]['totDim'] > 1:
        if self.variables2distributionsMapping[key]['totDim'] != len(self.distributions2variablesIndexList[distName]):
          self.raiseAnError(IOError,'The "dim" assigned to the variables insider Sampler are not correct! the "dim" should start from 1, and end with the full dimension of given distribution')

    #Checking the variables transformation
    if self.variablesTransformationDict:
      for dist,varsDict in self.variablesTransformationDict.items():
        maxDim = len(varsDict['manifestVariables'])
        listLatentElement = varsDict['latentVariables']
        if len(set(listLatentElement)) != len(listLatentElement):
          dups = set(var for var in listLatentElement if listLatentElement.count(var) > 1)
          self.raiseAnError(IOError,'The following are duplicated variables listed in the latentVariables: ' + str(dups))
        if len(set(varsDict['manifestVariables'])) != len(varsDict['manifestVariables']):
          dups = set(var for var in varsDict['manifestVariables'] if varsDict['manifestVariables'].count(var) > 1)
          self.raiseAnError(IOError,'The following are duplicated variables listed in the manifestVariables: ' + str(dups))
        if len(set(varsDict['manifestVariablesIndex'])) != len(varsDict['manifestVariablesIndex']):
          dups = set(var+1 for var in varsDict['manifestVariablesIndex'] if varsDict['manifestVariablesIndex'].count(var) > 1)
          self.raiseAnError(IOError,'The following are duplicated variables indices listed in the manifestVariablesIndex: ' + str(dups))
        listElement = self.distributions2variablesMapping[dist]
        for var in listElement:
          self.variables2distributionsMapping[utils.first(var.keys())]['totDim'] = maxDim #reset the totDim to reflect the totDim of original input space
        tempListElement = {k.strip():v for x in listElement for ks,v in x.items() for k in list(ks.strip().split(','))}
        listIndex = []
        for var in listLatentElement:
          if var not in set(tempListElement.keys()):
            self.raiseAnError(IOError, 'The variable listed in latentVariables ' + var + ' is not listed in the given distribution: ' + dist)
          listIndex.append(tempListElement[var]-1)
        if max(listIndex) > maxDim:
          self.raiseAnError(IOError,'The maximum dim = ' + str(max(listIndex)) + ' defined for latent variables is exceeded the dimension of the problem ' + str(maxDim))
        if len(set(listIndex)) != len(listIndex):
          dups = set(var+1 for var in listIndex if listIndex.count(var) > 1)
          self.raiseAnError(IOError,'Each of the following dimensions are assigned to multiple latent variables in Samplers: ' + str(dups))
        # update the index for latentVariables according to the 'dim' assigned for given var defined in Sampler
        self.variablesTransformationDict[dist]['latentVariablesIndex'] = listIndex
    return paramInput
Exemple #14
0
## test many points

vals = randomUtils.randomNormal(3,5,engine=None)
checkAnswer('randomNormal number of samples for engine not provided',len(vals),5)
checkAnswer('randomNormal size of sample for engine not provided',len(vals[0]),3)

vals = randomUtils.randomNormal(3,5,engine=eng)
checkAnswer('randomNormal number of samples for local engine provided',len(vals),5)
checkAnswer('randomNormal size of sample for local engine provided',len(vals[0]),3)

### randomIntegers(), sampling integers in a range
randomUtils.randomSeed(42,engine=None)
randomUtils.randomSeed(42,engine=eng)
right = [14,18,20,12,17]
for i in range(5):
  n = randomUtils.randomIntegers(10,20,None,engine=None) #no message handler, error handling will error out
  checkAnswer('random integer, {} sample for engine not provided'.format(i),n,right[i])

for i in range(5):
  n = randomUtils.randomIntegers(10,20,None,engine=eng) #no message handler, error handling will error out
  checkAnswer('random integer, {} sample for local engine provided'.format(i),n,right[i])
### randomPermutation(), rearranging lists
randomUtils.randomSeed(42,engine=None)
randomUtils.randomSeed(42,engine=eng)
l = [1,2,3,4,5]
l2 = randomUtils.randomPermutation(l,None,engine=None)
checkArray('random permutation for engine not provided',l2,[2,4,5,1,3])
l2 = randomUtils.randomPermutation(l,None,engine=eng)
checkArray('random permutation for local engine provided',l2,[2,4,5,1,3])
### randPointsOnHypersphere(), unit hypersphere surface sampling (aka random direction)
randomUtils.randomSeed(42,engine=None)
Exemple #15
0
    def _generateVarsUpdateConstrained(self, traj, ak, gradient, varK):
        """
      Method to generate input for model to run, considering also that the input satisfies the constraint
      @ In, traj, int, trajectory label for whom we are generating variables with constraint consideration
      @ In, ak, float or array, it is gain for variable update (if array, different gain for each variable)
      @ In, gradient, dictionary, contains the gradient information for variable update
      @ In, varK, dictionary, current variable values (normalized)
      @ Out, varKPlus, dictionary, variable values for next iteration.
      @ Out, modded, bool, if True the point was modified by the constraint
    """
        varKPlus = {}
        try:
            gain = ak[:]
        except (TypeError, IndexError):
            gain = [ak] * len(
                self.getOptVars()
            )  #technically incorrect, but missing ones will be *0 anyway just below here
        gain = np.asarray(gain)
        for index, var in enumerate(self.getOptVars(
        )):  #get full opt vars so all variables carried through
            varKPlus[var] = varK[var] - gain[index] * gradient.get(var,
                                                                   0.0) * 1.0
        satisfied, activeConstraints = self.checkConstraint(
            self.denormalizeData(varKPlus))
        if satisfied:
            return varKPlus, False
        # else if not satisfied ...
        # check if the active constraints are the boundary ones. In this case, try to project the gradient at an angle
        modded = False
        if len(activeConstraints['internal']) > 0:
            modded = True
            projectedOnBoundary = {}
            for activeConstraint in activeConstraints['internal']:
                projectedOnBoundary[activeConstraint[0]] = activeConstraint[1]
                gradient[activeConstraint[0]] = 0.0  # remove this component
            varKPlus.update(self.normalizeData(projectedOnBoundary))
            newNormWithoutComponents = LA.norm(gradient.values())
            for var in gradient.keys():
                gradient[var] = gradient[
                    var] / newNormWithoutComponents if newNormWithoutComponents != 0.0 else gradient[
                        var]

        if len(activeConstraints['external']) == 0:
            return varKPlus, modded

        # Try to find varKPlus by shorten the gradient vector
        self.raiseADebug('Trajectory "{}" hit constraints ...'.format(traj))
        self.raiseADebug('  Attempting to shorten step length ...')
        foundVarsUpdate, varKPlus = self._bisectionForConstrainedInput(
            traj, varK, ak, gradient)
        if foundVarsUpdate:
            self.raiseADebug(
                '   ... successfully found new point by shortening length.')
            return varKPlus, True

        # Try to find varKPlus by rotate the gradient towards its orthogonal, since we consider the gradient as perpendicular
        # with respect to the constraints hyper-surface
        self.raiseADebug('  Attempting instead to rotate trajectory ...')
        innerLoopLimit = self.constraintHandlingPara['innerLoopLimit']
        if innerLoopLimit < 0:
            self.raiseAnError(
                IOError,
                'Limit for internal loop for constraint handling shall be nonnegative'
            )
        loopCounter = 0
        foundPendVector = False
        while not foundPendVector and loopCounter < innerLoopLimit:
            loopCounter += 1
            depVarPos = randomUtils.randomIntegers(
                0,
                len(self.getOptVars(traj=traj)) - 1, self)
            pendVector = {}
            npDot = 0
            for varID, var in enumerate(self.getOptVars(traj=traj)):
                pendVector[
                    var] = self.stochasticEngineForConstraintHandling.rvs(
                    ) if varID != depVarPos else 0.0
                npDot += pendVector[var] * gradient[var]
            for varID, var in enumerate(self.getOptVars(traj=traj)):
                if varID == depVarPos:
                    pendVector[var] = -npDot / gradient[var]

            r = LA.norm(
                np.asarray(
                    [gradient[var]
                     for var in self.getOptVars(traj=traj)])) / LA.norm(
                         np.asarray([
                             pendVector[var]
                             for var in self.getOptVars(traj=traj)
                         ]))
            for var in self.getOptVars(traj=traj):
                pendVector[var] = copy.deepcopy(pendVector[var]) * r

            varKPlus = {}
            for index, var in enumerate(self.getOptVars(traj=traj)):
                varKPlus[var] = copy.copy(varK[var] -
                                          gain[index] * pendVector[var] * 1.0)
            foundPendVector, activeConstraints = self.checkConstraint(
                self.denormalizeData(varKPlus))
            if not foundPendVector:
                foundPendVector, varKPlus = self._bisectionForConstrainedInput(
                    traj, varK, gain, pendVector)
            gain = gain / 2.

        if foundPendVector:
            lenPendVector = 0
            for var in self.getOptVars(traj=traj):
                lenPendVector += pendVector[var]**2
            lenPendVector = np.sqrt(lenPendVector)

            rotateDegreeUpperLimit = 2
            while self.angleBetween(traj, gradient,
                                    pendVector) > rotateDegreeUpperLimit:
                sumVector, lenSumVector = {}, 0
                for var in self.getOptVars(traj=traj):
                    sumVector[var] = gradient[var] + pendVector[var]
                    lenSumVector += sumVector[var]**2

                tempTempVarKPlus = {}
                for index, var in enumerate(self.getOptVars(traj=traj)):
                    sumVector[var] = copy.deepcopy(
                        sumVector[var] / np.sqrt(lenSumVector) * lenPendVector)
                    tempTempVarKPlus[var] = copy.copy(varK[var] - gain[index] *
                                                      sumVector[var] * 1.0)
                satisfied, activeConstraints = self.checkConstraint(
                    self.denormalizeData(tempTempVarKPlus))
                if satisfied:
                    varKPlus = copy.deepcopy(tempTempVarKPlus)
                    pendVector = copy.deepcopy(sumVector)
                else:
                    gradient = copy.deepcopy(sumVector)
            self.raiseADebug(
                '   ... successfully found new point by rotating trajectory.')
            return varKPlus, True
        varKPlus = varK
        self.raiseADebug('   ... did not successfully find new point.')
        return varKPlus, False
Exemple #16
0
  def initialize(self,externalSeeding=None,solutionExport=None):
    """
      This function should be called every time a clean sampler is needed. Called before takeAstep in <Step>
      @ In, externalSeeding, int, optional, external seed
      @ In, solutionExport, DataObject, optional, in goal oriented sampling (a.k.a. adaptive sampling this is where the space/point satisfying the constrains)
      @ Out, None
    """
    if self.initSeed == None:
      self.initSeed = randomUtils.randomIntegers(0,2**31,self)
    self.counter = 0
    if not externalSeeding:
      randomUtils.randomSeed(self.initSeed)       #use the sampler initialization seed
      self.auxcnt = self.initSeed
    elif externalSeeding=='continue':
      pass        #in this case the random sequence needs to be preserved
    else                              :
      randomUtils.randomSeed(externalSeeding)     #the external seeding is used
      self.auxcnt = externalSeeding
    #grab restart dataobject if it's available, then in localInitialize the sampler can deal with it.
    if 'Restart' in self.assemblerDict.keys():
      self.raiseADebug('Restart object: '+str(self.assemblerDict['Restart']))
      self.restartData = self.assemblerDict['Restart'][0][3]
      # check the right variables are in the restart
      need = set(self.toBeSampled.keys()+self.dependentSample.keys())
      if not need.issubset(set(self.restartData.getVars())):
        missing = need - set(self.restartData.getVars())
        #TODO this could be a warning, instead, but user wouldn't see it until the run was deep in
        self.raiseAnError(KeyError,'Restart data object "{}" is missing the following variables: "{}". No restart can be performed.'.format(self.restartData.name,', '.join(missing)))
      else:
        self.raiseAMessage('Restarting from '+self.restartData.name)
      # we used to check distribution consistency here, but we want to give more flexibility to using
      #   restart data, so do NOT check distributions of restart data.
    else:
      self.raiseAMessage('No restart for '+self.printTag)

    #load restart data into existing points
    # TODO do not copy data!  Read directly from restart.
    #if self.restartData is not None:
    #  if len(self.restartData) > 0:
    #    inps = self.restartData.getInpParametersValues()
    #    outs = self.restartData.getOutParametersValues()
    #    #FIXME there is no guarantee ordering is accurate between restart data and sampler
    #    inputs = list(v for v in inps.values())
    #    existingInps = zip(*inputs)
    #    outVals = zip(*list(v for v in outs.values()))
    #    self.existing = dict(zip(existingInps,outVals))

    #specializing the self.localInitialize() to account for adaptive sampling
    if solutionExport != None:
      self.localInitialize(solutionExport=solutionExport)
    else:
      self.localInitialize()

    for distrib in self.NDSamplingParams:
      if distrib in self.distributions2variablesMapping:
        params = self.NDSamplingParams[distrib]
        temp = utils.first(self.distributions2variablesMapping[distrib][0].keys())
        self.distDict[temp].updateRNGParam(params)
      else:
        self.raiseAnError(IOError,'Distribution "%s" specified in distInit block of sampler "%s" does not exist!' %(distrib,self.name))

    # Store the transformation matrix in the metadata
    if self.variablesTransformationDict:
      self.entitiesToRemove = []
      for variable in self.variables2distributionsMapping.keys():
        distName = self.variables2distributionsMapping[variable]['name']
        dim      = self.variables2distributionsMapping[variable]['dim']
        totDim   = self.variables2distributionsMapping[variable]['totDim']
        if totDim > 1 and dim  == 1:
          transformDict = {}
          transformDict['type'] = self.distDict[variable.strip()].type
          transformDict['transformationMatrix'] = self.distDict[variable.strip()].transformationMatrix()
          self.inputInfo['transformation-'+distName] = transformDict
          self.entitiesToRemove.append('transformation-'+distName)

    # Register expected metadata
    meta = ['ProbabilityWeight','prefix','PointProbability']
    for var in self.toBeSampled.keys():
      meta +=  ['ProbabilityWeight-'+ key for key in var.split(",")]
    self.addMetaKeys(*meta)
Exemple #17
0
    def _readMoreXMLbase(self, xmlNode):
        """
      Function to read the portion of the xml input that belongs to the base optimizer only
      and initialize some stuff based on the inputs got
      @ In, xmlNode, xml.etree.ElementTree.Element, Xml element node1
      @ Out, None
    """
        paramInput = self.getInputSpecification()()
        paramInput.parseNode(xmlNode)

        # TODO some merging with base sampler XML reading might be possible, but in general requires different entries
        # first read all XML nodes
        for child in paramInput.subparts:
            #FIXME: the common variable reading should be wrapped up in a method to reduce the code redundancy
            if child.getName() == "variable":
                if self.fullOptVars is None:
                    self.fullOptVars = []
                # store variable name
                varName = child.parameterValues['name']
                self.optVarsInitialized[varName] = False
                # store varible requested shape, if any
                if 'shape' in child.parameterValues:
                    self.variableShapes[varName] = child.parameterValues[
                        'shape']
                self.fullOptVars.append(varName)
                self.optVarsInit['initial'][varName] = {}
                for childChild in child.subparts:
                    if childChild.getName() == "upperBound":
                        self.optVarsInit['upperBound'][
                            varName] = childChild.value
                    elif childChild.getName() == "lowerBound":
                        self.optVarsInit['lowerBound'][
                            varName] = childChild.value
                    elif childChild.getName() == "initial":
                        self.optVarsInit['initial'][varName] = {}
                        self.optVarsInitialized[varName] = True
                        initPoints = childChild.value
                        for trajInd, initVal in enumerate(initPoints):
                            try:
                                self.optVarsInit['initial'][varName][
                                    trajInd] = float(initVal)
                            except ValueError:
                                self.raiseAnError(
                                    ValueError,
                                    'Unable to convert to float the intial value for variable "{}" in trajectory "{}": {}'
                                    .format(varName, trajInd, initVal))
                        if self.optTraj == None:
                            self.optTraj = list(
                                range(
                                    len(self.optVarsInit['initial']
                                        [varName].keys())))

            elif child.getName() == "constant":
                name, value = self._readInConstant(child)
                self.constants[child.parameterValues['name']] = value

            elif child.getName() == "objectVar":
                self.objVar = child.value.strip()

            elif child.getName() == "initialization":
                self.initSeed = randomUtils.randomIntegers(0, 2**31, self)
                for childChild in child.subparts:
                    if childChild.getName() == "limit":
                        self.limit['mdlEval'] = childChild.value
                        #the manual once claimed that "A" defaults to iterationLimit/10, but it's actually this number/10.
                    elif childChild.getName() == "type":
                        self.optType = childChild.value
                        if self.optType not in ['min', 'max']:
                            self.raiseAnError(
                                IOError,
                                'Unknown optimization type "{}". Available: "min" or "max"'
                                .format(childChild.value))
                    elif childChild.getName() == "initialSeed":
                        self.initSeed = childChild.value
                    elif childChild.getName() == 'thresholdTrajRemoval':
                        self.thresholdTrajRemoval = childChild.value
                    elif childChild.getName() == 'writeSteps':
                        whenToWrite = childChild.value.strip().lower()
                        if whenToWrite == 'every':
                            self.writeSolnExportOn = 'every'
                        elif whenToWrite == 'final':
                            self.writeSolnExportOn = 'final'
                        else:
                            self.raiseAnError(
                                IOError,
                                'Unexpected frequency for <writeSteps>: "{}". Expected "every" or "final".'
                                .format(whenToWrite))
                    else:
                        self.raiseAnError(
                            IOError, 'Unknown tag: ' + childChild.getName())

            elif child.getName() == "convergence":
                for childChild in child.subparts:
                    if childChild.getName() == "iterationLimit":
                        self.limit['varsUpdate'] = childChild.value
                    elif childChild.getName() == "absoluteThreshold":
                        self.absConvergenceTol = childChild.value
                    elif childChild.getName() == "relativeThreshold":
                        self.relConvergenceTol = childChild.value
                    elif childChild.getName() == "minStepSize":
                        self.minStepSize = childChild.value
                    elif childChild.getName() == 'persistence':
                        self.convergencePersistence = childChild.value

            elif child.getName() == "restartTolerance":
                self.restartTolerance = child.value

            elif child.getName() == 'parameter':
                for childChild in child.subparts:
                    self.paramDict[childChild.getName()] = childChild.value

        # now that XML is read, do some checks and defaults
        # set defaults
        if self.writeSolnExportOn is None:
            self.writeSolnExportOn = 'every'
        self.raiseAMessage(
            'Writing to solution export on "{}" optimizer iteration.'.format(
                self.writeSolnExportOn))
        if self.optType is None:
            self.optType = 'min'
        if self.thresholdTrajRemoval is None:
            self.thresholdTrajRemoval = 0.05
        if self.initSeed is None:
            self.initSeed = randomUtils.randomIntegers(0, 2**31, self)
        # NOTE: optTraj can be changed in "initialize" if the user provides a sampler for seeding
        if self.optTraj is None:
            self.optTraj = [0]

        # check required settings TODO this can probably be removed thanks to the input checking!
        if self.objVar is None:
            self.raiseAnError(
                IOError, 'Object variable is not specified for optimizer!')
        if self.fullOptVars is None:
            self.raiseAnError(
                IOError, 'Decision variable(s) not specified for optimizer!')

        for var in self.getOptVars():
            if var not in self.variableShapes:
                self.variableShapes[var] = (1, )
            else:
                if len(self.variableShapes[var]) > 1:
                    self.raiseAnError(
                        NotImplementedError,
                        'Matrices as inputs are not yet supported in the Optimizer. For variable "{}" received shape "{}"!'
                        .format(var, self.variableShapes[var]))

        for varName in self.fullOptVars:
            if varName not in self.optVarsInit['upperBound'].keys():
                self.raiseAnError(
                    IOError, 'Upper bound for ' + varName + ' is not provided')
            if varName not in self.optVarsInit['lowerBound'].keys():
                self.raiseAnError(
                    IOError, 'Lower bound for ' + varName + ' is not provided')
            #store ranges of variables
            self.optVarsInit['ranges'][
                varName] = self.optVarsInit['upperBound'][
                    varName] - self.optVarsInit['lowerBound'][varName]
            if len(self.optVarsInit['initial'][varName]) == 0:
                for traj in self.optTraj:
                    self.optVarsInit['initial'][varName][traj] = None
Exemple #18
0
  def _generateVarsUpdateConstrained(self,traj,ak,gradient,varK):
    """
      Method to generate input for model to run, considering also that the input satisfies the constraint
      @ In, traj, int, trajectory label for whom we are generating variables with constraint consideration
      @ In, ak, float or array, it is gain for variable update (if array, different gain for each variable)
      @ In, gradient, dictionary, contains the gradient information for variable update
      @ In, varK, dictionary, current variable values (normalized)
      @ Out, varKPlus, dictionary, variable values for next iteration.
      @ Out, modded, bool, if True the point was modified by the constraint
    """
    varKPlus = {}
    gain = [ak]*self._numberOfSamples() #technically too many entries, but unneeded ones will be *0 anyway just below here
    gain = np.asarray(gain)
    index = 0
    for var in self.getOptVars():
      numSamples = np.prod(self.variableShapes[var])
      if numSamples == 1:
        new = varK[var]-gain[index]*gradient.get(var,0.0)
        index += 1
      else:
        new = np.zeros(numSamples)
        for i in range(numSamples):
          new[i] = varK[var][i] - gain[index] * gradient.get(var,[0.0]*i)[i]
          index += 1
      varKPlus[var] = new
    satisfied, activeViolations = self.checkConstraint(self.denormalizeData(varKPlus))
    if satisfied:
      return varKPlus, False
    # else if not satisfied ...
    # check if the active constraints are the boundary ones. In this case, try to project the gradient at an angle
    modded = False
    if len(activeViolations['internal']) > 0:
      self.raiseADebug('Attempting to fix constraint violation with gradient projection ...')
      modded = True
      projectedOnBoundary= {}
      for var,under,over in activeViolations['internal']:
        if np.prod(self.variableShapes[var]) == 1:
          if np.sum(over) > 0:
            projectedOnBoundary[var] = self.optVarsInit['upperBound'][var]
          elif np.sum(under) > 0:
            projectedOnBoundary[var] = self.optVarsInit['lowerBound'][var]
          gradient[var] = 0.0
        else:
          projectedOnBoundary[var] = self.denormalizeData({var:varKPlus[var]})[var]
          projectedOnBoundary[var][under] = self.optVarsInit['lowerBound'][var]
          projectedOnBoundary[var][over] = self.optVarsInit['upperBound'][var]
          gradient[var][np.logical_or(under,over)] = 0.0
      varKPlus.update(self.normalizeData(projectedOnBoundary))
      newNormWithoutComponents = self.calculateMultivectorMagnitude(gradient.values())
      for var in gradient.keys():
        gradient[var] = gradient[var]/newNormWithoutComponents if newNormWithoutComponents != 0.0 else gradient[var]

    if len(activeViolations['external']) == 0:
      return varKPlus, modded

    self.raiseADebug('Attempting to fix constraint violation by shortening gradient vector ...')
    # Try to find varKPlus by shorten the gradient vector
    self.raiseADebug('Trajectory "{}" hit constraints ...'.format(traj))
    self.raiseADebug('  Attempting to shorten step length ...')
    foundVarsUpdate, varKPlus = self._bisectionForConstrainedInput(traj,varK, ak, gradient)
    if foundVarsUpdate:
      self.raiseADebug('   ... successfully found new point by shortening length.')
      return varKPlus, True

    self.raiseADebug('Attempting to fix constraint violation by rotating towards orthogonal ...')
    # Try to find varKPlus by rotate the gradient towards its orthogonal, since we consider the gradient as perpendicular
    # with respect to the constraints hyper-surface
    self.raiseADebug('  Attempting instead to rotate trajectory ...')
    innerLoopLimit = self.constraintHandlingPara['innerLoopLimit']
    if innerLoopLimit < 0:
      self.raiseAnError(IOError, 'Limit for internal loop for constraint handling should be nonnegative')
    loopCounter = 0
    foundPendVector = False
    # search for the perpendicular vector
    while not foundPendVector and loopCounter < innerLoopLimit:
      loopCounter += 1
      # randomly choose the index of a variable to be the dependent? pivot
      depVarPos = randomUtils.randomIntegers(0,len(self.getOptVars())-1,self)
      # if that variable is multidimensional, pick a dimension -> this is not precisely equal probability of picking, but that should be okay.
      varSize = np.prod(self.variableShapes[var])
      if varSize > 1:
        depVarIdx = randomUtils.randomIntegers(0,varSize-1,self)
      pendVector = {}
      npDot = 0
      for varID, var in enumerate(self.getOptVars()):
        varSize = np.prod(self.variableShapes[var])
        if varSize == 1:
          pendVector[var] = self.stochasticEngineForConstraintHandling.rvs() if varID != depVarPos else 0.0
          npDot += pendVector[var]*gradient[var]
        else:
          for i in range(varSize):
            pendVector[var][i] = self.stochasticEngineForConstraintHandling.rvs() if (varID != depVarPos and depVarIdx != i) else 0.0
          npDot += np.sum(pendVector[var]*gradient[var])
      # TODO does this need to be in a separate loop or can it go with above?
      for varID, var in enumerate(self.getOptVars()):
        if varID == depVarPos:
          varSize = np.prod(self.variableShapes[var])
          if varSize == 1:
            pendVector[var] = -npDot/gradient[var]
          else:
            pendVector[var][depVarIdx] = -npDot/gradient[var][depVarIdx]

      r  = self.calculateMultivectorMagnitude([  gradient[var] for var in self.getOptVars()])
      r /= self.calculateMultivectorMagnitude([pendVector[var] for var in self.getOptVars()])
      for var in self.getOptVars():
        pendVector[var] = copy.deepcopy(pendVector[var])*r

      varKPlus = {}
      index = 0
      for var in self.getOptVars():
        varSize = np.prod(self.variableShapes[var])
        new = np.zeros(varSize)
        for i in range(varSize):
          if varSize == 1:
            new = copy.copy(varK[var]-gain[index]*pendVector[var]*1.0)
          else:
            new[i] = copy.copy(varK[var][i]-gain[index]*pendVector[var][i]*1.0)
          index += 1
        varKPlus[var] = new

      foundPendVector, activeConstraints = self.checkConstraint(self.denormalizeData(varKPlus))
      if not foundPendVector:
        foundPendVector, varKPlus = self._bisectionForConstrainedInput(traj,varK, gain, pendVector)
      gain = gain/2.

    if foundPendVector:
      lenPendVector = 0
      for var in self.getOptVars():
        lenPendVector += np.sum(pendVector[var]**2)
      lenPendVector = np.sqrt(lenPendVector)

      rotateDegreeUpperLimit = 2
      while self.angleBetween(traj,gradient, pendVector) > rotateDegreeUpperLimit:
        sumVector = {}
        lenSumVector = 0
        for var in self.getOptVars():
          sumVector[var] = gradient[var] + pendVector[var]
          lenSumVector += np.sum(sumVector[var]**2)

        tempTempVarKPlus = {}
        index = 0
        for var in self.getOptVars():
          sumVector[var] = copy.deepcopy(sumVector[var]/np.sqrt(lenSumVector)*lenPendVector)
          varSize = np.prod(self.variableShapes[var])
          new = np.zeros(varSize)
          for i in range(varSize):
            if varSize == 1:
              new = copy.copy(varK[var]-gain[index]*sumVector[var]*1.0)
            else:
              new[i] = copy.copy(varK[var][i]-gain[index]*sumVector[var][i]*1.0)
            index += 1
          tempTempVarKPlus[var] = new
        satisfied, activeConstraints = self.checkConstraint(self.denormalizeData(tempTempVarKPlus))
        if satisfied:
          varKPlus = copy.deepcopy(tempTempVarKPlus)
          pendVector = copy.deepcopy(sumVector)
        else:
          gradient = copy.deepcopy(sumVector)
      self.raiseADebug('   ... successfully found new point by rotating trajectory.')
      return varKPlus, True
    varKPlus = varK
    self.raiseADebug('   ... did not successfully find new point.')
    return varKPlus, False
Exemple #19
0
    def _readMoreXMLbase(self, xmlNode):
        """
      Function to read the portion of the xml input that belongs to the base sampler only
      and initialize some stuff based on the inputs got
      The text is supposed to contain the info where and which variable to change.
      In case of a code the syntax is specified by the code interface itself
      @ In, xmlNode, xml.etree.ElementTree.Element, Xml element node1
      @ Out, None
    """
        for child in xmlNode:
            prefix = ""
            if child.tag == 'Distribution':
                for childChild in child:
                    if childChild.tag == 'distribution':
                        prefix = "<distribution>"
                        tobesampled = childChild.text
                self.toBeSampled[prefix + child.attrib['name']] = tobesampled
                #if child.attrib['name'] != tobesampled:self.raiseAnError(IOError,"name of the <Distribution> node and <distribution> mismatches for node named "+ child.attrib['name'])
            elif child.tag == 'variable':
                foundDistOrFunc = False
                for childChild in child:
                    if childChild.tag == 'distribution':
                        if not foundDistOrFunc:
                            foundDistOrFunc = True
                        else:
                            self.raiseAnError(
                                IOError,
                                'A sampled variable cannot have both a distribution and a function!'
                            )
                        tobesampled = childChild.text
                        varData = {}
                        varData['name'] = childChild.text
                        if childChild.get('dim') == None:
                            dim = 1
                        else:
                            dim = childChild.attrib['dim']
                        varData['dim'] = int(dim)
                        self.variables2distributionsMapping[
                            child.attrib['name']] = varData
                        self.toBeSampled[prefix +
                                         child.attrib['name']] = tobesampled
                    elif childChild.tag == 'function':
                        if not foundDistOrFunc:
                            foundDistOrFunc = True
                        else:
                            self.raiseAnError(
                                IOError,
                                'A sampled variable cannot have both a distribution and a function!'
                            )
                        tobesampled = childChild.text
                        self.dependentSample[
                            prefix + child.attrib['name']] = tobesampled
                if not foundDistOrFunc:
                    self.raiseAnError(
                        IOError, 'Sampled variable', child.attrib['name'],
                        'has neither a <distribution> nor <function> node specified!'
                    )
            elif child.tag == "variablesTransformation":
                transformationDict = {}
                listIndex = None
                for childChild in child:
                    if childChild.tag == "latentVariables":
                        transformationDict[childChild.tag] = list(
                            inp.strip()
                            for inp in childChild.text.strip().split(','))
                    elif childChild.tag == "manifestVariables":
                        transformationDict[childChild.tag] = list(
                            inp.strip()
                            for inp in childChild.text.strip().split(','))
                    elif childChild.tag == "manifestVariablesIndex":
                        # the index provided by the input file starts from 1, but the index used by the code starts from 0.
                        listIndex = list(
                            int(inp.strip()) - 1
                            for inp in childChild.text.strip().split(','))
                    elif childChild.tag == "method":
                        self.transformationMethod[
                            child.attrib['distribution']] = childChild.text
                if listIndex == None:
                    self.raiseAWarning(
                        'Index is not provided for manifestVariables, default index will be used instead!'
                    )
                    listIndex = range(
                        len(transformationDict["manifestVariables"]))
                transformationDict["manifestVariablesIndex"] = listIndex
                self.variablesTransformationDict[
                    child.attrib['distribution']] = transformationDict
            elif child.tag == "constant":
                value = utils.partialEval(child.text)
                if value is None:
                    self.raiseAnError(
                        IOError,
                        'The body of "constant" XML block should be a number. Got: '
                        + child.text)
                try:
                    self.constants[child.attrib['name']] = value
                except KeyError:
                    self.raiseAnError(
                        KeyError,
                        child.tag + ' must have the attribute "name"!!!')
            elif child.tag == "restartTolerance":
                self.restartTolerance = float(child.text)

        if len(self.constants) > 0:
            # check if constant variables are also part of the sampled space. In case, error out
            if not set(self.toBeSampled.keys()).isdisjoint(
                    self.constants.keys()):
                self.raiseAnError(
                    IOError,
                    "Some constant variables are also in the sampling space:" +
                    ' '.join([
                        i if i in self.toBeSampled.keys() else ""
                        for i in self.constants.keys()
                    ]))

        if self.initSeed == None:
            self.initSeed = randomUtils.randomIntegers(0, 2**31, self)
        # Creation of the self.distributions2variablesMapping dictionary: {'distName': ({'variable_name1': dim1}, {'variable_name2': dim2})}
        for variable in self.variables2distributionsMapping.keys():
            distName = self.variables2distributionsMapping[variable]['name']
            dim = self.variables2distributionsMapping[variable]['dim']
            listElement = {}
            listElement[variable] = dim
            if (distName in self.distributions2variablesMapping.keys()):
                self.distributions2variablesMapping[distName].append(
                    listElement)
            else:
                self.distributions2variablesMapping[distName] = [listElement]

        # creation of the self.distributions2variablesIndexList dictionary:{'distName':[dim1,dim2,...,dimN]}
        self.distributions2variablesIndexList = {}
        for distName in self.distributions2variablesMapping.keys():
            positionList = []
            for var in self.distributions2variablesMapping[distName]:
                position = utils.first(var.values())
                positionList.append(position)
            positionList = list(set(positionList))
            positionList.sort()
            self.distributions2variablesIndexList[distName] = positionList

        for key in self.variables2distributionsMapping.keys():
            distName = self.variables2distributionsMapping[key]['name']
            dim = self.variables2distributionsMapping[key]['dim']
            reducedDim = self.distributions2variablesIndexList[distName].index(
                dim) + 1
            self.variables2distributionsMapping[key][
                'reducedDim'] = reducedDim  # the dimension of variable in the transformed space
            self.variables2distributionsMapping[key]['totDim'] = max(
                self.distributions2variablesIndexList[distName]
            )  # We will reset the value if the node <variablesTransformation> exist in the raven input file
            if not self.variablesTransformationDict and self.variables2distributionsMapping[
                    key]['totDim'] > 1:
                if self.variables2distributionsMapping[key]['totDim'] != len(
                        self.distributions2variablesIndexList[distName]):
                    self.raiseAnError(
                        IOError,
                        'The "dim" assigned to the variables insider Sampler are not correct! the "dim" should start from 1, and end with the full dimension of given distribution'
                    )

        #Checking the variables transformation
        if self.variablesTransformationDict:
            for dist, varsDict in self.variablesTransformationDict.items():
                maxDim = len(varsDict['manifestVariables'])
                listLatentElement = varsDict['latentVariables']
                if len(set(listLatentElement)) != len(listLatentElement):
                    dups = set(var for var in listLatentElement
                               if listLatentElement.count(var) > 1)
                    self.raiseAnError(
                        IOError,
                        'The following are duplicated variables listed in the latentVariables: '
                        + str(dups))
                if len(set(varsDict['manifestVariables'])) != len(
                        varsDict['manifestVariables']):
                    dups = set(var for var in varsDict['manifestVariables']
                               if varsDict['manifestVariables'].count(var) > 1)
                    self.raiseAnError(
                        IOError,
                        'The following are duplicated variables listed in the manifestVariables: '
                        + str(dups))
                if len(set(varsDict['manifestVariablesIndex'])) != len(
                        varsDict['manifestVariablesIndex']):
                    dups = set(
                        var + 1 for var in varsDict['manifestVariablesIndex']
                        if varsDict['manifestVariablesIndex'].count(var) > 1)
                    self.raiseAnError(
                        IOError,
                        'The following are duplicated variables indices listed in the manifestVariablesIndex: '
                        + str(dups))
                listElement = self.distributions2variablesMapping[dist]
                for var in listElement:
                    self.variables2distributionsMapping[utils.first(
                        var.keys()
                    )]['totDim'] = maxDim  #reset the totDim to reflect the totDim of original input space
                tempListElement = {
                    k.strip(): v
                    for x in listElement for ks, v in x.items()
                    for k in list(ks.strip().split(','))
                }
                listIndex = []
                for var in listLatentElement:
                    if var not in set(tempListElement.keys()):
                        self.raiseAnError(
                            IOError,
                            'The variable listed in latentVariables ' + var +
                            ' is not listed in the given distribution: ' +
                            dist)
                    listIndex.append(tempListElement[var] - 1)
                if max(listIndex) > maxDim:
                    self.raiseAnError(
                        IOError, 'The maximum dim = ' + str(max(listIndex)) +
                        ' defined for latent variables is exceeded the dimension of the problem '
                        + str(maxDim))
                if len(set(listIndex)) != len(listIndex):
                    dups = set(var + 1 for var in listIndex
                               if listIndex.count(var) > 1)
                    self.raiseAnError(
                        IOError,
                        'Each of the following dimensions  are assigned to multiple latent variables in Samplers: '
                        + str(dups))
                # update the index for latentVariables according to the 'dim' assigned for given var defined in Sampler
                self.variablesTransformationDict[dist][
                    'latentVariablesIndex'] = listIndex
Exemple #20
0
    def __init__(self, messageHandler, **kwargs):
        """
      A constructor that will appropriately intialize a supervised learning object
      @ In, messageHandler: a MessageHandler object in charge of raising errors,
                           and printing messages
      @ In, kwargs: an arbitrary dictionary of keywords and values
    """
        # general infrastructure
        supervisedLearning.__init__(self, messageHandler, **kwargs)
        self.printTag = 'ARMA'
        self._dynamicHandling = True  # This ROM is able to manage the time-series on its own.
        # training storage
        self.trainingData = {
        }  # holds normalized ('norm') and original ('raw') training data, by target
        self.cdfParams = {}  # dictionary of fitted CDF parameters, by target
        self.armaResult = {
        }  # dictionary of assorted useful arma information, by target
        self.correlations = []  # list of correlated variables
        self.fourierResults = {}  # dictionary of Fourier results, by target
        # training parameters
        self.fourierParams = {
        }  # dict of Fourier training params, by target (if requested, otherwise not present)
        self.Pmax = kwargs.get('Pmax', 3)  # bounds for autoregressive lag
        self.Pmin = kwargs.get('Pmin', 0)
        self.Qmax = kwargs.get('Qmax', 3)  # bounds for moving average lag
        self.Qmin = kwargs.get('Qmin', 0)
        self.segments = kwargs.get('segments', 1)
        # data manipulation
        self.reseedCopies = kwargs.get('reseedCopies', True)
        self.outTruncation = {
            'positive': set(),
            'negative': set()
        }  # store truncation requests
        self.pivotParameterID = kwargs['pivotParameter']
        self.pivotParameterValues = None  # In here we store the values of the pivot parameter (e.g. Time)
        self.seed = kwargs.get('seed', None)
        self.zeroFilterTarget = None  # target for whom zeros should be filtered out
        self.zeroFilterTol = None  # tolerance for zerofiltering to be considered zero, set below
        self.zeroFilterMask = None  # mask of places where zftarget is zero, or None if unused

        # check zeroFilterTarget is one of the targets given
        if self.zeroFilterTarget is not None and self.zeroFilterTarget not in self.target:
            self.raiseAnError(
                'Requested ZeroFilter on "{}" but this target was not found among the ROM targets!'
                .format(self.zeroFilterTarget))

        # get seed if provided
        ## FIXME only applies to VARMA sampling right now, since it has to be sampled through Numpy!
        ## see note under "check for correlation" below.
        if self.seed is None:
            self.seed = randomUtils.randomIntegers(0, 4294967295, self)
        else:
            self.seed = int(self.seed)

        self.normEngine = Distributions.returnInstance('Normal', self)
        self.normEngine.mean = 0.0
        self.normEngine.sigma = 1.0
        self.normEngine.upperBoundUsed = False
        self.normEngine.lowerBoundUsed = False
        self.normEngine.initializeDistribution()

        # check for correlation
        correlated = kwargs.get('correlate', None)
        if correlated is not None:
            # FIXME set the numpy seed
            ## we have to do this because VARMA.simulate does not accept a random number generator,
            ## but instead uses numpy directly.  As a result, for now, we have to seed numpy.
            ## Because we use our RNG to set the seed, though, it should follow the global seed still.
            self.raiseADebug('Setting Numpy seed to', self.seed)
            np.random.seed(self.seed)
            # store correlated targets
            corVars = [x.strip() for x in correlated.split(',')]
            for var in corVars:
                if var not in self.target:
                    self.raiseAnError(
                        IOError,
                        'Variable "{}" requested in "correlate" but not found among the targets!'
                        .format(var))
            # NOTE: someday, this could be expanded to include multiple sets of correlated variables.
            self.correlations = corVars

        # check if the pivotParameter is among the targetValues
        if self.pivotParameterID not in self.target:
            self.raiseAnError(
                IOError, "The pivotParameter " + self.pivotParameterID +
                " must be part of the Target space!")

        # can only handle one scaling input currently
        if len(self.features) != 1:
            self.raiseAnError(
                IOError,
                "The ARMA can only currently handle a single feature, which scales the outputs!"
            )

        # we aren't set up to optimize p and q anymore, so if they're different error out
        if self.Pmin != self.Pmax or self.Qmax != self.Qmin:
            self.raiseAnError(
                IOError,
                'ARMA temporarily has optimizing P and Q disabled; please set Pmax and Pmin to '
                +
                'the same value, and similarly for Q.  If optimizing is desired, please contact us so we can expedite '
                + 'the fix.')

        # read off of paramInput for more detailed inputs # TODO someday everything should read off this!
        paramInput = kwargs['paramInput']
        for child in paramInput.subparts:
            # read truncation requests (really value limits, not truncation)
            if child.getName() == 'outTruncation':
                # did the user request positive or negative?
                domain = child.parameterValues['domain']
                # if a recognized request, store it for later
                if domain in self.outTruncation:
                    self.outTruncation[
                        domain] = self.outTruncation[domain] | set(child.value)
                # if unrecognized, error out
                else:
                    self.raiseAnError(IOError,'Unrecognized "domain" for "outTruncation"! Was expecting "positive" '+\
                                              'or "negative" but got "{}"'.format(domain))
            # additional info for zerofilter
            elif child.getName() == 'ZeroFilter':
                self.zeroFilterTarget = child.value
                if self.zeroFilterTarget not in self.target:
                    self.raiseAnError(
                        IOError,
                        'Requested zero filtering for "{}" but not found among targets!'
                        .format(self.zeroFilterTarget))
                self.zeroFilterTol = child.parameterValues.get('tol', 1e-16)
            # read SPECIFIC parameters for Fourier detrending
            elif child.getName() == 'SpecificFourier':
                # clear old information
                periods = None
                orders = None
                # what variables share this Fourier?
                variables = child.parameterValues['variables']
                # check for variables that aren't targets
                missing = set(variables) - set(self.target)
                if len(missing):
                    self.raiseAnError(
                        IOError,
                        'Requested SpecificFourier for variables {} but not found among targets!'
                        .format(missing))
                # record requested Fourier periods, orders
                for cchild in child.subparts:
                    if cchild.getName() == 'periods':
                        periods = cchild.value
                    elif cchild.getName() == 'orders':
                        orders = cchild.value
                # sanity check
                if len(periods) != len(orders):
                    self.raiseADebug(IOError,'"periods" and "orders" need to have the same number of entries' +\
                                             'for variable group "{}"!'.format(variables))
                # set these params for each variable
                for v in variables:
                    self.raiseADebug(
                        'recording specific Fourier settings for "{}"'.format(
                            v))
                    if v in self.fourierParams:
                        self.raiseAWarning(
                            'Fourier params for "{}" were specified multiple times! Using first values ...'
                            .format(v))
                        continue
                    self.fourierParams[v] = {
                        'periods': periods,
                        'orders': dict(zip(periods, orders))
                    }

        # read GENERAL parameters for Fourier detrending
        ## these apply to everyone without SpecificFourier nodes
        ## use basePeriods to check if Fourier node present
        basePeriods = paramInput.findFirst('Fourier')
        if basePeriods is not None:
            # read periods
            basePeriods = basePeriods.value
            if len(set(basePeriods)) != len(basePeriods):
                self.raiseAnError(
                    IOError,
                    'Some <Fourier> periods have been listed multiple times!')
            # read orders
            baseOrders = self.initOptionDict.get('FourierOrder',
                                                 [1] * len(basePeriods))
            if len(basePeriods) != len(baseOrders):
                self.raiseAnError(
                    IOError,
                    '{} Fourier periods were requested, but {} Fourier order expansions were given!'
                    .format(len(basePeriods), len(baseOrders)))
            # set to any variable that doesn't already have a specific one
            for v in set(self.target) - set(self.fourierParams.keys()):
                self.raiseADebug(
                    'setting general Fourier settings for "{}"'.format(v))
                self.fourierParams[v] = {
                    'periods': basePeriods,
                    'orders': dict(zip(basePeriods, baseOrders))
                }
Exemple #21
0
  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.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')
Exemple #22
0
    def __init__(self, messageHandler, **kwargs):
        """
      A constructor that will appropriately intialize a supervised learning object
      @ In, messageHandler: a MessageHandler object in charge of raising errors,
                           and printing messages
      @ In, kwargs: an arbitrary dictionary of keywords and values
    """
        supervisedLearning.__init__(self, messageHandler, **kwargs)
        self.printTag = 'ARMA'
        self._dynamicHandling = True  # This ROM is able to manage the time-series on its own.
        self.trainingData = {
        }  # holds normalized ('norm') and original ('raw') training data, by target
        self.cdfParams = {}  # dictionary of fitted CDF parameters, by target
        self.fourierResults = {}  # dictionary of Fourier results, by target
        self.armaResult = {
        }  # dictionary of assorted useful arma information, by target
        self.correlations = []  # list of correlated variables
        self.Pmax = kwargs.get('Pmax', 3)  # bounds for autoregressive lag
        self.Pmin = kwargs.get('Pmin', 0)
        self.Qmax = kwargs.get('Qmax', 3)  # bounds for moving average lag
        self.Qmin = kwargs.get('Qmin', 0)
        self.reseedCopies = kwargs.get('reseedCopies', True)
        self.outTruncation = kwargs.get(
            'outTruncation', None
        )  # Additional parameters to allow user to specify the time series to be all positive or all negative
        self.pivotParameterID = kwargs['pivotParameter']
        self.pivotParameterValues = None  # In here we store the values of the pivot parameter (e.g. Time)
        self.seed = kwargs.get('seed', None)

        # get seed if provided
        ## FIXME only applies to VARMA sampling right now, since it has to be sampled through Numpy!
        ## see note under "check for correlation" below.
        if self.seed is None:
            self.seed = randomUtils.randomIntegers(0, 4294967295, self)
        else:
            self.seed = int(self.seed)

        self.normEngine = Distributions.returnInstance('Normal', self)
        self.normEngine.mean = 0.0
        self.normEngine.sigma = 1.0
        self.normEngine.upperBoundUsed = False
        self.normEngine.lowerBoundUsed = False
        self.normEngine.initializeDistribution()

        # check for correlation
        correlated = kwargs.get('correlate', None)
        if correlated is not None:
            # FIXME set the numpy seed
            ## we have to do this because VARMA.simulate does not accept a random number generator,
            ## but instead uses numpy directly.  As a result, for now, we have to seed numpy.
            ## Because we use our RNG to set the seed, though, it should follow the global seed still.
            self.raiseADebug('Setting Numpy seed to', self.seed)
            np.random.seed(self.seed)
            # store correlated targets
            corVars = [x.strip() for x in correlated.split(',')]
            for var in corVars:
                if var not in self.target:
                    self.raiseAnError(
                        IOError,
                        'Variable "{}" requested in "correlate" but not found among the targets!'
                        .format(var))
            # NOTE: someday, this could be expanded to include multiple sets of correlated variables.
            self.correlations = corVars

        # check if the pivotParameter is among the targetValues
        if self.pivotParameterID not in self.target:
            self.raiseAnError(
                IOError, "The pivotParameter " + self.pivotParameterID +
                " must be part of the Target space!")

        # can only handle one scaling input currently
        if len(self.features) != 1:
            self.raiseAnError(
                IOError,
                "The ARMA can only currently handle a single feature, which scales the outputs!"
            )

        # we aren't set up to optimize p and q anymore, so if they're different error out
        if self.Pmin != self.Pmax or self.Qmax != self.Qmin:
            self.raiseAnError(
                IOError,
                'ARMA temporarily has optimizing P and Q disabled; please set Pmax and Pmin to '
                +
                'the same value, and similarly for Q.  If optimizing is desired, please contact us so we can expedite '
                + 'the fix.')

        # Initialize parameters for Fourier detrending
        if 'Fourier' not in self.initOptionDict.keys():
            self.hasFourierSeries = False
        else:
            self.hasFourierSeries = True
            self.fourierPara = {}
            basePeriods = self.initOptionDict['Fourier']
            if isinstance(basePeriods, basestring):
                basePeriods = [float(s) for s in basePeriods.split(',')]
            else:
                basePeriods = [float(basePeriods)]
            self.fourierPara['basePeriod'] = basePeriods
            if len(set(self.fourierPara['basePeriod'])) != len(
                    self.fourierPara['basePeriod']):
                self.raiseAnError(
                    IOError,
                    'The same Fourier value was listed multiple times!')
            self.fourierPara['FourierOrder'] = {}
            if 'FourierOrder' not in self.initOptionDict.keys():
                self.fourierPara['basePeriod'] = dict(
                    (basePeriod, 4)
                    for basePeriod in self.fourierPara['basePeriod'])
            else:
                orders = self.initOptionDict['FourierOrder']
                if isinstance(orders, str):
                    orders = [int(x) for x in orders.split(',')]
                else:
                    orders = [orders]
                if len(self.fourierPara['basePeriod']) != len(orders):
                    self.raiseAnError(
                        ValueError,
                        'Number of FourierOrder entries should be "{}"'.format(
                            len(self.fourierPara['basePeriod'])))
                self.fourierPara['FourierOrder'] = dict(
                    (basePeriod, orders[i]) for i, basePeriod in enumerate(
                        self.fourierPara['basePeriod']))