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 _chunk_deliver(command, item, dest, mem_dest):
    """Return a chunk representing delivering an item to its destination."""
    delivery_explanation = "Deliver {!r} to {!r}.".format(item, dest)
    delivery_frag = and_([next_(sys_(dest)), next_(sys_(DROP))])
    delivery_chunk = SpecChunk(delivery_explanation, _frag_atleastonce(mem_dest, delivery_frag),
                               SpecChunk.SYS, command)
    return delivery_chunk
    def _gen_go(self, command):
        """Generate statements to go to a location once."""
        # Avoid if it's negated
        if command.negation:
            return self._gen_avoid(command)

        try:
            regions = [location.name for location in self._expand_argument(command.location, command)]
        except AttributeError:
            raise KeyError("Could not understand location for 'go' command.")
        # Raise an error if any of the regions are bad.
        for region in regions:
            if region not in self.regions:
                raise KeyError("Cannot go to location {!r} "
                               "because it is not on the map.".format(region))

        sys_chunks = []
        mem_props = []
        for region in regions:
            # Set memory false initially
            mem_prop = _prop_mem(region, VISIT)
            explanation1 = "Initially, {!r} has not been visited.".format(region)
            init_off = not_(sys_(mem_prop))
            init_chunk = SpecChunk(explanation1, [init_off], SpecChunk.SYS, command)
            sys_chunks.append(init_chunk)

            sys_lines = _frag_atleastonce(mem_prop, next_(sys_(region)))
            explanation2 = "Visit {!r}.".format(region)
            sys_chunk = SpecChunk(explanation2, sys_lines, SpecChunk.SYS, command)
            mem_props.append(mem_prop)
            sys_chunks.append(sys_chunk)

        return (sys_chunks, [], mem_props, [])
Exemple #4
0
    def _gen_go(self, command):
        """Generate statements to go to a location once."""
        # Avoid if it's negated
        if command.negation:
            return self._gen_avoid(command)

        try:
            regions = [location.name for location in self._expand_argument(command.location, command)]
        except AttributeError:
            raise KeyError("Could not understand location for 'go' command.")
        # Raise an error if any of the regions are bad.
        for region in regions:
            if region not in self.regions:
                raise KeyError("Cannot go to location {!r} " "because it is not on the map.".format(region))

        sys_chunks = []
        mem_props = []
        for region in regions:
            # Set memory false initially
            mem_prop = _prop_mem(region, VISIT)
            explanation1 = "Initially, {!r} has not been visited.".format(region)
            init_off = not_(sys_(mem_prop))
            init_chunk = SpecChunk(explanation1, [init_off], SpecChunk.SYS, command)
            sys_chunks.append(init_chunk)

            sys_lines = _frag_atleastonce(mem_prop, next_(sys_(region)))
            explanation2 = "Visit {!r}.".format(region)
            sys_chunk = SpecChunk(explanation2, sys_lines, SpecChunk.SYS, command)
            mem_props.append(mem_prop)
            sys_chunks.append(sys_chunk)

        return (sys_chunks, [], mem_props, [])
Exemple #5
0
    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_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, [], [], [])
Exemple #7
0
def _chunk_deliver(command, item, dest, mem_dest):
    """Return a chunk representing delivering an item to its destination."""
    delivery_explanation = "Deliver {!r} to {!r}.".format(item, dest)
    delivery_frag = and_([next_(sys_(dest)), next_(sys_(DROP))])
    delivery_chunk = SpecChunk(delivery_explanation, _frag_atleastonce(mem_dest, delivery_frag),
                               SpecChunk.SYS, command)
    return delivery_chunk
Exemple #8
0
    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_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, [], [], [])
Exemple #10
0
    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,
            [],
        )
Exemple #11
0
 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, [], [], [])
Exemple #12
0
 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, [], [], [])
Exemple #13
0
    def _gen_search(self, command):
        """Generate statements for searching a region."""
        regions = [location.name for location in self._expand_argument(command.location, command)]
        spec_chunks = []
        mem_props = []
        for region in regions:
            # Set memory false initially
            mem_prop = _prop_mem(region, SWEEP)
            init_off = [not_(sys_(mem_prop))]
            explanation0 = "Initially, {!r} has not been searched.".format(region)
            spec_chunks.append(SpecChunk(explanation0, init_off, SpecChunk.SYS, command))

            explanation1 = "Complete a search in {!r}.".format(region)
            cic_frag, cic_env = _frag_complete_context(SWEEP, sys_(region))
            alo_sys = _frag_atleastonce(mem_prop, cic_frag)
            mem_props.append(mem_prop)
            spec_chunks.append(SpecChunk(explanation1, alo_sys, SpecChunk.SYS, command))

        explanation2 = "Assume that searches eventually complete."
        env_chunk = SpecChunk(explanation2, cic_env, SpecChunk.ENV, command)

        return (spec_chunks, [env_chunk], mem_props, [_prop_actuator_done(SWEEP)])
Exemple #14
0
    def _gen_search(self, command):
        """Generate statements for searching a region."""
        regions = [location.name for location in self._expand_argument(command.location, command)]
        spec_chunks = []
        mem_props = []
        for region in regions:
            # Set memory false initially
            mem_prop = _prop_mem(region, SWEEP)
            init_off = [not_(sys_(mem_prop))]
            explanation0 = "Initially, {!r} has not been searched.".format(region)
            spec_chunks.append(SpecChunk(explanation0, init_off, SpecChunk.SYS, command))

            explanation1 = "Complete a search in {!r}.".format(region)
            cic_frag, cic_env = _frag_complete_context(SWEEP, sys_(region))
            alo_sys = _frag_atleastonce(mem_prop, cic_frag)
            mem_props.append(mem_prop)
            spec_chunks.append(SpecChunk(explanation1, alo_sys, SpecChunk.SYS, command))

        explanation2 = "Assume that searches eventually complete."
        env_chunk = SpecChunk(explanation2, cic_env, SpecChunk.ENV, command)

        return (spec_chunks, [env_chunk], mem_props, [_prop_actuator_done(SWEEP)])
Exemple #15
0
    def _gen_go(self, command):
        """Generate statements to go to a location once."""
        # Avoid if it's negated
        if command.negation:
            return self._gen_avoid(command)

        regions = [location.name for location in self._expand_argument(command.location, command)]
        sys_chunks = []
        mem_props = []
        for region in regions:
            # Set memory false initially
            mem_prop = _prop_mem(region, VISIT)
            explanation1 = "Initially, {!r} has not been visited.".format(region)
            init_off = not_(sys_(mem_prop))
            init_chunk = SpecChunk(explanation1, [init_off], SpecChunk.SYS, command)
            sys_chunks.append(init_chunk)

            sys_lines = _frag_atleastonce(mem_prop, next_(sys_(region)))
            explanation2 = "Visit {!r}.".format(region)
            sys_chunk = SpecChunk(explanation2, sys_lines, SpecChunk.SYS, command)
            mem_props.append(mem_prop)
            sys_chunks.append(sys_chunk)

        return (sys_chunks, [], mem_props, [])
Exemple #16
0
    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], [])
Exemple #17
0
 def _gen_begin(self, command):
     """Generate statements to begin in a location."""
     region = command.theme.name
     explanation = "The robot begins in {!r}.".format(region)
     sys_lines = SpecChunk(explanation, [sys_(region)], SpecChunk.SYS, command)
     return ([sys_lines], [], [], [])
Exemple #18
0
    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, [])
Exemple #19
0
    def generate(self, text, sensors, regions, props, tag_dict, realizable_reactions=False):
        """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()}

        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 = list(self.props) + self.GOALS.keys()

        parse_client = PipelineClient()
        results = []
        responses = []
        custom_props = set()
        custom_sensors = set()
        self.generation_trees = OrderedDict()
        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 = defaultdict(list)
            self.generation_trees[line] = generated_lines

            print "Sending to remote parser:", repr(line)
            parse = parse_client.parse(line, force_nouns, force_verbs=force_verbs)
            print "Response from parser:", repr(parse)
            frames, new_commands, kb_response = process_parse_tree(parse, line, self.kbase)

            if SEMANTICS_DEBUG:
                print "Returned values from semantics:"
                print "Semantics results:"
                for frame in frames:
                    print "\t" + str(frame)
                print "New commands:", new_commands

            # 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:
                try:
                    new_sys_lines, new_env_lines, new_custom_props, new_custom_sensors = \
                        self._apply_metapar(command)
                except KeyError as err:
                    problem = \
                        "Could not understand {!r} due to error {}.".format(command.action, err)
                    print "ERROR: " + problem
                    command_responses.append(str(err))
                    success = False
                    continue
                else:
                    command_responses.append(respond_okay(command.action))

                # Add in the new lines
                generated_lines[_format_command(command)].extend(new_sys_lines)
                generated_lines[_format_command(command)].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))
            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 once we re-enable reaction propositions
            for command_spec_lines in self.generation_trees.values():
                for spec_lines in command_spec_lines.values():
                    spec_lines.lines = [_insert_or_before_goal(reaction_or_frag, line)
                                        for line in spec_lines.lines]

        # Aggregate all the propositions
        # Identify goal numbers as we loop over sys lines
        sys_lines = []
        # The zeroth goal is always []<>(TRUE), so we skip it.
        goal_idx = 1
        for input_text, command_spec_lines in self.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 self.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)

        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 tree:", self.generation_trees
        return (env_lines, sys_lines, custom_props, custom_sensors, results, responses,
                self.generation_trees)
Exemple #20
0
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))]
Exemple #21
0
    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, [])
Exemple #22
0
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]]
Exemple #23
0
    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, [])
Exemple #24
0
def _frag_react_avoid(region):
    """Generate a fragment to reactively not go somewhere."""
    return not_(sys_(region))
Exemple #25
0
def _frag_props_off(props):
    """Generate a fragment for props being off in the initial state."""
    return and_([not_(sys_(prop)) for prop in props])
Exemple #26
0
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))]
Exemple #27
0
def _frag_react_go(region):
    """Generate a fragment to reactively go somewhere."""
    return sys_(region)
Exemple #28
0
def _frag_props_off(props):
    """Generate a fragment for props being off in the initial state."""
    return and_([not_(sys_(prop)) for prop in props])
Exemple #29
0
def _frag_react_avoid(region):
    """Generate a fragment to reactively not go somewhere."""
    return not_(sys_(region))
Exemple #30
0
 def _gen_begin(self, command):
     """Generate statements to begin in a location."""
     region = command.theme.name
     explanation = "The robot begins in {!r}.".format(region)
     sys_lines = SpecChunk(explanation, [sys_(region)], SpecChunk.SYS, command)
     return ([sys_lines], [], [], [])
Exemple #31
0
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]]
Exemple #32
0
def _frag_react_deactivate(command):
    """Generate a fragment to activate an actuator."""
    return not_(next_(sys_(command.theme.name)))
Exemple #33
0
def _frag_react_go(region):
    """Generate a fragment to reactively go somewhere."""
    return sys_(region)
Exemple #34
0
    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)
Exemple #35
0
def _frag_react_deactivate(command):
    """Generate a fragment to activate an actuator."""
    return not_(next_(sys_(command.theme.name)))
Exemple #36
0
    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)