Coverage for HARK/rewards.py: 74%

266 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-02 05:14 +0000

1import numpy as np 

2from HARK.metric import MetricObject 

3 

4# ============================================================================== 

5# ============== Define utility functions =============================== 

6# ============================================================================== 

7 

8 

9def CRRAutility(c, rho): 

10 """ 

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

12 given risk aversion parameter rho. 

13 

14 Parameters 

15 ---------- 

16 c : float 

17 Consumption value 

18 rho : float 

19 Risk aversion 

20 

21 Returns 

22 ------- 

23 (unnamed) : float 

24 Utility 

25 

26 Tests 

27 ----- 

28 Test a value which should pass: 

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

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

31 -1.0 

32 """ 

33 

34 if np.isscalar(c): 

35 c = np.asarray(c) 

36 if rho == 1: 

37 return np.log(c) 

38 

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

40 

41 

42def CRRAutilityP(c, rho): 

43 """ 

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

45 c given risk aversion parameter rho. 

46 

47 Parameters 

48 ---------- 

49 c : float 

50 Consumption value 

51 rho : float 

52 Risk aversion 

53 

54 Returns 

55 ------- 

56 (unnamed) : float 

57 Marginal utility 

58 """ 

59 

60 if np.isscalar(c): 

61 c = np.asarray(c) 

62 if rho == 1: 

63 return 1 / c 

64 

65 return c**-rho 

66 

67 

68def CRRAutilityPP(c, rho): 

69 """ 

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

71 consumption c given risk aversion parameter rho. 

72 

73 Parameters 

74 ---------- 

75 c : float 

76 Consumption value 

77 rho : float 

78 Risk aversion 

79 

80 Returns 

81 ------- 

82 (unnamed) : float 

83 Marginal marginal utility 

84 """ 

85 

86 if np.isscalar(c): 

87 c = np.asarray(c) 

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

89 

90 

91def CRRAutilityPPP(c, rho): 

92 """ 

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

94 utility of consumption c given risk aversion parameter rho. 

95 

96 Parameters 

97 ---------- 

98 c : float 

99 Consumption value 

100 rho : float 

101 Risk aversion 

102 

103 Returns 

104 ------- 

105 (unnamed) : float 

106 Marginal marginal marginal utility 

107 """ 

108 

109 if np.isscalar(c): 

110 c = np.asarray(c) 

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

112 

113 

114def CRRAutilityPPPP(c, rho): 

115 """ 

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

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

118 

119 Parameters 

120 ---------- 

121 c : float 

122 Consumption value 

123 rho : float 

124 Risk aversion 

125 

126 Returns 

127 ------- 

128 (unnamed) : float 

129 Marginal marginal marginal marginal utility 

130 """ 

131 

132 if np.isscalar(c): 

133 c = np.asarray(c) 

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

135 

136 

137def CRRAutility_inv(u, rho): 

138 """ 

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

140 meter rho) at a given utility level u. 

141 

142 Parameters 

143 ---------- 

144 u : float 

145 Utility value 

146 rho : float 

147 Risk aversion 

148 

149 Returns 

150 ------- 

151 (unnamed) : float 

152 Consumption corresponding to given utility value 

153 """ 

154 

155 if np.isscalar(u): 

156 u = np.asarray(u) 

157 if rho == 1: 

158 return np.exp(u) 

159 

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

161 

162 

163def CRRAutilityP_inv(uP, rho): 

164 """ 

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

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

167 

168 Parameters 

169 ---------- 

170 uP : float 

171 Marginal utility value 

172 rho : float 

173 Risk aversion 

174 

175 Returns 

176 ------- 

177 (unnamed) : float 

178 Consumption corresponding to given marginal utility value. 

179 """ 

180 

181 if np.isscalar(uP): 

182 uP = np.asarray(uP) 

183 return uP ** (-1.0 / rho) 

184 

185 

186def CRRAutility_invP(u, rho): 

187 """ 

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

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

190 

191 Parameters 

192 ---------- 

193 u : float 

194 Utility value 

195 rho : float 

196 Risk aversion 

197 

198 Returns 

199 ------- 

200 (unnamed) : float 

201 Marginal consumption corresponding to given utility value 

202 """ 

203 

204 if np.isscalar(u): 

205 u = np.asarray(u) 

206 if rho == 1: 

207 return np.exp(u) 

208 

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

210 

211 

212def CRRAutilityP_invP(uP, rho): 

213 """ 

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

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

216 

217 Parameters 

218 ---------- 

219 uP : float 

220 Marginal utility value 

221 rho : float 

222 Risk aversion 

223 

224 Returns 

225 ------- 

226 (unnamed) : float 

227 Consumption corresponding to given marginal utility value 

228 """ 

229 

230 if np.isscalar(uP): 

231 uP = np.asarray(uP) 

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

233 

234 

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

236 """ 

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

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

239 Stone-Geary intercept parameter shifter 

240 

241 Parameters 

242 ---------- 

243 c : float 

244 Consumption value 

245 rho : float 

246 Relative risk aversion 

247 shifter : float 

248 Intercept in Stone-Geary utility 

249 Returns 

250 ------- 

251 (unnamed) : float 

252 Utility 

253 

254 Tests 

255 ----- 

256 Test a value which should pass: 

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

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

259 -1.0 

260 """ 

261 

262 if np.isscalar(c): 

263 c = np.asarray(c) 

264 if rho == 1: 

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

266 

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

268 

269 

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

271 """ 

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

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

274 Stone-Geary intercept parameter shifter 

275 

276 Parameters 

277 ---------- 

278 c : float 

279 Consumption value 

280 rho : float 

281 Relative risk aversion 

282 shifter : float 

283 Intercept in Stone-Geary utility 

284 Returns 

285 ------- 

286 (unnamed) : float 

287 marginal utility 

288 

289 """ 

290 

291 if np.isscalar(c): 

292 c = np.asarray(c) 

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

294 

295 

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

297 """ 

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

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

300 

301 Parameters 

302 ---------- 

303 c : float 

304 Consumption value 

305 rho : float 

306 Relative risk aversion 

307 shifter : float 

308 Intercept in Stone-Geary utility 

309 Returns 

310 ------- 

311 (unnamed) : float 

312 marginal utility 

313 

314 """ 

315 

316 if np.isscalar(c): 

317 c = np.asarray(c) 

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

319 

320 

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

322 if np.isscalar(u): 

323 u = np.asarray(u) 

324 

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

326 

327 

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

329 if np.isscalar(uP): 

330 uP = np.asarray(uP) 

331 

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

333 

334 

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

336 if np.isscalar(u): 

337 u = np.asarray(u) 

338 

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

340 

341 

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

343 if np.isscalar(uP): 

344 uP = np.asarray(uP) 

345 

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

347 

348 

349def CARAutility(c, alpha): 

350 """ 

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

352 given risk aversion parameter alpha. 

353 

354 Parameters 

355 ---------- 

356 c: float 

357 Consumption value 

358 alpha: float 

359 Risk aversion 

360 

361 Returns 

362 ------- 

363 (unnamed): float 

364 Utility 

365 """ 

366 

367 if np.isscalar(c): 

368 c = np.asarray(c) 

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

370 

371 

372def CARAutilityP(c, alpha): 

373 """ 

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

375 consumption c given risk aversion parameter alpha. 

376 

377 Parameters 

378 ---------- 

379 c: float 

380 Consumption value 

381 alpha: float 

382 Risk aversion 

383 

384 Returns 

385 ------- 

386 (unnamed): float 

387 Marginal utility 

388 """ 

389 

390 if np.isscalar(c): 

391 c = np.asarray(c) 

392 return np.exp(-alpha * c) 

393 

394 

395def CARAutilityPP(c, alpha): 

396 """ 

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

398 of consumption c given risk aversion parameter alpha. 

399 

400 Parameters 

401 ---------- 

402 c: float 

403 Consumption value 

404 alpha: float 

405 Risk aversion 

406 

407 Returns 

408 ------- 

409 (unnamed): float 

410 Marginal marginal utility 

411 """ 

412 

413 if np.isscalar(c): 

414 c = np.asarray(c) 

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

416 

417 

418def CARAutilityPPP(c, alpha): 

419 """ 

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

421 utility of consumption c given risk aversion parameter alpha. 

422 

423 Parameters 

424 ---------- 

425 c: float 

426 Consumption value 

427 alpha: float 

428 Risk aversion 

429 

430 Returns 

431 ------- 

432 (unnamed): float 

433 Marginal marginal marginal utility 

434 """ 

435 

436 if np.isscalar(c): 

437 c = np.asarray(c) 

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

439 

440 

441def CARAutility_inv(u, alpha): 

442 """ 

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

444 at utility level u given risk aversion parameter alpha. 

445 

446 Parameters 

447 ---------- 

448 u: float 

449 Utility value 

450 alpha: float 

451 Risk aversion 

452 

453 Returns 

454 ------- 

455 (unnamed): float 

456 Consumption value corresponding to u 

457 """ 

458 

459 if np.isscalar(u): 

460 u = np.asarray(u) 

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

462 

463 

464def CARAutilityP_inv(u, alpha): 

465 """ 

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

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

468 

469 Parameters 

470 ---------- 

471 u: float 

472 Utility value 

473 alpha: float 

474 Risk aversion 

475 

476 Returns 

477 ------- 

478 (unnamed): float 

479 Consumption value corresponding to uP 

480 """ 

481 

482 if np.isscalar(u): 

483 u = np.asarray(u) 

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

485 

486 

487def CARAutility_invP(u, alpha): 

488 """ 

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

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

491 

492 Parameters 

493 ---------- 

494 u: float 

495 Utility value 

496 alpha: float 

497 Risk aversion 

498 

499 Returns 

500 ------- 

501 (unnamed): float 

502 Marginal onsumption value corresponding to u 

503 """ 

504 

505 if np.isscalar(u): 

506 u = np.asarray(u) 

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

508 

509 

510def cobb_douglas(x, zeta, factor): 

511 """ 

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

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

514 

515 Parameters 

516 ---------- 

517 x : np.ndarray 

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

519 zeta : np.ndarray 

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

521 factor : float 

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

523 

524 Returns 

525 ------- 

526 (unnamed) : np.ndarray 

527 Utility 

528 

529 """ 

530 

531 # move goods axis to the end 

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

533 

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

535 

536 

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

538 """ 

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

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

541 and efficiency parameter `factor`. 

542 

543 Parameters 

544 ---------- 

545 x : np.ndarray 

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

547 zeta : np.ndarray 

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

549 factor : float 

550 Multiplicative efficiency parameter. 

551 arg : int 

552 Index of good to evaluate marginal utility. 

553 

554 Returns 

555 ------- 

556 (unnamed) : np.ndarray 

557 Utility 

558 """ 

559 

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

561 

562 

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

564 """ 

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

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

567 and efficiency parameter `factor`. 

568 

569 Parameters 

570 ---------- 

571 x : np.ndarray 

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

573 zeta : np.ndarray 

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

575 factor : float 

576 Multiplicative efficiency parameter. 

577 args : tuple(int) 

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

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

580 the second derivative taken. 

581 

582 Returns 

583 ------- 

584 (unnamed) : np.ndarray 

585 Utility 

586 """ 

587 

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

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

590 else: 

591 coeff = zeta[args[1]] 

592 

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

594 

595 

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

597 """ 

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

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

600 and efficiency parameter `factor`. 

601 

602 Parameters 

603 ---------- 

604 x : np.ndarray 

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

606 zeta : np.ndarray 

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

608 factor : float 

609 Multiplicative efficiency parameter. 

610 args : tuple(int) 

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

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

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

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

615 

616 Returns 

617 ------- 

618 (unnamed) : np.ndarray 

619 Utility 

620 """ 

621 

622 if isinstance(args, int): 

623 args = (args,) 

624 

625 if len(args): 

626 idx = args[-1] # last index 

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

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

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

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

631 else: 

632 return cobb_douglas(x, zeta, factor) 

633 

634 

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

636 """ 

637 Evaluates Constant Elasticity of Substitution utility at quantities of 

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

639 

640 Parameters 

641 ---------- 

642 x : np.ndarray 

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

644 zeta : Sequence[float] 

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

646 subs : float 

647 Substitution parameter. 

648 factor : float 

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

650 power : float 

651 degree of homogeneity of the utility function 

652 

653 Returns 

654 ------- 

655 np.ndarray 

656 CES utility. 

657 """ 

658 

659 # move goods axis to the end 

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

661 

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

663 

664 

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

666 """ 

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

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

669 

670 Parameters 

671 ---------- 

672 x : np.ndarray 

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

674 zeta : Sequence[float] 

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

676 subs : float 

677 Substitution parameter. 

678 factor : float 

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

680 power : float 

681 degree of homogeneity of the utility function 

682 

683 Returns 

684 ------- 

685 np.ndarray 

686 CES marginal utility. 

687 """ 

688 

689 return ( 

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

691 * zeta[arg] 

692 * subs 

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

694 ) 

695 

696 

697class UtilityFunction(MetricObject): 

698 distance_criteria = ["eval_func"] 

699 

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

701 self.eval_func = eval_func 

702 self.der_func = der_func 

703 self.inv_func = inv_func 

704 

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

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

707 

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

709 if not hasattr(self, "der_func") or self.der_func is None: 

710 raise NotImplementedError("No derivative function available") 

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

712 

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

714 if not hasattr(self, "inv_func") or self.inv_func is None: 

715 raise NotImplementedError("No inverse function available") 

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

717 

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

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

720 

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

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

723 

724 

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

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

727 

728 

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

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

731 

732 

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

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

735 

736 

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

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

739 

740 

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

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

743 

744 

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

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

747 

748 

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

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

751 

752 

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

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

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

756 ) 

757 

758 

759class UtilityFuncCRRA(UtilityFunction): 

760 """ 

761 A class for representing a CRRA utility function. 

762 

763 Parameters 

764 ---------- 

765 CRRA : float 

766 The coefficient of constant relative risk aversion. 

767 """ 

768 

769 distance_criteria = ["CRRA"] 

770 

771 def __init__(self, CRRA): 

772 self.CRRA = CRRA 

773 

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

775 """ 

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

777 

778 Parameters 

779 ---------- 

780 c : float or np.ndarray 

781 Consumption level(s). 

782 order : int, optional 

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

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

785 

786 Returns 

787 ------- 

788 float or np.ndarray 

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

790 """ 

791 if order == 0: 

792 return CRRAutility(c, self.CRRA) 

793 else: # order >= 1 

794 return self.derivative(c, order) 

795 

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

797 """ 

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

799 

800 Parameters 

801 ---------- 

802 c : float or np.ndarray 

803 Consumption level(s). 

804 order : int, optional 

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

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

807 

808 Returns 

809 ------- 

810 float or np.ndarray 

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

812 

813 Raises 

814 ------ 

815 ValueError 

816 Derivative of order higher than 4 is not supported. 

817 """ 

818 if order == 1: 

819 return CRRAutilityP(c, self.CRRA) 

820 elif order == 2: 

821 return CRRAutilityPP(c, self.CRRA) 

822 elif order == 3: 

823 return CRRAutilityPPP(c, self.CRRA) 

824 elif order == 4: 

825 return CRRAutilityPPPP(c, self.CRRA) 

826 else: 

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

828 

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

830 """ 

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

832 

833 Parameters 

834 ---------- 

835 u : float or np.ndarray 

836 Utility level(s). 

837 order : tuple, optional 

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

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

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

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

842 which is just the inverse of utility. 

843 

844 Returns 

845 ------- 

846 float or np.ndarray 

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

848 

849 Raises 

850 ------ 

851 ValueError 

852 Higher order derivatives are not supported. 

853 """ 

854 if order == (0, 0): 

855 return CRRAutility_inv(u, self.CRRA) 

856 elif order == (1, 0): 

857 return CRRAutilityP_inv(u, self.CRRA) 

858 elif order == (0, 1): 

859 return CRRAutility_invP(u, self.CRRA) 

860 elif order == (1, 1): 

861 return CRRAutilityP_invP(u, self.CRRA) 

862 else: 

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

864 

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

866 """ 

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

868 """ 

869 return self.inverse(u, order) 

870 

871 

872class UtilityFuncStoneGeary(UtilityFuncCRRA): 

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

874 self.CRRA = CRRA 

875 self.factor = factor 

876 self.shifter = shifter 

877 

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

879 if order == 0: 

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

881 else: # order >= 1 

882 return self.derivative(c, order) 

883 

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

885 if order == 1: 

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

887 elif order == 2: 

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

889 else: 

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

891 

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

893 if order == (0, 0): 

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

895 elif order == (1, 0): 

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

897 elif order == (0, 1): 

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

899 elif order == (1, 1): 

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

901 else: 

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

903 

904 

905class UtilityFuncCobbDouglas(UtilityFunction): 

906 """ 

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

908 

909 TODO: Add inverse methods. 

910 

911 Parameters 

912 ---------- 

913 c_share : float 

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

915 d_bar : float 

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

917 """ 

918 

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

920 

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

922 self.c_share = c_share 

923 self.d_bar = d_bar 

924 

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

926 

927 def __call__(self, c, d): 

928 """ 

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

930 """ 

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

932 

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

934 if axis == 0: 

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

936 elif axis == 1: 

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

938 else: 

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

940 

941 def inverse(self, uc, d): 

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

943 

944 

945class UtilityFuncCobbDouglasCRRA(UtilityFuncCobbDouglas): 

946 """ 

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

948 

949 TODO: Add derivative and inverse methods. 

950 

951 Parameters 

952 ---------- 

953 c_share : float 

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

955 d_bar : float 

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

957 CRRA: float 

958 Coefficient of relative risk aversion. 

959 """ 

960 

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

962 

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

964 super().__init__(c_share, d_bar) 

965 self.CRRA = CRRA 

966 

967 def __call__(self, c, d): 

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

969 

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

971 if axis == 0: 

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

973 elif axis == 1: 

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

975 else: 

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

977 

978 def inverse(self, uc, d): 

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

980 

981 

982class UtilityFuncConstElastSubs(UtilityFunction): 

983 """ 

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

985 

986 TODO: Add derivative and inverse methods. 

987 

988 Parameters 

989 ---------- 

990 shares : Sequence[float] 

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

992 subs : float 

993 Substitution parameter. 

994 factor : float 

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

996 homogeneity : float 

997 degree of homogeneity of the utility function 

998 """ 

999 

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

1001 

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

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

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

1005 

1006 self.shares = np.asarray(shares) 

1007 self.subs = subs 

1008 self.homogeneity = homogeneity 

1009 self.factor = factor 

1010 

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

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

1013 

1014 def __call__(self, x): 

1015 return const_elast_subs( 

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

1017 ) 

1018 

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

1020 return const_elast_subs_p( 

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

1022 )