Skip to content

Commit

Permalink
Add slides for chapter 2
Browse files Browse the repository at this point in the history
  • Loading branch information
stanmart committed Oct 3, 2024
1 parent cb0a958 commit 5601611
Show file tree
Hide file tree
Showing 4 changed files with 632 additions and 10 deletions.
27 changes: 27 additions & 0 deletions Snakefile
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,24 @@ rule manim_shapley_value:
out/manim_figures/videos/shapley_value_demo/{wildcards.height}p{wildcards.fps}/sections"


rule manim_comparative_equilibrium_entry:
input:
script = "src/manim_figures/comparative_equilibrium_entry.py"
output:
videos = expand(
"out/manim_figures/videos/comparative_equilibrium_entry/{height}p{fps}/sections/{section}.mp4",
section = find_manim_sections("src/manim_figures/comparative_equilibrium_entry.py"),
allow_missing=True
)
params:
width = lambda wildcards: wildcards.height,
shell:
"manim render -qh {input.script} --save_sections --media_dir out/manim_figures \
-r {params.width},{wildcards.height} --fps {wildcards.fps} && \
python src/util/makeutils.py rename-manim-sections \
out/manim_figures/videos/comparative_equilibrium_entry/{wildcards.height}p{wildcards.fps}/sections"


rule figure_two_sided:
output:
fig = "out/figures/two_sided_lambda2-{lambda_2}.{ext}"
Expand All @@ -171,6 +189,15 @@ rule figure_weighting_functions:
"src/figures/weighting_functions.py"


rule figure_equilibrium_presentation:
input:
csv = "out/figures/equilibrium_{value_function}_{bargaining}_scale-{n_c}_lambda-{lambda_P}.csv"
output:
fig = "out/figures/equilibrium_{var}_{add_bargaining}-bargaining_{value_function}_{bargaining}_scale-{n_c}_lambda-{lambda_P}.{ext}"
script:
"src/figures/equilibrium_presentation.py"


rule figure_equilibrium:
input:
script = "src/figures/equilibrium_symbolic.py"
Expand Down
108 changes: 108 additions & 0 deletions src/figures/equilibrium_presentation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import matplotlib.pyplot as plt
import pandas as pd
from matplotlib.axes import Axes
from matplotlib.figure import Figure

plt.rcParams.update({"text.usetex": True})


VARS = {
"entry-fee": {
"benchmark": "K_F_opt",
"bargaining": "K_F_implied",
"pure_retail": None,
"y_label": "$K_F$",
},
"platform-profit": {
"benchmark": "pi_P_bench",
"bargaining": "pi_P",
"pure_retail": "pi_P_noF",
"y_label": "$pi_P$",
},
"fringe-number": {
"benchmark": "N_F_bench",
"bargaining": "N_F",
"pure_retail": None,
"y_label": "$N_F$",
},
"aggregate": {
"benchmark": "A_bench",
"bargaining": "A",
"pure_retail": "A_noF",
"y_label": "$A$",
},
"consumer-surplus": {
"benchmark": "CS_bench",
"bargaining": "CS",
"pure_retail": "CS_noF",
"y_label": "$CS$",
},
}


def plot_equilibrium_outcomes(
df: pd.DataFrame,
benchmark_var: str,
bargaining_var: str | None = None,
pure_retail_var: str | None = None,
y_label: str = "",
hybrid_indicator_benchmark: str | None = None,
hybrid_indicator_bargaining: str | None = None,
) -> tuple[Figure, Axes]:
fig, ax = plt.subplots()

ax.plot(df["N_P"], df[benchmark_var], label="Benchmark")

if bargaining_var is not None:
ax.plot(df["N_P"], df[bargaining_var], label="Bargaining")

if pure_retail_var is not None:
ax.plot(
df["N_P"],
df[pure_retail_var],
label="Pure retail",
color="black",
linestyle=":",
)

if hybrid_indicator_benchmark is not None:
shade_end = df.loc[df[hybrid_indicator_benchmark] != 0, "N_P"].max()
ax.axvspan(0, shade_end, color="gray", alpha=0.2)

if hybrid_indicator_bargaining is not None:
shade_end = df.loc[df[hybrid_indicator_bargaining] != 0, "N_P"].max()
ax.axvspan(0, shade_end, color="gray", alpha=0.2)

ax.set_xlabel("$N_P$")
ax.set_ylabel(y_label)

ax.set_xlim(0, df["N_P"].max())
ax.set_xticks([0, df["N_P"].max()])

ax.spines[["right", "top"]].set_visible(False)
ax.legend()

return fig, ax


if __name__ == "__main__":
input_data = snakemake.input.csv # type: ignore # noqa: F821
output_figure = snakemake.output[0] # type: ignore # noqa: F821
var = snakemake.wildcards.var # type: ignore # noqa: F821
plot_bargaining = snakemake.wildcards.add_bargaining == "with" # type: ignore # noqa: F821
# TODO: handle vars

df = pd.read_csv(input_data)
fig, _ = plot_equilibrium_outcomes(
df=df,
benchmark_var=VARS[var]["benchmark"],
bargaining_var=VARS[var]["bargaining"] if plot_bargaining else None,
pure_retail_var=VARS[var]["pure_retail"] if plot_bargaining else None,
y_label=VARS[var]["y_label"],
hybrid_indicator_benchmark="hybrid_bench",
hybrid_indicator_bargaining="hybrid" if plot_bargaining else None,
)

fig.set_size_inches(3, 2.5)
fig.tight_layout()
fig.savefig(output_figure, bbox_inches="tight", dpi=300)
131 changes: 131 additions & 0 deletions src/manim_figures/comparative_equilibrium_entry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
from manim import (
BLACK,
BLUE_D,
DOWN,
RED_D,
UP,
WHITE,
Axes,
Brace,
BraceBetweenPoints,
Create,
Line,
MathTex,
ParametricFunction,
Scene,
Tex,
Text,
Transform,
Write,
)
from numpy import log
from scipy.optimize import fsolve


def pi_F(N_P, N_F):
numerator = N_F * (N_F + N_P) - (N_F + N_P + 1) * (
N_F + log(N_P + 1) - log(N_F + N_P + 1)
)
denominator = N_F * (N_F + N_P + 1)
return numerator / denominator


class Baseline(Scene):
def construct(self):
self.next_section("draw_graph")

self.camera.background_color = WHITE # type: ignore
Text.set_default(color=BLACK)
Line.set_default(color=BLACK)
Tex.set_default(color=BLACK)
MathTex.set_default(color=BLACK)
Brace.set_default(color=BLACK)
ParametricFunction.set_default(color=BLACK)

# Create plane
ax = Axes(
x_range=[0.005, 30, 0.1],
y_range=[0, 0.25, 0.01],
x_length=12,
y_length=8,
x_axis_config={"include_ticks": False},
y_axis_config={"include_ticks": False},
)
x_label = ax.get_x_axis_label(r"N_F")
y_label = ax.get_y_axis_label(r"\pi_F")

N_P_0 = 0
N_P_1 = 0.5
I_F = 0.005

# Intersections
N_F_opt_0 = fsolve(lambda N_F: pi_F(N_P_0, N_F) - I_F * N_F, 20)[0]
N_F_opt_0_val = pi_F(N_P_0, N_F_opt_0)
N_F_opt_point_0 = ax.c2p(N_F_opt_0, N_F_opt_0_val)
N_F_opt_1 = fsolve(lambda N_F: pi_F(N_P_1, N_F) - I_F * N_F, 20)[0]
N_F_opt_1_val = pi_F(N_P_1, N_F_opt_1)
N_F_opt_point_1 = ax.c2p(N_F_opt_1, N_F_opt_1_val)

# Destinations
pi_F_orig = ax.plot(lambda x: pi_F(N_P_0, x), color=BLUE_D)
pi_F_alt = ax.plot(lambda x: pi_F(N_P_1, x), color=RED_D)
pi_F_orig_label = ax.get_graph_label(
pi_F_orig,
r"\pi_F(N_P, N_F)",
direction=UP, # type: ignore
)
pi_F_alt_label = ax.get_graph_label(
pi_F_alt,
r"\pi_F(N_P', N_F)",
direction=DOWN, # type: ignore
)

N_F_opt_0_bar = ax.get_vertical_line(N_F_opt_point_0, color=BLACK)
N_F_opt_1_bar = ax.get_vertical_line(N_F_opt_point_1, color=BLACK)
# N_F_opt_0_label = MathTex(r"N_F^*(N_P)", color=BLUE_D)
# N_F_opt_0_label.next_to(N_F_opt_0_bar, DOWN)
# N_F_opt_1_label = MathTex(r"N_F^*(N_P')", color=RED_D)
# N_F_opt_1_label.next_to(N_F_opt_1_bar, DOWN)

investment_cost = ax.plot(lambda x: I_F * x)
investment_cost_label = ax.get_graph_label(
investment_cost,
r"I_F N_F",
direction=UP, # type: ignore
)
brace_loss = BraceBetweenPoints(
N_F_opt_1_bar.get_bottom(), # type: ignore
N_F_opt_0_bar.get_bottom(), # type: ignore
)
brace_label = MathTex(r"> N_P' - N_P", color=BLACK)
brace_label.next_to(brace_loss, DOWN)

# Moving objects
pi_F_plot = pi_F_orig.copy()
pi_F_label = pi_F_orig_label.copy()
N_F_opt_bar = N_F_opt_0_bar.copy()
# N_F_opt_label = N_F_opt_0_label.copy()

# First phase: Draw pi_F and investment cost
self.play(Create(ax), Write(x_label), Write(y_label))
self.play(Create(pi_F_plot), Create(pi_F_label))
self.play(Create(investment_cost), Create(investment_cost_label))

# Second phase: mark equilibrium
self.next_section("mark_equilibrium")
self.play(Create(N_F_opt_bar))

# Third phase: move to alternative equilibrium
self.next_section("alternate_equilibrium")
self.add(pi_F_orig, pi_F_orig_label, N_F_opt_0_bar)
self.play(Transform(pi_F_plot, pi_F_alt), Transform(pi_F_label, pi_F_alt_label))
self.wait(0.5)
self.play(
Transform(N_F_opt_bar, N_F_opt_1_bar),
# Transform(N_F_opt_label, N_F_opt_1_label)
)

# Fourth phase: show loss
self.next_section("show_loss")
self.play(Create(brace_loss), Write(brace_label))
self.wait(0.1)
Loading

0 comments on commit 5601611

Please sign in to comment.