Skip to content

nested_sampler


Overview

NestedSampler composes multiple samplers into a single sampler by generating the Cartesian product of their initial sample sets.

Each nested sampler is initialized independently, and all combinations of their initial samples are merged into joint parameter configurations. The sampler produces exactly one batch of samples and does not support iterative or adaptive sampling.


NestedSampler

NestedSampler(samplers, *args, **kwargs)

Bases: Sampler

Configuration

The NestedSampler is configured by specifying a dictionary of sub-samplers, each with its own type and configuration.

Example configuration:

sampler:
  type: NestedSampler
  samplers:
    sampler_a:
      type: GridSampler
      parameters: ['x']
      bounds: [[0, 1]]
      num_samples: [4, 3]

    sampler_b:
      type: RandomSampler
      parameters: ['y', 'z']
      bounds: [[0, 1], [10, 20]]
      num_samples: 2

  budget: 24
In this example:

  • sampler_a generates 12 samples for (x)

  • sampler_b generates 2 samples for (y, z)

  • The nested sampler produces 12 × 2 = 24 combined configurations.

Attributes:

Name Type Description
all_samplers list[Sampler]

List of instantiated nested samplers.

budget int

Total number of samples allowed across the nested sampling process.

batch_size int

Number of samples returned per call to get_next_samples.

submitted int

Counter tracking the number of generated combined samples.

all_parameters list[str]

Flattened list of parameter names from all nested samplers.

initial_parameters list[list[dict]]

Initial samples collected from each nested sampler.

total_num_runs list[int]

Total number of combined parameter configurations.

depth_num_runs list[int]

Cumulative product of the number of initial samples per nested sampler.

current_batch int

Tracks whether the first (and only) batch has been returned.


Assumptions and notes

  • Each nested sampler must implement get_next_samples() and expose a parameters attribute.
  • All samples are generated eagerly during initialization.
  • The total number of generated samples grows multiplicatively with the number of nested samplers.

Initializes the NestedSampler and all nested samplers.

Each nested sampler is instantiated using its provided configuration, and its initial samples are collected immediately. The full Cartesian product of these samples defines the nested search space.

Parameters:

Name Type Description Default
samplers dict

Mapping of sampler names to sampler configurations. Each configuration must specify the sampler type and its corresponding parameters.

required
budget int

Total number of samples allowed across the nested sampling process.

required
batch_size int

Number of samples returned per batch. Defaults to the total budget.

required
Source code in src/enchanted_surrogates/samplers/nested_sampler.py
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
def __init__(self, samplers, *args, **kwargs):

    """
    Initializes the NestedSampler and all nested samplers.

    Each nested sampler is instantiated using its provided configuration,
    and its initial samples are collected immediately. The full Cartesian
    product of these samples defines the nested search space.

    Args:
        samplers (dict): Mapping of sampler names to sampler configurations.
            Each configuration must specify the sampler type and its
            corresponding parameters.
        budget (int, optional): Total number of samples allowed across the
            nested sampling process.
        batch_size (int, optional): Number of samples returned per batch.
            Defaults to the total budget.

    """
    samplers_keys = samplers.keys
    samplers_types = [samplers[k]['type'] for k in samplers_keys()]
    samplers_config = [samplers[k] for k in samplers_keys()]
    self.all_samplers = [import_sampler(sampler_type, sampler_config) for sampler_type, sampler_config in zip(samplers_types, samplers_config)]
    self.budget = kwargs.get('budget')
    self.batch_size = kwargs.get('batch_size', self.budget)
    self.submitted = 0
    self.all_parameters = [param for sampler in self.all_samplers for param in sampler.parameters]

    self.initial_parameters = []
    for sampler in self.all_samplers:
        self.initial_parameters.append(sampler.get_next_samples())

    self.total_num_runs = [np.sum(np.cumprod([len(ip) for ip in self.initial_parameters]))]
    self.depth_num_runs = [np.cumprod([len(ip) for ip in self.initial_parameters])]
    self.current_batch = 0 

get_next_samples

get_next_samples()

Returns the combined parameter configurations generated by the nested samplers.

On the first call, this method computes the Cartesian product of the initial samples from all nested samplers and merges their parameter dictionaries into joint configurations.

Returns:

Type Description

list[dict]: A list of combined parameter dictionaries.

Raises:

Type Description
NotImplementedError

If called more than once.

Source code in src/enchanted_surrogates/samplers/nested_sampler.py
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
def get_next_samples(self):

    """
    Returns the combined parameter configurations generated by the nested samplers.

    On the first call, this method computes the Cartesian product of the initial samples from all nested samplers and merges their parameter
    dictionaries into joint configurations.

    Returns:
        list[dict]:
          A list of combined parameter dictionaries.

    Raises:
        NotImplementedError:
          If called more than once.

    """

    if self.current_batch == 0:
        # get all initial parameters

        # Cartesian product of all sublists
        combinations = product(*self.initial_parameters)
        # Merge each tuple of dicts into a single dict
        combined = [dict(kv for d in combo for kv in d.items()) for combo in combinations]

        self.submitted += len(combined)
        # self.has_budget = False # nested sampler only supports one batch. For nested active sampling a bespoke nested sampler is needed for each application
        self.current_batch += 1
        return combined
    else:
        raise NotImplementedError

register_future

register_future(future)

Registers a completed or scheduled evaluation.

This method is part of the sampler interface but is not used by the NestedSampler, as it does not adapt based on evaluation results.

Parameters:

Name Type Description Default
future

A future or handle representing an asynchronous evaluation.

required

Returns:

Type Description

None

Source code in src/enchanted_surrogates/samplers/nested_sampler.py
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
def register_future(self, future):
    """
    Registers a completed or scheduled evaluation.

    This method is part of the sampler interface but is not used by the
    NestedSampler, as it does not adapt based on evaluation results.

    Args:
        future:
          A future or handle representing an asynchronous evaluation.

    Returns:
        None
    """
    return None

register_futures

register_futures(futures)

Registers multiple completed or scheduled evaluations.

This method is part of the sampler interface but is implemented as a no-op for the NestedSampler.

Parameters:

Name Type Description Default
futures

An iterable of futures or handles representing evaluations.

required

Returns:

Type Description

None

Source code in src/enchanted_surrogates/samplers/nested_sampler.py
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
def register_futures(self, futures):
    """
    Registers multiple completed or scheduled evaluations.

    This method is part of the sampler interface but is implemented as
    a no-op for the NestedSampler.

    Args:
        futures:
          An iterable of futures or handles representing evaluations.

    Returns:
        None
    """

    return None