Skip to content

perf(model): relabel assign_coords containers without a label dict - #1030

Open
olivier-lacroix wants to merge 1 commit into
PyPSA:masterfrom
olivier-lacroix:perf/assign-coords-relabel
Open

olivier-lacroix wants to merge 1 commit into
PyPSA:masterfrom
olivier-lacroix:perf/assign-coords-relabel

Conversation

@olivier-lacroix

@olivier-lacroix olivier-lacroix commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Taking assign_coords for a spin, I realised it was way too slow. this PR fixes it. Sorry for not catching this sooner...

Note

The following content was generated by AI.

What

Model.assign_coords (from #955) relabelled containers by rebuilding a master -> new dict per container and mapping each container's labels through Index.map. On a tz-aware DatetimeIndex — the rolling-horizon case the API is built for — that map falls into pandas' object-to-datetime inference (maybe_infer_to_datetimelike -> objects_to_datetime64), costing milliseconds per container; the dict was also rebuilt once per container.

Two changes, both inside the #955 code:

  1. Positional relabel. A full-length container (one carrying the master's labels) maps straight to the new values; a subset container's labels are located with master.get_indexer(index) and new.take(positions). get_indexer also replaces the separate index.isin(master).all() guard, folding the membership check and the mapping into one C-level lookup.
  2. One Dataset construction. assign_coords_multiindex_safe writes the new index into the coordinate's original position and builds the dataset once, instead of Dataset.assign_coords(...) followed by a second Dataset(...) to restore the coordinate order that assign_coords breaks.

Benchmark

30 containers (15 variables + 15 CSR constraints), 4380 tz-aware labels, Model(sparse=True), v1 semantics:

Model.assign_coords
before 816.7 ms
after 2.9 ms
Reproduction
import time
import pandas as pd
import linopy
from linopy import Model

linopy.options["semantics"] = "v1"
idx = pd.date_range("2024-01-01", periods=4380, freq="30min", tz="Australia/Melbourne", name="time")
new = pd.date_range("2024-04-01", periods=4380, freq="30min", tz="Australia/Melbourne", name="time")

m = Model(sparse=True)
vs = [m.add_variables(coords=[idx], name=f"x{i}") for i in range(15)]
for i, v in enumerate(vs):
    m.add_constraints(v >= 0, name=f"c{i}")

best = 1e9
for _ in range(3):
    t0 = time.perf_counter()
    m.assign_coords(time=new)
    best = min(best, time.perf_counter() - t0)
print(f"{best * 1000:.1f} ms")

Checklist

  • AI-generated content is marked (see AGENTS.md).
  • Code changes are sufficiently documented; i.e. new functions contain docstrings and further explanations may be given in doc.
  • Unit tests for new features were added (if applicable).
  • A note for the release notes doc/release_notes.rst of the upcoming release is included.
  • I consent to the release of this PR's code under the MIT license.

@codspeed

codspeed Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 181 untouched benchmarks
⏩ 181 skipped benchmarks1


Comparing olivier-lacroix:perf/assign-coords-relabel (663a15c) with master (ae80bc8)

Open in CodSpeed

Footnotes

  1. 181 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@olivier-lacroix
olivier-lacroix force-pushed the perf/assign-coords-relabel branch 3 times, most recently from 14e9d58 to 824d1f0 Compare October 11, 2026 12:21
@olivier-lacroix
olivier-lacroix marked this pull request as ready for review October 11, 2026 12:22
@olivier-lacroix
olivier-lacroix force-pushed the perf/assign-coords-relabel branch from 824d1f0 to 4739b55 Compare October 20, 2026 22:33
Model.assign_coords rebuilt a master->new dict per container and mapped each
container's labels through Index.map. On a tz-aware DatetimeIndex that map
falls into pandas' object-to-datetime inference, costing milliseconds per
container. Map full-length containers straight to the new values and locate
subset containers' labels with get_indexer/take, and build a reassigned
Dataset once instead of twice.

~817 ms -> ~3 ms for 30 containers on a 4380-label tz-aware axis.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@olivier-lacroix
olivier-lacroix force-pushed the perf/assign-coords-relabel branch from dbf4c67 to 663a15c Compare October 27, 2026 03:11

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant