def _gen_follow(self, command): """Generate statements for following.""" # Env is stationary iff in the last state change our region and the env's region were stable stationary_explanation = "Definition of when the target is moving." stationary_safeties = \ always(iff(next_(sys_(FOLLOW_STATIONARY)), ENV_STATIONARY)) stationary_lines = SpecChunk(stationary_explanation, [stationary_safeties], SpecChunk.SYS, command) # Stay there if environment is changing stay_there_explanation = "React immediately to the target moving." stay_there_safeties = \ always(implies(not_(next_(sys_(FOLLOW_STATIONARY))), self._frag_stay())) stay_there_lines = \ SpecChunk(stay_there_explanation, [stay_there_safeties], SpecChunk.SYS, command) # Match the sensor location to ours follow_goals = \ [SpecChunk("Follow the target to {!r}.".format(region), [always_eventually( implies(and_((sys_(FOLLOW_STATIONARY), env(region))), sys_(region)))], SpecChunk.SYS, command) for region in self.regions] follow_env = SpecChunk("Target must obey map topology.", [FOLLOW_SENSORS], SpecChunk.ENV, command) return ([stationary_lines, stay_there_lines] + follow_goals, [follow_env], [FOLLOW_STATIONARY], [])
def _gen_patrol(self, command): """Generate statements to always eventually be in a location.""" regions = [location.name for location in self._expand_argument(command.location, command)] sys_chunks = [] for region in regions: explanation = "Continuously visit {!r}.".format(region) sys_chunks.append(SpecChunk(explanation, [always_eventually(sys_(region))], SpecChunk.SYS, command)) return (sys_chunks, [], [], [])
def _gen_follow(self, command): """Generate statements for following.""" # Env is stationary iff in the last state change our region and the env's region were stable stationary_explanation = "Definition of when the target is moving." stationary_safeties = \ always(iff(next_(sys_(FOLLOW_STATIONARY)), ENV_STATIONARY)) stationary_lines = SpecChunk(stationary_explanation, [stationary_safeties], SpecChunk.SYS, command) # Stay there if environment is changing stay_there_explanation = "React immediately to the target moving." stay_there_safeties = always(implies(not_(next_(sys_(FOLLOW_STATIONARY))), self._frag_stay())) stay_there_lines = SpecChunk(stay_there_explanation, [stay_there_safeties], SpecChunk.SYS, command) # Match the sensor location to ours follow_goals = \ [SpecChunk("Follow the target to {!r}.".format(region), [always_eventually(implies(and_((sys_(FOLLOW_STATIONARY), env(region))), sys_(region)))], SpecChunk.SYS, command) for region in self.regions] follow_env = SpecChunk("Target must obey map topology.", [FOLLOW_SENSORS], SpecChunk.ENV, command) return ([stationary_lines, stay_there_lines] + follow_goals, [follow_env], [FOLLOW_STATIONARY], [])
def _frag_complete_context(actuator, context_prop): """Generate fragments for completing an action in context.""" actuator_done = _prop_actuator_done(actuator) eventually_actuator = always_eventually(env(actuator_done)) return [and_((sys_(actuator), next_(env(actuator_done)), context_prop)), [eventually_actuator]]
def _frag_atleastonce(mem_prop, fragment): """Generate fragments for performing an action at least once by using a memory proposition.""" return [always(iff(next_(sys_(mem_prop)), or_((sys_(mem_prop), fragment)))), always_eventually(sys_(mem_prop))]
def _gen_stay(self, command): """Generate statements to stay exactly where you are.""" sys_lines = SpecChunk("Stay in the same place.", [always_eventually(self._frag_stay())], SpecChunk.SYS, command) return ([sys_lines], [], [], [])
def _gen_conditional(self, command): """Generate a conditional action""" if isinstance(command.condition, Event): # Validate the condition if not command.condition.theme: raise KeyError("Cannot understand condition:\n{}".format(command.condition)) condition = command.condition.theme.name if condition not in self.sensors: raise KeyError("No sensor to detect condition {!r}".format(command.condition.theme.name)) if command.condition.sensor != SEE_ACTION: raise KeyError("Cannot use action {!r} as a condition".format(command.condition.action)) condition_frag = env(condition) explanation = "To react to {!r},".format(condition) elif isinstance(command.condition, Assertion): # TODO: Add support for assertions not about "you". Ex: If there is a hostage... # Validate the condition if not command.condition.location: raise KeyError("Cannot understand condition:\n{}".format(command.condition)) condition = command.condition.location.name condition_frag = sys_(condition) explanation = "When in {!r},".format(condition) else: raise KeyError("Cannot understand condition:\n{}".format(command.condition)) # Validate the action action = command.action if action not in self.REACTIONS: raise KeyError("Unknown reaction {!r}".format(action)) # Create the right type of reaction new_props = [] if action in self.props: # Simple actuator reaction_prop = action elif action == STAY_ACTION: reaction_prop = STAY_THERE else: # Reaction proposition reaction_prop_name = REACT + "_" + condition reaction_prop = sys_(reaction_prop_name) self.react_props.add(reaction_prop_name) new_props.append(reaction_prop_name) # Generate the response sys_statements = [] if action in (GO_ACTION, AVOID_ACTION): # Go is unusual because the outcome is not immediately satisfiable if not command.location: raise KeyError("No location in go reaction") destination = command.location.name # Negation is easy, so we take a shortcut if ((action == GO_ACTION and command.negation) or (action == AVOID_ACTION and not command.negation)): sys_statements.append(always(implies(next_(condition_frag), not_(next_(sys_(destination)))))) explanation += " avoid {!r}.".format(command.location.name) else: destination_stmt = sys_(destination) # New goal for where we should go go_goal = always_eventually(implies(reaction_prop, destination_stmt)) # Safety that persists go_safety = \ always(iff(next_(reaction_prop), or_([reaction_prop, next_(condition_frag)]))) # Make sure we act immediately: []((!react & next(react) )-> stay_there) stay_there = always(implies(and_((not_(reaction_prop), next_(reaction_prop))), self._frag_stay())) sys_statements.extend([go_goal, go_safety, stay_there]) explanation += " go to {!r}.".format(command.location.name) elif action == STAY_ACTION: sys_statements.append(always(implies(condition_frag, reaction_prop))) explanation += " stay there." else: if command.theme.name not in self.props: raise KeyError("Unknown actuator {!r}".format(command.theme.name)) # Otherwise we are always creating reaction safety sys_statements.append(always(iff(next_(condition_frag), next_(reaction_prop)))) template = " {} {!r}." if not command.negation else " do not {} {!r}." explanation += template.format(action, command.theme.name) if not command.negation: handler = self.REACTIONS[action] else: handler = self.NEG_REACTIONS[action] reaction_frag = handler(command) react = always(implies(next_(reaction_prop), reaction_frag)) stay_there = always(implies(and_((not_(reaction_prop), next_(reaction_prop))), self._frag_stay())) sys_statements.extend([react, stay_there]) sys_chunk = SpecChunk(explanation, sys_statements, SpecChunk.SYS, command) return ([sys_chunk], [], new_props, [])
def _gen_conditional(self, command, assume_eventual_relief=False): """Generate a conditional action""" # TODO: Properly document and condition assume_eventual_relief env_chunks = [] if isinstance(command.condition, Event): # Validate the condition if not command.condition.theme: raise KeyError("Cannot understand condition:\n{}".format(command.condition)) condition = command.condition.theme.name if condition not in self.sensors: raise KeyError( "No sensor to detect condition {!r}".format(command.condition.theme.name)) if command.condition.sensor != SEE_ACTION: raise KeyError( "Cannot use action {!r} as a condition".format(command.condition.action)) condition_frag = env(condition) explanation = "To react to {!r},".format(condition) if assume_eventual_relief: relief_explanation = "Assume {!r} eventually goes away.".format(condition) relief = always_eventually(not_(condition_frag)) relief_chunk = SpecChunk(relief_explanation, [relief], SpecChunk.ENV, command) env_chunks.append(relief_chunk) elif isinstance(command.condition, Assertion): # TODO: Add support for assertions not about "you". Ex: If there is a hostage... # Validate the condition if not command.condition.location: raise KeyError("Cannot understand condition:\n{}".format(command.condition)) condition = command.condition.location.name condition_frag = sys_(condition) explanation = "When in {!r},".format(condition) else: raise KeyError("Cannot understand condition:\n{}".format(command.condition)) # Validate the action action = command.action if action not in self.REACTIONS: raise KeyError("Unknown reaction {!r}".format(action)) # Create the right type of reaction new_props = [] if action in self.props: # Simple actuator reaction_prop = action else: # Reaction proposition reaction_prop_name = REACT + "_" + condition reaction_prop = sys_(reaction_prop_name) new_props.append(reaction_prop_name) self.react_props.add(reaction_prop_name) # Generate the response sys_statements = [] if action in (GO_ACTION, AVOID_ACTION): # Go is unusual because the outcome is not immediately satisfiable if not command.location: raise KeyError("No location in go reaction") destination = command.location.name # Negation is easy, so we take a shortcut if ((action == GO_ACTION and command.negation) or (action == AVOID_ACTION and not command.negation)): sys_statements.append(always(implies(next_(condition_frag), not_(next_(sys_(destination)))))) explanation += " avoid {!r}.".format(command.location.name) else: destination_stmt = sys_(destination) # New goal for where we should go go_goal = always_eventually(implies(reaction_prop, destination_stmt)) # Safety that persists go_safety = \ always(iff(next_(reaction_prop), or_([reaction_prop, next_(condition_frag)]))) # Make sure we act immediately: []((!react & next(react) )-> stay_there) stay_there = always(implies(and_((not_(reaction_prop), next_(reaction_prop))), self._frag_stay())) sys_statements.extend([go_goal, go_safety, stay_there]) explanation += " go to {!r}.".format(command.location.name) elif action == STAY_ACTION: sys_statements.append(always(iff(next_(condition_frag), next_(reaction_prop)))) sys_statements.append(always(implies(or_([reaction_prop, next_(reaction_prop)]), STAY_THERE))) explanation += " stay there." else: if command.theme.name not in self.props: raise KeyError("Unknown actuator {!r}".format(command.theme.name)) # Otherwise we are always creating reaction safety sys_statements.append(always(iff(next_(condition_frag), next_(reaction_prop)))) template = " {} {!r}." if not command.negation else " do not {} {!r}." explanation += template.format(action, command.theme.name) if not command.negation: handler = self.REACTIONS[action] else: handler = self.NEG_REACTIONS[action] reaction_frag = handler(command) react = always(implies(next_(reaction_prop), reaction_frag)) stay_there = always(implies(or_([reaction_prop, next_(reaction_prop)]), self._frag_stay())) sys_statements.extend([react, stay_there]) sys_chunk = SpecChunk(explanation, sys_statements, SpecChunk.SYS, command) return ([sys_chunk], env_chunks, new_props, [])