Section 1 · The big idea
Suits are interchangeable except for flush considerations
You hold Ah Kh. The flop comes 9h 7h 2h — you flopped a flush. Now imagine the identical sequence with all hearts replaced by spades:
Hand A Ah Kh vs board 9h 7h 2h
Hand B As Ks vs board 9s 7s 2s
These play strategically identically. The same equity, the same optimal bet sizes, the same EV. Only the labels differ. So when a poker solver computes the equilibrium for hand A, it gets the answer for hand B for free.
Card isomorphism is the technique of recognizing this symmetry and computing only one canonical representative per equivalence class. For HUNL, this is roughly a 24× compute reduction with zero accuracy loss.
Section 2 · Equivalence classes — visualized
The same hand under all 4 suit assignments
Below: As Kh on flop 9d 7c 2s — and three of its suit-permuted equivalents. All four configurations have identical equity and optimal play.
There are 24 total suit permutations of this configuration — 4! ways to relabel {♠ ♥ ♦ ♣}. Every permutation has the same equity vs every other hand, the same equilibrium strategy, the same EV. A solver that exploits this only needs to solve one of the 24.
Section 3 · Try it — interactive canonicalizer
Pick a hand + board. See the canonical form and equivalence class size.
Section 4 · The compute savings
What this means for poker solvers
| Scope | Without isomorphism | With isomorphism | Speedup |
|---|---|---|---|
| Distinct preflop hand types | 1,326 | 169 | ~7.8× |
| Distinct flop boards (3 cards from 50) | 22,100 | 1,755 | ~12.6× |
| Distinct (hand, flop) pairs | ~25M | ~1.3M | ~19× |
| Distinct turn boards (4 cards from 49) | ~270K | ~16K | ~17× |
| Distinct river boards (5 cards from 48) | ~2.6M | ~123K | ~21× |
| Full HUNL game tree (effective) | ~10160 states | ~10156–10158 | ~24× |
The 24× factor isn't constant — it's the upper bound (when all 4 suits are used distinguishably). Flushy and paired boards have smaller equivalence classes, so you save less. Across a representative HUNL workload, the average is roughly 18–22×.
Section 5 · When isomorphism breaks down
Not every "looks similar" situation is actually isomorphic
Iso only works when the suit relabeling preserves all strategic structure. The classic gotcha: flush-relevant relationships break when relabeling.
Config A's hero has 4 hearts → drawing to a flush. Config B's hero has only 1 heart → no flush draw. These play very differently on subsequent streets, so they're not in the same equivalence class.
The interactive canonicalizer above handles this correctly — try toggling between them and watch the class size change.
Section 6 · How it's implemented
Canonicalize, hash, deduplicate
The standard recipe in any production solver:
// 1. Pick a canonical ordering of suits function canonicalize(cards) { let best = null; // Try all 24 suit permutations for (const perm of allSuitPermutations()) { const transformed = cards.map(c => ({ rank: c.rank, suit: perm[c.suit] })); const repr = stringify(transformed); if (best === null || repr < best) best = repr; } return best; } // 2. Cache solver results by canonical key const cache = new Map(); function solveSpot(cards) { const key = canonicalize(cards); if (cache.has(key)) return cache.get(key); const result = runCFR(cards); // expensive — runs once per canonical class cache.set(key, result); return result; } // 3. When user asks about non-canonical input, look up by canonical form, // then apply the inverse permutation to the strategy
That's it. Most production poker solvers (postflop-solver, OpenSpiel's universal_poker, libratus open-source forks) ship this as library code — you fork it, you don't write it from scratch.
Section 7 · Why DeepStack training needs this
The difference between feasible and infeasible
DeepStack's training pipeline generates 10 million turn examples, each requiring one CFR solve. Without isomorphism:
- 10M examples × 24× more redundant work = effectively 240M solves of compute
- ~580 days on a single 2017 GTX 1080 (the paper's baseline)
- Becomes ~14,000 days = 38 years on that single GPU
With isomorphism applied at every layer (hand canonicalization, board canonicalization, range vector indexing):
- 10M examples × ~1× redundant work (each is a unique canonical form)
- ~580 days on a single GTX 1080 → days on a modern cluster