Skip to content

Commit 9dd9cc0

Browse files
committed
Edits by Karsten Chipeniuk ahead of journal submission
Note that a table has been added to the paper. Markdown seems to not have standardized syntax for captioning or referencing tables, so this has been simply hard-coded in the .md file. The code used to generate the table has been left in the Jupyter notebook (in the section with Figure 5) and plotting.py. This hasn't been tidied and the values have just been copied over to the paper by hand, to get it done sooner rather than later. It would be good to have the table generated automatically if these results are kept.
1 parent 165dd0d commit 9dd9cc0

File tree

1,767 files changed

+236862
-974
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,767 files changed

+236862
-974
lines changed

code/notebook.ipynb

Lines changed: 159 additions & 149 deletions
Large diffs are not rendered by default.

code/plotting.py

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,213 @@ def plot_precautionary_gaps(
750750

751751
plt.tight_layout()
752752

753+
def plot_solution_gaps(
754+
truth_solution,
755+
approx_solutions,
756+
title: str,
757+
subtitle: str,
758+
*,
759+
m_min: float = 0.0,
760+
m_max: float = 30.0,
761+
n_points: int = 100,
762+
legend: str | list[str] | None = None,
763+
) -> None:
764+
"""Plot savings function gaps comparing truth vs approximation(s).
765+
766+
Parameters
767+
----------
768+
truth_solution : ConsumerSolution
769+
High-precision "truth" solution for comparison
770+
approx_solutions : ConsumerSolution or list[ConsumerSolution]
771+
Approximation solution(s) to compare (single or list for multiple methods)
772+
title : str
773+
Figure title
774+
subtitle : str
775+
Figure subtitle
776+
m_max : float, optional
777+
Maximum market resources for plot range, by default 30.0
778+
n_points : int, optional
779+
Number of points in evaluation grid, by default 100
780+
legend : str or list[str], optional
781+
Legend labels for approximation(s). If None, auto-generates from solution type.
782+
783+
"""
784+
# Ensure approx_solutions is a list
785+
if not isinstance(approx_solutions, list):
786+
approx_solutions = [approx_solutions]
787+
788+
# Auto-generate legend labels if not provided
789+
if legend is None:
790+
legend = []
791+
for sol in approx_solutions:
792+
if _is_mom_solution(sol):
793+
legend.append("MoM Approximation")
794+
else:
795+
legend.append("EGM Approximation")
796+
elif not isinstance(legend, list):
797+
legend = [legend]
798+
799+
# Create evaluation grid
800+
#m_min = approx_solutions[0].cFunc.x_list[-2]#truth_solution.mNrmMin
801+
m_grid = np.linspace(m_min + 0.001, m_max, n_points)
802+
803+
# Compute solution differences
804+
approx_gaps = []
805+
for sol in approx_solutions:
806+
# Use the same optimist as truth solution
807+
gap = truth_solution.cFunc(m_grid) - sol.cFunc(m_grid)
808+
approx_gaps.append(gap)
809+
810+
_fig, ax = setup_figure(title=title)
811+
812+
# Plot each approximation method
813+
for gap_vals, method_label, sol in zip(
814+
approx_gaps,
815+
legend,
816+
approx_solutions,
817+
strict=False,
818+
):
819+
color = get_concept_color(method_label)
820+
linestyle = get_concept_linestyle(method_label)
821+
822+
ax.plot(
823+
m_grid,
824+
gap_vals,
825+
label=method_label,
826+
color=color,
827+
linewidth=LINE_WIDTH_THICK,
828+
linestyle=linestyle,
829+
)
830+
831+
# Extract and plot grid points for this solution
832+
try:
833+
# Use unified grid extraction
834+
grid_points_m, grid_points_c = extract_grid_points(
835+
sol,
836+
GridType.CONSUMPTION,
837+
)
838+
# Determine grid boundary based on solution type
839+
if grid_points_m is not None:
840+
if _is_mom_solution(sol) and len(grid_points_m) > 1:
841+
grid_boundary = grid_points_m[-2] # MoM: second-to-last point
842+
else:
843+
grid_boundary = grid_points_m[-1] # EGM: last point
844+
else:
845+
grid_boundary = None
846+
847+
# Plot grid points if successfully extracted
848+
if grid_points_m is not None and grid_points_c is not None:
849+
# Get the gap values at grid point locations by interpolation
850+
gap_at_grid_points = np.interp(grid_points_m, m_grid, gap_vals)
851+
852+
# Plot actual grid points as scatter
853+
_plot_grid_points_scatter(ax, grid_points_m, gap_at_grid_points, color)
854+
855+
# Also plot grid boundary line
856+
if grid_boundary is not None:
857+
ax.axvline(
858+
x=grid_boundary,
859+
color="gray",
860+
linestyle=LINE_STYLE_DASHED,
861+
alpha=ALPHA_MEDIUM,
862+
label="Grid boundary",
863+
)
864+
865+
except (AttributeError, KeyError, IndexError, TypeError):
866+
# Grid extraction can fail for various solution types or incomplete solutions.
867+
# Continue plotting without grid point markers.
868+
pass
869+
870+
_configure_standard_axes(
871+
ax,
872+
xlabel="Normalized Market Resources (m)",
873+
ylabel="Precautionary Saving Gap",
874+
subtitle=subtitle,
875+
)
876+
_add_reference_lines(ax)
877+
#ax.set_ylim(*YLIM_PRECAUTIONARY_GAPS)
878+
_set_xlim_with_padding(ax, m_grid)
879+
880+
plt.tight_layout()
881+
882+
def average_solution_gaps(
883+
truth_solution,
884+
approx_solutions,
885+
title: str,
886+
subtitle: str,
887+
*,
888+
m_min: float = 0.0,
889+
m_max: float = 30.0,
890+
n_points: int = 100,
891+
legend: str | list[str] | None = None,
892+
) -> None:
893+
"""Plot savings function gaps comparing truth vs approximation(s).
894+
895+
Parameters
896+
----------
897+
truth_solution : ConsumerSolution
898+
High-precision "truth" solution for comparison
899+
approx_solutions : ConsumerSolution or list[ConsumerSolution]
900+
Approximation solution(s) to compare (single or list for multiple methods)
901+
title : str
902+
Figure title
903+
subtitle : str
904+
Figure subtitle
905+
m_max : float, optional
906+
Maximum market resources for plot range, by default 30.0
907+
n_points : int, optional
908+
Number of points in evaluation grid, by default 100
909+
legend : str or list[str], optional
910+
Legend labels for approximation(s). If None, auto-generates from solution type.
911+
912+
"""
913+
# Ensure approx_solutions is a list
914+
if not isinstance(approx_solutions, list):
915+
approx_solutions = [approx_solutions]
916+
917+
approx_gaps_all_solutions = []
918+
# Compute the average error between each pair of grid points for each approximation method
919+
for sol in approx_solutions:
920+
approx_gaps = []
921+
# Extract and plot grid points for this solution
922+
try:
923+
# Use unified grid extraction
924+
grid_points_m, grid_points_c = extract_grid_points(
925+
sol,
926+
GridType.CONSUMPTION,
927+
)
928+
# Determine grid boundary based on solution type
929+
if grid_points_m is not None:
930+
if _is_mom_solution(sol) and len(grid_points_m) > 1:
931+
grid_boundary = grid_points_m[-2] # MoM: second-to-last point
932+
else:
933+
grid_boundary = grid_points_m[-1] # EGM: last point
934+
else:
935+
grid_boundary = None
936+
937+
# Plot grid points if successfully extracted
938+
if grid_points_m is not None and grid_points_c is not None:
939+
for left_point, right_point in zip(grid_points_m[0:-1],grid_points_m[1:]):
940+
# Contruct a fine grid between this pair of grid points
941+
m_grid = np.linspace(left_point + 0.001, right_point, n_points)
942+
# Get the mean absolute gap value for this interval
943+
ave_gap = np.max(np.abs(truth_solution.cFunc(m_grid) - sol.cFunc(m_grid)))
944+
approx_gaps.append(ave_gap)
945+
m_grid = np.linspace(grid_boundary + 0.001, m_max, n_points)
946+
ave_gap = np.max(np.abs(truth_solution.cFunc(m_grid) - sol.cFunc(m_grid)))
947+
approx_gaps.append(ave_gap)
948+
if _is_mom_solution(sol) and len(grid_points_m) > 1:
949+
approx_gaps = np.append(approx_gaps[1:-2],approx_gaps[-1]) # MoM: second-to-last point
950+
else:
951+
approx_gaps = approx_gaps[1:] # EGM: last point
952+
approx_gaps_all_solutions.append(approx_gaps)
953+
954+
except (AttributeError, KeyError, IndexError, TypeError):
955+
# Grid extraction can fail for various solution types or incomplete solutions.
956+
# Continue plotting without grid point markers.
957+
pass
958+
959+
return approx_gaps_all_solutions
753960

754961
def plot_consumption_bounds(
755962
solution,

condaenv/.nonadmin

Whitespace-only changes.

condaenv/DLLs/py.ico

74 KB
Binary file not shown.

condaenv/DLLs/pyc.ico

76.6 KB
Binary file not shown.

0 commit comments

Comments
 (0)