def located( arg1s: Union[_ObjectT, Iterable[_ObjectT]], arg2s: Union[_ObjectT, Iterable[_ObjectT]], *, distance: Distance, # We need to do `Direction[Any]` because we can't infer the generic type # when a GeonAxis is used as the relative_to_axis. # Using Any here let's use isolate the type:ignores to this function. direction: Direction[Any], ) -> Tuple[Relation[_ObjectT]]: """ All *arg1s* are located with at the given `Distance` and `Direction` with respect to *args2*. It is usually better to use more specialized relations derived using `make_opposite_dsl_region_relation`, etc., but then can be useful when you need, for example, to refer to particular concrete axes. """ arg1s = _ensure_iterable(arg1s) arg2s = _ensure_iterable(arg2s) return flatten([ tuple( Relation(IN_REGION, arg1, Region(arg2, distance=distance, direction=direction)) for arg1 in arg1s for arg2 in arg2s), tuple( Relation( IN_REGION, arg2, Region(arg1, distance=distance, direction=direction.opposite()), ) for arg1 in arg1s for arg2 in arg2s), ])
def test_dynamic_perception_graph_instantiation(): ball = ObjectPerception("ball", _BALL_SCHEMA.geon.copy()) table = ObjectPerception("table", axes=_TABLE_SCHEMA.axes.copy()) first_frame = DevelopmentalPrimitivePerceptionFrame( perceived_objects=[ball, table], relations=[ above(ball, table), Relation(IN_REGION, ball, Region(table, distance=EXTERIOR_BUT_IN_CONTACT)), Relation(IN_REGION, table, Region(ball, distance=EXTERIOR_BUT_IN_CONTACT)), ], ) second_frame = DevelopmentalPrimitivePerceptionFrame( perceived_objects=[ball, table], relations=[Relation(IN_REGION, ball, Region(table, distance=DISTAL))], ) perception_graph = PerceptionGraph.from_dynamic_perceptual_representation( PerceptualRepresentation(frames=[first_frame, second_frame])) assert perception_graph.dynamic # Ensure we don't attempt to handle more than two frames yet. with pytest.raises(ValueError): PerceptionGraph.from_dynamic_perceptual_representation( PerceptualRepresentation( frames=[first_frame, second_frame, second_frame]))
def _go_under_template( agent: TemplateObjectVariable, goal_object: TemplateObjectVariable, background: Iterable[TemplateObjectVariable], *, is_distal: bool, # pylint:disable=unused-argument ) -> Phase1SituationTemplate: return Phase1SituationTemplate( f"go_under-{agent.handle}-under-{goal_object.handle}", salient_object_variables=[agent, goal_object], background_object_variables=background, actions=[ Action( GO, argument_roles_to_fillers=[ (AGENT, agent), ( GOAL, Region( goal_object, distance=PROXIMAL, direction=GRAVITATIONAL_DOWN ), ), ], ) ], before_action_relations=[negate(on(goal_object, GROUND_OBJECT_TEMPLATE))], asserted_always_relations=[negate(on(goal_object, GROUND_OBJECT_TEMPLATE))], after_action_relations=[ negate(on(goal_object, GROUND_OBJECT_TEMPLATE)), near(agent, goal_object), ], constraining_relations=flatten_relations(bigger_than(goal_object, agent)), )
def test_no_region_in_gazed_objects(): putter = situation_object(MOM) object_put = situation_object(BALL) on_region_object = situation_object(TABLE) situation = HighLevelSemanticsSituation( salient_objects=[putter, object_put, on_region_object], actions=[ Action( PUT, argument_roles_to_fillers=[ (AGENT, putter), (THEME, object_put), ( GOAL, Region( on_region_object, distance=EXTERIOR_BUT_IN_CONTACT, direction=GRAVITATIONAL_UP, ), ), ], ) ], ontology=GAILA_PHASE_1_ONTOLOGY, ) assert situation.gazed_objects for object_ in situation.gazed_objects: assert not isinstance(object_, Region)
def make_bird_flies_over_a_house(): bird = situation_object(BIRD) house = situation_object(HOUSE) situation = HighLevelSemanticsSituation( ontology=GAILA_PHASE_1_ONTOLOGY, salient_objects=[bird, house], actions=[ Action( FLY, argument_roles_to_fillers=[(AGENT, bird)], during=DuringAction( at_some_point=[ Relation( IN_REGION, bird, Region( reference_object=house, distance=DISTAL, direction=Direction( positive=True, relative_to_axis=GRAVITATIONAL_AXIS_FUNCTION, ), ), ) ] ), ) ], ) return situation
def _put_on_body_part_template( # X puts Y on body part agent: TemplateObjectVariable, theme: TemplateObjectVariable, goal_reference: TemplateObjectVariable, background: Iterable[TemplateObjectVariable], ) -> Phase1SituationTemplate: return Phase1SituationTemplate( f"{agent.handle}-puts-{theme.handle}-on-{goal_reference.handle}", salient_object_variables=[agent, theme, goal_reference], background_object_variables=background, actions=[ Action( PUT, argument_roles_to_fillers=[ (AGENT, agent), (THEME, theme), ( GOAL, Region( goal_reference, distance=EXTERIOR_BUT_IN_CONTACT, direction=GRAVITATIONAL_UP, ), ), ], ) ], constraining_relations=flatten_relations( bigger_than([agent, goal_reference], theme) ), asserted_always_relations=flatten_relations(has(agent, goal_reference)), )
def test_relations(): # ball on a table ball = ObjectPerception("ball", _BALL_SCHEMA.geon.copy()) table = ObjectPerception("table", axes=_TABLE_SCHEMA.axes.copy()) PerceptualRepresentation.single_frame( DevelopmentalPrimitivePerceptionFrame( perceived_objects=[ball, table], relations=[ above(ball, table), Relation(IN_REGION, ball, Region(table, distance=EXTERIOR_BUT_IN_CONTACT)), Relation(IN_REGION, table, Region(ball, distance=EXTERIOR_BUT_IN_CONTACT)), ], ))
def test_objects_in_something_not_implcitly_grounded(): box = situation_object(ontology_node=BOX) ball = situation_object(ontology_node=BALL) situation = HighLevelSemanticsSituation( ontology=GAILA_PHASE_1_ONTOLOGY, salient_objects=[box, ball], always_relations=[ Relation( IN_REGION, ball, Region( box, distance=PROXIMAL, direction=Direction( positive=True, relative_to_axis=HorizontalAxisOfObject(box, index=0), ), ), ) ], ) perception = _PERCEPTION_GENERATOR.generate_perception( situation, chooser=RandomChooser.for_seed(0) ) first_frame = perception.frames[0] ball_perception = perception_with_handle(first_frame, "**ball_0") ground_perception = perception_with_handle(first_frame, "the ground") box_perception = perception_with_handle(first_frame, "**box_0") first_frame_relations = first_frame.relations assert on(ball_perception, ground_perception)[0] in first_frame_relations assert on(box_perception, ground_perception)[0] in first_frame_relations
def make_mom_put_ball_on_table(): mom = situation_object(ontology_node=MOM) ball = situation_object(ontology_node=BALL) table = situation_object(ontology_node=TABLE) return HighLevelSemanticsSituation( ontology=GAILA_PHASE_1_ONTOLOGY, salient_objects=[mom, ball, table], actions=[ Action( PUT, ( (AGENT, mom), (THEME, ball), ( GOAL, Region( reference_object=table, distance=EXTERIOR_BUT_IN_CONTACT, direction=GRAVITATIONAL_UP, ), ), ), ) ], )
def test_perceive_explicit_relations(): # we want to test that relations explicitly called out in the situation are perceived. # Such relations fall into three buckets: # those which hold before an action, # those which hold after an action, # and those which hold both before and after an action. # To test all three of these at once, we use a situation where # (a) Mom is putting a ball on a table # (b) before the action the ball is far from a box, # (c) but after the action the ball is near the box, # (d) throughout the action the box is on the table. mom = situation_object(MOM) ball = situation_object(BALL) box = situation_object(BOX) table = situation_object(TABLE) situation = HighLevelSemanticsSituation( ontology=GAILA_PHASE_1_ONTOLOGY, salient_objects=[mom, box, ball, table], always_relations=[on(ball, table)], before_action_relations=[far(ball, box)], after_action_relations=[near(ball, box)], actions=[ Action( PUT, argument_roles_to_fillers=[ (AGENT, mom), (THEME, ball), ( GOAL, Region( reference_object=table, distance=EXTERIOR_BUT_IN_CONTACT, direction=GRAVITATIONAL_UP, ), ), ], ) ], ) perception = _PERCEPTION_GENERATOR.generate_perception( situation, chooser=RandomChooser.for_seed(0) ) ball_perception = perception_with_handle(perception.frames[0], "**ball_0") box_perception = perception_with_handle(perception.frames[0], "**box_0") table_perception = perception_with_handle(perception.frames[0], "**table_0") assert only(on(ball_perception, table_perception)) in perception.frames[0].relations assert only(on(ball_perception, table_perception)) in perception.frames[0].relations assert only(far(ball_perception, box_perception)) in perception.frames[0].relations assert ( only(far(ball_perception, box_perception)) not in perception.frames[1].relations ) assert ( only(near(ball_perception, box_perception)) not in perception.frames[0].relations ) assert only(near(ball_perception, box_perception)) in perception.frames[1].relations
def test_difference(): ball = ObjectPerception("ball", _BALL_SCHEMA.geon.copy()) cup = ObjectPerception("cup", _make_cup_schema().geon.copy()) table = ObjectPerception("table", axes=_TABLE_SCHEMA.axes.copy()) first_frame = DevelopmentalPrimitivePerceptionFrame( perceived_objects=[ball, table], relations=[ above(ball, table), Relation(IN_REGION, ball, Region(table, distance=EXTERIOR_BUT_IN_CONTACT)), Relation(IN_REGION, table, Region(ball, distance=EXTERIOR_BUT_IN_CONTACT)), ], ) second_frame = DevelopmentalPrimitivePerceptionFrame( perceived_objects=[ball, table, cup], relations=[above(ball, table)]) diff = diff_primitive_perception_frames(before=first_frame, after=second_frame) assert len(diff.removed_relations) == 2 assert not diff.added_relations assert len(diff.added_objects) == 1 assert not diff.removed_objects assert diff.before_axis_info == first_frame.axis_info assert diff.after_axis_info == second_frame.axis_info assert not diff.added_property_assertions assert not diff.removed_property_assertions # Reversed diff_2 = diff_primitive_perception_frames(before=second_frame, after=first_frame) assert len(diff_2.added_relations) == 2 assert not diff_2.removed_relations assert not diff_2.added_objects assert len(diff_2.removed_objects) == 1 assert diff_2.before_axis_info == second_frame.axis_info assert diff_2.after_axis_info == first_frame.axis_info assert not diff_2.added_property_assertions assert not diff_2.removed_property_assertions
def __init__( self, object_perception_to_bounding_box: Mapping[ObjectPerception, AxisAlignedBoundingBox], in_region_relations: Mapping[ObjectPerception, List[Region[ObjectPerception]]], ) -> None: # pylint: disable=useless-super-delegation super().__init__() self.object_perception_to_bounding_box = object_perception_to_bounding_box self.in_region_relations = in_region_relations self.ground_region = Region(GROUND_PERCEPTION, EXTERIOR_BUT_IN_CONTACT, GRAVITATIONAL_UP)
def test_perceive_relations_during(): learner_perception = _PERCEPTION_GENERATOR.generate_perception( make_bird_flies_over_a_house(), chooser=RandomChooser.for_seed(0) ) assert learner_perception.during bird = perception_with_handle(learner_perception.frames[0], "**bird_0") house = perception_with_handle(learner_perception.frames[0], "**house_0") bird_over_the_house = Relation( IN_REGION, bird, Region(reference_object=house, distance=DISTAL, direction=GRAVITATIONAL_UP), ) assert bird_over_the_house in learner_perception.during.at_some_point
def test_liquid_in_and_out_of_container(): juice = situation_object(JUICE) box = situation_object(ontology_node=BOX) table = situation_object(ontology_node=TABLE) two_d_situation = HighLevelSemanticsSituation( ontology=GAILA_PHASE_1_ONTOLOGY, salient_objects=[juice, table], always_relations=[on(juice, table)], ) two_d_perception = _PERCEPTION_GENERATOR.generate_perception( two_d_situation, chooser=RandomChooser.for_seed(0) ) three_d_situation = HighLevelSemanticsSituation( ontology=GAILA_PHASE_1_ONTOLOGY, salient_objects=[juice, box], always_relations=[ Relation(IN_REGION, juice, Region(box, distance=INTERIOR, direction=None)) ], ) three_d_perception = _PERCEPTION_GENERATOR.generate_perception( three_d_situation, chooser=RandomChooser.for_seed(0) ) two_perceived_objects = two_d_perception.frames[0].perceived_objects two_object_handles = set(obj.debug_handle for obj in two_perceived_objects) assert all(handle in two_object_handles for handle in {"**juice_0", "**table_0"}) three_perceived_objects = three_d_perception.frames[0].perceived_objects three_object_handles = set(obj.debug_handle for obj in three_perceived_objects) assert all(handle in three_object_handles for handle in {"**juice_0", "**box_0"}) assert any( isinstance(p, HasBinaryProperty) and perception_with_handle(two_d_perception.frames[0], "**juice_0") == p.perceived_object and p.binary_property == TWO_DIMENSIONAL for p in two_d_perception.frames[0].property_assertions ) assert not any( isinstance(p, HasBinaryProperty) and perception_with_handle(three_d_perception.frames[0], "**juice_0") == p.perceived_object and p.binary_property == TWO_DIMENSIONAL for p in three_d_perception.frames[0].property_assertions )
def test_relations_between_objects_and_ground(): # person_put_ball_on_table person = situation_object(ontology_node=PERSON) ball = situation_object(ontology_node=BALL) table = situation_object(ontology_node=TABLE) situation = HighLevelSemanticsSituation( ontology=GAILA_PHASE_1_ONTOLOGY, salient_objects=[person, ball, table], actions=[ # What is the best way of representing the destination in the high-level semantics? # Here we represent it as indicating a relation which should be true. Action( PUT, ( (AGENT, person), (THEME, ball), ( GOAL, Region( reference_object=table, distance=EXTERIOR_BUT_IN_CONTACT, direction=GRAVITATIONAL_UP, ), ), ), ) ], ) perception = _PERCEPTION_GENERATOR.generate_perception( situation, chooser=RandomChooser.for_seed(0) ) first_frame = perception.frames[0] ball_perception = perception_with_handle(first_frame, "**ball_0") ground_perception = perception_with_handle(first_frame, "the ground") table_perception = perception_with_handle(first_frame, "**table_0") first_frame_relations = first_frame.relations second_frame_relations = perception.frames[1].relations assert on(ball_perception, ground_perception)[0] in first_frame_relations assert on(ball_perception, ground_perception)[0] not in second_frame_relations assert on(table_perception, ground_perception)[0] in first_frame_relations assert on(table_perception, ground_perception)[0] in second_frame_relations
def test_gravity_constraint() -> None: aabb_floating = AxisAlignedBoundingBox.create_at_center_point( center=np.array([0, 0, 5]) ) # boxes are 2 units tall by default, so this one is resting on the ground aabb_grounded = AxisAlignedBoundingBox.create_at_center_point( center=np.array([0, 0, 1]) ) ground_region = Region(GROUND_PERCEPTION, EXTERIOR_BUT_IN_CONTACT, GRAVITATIONAL_UP) floating_perception = ObjectPerception( "floating_thing", axes=Axes( primary_axis=symmetric_vertical("floating-thing-generating"), orienting_axes=immutableset( [symmetric("side-to-side0"), symmetric("side-to-side1")] ), ), ) grounded_perception = ObjectPerception( "grounded_thing", axes=Axes( primary_axis=symmetric_vertical("grounded-thing-generating"), orienting_axes=immutableset( [symmetric("side-to-side0"), symmetric("side-to-side1")] ), ), ) gravity_penalty = WeakGravityPenalty( {floating_perception: aabb_floating, grounded_perception: aabb_grounded}, {floating_perception: [ground_region], grounded_perception: [ground_region]}, ) floating_result = gravity_penalty(aabb_floating, immutableset([ground_region])) assert floating_result > 0 grounded_result = gravity_penalty(aabb_grounded, immutableset([ground_region])) assert grounded_result <= 0
def test_path_from_action_description(): ball = situation_object(BALL) situation = HighLevelSemanticsSituation( ontology=GAILA_PHASE_1_ONTOLOGY, salient_objects=[ball], actions=[Action(FALL, argument_roles_to_fillers=[(THEME, ball)])], ) perception = _PERCEPTION_GENERATOR.generate_perception( situation, chooser=RandomChooser.for_seed(0)) ball_perception = perception_with_handle(perception.frames[0], "**ball_0") ground_perception = perception_with_handle(perception.frames[0], "the ground") assert perception.during assert perception.during.objects_to_paths assert len(perception.during.objects_to_paths) == 1 path = only(perception.during.objects_to_paths[ball_perception]) assert path.reference_destination_object == ground_perception assert path.reference_source_object == Region(ground_perception, distance=DISTAL) assert path.operator == TOWARD
def test_grounding_of_unsupported_objects(): box = situation_object(ontology_node=BOX) ball = situation_object(ontology_node=BALL) situation = HighLevelSemanticsSituation( ontology=GAILA_PHASE_1_ONTOLOGY, salient_objects=[box, ball], always_relations=[Relation(IN_REGION, ball, Region(box, distance=INTERIOR))], ) perception = _PERCEPTION_GENERATOR.generate_perception( situation, chooser=RandomChooser.for_seed(0) ) first_frame = perception.frames[0] ball_perception = perception_with_handle(first_frame, "**ball_0") ground_perception = perception_with_handle(first_frame, "the ground") box_perception = perception_with_handle(first_frame, "**box_0") first_frame_relations = first_frame.relations assert on(ball_perception, ground_perception)[0] not in first_frame_relations assert on(box_perception, ground_perception)[0] in first_frame_relations
def _go_in_template( agent: TemplateObjectVariable, goal_object: TemplateObjectVariable, background: Iterable[TemplateObjectVariable], ) -> Phase1SituationTemplate: return Phase1SituationTemplate( f"go_in-{agent.handle}-in-{goal_object.handle}", salient_object_variables=[agent, goal_object], background_object_variables=background, actions=[ Action( GO, argument_roles_to_fillers=[ (AGENT, agent), (GOAL, Region(goal_object, distance=INTERIOR)), ], ) ], constraining_relations=flatten_relations(bigger_than(goal_object, agent)), after_action_relations=[inside(agent, goal_object)], )
def _go_to_template( agent: TemplateObjectVariable, goal_object: TemplateObjectVariable, background: Iterable[TemplateObjectVariable], ) -> Phase1SituationTemplate: return Phase1SituationTemplate( f"go_to-{agent.handle}-to-{goal_object.handle}", salient_object_variables=[agent, goal_object], background_object_variables=background, actions=[ Action( GO, argument_roles_to_fillers=[ (AGENT, agent), (GOAL, Region(goal_object, distance=PROXIMAL)), ], ) ], after_action_relations=[near(agent, goal_object)], gazed_objects=[agent], )
def _jump_over_template( # "Mom jumps over a ball" agent: TemplateObjectVariable, object_in_path: TemplateObjectVariable, background: Iterable[TemplateObjectVariable], ) -> Phase1SituationTemplate: return Phase1SituationTemplate( f"{agent.handle}-jumps-over-{object_in_path.handle}", salient_object_variables=[agent, object_in_path], background_object_variables=background, actions=[ Action( JUMP, argument_roles_to_fillers=[(AGENT, agent)], during=DuringAction( at_some_point=flatten_relations( strictly_above(agent, object_in_path) ), objects_to_paths=[ ( agent, SpatialPath( operator=VIA, reference_source_object=Region( object_in_path, direction=GRAVITATIONAL_UP, distance=DISTAL, ), reference_destination_object=GROUND_OBJECT_TEMPLATE, ), ) ], ), auxiliary_variable_bindings=[ (JUMP_INITIAL_SUPPORTER_AUX, GROUND_OBJECT_TEMPLATE) ], ) ], asserted_always_relations=[negate(on(agent, GROUND_OBJECT_TEMPLATE))], )
def _put_in_template( agent: TemplateObjectVariable, theme: TemplateObjectVariable, goal_reference: TemplateObjectVariable, background: Iterable[TemplateObjectVariable], ) -> Phase1SituationTemplate: return Phase1SituationTemplate( f"{agent.handle}-puts-{theme.handle}-in-{goal_reference.handle}", salient_object_variables=[agent, theme, goal_reference], background_object_variables=background, actions=[ Action( PUT, argument_roles_to_fillers=[ (AGENT, agent), (THEME, theme), (GOAL, Region(goal_reference, distance=INTERIOR)), ], ) ], constraining_relations=flatten_relations(bigger_than(goal_reference, theme)), )
def test_person_put_ball_on_table(): person = situation_object(ontology_node=PERSON) ball = situation_object(ontology_node=BALL) table = situation_object(ontology_node=TABLE) situation = HighLevelSemanticsSituation( ontology=GAILA_PHASE_1_ONTOLOGY, salient_objects=[person, ball, table], actions=[ # What is the best way of representing the destination in the high-level semantics? # Here we represent it as indicating a relation which should be true. Action( PUT, ( (AGENT, person), (THEME, ball), ( GOAL, Region( reference_object=table, distance=EXTERIOR_BUT_IN_CONTACT, direction=GRAVITATIONAL_UP, ), ), ), ) ], ) perception = _PERCEPTION_GENERATOR.generate_perception( situation, chooser=RandomChooser.for_seed(0) ) assert len(perception.frames) == 2 first_frame = perception.frames[0] person_perception = perception_with_handle(first_frame, "**person_0") ball_perception = perception_with_handle(first_frame, "**ball_0") table_perception = perception_with_handle(first_frame, "**table_0") hand_perception = perception_with_handle(first_frame, "**hand_0") assert ( HasBinaryProperty(person_perception, ANIMATE) in first_frame.property_assertions ) assert ( HasBinaryProperty(person_perception, SELF_MOVING) in first_frame.property_assertions ) assert ( HasBinaryProperty(ball_perception, INANIMATE) in first_frame.property_assertions ) assert ( HasBinaryProperty(table_perception, INANIMATE) in first_frame.property_assertions ) first_frame_relations = first_frame.relations second_frame_relations = perception.frames[1].relations # assert we generate at least some relations in each frame assert first_frame_relations assert second_frame_relations assert ( Relation(SMALLER_THAN, ball_perception, person_perception) in first_frame_relations ) # Disabled because of https://github.com/isi-vista/adam/issues/673 # assert Relation(PART_OF, hand_perception, person_perception) in first_frame_relations assert ( Relation( IN_REGION, ball_perception, Region(reference_object=hand_perception, distance=EXTERIOR_BUT_IN_CONTACT), ) in first_frame_relations ) # continuing relations: assert ( Relation(SMALLER_THAN, ball_perception, person_perception) in second_frame_relations ) # new relations: ball_on_table_relation = Relation( IN_REGION, ball_perception, Region( table_perception, distance=EXTERIOR_BUT_IN_CONTACT, direction=GRAVITATIONAL_UP ), ) assert ball_on_table_relation in second_frame_relations # removed relations: assert ( Relation( IN_REGION, ball_perception, Region(reference_object=hand_perception, distance=EXTERIOR_BUT_IN_CONTACT), ) not in second_frame_relations ) # proto-role properties assert ( HasBinaryProperty(person_perception, VOLITIONALLY_INVOLVED) in first_frame.property_assertions ) assert ( HasBinaryProperty(person_perception, CAUSES_CHANGE) in first_frame.property_assertions ) assert ( HasBinaryProperty(ball_perception, UNDERGOES_CHANGE) in first_frame.property_assertions ) assert ( HasBinaryProperty(table_perception, STATIONARY) in first_frame.property_assertions )
def test_in_region_constraint() -> None: ball = ObjectPerception( "ball", axes=Axes( primary_axis=symmetric_vertical("ball-generating"), orienting_axes=immutableset( [symmetric("side-to-side0"), symmetric("side-to-side1")] ), ), ) box = ObjectPerception( "box", axes=Axes( primary_axis=straight_up("top_to_bottom"), orienting_axes=immutableset( [symmetric("side-to-side0"), symmetric("side-to-side1")] ), ), ) aabb_ball = AxisAlignedBoundingBox.create_at_center_point(center=np.array([0, 0, 1])) aabb_box = AxisAlignedBoundingBox.create_at_center_point(center=np.array([0, -2, 1])) # specifying that the box should be to the right of the ball direction = Direction( positive=True, relative_to_axis=HorizontalAxisOfObject(ball, index=0) ) region = Region(ball, PROXIMAL, direction) obj_percept_to_aabb = {ball: aabb_ball, box: aabb_box} in_region_relations = {box: [region]} in_region_penalty = InRegionPenalty(obj_percept_to_aabb, {}, {}, in_region_relations) box_penalty = in_region_penalty(box, immutableset([region])) assert box_penalty > 0 # now with a box that *is* to the right of the ball box2 = ObjectPerception( "box2", axes=Axes( primary_axis=straight_up("top_to_bottom"), orienting_axes=immutableset( [symmetric("side-to-side0"), symmetric("side-to-side1")] ), ), ) aabb_box2 = AxisAlignedBoundingBox.create_at_center_point(center=np.array([2, 0, 1])) obj_percept_to_aabb = {ball: aabb_ball, box2: aabb_box2} in_region_relations = {box2: [region]} in_region_penalty = InRegionPenalty(obj_percept_to_aabb, {}, {}, in_region_relations) box_penalty = in_region_penalty(box2, immutableset([region])) assert box_penalty == 0