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 all 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
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ authors = ["ludoro <[email protected]>, Chris Rackauckas <accounts@chrisra
version = "0.3.0"

[deps]
Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697"
ConcreteStructs = "2569d6c7-a4a2-43d3-a901-331e8e4be471"
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
LatticeRules = "73f95e8e-ec14-4e6a-8b18-0d2e271c4e55"
Expand Down
98 changes: 88 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,21 @@ all sampled from the same low-discrepancy sequence.

## Available Sampling Methods

* `GridSample(dx)` where the grid is given by `lb:dx[i]:ub` in the ith direction.
* `UniformSample` for uniformly distributed random numbers.
* `SobolSample` for the Sobol sequence.
* `LatinHypercubeSample` for a Latin Hypercube.
* `LatticeRuleSample` for a randomly-shifted rank-1 lattice rule.
* `HaltonSample(base)` where `base[i]` is the base in the ith direction.
* `GoldenSample` for a Golden Ratio sequence.
* `KroneckerSample(alpha, s0)` for a Kronecker sequence, where alpha is an length-d vector of irrational numbers (often sqrt(d)) and s0 is a length-d seed vector (often 0).
* `SectionSample(x0, sampler)` where `sampler` is any sampler above and `x0` is a vector of either `NaN` for a free dimension or some scalar for a constrained dimension.
* Additionally, any `Distribution` can be used, and it will be sampled from.
Sampling methods `SamplingAlgorithm` are divided into two subtypes

- `DeterministicSamplingAlgorithm`
- `GridSample(dx)` where the grid is given by `lb:dx[i]:ub` in the ith direction.
- `SobolSample` for the Sobol' sequence.
- `FaureSample` for the Faure sequence.
- `LatticeRuleSample` for a randomly-shifted rank-1 lattice rule.
- `HaltonSample(base)` where `base[i]` is the base in the ith direction.
- `GoldenSample` for a Golden Ratio sequence.
- `KroneckerSample(alpha, s0)` for a Kronecker sequence, where alpha is an length-`d` vector of irrational numbers (often `sqrt(d`)) and `s0` is a length-`d` seed vector (often `0`).
- `RandomSamplingAlgorithm`
- `UniformSample` for uniformly distributed random numbers.
- `LatinHypercubeSample` for a Latin Hypercube.
- Additionally, any `Distribution` can be used, and it will be sampled from.
<!-- - `SectionSample(x0, sampler)` where `sampler` is any sampler above and `x0` is a vector of either `NaN` for a free dimension or some scalar for a constrained dimension. Not currently supported. -->

## Adding a new sampling method

Expand All @@ -107,3 +112,76 @@ function sample(n,lb,ub,::NewAmazingSamplingAlgorithm)
end
end
```

## Randomization of QMC sequences

Most of the previous methods are deterministic i.e. `sample(n, d, Sampler()::DeterministicSamplingAlgorithm)` always produces the same sequence $X = (X_1, \dots, X_n)$.
The API to randomize sequence is either
- Directly use `QuasiMonteCarlo.sample(n, d, DeterministicSamplingAlgorithm(R = RandomizationMethod()))` or `sample(n, lb, up, DeterministicSamplingAlgorithm(R = RandomizationMethod()))`
- Or given any matrix $X$ ($d\times n$) of $n$ points all in dimension $d$ in $[0,1]^d$ one can do `randomize(x, ::RandomizationMethod())`

There are the following randomization methods:

- Scrambling methods `ScramblingMethods(base, pad, rng)` where `base` is the base used to scramble and `pad` the number of bits in `b`-ary decomposition.
`pad` is generally chosen as $\gtrsim \log_b(n)$.
The implemented `ScramblingMethods` are
- `DigitalShift`
- `MatousekScramble` a.k.a Linear Matrix Scramble.
- `OwenScramble` a.k.a Nested Uniform Scramble is the most understood theoretically but is more costly to operate.
- `Shift(rng)` a.k.a. Cranley Patterson Rotation.

For numerous independent randomization, use `generate_design_matrices(n, d, ::DeterministicSamplingAlgorithm), ::RandomizationMethod, num_mats)` where `num_mats` is the number of independent `X` generated.

### Example

Randomization of a Faure sequence with various methods.

```julia
using QuasiMonteCarlo
m = 4
d = 3
b = QuasiMonteCarlo.nextprime(d)
N = b^m # Number of points
pad = m # Length of the b-ary decomposition number = sum(y[k]/b^k for k in 1:pad)

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

# Randomized version
x_nus = randomize(x_faure, OwenScramble(base = b, pad = pad)) # equivalent to sample(N, d, FaureSample(R = 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 # you can try every combination of dimension (d1, d2)
sequences = [x_uniform, x_faure, x_shift, x_digital_shift, x_lms, x_nus]
names = ["Uniform", "Faure (deterministic)", "Shift", "Digital Shift", "Matousek Scramble", "Owen Scramble"]
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])
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)
1 change: 1 addition & 0 deletions docs/pages.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
pages = [
"Home" => "index.md",
"samplers.md",
"randomization.md",
]
135 changes: 135 additions & 0 deletions docs/src/randomization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Randomization methods

Most of the methods presented in [sampler.jl Samplers](@ref) are deterministic i.e. `sample(n, d, Sampler()::DeterministicSamplingAlgorithm)` always produces the same sequence $X = (X_1, \dots, X_n)$.

The main issue with deterministic Quasi Monte Carlo sampling is that it does not allow easy error estimation as opposed to plain Monte Carlo where the variance can be estimated.

A Randomized Quasi Monte Carlo method must respect the two following criteria

1. Have $X_i\sim \mathbb{U}([0,1]^d)$ for each $i\in \{1,\cdots, n\}$.
2. Preserve the QMC (low discrepancy) properties of $X$.

This randomized version can be used to obtain confidence interval or sensitivity analysis for example.

## API

To randomize one can directly use a sampling algorithm with a randomization method as `sample(n, d, DeterministicSamplingAlgorim(R = RandomizationMethod()))` or `sample(n, lb, up, DeterministicSamplingAlgorim(R = RandomizationMethod()))`.

Randomization methods can also be used independently, that is, given a matrix $X$ ($d\times n$) of $n$ points in dimension $d$ in $[0,1]^d$ one can directly randomize it using `randomize(x, ::RandomizationMethod())`
```@docs
randomize
```

## Scrambling methods

`ScramblingMethods(base, pad, rng)` are well suited for $(t,m,d)$-nets. `base` is the base used to scramble and `pad` the number of bits in `b`-ary decomposition i.e. $y \simeq \sum_{k=1}^{\texttt{pad}} y_k/\texttt{base}^k $.
`pad` is generally chosen as $\gtrsim \log_b(n)$.
The implemented `ScramblingMethods` are
- `DigitalShift` the simplest and faster method. For a point $x\in [0,1]^d$ it does $y_k = (x_k + U_k) \mod b$ where $U_k ∼ \mathbb{U}(\{0, \cdots, b-1\})$
```@docs
DigitalShift
```
- `MatousekScramble` a.k.a Linear Matrix Scramble is what people use in practice. Indeed, the observed performances are similar to `OwenScramble` for a lesser numerical cost.
```@docs
MatousekScramble
```
- `OwenScramble` a.k.a Nested Uniform Scramble is the most understood theoretically but is more costly to operate.
```@docs
OwenScramble
```
s
## Other methods

`Shift(rng)` a.k.a. Cranley Patterson Rotation. It is by far the fastest method, it is used in `LatticeRuleScramble` for example.
```@docs
Shift
```

## Design Matrices

For numerous independent randomization, use `generate_design_matrices(n, d, ::DeterministicSamplingAlgorithm), ::RandomizationMethod, num_mats)` where `num_mats` is the number of independent `X` generated.
```@docs
generate_design_matrices
```

## Example

Randomization of a Faure sequence with various methods.

```julia
using QuasiMonteCarlo
m = 4
d = 3
b = QuasiMonteCarlo.nextprime(d)
N = b^m # Number of points
pad = m # Can also choose something as `2m` to get "better" randomization

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

# Randomized version
x_nus = randomize(x_faure, OwenScramble(base = b, pad = pad)) # equivalent to sample(N, d, FaureSample(R = 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 # you can try every combination of dimension (d1, d2)
sequences = [x_uniform, x_faure, x_shift, x_digital_shift, x_lms, x_nus]
names = ["Uniform", "Faure (deterministic)", "Shift", "Digital Shift", "Matousek Scramble", "Owen Scramble"]
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])
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,m,d)$-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$. Points on the "left" border of rectangles are included while those on the "right" are excluded.

```julia
begin
d1 = 1
d2 = 3 # you can try every combination of dimension (d1, d2)
x = x_nus # Owen Scramble, you can try x_lms and x_digital_shift
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
```

![All the different elementary rectangle contain only one points](../../img/equidistribution.svg)
20 changes: 17 additions & 3 deletions docs/src/samplers.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,28 @@ sample

## Samplers

Samplers are divided into two subtypes
```julia
abstract type SamplingAlgorithm end
abstract type RandomSamplingAlgorithm <: SamplingAlgorithm end
abstract type DeterministicSamplingAlgorithm <: SamplingAlgorithm end
```

### Deterministic Sampling Algorithm

```@docs
GridSample
UniformSample
SobolSample
LatinHypercubeSample
LatticeRuleSample
HaltonSample
GoldenSample
KroneckerSample
SectionSample
```

### Random Sampling Algorithm

```@docs
UniformSample
LatinHypercubeSample
<!-- SectionSample -->
```
Loading