Coverage for HARK / rewards.py: 92%

304 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-26 06:00 +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 

23def utility_fix_SG(func): 

24 @functools.wraps(func) 

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

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

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

28 else: 

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

30 return out 

31 

32 return wrapper 

33 

34 

35# ============================================================================== 

36# ============== Define utility functions =============================== 

37# ============================================================================== 

38 

39 

40@utility_fix 

41def CRRAutility(c, rho): 

42 """ 

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

44 given risk aversion parameter rho. 

45 

46 Parameters 

47 ---------- 

48 c : float or array 

49 Consumption value 

50 rho : float 

51 Risk aversion 

52 

53 Returns 

54 ------- 

55 u : float or array 

56 Utility 

57 

58 Tests 

59 ----- 

60 Test a value which should pass: 

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

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

63 -1.0 

64 """ 

65 if rho == 1: 

66 return np.log(c) 

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

68 

69 

70@utility_fix 

71def CRRAutilityP(c, rho): 

72 """ 

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

74 c given risk aversion parameter rho. 

75 

76 Parameters 

77 ---------- 

78 c : float 

79 Consumption value 

80 rho : float 

81 Risk aversion 

82 

83 Returns 

84 ------- 

85 uP : float or array 

86 Marginal utility 

87 """ 

88 if rho == 1: 

89 return 1 / c 

90 else: 

91 return c**-rho 

92 

93 

94@utility_fix 

95def CRRAutilityPP(c, rho): 

96 """ 

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

98 consumption c given risk aversion parameter rho. 

99 

100 Parameters 

101 ---------- 

102 c : float 

103 Consumption value 

104 rho : float 

105 Risk aversion 

106 

107 Returns 

108 ------- 

109 uPP : float 

110 Marginal marginal utility 

111 """ 

112 

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

114 

115 

116@utility_fix 

117def CRRAutilityPPP(c, rho): 

118 """ 

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

120 utility of consumption c given risk aversion parameter rho. 

121 

122 Parameters 

123 ---------- 

124 c : float 

125 Consumption value 

126 rho : float 

127 Risk aversion 

128 

129 Returns 

130 ------- 

131 (unnamed) : float 

132 Marginal marginal marginal utility 

133 """ 

134 

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

136 

137 

138@utility_fix 

139def CRRAutilityPPPP(c, rho): 

140 """ 

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

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

143 

144 Parameters 

145 ---------- 

146 c : float 

147 Consumption value 

148 rho : float 

149 Risk aversion 

150 

151 Returns 

152 ------- 

153 uPPPP : float 

154 Marginal marginal marginal marginal utility 

155 """ 

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

157 

158 

159def CRRAutility_inv(u, rho): 

160 """ 

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

162 meter rho) at a given utility level u. 

163 

164 Parameters 

165 ---------- 

166 u : float 

167 Utility value 

168 rho : float 

169 Risk aversion 

170 

171 Returns 

172 ------- 

173 (unnamed) : float 

174 Consumption corresponding to given utility value 

175 """ 

176 

177 if rho == 1: 

178 return np.exp(u) 

179 

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

181 

182 

183def CRRAutilityP_inv(uP, rho): 

184 """ 

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

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

187 

188 Parameters 

189 ---------- 

190 uP : float 

191 Marginal utility value 

192 rho : float 

193 Risk aversion 

194 

195 Returns 

196 ------- 

197 (unnamed) : float 

198 Consumption corresponding to given marginal utility value. 

199 """ 

200 

201 return uP ** (-1.0 / rho) 

202 

203 

204def CRRAutility_invP(u, rho): 

205 """ 

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

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

208 

209 Parameters 

210 ---------- 

211 u : float 

212 Utility value 

213 rho : float 

214 Risk aversion 

215 

216 Returns 

217 ------- 

218 (unnamed) : float 

219 Marginal consumption corresponding to given utility value 

220 """ 

221 

222 if rho == 1: 

223 return np.exp(u) 

224 

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

226 

227 

228def CRRAutilityP_invP(uP, rho): 

229 """ 

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

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

232 

233 Parameters 

234 ---------- 

235 uP : float 

236 Marginal utility value 

237 rho : float 

238 Risk aversion 

239 

240 Returns 

241 ------- 

242 (unnamed) : float 

243 Consumption corresponding to given marginal utility value 

244 """ 

245 

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

247 

248 

249############################################################################### 

250 

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

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

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

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

255 

256 

257def CRRAutility_X(c, rho): 

258 if rho == 1: 

259 return np.log(c) 

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

261 

262 

263def CRRAutilityP_X(c, rho): 

264 if rho == 1: 

265 return 1 / c 

266 else: 

267 return c**-rho 

268 

269 

270def CRRAutilityPP_X(c, rho): 

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

272 

273 

274############################################################################### 

275 

276 

277@utility_fix_SG 

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

279 """ 

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

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

282 Stone-Geary intercept parameter shifter 

283 

284 Parameters 

285 ---------- 

286 c : float 

287 Consumption value 

288 rho : float 

289 Relative risk aversion 

290 shifter : float 

291 Intercept in Stone-Geary utility 

292 Returns 

293 ------- 

294 (unnamed) : float 

295 Utility 

296 

297 Tests 

298 ----- 

299 Test a value which should pass: 

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

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

302 -1.0 

303 """ 

304 

305 if rho == 1: 

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

307 

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

309 

310 

311@utility_fix_SG 

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

313 """ 

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

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

316 Stone-Geary intercept parameter shifter 

317 

318 Parameters 

319 ---------- 

320 c : float 

321 Consumption value 

322 rho : float 

323 Relative risk aversion 

324 shifter : float 

325 Intercept in Stone-Geary utility 

326 Returns 

327 ------- 

328 (unnamed) : float 

329 marginal utility 

330 

331 """ 

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

333 

334 

335@utility_fix_SG 

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

337 """ 

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

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

340 

341 Parameters 

342 ---------- 

343 c : float 

344 Consumption value 

345 rho : float 

346 Relative risk aversion 

347 shifter : float 

348 Intercept in Stone-Geary utility 

349 Returns 

350 ------- 

351 (unnamed) : float 

352 marginal utility 

353 

354 """ 

355 

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

357 

358 

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

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

361 

362 

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

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

365 

366 

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

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

369 

370 

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

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

373 

374 

375@utility_fix 

376def CARAutility(c, alpha): 

377 """ 

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

379 given risk aversion parameter alpha. 

380 

381 Parameters 

382 ---------- 

383 c: float 

384 Consumption value 

385 alpha: float 

386 Risk aversion 

387 

388 Returns 

389 ------- 

390 (unnamed): float 

391 Utility 

392 """ 

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

394 

395 

396@utility_fix 

397def CARAutilityP(c, alpha): 

398 """ 

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

400 consumption c given risk aversion parameter alpha. 

401 

402 Parameters 

403 ---------- 

404 c: float 

405 Consumption value 

406 alpha: float 

407 Risk aversion 

408 

409 Returns 

410 ------- 

411 (unnamed): float 

412 Marginal utility 

413 """ 

414 return np.exp(-alpha * c) 

415 

416 

417@utility_fix 

418def CARAutilityPP(c, alpha): 

419 """ 

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

421 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 utility 

434 """ 

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

436 

437 

438@utility_fix 

439def CARAutilityPPP(c, alpha): 

440 """ 

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

442 utility of consumption c given risk aversion parameter alpha. 

443 

444 Parameters 

445 ---------- 

446 c: float 

447 Consumption value 

448 alpha: float 

449 Risk aversion 

450 

451 Returns 

452 ------- 

453 (unnamed): float 

454 Marginal marginal marginal utility 

455 """ 

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

457 

458 

459def CARAutility_inv(u, alpha): 

460 """ 

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

462 at utility level u given risk aversion parameter alpha. 

463 

464 Parameters 

465 ---------- 

466 u: float 

467 Utility value 

468 alpha: float 

469 Risk aversion 

470 

471 Returns 

472 ------- 

473 (unnamed): float 

474 Consumption value corresponding to u 

475 """ 

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

477 

478 

479@utility_fix 

480def CARAutilityP_inv(uP, alpha): 

481 """ 

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

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

484 

485 Parameters 

486 ---------- 

487 uP: float 

488 Utility value 

489 alpha: float 

490 Risk aversion 

491 

492 Returns 

493 ------- 

494 (unnamed): float 

495 Consumption value corresponding to uP 

496 """ 

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

498 

499 

500def CARAutilityP_invP(uP, alpha): 

501 """ 

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

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

504 

505 Parameters 

506 ---------- 

507 uP: float 

508 Utility value 

509 alpha: float 

510 Risk aversion 

511 

512 Returns 

513 ------- 

514 (unnamed): float 

515 Consumption value corresponding to uP 

516 """ 

517 

518 return -1.0 / (alpha * uP) 

519 

520 

521def CARAutility_invP(u, alpha): 

522 """ 

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

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

525 

526 Parameters 

527 ---------- 

528 u: float 

529 Utility value 

530 alpha: float 

531 Risk aversion 

532 

533 Returns 

534 ------- 

535 (unnamed): float 

536 Marginal onsumption value corresponding to u 

537 """ 

538 

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

540 

541 

542def cobb_douglas(x, zeta, factor): 

543 """ 

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

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

546 

547 Parameters 

548 ---------- 

549 x : np.ndarray 

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

551 zeta : np.ndarray 

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

553 factor : float 

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

555 

556 Returns 

557 ------- 

558 (unnamed) : np.ndarray 

559 Utility 

560 

561 """ 

562 

563 # move goods axis to the end 

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

565 

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

567 

568 

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

570 """ 

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

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

573 and efficiency parameter `factor`. 

574 

575 Parameters 

576 ---------- 

577 x : np.ndarray 

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

579 zeta : np.ndarray 

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

581 factor : float 

582 Multiplicative efficiency parameter. 

583 arg : int 

584 Index of good to evaluate marginal utility. 

585 

586 Returns 

587 ------- 

588 (unnamed) : np.ndarray 

589 Utility 

590 """ 

591 

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

593 

594 

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

596 """ 

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

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

599 and efficiency parameter `factor`. 

600 

601 Parameters 

602 ---------- 

603 x : np.ndarray 

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

605 zeta : np.ndarray 

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

607 factor : float 

608 Multiplicative efficiency parameter. 

609 args : tuple(int) 

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

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

612 the second derivative taken. 

613 

614 Returns 

615 ------- 

616 (unnamed) : np.ndarray 

617 Utility 

618 """ 

619 

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

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

622 else: 

623 coeff = zeta[args[1]] 

624 

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

626 

627 

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

629 """ 

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

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

632 and efficiency parameter `factor`. 

633 

634 Parameters 

635 ---------- 

636 x : np.ndarray 

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

638 zeta : np.ndarray 

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

640 factor : float 

641 Multiplicative efficiency parameter. 

642 args : tuple(int) 

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

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

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

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

647 

648 Returns 

649 ------- 

650 (unnamed) : np.ndarray 

651 Utility 

652 """ 

653 

654 if isinstance(args, int): 

655 args = (args,) 

656 

657 if len(args): 

658 idx = args[-1] # last index 

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

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

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

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

663 else: 

664 return cobb_douglas(x, zeta, factor) 

665 

666 

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

668 """ 

669 Evaluates Constant Elasticity of Substitution utility at quantities of 

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

671 

672 Parameters 

673 ---------- 

674 x : np.ndarray 

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

676 zeta : Sequence[float] 

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

678 subs : float 

679 Substitution parameter. 

680 factor : float 

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

682 power : float 

683 degree of homogeneity of the utility function 

684 

685 Returns 

686 ------- 

687 np.ndarray 

688 CES utility. 

689 """ 

690 

691 # move goods axis to the end 

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

693 

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

695 

696 

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

698 """ 

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

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

701 

702 Parameters 

703 ---------- 

704 x : np.ndarray 

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

706 zeta : Sequence[float] 

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

708 subs : float 

709 Substitution parameter. 

710 factor : float 

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

712 power : float 

713 degree of homogeneity of the utility function 

714 

715 Returns 

716 ------- 

717 np.ndarray 

718 CES marginal utility. 

719 """ 

720 

721 return ( 

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

723 * zeta[arg] 

724 * subs 

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

726 ) 

727 

728 

729class UtilityFunction(MetricObject): 

730 distance_criteria = ["eval_func"] 

731 

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

733 self.eval_func = eval_func 

734 self.der_func = der_func 

735 self.inv_func = inv_func 

736 

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

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

739 

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

741 if self.der_func is None: 

742 raise NotImplementedError("No derivative function available") 

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

744 

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

746 if self.inv_func is None: 

747 raise NotImplementedError("No inverse function available") 

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

749 

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

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

752 

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

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

755 

756 

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

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

759 

760 

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

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

763 

764 

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

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

767 

768 

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

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

771 

772 

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

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

775 

776 

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

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

779 

780 

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

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

783 

784 

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

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

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

788 ) 

789 

790 

791class UtilityFuncCRRA(UtilityFunction): 

792 """ 

793 A class for representing a CRRA utility function. 

794 

795 Parameters 

796 ---------- 

797 CRRA : float 

798 The coefficient of constant relative risk aversion. 

799 """ 

800 

801 distance_criteria = ["CRRA"] 

802 

803 def __init__(self, CRRA): 

804 self.CRRA = CRRA 

805 

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

807 """ 

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

809 

810 Parameters 

811 ---------- 

812 c : float or np.ndarray 

813 Consumption level(s). 

814 order : int, optional 

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

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

817 

818 Returns 

819 ------- 

820 float or np.ndarray 

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

822 """ 

823 if order == 0: 

824 try: 

825 return CRRAutility(c, self.CRRA) 

826 except: 

827 return CRRAutility_X(c, self.CRRA) 

828 else: # order >= 1 

829 return self.derivative(c, order) 

830 

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

832 """ 

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

834 

835 Parameters 

836 ---------- 

837 c : float or np.ndarray 

838 Consumption level(s). 

839 order : int, optional 

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

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

842 

843 Returns 

844 ------- 

845 float or np.ndarray 

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

847 

848 Raises 

849 ------ 

850 ValueError 

851 Derivative of order higher than 4 is not supported. 

852 """ 

853 if order == 1: 

854 try: 

855 return CRRAutilityP(c, self.CRRA) 

856 except: 

857 return CRRAutilityP_X(c, self.CRRA) 

858 elif order == 2: 

859 try: 

860 return CRRAutilityPP(c, self.CRRA) 

861 except: 

862 return CRRAutilityPP_X(c, self.CRRA) 

863 elif order == 3: 

864 return CRRAutilityPPP(c, self.CRRA) 

865 elif order == 4: 

866 return CRRAutilityPPPP(c, self.CRRA) 

867 else: 

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

869 

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

871 """ 

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

873 

874 Parameters 

875 ---------- 

876 u : float or np.ndarray 

877 Utility level(s). 

878 order : tuple, optional 

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

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

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

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

883 which is just the inverse of utility. 

884 

885 Returns 

886 ------- 

887 float or np.ndarray 

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

889 

890 Raises 

891 ------ 

892 ValueError 

893 Higher order derivatives are not supported. 

894 """ 

895 if order == (0, 0): 

896 return CRRAutility_inv(u, self.CRRA) 

897 elif order == (1, 0): 

898 return CRRAutilityP_inv(u, self.CRRA) 

899 elif order == (0, 1): 

900 return CRRAutility_invP(u, self.CRRA) 

901 elif order == (1, 1): 

902 return CRRAutilityP_invP(u, self.CRRA) 

903 else: 

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

905 

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

907 """ 

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

909 """ 

910 return self.inverse(u, order) 

911 

912 

913class UtilityFuncCARA(UtilityFunction): 

914 """ 

915 A class for representing a CARA utility function. 

916 

917 Parameters 

918 ---------- 

919 CARA : float 

920 The coefficient of constant absolute risk aversion. 

921 """ 

922 

923 distance_criteria = ["CARA"] 

924 

925 def __init__(self, CARA): 

926 self.CARA = CARA 

927 

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

929 """ 

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

931 

932 Parameters 

933 ---------- 

934 c : float or np.ndarray 

935 Consumption level(s). 

936 order : int, optional 

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

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

939 

940 Returns 

941 ------- 

942 float or np.ndarray 

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

944 """ 

945 if order == 0: 

946 return CARAutility(c, self.CARA) 

947 else: # order >= 1 

948 return self.derivative(c, order) 

949 

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

951 """ 

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

953 

954 Parameters 

955 ---------- 

956 c : float or np.ndarray 

957 Consumption level(s). 

958 order : int, optional 

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

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

961 

962 Returns 

963 ------- 

964 float or np.ndarray 

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

966 

967 Raises 

968 ------ 

969 ValueError 

970 Derivative of order higher than 4 is not supported. 

971 """ 

972 if order == 1: 

973 return CARAutilityP(c, self.CARA) 

974 elif order == 2: 

975 return CARAutilityPP(c, self.CARA) 

976 elif order == 3: 

977 return CARAutilityPPP(c, self.CARA) 

978 else: 

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

980 

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

982 """ 

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

984 

985 Parameters 

986 ---------- 

987 u : float or np.ndarray 

988 Utility level(s). 

989 order : tuple, optional 

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

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

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

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

994 which is just the inverse of utility. 

995 

996 Returns 

997 ------- 

998 float or np.ndarray 

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

1000 

1001 Raises 

1002 ------ 

1003 ValueError 

1004 Higher order derivatives are not supported. 

1005 """ 

1006 if order == (0, 0): 

1007 return CARAutility_inv(u, self.CARA) 

1008 elif order == (1, 0): 

1009 return CARAutilityP_inv(u, self.CARA) 

1010 elif order == (0, 1): 

1011 return CARAutility_invP(u, self.CARA) 

1012 elif order == (1, 1): 

1013 return CARAutilityP_invP(u, self.CARA) 

1014 else: 

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

1016 

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

1018 """ 

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

1020 """ 

1021 return self.inverse(u, order) 

1022 

1023 

1024class UtilityFuncStoneGeary(UtilityFunction): 

1025 distance_criteria = ["CRRA", "factor", "shifter"] 

1026 

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

1028 self.CRRA = CRRA 

1029 self.factor = factor 

1030 self.shifter = shifter 

1031 

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

1033 if order == 0: 

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

1035 else: # order >= 1 

1036 return self.derivative(c, order) 

1037 

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

1039 if order == 1: 

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

1041 elif order == 2: 

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

1043 else: 

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

1045 

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

1047 if order == (0, 0): 

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

1049 elif order == (1, 0): 

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

1051 elif order == (0, 1): 

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

1053 elif order == (1, 1): 

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

1055 else: 

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

1057 

1058 

1059class UtilityFuncCobbDouglas(UtilityFunction): 

1060 """ 

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

1062 

1063 TODO: Add inverse methods. 

1064 

1065 Parameters 

1066 ---------- 

1067 c_share : float 

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

1069 d_bar : float 

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

1071 """ 

1072 

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

1074 

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

1076 self.c_share = c_share 

1077 self.d_bar = d_bar 

1078 

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

1080 

1081 def __call__(self, c, d): 

1082 """ 

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

1084 """ 

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

1086 

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

1088 if axis == 0: 

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

1090 elif axis == 1: 

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

1092 else: 

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

1094 

1095 def inverse(self, uc, d): 

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

1097 

1098 

1099class UtilityFuncCobbDouglasCRRA(UtilityFuncCobbDouglas): 

1100 """ 

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

1102 

1103 TODO: Add derivative and inverse methods. 

1104 

1105 Parameters 

1106 ---------- 

1107 c_share : float 

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

1109 d_bar : float 

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

1111 CRRA: float 

1112 Coefficient of relative risk aversion. 

1113 """ 

1114 

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

1116 

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

1118 super().__init__(c_share, d_bar) 

1119 self.CRRA = CRRA 

1120 

1121 def __call__(self, c, d): 

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

1123 

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

1125 if axis == 0: 

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

1127 elif axis == 1: 

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

1129 else: 

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

1131 

1132 def inverse(self, uc, d): 

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

1134 

1135 

1136class UtilityFuncConstElastSubs(UtilityFunction): 

1137 """ 

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

1139 

1140 TODO: Add derivative and inverse methods. 

1141 

1142 Parameters 

1143 ---------- 

1144 shares : Sequence[float] 

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

1146 subs : float 

1147 Substitution parameter. 

1148 factor : float 

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

1150 homogeneity : float 

1151 degree of homogeneity of the utility function 

1152 """ 

1153 

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

1155 

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

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

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

1159 

1160 self.shares = np.asarray(shares) 

1161 self.subs = subs 

1162 self.homogeneity = homogeneity 

1163 self.factor = factor 

1164 

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

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

1167 

1168 def __call__(self, x): 

1169 return const_elast_subs( 

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

1171 ) 

1172 

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

1174 return const_elast_subs_p( 

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

1176 )