def _autIsNonTrivial(self): """ Check for a) empty automaton, or b) trivial initial-state automaton with no transitions (This can indicate unsatisfiable system initial conditions (case a), or an unsat environment (case b).) TODO: Do this in the Java code; it's super inefficient to load the whole aut just to check this. """ proj_copy = deepcopy(self.proj) proj_copy.rfi = self.parser.proj.rfi proj_copy.sensor_handler = None proj_copy.actuator_handler = None proj_copy.h_instance = None aut = fsa.Automaton(proj_copy) aut.loadFile(self.proj.getFilenamePrefix() + ".aut", self.proj.enabled_sensors, self.proj.enabled_actuators, self.proj.all_customs) nonTrivial = any([len(s.transitions) > 0 for s in aut.states]) return nonTrivial
def loadAutFile(self, filename): logging.info("Loading automaton...") aut = fsa.Automaton(self.proj) success = aut.loadFile( filename, self.proj.enabled_sensors, self.proj.enabled_actuators, self.proj.all_customs + self.proj.internal_props) return aut if success else None
def _coreFinding(self, to_highlight, unsat, badInit): #returns list of formulas that cause unsatisfiability/unrealizability (based on unsat flag). #takes as input sentences marked for highlighting, and formula describing bad initial states #from JTLV. #find number of states in automaton/counter for unsat/unreal core max unrolling depth ("recurrence diameter") proj_copy = deepcopy(self.proj) proj_copy.rfi = self.parser.proj.rfi proj_copy.sensor_handler = None proj_copy.actuator_handler = None proj_copy.h_instance = None num_bits = int( numpy.ceil(numpy.log2(len(self.parser.proj.rfi.regions))) ) # Number of bits necessary to encode all regions region_props = ["bit" + str(n) for n in xrange(num_bits)] aut = fsa.Automaton(proj_copy) aut.loadFile( self.proj.getFilenamePrefix() + ".aut", self.proj.enabled_actuators + self.proj.all_customs + region_props, self.proj.enabled_sensors, []) #find deadlocked states in the automaton (states with no out-transitions) deadStates = [s for s in aut.states if not s.transitions] #find states that can be forced by the environment into the deadlocked set forceDeadStates = [(s, e) for s in aut.states for e in deadStates if e in s.transitions] #LTL representation of these states and the deadlock-causing environment move in the next time step forceDeadlockLTL = map( lambda (s, e): " & ".join([stateToLTL(s), stateToLTL(e, 1, 1, True)]), forceDeadStates) #find livelocked goal and corresponding one-step propositional formula (by stripping LTL operators) desiredGoal = [ h_item[2] for h_item in to_highlight if h_item[1] == "goals" ] if desiredGoal: desiredGoal = desiredGoal[0] #Don't actually need LTL #desiredGoalLTL = stripLTLLine(self.ltlConjunctsFromBadLines([h_item for h_item in to_highlight if h_item[1] == "goals"], False)[0],True) def preventsDesiredGoal(s): rank_str = s.transitions[0].rank m = re.search(r"\(\d+,(-?\d+)\)", rank_str) if m is None: logging.error( "Error parsing jx in automaton. Are you sure the spec is unrealizable?" ) return jx = int(m.group(1)) return (jx == desiredGoal) #find livelocked states in the automaton (states with desired sys rank) livelockedStates = filter(preventsDesiredGoal, [s for s in aut.states if s.transitions]) #find states that can be forced by the environment into the livelocked set forceLivelockedStates = [(fro, to) for fro in aut.states for to in livelockedStates if to in s.transitions] #LTL representation of these states and the livelocked goal #forceLivelockLTL = map(lambda s: " & ".join([stateToLTL(s), desiredGoalLTL]), livelockedStates) ###Don't actually need to add goal -- will be added in 'conjuncts' forceLivelockLTL = map( lambda (s1, s2): " & ".join( [stateToLTL(s1, 1, 1), stateToLTL(s2, 1, 0, True)]), forceLivelockedStates) #forceLivelockLTL = map(stateToLTL, livelockedStates) numStates = len(aut.states) numRegions = len(self.parser.proj.rfi.regions) if forceDeadlockLTL: deadlockFlag = True badStatesLTL = forceDeadlockLTL else: #this means livelock deadlockFlag = False badStatesLTL = forceLivelockLTL ################################# # # # get conjuncts to be minimized # # # ################################# #topology topo = self.spec['Topo'].replace('\n', '') topo = topo.replace('\t', '') #have to use all initial conditions if no single bad initial state given useInitFlag = badInit is None #other highlighted LTL formulas conjuncts = self.ltlConjunctsFromBadLines(to_highlight, useInitFlag) #filter out props that are actually used #self.propList = [p for p in self.propList if [c for c in conjuncts if p in c] or [c for c in badStatesLTL if p in c and not unsat] or p in topo] cmd = self._getPicosatCommand() if unsat: guilty = self.unsatCores(cmd, topo, badInit, conjuncts, 15, 15) #returns LTL else: guilty = self.unrealCores(cmd, topo, badStatesLTL, conjuncts, deadlockFlag) #returns LTL return guilty
def __init__(self, *args, **kwds): # begin wxGlade: MopsyFrame.__init__ kwds["style"] = wx.DEFAULT_FRAME_STYLE wx.Frame.__init__(self, *args, **kwds) self.window_1 = wx.SplitterWindow(self, -1, style=wx.SP_3D | wx.SP_BORDER) self.window_1_pane_2 = wx.Panel(self.window_1, -1) self.window_1_pane_1 = wx.Panel(self.window_1, -1) self.mopsy_frame_statusbar = self.CreateStatusBar(1, 0) self.history_grid = wx.grid.Grid(self.window_1_pane_1, -1, size=(1, 1)) self.panel_1 = wx.Panel(self.window_1_pane_2, -1, style=wx.SUNKEN_BORDER | wx.TAB_TRAVERSAL) self.label_5 = wx.StaticText(self.window_1_pane_2, -1, "Current environment state:") self.label_6 = wx.StaticText(self.window_1_pane_2, -1, "Please choose your response:") self.label_movingto = wx.StaticText(self.window_1_pane_2, -1, "Moving to XXX ...") self.label_8 = wx.StaticText(self.window_1_pane_2, -1, "Actuator states:") self.label_9 = wx.StaticText(self.window_1_pane_2, -1, "Internal propositions:") self.label_violation = wx.StaticText(self.window_1_pane_2, -1, "") self.button_next = wx.Button(self.window_1_pane_2, -1, "Execute Move >>") self.__set_properties() self.__do_layout() self.Bind(wx.EVT_BUTTON, self.onButtonNext, self.button_next) # end wxGlade self.dest_region = None self.current_region = None self.regionsToHide = [] self.actuatorStates = {} self.sensorStates = {} self.panel_1.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) self.mapBitmap = None # Load in the project and map self.proj = project.Project() self.proj.loadProject(sys.argv[1]) self.proj.rfi = self.proj.loadRegionFile(decomposed=True) self.Bind(wx.EVT_SIZE, self.onResize, self) self.panel_1.Bind(wx.EVT_PAINT, self.onPaint) self.panel_1.Bind(wx.EVT_ERASE_BACKGROUND, self.onEraseBG) self.panel_1.Bind(wx.EVT_LEFT_DOWN, self.onMapClick) self.onResize() self.proj.determineEnabledPropositions() # Load in automatons self.sysDummySensorHandler = SysDummySensorHandler(self) self.envDummySensorHandler = EnvDummySensorHandler(self) self.sysDummyActuatorHandler = SysDummyActuatorHandler() self.envDummyActuatorHandler = EnvDummyActuatorHandler(self) self.dummyMotionHandler = DummyMotionHandler() self.proj.sensor_handler, self.proj.actuator_handler, self.proj.h_instance = [ None ] * 3 print "Loading safety constraints..." self.safety_aut = fsa.Automaton(self.proj) self.safety_aut.sensor_handler = self.sysDummySensorHandler self.safety_aut.actuator_handler = self.sysDummyActuatorHandler self.safety_aut.motion_handler = self.dummyMotionHandler self.safety_aut.loadFile(self.proj.getFilenamePrefix() + "_safety.aut", self.proj.enabled_sensors, self.proj.enabled_actuators, self.proj.all_customs) print "Loading environment counter-strategy..." self.num_bits = int(numpy.ceil(numpy.log2(len(self.proj.rfi.regions))) ) # Number of bits necessary to encode all regions region_props = ["bit" + str(n) for n in xrange(self.num_bits)] self.env_aut = fsa.Automaton(self.proj) self.env_aut.sensor_handler = self.envDummySensorHandler self.env_aut.actuator_handler = self.envDummyActuatorHandler self.env_aut.motion_handler = self.dummyMotionHandler # We are being a little tricky here by just reversing the sensor and actuator propositions # to create a sort of dual of the usual automaton self.env_aut.loadFile( self.proj.getFilenamePrefix() + ".aut", self.proj.enabled_actuators + self.proj.all_customs + region_props, self.proj.enabled_sensors, []) # Force initial state to state #0 in counter-strategy self.env_aut.current_region = None self.env_aut.current_state = self.env_aut.states[0] # Internal aut housekeeping (ripped from chooseInitialState; hacky) self.env_aut.last_next_states = [] self.env_aut.next_state = None self.env_aut.next_region = None #self.env_aut.dumpStates([self.env_aut.current_state]) # Set initial sensor values self.env_aut.updateOutputs() # Figure out what actuator/custom-prop settings the system should start with for k, v in self.env_aut.current_state.inputs.iteritems(): # Skip any "bitX" region encodings if re.match('^bit\d+$', k): continue self.actuatorStates[k] = int(v) # Figure out what region the system should start from (ripped from regionFromState) self.current_region = 0 for bit in range(self.num_bits): if (int(self.env_aut.current_state.inputs["bit" + str(bit)]) == 1): # bit0 is MSB self.current_region += int(2**(self.num_bits - bit - 1)) self.dest_region = self.current_region # Create all the sensor/actuator buttons self.env_buttons = [] # This will later hold our buttons self.act_buttons = [] # This will later hold our buttons self.cust_buttons = [] # This will later hold our buttons actprops = copy.deepcopy(self.actuatorStates) for s in self.proj.all_customs: del (actprops[s]) custprops = copy.deepcopy(self.actuatorStates) for s in self.proj.enabled_actuators: del (custprops[s]) self.populateToggleButtons(self.sizer_env, self.env_buttons, self.sensorStates) self.populateToggleButtons(self.sizer_act, self.act_buttons, actprops) self.populateToggleButtons(self.sizer_prop, self.cust_buttons, custprops) # Make the env buttons not clickable (TODO: maybe replace with non-buttons) for b in self.env_buttons: b.Enable(False) # Set up the logging grid colheaders = self.proj.enabled_sensors + [ "Region" ] + self.proj.enabled_actuators + self.proj.all_customs self.history_grid.CreateGrid(0, len(colheaders)) for i, n in enumerate(colheaders): self.history_grid.SetColLabelValue(i, " " + n + " ") self.history_grid.SetColSize(i, -1) # Auto-size self.history_grid.EnableEditing(False) # Put initial condition into log self.appendToHistory() # Find the appropriate starting state in the sys safety aut init_outputs = [ o for o in self.actuatorStates.keys() if (self.actuatorStates[o] == 1) ] if self.safety_aut.chooseInitialState(self.current_region, init_outputs) is None: print "Counterstrategy initial state not found in system safety automaton. Something's off." return # Start initial environment move # All transitionable states have the same env move, so just use the first if (len(self.env_aut.current_state.transitions) >= 1): self.env_aut.updateOutputs( self.env_aut.current_state.transitions[0]) self.label_movingto.SetLabel( "Stay in region " + self.safety_aut.getAnnotatedRegionName(self.current_region)) self.applySafetyConstraints()
message = "BG:" + proj.getFilenamePrefix() + ".spec" UDPSockTo.sendto(message, addrTo) # Redirect all output to the log redir = RedirectText(UDPSockTo, addrTo) sys.stdout = redir sys.stderr = redir ####################### # Load automaton file # ####################### print "Loading automaton..." FSA = fsa.Automaton(proj) success = FSA.loadFile(aut_file, proj.enabled_sensors, proj.enabled_actuators, proj.all_customs) if not success: return ############################# # Begin automaton execution # ############################# last_gui_update_time = 0 ### Wait for the initial start command if show_gui: while not runFSA:
def __init__(self, *args, **kwds): # begin wxGlade: MopsyFrame.__init__ kwds["style"] = wx.DEFAULT_FRAME_STYLE wx.Frame.__init__(self, *args, **kwds) self.mopsy_frame_statusbar = self.CreateStatusBar(1, 0) self.window_1 = wx.SplitterWindow(self, wx.ID_ANY, style=wx.SP_3D | wx.SP_BORDER) self.window_1_pane_1 = wx.Panel(self.window_1, wx.ID_ANY) self.history_grid = wx.grid.Grid(self.window_1_pane_1, wx.ID_ANY, size=(1, 1)) self.window_1_pane_2 = wx.Panel(self.window_1, wx.ID_ANY) self.panel_1 = wx.Panel(self.window_1_pane_2, wx.ID_ANY, style=wx.SUNKEN_BORDER | wx.TAB_TRAVERSAL) self.label_1 = wx.StaticText(self.window_1_pane_2, wx.ID_ANY, "Your goal:") self.label_goal = wx.StaticText(self.window_1_pane_2, wx.ID_ANY, "Wait patiently for Mopsy to load") self.label_5 = wx.StaticText(self.window_1_pane_2, wx.ID_ANY, "Current environment state:") self.label_6 = wx.StaticText(self.window_1_pane_2, wx.ID_ANY, "Please choose your response:") self.label_movingto = wx.StaticText(self.window_1_pane_2, wx.ID_ANY, "Moving to XXX ...") self.label_8 = wx.StaticText(self.window_1_pane_2, wx.ID_ANY, "Actuator states:") self.label_9 = wx.StaticText(self.window_1_pane_2, wx.ID_ANY, "Internal propositions:") self.label_violation = wx.StaticText(self.window_1_pane_2, wx.ID_ANY, "", style=wx.ST_NO_AUTORESIZE) self.button_next = wx.Button(self.window_1_pane_2, wx.ID_ANY, "Execute Move >>") self.__set_properties() self.__do_layout() self.Bind(wx.EVT_BUTTON, self.onButtonNext, self.button_next) # end wxGlade self.coreCalculationLock = threading.Lock() self.dest_region = None self.current_region = None self.regionsToHide = [] self.actuatorStates = {} self.sensorStates = {} self.panel_1.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) self.mapBitmap = None # Load in the project and map self.mopsy_frame_statusbar.SetStatusText("Loading project...", 0) self.compiler = SpecCompiler(sys.argv[1]) self.proj = copy.deepcopy(self.compiler.proj) self.proj.rfi = self.proj.loadRegionFile(decomposed=True) self.Bind(wx.EVT_SIZE, self.onResize, self) self.panel_1.Bind(wx.EVT_PAINT, self.onPaint) self.panel_1.Bind(wx.EVT_ERASE_BACKGROUND, self.onEraseBG) self.panel_1.Bind(wx.EVT_LEFT_DOWN, self.onMapClick) self.onResize() self.proj.determineEnabledPropositions() # Parse specification so we can give feedback self.mopsy_frame_statusbar.SetStatusText("Parsing specification...", 0) self.compiler._decompose() self.spec, self.tracebackTree, response = self.compiler._writeLTLFile() self.compiler._writeSMVFile() # to update propList if self.proj.compile_options["parser"] == "slurp": # Add SLURP to path for import p = os.path.dirname(os.path.abspath(__file__)) sys.path.append(os.path.join(p, "..", "etc", "SLURP")) global chunks_from_gentree, line_to_chunks from ltlbroom.specgeneration import chunks_from_gentree, line_to_chunks self.tracebackChunks = chunks_from_gentree(self.tracebackTree) # Load in counter-strategy automaton self.envDummySensorHandler = EnvDummySensorHandler(self) self.envDummyActuatorHandler = EnvDummyActuatorHandler(self) self.dummyMotionHandler = DummyMotionHandler() self.proj.sensor_handler, self.proj.actuator_handler, self.proj.h_instance = [ None ] * 3 self.mopsy_frame_statusbar.SetStatusText( "Loading environment counter-strategy...", 0) self.num_bits = int(numpy.ceil(numpy.log2(len(self.proj.rfi.regions))) ) # Number of bits necessary to encode all regions region_props = ["bit" + str(n) for n in xrange(self.num_bits)] self.env_aut = fsa.Automaton(self.proj) self.env_aut.sensor_handler = self.envDummySensorHandler self.env_aut.actuator_handler = self.envDummyActuatorHandler self.env_aut.motion_handler = self.dummyMotionHandler # We are being a little tricky here by just reversing the sensor and actuator propositions # to create a sort of dual of the usual automaton self.env_aut.loadFile( self.proj.getFilenamePrefix() + ".aut", self.proj.enabled_actuators + self.proj.all_customs + self.compiler.proj.internal_props + region_props, self.proj.enabled_sensors, []) self.env_aut.current_region = None # Find first state in counterstrategy that seeks to falsify the given liveness if len(sys.argv) > 2: desired_jx = int(sys.argv[2]) for s in self.env_aut.states: if s.transitions: rank_str = s.transitions[0].rank m = re.search(r"\(\d+,(-?\d+)\)", rank_str) if m is None: print "ERROR: Error parsing jx in automaton. Are you sure the spec is unrealizable?" return jx = int(m.group(1)) if jx == desired_jx: self.env_aut.current_state = s break if self.env_aut.current_state is None: print "ERROR: could not find state in counterstrategy to falsify sys goal #{}".format( desired_jx) return else: self.env_aut.current_state = self.env_aut.states[0] # Internal aut housekeeping (ripped from chooseInitialState; hacky) self.env_aut.last_next_states = [] self.env_aut.next_state = None self.env_aut.next_region = None #self.env_aut.dumpStates([self.env_aut.current_state]) # Set initial sensor values self.env_aut.updateOutputs() # Figure out what actuator/custom-prop settings the system should start with for k, v in self.env_aut.current_state.inputs.iteritems(): # Skip any "bitX" region encodings if re.match('^bit\d+$', k): continue self.actuatorStates[k] = int(v) # Figure out what region the system should start from self.current_region = self.regionFromEnvState( self.env_aut.current_state) self.dest_region = self.current_region # Create all the sensor/actuator buttons self.env_buttons = [] # This will later hold our buttons self.act_buttons = [] # This will later hold our buttons self.cust_buttons = [] # This will later hold our buttons actprops = dict((k, v) for k, v in self.actuatorStates.iteritems() if k in self.proj.enabled_actuators) custprops = dict( (k, v) for k, v in self.actuatorStates.iteritems() if k in self.proj.all_customs + self.compiler.proj.internal_props) self.populateToggleButtons(self.sizer_env, self.env_buttons, self.sensorStates) self.populateToggleButtons(self.sizer_act, self.act_buttons, actprops) self.populateToggleButtons(self.sizer_prop, self.cust_buttons, custprops) # Make the env buttons not clickable (TODO: maybe replace with non-buttons) #for b in self.env_buttons: # b.Enable(False) # Set up the logging grid self.history_grid.SetDefaultCellFont( wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, "")) self.history_grid.SetLabelFont( wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, "")) colheaders = self.proj.enabled_sensors + [ "Region" ] + self.proj.enabled_actuators + self.proj.all_customs + self.compiler.proj.internal_props self.history_grid.CreateGrid(0, len(colheaders)) for i, n in enumerate(colheaders): self.history_grid.SetColLabelValue(i, " " + n + " ") self.history_grid.SetColSize(i, -1) # Auto-size self.history_grid.EnableEditing(False) # Decide whether to enable core-finding self.coreFindingEnabled = self.compiler._getPicosatCommand( ) is not None # Put initial condition into log self.appendToHistory() # Start initial environment move # All transitionable states have the same env move, so just use the first if (len(self.env_aut.current_state.transitions) >= 1): self.env_aut.updateOutputs( self.env_aut.current_state.transitions[0]) self.label_movingto.SetLabel( "Stay in region " + self.env_aut.getAnnotatedRegionName(self.current_region)) self.showCurrentGoal() self.applySafetyConstraints()