From 5f7cb75454573e879f83969cddb766a936d87223 Mon Sep 17 00:00:00 2001 From: "Matthew N. White" Date: Thu, 18 Jun 2020 17:54:50 -0400 Subject: [PATCH 1/9] Added draft of calcExpectation Wrote first version of non-symbol expectation-evaluating function, completely untested. Also fixed a couple typos in distribution.py. --- HARK/distribution.py | 102 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/HARK/distribution.py b/HARK/distribution.py index b8ddbf109..c823a3981 100644 --- a/HARK/distribution.py +++ b/HARK/distribution.py @@ -84,6 +84,8 @@ def draw(self, N): ---------- N : int Number of draws in each row. + seed : int + Seed for random number generator. Returns: ------------ @@ -217,6 +219,9 @@ def __init__(self, sigma=1.0, seed=0): mu = -0.5 * sigma ** 2 super().__init__(mu=mu, sigma=sigma, seed=seed) + def __init__(self, sigma=1.0): + mu = -0.5*sigma**2 + super().__init__(mu=mu, sigma=sigma) class Normal(Distribution): """ @@ -583,6 +588,7 @@ def drawDiscrete(self, N, X=None, exact_match=False): int ) # cutoff points between discrete outcomes top = 0 + # Make a list of event indices that closely matches the discrete distribution event_list = [] for j in range(events.size): @@ -965,3 +971,99 @@ def combineIndepDstns(*distributions, seed=0): assert np.isclose(np.sum(P_out), 1), "Probabilities do not sum to 1!" return DiscreteDistribution(P_out, X_out, seed=seed) + + +def calcExpectation(func,values,dstn): + ''' + Calculate the expectation of a stochastic function at an array of values. + + Parameters + ---------- + func : function + The function to be evaluated, which can take M+N identically shaped arrays + as arguments and returns 1 array as an output (of the same shape). The + first M arguments are non-stochastic, representing the inputs passed in + the argument values. The latter N arguments are stochastic, where N is + the dimensionality of dstn. + values : np.array or [np.array] + One or more arrays of input values for func, representing the non-stochastic + arguments. If the array(s) are 1D, they are interpreted as grids over + each of the M non-stochastic arguments of the function; the expectation + will be evaluated at the tensor product set, so the output will have shape + (values[0].size,values[1].size,...,values[M].size). Otherwise, the arrays + in values must all have the same shape, and the expectation is computed + at f(values[0],values[1],...,values[M],*dstn). + dstn : DiscreteDistribution + The distribution over which the function is to be evaluated. It should + have dimension N, representing the last N arguments of func. + + Returns + ------- + f_exp : np.array + The expectation of the function at the queried values. + ''' + # Get the number of (non-)stochastic arguments of the function + if not isinstance(values,list): + values = [values] + M = len(values) + N = dstn.dim() + K = dstn.pmf.size + + # Determine whether the queried values are grids to be tensor-ed or just arrays + is_tensor = len(values[0].shape) == 1 + args_list = [] # Initialize list of argument arrays + + # Construct tensor arrays of the value grids + if is_tensor: + # Get length of each grid and shape of argument arrays + value_shape = np.array([values[i].size for i in range(M)]) + arg_shape = np.concatenate((value_shape,np.array([K]))) + shock_tiling_shape = np.concatenate((np.ones_like(value_shape), np.array([K]))) + + # Reshape each of the non-stochastic arrays to give them the tensor shape + for i in range(M): + new_array = values[i].copy() + for j in range(M): + if j < i: + new_array = new_array[np.newaxis,:] + elif j > i: + new_array = new_array[:,np.newaxis] + temp_shape = value_shape.copy() + temp_shape[i] = 1 + new_array = np.tile(new_array, temp_shape) + new_array = new_array[:,np.new_axis] # Add dimension for shocks + new_array = np.tile(new_array, shock_tiling_shape) + args_list.append(args_list) + + # Just add a dimension for the shocks + else: + # Get shape of argument arrays + value_shape = np.array(values[0].shape) + arg_shape = np.concatenate((value_shape,np.array([K]))) + shock_tiling_shape = np.concatenate((np.ones_like(value_shape), np.array([K]))) + + # Add the shock dimension to each of the query value arrays + for i in range(M): + new_array = values[i].copy()[:,np.newaxis] + new_array = np.tile(new_array, shock_tiling_shape) + args_list.append(args_list) + + # Make an argument array for each dimension of the distribution (and add to list) + value_tiling_shape = arg_shape.copy() + value_tiling_shape[-1] = 1 + if N == 1: + new_array = np.reshape(dstn.X, shock_tiling_shape) + new_array = np.tile(new_array, value_tiling_shape) + args_list.append(new_array) + else: + for j in range(N): + new_array = np.reshape(dstn.X[j], shock_tiling_shape) + new_array = np.tile(new_array, value_tiling_shape) + args_list.append(new_array) + + # Evaluate the function at the argument arrays + f_query = func(*args_list) + + # Compute expectations over the shocks and return it + f_exp = np.dot(f_query, dstn.pmf) + return f_exp From 0f0cb3f8494b4dfed98b8ff8586eda4b7611e0ac Mon Sep 17 00:00:00 2001 From: "Matthew N. White" Date: Thu, 18 Jun 2020 20:16:54 -0400 Subject: [PATCH 2/9] Fixed typos, basic evalExpectations works Still need to test alternate input versions. --- HARK/distribution.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/HARK/distribution.py b/HARK/distribution.py index c823a3981..dccfb1192 100644 --- a/HARK/distribution.py +++ b/HARK/distribution.py @@ -1031,9 +1031,9 @@ def calcExpectation(func,values,dstn): temp_shape = value_shape.copy() temp_shape[i] = 1 new_array = np.tile(new_array, temp_shape) - new_array = new_array[:,np.new_axis] # Add dimension for shocks + new_array = new_array[:,np.newaxis] # Add dimension for shocks new_array = np.tile(new_array, shock_tiling_shape) - args_list.append(args_list) + args_list.append(new_array) # Just add a dimension for the shocks else: From ec9d4d2f13febf05a5a45ae49a94f8123d221c8f Mon Sep 17 00:00:00 2001 From: "Matthew N. White" Date: Fri, 19 Jun 2020 11:37:39 -0400 Subject: [PATCH 3/9] Adjust colons to ellipses calcExpectations now tested on more advanced cases. Fixed dumb typo (copy-pasted from other line) and changed colons to ellipses in axis-construction step. Also wrote new version of ConsAggShock solver that uses calcExpectation, as a demonstration of what the code would look like. --- HARK/ConsumptionSaving/ConsAggShockModel.py | 133 +++++++++++++++++++- HARK/distribution.py | 12 +- 2 files changed, 138 insertions(+), 7 deletions(-) diff --git a/HARK/ConsumptionSaving/ConsAggShockModel.py b/HARK/ConsumptionSaving/ConsAggShockModel.py index 2ba1c03a4..731310a11 100644 --- a/HARK/ConsumptionSaving/ConsAggShockModel.py +++ b/HARK/ConsumptionSaving/ConsAggShockModel.py @@ -1110,6 +1110,8 @@ def solveConsAggShock( The net interest factor on assets as a function of capital ratio k. wFunc : function The wage rate for labor as a function of capital-to-labor ratio k. + DeprFac : float + Capital Depreciation Rate Returns ------- @@ -1255,6 +1257,133 @@ def solveConsAggShock( return solution_now +def solveConsAggShockNEW(solution_next, IncomeDstn, LivPrb, DiscFac, CRRA, PermGroFac, + PermGroFacAgg, aXtraGrid, BoroCnstArt, Mgrid, AFunc, Rfunc, wFunc, DeprFac): + ''' + Solve one period of a consumption-saving problem with idiosyncratic and + aggregate shocks (transitory and permanent). This is a basic solver that + can't handle cubic splines, nor can it calculate a value function. This + version uses calcExpectation to reduce code clutter. + + Parameters + ---------- + solution_next : ConsumerSolution + The solution to the succeeding one period problem. + IncomeDstn : [np.array] + A list containing five arrays of floats, representing a discrete + approximation to the income process between the period being solved + and the one immediately following (in solution_next). Order: event + probabilities, idisyncratic permanent shocks, idiosyncratic transitory + shocks, aggregate permanent shocks, aggregate transitory shocks. + LivPrb : float + Survival probability; likelihood of being alive at the beginning of + the succeeding period. + DiscFac : float + Intertemporal discount factor for future utility. + CRRA : float + Coefficient of relative risk aversion. + PermGroFac : float + Expected permanent income growth factor at the end of this period. + PermGroFacAgg : float + Expected aggregate productivity growth factor. + aXtraGrid : np.array + Array of "extra" end-of-period asset values-- assets above the + absolute minimum acceptable level. + BoroCnstArt : float + Artificial borrowing constraint; minimum allowable end-of-period asset-to- + permanent-income ratio. Unlike other models, this *can't* be None. + Mgrid : np.array + A grid of aggregate market resourses to permanent income in the economy. + AFunc : function + Aggregate savings as a function of aggregate market resources. + Rfunc : function + The net interest factor on assets as a function of capital ratio k. + wFunc : function + The wage rate for labor as a function of capital-to-labor ratio k. + DeprFac : float + Capital Depreciation Rate + + Returns + ------- + solution_now : ConsumerSolution + The solution to the single period consumption-saving problem. Includes + a consumption function cFunc (linear interpolation over linear interpola- + tions) and marginal value function vPfunc. + ''' + # Unpack the income shocks and get grid sizes + PermShkValsNext = IncomeDstn.X[0] + TranShkValsNext = IncomeDstn.X[1] + PermShkAggValsNext = IncomeDstn.X[2] + TranShkAggValsNext = IncomeDstn.X[3] + aCount = aXtraGrid.size + Mcount = Mgrid.size + + # Define a function that calculates M_{t+1} from M_t and the aggregate shocks; + # the function also returns the wage rate and effective interest factor + def calcAggObjects(M,Psi,Theta): + A = AFunc(M) # End-of-period aggregate assets (normalized) + kNext = A/(PermGroFacAgg*Psi) # Next period's aggregate capital/labor ratio + kNextEff = kNext/Theta # Same thing, but account for *transitory* shock + R = Rfunc(kNextEff) # Interest factor on aggregate assets + wEff = wFunc(kNextEff)*Theta # Effective wage rate (accounts for labor supply) + Reff = R/LivPrb # Account for redistribution of decedents' wealth + Mnext = kNext*R + wEff # Next period's aggregate market resources + return Mnext, Reff, wEff + + # Define a function that evaluates R*v'(m_{t+1},M_{t+1}) from a_t, M_t, and the income shocks + def vPnextFunc(a,M,psi,theta,Psi,Theta): + Mnext, Reff, wEff = calcAggObjects(M,Psi,Theta) + PermShkTotal = PermGroFac * PermGroFacAgg * psi * Psi # Total / combined permanent shock + mNext = Reff*a/PermShkTotal + theta*wEff # Idiosyncratic market resources + vPnext = Reff*PermShkTotal**(-CRRA)*solution_next.vPfunc(mNext, Mnext) + return vPnext + + # Make an array of a_t values at which to calculate end-of-period marginal value of assets + BoroCnstNat_vec = np.zeros(Mcount) # Natural borrowing constraint at each M_t + aNrmNow = np.zeros((aCount,Mcount)) + for j in range(Mcount): + Mnext, Reff, wEff = calcAggObjects(Mgrid[j], PermShkAggValsNext, TranShkAggValsNext) + aNrmMin_cand = (PermGroFac*PermGroFacAgg*PermShkValsNext*PermShkAggValsNext/Reff) * \ + (solution_next.mNrmMin(Mnext) - wEff*TranShkValsNext) + aNrmMin = np.max(aNrmMin_cand) # Lowest valid a_t value for this M_t + aNrmNow[:,j] = aNrmMin + aXtraGrid + BoroCnstNat_vec[j] = aNrmMin + + # Compute end-of-period marginal value of assets + MaggNow = np.tile(np.reshape(Mgrid,(1,Mcount)),(aCount,1)) # Tiled Mgrid + EndOfPrdvP = DiscFac*LivPrb*calcExpectation(vPnextFunc,[aNrmNow,MaggNow],IncomeDstn) + + # Calculate optimal consumption from each asset gridpoint and endogenous m_t gridpoint + cNrmNow = EndOfPrdvP**(-1.0/CRRA) + mNrmNow = aNrmNow + cNrmNow + + # Loop through the values in Mgrid and make a linear consumption function for each + cFuncBaseByM_list = [] + for j in range(Mcount): + c_temp = np.insert(cNrmNow[:,j], 0, 0.0) # Add point at bottom + m_temp = np.insert(mNrmNow[:,j] - BoroCnstNat_vec[j], 0, 0.0) + cFuncBaseByM_list.append(LinearInterp(m_temp, c_temp)) + + # Construct the overall unconstrained consumption function by combining the M-specific functions + BoroCnstNat = LinearInterp(np.insert(Mgrid, 0, 0.0), np.insert(BoroCnstNat_vec, 0, 0.0)) + cFuncBase = LinearInterpOnInterp1D(cFuncBaseByM_list, Mgrid) + cFuncUnc = VariableLowerBoundFunc2D(cFuncBase, BoroCnstNat) + + # Make the constrained consumption function and combine it with the unconstrained component + cFuncCnst = BilinearInterp(np.array([[0.0, 0.0], [1.0, 1.0]]), + np.array([BoroCnstArt, BoroCnstArt+1.0]), np.array([0.0, 1.0])) + cFuncNow = LowerEnvelope2D(cFuncUnc, cFuncCnst) + + # Make the minimum m function as the greater of the natural and artificial constraints + mNrmMinNow = UpperEnvelope(BoroCnstNat, ConstantFunction(BoroCnstArt)) + + # Construct the marginal value function using the envelope condition + vPfuncNow = MargValueFunc2D(cFuncNow, CRRA) + + # Pack up and return the solution + solution_now = ConsumerSolution(cFunc=cFuncNow, vPfunc=vPfuncNow, mNrmMin=mNrmMinNow) + return solution_now + ############################################################################### @@ -1322,6 +1451,8 @@ def solveConsAggMarkov( The net interest factor on assets as a function of capital ratio k. wFunc : function The wage rate for labor as a function of capital-to-labor ratio k. + DeprFac : float + Capital Depreciation Rate Returns ------- @@ -3021,7 +3152,7 @@ def __init__(self, intercept, slope): Returns ------- new instance of CapitalEvoRule - """ + ''' self.intercept = intercept self.slope = slope self.distance_criteria = ["slope", "intercept"] diff --git a/HARK/distribution.py b/HARK/distribution.py index dccfb1192..8566b5254 100644 --- a/HARK/distribution.py +++ b/HARK/distribution.py @@ -1025,13 +1025,13 @@ def calcExpectation(func,values,dstn): new_array = values[i].copy() for j in range(M): if j < i: - new_array = new_array[np.newaxis,:] + new_array = new_array[np.newaxis,...] elif j > i: - new_array = new_array[:,np.newaxis] + new_array = new_array[...,np.newaxis] temp_shape = value_shape.copy() temp_shape[i] = 1 new_array = np.tile(new_array, temp_shape) - new_array = new_array[:,np.newaxis] # Add dimension for shocks + new_array = new_array[...,np.newaxis] # Add dimension for shocks new_array = np.tile(new_array, shock_tiling_shape) args_list.append(new_array) @@ -1044,9 +1044,9 @@ def calcExpectation(func,values,dstn): # Add the shock dimension to each of the query value arrays for i in range(M): - new_array = values[i].copy()[:,np.newaxis] + new_array = values[i].copy()[...,np.newaxis] new_array = np.tile(new_array, shock_tiling_shape) - args_list.append(args_list) + args_list.append(new_array) # Make an argument array for each dimension of the distribution (and add to list) value_tiling_shape = arg_shape.copy() @@ -1060,7 +1060,7 @@ def calcExpectation(func,values,dstn): new_array = np.reshape(dstn.X[j], shock_tiling_shape) new_array = np.tile(new_array, value_tiling_shape) args_list.append(new_array) - + # Evaluate the function at the argument arrays f_query = func(*args_list) From 18f33686fee8311ffa2ed729c840fef00fdde8fd Mon Sep 17 00:00:00 2001 From: "Matthew N. White" Date: Mon, 22 Jun 2020 16:06:09 -0400 Subject: [PATCH 4/9] calcExpectation handles missing values calcExpectation now correctly handles passing a null argument (empty list) for values, representing a function with no deterministic arguments. --- HARK/distribution.py | 7 +++++-- examples/ConsumptionSaving/example_ConsAggShockModel.py | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/HARK/distribution.py b/HARK/distribution.py index 8566b5254..920f4e195 100644 --- a/HARK/distribution.py +++ b/HARK/distribution.py @@ -1010,7 +1010,7 @@ def calcExpectation(func,values,dstn): K = dstn.pmf.size # Determine whether the queried values are grids to be tensor-ed or just arrays - is_tensor = len(values[0].shape) == 1 + is_tensor = (len(values) > 0) and (len(values[0].shape) == 1) args_list = [] # Initialize list of argument arrays # Construct tensor arrays of the value grids @@ -1038,7 +1038,10 @@ def calcExpectation(func,values,dstn): # Just add a dimension for the shocks else: # Get shape of argument arrays - value_shape = np.array(values[0].shape) + if len(values) == 0: + value_shape = (1,) # No deterministic inputs, trivial shape + else: + value_shape = np.array(values[0].shape) arg_shape = np.concatenate((value_shape,np.array([K]))) shock_tiling_shape = np.concatenate((np.ones_like(value_shape), np.array([K]))) diff --git a/examples/ConsumptionSaving/example_ConsAggShockModel.py b/examples/ConsumptionSaving/example_ConsAggShockModel.py index 2812a562b..e83594c4c 100644 --- a/examples/ConsumptionSaving/example_ConsAggShockModel.py +++ b/examples/ConsumptionSaving/example_ConsAggShockModel.py @@ -21,7 +21,7 @@ def mystr(number): # Solve an AggShockMarkovConsumerType's microeconomic problem solve_markov_micro = False # Solve for the equilibrium aggregate saving rule in a CobbDouglasMarkovEconomy -solve_markov_market = True +solve_markov_market = False # Solve a simple Krusell-Smith-style two state, two shock model solve_krusell_smith = True # Solve a CobbDouglasEconomy with many states, potentially utilizing the "state jumper" From 83513e510446c6c07fbafccf1897419382ad4141 Mon Sep 17 00:00:00 2001 From: "Matthew N. White" Date: Mon, 22 Jun 2020 17:43:44 -0400 Subject: [PATCH 5/9] Change order of arguments dstn is now first --- HARK/ConsumptionSaving/ConsAggShockModel.py | 2 +- HARK/distribution.py | 22 +++++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/HARK/ConsumptionSaving/ConsAggShockModel.py b/HARK/ConsumptionSaving/ConsAggShockModel.py index 731310a11..983a30149 100644 --- a/HARK/ConsumptionSaving/ConsAggShockModel.py +++ b/HARK/ConsumptionSaving/ConsAggShockModel.py @@ -1351,7 +1351,7 @@ def vPnextFunc(a,M,psi,theta,Psi,Theta): # Compute end-of-period marginal value of assets MaggNow = np.tile(np.reshape(Mgrid,(1,Mcount)),(aCount,1)) # Tiled Mgrid - EndOfPrdvP = DiscFac*LivPrb*calcExpectation(vPnextFunc,[aNrmNow,MaggNow],IncomeDstn) + EndOfPrdvP = DiscFac*LivPrb*calcExpectation(IncomeDstn,vPnextFunc,[aNrmNow,MaggNow]) # Calculate optimal consumption from each asset gridpoint and endogenous m_t gridpoint cNrmNow = EndOfPrdvP**(-1.0/CRRA) diff --git a/HARK/distribution.py b/HARK/distribution.py index 920f4e195..a11b90e90 100644 --- a/HARK/distribution.py +++ b/HARK/distribution.py @@ -973,35 +973,41 @@ def combineIndepDstns(*distributions, seed=0): return DiscreteDistribution(P_out, X_out, seed=seed) -def calcExpectation(func,values,dstn): +def calcExpectation(dstn,func=None,values=None): ''' Calculate the expectation of a stochastic function at an array of values. Parameters ---------- - func : function + dstn : DiscreteDistribution + The distribution over which the function is to be evaluated. It should + have dimension N, representing the last N arguments of func. + func : function or None The function to be evaluated, which can take M+N identically shaped arrays as arguments and returns 1 array as an output (of the same shape). The first M arguments are non-stochastic, representing the inputs passed in the argument values. The latter N arguments are stochastic, where N is - the dimensionality of dstn. - values : np.array or [np.array] + the dimensionality of dstn. Defaults to identity function. + values : np.array or [np.array] or None One or more arrays of input values for func, representing the non-stochastic arguments. If the array(s) are 1D, they are interpreted as grids over each of the M non-stochastic arguments of the function; the expectation will be evaluated at the tensor product set, so the output will have shape (values[0].size,values[1].size,...,values[M].size). Otherwise, the arrays in values must all have the same shape, and the expectation is computed - at f(values[0],values[1],...,values[M],*dstn). - dstn : DiscreteDistribution - The distribution over which the function is to be evaluated. It should - have dimension N, representing the last N arguments of func. + at f(values[0],values[1],...,values[M],*dstn). Defaults to empty list. Returns ------- f_exp : np.array The expectation of the function at the queried values. ''' + # Fill in defaults + if func is None: + func = lambda x : x + if values is None: + values = [] + # Get the number of (non-)stochastic arguments of the function if not isinstance(values,list): values = [values] From 3801a24e468961ff2d97d0114e63135fa4f3d531 Mon Sep 17 00:00:00 2001 From: sb Date: Tue, 15 Dec 2020 13:14:17 -0500 Subject: [PATCH 6/9] fixing the merge --- HARK/ConsumptionSaving/ConsAggShockModel.py | 2 +- HARK/distribution.py | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/HARK/ConsumptionSaving/ConsAggShockModel.py b/HARK/ConsumptionSaving/ConsAggShockModel.py index 983a30149..3c341ab61 100644 --- a/HARK/ConsumptionSaving/ConsAggShockModel.py +++ b/HARK/ConsumptionSaving/ConsAggShockModel.py @@ -3152,7 +3152,7 @@ def __init__(self, intercept, slope): Returns ------- new instance of CapitalEvoRule - ''' + """ self.intercept = intercept self.slope = slope self.distance_criteria = ["slope", "intercept"] diff --git a/HARK/distribution.py b/HARK/distribution.py index a11b90e90..3e84b5766 100644 --- a/HARK/distribution.py +++ b/HARK/distribution.py @@ -219,10 +219,6 @@ def __init__(self, sigma=1.0, seed=0): mu = -0.5 * sigma ** 2 super().__init__(mu=mu, sigma=sigma, seed=seed) - def __init__(self, sigma=1.0): - mu = -0.5*sigma**2 - super().__init__(mu=mu, sigma=sigma) - class Normal(Distribution): """ A Normal distribution. From 2d612922e15ae0605e7e0bb430e50f3b618916d1 Mon Sep 17 00:00:00 2001 From: sb Date: Tue, 15 Dec 2020 13:44:38 -0500 Subject: [PATCH 7/9] adding CHANGELOG update --- Documentation/CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/CHANGELOG.md b/Documentation/CHANGELOG.md index c5573cea3..f16f1ad23 100644 --- a/Documentation/CHANGELOG.md +++ b/Documentation/CHANGELOG.md @@ -8,6 +8,14 @@ For more information on HARK, see [our Github organization](https://github.com/e ## Changes +### 0.10.9 + +Release Data: TBD + +#### Major Changes + +* calcExpectations method for taking the expectation of a distribution over a function (#884)[https://github.com/econ-ark/HARK/pull/884/] + ### 0.10.8 Release Data: Nov. 05 2020 From e138d0e4bf7387e5e98ebcda9fbff67dc3c093b8 Mon Sep 17 00:00:00 2001 From: sb Date: Tue, 15 Dec 2020 14:01:11 -0500 Subject: [PATCH 8/9] adding automated test for calcExpectation --- HARK/tests/test_distribution.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/HARK/tests/test_distribution.py b/HARK/tests/test_distribution.py index 952f47311..64efb9466 100644 --- a/HARK/tests/test_distribution.py +++ b/HARK/tests/test_distribution.py @@ -53,7 +53,14 @@ def test_Weibull(self): self.assertEqual(distribution.Weibull().draw(1)[0], 0.79587450816311) def test_Uniform(self): + uni = distribution.Uniform() + self.assertEqual(distribution.Uniform().draw(1)[0], 0.5488135039273248) + self.assertEqual( + distribution.calcExpectation(uni.approx(10))[0], + 0.5 + ) + def test_Bernoulli(self): self.assertEqual(distribution.Bernoulli().draw(1)[0], False) From df82892183c65cf1a28eedb08e7acc84f8afc2d3 Mon Sep 17 00:00:00 2001 From: sb Date: Tue, 15 Dec 2020 14:45:53 -0500 Subject: [PATCH 9/9] fixing the imports --- HARK/ConsumptionSaving/ConsAggShockModel.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/HARK/ConsumptionSaving/ConsAggShockModel.py b/HARK/ConsumptionSaving/ConsAggShockModel.py index 27fef52a1..4c446b53d 100644 --- a/HARK/ConsumptionSaving/ConsAggShockModel.py +++ b/HARK/ConsumptionSaving/ConsAggShockModel.py @@ -29,7 +29,12 @@ CRRAutility_inv, makeGridExpMult, ) -from HARK.distribution import Uniform, calcExpectation +from HARK.distribution import ( + MeanOneLogNormal, + Uniform, + combineIndepDstns, + calcExpectation +) from HARK.ConsumptionSaving.ConsIndShockModel import ( ConsumerSolution, IndShockConsumerType,