Quote:
Originally Posted by _dave_
Adrian, did you read the monster thread "7 card hand evaluators" now in the archives?
I do not know what isomorphisms are (and wikipedia looks too complex for me right now - saved tab for later
) - but there wer some silly fast evaluators in there.
Yes, I've read the monster thread a couple of times. But, the monster thread only deals with 7 cards hand evaluators (a function that from 7 cards returns an integer).
When you have to do a multiple players evaluation of ranges against ranges (or even hands against hands), the evaluation can be speed by using these suits isomorphisms as I've tried to explain in my post.
At the end is the relevant part of the paper, but only deals with two players optimizations.
TIA & Regards ...
Potential-aware automated abstraction of sequential games, and holistic equilibrium analysis of Texas Hold’em poker
http://www.cs.cmu.edu/~sandholm/gs3.aaai07.pdf
Exploiting suit isomorphisms
We now introduce ways in which we exploit suit isomorphisms. We first discuss a custom indexing scheme which dramatically reduces the space requirements of representing the abstraction. In the subsection after that, we present a way to exploit suit isomorphisms to speed up a key computation.
Indexing for efficient abstraction representation
One challenge that is especially difficult when using a four-round model is that the number of distinct hands a player can face is huge. Our algorithm requires an integer index for each distinct hand in order to perform the lookup to see which abstracted bucket the given hand belongs to. The number of distinct hands, C(52;2) * C(50;3) * C(47;1) * C(46;1) ≈ 5.6 * 1010, is an order of magnitude too big to give each hand a unique index. For example, encoding the bucket for each hand using two bytes requires more than 104 gigabytes of storage. This would severely limit the practicality of the approach, since this storage is also required by our player at run-time.
We therefore introduce a more compact representation of the abstraction that capitalizes on a canonical representation of each hand based on suit symmetries. (This technique is valid since the rules of poker state that all suits are equally strong.) These canonical representations are computed using permutations (total orderings of the suits) and partial permutations (partial orderings of the suits), as we will describe later in this section.
The best size reduction one could hope for with this approach is a factor of 4! = 24, since we can map any permutation of the four suits to the same canonical hand. That this is not fully achievable is due to the fact that some hands are unaffected by some of the permutations of the suits, e.g. 4c4h is equivalent to 4h4c, in which case there are less than 24 distinct hands mapping to the same canonical one. We call this phenomenon self symmetry.
Our approach uses the following concept. The colexicographical index (Bollob´as 1986) of a set of integers x = {xi … xk} {0..n-1}, with xi < xj whenever i < j, is colex(x) = ki=1 C(xi;i). This index has the important property that for a given n, each of the C(n;k) sets of size k has a distinct colexicographical index. Furthermore, these indices are compactly encoded as the integers from 0 to C(n;k)-1.
We need to compute indices for hands from each of the four rounds. We compute these indices incrementally, using the index from round i to compute the index in round i + 1. This approach gradually computes the permutations that map the given hand to its canonical representation. This incremental computation is useful both for providing a convenient way of computing the indices and for speeding up the index computation.
The index for the first round is computed, of course, using only the hole cards. If they are of the same suit, e.g. Ac7c, that suit is named “suit 1”, and we get the partial permutation c < {s,h,•}. If they are of different suits and different values, e.g. Ac7s, we name the suit of the card with the highest value “suit 1” and the other “suit 2”, resulting in the partial permutation c < s < {h,d}. Lastly, if they have the same value, e.g. 7s7h, the hand is self symmetric, and we have the partial permutation {s,h} < {c,d}. (At this point it is unspecified which of s and his “suit 1” and which is “suit 2”.)
The later rounds also give rise to partial permutations, which are then used to refine the permutation of suits that were undecided in previous rounds. For instance if the hole cards are 7s7h and the flop is 3dJdAh, we refine the partial permutation {s,h} < {c,d} with d < h < {s,c} to get < {h < s} < {d < c}, i.e. h < s < d < c.
Then, to compute the index from the (perhaps partial) permutation, our algorithm uses a case analysis which has far too many cases (60) to describe here. As an example, if the hole cards are 7s7h and the flop is 3dJdAh, then the analysis is in the category of one new suit in two cards and one old suit in a single card, breaking the self symmetry from the previous round. In this case the card with the old suit (h) only has 12 possible canonical values (even though there are 24 spades and hearts left in the deck), since no matter whether that new card would have been a s or a h, its suit will now have become “suit 1”. In the same way, the two other cards only have C(13;2) possible canonical values, since their suit will now have become “suit 3” no matter whether it is d or c. Thus this case has C(13;2) • 12 = 936 canonical hands representing four times that many actual hands, all of which share 7s7h as the hole cards.
Each of the cases simply breaks the hand up into sets to be encoded with colexicographical indexing. In the case above, the sets are {1,9} and {11} (index 11 is the highest of the 12 cards in the group). Here, 1 corresponds to the three, 9 corresponds to the Jack, and 11 corresponds to an Ace. The index within this case is then computed as colex({1, 9}) • 12 + colex({11}). This is then combined with the index within the case of the first round. There are 13 possible pairs, and our sevens have index 5. We thus get (colex({1, 9}) • 12 + colex({11})) • 13 + 5. Finally, to get the index, this is added to a global offset associated with this particular case.
With this indexing scheme, the memory consumption of the index for all four rounds reduces by a factor of 23.1 (which is close to the optimistic upper bound of 24).
Using symmetries to speed up 9-card rollout
For each pair of buckets in the fourth round, we need to compute the expected number of wins, losses, and draws for hands randomly drawn from those buckets. The straightforward approach of generating C(52;2) * C(50;2) * C(48;3) * C(45;1)* C(44;1) ≈ 5.56 • 1013 possible ways the cards can be dealt would require more than a month of CPU time. Furthermore, this computation would have to be started from scratch when we consider a new, different abstraction. But since we know that the indexing scheme will do the “suit renaming” anyway, we are able to just generate cards for all possible indices, with a weight indicating how many symmetric situations the current cards are representing. Furthermore, we use the fact that the two sets of hole cards are symmetric to only generate those where Player 1’s cards are “less” than Player 2’s cards, using an arbitrary ordering of the hole cards. Doing all this gives us more than a factor 44 speed up of the 9-card rollout, bringing it down to less than a day.