"""This module implements samplers for correlated private values settings."""
from abc import ABC, abstractmethod
from math import sqrt, ceil
import warnings
from typing import List, Tuple
import torch
from torch.cuda import _device_t as Device
from .base import PVSampler, CompositeValuationObservationSampler, FlushedWrappedSampler
from .samplers_ipv import UniformSymmetricIPVSampler
ERR_MSG_INVALID_LOCAL_GLOBAL_CORRELATION_METHOD = \
'Only "Bernoulli" and "constant" correlation methods are implemented for LocalGlobal samplers'
WRN_MSG_CORRELATED_BUT_CORR_IS_ZERO = \
"Warning: You specified a correlation method, but correlation is 0.0."
[docs]class LocalGlobalCompositePVSampler(CompositeValuationObservationSampler):
"""Settings with two groups of players: The local players have
symmetric (possibly correlated) uniform valuations on [0,1]. The
global bidders have symmetric (possibly correlated) uniform valuations on
[0,2].
"""
def __init__(self, n_locals: int, n_globals: int, valuation_size: int,
correlation_locals = 0.0, correlation_method_locals = None,
correlation_globals = 0.0, correlation_method_globals = None,
default_batch_size = 1 , default_device = None):
assert 0 <=correlation_locals <= 1, "invalid locals correlation"
assert 0 <=correlation_globals <= 1, "invalid globals correlation"
u_lo = 0.0
u_hi_locals = 1.0
u_hi_globals = 2.0
sampler_locals = self._get_group_sampler(
n_locals, correlation_locals, correlation_method_locals,
u_lo, u_hi_locals,
valuation_size, default_batch_size, default_device)
sampler_globals = self._get_group_sampler(
n_globals, correlation_globals, correlation_method_globals,
u_lo, u_hi_globals,
valuation_size, default_batch_size, default_device)
n_players = n_locals + n_globals
observation_size = valuation_size # this is a PV setting, valuations = observations
subgroup_samplers = [sampler_locals, sampler_globals]
super().__init__(n_players, valuation_size, observation_size, subgroup_samplers, default_batch_size, default_device)
def _get_group_sampler(self, n_group_players, correlation, correlation_method,
u_lo, u_hi,
valuation_size, default_batch_size, default_device) -> PVSampler:
"""Returns a sampler of possibly correlated Uniform PV players for a
symmetric group of players (e.g. the locals or globals)"""
# setup local sampler
if correlation > 0.0:
if correlation_method == 'Bernoulli':
sampler_class = BernoulliWeightsCorrelatedSymmetricUniformPVSampler
elif correlation_method == 'constant':
sampler_class = ConstantWeightCorrelatedSymmetricUniformPVSampler
else:
raise ValueError(ERR_MSG_INVALID_LOCAL_GLOBAL_CORRELATION_METHOD)
sampler = sampler_class(
n_players=n_group_players, valuation_size = valuation_size,
correlation = correlation, u_lo = 0.0, u_hi = 1.0,
default_batch_size=default_batch_size, default_device=default_device)
else:
# no correlation between locals
if correlation_method is not None:
warnings.warn(WRN_MSG_CORRELATED_BUT_CORR_IS_ZERO)
sampler = UniformSymmetricIPVSampler(
u_lo, u_hi, n_group_players, valuation_size, default_batch_size, default_device)
return sampler
[docs]class LLGSampler(LocalGlobalCompositePVSampler):
"""A sampler for the LLG settings in Ausubel & Baranov.
Args:
correlation (float), correlation coefficient between local bidders,
takes values in [0.0, 1.0]
correlation_method (str or None, default: None): The type of correlation
model. For correlation > 0.0, must be one of 'Bernoulli' or 'constant'
"""
def __init__(self, correlation = 0.0, correlation_method = None,
default_batch_size = 1, default_device= None):
super().__init__(n_locals =2, n_globals = 1, valuation_size = 1,
correlation_locals=correlation, correlation_method_locals=correlation_method,
correlation_globals=0.0, correlation_method_globals=None,
default_batch_size=default_batch_size, default_device=default_device)
[docs]class LLGFullSampler(LLGSampler):
"""A sampler for the LLG full setting."""
def _generate_grid(self, player_position: int, minimum_number_of_points: int,
reduced: bool, dtype=torch.float, device=None,
support_bounds: torch.Tensor=None) -> torch.Tensor:
"""Here, the grid could be three dimensional, as bidders can bid on all
three items, even though they're only interested in one.
"""
device = device or self.default_device
if support_bounds is None:
bounds = self.support_bounds[player_position]
else:
bounds = support_bounds[player_position]
# only a 1D grid for this single-minded bidder
if reduced:
grid = torch.linspace(
bounds[0][0], bounds[0][1], minimum_number_of_points,
device=device, dtype=dtype
).view(-1, 1)
else:
dims = 3
# create grid for actual 3D single-minded prior (e.g. all but one dim
# are zero)
if support_bounds is None:
grid = torch.zeros((minimum_number_of_points, dims),
device=device, dtype=dtype)
grid[:, player_position] = torch.linspace(
bounds[0][0], bounds[0][1], minimum_number_of_points,
device=device, dtype=dtype
)
# sample 3D but on other bounds
else:
# use equal density in each dimension of the valuation, such that
# the total number of points is at least as high as the specified one
n_points_per_dim = ceil(minimum_number_of_points**(1/dims))
# create equidistant lines along the support in each dimension
lines = [torch.linspace(bounds[0][0], bounds[0][1], n_points_per_dim,
device=device, dtype=dtype)
for i in range(dims)]
grid = torch.stack(torch.meshgrid(lines), dim=-1).view(-1, dims)
return grid
[docs] def generate_valuation_grid(self, player_position: int, minimum_number_of_points: int,
dtype=torch.float, device=None, support_bounds=None) -> torch.Tensor:
"""Here, the grid can be one dimensional, as bidders are single-minded.
Also has mesh funtionallity for creation of grid cells."""
return self._generate_grid(player_position, minimum_number_of_points, True,
dtype, device, support_bounds)
[docs] def generate_reduced_grid(self, player_position: int, minimum_number_of_points: int,
dtype=torch.float, device=None, support_bounds=None) -> torch.Tensor:
"""Here, the grid can be one dimensional, as bidders are single-minded."""
return self._generate_grid(player_position, minimum_number_of_points, True,
dtype, device, support_bounds)
[docs] def generate_action_grid(self, player_position: int, minimum_number_of_points: int,
dtype=torch.float, device=None, support_bounds=None) -> torch.Tensor:
"""Here, the grid needs to be three dimensional and the support bounds
need to be wider.
"""
support_bounds = self.support_bounds.clone()
# Grid bids should always start at zero if not specified otherwise
support_bounds[:, :, 0] = 0
return self._generate_grid(player_position, minimum_number_of_points, False,
dtype, device, support_bounds)
[docs]class LLLLGGSampler(LocalGlobalCompositePVSampler):
"""A sampler for the LLLLGG settings in Bosshard et al (2020).
Note: while the auction is for 6 players and 8 items, our auction implementation uses symmetries and
encodes each player's valuations with a valuation_size of 2.
Args:
correlation_locals (float), correlation coefficient between local bidders,
takes values in [0.0, 1.0]
correlation_method_locals (str or None, default: None): The type of correlation
model. For correlation > 0.0, must be one of 'Bernoulli' or 'constant'
"""
def __init__(self, correlation_locals = 0.0, correlation_method_locals = None,
default_batch_size = 1, default_device= None):
super().__init__(n_locals =4, n_globals = 2, valuation_size = 2,
correlation_locals=correlation_locals, correlation_method_locals=correlation_method_locals,
correlation_globals=0.0, correlation_method_globals=None,
default_batch_size=default_batch_size, default_device=default_device)
[docs]class LLLLRRGSampler(CompositeValuationObservationSampler):
"""Setting with three groups of players:
The local players (L's) have
symmetric (possibly correlated) uniform valuations on [0,1].
The regional (R) bidders have symmetric (possibly correlated) uniform
valuations on [0,2].
The global (G) bidder has a valuation
"""
def __init__(self,
correlation_locals = 0.0, correlation_method_locals = None,
correlation_regionals = 0.0, correlation_method_regionals = None,
default_batch_size = 1 , default_device = None):
assert 0 <=correlation_locals <= 1, "invalid locals correlation"
assert 0 <=correlation_regionals <= 1, "invalid regionals correlation"
valuation_size = 2
u_lo = 0.0
u_hi_locals = 1.0
u_hi_regionals = 2.0
u_hi_global = 4.0
n_locals = 4
n_regionals = 2
n_global = 1
correlation_global = 0.0
correlation_method_global = None
sampler_locals = self._get_group_sampler(
n_locals, correlation_locals, correlation_method_locals,
u_lo, u_hi_locals,
valuation_size, default_batch_size, default_device)
sampler_regionals = self._get_group_sampler(
n_regionals, correlation_regionals, correlation_method_regionals,
u_lo, u_hi_regionals,
valuation_size, default_batch_size, default_device)
# global player sampler: 1st obs/val dim is Univform IPV, second dim is always 0.0
sampler_global = FlushedWrappedSampler(
UniformSymmetricIPVSampler(u_lo, u_hi_global, 1, 2, default_batch_size, default_device),
flush_val_dims=1, flush_obs_dims=1)
n_players = n_locals + n_regionals + n_global
observation_size = valuation_size # this is a PV setting, valuations = observations
subgroup_samplers = [sampler_locals, sampler_regionals, sampler_global]
super().__init__(n_players, valuation_size, observation_size, subgroup_samplers, default_batch_size, default_device)
def _get_group_sampler(self, n_group_players, correlation, correlation_method,
u_lo, u_hi,
valuation_size, default_batch_size, default_device) -> PVSampler:
"""Returns a sampler of possibly correlated Uniform PV players for a
symmetric group of players (e.g. the locals or globals)"""
if correlation > 0.0:
if correlation_method == 'Bernoulli':
sampler_class = BernoulliWeightsCorrelatedSymmetricUniformPVSampler
elif correlation_method == 'constant':
sampler_class = ConstantWeightCorrelatedSymmetricUniformPVSampler
else:
raise ValueError(ERR_MSG_INVALID_LOCAL_GLOBAL_CORRELATION_METHOD)
sampler = sampler_class(
n_players=n_group_players, valuation_size = valuation_size,
correlation = correlation, u_lo = 0.0, u_hi = 1.0,
default_batch_size=default_batch_size, default_device=default_device)
else:
# no correlation players in group
if correlation_method is not None:
warnings.warn(WRN_MSG_CORRELATED_BUT_CORR_IS_ZERO)
sampler = UniformSymmetricIPVSampler(
u_lo, u_hi, n_group_players, valuation_size, default_batch_size, default_device)
return sampler