Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Randomized qmc #57

Merged
merged 67 commits into from
Jan 25, 2023
Merged
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
00830b2
add include and export from RQMC
dmetivie Nov 22, 2022
646b3cc
scrambling methods
dmetivie Nov 22, 2022
b8b5329
shifting methods
dmetivie Nov 22, 2022
ab44dd4
test if RQMC functions are working
dmetivie Nov 22, 2022
e73b2e4
Add ranmization section with example of current API
dmetivie Nov 22, 2022
563d554
fix bug in unif2bits + Type in bits2unif
dmetivie Nov 22, 2022
9f3a39e
test conversion unif2bits and bits2unif
dmetivie Nov 22, 2022
74d751d
update readme
dmetivie Nov 24, 2022
49be1e2
update Owen link
dmetivie Nov 24, 2022
b99865c
Delete .vscode directory
dmetivie Nov 24, 2022
ab885ba
Update README.md
ChrisRackauckas Nov 27, 2022
fc461d0
Merge branch 'SciML:master' into randomizedQMC
dmetivie Nov 28, 2022
e4c995f
Merge branch 'SciML:master' into randomizedQMC
dmetivie Nov 29, 2022
7e96838
format
ChrisRackauckas Nov 29, 2022
8a8eeac
comment about explicit Type in bits2unif
dmetivie Nov 30, 2022
6e2f76d
Merge branch 'randomizedQMC' of https://github.com/dmetivie/QuasiMont…
dmetivie Nov 30, 2022
ea84fb6
Merge branch 'master' into randomizedQMC
ParadaCarleton Dec 15, 2022
25ddd5c
struc of rand
dmetivie Dec 16, 2022
e2ada4e
Merge branch 'randomizedQMC' of https://github.com/dmetivie/QuasiMont…
dmetivie Dec 16, 2022
f94acf6
Merge branch 'SciML:master' into randomizedQMC
dmetivie Dec 16, 2022
a64f7b7
correction in function doc
dmetivie Dec 31, 2022
a21296b
in place version
dmetivie Dec 31, 2022
3080969
fix the convention n×d to d×n
dmetivie Jan 1, 2023
933c7f8
add rng to shift
dmetivie Jan 1, 2023
1e253f3
naming and typing
dmetivie Jan 1, 2023
fcadc55
add coma in export
dmetivie Jan 3, 2023
9279ed8
doc misprint
dmetivie Jan 3, 2023
0e07566
doc misprint
dmetivie Jan 3, 2023
f9658f0
Merge branch 'randomizedQMC' of https://github.com/dmetivie/QuasiMont…
dmetivie Jan 3, 2023
6facc73
mv DigitalShift into scrambling
dmetivie Jan 3, 2023
ab17bed
change API adding randomization function + methods
dmetivie Jan 3, 2023
bd94bae
naming
dmetivie Jan 3, 2023
e640045
Add randomize to Faure sample and struct
dmetivie Jan 3, 2023
f7e5f6c
fix typo
dmetivie Jan 3, 2023
3c6d39b
formatting
dmetivie Jan 3, 2023
762e110
formatting
dmetivie Jan 3, 2023
e8a591f
Merge branch 'randomizedQMC' of https://github.com/dmetivie/QuasiMont…
dmetivie Jan 3, 2023
2df85e7
apply JuliaFormatter
dmetivie Jan 4, 2023
445b8f8
cleaning
dmetivie Jan 4, 2023
4ad2f52
fix big typo!
dmetivie Jan 4, 2023
2a69efa
similar -> copy avoid error permutedims(#undef)
dmetivie Jan 4, 2023
5050730
add istms net function and test
dmetivie Jan 5, 2023
9859057
format
dmetivie Jan 5, 2023
03e46b3
fix(?) type in ScambleMethod
dmetivie Jan 9, 2023
c2fffdd
fix rand(0:b-1) in M>m Matoussek
dmetivie Jan 9, 2023
ffb13ad
force matrix and types in randomize!
dmetivie Jan 9, 2023
ede1984
fix iteration typo in DigitalShift
dmetivie Jan 9, 2023
55ff172
type in randomize! et co
dmetivie Jan 9, 2023
e80a2f2
rewrite Digital shift
dmetivie Jan 9, 2023
0f70820
change API for randomize_bit
dmetivie Jan 9, 2023
fb1a6cd
change struct RandomizationMethod field S -> R
dmetivie Jan 10, 2023
1e9dd2e
rm unused rng
dmetivie Jan 10, 2023
0249e6c
add Lattice randomize method field
dmetivie Jan 10, 2023
6ae1a0b
improve(?) typing in shift.jl
dmetivie Jan 10, 2023
080bace
add default randmethod to lattice
dmetivie Jan 10, 2023
5364d89
rebase generate_design_matrices
dmetivie Jan 10, 2023
5942843
add some doc
dmetivie Jan 11, 2023
3a25a7e
fix docstring
dmetivie Jan 11, 2023
092da9c
change keyword M to pad as in Base.digits
dmetivie Jan 11, 2023
a09042b
~3 to ~8 time speed up in function unif2bits
dmetivie Jan 11, 2023
d5730ea
Update README.md
ChrisRackauckas Jan 13, 2023
1f19588
update randomize part of readme
dmetivie Jan 16, 2023
bc70815
update and organize readme
dmetivie Jan 16, 2023
b1a45c0
change the generating_design_matrices methods
dmetivie Jan 17, 2023
96b1ec0
change i readme and docs
dmetivie Jan 17, 2023
f3068a3
add various comments + test LHS is tms net
dmetivie Jan 23, 2023
d49cefa
rm istmsnet from pkg to put in test
dmetivie Jan 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ authors = ["ludoro <[email protected]>, Chris Rackauckas <accounts@chrisra
version = "0.3.0"

[deps]
Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697"
Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"
ConcreteStructs = "2569d6c7-a4a2-43d3-a901-331e8e4be471"
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
IntervalArithmetic = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this addition necessary? It looks like it's only a test dependency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we keep istms in scr as I suggested in another comment, we need to keep that one

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we keep istms in scr as I suggested in another comment, we need to keep that one

I think istms is neat, but we don’t want to add new dependencies for it, especially heavy ones like IntervalArithmetic. I’d prefer moving istms to the tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not too concerned with dependency in general, so of course do as you see fit for this package.
Maybe some day we could do a QuasiMonteCarloUI.jl package with this function and other discrepancy measurements, something practitioners would not care too much about, but theoretician would like.

LatticeRules = "73f95e8e-ec14-4e6a-8b18-0d2e271c4e55"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Primes = "27ebfcd6-29c5-5fa9-bf4b-fb8fc14df3ae"
Expand Down
94 changes: 94 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,97 @@ function sample(n,lb,ub,::NewAmazingSamplingAlgorithm)
end
end
```

## Randomization

Note that this feature is currently experimental and is thus subject to interface changes in
non-breaking (minor) releases.

Given a matrix `x` of size `d×n` and `xᵢₛ∈[0,1]ᵈ` one obtain a randomized version `y` using one the following methods
* `owen_scramble(x, b; pad = pad)` where `b` is the base used to scramble and `pad` the number of bits in base `b` used to represent digits.
* `matousek_scramble(x, base; pad = pad)`.
* `digital_shift(x, base; pad = pad)`.
* `shift(x)`.

All these functions guarantee that the resulting array will have its components uniformly distributed `yᵢₛ∼𝐔([0,1]ᵈ)` (but not independent).

### Example

Randomization of a Faure sequence with various methods.

```julia
m = 4
d = 3
b = QuasiMonteCarlo.nextprime(d)
N = b^m # Number of points
pad = m

# Unrandomized low discrepency sequence
x_faure = QuasiMonteCarlo.sample(N, d, FaureSample())

# Randomized version
x_nus = randomize(x_faure, OwenScramble(base = b, pad = pad))
x_lms = randomize(x_faure, MatousekScramble(base = b, pad = pad))
x_digital_shift = randomize(x_faure, DigitalShift(base = b, pad = pad))
x_shift = randomize(x_faure, Shift())
x_uniform = rand(d, N) # plain i.i.d. uniform
```

```julia
using Plots
# Setting I like for plotting
default(fontfamily="Computer Modern", linewidth=1, label=nothing, grid=true, framestyle=:default)
```

Plot the resulting sequences along dimensions `1` and `3`.

```julia
d1 = 1
d2 = 3
sequences = [x_uniform, x_faure, x_shift, x_digital_shift, x_lms, x_nus]
names = ["Uniform", "Faure (unrandomized)", "Shift", "Digital Shift", "Linear Matrix Scrambling", "Nested Uniform Scrambling"]
p = [plot(thickness_scaling=1.5, aspect_ratio=:equal) for i in sequences]
for (i, x) in enumerate(sequences)
scatter!(p[i], x[d1, :], x[d2, :], ms=0.9, c=1, grid=false)
title!(names[i])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These have an extra tab in them

xlims!(p[i], (0, 1))
ylims!(p[i], (0, 1))
yticks!(p[i], [0, 1])
xticks!(p[i], [0, 1])
hline!(p[i], range(0, 1, step=1 / 4), c=:gray, alpha=0.2)
vline!(p[i], range(0, 1, step=1 / 4), c=:gray, alpha=0.2)
hline!(p[i], range(0, 1, step=1 / 2), c=:gray, alpha=0.8)
vline!(p[i], range(0, 1, step=1 / 2), c=:gray, alpha=0.8)
end
plot(p..., size=(1500, 900))
```

![Different randomize methods of the same initial set of points](img/various_randomization.svg)

Faure nets and scrambled versions of Faure nets are digital $(t,d,m)$-net ([see this nice book by A. Owen](https://artowen.su.domains/mc/qmcstuff.pdf)). It basically means that they have strong equipartition properties.
Here we can (visually) verify that with Nested Uniform Scrambling (it also works with Linear Matrix Scrambling and Digital Shift).
You must see one point per rectangle of volume $1/b^m$.

```julia
begin
d1 = 1
d2 = 3
x = x_nus
p = [plot(thickness_scaling=1.5, aspect_ratio=:equal) for i in 0:m]
for i in 0:m
j = m - i
xᵢ = range(0, 1, step=1 / b^(i))
xⱼ = range(0, 1, step=1 / b^(j))
scatter!(p[i+1], x[d1, :], x[d2, :], ms=2, c=1, grid=false)
xlims!(p[i+1], (0, 1.01))
ylims!(p[i+1], (0, 1.01))
yticks!(p[i+1], [0, 1])
xticks!(p[i+1], [0, 1])
hline!(p[i+1], xᵢ, c=:gray, alpha=0.2)
vline!(p[i+1], xⱼ, c=:gray, alpha=0.2)
end
plot(p..., size=(1500, 900))
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
begin
d1 = 1
d2 = 3
x = x_nus
p = [plot(thickness_scaling=1.5, aspect_ratio=:equal) for i in 0:m]
for i in 0:m
j = m - i
xᵢ = range(0, 1, step=1 / b^(i))
xⱼ = range(0, 1, step=1 / b^(j))
scatter!(p[i+1], x[d1, :], x[d2, :], ms=2, c=1, grid=false)
xlims!(p[i+1], (0, 1.01))
ylims!(p[i+1], (0, 1.01))
yticks!(p[i+1], [0, 1])
xticks!(p[i+1], [0, 1])
hline!(p[i+1], xᵢ, c=:gray, alpha=0.2)
vline!(p[i+1], xⱼ, c=:gray, alpha=0.2)
end
plot(p..., size=(1500, 900))
end
d1 = 1
d2 = 3
x = x_nus
p = [plot(thickness_scaling=1.5, aspect_ratio=:equal) for i in 0:m]
for i in 0:m
j = m - i
xᵢ = range(0, 1, step=1 / b^(i))
xⱼ = range(0, 1, step=1 / b^(j))
scatter!(p[i+1], x[d1, :], x[d2, :], ms=2, c=1, grid=false)
xlims!(p[i+1], (0, 1.01))
ylims!(p[i+1], (0, 1.01))
yticks!(p[i+1], [0, 1])
xticks!(p[i+1], [0, 1])
hline!(p[i+1], xᵢ, c=:gray, alpha=0.2)
vline!(p[i+1], xⱼ, c=:gray, alpha=0.2)
end
plot(p..., size=(1500, 900))

```

![All the different elementary rectangle contain only one points](img/equidistribution.svg)
1,306 changes: 1,306 additions & 0 deletions img/equidistribution.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
945 changes: 945 additions & 0 deletions img/various_randomization.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 7 additions & 4 deletions src/Faure.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ end
end

"""
FaureSample()
FaureSample(R::RandomizationMethod)

A Faure low-discrepancy sequence.

Expand All @@ -40,8 +40,11 @@ References:
Faure, H. (1982). Discrepance de suites associees a un systeme de numeration (en dimension s). *Acta Arith.*, 41, 337-351.
Owen, A. B. (1997). Monte Carlo variance of scrambled net quadrature. *SIAM Journal on Numerical Analysis*, 34(5), 1884-1910.
"""
struct FaureSample <: SamplingAlgorithm end
@fastmath function sample(n::Integer, dimension::Integer, ::FaureSample, T = Float64;
Base.@kwdef struct FaureSample <: DeterministicSamplingAlgorithm
R::RandomizationMethod = NoRand()
end

@fastmath function sample(n::Integer, dimension::Integer, S::FaureSample, T = Float64;
skipchecks = false)
base = nextprime(dimension)
n_digits = ceil(Int, log(base, n))
Expand All @@ -53,7 +56,7 @@ struct FaureSample <: SamplingAlgorithm end
"Try $n or $(n+base^power) instead."))
end

return _faure_samples(n, n_digits, dimension, T)
return randomize(_faure_samples(n, n_digits, dimension, T), S.R)
end

@fastmath @views function _faure_samples(n_samples::I, n_digits::I, dimension::I,
Expand Down
8 changes: 5 additions & 3 deletions src/Halton.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@

Create a Halton sequence.
"""
struct HaltonSample <: SamplingAlgorithm end
Base.@kwdef @concrete struct HaltonSample <: DeterministicSamplingAlgorithm
R::RandomizationMethod = NoRand()
end

@views function sample(n::I, d::I, ::HaltonSample, T::Type = Float64) where {I <: Integer}
@views function sample(n::I, d::I, S::HaltonSample, T::Type = Float64) where {I <: Integer}
bases = nextprimes(one(n), d)
n_digits = ceil.(I, log.(bases, n))
λ = n .÷ bases .^ n_digits
halton_seq = Matrix{T}(undef, d, n)
for i in axes(halton_seq, 1)
halton_seq[i, :] .= _vdc(λ[i], n_digits[i], bases[i], T; n)
end
return halton_seq
return randomize(halton_seq, S.R)
end

# @fastmath @views function vdc(n::I, base::Vector{I}, F=Float64) where I <: Integer
Expand Down
19 changes: 10 additions & 9 deletions src/Kronecker.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
KroneckerSample(generator::AbstractVector) <: SamplingAlgorithm
KroneckerSample(generator::AbstractVector) <: DeterministicSamplingAlgorithm
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for introducing the "Deterministic Sampling Algorithm" type? I'm not sure we need different types for the two kinds of algorithms, given you can turn a deterministic algorithm into a randomized one quite easily.

Copy link
Contributor Author

@dmetivie dmetivie Jan 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree I should document that at some point to clarify.
Most classical QMC are deterministic (with the NoRand() option). Of course, they can all been randomized, but at their core they are deterministic. Whereas, other random sequences like LHS and uniform are random at their core.
The distinction becomes important when using generate_design_matrices.
If you want independent realization for a Random Algorithm: easy just sample several times (see code), no need for randomize. For Deterministic Algorithm, the independent matrix will be made using randomize.

If we were to implement fast scrambled Sobol like in this paper you mentioned, then we would classify it as RandomAlgo as the randomization is done while creating the sequence.

Copy link
Collaborator

@ParadaCarleton ParadaCarleton Jan 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn’t the same algorithm applicable for both sets of matrices? In both cases we can take . I know the current implementation doesn’t randomize while creating the sequence, but creating multiple sequences with a randomizer other than NoRand should generate independent matrices.

Copy link
Contributor Author

@dmetivie dmetivie Jan 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I first introduced that to keep the default with NoRand as it is now, i.e. generating different matrices. If we use the same method as RandomAlgo it will produce with NoRand identical matrix and probably makes downstream bug.
I do agree that for NoRand it is not too crazy to expect that generating matrix is useless as it should always generate the same matrix. However, it seems that some people use [out[(j * d + 1):((j + 1) * d), :] for j in 0:(num_mats - 1)].

The second reasons, is that the way I coded DeterministicAlgo

  • Produces the NoRand sequence
  • Then randomize it in place several times. It is faster, especially for scrambling
    Whereas [sample(n, d, sampler(R= RandMethod), T) for j in 1:num_mats] is longer.

Third, (but again this is personal taste) I find it nice to classify algo by random or deterministic nature. (Especially for people not very familiar with QMC?)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that makes sense. Looks good!


A Kronecker sequence is a point set generated using a vector and equation:
`x[i] = i * generator .% 1`
Expand All @@ -19,25 +19,26 @@ References:
Leobacher, G., & Pillichshammer, F. (2014). *Introduction to quasi-Monte Carlo integration and applications.* Switzerland: Springer International Publishing.
https://link.springer.com/content/pdf/10.1007/978-3-319-03425-6.pdf
"""
struct KroneckerSample{V <: Union{AbstractVector, Missing}} <: SamplingAlgorithm
generator::V
Base.@kwdef struct KroneckerSample{V <: Union{AbstractVector, Missing}} <:
DeterministicSamplingAlgorithm
generator::V = missing
R::RandomizationMethod = NoRand()
end

KroneckerSample() = KroneckerSample(missing)
function KroneckerSample(d::Integer, T = Float64)
function KroneckerSample(d::Integer, R = NoRand(), T = Float64)
ratio = harmonious(d, 2eps(T))
generator = [ratio^i for i in 1:d]
return KroneckerSample(generator)
return KroneckerSample(generator, R)
end

function sample(n::Integer, d::Integer, ::KroneckerSample{Missing}, T = Float64)
return sample(n, d, KroneckerSample(d, T))
function sample(n::Integer, d::Integer, S::KroneckerSample{Missing}, T = Float64)
return sample(n, d, KroneckerSample(d, S.R, T))
end

function sample(n::Integer, d::Integer, k::KroneckerSample{V},
T) where {V <: AbstractVector}
@assert eltype(V)==T "Sample type must match generator type."
return sample(n, d, k)
return randomize(sample(n, d, k), k.R)
end

function sample(n::Integer, d::Integer, k::KroneckerSample{V}) where {V <: AbstractVector}
Expand Down
4 changes: 2 additions & 2 deletions src/LatinHypercube.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""
LatinHypercubeSample <: SamplingAlgorithm
LatinHypercubeSample <: RandomSamplingAlgorithm

A Latin Hypercube is a point set with the property that every one-dimensional interval `(i/n, i+1/n)` contains exactly one point. This is a good way to sample a high-dimensional space, as it is more uniform than a random sample but does not require as many points as a full net.
"""
Base.@kwdef @concrete struct LatinHypercubeSample <: SamplingAlgorithm
Base.@kwdef @concrete struct LatinHypercubeSample <: RandomSamplingAlgorithm
rng::AbstractRNG = Random.GLOBAL_RNG
end

Expand Down
15 changes: 8 additions & 7 deletions src/Lattices.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,25 @@ In more than 2 dimensions, grids have worse discrepancy than simple Monte Carlo.
result, they should almost never be used for multivariate integration; their use is as
a starting point for other algorithms.
"""
struct GridSample <: SamplingAlgorithm end
Base.@kwdef @concrete struct GridSample <: DeterministicSamplingAlgorithm
R::RandomizationMethod = NoRand()
end

function sample(n::Integer, d::Integer, ::GridSample, T = Float64)
function sample(n::Integer, d::Integer, S::GridSample, T = Float64)
n = convert(T, n)
return [(i - convert(T, 0.5)) / n for _ in 1:d, i in 1:n]
return randomize([(i - convert(T, 0.5)) / n for _ in 1:d, i in 1:n], S.R)
end

"""
LatticeRuleSample()

Generate a point set using a lattice rule.
"""
Base.@kwdef @concrete struct LatticeRuleSample <: SamplingAlgorithm
rng::AbstractRNG = Random.GLOBAL_RNG
Base.@kwdef @concrete struct LatticeRuleSample <: DeterministicSamplingAlgorithm
R::RandomizationMethod = NoRand()
end

function sample(n::Integer, d::Integer, S::LatticeRuleSample, T = Float64)
rng = S.rng
lat = LatticeRules.LatticeRule(d)
return reduce(hcat, lat[0:(n - 1)])
return randomize(reduce(hcat, lat[0:(n - 1)]), S.R)
end
87 changes: 81 additions & 6 deletions src/QuasiMonteCarlo.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
module QuasiMonteCarlo

using Sobol, LatticeRules, Distributions, Primes, LinearAlgebra, Random
using IntervalArithmetic, Combinatorics
using ConcreteStructs
using Accessors

abstract type SamplingAlgorithm end
abstract type RandomSamplingAlgorithm <: SamplingAlgorithm end
abstract type DeterministicSamplingAlgorithm <: SamplingAlgorithm end
abstract type RandomizationMethod end

include("VanDerCorput.jl")
include("Faure.jl")
Expand All @@ -27,11 +32,11 @@ end
_check_sequence(n::Integer) = @assert n>0 ZERO_SAMPLES_MESSAGE

"""
RandomSample <: SamplingAlgorithm
RandomSample <: RandomSamplingAlgorithm

Randomly distributed random numbers.
"""
Base.@kwdef @concrete struct RandomSample <: SamplingAlgorithm
Base.@kwdef @concrete struct RandomSample <: RandomSamplingAlgorithm
rng::AbstractRNG = Random.GLOBAL_RNG
end

Expand Down Expand Up @@ -72,7 +77,28 @@ function sample(n::Integer, d::Integer, D::Distributions.Sampleable)
return reduce(hcat, x)
end

function generate_design_matrices(n, lb, ub, sampler, num_mats = 2)
# See https://discourse.julialang.org/t/is-there-a-dedicated-function-computing-m-int-log-b-b-m/89776/10
function logi(b::Int, n::Int)
m = round(Int, log(b, n))
b^m == n || throw(ArgumentError("$n is not a power of $b"))
return m
end

"""
```julia
generate_design_matrices(n, lb, ub, sample_method, num_mats)
```

Create `num_mats` matrices each containing a QMC point set, where:
- `n` is the number of points to sample.
- `lb` is the lower bound for each variable. The length determines the dimensionality.
- `ub` is the upper bound.
- `d` is the dimensionality of the point set.
- `sample_method` is the quasi-Monte Carlo sampling strategy. Note that any Distributions.jl
distribution can be used in addition to any `SamplingAlgorithm`.
- You can specify a `RandomizationMethod` for `sample_method<:DeterministicSamplingAlgorithm`
"""
function generate_design_matrices(n, lb, ub, sampler::RandomSamplingAlgorithm, num_mats = 2)
if n <= 0
throw(ZeroSamplesError())
end
Expand All @@ -82,7 +108,49 @@ function generate_design_matrices(n, lb, ub, sampler, num_mats = 2)
[out[(j * d + 1):((j + 1) * d), :] for j in 0:(num_mats - 1)]
end

export GridSample,
function generate_design_matrices(n, lb, ub, sampler::DeterministicSamplingAlgorithm,
num_mats = 2)
if n <= 0
throw(ZeroSamplesError())
end
@assert length(lb) == length(ub)

# Generate a vector of num_mats independent "randomized" version of the QMC sequence
out = generate_design_matrices(n, length(lb), sampler, sampler.R, num_mats, eltype(lb))

# Scaling
for j in eachindex(out)
out[j] = (ub .- lb) .* out[j] .+ lb
end
return out
end

"""
```julia
NoRand
```

No Randomization is performed on the sampled sequence.
"""
struct NoRand <: RandomizationMethod end
randomize(x, S::NoRand) = x

"""
generate_design_matrices(n, d, sampler, R::NoRand, num_mats, T = Float64)
`R = NoRand()` produces `num_mats` matrices each containing a different deterministic point set in `[0, 1)ᵈ`.
Note that this is an ad hoc way to produce different `generate_design_matrices` as it create a deterministic point in dimension `d × num_mats` and split it in `num_mats` point set in dimension `d`.
"""
function generate_design_matrices(n, d, sampler, R::NoRand, num_mats, T = Float64)
out = sample(n, num_mats * d, sampler, T)
return [out[(j * d + 1):((j + 1) * d), :] for j in 0:(num_mats - 1)]
end

include("RandomizedQuasiMonteCarlo/shifting.jl")
include("RandomizedQuasiMonteCarlo/net_utilities.jl")
include("RandomizedQuasiMonteCarlo/scrambling_base_b.jl")

export SamplingAlgorithm,
GridSample,
UniformSample,
SobolSample,
LatinHypercubeSample,
Expand All @@ -94,6 +162,13 @@ export GridSample,
KroneckerSample,
SectionSample,
FaureSample,
SamplingAlgorithm

randomize,
RandomizationMethod,
NoRand,
Shift,
ScrambleMethod,
OwenScramble,
MatousekScramble,
DigitalShift,
istmsnet
end # module
Loading