From 3e0b93904e67fd6039219e2c7ffea90bc08b969f Mon Sep 17 00:00:00 2001 From: sb Date: Tue, 18 Jun 2024 16:25:47 -0400 Subject: [PATCH 1/5] adds a method to DBlock that returns a discretized version of the block --- HARK/model.py | 21 ++++++++++++++++++++- HARK/tests/test_model.py | 5 +++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/HARK/model.py b/HARK/model.py index c97a2ad76..99dd7b55d 100644 --- a/HARK/model.py +++ b/HARK/model.py @@ -2,7 +2,8 @@ Tools for crafting models. """ -from dataclasses import dataclass, field +from dataclasses import dataclass, field, replace +from copy import deepcopy from HARK.distribution import ( Distribution, DiscreteDistributionLabeled, @@ -152,6 +153,24 @@ class DBlock: dynamics: dict = field(default_factory=dict) reward: dict = field(default_factory=dict) + def discretize(self, disc_params): + """ + Returns a new DBlock which is a copy of this one, but with shock discretized. + """ + + disc_shocks = {} + + for shockn in self.shocks: + if shockn in disc_params: + disc_shocks[shockn] = self.shocks[shockn].discretize(**disc_params[shockn]) + else: + disc_shocks[shockn] = deepcopy(self.shocks[shockn]) + + # replace returns a modified copy + new_dblock = replace(self, shocks = disc_shocks) + + return new_dblock + def get_shocks(self): return self.shocks diff --git a/HARK/tests/test_model.py b/HARK/tests/test_model.py index f5b2db434..a5c4feff7 100644 --- a/HARK/tests/test_model.py +++ b/HARK/tests/test_model.py @@ -44,6 +44,11 @@ def setUp(self): def test_init(self): self.assertEqual(self.test_block_A.name, "test block A") + def test_discretize(self): + dbl = self.cblock.discretize({"theta" : {"N" : 5}}) + + self.assertEqual(len(dbl.shocks["theta"].pmv), 5) + def test_transition(self): post = self.cblock.transition(self.dpre, self.dr) From 5e2d74f4e9120dc2e2dc6d7718c119a3d78b48be Mon Sep 17 00:00:00 2001 From: sb Date: Tue, 18 Jun 2024 16:40:27 -0400 Subject: [PATCH 2/5] discretize() method for RBlock --- HARK/model.py | 36 ++++++++++++++++++++++++++++++------ HARK/tests/test_model.py | 10 +++++++++- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/HARK/model.py b/HARK/model.py index 99dd7b55d..122d75174 100644 --- a/HARK/model.py +++ b/HARK/model.py @@ -3,7 +3,7 @@ """ from dataclasses import dataclass, field, replace -from copy import deepcopy +from copy import copy, deepcopy from HARK.distribution import ( Distribution, DiscreteDistributionLabeled, @@ -135,8 +135,12 @@ def simulate_dynamics( return vals +class Block: + pass + + @dataclass -class DBlock: +class DBlock(Block): """ Represents a 'block' of model behavior. It prioritizes a representation of the dynamics of the block. @@ -162,12 +166,14 @@ def discretize(self, disc_params): for shockn in self.shocks: if shockn in disc_params: - disc_shocks[shockn] = self.shocks[shockn].discretize(**disc_params[shockn]) + disc_shocks[shockn] = self.shocks[shockn].discretize( + **disc_params[shockn] + ) else: disc_shocks[shockn] = deepcopy(self.shocks[shockn]) # replace returns a modified copy - new_dblock = replace(self, shocks = disc_shocks) + new_dblock = replace(self, shocks=disc_shocks) return new_dblock @@ -265,7 +271,7 @@ def mod_dvf(shock_value_array): @dataclass -class RBlock: +class RBlock(Block): """ A recursive block. @@ -276,7 +282,25 @@ class RBlock: name: str = "" description: str = "" - blocks: List[DBlock] = field(default_factory=list) + blocks: List[Block] = field(default_factory=list) + + def discretize(self, disc_params): + """ + Recursively discretizes all the blocks. + It replaces any DBlocks with new blocks with discretized shocks. + """ + # we will be mutating self.blocks so need to iterate through a copy + ib = copy(self.blocks) + + for i, b in enumerate(ib): + if isinstance(b, DBlock): + nb = b.discretize(disc_params) + self.blocks[i] = nb + elif isinstance(b, RBlock): + b.discretize(disc_params) + + # returns the rblock, which is modified, to align the type signatures across subclasses + return self def get_shocks(self): ### TODO: Bug in here is causing AttributeError: 'set' object has no attribute 'draw' diff --git a/HARK/tests/test_model.py b/HARK/tests/test_model.py index a5c4feff7..617fadc4e 100644 --- a/HARK/tests/test_model.py +++ b/HARK/tests/test_model.py @@ -45,7 +45,7 @@ def test_init(self): self.assertEqual(self.test_block_A.name, "test block A") def test_discretize(self): - dbl = self.cblock.discretize({"theta" : {"N" : 5}}) + dbl = self.cblock.discretize({"theta": {"N": 5}}) self.assertEqual(len(dbl.shocks["theta"].pmv), 5) @@ -84,6 +84,8 @@ def setUp(self): self.test_block_C = model.DBlock(**test_block_C_data) self.test_block_D = model.DBlock(**test_block_D_data) + self.cpp = cons.cons_portfolio_problem + def test_init(self): r_block_tree = model.RBlock( blocks=[ @@ -94,3 +96,9 @@ def test_init(self): r_block_tree.get_shocks() self.assertEqual(len(r_block_tree.get_shocks()), 3) + + def test_discretize(self): + cppd = self.cpp.discretize({"theta": {"N": 5}, "risky_return": {"N": 6}}) + + self.assertEqual(len(cppd.get_shocks()["theta"].pmv), 5) + self.assertEqual(len(cppd.get_shocks()["risky_return"].pmv), 6) From cc19df885f44fb9442ec6b2f9e4d7c96e3d596ea Mon Sep 17 00:00:00 2001 From: sb Date: Tue, 18 Jun 2024 16:50:14 -0400 Subject: [PATCH 3/5] change to make RBloc.discretize return copy not mutate original model --- HARK/model.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/HARK/model.py b/HARK/model.py index 122d75174..b4ad7943a 100644 --- a/HARK/model.py +++ b/HARK/model.py @@ -289,18 +289,17 @@ def discretize(self, disc_params): Recursively discretizes all the blocks. It replaces any DBlocks with new blocks with discretized shocks. """ - # we will be mutating self.blocks so need to iterate through a copy - ib = copy(self.blocks) + cbs = copy(self.blocks) - for i, b in enumerate(ib): + for i, b in list(enumerate(cbs)): if isinstance(b, DBlock): nb = b.discretize(disc_params) - self.blocks[i] = nb + cbs[i] = nb elif isinstance(b, RBlock): b.discretize(disc_params) - # returns the rblock, which is modified, to align the type signatures across subclasses - return self + # returns a copy of the RBlock with the blocks replaced + return replace(self, blocks = cbs) def get_shocks(self): ### TODO: Bug in here is causing AttributeError: 'set' object has no attribute 'draw' From 63640e60b62eb3308f4c13e01260811d943f91b1 Mon Sep 17 00:00:00 2001 From: sb Date: Tue, 18 Jun 2024 16:52:37 -0400 Subject: [PATCH 4/5] ruff --- HARK/model.py | 2 +- HARK/tests/test_model.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/HARK/model.py b/HARK/model.py index b4ad7943a..9b86fab02 100644 --- a/HARK/model.py +++ b/HARK/model.py @@ -299,7 +299,7 @@ def discretize(self, disc_params): b.discretize(disc_params) # returns a copy of the RBlock with the blocks replaced - return replace(self, blocks = cbs) + return replace(self, blocks=cbs) def get_shocks(self): ### TODO: Bug in here is causing AttributeError: 'set' object has no attribute 'draw' diff --git a/HARK/tests/test_model.py b/HARK/tests/test_model.py index 617fadc4e..ad14fe27e 100644 --- a/HARK/tests/test_model.py +++ b/HARK/tests/test_model.py @@ -1,6 +1,6 @@ import unittest -from HARK.distribution import Bernoulli +from HARK.distribution import Bernoulli, DiscreteDistribution import HARK.model as model from HARK.model import Control import HARK.models.consumer as cons @@ -102,3 +102,7 @@ def test_discretize(self): self.assertEqual(len(cppd.get_shocks()["theta"].pmv), 5) self.assertEqual(len(cppd.get_shocks()["risky_return"].pmv), 6) + + self.assertFalse( + isinstance(self.cpp.get_shocks()["theta"], DiscreteDistribution) + ) From 96a2185f79a13b839b909fb66d4fcfde19b21c5c Mon Sep 17 00:00:00 2001 From: sb Date: Tue, 18 Jun 2024 16:54:14 -0400 Subject: [PATCH 5/5] CHANGELOG #1460 --- Documentation/CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Documentation/CHANGELOG.md b/Documentation/CHANGELOG.md index ab9d67653..5c3ff22aa 100644 --- a/Documentation/CHANGELOG.md +++ b/Documentation/CHANGELOG.md @@ -8,6 +8,19 @@ For more information on HARK, see [our Github organization](https://github.com/e ## Changes +### 0.16.0 + +Release Date: TBD + +### Major Changes + +- Adds a discretize method to DBlocks and RBlocks (#1460)[https://github.com/econ-ark/HARK/pull/1460] + +### Minor Changes + +none + + ### 0.15.1 Release Date: June 15, 2024