Skip to content

Make label normalization mandatory and collision-safe, and retime it for players and strategies - #965

Merged
tturocy merged 4 commits into
gambitproject:masterfrom
d-kad:unique-labels
Jul 6, 2026
Merged

Make label normalization mandatory and collision-safe, and retime it for players and strategies#965
tturocy merged 4 commits into
gambitproject:masterfrom
d-kad:unique-labels

Conversation

@d-kad

@d-kad d-kad commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Issues closed by this PR

Description of the changes in this PR

Prepares the label-normalization path in the file loader for the unique-nonempty-label enforcement
planned in #861. Loading accepts files with empty or duplicate labels, with labels "unique-ified" as before.

Three self-contained commits:

  • Mandatory normalization. Label normalization on file read is now unconditional
    (the previous opt-in parameter is removed), so every loaded game has normalized labels.

  • Collision-safe normalization. NormalizeLabels now tracks the labels already used
    in each scope and never generates a duplicate suffix, so an input like {"x", "x", "x_1"}
    no longer renumbers to a second "x_1". NormalizeLabels is generalized to work over
    both game-object containers and raw label-string lists via accessor lambdas, with a
    NormalizeLabelStrings helper for the string case.

  • Retime player/strategy normalization ahead of construction (revertable). Player and strategy labels
    are normalized on the raw label lists read from the file, before the game objects are constructed,
    rather than renaming objects afterwards. This is a step toward retiming normalization planned in
    [ENH]: Require unique labels for game objects (within appropriate scope) #861.

Actions and outcomes are deliberately not retimed here

Both are multi-reference scopes: an infoset's actions are re-stated at each later node and
validated by CheckInfosetActions, and outcomes are keyed by id across the whole tree via the
outcome map. Normalizing their labels at first definition makes the later raw re-statement no longer
match what was stored.

Retiming actions breaks loading when an infoset has duplicate action labels (so normalization changes
them, e.g. { "1" "1" }1_1, 1_2) and is referenced at more than one node (so CheckInfosetActions
re-checks it against the raw labels) -- resulted in 8 test failures.

@d-kad
d-kad requested a review from tturocy June 29, 2026 17:02
@tturocy

tturocy commented Jun 30, 2026

Copy link
Copy Markdown
Member
  • Move the checking into C++ SetLabel() (and we don't need it in pygambit then)
  • Adding new objects (in C++) should require a label (and raise an exception if it is not valid or unique)
  • pygambit API will make label required (not optional) for new objects

@d-kad

d-kad commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

@tturocy one thing to confirm:

As per Bullet 2, user creation requires a valid unique label.

The one thing I want to confirm is the parser: it creates outcomes from file labels (can be empty/duplicate), so raise-on-duplicate there would break loading — am I right that we also want readers to accept those and generate _1/_2. Those only fit if the parser's exempt from the rule (sets labels non-enforcing, normalization after) -- is this the plan to follow?

Please let me know if you want loading strict instead, I'll rewrite the test games.

@tturocy

tturocy commented Jul 1, 2026

Copy link
Copy Markdown
Member

This is a good catch, I had mis-remembered how I implemented the normalisation. Normalisation in the loader is done after the game has been constructed, rather than as the file is read. So you're correct SetLabel would fail under the new semantics.

Our thinking when we introduced this normalisation is that there are potentially too many files out there with empty labels (in particular), which requiring unique labels will break. So we want to continue the behaviour of the loader to "unique-ify" labels. However this normalisation has to get moved now.

Probably, the right way to do this will be to make a separate branch and change the timing of the normalisation, so it's being done on the raw lists of labels rather than the game objects, and we'll merge that first. Then there won't be a clash with the new SetLabel semantics that throws an exception on duplicate.

@d-kad d-kad changed the title Enforce unique, nonempty player and strategy labels Make label normalization mandatory and collision-safe, and retime it for players and strategies Jul 1, 2026
@tturocy

tturocy commented Jul 3, 2026

Copy link
Copy Markdown
Member

Well, there's good news and bad news on the tests - it's good that it looks like our new BAGG checks are doing something, but it is puzzling that it is crashing on MacOS only (while I have tried locally on this branch on MacOS and do not have a problem...!)

@d-kad

d-kad commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

@tturocy, I pushed a fix for the AGG/BAGG parser crash blocking CI, as its own PR #971 since it's a master bug rather than label normalization work.

Once #971 merges we can rebase this branch onto master and drop the fix from here, so this PR remains just the normalization work.

d-kad added 4 commits July 3, 2026 18:24
the normalize_labels / p_normalizeLabels parameter throughout the read
path: ReadEfgFile, ReadNfgFile, ReadGbtFile, ReadAggFile, ReadBaggFile,
ReadGame, and the pygambit read_* wrappers.
…. Actions and outcomes are deliberately *not* retimed here.

This commit is self-contained and can be reverted independently of the mandatory and collision-safe normalization it builds on.
@tturocy
tturocy merged commit 469afe3 into gambitproject:master Jul 6, 2026
14 checks passed
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.

2 participants