Source code for egta.bootstrap

"""module for bootstrapping regret and surplus"""
import asyncio

import numpy as np
from gameanalysis import utils


[docs]async def deviation_payoffs(sched, mix, num, *, boots=0, chunk_size=None): """Bootstrap deviation payoffs Parameters ---------- prof_sched : Scheduler The scheduler to sample profiles from. mix : ndarray The mixture to calculate the regret of. num : int The number of samples to gather. Must be positive. boots : int, optional The number of bootstrap samples to take. The accuracy of bootstrap is independent of this number, but more will reduce the variance of the underlying confidence bounds. The default will compute no bootstrap gains. chunk_size : int, optional An implementation detail specifying how frequently profiles are scheduled since this algorithm inherently operates in a streaming manner. Ideally this number should be set such that the time to schedule and process chunk_size roughly equals the time for one simulation. It also controls how much memory this uses. By default this is set to ten times the number of bootstraps, or 1000 if no bootstraps are requested. Notes ----- This uses memory on the order of `boots + chunk_size`. It is inefficient if `num` is less than boots. Returns ------- mean_gains : ndarray (num_strats,) The mean deviation payoffs from the mixture. boot_gains : ndarray (boots, num_strats) The deviation payoffs for each bootstrap sample. """ utils.check(num > 0, "can't schedule zero samples") mix = np.asarray(mix, float) chunk_size = chunk_size or boots * 10 or 1000 devs = np.empty(mix.size) mean_devs = np.zeros(mix.size) boot_devs = np.zeros((boots, mix.size)) remaining = np.empty(boots, int) remaining.fill(num) # XXX This could be made less awkward, but it would help to require python # 3.6 i = 0 futures = [] async def update(): """update""" nonlocal i fiter = iter(futures) for _ in range(len(futures) // sched.num_strats): for j in range(sched.num_strats): pay = await next(fiter) devs[j] = pay[j] np.add((devs - mean_devs) / (i + 1), mean_devs, mean_devs) samps = np.random.binomial(remaining, 1 / (num - i)) np.subtract(remaining, samps, remaining) np.add(samps[:, None] * devs / num, boot_devs, boot_devs) i += 1 left = num while left > 0: new_profs = sched.random_deviation_profiles(min(left, chunk_size), mix).reshape( (-1, mix.size) ) left -= chunk_size new_futures = [ asyncio.ensure_future(sched.sample_payoffs(prof)) for prof in new_profs ] await update() futures = new_futures await update() return mean_devs, boot_devs