From 2f827370ec4652ec4607603d844da63e62a0b0e8 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Thu, 14 Apr 2016 16:58:02 -0400 Subject: [PATCH 1/6] Remove optional numba installation code and make numba jit a strict dependancy --- quantecon/lss.py | 8 +-- quantecon/markov/gth_solve.py | 102 +++++++++++++++++----------------- quantecon/markov/random.py | 8 +-- quantecon/random/utilities.py | 9 +-- quantecon/util/__init__.py | 1 - quantecon/util/array.py | 11 +--- quantecon/util/external.py | 24 -------- 7 files changed, 61 insertions(+), 102 deletions(-) delete mode 100644 quantecon/util/external.py diff --git a/quantecon/lss.py b/quantecon/lss.py index dbfc2d029..1deb600da 100644 --- a/quantecon/lss.py +++ b/quantecon/lss.py @@ -9,10 +9,10 @@ import numpy as np from numpy.random import multivariate_normal from scipy.linalg import solve +from numba import jit -#-Check if Numba is Available-# -from .util import numba_installed, jit - +#TODO: Should this be specified with (nopython=True?) +@jit def simulate_linear_model(A, x0, v, ts_length): """ This is a separate function for simulating a vector linear system of @@ -55,8 +55,6 @@ def simulate_linear_model(A, x0, v, ts_length): x[i, t+1] += A[i, j] * x[j, t] #Dot Product return x -if numba_installed: - simulate_linear_model = jit(simulate_linear_model) class LinearStateSpace(object): """ diff --git a/quantecon/markov/gth_solve.py b/quantecon/markov/gth_solve.py index 7881dd493..a8affa6a9 100644 --- a/quantecon/markov/gth_solve.py +++ b/quantecon/markov/gth_solve.py @@ -10,15 +10,14 @@ import numpy as np from numba import jit -from ..util import numba_installed, jit -if not numba_installed: - try: - xrange - except: # python3 - xrange = range +#TODO: Remove as we no longer officially support Python <3.5? +try: + xrange +except: # python3 + xrange = range -def gth_solve(A, overwrite=False): +def gth_solve(A, overwrite=False, use_jit=True): r""" This routine computes the stationary distribution of an irreducible Markov transition matrix (stochastic matrix) or transition rate @@ -74,11 +73,11 @@ def gth_solve(A, overwrite=False): n = A1.shape[0] x = np.zeros(n) - if numba_installed: + if use_jit: _gth_solve_jit(A1, x) return x - # if not numba_installed + # if not using jit # === Reduction === # for k in xrange(n-1): scale = np.sum(A1[k, k+1:n]) @@ -103,46 +102,45 @@ def gth_solve(A, overwrite=False): return x -if numba_installed: - @jit(nopython=True) - def _gth_solve_jit(A, out): - """ - JIT complied version of the main routine of gth_solve. - - Parameters - ---------- - A : numpy.ndarray(float, ndim=2) - Stochastic matrix or generator matrix. Must be of shape n x n. - Data will be overwritten. - - out : numpy.ndarray(float, ndim=1) - Output array in which to place the stationary distribution of A. - - """ - n = A.shape[0] - - # === Reduction === # - for k in range(n-1): - scale = np.sum(A[k, k+1:n]) - if scale <= 0: - # There is one (and only one) recurrent class contained in - # {0, ..., k}; - # compute the solution associated with that recurrent class. - n = k+1 - break - for i in range(k+1, n): - A[i, k] /= scale - - for j in range(k+1, n): - A[i, j] += A[i, k] * A[k, j] - - # === Backward substitution === # - out[n-1] = 1 - for k in range(n-2, -1, -1): - for i in range(k+1, n): - out[k] += out[i] * A[i, k] - - # === Normalization === # - norm = np.sum(out) - for k in range(n): - out[k] /= norm +@jit(nopython=True) +def _gth_solve_jit(A, out): + """ + JIT complied version of the main routine of gth_solve. + + Parameters + ---------- + A : numpy.ndarray(float, ndim=2) + Stochastic matrix or generator matrix. Must be of shape n x n. + Data will be overwritten. + + out : numpy.ndarray(float, ndim=1) + Output array in which to place the stationary distribution of A. + + """ + n = A.shape[0] + + # === Reduction === # + for k in range(n-1): + scale = np.sum(A[k, k+1:n]) + if scale <= 0: + # There is one (and only one) recurrent class contained in + # {0, ..., k}; + # compute the solution associated with that recurrent class. + n = k+1 + break + for i in range(k+1, n): + A[i, k] /= scale + + for j in range(k+1, n): + A[i, j] += A[i, k] * A[k, j] + + # === Backward substitution === # + out[n-1] = 1 + for k in range(n-2, -1, -1): + for i in range(k+1, n): + out[k] += out[i] * A[i, k] + + # === Normalization === # + norm = np.sum(out) + for k in range(n): + out[k] /= norm diff --git a/quantecon/markov/random.py b/quantecon/markov/random.py index 4ff567897..db0cdba95 100644 --- a/quantecon/markov/random.py +++ b/quantecon/markov/random.py @@ -8,10 +8,11 @@ """ import numpy as np import scipy.sparse +from numba import jit from .core import MarkovChain from .ddp import DiscreteDP -from ..util import check_random_state, numba_installed, jit +from ..util import check_random_state from ..random import probvec, sample_without_replacement @@ -218,7 +219,8 @@ def random_discrete_dp(num_states, num_actions, beta=None, ddp = DiscreteDP(R, Q, beta, s_indices, a_indices) return ddp - +#TODO: Should this use (nopython=True) flag? +@jit def _sa_indices(num_states, num_actions): L = num_states * num_actions s_indices = np.empty(L, dtype=int) @@ -233,5 +235,3 @@ def _sa_indices(num_states, num_actions): return s_indices, a_indices -if numba_installed: - _sa_indices = jit(_sa_indices) diff --git a/quantecon/random/utilities.py b/quantecon/random/utilities.py index 98944f01f..2883a250c 100644 --- a/quantecon/random/utilities.py +++ b/quantecon/random/utilities.py @@ -3,7 +3,7 @@ """ import numpy as np -from ..util import check_random_state, numba_installed, jit +from ..util import check_random_state #-Generating Arrays and Vectors-# @@ -75,6 +75,8 @@ def _diff(r, out): out[i, n] = 1 - r[i, n-1] +#TODO: Should this use (nopython=True) flag? +@jit def sample_without_replacement(n, k, num_trials=None, random_state=None): """ Randomly choose k integers without replacement from 0, ..., n-1. @@ -141,8 +143,3 @@ def sample_without_replacement(n, k, num_trials=None, random_state=None): return result[0] else: return result - -if numba_installed: - docs = sample_without_replacement.__doc__ - sample_without_replacement = jit(sample_without_replacement) - sample_without_replacement.__doc__ = docs diff --git a/quantecon/util/__init__.py b/quantecon/util/__init__.py index 39dcf0b0e..1bc7ef5ce 100644 --- a/quantecon/util/__init__.py +++ b/quantecon/util/__init__.py @@ -3,7 +3,6 @@ """ from .array import searchsorted -from .external import jit, numba_installed from .notebooks import fetch_nb_dependencies from .random import check_random_state from .timing import tic, tac, toc \ No newline at end of file diff --git a/quantecon/util/array.py b/quantecon/util/array.py index 17db7caac..02ebeff32 100644 --- a/quantecon/util/array.py +++ b/quantecon/util/array.py @@ -9,13 +9,12 @@ """ import numpy as np -from .external import numba_installed, jit # ----------------- # # -ARRAY UTILITIES- # # ----------------- # - +@jit(nopython=True) def _searchsorted(a, v): """ Custom version of np.searchsorted. Return the largest index `i` such @@ -61,11 +60,3 @@ def _searchsorted(a, v): else: lo = m return hi - -if numba_installed: - searchsorted = jit(nopython=True)(_searchsorted) -else: - def searchsorted(a, v): - return np.searchsorted(a, v, side='right') - -searchsorted.__doc__ = _searchsorted.__doc__ diff --git a/quantecon/util/external.py b/quantecon/util/external.py deleted file mode 100644 index 6578640d8..000000000 --- a/quantecon/util/external.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -External Module -=============== - -This module is an import location for flexible dependancies on external packages such as numba - -Packages --------- - 1. numba - -""" - -import warnings - -#-Setup Installed Indicator and Import JIT function for NUMBA-# - -numba_installed = True -try: - from numba import jit -except ImportError: - numba_installed = False - jit = None - from .common_messages import numba_import_fail_message - warnings.warn(numba_import_fail_message, UserWarning) From 7096e526438abce41c975ef77d3ce6c4215296e5 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Thu, 14 Apr 2016 16:59:07 -0400 Subject: [PATCH 2/6] Remove python27 support for xrange due to migration to python3.5+ --- quantecon/markov/gth_solve.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/quantecon/markov/gth_solve.py b/quantecon/markov/gth_solve.py index a8affa6a9..ef0ca2af7 100644 --- a/quantecon/markov/gth_solve.py +++ b/quantecon/markov/gth_solve.py @@ -10,13 +10,6 @@ import numpy as np from numba import jit -#TODO: Remove as we no longer officially support Python <3.5? -try: - xrange -except: # python3 - xrange = range - - def gth_solve(A, overwrite=False, use_jit=True): r""" This routine computes the stationary distribution of an irreducible @@ -79,7 +72,7 @@ def gth_solve(A, overwrite=False, use_jit=True): # if not using jit # === Reduction === # - for k in xrange(n-1): + for k in range(n-1): scale = np.sum(A1[k, k+1:n]) if scale <= 0: # There is one (and only one) recurrent class contained in @@ -93,7 +86,7 @@ def gth_solve(A, overwrite=False, use_jit=True): # === Backward substitution === # x[n-1] = 1 - for k in xrange(n-2, -1, -1): + for k in range(n-2, -1, -1): x[k] = np.dot(x[k+1:n], A1[k+1:n, k]) # === Normalization === # From c1c29c9a990d370e245d24efa152daca187f4cb0 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Thu, 14 Apr 2016 17:04:19 -0400 Subject: [PATCH 3/6] add import statement that was missed --- quantecon/util/array.py | 1 + 1 file changed, 1 insertion(+) diff --git a/quantecon/util/array.py b/quantecon/util/array.py index 02ebeff32..200d7ca98 100644 --- a/quantecon/util/array.py +++ b/quantecon/util/array.py @@ -9,6 +9,7 @@ """ import numpy as np +from numba import jit # ----------------- # # -ARRAY UTILITIES- # From bc75fc7cb6a76f11cc8f9f8e89ddfc97f0ba3ed0 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Thu, 14 Apr 2016 17:09:42 -0400 Subject: [PATCH 4/6] add adjustment to function name --- quantecon/util/array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quantecon/util/array.py b/quantecon/util/array.py index 200d7ca98..9b18e9ebe 100644 --- a/quantecon/util/array.py +++ b/quantecon/util/array.py @@ -16,7 +16,7 @@ # ----------------- # @jit(nopython=True) -def _searchsorted(a, v): +def searchsorted(a, v): """ Custom version of np.searchsorted. Return the largest index `i` such that `a[i-1] <= v < a[i]` (for `i = 0`, `v < a[0]`); if `v[n-1] <= From f58e62505252c20b70852f547107a6d54e4f3e1a Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Thu, 14 Apr 2016 17:13:28 -0400 Subject: [PATCH 5/6] Add jit import statement --- quantecon/random/utilities.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/quantecon/random/utilities.py b/quantecon/random/utilities.py index 2883a250c..904e3dd7d 100644 --- a/quantecon/random/utilities.py +++ b/quantecon/random/utilities.py @@ -3,6 +3,8 @@ """ import numpy as np +from numba import jit + from ..util import check_random_state #-Generating Arrays and Vectors-# From 59bddd5f9d42d9ecd5ae634f62a43420f9045a54 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Fri, 15 Apr 2016 10:24:25 -0400 Subject: [PATCH 6/6] Remove #TODO statements post discussion and review --- quantecon/lss.py | 1 - quantecon/markov/random.py | 1 - quantecon/random/utilities.py | 2 -- 3 files changed, 4 deletions(-) diff --git a/quantecon/lss.py b/quantecon/lss.py index 1deb600da..3cc2b91a4 100644 --- a/quantecon/lss.py +++ b/quantecon/lss.py @@ -11,7 +11,6 @@ from scipy.linalg import solve from numba import jit -#TODO: Should this be specified with (nopython=True?) @jit def simulate_linear_model(A, x0, v, ts_length): """ diff --git a/quantecon/markov/random.py b/quantecon/markov/random.py index db0cdba95..9fb1d055d 100644 --- a/quantecon/markov/random.py +++ b/quantecon/markov/random.py @@ -219,7 +219,6 @@ def random_discrete_dp(num_states, num_actions, beta=None, ddp = DiscreteDP(R, Q, beta, s_indices, a_indices) return ddp -#TODO: Should this use (nopython=True) flag? @jit def _sa_indices(num_states, num_actions): L = num_states * num_actions diff --git a/quantecon/random/utilities.py b/quantecon/random/utilities.py index 904e3dd7d..2142fe089 100644 --- a/quantecon/random/utilities.py +++ b/quantecon/random/utilities.py @@ -76,8 +76,6 @@ def _diff(r, out): out[i, j] = r[i, j] - r[i, j-1] out[i, n] = 1 - r[i, n-1] - -#TODO: Should this use (nopython=True) flag? @jit def sample_without_replacement(n, k, num_trials=None, random_state=None): """