GAMSのマクロ機能($macro)

いつのバージョンからかははっきり覚えていませんが(22の後半から?)、GAMSにマクロの機能が実装されました。

マクロは以下のように$macro命令で定義します。

set  i   / 1*10 /
;
parameter
    a(i)
    b(i)
;
a(i) = ord(i);

$macro f(i) a(i)**2

b(i) = f(i);

display b;

$macroから始まる行でマクロf(i)を定義しています。そして、そのf(i)というマクロはb(i)=f(i)という部分で利用されています。

マクロは(コンパイル時に)展開されるので、上のコードは実行時には次のようになります。

set     i       / 1*10 /
;
parameter
    a(i)
    b(i)
;
a(i) = ord(i);

$macro f(i) a(i)**2

b(i) = a(i)**2;

display b;

マクロのf(i)コンパイル時に展開されて a(i)**2に置き換わります。

このマクロの機能の一つの利点はモデルの変数を減らせるということです。特に、大型のモデルを解く場合には、このメリットが生きてきます。また、複雑な表現は一度マクロで定義しておけば、何度も何度も複雑な式を記述しなくて済むので非常に楽ですし、プログラムが読みやすくなります。ただし、マクロの利用にはデメリットもあります。それはモデルのデバッグがしにくくくなるということです。

GTAP7inGAMSのサンプルのモデル mrtmcp.gms ではマクロが多用されていますので、それを例にとります。

mrtmcp.gmsの中に以下のような式の定義があります。

Y(g,r) * vom(g,r) =e= (RA(r)/P(g,r))$sameas(g,"C") + 
    vom(g,r)$(sameas(g,"G") or sameas(g,"I")) +
    sum(i$sameas(i,g),  sum(gg,DDFM(i,gg,r)) + sum(s,DXMD(i,r,s)) + DST(i,r));

これは均衡条件の中の一本(市場均衡条件の一つ)です。これ自体は非常に短い式で単純です。しかし、この中の DDFM、DXMD、DSTはマクロであり、これは実行時には以下のように展開されます。

Y(g,r) * vom(g,r) =e= (RA(r)/P(g,r))$sameas(g,"C") + vom(g,r)$(sameas(g,"G") or
sameas(g,"I")) + sum(i$sameas(i,g), sum(gg,(vdfm(i,gg,r) * Y(gg,r) *((( sum(i_,
thetai(i_,gg,r)*((thetad(i_,gg,r)*((P(i_,r)*(1+rtfd(i_,gg,r))/
(1+rtfd0(i_,gg,r)))$thetad(i_,gg,r) + 1$(thetad(i_,gg,r)=0)) +
(1-thetad(i_,gg,r))*((PM(i_,r)*(1+rtfi(i_,gg,r))/
(1+rtfi0(i_,gg,r)))$(1-thetad(i_,gg,r)) +
1$(thetad(i_,gg,r)=1)))$(yes$(round(esubd(i_),2)=0))
+(((P(i_,r)*(1+rtfd(i_,gg,r))/ (1+rtfd0(i_,gg,r)))$thetad(i_,gg,r) +
1$(thetad(i_,gg,r)=0))**thetad(i_,gg,r) * ((PM(i_,r)*(1+rtfi(i_,gg,r))/
(1+rtfi0(i_,gg,r)))$(1-thetad(i_,gg,r)) +
1$(thetad(i_,gg,r)=1))**(1-thetad(i_,gg,r)))$(yes$(round(esubd(i_)-1,2)=0)) +(
(thetad(i_,gg,r) *((P(i_,r)*(1+rtfd(i_,gg,r))/
(1+rtfd0(i_,gg,r)))$thetad(i_,gg,r) + 1$(thetad(i_,gg,r)=0))**(1-esubd(i_))
+(1-thetad(i_,gg,r))*((PM(i_,r)*(1+rtfi(i_,gg,r))/
(1+rtfi0(i_,gg,r)))$(1-thetad(i_,gg,r)) +
1$(thetad(i_,gg,r)=1))**(1-esubd(i_)))**(1/(1-esubd(i_))))$(yes$(round(esubd(i_)-1,2)<>0
and round(esubd(i_),2)<>0)))) + theta_f(gg,r)*((sum(f_,
thetaf(f_,gg,r)*(((PF(f_,r)$mf(f_)+PS(f_,gg,r)$sf(f_))*(1+rtf(f_,gg,r))/
(1+rtf0(f_,gg,r)))$thetaf(f_,gg,r) + 1$(thetaf(f_,gg,r) =
0))))$(yes$(round(esubva(gg),2)=0)) +(prod(f_,
(((PF(f_,r)$mf(f_)+PS(f_,gg,r)$sf(f_))*(1+rtf(f_,gg,r))/
(1+rtf0(f_,gg,r)))$thetaf(f_,gg,r) + 1$(thetaf(f_,gg,r) =
0))**thetaf(f_,gg,r)))$(yes$(round(esubva(gg)-1,2)=0)) +(sum(f_,
thetaf(f_,gg,r)*(((PF(f_,r)$mf(f_)+PS(f_,gg,r)$sf(f_))*(1+rtf(f_,gg,r))/
(1+rtf0(f_,gg,r)))$thetaf(f_,gg,r) + 1$(thetaf(f_,gg,r) =
0))**(1-esubva(gg)))**(1/(1-esubva(gg))))$(yes$(round(esubva(gg)-1,2)<>0 and
round(esubva(gg),2)<>0))))$(yes$(round(esub(gg),2)=0)) +(prod(i_,
((thetad(i_,gg,r)*((P(i_,r)*(1+rtfd(i_,gg,r))/
(1+rtfd0(i_,gg,r)))$thetad(i_,gg,r) + 1$(thetad(i_,gg,r)=0)) +
(1-thetad(i_,gg,r))*((PM(i_,r)*(1+rtfi(i_,gg,r))/
(1+rtfi0(i_,gg,r)))$(1-thetad(i_,gg,r)) +
1$(thetad(i_,gg,r)=1)))$(yes$(round(esubd(i_),2)=0))
+(((P(i_,r)*(1+rtfd(i_,gg,r))/ (1+rtfd0(i_,gg,r)))$thetad(i_,gg,r) +
1$(thetad(i_,gg,r)=0))**thetad(i_,gg,r) * ((PM(i_,r)*(1+rtfi(i_,gg,r))/
(1+rtfi0(i_,gg,r)))$(1-thetad(i_,gg,r)) +
1$(thetad(i_,gg,r)=1))**(1-thetad(i_,gg,r)))$(yes$(round(esubd(i_)-1,2)=0)) +(
(thetad(i_,gg,r) *((P(i_,r)*(1+rtfd(i_,gg,r))/
(1+rtfd0(i_,gg,r)))$thetad(i_,gg,r) + 1$(thetad(i_,gg,r)=0))**(1-esubd(i_))
+(1-thetad(i_,gg,r))*((PM(i_,r)*(1+rtfi(i_,gg,r))/
(1+rtfi0(i_,gg,r)))$(1-thetad(i_,gg,r)) +
1$(thetad(i_,gg,r)=1))**(1-esubd(i_)))**(1/(1-esubd(i_))))$(yes$(round(esubd(i_)-1,2)<>0
and round(esubd(i_),2)<>0)))**thetai(i_,gg,r))*((sum(f_,
thetaf(f_,gg,r)*(((PF(f_,r)$mf(f_)+PS(f_,gg,r)$sf(f_))*(1+rtf(f_,gg,r))/
(1+rtf0(f_,gg,r)))$thetaf(f_,gg,r) + 1$(thetaf(f_,gg,r) =
0))))$(yes$(round(esubva(gg),2)=0)) +(prod(f_,
(((PF(f_,r)$mf(f_)+PS(f_,gg,r)$sf(f_))*(1+rtf(f_,gg,r))/
(1+rtf0(f_,gg,r)))$thetaf(f_,gg,r) + 1$(thetaf(f_,gg,r) =
0))**thetaf(f_,gg,r)))$(yes$(round(esubva(gg)-1,2)=0)) +(sum(f_,
thetaf(f_,gg,r)*(((PF(f_,r)$mf(f_)+PS(f_,gg,r)$sf(f_))*(1+rtf(f_,gg,r))/
(1+rtf0(f_,gg,r)))$thetaf(f_,gg,r) + 1$(thetaf(f_,gg,r) =
0))**(1-esubva(gg)))**(1/(1-esubva(gg))))$(yes$(round(esubva(gg)-1,2)<>0 and
round(esubva(gg),2)<>0)))**theta_f(gg,r))$(yes$(round(esub(gg)-1,2)=0))
+((sum(i_, thetai(i_,gg,r)*((thetad(i_,gg,r)*((P(i_,r)*(1+rtfd(i_,gg,r))/
(1+rtfd0(i_,gg,r)))$thetad(i_,gg,r) + 1$(thetad(i_,gg,r)=0)) +
(1-thetad(i_,gg,r))*((PM(i_,r)*(1+rtfi(i_,gg,r))/
(1+rtfi0(i_,gg,r)))$(1-thetad(i_,gg,r)) +
1$(thetad(i_,gg,r)=1)))$(yes$(round(esubd(i_),2)=0))
+(((P(i_,r)*(1+rtfd(i_,gg,r))/ (1+rtfd0(i_,gg,r)))$thetad(i_,gg,r) +
1$(thetad(i_,gg,r)=0))**thetad(i_,gg,r) * ((PM(i_,r)*(1+rtfi(i_,gg,r))/
(1+rtfi0(i_,gg,r)))$(1-thetad(i_,gg,r)) +
1$(thetad(i_,gg,r)=1))**(1-thetad(i_,gg,r)))$(yes$(round(esubd(i_)-1,2)=0)) +(
(thetad(i_,gg,r) *((P(i_,r)*(1+rtfd(i_,gg,r))/
(1+rtfd0(i_,gg,r)))$thetad(i_,gg,r) + 1$(thetad(i_,gg,r)=0))**(1-esubd(i_))
+(1-thetad(i_,gg,r))*((PM(i_,r)*(1+rtfi(i_,gg,r))/
(1+rtfi0(i_,gg,r)))$(1-thetad(i_,gg,r)) +
1$(thetad(i_,gg,r)=1))**(1-esubd(i_)))**(1/(1-esubd(i_))))$(yes$(round(esubd(i_)-1,2)<>0
and round(esubd(i_),2)<>0)))**(1-esub(gg))) +theta_f(gg,r)*((sum(f_,
thetaf(f_,gg,r)*(((PF(f_,r)$mf(f_)+PS(f_,gg,r)$sf(f_))*(1+rtf(f_,gg,r))/
(1+rtf0(f_,gg,r)))$thetaf(f_,gg,r) + 1$(thetaf(f_,gg,r) =
0))))$(yes$(round(esubva(gg),2)=0)) +(prod(f_,
(((PF(f_,r)$mf(f_)+PS(f_,gg,r)$sf(f_))*(1+rtf(f_,gg,r))/
(1+rtf0(f_,gg,r)))$thetaf(f_,gg,r) + 1$(thetaf(f_,gg,r) =
0))**thetaf(f_,gg,r)))$(yes$(round(esubva(gg)-1,2)=0)) +(sum(f_,
thetaf(f_,gg,r)*(((PF(f_,r)$mf(f_)+PS(f_,gg,r)$sf(f_))*(1+rtf(f_,gg,r))/
(1+rtf0(f_,gg,r)))$thetaf(f_,gg,r) + 1$(thetaf(f_,gg,r) =
0))**(1-esubva(gg)))**(1/(1-esubva(gg))))$(yes$(round(esubva(gg)-1,2)<>0 and
round(esubva(gg),2)<>0)))**(1-esub(gg)))**(1/(1-esub(gg))))$(yes$(round(esub(gg)-1,2)<>0
and round(esub(gg),2)<>0)) )/((thetad(i,gg,r)*((P(i,r)*(1+rtfd(i,gg,r))/
(1+rtfd0(i,gg,r)))$thetad(i,gg,r) + 1$(thetad(i,gg,r)=0)) +
(1-thetad(i,gg,r))*((PM(i,r)*(1+rtfi(i,gg,r))/
(1+rtfi0(i,gg,r)))$(1-thetad(i,gg,r)) +
1$(thetad(i,gg,r)=1)))$(yes$(round(esubd(i),2)=0)) +(((P(i,r)*(1+rtfd(i,gg,r))/
(1+rtfd0(i,gg,r)))$thetad(i,gg,r) + 1$(thetad(i,gg,r)=0))**thetad(i,gg,r) *
((PM(i,r)*(1+rtfi(i,gg,r))/ (1+rtfi0(i,gg,r)))$(1-thetad(i,gg,r)) +
1$(thetad(i,gg,r)=1))**(1-thetad(i,gg,r)))$(yes$(round(esubd(i)-1,2)=0)) +(
(thetad(i,gg,r) *((P(i,r)*(1+rtfd(i,gg,r))/ (1+rtfd0(i,gg,r)))$thetad(i,gg,r) +
1$(thetad(i,gg,r)=0))**(1-esubd(i))
+(1-thetad(i,gg,r))*((PM(i,r)*(1+rtfi(i,gg,r))/
(1+rtfi0(i,gg,r)))$(1-thetad(i,gg,r)) +
1$(thetad(i,gg,r)=1))**(1-esubd(i)))**(1/(1-esubd(i))))$(yes$(round(esubd(i)-1,2)<>0
and round(esubd(i),2)<>0))))**esub(gg)
*(((thetad(i,gg,r)*((P(i,r)*(1+rtfd(i,gg,r))/ (1+rtfd0(i,gg,r)))$thetad(i,gg,r)
+ 1$(thetad(i,gg,r)=0)) + (1-thetad(i,gg,r))*((PM(i,r)*(1+rtfi(i,gg,r))/
(1+rtfi0(i,gg,r)))$(1-thetad(i,gg,r)) +
1$(thetad(i,gg,r)=1)))$(yes$(round(esubd(i),2)=0)) +(((P(i,r)*(1+rtfd(i,gg,r))/
(1+rtfd0(i,gg,r)))$thetad(i,gg,r) + 1$(thetad(i,gg,r)=0))**thetad(i,gg,r) *
((PM(i,r)*(1+rtfi(i,gg,r))/ (1+rtfi0(i,gg,r)))$(1-thetad(i,gg,r)) +
1$(thetad(i,gg,r)=1))**(1-thetad(i,gg,r)))$(yes$(round(esubd(i)-1,2)=0)) +(
(thetad(i,gg,r) *((P(i,r)*(1+rtfd(i,gg,r))/ (1+rtfd0(i,gg,r)))$thetad(i,gg,r) +
1$(thetad(i,gg,r)=0))**(1-esubd(i))
+(1-thetad(i,gg,r))*((PM(i,r)*(1+rtfi(i,gg,r))/
(1+rtfi0(i,gg,r)))$(1-thetad(i,gg,r)) +
1$(thetad(i,gg,r)=1))**(1-esubd(i)))**(1/(1-esubd(i))))$(yes$(round(esubd(i)-1,2)<>0
and round(esubd(i),2)<>0)))/((P(i,r)*(1+rtfd(i,gg,r))/
(1+rtfd0(i,gg,r)))$thetad(i,gg,r) +
1$(thetad(i,gg,r)=0)))**esubd(i))$vdfm(i,gg,r)) + sum(s,((vxmd(i,r,s) * M(i,s) *
(PM(i,s)/(((P(i,r)*(1-rtxs(i,r,s))*(1+rtms(i,r,s))/pvxmd(i,r,s))$vxmd(i,r,s) +
1$(vxmd(i,r,s)=0))*thetavxmd(i,r,s) + sum(j1,
(PT(j1)*(1+rtms(i,r,s))/pvtwr(i,r,s))$vtwr(j1,i,r,s)*thetavtwr(j1,i,r,s))))**esubm(i))$vxmd(i,r,s)))
+ (vst(i,r)*YT(i)*PT(i)/P(i,r))$vst(i,r)); 

マクロが何個も何重にも渡って展開されるため、とんでもなく長い式になります。仮にこの式の中に間違いがあってエラーが出たとして、どこが間違っているのかわかるでしょうか?ほとんどの場合、エラー箇所を見つけるのは無理だと思います。

上記のように、マクロを多用するとデバッグが難しくなるという問題がありますので、マクロは次のように使うのがよいと思います。

  • まず、マクロを使わず、全て普通の式、変数でモデルを記述する。
  • モデルのデバッグをし、誤りがないことを確認する。
  • 少しずつ式・変数をマクロによって置き換えていく。
  • その際、マクロに間違いがないか確認しながら行う。
  • 以上を繰り返して、マクロに置き換えられる部分は置き換える。

以上のようにすれば、デバッグが難しいというマクロの問題を回避しながら、マクロを利用することができると思います。ただし、この方法では複雑な式を何度も書かなくてもすむというマクロの利点がなくなりますが(一度は書かないといけませんから)。