Skip to content

Commit 35e42ed

Browse files
authored
Add pypy support (#80)
* Add pypy support * Switch to builtin statistics library. Drop scipy requirement due to negligible difference in benchmark performance. * Using custom ranking function * Drop 3.7 support due to nearing EOL * Skip doctest for *.rst files * Adjust relative tolerance of doctest number comparison * Change doctest glob to *.py * Add changelog fragments
1 parent ff33d76 commit 35e42ed

File tree

13 files changed

+45
-19
lines changed

13 files changed

+45
-19
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
fail-fast: false
99
matrix:
1010
os: [Ubuntu, MacOS, Windows]
11-
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
11+
python-version: ["3.8", "3.9", "3.10", "3.11", "pypy3.8", "pypy3.9"]
1212
experimental: [false]
1313
include:
1414
- os: Ubuntu
@@ -42,7 +42,7 @@ jobs:
4242
python -m build
4343
twine check dist/*
4444
black --check .
45-
pytest . --cov --cov-report=term-missing -vv
45+
pytest . --cov --cov-report=term-missing -vv --doctest-glob="*.py"
4646
coverage report
4747
coverage html
4848
coverage xml --ignore-errors

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ Sometimes you want to know what the likelihood is someone will place at a partic
170170
>>> team_1, team_2, team_3 = [a1, a2, a3], [b1, b2, b3], [c1, c2, c3]
171171
>>> draw_probability = predict_draw(teams=[team_1, team_2, team_3])
172172
>>> draw_probability
173-
0.3295385074666581
173+
0.329538507466658
174174
>>> rank_probability = predict_rank(teams=[team_1, team_2, team_3])
175175
>>> rank_probability
176176
[(1, 0.4450361350569973), (2, 0.19655022513040032), (3, 0.028875132345944337)]

benchmark/benchmark.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from prompt_toolkit.completion import WordCompleter
66
from prompt_toolkit.validation import ValidationError, Validator
77

8-
from benchmark.processors import Competition
98
from openskill.models import (
109
BradleyTerryFull,
1110
BradleyTerryPart,

changes/80.breaking.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Drop support for Python 3.7

changes/80.doc.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve SEO of Documentation

changes/80.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add support for PyPy 3.8 and 3.9

docs/manual.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,10 @@ Sometimes you want to know what the likelihood is someone will place at a partic
137137
>>> team_1, team_2, team_3 = [a1, a2, a3], [b1, b2, b3], [c1, c2, c3]
138138
>>> draw_probability = predict_draw(teams=[team_1, team_2, team_3])
139139
>>> draw_probability
140-
0.3295385074666581
140+
0.329538507466658
141141
>>> rank_probability = predict_rank(teams=[team_1, team_2, team_3])
142142
>>> rank_probability
143-
[(1, 0.4450361350569973), (2, 0.19655022513040032), (3, 0.028875132345944337)]
143+
[(1, 0.4450361350569973), (2, 0.19655022513040035), (3, 0.02887513234594437)]
144144
>>> sum([y for x, y in rank_probability]) + draw_probability
145145
1.0
146146

openskill/rate.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,12 @@
44
from functools import reduce
55
from typing import List, Optional, Tuple, Union
66

7-
from scipy.stats import rankdata
8-
97
from openskill.constants import Constants, beta
108
from openskill.constants import mu as default_mu
119
from openskill.constants import sigma as default_sigma
1210
from openskill.models.plackett_luce import PlackettLuce
1311
from openskill.statistics import phi_major, phi_major_inverse
14-
from openskill.util import rankings, unwind
12+
from openskill.util import rank_minimum, rankings, unwind
1513

1614

1715
class Rating:
@@ -461,7 +459,7 @@ def predict_rank(
461459
]
462460

463461
ranked_probability = [abs(_) for _ in win_probability]
464-
ranks = list(rankdata(ranked_probability, method="min"))
462+
ranks = list(rank_minimum(ranked_probability))
465463
max_ordinal = max(ranks)
466464
ranks = [abs(_ - max_ordinal) + 1 for _ in ranks]
467465
predictions = list(zip(ranks, ranked_probability))

openskill/statistics.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
import sys
2+
from statistics import NormalDist
23

3-
import scipy.stats
4-
5-
normal = scipy.stats.norm(0, 1)
4+
normal = NormalDist()
65

76

87
def phi_major(x):
98
return normal.cdf(x)
109

1110

1211
def phi_major_inverse(x):
13-
return normal.ppf(x)
12+
return normal.inv_cdf(x)
1413

1514

1615
def phi_minor(x):

openskill/util.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,25 @@ def sorter(teams):
119119
return [x for x, _ in sorted_list], [x for _, x in sorted_list]
120120

121121
return sorter(teams) if isinstance(teams, list) else sorter
122+
123+
124+
def rank_simple(vector):
125+
return sorted(range(len(vector)), key=vector.__getitem__)
126+
127+
128+
def rank_minimum(a):
129+
n = len(a)
130+
i_vec = rank_simple(a)
131+
s_vec = [a[rank] for rank in i_vec]
132+
sum_ranks = 0
133+
duplicate_count = 0
134+
new_array = [0] * n
135+
for i in range(n):
136+
sum_ranks += i
137+
duplicate_count += 1
138+
if i == n - 1 or s_vec[i] != s_vec[i + 1]:
139+
for j in range(i - duplicate_count + 1, i + 1):
140+
new_array[i_vec[j]] = i + 1 - duplicate_count + 1
141+
sum_ranks = 0
142+
duplicate_count = 0
143+
return new_array

0 commit comments

Comments
 (0)