Metadata-Version: 2.4
Name: whole-history-rating
Version: 2.0.0
Summary: Python implementation of Rémi Coulom's Whole-History Rating (WHR) algorithm.
Project-URL: Homepage, https://github.com/pfmonville/whole_history_rating
Project-URL: Repository, https://github.com/pfmonville/whole_history_rating
Project-URL: Issues, https://github.com/pfmonville/whole_history_rating/issues
Author-email: Pierre-François Monville <pfmonville@users.noreply.github.com>
License: Copyright (c) 2012 Pete Schwamb
        
        Permission is hereby granted, free of charge, to any person obtaining
        a copy of this software and associated documentation files (the
        "Software"), to deal in the Software without restriction, including
        without limitation the rights to use, copy, modify, merge, publish,
        distribute, sublicense, and/or sell copies of the Software, and to
        permit persons to whom the Software is furnished to do so, subject to
        the following conditions:
        
        The above copyright notice and this permission notice shall be
        included in all copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
        EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
        MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
        NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
        LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
        OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
        WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
License-File: LICENCE.txt
Keywords: bayesian,chess,elo,go,ranking,rating,whole-history-rating,whr
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Python: >=3.11
Requires-Dist: numpy>=2.0
Description-Content-Type: text/markdown


# Whole History Rating (WHR) Python Implementation

This Python library is a conversion from the original Ruby implementation of Rémi Coulom's Whole-History Rating (WHR) algorithm, designed to provide a dynamic rating system for games or matches where players' skills are continuously estimated over time.

The original Ruby code is available here at [goshrine](https://github.com/goshrine/whole_history_rating).

## Installation

To install the library, use the following command:

```shell
pip install whole-history-rating
```

## Usage

### Basic Setup

Start by importing the library and initializing the WHR object:

```python
from whr import WHR

whr = WHR()
```

> The class used to be called `Base`. That name still works as a deprecated
> alias (it emits a `DeprecationWarning`); prefer `WHR` in new code.

### Creating Games

Add games to the system using `create_game()` method. It takes the names of the black and white players, the winner ('B' for black, 'W' for white), the day number, and an optional handicap (generally less than 500 elo).

```python
whr.create_game("shusaku", "shusai", "B", 1, 0)
whr.create_game("shusaku", "shusai", "W", 2, 0)
whr.create_game("shusaku", "shusai", "W", 3, 0)
```


### Refining Ratings Towards Stability

To achieve accurate and stable ratings, the WHR algorithm allows for iterative refinement. This process can be controlled manually or handled automatically to adjust player ratings until they reach a stable state.

#### Manual Iteration

For manual control over the iteration process, specify the number of iterations you wish to perform. This approach gives you direct oversight over the refinement steps.

```python
whr.iterate(50)
```

This command will perform 50 iterations, incrementally adjusting player ratings towards stability with each step.

#### Automatic Iteration

For a more hands-off approach, the algorithm can automatically iterate until the Elo ratings stabilize within a specified precision. Automatic iteration is particularly useful when dealing with large datasets or when seeking to automate the rating process.

```python
whr.auto_iterate(time_limit=10, precision=1e-3, batch_size=10)
```

- `time_limit` (optional): Sets a maximum duration (in seconds) for the iteration process. If `None` (the default), the algorithm will run indefinitely until the specified precision is achieved.
- `precision` (optional): Defines the desired level of accuracy for the ratings' stability. The default value is `0.001`, indicating that iteration will stop when changes between iterations are less than or equal to this threshold.
- `batch_size` (optional): Determines the number of iterations to perform before checking for convergence and, if a `time_limit` is set, before evaluating whether the time limit has been reached. The default value is `10`, balancing between frequent convergence checks and computational efficiency.

This automated process allows the algorithm to efficiently converge to stable ratings, adjusting the number of iterations dynamically based on the complexity of the data and the specified precision and time constraints.


### Viewing Ratings

Retrieve and view player ratings, which include the day number, elo rating, and uncertainty:

```python
# Example output for whr.ratings_for_player("shusaku")
print(whr.ratings_for_player("shusaku"))
# Output (one (day, elo, uncertainty) tuple per playing day):
#   [(1, -43, 0.84),
#    (2, -45, 0.84),
#    (3, -45, 0.84)]

# Example output for whr.ratings_for_player("shusai")
print(whr.ratings_for_player("shusai"))
# Output:
#   [(1, 43, 0.84),
#    (2, 45, 0.84),
#    (3, 45, 0.84)]
```

Querying an unknown player raises a `ValueError`.

You can also view or retrieve all ratings in order:

```python
whr.print_ordered_ratings(current=False)  # Set `current=True` for the latest rankings only.
ratings = whr.get_ordered_ratings(current=False, compact=False)  # Set `compact=True` for a condensed list.
```

### Predicting Match Outcomes

Predict the outcome of future matches, including between non-existent players:

```python
# Example of predicting a future match outcome. It is a pure query: it does
# not print anything, and unknown players are treated as an even (gamma = 1)
# reference without being added to the base.
probability = whr.probability_future_match("shusaku", "shusai", 0)
print(f"Win probability: shusaku: {probability[0]*100}%; shusai: {probability[1]*100}%")
# Output:
#   Win probability: shusaku: 37.24%; shusai: 62.76%
```


### Enhanced Batch Loading of Games

This feature facilitates the batch loading of multiple games simultaneously by accepting a list of strings, where each string encapsulates the details of a single game. To accommodate names with both first and last names and ensure flexibility in data formatting, you can specify a custom separator (e.g., a comma) to delineate the game attributes.

#### Standard Loading

Without specifying a separator, the default space (' ') is used to split the game details:

```python
whr.load_games([
    "shusaku shusai B 1 0",  # Game 1: Shusaku vs. Shusai, Black wins, Day 1, no handicap.
    "shusaku shusai W 2 0",  # Game 2: Shusaku vs. Shusai, White wins, Day 2, no handicap.
    "shusaku shusai W 3 0"   # Game 3: Shusaku vs. Shusai, White wins, Day 3, no handicap.
])
```

#### Custom Separator for Complex Names

When game details include names with spaces, such as first and last names, utilize the `separator` parameter to define an alternative delimiter, ensuring the integrity of each data point:

```python
whr.load_games([
    "John Doe, Jane Smith, W, 1, 0",  # Game 1: John Doe vs. Jane Smith, White wins, Day 1, no handicap.
    "Emily Chen, Liam Brown, B, 2, 0"  # Game 2: Emily Chen vs. Liam Brown, Black wins, Day 2, no handicap.
], separator=",")
```

This method allows for a clear and error-free way to load game data, especially when player names or game details include spaces, providing a robust solution for managing diverse datasets.


### Saving and Loading States

Save the current state to a file and reload it later to avoid recalculating:

```python
whr.save_base('path_to_save.whr')
whr2 = WHR.load_base('path_to_save.whr')
```

The state is serialized as a flat description (config, games and computed
ratings) rather than the raw object graph, so saving and loading works for a
history of any size and the computed ratings are preserved on reload. Files
written by older versions are still readable.

## Optional Configuration

Adjust the `w2` parameter, which influences the variance of rating change over time, allowing for faster or slower progression. The default is set to 300, but Rémi Coulom used a value of 14 in his paper to achieve his results.

```python
whr = WHR({'w2': 14})
```

Enable case-insensitive player names to treat "shusaku" and "ShUsAkU" as the same entity:

```python
whr = WHR({'uncased': True})
```
