Skip to content

Commit f106e60

Browse files
authored
Merge pull request #8 from robertdj/vector
Work with vector of vectors
2 parents 6250374 + c430e68 commit f106e60

8 files changed

Lines changed: 173 additions & 131 deletions

File tree

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "SparseGrids"
22
uuid = "bafbe729-afc6-5148-bb4f-226bf3d46895"
33
authors = ["Robert DJ <[email protected]>"]
4-
version = "1.2.0"
4+
version = "2.0.0"
55

66
[deps]
77
FastGaussQuadrature = "442a2c76-b920-505d-bb47-c5924d526838"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ The `gausshermite` quadrature rule is used for computing integrals over `R^D` wi
2828
To approximate such an integral, compute
2929

3030
```julia
31-
dot( weigths, g(nodes) )
31+
dot(weigths, g.(nodes))
3232
```
3333

3434
Note that when integrating against `exp(-|x|^2)` instead of the standard Gaussian density, the nodes and weigths are rescaled compared to e.g. the source of the Kronrod-Patterson nodes mentioned below.

src/grids.jl

Lines changed: 60 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -41,64 +41,59 @@ end
4141
function sparsegrid(D::Integer, nodes1D::Vector{Vector{Float64}}, weights1D::Vector{Vector{Float64}})
4242
order = length(nodes1D)
4343

44-
# Final nodes and weights in D dimensions
45-
nodes = Array{Float64}(undef, D, 0)
46-
weights = Array{Float64}(undef, 0)
47-
48-
mink = max(0, order-D)
44+
mink = max(0, order - D)
4945
maxk = order - 1
46+
all_alpha_lists = [listNdq(D, D + k) for k in mink:maxk]
47+
48+
number_of_nodes = map(x -> sum(prod.(x)), all_alpha_lists) |> sum
49+
50+
# Final nodes and weights in D dimensions
51+
nodes = Vector{Vector{Float64}}(undef, 0)
52+
sizehint!(nodes, number_of_nodes)
5053

51-
# Temporary nodes and weights for 1 dimension
52-
N = Vector{Vector{Float64}}(undef, D)
53-
W = similar(N)
54+
weights = Array{Float64}(undef, 0)
55+
sizehint!(weights, number_of_nodes)
5456

55-
for k in mink:maxk
56-
alpha = listNdq(D, D+k)
57-
nalpha = size(alpha, 2)
57+
for (alpha_idx, k) in enumerate(mink:maxk)
58+
this_alpha_list = all_alpha_lists[alpha_idx]
5859

59-
for n in 1:nalpha
60+
for alpha in this_alpha_list
6061
# The nodes and weights for this alpha mixture
61-
for d in 1:D
62-
N[d] = nodes1D[ alpha[d,n] ]
63-
W[d] = weights1D[ alpha[d,n] ]
64-
end
62+
N = [nodes1D[index] for index in alpha]
63+
W = [weights1D[index] for index in alpha]
6564

6665
# Compute all the possible combinations of D-dimensional nodes
6766
combN = combvec(N)
6867

6968
# Compute the associated weights
7069
cw = combvec(W)
71-
combW = (-1)^(maxk-k) * binomial(D-1, D+k-order) * prod(cw, dims = 1)
70+
combW = (-1)^(maxk - k) * binomial(D - 1, D + k - order) * map(prod, cw)
7271

73-
# Save nodes and weights
74-
nodes = hcat(nodes, combN)
75-
weights = vcat(weights, vec(combW))
72+
append!(nodes, combN)
73+
append!(weights, combW)
7674
end
7775
end
7876

79-
# Remove redundant nodes
80-
unodes, uweights = uniquenodes(nodes, weights)
81-
82-
return unodes, uweights
77+
unique_nodes, combined_weights = uniquenodes(nodes, weights)
78+
return unique_nodes, combined_weights
8379
end
8480

8581

86-
# ------------------------------------------------------------
87-
# To correctly reduce "overlapping" nodes the middle node in an
88-
# uneven number must be exactly zero
89-
82+
"""
83+
To correctly reduce "overlapping" nodes the middle node in an uneven number must be exactly zero
84+
"""
9085
function symmetrize!(nodes::Vector{Float64})
9186
N = length(nodes)
9287

9388
if isodd(N)
94-
midpoint = div( N-1, 2 )
95-
nodes[ midpoint+1 ] = 0.0
89+
midpoint = div(N - 1, 2)
90+
nodes[midpoint + 1] = 0.0
9691
else
97-
midpoint = div( N, 2 )
92+
midpoint = div(N, 2)
9893
end
9994

10095
for n in 1:midpoint
101-
nodes[n] = -nodes[N+1-n]
96+
nodes[n] = -nodes[N + 1 - n]
10297
end
10398
end
10499

@@ -118,11 +113,11 @@ The algorithm and the formula for computing the number of elements in this set i
118113
"""
119114
function listNdq(D::Integer, q::Integer)
120115
if q < D
121-
error("listNdq: q must be larger than D")
116+
error("q must be larger than D")
122117
end
123118

124-
M = binomial(q-1, D-1)
125-
L = ones(Int, D, M)
119+
M = binomial(q - 1, D - 1)
120+
L = [ones(Int, D) for _ in 1:M]
126121

127122
k = ones(Int, D)
128123
maxk = q - D + 1
@@ -138,15 +133,15 @@ function listNdq(D::Integer, q::Integer)
138133
k[p] = 1
139134
p += 1
140135
else
141-
for i in 1:p-1
136+
for i in 1:p - 1
142137
khat[i] = khat[p] - k[p] + 1
143138
end
144139

145140
k[1] = khat[1]
146141
p = 1
147142

148143
count += 1
149-
L[:,count] = k
144+
copy!(L[count], k)
150145
end
151146
end
152147

@@ -155,70 +150,57 @@ end
155150

156151

157152
"""
158-
combvec( vecs::Array{Any} ) -> Matrix
153+
combvec(vecs)
159154
160155
Counterpart of Matlab's combvec:
161156
Creates all combinations of vectors in `vecs`, an array of vectors.
162157
"""
163158
function combvec(vecs::Vector{Vector{T}}) where T
164-
# Construct all Cartesian combinations of elements in vec as tuples
165-
P = Iterators.product(vecs...)
166-
167159
D = length(vecs)
168-
N = length(P)::Int64
169-
y = Array{T}(undef, D,N)
160+
N = map(length, vecs) |> prod
161+
y = [Vector{T}(undef, D) for _ in 1:N]
170162

171-
n = 0
172-
for p in P
173-
y[:, n+=1] = [p...]
163+
# Construct all Cartesian combinations of elements in vec as tuples
164+
P = Iterators.product(vecs...)
165+
for (n, p) in enumerate(P)
166+
y[n] = [p...]
174167
end
175168

176169
return y
177170
end
178171

179172

180-
# ------------------------------------------------------------
181-
182-
# Copy of Base._sortslices for dims = 2 that also returns the permutation indices
183-
# Previously Base.Sort.sortcols
184-
function sortcolsidx(A::AbstractMatrix)
185-
itspace = Base.compute_itspace(A, Val(2))
186-
vecs = map(its->view(A, its...), itspace)
187-
p = sortperm(vecs)
188-
189-
return A[:,p], p
190-
end
191-
192-
193173
"""
194-
uniquenodes(nodes::Matrix, weights::Vector)
174+
uniquenodes(nodes, weights)
195175
196-
Find unique nodes and sum the weights of the identical nodes
176+
Find unique nodes and sum the weights of identical nodes
197177
"""
198-
function uniquenodes(nodes::AbstractMatrix, weights::AbstractVector)
178+
function uniquenodes(nodes, weights)
199179
# Sort nodes and weights according to nodes
200-
sortnodes, P = sortcolsidx(nodes)
201-
weights = weights[P]
180+
perm = sortperm(nodes)
181+
sorted_nodes = nodes[perm]
182+
perm_weights = weights[perm]
202183

203-
D, N = size(nodes)
184+
unique_nodes = similar(nodes, 0)
185+
N = length(nodes)
204186

205-
keep = [1]
206-
lastkeep = 1
187+
indices_to_keep = [1]
188+
sizehint!(indices_to_keep, N)
189+
last_index_to_keep = 1
207190

208191
for n in 2:N
209-
if sortnodes[:,n] == sortnodes[:,n-1]
210-
weights[lastkeep] += weights[n]
192+
if sorted_nodes[last_index_to_keep] == sorted_nodes[n]
193+
perm_weights[last_index_to_keep] += perm_weights[n]
211194
else
212-
lastkeep = n
213-
push!(keep, n)
195+
last_index_to_keep = n
196+
push!(indices_to_keep, n)
214197
end
215198
end
216199

217-
# Keep unique nodes
218-
unodes = sortnodes[:, keep]
219-
uweights = weights[keep]
200+
unique_nodes = sorted_nodes[indices_to_keep]
201+
collected_weights = perm_weights[indices_to_keep]
220202

221-
return unodes, uweights
203+
return unique_nodes, collected_weights
222204
end
223205

224206

@@ -231,11 +213,11 @@ end
231213
Compute tensor grid of `N` nodes and corresponding weights `W` for `D` dimensions.
232214
"""
233215
function tensorgrid(N::Vector, W::Vector, D::Integer)
234-
NN = repeat( [N], outer=[D; 1] )
235-
WW = repeat( [W], outer=[D; 1] )
216+
NN = repeat([N], outer=[D; 1])
217+
WW = repeat([W], outer=[D; 1])
236218

237219
tensorN = combvec(NN[:])
238-
tensorW = vec(prod( combvec(WW[:]), 1 ))
220+
tensorW = vec(prod(combvec(WW[:]), 1))
239221

240222
return tensorN, tensorW
241223
end
@@ -248,4 +230,3 @@ function tensorgrid(D::Integer, order::Integer, f::Function=gausshermite)
248230

249231
return tensorN, tensorW
250232
end
251-

test/Ndq.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using SparseGrids
2+
using Test
3+
4+
@testset "N_q^D" begin
5+
@testset "Only one element with D == q" begin
6+
ndq = listNdq(3, 3)
7+
8+
@test ndq == [ones(Int, 3)]
9+
end
10+
11+
12+
@testset "Split a dice" begin
13+
ndq = listNdq(2, 6)
14+
15+
@test ndq == [[5, 1], [4, 2], [3, 3], [2, 4], [1, 5]]
16+
end
17+
end
18+

test/combvec.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using SparseGrids
2+
using Test
3+
4+
@testset "combvec" begin
5+
@testset "Two 2D vectors" begin
6+
a1 = [[1; 3], [2; 4]]
7+
a = combvec(a1)
8+
9+
@test a == [[1; 2], [3; 2], [1; 4], [3; 4]]
10+
end
11+
12+
13+
@testset "A 2D vector and two 1D vectors" begin
14+
a1 = [[1; 2], [3], [4]]
15+
a = combvec(a1)
16+
17+
@test a == [[1; 3; 4], [2; 3; 4]]
18+
end
19+
end
20+

0 commit comments

Comments
 (0)