Source code for psp.models.voronoi

"""A cheap toy model whose ordinal regions are Voronoi cells of the unit hypercube.

The model returns the squared distance from the parameter set to each of
``regions`` random centres, and ``discretize`` returns the index of the nearest
centre in a 2 x 2 matrix. It is the model of the package tests and benchmarks.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any

import torch

__all__ = ["VoronoiModel", "voronoi_model", "voronoi_control"]


[docs] @dataclass(frozen=True) class VoronoiModel: """A Voronoi model: ``centres`` of shape ``(regions, dimensions)``.""" centres: torch.Tensor @property def regions(self) -> int: return int(self.centres.shape[0]) @property def dimensions(self) -> int: return int(self.centres.shape[1])
[docs] def model(self, parameters: torch.Tensor) -> torch.Tensor: """Squared distance to every centre, shape ``(n, regions)``.""" centres = self.centres.to(parameters.device, parameters.dtype) return ((parameters.unsqueeze(1) - centres.unsqueeze(0)) ** 2).sum(dim=2)
[docs] @staticmethod def discretize(distances: torch.Tensor) -> torch.Tensor: """Index of the nearest centre, as a 2 x 2 matrix per row.""" nearest = distances.argmin(dim=1).to(distances.dtype) return nearest.reshape(-1, 1, 1).expand(-1, 2, 2)
[docs] def voronoi_model(regions: int, dimensions: int, seed: int = 7624) -> VoronoiModel: """A Voronoi model with ``regions`` centres drawn uniformly in the unit hypercube.""" generator = torch.Generator().manual_seed(seed) centres = torch.rand((regions, dimensions), generator=generator, dtype=torch.float64) return VoronoiModel(centres)
[docs] def voronoi_control( regions: int, dimensions: int, iterations: int, population: int, radius: float = 0.3, init: torch.Tensor | None = None, ) -> dict[str, Any]: """The control settings used by the tests and benchmarks for a Voronoi model.""" if init is None: init = torch.full((1, dimensions), 0.5, dtype=torch.float64) return { "iterations": iterations, "population": population, "radius": radius, "lower": [0.0] * dimensions, "upper": [1.0] * dimensions, "init": init, "parameter_names": [f"p{i + 1}" for i in range(dimensions)], "stimuli_names": [f"s{i + 1}" for i in range(regions)], "dimensionality": 2, "responses": regions, }