Skip to content

Feature: Price effect decomposition (Hicks + Slutsky) — substitution/income effects on a single canvas #55

@yueswater

Description

@yueswater

Summary

Add a unified price-effect decomposition pipeline that splits the total effect of a price change into substitution effect and income effect, supporting both Hicks (utility-compensated) and Slutsky (purchasing-power-compensated) compensation rules, and renders the canonical three-bundle / two-arrow diagram on a single Canvas.

Consolidates and supersedes the decomposition portions of #6 and #32; uses #13 (Slutsky matrix) as the underlying derivative engine; provides the diagram component referenced by #20.

Motivation

Price-effect decomposition is the core teaching exercise in chapters 5–8 of every intermediate microeconomics textbook (Varian, Mankiw, Mas-Colell). The library can already plot equilibria but has no way to draw the standard A → B → C three-bundle picture with substitution and income arrows. This is the highest-priority remaining gap for teaching consumer theory.

Background

For a price change $p_x^0 \to p_x^1$ (with $p_y$, $I$ fixed), define the compensated bundle $B$ differently for the two methods:

Method Compensation rule Compensated bundle $B$
Hicks Adjust nominal income so the consumer reaches the original utility $U^0$ at new prices $\arg\min_{x,y} p_x^1 x + p_y y ;;\text{s.t.};; U(x,y) = U^0$
Slutsky Adjust nominal income so the consumer can just afford the original bundle $(x^0, y^0)$ at new prices $\arg\max U(x,y) ;;\text{s.t.};; p_x^1 x + p_y y = p_x^1 x^0 + p_y y^0$

Both yield substitution effects in the same direction; magnitudes differ.

Proposed API

Solver

from econ_viz.optimizer import decompose_price_effect, DecompositionMethod

result = decompose_price_effect(
    model,
    px=(2.0, 4.0),                       # before, after
    py=3.0,
    income=60.0,
    method=DecompositionMethod.HICKS,    # or SLUTSKY
)

# result.A : Equilibrium  — original optimum at p_x^0
# result.B : Equilibrium  — compensated optimum at p_x^1
# result.C : Equilibrium  — final optimum at p_x^1
# result.substitution_effect : tuple[float, float]  # A → B
# result.income_effect       : tuple[float, float]  # B → C
# result.total_effect        : tuple[float, float]  # A → C
# result.method              : DecompositionMethod
# result.compensated_income  : float

Canvas integration

from econ_viz import Canvas

canvas = Canvas(x_label='x', y_label='y')
canvas.add_utility(
    model,
    levels=[result.A.utility, result.B.utility, result.C.utility],
)
canvas.add_decomposition(result, show_arrows=True, label_effects=True)
canvas.save('hicks.tex')

What add_decomposition draws

  • Three budget lines (theme-distinguished line styles):
    • Original: $p_x^0 x + p_y y = I$
    • Compensated (dashed): Hicks → tangent to $U^0$; Slutsky → through $(x^0, y^0)$
    • Final: $p_x^1 x + p_y y = I$
  • Three points labelled $A$, $B$, $C$
  • Substitution-effect arrow $A \to B$ (along $U^0$ for Hicks; between two ICs for Slutsky)
  • Income-effect arrow $B \to C$
  • Optional dotted projections to the $x$-axis with brackets for $\Delta x_{\text{sub}}$ and $\Delta x_{\text{inc}}$

Implementation notes

  • Hicks $B$: `scipy.optimize.minimize` minimising $p_x^1 x + p_y y$ subject to $U(x, y) \ge U^0$.
  • Slutsky $B$: re-uses existing `solve(model, p_x^1, p_y, I')` with $I' = p_x^1 x^0 + p_y y^0$.
  • Slutsky matrix (Solver: Slutsky matrix numerical computation #13) shares the underlying derivative computation; Solver: Slutsky matrix numerical computation #13 stays scoped to matrix-as-output, this issue uses the same comparative-statics engine.
  • Arrow + bracket styling lives on `Theme` (separate colour / linewidth keys for substitution vs income).
  • Edge cases: corner solutions for $B$ (Hicks may have no interior optimum at extreme prices); Giffen goods (income arrow opposite to substitution arrow); inferior goods (income arrow shorter and possibly opposite).
  • Both methods must be testable against Cobb-Douglas closed forms.

Sanity-check example

Cobb-Douglas with $\alpha = 0.5$, $p_x: 2 \to 4$, $p_y = 3$, $I = 60$:

  • Hicks: $A=(15,10)$, $B \approx (10.6, 14.1)$, $C=(7.5,10)$ — substitution on $x$ ≈ −4.4, income ≈ −3.1
  • Slutsky: $A=(15,10)$, $B' \approx (11.25, 12.5)$, $C=(7.5,10)$ — substitution ≈ −3.75, income ≈ −3.75

Tasks

  • `DecompositionMethod` enum (`HICKS`, `SLUTSKY`)
  • `decompose_price_effect(...)` returning `PriceEffectDecomposition` dataclass
  • Hicksian compensated-bundle solver (expenditure minimisation)
  • Slutsky compensated-bundle solver (income adjustment + existing `solve`)
  • `Canvas.add_decomposition(result, ...)` with arrows + bracket projections
  • `Theme` extensions for decomposition arrow / bracket styles
  • Cobb-Douglas regression tests for both methods (closed-form)
  • Coverage for inferior and Giffen cases (opposite-direction income effect)
  • Example notebook: side-by-side Hicks vs Slutsky for a normal good
  • Tutorial doc walking through the chart and labels

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthelp wantedExtra attention is needed

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions