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 = {}
vocs.observables = ["y1"]
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, vocs=vocs)
X
Xopt ________________________________ Version: 2.4.6.dev5+ga295b108.d20250107 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 scramble: true seed: null strength: 1 supports_batch_generation: true supports_multi_objective: true max_evaluations: null serialize_inline: false serialize_torch: false strict: true vocs: constants: a: dummy_constant constraints: c1: - GREATER_THAN - 0.0 c2: - LESS_THAN - 0.5 objectives: {} observables: - y1 variables: x1: - 0.0 - 3.14159 x2: - 0.0 - 3.14159
# 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.042167 | 0.535971 | dummy_constant | 2.042167 | 0.535971 | 3.514651 | 2.379574 | 0.000158 | False |
1 | 2.853729 | 2.237799 | dummy_constant | 2.853729 | 2.237799 | 12.186314 | 8.559989 | 0.000146 | False |
2 | 0.929324 | 0.455336 | dummy_constant | 0.929324 | 0.455336 | 0.017469 | 0.186314 | 0.000145 | False |
3 | 2.770776 | 0.314067 | dummy_constant | 2.770776 | 0.314067 | 6.799130 | 5.190995 | 0.000144 | False |
4 | 0.490484 | 0.196875 | dummy_constant | 0.490484 | 0.196875 | -0.819122 | 0.091975 | 0.000143 | 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 0x7ff5707174d0>
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, vocs=vocs)
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 0x7ff568576350>
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, vocs=vocs)
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 0x7ff567374690>