Esempio n. 1
0
    def transform(self, particleArray):
        '''Given an array of Particles, select one and process
        '''
        if len(particleArray) == 0:  # empty
            return None

        pIndex = random.choice(range(len(particleArray)))
        p = particleArray[pIndex]
        # only process if state matches current state of this particle
        if p.getState() != self.getState():
            return particleArray  # return unaltered reference
        # find transform
        if self.getState() not in self.transformMap.keys():
            return None

        weights = []
        options = []
        for o, w in self.transformMap[self.getState()]:
            weights.append(w)
            options.append(o)
        boundary = unit.unitBoundaryProportion(weights)
        i = unit.unitBoundaryPos(random.random(), boundary)
        result = options[i]  # index is in position

        if result == None:  # remove
            #environment.printDebug(['transform(); removing particle:', p])
            particleArray.pop(pIndex)
        # if a string value the same as the current state of the particle
        elif result == p.getState():
            pass  # do nothing
        else:  # not yet implemented: need to convert do a different state
            # for this wee need a life cycle argument
            pass
Esempio n. 2
0
 def next(self, unitVal, previous, order, slide=1):
     """generate the next value of the a new chain
     unitVal is floating point number b/n 0 and 1; can be generated
     by any distribution. this value determines the selection of values
     previous is a list of values that will be analaized
     if the list is insufficiant for the order, if slide, will use 
     next available order, otherwise, will use zero order
     order may be a float; if so it will use weighting from drawer
     """
     symbols = list(self._symbols.keys())
     # get appropriate order: if a float, will be dynamically allocated
     order = drawer.floatToInt(order, 'weight')
     if order not in list(range(0, self._ordersSrc[-1]+1)):
         order = self._ordersSrc[-1] # use highest defined
     #print _MOD, 'using order', order
     # get appropriate key of given length of previous
     prevLen = len(previous)
     if prevLen <= order: # if less then specified by order
         if slide: # use length as order
             srcSeq = self._valueListToSymbolList(previous)
         else: srcSeq = () # jump to zeroth
     else: # if values are greater in length than order
         if order == 0: srcSeq = ()
         else:                 
             srcSeq = self._valueListToSymbolList(previous[-order:])      
     # determine of there is a direct match, or an expression match
     wList = self._findWeights(srcSeq) # may return None
     # if none, will create equal distribution of all symbols
     # return parallel lists of weights, symbols, both in same order
     wPost, sDeclared = self._scrubWeights(wList)
     #print _MOD, 'order, wPost, sDeclared', order, wPost, sDeclared
     boundary = unit.unitBoundaryProportion(wPost)
     i = unit.unitBoundaryPos(unitVal, boundary)
     # get symbol, then de-signify symbols into the store value
     return self._symbols[sDeclared[i]]
Esempio n. 3
0
    def transform(self, particleArray):
        '''Given an array of Particles, select one and process
        '''
        if len(particleArray) == 0: # empty
            return None

        pIndex = random.choice(range(len(particleArray)))
        p = particleArray[pIndex]
        # only process if state matches current state of this particle
        if p.getState() != self.getState():
            return particleArray # return unaltered reference
        # find transform        
        if self.getState() not in self.transformMap.keys():
            return None

        weights = []
        options = []
        for o, w in self.transformMap[self.getState()]:
            weights.append(w)
            options.append(o)
        boundary = unit.unitBoundaryProportion(weights)
        i = unit.unitBoundaryPos(random.random(), boundary)
        result = options[i] # index is in position

        if result == None: # remove
            #environment.printDebug(['transform(); removing particle:', p])
            particleArray.pop(pIndex)
        # if a string value the same as the current state of the particle
        elif result == p.getState():
            pass # do nothing
        else: # not yet implemented: need to convert do a different state
            # for this wee need a life cycle argument 
            pass
Esempio n. 4
0
 def next(self, unitVal, previous, order, slide=1):
     """generate the next value of the a new chain
     unitVal is floating point number b/n 0 and 1; can be generated
     by any distribution. this value determines the selection of values
     previous is a list of values that will be analaized
     if the list is insufficiant for the order, if slide, will use 
     next available order, otherwise, will use zero order
     order may be a float; if so it will use weighting from drawer
     """
     symbols = self._symbols.keys()
     # get appropriate order: if a float, will be dynamically allocated
     order = drawer.floatToInt(order, 'weight')
     if order not in range(0, self._ordersSrc[-1]+1):
         order = self._ordersSrc[-1] # use highest defined
     #print _MOD, 'using order', order
     # get appropriate key of given length of previous
     prevLen = len(previous)
     if prevLen <= order: # if less then specified by order
         if slide: # use length as order
             srcSeq = self._valueListToSymbolList(previous)
         else: srcSeq = () # jump to zeroth
     else: # if values are greater in length than order
         if order == 0: srcSeq = ()
         else:                 
             srcSeq = self._valueListToSymbolList(previous[-order:])      
     # determine of there is a direct match, or an expression match
     wList = self._findWeights(srcSeq) # may return None
     # if none, will create equal distribution of all symbols
     # return parallel lists of weights, symbols, both in same order
     wPost, sDeclared = self._scrubWeights(wList)
     #print _MOD, 'order, wPost, sDeclared', order, wPost, sDeclared
     boundary = unit.unitBoundaryProportion(wPost)
     i = unit.unitBoundaryPos(unitVal, boundary)
     # get symbol, then de-signify symbols into the store value
     return self._symbols[sDeclared[i]]
Esempio n. 5
0
    def __next__(self):
        '''Apply all rules and produce a new self._state

        >>> g = Grammar()
        >>> g.load('a{3}b{4} @ a{bab}b{aab} @ abaa')
        >>> str(g)
        'a{3}b{4}@a{bab}b{aab}@abaa'
        >>> g.next()
        >>> g.getState()
        'babaabbabbab'
        >>> g.next()
        >>> g.getState()
        'aabbabaabbabbabaabaabbabaabaabbabaab'
        >>> g.getState(values=True)
        [3.0, 3.0, 4.0, 4.0, 3.0, 4.0, 3.0, 3.0, 4.0, 4.0, 3.0, 4.0, 4.0, 3.0, 4.0, 3.0, 3.0, 4.0, 3.0, 3.0, 4.0, 4.0, 3.0, 4.0, 3.0, 3.0, 4.0, 3.0, 3.0, 4.0, 4.0, 3.0, 4.0, 3.0, 3.0, 4.0]

        >>> g = Grammar()
        >>> g.load('a{3}b{4}c{20}d{2} @ a{bab}b{acb}c{ac}d{cd} @ abd')
        >>> str(g)
        'a{3}b{4}c{20}d{2}@a{bab}c{ac}b{acb}d{cd}@abd'
        >>> g.next()
        >>> g.getState() 
        'babacbcd'
        >>> g.next()
        >>> g.getState() 
        'acbbabacbbabacacbaccd'


        >>> g = Grammar()
        >>> g.load('a{a}b{b} @ a{ab}b{a} @ b')
        >>> g.getState()
        'b'
        >>> g.next()
        >>> g.getState()
        'a'
        >>> g.next()
        >>> g.getState()
        'ab'
        >>> g.next()
        >>> g.getState()
        'aba'
        >>> g.next()
        >>> g.getState()
        'abaab'
        >>> g.next()
        >>> g.getState()
        'abaababa'

        '''
        if self._state == None or self._state == '':
            self._state = self._axiom  # a copy in principle

        stateNew = self._state  # copy
        matchTag = []  # matched start indices
        replacementCount = 0

        # make groups of index values, src groups to match
        # need to find largest rule in size
        indexCharPairs = []
        # self._maxRuleOutputSize

        #environment.printDebug(['next(): self._state', self._state])

        for size in range(1, self._maxRuleOutputSize + 1):
            #for size in range(1, 2):
            for i in range(0, len(self._state), size):
                pair = (i, self._state[i:i + size])
                if pair not in indexCharPairs:
                    indexCharPairs.append(pair)

        for inRule, outRule in list(self._rules.items()):

            environment.printDebug(
                ['next(): in/out rule',
                 repr(inRule), outRule])
            # grammar.py: next(): in/out rule '*' [('ab',1)]

            #environment.printDebug(['next(): index char pairs', indexCharPairs])

            # char here may be 1 or more characters, up to max rule size
            for i, char in indexCharPairs:
                #for i, char in enumerate(self._state):

                # comparison to rule
                match = False
                # test single character, exact match
                if inRule == self.EXPRESSALL or inRule == char:
                    match = True
                # if mutliple characters, and one is expressall, and lenght
                # is the same
                elif (len(inRule) > 1 and len(inRule) == len(char)
                      and self.EXPRESSALL in inRule):
                    matchChar = 0
                    for j in range(len(inRule)):
                        if inRule[j] == char[j] or inRule[j] == self.EXPRESSALL:
                            matchChar += 1
                    if matchChar == len(inRule):
                        match = True

                if not match:
                    continue

                # need to find cases of two or more char matches

                # store a string of index in the temp to mark positions
                # can find first instance of symbol; will not
                # be past symbols b/c we are replacing with the old index nums
                iNew = stateNew.find(char)
                pre = stateNew[:iNew]
                # skip location
                post = stateNew[iNew + len(char):]
                # insert marker as a random cod
                tag = self._tagIn(replacementCount)
                replacementCount += 1
                stateNew = pre + tag + post

                #environment.printDebug(['next(): stateNew', stateNew, repr(pre), repr(tag), repr(post)])
                # make a rule section

                # if only one rule, simply provide it
                if len(outRule) == 1:
                    # a list of value, weight pairSymbol
                    replacement = outRule[0][0]
                # if more than one rule, do a weighted selection
                else:
                    options = []
                    weights = []
                    for o, w in outRule:
                        options.append(o)
                        weights.append(w)
                    # create unit boundaries from ordered weights
                    boundary = unit.unitBoundaryProportion(weights)
                    i = unit.unitBoundaryPos(random.random(), boundary)
                    replacement = options[i]  # index is in position

                # a lost of all replacements to make, between the tag and the
                # replacement
                matchTag.append([tag, replacement])

        # do all replacements
        #environment.printDebug(['stateNew: prereplace', stateNew])
        for tag, replacement in matchTag:
            # these are not actual indices but tags to points in the scratch
            iNew = stateNew.find(tag)
            pre = stateNew[:iNew]
            # skip location
            post = stateNew[iNew + len(tag):]
            # insert final value
            stateNew = pre + replacement + post

        #environment.printDebug(['stateNew: post', stateNew])

        # replace old
        self._state = stateNew
Esempio n. 6
0
    def _scoreMain(self):
        """creates score
        note: octave choose for every note

        >>> from athenaCL.libATH.libTM import texture
        >>> ti = texture.factory('TimeSegment')
        >>> ti.tmName == 'TimeSegment'
        True
        >>> ti.loadDefault()
        >>> ti.score() == True
        True

        """
        # texture-wide time elements
        inst = self.getInst()
        
        # needed for preliminary parameter values
        # tStart, tEnd = self.getTimeRange()
        # tCurrent = tStart

        # get field, octave selection method value
        textPitchSelectorControl = self.getTextStatic('psc', 'selectionString') 
        textFieldLevel = self.getTextStatic('lfm', 'level') 
        textOctaveLevel = self.getTextStatic('lom', 'level')        

        # this is level tt event count numbers are applied (not count itself)
        textLevelEventCount = self.getTextStatic('lec', 'level')
        textTotalSegmentCount = self.getTextStatic('tsc', 'count')

        segmentManifest = [] # store [count, segWeight, start, end]
        # process segments first, determine events per segemtn
        tSeg = 0
        if textLevelEventCount == 'segment': # event count per segement
            for q in range(textTotalSegmentCount):
                eventCount = self.getTextDynamic('eventCountGenerator', tSeg)
                segmentManifest.append([int(round(eventCount))]) # store as list
                tSeg = tSeg + 1
        elif textLevelEventCount == 'count': # event count is total
            # get one value and divide
            eventCount = self.getTextDynamic('eventCountGenerator', tSeg)
            segEventCount = int(round((eventCount/textTotalSegmentCount)))
            if segEventCount <= 0: segEventCount = 1 # force minimum per seg
            for q in range(textTotalSegmentCount):
                segmentManifest.append([segEventCount]) # store as list      
        
        #print _MOD, 'levelEventCount', textLevelEventCount
        #print _MOD, 'textTotalSegmentCount', textTotalSegmentCount
        #print _MOD, 'segmentManifest', segmentManifest

        # get total duration
        tStart, tEnd = self.getTimeRange()
        tDurSpan = tEnd - tStart # not final dur, but time start span
        
        # get segment proportions
        tSeg = 0 # segment count as event step size
        segmentWidth = [] # store widths before getting scaled size
        for q in range(textTotalSegmentCount):
            # what if segment widht is zero?
            val = self.getTextDynamic('segmentWidthGenerator', tSeg)
            if val <= 0: pass # continue or warn?
            segmentWidth.append(val)
            tSeg = tSeg + 1
        # transfrom segment width into a collection of boundaries   
        #print _MOD, 'segmentWidth', segmentWidth
        segmentBounds = unit.unitBoundaryProportion(segmentWidth)
        
        for q in range(textTotalSegmentCount):
            s, m, e = segmentBounds[q]
            segmentManifest[q].append(s * tDurSpan)
            segmentManifest[q].append(e * tDurSpan)
            
        #print _MOD, 'segmentWidth', segmentManifest
                
        # get texture start time as init time
        tCurrent = tStart # defined abovie

        # if field/oct vals are taken once per set, pre calculate and store 
        # in a list; access from this list with pathPos index
        fieldValBuf = []
        if textFieldLevel == 'set':
            for q in range(self.getPathLen()):
                s, e = self.clockPoints(q) # use path df start time
                fieldValBuf.append(self.getField(s))
        octValBuf = []
        if textOctaveLevel == 'set':
            for q in range(self.getPathLen()):
                s, e = self.clockPoints(q)
                octValBuf.append(self.getOct(s))

        # iterate through segments in order
        for segPos in range(textTotalSegmentCount):      
            segEventCount = segmentManifest[segPos][0] # count is first in list
            tStartSeg = segmentManifest[segPos][1]
            tEndSeg = segmentManifest[segPos][2]
            # create events for thsi segment
            #print _MOD, 'segPos', segPos
            for i in range(segEventCount): #
                # get generator value w/n unit interval         
                tUnit = unit.limit(self.getTextDynamic('fillGenerator', tCurrent))
                tCurrent = unit.denorm(tUnit, tStartSeg, tEndSeg)
                pathPos = self.clockFindPos(tCurrent) # get pos for current time
                if pathPos == None: 
                    raise ValueError, 'tCurrent out of all time ranges'
    
                #print _MOD, 'pp, tc', pathPos, tCurrent
                #print _MOD, 'tss, tes', tStartSeg, tEndSeg
                         
                # need to determin path position based on time point of event
                chordCurrent = self.getPitchGroup(pathPos)
                multisetCurrent = self.getMultiset(pathPos)
    
                # create a generator to get pitches from chord as index values
                selectorChordPos = basePmtr.Selector(range(0,len(chordCurrent)),
                                                                 textPitchSelectorControl)
    
                # choose pc from chord
                ps = chordCurrent[selectorChordPos()] # get position w/n chord

                # a if the division of path dfs is w/n a single segment
                # either side of the path may occur more than once.
                # perhaps pre calculate and store in a list?

                if textFieldLevel == 'event': # every event
                    transCurrent = self.getField(tCurrent) # choose PITCHFIELD
                elif textFieldLevel == 'set':
                    transCurrent = fieldValBuf[pathPos] # choose PITCHFIELD

                if textOctaveLevel == 'event':
                    octCurrent = self.getOct(tCurrent) # choose OCTAVE
                elif textOctaveLevel == 'set':
                    octCurrent = octValBuf[pathPos] # choose OCTAVE
                    #print _MOD, 'pathPos, oct, t', pathPos, octCurrent, tCurrent

                psReal = pitchTools.psToTempered(ps, octCurrent, 
                                      self.temperamentObj, transCurrent)                                      
                self.stateUpdate(tCurrent, chordCurrent, ps, 
                                      multisetCurrent, None, psReal)

                bpm, pulse, dur, sus, acc = self.getRhythm(tCurrent) 
                if acc == 0 and not self.silenceMode: # this is a rest
                    tCurrent = tCurrent + dur
                    continue

                amp = self.getAmp(tCurrent) * acc # choose amp, pan
                pan = self.getPan(tCurrent)
                auxiliary = self.getAux(tCurrent) # chooose AUX, pack into list
                eventDict = self.makeEvent(tCurrent, bpm, pulse, dur, sus, acc, 
                                                             amp, psReal, pan, auxiliary)
                self.storeEvent(eventDict)
                # tCurrent = tCurrent + dur # move clocks forward by dur unit

            # self.clockForward() # advances path positon
        return 1
Esempio n. 7
0
    def _scoreMain(self):
        """creates score
        note: octave choose for every note

        >>> from athenaCL.libATH.libTM import texture
        >>> ti = texture.factory('TimeSegment')
        >>> ti.tmName == 'TimeSegment'
        True
        >>> ti.loadDefault()
        >>> ti.score() == True
        True

        """
        # texture-wide time elements
        inst = self.getInst()

        # needed for preliminary parameter values
        # tStart, tEnd = self.getTimeRange()
        # tCurrent = tStart

        # get field, octave selection method value
        textPitchSelectorControl = self.getTextStatic('psc', 'selectionString')
        textFieldLevel = self.getTextStatic('lfm', 'level')
        textOctaveLevel = self.getTextStatic('lom', 'level')

        # this is level tt event count numbers are applied (not count itself)
        textLevelEventCount = self.getTextStatic('lec', 'level')
        textTotalSegmentCount = self.getTextStatic('tsc', 'count')

        segmentManifest = []  # store [count, segWeight, start, end]
        # process segments first, determine events per segemtn
        tSeg = 0
        if textLevelEventCount == 'segment':  # event count per segement
            for q in range(textTotalSegmentCount):
                eventCount = self.getTextDynamic('eventCountGenerator', tSeg)
                segmentManifest.append([int(round(eventCount))
                                        ])  # store as list
                tSeg = tSeg + 1
        elif textLevelEventCount == 'count':  # event count is total
            # get one value and divide
            eventCount = self.getTextDynamic('eventCountGenerator', tSeg)
            segEventCount = int(round((eventCount / textTotalSegmentCount)))
            if segEventCount <= 0: segEventCount = 1  # force minimum per seg
            for q in range(textTotalSegmentCount):
                segmentManifest.append([segEventCount])  # store as list

        #print _MOD, 'levelEventCount', textLevelEventCount
        #print _MOD, 'textTotalSegmentCount', textTotalSegmentCount
        #print _MOD, 'segmentManifest', segmentManifest

        # get total duration
        tStart, tEnd = self.getTimeRange()
        tDurSpan = tEnd - tStart  # not final dur, but time start span

        # get segment proportions
        tSeg = 0  # segment count as event step size
        segmentWidth = []  # store widths before getting scaled size
        for q in range(textTotalSegmentCount):
            # what if segment widht is zero?
            val = self.getTextDynamic('segmentWidthGenerator', tSeg)
            if val <= 0: pass  # continue or warn?
            segmentWidth.append(val)
            tSeg = tSeg + 1
        # transfrom segment width into a collection of boundaries
        #print _MOD, 'segmentWidth', segmentWidth
        segmentBounds = unit.unitBoundaryProportion(segmentWidth)

        for q in range(textTotalSegmentCount):
            s, m, e = segmentBounds[q]
            segmentManifest[q].append(s * tDurSpan)
            segmentManifest[q].append(e * tDurSpan)

        #print _MOD, 'segmentWidth', segmentManifest

        # get texture start time as init time
        tCurrent = tStart  # defined abovie

        # if field/oct vals are taken once per set, pre calculate and store
        # in a list; access from this list with pathPos index
        fieldValBuf = []
        if textFieldLevel == 'set':
            for q in range(self.getPathLen()):
                s, e = self.clockPoints(q)  # use path df start time
                fieldValBuf.append(self.getField(s))
        octValBuf = []
        if textOctaveLevel == 'set':
            for q in range(self.getPathLen()):
                s, e = self.clockPoints(q)
                octValBuf.append(self.getOct(s))

        # iterate through segments in order
        for segPos in range(textTotalSegmentCount):
            segEventCount = segmentManifest[segPos][
                0]  # count is first in list
            tStartSeg = segmentManifest[segPos][1]
            tEndSeg = segmentManifest[segPos][2]
            # create events for thsi segment
            #print _MOD, 'segPos', segPos
            for i in range(segEventCount):  #
                # get generator value w/n unit interval
                tUnit = unit.limit(
                    self.getTextDynamic('fillGenerator', tCurrent))
                tCurrent = unit.denorm(tUnit, tStartSeg, tEndSeg)
                pathPos = self.clockFindPos(
                    tCurrent)  # get pos for current time
                if pathPos == None:
                    raise ValueError, 'tCurrent out of all time ranges'

                #print _MOD, 'pp, tc', pathPos, tCurrent
                #print _MOD, 'tss, tes', tStartSeg, tEndSeg

                # need to determin path position based on time point of event
                chordCurrent = self.getPitchGroup(pathPos)
                multisetCurrent = self.getMultiset(pathPos)

                # create a generator to get pitches from chord as index values
                selectorChordPos = basePmtr.Selector(
                    range(0, len(chordCurrent)), textPitchSelectorControl)

                # choose pc from chord
                ps = chordCurrent[selectorChordPos()]  # get position w/n chord

                # a if the division of path dfs is w/n a single segment
                # either side of the path may occur more than once.
                # perhaps pre calculate and store in a list?

                if textFieldLevel == 'event':  # every event
                    transCurrent = self.getField(tCurrent)  # choose PITCHFIELD
                elif textFieldLevel == 'set':
                    transCurrent = fieldValBuf[pathPos]  # choose PITCHFIELD

                if textOctaveLevel == 'event':
                    octCurrent = self.getOct(tCurrent)  # choose OCTAVE
                elif textOctaveLevel == 'set':
                    octCurrent = octValBuf[pathPos]  # choose OCTAVE
                    #print _MOD, 'pathPos, oct, t', pathPos, octCurrent, tCurrent

                psReal = pitchTools.psToTempered(ps, octCurrent,
                                                 self.temperamentObj,
                                                 transCurrent)
                self.stateUpdate(tCurrent, chordCurrent, ps, multisetCurrent,
                                 None, psReal)

                bpm, pulse, dur, sus, acc = self.getRhythm(tCurrent)
                if acc == 0 and not self.silenceMode:  # this is a rest
                    tCurrent = tCurrent + dur
                    continue

                amp = self.getAmp(tCurrent) * acc  # choose amp, pan
                pan = self.getPan(tCurrent)
                auxiliary = self.getAux(
                    tCurrent)  # chooose AUX, pack into list
                eventDict = self.makeEvent(tCurrent, bpm, pulse, dur, sus, acc,
                                           amp, psReal, pan, auxiliary)
                self.storeEvent(eventDict)
                # tCurrent = tCurrent + dur # move clocks forward by dur unit

            # self.clockForward() # advances path positon
        return 1
Esempio n. 8
0
    def next(self):
        '''Apply all rules and produce a new self._state

        >>> g = Grammar()
        >>> g.load('a{3}b{4} @ a{bab}b{aab} @ abaa')
        >>> str(g)
        'a{3}b{4}@a{bab}b{aab}@abaa'
        >>> g.next()
        >>> g.getState()
        'babaabbabbab'
        >>> g.next()
        >>> g.getState()
        'aabbabaabbabbabaabaabbabaabaabbabaab'
        >>> g.getState(values=True)
        [3.0, 3.0, 4.0, 4.0, 3.0, 4.0, 3.0, 3.0, 4.0, 4.0, 3.0, 4.0, 4.0, 3.0, 4.0, 3.0, 3.0, 4.0, 3.0, 3.0, 4.0, 4.0, 3.0, 4.0, 3.0, 3.0, 4.0, 3.0, 3.0, 4.0, 4.0, 3.0, 4.0, 3.0, 3.0, 4.0]

        >>> g = Grammar()
        >>> g.load('a{3}b{4}c{20}d{2} @ a{bab}b{acb}c{ac}d{cd} @ abd')
        >>> str(g)
        'a{3}b{4}c{20}d{2}@a{bab}c{ac}b{acb}d{cd}@abd'
        >>> g.next()
        >>> g.getState() 
        'babacbcd'
        >>> g.next()
        >>> g.getState() 
        'acbbabacbbabacacbaccd'


        >>> g = Grammar()
        >>> g.load('a{a}b{b} @ a{ab}b{a} @ b')
        >>> g.getState()
        'b'
        >>> g.next()
        >>> g.getState()
        'a'
        >>> g.next()
        >>> g.getState()
        'ab'
        >>> g.next()
        >>> g.getState()
        'aba'
        >>> g.next()
        >>> g.getState()
        'abaab'
        >>> g.next()
        >>> g.getState()
        'abaababa'

        '''
        if self._state == None or self._state == '':
            self._state = self._axiom # a copy in principle

        stateNew = self._state # copy
        matchTag = [] # matched start indices
        replacementCount = 0

        # make groups of index values, src groups to match
        # need to find largest rule in size
        indexCharPairs = []
        # self._maxRuleOutputSize

        #environment.printDebug(['next(): self._state', self._state])

        for size in range(1, self._maxRuleOutputSize+1):
        #for size in range(1, 2):
            for i in range(0, len(self._state), size):      
                pair = (i, self._state[i:i+size])
                if pair not in indexCharPairs:
                    indexCharPairs.append(pair) 

        for inRule, outRule in self._rules.items():

            environment.printDebug(['next(): in/out rule', repr(inRule), outRule])
            # grammar.py: next(): in/out rule '*' [('ab',1)] 

            #environment.printDebug(['next(): index char pairs', indexCharPairs])

            # char here may be 1 or more characters, up to max rule size
            for i, char in indexCharPairs:
            #for i, char in enumerate(self._state):

                # comparison to rule
                match = False
                # test single character, exact match
                if inRule == self.EXPRESSALL or inRule == char: 
                    match = True
                # if mutliple characters, and one is expressall, and lenght
                # is the same
                elif (len(inRule) > 1 and len(inRule) == len(char) 
                and self.EXPRESSALL in inRule):
                    matchChar = 0
                    for j in range(len(inRule)):
                        if inRule[j] == char[j] or inRule[j] == self.EXPRESSALL:
                            matchChar += 1
                    if matchChar == len(inRule):
                        match = True

                if not match:
                    continue

                # need to find cases of two or more char matches

                # store a string of index in the temp to mark positions
                # can find first instance of symbol; will not 
                # be past symbols b/c we are replacing with the old index nums
                iNew = stateNew.find(char)
                pre = stateNew[:iNew]
                # skip location
                post = stateNew[iNew+len(char):] 
                # insert marker as a random cod
                tag = self._tagIn(replacementCount)
                replacementCount += 1
                stateNew = pre + tag + post

                #environment.printDebug(['next(): stateNew', stateNew, repr(pre), repr(tag), repr(post)])
                # make a rule section

                # if only one rule, simply provide it 
                if len(outRule) == 1:
                    # a list of value, weight pairSymbol
                    replacement = outRule[0][0] 
                # if more than one rule, do a weighted selection
                else:
                    options = []
                    weights = []
                    for o, w in outRule:
                        options.append(o)
                        weights.append(w)
                    # create unit boundaries from ordered weights
                    boundary = unit.unitBoundaryProportion(weights)
                    i = unit.unitBoundaryPos(random.random(), boundary)
                    replacement = options[i] # index is in position

                # a lost of all replacements to make, between the tag and the
                # replacement
                matchTag.append([tag, replacement])

        # do all replacements
        #environment.printDebug(['stateNew: prereplace', stateNew])
        for tag, replacement in matchTag:
            # these are not actual indices but tags to points in the scratch
            iNew = stateNew.find(tag)
            pre = stateNew[:iNew]
            # skip location
            post = stateNew[iNew+len(tag):]
            # insert final value
            stateNew = pre + replacement + post

        #environment.printDebug(['stateNew: post', stateNew])

        # replace old
        self._state = stateNew