Skip to content

Commit

Permalink
- add scipycompat for compatibility with non-scipy
Browse files Browse the repository at this point in the history
- support Python 2.5
- add tox.ini for testing with Tox
  • Loading branch information
Heungsub Lee committed Nov 13, 2012
1 parent 4a7d0f8 commit 80f8875
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 58 deletions.
6 changes: 6 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[tox]
envlist = py25, py26, py27, pypy

[testenv]
deps = Attest
commands = python -m attest.run trueskilltests.suite
19 changes: 9 additions & 10 deletions trueskill/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
import math

from .mathematics import cdf, pdf, ppf, Gaussian, Matrix
from .factorgraph import (
Variable, PriorFactor, LikelihoodFactor, SumFactor, TruncateFactor)
from .factorgraph import (Variable, PriorFactor, LikelihoodFactor, SumFactor,
TruncateFactor)


__copyright__ = 'Copyright 2012 by Heungsub Lee'
Expand Down Expand Up @@ -241,10 +241,10 @@ def build_factor_graph(self, rating_groups, ranks):
team_sizes = _team_sizes(rating_groups)
# layer builders
def build_rating_layer():
for rating_var, rating in zip(rating_vars, ratings):
for rating_var, rating in itertools.izip(rating_vars, ratings):
yield PriorFactor(rating_var, rating, self.tau)
def build_perf_layer():
for rating_var, perf_var in zip(rating_vars, perf_vars):
for rating_var, perf_var in itertools.izip(rating_vars, perf_vars):
yield LikelihoodFactor(rating_var, perf_var, self.beta ** 2)
def build_teamperf_layer():
for team, teamperf_var in enumerate(teamperf_vars):
Expand All @@ -271,11 +271,9 @@ def build_trunc_layer():
v_func, w_func = v_win, w_win
yield TruncateFactor(teamdiff_var, v_func, w_func, draw_margin)
# build layers
return (
list(build_rating_layer()), list(build_perf_layer()),
list(build_teamperf_layer()), list(build_teamdiff_layer()),
list(build_trunc_layer())
)
return (list(build_rating_layer()), list(build_perf_layer()),
list(build_teamperf_layer()), list(build_teamdiff_layer()),
list(build_trunc_layer()))

def run_schedule(self, rating_layer, perf_layer, teamperf_layer,
teamdiff_layer, trunc_layer, min_delta=DELTA):
Expand Down Expand Up @@ -364,7 +362,8 @@ def compare(x, y):
unsorting_hint = [x for x, (r, g) in sorting]
# build factor graph
layers = self.build_factor_graph(sorted_groups, sorted_ranks)
self.run_schedule(*layers, min_delta=min_delta)
args = layers + (min_delta,)
self.run_schedule(*args)
# make result
rating_layer, team_sizes = layers[0], _team_sizes(sorted_groups)
transformed_groups = []
Expand Down
56 changes: 10 additions & 46 deletions trueskill/mathematics.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,59 +9,23 @@
:copyright: (c) 2012 by Heungsub Lee.
:license: BSD, see LICENSE for more details.
"""
from __future__ import absolute_import
import copy
import math
from numbers import Number


__all__ = 'cdf', 'pdf', 'ppf', 'Gaussian', 'Matrix'

try:
from numbers import Number
except ImportError:
Number = (int, long, float, complex)

try:
from scipy.stats import norm
cdf, pdf, ppf = norm.cdf, norm.pdf, norm.ppf
except ImportError:
from .scipycompat import cdf, pdf, ppf
else:
cdf, pdf, ppf = norm.cdf, norm.pdf, norm.ppf


def erfcc(x):
"""Complementary error function (via http://bit.ly/zOLqbc)"""
z = abs(x)
t = 1. / (1. + z / 2.)
r = t * math.exp(-z * z - 1.26551223 + t * (1.00002368 + t * (
0.37409196 + t * (0.09678418 + t * (
-0.18628806 + t * (0.27886807 + t * (
-1.13520398 + t * (1.48851587 + t * (
-0.82215223 + t * 0.17087277)))))))))
return 2. - r if x < 0 else r

def ierfcc(y):
"""The inverse function of erfcc"""
if y >= 2:
return -100
elif y <= 0:
return 100
zero_point = y < 1
if not zero_point:
y = 2 - y
t = math.sqrt(-2 * math.log(y / 2.))
x = -0.70711 * \
((2.30753 + t * 0.27061) / (1. + t * (0.99229 + t * 0.04481)) - t)
for i in xrange(2):
err = erfcc(x) - y
x += err / (1.12837916709551257 * math.exp(-(x ** 2)) - x * err)
return x if zero_point else -x

def cdf(x, mu=0, sigma=1):
"""Cumulative distribution function"""
return 0.5 * erfcc(-(x - mu) / (sigma * math.sqrt(2)))

def pdf(x, mu=0, sigma=1):
"""Probability density function"""
return (1 / math.sqrt(2 * math.pi) * abs(sigma)) * \
math.exp(-(((x - mu) / abs(sigma)) ** 2 / 2))

def ppf(x, mu=0, sigma=1):
"""The inverse function of CDF"""
return mu - sigma * math.sqrt(2) * ierfcc(2 * x)
__all__ = ['cdf', 'pdf', 'ppf', 'Gaussian', 'Matrix']


class Gaussian(object):
Expand Down
62 changes: 62 additions & 0 deletions trueskill/scipycompat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
"""
trueskill.scipycompat
~~~~~~~~~~~~~~~~~~~~~
A fallback of `scipy`_.
.. _scipy:: http://www.scipy.org/
:copyright: (c) 2012 by Heungsub Lee.
:license: BSD, see LICENSE for more details.
"""
import math


__all__ = ['cdf', 'pdf', 'ppf']


def erfcc(x):
"""Complementary error function (via http://bit.ly/zOLqbc)"""
z = abs(x)
t = 1. / (1. + z / 2.)
r = t * math.exp(-z * z - 1.26551223 + t * (1.00002368 + t * (
0.37409196 + t * (0.09678418 + t * (
-0.18628806 + t * (0.27886807 + t * (
-1.13520398 + t * (1.48851587 + t * (
-0.82215223 + t * 0.17087277)))))))))
return 2. - r if x < 0 else r


def ierfcc(y):
"""The inverse function of erfcc"""
if y >= 2:
return -100
elif y <= 0:
return 100
zero_point = y < 1
if not zero_point:
y = 2 - y
t = math.sqrt(-2 * math.log(y / 2.))
x = -0.70711 * \
((2.30753 + t * 0.27061) / (1. + t * (0.99229 + t * 0.04481)) - t)
for i in xrange(2):
err = erfcc(x) - y
x += err / (1.12837916709551257 * math.exp(-(x ** 2)) - x * err)
return x if zero_point else -x


def cdf(x, mu=0, sigma=1):
"""Cumulative distribution function"""
return 0.5 * erfcc(-(x - mu) / (sigma * math.sqrt(2)))


def pdf(x, mu=0, sigma=1):
"""Probability density function"""
return (1 / math.sqrt(2 * math.pi) * abs(sigma)) * \
math.exp(-(((x - mu) / abs(sigma)) ** 2 / 2))


def ppf(x, mu=0, sigma=1):
"""The inverse function of CDF"""
return mu - sigma * math.sqrt(2) * ierfcc(2 * x)
13 changes: 11 additions & 2 deletions trueskilltests.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import with_statement
import sys
import warnings

from attest import Tests, assert_hook, raises
Expand Down Expand Up @@ -91,7 +93,8 @@ def invalid_rating_groups():
env.validate_rating_groups([(Rating(),), {0: Rating()}])


@suite.test
# there's no warnings.catch_warnings under Python 2.6
@suite.test_if(sys.version_info >= (2, 6))
def deprecated_methods():
env = TrueSkill()
r1, r2, r3 = Rating(), Rating(), Rating()
Expand Down Expand Up @@ -122,6 +125,9 @@ def deprecated_individual_rating_groups():
rate([r1, r2, r3])
with raises(TypeError):
quality([r1, r2, r3])
if sys.version_info < (2, 6):
# there's no warnings.catch_warnings under Python 2.6
return
# deprecated methods accept individual rating groups
with warnings.catch_warnings():
warnings.simplefilter('ignore')
Expand Down Expand Up @@ -214,7 +220,10 @@ def almost_equals(self, val1, val2):
else:
if round(val1, self.precision) == round(val2, self.precision):
return True
fmt = '%.{0}f'.format(self.precision)
try:
fmt = '%.{0}f'.format(self.precision)
except AttributeError:
fmt = '%%.%df' % self.precision
mantissa = lambda f: int((fmt % f).replace('.', ''))
return abs(mantissa(val1) - mantissa(val2)) <= 1

Expand Down

0 comments on commit 80f8875

Please sign in to comment.