diff --git a/demos/lattice_kronecker_methods.ipynb b/demos/lattice_kronecker_methods.ipynb new file mode 100644 index 000000000..1d11ccc52 --- /dev/null +++ b/demos/lattice_kronecker_methods.ipynb @@ -0,0 +1,270 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "492a51ed", + "metadata": {}, + "source": [ + "# Lattice and Kronecker Methods" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "2e06ea48", + "metadata": {}, + "outputs": [], + "source": [ + "from qmcpy import *\n", + "import numpy as np\n", + "from time import time\n", + "from matplotlib import pyplot" + ] + }, + { + "cell_type": "markdown", + "id": "39b265e4", + "metadata": {}, + "source": [ + "## Discrepancy Values" + ] + }, + { + "cell_type": "markdown", + "id": "2c1e69d8", + "metadata": {}, + "source": [ + "#### Lattice" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "958e16e1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "42.48726056679934\n" + ] + } + ], + "source": [ + "dim = 20\n", + "n = 2**15\n", + "lat = Lattice(dimension=dim, order=\"RADICAL_INVERSE\", seed=12) # initialize a lattice as usual\n", + "coord_weights = np.array([j**(-2) for j in range(1, dim + 1)]) # define some coordinate weights\n", + "\n", + "lat_discs = lat.expected_squared_periodic_discrepancies(n_max=n, coord_weights=coord_weights) # compute the expected squared periodic discrepancies for n = 1, 2, ...\n", + "\n", + "sample_weights = np.arange(1, n+1) # define some sample weights\n", + "lat_wssd = lat.wssd(n_max=n, coord_weights=coord_weights, sample_weights=sample_weights) # compute the wssd\n", + "print(lat_wssd)" + ] + }, + { + "cell_type": "markdown", + "id": "f174209f", + "metadata": {}, + "source": [ + "#### Kronecker" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "c8eec507", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[34.65168403]\n" + ] + } + ], + "source": [ + "dim = 20\n", + "n = 2**15\n", + "coord_weights = np.array([j**(-2) for j in range(1, dim + 1)])\n", + "\n", + "kron = Kronecker(dimension=dim, seed=12, generating_vector=\"CBC_MT\") # initialize a Kronecker sequence as usual\n", + "kron_k_tilde = (lambda x, gamma: np.prod(1 + (x * (x - 1) + 1/6) * gamma, axis=-1), 1) # define the kernel function (in this case, the second Bernoulli polynomial)\n", + "\n", + "kron_discs = kron._square_periodic_discrepancies(n = n, k_tilde = kron_k_tilde, gamma = coord_weights).reshape(-1) # compute the expected squared periodic discrepancies for n = 1, 2, ...\n", + "\n", + "sample_weights = np.arange(1, n+1) # define some sample weights\n", + "kron_wssd = kron.wssd_discrepancy(n = n, sample_weights = sample_weights, k_tilde = kron_k_tilde, gamma = coord_weights) # compute the wssd\n", + "print(kron_wssd)" + ] + }, + { + "cell_type": "markdown", + "id": "f1c20186", + "metadata": {}, + "source": [ + "## Searches" + ] + }, + { + "cell_type": "markdown", + "id": "01664bbb", + "metadata": {}, + "source": [ + "#### Lattice" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "524e3b99", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Time taken for lattice vector wssd search: 0.1818382740020752\n", + "Searched lattice vector: [ 1 4825 13541 15249 15405 9909 7493 11407 14819 10089 3683 3347\n", + " 13789 8837 5309 6307 6447 12103 9097 2767]\n" + ] + } + ], + "source": [ + "# note that the search method requires that the sample weights be w_n = n, so they are not customizable\n", + "n = 2**15\n", + "dim = 20\n", + "coord_weights = np.array([j**(-2) for j in range(1, dim + 1)])\n", + "\n", + "bernoulli2 = lambda x: x * (x - 1) + 1 / 6\n", + "\n", + "time_start = time()\n", + "searched_lattice_vector = lattice_vector_wssd_search(n_max=n, d_max=dim, kernel=bernoulli2, coord_weights=coord_weights) # search for a lattice vector with low wssd\n", + "time_end = time()\n", + "print(\"Time taken for lattice vector wssd search: \", time_end - time_start)\n", + "print(\"Searched lattice vector: \", searched_lattice_vector)" + ] + }, + { + "cell_type": "markdown", + "id": "9efeb0fa", + "metadata": {}, + "source": [ + "#### Kronecker" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "09388fbc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Time taken for kronecker vector wssd search: 6.114294052124023\n", + "Searched Kronecker vector: [0.61803399 0.26774665 0.91444648 0.22708655 0.12137476 0.71267465\n", + " 0.69787961 0.10230792 0.18609503 0.31195642 0.41561801 0.13176115\n", + " 0.22004561 0.56882224 0.8920797 0.79690426 0.54748361 0.74175085\n", + " 0.42012299 0.41261152]\n" + ] + } + ], + "source": [ + "# note that the search method requires that the sample weights be w_n = n, so they are not customizable\n", + "n = 2**15\n", + "dim = 20\n", + "coord_weights = np.array([j**(-2) for j in range(1, dim + 1)])\n", + "searchsize = 20 # the time cost is O(dim * n * searchsize^2), so searchsize should be chosen with care. The largest search I have run was in MATLAB with searchsize = 300, N = 2^20, d = 100, which took about 24 hours \n", + "\n", + "time_start = time()\n", + "searched_kron_vector, wssd, new_kron_discs, _ = kronecker_vector_search_mobius_transform(n_max = n, d_max = dim, kernel = lambda x: x * (x - 1) + 1 / 6, searchsize = searchsize, coord_weights = coord_weights) # search for a Kronecker vector with low wssd\n", + "time_end = time()\n", + "\n", + "print(\"Time taken for kronecker vector wssd search: \", time_end - time_start)\n", + "print(\"Searched Kronecker vector: \", searched_kron_vector)" + ] + }, + { + "cell_type": "markdown", + "id": "26a634f7", + "metadata": {}, + "source": [ + "## Plotting" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "9f66d72b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0, 0.5, 'Periodic Discrepancy')" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "lat1 = Lattice(dimension=dim, order=\"RADICAL_INVERSE\", seed=12, generating_vector=\"kuo.lattice-39102-1024-1048576.3600.txt\", m_max=20)\n", + "lat_discs1 = lat1.expected_squared_periodic_discrepancies(n_max=n, coord_weights=coord_weights)\n", + "\n", + "lat2 = Lattice(dimension=dim, order=\"RADICAL_INVERSE\", seed=12, generating_vector=np.uint64(searched_lattice_vector), m_max=20)\n", + "lat_discs2 = lat2.expected_squared_periodic_discrepancies(n_max=n, coord_weights=coord_weights)\n", + "\n", + "# Note that the new lattice rule beats the Kuo lattice rule for the somewhat low sample sizes here, but they are comparable closer to n = 2^20.\n", + "\n", + "fig, ax = pyplot.subplots(nrows=1, ncols=1, figsize=(12,10))\n", + "ax.plot(np.arange(1, n+1), np.sqrt(lat_discs1), label=\"Kuo Lattice Discrepancy\")\n", + "ax.plot(np.arange(1, n+1), np.sqrt(lat_discs2), label=\"New Lattice Discrepancy\")\n", + "ax.plot(np.arange(1, n+1), np.sqrt(new_kron_discs), label=\"CBC_MT Kronecker (for N=2^15)\")\n", + "ax.plot(np.arange(1, n+1), np.sqrt(kron_discs), label=\"CBC_MT Kronecker (for N=2^20)\")\n", + "ax.set_xscale(\"log\")\n", + "ax.set_yscale(\"log\")\n", + "ax.legend()\n", + "ax.set_xlabel(\"Sample Size\")\n", + "ax.set_ylabel(\"Periodic Discrepancy\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "qmcpy", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/api/discrete_distributions.md b/docs/api/discrete_distributions.md index daa782cb6..1bb33923f 100644 --- a/docs/api/discrete_distributions.md +++ b/docs/api/discrete_distributions.md @@ -28,6 +28,10 @@ jupyter: ::: qmcpy.discrete_distribution.korobov.KorobovLattice +## `lattice_vector_wssd_search` + +::: qmcpy.discrete_distribution.lattice.lattice_vector_wssd_search.lattice_vector_wssd_search + ## `Halton` ::: qmcpy.discrete_distribution.digital_net_any_bases.halton.Halton @@ -52,6 +56,10 @@ jupyter: ::: qmcpy.discrete_distribution.latin_hypercube.LatinHypercube +## `kronecker_vector_search_mobius_transform` + +::: qmcpy.discrete_distribution.kronecker.kronecker_search_methods.kronecker_vector_search_mobius_transform + ## `DummySampler` ::: qmcpy.discrete_distribution.dummy_sampler.DummySampler diff --git a/pyproject.toml b/pyproject.toml index dac48d235..1e082e593 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -179,6 +179,7 @@ includes = [ "qmcpy/discrete_distribution/generating_params/korobov_p2_table.npz", "qmcpy/discrete_distribution/digital_net_b2/generating_matrices/*.npy", "qmcpy/discrete_distribution/lattice/generating_vectors/*.npy", + "qmcpy/discrete_distribution/kronecker/generating_vectors/*.txt", "qmcpy/util/qmcpy.mplstyle", ] excludes = [] diff --git a/qmcpy/discrete_distribution/__init__.py b/qmcpy/discrete_distribution/__init__.py index 3ee36327d..f95fe5931 100644 --- a/qmcpy/discrete_distribution/__init__.py +++ b/qmcpy/discrete_distribution/__init__.py @@ -1,10 +1,10 @@ from .abstract_discrete_distribution import AbstractDiscreteDistribution from .iid_std_uniform import IIDStdUniform -from .lattice import Lattice +from .lattice import Lattice, lattice_vector_wssd_search from .digital_net_b2 import DigitalNetB2 from .digital_net_any_bases import DigitalNetAnyBases,Halton,Faure,Hammersley from .mpmc import MPMC -from .kronecker import Kronecker +from .kronecker import Kronecker, kronecker_vector_search_mobius_transform from .korobov import KorobovLattice from .dummy_sampler import DummySampler from .latin_hypercube import LatinHypercube @@ -15,4 +15,3 @@ DigitalNet = DigitalNetB2 Net = DigitalNetB2 NetB2 = DigitalNetB2 - diff --git a/qmcpy/discrete_distribution/kronecker/__init__.py b/qmcpy/discrete_distribution/kronecker/__init__.py new file mode 100644 index 000000000..69aed4fdf --- /dev/null +++ b/qmcpy/discrete_distribution/kronecker/__init__.py @@ -0,0 +1,2 @@ +from .kronecker import Kronecker +from .kronecker_search_methods import kronecker_vector_search_mobius_transform \ No newline at end of file diff --git a/qmcpy/discrete_distribution/kronecker/generating_vectors/kron_vector_d-100_N-2exp20_2026_06_01.txt b/qmcpy/discrete_distribution/kronecker/generating_vectors/kron_vector_d-100_N-2exp20_2026_06_01.txt new file mode 100644 index 000000000..098e8d858 --- /dev/null +++ b/qmcpy/discrete_distribution/kronecker/generating_vectors/kron_vector_d-100_N-2exp20_2026_06_01.txt @@ -0,0 +1,100 @@ +0.618033988749895 +0.3173225474723 +0.59332263014446 +0.20776441643926 +0.27373719258623 +0.649734278361753 +0.478954018631769 +0.86866022435182 +0.22845082022244 +0.581365429377986 +0.282365231829842 +0.0822850909119904 +0.223849641007295 +0.5770772201756 +0.51769659336634 +0.568025390904592 +0.156782234569368 +0.82246227056154 +0.805675312097409 +0.63877102813393 +0.358300563495856 +0.241741343018598 +0.705003192174204 +0.1931911954956 +0.261022001488623 +0.897938992038015 +0.46839743115877 +0.884022067965329 +0.752352896871505 +0.1601583600427 +0.10727599509739 +0.151478435512877 +0.163863657127101 +0.948303450359399 +0.80350943597439 +0.426371623468333 +0.435930910910882 +0.21329852459791 +0.661698149534002 +0.900679822160453 +0.122436710671457 +0.483663584095611 +0.928181067731583 +0.443143014606576 +0.74491332336194 +0.87948409225588 +0.0428242449803 +0.534576896789579 +0.24340042100879 +0.30424418245585 +0.574003104342617 +0.897289023268963 +0.541424476559586 +0.356895660350464 +0.507567280910795 +0.513983550428507 +0.0610821922457415 +0.183871471606587 +0.446015178033969 +0.455684287415085 +0.280817534817491 +0.115220095666085 +0.433740673279323 +0.515605957977756 +0.113076735656464 +0.733928297688305 +0.0597515651584137 +0.422268695684775 +0.0979181139173599 +0.213699261322352 +0.866811679881922 +0.0878569329036737 +0.678412735893121 +0.181093969536107 +0.128913741473518 +0.109341703717108 +0.289067270578427 +0.352218331663839 +0.303605902333137 +0.0613899204730832 +0.959535877660851 +0.475508309069064 +0.688698902674194 +0.657037932118495 +0.645555897563869 +0.720658665263604 +0.914423387894897 +0.425763295044487 +0.328825255006553 +0.892452975558004 +0.16973367306396 +0.912292406867098 +0.0923260018966512 +0.216301713289429 +0.147861410064151 +0.8600781655845 +0.752129792595509 +0.337431120990153 +0.542476014178907 +0.307279789725491 diff --git a/qmcpy/discrete_distribution/kronecker.py b/qmcpy/discrete_distribution/kronecker/kronecker.py similarity index 85% rename from qmcpy/discrete_distribution/kronecker.py rename to qmcpy/discrete_distribution/kronecker/kronecker.py index 89311121a..0041269bf 100644 --- a/qmcpy/discrete_distribution/kronecker.py +++ b/qmcpy/discrete_distribution/kronecker/kronecker.py @@ -1,5 +1,5 @@ -from .abstract_discrete_distribution import AbstractLDDiscreteDistribution -from ..util import ParameterError +from ..abstract_discrete_distribution import AbstractLDDiscreteDistribution +from ...util import ParameterError import numpy as np import warnings @@ -243,6 +243,7 @@ def __init__(self, - `"CBC"`: uses the first $d$ components of a known good Component-by-Component (CBC) generating vector. - `"RICHTMYER"`: uses $\boldsymbol{\alpha}_j = \sqrt{p_j} \bmod 1$, where $p_j$ are primes. This is the classical Richtmyer construction. - `"SUZUKI"`: uses a deterministic construction $\boldsymbol{\alpha}_j = 2^{j/(d+1)}$. + - `"CBC_MT"`: uses the first $d$ components of a known good CBC generating vector obtained using the Mobius transformation method, which can be found in kronecker_search_methods.py. - np.array: user-specified generating vector. shift (np.ndarray): Shift vector $\boldsymbol{\delta}$. If `randomize=True`, this is ignored and a random shift is generated. Otherwise, a fixed shift is used. @@ -284,7 +285,118 @@ def __init__(self, gen_vec = _richtmyer_generating_vector(self.dvec.max()+1) elif isinstance(generating_vector, str) and generating_vector.lower() == "suzuki": self.gen_vec_source = "SUZUKI" - gen_vec = _suzuki_generating_vector(self.dvec.max()+1) + gen_vec = _suzuki_generating_vector(self.dvec.max()+1) + elif isinstance(generating_vector, str) and generating_vector.lower() == "cbc_mt": + self.gen_vec_source = "CBC_MT" + CBC_MT = np.array([0.618033988749895, + 0.3173225474723, + 0.59332263014446, + 0.20776441643926, + 0.27373719258623, + 0.649734278361753, + 0.478954018631769, + 0.86866022435182, + 0.22845082022244, + 0.581365429377986, + 0.282365231829842, + 0.0822850909119904, + 0.223849641007295, + 0.5770772201756, + 0.51769659336634, + 0.568025390904592, + 0.156782234569368, + 0.82246227056154, + 0.805675312097409, + 0.63877102813393, + 0.358300563495856, + 0.241741343018598, + 0.705003192174204, + 0.1931911954956, + 0.261022001488623, + 0.897938992038015, + 0.46839743115877, + 0.884022067965329, + 0.752352896871505, + 0.1601583600427, + 0.10727599509739, + 0.151478435512877, + 0.163863657127101, + 0.948303450359399, + 0.80350943597439, + 0.426371623468333, + 0.435930910910882, + 0.21329852459791, + 0.661698149534002, + 0.900679822160453, + 0.122436710671457, + 0.483663584095611, + 0.928181067731583, + 0.443143014606576, + 0.74491332336194, + 0.87948409225588, + 0.0428242449803, + 0.534576896789579, + 0.24340042100879, + 0.30424418245585, + 0.574003104342617, + 0.897289023268963, + 0.541424476559586, + 0.356895660350464, + 0.507567280910795, + 0.513983550428507, + 0.0610821922457415, + 0.183871471606587, + 0.446015178033969, + 0.455684287415085, + 0.280817534817491, + 0.115220095666085, + 0.433740673279323, + 0.515605957977756, + 0.113076735656464, + 0.733928297688305, + 0.0597515651584137, + 0.422268695684775, + 0.0979181139173599, + 0.213699261322352, + 0.866811679881922, + 0.0878569329036737, + 0.678412735893121, + 0.181093969536107, + 0.128913741473518, + 0.109341703717108, + 0.289067270578427, + 0.352218331663839, + 0.303605902333137, + 0.0613899204730832, + 0.959535877660851, + 0.475508309069064, + 0.688698902674194, + 0.657037932118495, + 0.645555897563869, + 0.720658665263604, + 0.914423387894897, + 0.425763295044487, + 0.328825255006553, + 0.892452975558004, + 0.16973367306396, + 0.912292406867098, + 0.0923260018966512, + 0.216301713289429, + 0.147861410064151, + 0.8600781655845, + 0.752129792595509, + 0.337431120990153, + 0.542476014178907, + 0.307279789725491], dtype=np.float64) + gen_vec = CBC_MT + if not (self.dvec.max() < len(gen_vec)): + if warn: + warnings.warn( + f"CBC_MT generating vector only supports dimension <= {len(CBC_MT)}; falling back to Richtmyer.", + RuntimeWarning, + ) + self.gen_vec_source = "RICHTMYER" + gen_vec = _richtmyer_generating_vector(self.dvec.max()+1) else: self.gen_vec_source = "CUSTOM" gen_vec = np.asarray(generating_vector, dtype=float) @@ -347,9 +459,9 @@ def periodic_discrepancy(self, n, k_tilde=None, gamma=None): k_tilde = (lambda x, gamma: np.prod(1 + (x * (x - 1) + 1/6) * gamma, axis=-1), 1) return np.sqrt(self._square_periodic_discrepancies(n, k_tilde, gamma)) + - - def wssd_discrepancy(self, n, weights, k_tilde = None, gamma = None): + def wssd_discrepancy(self, n, sample_weights, k_tilde = None, gamma = None): # calculates the weighted sum of square discrepancy if gamma is None: gamma = np.ones(self.d) @@ -358,12 +470,15 @@ def wssd_discrepancy(self, n, weights, k_tilde = None, gamma = None): k_tilde = (lambda x, gamma: np.prod(1 + (x * (x - 1) + 1/6) * gamma, axis=-1), 1) discrepancies = self._square_periodic_discrepancies(n, k_tilde, gamma) - return np.sum(weights * discrepancies, axis=-1) - + return np.sum(sample_weights * discrepancies, axis=-1) + def _square_periodic_discrepancies(self, n, k_tilde, gamma): n_array = np.arange(1, n + 1) - k_tilde_terms = k_tilde[0](self.gen_samples(n=n), gamma) + # we need the points without a random shift for the calculation, so we can't use self._gen_samples + i = np.arange(0, n) + points = (i[:,None] * self.gen_vec[:,None,:]) % 1 + k_tilde_terms = k_tilde[0](points, gamma) left_sum = np.cumsum(k_tilde_terms[...,1:], axis=-1) * n_array[1:] right_sum = np.cumsum(n_array[:-1] * k_tilde_terms[...,1:], axis=-1) diff --git a/qmcpy/discrete_distribution/kronecker/kronecker_search_methods.py b/qmcpy/discrete_distribution/kronecker/kronecker_search_methods.py new file mode 100644 index 000000000..ac5fccb4b --- /dev/null +++ b/qmcpy/discrete_distribution/kronecker/kronecker_search_methods.py @@ -0,0 +1,225 @@ +import numpy as np + +def kronecker_vector_search_mobius_transform(n_max, d_max, searchsize, kernel=None, coord_weights=None, gen_vec_init=None): + """ + Note that the sympy package is highly recommended for this search method, though not required. + + A deterministic CBC search method for finding a generating vector for a Kronecker sequence, minimizing the weighted sum of squared discrepancies (WSSD). + - The first component is gen_vec_init, defaults to the golden ratio. + - We use a modified mobius transformation f(x) = (a*x + b)/(c*x + d) where a, c are distinct primes and b, d are the two pairs of the smallest positive integers such that |a*d - b*c| = 1. + - Each subsequent component is found by performing the mobius transformation on the previous component, searching over all pairs of distinct primes from the first searchsize many primes. + + Args: + n_max (int): The maximum sample size to be searched over. + d_max (int): The maximum dimension for which to find the generating vector. + kernel (callable): The kernel function to use in the search. + searchsize (int): The number of primes to search over for each component of the generating vector. + coord_weights (array-like, optional): An array of coordinate weights to use in the search. If None, weights are set to j^(-2). + gen_vec_init (array-like, optional): The initial value for the generating vector. If None, the golden ratio is used for the first component. Note that gen_vec_init is taken mod 1. + + Returns: + generating_vector, wssd, discrepancies, coeff (tuple): + - generating_vector (numpy array): The generating vector found by the search. + - wssd (float): The weighted sum of squared discrepancies for n = 1,...,n_max, for the generating vector found. + - discrepancies (numpy array): The discrepancies for n = 1,...,n_max. + - coeff (numpy array): The coefficients of the linear transformation used in the search. A description of the coeff array is found below. + + Time cost: + The time cost of the search is O(searchsize^2 * d_max * n_max). + + Approach: + Conducts a deterministic CBC search for a generating vector, minimizing the weighted sum of squared discrepancies (wssd) with sample weights w_n = n. + + Details on coeff array: + The coeff array is a (d_max-1) x 4 array where each row corresponds to a dimension from 2 to d_max. The columns correspond to the coefficients of the linear transformation used to compute the gen_vec component for that dimension. Specifically, + - gen_vec[dim+1] = (coeff[dim, 0] * gen_vec[dim] + coeff[dim, 1]) / (coeff[dim, 2] * gen_vec[dim] + coeff[dim, 3]) + """ + + if searchsize < 2: + raise ValueError("searchsize must be at least 2.") + if n_max < 2: + raise ValueError("n_max must be at least 2.") + if d_max < 1: + raise ValueError("d_max must be at least 1.") + if coord_weights is not None and len(coord_weights) < d_max: + raise ValueError("Length of coord_weights must be greater than or equal to d_max.") + + + # the quadratic Bernoulli polynomial + if kernel is None: + kernel = lambda t: t * (t - 1) + 1/6 + + # define coordinate weights if not provided, default to j^(-2) + if coord_weights is None: + coord_weights = np.array([j**(-2) for j in range(1, d_max + 1)], dtype=np.float64) + else: + coord_weights = np.asarray(coord_weights, dtype=np.float64) + + # use sympy if it's already installed, otherwise uses slower and recursive direct implementation + try: + import sympy + except ImportError: + print("While not required, installing the sympy package is recommended for this search method. It is used to compute the Bezout coefficients for the linear transformation used in the search. If sympy is not installed, the search will use a recursive and likely slower implementation of the Euclidean algorithm instead.") + has_sympy = False + else: + has_sympy = True + + if has_sympy: + # search over the first n primes, n = searchsize + searchspace = np.array(list(sympy.primerange(1, sympy.prime(searchsize)+1)), dtype=np.float64) + else: + def get_primes(n): + primes = [] + num = 2 + while len(primes) < n: + is_prime = True + for p in primes: + if p * p > num: + break + if num % p == 0: + is_prime = False + break + if is_prime: + primes.append(num) + num += 1 + return primes + + # search over the first n primes, n = searchsize + searchspace = np.array(get_primes(searchsize), dtype=np.float64) + + # we define this method here for convenience, to use in computing Bezout coefficients if necessary + def recursive_euclidean_algorithm(a, b): + if b == 0: + return 1, 0, a + x1, y1, gcd = recursive_euclidean_algorithm(b, a % b) + x = y1 + y = x1 - (a // b) * y1 + return x, y, gcd + + # gen_vec is our generating vector, will be found cbc + gen_vec = np.zeros(d_max, dtype=np.float64) + + # we pick the golden ratio as the first component of gen_vec, or let the user specify + if gen_vec_init is None: + gen_vec[0] = np.float64((np.sqrt(5) - 1) / 2) + else: + gen_vec[0] = np.mod(gen_vec_init, 1,dtype=np.float64) + + # precompute several constants for the wssd calculation + diff = np.cumsum(1.0 / np.arange(n_max, 1, -1,dtype=np.float64)) + freq = np.cumsum(diff) + freq = np.flip(freq) + + num = n_max * (n_max + 1) / 2 + + nK0 = (1 + coord_weights * kernel(0)) + nK0 = n_max * np.cumprod(nK0) + + # precompute Bezout coefficients for all pairs of primes in the search space + bezoutCoeffs = np.zeros((searchsize, searchsize)) + if has_sympy: + from sympy.core.intfunc import igcdex + for i in range(searchsize - 1): + a = searchspace[i] + for j in range(i + 1, searchsize): + c = searchspace[j] + # Use sympy.igcdex to get Bezout coefficients + d_coeff, b_coeff, _ = igcdex(int(a), int(c)) + bezoutCoeffs[i, j] = np.float64(b_coeff) + bezoutCoeffs[j, i] = np.float64(d_coeff) + else: + for i in range(searchsize - 1): + a = searchspace[i] + for j in range(i + 1, searchsize): + c = searchspace[j] + # Use the recursive Euclidean algorithm to get Bezout coefficients + d_coeff, b_coeff, _ = recursive_euclidean_algorithm(int(a), int(c)) + bezoutCoeffs[i, j] = np.float64(b_coeff) + bezoutCoeffs[j, i] = np.float64(d_coeff) + + + # setting up some useful variables for the search + coeff = np.zeros((d_max - 1, 4)) # stores the coefficients of the linear transformation at each dimension + t = gen_vec[0] * np.arange(1, n_max) % 1 # t vector is the vector of coordinates generated for the first dimension + kPrev = 1 + coord_weights[0] * kernel(t) # gets the k vector for the first dimension, which is used in the wssd calculation and updated each dimension of the search. + # The k vector is Ktilde(x_i) for i = 1,...,n_max-1, where Ktilde is the kernel and x_i are the points generated by the gen_vec vector, up to the current dimension. + + # the main search loop + for dim in range(1, d_max): + best_wssd = np.inf # stores the current wssd found for each dimension, initialized to infinity + best_gen_vec = 0 # stores the current best gen_vec component found for this dimension, initialized to 0 + best_k = None # stores the k vector for the current best gen_vec, used to update the k vector for the next dimension after the search is done for this dimension + for i in range(searchsize): + p1 = searchspace[i] + for j in range(searchsize): + if j == i: # the two primes have to be distinct, so we skip this case + continue + + p2 = searchspace[j] + + b = bezoutCoeffs[i, j] + d = bezoutCoeffs[j, i] + + if b < 0: # we search over both minimal Bezout coefficients + b1 = -b + d1 = d + b2 = np.abs(b + p1) + d2 = np.abs(d -p2) + else: + d1 = -d + b1 = b + d2 = np.abs(d + p2) + b2 = np.abs(b - p1) + + gen_vec_dim1 = (p1 * gen_vec[dim - 1] + b1) / (p2 * gen_vec[dim - 1] + d1) # the linear transformation to get the next gen_vec_dim candidate to test + gen_vec_dim2 = (p1 * gen_vec[dim - 1] + b2) / (p2 * gen_vec[dim - 1] + d2) # the other candidate from the linear transformation + t1 = (gen_vec_dim1 * np.arange(1, n_max)) - np.floor(gen_vec_dim1 * np.arange(1, n_max)) # vector of coordinates generated by this candidate component + t2 = (gen_vec_dim2 * np.arange(1, n_max)) - np.floor(gen_vec_dim2 * np.arange(1, n_max)) + k_vector1 = kPrev * (1 + kernel(t1) * coord_weights[dim]) # get the k vector for this candidate component, used in the wssd calculation + k_vector2 = kPrev * (1 + kernel(t2) * coord_weights[dim]) + + wssd1 = np.dot(freq, k_vector1) + wssd2 = np.dot(freq, k_vector2) + + if wssd1 < wssd2: + b = b1 + d = d1 + wssd = wssd1 + k_vector = k_vector1 + gen_vec_dim = gen_vec_dim1 + else: + b = b2 + d = d2 + wssd = wssd2 + k_vector = k_vector2 + gen_vec_dim = gen_vec_dim2 + + if wssd < best_wssd: # if this candidate has a better wssd than the best found so far, we update the best coefficients and wssd + coeff[dim-1, 0] = p1 + coeff[dim-1, 1] = b + coeff[dim-1, 2] = p2 + coeff[dim-1, 3] = d + best_wssd = wssd + best_gen_vec = gen_vec_dim % 1 + best_k = k_vector + gen_vec[dim] = best_gen_vec # update the gen_vec vector with the best candidate found for this dimension + + kPrev = best_k # update the k vector for the next dimension with the k vector of the best candidate found for this dimension + best_wssd = nK0[dim] - num + 2 * best_wssd # calculate the best wssd for this dimension using the formula from the paper, which involves the nK0 constants precomputed at the beginning of the function. This is used for debugging and to check the wssd at each dimension of the search. + + # print(coeff[dim - 1, :], (nK0[dim] - num + 2 * best_wssd)) # debugging line to check the coefficients and wssd at each dimension + + # Adapted from Jimmy's code for calculating the discrepancies for n = 1,...,n_max from SURE 2025 + n_array = np.arange(1, n_max + 1) + k_tilde = lambda x, coord_weight: np.prod(1 + kernel(x) * coord_weight, axis=1) + k_tilde_terms = k_tilde(gen_vec * np.arange(n_max).reshape((n_max, 1)) - np.floor(gen_vec * np.arange(n_max).reshape((n_max, 1))), coord_weights) + + left_sum = np.cumsum(k_tilde_terms[1:]) * n_array[1:] + right_sum = np.cumsum(n_array[:-1] * k_tilde_terms[1:]) + + k_tilde_zero_terms = k_tilde_terms[0] * n_array + summation = np.zeros(n_max) + summation[1:] = left_sum - right_sum + discrepancies = (k_tilde_zero_terms + 2 * summation) / (n_array ** 2) - 1 + + return gen_vec, best_wssd, discrepancies, coeff \ No newline at end of file diff --git a/qmcpy/discrete_distribution/lattice/__init__.py b/qmcpy/discrete_distribution/lattice/__init__.py index b57762ece..3eb626fd7 100644 --- a/qmcpy/discrete_distribution/lattice/__init__.py +++ b/qmcpy/discrete_distribution/lattice/__init__.py @@ -1 +1,2 @@ from .lattice import Lattice +from .lattice_vector_wssd_search import lattice_vector_wssd_search diff --git a/qmcpy/discrete_distribution/lattice/generating_vectors/kuo.lattice-39102-1024-1048576.3600.npy b/qmcpy/discrete_distribution/lattice/generating_vectors/kuo.lattice-39102-1024-1048576.3600.npy new file mode 100644 index 000000000..40fe8cb4e Binary files /dev/null and b/qmcpy/discrete_distribution/lattice/generating_vectors/kuo.lattice-39102-1024-1048576.3600.npy differ diff --git a/qmcpy/discrete_distribution/lattice/lattice.py b/qmcpy/discrete_distribution/lattice/lattice.py index 248692c32..451a5aa80 100644 --- a/qmcpy/discrete_distribution/lattice/lattice.py +++ b/qmcpy/discrete_distribution/lattice/lattice.py @@ -187,6 +187,17 @@ def __init__( )[None, :] d_limit = 9125 n_limit = 1048576 + elif ( + isinstance(generating_vector, str) + and generating_vector == "kuo.lattice-39102-1024-1048576.3600.txt" + ): + self.gen_vec_source = generating_vector + gen_vec = np.load( + dirname(abspath(__file__)) + + "/generating_vectors/kuo.lattice-39102-1024-1048576.3600.npy" + )[None, :] + d_limit = 3600 + n_limit = 1048576 elif isinstance(generating_vector, str): self.gen_vec_source = generating_vector assert generating_vector[-4:] == ".txt" @@ -385,3 +396,94 @@ def _spawn(self, child_seed, dimension): order=self.order, m_max=self.input_m_max, ) + + def expected_squared_periodic_discrepancies(self, n_max, coord_weights=None, kernel=None): + """Returns the expected squared periodic discrepancies for each of the first n_max points of the lattice sequence. + + Args: + n_max (int): Maximum number of points to calculate the squared periodic discrepancies for. + coord_weights (Union[None, np.ndarray]): Coordinate weights for the discrepancy calculation. If None, uses weights gamma_j = j^(-2). + kernel (Union[None, Callable]): Kernel function for the discrepancy calculation. If None, uses the second bernoulli polynomial. + + Returns: + discs (np.ndarray): The expected squared periodic discrepancies for the first n_max points. + """ + + if coord_weights is not None and len(coord_weights) < self.d: + raise ValueError("Length of coord_weights must be greater than or equal to the dimension of the lattice") + if coord_weights is None: + coord_weights = np.array([j**(-2) for j in range(1, self.d + 1)], dtype=np.float64) + if self.order == "LINEAR": + raise NotImplementedError("expected_squared_periodic_discrepancies not implemented for linear order") + + if kernel is None: + kernel = lambda x: x * (x - 1) + 1/6 + + coord_weights = coord_weights[:self.d] + + k_tilde = lambda x: np.prod(1 + coord_weights * kernel(x), axis=-1) + + # generate the vdc points without any random shift + r_x = np.uint64(self.gen_vec.shape[0]) + n = np.uint64(2**(np.ceil(np.log2(n_max)))) + d = np.uint64(self.d) + n_start = np.uint64(0) + x = np.empty((r_x, n, d), dtype=np.float64) + _ = qmctoolscl.lat_gen_natural(r_x, n, d, n_start, self.gen_vec, x, backend="c") + s = x + + # evaluate the kernel on the sample points + k_vector = k_tilde(s) + k_vector = k_vector.reshape(-1) + + # get the constant vector term of the summation + k_const = -1 + k_vector[0]*np.array([j**(-1) for j in range(1, n_max + 1)], dtype=np.float64) + + # group the kernel evaluations by powers of 2 + k_sum = np.zeros(np.ceil(np.log2(n_max)).astype(int), dtype=np.float64) + for i in range(k_sum.size): + k_sum[i] = np.sum(k_vector[2**i:(2**(i+1))]) + + # get the frequency matrix for how often each kernel evaluation appears + # this is always the same and can be precomputed, but for values of n_max large enough to matter (~ 2^25) + # the precomputed file is >1GB and would take longer to load than to compute + i = np.arange(2**k_sum.size) + pattern = np.zeros((k_sum.size, 2**k_sum.size), dtype=np.float64) # start with the pattern for the full power of two + for l in range(k_sum.size): + pattern[l] = ((i >> (l)) & 1) * 2 + + # truncate the matrix to the correct size, get the cumsum and divide by the square of the index + pattern = pattern[:, :n_max] + freq_mtx = np.cumsum(pattern, axis=1) + divisor = np.arange(1, n_max + 1) ** 2 + freq_mtx /= divisor + + # multiply by the frequency matrix and add the constant vector + discs = k_const + (k_sum @ freq_mtx) + return discs + + + def wssd(self, n_max, coord_weights=None, sample_weights=None): + """Returns the weighted sum of the expected squared periodic discrepancies for the first n_max points of the lattice sequence. + + Args: + n_max (int): Number of points to calculate the weighted squared periodic discrepancy for. + coord_weights (Union[None, np.ndarray]): Coordinate weights for the discrepancy calculation. If None, uses weights gamma_j = j^(-2). + sample_weights (Union[None, np.ndarray]): Sample weights for the weighted squared periodic discrepancy calculation. If None, uses weights w_n = n. Note that the time cost may be higher for other sample weights. + + Returns: + wssd (float): The weighted squared periodic discrepancy. + """ + if coord_weights is not None and len(coord_weights) < self.d: + raise ValueError("Length of coord_weights must be greater than or equal to the dimension of the lattice") + if coord_weights is not None: + coord_weights = coord_weights[:self.d] + if sample_weights is not None and len(sample_weights) < n_max: + raise ValueError("Length of sample_weights must be at least n_max") + if sample_weights is None: + sample_weights = np.arange(1, n_max + 1, dtype=np.float64) + + discs = self.expected_squared_periodic_discrepancies(n_max, coord_weights=coord_weights) + wssd = np.dot(sample_weights, discs) + + return wssd \ No newline at end of file diff --git a/qmcpy/discrete_distribution/lattice/lattice_vector_wssd_search.py b/qmcpy/discrete_distribution/lattice/lattice_vector_wssd_search.py new file mode 100644 index 000000000..f95ac7f0d --- /dev/null +++ b/qmcpy/discrete_distribution/lattice/lattice_vector_wssd_search.py @@ -0,0 +1,184 @@ +import numpy as np + +def lattice_vector_wssd_search(n_max, d_max, coord_weights=None, kernel=None): + """ + CBC search method for finding a lattice rule minimizing the WSSD. + + Args: + n_max (int): The maximum number of points the lattice rule is optimized for. + d_max (int): The dimension of the lattice rule. + coord_weights (array-like, optional): The coordinate weights used to compute the discrepancy. Defaults to j^(-2) for j=1,...,d_max. + kernel (callable, optional): The kernel used to compute the discrepancy. Should accept a single argument and return a scalar. Defaults to the second Bernoulli polynomial. + Returns: + gen_vec (array-like): The generating vector of the lattice that minimizes the WSSD. + + Time cost: + The time cost of the search is O(d_max * n_max * log(n_max)), though the contribution of d_max is smaller until around d_max = 100. + + Note: + Uses sample weights of w_n = n for n = 1,...,n_max when calculating the WSSD. + + Examples: + >>> lattice_vector_wssd_search(n_max=2**10, d_max=5) + array([ 1, 403, 361, 281, 421]) + >>> lattice_vector_wssd_search(n_max=2**15, d_max=10) + array([ 1, 4825, 13541, 15249, 15405, 9909, 7493, 11407, 14819, + 10089]) + + Custom coordinate weights + + >>> lattice_vector_wssd_search(n_max=2**15, d_max=10, coord_weights=[j**(-1) for j in range(1, 11)]) + array([ 1, 4825, 13541, 15249, 7311, 10339, 5933, 6307, 14729, + 13037]) + + Custom kernels + + >>> bernoulli6 = lambda x: x * (x * (-1/2 + x * (x * (5/2 + x * (-3 + x))))) + 1/42 + >>> gen_vec = lattice_vector_wssd_search(n_max=2**15, d_max=10, coord_weights=None, kernel=bernoulli6) + >>> gen_vec[0] + 1 + >>> len(gen_vec) + 10 + + The algorithm in its current form is sensitive to differences in floating point precision across platforms, hence the lack of specificity in the previous example. This can cause differences in generator quality, though in my ad hoc testing it is usually not catastrophic. It was originally built on a Windows machine. + + """ + np.seterr(all='warn') + if kernel is None: + kernel = lambda x: x * (x - 1) + 1 / 6 # default kernel is the second Bernoulli polynomial + if coord_weights is None: + coord_weights = np.array([j**(-2) for j in range(1, d_max + 1)], dtype=np.float64) # default coordinate weights are j^(-2) + + if not callable(kernel): + raise ValueError("kernel must be a callable function") + if not isinstance(coord_weights, (list, np.ndarray)): + raise ValueError("coord_weights must be array-like") + if not isinstance(n_max, int) or not isinstance(d_max, int): + raise ValueError("n_max and d_max must be integers") + + if len(coord_weights) < d_max: + raise ValueError("coord_weights must have length at least d_max") + if n_max < 8: + raise ValueError("n_max must be at least 8") + if d_max < 1: + raise ValueError("d_max must be at least 1") + + m = np.ceil(np.log2(n_max)).astype(int) + + # ---------------------------------------------------------------------- + # Set up rhovector - how often each value appears + # ---------------------------------------------------------------------- + bits = np.zeros((n_max, m), dtype=np.uint64) + for i in range(n_max): + bits[i, :] = 2 * np.array([((i >> j) & 1) for j in range(m)], dtype=np.uint64) + + cumsumbits = np.cumsum(bits, axis=0) # n_max x m + rhovector = np.dot((1.0 / np.arange(1, n_max + 1)), cumsumbits) # 1 x m + + rhovectorNx1 = np.zeros((2**m - 1, 1)) + rIdx1 = 0 + for r in range(m, 0, -1): + rIdx2 = rIdx1 + 2**(r - 1) - 1 + rhovectorNx1[rIdx1:rIdx2 + 1, 0] = rhovector[r - 1] + rIdx1 = rIdx2 + 1 + + # ---------------------------------------------------------------------- + # Get ordering of the search space - needed for circulant matrix + # ---------------------------------------------------------------------- + gR = np.ones(2**(m - 2), dtype=int) # gR determines ordering of rows to have circulant matrix + intMod = 2**m + for idx in range(1, 2**(m - 2)): + temp = (gR[idx - 1] * 5) % intMod + gR[idx] = min(intMod - temp, temp) + + gRows = np.ones(2**(m - 1), dtype=int) # gRows determines* ordering of cols to have circulant matrix + gRows[-1] = 0 + rowVects = np.ones(2**m - 1, dtype=int) # rowVects + gStrtIdx = 0 + vStrtIdx = 0 + + for l in range(m, 1, -1): + gEndIdx = gStrtIdx + 2**(l - 2) - 1 + vEndIdx = vStrtIdx + 2**(l - 1) - 1 + + gRow = np.ones(2**(l - 2), dtype=int) + intMod = 2**l + for idx in range(1, 2**(l - 2)): + temp = (gRow[idx - 1] * 5) % intMod + gRow[idx] = min(intMod - temp, temp) + + gRows[gStrtIdx:gEndIdx + 1] = gRow + rowV = np.concatenate(([1], np.flip(gRow[1:]))) + doubled = np.concatenate((rowV, rowV)) + rowVects[vStrtIdx:vEndIdx + 1] = 2**(m - l) * doubled + + gStrtIdx = gEndIdx + 1 + vStrtIdx = vEndIdx + 1 + + rowVects[-1] = 2**(m - 1) + + # ---------------------------------------------------------------------- + # Set up prodV - where we store information about previous components + # ---------------------------------------------------------------------- + prodV = np.ones((2**m - 1, 1)) + prodV = prodV * rhovectorNx1 + + # Initial 1D case + rowV = rowVects / 2**m + rowV = 1 + coord_weights[0] * kernel(rowV) + prodV = prodV * rowV[:, None] + + # Set up k0 + k0 = 1 + coord_weights[0] * kernel(0) + + # ---------------------------------------------------------------------- + # Begin search + # ---------------------------------------------------------------------- + gen_vec = np.ones(d_max, dtype=int) + + for hComp in range(2, d_max + 1): + wssd = np.zeros(2**(m - 2), dtype=np.float64) + + gamma = coord_weights[hComp - 1] + omega = lambda x: 1 + gamma * kernel(x) + k0 = k0 * (1 + gamma * kernel(0)) + + curIdx2 = 0 + prodIdx1 = 0 + for l in range(m, 1, -1): # we iterate over decreasing size blocks of powers of two= + nextIdx2 = curIdx2 + 2**(l - 2) + prodIdx2 = prodIdx1 + 2**(l - 2) + + curRow = gRows[curIdx2:nextIdx2] + col = curRow / 2**l + fftCol = omega(col).astype(np.complex128) # first column of this circulant matrix block + + pCol = prodV[prodIdx1:prodIdx2, 0].astype(np.complex128) # corresponding section of prodV + + wVector = 2 * np.fft.ifft(np.fft.fft(fftCol) * np.fft.fft(pCol)).real # matrix vector product as fft + numrep = 2**(m - l) + wssd = wssd + np.tile(wVector, numrep) + + curIdx2 = nextIdx2 + prodIdx1 = prodIdx2 + 2**(l - 2) + + wssd = wssd + omega(1 / 2) * prodV[-1, 0] # not actually wssd; we avoid subtracting a constant to save precision + + bestIdx = np.uint64(np.argmin(wssd)) + newH = np.uint64(gR[bestIdx]) + + # Avoid duplicates + while newH in gen_vec: + wssd[bestIdx] = np.inf + bestIdx = np.uint64(np.argmin(wssd)) + newH = int(gR[bestIdx]) + + gen_vec[hComp - 1] = newH + + # set up prodV for next iteration + rowV = (newH * rowVects) % 2**m + rowV = rowV / 2**m + rowV = omega(rowV) + prodV = prodV * rowV[:, None] + + return gen_vec \ No newline at end of file diff --git a/test/booktests/tb_lattice_kronecker_methods.py b/test/booktests/tb_lattice_kronecker_methods.py new file mode 100644 index 000000000..5164e63a2 --- /dev/null +++ b/test/booktests/tb_lattice_kronecker_methods.py @@ -0,0 +1,22 @@ +import unittest +from __init__ import BaseNotebookTest + + +class NotebookTests(BaseNotebookTest): + + def test_lattice_kronecker_methods_notebook(self): + # Keep enough lattice candidates for the reduced dimension: dim <= n / 4. + replacements = { + "dim = 100": "dim = 8", + "dim = 20": "dim = 8", + "n = 2**15": "n = 2**5", + "searchsize = 20": "searchsize = 4", + } + self.run_notebook( + "../../demos/lattice_kronecker_methods.ipynb", + replacements=replacements, + ) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_dd_lattice_kronecker.py b/test/test_dd_lattice_kronecker.py new file mode 100644 index 000000000..4c1355b63 --- /dev/null +++ b/test/test_dd_lattice_kronecker.py @@ -0,0 +1,213 @@ +import numpy as np +import numpy.testing as npt +import pytest + +from qmcpy import ( + Kronecker, + Lattice, + kronecker_vector_search_mobius_transform, + lattice_vector_wssd_search, +) + +###################################################### +# Helper functions +###################################################### +def _bernoulli_two(x): + return x * (x - 1) + 1 / 6 + + +def _periodic_kernel(x, coord_weights): + return np.prod(1 + _bernoulli_two(x) * coord_weights, axis=-1) + + +def _direct_squared_discrepancies(points, coord_weights): + """Evaluate the periodic-kernel definition directly for small prefixes.""" + return np.array( + [ + _periodic_kernel( + (points[:n, None] - points[None, :n]) % 1, coord_weights + ).mean() + - 1 + for n in range(1, len(points) + 1) + ] + ) + + +###################################################### +# Test class for Lattice and Kronecker methods +###################################################### +class TestLatticeKroneckerMethods(object): + + def test_lattice_discrepancy_and_wssd(self): + n, coord_weights = 8, np.array([1.0, 0.25]) + lattice = Lattice(2, randomize=False, order="RADICAL_INVERSE") + expected = _direct_squared_discrepancies( + lattice.gen_samples(n=n, warn=False), coord_weights + ) + + for actual in ( + lattice.expected_squared_periodic_discrepancies(n), + lattice.expected_squared_periodic_discrepancies( + n, coord_weights=coord_weights, kernel=_bernoulli_two + ), + ): + assert actual.shape == (n,) and np.isfinite(actual).all() + npt.assert_allclose(actual, expected, rtol=0, atol=5e-15) + + npt.assert_allclose( + lattice.wssd(n), np.arange(1, n + 1) @ expected, rtol=0, atol=5e-14 + ) + sample_weights = np.linspace(0.5, 1.5, n) + npt.assert_allclose( + lattice.wssd( + n, coord_weights=coord_weights, sample_weights=sample_weights + ), + sample_weights @ expected, + rtol=0, + atol=5e-14, + ) + + def test_lattice_validation(self): + lattice = Lattice(2, randomize=False) + with pytest.raises(ValueError, match="coord_weights"): + lattice.expected_squared_periodic_discrepancies(8, coord_weights=[1.0]) + with pytest.raises(ValueError, match="coord_weights"): + lattice.wssd(8, coord_weights=[1.0]) + with pytest.raises(ValueError, match="sample_weights"): + lattice.wssd(8, sample_weights=np.ones(7)) + with pytest.raises(NotImplementedError, match="linear order"): + Lattice(2, randomize=False, order="LINEAR").expected_squared_periodic_discrepancies(8) + + def test_lattice_vector_search(self): + default = lattice_vector_wssd_search(16, 4, None, None) + explicit = lattice_vector_wssd_search( + n_max=16, + d_max=4, + coord_weights=np.array([1.0, 0.25, 1 / 9, 1 / 16]), + kernel=_bernoulli_two, + ) + npt.assert_array_equal(default, np.array([1, 5, 3, 7])) + npt.assert_array_equal(explicit, default) + assert default.shape == (4,) and default.dtype.kind in "iu" + assert len(np.unique(default)) == len(default) and np.all(default % 2 == 1) + + def test_kronecker_discrepancy_and_wssd(self): + n = 8 + kronecker = Kronecker( + 2, generating_vector="SUZUKI", randomize="SHIFT", shift=[0.1, 0.2] + ) + points = (np.arange(n)[:, None] * kronecker.gen_vec[0]) % 1 + sample_weights = np.arange(1, n + 1) + expected = _direct_squared_discrepancies(points, np.ones(2)) + actual = kronecker.periodic_discrepancy(n) ** 2 + assert actual.shape == (1, n) + npt.assert_allclose(actual, expected[None], rtol=0, atol=5e-15) + npt.assert_allclose( + kronecker.wssd_discrepancy(n, sample_weights), + [sample_weights @ expected], + rtol=0, + atol=5e-14, + ) + + coord_weights, kernel = np.array([1.0, 0.25]), (_periodic_kernel, 1) + expected = _direct_squared_discrepancies(points, coord_weights) + for actual in ( + kronecker._square_periodic_discrepancies(n, kernel, coord_weights), + kronecker.periodic_discrepancy( + n, k_tilde=kernel, gamma=coord_weights + ) + ** 2, + ): + npt.assert_allclose(actual, expected[None], rtol=0, atol=5e-15) + npt.assert_allclose( + kronecker.wssd_discrepancy( + n, sample_weights, k_tilde=kernel, gamma=coord_weights + ), + [sample_weights @ expected], + rtol=0, + atol=5e-14, + ) + + def test_cbc_mobius_fallback(self): + kronecker = Kronecker(3, generating_vector="CBC_MT", randomize=False) + assert kronecker.gen_vec_source == "CBC_MT" + assert kronecker.gen_vec.shape == (1, 3) + assert np.isfinite(kronecker.gen_vec).all() + + with pytest.warns(RuntimeWarning, match="CBC_MT.*dimension <= 100"): + fallback = Kronecker(101, generating_vector="CBC_MT", randomize=False) + assert fallback.gen_vec_source == "RICHTMYER" + assert fallback.gen_vec.shape == (1, 101) + + def test_kronecker_search(self): + n = 8 + vector, wssd, discrepancies, coefficients = ( + kronecker_vector_search_mobius_transform( + n_max=n, d_max=3, searchsize=3 + ) + ) + assert vector.shape == (3,) and discrepancies.shape == (n,) + assert coefficients.shape == (2, 4) + assert np.isfinite(vector).all() and np.isfinite(discrepancies).all() + assert np.all((0 <= vector) & (vector < 1)) + assert 0 < wssd + npt.assert_allclose( + wssd, np.arange(1, n + 1) @ discrepancies, rtol=0, atol=5e-14 + ) + + coord_weights = np.array([1.0, 0.25, 1 / 9]) + points = (np.arange(n)[:, None] * vector) % 1 + npt.assert_allclose( + discrepancies, + _direct_squared_discrepancies(points, coord_weights), + rtol=0, + atol=5e-15, + ) + + vector, wssd, discrepancies, coefficients = ( + kronecker_vector_search_mobius_transform( + n_max=n, + d_max=3, + searchsize=3, + kernel=_bernoulli_two, + coord_weights=coord_weights, + gen_vec_init=1.25, + ) + ) + assert vector[0] == pytest.approx(0.25) and coefficients.shape == (2, 4) + npt.assert_allclose( + wssd, np.arange(1, n + 1) @ discrepancies, rtol=0, atol=5e-14 + ) + + vector, wssd, discrepancies, coefficients = ( + kronecker_vector_search_mobius_transform( + n_max=n, + d_max=3, + searchsize=3, + kernel= lambda x: 3 * _bernoulli_two(x), + coord_weights=coord_weights, + gen_vec_init=1.25, + ) + ) + assert 0 < wssd + + @pytest.mark.parametrize( + ("kwargs", "message"), + [ + ({"n_max": 8, "d_max": 2, "searchsize": 1}, "searchsize"), + ({"n_max": 1, "d_max": 2, "searchsize": 2}, "n_max must"), + ({"n_max": 8, "d_max": 0, "searchsize": 2}, "d_max"), + ( + { + "n_max": 8, + "d_max": 3, + "searchsize": 2, + "coord_weights": np.ones(2), + }, + "coord_weights", + ), + ], + ) + def test_kronecker_search_validation(self, kwargs, message): + with pytest.raises(ValueError, match=message): + kronecker_vector_search_mobius_transform(**kwargs)