Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorporate error into handicap adjustments #70

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 57 additions & 4 deletions analysis/util/RatingMath.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,27 @@
optimizer_rating_control_points: List[float]


HANDICAP_ERROR_KOMI=0.5
HANDICAP_ERROR_EXTREME=9
HANDICAP_ERROR_EXTREME_BASE=1.5

handicap_error = cli.add_argument_group("rating error variables")
handicap_error.add_argument(
"--handicap-error-komi", dest="handicap_error_komi", type=float,
default=HANDICAP_ERROR_KOMI,
help="error to add if handicap includes a significant komi adjustment",
)
handicap_error.add_argument(
"--handicap-error-extreme", dest="handicap_error_extreme", type=int,
default=HANDICAP_ERROR_EXTREME,
help="number of ranks before extreme handicap error is 1; 0 to turn off"
)
handicap_error.add_argument(
"--handicap-error-extreme-base", dest="handicap_error_extreme_base", type=float,
default=HANDICAP_ERROR_EXTREME_BASE,
help="power base to scale error for extreme handicaps"
)

def rank_to_rating(rank: float) -> float:
return _rank_to_rating(rank)

Expand Down Expand Up @@ -123,18 +144,44 @@ def get_handicap_rank_difference(handicap: int, size: int, komi: float, rules: s
return black_head_start / stone_value_territory


def get_handicap_adjustment(player: str, rating: float, handicap: int, size: int, komi: float, rules: str) -> float:
def get_handicap_adjustment(player: str, rating: float, handicap: int, size: int, komi: float, rules: str) -> (float, float):
global HANDICAP_ERROR_KOMI
global HANDICAP_ERROR_EXTREME
global HANDICAP_ERROR_EXTREME_BASE

rank_difference = get_handicap_rank_difference(handicap, size, komi, rules)

# Add ±0.5 error if the rank difference isn't (mostly) coming from handicap
# stones.
komi_error = 0
if abs(rank_difference) < (handicap - 0.5)/1.5:
komi_error = HANDICAP_ERROR_KOMI
elif abs(rank_difference) > (handicap + 0.5)*1.5:
komi_error = HANDICAP_ERROR_KOMI

# Scale error by rank difference, with ±1 error at rank difference of 9.
if HANDICAP_ERROR_EXTREME > 0:
rank_error = max(
komi_error,
(abs(rank_difference)/HANDICAP_ERROR_EXTREME + komi_error)**HANDICAP_ERROR_EXTREME_BASE,
)
else:
rank_error = komi_error

# Apply the +/- for white/black in the "rank" domain where it's symmetric.
# Note that the "rating" domain is log-scale, where +/- is asymmetric.
assert player == "white" or player == "black"
if player == "black":
effective_rank = rating_to_rank(rating) + rank_difference
else:
effective_rank = rating_to_rank(rating) - rank_difference
effective_rating = rank_to_rating(effective_rank)

# Convert the error to the rating domain.
rating_error = max(rank_to_rating(effective_rank + rank_error) - effective_rating,
effective_rating - rank_to_rating(effective_rank - rank_error))

return rank_to_rating(effective_rank) - rating
return (effective_rating - rating, rating_error)


def set_optimizer_rating_points(points: List[float]) -> None:
Expand All @@ -148,6 +195,12 @@ def configure_rating_to_rank(args: argparse.Namespace) -> None:
global A
global C
global D
global HANDICAP_ERROR_KOMI
global HANDICAP_ERROR_EXTREME
global HANDICAP_ERROR_EXTREME_BASE
HANDICAP_ERROR_KOMI=args.handicap_error_komi
HANDICAP_ERROR_EXTREME=args.handicap_error_extreme
HANDICAP_ERROR_EXTREME_BASE=args.handicap_error_extreme_base

system: str = args.ranks
a: float = args.a
Expand Down Expand Up @@ -318,8 +371,8 @@ def __rating_to_rank(rating: float) -> float:
# Sensei's Library: <https://senseis.xmp.net/?Komi#toc8>
# - "Perfect Komi" on the "Komi (Go)" page at Wikipedia:
# <https://en.wikipedia.org/wiki/Komi_(Go)#Perfect_Komi>
assert round(get_handicap_adjustment(player, 1000.0, 0, size=size, rules="japanese", komi=6), 8) == 0
assert round(get_handicap_adjustment(player, 1000.0, 0, size=size, rules="aga", komi=7), 8) == 0
assert round(get_handicap_adjustment(player, 1000.0, 0, size=size, rules="japanese", komi=6)[0], 8) == 0
assert round(get_handicap_adjustment(player, 1000.0, 0, size=size, rules="aga", komi=7)[0], 8) == 0


def lerp(x:float, y:float, a:float):
Expand Down
8 changes: 4 additions & 4 deletions goratings/math/glicko2.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ def __str__(self) -> str:
0 if self.timestamp is None else self.timestamp,
)

def copy(self, rating_adjustment: float = 0.0, rd_adjustment: float = 0.0) -> "Glicko2Entry":
ret = Glicko2Entry(self.rating + rating_adjustment, self.deviation + rd_adjustment, self.volatility, self.timestamp,)
def copy(self, adjustment: (float, float) = (0.0, 0.0)) -> "Glicko2Entry":
ret = Glicko2Entry(self.rating + adjustment[0], (self.deviation ** 2 + adjustment[1] ** 2) ** 0.5, self.volatility, self.timestamp,)
return ret

def expand_deviation_because_no_games_played(self, n_periods: int = 1) -> "Glicko2Entry":
Expand Down Expand Up @@ -81,7 +81,7 @@ def expand_deviation(self, age: int, override_aging_period: int | None = None) -

return self

def expected_win_probability(self, white: "Glicko2Entry", handicap_adjustment: float, ignore_g: bool = False) -> float:
def expected_win_probability(self, white: "Glicko2Entry", handicap_adjustment: (float, float), ignore_g: bool = False) -> float:
# Implementation extracted from glicko2_update below.
if not ignore_g:
def g() -> float:
Expand All @@ -90,7 +90,7 @@ def g() -> float:
def g() -> float:
return 1 / sqrt(1 + (3 * white.phi ** 2) / (pi ** 2))

E = 1 / (1 + exp(-g() * (self.rating + handicap_adjustment - white.rating) / GLICKO2_SCALE))
E = 1 / (1 + exp(-g() * (self.rating + handicap_adjustment[0] - white.rating) / GLICKO2_SCALE))
return E


Expand Down