MIP → SAM pipeline¶
equilibria.sam_tools converts a raw input-output / MIP (Matrix of
Intermediate Production) table into a balanced Social Accounting Matrix.
The conversion is decomposed into ten transformation steps that each
record their own balance statistics, so you can audit exactly where flows
were created or rebalanced.
Pipeline overview¶
Step |
Purpose |
|---|---|
|
Re-classify raw |
|
Split |
|
Route factor income to institutional accounts ( |
|
Convert |
|
Build |
|
Attach a Rest-of-World account from import rows. |
|
Add the diagonal J → I closure for sectoral production. |
|
Place investment and savings flows. |
|
Attach the export/X block. |
|
Re-route |
|
Final iterated GRAS balance using |
One-shot conversion¶
The simplest entry point is :func:equilibria.sam_tools.run_mip_to_sam,
which loads the MIP, runs every step, balances the SAM, and returns a
result object with both the final SAM and the per-step report.
from pathlib import Path
from equilibria.sam_tools import run_mip_to_sam
result = run_mip_to_sam(
Path("data/my_mip.xlsx"),
sheet_name="MIP",
va_factor_shares={"L": 0.65, "K": 0.35},
ras_max_iter=200,
output_path=Path("out/my_sam.xlsx"),
report_path=Path("out/my_sam_report.json"),
)
print(f"Steps run: {len(result.steps)}")
print(f"Final balance: {result.steps[-1]['balance']['max_row_col_abs_diff']:.2e}")
A complete runnable version of this snippet (with output) lives in the example gallery.
Calling individual transforms¶
When you need fine-grained control — e.g. for diagnostic comparisons or
to inject a custom step — call the transforms directly. Each one mutates
the MIPRawSAM in place and returns a small report dict.
from equilibria.sam_tools.mip_raw_excel import MIPRawSAM
from equilibria.sam_tools.mip_to_sam_transforms import (
normalize_mip_accounts,
disaggregate_va_to_factors,
create_factor_income_distribution,
)
sam = MIPRawSAM.from_mip_excel("data/my_mip.xlsx", sheet_name="MIP")
normalize_mip_accounts(sam, {})
disaggregate_va_to_factors(sam, {"va_factor_shares": {"L": 0.65, "K": 0.35}})
create_factor_income_distribution(sam, {})
# ... run the rest of the pipeline ...
SAM convention¶
equilibria follows the convention df.loc[receiver_row, payer_col] —
rows receive, columns pay. Whenever you write your own transforms,
keep this orientation; the post-step balance check compares row sums to
column sums.
Troubleshooting¶
max_row_col_abs_diffdoes not converge — the source MIP is square-padded with zero rows/columns that should not exist; check the fixture againstMIPRawSAM.from_mip_excel(..., debug=True)and either trim the padding or rebalance the inputs.Negative GRAS targets —
gras_balancehandles negative entries, but the iterated outer loop in_final_balance_normalized_samaverages row and column sums; if any target stays negative the iteration stalls. The fix is usually to inspect which sub-step introduced a negative flow (look fortotal_distributed < 0in the report).