diff --git a/demos/lattice_kronecker_methods.ipynb b/demos/lattice_kronecker_methods.ipynb new file mode 100644 index 000000000..941bd9fb0 --- /dev/null +++ b/demos/lattice_kronecker_methods.ipynb @@ -0,0 +1,267 @@ +{ + "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": [ + "63.560193378767764\n" + ] + } + ], + "source": [ + "dim = 100\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": [ + "[39.95402303]\n" + ] + } + ], + "source": [ + "dim = 100\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.16274404525756836\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", + "time_start = time()\n", + "searched_lattice_vector = lattice_vector_wssd_search(n_max = n, d_max = dim, kernel = lambda x: x * (x - 1) + 1 / 6, 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: 3.8035733699798584\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.1079203 0.10500649 0.16477572 0.85934099\n", + " 0.20308554 0.43452109]\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 = 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[0])" + ] + }, + { + "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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Dg62JcJz587l119/xTCMW2rzGl9fXy5evIjFYkkzqn2Nj48P8fHxfP7551km0tlx7tw5jh07lmaU/b8GDx7M4MGDOXXqFJMmTaJZs2YcPHgwy1gz4+Pjg5ubG6NGjbphveTkZK5evYqXl5et7N+/t9GjR/Poo48yZ84c2/kXX3zxpr+o8fX1TfMkhYz06tWLBg0aEBoayvTp0/nss89u6h63SyP0IiIiIiJiYzKZcHW0v+c/tzodvVSpUqxbt47Tp0/TunVrYmJi8PLywsPDg3379tnqrV69mvj4eNtxw4YNiY+PZ968ebayRYsWERERQePGjW8plqioKJydnXF1dQWsXyJc22393zw8PGybwN2o7N/atGlDQkICU6dOTVN+/PhxANq2bYvFYkm3qV94eDjBwcE31Y+EhAT69euHv78/zzzzTIZ1Dh8+TGJiIgDFixfn/fffx2KxcOLEiSxjzUynTp1YuHAhhw4dSlO+Z8+edHV/+eUX2/u1a9cSGhpKkyZNAOvvIW/evLbzly9fZsGCBTe8d0Y6duzIvHnz0nx+UVFRXLx40XZcp04dypUrx7PPPktcXBxPP/30Td/ndmiEXkREREREHmglSpSwjdS3atWKpUuX0r9/fwYMGMDRo0eJjY1lxYoVtsevARQrVoxhw4bx3HPP0bNnT8xmMz/++CNDhgzJ8lnrGzduTDeK3LhxY1q1aoXJZKJNmzY0aNCANWvWZJjENmzYkBEjRnDq1Cm8vb3p06dPhmX/VqRIEcaNG0efPn1YtWoVJUqUYM2aNTzzzDOUKlWKIkWKMGHCBPr06cPGjRupUqUKp0+fZsOGDcycOTPNo9Uy609ycjLBwcHMnz8fT09Pli5danus3n/t2bOHxx57jObNm5M/f36WLl1KuXLlePTRR8mTJ88NY81Mr169WLNmDXXr1qVbt274+Piwa9cuzp07x44dO2z1HBwcGDNmDPv378fd3Z2JEyfyyiuvUL58eQCef/55unTpgqOjI97e3vz666+2L1luRq9evViyZAk1atTgueeew87OjuXLlzNr1iz8/f1t9Xr27Mnrr79O9+7dM/287haN0IuIiIiIyAMlICCAgQMH4uvraysrWrQo69ato379+ixdupTPPvuMKVOm4ODgQKVKldi4cSNvv/025cqVs13z7rvvsmLFCvLmzYunpydLlizh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zXTkyBGlpKToueee08SJE3Xy5ElHmxMnTmj06NGaO3cuR3PgkY4dO6ahQ4fq4MGDatKkSZltXn75ZSUlJWn//v3avXu3vvzyS7388stORzhDQkJ08eJFnTx5Unv37tWaNWtqaxeACikqKtJdd92lQ4cOqW/fvmW2WbNmjVauXKnt27frn//8p/bu3au8vDzNmzdPkrRz507t379fw4cP1913360zZ85o/PjxtbkbMCgCvQcLCgrSCy+8oBYtWpTbZvny5erRo4eGDRsmSQoNDVVsbKyWL18uu92uhg0bqlu3blq0aJFOnTqlN954w9EW8CT333+/QkNDy13+6aefymazOV0rHBcXp/T0dH3//feSpHPnzikjI0P5+fk6fvy4MjMza7xuoLIeeOABDR06VCaTqczlWVlZ2rx5s5588knVr19fkvTYY49JkuMIZ1pamkaNGqXXXntN/fv318WLF2uneKASBgwYoIkTJ8rf37/cNsuXL9fEiRMdn3UGDBigW2+9VcuWLZMkrV+/XsuXL1dubq7y8/N14cIFXbhwoTbKByosNDRUMTExjvfssqxatUqDBg1SZGSkJKlRo0aaNGmSVq1aJUkaN26cMjIylJGRod27dys0NNTx+Qa4GgK9wSUnJ6tXr15O83r16qUzZ87o6NGjkqT33ntPK1euVMeOHWUymco9Agp4stTUVLVp00YNGjRwzOvWrZtjmSRt375dw4YN088//6z58+dr0qRJbqkVqI7k5GRJcnpvDwgIUOfOnZWSkiJJio+PV0pKim655Rb5+/vL399fhYWF7igXqLLc3FxlZmaW+TmmZKwPGjRISUlJat++vQYOHKjo6Gjdfffd7igXqJbU1FRHmC8RGRmpnJwcZWdnO8339fVVREREbZYHA+M59AaXm5urpk2bOs0ruVnMmTNn1Lp1a3Xt2lXfffedO8oDXMZqtapx48ZO8wICAuTn5yer1SpJGjZsmDIyMtxQHeA6ubm5klTme3vJtZbx8fGKj4+v7dIAl6rIWA8ICNCiRYu0aNGi2i4PcKmyPseUXIpitVoVHh7uND8xMbE2y4OBcYTe4Pz8/HT+/Hmnefn5+Y5lgLcICAgodafvklMvAwMD3VQV4Hol791lvbfzvg5vwlhHXVLW55iSAxJ8jkF1EOgNrm3bto5T60tkZWXJx8dHrVu3dlNVgOt1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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 low sample sizes, 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(kron_discs), label=\"Kronecker Discrepancy\")\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 fd71cbc5a..e3c2b4c3d 100644 --- a/docs/api/discrete_distributions.md +++ b/docs/api/discrete_distributions.md @@ -24,6 +24,10 @@ jupyter: ::: qmcpy.discrete_distribution.lattice.Lattice +## `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 @@ -40,6 +44,10 @@ jupyter: ::: qmcpy.discrete_distribution.kronecker.Kronecker +## `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 704a112bc..9a273cb9a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -164,6 +164,7 @@ includes = [ "qmcpy", "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 9e00f8074..ea01ca628 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 from .mpmc import MPMC -from .kronecker import Kronecker +from .kronecker import Kronecker, kronecker_vector_search_mobius_transform from .dummy_sampler import DummySampler DiscreteDistribution = AbstractDiscreteDistribution @@ -13,4 +13,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 b66b2b99b..f06a50045 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) @@ -349,7 +461,7 @@ def periodic_discrepancy(self, n, k_tilde=None, gamma=None): 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..be4e4fc52 --- /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.") + response = input("Do you want to continue without sympy? (y/n): ") + if response.lower() != 'y': + raise ImportError("Please install sympy and try again.") + else: + has_sympy = False + print("Continuing without sympy. This may take longer.") + 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/6) + 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..8124542bc 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,86 @@ 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 + + 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 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..ce41f80b2 --- /dev/null +++ b/qmcpy/discrete_distribution/lattice/lattice_vector_wssd_search.py @@ -0,0 +1,183 @@ +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**6 - 3 * x**5 + 5 / 2 * x**4 - 1 / 2 * x**2 + 1 / 42 + >>> lattice_vector_wssd_search(n_max=2**15, d_max=10, coord_weights=None, kernel=bernoulli6) + array([ 1, 12589, 15515, 3957, 1879, 8985, 15139, 9529, 7363, 6089]) + """ + + 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 < 3: + raise ValueError("n_max must be at least 3") + 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 + # ---------------------------------------------------------------------- + bits = np.zeros((n_max, m), dtype=int) + for i in range(n_max): + bits[i, :] = 2 * np.array([((i >> j) & 1) for j in range(m)], dtype=int) + + 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 + # ---------------------------------------------------------------------- + gR = np.ones(2**(m - 2), dtype=int) + 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[-1] = 0 + rowVects = np.ones(2**m - 1, dtype=int) + 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 + # ---------------------------------------------------------------------- + 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)) + + 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): + nextIdx2 = curIdx2 + 2**(l - 2) - 1 + prodIdx2 = prodIdx1 + 2**(l - 2) - 1 + + curRow = gRows[curIdx2:nextIdx2 + 1] + col = curRow / 2**l + fftCol = omega(col) + + pCol = prodV[prodIdx1:prodIdx2 + 1, 0] + + wVector = 2 * np.fft.ifft(np.fft.fft(fftCol) * np.fft.fft(pCol)).real + numrep = 2**(m - l) + wssd = wssd + np.tile(wVector, numrep) + + curIdx2 = nextIdx2 + 1 + prodIdx1 = prodIdx2 + 2**(l - 2) + 1 + + wssd = wssd + omega(1 / 2) * prodV[-1, 0] + wssd = wssd + n_max * k0 - n_max * (n_max + 1) / 2 + + # Choose the best candidate, using the smallest index in case of ties to avoid different platforms providing different outputs + min_wssd = np.min(wssd) + rtol = 1e-15 + best_indices = np.where(np.abs(wssd - min_wssd) <= rtol * np.abs(min_wssd))[0] + bestIdx = int(best_indices[0]) + newH = int(gR[bestIdx]) + + # Avoid duplicates + while newH in gen_vec: + wssd[bestIdx] = np.inf + min_wssd = np.min(wssd) + best_indices = np.where(np.abs(wssd - min_wssd) <= rtol * np.abs(min_wssd))[0] + bestIdx = int(best_indices[0]) + newH = int(gR[bestIdx]) + + gen_vec[hComp - 1] = newH + + 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..a16690826 --- /dev/null +++ b/test/test_dd_lattice_kronecker.py @@ -0,0 +1,200 @@ +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)) + 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 + ) + + @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)