From be16a028d4b3c1187e0f42943948d75e2e225141 Mon Sep 17 00:00:00 2001 From: Theodore Turocy Date: Fri, 21 Aug 2026 19:50:01 +0100 Subject: [PATCH] Bump version to 17.0.0-alpha.2 --- ChangeLog | 159 ++--------------------------------- build_support/GAMBIT_VERSION | 2 +- 2 files changed, 9 insertions(+), 152 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1662797238..7c8af4afca 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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) @@ -46,9 +17,6 @@ - `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 @@ -56,93 +24,9 @@ - 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 @@ -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 diff --git a/build_support/GAMBIT_VERSION b/build_support/GAMBIT_VERSION index 62fc60cb0c..66f3a79ad8 100644 --- a/build_support/GAMBIT_VERSION +++ b/build_support/GAMBIT_VERSION @@ -1 +1 @@ -17.0.0-alpha.1 +17.0.0-alpha.2