Skip to content

Resolve DataRecorder off-by-one timestamp error#3299

Merged
quaquel merged 2 commits intomesa:mainfrom
codebreaker32:fix_datarecorder
Feb 13, 2026
Merged

Resolve DataRecorder off-by-one timestamp error#3299
quaquel merged 2 commits intomesa:mainfrom
codebreaker32:fix_datarecorder

Conversation

@codebreaker32
Copy link
Copy Markdown
Collaborator

Summary

This PR fixes the "off-by-one" recording error in the experimental DataRecorder (introduced in #3145) by correctly aligning the recorded data with its actual chronological timestamp.

Bug / Issue

In the new Discrete Event Simulation (DES) architecture, the DataRecorder exhibits a bug where it permanently captures the "past" state of the model, but tags it with the "future" timestamp.

The root cause lies in the execution order of the DES engine:

  1. The engine pops an event for the next time slice (e.g., $t=1.0$).
  2. The engine updates the simulation clock (self.time = event.time).
  3. The engine executes the event (event.execute()).

Because model.time is an Observable, updating the clock in step 2 instantly broadcasts a CHANGED signal. The DataRecorder reacts to this signal and takes a snapshot of the agents before they are actually allowed to execute their logic in step 3.

Previously, the recorder was tagging this snapshot with the new time signal. This meant the initial state was being recorded and labeled as $t=1.0$, the $t=1.0$ state was labeled as $t=2.0$, and so on, permanently shifting the data one step out of alignment.

Implementation

This PR resolves the timestamp misalignment by extracting the pre-transition time from the Observable signal by changing the timestamp assignment in _on_time_change to use current_time = signal.additional_kwargs.get("old").

Fixes #3297

@codebreaker32 codebreaker32 changed the title Fix: Resolve DataRecorder off-by-one timestamp error Feb 13, 2026
@quaquel quaquel added the bug Release notes label label Feb 13, 2026
@quaquel quaquel merged commit 744df2a into mesa:main Feb 13, 2026
15 checks passed
@github-actions
Copy link
Copy Markdown

Performance benchmarks:

Model Size Init time [95% CI] Run time [95% CI]
BoltzmannWealth small 🔵 -0.3% [-0.9%, +0.3%] 🔵 -0.8% [-1.0%, -0.7%]
BoltzmannWealth large 🔵 -0.1% [-0.6%, +0.4%] 🔵 -1.3% [-1.9%, -0.6%]
Schelling small 🔵 +0.2% [-0.3%, +0.6%] 🔵 -0.5% [-0.7%, -0.2%]
Schelling large 🔵 +0.7% [+0.0%, +1.5%] 🔵 +1.2% [+0.7%, +1.8%]
WolfSheep small 🔵 -0.5% [-0.7%, -0.3%] 🔵 -0.8% [-1.0%, -0.6%]
WolfSheep large 🔵 -0.7% [-1.5%, +0.2%] 🔵 -4.1% [-5.1%, -3.0%]
BoidFlockers small 🔵 +1.6% [+1.3%, +1.9%] 🔵 -1.3% [-1.5%, -1.2%]
BoidFlockers large 🔵 +1.2% [+0.8%, +1.6%] 🔵 +0.2% [+0.1%, +0.3%]

@EwoutH EwoutH added the experimental Release notes label label Feb 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Release notes label experimental Release notes label

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DataRecorder exhibits off-by-one error (captures pre-execution state)

3 participants