diff --git a/aepsych/benchmark/example_problems.py b/aepsych/benchmark/example_problems.py index 5e561a5dd..76df6a77d 100644 --- a/aepsych/benchmark/example_problems.py +++ b/aepsych/benchmark/example_problems.py @@ -105,8 +105,10 @@ def __init__( ) y = torch.LongTensor(self.data[:, 0]) x = torch.Tensor(self.data[:, 1:]) - inducing_size=100 - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=self.bounds)) + inducing_size = 100 + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=self.bounds) + ) # Fit a model, with a large number of inducing points self.m = GPClassificationModel( diff --git a/aepsych/generators/epsilon_greedy_generator.py b/aepsych/generators/epsilon_greedy_generator.py index b973d3ec1..d1560a2f2 100644 --- a/aepsych/generators/epsilon_greedy_generator.py +++ b/aepsych/generators/epsilon_greedy_generator.py @@ -15,7 +15,13 @@ class EpsilonGreedyGenerator(AEPsychGenerator): - def __init__(self, lb:torch.Tensor, ub:torch.Tensor,subgenerator: AEPsychGenerator, epsilon: float = 0.1) -> None: + def __init__( + self, + lb: torch.Tensor, + ub: torch.Tensor, + subgenerator: AEPsychGenerator, + epsilon: float = 0.1, + ) -> None: self.subgenerator = subgenerator self.epsilon = epsilon self.lb = lb @@ -31,7 +37,7 @@ def from_config(cls, config: Config) -> "EpsilonGreedyGenerator": ) subgen = subgen_cls.from_config(config) epsilon = config.getfloat(classname, "epsilon", fallback=0.1) - return cls(lb=lb, ub=ub,subgenerator=subgen, epsilon=epsilon) + return cls(lb=lb, ub=ub, subgenerator=subgen, epsilon=epsilon) def gen(self, num_points: int, model: ModelProtocol) -> torch.Tensor: if num_points > 1: diff --git a/aepsych/generators/monotonic_rejection_generator.py b/aepsych/generators/monotonic_rejection_generator.py index 0775150f4..88079f03b 100644 --- a/aepsych/generators/monotonic_rejection_generator.py +++ b/aepsych/generators/monotonic_rejection_generator.py @@ -180,7 +180,6 @@ def from_config(cls, config: Config) -> "MonotonicRejectionGenerator": extra_acqf_args = cls._get_acqf_options(acqf, config) lb = torch.tensor(config.getlist(classname, "lb")) ub = torch.tensor(config.getlist(classname, "ub")) - options = {} options["num_restarts"] = config.getint(classname, "restarts", fallback=10) diff --git a/aepsych/generators/optimize_acqf_generator.py b/aepsych/generators/optimize_acqf_generator.py index 60d893613..486c0c1ee 100644 --- a/aepsych/generators/optimize_acqf_generator.py +++ b/aepsych/generators/optimize_acqf_generator.py @@ -72,14 +72,22 @@ def __init__( self.lb = lb self.ub = ub - def _instantiate_acquisition_fn(self, model: ModelProtocol) -> AcquisitionFunction: - if "lb" in inspect.signature(self.acqf).parameters and "ub" in inspect.signature(self.acqf).parameters: + if ( + "lb" in inspect.signature(self.acqf).parameters + and "ub" in inspect.signature(self.acqf).parameters + ): if self.acqf == AnalyticExpectedUtilityOfBestOption: return self.acqf(pref_model=model, lb=self.lb, ub=self.ub) if self.acqf in self.baseline_requiring_acqfs: - return self.acqf(model, model.train_inputs[0], lb=self.lb, ub=self.ub, **self.acqf_kwargs) + return self.acqf( + model, + model.train_inputs[0], + lb=self.lb, + ub=self.ub, + **self.acqf_kwargs, + ) return self.acqf(model=model, lb=self.lb, ub=self.ub, **self.acqf_kwargs) diff --git a/aepsych/kernels/pairwisekernel.py b/aepsych/kernels/pairwisekernel.py index c30bbf512..f85a6bc3d 100644 --- a/aepsych/kernels/pairwisekernel.py +++ b/aepsych/kernels/pairwisekernel.py @@ -16,12 +16,12 @@ class PairwiseKernel(Kernel): """ def __init__( - self, latent_kernel: Kernel, is_partial_obs: bool=False, **kwargs + self, latent_kernel: Kernel, is_partial_obs: bool = False, **kwargs ) -> None: """ - Args: - latent_kernel (Kernel): The underlying kernel used to compute the covariance for the GP. - is_partial_obs (bool): If the kernel should handle partial observations. Defaults to False. + Args: + latent_kernel (Kernel): The underlying kernel used to compute the covariance for the GP. + is_partial_obs (bool): If the kernel should handle partial observations. Defaults to False. """ super(PairwiseKernel, self).__init__(**kwargs) @@ -40,11 +40,11 @@ def forward( x1 (torch.Tensor): A `b x n x d` or `n x d` tensor, where `d = 2k` and `k` is the dimension of the latent space. x2 (torch.Tensor): A `b x m x d` or `m x d` tensor, where `d = 2k` and `k` is the dimension of the latent space. diag (bool): Should the Kernel compute the whole covariance matrix or just the diagonal? Defaults to False. - + Returns: torch.Tensor (or :class:`gpytorch.lazy.LazyTensor`) : A `b x n x m` or `n x m` tensor representing - the covariance matrix between `x1` and `x2`. + the covariance matrix between `x1` and `x2`. The exact size depends on the kernel's evaluation mode: * `full_covar`: `n x m` or `b x n x m` * `diag`: `n` or `b x n` diff --git a/aepsych/kernels/rbf_partial_grad.py b/aepsych/kernels/rbf_partial_grad.py index 09a204130..43c574d75 100644 --- a/aepsych/kernels/rbf_partial_grad.py +++ b/aepsych/kernels/rbf_partial_grad.py @@ -31,14 +31,14 @@ def forward( self, x1: torch.Tensor, x2: torch.Tensor, diag: bool = False, **params: Any ) -> torch.Tensor: """Computes the covariance matrix between x1 and x2 based on the RBF - + Args: x1 (torch.Tensor): A `b x n x d` or `n x d` tensor, where `d = 2k` and `k` is the dimension of the latent space. x2 (torch.Tensor): A `b x m x d` or `m x d` tensor, where `d = 2k` and `k` is the dimension of the latent space. diag (bool): Should the Kernel compute the whole covariance matrix (False) or just the diagonal (True)? Defaults to False. - - + + Returns: torch.Tensor: A `b x n x m` or `n x m` tensor representing the covariance matrix between `x1` and `x2`. The exact size depends on the kernel's evaluation mode: diff --git a/aepsych/models/gp_classification.py b/aepsych/models/gp_classification.py index 5a7768a70..9fe202652 100644 --- a/aepsych/models/gp_classification.py +++ b/aepsych/models/gp_classification.py @@ -97,10 +97,14 @@ def __init__( "Both inducing_points and SobolAllocator are provided. " "The initial inducing_points will be overwritten by the allocator." ) - self.inducing_points = inducing_point_method.allocate_inducing_points(num_inducing=self.inducing_size) + self.inducing_points = inducing_point_method.allocate_inducing_points( + num_inducing=self.inducing_size + ) elif inducing_points is None and inducing_point_method is SobolAllocator: - self.inducing_points = inducing_point_method.allocate_inducing_points(num_inducing=self.inducing_size) + self.inducing_points = inducing_point_method.allocate_inducing_points( + num_inducing=self.inducing_size + ) elif inducing_points is None and dim is not None: # No allocator or unsupported allocator: create a dummy tensor @@ -119,14 +123,12 @@ def __init__( # Always assign the inducing point method self.inducing_point_method = inducing_point_method or AutoAllocator() - self.dim = dim or self.inducing_points.size(-1) - + if mean_module is None or covar_module is None: default_mean, default_covar = default_mean_covar_factory( dim=self.dim, stimuli_per_trial=self.stimuli_per_trial ) - variational_distribution = CholeskyVariationalDistribution( self.inducing_points.size(0), batch_shape=torch.Size([self._batch_size]) @@ -140,8 +142,6 @@ def __init__( ) super().__init__(variational_strategy) - - self.likelihood = likelihood self.mean_module = mean_module or default_mean self.covar_module = covar_module or default_covar @@ -225,7 +225,7 @@ def _reset_variational_strategy(self) -> None: allocator=self.inducing_point_method, inducing_size=self.inducing_size, covar_module=self.covar_module, - X=self.train_inputs[0] + X=self.train_inputs[0], ).to(device) variational_distribution = CholeskyVariationalDistribution( diff --git a/aepsych/models/gp_regression.py b/aepsych/models/gp_regression.py index 8b2d736a8..c7b86fb7e 100644 --- a/aepsych/models/gp_regression.py +++ b/aepsych/models/gp_regression.py @@ -6,9 +6,10 @@ # LICENSE file in the root directory of this source tree. from __future__ import annotations +import warnings + from copy import deepcopy from typing import Dict, Optional, Tuple, Union -import warnings import gpytorch import numpy as np @@ -16,12 +17,12 @@ from aepsych.config import Config from aepsych.factory.default import default_mean_covar_factory from aepsych.models.base import AEPsychModelDeviceMixin +from aepsych.models.inducing_point_allocators import AutoAllocator, SobolAllocator from aepsych.utils import _process_bounds, promote_0d from aepsych.utils_logging import getLogger +from botorch.models.utils.inducing_point_allocators import InducingPointAllocator from gpytorch.likelihoods import GaussianLikelihood, Likelihood from gpytorch.models import ExactGP -from botorch.models.utils.inducing_point_allocators import InducingPointAllocator -from aepsych.models.inducing_point_allocators import AutoAllocator, SobolAllocator logger = getLogger() @@ -59,26 +60,30 @@ def __init__( max_fit_time (float, optional): The maximum amount of time, in seconds, to spend fitting the model. If None, there is no limit to the fitting time. inducing_point_method (InducingPointAllocator, optional): The method to use for allocating inducing points. - If None, defaults to AutoAllocator. + If None, defaults to AutoAllocator. inducing_size (int, optional): The number of inducing points to use. If None, defaults to 99. - + """ if likelihood is None: likelihood = GaussianLikelihood() self.inducing_size = inducing_size or 99 super().__init__(None, None, likelihood) - + self.inducing_point_method: Optional[InducingPointAllocator] if inducing_points is not None and inducing_point_method is SobolAllocator: warnings.warn( "Both inducing_points and SobolAllocator are provided. " "The initial inducing_points will be overwritten by the allocator." ) - self.inducing_points = inducing_point_method.allocate_inducing_points(num_inducing=self.inducing_size) + self.inducing_points = inducing_point_method.allocate_inducing_points( + num_inducing=self.inducing_size + ) elif inducing_points is None and inducing_point_method is SobolAllocator: - self.inducing_points = inducing_point_method.allocate_inducing_points(num_inducing=self.inducing_size) + self.inducing_points = inducing_point_method.allocate_inducing_points( + num_inducing=self.inducing_size + ) elif inducing_points is None and dim is not None: # No allocator or unsupported allocator: create a dummy tensor @@ -97,7 +102,6 @@ def __init__( # Always assign the inducing point method self.inducing_point_method = inducing_point_method or AutoAllocator() - self.dim = dim or self.inducing_points.size(-1) self.max_fit_time = max_fit_time diff --git a/aepsych/models/monotonic_rejection_gp.py b/aepsych/models/monotonic_rejection_gp.py index bb4fdcf3f..78c2333b4 100644 --- a/aepsych/models/monotonic_rejection_gp.py +++ b/aepsych/models/monotonic_rejection_gp.py @@ -92,18 +92,20 @@ def __init__( self.inducing_size = num_induc - - self.inducing_point_method: Optional[InducingPointAllocator] if inducing_points is not None and inducing_point_method is SobolAllocator: warnings.warn( "Both inducing_points and SobolAllocator are provided. " "The initial inducing_points will be overwritten by the allocator." ) - self.inducing_points = inducing_point_method.allocate_inducing_points(num_inducing=self.inducing_size) + self.inducing_points = inducing_point_method.allocate_inducing_points( + num_inducing=self.inducing_size + ) elif inducing_points is None and inducing_point_method is SobolAllocator: - self.inducing_points = inducing_point_method.allocate_inducing_points(num_inducing=self.inducing_size) + self.inducing_points = inducing_point_method.allocate_inducing_points( + num_inducing=self.inducing_size + ) elif inducing_points is None and dim is not None: # No allocator or unsupported allocator: create a dummy tensor @@ -122,7 +124,6 @@ def __init__( # Always assign the inducing point method self.inducing_point_method = inducing_point_method or AutoAllocator() - self.dim = dim or self.inducing_points.size(-1) inducing_points_aug = self._augment_with_deriv_index(self.inducing_points, 0) @@ -187,7 +188,7 @@ def fit(self, train_x: Tensor, train_y: Tensor, **kwargs) -> None: allocator=self.inducing_point_method, inducing_size=self.inducing_size, covar_module=self.covar_module, - X=self.train_inputs[0] + X=self.train_inputs[0], ) self._set_model(train_x, train_y) diff --git a/aepsych/models/semi_p.py b/aepsych/models/semi_p.py index 4c32f6cbe..d914be5e5 100644 --- a/aepsych/models/semi_p.py +++ b/aepsych/models/semi_p.py @@ -7,9 +7,10 @@ from __future__ import annotations +import warnings + from copy import deepcopy from typing import Any, Optional, Tuple, Union -import warnings import gpytorch import numpy as np @@ -214,17 +215,21 @@ def __init__( If "kmeans++", selects points by performing kmeans++ clustering on the training data. If "auto", tries to determine the best method automatically. """ - + self.inducing_point_method: Optional[InducingPointAllocator] if inducing_points is not None and inducing_point_method is SobolAllocator: warnings.warn( "Both inducing_points and SobolAllocator are provided. " "The initial inducing_points will be overwritten by the allocator." ) - self.inducing_points = inducing_point_method.allocate_inducing_points(num_inducing=self.inducing_size) + self.inducing_points = inducing_point_method.allocate_inducing_points( + num_inducing=self.inducing_size + ) elif inducing_points is None and inducing_point_method is SobolAllocator: - self.inducing_points = inducing_point_method.allocate_inducing_points(num_inducing=self.inducing_size) + self.inducing_points = inducing_point_method.allocate_inducing_points( + num_inducing=self.inducing_size + ) elif inducing_points is None and dim is not None: # No allocator or unsupported allocator: create a dummy tensor @@ -243,12 +248,11 @@ def __init__( # Always assign the inducing point method self.inducing_point_method = inducing_point_method or AutoAllocator() - self.dim = dim or self.inducing_points.size(-1) dim = self.dim self.stim_dim = stim_dim - self.context_dims = list(range(self.dim )) + self.context_dims = list(range(self.dim)) self.context_dims.pop(stim_dim) if mean_module is None: @@ -261,7 +265,7 @@ def __init__( if covar_module is None: covar_module = ScaleKernel( RBFKernel( - ard_num_dims=self.dim - 1, + ard_num_dims=self.dim - 1, lengthscale_prior=GammaPrior(3, 6), active_dims=self.context_dims, # Operate only on x_s batch_shape=torch.Size([2]), @@ -474,7 +478,6 @@ def __init__( inducing_size: Optional[int] = None, max_fit_time: Optional[float] = None, inducing_point_method: Optional[InducingPointAllocator] = None, - ) -> None: """ Initialize HadamardSemiPModel. @@ -507,17 +510,21 @@ def __init__( "at >=100 inducing points is especially slow." ) ) - + self.inducing_point_method: Optional[InducingPointAllocator] if inducing_points is not None and inducing_point_method is SobolAllocator: warnings.warn( "Both inducing_points and SobolAllocator are provided. " "The initial inducing_points will be overwritten by the allocator." ) - self.inducing_points = inducing_point_method.allocate_inducing_points(num_inducing=self.inducing_size) + self.inducing_points = inducing_point_method.allocate_inducing_points( + num_inducing=self.inducing_size + ) elif inducing_points is None and inducing_point_method is SobolAllocator: - self.inducing_points = inducing_point_method.allocate_inducing_points(num_inducing=self.inducing_size) + self.inducing_points = inducing_point_method.allocate_inducing_points( + num_inducing=self.inducing_size + ) elif inducing_points is None and dim is not None: # No allocator or unsupported allocator: create a dummy tensor @@ -536,9 +543,8 @@ def __init__( # Always assign the inducing point method self.inducing_point_method = inducing_point_method or AutoAllocator() - self.dim = dim or self.inducing_points.size(-1) - + super().__init__( inducing_points=inducing_points, dim=dim, diff --git a/aepsych/strategy.py b/aepsych/strategy.py index 8207677e2..7e9f7f138 100644 --- a/aepsych/strategy.py +++ b/aepsych/strategy.py @@ -34,6 +34,7 @@ from aepsych.config import Config from aepsych.generators.base import AEPsychGenerator from aepsych.models.base import AEPsychMixin, ModelProtocol +from aepsych.models.utils import get_extremum, inv_query from aepsych.transforms import ( ParameterTransformedGenerator, ParameterTransformedModel, @@ -43,7 +44,6 @@ from aepsych.utils_logging import getLogger from botorch.exceptions.errors import ModelFittingError from botorch.models.transforms.input import ChainedInputTransform -from aepsych.models.utils import get_extremum, inv_query logger = getLogger() @@ -52,7 +52,11 @@ def ensure_model_is_fresh(f: Callable) -> Callable: def wrapper(self, *args, **kwargs): if self.can_fit and not self._model_is_fresh: starttime = time.time() - if self._is_first_fit or self._count % self.refit_every == 0 or self.refit_every == 1: + if ( + self._is_first_fit + or self._count % self.refit_every == 0 + or self.refit_every == 1 + ): logger.info("Starting fitting (no warm start)...") # don't warm start self.fit() @@ -337,7 +341,10 @@ def get_max( ), "model is None! Cannot get the max without a model!" self.model.to(self.model_device) return self.get_max_( - self.model, constraints, probability_space=probability_space, max_time=max_time + self.model, + constraints, + probability_space=probability_space, + max_time=max_time, ) def get_max_( @@ -382,9 +389,12 @@ def get_min( ), "model is None! Cannot get the min without a model!" self.model.to(self.model_device) return self.get_min_( - self.model, constraints, probability_space=probability_space, max_time=max_time + self.model, + constraints, + probability_space=probability_space, + max_time=max_time, ) - + def get_min_( self, model: ModelProtocol, @@ -414,8 +424,6 @@ def get_min_( val, _ = model.predict(arg) return float(val.item()), arg - - @ensure_model_is_fresh def inv_query( self, @@ -432,7 +440,7 @@ def inv_query( return self.inv_query_( self.model, y, constraints, probability_space, max_time=max_time ) - + def inv_query_( self, model: ModelProtocol, @@ -473,7 +481,6 @@ def inv_query_( val, _ = model.predict(arg.reshape(1, self.dim)) return float(val.item()), arg - @ensure_model_is_fresh def predict(self, x: torch.Tensor, probability_space: bool = False) -> torch.Tensor: assert self.model is not None, "model is None! Cannot predict without a model!" @@ -489,7 +496,7 @@ def get_jnd( ), "model is None! Cannot get the get jnd without a model!" self.model.to(self.model_device) return self.get_jnd_(self.model, *args, **kwargs) - + def get_jnd_( self, model: ModelProtocol, @@ -584,7 +591,6 @@ def get_jnd_( median = torch.clip(torch.quantile(jnds, 0.5, axis=0), 0, np.inf) # type: ignore return median, lower, upper - @ensure_model_is_fresh def sample( self, x: torch.Tensor, num_samples: Optional[int] = None diff --git a/tests/acquisition/test_mi.py b/tests/acquisition/test_mi.py index 602385960..21c944cfe 100644 --- a/tests/acquisition/test_mi.py +++ b/tests/acquisition/test_mi.py @@ -21,12 +21,12 @@ SobolGenerator, ) from aepsych.models import GPClassificationModel, MonotonicRejectionGP +from aepsych.models.inducing_point_allocators import SobolAllocator +from aepsych.models.utils import select_inducing_points from aepsych.strategy import SequentialStrategy, Strategy from gpytorch.kernels import LinearKernel from gpytorch.means import ConstantMean from scipy.stats import bernoulli, multivariate_normal, norm, pearsonr -from aepsych.models.inducing_point_allocators import SobolAllocator -from aepsych.models.utils import select_inducing_points class SingleProbitMI(unittest.TestCase): @@ -40,7 +40,9 @@ def test_1d_monotonic_single_probit(self): ub = torch.tensor([4.0]) inducing_size = 10 bounds = torch.stack([lb, ub]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) acqf = MonotonicBernoulliMCMutualInformation acqf_kwargs = {"objective": ProbitObjective()} @@ -57,8 +59,12 @@ def test_1d_monotonic_single_probit(self): lb=lb, ub=ub, min_asks=n_opt, - model=MonotonicRejectionGP(inducing_points=inducing_points, dim=1, monotonic_idxs=[0]), - generator=MonotonicRejectionGenerator(acqf=acqf, acqf_kwargs=acqf_kwargs, lb=lb, ub=ub), + model=MonotonicRejectionGP( + inducing_points=inducing_points, dim=1, monotonic_idxs=[0] + ), + generator=MonotonicRejectionGenerator( + acqf=acqf, acqf_kwargs=acqf_kwargs, lb=lb, ub=ub + ), stimuli_per_trial=1, outcome_types=["binary"], ), @@ -91,8 +97,9 @@ def test_1d_single_probit(self): ub = torch.tensor([4.0]) inducing_size = 10 bounds = torch.stack([lb, ub]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) - + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) acqf = BernoulliMCMutualInformation extra_acqf_args = {"objective": ProbitObjective()} @@ -109,8 +116,12 @@ def test_1d_single_probit(self): Strategy( lb=lb, ub=ub, - model=GPClassificationModel(inducing_points=inducing_points, dim=1, inducing_size=10), - generator=OptimizeAcqfGenerator(acqf=acqf, lb=lb, ub=ub, acqf_kwargs=extra_acqf_args), + model=GPClassificationModel( + inducing_points=inducing_points, dim=1, inducing_size=10 + ), + generator=OptimizeAcqfGenerator( + acqf=acqf, lb=lb, ub=ub, acqf_kwargs=extra_acqf_args + ), min_asks=n_opt, stimuli_per_trial=1, outcome_types=["binary"], @@ -141,7 +152,9 @@ def test_mi_acqf(self): ub = torch.tensor([1.0]) inducing_size = 10 bounds = torch.stack([lb, ub]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) model = GPClassificationModel( inducing_points=inducing_points, diff --git a/tests/generators/test_epsilon_greedy_generator.py b/tests/generators/test_epsilon_greedy_generator.py index 16aaa2f5e..4dfb31404 100644 --- a/tests/generators/test_epsilon_greedy_generator.py +++ b/tests/generators/test_epsilon_greedy_generator.py @@ -31,8 +31,8 @@ def test_epsilon_greedy(self): acqf=MonotonicMCLSE, acqf_kwargs=extra_acqf_args, lb=lb, ub=ub ), epsilon=epsilon, - lb=lb, - ub=ub + lb=lb, + ub=ub, ) model = MagicMock() gen.subgenerator.gen = MagicMock() diff --git a/tests/generators/test_optimize_acqf_generator.py b/tests/generators/test_optimize_acqf_generator.py index e4d423401..cb697ca27 100644 --- a/tests/generators/test_optimize_acqf_generator.py +++ b/tests/generators/test_optimize_acqf_generator.py @@ -14,10 +14,11 @@ from aepsych.config import Config from aepsych.generators import OptimizeAcqfGenerator from aepsych.models import GPClassificationModel, PairwiseProbitModel -from botorch.acquisition.preference import AnalyticExpectedUtilityOfBestOption -from sklearn.datasets import make_classification from aepsych.models.inducing_point_allocators import SobolAllocator from aepsych.models.utils import select_inducing_points +from botorch.acquisition.preference import AnalyticExpectedUtilityOfBestOption +from sklearn.datasets import make_classification + class TestOptimizeAcqfGenerator(unittest.TestCase): def test_time_limits(self): @@ -38,7 +39,9 @@ def test_time_limits(self): ub = 3 * torch.ones(8) inducing_size = 10 bounds = torch.stack([lb, ub]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) model = GPClassificationModel( inducing_points=inducing_points, @@ -48,7 +51,10 @@ def test_time_limits(self): model.fit(X, y) generator = OptimizeAcqfGenerator( - acqf=MCLevelSetEstimation, acqf_kwargs={"beta": 1.96, "target": 0.5}, lb=lb, ub=ub, + acqf=MCLevelSetEstimation, + acqf_kwargs={"beta": 1.96, "target": 0.5}, + lb=lb, + ub=ub, ) start = time.time() diff --git a/tests/models/test_gp_classification.py b/tests/models/test_gp_classification.py index 3e883704e..1f5afb96e 100644 --- a/tests/models/test_gp_classification.py +++ b/tests/models/test_gp_classification.py @@ -123,7 +123,9 @@ def test_1d_classification(self): X, y = self.X, self.y inducing_size = 10 bounds = torch.stack([torch.tensor([-3.0]), torch.tensor([3.0])]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) model = GPClassificationModel( inducing_points=inducing_points, @@ -162,7 +164,9 @@ def test_1d_classification_pytorchopt(self): X, y = self.X, self.y inducing_size = 10 bounds = torch.stack([torch.tensor([-3.0]), torch.tensor([3.0])]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) model = GPClassificationModel( inducing_points=inducing_points, inducing_size=inducing_size @@ -226,8 +230,9 @@ def test_1d_classification_different_scales(self): ub = torch.tensor([3000.0, 0.003]) inducing_size = 20 bounds = torch.stack([lb, ub]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) - + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) # model = GPClassificationModel(inducing_points=inducing_points, inducing_size=inducing_size) @@ -268,9 +273,13 @@ def test_1d_classification_different_scales(self): def test_reset_hyperparams(self): inducing_size = 20 bounds = torch.stack([torch.tensor([-3.0]), torch.tensor([3.0])]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) - model = GPClassificationModel(inducing_points=inducing_points, inducing_size=inducing_size) + model = GPClassificationModel( + inducing_points=inducing_points, inducing_size=inducing_size + ) ls_before = model.covar_module.lengthscale.clone().detach().numpy() model.fit(torch.Tensor(self.X), torch.Tensor(self.y)) @@ -290,9 +299,13 @@ def test_reset_hyperparams(self): def test_reset_variational_strategy(self): inducing_size = 20 bounds = torch.stack([torch.tensor([-3.0]), torch.tensor([3.0])]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) - model = GPClassificationModel(inducing_points=inducing_points, inducing_size=inducing_size) + model = GPClassificationModel( + inducing_points=inducing_points, inducing_size=inducing_size + ) variational_params_before = [ v.clone().detach().numpy() for v in model.variational_parameters() @@ -330,7 +343,9 @@ def test_predict_p(self): X, y = self.X, self.y inducing_size = 10 bounds = torch.stack([torch.tensor([-3.0]), torch.tensor([3.0])]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) model = GPClassificationModel( inducing_points=inducing_points, inducing_size=inducing_size @@ -359,7 +374,9 @@ def test_1d_single_probit_new_interface(self): ub = torch.tensor([4.0]) inducing_size = 10 bounds = torch.stack([lb, ub]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) model_list = [ Strategy( @@ -373,9 +390,11 @@ def test_1d_single_probit_new_interface(self): Strategy( lb=lb, ub=ub, - model=GPClassificationModel(inducing_points=inducing_points, inducing_size=inducing_size), + model=GPClassificationModel( + inducing_points=inducing_points, inducing_size=inducing_size + ), generator=OptimizeAcqfGenerator( - acqf = qUpperConfidenceBound, acqf_kwargs={"beta": 1.96}, lb=lb, ub=ub + acqf=qUpperConfidenceBound, acqf_kwargs={"beta": 1.96}, lb=lb, ub=ub ), min_asks=n_opt, stimuli_per_trial=1, @@ -408,7 +427,9 @@ def test_1d_single_probit_batched(self): ub = torch.tensor([4.0]) inducing_size = 10 bounds = torch.stack([lb, ub]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) model_list = [ Strategy( @@ -422,9 +443,11 @@ def test_1d_single_probit_batched(self): Strategy( lb=lb, ub=ub, - model=GPClassificationModel(inducing_points=inducing_points, inducing_size=inducing_size), + model=GPClassificationModel( + inducing_points=inducing_points, inducing_size=inducing_size + ), generator=OptimizeAcqfGenerator( - acqf = qUpperConfidenceBound, acqf_kwargs={"beta": 1.96}, lb=lb, ub=ub + acqf=qUpperConfidenceBound, acqf_kwargs={"beta": 1.96}, lb=lb, ub=ub ), min_asks=n_opt, stimuli_per_trial=1, @@ -456,8 +479,9 @@ def test_1d_single_probit(self): ub = torch.tensor([4.0]) inducing_size = 10 bounds = torch.stack([lb, ub]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) - + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) model_list = [ Strategy( @@ -471,9 +495,11 @@ def test_1d_single_probit(self): Strategy( lb=lb, ub=ub, - model=GPClassificationModel(inducing_points=inducing_points, inducing_size=inducing_size), + model=GPClassificationModel( + inducing_points=inducing_points, inducing_size=inducing_size + ), generator=OptimizeAcqfGenerator( - acqf = qUpperConfidenceBound, acqf_kwargs={"beta": 1.96}, lb=lb, ub=ub + acqf=qUpperConfidenceBound, acqf_kwargs={"beta": 1.96}, lb=lb, ub=ub ), min_asks=n_opt, stimuli_per_trial=1, @@ -504,7 +530,9 @@ def test_1d_single_probit_pure_exploration(self): ub = torch.tensor([4.0]) inducing_size = 10 bounds = torch.stack([lb, ub]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) strat_list = [ Strategy( @@ -518,9 +546,11 @@ def test_1d_single_probit_pure_exploration(self): Strategy( lb=lb, ub=ub, - model=GPClassificationModel(inducing_points=inducing_points, inducing_size=inducing_size), + model=GPClassificationModel( + inducing_points=inducing_points, inducing_size=inducing_size + ), generator=OptimizeAcqfGenerator( - acqf = qUpperConfidenceBound, acqf_kwargs={"beta": 1.96}, lb=lb, ub=ub + acqf=qUpperConfidenceBound, acqf_kwargs={"beta": 1.96}, lb=lb, ub=ub ), min_asks=n_opt, stimuli_per_trial=1, @@ -556,7 +586,9 @@ def test_2d_single_probit_pure_exploration(self): ub = torch.tensor([1.0, 1.0]) inducing_size = 10 bounds = torch.stack([lb, ub]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) strat_list = [ Strategy( @@ -570,9 +602,11 @@ def test_2d_single_probit_pure_exploration(self): Strategy( lb=lb, ub=ub, - model=GPClassificationModel(inducing_points=inducing_points, inducing_size=inducing_size), + model=GPClassificationModel( + inducing_points=inducing_points, inducing_size=inducing_size + ), generator=OptimizeAcqfGenerator( - acqf = qUpperConfidenceBound, acqf_kwargs={"beta": 1.96}, lb=lb, ub=ub + acqf=qUpperConfidenceBound, acqf_kwargs={"beta": 1.96}, lb=lb, ub=ub ), min_asks=n_opt, stimuli_per_trial=1, @@ -606,7 +640,9 @@ def test_1d_single_targeting(self): ub = torch.tensor([4.0]) inducing_size = 10 bounds = torch.stack([lb, ub]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) target = 0.75 def obj(x): @@ -624,9 +660,11 @@ def obj(x): Strategy( lb=lb, ub=ub, - model=GPClassificationModel(inducing_points=inducing_points, inducing_size=inducing_size), + model=GPClassificationModel( + inducing_points=inducing_points, inducing_size=inducing_size + ), generator=OptimizeAcqfGenerator( - acqf = qUpperConfidenceBound, acqf_kwargs={"beta": 1.96}, lb=lb, ub=ub + acqf=qUpperConfidenceBound, acqf_kwargs={"beta": 1.96}, lb=lb, ub=ub ), min_asks=n_opt, stimuli_per_trial=1, @@ -659,7 +697,9 @@ def test_1d_jnd(self): ub = torch.tensor([4.0]) inducing_size = 10 bounds = torch.stack([lb, ub]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) target = 0.5 @@ -678,9 +718,11 @@ def obj(x): Strategy( lb=lb, ub=ub, - model=GPClassificationModel(inducing_points=inducing_points, inducing_size=inducing_size), + model=GPClassificationModel( + inducing_points=inducing_points, inducing_size=inducing_size + ), generator=OptimizeAcqfGenerator( - acqf = qUpperConfidenceBound, acqf_kwargs={"beta": 1.96}, lb=lb, ub=ub + acqf=qUpperConfidenceBound, acqf_kwargs={"beta": 1.96}, lb=lb, ub=ub ), min_asks=n_opt, stimuli_per_trial=1, @@ -720,7 +762,9 @@ def test_1d_single_lse(self): ub = torch.tensor([4.0]) inducing_size = 10 bounds = torch.stack([lb, ub]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) # target is in z space not phi(z) space, maybe that's # weird @@ -738,10 +782,12 @@ def test_1d_single_lse(self): Strategy( lb=lb, ub=ub, - model=GPClassificationModel(inducing_points=inducing_points, inducing_size=inducing_size), + model=GPClassificationModel( + inducing_points=inducing_points, inducing_size=inducing_size + ), min_asks=n_opt, generator=OptimizeAcqfGenerator( - acqf = MCLevelSetEstimation, acqf_kwargs=extra_acqf_args, lb=lb, ub=ub + acqf=MCLevelSetEstimation, acqf_kwargs=extra_acqf_args, lb=lb, ub=ub ), stimuli_per_trial=1, outcome_types=["binary"], @@ -772,7 +818,9 @@ def test_2d_single_probit(self): ub = torch.tensor([1.0, 1.0]) inducing_size = 20 bounds = torch.stack([lb, ub]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) strat_list = [ Strategy( lb=lb, @@ -785,9 +833,11 @@ def test_2d_single_probit(self): Strategy( lb=lb, ub=ub, - model=GPClassificationModel(inducing_points=inducing_points, inducing_size=inducing_size), + model=GPClassificationModel( + inducing_points=inducing_points, inducing_size=inducing_size + ), generator=OptimizeAcqfGenerator( - acqf = qUpperConfidenceBound, acqf_kwargs={"beta": 1.96}, lb=lb, ub=ub + acqf=qUpperConfidenceBound, acqf_kwargs={"beta": 1.96}, lb=lb, ub=ub ), min_asks=n_opt, stimuli_per_trial=1, @@ -817,7 +867,9 @@ def test_extra_ask_warns(self): ub = torch.tensor([4.0]) inducing_size = 10 bounds = torch.stack([torch.tensor(lb), torch.tensor(ub)]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) model_list = [ Strategy( lb=lb, @@ -830,9 +882,11 @@ def test_extra_ask_warns(self): Strategy( lb=lb, ub=ub, - model=GPClassificationModel(inducing_points=inducing_points, inducing_size=inducing_size), + model=GPClassificationModel( + inducing_points=inducing_points, inducing_size=inducing_size + ), generator=OptimizeAcqfGenerator( - acqf = qUpperConfidenceBound, acqf_kwargs={"beta": 1.96}, lb=lb, ub=ub + acqf=qUpperConfidenceBound, acqf_kwargs={"beta": 1.96}, lb=lb, ub=ub ), min_asks=n_opt, stimuli_per_trial=1, diff --git a/tests/models/test_monotonic_rejection_gp.py b/tests/models/test_monotonic_rejection_gp.py index 534b2db7e..2a766f21a 100644 --- a/tests/models/test_monotonic_rejection_gp.py +++ b/tests/models/test_monotonic_rejection_gp.py @@ -18,13 +18,13 @@ from aepsych.acquisition.objective import ProbitObjective from aepsych.generators import MonotonicRejectionGenerator from aepsych.models import MonotonicRejectionGP +from aepsych.models.inducing_point_allocators import SobolAllocator +from aepsych.models.utils import select_inducing_points from aepsych.strategy import Strategy from botorch.acquisition.objective import IdentityMCObjective from botorch.utils.testing import BotorchTestCase from gpytorch.likelihoods import BernoulliLikelihood, GaussianLikelihood from scipy.stats import norm -from aepsych.models.inducing_point_allocators import SobolAllocator -from aepsych.models.utils import select_inducing_points class MonotonicRejectionGPLSETest(BotorchTestCase): @@ -36,7 +36,9 @@ def testRegression(self): ub = torch.tensor([4.0, 4.0]) inducing_size = 2 bounds = torch.stack([lb, ub]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) m = MonotonicRejectionGP( inducing_points=inducing_points, @@ -93,7 +95,9 @@ def testClassification(self): ub = torch.tensor([4.0, 4.0]) inducing_size = 2 bounds = torch.stack([lb, ub]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) m = MonotonicRejectionGP( inducing_points=inducing_points, diff --git a/tests/models/test_multitask_regression.py b/tests/models/test_multitask_regression.py index 7b5151a75..be9647d56 100644 --- a/tests/models/test_multitask_regression.py +++ b/tests/models/test_multitask_regression.py @@ -12,18 +12,39 @@ import torch from aepsych.generators import SobolGenerator from aepsych.models import IndependentMultitaskGPRModel, MultitaskGPRModel -from parameterized import parameterized from aepsych.models.inducing_point_allocators import SobolAllocator from aepsych.models.utils import select_inducing_points +from parameterized import parameterized # run on single threads to keep us from deadlocking weirdly in CI if "CI" in os.environ or "SANDCASTLE" in os.environ: torch.set_num_threads(1) models = [ - (MultitaskGPRModel(num_outputs=2, rank=2,inducing_points=select_inducing_points(inducing_size=10, allocator=SobolAllocator(bounds=torch.stack([torch.tensor(-1.0), torch.tensor(3.0)]))))), - (IndependentMultitaskGPRModel(num_outputs=2,inducing_points=select_inducing_points(inducing_size=10, allocator=SobolAllocator(bounds=torch.stack([torch.tensor(-1.0), torch.tensor(3.0)]))))), - ] + ( + MultitaskGPRModel( + num_outputs=2, + rank=2, + inducing_points=select_inducing_points( + inducing_size=10, + allocator=SobolAllocator( + bounds=torch.stack([torch.tensor(-1.0), torch.tensor(3.0)]) + ), + ), + ) + ), + ( + IndependentMultitaskGPRModel( + num_outputs=2, + inducing_points=select_inducing_points( + inducing_size=10, + allocator=SobolAllocator( + bounds=torch.stack([torch.tensor(-1.0), torch.tensor(3.0)]) + ), + ), + ) + ), +] class MultitaskGPRegressionTest(unittest.TestCase): diff --git a/tests/models/test_pairwise_probit.py b/tests/models/test_pairwise_probit.py index e5778b029..ad2e07534 100644 --- a/tests/models/test_pairwise_probit.py +++ b/tests/models/test_pairwise_probit.py @@ -288,7 +288,11 @@ def test_1d_pairwise_probit_pure_exploration(self): ub=ub, model=PairwiseProbitModel(lb=lb, ub=ub), generator=OptimizeAcqfGenerator( - acqf=acqf, acqf_kwargs=extra_acqf_args, stimuli_per_trial=2, lb=lb, ub=ub + acqf=acqf, + acqf_kwargs=extra_acqf_args, + stimuli_per_trial=2, + lb=lb, + ub=ub, ), min_asks=n_opt, stimuli_per_trial=2, @@ -402,7 +406,11 @@ def test_2d_pairwise_probit_pure_exploration(self): ub=ub, model=PairwiseProbitModel(lb=lb, ub=ub), generator=OptimizeAcqfGenerator( - acqf=acqf, acqf_kwargs=extra_acqf_args, stimuli_per_trial=2, lb=lb, ub=ub + acqf=acqf, + acqf_kwargs=extra_acqf_args, + stimuli_per_trial=2, + lb=lb, + ub=ub, ), min_asks=n_opt, stimuli_per_trial=2, diff --git a/tests/models/test_semi_p.py b/tests/models/test_semi_p.py index 02fd20cec..4e6f66578 100644 --- a/tests/models/test_semi_p.py +++ b/tests/models/test_semi_p.py @@ -26,16 +26,18 @@ from aepsych.likelihoods import BernoulliObjectiveLikelihood from aepsych.likelihoods.semi_p import LinearBernoulliLikelihood from aepsych.models import HadamardSemiPModel, SemiParametricGPModel -from aepsych.models.inducing_point_allocators import AutoAllocator +from aepsych.models.inducing_point_allocators import AutoAllocator, SobolAllocator from aepsych.models.semi_p import _hadamard_mvn_approx, semi_p_posterior_transform +from aepsych.models.utils import select_inducing_points from aepsych.strategy import SequentialStrategy, Strategy +from aepsych.utils import make_scaled_sobol from gpytorch.distributions import MultivariateNormal from parameterized import parameterized -from aepsych.models.inducing_point_allocators import SobolAllocator -from aepsych.models.utils import select_inducing_points -from aepsych.utils import make_scaled_sobol -def _hadamard_model_constructor(inducing_points, stim_dim, floor, objective=FloorLogitObjective): + +def _hadamard_model_constructor( + inducing_points, stim_dim, floor, objective=FloorLogitObjective +): return HadamardSemiPModel( inducing_points=inducing_points, stim_dim=stim_dim, @@ -46,7 +48,9 @@ def _hadamard_model_constructor(inducing_points, stim_dim, floor, objective=Floo ) -def _semip_model_constructor(inducing_points, stim_dim, floor, objective=FloorLogitObjective): +def _semip_model_constructor( + inducing_points, stim_dim, floor, objective=FloorLogitObjective +): return SemiParametricGPModel( inducing_points=inducing_points, stim_dim=stim_dim, @@ -87,8 +91,9 @@ def setUp(self): self.X = torch.Tensor(X) self.inducing_size = 10 bounds = torch.stack([self.lb, self.ub]) - self.inducing_points = select_inducing_points(inducing_size=self.inducing_size, allocator=SobolAllocator(bounds=bounds)) - + self.inducing_points = select_inducing_points( + inducing_size=self.inducing_size, allocator=SobolAllocator(bounds=bounds) + ) @parameterized.expand( [(SemiPThresholdObjective(target=0.75),), (SemiPProbabilityObjective(),)] @@ -134,7 +139,7 @@ def test_analytic_lookahead_generation(self): floor=floor, objective=objective, ) - + generator = OptimizeAcqfGenerator( acqf=GlobalMI, acqf_kwargs={ @@ -204,12 +209,15 @@ def test_prediction_shapes(self, model_constructor): lb = torch.tensor([-1.0, -1.0]) ub = torch.tensor([1.0, 1.0]) inducing_size = 10 - bounds = torch.stack([lb,ub]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) - + bounds = torch.stack([lb, ub]) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) with self.subTest(model_constructor=model_constructor): - model = model_constructor(inducing_points=inducing_points, stim_dim=self.stim_dim, floor=0) + model = model_constructor( + inducing_points=inducing_points, stim_dim=self.stim_dim, floor=0 + ) strat_list = [ Strategy( @@ -267,14 +275,18 @@ def test_prediction_shapes(self, model_constructor): @parameterized.expand([(_semip_model_constructor,), (_hadamard_model_constructor,)]) def test_reset_variational_strategy(self, model_constructor): lb = torch.tensor([-3.0, -3.0]) - ub =torch.tensor([3.0, 3.0]) + ub = torch.tensor([3.0, 3.0]) inducing_size = 10 bounds = torch.stack([lb, ub]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) stim_dim = 0 with self.subTest(model_constructor=model_constructor): - model = model_constructor(inducing_points=inducing_points, stim_dim=stim_dim, floor=0) + model = model_constructor( + inducing_points=inducing_points, stim_dim=stim_dim, floor=0 + ) link = FloorLogitObjective(floor=0) y = torch.bernoulli(link(self.f)) @@ -346,9 +358,13 @@ def setUp(self): def test_reset_hyperparams(self): inducing_size = 20 bounds = torch.stack([torch.tensor([-3.0, -3.0]), torch.tensor([3.0, 3.0])]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) - model = HadamardSemiPModel(inducing_points=inducing_points, inducing_size=inducing_size) + model = HadamardSemiPModel( + inducing_points=inducing_points, inducing_size=inducing_size + ) slope_os_before = model.slope_covar_module.outputscale.clone().detach().numpy() offset_os_before = ( diff --git a/tests/models/test_utils.py b/tests/models/test_utils.py index ade1af54b..6cdc8d0a6 100644 --- a/tests/models/test_utils.py +++ b/tests/models/test_utils.py @@ -11,14 +11,17 @@ import numpy as np import torch from aepsych.models import GPClassificationModel -from aepsych.models.inducing_point_allocators import AutoAllocator, KMeansAllocator +from aepsych.models.inducing_point_allocators import ( + AutoAllocator, + KMeansAllocator, + SobolAllocator, +) from aepsych.models.utils import select_inducing_points from botorch.models.utils.inducing_point_allocators import ( GreedyVarianceReduction, InducingPointAllocator, ) from sklearn.datasets import make_classification -from aepsych.models.inducing_point_allocators import SobolAllocator class UtilsTestCase(unittest.TestCase): @@ -38,7 +41,9 @@ def test_select_inducing_points(self): lb = torch.Tensor([-3]) ub = torch.Tensor([3]) bounds = torch.stack([lb, ub]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) model = GPClassificationModel( inducing_points=inducing_points, inducing_size=inducing_size diff --git a/tests/test_benchmark.py b/tests/test_benchmark.py index a7e4e451b..34183aaa8 100644 --- a/tests/test_benchmark.py +++ b/tests/test_benchmark.py @@ -21,9 +21,10 @@ Problem, ) from aepsych.models import GPClassificationModel -from scipy.stats import norm from aepsych.models.inducing_point_allocators import SobolAllocator from aepsych.models.utils import select_inducing_points +from scipy.stats import norm + torch.set_num_threads(1) torch.set_num_interop_threads(1) @@ -75,12 +76,12 @@ def setUp(self): self.test_problem = example_problems.DiscrimLowDim(thresholds=self.thresholds) inducing_size = 99 bounds = torch.stack([self.test_problem.lb, self.test_problem.ub]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) - - self.model = GPClassificationModel( - inducing_points=inducing_points + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) ) + self.model = GPClassificationModel(inducing_points=inducing_points) + def unvectorized_p_below_threshold(self, x, f_thresh) -> torch.Tensor: """this is the original p_below_threshold method in the AEPsychMixin that calculates model prediction of the probability of the stimulus being below a threshold diff --git a/tests/test_config.py b/tests/test_config.py index 9fa673012..ea94b1e83 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -172,7 +172,8 @@ def test_single_probit_config_file(self): ) self.assertTrue(strat.strat_list[1].generator.acqf is EAVC) self.assertTrue( - set(strat.strat_list[1].generator.acqf_kwargs.keys()) == {"lb", "ub", "target"} + set(strat.strat_list[1].generator.acqf_kwargs.keys()) + == {"lb", "ub", "target"} ) self.assertTrue(strat.strat_list[1].generator.acqf_kwargs["target"] == 0.75) self.assertTrue(strat.strat_list[1].generator.samps == 1000) diff --git a/tests/test_strategy.py b/tests/test_strategy.py index ec908adf0..d6a7c5fc4 100644 --- a/tests/test_strategy.py +++ b/tests/test_strategy.py @@ -18,7 +18,9 @@ SobolGenerator, ) from aepsych.models.gp_classification import GPClassificationModel +from aepsych.models.inducing_point_allocators import SobolAllocator from aepsych.models.monotonic_rejection_gp import MonotonicRejectionGP +from aepsych.models.utils import select_inducing_points from aepsych.strategy import SequentialStrategy, Strategy from aepsych.transforms import ( ParameterTransformedGenerator, @@ -26,8 +28,7 @@ ParameterTransforms, ) from aepsych.transforms.parameters import Normalize -from aepsych.models.inducing_point_allocators import SobolAllocator -from aepsych.models.utils import select_inducing_points + class TestSequenceGenerators(unittest.TestCase): def setUp(self): @@ -44,8 +45,9 @@ def setUp(self): ) inducing_size = 99 bounds = torch.stack((lb, ub)) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) - + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) self.strat = Strategy( model=ParameterTransformedModel( @@ -169,7 +171,9 @@ def test_keep_most_recent(self): ub = torch.tensor([1.0, 1.0]) inducing_size = 99 bounds = torch.stack((lb, ub)) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) self.strat = Strategy( model=GPClassificationModel( @@ -199,8 +203,9 @@ def test_run_indefinitely(self): ub = torch.tensor([1.0, 1.0]) inducing_size = 99 bounds = torch.stack((lb, ub)) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) - + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) with self.assertWarns(UserWarning): self.strat = Strategy( diff --git a/tests/test_transforms.py b/tests/test_transforms.py index 91a7b533e..3b96599d5 100644 --- a/tests/test_transforms.py +++ b/tests/test_transforms.py @@ -11,6 +11,8 @@ from aepsych.config import Config from aepsych.generators import SobolGenerator from aepsych.models import GPClassificationModel +from aepsych.models.inducing_point_allocators import SobolAllocator +from aepsych.models.utils import select_inducing_points from aepsych.strategy import SequentialStrategy from aepsych.transforms import ( ParameterTransformedGenerator, @@ -18,8 +20,7 @@ ParameterTransforms, ) from aepsych.transforms.parameters import Log10Plus, NormalizeScale -from aepsych.models.inducing_point_allocators import SobolAllocator -from aepsych.models.utils import select_inducing_points + class TransformsConfigTest(unittest.TestCase): def setUp(self): @@ -94,7 +95,14 @@ def test_model_init_equivalent(self): obj_model = ParameterTransformedModel( model=GPClassificationModel, - inducing_points = select_inducing_points(inducing_size=99, allocator=SobolAllocator(bounds=torch.stack((torch.tensor([1.0, 1.0]), torch.tensor([100.0, 100.0]))))), + inducing_points=select_inducing_points( + inducing_size=99, + allocator=SobolAllocator( + bounds=torch.stack( + (torch.tensor([1.0, 1.0]), torch.tensor([100.0, 100.0])) + ) + ), + ), transforms=self.strat.strat_list[1].transforms, ) diff --git a/tests/test_utils.py b/tests/test_utils.py index 313a2b79d..cfd0f39ce 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -12,10 +12,16 @@ import torch from aepsych.config import Config from aepsych.models import GPClassificationModel -from aepsych.utils import _process_bounds, get_dim, get_parameters, make_scaled_sobol from aepsych.models.inducing_point_allocators import SobolAllocator from aepsych.models.utils import select_inducing_points -from aepsych.utils import dim_grid +from aepsych.utils import ( + _process_bounds, + dim_grid, + get_dim, + get_parameters, + make_scaled_sobol, +) + class UtilsTestCase(unittest.TestCase): def test_scaled_sobol_asserts(self): @@ -30,7 +36,6 @@ def test_scaled_sobol_sizes(self): grid = make_scaled_sobol(lb, ub, 100) self.assertEqual(grid.shape, (100, 2)) - def test_process_bounds(self): lb, ub, dim = _process_bounds(np.r_[0, 1], np.r_[2, 3], None) self.assertTrue(torch.all(lb == torch.tensor([0.0, 1.0]))) diff --git a/tests_gpu/generators/test_optimize_acqf_generator.py b/tests_gpu/generators/test_optimize_acqf_generator.py index 899eedc2b..9b5849e68 100644 --- a/tests_gpu/generators/test_optimize_acqf_generator.py +++ b/tests_gpu/generators/test_optimize_acqf_generator.py @@ -22,9 +22,9 @@ from aepsych.acquisition.mutual_information import BernoulliMCMutualInformation from aepsych.generators import OptimizeAcqfGenerator from aepsych.models import GPClassificationModel -from aepsych.strategy import Strategy from aepsych.models.inducing_point_allocators import SobolAllocator from aepsych.models.utils import select_inducing_points +from aepsych.strategy import Strategy from parameterized import parameterized acqf_kwargs_target = {"target": 0.75} @@ -56,12 +56,17 @@ def test_gpu_smoketest(self, acqf, acqf_kwargs): bounds = torch.stack([lb, ub]) inducing_size = 10 inducing_points = select_inducing_points( - inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)).to(self.device) + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ).to(self.device) model = GPClassificationModel( - inducing_points=inducing_points, inducing_size=inducing_size, inducing_point_method=SobolAllocator(bounds=bounds) + inducing_points=inducing_points, + inducing_size=inducing_size, + inducing_point_method=SobolAllocator(bounds=bounds), ) - generator = OptimizeAcqfGenerator(acqf=acqf, acqf_kwargs=acqf_kwargs, lb=lb, ub=ub) + generator = OptimizeAcqfGenerator( + acqf=acqf, acqf_kwargs=acqf_kwargs, lb=lb, ub=ub + ) strat = Strategy( lb=torch.tensor([0]), diff --git a/tests_gpu/models/test_gp_classification.py b/tests_gpu/models/test_gp_classification.py index 281b32603..a3f29ae14 100644 --- a/tests_gpu/models/test_gp_classification.py +++ b/tests_gpu/models/test_gp_classification.py @@ -17,10 +17,11 @@ import numpy as np import numpy.testing as npt from aepsych.models import GPClassificationModel -from scipy.stats import norm -from sklearn.datasets import make_classification from aepsych.models.inducing_point_allocators import SobolAllocator from aepsych.models.utils import select_inducing_points +from scipy.stats import norm +from sklearn.datasets import make_classification + def f_1d(x, mu=0): """ @@ -108,7 +109,9 @@ def test_1d_classification_gpu(self): X, y = self.X, self.y inducing_size = 10 bounds = torch.stack([torch.tensor([-3.0]), torch.tensor([3.0])]) - inducing_points = select_inducing_points(inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds)) + inducing_points = select_inducing_points( + inducing_size=inducing_size, allocator=SobolAllocator(bounds=bounds) + ) model = GPClassificationModel( inducing_points=inducing_points, diff --git a/tests_gpu/test_strategy.py b/tests_gpu/test_strategy.py index c00fa5a92..efa0482b8 100644 --- a/tests_gpu/test_strategy.py +++ b/tests_gpu/test_strategy.py @@ -11,9 +11,9 @@ from aepsych.acquisition.monotonic_rejection import MonotonicMCLSE from aepsych.generators import OptimizeAcqfGenerator, SobolGenerator from aepsych.models.gp_classification import GPClassificationModel -from aepsych.strategy import Strategy -from aepsych.models.utils import select_inducing_points from aepsych.models.inducing_point_allocators import SobolAllocator +from aepsych.models.utils import select_inducing_points +from aepsych.strategy import Strategy class TestStrategyGPU(unittest.TestCase): @@ -37,8 +37,20 @@ def test_no_gpu_acqf(self): stimuli_per_trial=1, outcome_types=["binary"], min_asks=1, - model=GPClassificationModel(inducing_points=select_inducing_points(inducing_size=10, allocator=SobolAllocator(bounds=torch.stack([torch.tensor([0.0]), torch.tensor([1.0])]))), dim=1), - generator=OptimizeAcqfGenerator(acqf=MonotonicMCLSE, lb=[0.0], ub=[1.0]), + model=GPClassificationModel( + inducing_points=select_inducing_points( + inducing_size=10, + allocator=SobolAllocator( + bounds=torch.stack( + [torch.tensor([0.0]), torch.tensor([1.0])] + ) + ), + ), + dim=1, + ), + generator=OptimizeAcqfGenerator( + acqf=MonotonicMCLSE, lb=[0.0], ub=[1.0] + ), use_gpu_generating=True, )