Esempio n. 1
0
 def _validate_start(self, attr, val):  # pylint:disable=unused-argument
     check_arg(
         self.start < self.end,
         "Start offset must be strictly less then end offset but "
         "got [%s,%s)",
         (self.start, self.end),
     )
Esempio n. 2
0
def sampled(
    situation_template: Phase1SituationTemplate,
    *,
    ontology: Ontology,
    chooser: SequenceChooser,
    max_to_sample: int,
    default_addressee_node: OntologyNode = LEARNER,
    block_multiple_of_the_same_type: bool,
) -> Iterable[HighLevelSemanticsSituation]:
    """
    Gets *max_to_sample* instantiations of *situation_template* with *ontology*
    """
    check_arg(max_to_sample >= 0)
    return list(
        take(
            max_to_sample,
            _Phase1SituationTemplateGenerator(
                ontology=ontology,
                variable_assigner=_SamplingVariableAssigner(),
                block_multiple_objects_of_the_same_type=
                block_multiple_of_the_same_type,
            ).generate_situations(
                situation_template,
                chooser=chooser,
                default_addressee_node=default_addressee_node,
            ),
        ))
Esempio n. 3
0
    def create_at_random_position_scaled(
        *,
        min_distance_from_origin: float,
        max_distance_from_origin: float,
        object_scale: torch.Tensor,
    ):
        check_arg(min_distance_from_origin > 0.0)
        check_arg(min_distance_from_origin < max_distance_from_origin)
        # we first generate a random point on the unit sphere by
        # generating a random vector in cube...
        center = np.random.randn(3, 1).squeeze()
        # and then normalizing.
        center /= np.linalg.norm(center)

        # then we scale according to the distances above
        scale_factor = np.random.uniform(min_distance_from_origin,
                                         max_distance_from_origin)
        center *= scale_factor
        return AxisAlignedBoundingBox(
            Parameter(
                torch.tensor(center, dtype=torch.float),  # pylint: disable=not-callable
                requires_grad=True,
            ),
            torch.diag(object_scale),
            offset=None,
        )
Esempio n. 4
0
 def __attrs_post_init__(self) -> None:
     for relation in self.relations:
         check_arg(
             not relation.negated,
             "Negated relations cannot appear in perceptual "
             "representations but got %s",
             (relation, ),
         )
Esempio n. 5
0
 def test_check_arg_interpolation(self):
     with self.assertRaisesRegex(
         ValueError, "Expected height to exceed 48 but got 41"
     ):
         height = 41
         reference = 48
         check_arg(
             height > reference,
             "Expected height to exceed %s but got %s",
             (reference, height),
         )
Esempio n. 6
0
 def __attrs_post_init__(self) -> None:
     # you either need a path operator
     #  or an orientation change around an axis
     #  (e.g. for rotation without translation)
     # weird conditional to make mypy happy
     if (not self.reference_object and not self.reference_axis
             and not self.orientation_changed):
         raise RuntimeError(
             "A path must have at least one of a reference objects, "
             "a reference axis, or an orientation change")
     if self.reference_axis:
         check_arg(isinstance(self.reference_axis,
                              (GeonAxis, AxisFunction)))
Esempio n. 7
0
 def put(self, key: str, value: V) -> None:
     check_state(self._zip_file, "Must use zip key-value sink as a context manager")
     check_not_none(key)
     check_not_none(value)
     if key in self._keys:
         raise ValueError(
             "Zip-backed key-value sinks do not support duplicate puts on the "
             "same key"
         )
     self._keys.add(key)
     filename = self._filename_function(key)
     check_arg(isinstance(value, str) or isinstance(value, bytes))
     self._zip_file.writestr(filename, value)  # type: ignore
Esempio n. 8
0
    def corners_onto_axes_projections(self,
                                      axes: torch.Tensor) -> torch.Tensor:
        """
        Projects each of 8 corners onto each of three axes.
        Args:
            axes: (3,3) tensor -> the three axes we are projecting points onto

        Returns:
            (3, 8) tensor -> each point projected onto each of three dimensions

        """
        check_arg(axes.shape == (3, 3))
        corners = self.get_corners()
        return axes.matmul(corners.transpose(0, 1))
Esempio n. 9
0
    def annotated_text(
        self,
        text: str,
        annotations: Collection[AnnotatedSpan],
        *,
        text_offsets: Optional[Span] = None,
    ) -> str:
        """
        Mark annotations on text in an HTML-like style.

        Each annotation will becomes an HTML tag wrapping the text at the corresponding offsets.
        Any attributes will become HTML attributes.

        This does not add any other HTML annotations (`head`, `body`, etc.), so if desired the
        user should add them afterwards.

        If `text_offsets` is specified, the annotations are assumed to have offsets with respect
        to some larger string, where `text` is a substring of that string with offsets
        `text_offsets` relative to it.  You might use this, for example, to render a single
        paragraph from a document.
        """
        if not text_offsets:
            text_offsets = Span.from_inclusive_to_exclusive(0, len(text))
        check_arg(
            len(text_offsets) == len(text),
            f"Text offsets length {len(text_offsets)} "
            f"does not match text length {len(text)}",
        )

        # we process the annotations to (a) ensure they all fit within the requested snippet
        # and (b) shift their offsets so that all offsets are relative to the text being
        # formatted
        processed_annotations = self._clip_to_offsets_and_shift(
            annotations, text_offsets)

        ret = io.StringIO()
        last_uncopied_offset = 0
        for tag in self._tag_sequence(processed_annotations):
            if last_uncopied_offset < tag.offset:
                ret.write(text[last_uncopied_offset:tag.offset])
                last_uncopied_offset = tag.offset

            ret.write(tag.string)

        # get any trailing text after last tag
        if last_uncopied_offset < text_offsets.end:
            ret.write(text[last_uncopied_offset:text_offsets.end])
        return ret.getvalue()
Esempio n. 10
0
def windowed(it,
             window_size: int,
             *,
             partial_windows: bool = False):  # noqa: F811
    check_arg(window_size >= 1)

    if not hasattr(it, "__next__"):
        return _WindowedIterable(wrapped_iterable=it,
                                 window_size=window_size,
                                 partial_windows=partial_windows)

    # we know at this point that it is an Iterable, but mypy might not, hence the ignores below
    if partial_windows:
        return _possibly_incomplete_windows(it, window_size)
    else:
        return _complete_windows(it, window_size)
Esempio n. 11
0
 def __attrs_post_init__(self) -> None:
     # disabled warning below is due to a PyCharm bug
     # noinspection PyTypeChecker
     for property_ in self.properties:
         if not isinstance(property_, OntologyNode):
             raise ValueError(
                 f"Situation object property {property_} is not an "
                 f"OntologyNode")
     for concrete_axis in self.schema_axis_to_object_axis.values():
         check_arg(concrete_axis in self.axes.all_axes)
     # Every object should either have axes mapped to the axes of a schema object,
     # or should have WORLD_AXES, which is what we use by default for things
     # like substances which have no particular shape.
     check_arg(
         self.schema_axis_to_object_axis or self.axes == WORLD_AXES,
         "Axes must be aligned to a scheme or else be WORLD_AXES",
     )
Esempio n. 12
0
    def get_min_max_overlaps(min_max_proj_0: torch.Tensor,
                             min_max_proj_1: torch.Tensor) -> torch.Tensor:
        """
        Given min/max corner projections onto 3 axes from two different objects,
        return an interval for each dimension representing the degree of overlap or
        separation between the two objects.
        Args:
            min_max_proj_0: Tensor(3,2) min_max_projections for box 0
            min_max_proj_1: Tensor(3,2) min_max projections for box 1

        Returns:
            (3, 2) tensor -> ranges (start, end) of overlap OR separation in each of three dimensions.
            If (start - end) is positive, this indicates that the boxes do not overlap along this dimension,
            otherwise, a negative value indicates an overlap along that dimension.
        """
        check_arg(min_max_proj_0.shape == (3, 2))
        check_arg(min_max_proj_1.shape == (3, 2))

        # see https://github.com/pytorch/pytorch/issues/24807 re: pylint issue
        dims = torch.tensor([0, 1, 2], dtype=torch.int)  # pylint: disable=not-callable

        mins_0 = min_max_proj_0.gather(1, torch.zeros((3, 1),
                                                      dtype=torch.long))
        mins_1 = min_max_proj_1.gather(1, torch.zeros((3, 1),
                                                      dtype=torch.long))

        combined_mins = torch.stack((mins_0, mins_1), 1).squeeze()
        max_indices = torch.max(combined_mins, 1)
        maximum_mins = torch.take(combined_mins, max_indices[1] + (dims * 2))

        # should stick together the minimum parts and the maximum parts
        # with columns like:
        # [ min0x   min1x
        #   min0y   min1y
        #   min0z   min1z
        #                ]
        # then find the maximum element from each row

        # repeat the process for the min of the max projections
        maxs_0 = min_max_proj_0.gather(1, torch.ones((3, 1), dtype=torch.long))
        maxs_1 = min_max_proj_1.gather(1, torch.ones((3, 1), dtype=torch.long))
        combined_maxes = torch.stack((maxs_0, maxs_1), 1).squeeze()
        min_indices = torch.min(combined_maxes, 1)
        minimum_maxes = torch.take(combined_maxes, min_indices[1] + (dims * 2))

        return torch.stack((maximum_mins, minimum_maxes), 1)
Esempio n. 13
0
def drop(it, num_to_skip: int):  # noqa: F811
    """
    Skip `num_to_skip` elements of an ``Iterable`` or ``Iterator``.

    If `it` is an ``Iterator``, makes an ``Iterator`` which returns elements of the original
    iterator starting from the `num_to_skip+1`th element.

    If `it` is an ``Iterable``, makes an ``Iterable`` which returns elements of the original
    iterable starting from the `num_to_skip+1`th element.

    `num_to_skip` must be non-negative or a `ValueError` will be raised.
    """
    check_arg(
        num_to_skip >= 0,
        "Number of items to skip must be positive but got %s",
        (num_to_skip, ),
    )
    if hasattr(it, "__next__"):
        return itertools.islice(it, num_to_skip, None)
    else:
        return _DropIterable(it, num_to_skip)
Esempio n. 14
0
 def __init__(
     self,
     path: Path,
     *,
     filename_function: Callable[[str], str] = _identity,
     keys_in_function: Callable[[ZipFile], Optional[AbstractSet[str]]] = None,
     keys_out_function: Callable[[ZipFile, AbstractSet[str]], None] = None,
     overwrite: bool = True,
 ) -> None:
     self._path = path
     self._zip_file: Optional[ZipFile] = None
     self._filename_function = filename_function
     self._overwrite = overwrite
     self._keys_in_function = keys_in_function
     self._keys_out_function = keys_out_function
     self._keys: Set[str] = set()
     check_arg(
         self._keys_out_function or not self._keys_in_function,
         "If you specify a key output function, you should also specify a key input"
         " function",
     )
Esempio n. 15
0
    def __attrs_post_init__(self) -> None:
        check_arg(self.salient_object_variables,
                  "A situation must contain at least one object")

        # ensure all objects referenced anywhere are on the object list
        objects_referenced_accumulator = list(self.salient_object_variables)
        for relation in chain(self.constraining_relations,
                              self.asserted_always_relations):
            relation.accumulate_referenced_objects(
                objects_referenced_accumulator)
        for action in self.actions:
            action.accumulate_referenced_objects(
                objects_referenced_accumulator)

        unique_objects_referenced = immutableset(
            objects_referenced_accumulator)
        missing_objects = unique_objects_referenced - self.all_object_variables
        if missing_objects:
            raise RuntimeError(
                f"Set of referenced objects {unique_objects_referenced} does not match "
                f"object variables {self.all_object_variables} for template {self}: "
                f"the following are missing {missing_objects}")
Esempio n. 16
0
    def __attrs_post_init__(self) -> None:
        for cycle in simple_cycles(self._graph):
            raise ValueError(
                f"The ontology graph may not have cycles but got {cycle}")

        for required_node in REQUIRED_ONTOLOGY_NODES:
            check_arg(
                required_node in self,
                f"Ontology lacks required {required_node.handle} node",
            )

        # every sub-type of THING must either have a structural schema
        # or be a sub-type of something with a structural schema
        for thing_node in dfs_preorder_nodes(self._graph.reverse(copy=False),
                                             THING):
            if not any(node in self._structural_schemata
                       for node in self.ancestors(thing_node)):
                # e.g. "milk" does not have a structural schema
                if self.has_all_properties(thing_node, [
                        CAN_FILL_TEMPLATE_SLOT
                ]) and not self.has_all_properties(thing_node, [IS_SUBSTANCE]):
                    raise RuntimeError(
                        f"No structural schema is available for {thing_node}")
Esempio n. 17
0
    def get_min_max_corner_projections(projections: torch.Tensor):
        """
        Retrieve the minimum and maximum corner projection (min/max extent in that dimension) for each axis
        Args:
            projections: Tensor(3, 8) -> corner projections onto each of three dimensions

        Returns:
            Tensor(3, 2) -> (min, max) values for each of three dimensions

        """
        check_arg(projections.shape == (3, 8))

        min_indices = torch.min(projections, 1)
        max_indices = torch.max(projections, 1)
        # these are tuples of (values, indices), both of which are tensors

        # helper variable for representing dimension numbers
        # see https://github.com/pytorch/pytorch/issues/24807 re: pylint issue
        dims = torch.tensor([0, 1, 2], dtype=torch.int)  # pylint: disable=not-callable
        # select the indexed items (from a 24 element tensor)
        minima = torch.take(projections, min_indices[1] + (dims * 8))
        maxima = torch.take(projections, max_indices[1] + (dims * 8))
        # stack the minim
        return torch.stack((minima, maxima), 1)
Esempio n. 18
0
    def overlap_penalty(min_max_overlaps: torch.Tensor) -> torch.Tensor:
        """
        Return penalty depending on degree of overlap between two 3d boxes.
        Args:
            min_max_overlaps: (3, 2) tensor -> intervals describing degree of overlap between the two boxes

        Returns: Tensor with a positive scalar of the collision penalty, or tensor with zero scalar
        for no collision.
        """
        check_arg(min_max_overlaps.shape == (3, 2))
        # subtract each minimum max from each maximum min:
        overlap_distance = min_max_overlaps[:, 0] - min_max_overlaps[:, 1]

        # as long as at least one dimension's overlap distance is positive (not overlapping),
        # then the boxes are not colliding
        for dim in range(3):
            if overlap_distance[dim] >= 0:
                return torch.zeros(1, dtype=torch.float)

        # otherwise the penetration distance is the maximum negative value
        # (the smallest translation that would disentangle the two

        # overlap is represented by a negative value, which we return as a positive penalty
        return overlap_distance.max() * -1 * COLLISION_PENALTY
Esempio n. 19
0
    def variable_assignments(
        self,
        *,
        ontology: Ontology,  # pylint:disable=unused-argument
        object_variables: AbstractSet["TemplateObjectVariable"],
        property_variables: AbstractSet["TemplatePropertyVariable"],
        action_variables: AbstractSet["TemplateActionTypeVariable"],
        chooser: SequenceChooser,  # pylint:disable=unused-argument
    ) -> Iterable[TemplateVariableAssignment]:
        check_arg(
            all(obj_var in self.assignment.object_variables_to_fillers
                for obj_var in object_variables))
        check_arg(
            all(prop_var in self.assignment.property_variables_to_fillers
                for prop_var in property_variables))
        check_arg(
            all(action_var in self.assignment.action_variables_to_fillers
                for action_var in action_variables))

        return (self.assignment, )
Esempio n. 20
0
 def __attrs_post_init__(self) -> None:
     check_arg(
         self.distance or self.direction,
         "A region must have either a distance or direction specified.",
     )
Esempio n. 21
0
 def __attrs_post_init__(self) -> None:
     check_arg(isinstance(self.relative_to_axis, (GeonAxis, AxisFunction)))
Esempio n. 22
0
 def __attrs_post_init__(self) -> None:
     check_arg(callable(self.learner_factory),
               "Learner factory must be callable")
Esempio n. 23
0
 def __attrs_post_init__(self) -> None:
     if self.geon:
         check_arg(self.geon.axes == self.axes)
Esempio n. 24
0
 def __attrs_post_init__(self) -> None:
     for node in self._ontology_node_to_word:
         check_arg(
             node in self.ontology,
             f"Ontology lexicon refers to non-ontology node {node}",
         )
Esempio n. 25
0
 def __attrs_post_init__(self) -> None:
     check_arg(not isinstance(self.second_slot, CanRemapObjects)
               or self.relation_type == IN_REGION)
Esempio n. 26
0
 def __attrs_post_init__(self) -> None:
     check_arg(
         len(self._sub_selectors) > 1,
         "_And requires at least two sub-selectors")
Esempio n. 27
0
 def _select_nodes(self, ontology: Ontology) -> AbstractSet[OntologyNode]:
     for node in self._nodes:
         check_arg(node in ontology, f"{node} is not in the ontology")
     return self._nodes
Esempio n. 28
0
    def __attrs_post_init__(self) -> None:
        check_arg(self.salient_objects, "A situation must contain at least one object")
        for relation in chain(
            self.always_relations,
            self.before_action_relations,
            self.after_action_relations,
        ):
            if not isinstance(relation.first_slot, SituationObject) or not isinstance(
                relation.second_slot, (SituationObject, Region)
            ):
                raise RuntimeError(
                    f"Relation fillers for situations must be situation objects "
                    f"but got {relation}"
                )
            if (
                isinstance(relation.second_slot, Region)
                and relation.second_slot.reference_object not in self.all_objects
            ):
                raise RuntimeError(
                    f"Any object referred to by a region must be included in the "
                    f"set of situation objects but region {relation.second_slot}"
                    f" with situation objects {self.all_objects}"
                )
            if relation.relation_type == IN_REGION and not isinstance(
                relation.second_slot, Region
            ):
                raise RuntimeError(
                    f"Any relation of relation type IN_REGION must have a second slot "
                    f"which is a region but got {relation.second_slot} instead"
                )
        self.is_dynamic = len(self.actions) > 0
        for action in self.actions:
            for action_role_filler in flatten(
                action.argument_roles_to_fillers.value_groups()
            ):
                if (
                    isinstance(action_role_filler, SituationObject)
                    and action_role_filler not in self.all_objects
                ):
                    raise RuntimeError(
                        "Any object filling a semantic role must be included in the "
                        "set of situation objects."
                    )
                elif (
                    isinstance(action_role_filler, Region)
                    and action_role_filler.reference_object not in self.all_objects
                ):
                    raise RuntimeError(
                        "Any object referred to by a region must be included in the "
                        "set of situation objects."
                    )
        if not self.actions and (
            self.before_action_relations or self.after_action_relations
        ):
            raise RuntimeError(
                "Cannot specify relations to hold before or after actions "
                "if there are no actions"
            )

        # A situation cannot have multiple instances of the same recognized particular.
        # This blocks e.g. Dad gave Dad a house.
        recognized_particular_count = Counter(
            object_.ontology_node
            for object_ in self.all_objects
            if is_recognized_particular(self.ontology, object_.ontology_node)
        )
        for (recognized_particular, count) in recognized_particular_count.items():
            if count > 1:
                raise RuntimeError(
                    f"Cannot have two instances of a recognized particular in a "
                    f"situation, but got {count} instances of {recognized_particular}"
                    f" in {self}"
                )

        for object_ in self.gazed_objects:
            if isinstance(object_, Region):
                raise RuntimeError(
                    f"Cannot have a Region as a gazed object in a situation, got"
                    f"{object_} which is a region."
                )
Esempio n. 29
0
    def generate_situations(
        self,
        template: Phase1SituationTemplate,
        *,
        chooser: SequenceChooser = Factory(RandomChooser.for_seed),  # pylint:disable=unused-argument
        default_addressee_node: OntologyNode = LEARNER,
    ) -> Iterable[HighLevelSemanticsSituation]:
        check_arg(isinstance(template, Phase1SituationTemplate))
        try:
            # gather property variables from object variables
            property_variables = immutableset(
                property_ for obj_var in template.salient_object_variables
                for property_ in obj_var.asserted_properties
                if isinstance(property_, TemplatePropertyVariable))

            action_type_variables = immutableset(
                action.action_type for action in template.actions
                if isinstance(action.action_type, TemplateActionTypeVariable))

            failures_in_a_row = 0

            for variable_assignment in self._variable_assigner.variable_assignments(
                    ontology=self.ontology,
                    object_variables=template.all_object_variables,
                    property_variables=property_variables,
                    action_variables=action_type_variables,
                    chooser=chooser,
            ):
                # instantiate all objects in the situation according to the variable assignment.
                object_var_to_instantiations = self._instantiate_objects(
                    template,
                    variable_assignment,
                    default_addressee_node=default_addressee_node,
                )

                # Cannot have multiple instantiations of the same recognized particular.
                # e.g. "Dad gave Dad a box"
                if self._has_multiple_recognized_particulars(
                        object_var_to_instantiations):
                    continue

                if self.block_multiple_objects_of_the_same_type:
                    object_instantiations_ontology_nodes = [
                        object_instantiation.ontology_node
                        for object_instantiation in
                        object_var_to_instantiations.values()
                    ]
                    if len(set(object_instantiations_ontology_nodes)) != len(
                            object_instantiations_ontology_nodes):
                        # There must be two objects of the same ontology type.
                        continue

                # use them to instantiate the entire situation
                situation = self._instantiate_situation(
                    template, variable_assignment,
                    object_var_to_instantiations)
                if self._satisfies_constraints(template, situation,
                                               object_var_to_instantiations):
                    failures_in_a_row = 0
                    yield situation
                else:
                    failures_in_a_row += 1
                    if failures_in_a_row >= 250:
                        raise RuntimeError(
                            f"Failed to find a satisfying variable assignment "
                            f"for situation template constraints after "
                            f"{failures_in_a_row} consecutive attempts."
                            f"Try shifting constraints from relations to properties."
                        )
                    continue
        except Exception as e:
            raise RuntimeError(
                f"Exception while generating from situation template {template}"
            ) from e