Skip to content

Commit 73d2425

Browse files
committed
fix-3
1 parent 009678d commit 73d2425

13 files changed

Lines changed: 2165 additions & 1 deletion

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ exclude = [
7171
".git",
7272
"__pycache__",
7373
]
74-
target-version = "py311"
74+
target-version = "py312"
7575

7676
[tool.ruff.lint]
7777
select = ["I"] # enable isort (import sorting) rules

umbi/ats/actions_mixin.py

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import logging
2+
from collections.abc import Sequence
3+
from dataclasses import dataclass, field
4+
5+
from .choices_mixin import HasBranchSpace, HasChoiceSpace
6+
from .entity_space import EntityMapping, OptionalMappingManager
7+
from .entity_space_mixins import HasBranchActionSpace, HasChoiceActionSpace
8+
9+
logger = logging.getLogger(__name__)
10+
11+
12+
@dataclass
13+
class ChoiceToChoiceActionMixin(HasChoiceSpace, HasChoiceActionSpace):
14+
#: Choice-to-choice-action mapping manager.
15+
_choice_to_choice_action_manager: OptionalMappingManager[int] = field(init=False)
16+
17+
def __post_init__(self):
18+
self._choice_to_choice_action_manager = OptionalMappingManager[int](
19+
name="choice_to_choice_action",
20+
domain=self._choice_space,
21+
can_have_mapping=lambda: self._can_have_choice_to_choice_action(),
22+
must_have_mapping=lambda: self._must_have_choice_to_choice_action(),
23+
)
24+
self._choice_action_space._subscribe(self, lambda *args: self._choice_to_choice_action_manager.auto_manage())
25+
super().__post_init__()
26+
27+
def _can_have_choice_to_choice_action(self) -> bool:
28+
return self._choice_action_space.has_entities
29+
30+
def _must_have_choice_to_choice_action(self) -> bool:
31+
return self._can_have_choice_to_choice_action()
32+
33+
@property
34+
def has_choice_to_choice_action(self) -> bool:
35+
return self._choice_to_choice_action_manager.has_mapping
36+
37+
@property
38+
def choice_to_choice_action(self) -> EntityMapping[int]:
39+
return self._choice_to_choice_action_manager.mapping
40+
41+
@choice_to_choice_action.setter
42+
def choice_to_choice_action(self, values: Sequence[int]) -> None:
43+
self._choice_to_choice_action_manager.mapping = values
44+
45+
def validate(self):
46+
self._choice_to_choice_action_manager.validate()
47+
super().validate()
48+
49+
50+
@dataclass
51+
class ChoiceActionToNameMixin(HasChoiceActionSpace):
52+
#: Choice-to-choice-action mapping manager.
53+
_choice_action_to_name_manager: OptionalMappingManager[str] = field(init=False)
54+
55+
def __post_init__(self):
56+
self._choice_action_to_name_manager = OptionalMappingManager[str](
57+
name="choice_action_to_name",
58+
domain=self._choice_action_space,
59+
can_have_mapping=lambda: self._can_have_choice_action_to_name(),
60+
must_have_mapping=lambda: self._must_have_choice_action_to_name(),
61+
)
62+
self._choice_action_space._subscribe(self, lambda *args: self._choice_action_to_name_manager.auto_manage())
63+
super().__post_init__()
64+
65+
def _can_have_choice_action_to_name(self) -> bool:
66+
return self._choice_action_space.has_entities
67+
68+
def _must_have_choice_action_to_name(self) -> bool:
69+
return False # choice_action_to_name is always optional
70+
71+
def new_choice_action_to_name(self):
72+
self._choice_action_to_name_manager.create_mapping()
73+
74+
def remove_choice_action_to_name(self):
75+
self._choice_action_to_name_manager.remove_mapping()
76+
77+
@property
78+
def has_choice_action_to_name(self) -> bool:
79+
return self._choice_action_to_name_manager.has_mapping
80+
81+
@property
82+
def choice_action_to_name(self) -> EntityMapping[str]:
83+
return self._choice_action_to_name_manager.mapping
84+
85+
@choice_action_to_name.setter
86+
def choice_action_to_name(self, values: Sequence[str] | None) -> None:
87+
self._choice_action_to_name_manager.mapping = values
88+
89+
def validate(self):
90+
self._choice_action_to_name_manager.validate()
91+
super().validate()
92+
93+
94+
@dataclass
95+
class BranchToBranchActionMixin(HasBranchSpace, HasBranchActionSpace):
96+
#: Branch-to-branch-action mapping manager.
97+
_branch_to_branch_action_manager: OptionalMappingManager[int] = field(init=False)
98+
99+
def __post_init__(self):
100+
self._branch_to_branch_action_manager = OptionalMappingManager[int](
101+
name="branch_to_branch_action",
102+
domain=self._branch_space,
103+
can_have_mapping=lambda *args: self._can_have_branch_to_branch_action(),
104+
must_have_mapping=lambda *args: self._must_have_branch_to_branch_action(),
105+
)
106+
self._branch_action_space._subscribe(self, lambda *args: self._branch_to_branch_action_manager.auto_manage())
107+
super().__post_init__()
108+
109+
def _can_have_branch_to_branch_action(self) -> bool:
110+
return self._branch_action_space.has_entities
111+
112+
def _must_have_branch_to_branch_action(self) -> bool:
113+
return self._can_have_branch_to_branch_action()
114+
115+
@property
116+
def has_branch_to_branch_action(self) -> bool:
117+
return self._branch_to_branch_action_manager.has_mapping
118+
119+
@property
120+
def branch_to_branch_action(self) -> EntityMapping[int]:
121+
return self._branch_to_branch_action_manager.mapping
122+
123+
@branch_to_branch_action.setter
124+
def branch_to_branch_action(self, values: Sequence[int]) -> None:
125+
self._branch_to_branch_action_manager.mapping = values
126+
127+
def validate(self):
128+
self._branch_to_branch_action_manager.validate()
129+
super().validate()
130+
131+
132+
@dataclass
133+
class BranchActionToNameMixin(HasBranchActionSpace):
134+
#: Branch-to-branch-action mapping manager.
135+
_branch_action_to_name_manager: OptionalMappingManager[str] = field(init=False)
136+
137+
def __post_init__(self):
138+
self._branch_action_to_name_manager = OptionalMappingManager[str](
139+
name="branch_action_to_name",
140+
domain=self._branch_action_space,
141+
can_have_mapping=lambda *args: self._can_have_branch_action_to_name(),
142+
must_have_mapping=lambda *args: self._must_have_branch_action_to_name(),
143+
)
144+
self._branch_action_space._subscribe(self, lambda *args: self._branch_action_to_name_manager.auto_manage())
145+
super().__post_init__()
146+
147+
def _can_have_branch_action_to_name(self) -> bool:
148+
return self._branch_action_space.has_entities
149+
150+
def _must_have_branch_action_to_name(self) -> bool:
151+
return False # branch_action_to_name is always optional
152+
153+
def add_branch_action_to_name(self):
154+
self._branch_action_to_name_manager.create_mapping()
155+
156+
def remove_branch_action_to_name(self):
157+
self._branch_action_to_name_manager.remove_mapping()
158+
159+
@property
160+
def has_branch_action_to_name(self) -> bool:
161+
return self._branch_action_to_name_manager.has_mapping
162+
163+
@property
164+
def branch_action_to_name(self) -> EntityMapping[str]:
165+
return self._branch_action_to_name_manager.mapping
166+
167+
@branch_action_to_name.setter
168+
def branch_action_to_name(self, values: Sequence[str] | None) -> None:
169+
self._branch_action_to_name_manager.mapping = values
170+
171+
def validate(self):
172+
self._branch_action_to_name_manager.validate()
173+
super().validate()
174+
175+
176+
class ActionsMixin(
177+
ChoiceToChoiceActionMixin,
178+
ChoiceActionToNameMixin,
179+
BranchToBranchActionMixin,
180+
BranchActionToNameMixin,
181+
):
182+
def __post_init__(self):
183+
super().__post_init__()

umbi/ats/annotations_mixin.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import logging
2+
from dataclasses import dataclass, field
3+
4+
logger = logging.getLogger(__name__)
5+
6+
from .annotations import (
7+
Annotation,
8+
AtomicPropositionAnnotation,
9+
RewardAnnotation,
10+
)
11+
from .entity_space_mixins import HasCommonEntitySpaces
12+
13+
14+
@dataclass
15+
class AnnotationsMixin(HasCommonEntitySpaces):
16+
#: Annotation category -> (annotation name -> annotation) mapping. Categories 'rewards' and 'aps' can be used, but
17+
#: must be of the type RewardAnnotation and AtomicPropositionAnnotation, respectively.
18+
_annotations: dict[str, dict[str, Annotation]] = field(default_factory=lambda: {})
19+
20+
@property
21+
def annotations(self) -> dict[str, dict[str, Annotation]]:
22+
"""Get the annotations of the ATS."""
23+
return self._annotations
24+
25+
@property
26+
def annotation_categories(self) -> list[str]:
27+
"""Get the annotation categories of the ATS."""
28+
return list(self.annotations.keys())
29+
30+
def new_annotation(self, category: str, name: str, **kwargs) -> Annotation:
31+
"""Add an annotation to the ATS."""
32+
if category not in self.annotations:
33+
self.annotations[category] = {}
34+
if name in self.annotations[category]:
35+
raise ValueError(f"annotation with name {name} already exists in category {category}")
36+
annotation = Annotation(name=name, entity_spaces=self._common_entity_spaces, **kwargs)
37+
self.annotations[category][name] = annotation
38+
return annotation
39+
40+
@property
41+
def reward_annotations(self) -> dict[str, RewardAnnotation]:
42+
"""Get the reward annotations."""
43+
if "rewards" not in self.annotations:
44+
self.annotations["rewards"] = {}
45+
return self.annotations["rewards"] # type: ignore
46+
47+
@property
48+
def has_reward_annotations(self) -> bool:
49+
"""Check if there are any reward annotations."""
50+
return len(self.reward_annotations) > 0
51+
52+
@property
53+
def reward_annotation_names(self) -> list[str]:
54+
"""Get the names of all reward annotations."""
55+
return list(self.reward_annotations.keys())
56+
57+
def has_reward_annotation(self, name: str) -> bool:
58+
"""Check if a reward annotation with the given name exists."""
59+
return name in self.reward_annotations
60+
61+
def new_reward_annotation(self, name: str, **kwargs) -> RewardAnnotation:
62+
"""Add a reward annotation."""
63+
if self.has_reward_annotation(name):
64+
raise ValueError(f"reward annotation with name {name} already exists")
65+
annotation = RewardAnnotation(name=name, entity_spaces=self._common_entity_spaces, **kwargs)
66+
self.reward_annotations[name] = annotation
67+
return annotation
68+
69+
def get_reward_annotation(self, name: str) -> RewardAnnotation:
70+
"""Get the reward annotation with the given name."""
71+
if not self.has_reward_annotation(name):
72+
raise ValueError(f"reward annotation with name {name} does not exist")
73+
return self.reward_annotations[name]
74+
75+
### Atomic propositions. ###
76+
77+
@property
78+
def has_ap_annotations(self) -> bool:
79+
"""Check if there are any atomic proposition annotations."""
80+
return len(self.ap_annotations) > 0
81+
82+
@property
83+
def ap_annotations(self) -> dict[str, AtomicPropositionAnnotation]:
84+
"""Get the atomic proposition annotations."""
85+
if "aps" not in self.annotations:
86+
self.annotations["aps"] = {}
87+
return self.annotations["aps"] # type: ignore
88+
89+
@property
90+
def ap_annotation_names(self) -> list[str]:
91+
"""Get the names of all atomic proposition annotations."""
92+
return list(self.ap_annotations.keys())
93+
94+
def has_ap_annotation(self, name: str) -> bool:
95+
"""Check if an atomic proposition annotation with the given name exists."""
96+
return name in self.ap_annotations
97+
98+
def new_ap_annotation(self, name: str, **kwargs) -> AtomicPropositionAnnotation:
99+
"""Add an atomic proposition annotation."""
100+
if self.has_ap_annotation(name):
101+
raise ValueError(f"atomic proposition annotation with name {name} already exists")
102+
annotation = AtomicPropositionAnnotation(name=name, entity_spaces=self._common_entity_spaces, **kwargs)
103+
self.ap_annotations[name] = annotation
104+
return annotation
105+
106+
def get_ap_annotation(self, name: str) -> AtomicPropositionAnnotation:
107+
"""Get the atomic proposition annotation with the given name."""
108+
if not self.has_ap_annotation(name):
109+
raise ValueError(f"atomic proposition annotation with name {name} does not exist")
110+
return self.ap_annotations[name]
111+
112+
def validate(self):
113+
"""Validate annotations of the ATS."""
114+
for _, annotations in self.annotations.items():
115+
for _, annotation in annotations.items():
116+
annotation.validate()
117+
super().validate()

0 commit comments

Comments
 (0)