Skip to content

Commit ecc6ad2

Browse files
CopilotmmckyHumphreyYangCopilot
authored
Migrate np.dot and .dot() method calls to @ operator in library code only (#787)
* Initial plan * Convert np.dot to @ operator in _lqcontrol.py Co-authored-by: mmcky <[email protected]> * Convert np.dot to @ operator in _matrix_eqn.py and _robustlq.py Co-authored-by: mmcky <[email protected]> * Convert np.dot to @ operator in _kalman.py, _lss.py, _lqnash.py, _quadsums.py, and game_theory files Co-authored-by: mmcky <[email protected]> * Complete migration: convert all remaining np.dot to @ operator in test files and markov module Co-authored-by: mmcky <[email protected]> * Convert all remaining .dot method calls to @ operator Co-authored-by: mmcky <[email protected]> * Fix @ operator issues with scalar parameters in lqnash and update docstring Co-authored-by: mmcky <[email protected]> * Revert test files to original state - separate library changes from test updates Per feedback, this keeps only the library code changes with @ operator migrations and reverts all test files to their original state. All 536 tests pass with the library changes, confirming compatibility. Test file updates can be done in a separate PR later. Co-authored-by: mmcky <[email protected]> * Apply suggestions from code review Co-authored-by: Copilot <[email protected]> * Apply suggestions from code review Co-authored-by: Copilot <[email protected]> * Revert "Apply suggestions from code review" This reverts commit 4149f3b. * improve coveralls robustness * fix yaml syntax error * setup env for coveralls-finish * ensure coveralls is installed * Fix coveralls setup: add environment variables, shell activation, and repository checkout Co-authored-by: mmcky <[email protected]> * Fix coveralls setup: revert to using coverallsapp/github-action instead of CLI tool Co-authored-by: mmcky <[email protected]> --------- Co-authored-by: Copilot <[email protected]> Co-authored-by: mmcky <[email protected]> Co-authored-by: Matt McKay <[email protected]> Co-authored-by: Humphrey Yang <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: Humphrey Yang <[email protected]> Co-authored-by: mmcky <[email protected]>
1 parent 4a889ad commit ecc6ad2

File tree

17 files changed

+195
-201
lines changed

17 files changed

+195
-201
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,10 @@ jobs:
1818
matrix:
1919
os: [windows-latest, ubuntu-latest, macos-latest]
2020
python-version: [3.11, 3.12, 3.13]
21-
2221
steps:
2322
- uses: actions/checkout@v5
2423
with:
2524
fetch-depth: 0
26-
2725
- name: Cache conda
2826
uses: actions/cache@v4
2927
env:
@@ -32,7 +30,6 @@ jobs:
3230
path: ~/conda_pkgs_dir
3331
key:
3432
${{ runner.os }}-${{ matrix.python-version }}-conda-${{ env.CACHE_NUMBER }}-${{ hashFiles('environment.yml') }}
35-
3633
- uses: conda-incubator/setup-miniconda@v3
3734
with:
3835
auto-update-conda: true
@@ -42,24 +39,20 @@ jobs:
4239
auto-activate-base: false
4340
use-only-tar-bz2: true
4441
activate-environment: qe
45-
4642
- name: Conda info
4743
shell: bash -l {0}
4844
run: |
4945
conda info
5046
conda list
51-
5247
- name: flake8 Tests
5348
shell: bash -l {0}
5449
run: |
5550
flake8 --select F401, F405, E231 quantecon
56-
5751
- name: Run Tests (pytest)
5852
shell: bash -l {0}
5953
run: |
6054
coverage run -m pytest quantecon
6155
coverage lcov
62-
6356
- name: Coveralls
6457
uses: coverallsapp/github-action@master
6558
if: runner.os == 'Linux'

environment_np2.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ channels:
55
- numba
66
dependencies:
77
- coverage
8+
- coveralls
89
- numpy>=2
910
- scipy
1011
- pandas

quantecon/_compute_fp.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,11 +262,11 @@ def _compute_fixed_point_ig(T, v, max_iter, verbose, print_skip, is_approx_fp,
262262
_, rho = _get_mixed_actions(tableaux_curr, bases_curr)
263263

264264
if Y.ndim <= 2:
265-
x_new = rho.dot(Y[:m])
265+
x_new = rho @ Y[:m]
266266
else:
267267
shape_Y = Y.shape
268268
Y_2d = Y.reshape(shape_Y[0], np.prod(shape_Y[1:]))
269-
x_new = rho.dot(Y_2d[:m]).reshape(shape_Y[1:])
269+
x_new = (rho @ Y_2d[:m]).reshape(shape_Y[1:])
270270

271271
if verbose == 2:
272272
error = np.max(np.abs(y_new - x_new))

quantecon/_dle.py

Lines changed: 73 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,11 @@ def __init__(self, information, technology, preferences):
7676
uc = np.hstack((np.eye(self.nc), np.zeros((self.nc, self.ng))))
7777
ug = np.hstack((np.zeros((self.ng, self.nc)), np.eye(self.ng)))
7878
phiin = np.linalg.inv(np.hstack((self.phic, self.phig)))
79-
phiinc = uc.dot(phiin)
80-
b11 = - self.thetah.dot(phiinc).dot(self.phii)
81-
a1 = self.thetah.dot(phiinc).dot(self.gamma)
82-
a12 = np.vstack((self.thetah.dot(phiinc).dot(
83-
self.ud), np.zeros((self.nk, self.nz))))
79+
phiinc = uc @ phiin
80+
b11 = - self.thetah @ phiinc @ self.phii
81+
a1 = self.thetah @ phiinc @ self.gamma
82+
a12 = np.vstack((self.thetah @ phiinc @
83+
self.ud, np.zeros((self.nk, self.nz))))
8484

8585
# === Creation of the A Matrix for the state transition of the LQ problem === #
8686

@@ -100,11 +100,11 @@ def __init__(self, information, technology, preferences):
100100

101101
# === Define R,W and Q for the payoff function of the LQ problem === #
102102

103-
self.H = np.hstack((self.llambda, self.pih.dot(uc).dot(phiin).dot(self.gamma), self.pih.dot(
104-
uc).dot(phiin).dot(self.ud) - self.ub, -self.pih.dot(uc).dot(phiin).dot(self.phii)))
105-
self.G = ug.dot(phiin).dot(
103+
self.H = np.hstack((self.llambda, self.pih @ uc @ phiin @ self.gamma, self.pih @
104+
uc @ phiin @ self.ud - self.ub, -self.pih @ uc @ phiin @ self.phii))
105+
self.G = (ug @ phiin @
106106
np.hstack((np.zeros((self.nd, self.nh)), self.gamma, self.ud, -self.phii)))
107-
self.S = (self.G.T.dot(self.G) + self.H.T.dot(self.H)) / 2
107+
self.S = (self.G.T @ self.G + self.H.T @ self.H) / 2
108108

109109
self.nx = self.nh + self.nk + self.nz
110110
self.n = self.ni + self.nh + self.nk + self.nz
@@ -122,7 +122,7 @@ def __init__(self, information, technology, preferences):
122122

123123
# === Construct output matrices for our economy using the solution to the LQ problem === #
124124

125-
self.A0 = self.A - self.B.dot(self.F)
125+
self.A0 = self.A - self.B @ self.F
126126

127127
self.Sh = self.A0[0:self.nh, 0:self.nx]
128128
self.Sk = self.A0[self.nh:self.nh + self.nk, 0:self.nx]
@@ -131,12 +131,12 @@ def __init__(self, information, technology, preferences):
131131
self.Si = -self.F
132132
self.Sd = np.hstack((np.zeros((self.nd, self.nh + self.nk)), self.ud))
133133
self.Sb = np.hstack((np.zeros((self.nb, self.nh + self.nk)), self.ub))
134-
self.Sc = uc.dot(phiin).dot(-self.phii.dot(self.Si) +
135-
self.gamma.dot(self.Sk1) + self.Sd)
136-
self.Sg = ug.dot(phiin).dot(-self.phii.dot(self.Si) +
137-
self.gamma.dot(self.Sk1) + self.Sd)
138-
self.Ss = self.llambda.dot(np.hstack((np.eye(self.nh), np.zeros(
139-
(self.nh, self.nk + self.nz))))) + self.pih.dot(self.Sc)
134+
self.Sc = uc @ phiin @ (-self.phii @ self.Si +
135+
self.gamma @ self.Sk1 + self.Sd)
136+
self.Sg = ug @ phiin @ (-self.phii @ self.Si +
137+
self.gamma @ self.Sk1 + self.Sd)
138+
self.Ss = self.llambda @ np.hstack((np.eye(self.nh), np.zeros(
139+
(self.nh, self.nk + self.nz)))) + self.pih @ self.Sc
140140

141141
# === Calculate eigenvalues of A0 === #
142142
self.A110 = self.A0[0:self.nh + self.nk, 0:self.nh + self.nk]
@@ -146,14 +146,14 @@ def __init__(self, information, technology, preferences):
146146
# === Construct matrices for Lagrange Multipliers === #
147147

148148
self.Mk = -2 * self.beta.item() * (np.hstack((np.zeros((self.nk, self.nh)), np.eye(
149-
self.nk), np.zeros((self.nk, self.nz))))).dot(self.P).dot(self.A0)
149+
self.nk), np.zeros((self.nk, self.nz))))) @ self.P @ self.A0
150150
self.Mh = -2 * self.beta.item() * (np.hstack((np.eye(self.nh), np.zeros(
151-
(self.nh, self.nk)), np.zeros((self.nh, self.nz))))).dot(self.P).dot(self.A0)
151+
(self.nh, self.nk)), np.zeros((self.nh, self.nz))))) @ self.P @ self.A0
152152
self.Ms = -(self.Sb - self.Ss)
153-
self.Md = -(np.linalg.inv(np.vstack((self.phic.T, self.phig.T))).dot(
154-
np.vstack((self.thetah.T.dot(self.Mh) + self.pih.T.dot(self.Ms), -self.Sg))))
155-
self.Mc = -(self.thetah.T.dot(self.Mh) + self.pih.T.dot(self.Ms))
156-
self.Mi = -(self.thetak.T.dot(self.Mk))
153+
self.Md = -(np.linalg.inv(np.vstack((self.phic.T, self.phig.T))) @
154+
np.vstack((self.thetah.T @ self.Mh + self.pih.T @ self.Ms, -self.Sg)))
155+
self.Mc = -(self.thetah.T @ self.Mh + self.pih.T @ self.Ms)
156+
self.Mi = -(self.thetak.T @ self.Mk)
157157

158158
def compute_steadystate(self, nnc=2):
159159
"""
@@ -168,13 +168,13 @@ def compute_steadystate(self, nnc=2):
168168
zx = np.eye(self.A0.shape[0])-self.A0
169169
self.zz = nullspace(zx)
170170
self.zz /= self.zz[nnc]
171-
self.css = self.Sc.dot(self.zz)
172-
self.sss = self.Ss.dot(self.zz)
173-
self.iss = self.Si.dot(self.zz)
174-
self.dss = self.Sd.dot(self.zz)
175-
self.bss = self.Sb.dot(self.zz)
176-
self.kss = self.Sk.dot(self.zz)
177-
self.hss = self.Sh.dot(self.zz)
171+
self.css = self.Sc @ self.zz
172+
self.sss = self.Ss @ self.zz
173+
self.iss = self.Si @ self.zz
174+
self.dss = self.Sd @ self.zz
175+
self.bss = self.Sb @ self.zz
176+
self.kss = self.Sk @ self.zz
177+
self.hss = self.Sh @ self.zz
178178

179179
def compute_sequence(self, x0, ts_length=None, Pay=None):
180180
"""
@@ -195,14 +195,14 @@ def compute_sequence(self, x0, ts_length=None, Pay=None):
195195
lq = LQ(self.Q, self.R, self.A, self.B,
196196
self.C, N=self.W, beta=self.beta)
197197
xp, up, wp = lq.compute_sequence(x0, ts_length)
198-
self.h = self.Sh.dot(xp)
199-
self.k = self.Sk.dot(xp)
200-
self.i = self.Si.dot(xp)
201-
self.b = self.Sb.dot(xp)
202-
self.d = self.Sd.dot(xp)
203-
self.c = self.Sc.dot(xp)
204-
self.g = self.Sg.dot(xp)
205-
self.s = self.Ss.dot(xp)
198+
self.h = self.Sh @ xp
199+
self.k = self.Sk @ xp
200+
self.i = self.Si @ xp
201+
self.b = self.Sb @ xp
202+
self.d = self.Sd @ xp
203+
self.c = self.Sc @ xp
204+
self.g = self.Sg @ xp
205+
self.s = self.Ss @ xp
206206

207207
# === Value of J-period risk-free bonds === #
208208
# === See p.145: Equation (7.11.2) === #
@@ -212,12 +212,12 @@ def compute_sequence(self, x0, ts_length=None, Pay=None):
212212
self.R2_Price = np.empty((ts_length + 1, 1))
213213
self.R5_Price = np.empty((ts_length + 1, 1))
214214
for i in range(ts_length + 1):
215-
self.R1_Price[i, 0] = self.beta * e1.dot(self.Mc).dot(np.linalg.matrix_power(
216-
self.A0, 1)).dot(xp[:, i]) / e1.dot(self.Mc).dot(xp[:, i])
217-
self.R2_Price[i, 0] = self.beta**2 * e1.dot(self.Mc).dot(
218-
np.linalg.matrix_power(self.A0, 2)).dot(xp[:, i]) / e1.dot(self.Mc).dot(xp[:, i])
219-
self.R5_Price[i, 0] = self.beta**5 * e1.dot(self.Mc).dot(
220-
np.linalg.matrix_power(self.A0, 5)).dot(xp[:, i]) / e1.dot(self.Mc).dot(xp[:, i])
215+
self.R1_Price[i, 0] = self.beta * e1 @ self.Mc @ np.linalg.matrix_power(
216+
self.A0, 1) @ xp[:, i] / (e1 @ self.Mc @ xp[:, i])
217+
self.R2_Price[i, 0] = self.beta**2 * (e1 @ self.Mc @
218+
np.linalg.matrix_power(self.A0, 2) @ xp[:, i]) / (e1 @ self.Mc @ xp[:, i])
219+
self.R5_Price[i, 0] = self.beta**5 * (e1 @ self.Mc @
220+
np.linalg.matrix_power(self.A0, 5) @ xp[:, i]) / (e1 @ self.Mc @ xp[:, i])
221221

222222
# === Gross rates of return on 1-period risk-free bonds === #
223223
self.R1_Gross = 1 / self.R1_Price
@@ -231,20 +231,20 @@ def compute_sequence(self, x0, ts_length=None, Pay=None):
231231
# === Value of asset whose payout vector is Pay*xt === #
232232
# See p.145: Equation (7.11.1)
233233
if isinstance(Pay, np.ndarray) == True:
234-
self.Za = Pay.T.dot(self.Mc)
234+
self.Za = Pay.T @ self.Mc
235235
self.Q = solve_discrete_lyapunov(
236236
self.A0.T * self.beta**0.5, self.Za)
237237
self.q = self.beta / (1 - self.beta) * \
238-
np.trace(self.C.T.dot(self.Q).dot(self.C))
238+
np.trace(self.C.T @ self.Q @ self.C)
239239
self.Pay_Price = np.empty((ts_length + 1, 1))
240240
self.Pay_Gross = np.empty((ts_length + 1, 1))
241241
self.Pay_Gross[0, 0] = np.nan
242242
for i in range(ts_length + 1):
243-
self.Pay_Price[i, 0] = (xp[:, i].T.dot(self.Q).dot(
244-
xp[:, i]) + self.q) / e1.dot(self.Mc).dot(xp[:, i])
243+
self.Pay_Price[i, 0] = (xp[:, i].T @ self.Q @
244+
xp[:, i] + self.q) / (e1 @ self.Mc @ xp[:, i])
245245
for i in range(ts_length):
246246
self.Pay_Gross[i + 1, 0] = self.Pay_Price[i + 1,
247-
0] / (self.Pay_Price[i, 0] - Pay.dot(xp[:, i]))
247+
0] / (self.Pay_Price[i, 0] - Pay @ xp[:, i])
248248
return
249249

250250
def irf(self, ts_length=100, shock=None):
@@ -276,22 +276,22 @@ def irf(self, ts_length=100, shock=None):
276276
self.b_irf = np.empty((ts_length, self.nb))
277277

278278
for i in range(ts_length):
279-
self.c_irf[i, :] = self.Sc.dot(
280-
np.linalg.matrix_power(self.A0, i)).dot(self.C).dot(shock).T
281-
self.s_irf[i, :] = self.Ss.dot(
282-
np.linalg.matrix_power(self.A0, i)).dot(self.C).dot(shock).T
283-
self.i_irf[i, :] = self.Si.dot(
284-
np.linalg.matrix_power(self.A0, i)).dot(self.C).dot(shock).T
285-
self.k_irf[i, :] = self.Sk.dot(
286-
np.linalg.matrix_power(self.A0, i)).dot(self.C).dot(shock).T
287-
self.h_irf[i, :] = self.Sh.dot(
288-
np.linalg.matrix_power(self.A0, i)).dot(self.C).dot(shock).T
289-
self.g_irf[i, :] = self.Sg.dot(
290-
np.linalg.matrix_power(self.A0, i)).dot(self.C).dot(shock).T
291-
self.d_irf[i, :] = self.Sd.dot(
292-
np.linalg.matrix_power(self.A0, i)).dot(self.C).dot(shock).T
293-
self.b_irf[i, :] = self.Sb.dot(
294-
np.linalg.matrix_power(self.A0, i)).dot(self.C).dot(shock).T
279+
self.c_irf[i, :] = (self.Sc @
280+
np.linalg.matrix_power(self.A0, i) @ self.C @ shock).T
281+
self.s_irf[i, :] = (self.Ss @
282+
np.linalg.matrix_power(self.A0, i) @ self.C @ shock).T
283+
self.i_irf[i, :] = (self.Si @
284+
np.linalg.matrix_power(self.A0, i) @ self.C @ shock).T
285+
self.k_irf[i, :] = (self.Sk @
286+
np.linalg.matrix_power(self.A0, i) @ self.C @ shock).T
287+
self.h_irf[i, :] = (self.Sh @
288+
np.linalg.matrix_power(self.A0, i) @ self.C @ shock).T
289+
self.g_irf[i, :] = (self.Sg @
290+
np.linalg.matrix_power(self.A0, i) @ self.C @ shock).T
291+
self.d_irf[i, :] = (self.Sd @
292+
np.linalg.matrix_power(self.A0, i) @ self.C @ shock).T
293+
self.b_irf[i, :] = (self.Sb @
294+
np.linalg.matrix_power(self.A0, i) @ self.C @ shock).T
295295

296296
return
297297

@@ -305,13 +305,13 @@ def canonical(self):
305305
Ac2 = np.hstack((np.zeros((self.nz, self.nh)), self.a22))
306306
Ac = np.vstack((Ac1, Ac2))
307307
Bc = np.vstack((self.thetah, np.zeros((self.nz, self.nc))))
308-
Rc1 = np.hstack((self.llambda.T.dot(self.llambda), -
309-
self.llambda.T.dot(self.ub)))
310-
Rc2 = np.hstack((-self.ub.T.dot(self.llambda), self.ub.T.dot(self.ub)))
308+
Rc1 = np.hstack((self.llambda.T @ self.llambda, -
309+
self.llambda.T @ self.ub))
310+
Rc2 = np.hstack((-self.ub.T @ self.llambda, self.ub.T @ self.ub))
311311
Rc = np.vstack((Rc1, Rc2))
312-
Qc = self.pih.T.dot(self.pih)
312+
Qc = self.pih.T @ self.pih
313313
Nc = np.hstack(
314-
(self.pih.T.dot(self.llambda), -self.pih.T.dot(self.ub)))
314+
(self.pih.T @ self.llambda, -self.pih.T @ self.ub))
315315

316316
lq_aux = LQ(Qc, Rc, Ac, Bc, N=Nc, beta=self.beta)
317317

@@ -320,9 +320,9 @@ def canonical(self):
320320
self.F_b = F1[:, 0:self.nh]
321321
self.F_f = F1[:, self.nh:]
322322

323-
self.pihat = np.linalg.cholesky(self.pih.T.dot(
324-
self.pih) + self.beta.dot(self.thetah.T).dot(P1[0:self.nh, 0:self.nh]).dot(self.thetah)).T
325-
self.llambdahat = self.pihat.dot(self.F_b)
326-
self.ubhat = - self.pihat.dot(self.F_f)
323+
self.pihat = np.linalg.cholesky((self.pih.T @
324+
self.pih) + (self.beta @ self.thetah.T @ P1[0:self.nh, 0:self.nh] @ self.thetah)).T
325+
self.llambdahat = self.pihat @ self.F_b
326+
self.ubhat = - self.pihat @ self.F_f
327327

328328
return

0 commit comments

Comments
 (0)