Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 8 additions & 151 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,36 +1,7 @@
## [17.0.0-alpha.2] - unreleased
## [17.0.0-alpha.2] - 2026-08-21

### Added
- Undo/redo support has been re-enabled in the graphical interface.
- `MixedStrategyProfile.payoffs`, `.player_regrets`, `.strategy_values`, and
`.strategy_regrets` return the corresponding quantity for every player (and, for the
latter two, every strategy) at once, rather than one at a time.
`.payoffs`/`.player_regrets` return a `PayoffVector`/`PlayerRegretVector` (each a
`PlayerIndexedVector`, keyed by player label); `.strategy_values`/`.strategy_regrets`
return a `StrategyValuesVector`/`StrategyRegretsVector` (each a `PlayerIndexedVector` of
`StrategyValueVector`/`StrategyRegretVector`, keyed by player label then strategy
label). Each quantity is its own type — a `PayoffVector` and a `PlayerRegretVector`
never compare equal, even if they happen to hold the same numbers, though either
compares equal to a plain `Mapping` with the same contents. (#763)
- `MixedStrategyProfile.set_mixed_strategy(player, distribution, sparse=False)` sets a player's whole
mixed strategy, like `profile[player] = distribution`, but can require `distribution` to give
a weight for every one of the player's strategies (the default) rather than treating strategies
it omits as having weight zero. (#763)
- `MixedBehaviorProfile.payoffs`, `.node_values`, `.infoset_values`, `.infoset_regrets`,
`.action_values`, `.action_regrets`, `.realiz_probs`, `.infoset_probs`, and `.beliefs` return the
corresponding quantity for every player/node/information set/action at once, rather than one at
a time, mirroring `MixedStrategyProfile`'s vectorized quantities. Since information sets don't
reliably have unique persistent labels, `.infoset_values`/`.infoset_regrets`/`.infoset_probs`/
`.action_values`/`.action_regrets` are indexed by a `Node` belonging to the information set (any
member node works, resolved fresh each time) rather than by label; `.realiz_probs`/`.beliefs`
are indexed directly by `Node`, and `.payoffs`/`.node_values` by player label. Each quantity is
its own type, following the same `_LabeledVector` family as the strategy side's; `.node_values`/
`.action_values`/`.action_regrets` nest a per-player/per-information-set vector, keyed by player
label/`Node` then `Node`/action label respectively. (#763)
- `MixedBehaviorProfile.set_mixed_action(node, distribution, sparse=False)` sets the mixed action
at `node`'s information set, like `profile[node] = distribution`, but can require `distribution`
to give a weight for every one of the information set's actions (the default) rather than
treating actions it omits as having weight zero. (#763)
- Added `Game.relabel_strategies`, which simultaneously reassigns the labels of a player's strategies. (#1047)
- Added `Game.set_strategies`, which declares the ordered list of a player's strategies in a game in strategic form,
matching by label. (#1056)
Expand All @@ -46,103 +17,16 @@
- `MixedStrategy.__eq__` raised `AttributeError` when comparing two `MixedStrategy` instances
directly (as opposed to a `MixedStrategy` and a plain `Mapping`), because it read a Cython
``cdef``-declared attribute off `other` without first casting it to `MixedStrategy`.
- `Game.add_player`, on a game in strategic form, no longer leaves the new player's sole
strategy with an empty label; it is now labeled `"1"`, as `Game.new_table` already labels
every other player's initial strategies. (#1047)
- Numerical data in action graph games is now read with exact precision, to match other game formats. (#879)
- `enumpoly_solve` correctly handles failed perturbations at singular Jacobians (#591)
- `enumpoly_solve` iterates sequences of actions in a deterministic order, pre-empting the
possibility of equilibria being found in orders that differ across runs. (#1016)
- Improved the numerical stability of `enumpoly_solve` handling less-well-conditioned systems. (#1055)

### Changed
- `MixedBehavior.__iter__` now yields `(Infoset, MixedAction)` pairs, one per information set
belonging to the player, matching what `mixed_actions()` returned; previously it yielded
`(Action, probability)` pairs flattened across all of the player's information sets, which
discarded the information set each action belonged to. `len(MixedBehavior)` is now the number
of the player's information sets, to match. Use `MixedAction`'s own iteration to get
action/probability pairs at a specific information set.
- `MixedAction.__getitem__`/`__setitem__` now only accept a `str` action label, not an `Action`
object; action labels are enforced unique within an information set, so there is no longer any
ambiguity requiring an `Action` handle. `MixedAction.__iter__` now yields `(label, probability)`
pairs instead of `(Action, probability)`.
- `MixedStrategy.__getitem__`/`__setitem__` now only accept a `str` strategy label, not a
`Strategy` object; strategy labels are enforced unique within a player's strategies, so there
is no longer any ambiguity requiring a `Strategy` handle. `MixedStrategy.__iter__` now yields
`(label, probability)` pairs instead of `(Strategy, probability)`.
- `MixedStrategyProfile.__iter__` now yields one `MixedStrategy` per player, matching what
`mixed_strategies()` returned; previously it yielded `(Strategy, probability)` pairs flattened
across every player's strategies, which discarded which player each strategy belonged to.
`len(MixedStrategyProfile)` is now the number of players, to match; previously it was the
total number of strategies across all players, inconsistent with the number of items
iterating the profile actually yields.
- `repr(MixedAction)`/`repr(MixedStrategy)` now show a `{label: probability}` mapping instead of
a bare list of probabilities, reflecting that these are dictionaries keyed by (enforced-unique)
action/strategy label, not positional vectors. (#433)
- `repr(MixedStrategyProfile)` now shows a `{player_label: MixedStrategy}` mapping instead of a
bare list of `MixedStrategy` values, matching the same `{label: value}` convention already used
by `repr(MixedStrategy)` and every vectorized computed quantity (`payoffs`, `strategy_values`,
etc.). (#433)
- `MixedStrategy.__eq__` now compares against a `Mapping` (label to probability) instead of a
bare `list`; comparing to a positional list of probabilities no longer works, since a
`MixedStrategy` is keyed by strategy label, not position. (#433)
- `MixedStrategyProfile.__getitem__` now only accepts a `str` player label, returning the
`MixedStrategy` for that player; indexing directly by a `Player` or `Strategy` object, or by a
bare (game-wide-unique) strategy label, is no longer supported. Access a strategy's probability
via `profile[player_label][strategy_label]` instead.
- `MixedStrategy` is now an immutable snapshot of a player's strategy probabilities at the
moment it was retrieved from a `MixedStrategyProfile`, rather than a live view onto the
profile; it no longer reflects later changes to the profile, and can no longer be assigned
into (`MixedStrategy.__setitem__` is removed). Set a player's whole distribution via
`MixedStrategyProfile.__setitem__` instead.
- `MixedStrategyProfile.copy()` now shares its underlying data with the original until either
profile is next assigned into (copy-on-write), rather than eagerly duplicating; this is not
observable from the profiles' behavior, only from how soon the duplication happens.
- `MixedStrategyProfile.__setitem__` now only accepts a whole distribution for a player (a
`Mapping` from strategy label to a non-negative weight, keyed by strategy label); setting a
single strategy's probability directly is no longer supported. `distribution` need not specify
every strategy: strategies it omits are treated as having weight zero (use the new
`set_mixed_strategy` to make that an error instead). A weight may be given as any value Gambit can
interpret as a number (`int`, `float`, `str`, `Decimal`, or `Rational`), and given weights need
not sum to one. Validation moved here from `normalize()`, which no longer needs to check for
negative or all-zero entries, since a `MixedStrategyProfile` can no longer hold them. (#763)
- `MixedAction.__eq__` now compares against a `Mapping` instead of a bare `list`, matching the
same change to `MixedStrategy.__eq__`. (#433)
- `repr(MixedBehaviorProfile)` now shows a `{player_label: MixedBehavior}` mapping instead of a
bare list, matching `repr(MixedStrategyProfile)`. (#433)
- `MixedBehaviorProfile.__iter__` now yields one `MixedBehavior` per player, matching what
`mixed_behaviors()` returned; previously it yielded `(Action, probability)` pairs flattened
across every player's actions, which discarded which player each action belonged to. Mirrors
the earlier `MixedStrategyProfile.__iter__` change.
- `MixedBehaviorProfile`/`MixedBehavior.__getitem__` now only accept a `player: str` label
(returning a `MixedBehavior`) or a `node: Node` (returning the `MixedAction` at that node's
information set); indexing directly by an `Infoset`, `Action`, or a bare infoset/action label
string is no longer supported. Since information sets don't reliably have unique persistent
labels the way players/strategies/actions do, a `Node` belonging to the information set is used
as the stable anchor instead — any member node works, since it resolves the information set
fresh each time rather than caching a handle to it. `MixedAction`'s own indexing is unaffected:
still by `str` action label. A terminal `Node` (belonging to no information set) raises
`ValueError`.
- `MixedAction`/`MixedBehavior` are now immutable snapshots of a profile's state at the moment
they were retrieved, rather than live views onto the profile; they no longer reflect later
changes to the profile, and can no longer be assigned into (`MixedAction.__setitem__`/
`MixedBehavior.__setitem__` are removed). Set a distribution via
`MixedBehaviorProfile.__setitem__` instead. Mirrors the earlier `MixedStrategy` change.
- `MixedBehaviorProfile.copy()` now shares its underlying data with the original until either
profile is next assigned into (copy-on-write), rather than eagerly duplicating; this is not
observable from the profiles' behavior, only from how soon the duplication happens. Mirrors the
earlier `MixedStrategyProfile.copy()` change.
- `MixedBehaviorProfile.__setitem__` now only accepts a `node: Node`, setting a validated whole
distribution over that node's information set (a `Mapping` from action label to a non-negative
weight); setting a single action's probability directly, or a whole player's behavior in one
call, is no longer supported. Unlike a mixed strategy, a whole player's behavior has no
meaningful atomic unit larger than one information set's distribution, so there is no
player-level assignment to mirror `MixedStrategyProfile.__setitem__`'s player-label form —
assign information set by information set instead. `distribution` need not specify every
action: actions it omits are treated as having weight zero (use the new `set_mixed_action` to
make that an error instead). A weight may be given as any value Gambit can interpret as a
number (`int`, `float`, `str`, `Decimal`, or `Rational`), and given weights need not sum to
one. (#763)
- `MixedStrategyProfile` and `MixedBehaviorProfile` objects are now treated as products of
distributions. Computed quantities (payoffs, probabilities, and so on) are returned as
labeled vectors/mappings of values, rather than individual values per object.
- All long-running calculations are now done in a thread in the GUI process, rather than calling the
command-line executables as external processes. (#990)
- When displaying a strategic form, the graphical interface preserves decimal payoffs when those are
Expand All @@ -153,41 +37,14 @@
label's right-click "Delete strategy" menu, are removed, superseded by the dialog. (#1056)

### Removed
- `MixedBehavior.mixed_actions` has been removed; it is now redundant with iterating the
`MixedBehavior` directly.
- `MixedStrategyProfile.mixed_strategies` has been removed; it is now redundant with iterating
the `MixedStrategyProfile` directly.
- `MixedBehaviorProfile.mixed_behaviors` and `.mixed_actions` have been removed; the former is
now redundant with iterating the `MixedBehaviorProfile` directly, and the latter has no
direct replacement at the profile level (iterate a player's `MixedBehavior` for
`(Infoset, MixedAction)` pairs at that player's information sets).
- `MixedStrategyProfile.payoff`, `.strategy_value`, `.strategy_regret`, and `.player_regret`
have been removed, superseded by `.payoffs`, `.strategy_values`, `.strategy_regrets`, and
`.player_regrets`; index the result for a single player/strategy (e.g.
`profile.payoffs[player_label]` instead of `profile.payoff(player_label)`). (#763)
- `MixedStrategyProfile.strategy_value_deriv` has been removed; it had no internal usage,
and is a specific evaluation of the payoff Jacobian that is straightforward to reconstruct
directly: it equals the payoff obtained by substituting the two given pure strategies into
the profile (0 if both belong to the same player).
- `MixedBehaviorProfile.payoff`, `.node_value`, `.infoset_value`, `.action_value`, `.realiz_prob`,
`.infoset_prob`, `.belief`, `.action_regret`, and `.infoset_regret` have been removed, superseded
by `.payoffs`, `.node_values`, `.infoset_values`, `.action_values`, `.realiz_probs`,
`.infoset_probs`, `.beliefs`, `.action_regrets`, and `.infoset_regrets`; index the result for a
single player/node/information set/action (e.g. `profile.payoffs[player_label]` instead of
`profile.payoff(player_label)`, or `profile.infoset_values[node]` instead of
`profile.infoset_value(infoset)`). (#763)
- Assigning to `Strategy.label` has been removed; use `Game.relabel_strategies`, which enforces
nonempty, unique labels. (#1047)
- `Game.add_strategy` and `Game.delete_strategy` have been removed; use `Game.set_strategies`,
specifying the labels of the strategies the player is to have. (#1056)
- The C++ `Game` interface's `NewStrategy` and `DeleteStrategy` methods have been removed; they
were subsumed by `SetStrategies`, and by this point were used only in support of `pygambit`'s
`add_strategy`/`delete_strategy` and the GUI's strategy add/delete controls, which now build the
equivalent `SetStrategies` call directly. (#1056)
- Assigning to `Player.label` has been removed; use `Game.relabel_players`, which enforces
nonempty, unique labels. (#1058)
- `Game.add_player` has been removed; use `Game.set_players`, specifying the labels
of the players. (#1059)
- Assigning to `Player.label` has been removed; use `Game.relabel_players`, which enforces
nonempty, unique labels. (#1058)
- `Game.add_player` has been removed; use `Game.set_players`, specifying the labels
of the players. (#1059)


## [17.0.0-alpha.1] - 2026-08-13
Expand Down
2 changes: 1 addition & 1 deletion build_support/GAMBIT_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
17.0.0-alpha.1
17.0.0-alpha.2
Loading