Skip to content

Commit bef80e7

Browse files
committed
Added cartesian product.
1 parent fb2afbf commit bef80e7

File tree

2 files changed

+265
-0
lines changed

2 files changed

+265
-0
lines changed

quantecon/cartesian.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
"""
2+
Filename: cartesian.py
3+
4+
Authors: Pablo Winant
5+
6+
Implements cartesian products and regular cartesian grids.
7+
"""
8+
9+
import numpy
10+
from numba import njit
11+
12+
def cartesian(nodes, order='C'):
13+
'''Cartesian product of a list of arrays
14+
15+
Parameters:
16+
-----------
17+
nodes: (list of 1d-arrays)
18+
order: ('C' or 'F') order in which the product is enumerated
19+
20+
Returns:
21+
--------
22+
out: (2d-array) each line corresponds to one point of the product space
23+
'''
24+
25+
nodes = [numpy.array(e) for e in nodes]
26+
shapes = [e.shape[0] for e in nodes]
27+
28+
n = len(nodes)
29+
l = numpy.prod(shapes)
30+
out = numpy.zeros((l, n))
31+
32+
33+
if order == 'C':
34+
repetitions = numpy.cumprod([1] + shapes[:-1])
35+
else:
36+
shapes.reverse()
37+
sh = [1] + shapes[:-1]
38+
repetitions = numpy.cumprod(sh)
39+
repetitions = repetitions.tolist()
40+
repetitions.reverse()
41+
42+
for i in range(n):
43+
_repeat_1d(nodes[i], repetitions[i], out[:,i])
44+
45+
return out
46+
47+
def mlinspace(a, b, nums, order='C'):
48+
'''Constructs a regular cartesian grid
49+
50+
Parameters:
51+
-----------
52+
a: (1d-array) lower bounds in each dimension
53+
b: (1d-array) upper bounds in each dimension
54+
nums: (1d-array) number of nodes along each dimension
55+
order: ('C' or 'F') order in which the product is enumerated
56+
57+
Returns:
58+
--------
59+
out: (2d-array) each line corresponds to one point of the product space
60+
'''
61+
62+
a = numpy.array(a, dtype='float64')
63+
b = numpy.array(a, dtype='float64')
64+
nums = numpy.array(a, dtype='int64')
65+
nodes = [numpy.linspace(a[i], b[i], nums[i]) for i in orders]
66+
67+
return cartesian(nodes, order=order)
68+
69+
70+
@njit
71+
def _repeat_1d(x, K, out):
72+
'''Repeats each element of a vector many times and repeats the whole result many times
73+
74+
Parameters
75+
----------
76+
77+
x: (1d array) vector to be repeated
78+
K: (int) number of times each element of x is repeated (inner iterations)
79+
out: (1d array) placeholder for the result
80+
81+
Returns
82+
-------
83+
None
84+
'''
85+
86+
N = x.shape[0]
87+
L = out.shape[0] // (K*N) # number of outer iterations
88+
# K # number of inner iterations
89+
90+
# the result out should enumerate in C-order the elements
91+
# of a 3-dimensional array T of dimensions (K,N,L)
92+
# such that for all k,n,l, we have T[k,n,l] == x[n]
93+
94+
for n in range(N):
95+
val = x[n]
96+
for k in range(K):
97+
for l in range(L):
98+
ind = k*N*L + n*L + l
99+
out[ind] = val

quantecon/tests/test_cartesian.py

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
"""
2+
Author: Pablo Winant
3+
Filename: test_cartesian.py
4+
5+
Tests for cartesian.py file
6+
7+
"""
8+
9+
from quantecon.cartesian import cartesian, _repeat_1d
10+
11+
def test_cartesian_C_order():
12+
13+
from numpy import linspace
14+
x = linspace(0,9,10)
15+
16+
prod = cartesian([x,x,x])
17+
18+
correct = True
19+
for i in range(999):
20+
n = prod[i,0]*100+prod[i,1]*10+prod[i,2]
21+
correct *= (i == n)
22+
23+
assert(correct)
24+
25+
def test_cartesian_F_order():
26+
27+
from numpy import linspace
28+
x = linspace(0,9,10)
29+
30+
prod = cartesian([x,x,x], order='F')
31+
32+
correct = True
33+
for i in range(999):
34+
n = prod[i,2]*100+prod[i,1]*10+prod[i,0]
35+
correct *= (i == n)
36+
37+
assert(correct)
38+
39+
def test_performance_C():
40+
41+
from numpy import linspace, column_stack, repeat, tile
42+
import time
43+
44+
N_x = 1000
45+
N_y = 7777
46+
x = linspace(1,N_x,N_x)
47+
y = linspace(1,N_y,N_y)
48+
49+
cartesian([x[:10],y[:10]]) # warmup
50+
51+
t1 = time.time()
52+
for i in range(100):
53+
prod = cartesian([x,y])
54+
t2 = time.time()
55+
# print(prod.shape)
56+
57+
# compute the same produce using numpy:
58+
import numpy
59+
60+
t3 = time.time()
61+
for i in range(100):
62+
prod_numpy = column_stack([
63+
repeat(x,N_y),
64+
tile(y,N_x)
65+
])
66+
t4 = time.time()
67+
68+
print("Timings for 'cartesian' (C order)")
69+
print("Cartesian: {}".format(t2-t1))
70+
print("Numpy: {}".format(t4-t3))
71+
assert(abs(prod-prod_numpy).max()==0)
72+
73+
def test_performance_F():
74+
75+
from numpy import linspace, column_stack, repeat, tile
76+
import time
77+
78+
N_x = 1000
79+
N_y = 7777
80+
x = linspace(1,N_x,N_x)
81+
y = linspace(1,N_y,N_y)
82+
83+
cartesian([x[:10],y[:10]]) # warmup
84+
85+
t1 = time.time()
86+
for i in range(100):
87+
prod = cartesian([x,y], order='F')
88+
t2 = time.time()
89+
# print(prod.shape)
90+
91+
# compute the same produce using numpy:
92+
import numpy
93+
94+
t3 = time.time()
95+
for i in range(100):
96+
prod_numpy = column_stack([
97+
tile(x,N_y),
98+
repeat(y,N_x)
99+
])
100+
t4 = time.time()
101+
102+
print("Timings for 'cartesian'(Fortran order)")
103+
print("Cartesian: {}".format(t2-t1))
104+
print("Numpy: {}".format(t4-t3))
105+
assert(abs(prod-prod_numpy).max()==0)
106+
107+
def test_mlinsplace():
108+
109+
from numpy import linspace
110+
from quantecon.cartesian import mlinspace
111+
112+
grid1 = mlinspace([-1,-1],[2,3],[30,50])
113+
grid2 = cartesian([linspace(-1,2,30), linspace(-1,3,50)])
114+
115+
def test_tile():
116+
117+
from numpy import linspace, tile, zeros
118+
x = linspace(1,100, 100)
119+
120+
import time
121+
t1 = time.time()
122+
t_repeat = zeros(100*1000)
123+
_repeat_1d(x,1,t_repeat)
124+
t2 = time.time()
125+
126+
t3 = time.time()
127+
t_numpy = tile(x, 1000)
128+
t4 = time.time()
129+
130+
print("Timings for 'tile' operation")
131+
print("Repeat_1d: {}".format(t2-t1))
132+
print("Numpy: {}".format(t4-t3))
133+
134+
assert( abs(t_numpy-t_repeat).max())
135+
136+
def test_repeat():
137+
138+
from numpy import linspace, repeat, zeros
139+
x = linspace(1,100 , 100)
140+
141+
import time
142+
t1 = time.time()
143+
t_repeat = zeros(100*1000)
144+
_repeat_1d(x,1000,t_repeat)
145+
t2 = time.time()
146+
147+
t3 = time.time()
148+
t_numpy = repeat(x, 1000)
149+
t4 = time.time()
150+
151+
print("Timings for 'repeat' operation")
152+
print("Repeat_1d: {}".format(t2-t1))
153+
print("Numpy: {}".format(t4-t3))
154+
155+
assert( abs(t_numpy-t_repeat).max())
156+
157+
158+
159+
160+
if __name__ == '__main__':
161+
test_cartesian_C_order()
162+
test_cartesian_F_order()
163+
test_performance_C()
164+
test_performance_F()
165+
test_tile()
166+
test_repeat()

0 commit comments

Comments
 (0)