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.123456789

mm_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

  1. Initializes a “best” Latin hypercube (X_best) from the provided X_start.
  2. Iteratively perturbs X_best to create offspring.
  3. Evaluates the space-fillingness of each offspring via the Morris-Mitchell metric (using mmphi).
  4. 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.  ]]