← Về thư mục
📄 / / projects / fsppm / econ_sim / docs / MODULE_MIGRATION.md

MODULE MIGRATION: Phân tích bản gốc và kế hoạch chuyển đổi 3 module

Bản gốc: reference/micro_macro_animation_legacy.html (1.826 dòng, 91.871 byte)

Tổng quan những gì giữ lại từ bản gốc

Giao diện và bố cục (rất tốt, chuyển thẳng sang UI shell)

• Bố cục 2 cột: trái 7/12 (đồ thị + điều khiển + số liệu), phải 5/12 (giải thích lý thuyết) • Header với badge chương sách + tab chuyển chế độ • Banner thông số đầu vào (I, Px, Py, hàm U, điểm cân bằng gốc) • 4 ô stat cards: Tối ưu gốc (A), Lựa chọn mới (B), Biến thiên ΔU, Độ dốc MRS • Khung "Các bước tính toán chi tiết" tự cập nhật • Bảng đối sánh A vs B (Rổ hàng, Mức thỏa dụng, MRS, Nhận xét) • Nút kịch bản nhanh (preset buttons) • Tooltip khi rê chuột trên đồ thị (X, Y, U) • Nút "Đặt lại mặc định"

Đồ thị (chuyển sang SVG primitives)

• Hệ biến đổi tọa độ getTransforms(): toScreenX, toScreenY, toModelX, toModelY • Lưới tọa độ nhạt, trục có mũi tên và vạch chia • Đường ngân sách gốc (nét liền xám) và giới hạn mới (nét đậm màu) • Vùng tô màu nhẹ cho tập khả thi mới (rgba) • Đường bàng quan (nét liền cho U gốc, nét đứt cho U mới) • Điểm đánh dấu có viền trắng + nhãn • Đường gióng tọa độ nét chấm xuống hai trục • Mũi tên chỉ hướng dịch chuyển từ A sang B

Module 1: Định lượng hàng hóa (Rationing)

Tham chiếu giáo trình

Pindyck & Rubinfeld (2015), Chương 3, Câu hỏi ôn tập 10(a), trang 107.

Tình trạng trong bản gốc

• Toán học: ĐÚNG. I = 200, Px = 10, Py = 10, U(X,Y) = X·Y. Điểm gốc A(10, 10), U_A = 100, MRS_A = 1.00. Khi X̄ = 6: B(6, 14), U_B = 84, ΔU = −16, MRS_B = 14/6 ≈ 2.33 > Px/Py = 1. • Lỗi cấu trúc: phần giải thích cột phải (dòng 308 đến 379) ghi cứng số X̄ = 6, B(6, 14), U_B = 84, MRS = 2.33. Kéo slider sang 8, đồ thị và 4 ô stat đổi, nhưng cột phải và bảng so sánh vẫn đứng ở số của X̄ = 6.

Kế hoạch chuyển sang module mới

• Thư mục: src/scenarios/micro/consumer/rationing/ • Engine dùng: • CobbDouglas({ alpha: 0.5, scale: 1 }) (hoặc dạng nhân U = X·Y để giữ số đẹp 100 và 84) • RationedBudget({ I: 200, Px: 10, Py: 10, maxX: quotaX }) • solveConsumer() → trả về A(10,10) và B(min(quotaX, 10), (200 − 10·X_B)/10) • Param: quotaX (min 2, max 14, step 0.5, default 6, unit "đơn vị") • Presets: • strict: X̄ = 6 (ví dụ mặc định, thiếu hụt rõ) • mild: X̄ = 8 (hạn mức nhẹ) • non_binding: X̄ = 12 (hạn mức > 10, không ràng buộc, B trùng A) • Sửa lỗi cột phải: toàn bộ văn bản giải thích và bảng đối sánh đọc từ result, tự đổi trạng thái khi quotaX >= 10 (chuyển sang thông báo "Hạn mức không ràng buộc, người tiêu dùng giữ nguyên điểm A").

Module 2: Giá trần và thiếu hụt nguồn cung (Price Ceiling & Shortage)

Tham chiếu giáo trình

Pindyck & Rubinfeld (2015), Chương 3, Câu hỏi ôn tập 10(b), trang 107.

Tình trạng trong bản gốc

• Toán học: ĐÚNG. I = 200, Px gốc = 10, Py = 10, giá trần Px' = 5, lượng thực mua được X_cap. Ở giá Px' = 5, lượng cầu mong muốn là X_desired = 200 / (2·5) = 20. Do thiếu hụt, X_B = min(X_cap, X_desired), Y_B = (200 − Px'·X_B) / 10. • Kịch bản 1 (X_cap = 6): B(6, 17), U_B = 102 > 100 (dư giả hơn). • Kịch bản 2 (X_cap = 4): B(4, 18), U_B = 72 < 100 (khó khăn hơn). • Thiếu sót: chưa hiển thị ngưỡng hòa vốn thỏa dụng (breakeven). Với Px' = 5, ngưỡng để U_B = U_A = 100 là nghiệm của X · (200 − 5X)/10 = 100 ⇒ X* = 20 − √200 ≈ 5.86. Có con số này trên đồ thị thì người học thấy ngay ranh giới giữa "dư giả hơn" và "khó khăn hơn".

Kế hoạch chuyển sang module mới

• Thư mục: src/scenarios/micro/consumer/price_ceiling/ • Engine dùng: • CobbDouglas (như Module 1) • PriceCeilingShortageBudget({ I: 200, Px_ ceiling: pFixed, Py: 10, maxX: xCap }) • Hàm phụ findBreakevenCap(prefs, baseUtility, ceilingPrice) → tính điểm ranh giới X_breakeven • Params: • pFixed: min 2, max 9, step 0.5, default 5, unit "USD" • xCap: min 2, max 16, step 0.5, default 6, unit "đơn vị" • Presets: • better_off: Px' = 5, X_cap = 6 ⇒ U_B = 102 > 100 • worse_off: Px' = 5, X_cap = 4 ⇒ U_B = 72 < 100 • breakeven: Px' = 5, X_cap = 5.86 ⇒ U_B = 100 = U_A • Bổ sung trên đồ thị: vạch dọc mảnh tại X_breakeven để thấy ngay vùng nào trên đường ngân sách mới nằm phía trên đường bàng quan U_0 = 100.

Module 3: Giải pháp góc và Trợ cấp hiện vật vs Tiền mặt

Trong bản gốc, Tab C gộp 3 nút: (1) Góc trên trục: Kem vs Sữa chua, (2) Quỹ ủy thác học tập, (3) Tem phiếu thực phẩm. Khi tách sang kiến trúc mới, ta chia thành 2 module rõ nghĩa vì bản chất toán học khác nhau:

• Module 3A (corner_solution): Giải pháp góc trên trục tọa độ do sở thích nghiêng hẳn về một hàng hóa (Hình 3.15, trang 89). • Module 3B (inkind_vs_cash): Giải pháp góc tại đỉnh gãy khúc do trợ cấp hiện vật không hoàn lại, đối chiếu với trợ cấp tiền mặt tương đương (Hình 3.17 trang 91 và ví dụ Food Stamps trang 67 và 68). Quỹ ủy thác và Tem phiếu là 2 preset của cùng một module này.

Lỗi nghiêm trọng trong bản gốc cần fix

  1. Định nghĩa trùng hàm drawQuestionC: • Bản 1 tại dòng 966 (có đọc biến slider prefAlpha, trustFund, foodStampVal). • Bản 2 tại dòng 1379 (vẽ cố định hoàn toàn). • JavaScript ghi đè bản 1 bằng bản 2 ⇒ kéo slider chỉ đổi chữ, đồ thị chết cứng.

  2. Sai hàm thỏa dụng ở kịch bản Kem vs Sữa chua (Hình 3.15): • Nhãn ghi U = α·ln(X) + (1−α)·ln(Y) và nói α ≥ 0.65 cho giải pháp góc. • Sự thật toán học: hàm α·ln(X) + (1−α)·ln(Y) có MRS = (α/(1−α))·(Y/X). Khi Y → 0 thì MRS → 0, nên không bao giờ có giải pháp góc tại Y = 0 với mọi 0 < α < 1. • Trong comment dòng 851, Gemini định dùng hàm dịch chuyển (Y+2)/(X+2), nhưng ngưỡng để MRS ≥ 1 tại (20, 0) của hàm đó là (α/(1−α))·(2/22) ≥ 1 ⇒ α ≥ 11/12 ≈ 0.917, không phải 0.65. • Dòng 865 bịa công thức mrsVal = (alpha/(1-alpha)) * 1.5. • Dòng 999 và 1405 vẽ đường bàng quan bằng một đa thức bậc hai tự chế x = 20 − y/slope − 0.02·y², không liên quan gì đến hàm U đã viết.

  3. Sai ngược kết luận ở Quỹ ủy thác (Hình 3.17) và Tem phiếu: • Bản gốc dùng hàm vẽ đường bàng quan drawIndifferenceCurveCustom với U = X·Y (tương đương α = 0.5). • Ở Quỹ ủy thác: ngân sách gốc X + Y = 10, quỹ học bổng 10 đơn vị tạo đỉnh gãy B(10, 10), tiền mặt tương đương cho ngân sách X + Y = 20. • Với U = X·Y, trên đường X + Y = 20, điểm tối ưu tự do chính là (10, 10) với U = 100! • Gemini đặt tay điểm tiền mặt tại C(7, 13) và ghi U_3 > U_2, nhưng thực tế U(7, 13) = 91 < U(10, 10) = 100. Đường cong U_3 vẽ qua (7, 13) nằm thấp hơn đường U_2 qua (10, 10). • Tương tự ở Tem phiếu: B(8, 10) có U = 80, còn điểm tiền mặt C(5, 13) có U = 65 < 80. • Ở dashboard (dòng 915, 918, 947, 950), Gemini nhân thêm hệ số bịa 0.4 cho B và 0.45 cho C để ép số hiển thị của C lớn hơn B.

Cách fix toán học trong kế hoạch mới

Fix cho Module 3A (corner_solution: Hình 3.15 Kem vs Sữa chua)

Dùng hàm Shifted Cobb Douglas (Stone Geary dịch chuyển dương): $$U(X, Y) = (X + s)^\alpha (Y + s)^{1 - \alpha}, \quad s = 10$$

• Ý nghĩa kinh tế: dù không ăn sữa chua ($Y = 0$), độ thỏa dụng biên của sữa chua vẫn hữu hạn vì hai hàng hóa có tính thay thế cao (kem và sữa chua đông lạnh rất giống nhau). • Đạo hàm và MRS: $$MRS_{XY} = \frac{\alpha}{1 - \alpha} \cdot \frac{Y + s}{X + s}$$ • Với ngân sách $10X + 10Y = 200 \Rightarrow X + Y = 20$ và $s = 10$: • Nghiệm nội bộ khi chưa chạm biên: $$X^ = 40\alpha - 10, \quad Y^ = 30 - 40\alpha$$ • Khi $\alpha = 0.50$: $X^ = 10, Y^ = 10$, $MRS = 1.00$ (tiếp xúc chính giữa). • Khi $\alpha = 0.60$: $X^ = 14, Y^ = 6$, $MRS = 1.00$ (tiếp xúc bên trong). • Ngưỡng góc tại $Y = 0$: $30 - 40\alpha \le 0 \iff \alpha \ge 0.75$. • Khi $\alpha = 0.85$ (mặc định): nghiệm góc $B(20, 0)$, tại đó $MRS = \frac{0.85}{0.15} \cdot \frac{10}{30} = 1.89 > \frac{P_X}{P_Y} = 1.00$. • Toàn bộ đường bàng quan được vẽ từ phương trình thật: $$Y(X) = \left( \frac{U}{(X + s)^\alpha} \right)^{\frac{1}{1-\alpha}} - s$$ Không có bất kỳ đa thức giả nào.

Fix cho Module 3B (inkind_vs_cash: Hình 3.17 Quỹ ủy thác & Tem phiếu)

Để trợ cấp hiện vật $T$ thật sự bị ràng buộc (binding) tại đỉnh gãy $B(T, I_0)$ và kém hiệu quả hơn tiền mặt $C$, người tiêu dùng phải có sở thích chi cho hàng hóa được trợ cấp $X$ ở mức thấp hơn quy mô khoản trợ cấp khi được tự do: $$\alpha (I_0 + T) < T \iff T > T^* = \frac{\alpha}{1 - \alpha} I_0$$

Dùng hàm Cobb Douglas lệch trọng số $U(X, Y) = X^\alpha Y^{1-\alpha}$ với $P_X = 1, P_Y = 1$ (đơn vị tính bằng USD chi tiêu, hoặc quy đổi 1 đơn vị = 10 USD) và thu nhập gốc $I_0 = 100\text{ USD}$:

  1. Preset 1: Quỹ ủy thác giáo dục của Jane (Hình 3.17) • Đặt $\alpha = 0.30$ (khi tự do, Jane dành 30% tổng nguồn lực cho giáo dục đại học $X$, 70% cho tiêu dùng khác $Y$). • Ngưỡng bắt đầu bị ép (binding threshold): $T^ = \frac{0.30}{0.70} \times 100 \approx 42.86\text{ USD}$. • Điểm gốc chưa có quỹ ($I_0 = 100$): $A(30, 70)$, $U_A = 54.29$. • Khi quỹ ủy thác $T = 80\text{ USD}$ (mặc định): • Vì $80 > 42.86$, Jane bị kẹt tại đỉnh gãy $B(80, 100)$, đạt $U_B = 93.52$, tại đó $MRS_B = \frac{0.3}{0.7}\cdot\frac{100}{80} = 0.54 < 1.00$. • Nếu nhận tiền mặt $80\text{ USD}$: Jane chọn tiếp điểm nội bộ $C(54, 126)$, đạt $U_C = 97.72 > U_B$. • Giá trị tiền mặt tương đương (Cash Equivalent) của gói học bổng 80 USD chỉ là 72.27 USD ⇒ tổn thất vô ích do ràng buộc là 7.73 USD. • Khi kéo slider xuống $T = 40\text{ USD} < 42.86\text{ USD}$: • Học bổng nhỏ hơn mức Jane tự nguyện chi, nên không ràng buộc*: $B$ và $C$ trùng nhau tại $(42, 98)$, $U_B = U_C = 76.00$, $MRS = 1.00$. Đây là bài học chính sách rất đắt giá khi kéo slider!

  2. Preset 2: Tem phiếu thực phẩm (Food Stamps) • Đặt $\alpha = 0.25$ (hộ gia đình nghèo dành 25% ngân sách cho thực phẩm $X$, 75% cho nhà ở, y tế, điện nước $Y$). • Ngưỡng bắt đầu bị ép: $T^ = \frac{0.25}{0.75} \times 100 = 33.33\text{ USD}$. • Điểm gốc ($I_0 = 100$): $A(25, 75)$, $U_A = 56.99$. • Khi mệnh giá tem phiếu $T = 70\text{ USD}$ (mặc định): • Gia đình bị kẹt tại đỉnh gãy $B(70, 100)$, đạt $U_B = 91.47$, $MRS_B = 0.48 < 1.00$. • Nếu nhận tiền mặt $70\text{ USD}$: gia đình chọn $C(42.5, 127.5)$, đạt $U_C = 96.88 > U_B$. • Giá trị tiền mặt tương đương của 70 USD tem phiếu chỉ là 60.51 USD (tổn thất phúc lợi 9.49 USD*). • Khi kéo slider xuống $T = 30\text{ USD} < 33.33\text{ USD}$: tem phiếu hoàn toàn tương đương tiền mặt, $B \equiv C(32.5, 97.5)$.