Skip to content

Commit 3c4d081

Browse files
committed
introduce EntityToAction
1 parent 0a81ce9 commit 3c4d081

5 files changed

Lines changed: 315 additions & 140 deletions

File tree

umbi/ats/ats_to_umb.py

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from .entity_class import EntityClass
1111
from .explicit_ats import ExplicitAts, TimeType
1212
from .model_info import ModelInfo
13-
from .variable_valuations import EntityClassValuations, EntityValuations
1413

1514

1615
def explicit_umb_to_explicit_ats(umb: umbi.umb.ExplicitUmb) -> ExplicitAts:
@@ -36,13 +35,10 @@ def explicit_umb_to_explicit_ats(umb: umbi.umb.ExplicitUmb) -> ExplicitAts:
3635
ts = umb.index.transition_system
3736
ats.time = TimeType(ts.time)
3837
ats.num_players = ts.num_players
39-
ats.num_initial_states = ts.num_initial_states
40-
ats.num_choice_actions = ts.num_choice_actions
41-
ats.num_branch_actions = ts.num_branch_actions
4238
ats.player_to_name = ts.player_to_name
4339

4440
## values
45-
ats.state_is_initial = umb.state_is_initial
41+
ats.set_state_is_initial(umb.state_is_initial)
4642
ats.state_to_player = umb.state_to_player
4743

4844
ats.state_is_markovian = umb.state_is_markovian
@@ -55,6 +51,20 @@ def explicit_umb_to_explicit_ats(umb: umbi.umb.ExplicitUmb) -> ExplicitAts:
5551
state_to_choices = (
5652
umb.state_to_choices if umb.state_to_choices is not None else [s for s in range(ats.num_states + 1)]
5753
)
54+
have_choice_actions = ts.num_choice_actions > 0
55+
if have_choice_actions:
56+
assert umb.choice_to_choice_action is not None, "num_choice_actions > 0 but choice_to_choice_action is None"
57+
ats.add_choice_actions(num_actions=ts.num_choice_actions)
58+
if umb.choice_action_to_string is not None:
59+
ats.choice_actions.set_names(umb.choice_action_to_string)
60+
have_branch_actions = ts.num_branch_actions > 0
61+
if have_branch_actions:
62+
assert umb.branch_to_branch_action is not None, "num_branch_actions > 0 but branch_to_branch_action is None"
63+
ats.add_branch_actions(num_actions=ts.num_branch_actions)
64+
if umb.branch_action_to_string is not None:
65+
ats.branch_actions.set_names(umb.branch_action_to_string)
66+
67+
branch_index = 0
5868
if ts.num_choices > 0:
5969
for state in ats.states:
6070
for choice_idx in range(state_to_choices[state], state_to_choices[state + 1]):
@@ -65,18 +75,12 @@ def explicit_umb_to_explicit_ats(umb: umbi.umb.ExplicitUmb) -> ExplicitAts:
6575
assert umb.branch_to_target is not None, "num_branches > 0 but branch_to_target is None"
6676
target = umb.branch_to_target[branch_idx]
6777
prob = umb.branch_to_probability[branch_idx] if umb.branch_to_probability is not None else None
68-
branch_action = (
69-
umb.branch_to_branch_action[branch_idx] if umb.branch_to_branch_action is not None else None
70-
)
71-
choice.add_branch(target=target, prob=prob, action=branch_action)
72-
if ts.num_choice_actions > 0:
73-
assert umb.choice_to_choice_action is not None, (
74-
"num_choice_actions > 0 but choice_to_choice_action is None"
75-
)
76-
choice.action = umb.choice_to_choice_action[choice_idx]
77-
78-
ats.choice_action_to_name = umb.choice_action_to_string
79-
ats.branch_action_to_name = umb.branch_action_to_string
78+
choice.add_branch(target=target, prob=prob)
79+
branch_index += 1 # TODO make ats.add_branch return the branch index
80+
if have_branch_actions:
81+
ats.branch_actions[branch_index] = umb.branch_to_branch_action[branch_idx] # type: ignore[subscript]
82+
if have_choice_actions:
83+
ats.choice_actions[choice.index] = umb.choice_to_choice_action[choice_idx] # type: ignore[subscript]
8084

8185
# load annotations
8286
if umb.index.annotations is not None:
@@ -102,7 +106,7 @@ def explicit_umb_to_explicit_ats(umb: umbi.umb.ExplicitUmb) -> ExplicitAts:
102106
# load valuations
103107
if umb.index.valuations is not None:
104108
assert umb.valuations is not None
105-
ats.variable_valuations = EntityClassValuations()
109+
ats.add_variable_valuations()
106110
for applies_to, valuation_description in umb.index.valuations.items():
107111
# ignore unique and num_strings
108112
# assume a single valuation class
@@ -112,7 +116,7 @@ def explicit_umb_to_explicit_ats(umb: umbi.umb.ExplicitUmb) -> ExplicitAts:
112116

113117
entity_to_valuation = umb.valuations[applies_to]
114118
entity_class = EntityClass(applies_to)
115-
entity_valuations = EntityValuations()
119+
entity_valuations = ats.variable_valuations.add_valuations_for(entity_class)
116120
for attribute in struct_type.attributes:
117121
if attribute.lower is not None or attribute.upper is not None:
118122
raise NotImplementedError("bounds on valuation variables not supported yet")
@@ -122,7 +126,6 @@ def explicit_umb_to_explicit_ats(umb: umbi.umb.ExplicitUmb) -> ExplicitAts:
122126
for entity, valuation in enumerate(entity_to_valuation):
123127
valuation = {entity_valuations.get_variable(var_name): value for var_name, value in valuation.items()}
124128
entity_valuations.set_entity_valuation(entity, valuation)
125-
ats.variable_valuations.set_valuations_for(entity_class, entity_valuations)
126129

127130
# load observations
128131
if ts.observations_apply_to is not None:
@@ -167,9 +170,9 @@ def explicit_ats_to_explicit_umb(ats: ExplicitAts) -> umbi.umb.ExplicitUmb:
167170
num_states=ats.num_states,
168171
num_initial_states=ats.num_initial_states,
169172
num_choices=ats.num_choices,
170-
num_choice_actions=ats.num_choice_actions,
173+
num_choice_actions=0, # for now
171174
num_branches=ats.num_branches,
172-
num_branch_actions=ats.num_branch_actions,
175+
num_branch_actions=0, # for now
173176
num_observations=ats.num_observations,
174177
observations_apply_to=None, # for now
175178
branch_probability_type=None, # for now
@@ -185,7 +188,7 @@ def explicit_ats_to_explicit_umb(ats: ExplicitAts) -> umbi.umb.ExplicitUmb:
185188
## values
186189
# warning: this does not copy the values, but directly uses the lists from ats.
187190
# this is fine as long as we don't modify them
188-
umb.state_is_initial = ats.state_is_initial
191+
umb.state_is_initial = list(ats.state_is_initial)
189192
umb.state_to_player = ats.state_to_player
190193

191194
umb.state_is_markovian = ats.state_is_markovian
@@ -204,8 +207,11 @@ def explicit_ats_to_explicit_umb(ats: ExplicitAts) -> umbi.umb.ExplicitUmb:
204207
umb.state_to_choices.append(num_choices)
205208

206209
if ats.num_choices > 0:
207-
if ats.num_choice_actions > 0:
208-
umb.choice_to_choice_action = [choice.action for choice in ats.choices] # type: ignore
210+
if ats.has_choice_actions > 0:
211+
umb.index.transition_system.num_choice_actions = ats.choice_actions.num_actions
212+
umb.choice_to_choice_action = list(ats.choice_actions.values)
213+
if ats.choice_actions.has_names:
214+
umb.choice_action_to_string = list(ats.choice_actions.names)
209215

210216
branches = []
211217
umb.choice_to_branches = []
@@ -223,14 +229,11 @@ def explicit_ats_to_explicit_umb(ats: ExplicitAts) -> umbi.umb.ExplicitUmb:
223229
# assert isinstance(target_type, umbi.datatypes.NumericType), "branch probabilities must be numeric"
224230
umb.index.transition_system.branch_probability_type = SizedType.for_type(target_type)
225231
umb.branch_to_probability = vector # type: ignore
226-
if ats.num_branch_actions > 0:
227-
branch_actions = [branch.action for branch in branches]
228-
if any(action is None for action in branch_actions):
229-
raise ValueError("if num_branch_actions > 0, all branches must have an action")
230-
umb.branch_to_branch_action = branch_actions # type: ignore
231-
232-
umb.choice_action_to_string = ats.choice_action_to_name
233-
umb.branch_action_to_string = ats.branch_action_to_name
232+
if ats.has_branch_actions:
233+
umb.index.transition_system.num_branch_actions = ats.branch_actions.num_actions
234+
umb.branch_to_branch_action = list(ats.branch_actions.values)
235+
if ats.branch_actions.has_names:
236+
umb.branch_action_to_string = list(ats.branch_actions.names)
234237

235238
# add annotations
236239
umb.index.annotations = {}

umbi/ats/examples/grid.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from fractions import Fraction
66

77
from ..explicit_ats import ExplicitAts, TimeType
8-
from ..variable_valuations import EntityClassValuations, EntityValuations
98

109
logger = logging.getLogger(__name__)
1110

@@ -93,7 +92,7 @@ def grid(grid: str) -> ExplicitAts:
9392
goal_states.add(state)
9493

9594
num_states = len(cell_to_state)
96-
ats.add_states(num_states)
95+
ats.set_num_states(num_states)
9796
ats.set_initial_states(list(initial_states))
9897

9998
variable_valuations = ats.add_variable_valuations()
@@ -111,14 +110,14 @@ def grid(grid: str) -> ExplicitAts:
111110
"right": (1, 0),
112111
}
113112

114-
ats.choice_action_to_name = list(direction_dxdy.keys())
115-
ats.num_choice_actions = len(ats.choice_action_to_name)
113+
choice_actions = ats.add_choice_actions(num_actions=len(direction_dxdy))
114+
choice_actions.set_names(list(direction_dxdy.keys()))
116115

117116
for (x, y), state in cell_to_state.items():
118117
for direction, (dx, dy) in direction_dxdy.items():
119118
target = (x + dx, y + dy)
120119
choice = ats.add_state_choice(state=state)
121-
choice.action = ats.choice_action_to_name.index(direction)
120+
choice_actions[choice.index] = choice_actions.names.index(direction)
122121

123122
if target in cell_to_state:
124123
target_state = cell_to_state[target]

umbi/ats/examples/random_game.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,21 +197,21 @@ def random_game(num_states: int, seed: int | None = None) -> ExplicitAts:
197197
ats.state_to_player = [0 if is_player0 else 1 for is_player0 in is_player0_state]
198198

199199
# build the ATS structure from delta and actions_at_state
200-
ats.num_choice_actions = len(all_actions)
201-
ats.choice_action_to_name = all_actions
200+
choice_actions = ats.add_choice_actions(num_actions=len(all_actions))
201+
choice_actions.set_names(all_actions)
202202

203203
# build CSR structures
204204
logger.info("building CSR structures ...")
205205
build_start = time.time()
206206

207-
ats.add_states(num_states)
207+
ats.set_num_states(num_states)
208208
# set initial state to the state with index 0
209209
ats.set_initial_states([0])
210210

211211
for s in ats.states:
212212
for action in sorted(actions_at_state[s]):
213213
choice = ats.add_state_choice(state=s)
214-
choice.action = action
214+
choice_actions[choice.index] = action
215215

216216
# get all branches for this (s, action) pair
217217
branches = delta[s][action]

umbi/ats/examples/random_walk.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,30 +13,30 @@ def random_walk(num_states: int) -> ExplicitAts:
1313
ats = ExplicitAts()
1414

1515
# basic parameters
16-
ats.time = TimeType.DISCRETE
16+
ats.time = TimeType.STOCHASTIC
1717
ats.num_players = 0
1818

1919
# two actions: left (0) and right (1), each with 0.9 success prob
20-
ats.num_choice_actions = 2
21-
ats.choice_action_to_name = ["left", "right"]
20+
choice_actions = ats.add_choice_actions(num_actions=2)
21+
choice_actions.set_names(["left", "right"])
2222

23-
ats.add_states(num_states)
23+
ats.set_num_states(num_states)
2424
ats.set_initial_states([num_states // 2]) # start in the middle state
2525
ats.state_is_markovian = [True] * ats.num_states
2626
ats.state_to_exit_rate = [1] * ats.num_states # type: ignore[assignment]
2727

2828
# build structure
29-
for state in range(num_states):
29+
for state in ats.states:
3030
# left action
3131
choice = ats.add_state_choice(state=state)
32-
choice.action = 0
32+
choice_actions[choice.index] = 0
3333
left = max(0, state - 1)
3434
choice.add_branch(target=left, prob=Fraction(9, 10))
3535
choice.add_branch(target=state, prob=Fraction(1, 10))
3636

3737
# right action
3838
choice = ats.add_state_choice(state=state)
39-
choice.action = 1
39+
choice_actions[choice.index] = 1
4040
right = min(num_states - 1, state + 1)
4141
choice.add_branch(target=right, prob=Fraction(9, 10))
4242
choice.add_branch(target=state, prob=Fraction(1, 10))
@@ -54,12 +54,10 @@ def random_walk(num_states: int) -> ExplicitAts:
5454
# wall hit penalty
5555
choice_penalty = [0] * ats.num_choices
5656
for choice in ats.get_state_choices(0):
57-
assert choice.action is not None
58-
if ats.choice_action_to_name[choice.action] == "left": # left action in first state
57+
if choice_actions.names[choice_actions[choice.index]] == "left": # left action in first state
5958
choice_penalty[choice.index] = -10
6059
for choice in ats.get_state_choices(ats.num_states - 1):
61-
assert choice.action is not None
62-
if ats.choice_action_to_name[choice.action] == "right": # right action in last state
60+
if choice_actions.names[choice_actions[choice.index]] == "right": # right action in last state
6361
choice_penalty[choice.index] = -10
6462
annotation = ats.add_reward_annotation(
6563
name="wall_hit_penalty",

0 commit comments

Comments
 (0)