Latin Hypercube Generator Example¶
This notebook demonstrates basic use of the latin hypercube generator. This generator is a wrapper for the scipy latin hypercube method and allows users to efficiently sample functions (eg for surrogate models). Because the distribution of points depends on the number of sample requested, internally the xopt routine stores a batch of samples. The batch size is specified as an argument to the object's constructor. All other parameters to the scipy function are broken out this way and for a detailed explanation of what they do, the scipy documentation should be consulted.
from copy import deepcopy
from xopt import Xopt, Evaluator
from xopt.generators.scipy.latin_hypercube import LatinHypercubeGenerator
from xopt.resources.test_functions.tnk import evaluate_TNK, tnk_vocs
import matplotlib.pyplot as plt
import numpy as np
# Create the test problem
vocs = deepcopy(tnk_vocs)
vocs.objectives = {"y1": "EXPLORE"}
evaluator = Evaluator(function=evaluate_TNK)
# Create the generator and xopt object. Note: the samples are generated in
# batches and the batch size determines the arrangement of points to cover
# the bounded region of the variables.
generator = LatinHypercubeGenerator(vocs=vocs, batch_size=1024)
X = Xopt(generator=generator, evaluator=evaluator)
X
/home/runner/work/Xopt/Xopt/.venv/lib/python3.12/site-packages/pydantic/main.py:464: UserWarning: Pydantic serializer warnings: PydanticSerializationUnexpectedValue(Unexpected field `_sampler`: Expected `LatinHypercubeGenerator`) PydanticSerializationUnexpectedValue(Unexpected field `_sampler`: Expected `LatinHypercubeGenerator`) return self.__pydantic_serializer__.to_python(
Xopt
________________________________
Version: 0.1.dev1+gb834d2348
Data size: 0
Config as YAML:
dump_file: null
evaluator:
function: xopt.resources.test_functions.tnk.evaluate_TNK
function_kwargs:
raise_probability: 0
random_sleep: 0
sleep: 0
max_workers: 1
vectorized: false
generator:
batch_size: 1024
name: latin_hypercube
optimization: random-cd
returns_id: false
scramble: true
seed: null
strength: 1
supports_batch_generation: true
supports_constraints: true
supports_multi_objective: true
supports_single_objective: true
vocs:
constants:
a:
dtype: null
type: Constant
value: dummy_constant
constraints:
c1:
dtype: null
type: GreaterThanConstraint
value: 0.0
c2:
dtype: null
type: LessThanConstraint
value: 0.5
objectives:
y1:
dtype: null
type: ExploreObjective
observables: {}
variables:
x1:
default_value: null
domain:
- 0.0
- 3.14159
dtype: null
type: ContinuousVariable
x2:
default_value: null
domain:
- 0.0
- 3.14159
dtype: null
type: ContinuousVariable
serialize_inline: false
serialize_torch: false
stopping_condition: null
strict: true
# Sample the function a number of times using latin hypercube points
for _ in range(1024):
X.step()
X.data.head()
| x1 | x2 | a | y1 | y2 | c1 | c2 | xopt_runtime | xopt_error | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 2.407659 | 3.022452 | dummy_constant | 2.407659 | 3.022452 | 13.955132 | 10.001928 | 0.015053 | False |
| 1 | 0.892547 | 2.011072 | dummy_constant | 0.892547 | 2.011072 | 3.748945 | 2.437432 | 0.000833 | False |
| 2 | 2.692674 | 2.006251 | dummy_constant | 2.692674 | 2.006251 | 10.343705 | 7.076611 | 0.009184 | False |
| 3 | 2.172244 | 2.256018 | dummy_constant | 2.172244 | 2.256018 | 8.712804 | 5.879997 | 0.002210 | False |
| 4 | 1.417345 | 1.753151 | dummy_constant | 1.417345 | 1.753151 | 4.094134 | 2.411909 | 0.007561 | False |
# Plot the data
plt.scatter(X.data["x1"], X.data["x2"], c=X.data["y1"])
plt.xlabel("x1")
plt.ylabel("x2")
plt.colorbar(label="y1")
<matplotlib.colorbar.Colorbar at 0x7f8dae10e000>
Distribution of Latin Hypercube points¶
Points in latin hypercube sampling are arranged in a grid such that none occupy the same row or column. That is, in chess it is similar to having n rooks on the board which cannot take each other. We can demonstrate this in the sampler by turning off the "scramble" feature (turned on by default).
n = 16
generator = LatinHypercubeGenerator(vocs=vocs, batch_size=n, scramble=False, seed=0)
X = Xopt(generator=generator, evaluator=evaluator)
for _ in range(n):
X.step()
plt.scatter(X.data["x1"], X.data["x2"], c=X.data["y1"])
plt.xlabel("x1")
plt.ylabel("x2")
plt.colorbar(label="y1")
plt.hlines(np.linspace(0, 3.1416, n + 1), 0, 3.1416, color="k")
plt.vlines(np.linspace(0, 3.1416, n + 1), 0, 3.1416, color="k")
<matplotlib.collections.LineCollection at 0x7f8dadfe05c0>
The scramble feature will randomize the location of the points within each square while still maintaining the latin hypercube style cells.
n = 16
generator = LatinHypercubeGenerator(vocs=vocs, batch_size=n, scramble=True, seed=0)
X = Xopt(generator=generator, evaluator=evaluator)
for _ in range(n):
X.step()
plt.scatter(X.data["x1"], X.data["x2"], c=X.data["y1"])
plt.xlabel("x1")
plt.ylabel("x2")
plt.colorbar(label="y1")
plt.hlines(np.linspace(0, 3.1416, n + 1), 0, 3.1416, color="k")
plt.vlines(np.linspace(0, 3.1416, n + 1), 0, 3.1416, color="k")
<matplotlib.collections.LineCollection at 0x7f8dae190dd0>