Skip to content

Commit

Permalink
half fix of the issue #5
Browse files Browse the repository at this point in the history
  • Loading branch information
sublee committed Feb 28, 2013
1 parent bc56f1e commit 1d3208a
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 16 deletions.
5 changes: 5 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
Version 0.2.2
-------------

Under development.

Version 0.2.1
-------------

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,6 @@ def run_tests(self):
'Topic :: Games/Entertainment'],
install_requires=['distribute'],
test_suite='trueskilltests',
tests_require=['pytest', 'almost'],
tests_require=['pytest', 'almost>=0.1.4'],
use_2to3=(sys.version_info[0] >= 3),
)
7 changes: 5 additions & 2 deletions trueskill/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ def v_win(diff, draw_margin):
mean.
"""
x = diff - draw_margin
return pdf(x) / cdf(x)
denom = cdf(x)
return (pdf(x) / denom) if denom else -x


def v_draw(diff, draw_margin):
Expand All @@ -51,7 +52,7 @@ def v_draw(diff, draw_margin):
a, b = draw_margin - abs_diff, -draw_margin - abs_diff
denom = cdf(a) - cdf(b)
numer = pdf(b) - pdf(a)
return numer / denom * (-1 if diff < 0 else 1)
return (numer / denom) * (-1 if diff < 0 else 1)


def w_win(diff, draw_margin):
Expand All @@ -60,6 +61,8 @@ def w_win(diff, draw_margin):
"""
x = diff - draw_margin
v = v_win(diff, draw_margin)
if v == -x:
return 1. if diff < 0 else 0.
return v * (v + x)


Expand Down
14 changes: 10 additions & 4 deletions trueskill/factorgraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ def set(self, val):
return delta

def delta(self, other):
return max(abs(self.tau - other.tau),
math.sqrt(abs(self.pi - other.pi)))
pi_delta = abs(self.pi - other.pi)
if pi_delta == inf:
return 0.
return max(abs(self.tau - other.tau), math.sqrt(pi_delta))

def update_message(self, factor, pi=0, tau=0, message=None):
message = message or Gaussian(pi=pi, tau=tau)
Expand Down Expand Up @@ -193,5 +195,9 @@ def up(self):
v = self.v_func(*args)
w = self.w_func(*args)
denom = (1. - w)
pi, tau = div.pi / denom, (div.tau + sqrt_pi * v) / denom
return val.update_value(self, pi, tau)
if denom:
pi, tau = div.pi / denom, (div.tau + sqrt_pi * v) / denom
else:
pi = tau = inf
delta = val.update_value(self, pi, tau)
return delta if denom else 0
27 changes: 27 additions & 0 deletions trueskillhelpers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import with_statement
from contextlib import contextmanager
import functools
import inspect
import logging


Expand Down Expand Up @@ -34,3 +37,27 @@ def force_scipycompat():
t.cdf, t.pdf, t.ppf = c.cdf, c.pdf, c.ppf
yield
t.cdf, t.pdf, t.ppf = cdf, pdf, ppf


def with_or_without_scipy(f=None):
if f is None:
def iterate():
try:
import scipy
except ImportError:
# without
yield False
else:
# with
yield True
# without
with force_scipycompat():
yield False
return iterate()
@functools.wraps(f)
def wrapped(*args, **kwargs):
for with_scipy in with_or_without_scipy():
if 'with_scipy' in inspect.getargspec(f)[0]:
kwargs['with_scipy'] = with_scipy
f(*args, **kwargs)
return wrapped
26 changes: 17 additions & 9 deletions trueskilltests.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
numpy = False

from trueskill import *
from trueskillhelpers import with_or_without_scipy


inf = float('inf')
nan = float('nan')


class almost(Approximate):
Expand Down Expand Up @@ -190,6 +195,7 @@ def generate_individual(size, env=None):
return generate_teams([1] * size, env)


@with_or_without_scipy
def test_n_vs_n():
# 1 vs 1
t1, t2 = generate_teams([1, 1])
Expand All @@ -213,6 +219,7 @@ def test_n_vs_n():
(22.802, 8.059), (22.802, 8.059), (22.802, 8.059), (22.802, 8.059)]


@with_or_without_scipy
def test_1_vs_n():
t1, = generate_teams([1])
# 1 vs 2
Expand All @@ -237,6 +244,7 @@ def test_1_vs_n():
(9.418, 7.917), (9.418, 7.917), (9.418, 7.917), (9.418, 7.917)]


@with_or_without_scipy
def test_individual():
# 3 players
players = generate_individual(3)
Expand Down Expand Up @@ -271,6 +279,7 @@ def test_individual():
(17.664, 4.433), (15.653, 4.524), (13.190, 4.711), (9.461, 5.276)]


@with_or_without_scipy
def test_multiple_teams():
# 2 vs 4 vs 2
t1 = (Rating(40, 4), Rating(45, 3))
Expand All @@ -287,6 +296,7 @@ def test_multiple_teams():
assert almost(quality([t1, t2, t3])) == 0.047


@with_or_without_scipy
def test_upset():
# 1 vs 1
t1, t2 = (Rating(),), (Rating(50, 12.5),)
Expand Down Expand Up @@ -318,6 +328,7 @@ def test_upset():
(31.751, 3.064), (34.051, 2.541), (38.263, 1.849), (44.118, 0.983)]


@with_or_without_scipy
def test_partial_play():
t1, t2 = (Rating(),), (Rating(), Rating())
# each results from C# Skills:
Expand All @@ -340,6 +351,7 @@ def test_partial_play():
assert almost(quality([t1, t2, t3], [(1,), (0.8, 0.9), (1,)])) == 0.0809


@with_or_without_scipy
def test_partial_play_with_weights_dict():
t1, t2 = (Rating(),), (Rating(), Rating())
assert rate([t1, t2], weights={(0, 0): 0.5, (1, 0): 0.5, (1, 1): 0.5}) == \
Expand All @@ -353,6 +365,7 @@ def test_partial_play_with_weights_dict():
# reported bugs


@with_or_without_scipy
def test_issue3():
"""The `issue #3`_, opened by @youknowone.
Expand Down Expand Up @@ -398,21 +411,16 @@ def test_issue4():
numpy.seterr(**old_settings)


@with_or_without_scipy
def test_issue5():
"""The `issue #5`_, opened by @warner121.
The result of TrueSkill calculator by Microsoft is N(-273.092, 2.683) and
N(-75.830, 2.080), of Moserware's C# Skills is N(NaN, 2.6826) and
N(-75.830, 2.080), of C# Skills by Moserware is N(NaN, 2.6826) and
N(NaN, 2.0798). I choose Microsoft's result as an expectation.
.. _issue #5: https://github.com/sublee/trueskill/issues/5
"""
from logging import StreamHandler, DEBUG
from trueskillhelpers import factorgraph_logging, force_scipycompat
r1, r2 = Rating(-323.263, 2.965), Rating(-48.441, 2.190)
with factorgraph_logging() as logger:
logger.setLevel(DEBUG)
logger.addHandler(StreamHandler(sys.stderr))
with force_scipycompat():
assert almost(rate_1vs1(r1, r2)) == \
[(-273.092, 2.683), (-75.830, 2.080)]
# the result of C# Skills by Moserware
assert almost(rate_1vs1(r1, r2)) == [(nan, 2.683), (nan, 2.080)]

0 comments on commit 1d3208a

Please # to comment.