def _gen_activate(self, command, negated=False): """Generate statements for activating an actuator.""" try: actuator = command.theme.name except AttributeError: raise KeyError("Missing actuator for activate/deactivate.") # If negation isn't set, allow the command to set it negated = negated or command.negation actuator_frag = sys_(actuator) if not negated else not_(sys_(actuator)) when = "Always" if not negated else "Never" chunks = [] if command.location: regions = [location.name for location in self._expand_argument(command.location, command)] for region in regions: # Generate a location-restricted action explanation = "{} activate {!r} in {!r}.".format(when, actuator, region) formula = always(implies(sys_(region), actuator_frag)) chunks.append(SpecChunk(explanation, [formula], SpecChunk.SYS, command)) else: # Always activate explanation = "{} activate {!r}.".format(when, actuator) formula = always(actuator_frag) chunks.append(SpecChunk(explanation, [formula], SpecChunk.SYS, command)) return (chunks, [], [], [])
def _gen_activate(self, command, negated=False): """Generate statements for activating an actuator.""" try: actuator = command.theme.name except AttributeError: raise KeyError("Missing actuator for activate/deactivate.") # If negation isn't set, allow the command to set it negated = negated or command.negation actuator_frag = sys_(actuator) if not negated else not_(sys_(actuator)) when = "Always" if not negated else "Never" chunks = [] if command.location: regions = [location.name for location in self._expand_argument(command.location, command)] for region in regions: # Generate a location-restricted action explanation = "{} activate {!r} in {!r}.".format(when, actuator, region) formula = always(implies(next_(sys_(region)), next_(actuator_frag))) chunks.append(SpecChunk(explanation, [formula], SpecChunk.SYS, command)) else: # Always activate explanation = "{} activate {!r}.".format(when, actuator) formula = always(next_(actuator_frag)) chunks.append(SpecChunk(explanation, [formula], SpecChunk.SYS, command)) return (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 _gen_carry(self, command): """Generate statements for carrying items from one region to another.""" if not all((command.theme, command.source, command.destination)): raise KeyError("Missing item, source, or destination for carry.") item = command.theme.name source = command.source.name dest_arg = command.destination destinations = [dest.name for dest in self._expand_argument(dest_arg, command)] # Start the carry actuators and props as off start_explanation = "Nothing is carried or delivered at the start." deliver_mems = [_prop_mem(dest, DELIVER) for dest in destinations] holding_props = [HOLDING] start_lines = [_frag_props_off([PICKUP, DROP] + deliver_mems + holding_props)] start_chunk = SpecChunk(start_explanation, start_lines, SpecChunk.SYS, command) pickup_explanation = "Only pick up if you can carry more." pickup_lines = [always(implies(next_(sys_(HOLDING)), not_(next_(sys_(PICKUP)))))] pickup_chunk = SpecChunk(pickup_explanation, pickup_lines, SpecChunk.SYS, command) drop_explanation = "Only drop if you are carrying something." drop_lines = [always(implies(not_(next_(sys_(HOLDING))), not_(next_(sys_(DROP)))))] drop_chunk = SpecChunk(drop_explanation, drop_lines, SpecChunk.SYS, command) stay_explanation = "Stay where you are when picking up and dropping." stay_lines = [always(implies(or_([sys_(PICKUP), sys_(DROP)]), self._frag_stay()))] stay_chunk = SpecChunk(stay_explanation, stay_lines, SpecChunk.SYS, command) source_explanation = "Pick up {!r} in {!r}.".format(item, source) source_lines = [ always( iff( and_([or_([and_([sys_(source), sys_(PICKUP)]), sys_(HOLDING)]), not_(sys_(DROP))]), next_(sys_(HOLDING)), ) ) ] source_chunk = SpecChunk(source_explanation, source_lines, SpecChunk.SYS, command) # This is not a list comprehension solely for readability delivery_chunks = [ _chunk_deliver(command, item, dest, mem_dest) for dest, mem_dest in zip(destinations, deliver_mems) ] return ( [start_chunk, pickup_chunk, drop_chunk, stay_chunk, source_chunk] + delivery_chunks, [], deliver_mems + holding_props, [], )
def _gen_carry(self, command): """Generate statements for carrying items from one region to another.""" if not all((command.theme, command.source, command.destination)): raise KeyError("Missing item, source, or destination for carry.") item = command.theme.name source = command.source.name dest_arg = command.destination destinations = [dest.name for dest in self._expand_argument(dest_arg, command)] # Start the carry actuators and props as off start_explanation = "Nothing is carried or delivered at the start." deliver_mems = [_prop_mem(dest, DELIVER) for dest in destinations] holding_props = [HOLDING] start_lines = [_frag_props_off([PICKUP, DROP] + deliver_mems + holding_props)] start_chunk = SpecChunk(start_explanation, start_lines, SpecChunk.SYS, command) pickup_explanation = "Only pick up if you can carry more." pickup_lines = [always(implies(next_(sys_(HOLDING)), not_(next_(sys_(PICKUP)))))] pickup_chunk = SpecChunk(pickup_explanation, pickup_lines, SpecChunk.SYS, command) drop_explanation = "Only drop if you are carrying something." drop_lines = [always(implies(not_(next_(sys_(HOLDING))), not_(next_(sys_(DROP)))))] drop_chunk = SpecChunk(drop_explanation, drop_lines, SpecChunk.SYS, command) stay_explanation = "Stay where you are when picking up and dropping." stay_lines = [always(implies(or_([sys_(PICKUP), sys_(DROP)]), self._frag_stay()))] stay_chunk = SpecChunk(stay_explanation, stay_lines, SpecChunk.SYS, command) source_explanation = "Pick up {!r} in {!r}.".format(item, source) source_lines = [always(iff(and_([or_([and_([sys_(source), sys_(PICKUP)]), sys_(HOLDING)]), not_(sys_(DROP))]), next_(sys_(HOLDING))))] source_chunk = SpecChunk(source_explanation, source_lines, SpecChunk.SYS, command) # This is not a list comprehension solely for readability delivery_chunks = [_chunk_deliver(command, item, dest, mem_dest) for dest, mem_dest in zip(destinations, deliver_mems)] return ([start_chunk, pickup_chunk, drop_chunk, stay_chunk, source_chunk] + delivery_chunks, [], deliver_mems + holding_props, [])
def _gen_avoid(self, command): """Generate statements for never going to a location.""" regions = [location.name for location in self._expand_argument(command.location, command)] spec_chunks = [] for region in regions: explanation1 = "Do not go to {!r}.".format(region) sys_lines1 = SpecChunk(explanation1, [always(not_(sys_(region)))], SpecChunk.SYS, command) explanation2 = "The robot does not begin in {!r}.".format(region) sys_lines2 = SpecChunk(explanation2, [not_(sys_(region))], SpecChunk.SYS, command) spec_chunks.extend([sys_lines1, sys_lines2]) return (spec_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_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_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 generate(self, text, sensors, regions, props, tag_dict, realizable_reactions=True, verbose=True): """Generate a logical specification from natural language and propositions.""" # Clean unicode out of everything text = text.encode("ascii", "ignore") self.sensors = [astr.encode("ascii", "ignore") for astr in sensors] self.regions = [astr.encode("ascii", "ignore") for astr in regions] self.props = [astr.encode("ascii", "ignore") for astr in props] self.tag_dict = { key.encode("ascii", "ignore"): [value.encode("ascii", "ignore") for value in values] for key, values in tag_dict.items() } if verbose: print "NL->LTL Generation called on:" print "Sensors:", self.sensors print "Props:", self.props print "Regions:", self.regions print "Tag dict:", self.tag_dict print "Text:", repr(text) print # Make lists for POS conversions, including the metapar keywords force_nouns = list(self.regions) + list(self.sensors) force_verbs = self.GOALS.keys() # Set up parse_client = PipelineClient() results = [] responses = [] custom_props = set() self.react_props = set() # TODO: Make this a local custom_sensors = set() generation_trees = OrderedDict() # Add the actuator mutex if len(self.props) > 1: actuator_mutex = mutex_([sys_(prop) for prop in self.props], True) generation_trees["Safety assumptions"] = { "Safety assumptions": [ SpecChunk( "Robot can perform only one action at a time.", [actuator_mutex, always(actuator_mutex)], SpecChunk.SYS, None, ) ] } for line in text.split("\n"): # Strip the text before using it and ignore any comments line = line.strip() line = _remove_comments(line) if not line: # Blank lines are counted as being processed correctly but are skipped results.append(True) responses.append("") continue # Init the generation tree to the empty result generated_lines = OrderedDict() generation_trees[line] = generated_lines if verbose: print "Sending to remote parser:", repr(line) parse = parse_client.parse(line, force_nouns, force_verbs=force_verbs) if verbose: print "Response from parser:", repr(parse) frames, new_commands, kb_response = process_parse_tree(parse, line, self.kbase, quiet=True) frames, new_commands, kb_response = process_parse_tree(parse, line, self.kbase, quiet=not verbose) # Build the metapars # For now, assume success if there were commands or a kb_response success = bool(new_commands) or bool(kb_response) command_responses = [kb_response] if kb_response else [] for command in new_commands: if COMMAND_DEBUG: print "Processing command:" print command try: new_sys_lines, new_env_lines, new_custom_props, new_custom_sensors = self._apply_metapar(command) except KeyError as err: cause = err.message problem = "Could not understand {!r} due to error {}.".format(command.action, cause) if verbose: print >> sys.stderr, "Error: " + problem command_responses.append(cause) success = False continue else: command_responses.append(respond_okay(command.action)) # Add in the new lines command_key = _format_command(command) if command_key not in generated_lines: generated_lines[command_key] = [] generated_lines[command_key].extend(new_sys_lines) generated_lines[command_key].extend(new_env_lines) # Add custom props/sensors custom_props.update(new_custom_props) custom_sensors.update(new_custom_sensors) # If we've got no responses, say we didn't understand at all. if not command_responses: command_responses.append(respond_nocommand()) # Add responses and successes results.append(success) responses.append(" ".join(command_responses)) # Add some space between commands if verbose: print if COMMAND_DEBUG: print "Generation trees:" for line, output in generation_trees.items(): print line print output print # We need to modify non-reaction goals to be or'd with the reactions if realizable_reactions and self.react_props: # Dedupe and make an or over all the reaction properties reaction_or_frag = or_([sys_(prop) for prop in self.react_props]) # HACK: Rewrite all the goals! # TODO: Test again with reaction propositions other than defuse for command_spec_chunks in generation_trees.values(): for spec_chunks in command_spec_chunks.values(): for spec_chunk in spec_chunks: if not spec_chunk.issys(): continue spec_chunk.lines = [_insert_or_before_goal(reaction_or_frag, line) for line in spec_chunk.lines] # Aggregate all the propositions # Identify goal numbers as we loop over sys lines sys_lines = [] # At the moment, there are no useless goals in specs, so we # begin at 0 goal_idx = 0 for input_text, command_spec_lines in generation_trees.items(): for command, spec_lines_list in command_spec_lines.items(): for spec_lines in spec_lines_list: if not spec_lines.issys(): continue for line in spec_lines.lines: spec_lines.input = input_text sys_lines.append(line) if isgoal(line): spec_lines.goal_indices.add(goal_idx) goal_idx += 1 # Filter out any duplicates from the env_lines env_lines = OrderedDict() for command_spec_lines in generation_trees.values(): for spec_lines_list in command_spec_lines.values(): for spec_lines in spec_lines_list: if not spec_lines.isenv(): continue for line in spec_lines.lines: env_lines[line] = None env_lines = env_lines.keys() # Convert sets to lists for the caller custom_props = list(custom_props) custom_sensors = list(custom_sensors) if verbose: print "Spec generation complete." print "Results:", results print "Responses:", responses print "Environment lines:", env_lines print "System lines:", sys_lines print "Custom props:", custom_props print "Custom sensors:", custom_sensors print "Generation trees:", generation_trees return (env_lines, sys_lines, custom_props, custom_sensors, results, responses, generation_trees)
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, [])
def generate(self, text, sensors, regions, props, tag_dict, realizable_reactions=True, verbose=True): """Generate a logical specification from natural language and propositions.""" # Clean unicode out of everything text = text.encode('ascii', 'ignore') self.sensors = [astr.encode('ascii', 'ignore') for astr in sensors] self.regions = [astr.encode('ascii', 'ignore') for astr in regions] self.props = [astr.encode('ascii', 'ignore') for astr in props] self.tag_dict = {key.encode('ascii', 'ignore'): [value.encode('ascii', 'ignore') for value in values] for key, values in tag_dict.items()} if verbose: print "NL->LTL Generation called on:" print "Sensors:", self.sensors print "Props:", self.props print "Regions:", self.regions print "Tag dict:", self.tag_dict print "Text:", repr(text) print # Make lists for POS conversions, including the metapar keywords force_nouns = list(self.regions) + list(self.sensors) force_verbs = self.GOALS.keys() # Set up parse_client = PipelineClient() results = [] responses = [] custom_props = set() self.react_props = set() # TODO: Make this a local custom_sensors = set() generation_trees = OrderedDict() # Add the actuator mutex if len(self.props) > 1: actuator_mutex = mutex_([sys_(prop) for prop in self.props], True) generation_trees["Safety assumptions"] = \ {"Safety assumptions": [SpecChunk("Robot can perform only one action at a time.", [actuator_mutex, always(actuator_mutex)], SpecChunk.SYS, None)]} for line in text.split('\n'): # Strip the text before using it and ignore any comments line = line.strip() line = _remove_comments(line) if not line: # Blank lines are counted as being processed correctly but are skipped results.append(True) responses.append('') continue # Init the generation tree to the empty result generated_lines = OrderedDict() generation_trees[line] = generated_lines if verbose: print "Sending to remote parser:", repr(line) parse = parse_client.parse(line, force_nouns, force_verbs=force_verbs) if verbose: print "Response from parser:", repr(parse) frames, new_commands, kb_response = \ process_parse_tree(parse, line, self.kbase, quiet=True) frames, new_commands, kb_response = process_parse_tree(parse, line, self.kbase, quiet=not verbose) # Build the metapars # For now, assume success if there were commands or a kb_response success = bool(new_commands) or bool(kb_response) command_responses = [kb_response] if kb_response else [] for command in new_commands: if COMMAND_DEBUG: print "Processing command:" print command try: new_sys_lines, new_env_lines, new_custom_props, new_custom_sensors = \ self._apply_metapar(command) except KeyError as err: cause = err.message problem = \ "Could not understand {!r} due to error {}.".format(command.action, cause) if verbose: print >> sys.stderr, "Error: " + problem command_responses.append(cause) success = False continue else: command_responses.append(respond_okay(command.action)) # Add in the new lines command_key = _format_command(command) if command_key not in generated_lines: generated_lines[command_key] = [] generated_lines[command_key].extend(new_sys_lines) generated_lines[command_key].extend(new_env_lines) # Add custom props/sensors custom_props.update(new_custom_props) custom_sensors.update(new_custom_sensors) # If we've got no responses, say we didn't understand at all. if not command_responses: command_responses.append(respond_nocommand()) # Add responses and successes results.append(success) responses.append(' '.join(command_responses)) # Add some space between commands if verbose: print if COMMAND_DEBUG: print "Generation trees:" for line, output in generation_trees.items(): print line print output print # We need to modify non-reaction goals to be or'd with the reactions if realizable_reactions and self.react_props: # Dedupe and make an or over all the reaction properties reaction_or_frag = or_([sys_(prop) for prop in self.react_props]) # HACK: Rewrite all the goals! # TODO: Test again with reaction propositions other than defuse for command_spec_chunks in generation_trees.values(): for spec_chunks in command_spec_chunks.values(): for spec_chunk in spec_chunks: if not spec_chunk.issys(): continue spec_chunk.lines = [_insert_or_before_goal(reaction_or_frag, line) for line in spec_chunk.lines] # Aggregate all the propositions # Identify goal numbers as we loop over sys lines sys_lines = [] # At the moment, there are no useless goals in specs, so we # begin at 0 goal_idx = 0 for input_text, command_spec_lines in generation_trees.items(): for command, spec_lines_list in command_spec_lines.items(): for spec_lines in spec_lines_list: if not spec_lines.issys(): continue for line in spec_lines.lines: spec_lines.input = input_text sys_lines.append(line) if isgoal(line): spec_lines.goal_indices.add(goal_idx) goal_idx += 1 # Filter out any duplicates from the env_lines env_lines = OrderedDict() for command_spec_lines in generation_trees.values(): for spec_lines_list in command_spec_lines.values(): for spec_lines in spec_lines_list: if not spec_lines.isenv(): continue for line in spec_lines.lines: env_lines[line] = None env_lines = env_lines.keys() # Convert sets to lists for the caller custom_props = list(custom_props) custom_sensors = list(custom_sensors) if verbose: print "Spec generation complete." print "Results:", results print "Responses:", responses print "Environment lines:", env_lines print "System lines:", sys_lines print "Custom props:", custom_props print "Custom sensors:", custom_sensors print "Generation trees:", generation_trees return (env_lines, sys_lines, custom_props, custom_sensors, results, responses, generation_trees)