sampling.mm
sampling.mm
Functions
| Name | Description |
|---|---|
| bestlh | Generates an optimized Latin hypercube by evolving the Morris-Mitchell |
| jd | Computes and counts the distinct p-norm distances between all pairs of points in X. |
| mm | Determines which of two sampling plans has better space-filling properties |
| mm_corrected_improvement | Calculates the corrected Morris-Mitchell improvement for a candidate point. |
| mm_improvement | Calculates the Morris-Mitchell improvement for a candidate point x. |
| mm_improvement_contour | Generates a contour plot of the Morris-Mitchell improvement over a grid defined by x1 and x2. |
| mmlhs | Performs an evolutionary search (using perturbations) to find a Morris-Mitchell |
| mmphi | Calculates the Morris-Mitchell sampling plan quality criterion. |
| mmphi_corrected | Calculates the corrected, dimension-aware Morris-Mitchell criterion. |
| mmphi_corrected_update | Updates the corrected Morris-Mitchell criterion after adding one point. |
| mmphi_intensive | Calculates a size-invariant Morris-Mitchell criterion. |
| mmphi_intensive_update | Updates the Morris-Mitchell intensive criterion for n+1 points by adding a new point to the design. |
| mmsort | Ranks multiple sampling plans stored in a 3D array according to the |
| perturb | Performs a specified number of random element swaps on a sampling plan. |
| phisort | Ranks multiple sampling plans stored in a 3D array by the Morris-Mitchell |
| plot_mmphi_corrected_vs_n_lhs | Generate LHS designs for varying n and plot the Corrected Morris-Mitchell |
| plot_mmphi_corrected_vs_points | Plot the Corrected Morris-Mitchell Criterion versus the number of added points. |
| plot_mmphi_vs_n_lhs | Generates LHS designs for varying n, calculates mmphi and mmphi_intensive, |
| plot_mmphi_vs_points | Plot the Morris-Mitchell criterion versus the number of added points. |
| propose_mmphi_corrected_minimizing_point | Proposes a new point that minimizes the corrected Morris-Mitchell criterion. |
| propose_mmphi_intensive_minimizing_point | Propose a new point that, when added to X, minimizes the intensive Morris-Mitchell (mmphi_intensive) criterion. |
| subset | Returns a space-filling subset of a given size from a sampling plan, along with |
bestlh
sampling.mm.bestlh(
n,
k,
population,
iterations,
p=1,
plot=False,
verbosity=0,
edges=0,
q_list=[1, 2, 5, 10, 20, 50, 100],
)Generates an optimized Latin hypercube by evolving the Morris-Mitchell criterion across multiple exponents (q values) and selecting the best plan.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| n | int | Number of points required in the Latin hypercube. | required |
| k | int | Number of design variables (dimensions). | required |
| population | int | Number of offspring in each generation of the evolutionary search. | required |
| iterations | int | Number of generations for the evolutionary search. | required |
| p | int | The distance norm to use. p=1 for Manhattan (L1), p=2 for Euclidean (L2). Defaults to 1 (faster than 2). | 1 |
| plot | bool | If True, a scatter plot of the optimized plan in the first two dimensions will be displayed. Only if k>=2. Defaults to False. | False |
| verbosity | int | Verbosity level. 0 is silent, 1 prints the best q value found. Defaults to 0. | 0 |
| edges | int | If 1, places centers of the extreme bins at the domain edges ([0,1]). Otherwise, bins are fully contained within the domain, i.e. midpoints. Defaults to 0. | 0 |
| q_list | list | A list of q values to optimize. Defaults to [1, 2, 5, 10, 20, 50, 100]. These values are used to evaluate the space-fillingness of the Latin hypercube. The best plan is selected based on the lowest mmphi value. | [1, 2, 5, 10, 20, 50, 100] |
Returns
| Name | Type | Description |
|---|---|---|
| np.ndarray | np.ndarray: A 2D array of shape (n, k) representing an optimized Latin hypercube. |
Examples
>>> import numpy as np
>>> from spotoptim.sampling.mm import bestlh
# Generate a 5-point, 2-dimensional Latin hypercube
>>> X = bestlh(n=5, k=2, population=5, iterations=10)
>>> print(X.shape)
(5, 2)jd
sampling.mm.jd(X, p=1.0)Computes and counts the distinct p-norm distances between all pairs of points in X. It returns: 1) A list of distinct distances (sorted), and 2) A corresponding multiplicity array that indicates how often each distance occurs.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| X | np.ndarray | A 2D array of shape (n, d) representing n points in d-dimensional space. | required |
| p | float | The distance norm to use. p=1 uses the Manhattan (L1) norm, while p=2 uses the Euclidean (L2) norm. Defaults to 1.0 (Manhattan norm). | 1.0 |
Returns
| Name | Type | Description |
|---|---|---|
| (np.ndarray, np.ndarray) | A tuple (J, distinct_d), where: - distinct_d is a 1D float array of unique, sorted distances between points. - J is a 1D integer array that provides the multiplicity (occurrence count) of each distance in distinct_d. |
Notes
Many thanks to the original author of this code, A Sobester, for providing the original Matlab code under the GNU Licence. Original Matlab Code: Copyright 2007 A Sobester: “This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU General Public License and GNU Lesser General Public License along with this program. If not, see http://www.gnu.org/licenses/.”
Examples
>>> import numpy as np
>>> from spotoptim.sampling.mm import jd
>>> # A small 3-point set in 2D
>>> X = np.array([[0.0, 0.0],
... [1.0, 1.0],
... [2.0, 2.0]])
>>> J, distinct_d = jd(X, p=2.0)
>>> print("Distinct distances:", distinct_d)
>>> print("Occurrences:", J)
# Possible output (using Euclidean norm):
# Distinct distances: [1.41421356 2.82842712]
# Occurrences: [1 1]
# Explanation: Distances are sqrt(2) between consecutive points and 2*sqrt(2) for the farthest pair.
Distinct distances: [1.41421356 2.82842712]
Occurrences: [2 1]mm
sampling.mm.mm(X1, X2, p=1.0)Determines which of two sampling plans has better space-filling properties according to the Morris-Mitchell criterion.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| X1 | np.ndarray | A 2D array representing the first sampling plan. | required |
| X2 | np.ndarray | A 2D array representing the second sampling plan. | required |
| p | float | The distance metric. p=1 uses Manhattan (L1) distance, while p=2 uses Euclidean (L2). Defaults to 1.0. | 1.0 |
Returns
| Name | Type | Description |
|---|---|---|
| int | int | - 0 if both plans are identical or equally space-filling - 1 if X1 is more space-filling - 2 if X2 is more space-filling |
Notes
Many thanks to the original author of this code, A Sobester, for providing the original Matlab code under the GNU Licence. Original Matlab Code: Copyright 2007 A Sobester: “This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU General Public License and GNU Lesser General Public License along with this program. If not, see http://www.gnu.org/licenses/.”
Examples
>>> import numpy as np
>>> from spotoptim.sampling.mm import mm
>>> # Create two 3-point sampling plans in 2D
>>> X1 = np.array([[0.0, 0.0],
... [0.5, 0.5],
... [0.0, 1.0]])
>>> X2 = np.array([[0.1, 0.1],
... [0.4, 0.6],
... [0.1, 0.9]])
>>> # Compare which plan has better space-filling (Morris-Mitchell)
>>> better = mm(X1, X2, p=2.0)
>>> print(better)
# Prints either 0, 1, or 2 depending on which plan is more space-filling.mm_corrected_improvement
sampling.mm.mm_corrected_improvement(
x,
X_base,
phi_base=None,
J_base=None,
d_base=None,
q=2,
p=2,
normalize_flag=False,
verbose=False,
exponential=True,
)Calculates the corrected Morris-Mitchell improvement for a candidate point.
Measures how much the corrected criterion :math:\hat{\Phi}_q improves (decreases) when x is added to the existing design X_base.
The improvement is defined as:
.. math::
\Delta\hat{\Phi}(x) =
\begin{cases}
\exp\bigl(\hat{\Phi}_q(X_{\text{base}}) -
\hat{\Phi}_q(X_{\text{base}} \cup \{x\})\bigr)
& \text{if } \texttt{exponential=True} \\
\hat{\Phi}_q(X_{\text{base}}) -
\hat{\Phi}_q(X_{\text{base}} \cup \{x\})
& \text{otherwise}
\end{cases}
A positive value indicates that adding x improves the design (lowers :math:\hat{\Phi}_q). The exponential form maps the improvement to :math:(0, \infty) and is convenient as a desirability-function input.
The cached (phi_base, J_base, d_base) triple can be supplied to avoid recomputing the base criterion when calling this function repeatedly for many candidates. If omitted, the base criterion is computed from scratch.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| x | np.ndarray | Candidate point, shape (k,) or (1, k). |
required |
| X_base | np.ndarray | Existing design points, shape (n, k). |
required |
| phi_base | float | Pre-computed :math:\hat{\Phi}_q for X_base. Computed internally if not provided. |
None |
| J_base | np.ndarray | Pre-computed multiplicity array for X_base. Computed internally if not provided. |
None |
| d_base | np.ndarray | Pre-computed unique-distance array for X_base. Computed internally if not provided. |
None |
| q | float | Exponent for the corrected Morris-Mitchell criterion. Defaults to 2. | 2 |
| p | float | Distance norm (p=1 Manhattan, p=2 Euclidean). Defaults to 2. | 2 |
| normalize_flag | bool | If True, normalizes X_base and x to [0, 1] before computing distances. Defaults to False. |
False |
| verbose | bool | If True, prints the base criterion, new criterion, and improvement. Defaults to False. | False |
| exponential | bool | If True, returns :math:\exp(\hat{\Phi}_{\text{base}} - \hat{\Phi}_{\text{new}}). If False, returns the raw difference. Defaults to True. |
True |
Returns
| Name | Type | Description |
|---|---|---|
| float | float | The corrected Morris-Mitchell improvement score. |
Examples
>>> import numpy as np
>>> from spotoptim.sampling.mm import mm_corrected_improvement
>>> X_base = np.array([[0.1, 0.2], [0.4, 0.5], [0.7, 0.8]])
>>> x = np.array([0.0, 1.0])
>>> score = mm_corrected_improvement(x, X_base, q=2, p=2)
>>> print(score)mm_improvement
sampling.mm.mm_improvement(
x,
X_base,
phi_base=None,
J_base=None,
d_base=None,
q=2,
p=2,
normalize_flag=False,
verbose=False,
exponential=True,
)Calculates the Morris-Mitchell improvement for a candidate point x.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| x | np.ndarray | Candidate point (1D array). | required |
| X_base | np.ndarray | Existing design points. | required |
| J_base | np.ndarray | Multiplicities of distances for X_base. | None |
| d_base | np.ndarray | Unique distances for X_base. | None |
| q | int | Number of nearest neighbors for MM metric. | 2 |
| p | int | Power for MM metric. | 2 |
| normalize_flag | bool | If True, normalizes the X array and candidate point before computing distances. Defaults to False. | False |
| exponential | bool | If True, the exponential is applied. | True |
Returns
| Name | Type | Description |
|---|---|---|
| float | float | Morris-Mitchell improvement. |
Examples
>>> import numpy as np
>>> from spotoptim.sampling.mm import mm_improvement
>>> X_base = np.array([[0.1, 0.2], [0.4, 0.5], [0.7, 0.8]])
>>> x = np.array([0.5, 0.5])
>>> improvement = mm_improvement(x, X_base, q=2, p=2)
>>> print(improvement)
0.123456789mm_improvement_contour
sampling.mm.mm_improvement_contour(
X_base,
x1=np.linspace(0, 1, 100),
x2=np.linspace(0, 1, 100),
q=2,
p=2,
)Generates a contour plot of the Morris-Mitchell improvement over a grid defined by x1 and x2.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| X_base | np.ndarray | Base design points. | required |
| x1 | np.ndarray | Grid values for the first dimension. Default is np.linspace(0, 1, 100). | np.linspace(0, 1, 100) |
| x2 | np.ndarray | Grid values for the second dimension. Default is np.linspace(0, 1, 100). | np.linspace(0, 1, 100) |
| q | int | Morris-Mitchell metric parameter. Default is 2. | 2 |
| p | int | Morris-Mitchell metric parameter. Default is 2. | 2 |
Returns: None: Displays a contour plot of the Morris-Mitchell improvement.
Examples
>>> import numpy as np
from spotoptim.sampling.mm import mm_improvement_contour
X_base = np.array([[0.1, 0.1], [0.2, 0.2], [0.7, 0.7]])
mm_improvement_contour(X_base)mmlhs
sampling.mm.mmlhs(X_start, population, iterations, q=2.0, plot=False)Performs an evolutionary search (using perturbations) to find a Morris-Mitchell optimal Latin hypercube, starting from an initial plan X_start.
This function does the following
- Initializes a “best” Latin hypercube (X_best) from the provided X_start.
- Iteratively perturbs X_best to create offspring.
- Evaluates the space-fillingness of each offspring via the Morris-Mitchell metric (using mmphi).
- Updates the best plan whenever a better offspring is found.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| X_start | np.ndarray | A 2D array of shape (n, k) providing the initial Latin hypercube (n points in k dimensions). | required |
| population | int | Number of offspring to create in each generation. | required |
| iterations | int | Total number of generations to run the evolutionary search. | required |
| q | float | The exponent used by the Morris-Mitchell space-filling criterion. Defaults to 2.0. | 2.0 |
| plot | bool | If True, a simple scatter plot of the first two dimensions will be displayed at each iteration. Only if k >= 2. Defaults to False. | False |
Returns
| Name | Type | Description |
|---|---|---|
| np.ndarray | np.ndarray: A 2D array representing the most space-filling Latin hypercube found after all iterations, of the same shape as X_start. |
Notes
Many thanks to the original author of this code, A Sobester, for providing the original Matlab code under the GNU Licence. Original Matlab Code: Copyright 2007 A Sobester: “This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU General Public License and GNU Lesser General Public License along with this program. If not, see http://www.gnu.org/licenses/.”
Examples
>>> import numpy as np
>>> from spotoptim.sampling.mm import mmlhs
>>> # Suppose we have an initial 4x2 plan
>>> X_start = np.array([
... [0, 0],
... [1, 3],
... [2, 1],
... [3, 2]
... ])
>>> # Search for a more space-filling plan
>>> X_opt = mmlhs(X_start, population=5, iterations=10, q=2)
>>> print("Optimized plan:")
>>> print(X_opt)mmphi
sampling.mm.mmphi(X, q=2.0, p=1.0, verbosity=0)Calculates the Morris-Mitchell sampling plan quality criterion.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| X | np.ndarray | A 2D array representing the sampling plan, where each row is a point in d-dimensional space (shape: (n, d)). | required |
| q | float | Exponent used in the computation of the metric. Defaults to 2.0. | 2.0 |
| p | float | The distance norm to use. For example, p=1 is Manhattan (L1), p=2 is Euclidean (L2). Defaults to 1.0. | 1.0 |
| verbosity | int | If set to 1, prints additional information about the computation. Defaults to 0 (no additional output). | 0 |
Returns
| Name | Type | Description |
|---|---|---|
| float | float | The space-fillingness metric Phiq. Larger values typically indicate a more space-filling plan according to the Morris-Mitchell criterion. |
Notes
Many thanks to the original author of this code, A Sobester, for providing the original Matlab code under the GNU Licence. Original Matlab Code: Copyright 2007 A Sobester: “This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU General Public License and GNU Lesser General Public License along with this program. If not, see http://www.gnu.org/licenses/.”
Examples
>>> import numpy as np
>>> from spotoptim.sampling.mm import mmphi
>>> # Simple 3-point sampling plan in 2D
>>> X = np.array([
... [0.0, 0.0],
... [0.5, 0.5],
... [1.0, 1.0]
... ])
>>> # Calculate the space-fillingness metric with q=2, using Euclidean distances (p=2)
>>> quality = mmphi(X, q=2, p=2)
>>> print(quality)
# This value indicates how well points are spread out, with smaller being better.mmphi_corrected
sampling.mm.mmphi_corrected(X, q=2.0, p=2.0, normalize_flag=False)Calculates the corrected, dimension-aware Morris-Mitchell criterion.
This criterion is size-invariant by normalizing the standard Morris-Mitchell criterion by :math:n^{1+q/k}, where n is the number of design points and k is the dimension of the design space. It is defined as:
.. math::
\hat{\Phi}_q(P)
= \left(\frac{\sum_{j=1}^{m} J_j\, d_j^{-q}}{n^{1+q/k}}\right)^{1/q}
= \frac{\Phi_q(P)}{n^{1/q+1/k}}
Unlike the standard criterion :math:\Phi_q (which always increases with n) and the intensive criterion :math:\Phi_q^{*} (which increases for :math:q > k), :math:\hat{\Phi}_q is asymptotically size-invariant and decreases when an optimally placed point is added for sufficiently large n.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| X | np.ndarray | A 2D array representing the sampling plan (shape: (n, k)). | required |
| q | float | The exponent used in the computation of the metric. Defaults to 2.0. | 2.0 |
| p | float | The distance norm to use (e.g., p=1 for Manhattan, p=2 for Euclidean). Defaults to 2.0. | 2.0 |
| normalize_flag | bool | If True, normalizes the X array to [0, 1] before computing distances. Defaults to False. | False |
Returns
| Name | Type | Description |
|---|---|---|
| tuple[float, np.ndarray, np.ndarray] | tuple[float, np.ndarray, np.ndarray]: A tuple containing: - corrected_phiq (float): The corrected space-fillingness metric. Smaller values indicate better (more space-filling) designs. Returns np.inf for degenerate inputs (fewer than 2 unique points). - J (np.ndarray): Multiplicities of the unique distances. - d (np.ndarray): Unique pairwise distances. |
Examples
>>> import numpy as np
>>> from spotoptim.sampling.mm import mmphi_corrected
>>> # Simple 3-point sampling plan in 2D
>>> X = np.array([
... [0.0, 0.0],
... [0.5, 0.5],
... [1.0, 1.0]
... ])
>>> phi_hat, J, d = mmphi_corrected(X, q=2, p=2)
>>> print(phi_hat)mmphi_corrected_update
sampling.mm.mmphi_corrected_update(
X,
new_point,
J,
d,
q=2.0,
p=2.0,
normalize_flag=False,
)Updates the corrected Morris-Mitchell criterion after adding one point.
Incrementally computes :math:\hat{\Phi}_q for the design :math:P \cup \{x_{n+1}\} using the cached distances J and d from the existing n-point design P. Only the n new distances between new_point and each existing point need to be computed, making this more efficient than calling :func:mmphi_corrected from scratch.
The corrected criterion for the updated :math:n+1 point design is:
.. math::
\hat{\Phi}_q(P \cup \{x_{n+1}\})
= \left(\frac{\sum_{j} J_j^{\prime}\, d_j^{\prime\,-q}}{
(n+1)^{1+q/k}}\right)^{1/q}
where :math:J^{\prime} and :math:d^{\prime} are the updated multiplicities and distances, :math:n+1 is the new design size, and :math:k is the dimension of the design space.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| X | np.ndarray | Existing sampling plan of shape (n, k). |
required |
| new_point | np.ndarray | New point to add, shape (k,). |
required |
| J | np.ndarray | Multiplicity array for the existing n-point design, as returned by :func:mmphi_corrected. |
required |
| d | np.ndarray | Unique-distance array for the existing n-point design, as returned by :func:mmphi_corrected. |
required |
| q | float | Exponent used in the Morris-Mitchell metric. Defaults to 2.0. | 2.0 |
| p | float | Distance norm (e.g., p=1 Manhattan, p=2 Euclidean). Defaults to 2.0. | 2.0 |
| normalize_flag | bool | If True, normalizes X and new_point to [0, 1] before computing distances. Defaults to False. |
False |
Returns
| Name | Type | Description |
|---|---|---|
| tuple[float, np.ndarray, np.ndarray] | tuple[float, np.ndarray, np.ndarray]: A tuple containing: - corrected_phiq (float): Updated corrected criterion value for the n+1 point design. - updated_J (np.ndarray): Updated multiplicities array. - updated_d (np.ndarray): Updated unique-distance array. |
Examples
>>> import numpy as np
>>> from spotoptim.sampling.mm import mmphi_corrected, mmphi_corrected_update
>>> X = np.array([[0.0, 0.0], [0.5, 0.5], [1.0, 1.0]])
>>> phi_hat, J, d = mmphi_corrected(X, q=2, p=2)
>>> new_point = np.array([0.25, 0.75])
>>> updated_phi, updated_J, updated_d = mmphi_corrected_update(
... X, new_point, J, d, q=2, p=2
... )
>>> print(updated_phi)mmphi_intensive
sampling.mm.mmphi_intensive(X, q=2.0, p=2.0, normalize_flag=False)Calculates a size-invariant Morris-Mitchell criterion.
This “intensive” version of the criterion allows for the comparison of sampling plans with different sample sizes by normalizing for the number of point pairs. A smaller value indicates a better (more space-filling) design.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| X | np.ndarray | A 2D array representing the sampling plan (shape: (n, d)). | required |
| q | float | The exponent used in the computation of the metric. Defaults to 2.0. | 2.0 |
| p | float | The distance norm to use (e.g., p=1 for Manhattan, p=2 for Euclidean). Defaults to 2.0. | 2.0 |
| normalize_flag | bool | If True, normalizes the X array before computing distances. Defaults to False. | False |
Returns
| Name | Type | Description |
|---|---|---|
| tuple[float, np.ndarray, np.ndarray] | tuple[float, np.ndarray, np.ndarray]: A tuple containing: - intensive_phiq: The intensive space-fillingness metric. - J: Multiplicities of distances. - d: Unique distances. |
Examples
>>> import numpy as np
>>> from spotoptim.sampling.mm import mmphi_intensive
>>> # Create a simple 3-point sampling plan in 2D
>>> X = np.array([
... [0.0, 0.0],
... [0.5, 0.5],
... [1.0, 1.0]
... ])
>>> # Calculate the intensive space-fillingness metric with q=2, using Euclidean distances (p=2)
>>> quality, J, d = mmphi_intensive(X, q=2, p=2)
>>> print(quality)mmphi_intensive_update
sampling.mm.mmphi_intensive_update(
X,
new_point,
J,
d,
q=2.0,
p=2.0,
normalize_flag=False,
)Updates the Morris-Mitchell intensive criterion for n+1 points by adding a new point to the design. This should be more efficient than recalculating the metric from scratch, because it only needs to compute the distances between the new point and the existing points.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| X | np.ndarray | Existing sampling plan (shape: (n, d)). | required |
| new_point | np.ndarray | New point to add (shape: (d,)). | required |
| J | np.ndarray | Multiplicities of distances for the existing design. | required |
| d | np.ndarray | Unique distances for the existing design. | required |
| q | float | Exponent used in the computation of the Morris-Mitchell metric. Defaults to 2.0. | 2.0 |
| p | float | Distance norm to use (e.g., p=1 for Manhattan, p=2 for Euclidean). Defaults to 2.0. | 2.0 |
| normalize_flag | bool | If True, normalizes the X array and the new_point before computing distances. Defaults to False. | False |
Returns
| Name | Type | Description |
|---|---|---|
| tuple[float, np.ndarray, np.ndarray] | tuple[float, np.ndarray, np.ndarray]: Updated intensive_phiq, updated_J, updated_d. |
Examples
>>> import numpy as np
>>> from spotoptim.sampling.mm import mmphi_intensive_update
>>> # Existing design with 3 points in 2D
>>> X = np.array([[0.0, 0.0], [0.5, 0.5], [1.0, 1.0]])
>>> phiq, J, d = mmphi_intensive(X, q=2, p=2)
>>> # New point to add
>>> new_point = np.array([0.1, 0.1])
>>> # Update the intensive criterion
>>> updated_phiq, updated_J, updated_d = mmphi_intensive_update(X, new_point, J, d, q=2, p=2)mmsort
sampling.mm.mmsort(X3D, p=1.0)Ranks multiple sampling plans stored in a 3D array according to the Morris-Mitchell criterion, using a simple bubble sort.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| X3D | np.ndarray | A 3D NumPy array of shape (n, d, m), where m is the number of sampling plans, and each plan is an (n, d) matrix of points. | required |
| p | float | The distance metric to use. p=1 for Manhattan (L1), p=2 for Euclidean (L2). Defaults to 1.0. | 1.0 |
Returns
| Name | Type | Description |
|---|---|---|
| np.ndarray | np.ndarray: A 1D integer array of length m that holds the plan indices in ascending order of space-filling quality. The first index in the returned array corresponds to the most space-filling plan. |
Notes
Many thanks to the original author of this code, A Sobester, for providing the original Matlab code under the GNU Licence. Original Matlab Code: Copyright 2007 A Sobester: “This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU General Public License and GNU Lesser General Public License along with this program. If not, see http://www.gnu.org/licenses/.”
Examples
>>> import numpy as np
>>> from spotoptim.sampling.mm import mmsort
>>> # Suppose we have two 3-point sampling plans in 2D, stored in X3D:
>>> X1 = np.array([[0.0, 0.0],
... [0.5, 0.5],
... [1.0, 1.0]])
>>> X2 = np.array([[0.2, 0.2],
... [0.6, 0.4],
... [0.9, 0.9]])
>>> # Stack them along the third dimension: shape will be (3, 2, 2)
>>> X3D = np.stack([X1, X2], axis=2)
>>> # Sort them using the Morris-Mitchell criterion with p=2
>>> ranking = mmsort(X3D, p=2.0)
>>> print(ranking)
# It might print [2 1] or [1 2], depending on which plan is more space-filling.perturb
sampling.mm.perturb(X, PertNum=1)Performs a specified number of random element swaps on a sampling plan. If the plan is a Latin hypercube, the result remains a valid Latin hypercube.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| X | np.ndarray | A 2D array (sampling plan) of shape (n, k), where each row is a point and each column is a dimension. | required |
| PertNum | int | The number of element swaps (perturbations) to perform. Defaults to 1. | 1 |
Returns
| Name | Type | Description |
|---|---|---|
| np.ndarray | np.ndarray: The perturbed sampling plan, identical in shape to the input, with one or more random column swaps executed. |
Notes
Many thanks to the original author of this code, A Sobester, for providing the original Matlab code under the GNU Licence. Original Matlab Code: Copyright 2007 A Sobester: “This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU General Public License and GNU Lesser General Public License along with this program. If not, see http://www.gnu.org/licenses/.”
Examples
>>> import numpy as np
>>> from spotoptim.sampling.mm import perturb
>>> # Create a simple 4x2 sampling plan
>>> X_original = np.array([
... [1, 3],
... [2, 4],
... [3, 1],
... [4, 2]
... ])
>>> # Perturb it once
>>> X_perturbed = perturb(X_original, PertNum=1)
>>> print(X_perturbed)
# The output may differ due to random swaps, but each column is still a permutation of [1,2,3,4].
[[1 3]
[2 2]
[3 1]
[4 4]]phisort
sampling.mm.phisort(X3D, q=2.0, p=1.0)Ranks multiple sampling plans stored in a 3D array by the Morris-Mitchell numerical quality metric (mmphi). Uses a simple bubble-sort: sampling plans with smaller mmphi values are placed first in the index array.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| X3D | np.ndarray | A 3D array of shape (n, d, m), where m is the number of sampling plans. | required |
| q | float | Exponent for the mmphi metric. Defaults to 2.0. | 2.0 |
| p | float | Distance norm for mmphi. p=1 is Manhattan; p=2 is Euclidean. Defaults to 1.0. | 1.0 |
Returns
| Name | Type | Description |
|---|---|---|
| np.ndarray | np.ndarray: A 1D integer array of length m, giving the plan indices in ascending order of mmphi. The first index in the returned array corresponds to the numerically lowest mmphi value. |
Notes
Many thanks to the original author of this code, A Sobester, for providing the original Matlab code under the GNU Licence. Original Matlab Code: Copyright 2007 A Sobester: “This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU General Public License and GNU Lesser General Public License along with this program. If not, see http://www.gnu.org/licenses/.”
Examples
>>> import numpy as np
from spotoptim.sampling.mm import phisort
from spotoptim.sampling.mm import bestlh
X1 = bestlh(n=5, k=2, population=5, iterations=10)
X2 = bestlh(n=5, k=2, population=15, iterations=20)
X3 = bestlh(n=5, k=2, population=25, iterations=30)
# Map X1 and X2 so that X3D has the two sampling plans in X3D[:, :, 0] and X3D[:, :, 1]
X3D = np.array([X1, X2])
print(phisort(X3D))
X3D = np.array([X3, X2])
print(phisort(X3D))
[2 1]
[1 2]plot_mmphi_corrected_vs_n_lhs
sampling.mm.plot_mmphi_corrected_vs_n_lhs(
k_dim,
seed,
n_min=10,
n_max=100,
n_step=5,
q_phi=2.0,
p_phi=2.0,
)Generate LHS designs for varying n and plot the Corrected Morris-Mitchell Criterion against the standard criterion.
For each sample size n in range(n_min, n_max + 1, n_step) a fresh Latin Hypercube design is drawn and both the intensive criterion hat_Phi_q^I (normalized by M = n(n-1)/2) and the corrected criterion hat_Phi_q (normalized by n^{1+q/k}) are computed. The two series are displayed on a shared x-axis with independent y-axes so their trends can be compared directly.
The corrected criterion is asymptotically size-invariant: for large n its expected value stabilizes at a finite constant that depends only on the spatial distribution of the design, not on n itself. This plot makes that convergence behaviour visible.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| k_dim | int | Number of dimensions for the LHS design. | required |
| seed | int | Random seed for reproducibility. | required |
| n_min | int | Minimum number of samples. Defaults to 10. | 10 |
| n_max | int | Maximum number of samples. Defaults to 100. | 100 |
| n_step | int | Step size for increasing n. Defaults to 5. | 5 |
| q_phi | float | Exponent q for the Morris-Mitchell criteria. Defaults to 2.0. | 2.0 |
| p_phi | float | Distance norm p for the Morris-Mitchell criteria. Defaults to 2.0. | 2.0 |
Returns
| Name | Type | Description |
|---|---|---|
| None | None | Displays a dual-axis plot of mmphi_intensive and |
| None | mmphi_corrected vs. number of samples (n). |
Examples
>>> from spotoptim.sampling.mm import plot_mmphi_corrected_vs_n_lhs
>>> plot_mmphi_corrected_vs_n_lhs(k_dim=3, seed=42, n_min=10, n_max=50, n_step=5, q_phi=2.0, p_phi=2.0)plot_mmphi_corrected_vs_points
sampling.mm.plot_mmphi_corrected_vs_points(
X_base,
x_min,
x_max,
p_min=10,
p_max=100,
p_step=10,
n_repeats=5,
q=2.0,
p_norm=2.0,
figsize=(10, 6),
)Plot the Corrected Morris-Mitchell Criterion versus the number of added points.
For each point count in range(p_min, p_max + 1, p_step) a set of uniformly random points is appended to X_base and the corrected criterion hat_Phi_q is evaluated on the extended design. This is repeated n_repeats times per point count to capture stochastic variability. The resulting mean ± std curve is compared to the baseline criterion of X_base alone.
Unlike plot_mmphi_vs_points, which uses the intensive criterion (normalized by M = n(n-1)/2), this function uses the corrected criterion (normalized by n^{1+q/k}), which is asymptotically size-invariant and therefore provides a more reliable comparison across designs of different sizes.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| X_base | np.ndarray | Base design matrix of shape (n, k). |
required |
| x_min | np.ndarray | Lower bounds for each dimension, shape (k,). |
required |
| x_max | np.ndarray | Upper bounds for each dimension, shape (k,). |
required |
| p_min | int | Minimum number of points to add. Defaults to 10. | 10 |
| p_max | int | Maximum number of points to add. Defaults to 100. | 100 |
| p_step | int | Step size for the number of added points. Defaults to 10. | 10 |
| n_repeats | int | Number of repetitions per point count. Defaults to 5. | 5 |
| q | float | Exponent q for the corrected Morris-Mitchell criterion. Defaults to 2.0. | 2.0 |
| p_norm | float | Distance norm p. Defaults to 2.0. | 2.0 |
| figsize | tuple | Size of the plot (width, height). Defaults to (10, 6). | (10, 6) |
Returns
| Name | Type | Description |
|---|---|---|
| pd.DataFrame | pd.DataFrame: Summary DataFrame with columns n_points, |
|
| pd.DataFrame | (mmphi_corrected, mean), and (mmphi_corrected, std) for each |
|
| pd.DataFrame | number of added points. |
Examples
>>> import numpy as np
>>> from spotoptim.sampling.mm import plot_mmphi_corrected_vs_points
>>> X_base = np.array([[0.1, 0.2], [0.4, 0.5], [0.7, 0.8]])
>>> x_min = np.array([0.0, 0.0])
>>> x_max = np.array([1.0, 1.0])
>>> df_summary = plot_mmphi_corrected_vs_points(X_base, x_min, x_max, p_min=10, p_max=50, p_step=10, n_repeats=3)plot_mmphi_vs_n_lhs
sampling.mm.plot_mmphi_vs_n_lhs(
k_dim,
seed,
n_min=10,
n_max=100,
n_step=5,
q_phi=2.0,
p_phi=2.0,
)Generates LHS designs for varying n, calculates mmphi and mmphi_intensive, and plots them against the number of samples (n).
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| k_dim | int | Number of dimensions for the LHS design. | required |
| seed | int | Random seed for reproducibility. | required |
| n_min | int | Minimum number of samples. | 10 |
| n_max | int | Maximum number of samples. | 100 |
| n_step | int | Step size for increasing n. | 5 |
| q_phi | float | Exponent q for the Morris-Mitchell criteria. | 2.0 |
| p_phi | float | Distance norm p for the Morris-Mitchell criteria. | 2.0 |
Returns
| Name | Type | Description |
|---|---|---|
| None | None | Displays a plot of mmphi and mmphi_intensive vs. number of samples (n). |
Examples
>>> from spotoptim.sampling.mm import plot_mmphi_vs_n_lhs
>>> plot_mmphi_vs_n_lhs(k_dim=3, seed=42, n_min=10, n_max=50, n_step=5, q_phi=2.0, p_phi=2.0)plot_mmphi_vs_points
sampling.mm.plot_mmphi_vs_points(
X_base,
x_min,
x_max,
p_min=10,
p_max=100,
p_step=10,
n_repeats=5,
figsize=(10, 6),
)Plot the Morris-Mitchell criterion versus the number of added points.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| X_base | np.ndarray | Base design matrix | required |
| x_min | np.ndarray | Lower bounds for variables | required |
| x_max | np.ndarray | Upper bounds for variables | required |
| p_min | int | Minimum number of points to add | 10 |
| p_max | int | Maximum number of points to add | 100 |
| p_step | int | Step size for number of points | 10 |
| n_repeats | int | Number of repetitions for each point count | 5 |
| figsize | tuple | Size of the plot (width, height) | (10, 6) |
Returns
| Name | Type | Description |
|---|---|---|
| pd.DataFrame | pd.DataFrame: Summary DataFrame with mean and std of mmphi for each number of added points. |
Examples
>>> import numpy as np
>>> from spotoptim.sampling.mm import plot_mmphi_vs_points
>>> # Define base design
>>> X_base = np.array([[0.1, 0.2], [0.4, 0.5], [0.7, 0.8]])
>>> # Define variable bounds
>>> x_min = np.array([0.0, 0.0])
>>> x_max = np.array([1.0, 1.0])
>>> # Plot mmphi vs number of added points
>>> df_summary = plot_mmphi_vs_points(X_base, x_min, x_max, p_min=10, p_max=50, p_step=10, n_repeats=3)propose_mmphi_corrected_minimizing_point
sampling.mm.propose_mmphi_corrected_minimizing_point(
X,
n_candidates=1000,
q=2.0,
p=2.0,
seed=None,
lower=None,
upper=None,
normalize_flag=False,
)Proposes a new point that minimizes the corrected Morris-Mitchell criterion.
Samples n_candidates points uniformly at random within [lower, upper] and returns the one whose addition to X yields the lowest value of the corrected criterion :math:\hat{\Phi}_q (see :func:mmphi_corrected).
Internally the function pre-computes the base distance cache (J, d) for X once and then evaluates each candidate via :func:mmphi_corrected_update, which only needs to compute the n new distances between the candidate and the existing points rather than all :math:\binom{n+1}{2} pairwise distances. This makes the search :math:O(n) per candidate instead of :math:O(n^2).
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| X | np.ndarray | Existing design points, shape (n_points, n_dim). |
required |
| n_candidates | int | Number of random candidate points to evaluate. Defaults to 1000. | 1000 |
| q | float | Exponent for the corrected Morris-Mitchell criterion. Defaults to 2.0. | 2.0 |
| p | float | Distance norm (e.g., p=1 Manhattan, p=2 Euclidean). Defaults to 2.0. | 2.0 |
| seed | int | Random seed for reproducibility. Defaults to None. | None |
| lower | np.ndarray | Lower bounds for each dimension, shape (n_dim,). Defaults to np.zeros(n_dim). |
None |
| upper | np.ndarray | Upper bounds for each dimension, shape (n_dim,). Defaults to np.ones(n_dim). |
None |
| normalize_flag | bool | If True, normalizes X and all candidate points to [0, 1] before computing distances. Defaults to False. |
False |
Returns
| Name | Type | Description |
|---|---|---|
| np.ndarray | np.ndarray: The best candidate point found, shape (1, n_dim). |
Raises
| Name | Type | Description |
|---|---|---|
| ValueError | If any element of lower is greater than or equal to the corresponding element of upper. |
Examples
>>> import numpy as np
>>> from spotoptim.sampling.mm import propose_mmphi_corrected_minimizing_point
>>> X = np.array([[0.1, 0.2], [0.5, 0.5], [0.9, 0.8]])
>>> new_point = propose_mmphi_corrected_minimizing_point(
... X, n_candidates=200, q=2, p=2, seed=0
... )
>>> print(new_point.shape)
(1, 2)propose_mmphi_intensive_minimizing_point
sampling.mm.propose_mmphi_intensive_minimizing_point(
X,
n_candidates=1000,
q=2.0,
p=2.0,
seed=None,
lower=None,
upper=None,
normalize_flag=False,
)Propose a new point that, when added to X, minimizes the intensive Morris-Mitchell (mmphi_intensive) criterion.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| X | np.ndarray | Existing points, shape (n_points, n_dim). | required |
| n_candidates | int | Number of random candidates to sample. | 1000 |
| q | float | Exponent for mmphi_intensive. | 2.0 |
| p | float | Distance norm for mmphi_intensive. | 2.0 |
| seed | int | Random seed. | None |
| lower | np.ndarray | Lower bounds for each dimension (default: 0). | None |
| upper | np.ndarray | Upper bounds for each dimension (default: 1). | None |
| normalize_flag | bool | If True, normalizes the X array and candidate points before computing distances. Defaults to False. | False |
Returns
| Name | Type | Description |
|---|---|---|
| np.ndarray | np.ndarray: Proposed new point, shape (1, n_dim). |
Examples
>>> import numpy as np
from spotoptim.sampling.mm import propose_mmphi_intensive_minimizing_point
# Existing design with 3 points in 2D
X = np.array([[1.0, 0.0], [0.5, 0.5], [1.0, 1.0]])
# Propose a new point
new_point = propose_mmphi_intensive_minimizing_point(X, n_candidates=500, q=2, p=2, seed=42)
print(new_point)
# plot the existing points and the new proposed point
import matplotlib.pyplot as plt
plt.scatter(X[:, 0], X[:, 1], color='blue', label='Existing Points')
plt.scatter(new_point[0, 0], new_point[0, 1], color='red', label='Proposed Point')
plt.legend()
# add grid and labels
plt.grid()
plt.title('MM-PHI Proposed Point')
plt.xlabel('X1')
plt.ylabel('X2')
plt.show()subset
sampling.mm.subset(X, ns)Returns a space-filling subset of a given size from a sampling plan, along with the remainder. It repeatedly attempts to substitute each point in the subset with a point from the remainder if doing so improves the Morris-Mitchell metric.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| X | np.ndarray | A 2D array representing the original sampling plan, of shape (n, d). | required |
| ns | int | The size of the desired subset. | required |
Returns
| Name | Type | Description |
|---|---|---|
| (np.ndarray, np.ndarray) | A tuple (Xs, Xr) where: - Xs is the chosen subset of size ns, with space-filling properties. - Xr is the remainder (X Xs). |
Notes
Many thanks to the original author of this code, A Sobester, for providing the original Matlab code under the GNU Licence. Original Matlab Code: Copyright 2007 A Sobester: “This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU General Public License and GNU Lesser General Public License along with this program. If not, see http://www.gnu.org/licenses/.”
Examples
>>> from spotoptim.sampling.mm import subset, bestlh
X = bestlh(n=5, k=3, population=5, iterations=10)
Xs, Xr = subset(X, ns=2)
print(Xs)
print(Xr)
[[0.25 0. 0.5 ]
[0.5 0.75 0. ]]
[[1. 0.25 0.25]
[0. 1. 0.75]
[0.75 0.5 1. ]]