|
1 | 1 | import logging |
2 | | -from collections.abc import Sequence |
| 2 | +from collections.abc import Callable, Iterable, Sequence |
3 | 3 | from dataclasses import dataclass, field |
4 | 4 |
|
5 | 5 | from umbi.datatypes import Numeric, is_numeric_a_probability |
6 | 6 |
|
7 | | -from .base_mixin import BaseMixin, CompositeMixin |
8 | 7 | from .entity_space import EntityMapping |
9 | 8 | from .entity_space_mixins import HasBranchSpace, HasChoiceSpace, HasStateSpace |
10 | 9 |
|
@@ -155,12 +154,24 @@ def validate(self) -> None: |
155 | 154 | # TODO check that probabilities sum to 1 |
156 | 155 | super().validate() |
157 | 156 |
|
158 | | - def new_state_choice(self, state: int) -> int: |
159 | | - """Add a choice to the given state.""" |
| 157 | + def new_state_choice( |
| 158 | + self, |
| 159 | + state: int, |
| 160 | + targets: Iterable[int] | None = None, |
| 161 | + target_prob: Callable[[int], Numeric | None] | None = None, |
| 162 | + ) -> int: |
| 163 | + """Add a choice to the given state. |
| 164 | + :param state: source state for the new choice |
| 165 | + :param targets: optional iterable of target states for the branches of the new choice |
| 166 | + :param target_prob: optional function mapping target states to branch probabilities |
| 167 | + :return: the new choice index |
| 168 | + """ |
160 | 169 | # self.state_space._check_entity(state) |
161 | 170 | choice = self._new_choice() |
162 | 171 | self.state_to_choices[state].append(choice) |
163 | 172 | self._choice_to_state[choice] = state |
| 173 | + if targets is not None and target_prob is not None: |
| 174 | + self.new_choice_branches(choice, targets=targets, target_prob=target_prob) |
164 | 175 | return choice |
165 | 176 |
|
166 | 177 | def remove_choice(self, choice: int) -> list[int]: |
@@ -196,6 +207,22 @@ def new_choice_branch(self, choice: int, target: int | None = None, prob: Numeri |
196 | 207 | self._branch_to_probability[new_branch] = prob |
197 | 208 | return new_branch |
198 | 209 |
|
| 210 | + def new_choice_branches( |
| 211 | + self, choice: int, targets: Iterable[int], target_prob: Callable[[int], Numeric | None] |
| 212 | + ) -> list[int]: |
| 213 | + """Add branches to the given choice for multiple targets. |
| 214 | + :param choice: choice to add branches to |
| 215 | + :param targets: target states for the new branches |
| 216 | + :param target_prob: function mapping target states to branch probabilities |
| 217 | + :return: list of new branches added |
| 218 | + """ |
| 219 | + new_branches = [] |
| 220 | + for target in targets: |
| 221 | + prob = target_prob(target) |
| 222 | + branch = self.new_choice_branch(choice, target=target, prob=prob) |
| 223 | + new_branches.append(branch) |
| 224 | + return new_branches |
| 225 | + |
199 | 226 | def remove_branch(self, branch: int) -> None: |
200 | 227 | self._branch_space.check_entity(branch) |
201 | 228 | choice = self._branch_to_choice[branch] |
|
0 commit comments