Três objetos separáveis, e mantê-los assim é o ponto
| Objeto | Diz |
|---|---|
SAM | o que o ano-base foi |
ModelSpec | o que se supõe sobre substituição e forma funcional |
Closure | quais balanços são impostos e quem se ajusta |
Resultados podem virar nos dois últimos, e por isso eles viajam junto com os resultados em vez de morar num ramo dentro de um solucionador.
O mercado do fator numerário não é resolvido. Fica para a lei de Walras, de sorte que o seu resíduo é uma verificação independente da contabilidade e não uma identidade que se mandou o solucionador satisfazer. É registrado em cada período.
Instalar
pip install cgelab
pip install "cgelab[parquet]" # MCS em .parquet
pip install "cgelab[dsge]" # investimento com expectativas, pelo dsgelab
pip install "cgelab @ git+https://github.com/GustavoHM/cgelab" # o ramo main atual
Para desenvolver:
git clone https://github.com/GustavoHM/cgelab && cd cgelab
pip install -e ".[test]" && pytest
cgelab # roda a economia de brinquedo e imprime os diagnósticos
cgelab minha_mcs.parquet --sigma-armington 2.0 --sigma-va 0.5
Exemplos
Todos rodam como estão, logo depois do pip install: usam a economia de
brinquedo que vem no pacote, escrita entrada por entrada para que o ano-base seja conhecido no
papel. São os mesmos exemplos que a suíte de testes executa.
1. Da MCS ao equilíbrio
import cgelab as cge
from cgelab.examples import toy
sam, econ = toy.build() # 2 atividades, 2 mercadorias, 2 fatores, 2 domicílios
print(cge.report(econ)) # ano-base, Walras, homogeneidade
base = cge.solve_period(econ)
print(base.summary())
base.pq # preços ao consumidor
base.X # nível de cada atividade
base.walras # o mercado que o solucionador NÃO resolveu
2. Um contrafactual
choque = cge.Scenario(name="tributo +50%", product_tax=econ.ptax * 1.5)
alt = cge.solve_period(econ, scenario=choque)
alt.pq / base.pq - 1 # variação de preço, mercadoria a mercadoria
alt.real["hh_real_income"] # renda real por domicílio
abs(alt.walras) < 1e-9 # a contabilidade continua fechando
3. Um gravame ao consumidor, e a quem volta a receita
W = cge.consumer_wedge(econ, {"c_ind": 0.08}) # 8% sobre uma mercadoria
capital = cge.solve_period(econ, scenario=cge.Scenario(
consumer_wedge=W, wedge_revenue_to="capital")) # devolve por propriedade do capital
percap = cge.solve_period(econ, scenario=cge.Scenario(
consumer_wedge=W, wedge_revenue_to="lumpsum")) # devolve per capita
meio = cge.solve_period(econ, scenario=cge.Scenario(
consumer_wedge=W, wedge_revenue_to=0.5)) # combinação convexa
capital.hh_income[0] > percap.hh_income[0] # o domicílio rico em capital ganha
sum(capital.hh_wedge_paid) == capital.wedge_revenue # a identidade vale exatamente
Isso não é detalhe de implementação. A propriedade do
capital é concentrada no topo: sob "capital" os pobres pagam o gravame nas suas
compras e os ricos o recebem como renda, e parte de qualquer regressividade medida é fabricada por
essa hipótese. A forma convexa existe para que o achado ganhe magnitude em vez de ser binário —
existe em geral um peso em que o gradiente distributivo cruza o zero, e é essa fração que diz se o
resultado tem folga.
A receita reciclada é uma série infinita: creditá-la à renda eleva o gasto, que eleva o gravame, que eleva a renda de novo. A demanda é afim no gasto, logo a série é somada em forma fechada — e iterá-la converge, mas deixa descontinuidades que arruínam a jacobiana do Newton externo.
4. Uma trajetória de 25 anos
crescimento = cge.Growth(labour=0.005, tfp=0.010,
depreciation=0.05, discount=0.05)
def politica(ano, t):
return cge.Scenario(consumer_wedge=W) if ano >= 2026 else cge.Scenario()
ref = cge.run_path(econ, years=range(2021, 2046), growth=crescimento)
alt = cge.run_path(econ, years=range(2021, 2046), growth=crescimento, scenario=politica)
ref.aggregates() # preços, absorção real, poupança, Walras — uma linha por ano
ref.worst_walras # o número a olhar primeiro
ref.capital_stock # estoque, recuperado do custo de uso e não da MCS
Um resíduo de Walras que cresce ao longo da trajetória é a assinatura de um coeficiente que o bloco de preços viu e o de quantidades não. É invisível num período isolado e inconfundível num horizonte de vinte e cinco anos.
5. Bem-estar, sem confundir composição com bem-estar
ev = cge.equivalent_variation(ref, alt)
ev.ev # valorado aos preços do cenário de referência
ev.ev_base # valorado aos preços do ano-base
ev.ev_pct_account # por CONTA — leia a nota abaixo antes de usar
Quando domicílios migram entre contas, a medida por conta reporta a migração como bem-estar. A economia de brinquedo tem uma variante com duas contas gêmeas — mesmas preferências, mesma taxa de poupança — justamente para mostrar isso:
sam, econ2 = toy.build_with_twin_accounts() # hh_1, e hh_2 partido em hh_2a e hh_2b
migrado = econ2.hh_facshare.copy() # move renda entre as gêmeas, sem mudar o total
a, b = econ2.household("hh_2a"), econ2.household("hh_2b")
for f in range(econ2.n_fac):
tot = migrado[a, f] + migrado[b, f]
migrado[a, f], migrado[b, f] = 0.6 * tot, 0.4 * tot
r1 = cge.run_path(econ2, years=range(2021, 2024))
r2 = cge.run_path(econ2, years=range(2021, 2024),
scenario=cge.Scenario(factor_ownership=migrado))
ev2 = cge.equivalent_variation(r1, r2)
ev2.ev_pct_account.abs().max() # > 5% — composição, reportada como bem-estar
grupos = {"hh_1": "g1", "hh_2a": "g2", "hh_2b": "g2"}
cge.ev_by_group(ev2, grupos).ev_pct.abs().max() # ~0 — o zero que de fato está lá
ev_by_group soma rendas reais dentro do grupo antes de dividir, de modo
que o conjunto de domicílios somado é o mesmo nos dois cenários e a razão não depende de quantos
migraram. Toda conta precisa estar no mapeamento: descartar uma em silêncio subestimaria o grupo, e
o erro pareceria um resultado.
6. Exportações que respondem ao preço mundial
import numpy as np
_, econ_cet = toy.build(model_spec=toy.spec(export_rule="cet", sigma_cet=2.0))
j = econ_cet.commodity("c_ind")
pe = np.ones(econ_cet.n_com)
pe[j] = 1.2 # o mundo paga 20% mais pela indústria
antes = cge.solve_period(econ_cet)
depois = cge.solve_period(econ_cet, scenario=cge.Scenario(export_price=pe))
depois.exports[j] / depois.Q[j] # 0,119: a parcela exportada sobe de 0,100
depois.pe[j] / depois.pd[j] # 1,09: exportar agora rende mais que vender em casa
depois.exchange_rate # 0,89: o câmbio se valoriza e absorve parte do choque
Sob export_rule="cet" o produto se reparte entre mercado interno e exportação ao
longo de uma fronteira de transformação, e o preço doméstico é o que faz a receita dessa fronteira
igualar o custo unitário. Com participações fixas, o padrão, export_price é recusado
em vez de ignorado em silêncio.
_, econ_big = toy.build(model_spec=toy.spec(export_rule="cet", sigma_cet=2.0,
export_demand={"c_ind": 2.0}))
big = cge.solve_period(econ_big, scenario=cge.Scenario(export_price=pe))
big.world_export_price[j] # 1.12: vender mais desce pela curva de demanda do comprador
big.exports[j] / econ_big.exp0[j] # 1.14: as exportações sobem 14%, contra 22% do tomador de preço
Uma região que vende boa parte de um produto não é tomadora de preço.
ModelSpec.export_demand põe as suas exportações numa curva de demanda,
E / E0 = (pWe / export_price)^(-η), a forma do lrgcge de Hosoe;
import_supply faz o mesmo com o preço mundial de importação. No ano-base as duas
curvas passam pelo ponto calibrado.
7. Impostos, transferências e capital estrangeiro
import dataclasses
_, econ_i = toy.build_with_institutions()
econ_i.hh_dtax # alíquota de imposto direto por domicílio, lida da MCS
econ_i.fac_row_share # parcela de cada fator que pertence ao exterior
econ_i.foreign_sav # 18: a conta corrente, e não só a balança comercial (10)
cresc = cge.Growth(labour=0.01, tfp=0.01)
anos = range(2021, 2031)
ied = cge.run_path(econ_i, years=anos, growth=cresc,
scenario=lambda ano, t: cge.Scenario(fdi={"f_cap": 3.0}))
doado = cge.run_path(econ_i, years=anos, growth=cresc,
economy=lambda ano, t: dataclasses.replace(
econ_i, foreign_sav=econ_i.foreign_sav + 3.0))
ied[2030].factor_income_abroad > doado[2030].factor_income_abroad # os lucros saem
ied[2030].hh_income.sum() < doado[2030].hh_income.sum() # e a renda local é menor
O mesmo fluxo de capital, duas leituras. Como fdi, ele financia investimento e
vira capital de propriedade estrangeira, cujo retorno sai pela conta corrente. Como mera poupança
externa, o capital fica com os residentes e todo o ganho que produz é creditado a eles. É a
diferença entre medir o efeito de um investimento estrangeiro e superestimá-lo.
8. Vários estoques de capital e uma tecnologia que muda com o tempo
_, econ_k = toy.build_with_specific_capital() # a indústria usa um capital só dela
g = cge.Growth(capital_factor=("f_cap", "f_kind"),
depreciation={"f_cap": 0.05, "f_kind": 0.08},
allocation=2.0) # o investimento segue o aluguel relativo
def economia(ano, t): # a indústria poupa 2% de insumo agrícola ao ano
io = econ_k.io.copy()
io[econ_k.commodity("c_agr"), econ_k.activity("a_ind")] *= 0.98 ** t
return dataclasses.replace(econ_k, io=io)
traj = cge.run_path(econ_k, years=range(2021, 2036), growth=g, economy=economia)
traj.capital_stock[-1] # um estoque por fator de capital
traj.worst_walras # continua à precisão de máquina
Uma mudança que não é preço, tributo nem dotação (a adoção de uma tecnologia, um plano de
financiamento) entra pelo argumento economy, que devolve a economia de cada ano. É o
que um cenário de transição energética precisa: os coeficientes técnicos mudam, e os blocos de
preço e de quantidade continuam vendo a mesma tecnologia.
9. Com a sua própria MCS
A economia de brinquedo existe para os exemplos rodarem. Com dados seus, o que muda são as duas
primeiras linhas e o ModelSpec:
sam = cge.SAM.read("minha_mcs.parquet") # ou SAM.from_long(df) / SAM.from_square(df)
sam.check_balance() # porta dura: nada roda sobre MCS desbalanceada
spec = cge.ModelSpec(
activity_prefix="a_", commodity_prefix="c_", household_prefix="hh_",
factors=("f_lab", "f_cap"),
government="gov", investment="s_i", rest_of_world="row",
numeraire_factor="f_lab", # o mercado dele fica para a lei de Walras
value_added="ces", sigma_va=0.5,
sigma_armington=2.0, # escalar, ou um dicionário por mercadoria
export_rule="fixed_shares", # ou "cet": a elasticidade CET não tem fonte de insumo-produto
household_demand="cobb_douglas", # ou "les", com o parâmetro de Frisch
negative_value_added="leontief", # um excedente operacional negativo é dado, não erro
)
econ = cge.calibrate(sam, spec, population={"hh_d1": 8.3e6, "hh_d2": 8.3e6})
assert cge.base_year_reproduction(econ)["ok"] # não prossiga sem isto
assert cge.homogeneity_test(econ)["ok"]
econ.diagnostics["unmodelled_flows"] # células da MCS que o modelo não lê
Contas são encontradas por prefixo, ou por listas explícitas — que são a escolha mais segura numa MCS cuja nomenclatura não é sistemática, já que uma regra de prefixo que perde uma conta em silêncio produz um modelo que balanceia e está errado.
10. Multiplicadores, com todos os preços fixos
sam_toy, _ = toy.build()
m = cge.accounting_multipliers(sam_toy, spec=toy.spec()) # gov, investment, rest of world exogenous
m.M.loc["hh_2", "c_agr"] # 0.546: o que uma unidade de demanda por agricultura soma à renda do domicílio 2
paths = cge.structural_paths(m, "c_agr", "hh_2", threshold=1e-3)
paths[["path", "total", "share"]].head(2)
# (c_agr, a_agr, f_lab, hh_2) 0.323 0.59 pelos salários, com a realimentação de fora do caminho
# (c_agr, a_agr, f_cap, hh_2) 0.131 0.24
A mesma MCS lida como modelo a preço fixo: as quantidades respondem a uma injeção nas
proporções do ano-base. leontief_multipliers deixa endógena só a produção,
fixed_price_multipliers põe as famílias em propensões marginais, e
linkages dá a tabela de setores-chave, para trás e para frente. Somadas sobre todos
os caminhos elementares, as influências totais dão o multiplicador.
11. Contas
taxed = cge.solve_period(econ, scenario=cge.Scenario(product_tax=econ.ptax * 1.5))
na = cge.national_accounts(taxed) # o contrafactual do exemplo 2
na["gdp_expenditure"], na["gdp_income"] # 202.78 e 202.78: o PIB pelos dois lados, em qualquer equilíbrio
pi = cge.price_indices(taxed)
pi.loc["gdp", ["fisher", "quantity_fisher"]] # 1.040, 0.9999: preços 4% acima, volume parado
cge.government_account(taxed)["saving"] # 12.04, contra 5 no ano-base
O PIB a preços correntes pelos lados da despesa e da renda, índices de Laspeyres, Paasche e Fisher para cada componente da demanda final, para o PIB e para os termos de troca, e a conta do governo por instrumento. A conta do governo é registrada pelo solver na solução, então a poupança dela é a que financiou o investimento.
12. Quanto um resultado depende das elasticidades
run = cge.spec_sweep(sam, toy.spec(), scenario=cge.Scenario(product_tax=econ.ptax * 1.5),
measure=lambda base, alt: {"gdp_pct": 100 * (
cge.real_gdp(alt)["expenditure"] / cge.real_gdp(base)["expenditure"] - 1)})
params = {"sigma_va": cge.Uniform(0.3, 1.3), "sigma_armington": cge.Uniform(1.5, 3.5)}
cge.sensitivity(run, params).summary() # quadratura de Stroud: 4 pontos para 2 parâmetros
cge.sensitivity(run, params, method="monte_carlo", draws=40).summary()
Cada ponto recalibra e resolve o ano-base e o cenário. A regra de Stroud dá uma média exata
até o grau três nos parâmetros com 2n soluções; o Monte Carlo acrescenta quantis. A
elasticidade de Armington perto de 1 fica de fora de propósito: com parcelas de exportação fixas
o câmbio não é determinado em 1, e é mal condicionado perto dele.
13. Que choque fez o quê
_, econ_tt = toy.build_with_trade_taxes()
reform = cge.Scenario(tariff=np.zeros(2), world_price=np.array([1.1, 0.95]),
product_tax=econ_tt.ptax * 1.2)
parts = cge.subtotals(econ_tt, cge.Scenario(), reform,
{"tariff": ["tariff"], "world": ["world_price"], "tax": ["product_tax"]},
measure=lambda p: {"gdp": cge.real_gdp(p)["expenditure"]})
parts[["tariff", "world", "tax", "change", "residual"]]
# gdp -0.723 -0.125 -0.007 -0.855 ~1e-10 as partes somam a variação resolvida
A decomposição que o GEMPACK chama de subtotals. Todos os choques andam juntos pela reta da referência à alternativa, e o termo de cada grupo é integrado à parte, então as partes somam o total sem depender de ordem alguma.
14. De onde veio uma variação de bem-estar
from cgelab.examples import hosoe
_, lrg = hosoe.build(large_country=True)
w = cge.welfare_decomposition(lrg, cge.Scenario(), cge.Scenario(tariff=np.zeros(2)),
closure=hosoe.CLOSURE)
w["allocative_tariff"], w["terms_of_trade"] # +0.054, -0.831: o ganho com a tarifa, e o que um país grande paga por ele lá fora
w["absorption_households"] # +0.506: a VE de Hosoe para o mesmo experimento é +0,508
w["residual"] # ~1e-10: os termos somam a variação da absorção
A decomposição de Huff e Hertel, escrita para os instrumentos deste modelo: dotações, tecnologia, um termo alocativo por distorção, termos de troca e conta externa, integrados ao longo da trajetória. A medida é a absorção real, com governo e investimento. A parte das famílias é a que se compara a uma VE.
15. Um preço de carbono, com reciclagem
import pandas as pd
_, inst = toy.build_with_institutions()
fuel = pd.DataFrame(0.0, index=inst.coms, columns=inst.acts)
fuel.loc["c_ind", ["a_agr", "a_ind"]] = [3.0, 12.0] # toneladas queimadas na produção
green = cge.add_emissions(inst, intermediate=fuel, household={"c_ind": 5.0},
process={"a_ind": 2.0})
neutral = cge.Closure(government_target="revenue", government_adjusts="product_tax")
r = cge.solve_period(green, neutral, cge.Scenario(carbon_price=0.5))
cge.emissions(r)["total"] # 21.79, eram 22 no ano-base
r.tax_scale # 0.26: a receita do carbono paga um corte de 74% nas alíquotas sobre produtos
As emissões são uma conta satélite calibrada a partir dos totais do ano-base, por fonte. O preço do carbono é um imposto por unidade de produto, pela intensidade de emissão, nas atividades, e um imposto específico no combustível das famílias. Com meta de receita, outro imposto se move para manter a arrecadação; aqui, o imposto sobre produtos.
16. Combustível que responde ao preço
_, nested = toy.build_with_institutions(model_spec=toy.spec(energy=["c_ind"], sigma_vae=0.6))
nested = cge.add_emissions(nested, intermediate=fuel, household={"c_ind": 5.0},
process={"a_ind": 2.0})
r2 = cge.solve_period(nested, neutral, cge.Scenario(carbon_price=0.5))
cge.emissions(r2)["total"] # 20.85, contra 21,79 com coeficientes fixos
r2.input_coefficients[1] # combustível por unidade de produto: 0,089 e 0,276, eram 0,095 e 0,286
Com ModelSpec.energy, cada atividade compra um agregado de energia que substitui
valor adicionado. O carbono de um combustível entra no preço que aquela atividade paga por ele,
então o mesmo preço de carbono muda como se produz, não só quanto. Com as duas elasticidades em
zero, o ninho é o modelo Leontief, à precisão de máquina.
As três portarias
Não são formalidades. Um modelo que falha a primeira não está calibrado.
# cge.report(econ) em toy.build_with_margins()
cgelab diagnostics -- Economy(3 activities, 3 commodities, 2 households, 2 factors)
base-year reproduction : PASS (worst 7.11e-16)
imports 7.11e-16
activity_output 4.26e-16
commodity_output 4.26e-16
exports 3.55e-16
Walras residual : -2.41e-16 [f_lab]
price homogeneity : PASS (worst 3.72e-16)
| Portaria | O que pega |
|---|---|
| Reprodução do ano-base | Sem choque, o modelo tem de devolver a MCS: todo preço em um, toda quantidade no seu valor de base. É o que pega um erro de unidade na calibração — um preço de comprador tratado como preço básico, um tributo lançado sobre a base errada — antes que ele seja absorvido num resultado. |
| Lei de Walras | Resíduo não nulo significa renda sendo perdida ou fabricada em algum lugar, e o tamanho do vazamento não limita o tamanho do erro que causa. |
| Homogeneidade | Escalar o numerário tem de deixar toda magnitude real intacta. Falha significa que uma magnitude nominal denominada no numerário ficou presa enquanto o nível de preços se moveu — poupança externa, meta de balança comercial, um tributo específico que entra aditivamente. Cada uma desloca uma alocação real, e nenhuma é visível na saída. |
O que tem na caixa
MCS
Leitura em formato longo ou quadrado, verificação de balanço como porta dura e RAS que preserva sinais, para as células negativas que MCS reais têm.
Calibração
Coeficientes em preços básicos, bloco de margens, valor adicionado negativo, subsistência LES, atividades de conta própria, e a lista das células da MCS que o modelo não lê.
Produção
Leontief entre agregado intermediário e valor adicionado, com CES, Cobb-Douglas ou Leontief no ninho de fatores.
Comércio
Armington CES; exportações por participações fixas ou por CET com preço mundial de exportação.
Instituições
Impostos diretos, transferências do governo, remessas, renda de fatores do governo e do exterior; a poupança externa é a conta corrente.
Dinâmica
Recursiva, com um ou vários estoques de capital, investimento repartido pelo retorno, IED que cria capital estrangeiro e economia por ano.
Fechamentos
Investimento, governo e setor externo, cada um com a sua variável de ajuste. Cinco combinações testadas.
Bem-estar
VE e VC, com agregação por grupo imune à composição e as duas réguas de preço explicitadas.
Réplica em JuMP
O mesmo equilíbrio escrito como modelo JuMP sem aninhamento, para conferir a álgebra com o Ipopt.
Uma advertência que o padrão codifica
A elasticidade de transformação (CET) é o único parâmetro de um EGC padrão que as fontes de
insumo-produto não fornecem. Fixar participações de exportação mantém esse parâmetro fora do
cenário-base por inteiro, de modo que nenhum número de destaque repouse sobre um valor inventado.
export_rule="cet" resolve a fronteira de transformação por inteiro, para quando o
exercício precisa que as exportações respondam a preço. É uma posição metodológica, e o padrão a
codifica.
Validação
Testes sobre MCS escritas à mão
Oito economias de brinquedo com cada entrada escrita no código, de modo que a comparação é contra aritmética e não contra saída anterior:
- o caso simples;
- um com bloco de margens de contabilidade nacional (serviços de margem vendidos na coluna de oferta das demais mercadorias, contra uma entrada negativa na própria diagonal);
- um com atividade de conta própria;
- um com contas gêmeas, para o teste de composição;
- um com impostos diretos, transferências, remessas e capital do governo e do exterior;
- um com capital específico de setor;
- um com tarifa e imposto indireto sobre o produto dentro dos preços;
- uma economia fechada de um bem com produção Cobb–Douglas, que é o modelo de crescimento estocástico contra o qual a ponte com o dsgelab é conferida.
Os testes cobrem reprodução do ano-base à precisão de máquina, homogeneidade, e Walras sob choque em todas as combinações de fechamento e em cada data de uma trajetória em crescimento. Cobrem também a identidade de receita da CET e a propriedade estrangeira do capital comprado com IED. E todo exemplo executável desta página roda na suíte.
Uma réplica independente em JuMP
to_jump escreve o mesmo equilíbrio como modelo JuMP em que nada é aninhado: o
ponto fixo de preços, a série da reciclagem e a relação entre poupança e arrecadação são
restrições comuns, e o Ipopt as resolve de um ponto de partida deslocado. Em 34 casos (margens, curvas de demanda de exportação e de oferta de importação, tarifa e imposto sobre o produto dentro dos preços, o fechamento de Hosoe, metas do governo, preço do carbono, dois ninhos de energia,
conta própria, CET, impostos diretos, transferências, capital estrangeiro, IED, LES e fechamentos
alternativos), os dois resolvedores concordam a no máximo 1.2e-11. Perturbar os dados
exportados faz a comparação divergir de 1e-3 a 3e-1, o que mostra que ela enxerga erro.
Como se compara ao que já existe
Aqui a diferença não é de opinião: é de escala.
| Capacidade | cgelab | pycge | PSLmodels/CGE | python-cge |
|---|---|---|---|---|
| Escala demonstrada | MCS aplicada ainda não pública | didática | MCS 16×18 4 setores | 2 bens |
| Origem | MCS aplicada | porte do splcge.gams | Hosoe et al. (2010) | Hosoe et al., p. 72 |
| Bloco de margens de comércio e transporte | sim | ? | — | — |
| Valor adicionado negativo tratado | sim | ? | — | — |
| Atividades de conta própria | sim | — | — | — |
| Fechamentos alternativos | 5 combinações | ? | — | — |
| Walras como verificação, não equação | sim | ? | — | — |
| Reprodução do ano-base e homogeneidade automáticas | sim | parcial | — | — |
| CET resolvida, com preço mundial de exportação | sim | ? | sim | — |
| Impostos diretos, transferências, capital estrangeiro e IED | sim | ? | ? | ? |
| Dinâmica recursiva | vários capitais | — | sim | — |
| Bem-estar (VE, VC, por grupo) | sim | — | — | — |
Quem faz EGC aplicado usa GAMS ou GEMPACK, e com bons motivos:
resolvedores dedicados, comunidade, bases como a GTAP. Os três pacotes de EGC em Python que
encontrei são implementações do modelo-texto de Hosoe, Gasawa e Hashimoto (2010) ou do
splcge da biblioteca do GAMS — dois a quatro setores; o
PSLmodels/CGE diz de si mesmo, no README, que é
"muito preliminar" e pede que não seja citado.
A afirmação defensável do cgelab é estreita: se você quer um EGC de escala aplicada
em Python, sem licença comercial e com as portarias contábeis embutidas, não achei
alternativa. Isso não é o mesmo que cobrir o que um GAMS cobre — não há múltiplas regiões, nem
estimação de elasticidades, nem trinta anos de uso. A tabela completa, com o que foi verificado
lendo código e o que apenas se leu na documentação, está em
docs/comparison.pt-br.md.
Como citar
Se o pacote entrou num resultado publicado, ele entra nas referências. Software é um insumo metodológico como qualquer outro, e a versão importa: um número que só se reproduz numa versão específica não é reproduzível sem ela.
BibTeX
@software{cgelab_2026,
author = {Moreira, Gustavo Hoffmann},
title = {{cgelab}: a general toolkit for SAM-based computable general equilibrium models in Python},
year = {2026},
version = {0.5.0},
license = {MIT},
url = {https://github.com/GustavoHM/cgelab},
note = {Pacote em Python}
}
ABNT
MOREIRA, Gustavo Hoffmann. cgelab: a general toolkit for SAM-based computable general equilibrium models in Python.
Versão 0.5.0. 2026. Software em Python. Disponível em:
https://github.com/GustavoHM/cgelab. Acesso em: [data].
No corpo do texto
Cite a versão junto do resultado, não só o nome:
O modelo foi calibrado e resolvido com o cgelab 0.5.0
(Moreira, 2026), em dinâmica recursiva de 2021 a 2045.
Antes de citar, arquive. Uma URL de repositório não é uma
referência arquivável: o conteúdo muda e o link morre. Ligue o repositório ao
Zenodo, crie um release, e o DOI que sai daí é o que se
cita. O arquivo CITATION.cff na raiz do pacote, que já
traz o ORCID do autor, está no formato que o GitHub lê, e o botão "Cite this repository" fornece a
entrada a partir dele. O DOI será acrescentado a esse arquivo com a primeira versão arquivada.
E as ferramentas que este pacote usa
NumPy e SciPy pedem citação explicitamente e sustentam tudo o que está aqui; o pandas lê e guarda a MCS. Se o resultado foi conferido pela réplica, entram também o JuMP e o Ipopt.
Documentação
A documentação detalhada está em inglês, exceto a comparação.
Guia de uso
Da MCS ao bem-estar, com as armadilhas de cada etapa.
Referência da API
Toda função, todo campo, toda forma de array.
Notas de projeto
As decisões contábeis e os erros que cada uma evita.
Portar um pipeline
Tabela de
tradução de um solucionador existente para Scenario e Closure.
Comparação
Onde o pacote está frente aos outros EGC em Python e ao GAMS, e onde não ganha.
Mudanças
O que mudou de uma versão para outra.
Roadmap
O que ainda falta no pacote, e por que importa.
Replicações
Modelos publicados refeitos a partir da matriz de contabilidade social deles, e onde um resultado ainda não pode ser reivindicado.