Coverage for HARK / rewards.py: 92%

295 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-12-07 05:16 +0000

1import numpy as np 

2from HARK.metric import MetricObject 

3import functools 

4 

5 

6def utility_fix(func): 

7 @functools.wraps(func) 

8 def wrapper(*args, **kwargs): 

9 if np.ndim(args[0]) == 0: 

10 if args[0] < 0.0: 

11 return np.nan 

12 else: 

13 return func(*[np.array([args[0]])] + list(args[1:]), **kwargs)[0] 

14 else: 

15 out = func(*args, **kwargs) 

16 neg = args[0] < 0.0 

17 out[neg] = np.nan 

18 return out 

19 

20 return wrapper 

21 

22 

23# ============================================================================== 

24# ============== Define utility functions =============================== 

25# ============================================================================== 

26 

27 

28@utility_fix 

29def CRRAutility(c, rho): 

30 """ 

31 Evaluates constant relative risk aversion (CRRA) utility of consumption c 

32 given risk aversion parameter rho. 

33 

34 Parameters 

35 ---------- 

36 c : float or array 

37 Consumption value 

38 rho : float 

39 Risk aversion 

40 

41 Returns 

42 ------- 

43 u : float or array 

44 Utility 

45 

46 Tests 

47 ----- 

48 Test a value which should pass: 

49 >>> c, CRRA = 1.0, 2.0 # Set two values at once with Python syntax 

50 >>> CRRAutility(c=c, rho=CRRA) 

51 -1.0 

52 """ 

53 if rho == 1: 

54 return np.log(c) 

55 return c ** (1.0 - rho) / (1.0 - rho) 

56 

57 

58@utility_fix 

59def CRRAutilityP(c, rho): 

60 """ 

61 Evaluates constant relative risk aversion (CRRA) marginal utility of consumption 

62 c given risk aversion parameter rho. 

63 

64 Parameters 

65 ---------- 

66 c : float 

67 Consumption value 

68 rho : float 

69 Risk aversion 

70 

71 Returns 

72 ------- 

73 uP : float or array 

74 Marginal utility 

75 """ 

76 if rho == 1: 

77 return 1 / c 

78 else: 

79 return c**-rho 

80 

81 

82@utility_fix 

83def CRRAutilityPP(c, rho): 

84 """ 

85 Evaluates constant relative risk aversion (CRRA) marginal marginal utility of 

86 consumption c given risk aversion parameter rho. 

87 

88 Parameters 

89 ---------- 

90 c : float 

91 Consumption value 

92 rho : float 

93 Risk aversion 

94 

95 Returns 

96 ------- 

97 uPP : float 

98 Marginal marginal utility 

99 """ 

100 

101 return -rho * c ** (-rho - 1.0) 

102 

103 

104@utility_fix 

105def CRRAutilityPPP(c, rho): 

106 """ 

107 Evaluates constant relative risk aversion (CRRA) marginal marginal marginal 

108 utility of consumption c given risk aversion parameter rho. 

109 

110 Parameters 

111 ---------- 

112 c : float 

113 Consumption value 

114 rho : float 

115 Risk aversion 

116 

117 Returns 

118 ------- 

119 (unnamed) : float 

120 Marginal marginal marginal utility 

121 """ 

122 

123 return (rho + 1.0) * rho * c ** (-rho - 2.0) 

124 

125 

126@utility_fix 

127def CRRAutilityPPPP(c, rho): 

128 """ 

129 Evaluates constant relative risk aversion (CRRA) marginal marginal marginal 

130 marginal utility of consumption c given risk aversion parameter rho. 

131 

132 Parameters 

133 ---------- 

134 c : float 

135 Consumption value 

136 rho : float 

137 Risk aversion 

138 

139 Returns 

140 ------- 

141 uPPPP : float 

142 Marginal marginal marginal marginal utility 

143 """ 

144 return -(rho + 2.0) * (rho + 1.0) * rho * c ** (-rho - 3.0) 

145 

146 

147def CRRAutility_inv(u, rho): 

148 """ 

149 Evaluates the inverse of the CRRA utility function (with risk aversion para- 

150 meter rho) at a given utility level u. 

151 

152 Parameters 

153 ---------- 

154 u : float 

155 Utility value 

156 rho : float 

157 Risk aversion 

158 

159 Returns 

160 ------- 

161 (unnamed) : float 

162 Consumption corresponding to given utility value 

163 """ 

164 

165 if rho == 1: 

166 return np.exp(u) 

167 

168 return ((1.0 - rho) * u) ** (1 / (1.0 - rho)) 

169 

170 

171def CRRAutilityP_inv(uP, rho): 

172 """ 

173 Evaluates the inverse of the CRRA marginal utility function (with risk aversion 

174 parameter rho) at a given marginal utility level uP. 

175 

176 Parameters 

177 ---------- 

178 uP : float 

179 Marginal utility value 

180 rho : float 

181 Risk aversion 

182 

183 Returns 

184 ------- 

185 (unnamed) : float 

186 Consumption corresponding to given marginal utility value. 

187 """ 

188 

189 return uP ** (-1.0 / rho) 

190 

191 

192def CRRAutility_invP(u, rho): 

193 """ 

194 Evaluates the derivative of the inverse of the CRRA utility function (with 

195 risk aversion parameter rho) at a given utility level u. 

196 

197 Parameters 

198 ---------- 

199 u : float 

200 Utility value 

201 rho : float 

202 Risk aversion 

203 

204 Returns 

205 ------- 

206 (unnamed) : float 

207 Marginal consumption corresponding to given utility value 

208 """ 

209 

210 if rho == 1: 

211 return np.exp(u) 

212 

213 return ((1.0 - rho) * u) ** (rho / (1.0 - rho)) 

214 

215 

216def CRRAutilityP_invP(uP, rho): 

217 """ 

218 Evaluates the derivative of the inverse of the CRRA marginal utility function 

219 (with risk aversion parameter rho) at a given marginal utility level uP. 

220 

221 Parameters 

222 ---------- 

223 uP : float 

224 Marginal utility value 

225 rho : float 

226 Risk aversion 

227 

228 Returns 

229 ------- 

230 (unnamed) : float 

231 Consumption corresponding to given marginal utility value 

232 """ 

233 

234 return (-1.0 / rho) * uP ** (-1.0 / rho - 1.0) 

235 

236 

237############################################################################### 

238 

239# Define legacy versions of CRRA utility functions with no decorator. 

240# These are only used by the numba-fied ConsIndShockModelFast, which is not 

241# compatible with the @utility_fix decorator. These functions have no docstrings 

242# because they are identical to the ones above but for the lack of decorator. 

243 

244 

245def CRRAutility_X(c, rho): 

246 if rho == 1: 

247 return np.log(c) 

248 return c ** (1.0 - rho) / (1.0 - rho) 

249 

250 

251def CRRAutilityP_X(c, rho): 

252 if rho == 1: 

253 return 1 / c 

254 else: 

255 return c**-rho 

256 

257 

258def CRRAutilityPP_X(c, rho): 

259 return -rho * c ** (-rho - 1.0) 

260 

261 

262############################################################################### 

263 

264 

265@utility_fix 

266def StoneGearyCRRAutility(c, rho, shifter, factor=1.0): 

267 """ 

268 Evaluates Stone-Geary version of a constant relative risk aversion (CRRA) 

269 utility of consumption c with given risk aversion parameter rho and 

270 Stone-Geary intercept parameter shifter 

271 

272 Parameters 

273 ---------- 

274 c : float 

275 Consumption value 

276 rho : float 

277 Relative risk aversion 

278 shifter : float 

279 Intercept in Stone-Geary utility 

280 Returns 

281 ------- 

282 (unnamed) : float 

283 Utility 

284 

285 Tests 

286 ----- 

287 Test a value which should pass: 

288 >>> c, CRRA, stone_geary = 1.0, 2.0, 0.0 

289 >>> StoneGearyCRRAutility(c=c, rho=CRRA, shifter=stone_geary) 

290 -1.0 

291 """ 

292 

293 if rho == 1: 

294 return factor * np.log(shifter + c) 

295 

296 return factor * (shifter + c) ** (1.0 - rho) / (1.0 - rho) 

297 

298 

299@utility_fix 

300def StoneGearyCRRAutilityP(c, rho, shifter, factor=1.0): 

301 """ 

302 Marginal utility of Stone-Geary version of a constant relative risk aversion (CRRA) 

303 utility of consumption c with a given risk aversion parameter rho and 

304 Stone-Geary intercept parameter shifter 

305 

306 Parameters 

307 ---------- 

308 c : float 

309 Consumption value 

310 rho : float 

311 Relative risk aversion 

312 shifter : float 

313 Intercept in Stone-Geary utility 

314 Returns 

315 ------- 

316 (unnamed) : float 

317 marginal utility 

318 

319 """ 

320 

321 return factor * (shifter + c) ** (-rho) 

322 

323 

324@utility_fix 

325def StoneGearyCRRAutilityPP(c, rho, shifter, factor=1.0): 

326 """ 

327 Marginal marginal utility of Stone-Geary version of a CRRA utilty function 

328 with risk aversion parameter rho and Stone-Geary intercept parameter shifter 

329 

330 Parameters 

331 ---------- 

332 c : float 

333 Consumption value 

334 rho : float 

335 Relative risk aversion 

336 shifter : float 

337 Intercept in Stone-Geary utility 

338 Returns 

339 ------- 

340 (unnamed) : float 

341 marginal utility 

342 

343 """ 

344 

345 return factor * (-rho) * (shifter + c) ** (-rho - 1) 

346 

347 

348def StoneGearyCRRAutility_inv(u, rho, shifter, factor=1.0): 

349 return (u * (1.0 - rho) / factor) ** (1.0 / (1.0 - rho)) - shifter 

350 

351 

352def StoneGearyCRRAutilityP_inv(uP, rho, shifter, factor=1.0): 

353 return (uP / factor) ** (-1.0 / rho) - shifter 

354 

355 

356def StoneGearyCRRAutility_invP(u, rho, shifter, factor=1.0): 

357 return (1.0 / (1.0 - rho)) * (u * (1.0 - rho) / factor) ** (1.0 / (1.0 - rho) - 1.0) 

358 

359 

360def StoneGearyCRRAutilityP_invP(uP, rho, shifter, factor=1.0): 

361 return (-1.0 / rho) * (uP / factor) ** (-1.0 / rho - 1.0) 

362 

363 

364@utility_fix 

365def CARAutility(c, alpha): 

366 """ 

367 Evaluates constant absolute risk aversion (CARA) utility of consumption c 

368 given risk aversion parameter alpha. 

369 

370 Parameters 

371 ---------- 

372 c: float 

373 Consumption value 

374 alpha: float 

375 Risk aversion 

376 

377 Returns 

378 ------- 

379 (unnamed): float 

380 Utility 

381 """ 

382 return 1 - np.exp(-alpha * c) / alpha 

383 

384 

385@utility_fix 

386def CARAutilityP(c, alpha): 

387 """ 

388 Evaluates constant absolute risk aversion (CARA) marginal utility of 

389 consumption c given risk aversion parameter alpha. 

390 

391 Parameters 

392 ---------- 

393 c: float 

394 Consumption value 

395 alpha: float 

396 Risk aversion 

397 

398 Returns 

399 ------- 

400 (unnamed): float 

401 Marginal utility 

402 """ 

403 return np.exp(-alpha * c) 

404 

405 

406@utility_fix 

407def CARAutilityPP(c, alpha): 

408 """ 

409 Evaluates constant absolute risk aversion (CARA) marginal marginal utility 

410 of consumption c given risk aversion parameter alpha. 

411 

412 Parameters 

413 ---------- 

414 c: float 

415 Consumption value 

416 alpha: float 

417 Risk aversion 

418 

419 Returns 

420 ------- 

421 (unnamed): float 

422 Marginal marginal utility 

423 """ 

424 return -alpha * np.exp(-alpha * c) 

425 

426 

427@utility_fix 

428def CARAutilityPPP(c, alpha): 

429 """ 

430 Evaluates constant absolute risk aversion (CARA) marginal marginal marginal 

431 utility of consumption c given risk aversion parameter alpha. 

432 

433 Parameters 

434 ---------- 

435 c: float 

436 Consumption value 

437 alpha: float 

438 Risk aversion 

439 

440 Returns 

441 ------- 

442 (unnamed): float 

443 Marginal marginal marginal utility 

444 """ 

445 return alpha**2.0 * np.exp(-alpha * c) 

446 

447 

448def CARAutility_inv(u, alpha): 

449 """ 

450 Evaluates inverse of constant absolute risk aversion (CARA) utility function 

451 at utility level u given risk aversion parameter alpha. 

452 

453 Parameters 

454 ---------- 

455 u: float 

456 Utility value 

457 alpha: float 

458 Risk aversion 

459 

460 Returns 

461 ------- 

462 (unnamed): float 

463 Consumption value corresponding to u 

464 """ 

465 return -1.0 / alpha * np.log(alpha * (1 - u)) 

466 

467 

468@utility_fix 

469def CARAutilityP_inv(uP, alpha): 

470 """ 

471 Evaluates the inverse of constant absolute risk aversion (CARA) marginal 

472 utility function at marginal utility uP given risk aversion parameter alpha. 

473 

474 Parameters 

475 ---------- 

476 uP: float 

477 Utility value 

478 alpha: float 

479 Risk aversion 

480 

481 Returns 

482 ------- 

483 (unnamed): float 

484 Consumption value corresponding to uP 

485 """ 

486 return -1.0 / alpha * np.log(uP) 

487 

488 

489def CARAutilityP_invP(uP, alpha): 

490 """ 

491 Evaluates the derivative of inverse of constant absolute risk aversion (CARA) 

492 marginal utility function at marginal utility uP given risk aversion parameter alpha. 

493 

494 Parameters 

495 ---------- 

496 uP: float 

497 Utility value 

498 alpha: float 

499 Risk aversion 

500 

501 Returns 

502 ------- 

503 (unnamed): float 

504 Consumption value corresponding to uP 

505 """ 

506 

507 return -1.0 / (alpha * uP) 

508 

509 

510def CARAutility_invP(u, alpha): 

511 """ 

512 Evaluates the derivative of inverse of constant absolute risk aversion (CARA) 

513 utility function at utility level u given risk aversion parameter alpha. 

514 

515 Parameters 

516 ---------- 

517 u: float 

518 Utility value 

519 alpha: float 

520 Risk aversion 

521 

522 Returns 

523 ------- 

524 (unnamed): float 

525 Marginal onsumption value corresponding to u 

526 """ 

527 

528 return 1.0 / (alpha * (1.0 - u)) 

529 

530 

531def cobb_douglas(x, zeta, factor): 

532 """ 

533 Evaluates Cobb Douglas utility at quantities of goods consumed `x` 

534 given elasticity parameters `zeta` and efficiency parameter `factor`. 

535 

536 Parameters 

537 ---------- 

538 x : np.ndarray 

539 Quantities of goods consumed. First axis must index goods. 

540 zeta : np.ndarray 

541 Elasticity parameters for each good. Must be consistent with `x`. 

542 factor : float 

543 Multiplicative efficiency parameter. (e.g. TFP in production function) 

544 

545 Returns 

546 ------- 

547 (unnamed) : np.ndarray 

548 Utility 

549 

550 """ 

551 

552 # move goods axis to the end 

553 goods = np.moveaxis(x, 0, -1) 

554 

555 return factor * np.sum(goods**zeta, axis=-1) 

556 

557 

558def cobb_douglas_p(x, zeta, factor, arg=0): 

559 """ 

560 Evaluates the marginal utility of consumption indexed by `arg` good at 

561 quantities of goods consumed `x` given elasticity parameters `zeta` 

562 and efficiency parameter `factor`. 

563 

564 Parameters 

565 ---------- 

566 x : np.ndarray 

567 Quantities of goods consumed. First axis must index goods. 

568 zeta : np.ndarray 

569 Elasticity parameters for each good. Must be consistent with `x`. 

570 factor : float 

571 Multiplicative efficiency parameter. 

572 arg : int 

573 Index of good to evaluate marginal utility. 

574 

575 Returns 

576 ------- 

577 (unnamed) : np.ndarray 

578 Utility 

579 """ 

580 

581 return cobb_douglas(x, zeta, factor) * zeta[arg] / x[arg] 

582 

583 

584def cobb_douglas_pp(x, zeta, factor, args=(0, 1)): 

585 """ 

586 Evaluates the marginal marginal utility of consumption indexed by `args` 

587 at quantities of goods consumed `x` given elasticity parameters `zeta` 

588 and efficiency parameter `factor`. 

589 

590 Parameters 

591 ---------- 

592 x : np.ndarray 

593 Quantities of goods consumed. First axis must index goods. 

594 zeta : np.ndarray 

595 Elasticity parameters for each good. Must be consistent with `x`. 

596 factor : float 

597 Multiplicative efficiency parameter. 

598 args : tuple(int) 

599 Indexes of goods to evaluate marginal utility. `args[0]` is the 

600 index of the first derivative taken, and `args[1]` is the index of 

601 the second derivative taken. 

602 

603 Returns 

604 ------- 

605 (unnamed) : np.ndarray 

606 Utility 

607 """ 

608 

609 if args[0] == args[1]: 

610 coeff = zeta[args[0]] - 1 

611 else: 

612 coeff = zeta[args[1]] 

613 

614 return cobb_douglas_p(x, zeta, factor, args[0]) * coeff / x[args[1]] 

615 

616 

617def cobb_douglas_pn(x, zeta, factor, args=()): 

618 """ 

619 Evaluates the nth marginal utility of consumption indexed by `args` 

620 at quantities of goods consumed `x` given elasticity parameters `zeta` 

621 and efficiency parameter `factor`. 

622 

623 Parameters 

624 ---------- 

625 x : np.ndarray 

626 Quantities of goods consumed. First axis must index goods. 

627 zeta : np.ndarray 

628 Elasticity parameters for each good. Must be consistent with `x`. 

629 factor : float 

630 Multiplicative efficiency parameter. 

631 args : tuple(int) 

632 Indexes of goods to evaluate marginal utility. `args[0]` is the 

633 index of the first derivative taken, and `args[1]` is the index of 

634 the second derivative taken. This function works by recursively taking 

635 derivatives, so `args` can be of any length. 

636 

637 Returns 

638 ------- 

639 (unnamed) : np.ndarray 

640 Utility 

641 """ 

642 

643 if isinstance(args, int): 

644 args = (args,) 

645 

646 if len(args): 

647 idx = args[-1] # last index 

648 counts = dict(zip(*np.unique(args, return_counts=True))) 

649 coeff = zeta[idx] - counts[idx] + 1 

650 new_args = tuple(list(args)[:-1]) # remove last element 

651 return cobb_douglas_pn(x, zeta, factor, new_args) * coeff / x[idx] 

652 else: 

653 return cobb_douglas(x, zeta, factor) 

654 

655 

656def const_elast_subs(x, zeta, subs, factor, power): 

657 """ 

658 Evaluates Constant Elasticity of Substitution utility at quantities of 

659 goods consumed `x` given parameters `alpha`, 'subs', 'factor', and 'power'. 

660 

661 Parameters 

662 ---------- 

663 x : np.ndarray 

664 Quantities of goods consumed. First axis must index goods. 

665 zeta : Sequence[float] 

666 Share parameter for each good. Must be consistent with `x`. 

667 subs : float 

668 Substitution parameter. 

669 factor : float 

670 Factor productivity parameter. (e.g. TFP in production function) 

671 power : float 

672 degree of homogeneity of the utility function 

673 

674 Returns 

675 ------- 

676 np.ndarray 

677 CES utility. 

678 """ 

679 

680 # move goods axis to the end 

681 goods = np.moveaxis(x, 0, -1) 

682 

683 return factor * np.sum(zeta * goods**subs, axis=-1) ** (power / subs) 

684 

685 

686def const_elast_subs_p(x, zeta, subs, factor, power, arg=0): 

687 """ 

688 Evaluates the marginal utility of consumption indexed by `arg` good at quantities 

689 of goods consumed `x` given parameters `alpha`, 'subs', 'factor', and 'power'. 

690 

691 Parameters 

692 ---------- 

693 x : np.ndarray 

694 Quantities of goods consumed. First axis must index goods. 

695 zeta : Sequence[float] 

696 Share parameter for each good. Must be consistent with `x`. 

697 subs : float 

698 Substitution parameter. 

699 factor : float 

700 Factor productivity parameter. (e.g. TFP in production function) 

701 power : float 

702 degree of homogeneity of the utility function 

703 

704 Returns 

705 ------- 

706 np.ndarray 

707 CES marginal utility. 

708 """ 

709 

710 return ( 

711 const_elast_subs(x, zeta, factor * power / subs, subs, power - subs) 

712 * zeta[arg] 

713 * subs 

714 * x[arg] ** (subs - 1) 

715 ) 

716 

717 

718class UtilityFunction(MetricObject): 

719 distance_criteria = ["eval_func"] 

720 

721 def __init__(self, eval_func, der_func=None, inv_func=None): 

722 self.eval_func = eval_func 

723 self.der_func = der_func 

724 self.inv_func = inv_func 

725 

726 def __call__(self, *args, **kwargs): 

727 return self.eval_func(*args, **kwargs) 

728 

729 def derivative(self, *args, **kwargs): 

730 if self.der_func is None: 

731 raise NotImplementedError("No derivative function available") 

732 return self.der_func(*args, **kwargs) 

733 

734 def inverse(self, *args, **kwargs): 

735 if self.inv_func is None: 

736 raise NotImplementedError("No inverse function available") 

737 return self.inv_func(*args, **kwargs) 

738 

739 def der(self, *args, **kwargs): 

740 return self.derivative(*args, **kwargs) 

741 

742 def inv(self, *args, **kwargs): 

743 return self.inverse(*args, **kwargs) 

744 

745 

746def CDutility(c, d, c_share, d_bar): 

747 return c**c_share * (d + d_bar) ** (1 - c_share) 

748 

749 

750def CDutilityPc(c, d, c_share, d_bar): 

751 return c_share * ((d + d_bar) / c) ** (1 - c_share) 

752 

753 

754def CDutilityPd(c, d, c_share, d_bar): 

755 return (1 - c_share) * (c / (d + d_bar)) ** c_share 

756 

757 

758def CDutilityPc_inv(uc, d, c_share, d_bar): 

759 return (d + d_bar) * (uc / c_share) ** (1 / (1 - c_share)) 

760 

761 

762def CRRACDutility(c, d, c_share, d_bar, CRRA): 

763 return CDutility(c, d, c_share, d_bar) ** (1 - CRRA) / (1 - CRRA) 

764 

765 

766def CRRACDutilityPc(c, d, c_share, d_bar, CRRA): 

767 return c_share / c * CDutility(c, d, c_share, d_bar) ** (1 - CRRA) 

768 

769 

770def CRRACDutilityPd(c, d, c_share, d_bar, CRRA): 

771 return (1 - c_share) / (d + d_bar) * CDutility(c, d, c_share, d_bar) ** (1 - CRRA) 

772 

773 

774def CRRACDutilityPc_inv(uc, d, c_share, d_bar, CRRA): 

775 return (c_share / uc * (d + d_bar) ** (c_share * CRRA - c_share - CRRA + 1)) ** ( 

776 1 / (c_share * CRRA - c_share + 1) 

777 ) 

778 

779 

780class UtilityFuncCRRA(UtilityFunction): 

781 """ 

782 A class for representing a CRRA utility function. 

783 

784 Parameters 

785 ---------- 

786 CRRA : float 

787 The coefficient of constant relative risk aversion. 

788 """ 

789 

790 distance_criteria = ["CRRA"] 

791 

792 def __init__(self, CRRA): 

793 self.CRRA = CRRA 

794 

795 def __call__(self, c, order=0): 

796 """ 

797 Evaluate the utility function at a given level of consumption c. 

798 

799 Parameters 

800 ---------- 

801 c : float or np.ndarray 

802 Consumption level(s). 

803 order : int, optional 

804 Order of derivative. For example, `order == 1` returns the 

805 first derivative of utility of consumption, and so on. By default 0. 

806 

807 Returns 

808 ------- 

809 float or np.ndarray 

810 Utility (or its derivative) evaluated at given consumption level(s). 

811 """ 

812 if order == 0: 

813 try: 

814 return CRRAutility(c, self.CRRA) 

815 except: 

816 return CRRAutility_X(c, self.CRRA) 

817 else: # order >= 1 

818 return self.derivative(c, order) 

819 

820 def derivative(self, c, order=1): 

821 """ 

822 The derivative of the utility function at a given level of consumption c. 

823 

824 Parameters 

825 ---------- 

826 c : float or np.ndarray 

827 Consumption level(s). 

828 order : int, optional 

829 Order of derivative. For example, `order == 1` returns the 

830 first derivative of utility of consumption, and so on. By default 1. 

831 

832 Returns 

833 ------- 

834 float or np.ndarray 

835 Derivative of CRRA utility evaluated at given consumption level(s). 

836 

837 Raises 

838 ------ 

839 ValueError 

840 Derivative of order higher than 4 is not supported. 

841 """ 

842 if order == 1: 

843 try: 

844 return CRRAutilityP(c, self.CRRA) 

845 except: 

846 return CRRAutilityP_X(c, self.CRRA) 

847 elif order == 2: 

848 try: 

849 return CRRAutilityPP(c, self.CRRA) 

850 except: 

851 return CRRAutilityPP_X(c, self.CRRA) 

852 elif order == 3: 

853 return CRRAutilityPPP(c, self.CRRA) 

854 elif order == 4: 

855 return CRRAutilityPPPP(c, self.CRRA) 

856 else: 

857 raise ValueError(f"Derivative of order {order} not supported") 

858 

859 def inverse(self, u, order=(0, 0)): 

860 """ 

861 The inverse of the utility function at a given level of utility u. 

862 

863 Parameters 

864 ---------- 

865 u : float or np.ndarray 

866 Utility level(s). 

867 order : tuple, optional 

868 Order of derivatives. For example, `order == (1,1)` represents 

869 the first derivative of utility, inverted, and then differentiated 

870 once. For a simple mnemonic, order refers to the number of `P`s in 

871 the function `CRRAutility[#1]_inv[#2]`. By default (0, 0), 

872 which is just the inverse of utility. 

873 

874 Returns 

875 ------- 

876 float or np.ndarray 

877 Inverse of CRRA utility evaluated at given utility level(s). 

878 

879 Raises 

880 ------ 

881 ValueError 

882 Higher order derivatives are not supported. 

883 """ 

884 if order == (0, 0): 

885 return CRRAutility_inv(u, self.CRRA) 

886 elif order == (1, 0): 

887 return CRRAutilityP_inv(u, self.CRRA) 

888 elif order == (0, 1): 

889 return CRRAutility_invP(u, self.CRRA) 

890 elif order == (1, 1): 

891 return CRRAutilityP_invP(u, self.CRRA) 

892 else: 

893 raise ValueError(f"Inverse of order {order} not supported") 

894 

895 def derinv(self, u, order=(1, 0)): 

896 """ 

897 Short alias for inverse with default order = (1,0). See `self.inverse`. 

898 """ 

899 return self.inverse(u, order) 

900 

901 

902class UtilityFuncCARA(UtilityFunction): 

903 """ 

904 A class for representing a CARA utility function. 

905 

906 Parameters 

907 ---------- 

908 CARA : float 

909 The coefficient of constant absolute risk aversion. 

910 """ 

911 

912 distance_criteria = ["CARA"] 

913 

914 def __init__(self, CARA): 

915 self.CARA = CARA 

916 

917 def __call__(self, c, order=0): 

918 """ 

919 Evaluate the utility function at a given level of consumption c. 

920 

921 Parameters 

922 ---------- 

923 c : float or np.ndarray 

924 Consumption level(s). 

925 order : int, optional 

926 Order of derivative. For example, `order == 1` returns the 

927 first derivative of utility of consumption, and so on. By default 0. 

928 

929 Returns 

930 ------- 

931 float or np.ndarray 

932 Utility (or its derivative) evaluated at given consumption level(s). 

933 """ 

934 if order == 0: 

935 return CARAutility(c, self.CARA) 

936 else: # order >= 1 

937 return self.derivative(c, order) 

938 

939 def derivative(self, c, order=1): 

940 """ 

941 The derivative of the utility function at a given level of consumption c. 

942 

943 Parameters 

944 ---------- 

945 c : float or np.ndarray 

946 Consumption level(s). 

947 order : int, optional 

948 Order of derivative. For example, `order == 1` returns the 

949 first derivative of utility of consumption, and so on. By default 1. 

950 

951 Returns 

952 ------- 

953 float or np.ndarray 

954 Derivative of CRRA utility evaluated at given consumption level(s). 

955 

956 Raises 

957 ------ 

958 ValueError 

959 Derivative of order higher than 4 is not supported. 

960 """ 

961 if order == 1: 

962 return CARAutilityP(c, self.CARA) 

963 elif order == 2: 

964 return CARAutilityPP(c, self.CARA) 

965 elif order == 3: 

966 return CARAutilityPPP(c, self.CARA) 

967 else: 

968 raise ValueError(f"Derivative of order {order} not supported") 

969 

970 def inverse(self, u, order=(0, 0)): 

971 """ 

972 The inverse of the utility function at a given level of utility u. 

973 

974 Parameters 

975 ---------- 

976 u : float or np.ndarray 

977 Utility level(s). 

978 order : tuple, optional 

979 Order of derivatives. For example, `order == (1,1)` represents 

980 the first derivative of utility, inverted, and then differentiated 

981 once. For a simple mnemonic, order refers to the number of `P`s in 

982 the function `CRRAutility[#1]_inv[#2]`. By default (0, 0), 

983 which is just the inverse of utility. 

984 

985 Returns 

986 ------- 

987 float or np.ndarray 

988 Inverse of CRRA utility evaluated at given utility level(s). 

989 

990 Raises 

991 ------ 

992 ValueError 

993 Higher order derivatives are not supported. 

994 """ 

995 if order == (0, 0): 

996 return CARAutility_inv(u, self.CARA) 

997 elif order == (1, 0): 

998 return CARAutilityP_inv(u, self.CARA) 

999 elif order == (0, 1): 

1000 return CARAutility_invP(u, self.CARA) 

1001 elif order == (1, 1): 

1002 return CARAutilityP_invP(u, self.CARA) 

1003 else: 

1004 raise ValueError(f"Inverse of order {order} not supported") 

1005 

1006 def derinv(self, u, order=(1, 0)): 

1007 """ 

1008 Short alias for inverse with default order = (1,0). See `self.inverse`. 

1009 """ 

1010 return self.inverse(u, order) 

1011 

1012 

1013class UtilityFuncStoneGeary(UtilityFuncCRRA): 

1014 def __init__(self, CRRA, factor=1.0, shifter=0.0): 

1015 self.CRRA = CRRA 

1016 self.factor = factor 

1017 self.shifter = shifter 

1018 

1019 def __call__(self, c, order=0): 

1020 if order == 0: 

1021 return StoneGearyCRRAutility(c, self.CRRA, self.shifter, self.factor) 

1022 else: # order >= 1 

1023 return self.derivative(c, order) 

1024 

1025 def derivative(self, c, order=1): 

1026 if order == 1: 

1027 return StoneGearyCRRAutilityP(c, self.CRRA, self.shifter, self.factor) 

1028 elif order == 2: 

1029 return StoneGearyCRRAutilityPP(c, self.CRRA, self.shifter, self.factor) 

1030 else: 

1031 raise ValueError(f"Derivative of order {order} not supported") 

1032 

1033 def inverse(self, u, order=(0, 0)): 

1034 if order == (0, 0): 

1035 return StoneGearyCRRAutility_inv(u, self.CRRA, self.shifter, self.factor) 

1036 elif order == (1, 0): 

1037 return StoneGearyCRRAutilityP_inv(u, self.CRRA, self.shifter, self.factor) 

1038 elif order == (0, 1): 

1039 return StoneGearyCRRAutility_invP(u, self.CRRA, self.shifter, self.factor) 

1040 elif order == (1, 1): 

1041 return StoneGearyCRRAutilityP_invP(u, self.CRRA, self.shifter, self.factor) 

1042 else: 

1043 raise ValueError(f"Inverse of order {order} not supported") 

1044 

1045 

1046class UtilityFuncCobbDouglas(UtilityFunction): 

1047 """ 

1048 A class for representing a Cobb-Douglas utility function. 

1049 

1050 TODO: Add inverse methods. 

1051 

1052 Parameters 

1053 ---------- 

1054 c_share : float 

1055 Share parameter for consumption. Must be between 0 and 1. 

1056 d_bar : float 

1057 Intercept parameter for durable consumption. Must be non-negative. 

1058 """ 

1059 

1060 distance_criteria = ["c_share", "d_bar"] 

1061 

1062 def __init__(self, c_share, d_bar=0): 

1063 self.c_share = c_share 

1064 self.d_bar = d_bar 

1065 

1066 assert 0 <= c_share <= 1, "Share parameter must be between 0 and 1." 

1067 

1068 def __call__(self, c, d): 

1069 """ 

1070 Evaluate the utility function at a given level of consumption c. 

1071 """ 

1072 return CDutility(c, d, self.c_share, self.d_bar) 

1073 

1074 def derivative(self, c, d, axis=0): 

1075 if axis == 0: 

1076 return CDutilityPc(c, d, self.c_share, self.d_bar) 

1077 elif axis == 1: 

1078 return CDutilityPd(c, d, self.c_share, self.d_bar) 

1079 else: 

1080 raise ValueError(f"Axis must be 0 or 1, not {axis}") 

1081 

1082 def inverse(self, uc, d): 

1083 return CDutilityPc_inv(uc, d, self.c_share, self.d_bar) 

1084 

1085 

1086class UtilityFuncCobbDouglasCRRA(UtilityFuncCobbDouglas): 

1087 """ 

1088 A class for representing a Cobb-Douglas aggregated CRRA utility function. 

1089 

1090 TODO: Add derivative and inverse methods. 

1091 

1092 Parameters 

1093 ---------- 

1094 c_share : float 

1095 Share parameter for consumption. Must be between 0 and 1. 

1096 d_bar : float 

1097 Intercept parameter for durable consumption. Must be non-negative. 

1098 CRRA: float 

1099 Coefficient of relative risk aversion. 

1100 """ 

1101 

1102 distance_criteria = ["c_share", "d_bar", "CRRA"] 

1103 

1104 def __init__(self, c_share, CRRA, d_bar=0): 

1105 super().__init__(c_share, d_bar) 

1106 self.CRRA = CRRA 

1107 

1108 def __call__(self, c, d): 

1109 return CRRACDutility(c, d, self.c_share, self.d_bar, self.CRRA) 

1110 

1111 def derivative(self, c, d, axis=0): 

1112 if axis == 0: 

1113 return CRRACDutilityPc(c, d, self.c_share, self.d_bar, self.CRRA) 

1114 elif axis == 1: 

1115 return CRRACDutilityPd(c, d, self.c_share, self.d_bar, self.CRRA) 

1116 else: 

1117 raise ValueError(f"Axis must be 0 or 1, not {axis}") 

1118 

1119 def inverse(self, uc, d): 

1120 return CRRACDutilityPc_inv(uc, d, self.c_share, self.d_bar, self.CRRA) 

1121 

1122 

1123class UtilityFuncConstElastSubs(UtilityFunction): 

1124 """ 

1125 A class for representing a constant elasticity of substitution utility function. 

1126 

1127 TODO: Add derivative and inverse methods. 

1128 

1129 Parameters 

1130 ---------- 

1131 shares : Sequence[float] 

1132 Share parameter for each good. Must be consistent with `x`. 

1133 subs : float 

1134 Substitution parameter. 

1135 factor : float 

1136 Factor productivity parameter. (e.g. TFP in production function) 

1137 homogeneity : float 

1138 degree of homogeneity of the utility function 

1139 """ 

1140 

1141 distance_criteria = ["shares", "subs", "factor", "homogeneity"] 

1142 

1143 def __init__(self, shares, subs, homogeneity=1.0, factor=1.0): 

1144 assert subs != 0.0, "Consider using a Cobb-Douglas utility function instead." 

1145 assert subs != 1.0, "Linear utility is not implemented." 

1146 

1147 self.shares = np.asarray(shares) 

1148 self.subs = subs 

1149 self.homogeneity = homogeneity 

1150 self.factor = factor 

1151 

1152 self.CES = 1 / (1 - subs) 

1153 self.dim = self.shares.size # number of goods 

1154 

1155 def __call__(self, x): 

1156 return const_elast_subs( 

1157 x, self.shares, self.subs, self.factor, self.homogeneity 

1158 ) 

1159 

1160 def derivative(self, x, arg=0): 

1161 return const_elast_subs_p( 

1162 x, self.shares, self.subs, self.factor, self.homogeneity, arg 

1163 )