Skip to content

Use the built-in sampling strategies¤

In this example, we will use the built-in sampling strategies provided by f3dasm to generate samples for a data-driven experiment. We first create 2D continuous input domain with the make_nd_continuous_domain() helper function:

from f3dasm.design import make_nd_continuous_domain

domain = make_nd_continuous_domain(bounds=[[0.0, 1.0], [0.0, 1.0]])
domain
Domain(input_space={'x0': ContinuousParameter(lower_bound=0.0, upper_bound=1.0, log=False), 'x1': ContinuousParameter(lower_bound=0.0, upper_bound=1.0, log=False)}, output_space={})

Sampling from this domain can be done in two ways:

Method 1: Providing a sampler name as a string¤

Call create_sampler with the sampler's name to get a Block, then invoke its call method on an ExperimentData. Some samplers accept additional parameters, which can be passed as keyword arguments to the factory:

from f3dasm import ExperimentData, create_sampler

# Initialize the sampler
sampler = create_sampler(sampler="latin", seed=42)

# Sample from the domain
samples = ExperimentData(domain=domain)
samples = sampler.call(samples, n_samples=10)

samples
jobs     input          
               x0        x1
0  open  0.173199  0.852476
1  open  0.502058  0.215599
2  open  0.460112  0.596991
3  open  0.830424  0.718340
4  open  0.037454  0.386618
5  open  0.718182  0.159866
6  open  0.683244  0.929123
7  open  0.305808  0.095071
8  open  0.215602  0.470807
9  open  0.943195  0.621234

Method 2: Importing the sampler from the f3dasm.design module¤

Another way is to import e.g. the latin() factory from the f3dasm.design module directly and build the sampler block yourself:

from f3dasm.design import latin

sampler = latin(seed=42)
# Sample from the domain
samples = ExperimentData(domain=domain)
samples = sampler.call(samples, n_samples=10)

samples
jobs     input          
               x0        x1
0  open  0.173199  0.852476
1  open  0.502058  0.215599
2  open  0.460112  0.596991
3  open  0.830424  0.718340
4  open  0.037454  0.386618
5  open  0.718182  0.159866
6  open  0.683244  0.929123
7  open  0.305808  0.095071
8  open  0.215602  0.470807
9  open  0.943195  0.621234
import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(4, 4))

df_random, _ = samples.to_pandas()
ax.scatter(df_random.iloc[:, 0], df_random.iloc[:, 1])
ax.set_xlabel(domain.input_names[0])
ax.set_ylabel(domain.input_names[1])
Text(0, 0.5, 'x1')

img

Append semantics¤

Sampler blocks append their new samples to the input ExperimentData (via data + new_data) rather than returning a fresh ExperimentData. Calling a sampler twice therefore grows the dataset:

data = create_sampler("random", seed=0).call(data, n_samples=3)
data = create_sampler("latin",  seed=0).call(data, n_samples=4)
# data now has 3 + 4 = 7 rows

Next: Optuna Integration