quantecon 0.11.2__tar.gz → 0.11.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. {quantecon-0.11.2 → quantecon-0.11.3}/CHANGELOG.md +4 -0
  2. quantecon-0.11.3/CONTRIBUTING.md +11 -0
  3. {quantecon-0.11.2 → quantecon-0.11.3}/PKG-INFO +1 -1
  4. {quantecon-0.11.2 → quantecon-0.11.3}/environment_np2.yml +1 -1
  5. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/__init__.py +1 -1
  6. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_compute_fp.py +1 -1
  7. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_dle.py +25 -10
  8. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_gridtools.py +1 -1
  9. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/repeated_game.py +33 -11
  10. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/tests/test_repeated_game.py +23 -0
  11. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/vertex_enumeration.py +7 -3
  12. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/markov/tests/test_approximation.py +2 -2
  13. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/util/numba.py +1 -1
  14. {quantecon-0.11.2 → quantecon-0.11.3}/CITATION.cff +0 -0
  15. {quantecon-0.11.2 → quantecon-0.11.3}/LICENSE +0 -0
  16. {quantecon-0.11.2 → quantecon-0.11.3}/MANIFEST.in +0 -0
  17. {quantecon-0.11.2 → quantecon-0.11.3}/README.md +0 -0
  18. {quantecon-0.11.2 → quantecon-0.11.3}/pyproject.toml +0 -0
  19. {quantecon-0.11.2 → quantecon-0.11.3}/pytest.ini +0 -0
  20. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_arma.py +0 -0
  21. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_ce_util.py +0 -0
  22. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_discrete_rv.py +0 -0
  23. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_ecdf.py +0 -0
  24. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_estspec.py +0 -0
  25. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_filter.py +0 -0
  26. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_graph_tools.py +0 -0
  27. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_inequality.py +0 -0
  28. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_ivp.py +0 -0
  29. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_kalman.py +0 -0
  30. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_lae.py +0 -0
  31. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_lqcontrol.py +0 -0
  32. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_lqnash.py +0 -0
  33. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_lss.py +0 -0
  34. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_matrix_eqn.py +0 -0
  35. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_quadsums.py +0 -0
  36. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_rank_nullspace.py +0 -0
  37. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/_robustlq.py +0 -0
  38. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/arma.py +0 -0
  39. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/ce_util.py +0 -0
  40. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/compute_fp.py +0 -0
  41. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/discrete_rv.py +0 -0
  42. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/distributions.py +0 -0
  43. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/dle.py +0 -0
  44. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/ecdf.py +0 -0
  45. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/estspec.py +0 -0
  46. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/filter.py +0 -0
  47. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/__init__.py +0 -0
  48. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/brd.py +0 -0
  49. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/fictplay.py +0 -0
  50. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/game_converters.py +0 -0
  51. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/game_generators/__init__.py +0 -0
  52. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/game_generators/bimatrix_generators.py +0 -0
  53. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/game_generators/tests/__init__.py +0 -0
  54. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/game_generators/tests/test_bimatrix_generators.py +0 -0
  55. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/howson_lcp.py +0 -0
  56. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/lemke_howson.py +0 -0
  57. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/localint.py +0 -0
  58. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/logitdyn.py +0 -0
  59. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/mclennan_tourky.py +0 -0
  60. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/normal_form_game.py +0 -0
  61. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/polymatrix_game.py +0 -0
  62. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/pure_nash.py +0 -0
  63. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/random.py +0 -0
  64. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/support_enumeration.py +0 -0
  65. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/tests/__init__.py +0 -0
  66. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/tests/game_files/big_polym.gam +0 -0
  67. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/tests/game_files/minimum_effort_game.gam +0 -0
  68. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/tests/game_files/triggers_back_case.gam +0 -0
  69. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/tests/test_brd.py +0 -0
  70. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/tests/test_fictplay.py +0 -0
  71. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/tests/test_game_converters.py +0 -0
  72. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/tests/test_howson_lcp.py +0 -0
  73. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/tests/test_lemke_howson.py +0 -0
  74. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/tests/test_localint.py +0 -0
  75. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/tests/test_logitdyn.py +0 -0
  76. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/tests/test_mclennan_tourky.py +0 -0
  77. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/tests/test_normal_form_game.py +0 -0
  78. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/tests/test_polymatrix_game.py +0 -0
  79. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/tests/test_pure_nash.py +0 -0
  80. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/tests/test_random.py +0 -0
  81. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/tests/test_support_enumeration.py +0 -0
  82. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/tests/test_utilities.py +0 -0
  83. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/tests/test_vertex_enumeration.py +0 -0
  84. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/game_theory/utilities.py +0 -0
  85. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/graph_tools.py +0 -0
  86. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/gridtools.py +0 -0
  87. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/inequality.py +0 -0
  88. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/ivp.py +0 -0
  89. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/kalman.py +0 -0
  90. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/lae.py +0 -0
  91. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/lqcontrol.py +0 -0
  92. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/lqnash.py +0 -0
  93. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/lss.py +0 -0
  94. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/markov/__init__.py +0 -0
  95. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/markov/_ddp_linprog_simplex.py +0 -0
  96. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/markov/approximation.py +0 -0
  97. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/markov/core.py +0 -0
  98. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/markov/ddp.py +0 -0
  99. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/markov/estimate.py +0 -0
  100. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/markov/gth_solve.py +0 -0
  101. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/markov/random.py +0 -0
  102. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/markov/tests/__init__.py +0 -0
  103. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/markov/tests/test_core.py +0 -0
  104. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/markov/tests/test_ddp.py +0 -0
  105. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/markov/tests/test_estimate.py +0 -0
  106. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/markov/tests/test_gth_solve.py +0 -0
  107. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/markov/tests/test_random.py +0 -0
  108. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/markov/tests/test_utilities.py +0 -0
  109. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/markov/utilities.py +0 -0
  110. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/matrix_eqn.py +0 -0
  111. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/optimize/__init__.py +0 -0
  112. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/optimize/lcp_lemke.py +0 -0
  113. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/optimize/linprog_simplex.py +0 -0
  114. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/optimize/minmax.py +0 -0
  115. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/optimize/nelder_mead.py +0 -0
  116. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/optimize/pivoting.py +0 -0
  117. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/optimize/root_finding.py +0 -0
  118. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/optimize/scalar_maximization.py +0 -0
  119. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/optimize/tests/__init__.py +0 -0
  120. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/optimize/tests/test_lcp_lemke.py +0 -0
  121. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/optimize/tests/test_linprog_simplex.py +0 -0
  122. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/optimize/tests/test_minmax.py +0 -0
  123. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/optimize/tests/test_nelder_mead.py +0 -0
  124. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/optimize/tests/test_root_finding.py +0 -0
  125. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/optimize/tests/test_scalar_max.py +0 -0
  126. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/quad.py +0 -0
  127. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/quadsums.py +0 -0
  128. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/random/__init__.py +0 -0
  129. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/random/tests/__init__.py +0 -0
  130. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/random/tests/test_utilities.py +0 -0
  131. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/random/utilities.py +0 -0
  132. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/rank_nullspace.py +0 -0
  133. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/robustlq.py +0 -0
  134. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/timings/__init__.py +0 -0
  135. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/timings/timings.py +0 -0
  136. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/util/__init__.py +0 -0
  137. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/util/array.py +0 -0
  138. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/util/combinatorics.py +0 -0
  139. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/util/common_messages.py +0 -0
  140. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/util/compat.py +0 -0
  141. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/util/notebooks.py +0 -0
  142. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/util/random.py +0 -0
  143. {quantecon-0.11.2 → quantecon-0.11.3}/quantecon/util/timing.py +0 -0
@@ -2,6 +2,10 @@
2
2
 
3
3
  Future CHANGELOG entries will be documented under the [release notes on GitHub](https://github.com/QuantEcon/QuantEcon.py/releases)
4
4
 
5
+ ## Ver 0.11.3 (8th-July-2026)
6
+
7
+ See [release notes](https://github.com/QuantEcon/QuantEcon.py/releases/tag/v0.11.3)
8
+
5
9
  ## Ver 0.11.2 (6th-April-2026)
6
10
 
7
11
  See [release notes](https://github.com/QuantEcon/QuantEcon.py/releases/tag/v0.11.2)
@@ -0,0 +1,11 @@
1
+ # Contributing to QuantEcon.py
2
+
3
+ Thanks for your interest in contributing to QuantEcon.py.
4
+
5
+ The contribution guide is maintained in the project documentation:
6
+
7
+ - [Rendered guide on Read the Docs](https://quanteconpy.readthedocs.io/en/latest/contributing.html)
8
+ - [Source file in this repository](docs/source/contributing.rst)
9
+
10
+ The guide covers where to start, how to set up a development environment,
11
+ and expectations for tests and documentation.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quantecon
3
- Version: 0.11.2
3
+ Version: 0.11.3
4
4
  Summary: Import the main names to top level.
5
5
  Keywords: quantitative,economics
6
6
  Author-email: QuantEcon Project <admin@quantecon.org>
@@ -10,7 +10,7 @@ dependencies:
10
10
  - numpy>=2
11
11
  - scipy
12
12
  - pandas
13
- - numba>=0.64
13
+ - numba=0.66.0rc1
14
14
  - sympy
15
15
  - ipython
16
16
  - flake8
@@ -3,7 +3,7 @@
3
3
  Import the main names to top level.
4
4
  """
5
5
 
6
- __version__ = '0.11.2'
6
+ __version__ = '0.11.3'
7
7
 
8
8
  try:
9
9
  import numba
@@ -357,7 +357,7 @@ def _square_sum(a): # pragma: no cover
357
357
  pass
358
358
 
359
359
 
360
- @overload(_square_sum, jit_options={'cache':True})
360
+ @overload(_square_sum, jit_options={'cache': True})
361
361
  def _square_sum_ol(a):
362
362
  if isinstance(a, types.Number):
363
363
  return lambda a: a**2
@@ -211,13 +211,26 @@ class DLE(object):
211
211
  self.R1_Price = np.empty((ts_length + 1, 1))
212
212
  self.R2_Price = np.empty((ts_length + 1, 1))
213
213
  self.R5_Price = np.empty((ts_length + 1, 1))
214
+ # Hoist the loop-invariant terms out of the loop: ``e1 @ self.Mc`` and
215
+ # the matrix powers of ``self.A0`` do not depend on ``i``.
216
+ eMc = e1 @ self.Mc
217
+ A0_1 = np.linalg.matrix_power(self.A0, 1)
218
+ A0_2 = np.linalg.matrix_power(self.A0, 2)
219
+ A0_5 = np.linalg.matrix_power(self.A0, 5)
220
+ # Each price expression evaluates to a size-1 array (``self.beta`` is a
221
+ # (1, 1) array and ``eMc`` is a row vector). Coerce the size-1 result to
222
+ # a scalar with ``.item()`` *after* dividing, so assignment works under
223
+ # NumPy >= 2.4 (which raises instead of silently converting an
224
+ # ``ndim > 0`` array to a scalar on assignment). Keeping the division
225
+ # inside NumPy preserves the pre-2.4 behaviour when ``denom == 0`` (an
226
+ # ``inf``/``nan`` with a warning, rather than a ``ZeroDivisionError``).
227
+ # See #839.
214
228
  for i in range(ts_length + 1):
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])
229
+ xi = xp[:, i]
230
+ denom = eMc @ xi
231
+ self.R1_Price[i, 0] = (self.beta * eMc @ A0_1 @ xi / denom).item()
232
+ self.R2_Price[i, 0] = (self.beta**2 * (eMc @ A0_2 @ xi) / denom).item()
233
+ self.R5_Price[i, 0] = (self.beta**5 * (eMc @ A0_5 @ xi) / denom).item()
221
234
 
222
235
  # === Gross rates of return on 1-period risk-free bonds === #
223
236
  self.R1_Gross = 1 / self.R1_Price
@@ -239,12 +252,14 @@ class DLE(object):
239
252
  self.Pay_Price = np.empty((ts_length + 1, 1))
240
253
  self.Pay_Gross = np.empty((ts_length + 1, 1))
241
254
  self.Pay_Gross[0, 0] = np.nan
255
+ # As above, coerce each size-1 result to a scalar with ``.item()``
256
+ # so assignment works under NumPy >= 2.4. See #839.
242
257
  for i in range(ts_length + 1):
243
- self.Pay_Price[i, 0] = (xp[:, i].T @ self.Q @
244
- xp[:, i] + self.q) / (e1 @ self.Mc @ xp[:, i])
258
+ self.Pay_Price[i, 0] = ((xp[:, i].T @ self.Q @
259
+ xp[:, i] + self.q) / (e1 @ self.Mc @ xp[:, i])).item()
245
260
  for i in range(ts_length):
246
- self.Pay_Gross[i + 1, 0] = self.Pay_Price[i + 1,
247
- 0] / (self.Pay_Price[i, 0] - Pay @ xp[:, i])
261
+ self.Pay_Gross[i + 1, 0] = (self.Pay_Price[i + 1,
262
+ 0] / (self.Pay_Price[i, 0] - Pay @ xp[:, i])).item()
248
263
  return
249
264
 
250
265
  def irf(self, ts_length=100, shock=None):
@@ -254,7 +254,7 @@ def _cartesian_index(indices, nums_grids):
254
254
  n = len(indices)
255
255
  idx = 0
256
256
  de_cumprod = 1
257
- for i in range(1,n+1):
257
+ for i in range(1, n+1):
258
258
  idx += de_cumprod * indices[n-i]
259
259
  de_cumprod *= nums_grids[n-i]
260
260
  return idx
@@ -60,7 +60,9 @@ class RepeatedGame:
60
60
  max_iter : scalar(int)
61
61
  Maximum number of iterations.
62
62
  u_init : ndarray(float, ndim=1)
63
- The initial guess of threat points.
63
+ The initial guess of threat points. By default, the
64
+ minimum payoff of each player is used. A supplied
65
+ guess is clipped from below by the minimum payoffs.
64
66
 
65
67
  Returns
66
68
  -------
@@ -88,7 +90,7 @@ class RepeatedGame:
88
90
 
89
91
 
90
92
  def _equilibrium_payoffs_abreu_sannikov(rpg, tol=1e-12, max_iter=500,
91
- u_init=np.zeros(2)):
93
+ u_init=None):
92
94
  """
93
95
  Using 'abreu_sannikov' algorithm to compute the set of payoff pairs
94
96
  of all pure-strategy subgame-perfect equilibria with public randomization
@@ -106,8 +108,11 @@ def _equilibrium_payoffs_abreu_sannikov(rpg, tol=1e-12, max_iter=500,
106
108
  max_iter : scalar(int), optional(default=500)
107
109
  Maximum number of iterations.
108
110
 
109
- u_init : ndarray(float, ndim=1), optional(default=np.zeros(2))
110
- The initial guess of threat points.
111
+ u_init : ndarray(float, ndim=1), optional
112
+ The initial guess of threat points. It must not exceed the
113
+ threat points of the equilibrium payoff set. By default, the
114
+ minimum payoff of each player is used. A supplied guess is
115
+ clipped from below by the minimum payoffs.
111
116
 
112
117
  Returns
113
118
  -------
@@ -142,8 +147,14 @@ def _equilibrium_payoffs_abreu_sannikov(rpg, tol=1e-12, max_iter=500,
142
147
  # count the new points generated in each iteration
143
148
  n_new_pt = 0
144
149
 
145
- # copy the threat points
146
- u = np.copy(u_init)
150
+ # initial threat points: the minimum payoffs by default; a supplied
151
+ # guess is clipped from below by the minimum payoffs
152
+ payoff_mins = np.array([sg.payoff_arrays[0].min(),
153
+ sg.payoff_arrays[1].min()], dtype=float)
154
+ if u_init is None:
155
+ u = payoff_mins
156
+ else:
157
+ u = np.maximum(np.asarray(u_init, dtype=float), payoff_mins)
147
158
 
148
159
  # initialization
149
160
  payoff_pts = \
@@ -163,18 +174,29 @@ def _equilibrium_payoffs_abreu_sannikov(rpg, tol=1e-12, max_iter=500,
163
174
  hull.equations, u, IC, action_profile_payoff,
164
175
  extended_payoff, new_pts, W_new)
165
176
 
177
+ if n_new_pt == 0:
178
+ msg = ("no incentive-compatible payoff vector found; the game "
179
+ "may have no pure-action subgame perfect equilibrium "
180
+ "for this value of delta")
181
+ raise ValueError(msg)
182
+
166
183
  n_iter += 1
167
184
  if n_iter >= max_iter:
168
185
  break
169
186
 
170
- # check convergence
171
- if n_new_pt == n_old_pt:
187
+ # update threat points; done before the convergence check, since
188
+ # the set has not converged while the threat points are moving
189
+ u_old = np.copy(u)
190
+ _update_u(u, W_new[:n_new_pt])
191
+
192
+ # check convergence; u is updated only by assignment from payoff
193
+ # values in _update_u, so exact comparison is appropriate, and
194
+ # stopping while u is still moving by sub-tol amounts can leave
195
+ # near-duplicate vertices in the hull
196
+ if n_new_pt == n_old_pt and np.array_equal(u, u_old):
172
197
  if np.linalg.norm(W_new[:n_new_pt] - W_old[:n_new_pt]) < tol:
173
198
  break
174
199
 
175
- # update threat points
176
- _update_u(u, W_new[:n_new_pt])
177
-
178
200
  hull = ConvexHull(W_new[:n_new_pt])
179
201
 
180
202
  return hull
@@ -3,6 +3,7 @@ Tests for repeated_game.py
3
3
 
4
4
  """
5
5
  import numpy as np
6
+ import pytest
6
7
  from numpy.testing import assert_allclose
7
8
  from quantecon.game_theory import NormalFormGame, RepeatedGame
8
9
 
@@ -47,3 +48,25 @@ class TestAS():
47
48
  hull = rpg.equilibrium_payoffs(method=method,
48
49
  options={'u_init': d['u']})
49
50
  assert_allclose(hull.points[hull.vertices], d['vertices'])
51
+
52
+ def test_abreu_sannikov_default_u_init(self):
53
+ for d in self.game_dicts:
54
+ rpg = RepeatedGame(d['sg'], d['delta'])
55
+ hull = rpg.equilibrium_payoffs()
56
+ assert_allclose(hull.points[hull.vertices], d['vertices'])
57
+
58
+ def test_abreu_sannikov_low_u_init(self):
59
+ # A u_init below the threat points must not stop the iteration
60
+ # before the first threat point update
61
+ for d in self.game_dicts:
62
+ rpg = RepeatedGame(d['sg'], d['delta'])
63
+ hull = rpg.equilibrium_payoffs(options={'u_init': np.zeros(2)})
64
+ assert_allclose(hull.points[hull.vertices], d['vertices'])
65
+
66
+ def test_abreu_sannikov_no_pure_action_spe(self):
67
+ # Game with no pure-action subgame perfect equilibrium at low delta
68
+ bimatrix = [[(1, -1), (-1, 2)],
69
+ [(-2, 2), (1, 0)]]
70
+ rpg = RepeatedGame(NormalFormGame(bimatrix), 0.05)
71
+ with pytest.raises(ValueError):
72
+ rpg.equilibrium_payoffs()
@@ -136,18 +136,22 @@ def _vertex_enumeration_gen(labelings_bits_tup, equations_tup, trans_recips):
136
136
  m, n = equations_tup[0].shape[1] - 1, equations_tup[1].shape[1] - 1
137
137
  num_vertices0, num_vertices1 = \
138
138
  equations_tup[0].shape[0], equations_tup[1].shape[0]
139
- ZERO_LABELING0_BITS = (np.uint64(1) << np.uint64(m)) - np.uint64(1)
140
139
  COMPLETE_LABELING_BITS = (np.uint64(1) << np.uint64(m+n)) - np.uint64(1)
140
+ ZERO_LABELING1_BITS = \
141
+ ((np.uint64(1) << np.uint64(n)) - np.uint64(1)) << np.uint64(m)
141
142
 
142
143
  labelings_bits_dict1 = Dict.empty(key_type=types.uint64,
143
144
  value_type=types.intp)
144
145
  for j in range(num_vertices1):
145
146
  labelings_bits_dict1[labelings_bits_tup[1][j]] = j
146
147
 
148
+ try:
149
+ del labelings_bits_dict1[ZERO_LABELING1_BITS]
150
+ except Exception:
151
+ pass
152
+
147
153
  for i in range(num_vertices0):
148
154
  bits0 = labelings_bits_tup[0][i]
149
- if bits0 == ZERO_LABELING0_BITS:
150
- continue
151
155
  complement0 = bits0 ^ COMPLETE_LABELING_BITS
152
156
  try:
153
157
  j = labelings_bits_dict1[complement0]
@@ -179,9 +179,9 @@ class TestDiscreteVar:
179
179
  [0.00000000e+00, 4.70588235e-01, 3.08823529e-01, 0.00000000e+00,
180
180
  1.76470588e-01, 4.41176471e-02]]
181
181
 
182
- self.A, self.C, self.S_out, self.P_out, self.S_out_orderF,\
182
+ self.A, self.C, self.S_out, self.P_out, self.S_out_orderF, \
183
183
  self.P_out_orderF, self.P_out_non_square \
184
- = map(np.array,(self.A, self.C, self.S_out, self.P_out,
184
+ = map(np.array, (self.A, self.C, self.S_out, self.P_out,
185
185
  self.S_out_orderF, self.P_out_orderF,
186
186
  self.P_out_non_square))
187
187
 
@@ -21,7 +21,7 @@ def _numba_linalg_solve(a, b): # pragma: no cover
21
21
  pass
22
22
 
23
23
 
24
- @overload(_numba_linalg_solve, jit_options={'cache':True})
24
+ @overload(_numba_linalg_solve, jit_options={'cache': True})
25
25
  def _numba_linalg_solve_ol(a, b):
26
26
  """
27
27
  Solve the linear equation ax = b directly calling a Numba internal
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes