def loadProjectAndRegions(self, proj): """ This function is used to (re)initialize region objects and proj object. """ self.proj = proj self.regions = self.proj.rfi.regions # contains decomposed names if self.fastslow: self.regionCompleted_domain = strategy.Domain( "regionCompleted", proj.rfi.regions, strategy.Domain.B0_IS_MSB) self.region_domain = strategy.Domain("region", proj.rfi.regions, strategy.Domain.B0_IS_MSB) #find out mapping from new_names to old ones self.newRegionNameToOld = {} for rname, subregs in proj.regionMapping.iteritems(): for newReg in subregs: self.newRegionNameToOld[newReg] = rname # form regionList with original names self.regionList = [] for region in self.regions: self.regionList.append(self.newRegionNameToOld[region.name]) # for mapping original region name to bit encoding self.bitEncode = parseEnglishToLTL.bitEncoding( len(self.regionList), int(numpy.ceil(numpy.log2(len(self.regionList)))))
def postprocessLTL(self, text, sensorList, robotPropList): # TODO: make everything use this if self.proj.compile_options["decompose"]: # substitute decomposed region names for r in self.proj.rfi.regions: if not (r.isObstacle or r.name.lower() == "boundary"): text = re.sub('\\bs\.' + r.name + '\\b', "("+' | '.join(["s."+x for x in self.parser.proj.regionMapping[r.name]])+")", text) text = re.sub('\\be\.' + r.name + '\\b', "("+' | '.join(["e."+x for x in self.parser.proj.regionMapping[r.name]])+")", text) if self.proj.compile_options["decompose"]: regionList = [x.name for x in self.parser.proj.rfi.regions] else: regionList = [x.name for x in self.proj.rfi.regions] # Define the number of bits needed to encode the regions numBits = int(math.ceil(math.log(len(regionList),2))) # creating the region bit encoding bitEncode = bitEncoding(len(regionList),numBits) currBitEnc = bitEncode['current'] nextBitEnc = bitEncode['next'] # switch to bit encodings for regions if self.proj.compile_options["use_region_bit_encoding"]: text = replaceRegionName(text, bitEncode, regionList) text = self.substituteMacros(text) return text
def replaceIndividualSbitsToGroupSbits(self, LTLList): """ This function takes in an LTL list, extract sbits and put in the right bits afterwards """ numBits = int(math.ceil(math.log(len(self.proj.rfi.regions),2))) # TODO: only calc bitencoding once bitEncode = parseEnglishToLTL.bitEncoding(len(self.proj.rfi.regions), numBits) currBitEnc = bitEncode['current'] nextBitEnc = bitEncode['next'] envBitEnc = bitEncode['env'] envNextBitEnc = bitEncode['envNext'] newLTLList = sets.Set([]) #newLTLList = [] for LTLStr in LTLList: # LTLStr are of the form a | b | c propList = sets.Set(LTLStr.split(' | ')) # run four times propList = self.replaceIndividualSbitsToGroupSbitsForTypeOfBits(numBits, '!?next\(s.bit(?P<bitNo>\d+)\)', nextBitEnc, propList) propList = self.replaceIndividualSbitsToGroupSbitsForTypeOfBits(numBits, '!?s.bit(?P<bitNo>\d+)', currBitEnc, propList) propList = self.replaceIndividualSbitsToGroupSbitsForTypeOfBits(numBits, '!?next\(e.sbit(?P<bitNo>\d+)\)', envNextBitEnc, propList) propList = self.replaceIndividualSbitsToGroupSbitsForTypeOfBits(numBits, '!?e.sbit(?P<bitNo>\d+)', envBitEnc, propList) #newLTLList.append(' | '.join(propList)) newLTLList.add(propList) #return '&\n'.join(['('+x+')' for x in newLTLList]) return '&\n'.join(['('+' | '.join(x)+')' for x in newLTLList])
def write_cost_file(self, compiler): """ Generates *.cost file from the cost text supplied by the specification :param compiler: A parser :type compiler: SpecCompiler :return: None """ costFile = open(self._cost_filename(), 'w') # Must use bit encoding with slugs assert self.proj.compile_options["use_region_bit_encoding"] # Regular Expressions used for parsing cost spec RE_FACTOR = re.compile('\d \d (<|>)', re.IGNORECASE) RE_ENTRY = re.compile('(\\d+\\.\\d+)\\s(.*)', re.IGNORECASE) # Step through Cost Specification costText = [] for line in self.cost_text.split('\n'): # Check if First Line defines the Cost Factors for waiting and # delay costs if len(costText) == 0: if RE_FACTOR.search(line) is not None: costText.append(line) continue else: RuntimeError('The first line of the cost spec must ' + 'always represent the cost factors for' + 'waiting and delay cost.') # Split each line into the value and formula portions entryRE = RE_ENTRY.search(line) value = entryRE.group(1) formula = entryRE.group(2) # Replace region names in cost with decomposed region names formula = compiler._subDecompedRegion(formula) # Replace Formula with bit encoding regionList = compiler.getRegionList() numBits = int(math.ceil(math.log(len(regionList), 2))) bitEncode = bitEncoding(len(regionList), numBits) formula = replaceRegionName(formula, bitEncode, regionList) # Replace Input (Sensor) names formula = self._replace_input_names(formula) # Replace Output (Actuator) names formula = self._replace_output_names(formula) # Parse into SLUGS format (Postfix notation) formulaTree = parseLTL(formula + ';') formula = parseSimpleFormula(formulaTree[1], False) formula = ' '.join(formula) # Append to cost text costText.append(value + ' ' + str(formula)) # Write costText to file costFile.write("\n".join(costText))
def createTopologyFragment(adjData, regions, use_bits=True): if use_bits: numBits = int(math.ceil(math.log(len(adjData),2))) # TODO: only calc bitencoding once bitEncode = parseEnglishToLTL.bitEncoding(len(adjData), numBits) currBitEnc = bitEncode['current'] nextBitEnc = bitEncode['next'] # The topological relation (adjacency) adjFormulas = [] for Origin in range(len(adjData)): # from region i we can stay in region i adjFormula = '\t\t\t []( (' adjFormula = adjFormula + (currBitEnc[Origin] if use_bits else "s."+regions[Origin].name) adjFormula = adjFormula + ') -> ( (' adjFormula = adjFormula + (nextBitEnc[Origin] if use_bits else "next(s."+regions[Origin].name+")") adjFormula = adjFormula + ')' for dest in range(len(adjData)): if adjData[Origin][dest]: # not empty, hence there is a transition adjFormula = adjFormula + '\n\t\t\t\t\t\t\t\t\t| (' adjFormula = adjFormula + (nextBitEnc[dest] if use_bits else "next(s."+regions[dest].name+")") adjFormula = adjFormula + ') ' # closing this region adjFormula = adjFormula + ' ) ) ' adjFormulas.append(adjFormula) # In a BDD strategy, it's best to explicitly exclude these adjFormulas.append("[]"+createInitialRegionFragment(regions, use_bits)) return " & \n".join(adjFormulas)
def createInitialRegionFragment(regions, use_bits=True): # Setting the system initial formula to allow only valid # region (encoding). This may be redundant if an initial region is # specified, but it is here to ensure the system cannot start from # an invalid, or empty region (encoding). if use_bits: numBits = int(math.ceil(math.log(len(regions), 2))) # TODO: only calc bitencoding once bitEncode = parseEnglishToLTL.bitEncoding(len(regions), numBits) currBitEnc = bitEncode['current'] nextBitEnc = bitEncode['next'] initreg_formula = '\t\t\t( ' + currBitEnc[0] + ' \n' for regionInd in range(1, len(currBitEnc)): initreg_formula = initreg_formula + '\t\t\t\t | ' + currBitEnc[ regionInd] + '\n' initreg_formula = initreg_formula + '\t\t\t) \n' else: initreg_formula = "\n\t({})".format(" | ".join([ "({})".format(" & ".join([ "s." + r2.name if r is r2 else "!s." + r2.name for r2 in regions ])) for r in regions ])) return initreg_formula
def createIAInitialEnvRegionFragment(regions, use_bits=True, nextProp=False): # Setting the system initial formula to allow only valid # region (encoding). This may be redundant if an initial region is # specified, but it is here to ensure the system cannot start from # an invalid, or empty region (encoding). if use_bits: numBits = int(math.ceil(math.log(len(regions), 2))) # TODO: only calc bitencoding once bitEncode = parseEnglishToLTL.bitEncoding(len(regions), numBits) envBitEnc = bitEncode['env'] envNextBitEnc = bitEncode['envNext'] if nextProp: initreg_formula_list = [] for regionInd in range(0, len(envNextBitEnc)): initreg_formula = '( ' + envNextBitEnc[regionInd] #+ ' \n' for notRegionInd in range(0, len(envNextBitEnc)): if notRegionInd != regionInd: initreg_formula = initreg_formula + '& !' + envNextBitEnc[ notRegionInd] #+ '\n' initreg_formula = initreg_formula + ')' initreg_formula_list.append(initreg_formula) initreg_formula = '(' + '|\n'.join(initreg_formula_list) + ')' else: initreg_formula_list = [] for regionInd in range(0, len(envBitEnc)): initreg_formula = '( ' + envBitEnc[regionInd] #+ ' \n' for notRegionInd in range(0, len(envBitEnc)): if notRegionInd != regionInd: initreg_formula = initreg_formula + '& !' + envBitEnc[ notRegionInd] #+ '\n' initreg_formula = initreg_formula + ')' initreg_formula_list.append(initreg_formula) initreg_formula = '(' + '|\n'.join(initreg_formula_list) + ')' else: region_filtered = [] for r in regions: if r.name != "boundary" and not r.isObstacle: region_filtered.append(r) if nextProp: initreg_formula = "\n\t({})".format(" | ".join([ "({})".format(" & ".join([ "next(e." + r2.name + "_rc)" if r is r2 else "!next(e." + r2.name + "_rc)" for r2 in region_filtered ])) for r in region_filtered ])) else: initreg_formula = "\n\t({})".format(" | ".join([ "({})".format(" & ".join([ "e." + r2.name + "_rc" if r is r2 else "!e." + r2.name + "_rc" for r2 in region_filtered ])) for r in region_filtered ])) return initreg_formula
def createInitialEnvRegionFragment(regions, use_bits=True, nextProp=True, other_robot_name='', suffix=''): # Setting the system initial formula to allow only valid # region (encoding). This may be redundant if an initial region is # specified, but it is here to ensure the system cannot start from # an invalid, or empty region (encoding). # skip boundary and obstacles regions_old = regions regions = [] for reg in regions_old: if reg.name == 'boundary' or reg.isObstacle: continue else: regions.append(reg) if use_bits: numBits = int(math.ceil(math.log(len(regions), 2))) # TODO: only calc bitencoding once bitEncode = parseEnglishToLTL.bitEncoding(len(regions), numBits) currBitEnc = bitEncode['current'] nextBitEnc = bitEncode['next'] initreg_formula_list = [] for regionInd in range(0, len(currBitEnc)): initreg_formula = '( ' + currBitEnc[regionInd] #+ ' \n' for notRegionInd in range(0, len(currBitEnc)): if notRegionInd != regionInd: initreg_formula = initreg_formula + ' & !' + currBitEnc[ notRegionInd] #+ '\n' initreg_formula = initreg_formula + ')' initreg_formula_list.append(initreg_formula) initreg_formula = '(' + '|\n'.join(initreg_formula_list) + ')' else: if nextProp: initreg_formula = "\n\t({})".format(" |\n ".join([ "({})".format(" & ".join([ "next(e." + other_robot_name + '_' + r2.name + suffix + ')' if r is r2 else "!next(e." + other_robot_name + '_' + r2.name + suffix + ")" for r2 in regions ])) for r in regions ])) else: initreg_formula = "\n\t({})".format(" |\n ".join([ "({})".format(" & ".join([ "e." + other_robot_name + '_' + r2.name + suffix if r is r2 else "!e." + other_robot_name + '_' + r2.name + suffix for r2 in regions ])) for r in regions ])) return initreg_formula
def createTopologyFragment(adjData, regions, use_bits=True): if use_bits: numBits = int(math.ceil(math.log(len(adjData), 2))) # TODO: only calc bitencoding once bitEncode = parseEnglishToLTL.bitEncoding(len(adjData), numBits) currBitEnc = bitEncode['current'] nextBitEnc = bitEncode['next'] # The topological relation (adjacency) adjFormulas = [] for Origin in range(len(adjData)): if (regions[Origin].name == 'boundary' or regions[Origin].isObstacle): continue # from region i we can stay in region i adjFormula = '\t\t\t []( (' adjFormula = adjFormula + (currBitEnc[Origin] if use_bits else "s." + regions[Origin].name) adjFormula = adjFormula + ') -> ( (' adjFormula = adjFormula + (nextBitEnc[Origin] if use_bits else "next(s." + regions[Origin].name + ")") adjFormula = adjFormula + ')' for dest in range(len(adjData)): if (regions[dest].name == 'boundary' or regions[dest].isObstacle): continue if adjData[Origin][dest]: # not empty, hence there is a transition adjFormula = adjFormula + '\n\t\t\t\t\t\t\t\t\t| (' adjFormula = adjFormula + (nextBitEnc[dest] if use_bits else "next(s." + regions[dest].name + ")") adjFormula = adjFormula + ') ' # closing this region adjFormula = adjFormula + ' ) ) ' adjFormulas.append(adjFormula) # In a BDD strategy, it's best to explicitly exclude these adjFormulas.append("[]" + createInitialRegionFragment(regions, use_bits)) return " & \n".join(adjFormulas)
def createInitialRegionFragment(regions, use_bits=True): # Setting the system initial formula to allow only valid # region (encoding). This may be redundant if an initial region is # specified, but it is here to ensure the system cannot start from # an invalid, or empty region (encoding). if use_bits: numBits = int(math.ceil(math.log(len(regions),2))) # TODO: only calc bitencoding once bitEncode = parseEnglishToLTL.bitEncoding(len(regions), numBits) currBitEnc = bitEncode['current'] nextBitEnc = bitEncode['next'] initreg_formula = '\t\t\t( ' + currBitEnc[0] + ' \n' for regionInd in range(1,len(currBitEnc)): initreg_formula = initreg_formula + '\t\t\t\t | ' + currBitEnc[regionInd] + '\n' initreg_formula = initreg_formula + '\t\t\t) \n' else: initreg_formula = "\n\t({})".format(" | ".join(["({})".format(" & ".join(["s."+r2.name if r is r2 else "!s."+r2.name for r2 in regions])) for r in regions])) return initreg_formula
def getCurrentStateAsLTL(self, include_env=False): """ Return a boolean formula (as a string) capturing the current discrete state of the system (and, optionally, the environment as well) """ # For now, only constrain pose (based on PoseHandler) current_region_idx = self._getCurrentRegionFromPose() # TODO: support non-bitvec numBits = int(math.ceil(math.log(len(self.proj.rfi.regions), 2))) current_region_LTL = bitEncoding(len( self.proj.rfi.regions), numBits)['current'][current_region_idx] return current_region_LTL if self.aut: # If we have current state in the automaton, use it (since it can capture # state of internal propositions). return "" #return fsa.stateToLTL(self.aut.current_state, include_env=include_env) else: # If we have no automaton yet, determine our state manually # TODO: support env # TODO: look at self.proj.currentConfig.initial_truths and pose return ""
def createTopologyFragment(adjData, regions, use_bits=True): if use_bits: numBits = int(math.ceil(math.log(len(adjData), 2))) # TODO: only calc bitencoding once bitEncode = parseEnglishToLTL.bitEncoding(len(adjData), numBits) currBitEnc = bitEncode['current'] nextBitEnc = bitEncode['next'] # The topological relation (adjacency) adjFormulas = [] for Origin in range(len(adjData)): # from region i we can stay in region i adjFormula = '\t\t\t []( (' adjFormula = adjFormula + (currBitEnc[Origin] if use_bits else "s." + regions[Origin].name) adjFormula = adjFormula + ') -> ( (' adjFormula = adjFormula + (nextBitEnc[Origin] if use_bits else "next(s." + regions[Origin].name + ")") adjFormula = adjFormula + ')' for dest in range(len(adjData)): if adjData[Origin][dest]: # not empty, hence there is a transition adjFormula = adjFormula + '\n\t\t\t\t\t\t\t\t\t| (' adjFormula = adjFormula + (nextBitEnc[dest] if use_bits else "next(s." + regions[dest].name + ")") adjFormula = adjFormula + ') ' # closing this region adjFormula = adjFormula + ' ) ) ' adjFormulas.append(adjFormula) return " & \n".join(adjFormulas)
def createIAMaintainDistanceSysTopologyFragment(regionMapping, regions, adjData, use_bits=True, other_robot_names_list=[]): """ []( (next(e.other_robot_name_reg)) -> ( !(next(e.reg_rc)|() & next(s.reg)|() ))) """ # skip any boundary or obstacles regions_old = regions regions = [] for reg in regions_old: if reg.name == 'boundary' or reg.isObstacle: continue else: regions.append(reg) if use_bits: numBits = int(math.ceil(math.log(len(regions), 2))) # TODO: only calc bitencoding once bitEncode = parseEnglishToLTL.bitEncoding(len(regions), numBits) currBitEnc = bitEncode['current'] nextBitEnc = bitEncode['next'] envBitEnc = bitEncode['env'] envNextBitEnc = bitEncode['envNext'] # The topological relation (adjacency) adjFormulas = [] if not other_robot_names_list: ltlmop_logger.info('robot_name not provided!') return # retrieve only the region names regionNames = [x.name for x in regions] for robot in other_robot_names_list: for reg, subregList in regionMapping.iteritems(): reg = reg.encode('ascii', 'ignore') # skip boundary and obstacles skipRegion = False for subReg in subregList: if regions[regionNames.index(subReg)].isObstacle: skipRegion = True continue if reg == 'boundary' or reg == 'others' or skipRegion: continue toExcludeList = [] toIncludeList = [] for subReg in subregList: subRegIdx = regionNames.index(subReg) toExcludeList.append(subReg) # first find neighbour regions to exclude for destIdx in range(len(adjData)): if adjData[regionNames.index(subReg)][destIdx]: toExcludeList.append(regionNames[destIdx]) #ltlmop_logger.debug("toExcludeList:" + str(toExcludeList)) # 1st time for excludedSubReg in toExcludeList: for destIdx in range(len(adjData)): if adjData[regionNames.index( excludedSubReg )][destIdx] and regionNames[destIdx] not in toExcludeList: toIncludeList.append(regionNames[destIdx]) # 2nd time toIncludeListNew = [] for includedSubReg in toIncludeList: for destIdx in range(len(adjData)): if adjData[regionNames.index( includedSubReg)][destIdx] and regionNames[ destIdx] not in toExcludeList and regionNames[ destIdx] not in toIncludeList: toIncludeListNew.append(regionNames[destIdx]) toIncludeList = list(set(toIncludeList + toIncludeListNew)) # 3rd time toIncludeListNew = [] for includedSubReg in toIncludeList: for destIdx in range(len(adjData)): if adjData[regionNames.index( includedSubReg)][destIdx] and regionNames[ destIdx] not in toExcludeList and regionNames[ destIdx] not in toIncludeList: toIncludeListNew.append(regionNames[destIdx]) toIncludeList = list(set(toIncludeList + toIncludeListNew)) #ltlmop_logger.debug("toIncludeList:" + str(toIncludeList)) adjFormula = '\t\t\t []( next(e.'+ robot + '_' + reg + ') -> (' +\ ' ! (' + '|'.join(filter(None, list(set([envNextBitEnc[regionNames.index(x)] if use_bits else "next(s."+ x + ")" for x in toExcludeList])))) + ') & ' +\ '(' + '|'.join(filter(None, list(set([envNextBitEnc[regionNames.index(x)] if use_bits else "next(s."+ x + ")" for x in toIncludeList])))) + ') ) )' adjFormulas.append(adjFormula) return " & \n".join(adjFormulas)
def createLTLfile(fileName, sensorList, robotPropList, adjData, spec): ''' This function writes the LTL file. It encodes the specification and topological relation. It takes as input a filename, the list of the sensor propositions, the list of robot propositions (without the regions), the adjacency data (transition data structure) and a dictionary containing the specification strings. ''' fileName = fileName + '.ltl' ltlFile = open(fileName, 'w') numBits = int(numpy.ceil(numpy.log2(len(adjData)))) bitEncode = parseEnglishToLTL.bitEncoding(len(adjData), numBits) currBitEnc = bitEncode['current'] nextBitEnc = bitEncode['next'] # Write the header and begining of the formula ltlFile.write(textwrap.dedent(""" -- LTL specification file -- (Generated by the LTLMoP toolkit) """)) ltlFile.write('LTLSPEC -- Assumptions\n') ltlFile.write('\t(\n') # Write the environment assumptions # from the 'spec' input ltlFile.write(spec['EnvInit']) ltlFile.write(spec['EnvTrans']) ltlFile.write(spec['EnvGoals']) ltlFile.write('\n\t);\n\n') ltlFile.write('LTLSPEC -- Guarantees\n') ltlFile.write('\t(\n') # Write the desired robot behavior ltlFile.write(spec['SysInit']) # The topological relation (adjacency) for Origin in range(len(adjData)): # from region i we can stay in region i ltlFile.write('\t\t\t []( (') ltlFile.write(currBitEnc[Origin]) ltlFile.write(') -> ( (') ltlFile.write(nextBitEnc[Origin]) ltlFile.write(')') for dest in range(len(adjData)): if adjData[Origin][dest]: # not empty, hence there is a transition ltlFile.write('\n\t\t\t\t\t\t\t\t\t| (') ltlFile.write(nextBitEnc[dest]) ltlFile.write(') ') # closing this region ltlFile.write(' ) ) & \n ') # The rest of the spec ltlFile.write(spec['SysTrans']) ltlFile.write(spec['SysGoals']) # Close the LTL formula ltlFile.write('\n\t);\n') # close the file ltlFile.close()
def createSysMutualExclusion(regionMapping, regions, use_bits=True, other_robot_name='', use_robot_heading=False, fastslow=False): """ []( (next(e.other_robot_name_reg)) -> ( !(next(e.reg_rc)))) """ # skip any boundary or obstacles regions_old = regions regions = [] for reg in regions_old: if reg.name == 'boundary' or reg.isObstacle: continue else: regions.append(reg) if use_bits: numBits = int(math.ceil(math.log(len(regions), 2))) # TODO: only calc bitencoding once bitEncode = parseEnglishToLTL.bitEncoding(len(regions), numBits) currBitEnc = bitEncode['current'] nextBitEnc = bitEncode['next'] envBitEnc = bitEncode['env'] envNextBitEnc = bitEncode['envNext'] # The topological relation (adjacency) adjFormulas = [] if not other_robot_name: ltlmop_logger.info('robot_name not provided!') return # retrieve only the region names regionNames = [x.name for x in regions] for reg, subregList in regionMapping.iteritems(): reg = reg.encode('ascii', 'ignore') # skip boundary and obstacles skipRegion = False for subReg in subregList: if regions[regionNames.index(subReg)].isObstacle: skipRegion = True continue if reg == 'boundary' or reg == 'others' or skipRegion: continue # from region i we can stay in region i adjFormula = '\t\t\t []( (' adjFormula = adjFormula + ("next(e." + other_robot_name + '_' + reg + "_rc)" if use_robot_heading else "next(e." + other_robot_name + '_' + reg + ")") adjFormula = adjFormula + ') -> ( !(' first = True for subReg in subregList: subRegIdx = regionNames.index(subReg) if first: first = False else: adjFormula = adjFormula + '\n\t\t\t\t\t\t\t\t\t| (' if fastslow: adjFormula = adjFormula + (envNextBitEnc[subRegIdx] if use_bits else "next(e." + subReg + "_rc)") else: adjFormula = adjFormula + (nextBitEnc[subRegIdx] if use_bits else "next(s." + subReg) adjFormula = adjFormula + ')' # closing this region adjFormula = adjFormula + ' ) )' adjFormulas.append(adjFormula) return " & \n".join(adjFormulas)
def createIASysPropImpliesEnvPropLivenessFragment(sysProp, regions, envProp, adjData, use_bits=True, other_robot_name=''): """ Obtain for region: "[]<>((regionProp & regionProp_rc(envProp)) | (regionProp & !next(regionProp)))" Obtain for actions:"[]<>((actionProp & next(actionProp_ac(envProp))) | (!actionProp & !next(actionProp_ac(envProp))) | (actionProp & !next(actionProp)) | (!actionProp & next(actionProp)) )" """ # The topological relation (adjacency) adjFormulas = [] if regions: if use_bits: numBits = int(math.ceil(math.log(len(adjData), 2))) # TODO: only calc bitencoding once bitEncode = parseEnglishToLTL.bitEncoding(len(adjData), numBits) currBitEnc = bitEncode['current'] nextBitEnc = bitEncode['next'] envBitEnc = bitEncode['env'] envNextBitEnc = bitEncode['envNext'] adjFormula = '\t\t\t []<>( ' adjSubFormulaArray = [] for Origin in range(len(regions)): # skip boundary and obstacles if (regions[Origin].name == 'boundary' or regions[Origin].isObstacle): continue curProp = (currBitEnc[Origin] if use_bits else "s." + regions[Origin].name) nextProp = (nextBitEnc[Origin] if use_bits else "next(s." + regions[Origin].name + ")") # TODO: maybe pass in env region list and use it here instead nextEnvProp = (envNextBitEnc[Origin] if use_bits else "next(e." + regions[Origin].name + "_rc)") adjSubFormulaArray.append('(' + curProp + ' & ' + nextEnvProp + ') | (' + curProp + ' & !' + nextProp + ')\n') # closing the formula adjFormula = adjFormula + "\t\t\t\t| ".join(adjSubFormulaArray) adjFormula = adjFormula + ' ) ' adjFormulas.append(adjFormula) if len(sysProp): adjFormula = '\t\t\t []<>( ' adjSubFormulaArray = [] for idx in range(len(sysProp)): # from action to action completion if sysProp[idx] + "_ac" in envProp: curActProp = 's.' + sysProp[idx] nextActProp = 'next(s.' + sysProp[idx] + ')' nextEnvActProp = 'next(e.' + sysProp[idx] + '_ac)' # from region i we can stay in region i adjSubFormulaArray.append('(' + curActProp + ' & ' + nextEnvActProp + ') | (!' + curActProp + ' & !' + nextEnvActProp + ') | (' + curActProp + ' & !' + nextActProp + ') | (!' + curActProp + ' & ' + nextActProp + ')\n') # closing the formula adjFormula = adjFormula + "\t\t\t\t| ".join(adjSubFormulaArray) adjFormula = adjFormula + ' ) ' adjFormulas.append(adjFormula) if onDebugMode: ltlmop_logger.debug(" & \n".join(adjFormulas)) return " & \n".join(adjFormulas)
def createAnzuFile(fileName, sensorList, robotPropList, adjData, spec): ''' This function writes the Anzu file. It encodes the specification and topological relation. It takes as input a filename, the list of the sensor propositions, the list of robot propositions (without the regions), the adjacency data (transition data structure) and a dictionary containing the specification strings. ''' fileName = fileName + '.anzu' anzuFile = open(fileName, 'w') numBits = int(numpy.ceil(numpy.log2(len(adjData)))) bitEncode = parseEnglishToLTL.bitEncoding(len(adjData), numBits) currBitEnc = bitEncode['current'] nextBitEnc = bitEncode['next'] # Write the header and begining of the formula anzuFile.write('[INPUT_VARIABLES]\n') anzuFile.writelines( "%s;\n" % item for item in sensorList ) anzuFile.write('\n\n') anzuFile.write('[OUTPUT_VARIABLES]\n') anzuFile.writelines( "%s;\n" % item for item in robotPropList) anzuFile.write('\n\n') # Write the environment assumptions # from the 'spec' input anzuFile.write('[ENV_INITIAL]\n') anzuFile.write(reformat(spec['EnvInit'], sensorList, robotPropList, numBits)); anzuFile.write('\n\n') anzuFile.write('[ENV_TRANSITIONS]\n') anzuFile.write(reformat(spec['EnvTrans'], sensorList, robotPropList, numBits)); anzuFile.write('\n\n') anzuFile.write('[ENV_FAIRNESS]\n') anzuFile.write(reformat(spec['EnvGoals']+')', sensorList, robotPropList, numBits)) anzuFile.write(';\n\n') # Write the desired robot behavior anzuFile.write('[SYS_INITIAL]\n') anzuFile.write(reformat(spec['SysInit'], sensorList, robotPropList, numBits)); anzuFile.write('\n\n') anzuFile.write('[SYS_TRANSITIONS]\n') # The topological relation (adjacency) for Origin in range(len(adjData)): # from region i we can stay in region i anzuFile.write('\t\t\t G( (') anzuFile.write(reformat(currBitEnc[Origin], sensorList, robotPropList, numBits)) anzuFile.write(') -> ( (') anzuFile.write(reformat(nextBitEnc[Origin], sensorList, robotPropList, numBits)) anzuFile.write(')') for dest in range(len(adjData)): if adjData[Origin][dest]: # not empty, hence there is a transition anzuFile.write('\n\t\t\t\t\t\t\t\t\t+ (') anzuFile.write(reformat(nextBitEnc[dest], sensorList, robotPropList, numBits)) anzuFile.write(') ') # The rest of the system transitions anzuFile.write(reformat(spec['SysTrans'], sensorList, robotPropList, numBits)); anzuFile.write('\n\n') anzuFile.write('[SYS_FAIRNESS]\n') anzuFile.write(reformat(spec['SysGoals'].strip()+')', sensorList, robotPropList, numBits)) anzuFile.write(';\n\n') # close the file anzuFile.close()
def createEnvTopologyFragmentNoHeading(adjData, regions, use_bits=True, other_robot_names_list=''): """ other_robot_names_list: a tuple of names of robots around """ if use_bits: numBits = int(math.ceil(math.log(len(adjData), 2))) # TODO: only calc bitencoding once bitEncode = parseEnglishToLTL.bitEncoding(len(adjData), numBits) currBitEnc = bitEncode['current'] nextBitEnc = bitEncode['next'] # The topological relation (adjacency) adjFormulas = [] if not other_robot_names_list: ltlmop_logger.info('robot_name not provided!') return for other_robot_name in other_robot_names_list: for Origin in range(len(adjData)): # skip boundary and obstacles if (regions[Origin].name == 'boundary' or regions[Origin].isObstacle): continue """ Obtain []( (robotName_regionProp1_rc & robotName_regionProp2)) -> (next(robotName_regionProp1_rc)|next(robotName_regionProp2_rc)) """ # from region i we can stay in region i adjFormula = '\t\t\t []( (' adjFormula = adjFormula + (currBitEnc[Origin] if use_bits else "e." + other_robot_name + '_' + regions[Origin].name) adjFormula = adjFormula + ') -> ( (' adjFormula = adjFormula + (nextBitEnc[Origin] if use_bits else "next(e." + other_robot_name + '_' + regions[Origin].name + ")") adjFormula = adjFormula + ')' for dest in range(len(adjData)): # skip boundary and obstacles if adjData[Origin][dest] and not (regions[dest].name == 'boundary' or regions[dest].isObstacle): # not empty, hence there is a transition adjFormula = adjFormula + '\n\t\t\t\t\t\t\t\t\t| (' adjFormula = adjFormula + ( nextBitEnc[dest] if use_bits else "next(e." + other_robot_name + '_' + regions[dest].name + ")") adjFormula = adjFormula + ') ' # closing this region adjFormula = adjFormula + ' ) ) ' adjFormulas.append(adjFormula) """ Obtain [](next(robotName_regionProp1) & ! next(robotName_regionProp2))|()|() """ # In a BDD strategy, it's best to explicitly exclude these adjFormulas.append("[]" + createInitialEnvRegionFragment( regions, use_bits, True, other_robot_name)) return " & \n".join(adjFormulas)
def _writeLTLFile(self): self.LTL2SpecLineNumber = None #regionList = [r.name for r in self.parser.proj.rfi.regions] regionList = [r.name for r in self.proj.rfi.regions] sensorList = deepcopy(self.proj.enabled_sensors) robotPropList = self.proj.enabled_actuators + self.proj.all_customs text = self.proj.specText response = None # Create LTL using selected parser # TODO: rename decomposition object to something other than 'parser' if self.proj.compile_options["parser"] == "slurp": # default to no region tags if no simconfig is defined, so we can compile without if self.proj.currentConfig is None: region_tags = {} else: region_tags = self.proj.currentConfig.region_tags # Hack: We need to make sure there's only one of these global _SLURP_SPEC_GENERATOR # Make a new specgenerator and have it process the text if not _SLURP_SPEC_GENERATOR: # Add SLURP to path for import p = os.path.dirname(os.path.abspath(__file__)) sys.path.append(os.path.join(p, "..", "etc", "SLURP")) from ltlbroom.specgeneration import SpecGenerator _SLURP_SPEC_GENERATOR = SpecGenerator() # Filter out regions it shouldn't know about filtered_regions = [region.name for region in self.proj.rfi.regions if not (region.isObstacle or region.name.lower() == "boundary")] LTLspec_env, LTLspec_sys, self.proj.internal_props, internal_sensors, results, responses, traceback = \ _SLURP_SPEC_GENERATOR.generate(text, sensorList, filtered_regions, robotPropList, region_tags) oldspec_env = LTLspec_env oldspec_sys = LTLspec_sys for ln, result in enumerate(results): if not result: logging.warning("Could not parse the sentence in line {0}".format(ln)) # Abort compilation if there were any errors if not all(results): return None, None, responses # Add in the sensors so they go into the SMV and spec files for s in internal_sensors: if s not in sensorList: sensorList.append(s) self.proj.all_sensors.append(s) self.proj.enabled_sensors.append(s) # Conjoin all the spec chunks LTLspec_env = '\t\t' + ' & \n\t\t'.join(LTLspec_env) LTLspec_sys = '\t\t' + ' & \n\t\t'.join(LTLspec_sys) if self.proj.compile_options["decompose"]: # substitute decomposed region names for r in self.proj.rfi.regions: if not (r.isObstacle or r.name.lower() == "boundary"): LTLspec_env = re.sub('\\bs\.' + r.name + '\\b', "("+' | '.join(["s."+x for x in self.parser.proj.regionMapping[r.name]])+")", LTLspec_env) LTLspec_env = re.sub('\\be\.' + r.name + '\\b', "("+' | '.join(["e."+x for x in self.parser.proj.regionMapping[r.name]])+")", LTLspec_env) LTLspec_sys = re.sub('\\bs\.' + r.name + '\\b', "("+' | '.join(["s."+x for x in self.parser.proj.regionMapping[r.name]])+")", LTLspec_sys) LTLspec_sys = re.sub('\\be\.' + r.name + '\\b', "("+' | '.join(["e."+x for x in self.parser.proj.regionMapping[r.name]])+")", LTLspec_sys) response = responses elif self.proj.compile_options["parser"] == "ltl": # delete comments text = re.sub(r"#.*$", "", text, flags=re.MULTILINE) # split into env and sys parts (by looking for a line of just dashes in between) LTLspec_env, LTLspec_sys = re.split(r"^\s*-+\s*$", text, maxsplit=1, flags=re.MULTILINE) # split into subformulas LTLspec_env = re.split(r"(?:[ \t]*[\n\r][ \t]*)+", LTLspec_env) LTLspec_sys = re.split(r"(?:[ \t]*[\n\r][ \t]*)+", LTLspec_sys) # remove any empty initial entries (HACK?) while '' in LTLspec_env: LTLspec_env.remove('') while '' in LTLspec_sys: LTLspec_sys.remove('') # automatically conjoin all the subformulas LTLspec_env = '\t\t' + ' & \n\t\t'.join(LTLspec_env) LTLspec_sys = '\t\t' + ' & \n\t\t'.join(LTLspec_sys) if self.proj.compile_options["decompose"]: # substitute decomposed region for r in self.proj.rfi.regions: if not (r.isObstacle or r.name.lower() == "boundary"): LTLspec_env = re.sub('\\b(?:s\.)?' + r.name + '\\b', "("+' | '.join(["s."+x for x in self.parser.proj.regionMapping[r.name]])+")", LTLspec_env) LTLspec_sys = re.sub('\\b(?:s\.)?' + r.name + '\\b', "("+' | '.join(["s."+x for x in self.parser.proj.regionMapping[r.name]])+")", LTLspec_sys) else: for r in self.proj.rfi.regions: if not (r.isObstacle or r.name.lower() == "boundary"): LTLspec_env = re.sub('\\b(?:s\.)?' + r.name + '\\b', "s."+r.name, LTLspec_env) LTLspec_sys = re.sub('\\b(?:s\.)?' + r.name + '\\b', "s."+r.name, LTLspec_sys) traceback = [] # HACK: needs to be something other than None elif self.proj.compile_options["parser"] == "structured": import parseEnglishToLTL if self.proj.compile_options["decompose"]: # substitute the regions name in specs for m in re.finditer(r'near (?P<rA>\w+)', text): text=re.sub(r'near (?P<rA>\w+)', "("+' or '.join(["s."+r for r in self.parser.proj.regionMapping['near$'+m.group('rA')+'$'+str(50)]])+")", text) for m in re.finditer(r'within (?P<dist>\d+) (from|of) (?P<rA>\w+)', text): text=re.sub(r'within ' + m.group('dist')+' (from|of) '+ m.group('rA'), "("+' or '.join(["s."+r for r in self.parser.proj.regionMapping['near$'+m.group('rA')+'$'+m.group('dist')]])+")", text) for m in re.finditer(r'between (?P<rA>\w+) and (?P<rB>\w+)', text): text=re.sub(r'between ' + m.group('rA')+' and '+ m.group('rB'),"("+' or '.join(["s."+r for r in self.parser.proj.regionMapping['between$'+m.group('rA')+'$and$'+m.group('rB')+"$"]])+")", text) # substitute decomposed region for r in self.proj.rfi.regions: if not (r.isObstacle or r.name.lower() == "boundary"): text = re.sub('\\b' + r.name + '\\b', "("+' | '.join(["s."+x for x in self.parser.proj.regionMapping[r.name]])+")", text) regionList = ["s."+x.name for x in self.parser.proj.rfi.regions] else: for r in self.proj.rfi.regions: if not (r.isObstacle or r.name.lower() == "boundary"): text = re.sub('\\b' + r.name + '\\b', "s."+r.name, text) regionList = ["s."+x.name for x in self.proj.rfi.regions] spec, traceback, failed, self.LTL2SpecLineNumber, self.proj.internal_props = parseEnglishToLTL.writeSpec(text, sensorList, regionList, robotPropList) # Abort compilation if there were any errors if failed: return None, None, None LTLspec_env = spec["EnvInit"] + spec["EnvTrans"] + spec["EnvGoals"] LTLspec_sys = spec["SysInit"] + spec["SysTrans"] + spec["SysGoals"] else: logging.error("Parser type '{0}' not currently supported".format(self.proj.compile_options["parser"])) return None, None, None if self.proj.compile_options["decompose"]: regionList = [x.name for x in self.parser.proj.rfi.regions] else: regionList = [x.name for x in self.proj.rfi.regions] if self.proj.compile_options["use_region_bit_encoding"]: # Define the number of bits needed to encode the regions numBits = int(math.ceil(math.log(len(regionList),2))) # creating the region bit encoding bitEncode = bitEncoding(len(regionList),numBits) currBitEnc = bitEncode['current'] nextBitEnc = bitEncode['next'] # switch to bit encodings for regions LTLspec_env = replaceRegionName(LTLspec_env, bitEncode, regionList) LTLspec_sys = replaceRegionName(LTLspec_sys, bitEncode, regionList) if self.LTL2SpecLineNumber is not None: for k in self.LTL2SpecLineNumber.keys(): new_k = replaceRegionName(k, bitEncode, regionList) if new_k != k: self.LTL2SpecLineNumber[new_k] = self.LTL2SpecLineNumber[k] del self.LTL2SpecLineNumber[k] if self.proj.compile_options["decompose"]: adjData = self.parser.proj.rfi.transitions else: adjData = self.proj.rfi.transitions # Store some data needed for later analysis self.spec = {} if self.proj.compile_options["decompose"]: self.spec['Topo'] = createTopologyFragment(adjData, self.parser.proj.rfi.regions, use_bits=self.proj.compile_options["use_region_bit_encoding"]) else: self.spec['Topo'] = createTopologyFragment(adjData, self.proj.rfi.regions, use_bits=self.proj.compile_options["use_region_bit_encoding"]) # Substitute any macros that the parsers passed us LTLspec_env = self.substituteMacros(LTLspec_env) LTLspec_sys = self.substituteMacros(LTLspec_sys) # If we are not using bit-encoding, we need to # explicitly encode a mutex for regions if not self.proj.compile_options["use_region_bit_encoding"]: # DNF version (extremely slow for core-finding) #mutex = "\n\t&\n\t []({})".format(" | ".join(["({})".format(" & ".join(["s."+r2.name if r is r2 else "!s."+r2.name for r2 in self.parser.proj.rfi.regions])) for r in self.parser.proj.rfi.regions])) if self.proj.compile_options["decompose"]: region_list = self.parser.proj.rfi.regions else: region_list = self.proj.rfi.regions # Almost-CNF version exclusions = [] for i, r1 in enumerate(region_list): for r2 in region_list[i+1:]: exclusions.append("!(s.{} & s.{})".format(r1.name, r2.name)) mutex = "\n&\n\t []({})".format(" & ".join(exclusions)) LTLspec_sys += mutex self.spec.update(self.splitSpecIntoComponents(LTLspec_env, LTLspec_sys)) # Add in a fragment to make sure that we start in a valid region if self.proj.compile_options["decompose"]: self.spec['InitRegionSanityCheck'] = createInitialRegionFragment(self.parser.proj.rfi.regions, use_bits=self.proj.compile_options["use_region_bit_encoding"]) else: self.spec['InitRegionSanityCheck'] = createInitialRegionFragment(self.proj.rfi.regions, use_bits=self.proj.compile_options["use_region_bit_encoding"]) LTLspec_sys += "\n&\n" + self.spec['InitRegionSanityCheck'] LTLspec_sys += "\n&\n" + self.spec['Topo'] createLTLfile(self.proj.getFilenamePrefix(), LTLspec_env, LTLspec_sys) if self.proj.compile_options["parser"] == "slurp": self.reversemapping = {self.postprocessLTL(line,sensorList,robotPropList).strip():line.strip() for line in oldspec_env + oldspec_sys} self.reversemapping[self.spec['Topo'].replace("\n","").replace("\t","").lstrip().rstrip("\n\t &")] = "TOPOLOGY" #for k,v in self.reversemapping.iteritems(): # print "{!r}:{!r}".format(k,v) return self.spec, traceback, response
def createAnzuFile(fileName, sensorList, robotPropList, adjData, spec): ''' This function writes the Anzu file. It encodes the specification and topological relation. It takes as input a filename, the list of the sensor propositions, the list of robot propositions (without the regions), the adjacency data (transition data structure) and a dictionary containing the specification strings. ''' fileName = fileName + '.anzu' anzuFile = open(fileName, 'w') numBits = int(numpy.ceil(numpy.log2(len(adjData)))) bitEncode = parseEnglishToLTL.bitEncoding(len(adjData), numBits) currBitEnc = bitEncode['current'] nextBitEnc = bitEncode['next'] # Write the header and begining of the formula anzuFile.write('[INPUT_VARIABLES]\n') anzuFile.writelines("%s;\n" % item for item in sensorList) anzuFile.write('\n\n') anzuFile.write('[OUTPUT_VARIABLES]\n') anzuFile.writelines("%s;\n" % item for item in robotPropList) anzuFile.write('\n\n') # Write the environment assumptions # from the 'spec' input anzuFile.write('[ENV_INITIAL]\n') anzuFile.write( reformat(spec['EnvInit'], sensorList, robotPropList, numBits)) anzuFile.write('\n\n') anzuFile.write('[ENV_TRANSITIONS]\n') anzuFile.write( reformat(spec['EnvTrans'], sensorList, robotPropList, numBits)) anzuFile.write('\n\n') anzuFile.write('[ENV_FAIRNESS]\n') anzuFile.write( reformat(spec['EnvGoals'] + ')', sensorList, robotPropList, numBits)) anzuFile.write(';\n\n') # Write the desired robot behavior anzuFile.write('[SYS_INITIAL]\n') anzuFile.write( reformat(spec['SysInit'], sensorList, robotPropList, numBits)) anzuFile.write('\n\n') anzuFile.write('[SYS_TRANSITIONS]\n') # The topological relation (adjacency) for Origin in range(len(adjData)): # from region i we can stay in region i anzuFile.write('\t\t\t G( (') anzuFile.write( reformat(currBitEnc[Origin], sensorList, robotPropList, numBits)) anzuFile.write(') -> ( (') anzuFile.write( reformat(nextBitEnc[Origin], sensorList, robotPropList, numBits)) anzuFile.write(')') for dest in range(len(adjData)): if adjData[Origin][dest]: # not empty, hence there is a transition anzuFile.write('\n\t\t\t\t\t\t\t\t\t+ (') anzuFile.write( reformat(nextBitEnc[dest], sensorList, robotPropList, numBits)) anzuFile.write(') ') # The rest of the system transitions anzuFile.write( reformat(spec['SysTrans'], sensorList, robotPropList, numBits)) anzuFile.write('\n\n') anzuFile.write('[SYS_FAIRNESS]\n') anzuFile.write( reformat(spec['SysGoals'].strip() + ')', sensorList, robotPropList, numBits)) anzuFile.write(';\n\n') # close the file anzuFile.close()
def createLTLfile(fileName, sensorList, robotPropList, adjData, spec): ''' This function writes the LTL file. It encodes the specification and topological relation. It takes as input a filename, the list of the sensor propositions, the list of robot propositions (without the regions), the adjacency data (transition data structure) and a dictionary containing the specification strings. ''' fileName = fileName + '.ltl' ltlFile = open(fileName, 'w') numBits = int(numpy.ceil(numpy.log2(len(adjData)))) bitEncode = parseEnglishToLTL.bitEncoding(len(adjData), numBits) currBitEnc = bitEncode['current'] nextBitEnc = bitEncode['next'] # Write the header and begining of the formula ltlFile.write( textwrap.dedent(""" -- LTL specification file -- (Generated by the LTLMoP toolkit) """)) ltlFile.write('LTLSPEC -- Assumptions\n') ltlFile.write('\t(\n') # Write the environment assumptions # from the 'spec' input ltlFile.write(spec['EnvInit']) ltlFile.write(spec['EnvTrans']) ltlFile.write(spec['EnvGoals']) ltlFile.write('\n\t);\n\n') ltlFile.write('LTLSPEC -- Guarantees\n') ltlFile.write('\t(\n') # Write the desired robot behavior ltlFile.write(spec['SysInit']) # The topological relation (adjacency) for Origin in range(len(adjData)): # from region i we can stay in region i ltlFile.write('\t\t\t []( (') ltlFile.write(currBitEnc[Origin]) ltlFile.write(') -> ( (') ltlFile.write(nextBitEnc[Origin]) ltlFile.write(')') for dest in range(len(adjData)): if adjData[Origin][dest]: # not empty, hence there is a transition ltlFile.write('\n\t\t\t\t\t\t\t\t\t| (') ltlFile.write(nextBitEnc[dest]) ltlFile.write(') ') # closing this region ltlFile.write(' ) ) & \n ') # The rest of the spec ltlFile.write(spec['SysTrans']) ltlFile.write(spec['SysGoals']) # Close the LTL formula ltlFile.write('\n\t);\n') # close the file ltlFile.close()
def createIASysTopologyFragment(adjData, regions, use_bits=True): """ Obtain []( next(regionProp1_rc) -> (next(regionProp1) | next(regionProp2)) """ if use_bits: numBits = int(math.ceil(math.log(len(adjData), 2))) # TODO: only calc bitencoding once bitEncode = parseEnglishToLTL.bitEncoding(len(adjData), numBits) currBitEnc = bitEncode['current'] nextBitEnc = bitEncode['next'] envBitEnc = bitEncode['env'] envNextBitEnc = bitEncode['envNext'] # The topological relation (adjacency) adjFormulas = [] for Origin in range(len(adjData)): if (regions[Origin].name == 'boundary' or regions[Origin].isObstacle): continue # from region i we can stay in region i adjFormula = '\t\t\t []( (' adjFormula = adjFormula + (envNextBitEnc[Origin] if use_bits else "next(e." + regions[Origin].name + "_rc)") adjFormula = adjFormula + ') -> ( (' adjFormula = adjFormula + (nextBitEnc[Origin] if use_bits else "next(s." + regions[Origin].name + ")") adjFormula = adjFormula + ')' for dest in range(len(adjData)): if regions[dest].name == 'boundary' or regions[dest].isObstacle: continue if adjData[Origin][dest]: # not empty, hence there is a transition adjFormula = adjFormula + ' | (' adjFormula = adjFormula + (nextBitEnc[dest] if use_bits else "next(s." + regions[dest].name + ")") adjFormula = adjFormula + ') ' # closing this region adjFormula = adjFormula + ' ) ) ' adjFormulas.append(adjFormula) if onDebugMode: ltlmop_logger.debug( "[]( next(regionProp1_rc) -> (next(regionProp1) | next(regionProp2))" ) ltlmop_logger.debug(adjFormulas) """ []regionProp1' -> ! (regionProp2' | regionProp3' | regionProp4') """ for Origin in range(len(adjData)): if regions[Origin].name == 'boundary' or regions[Origin].isObstacle: continue # from region i we can stay in region i adjFormula = '\t\t\t []( (' adjFormula = adjFormula + (nextBitEnc[Origin] if use_bits else "next(s." + regions[Origin].name + ")") adjFormula = adjFormula + ') -> ! ( ' regProps = [] for Others in range(len(adjData)): if regions[Others].name == 'boundary' or regions[Others].isObstacle: continue if not regions[Origin].name == regions[Others].name: # not empty, hence there is a transition regProps.append(nextBitEnc[Others] if use_bits else "next(s." + regions[Others].name + ")") adjFormula = adjFormula + " | ".join(regProps) # closing this region adjFormula = adjFormula + ' ) ) ' adjFormulas.append(adjFormula) if onDebugMode: ltlmop_logger.debug( "[]regionProp1' -> ! (regionProp2' | regionProp3' | regionProp4')") ltlmop_logger.debug(adjFormula) """ [] regionProp1' | regionProp2' | regionProp3' """ # In a BDD strategy, it's best to explicitly exclude these adjFormulas.append("[]" + createInitialRegionFragment(regions, use_bits)) if onDebugMode: ltlmop_logger.debug("[] regionProp1' | regionProp2' | regionProp3'") ltlmop_logger.debug("[]" + createInitialRegionFragment(regions, use_bits)) return " & \n".join(adjFormulas)
def _writeLTLFile(self): self.LTL2SpecLineNumber = None #regionList = [r.name for r in self.parser.proj.rfi.regions] regionList = [r.name for r in self.proj.rfi.regions] sensorList = deepcopy(self.proj.enabled_sensors) robotPropList = self.proj.enabled_actuators + self.proj.all_customs text = self.proj.specText response = None # Create LTL using selected parser # TODO: rename decomposition object to something other than 'parser' if self.proj.compile_options["parser"] == "slurp": # default to no region tags if no simconfig is defined, so we can compile without if self.proj.current_config == "": region_tags = {} else: self.hsub = handlerSubsystem.HandlerSubsystem( None, self.proj.project_root) config, success = self.hsub.loadConfigFile( self.proj.current_config) if success: self.hsub.configs.append(config) self.hsub.setExecutingConfig(self.proj.current_config) region_tags = self.hsub.executing_config.region_tags # Hack: We need to make sure there's only one of these global _SLURP_SPEC_GENERATOR # Make a new specgenerator and have it process the text if not _SLURP_SPEC_GENERATOR: # Add SLURP to path for import p = os.path.dirname(os.path.abspath(__file__)) sys.path.append(os.path.join(p, "..", "etc", "SLURP")) from ltlbroom.specgeneration import SpecGenerator _SLURP_SPEC_GENERATOR = SpecGenerator() # Filter out regions it shouldn't know about filtered_regions = [ region.name for region in self.proj.rfi.regions if not (region.isObstacle or region.name.lower() == "boundary") ] LTLspec_env, LTLspec_sys, self.proj.internal_props, internal_sensors, results, responses, traceback = \ _SLURP_SPEC_GENERATOR.generate(text, sensorList, filtered_regions, robotPropList, region_tags) oldspec_env = LTLspec_env oldspec_sys = LTLspec_sys for ln, result in enumerate(results): if not result: logging.warning( "Could not parse the sentence in line {0}".format(ln)) # Abort compilation if there were any errors if not all(results): return None, None, responses # Add in the sensors so they go into the SMV and spec files for s in internal_sensors: if s not in sensorList: sensorList.append(s) self.proj.all_sensors.append(s) self.proj.enabled_sensors.append(s) # Conjoin all the spec chunks LTLspec_env = '\t\t' + ' & \n\t\t'.join(LTLspec_env) LTLspec_sys = '\t\t' + ' & \n\t\t'.join(LTLspec_sys) if self.proj.compile_options["decompose"]: # substitute decomposed region names for r in self.proj.rfi.regions: if not (r.isObstacle or r.name.lower() == "boundary"): LTLspec_env = re.sub( '\\bs\.' + r.name + '\\b', "(" + ' | '.join([ "s." + x for x in self.parser.proj.regionMapping[r.name] ]) + ")", LTLspec_env) LTLspec_env = re.sub( '\\be\.' + r.name + '\\b', "(" + ' | '.join([ "e." + x for x in self.parser.proj.regionMapping[r.name] ]) + ")", LTLspec_env) LTLspec_sys = re.sub( '\\bs\.' + r.name + '\\b', "(" + ' | '.join([ "s." + x for x in self.parser.proj.regionMapping[r.name] ]) + ")", LTLspec_sys) LTLspec_sys = re.sub( '\\be\.' + r.name + '\\b', "(" + ' | '.join([ "e." + x for x in self.parser.proj.regionMapping[r.name] ]) + ")", LTLspec_sys) response = responses elif self.proj.compile_options["parser"] == "ltl": # delete comments text = re.sub(r"#.*$", "", text, flags=re.MULTILINE) # split into env and sys parts (by looking for a line of just dashes in between) LTLspec_env, LTLspec_sys = re.split(r"^\s*-+\s*$", text, maxsplit=1, flags=re.MULTILINE) # split into subformulas LTLspec_env = re.split(r"(?:[ \t]*[\n\r][ \t]*)+", LTLspec_env) LTLspec_sys = re.split(r"(?:[ \t]*[\n\r][ \t]*)+", LTLspec_sys) # remove any empty initial entries (HACK?) while '' in LTLspec_env: LTLspec_env.remove('') while '' in LTLspec_sys: LTLspec_sys.remove('') # automatically conjoin all the subformulas LTLspec_env = '\t\t' + ' & \n\t\t'.join(LTLspec_env) LTLspec_sys = '\t\t' + ' & \n\t\t'.join(LTLspec_sys) if self.proj.compile_options["decompose"]: # substitute decomposed region for r in self.proj.rfi.regions: if not (r.isObstacle or r.name.lower() == "boundary"): LTLspec_env = re.sub( '\\b(?:s\.)?' + r.name + '\\b', "(" + ' | '.join([ "s." + x for x in self.parser.proj.regionMapping[r.name] ]) + ")", LTLspec_env) LTLspec_sys = re.sub( '\\b(?:s\.)?' + r.name + '\\b', "(" + ' | '.join([ "s." + x for x in self.parser.proj.regionMapping[r.name] ]) + ")", LTLspec_sys) else: for r in self.proj.rfi.regions: if not (r.isObstacle or r.name.lower() == "boundary"): LTLspec_env = re.sub('\\b(?:s\.)?' + r.name + '\\b', "s." + r.name, LTLspec_env) LTLspec_sys = re.sub('\\b(?:s\.)?' + r.name + '\\b', "s." + r.name, LTLspec_sys) traceback = [] # HACK: needs to be something other than None elif self.proj.compile_options["parser"] == "structured": import parseEnglishToLTL if self.proj.compile_options["decompose"]: # substitute the regions name in specs for m in re.finditer(r'near (?P<rA>\w+)', text): text = re.sub( r'near (?P<rA>\w+)', "(" + ' or '.join([ "s." + r for r in self.parser.proj.regionMapping[ 'near$' + m.group('rA') + '$' + str(50)] ]) + ")", text) for m in re.finditer( r'within (?P<dist>\d+) (from|of) (?P<rA>\w+)', text): text = re.sub( r'within ' + m.group('dist') + ' (from|of) ' + m.group('rA'), "(" + ' or '.join([ "s." + r for r in self.parser.proj.regionMapping[ 'near$' + m.group('rA') + '$' + m.group('dist')] ]) + ")", text) for m in re.finditer(r'between (?P<rA>\w+) and (?P<rB>\w+)', text): text = re.sub( r'between ' + m.group('rA') + ' and ' + m.group('rB'), "(" + ' or '.join([ "s." + r for r in self.parser.proj.regionMapping[ 'between$' + m.group('rA') + '$and$' + m.group('rB') + "$"] ]) + ")", text) # substitute decomposed region for r in self.proj.rfi.regions: if not (r.isObstacle or r.name.lower() == "boundary"): text = re.sub( '\\b' + r.name + '\\b', "(" + ' | '.join([ "s." + x for x in self.parser.proj.regionMapping[r.name] ]) + ")", text) regionList = [ "s." + x.name for x in self.parser.proj.rfi.regions ] else: for r in self.proj.rfi.regions: if not (r.isObstacle or r.name.lower() == "boundary"): text = re.sub('\\b' + r.name + '\\b', "s." + r.name, text) regionList = ["s." + x.name for x in self.proj.rfi.regions] spec, traceback, failed, self.LTL2SpecLineNumber, self.proj.internal_props = parseEnglishToLTL.writeSpec( text, sensorList, regionList, robotPropList) # Abort compilation if there were any errors if failed: return None, None, None LTLspec_env = spec["EnvInit"] + spec["EnvTrans"] + spec["EnvGoals"] LTLspec_sys = spec["SysInit"] + spec["SysTrans"] + spec["SysGoals"] else: logging.error("Parser type '{0}' not currently supported".format( self.proj.compile_options["parser"])) return None, None, None if self.proj.compile_options["decompose"]: regionList = [x.name for x in self.parser.proj.rfi.regions] else: regionList = [x.name for x in self.proj.rfi.regions] if self.proj.compile_options["use_region_bit_encoding"]: # Define the number of bits needed to encode the regions numBits = int(math.ceil(math.log(len(regionList), 2))) # creating the region bit encoding bitEncode = bitEncoding(len(regionList), numBits) currBitEnc = bitEncode['current'] nextBitEnc = bitEncode['next'] # switch to bit encodings for regions LTLspec_env = replaceRegionName(LTLspec_env, bitEncode, regionList) LTLspec_sys = replaceRegionName(LTLspec_sys, bitEncode, regionList) if self.LTL2SpecLineNumber is not None: for k in self.LTL2SpecLineNumber.keys(): new_k = replaceRegionName(k, bitEncode, regionList) if new_k != k: self.LTL2SpecLineNumber[ new_k] = self.LTL2SpecLineNumber[k] del self.LTL2SpecLineNumber[k] if self.proj.compile_options["decompose"]: adjData = self.parser.proj.rfi.transitions else: adjData = self.proj.rfi.transitions # Store some data needed for later analysis self.spec = {} if self.proj.compile_options["decompose"]: self.spec['Topo'] = createTopologyFragment( adjData, self.parser.proj.rfi.regions, use_bits=self.proj.compile_options["use_region_bit_encoding"]) else: self.spec['Topo'] = createTopologyFragment( adjData, self.proj.rfi.regions, use_bits=self.proj.compile_options["use_region_bit_encoding"]) # Substitute any macros that the parsers passed us LTLspec_env = self.substituteMacros(LTLspec_env) LTLspec_sys = self.substituteMacros(LTLspec_sys) # If we are not using bit-encoding, we need to # explicitly encode a mutex for regions if not self.proj.compile_options["use_region_bit_encoding"]: # DNF version (extremely slow for core-finding) #mutex = "\n\t&\n\t []({})".format(" | ".join(["({})".format(" & ".join(["s."+r2.name if r is r2 else "!s."+r2.name for r2 in self.parser.proj.rfi.regions])) for r in self.parser.proj.rfi.regions])) if self.proj.compile_options["decompose"]: region_list = self.parser.proj.rfi.regions else: region_list = self.proj.rfi.regions # Almost-CNF version exclusions = [] for i, r1 in enumerate(region_list): for r2 in region_list[i + 1:]: exclusions.append("!(s.{} & s.{})".format( r1.name, r2.name)) mutex = "\n&\n\t []({})".format(" & ".join(exclusions)) LTLspec_sys += mutex self.spec.update(self.splitSpecIntoComponents(LTLspec_env, LTLspec_sys)) # Add in a fragment to make sure that we start in a valid region if self.proj.compile_options["decompose"]: self.spec['InitRegionSanityCheck'] = createInitialRegionFragment( self.parser.proj.rfi.regions, use_bits=self.proj.compile_options["use_region_bit_encoding"]) else: self.spec['InitRegionSanityCheck'] = createInitialRegionFragment( self.proj.rfi.regions, use_bits=self.proj.compile_options["use_region_bit_encoding"]) LTLspec_sys += "\n&\n" + self.spec['InitRegionSanityCheck'] LTLspec_sys += "\n&\n" + self.spec['Topo'] createLTLfile(self.proj.getFilenamePrefix(), LTLspec_env, LTLspec_sys) if self.proj.compile_options["parser"] == "slurp": self.reversemapping = { self.postprocessLTL(line, sensorList, robotPropList).strip(): line.strip() for line in oldspec_env + oldspec_sys } self.reversemapping[self.spec['Topo'].replace("\n", "").replace( "\t", "").lstrip().rstrip("\n\t &")] = "TOPOLOGY" #for k,v in self.reversemapping.iteritems(): # print "{!r}:{!r}".format(k,v) return self.spec, traceback, response
def createIAEnvTopologyFragment(adjData, regions, actuatorList, use_bits=True): """ Obtain []( (regionProp1_rc & regionProp1) -> (next(regionProp1_rc))) """ # The topological relation (adjacency) adjFormulas = [] if regions: if use_bits: numBits = int(math.ceil(math.log(len(adjData), 2))) # TODO: only calc bitencoding once bitEncode = parseEnglishToLTL.bitEncoding(len(adjData), numBits) currBitEnc = bitEncode['current'] nextBitEnc = bitEncode['next'] envBitEnc = bitEncode['env'] envNextBitEnc = bitEncode['envNext'] for Origin in range(len(adjData)): if regions[Origin].name == 'boundary' or regions[Origin].isObstacle: continue # from region i we can stay in region i adjFormula = '\t\t\t []( (' adjFormula = adjFormula + (envBitEnc[Origin] if use_bits else "e." + regions[Origin].name + "_rc") adjFormula = adjFormula + ' & ' adjFormula = adjFormula + (currBitEnc[Origin] if use_bits else "s." + regions[Origin].name) adjFormula = adjFormula + ') -> (' adjFormula = adjFormula + (envNextBitEnc[Origin] if use_bits else "next(e." + regions[Origin].name + "_rc)") adjFormula = adjFormula + ") ) " adjFormulas.append(adjFormula) if onDebugMode: ltlmop_logger.debug( "[]( (regionProp1_rc & regionProp1) -> (next(regionProp1_rc)))" ) ltlmop_logger.debug(adjFormulas) """ Obtain []( (regionProp1_rc & regionProp2)) -> (next(regionProp1_rc)|next(regionProp2_rc)) """ for Origin in range(len(adjData)): if regions[Origin].name == 'boundary' or regions[Origin].isObstacle: continue for dest in range(len(adjData)): if regions[dest].name == 'boundary' or regions[dest].isObstacle: continue if adjData[Origin][dest]: # from region i we can head to dest stay in Origin adjFormula = '\t\t\t []( (' adjFormula = adjFormula + (envBitEnc[Origin] if use_bits else "e." + regions[Origin].name + "_rc") adjFormula = adjFormula + ' & ' adjFormula = adjFormula + (currBitEnc[dest] if use_bits else "s." + regions[dest].name) adjFormula = adjFormula + ') -> (' # still in the current region adjFormula = adjFormula + (envNextBitEnc[Origin] if use_bits else "next(e." + regions[Origin].name + "_rc)") # not empty, hence there is a transition adjFormula = adjFormula + ' | ' adjFormula = adjFormula + (envNextBitEnc[dest] if use_bits else "next(e." + regions[dest].name + "_rc)") adjFormula = adjFormula + ') )' adjFormulas.append(adjFormula) if onDebugMode: ltlmop_logger.debug( "[]( (regionProp1_rc & regionProp2)) -> (next(regionProp1_rc)|next(regionProp2_rc))" ) ltlmop_logger.debug(adjFormula) """ []regionProp1_rc' <-> ! (regionProp2_rc' | regionProp3_rc' | regionProp4_rc') """ for Origin in range(len(adjData)): if (regions[Origin].name == 'boundary' or regions[Origin].isObstacle): continue # from region i we can stay in region i adjFormula = '\t\t\t []( (' adjFormula = adjFormula + (envNextBitEnc[Origin] if use_bits else "next(e." + regions[Origin].name + "_rc)") adjFormula = adjFormula + ') <-> ! ( ' regPropRCs = [] for Others in range(len(adjData)): if regions[Others].name == 'boundary' or regions[ Others].isObstacle: continue if not regions[Origin].name == regions[Others].name: # not empty, hence there is a transition regPropRCs.append( envNextBitEnc[Others] if use_bits else "next(e." + regions[Others].name + "_rc)") adjFormula = adjFormula + " | ".join(regPropRCs) adjFormula = adjFormula + ") ) " adjFormulas.append(adjFormula) if onDebugMode: ltlmop_logger.debug( "[]regionProp1_rc' -> ! (regionProp2_rc' | regionProp3_rc' | regionProp4_rc')" ) ltlmop_logger.debug(adjFormula) """ [] regionProp1' | regionProp2' | regionProp3' """ if regions: # In a BDD strategy, it's best to explicitly exclude these adjFormulas.append( "\t\t\t []" + createIAInitialEnvRegionFragment(regions, use_bits, True)) if onDebugMode: ltlmop_logger.debug( "[] regionProp1' | regionProp2' | regionProp3'") ltlmop_logger.debug( "[]" + createIAInitialEnvRegionFragment(regions, use_bits, True)) return " & \n".join(adjFormulas)