Skip to content

Commit a50dfdc

Browse files
committed
feat: Add agent state management system with discrete & continuous states
Adds a minimal state management system to Mesa that supports: - Discrete states with explicit value changes - Continuous states that update based on elapsed time - Composite states derived from other states - Integration with Mesa's Agent class via StateAgent This provides a clean API for managing agent states in simulations while handling both discrete events and continuous changes efficiently.
1 parent e4e92f4 commit a50dfdc

File tree

1 file changed

+140
-0
lines changed

1 file changed

+140
-0
lines changed

mesa/experimental/states.py

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
"""State management system for Mesa agents.
2+
3+
This module provides a flexible state management system for Mesa agents, supporting
4+
both discrete and continuous state changes. It enables agents to maintain multiple
5+
states that can change either explicitly (discrete) or based on time (continuous),
6+
and includes support for composite states derived from other states.
7+
8+
Core Classes:
9+
State: Base class defining the state interface
10+
DiscreteState: States with explicit value changes
11+
ContinuousState: States that change over time
12+
CompositeState: States computed from other states
13+
StateAgent: Mesa Agent subclass with state management
14+
"""
15+
16+
from collections.abc import Callable
17+
from typing import Any
18+
19+
from mesa import Agent
20+
21+
22+
class State:
23+
"""Base class for all states."""
24+
25+
def __init__(self, name: str, initial_value: Any):
26+
"""Create a new state."""
27+
self.name = name
28+
self._value = initial_value
29+
self._last_update_time = 0
30+
self.model = None # Set when state is added to agent
31+
32+
@property
33+
def value(self) -> Any:
34+
"""Get current state value."""
35+
raise NotImplementedError
36+
37+
def update(self, time: float) -> None:
38+
"""Update state to current time."""
39+
raise NotImplementedError
40+
41+
42+
class DiscreteState(State):
43+
"""A state with discrete values that change explicitly."""
44+
45+
@property
46+
def value(self) -> Any:
47+
"""Get the current state value."""
48+
return self._value
49+
50+
@value.setter
51+
def value(self, new_value: Any) -> None:
52+
"""Set the state value."""
53+
self._value = new_value
54+
55+
def update(self, time: float) -> None:
56+
"""DiscreteStates only update when value is explicitly changed."""
57+
58+
59+
class ContinuousState(State):
60+
"""A state that changes continuously over time."""
61+
62+
def __init__(
63+
self,
64+
name: str,
65+
initial_value: float,
66+
rate_function: Callable[[float, float], float],
67+
):
68+
"""Create a new continuous state."""
69+
super().__init__(name, initial_value)
70+
self.rate_function = rate_function
71+
72+
@property
73+
def value(self) -> float:
74+
"""Calculate and return current value based on elapsed time."""
75+
current_time = self.model.time
76+
if current_time > self._last_update_time:
77+
self.update(current_time)
78+
return self._value
79+
80+
def update(self, time: float) -> None:
81+
"""Update state value based on elapsed time."""
82+
elapsed = time - self._last_update_time
83+
self._value = self._value + self.rate_function(self._value, elapsed)
84+
self._last_update_time = time
85+
86+
87+
class CompositeState(State):
88+
"""A state derived from other states."""
89+
90+
def __init__(
91+
self,
92+
name: str,
93+
dependent_states: list[State],
94+
computation_function: Callable[..., Any],
95+
):
96+
"""Create a new composite state."""
97+
self.dependent_states = dependent_states
98+
self.computation_function = computation_function
99+
super().__init__(name, None) # Value computed on first access
100+
101+
@property
102+
def value(self) -> Any:
103+
"""Compute value based on dependent states."""
104+
state_values = [state.value for state in self.dependent_states]
105+
return self.computation_function(*state_values)
106+
107+
def update(self, time: float) -> None:
108+
"""Update all dependent states."""
109+
for state in self.dependent_states:
110+
state.update(time)
111+
112+
113+
class StateAgent(Agent):
114+
"""An agent with integrated state management."""
115+
116+
def __init__(self, model) -> None:
117+
"""Create a new agent with state management."""
118+
super().__init__(model)
119+
self.states = {} # name -> State mapping
120+
121+
def add_state(self, state: State) -> None:
122+
"""Add a new state to the agent."""
123+
state.model = self.model
124+
self.states[state.name] = state
125+
126+
def get_state(self, name: str) -> Any:
127+
"""Get the current value of a state."""
128+
return self.states[name].value
129+
130+
def set_state(self, name: str, value: Any) -> None:
131+
"""Set value for a discrete state."""
132+
if isinstance(self.states[name], DiscreteState):
133+
self.states[name].value = value
134+
else:
135+
raise ValueError("Cannot directly set value of non-discrete state")
136+
137+
def update_states(self) -> None:
138+
"""Update all states to current time."""
139+
for state in self.states.values():
140+
state.update(self.model.time)

0 commit comments

Comments
 (0)