エンジニアによって憧れのツールの一つに数式処理システムがあります。 しかし、数式処理システムは、高価でかつ多機能なため簡単には使うことができませんでした。
ここでは、フリーの数式処理システムSageを例に、基礎編、応用編に分けて数式処理システムの使い方を紹介します。
sageは、Mathematicaのような数式処理を行うオープンソースのソフトウェアです。 Maxima, R等の既存のオープンソースパッケージをPythonをベースとしたインタフェースで結合し、 ブラウザー(Firefox等)から簡単に操作できるノートブックを提供しています。(図参照)
sageの目標は、商用のMagma, Maple, MathematicaそしてMatlabの代わりとなる フリーかつオープンソースのシステムを開発することです。
sageは、2005年2月にウィリアム・スタイン氏(William Stein)によって開発がスタートし、 2006年2月のUCSD SAGE Days 1でSage 1.0が公開されました注1。
sageに含まれる主なコンポーネントは、以下の通りです注2。
計算処理 | GMP, MPFR |
可換環論 | Singular (libcf, libfactor y) |
暗号処理 | OpenSSL, PyOpenSSL, PyCrypto |
組み合わせ | GAP |
グラフ理論 | NetworkX |
数論 | PARI, NTL |
数値計算 | GSL, Numpy |
計算機代数 | Maxima |
統計処理 | R |
特殊数値計算 | 多くの C/C++プログラム |
コマンドライン処理 | IPython |
グラフィックインタフェース | Notebook, jsmath, Moin wiki |
プロット処理 | Matplotlib, Tachyon, libgd |
ネットワーク処理 | Twisted |
データベース | ZODB, Python Pickles |
プログラミング言語 | Python, SageX (compiled python) |
Sageのおもしろいところは、sageをインストールしなくてもオンラインでsageを使うことができるところです。
Sageのホームページ(http://www.sagemath.org/)のTry Sage Onlineをクリックして、 Sign up for a new Sage Notebook accountでアカウントを作ったら早速使ってみよう!
ログインが完了すると以下のようなNotebook画面になります。
Sageを使うときには、クイックレファレンス注3を印刷して傍らに置いておくと便利です。
ワークシートの基本操作は、以下のように行います。
ワークシートで数式を評価するには、セルと呼ばれるテキストエリアを利用します。
セルの操作には、以下の種類があります。
sageの関数は、関数名の後に?を付けてshit-enterでヘルプが表示されます。?を2個付けるとさらに詳しいヘルプが表示されます。
|
関数名やpythonの変数の属性は、タブキーで補完することができます。
以下の例では、plを入力した後にタブキーを入力すると、以下のような 補完の候補が表示されます。マウスまたはカーソルで選択してください。
|
sageを実行する際に必要な表記に関する約束事のうち、代表的なものを以下に示します。
sageは、pythonのインタプリタを使っているため、pythonの構文がそのまま使えます。
pythonでのコメント(実行に影響しない注釈)は、#記号から行末がコメントとして扱われます。
6 6 |
複数の式を1行にまとめるには、式を;(カンマ)で区切ります。
1 1 |
計算結果をpython変数に代入するときには、= (イコール)を使います。 python変数と断っているのは、sageの数式で使われる変数と区別するためです。
変数aに$\frac{1}{2} + \frac{1}{3}$の結果を代入します。結果は、$\frac{5}{6}$と分数で表示されます。 aの値を数値として出力するには、Nまたはn関数を使います。
5/6 0.833333333333333 5/6 0.833333333333333 |
タプルは、カンマで区切られた値からなるシーケンスデータ型です。 タプルの出力は()カッコで括られたカンマ区切りの形式で表示されます。
タプルの変数への代入をタプル・パッキングと呼び、逆にタプルから変数への代入をシーケンス・アンパッキングと呼びます。
(1, 2, 3) 1 2 3 (1, 2, 3) 1 2 3 |
sageでは、リストがもっとも重要なデータ構造になります。 リストの操作を以下に簡単にまとめます。(範囲指定では、終了インデックスを含めません。)
[1, 2, 3] [1, 2, 3, 4] 3 [2, 3] [3, 4] [1, 2] [1, 2, 3, 4, 9, 8, 7] [1, 2, 3] [1, 2, 3, 4] 3 [2, 3] [3, 4] [1, 2] [1, 2, 3, 4, 9, 8, 7] |
範囲生成関数rangeを使うと簡単に指定した範囲のインデックスのリストを生成する ことができます。
[0, 1, 2] [1, 2, 3] [-1, 1, 3, 5] [0, 1, 2] [1, 2, 3] [-1, 1, 3, 5] |
[1, 2, 3, 4] ['a', 'b', 'c', 'd'] [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')] 10 24 [1, 2, 3, 4] ['a', 'b', 'c', 'd'] [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')] 10 24 |
リスト内包は、式とそれにつづくfor節からなり、各要素に対し、式を評価した結果をリストとして返します。 最初は分かりにくいですがリストをコンパクトに生成できるので重宝します。
[1, sqrt(2), sqrt(3), 2]
[1.0000, 1.4142, 1.7320, 2.0000]
\newcommand{\Bold}[1]{\mathbf{#1}}\left[1, \sqrt{2}, \sqrt{3}, 2\right]
[1, sqrt(2), sqrt(3), 2]
[1.0000, 1.4142, 1.7320, 2.0000]
\newcommand{\Bold}[1]{\mathbf{#1}}\left[1, \sqrt{2}, \sqrt{3}, 2\right]
|
ディクショナリは、別名「連想配列」と言われ、リストのインデックスの代わりにキーで値を取得することが できます。
{'Sarah': 28, 'Mike': 31, 'John': 24} 28 {'Sarah': 28, 'Mike': 31, 'John': 24} 28 |
sageの式で変数として認識させるには変数をvar関数で宣言しなくてなりません。変数の宣言は、
var('変数名')複数の変数を宣言する場合には、スペースを空けて指定します。 宣言される変数を参照したりする場合には、
x, y = var('x y')のようにpython変数x, yに宣言したsage変数を代入します。
sageの関数には、以下の3種類の指定方法があります。必要に応じて使い分けて下さい。
8 8 8 (x + y)^3 (x + y)^3 (x + y)^3 8 8 8 (x + y)^3 (x + y)^3 (x + y)^3 |
変数の値を指定して関数の値を取得するには、以下の3つの方法があります。
2*x + 1 2*x + 1 x |--> 2*x + 1 2*x + 1 2*x + 1 x |--> 2*x + 1 |
規則の代入には、subs_expr関数を使います。例として、関数hの$x^2$をwに置き換えてみます。
x^2 + 1 w + 1 x^2 + 1 w + 1 |
直前の実行結果を _ で参照することができます。
[1, 2, 3] [1, 2, 3] |
6 6 |
|
sageで使われる基本的な計算の表現方法を以下に示します。
|
数式処理システムの恩恵は、中学や高校の数学で悩まされた計算から解放?されることです。 まずは、
(x - 1)*(x^2 - 1) x^3 - x^2 - x + 1 (x - 1)^2*(x + 1) 2*x/((x - 2)*(x + 2)) I (x - 1)*(x^2 - 1) x^3 - x^2 - x + 1 (x - 1)^2*(x + 1) 2*x/((x - 2)*(x + 2)) I |
三角関数や指数関数を含む式の簡単化には simplify_full関数を使います注5。
-sin(x)^2 + cos(x)^2 2*cos(x)^2 - 1 -sin(x)^2 + cos(x)^2 2*cos(x)^2 - 1 |
数式処理システムのもう一つの便利な機能にグラフ表示があります。関数の形を可視化することに よってその性質を把握することができます。
plot関数の呼び出しは、以下のように指定します。
plot(関数, [変数名, 最小, 最大], オプション)オプションは、省略可能です。plotのオプションは、plot.optionsで知ることができます。 それ以外にもGraphicsのオプションも使えます。よく使うオプションは、
plotの例として、$f(x) = \frac{sin(x)}{x}$を-100から100の範囲で表示してみます。
|
グラフをみて、おやっと思われた方もいると思いますが、$f(x) = \frac{x}{sin(x)}$ は、$x = 0$で分母が0になりエラーとなるはずですが、plotは、limitを計算して表示しています。 これが、数式処理システムのグラフ化の強みです。
1 1 |
計算結果の表示の他に、補足説明などのために基本図形を表示したい場合があります。 以下によく使う基本図形(グラフオブジェクト)を示します。
|
リストの値をプロットするには、list_plot関数を使用します。list_plotの使い方は以下の通りです。
list_plot(プロットするリスト, [オプションにはxmax, ymax, plotjoined等を指定])
|
2次元グラフと同様に3次元のグラフを表示することができます。 表示された図形はマウスで自由に回転することができます。(驚きました)
Sleeping...
![]() |
その他の表示方法としては、
数式処理システムのありがたい機能の一つに、方程式の解法があります。
方程式の解法は、solve関数を使って以下のように行います。
solve(解きたい方程式また方程式のリスト, 解を得る変数)例として、$f(x)=ax+b$ の一次方程式を解いてみます。解として$x = \frac{−b}{a}$ が得られたので、$a=2, b=1$を代入して解の値を取得します。
\newcommand{\Bold}[1]{\mathbf{#1}}[
x == -b/a
]
x == (-1/2)
\newcommand{\Bold}[1]{\mathbf{#1}}[
x == -b/a
]
x == (-1/2)
|
多項式の解法を例に、解法、グラフ化、数値解の求め方を説明します。
多項式 $$ f(x) = x^3 - 2 x + 4 $$ をプロットし、方程式の解と数値解を比べてみます。数値解には、find_root関数を使用します。
|
\newcommand{\Bold}[1]{\mathbf{#1}}[
x == (-I + 1),
x == (I + 1),
x == -2
]
-1.9999999999999949
\newcommand{\Bold}[1]{\mathbf{#1}}[
x == (-I + 1),
x == (I + 1),
x == -2
]
-1.9999999999999949
|
方程式のリストをsolve関数に与えることで、連立方程式の解を得ることができます。
solve関数を使って、次の連立方程式を解いてみましょう。 多項式と同様にグラフを使った解法も試してみます。 $$ \begin{eqnarray} x^2 - 2y & = & 2 \\ x - y & = & 1 \end{eqnarray} $$
\newcommand{\Bold}[1]{\mathbf{#1}}[ [x == 2, y == 1], [x == 0, y == -1] ] \newcommand{\Bold}[1]{\mathbf{#1}}[ [x == 2, y == 1], [x == 0, y == -1] ] |
微分は、diff関数を使って以下のように行います。
diff(関数, 微分する変数)いろんな関数の微分を以下に示します。
|
積分には、integrate関数を使用します。sageでのintegrate関数の使い方は、以下の 通りです。
integrate(被積分関数, 積分変数) また integrate(被積分関数, 積分変数, 積分範囲) とすると定積分を計算します。いろんな関数の積分を以下に示します注7。
|
定積分の例として、$\int_0^{\pi/2}sin(x)dx$を計算してみます。
1 1 |
数値積分には、numerical_integral関数を使用します。
numerical_integral(関数, 積分範囲)
数値積分の例として、サイクロイドを計算してみます。 サイクロイドは、tをパラメータとして、以下のような式で表されます。 $$ \begin{eqnarray} x & = & 2(t-sin(t)) \\ y & = & 2(1-cos(t)) \end{eqnarray} $$ この曲線の長さを計算すると、 $$ \int_{0}^{2\pi}\sqrt{\left(\frac{dx}{dt}\right)^2+\left(\frac{dy}{dt}\right)^2}dt $$ 解析解では、この結果は16となります。
|
(15.999999999999998, 1.7763568394002502e-13) (15.999999999999998, 1.7763568394002502e-13) |
ベクトルは、vector関数で生成します。
vector(値のリスト) または、 vector(環, 値のリスト)よく使われる環を以下に示します。
ベクトルの生成例として、$\mathbf{v}=(2,1,3),\mathbf{w}=(1,1,−4)$
(2, 1, 3) (1, 1, -4) (2, 1, 3) (1, 1, -4) |
ベクトルの内積$$\mathbf{v}\cdot\mathbf{w}$ は、dot_product関数で計算します。
また、ベクトルの内積は、 $$ \mathbf{v}\cdot\mathbf{w} = |\mathbf{v}||\mathbf{w}|cos(\theta) $$ と表現され、2つのベクトルの角度$cos(\theta)$を知るために利用されます。
-9 (-9) -9 (-9) |
ベクトルの外積$\mathbf{v}\times\mathbf{w}$は、cross_product関数で計算します。
ベクトルの外積の大きさは、 $$ |\mathbf{v}\times\mathbf{w}| = |\mathbf{v}||\mathbf{w}|sin(\theta) $$ であり、その方向は、ベクトルvからベクトルwにねじを回して進む方向となります。
(-7, 11, 1) (-7, 11, 1) |
行列は、matrix関数で生成します。
matrix(行列の要素のリスト) または matrix(環, 行列の要素のリスト)
[1 2 3] [3 2 1] [1 2 1] [1 2 3] [3 2 1] [1 2 1] |
特殊な行列として、
identity_matrixの呼び出し形式は以下の通りです。
identity_matrix(対角要素の数)
diagonal_matrixの呼び出し形式は以下の通りです。
diagonal_matrix(対角要素のリスト)
I= [1 0 0] [0 1 0] [0 0 1] D= [1 0 0] [0 2 0] [0 0 3] I= [1 0 0] [0 1 0] [0 0 1] D= [1 0 0] [0 2 0] [0 0 3] |
ベクトル、行列も基本演算子+, -, *を使って和、差、積を計算することができます。
(0, -4, 0) (3, 2, -1) (0, -4, 0) (3, 2, -1) |
転置行列は、要素の行と列を入れ替えた行列です。転置行列は、transpose関数で取得できます。
転置行列の性質には、
Aの転置行列= [1 3 1] [2 2 2] [3 1 1] vの転置行列= [2] [1] [3] Aの転置行列= [1 3 1] [2 2 2] [3 1 1] vの転置行列= [2] [1] [3] |
逆行列は、行列$\mathbf{A}$とその逆行列$\mathbf{A}^{-1}$を掛けると単位行列になる行列です。 逆行列は、inverse関数で取得できます。
逆行列の性質には、
[ 0 1/2 -1/2] [-1/4 -1/4 1] [ 1/2 0 -1/2] [ 0 1/2 -1/2] [-1/4 -1/4 1] [ 1/2 0 -1/2] |
行列式は、det関数で取得できます。行列式は逆行列の計算に使われるので、逆行列が存在するか否かの判別に使用されます。
8 8 |
solve_right関数を使って行列の解を得ることができます。 $$ \mathbf{Y} = \mathbf{A}\mathbf{X} $$ となるようなXを求めるのが、solve_right関数です。
(-3/2, 0, 1/2) (0, -4, -1) (-3/2, 0, 1/2) (0, -4, -1) |
応用編では、
計量経済学では、経済の構造をモデルで表現し、モデルの要素間の 相互関係を連立方程式で表し、過去の経済統計データからモデルの 係数を求め、未来の予測を行います。
ここでは、「MathematicaによるOR」4章で紹介されているモデル と経済データを使って、sageの活用例を紹介します。
以下のモデルでは、矩形を要素としY:所得、C:消費、I:投資、 G:政府支出とし、矢印の方向は影響与える向きを表します。 消費は自分自身へ矢印があり、これは前年度の消費が今年度の 消費に影響を与えていることを表しています。
このモデルを連立方程式で表すと、以下のようになります。 $$ \begin{array}{l c l} C_t & = & a_0 + a_1 Y_t + a_2 C_{t-1} \\ I_t & = & b_0 + b_1 Y_t \\ Y_t & = & C_t + I_t + G_t \\ \end{array} $$
sageでは、統計情報の分析に有益なRパッケージとのインタフェースを 持っています。 ここでは、Rのデータ取り込みを関数であるread.table関数を 使ってデータを読み込み、取り込んだデータをsageの形式に変換する方法を紹介 します。
Rとのインタフェースは、以下のようになっています。
r("Rのコマンド")Rでデータを読み込むコマンドは、read.table関数でオプションにheader=Tを 付けて最初の行がヘッダであることを知らせます。
read.table('入力ファイル名' [,オプション] )ファイル名に'%s'とし、%filenameをコマンド文字列の後に付けることで %sが変数filenameの値に置き換えれます。
経済データは、Econometrics.txtファイルに入っています注8。 最初の1行がYear, Ct, Yt, It, Gtのヘッダで、その後に 1965年から1989年までの各年度の消費、所得、投資、政府支出 の値が続きます。
Year Ct Yt It Gt 1 1965 57176.9 89662.8 10325.1 26408.3 2 1966 63042.7 100033.0 12874.9 29014.0 3 1967 69239.4 110974.0 16394.3 32237.9 4 1968 75771.6 125997.0 19831.2 36869.6 5 1969 83092.1 141402.0 25595.8 39998.9 6 1970 88847.4 153915.0 28909.3 45352.6 7 1971 94196.4 161688.0 27715.0 47546.6 8 1972 103848.0 176628.0 29409.4 53396.2 9 1973 110290.0 184569.0 33396.1 56225.9 10 1974 111694.0 183798.0 30345.5 52550.3 11 1975 115765.0 190875.0 29288.2 53985.8 12 1976 120366.0 199630.0 29478.3 56413.9 13 1977 125394.0 210235.0 29722.5 60068.7 14 1978 133154.0 221243.0 32393.5 64567.0 15 1979 140184.0 232878.0 35569.1 65560.4 16 1980 141398.0 242131.0 38293.4 63846.2 17 1981 144257.0 250159.0 39917.0 64394.4 18 1982 150360.0 258241.0 40698.0 64297.5 19 1983 154910.0 267700.0 42711.7 63274.3 20 1984 158910.0 281399.0 47631.2 64695.8 21 1985 163395.0 293982.0 53899.5 64404.0 22 1986 169016.0 301846.0 56236.0 68598.6 23 1987 176556.0 318109.0 61897.9 74350.9 24 1988 185318.0 334969.0 72617.1 76545.4 25 1989 191233.0 351877.0 84584.1 77764.1 Year Ct Yt It Gt 1 1965 57176.9 89662.8 10325.1 26408.3 2 1966 63042.7 100033.0 12874.9 29014.0 3 1967 69239.4 110974.0 16394.3 32237.9 4 1968 75771.6 125997.0 19831.2 36869.6 5 1969 83092.1 141402.0 25595.8 39998.9 6 1970 88847.4 153915.0 28909.3 45352.6 7 1971 94196.4 161688.0 27715.0 47546.6 8 1972 103848.0 176628.0 29409.4 53396.2 9 1973 110290.0 184569.0 33396.1 56225.9 10 1974 111694.0 183798.0 30345.5 52550.3 11 1975 115765.0 190875.0 29288.2 53985.8 12 1976 120366.0 199630.0 29478.3 56413.9 13 1977 125394.0 210235.0 29722.5 60068.7 14 1978 133154.0 221243.0 32393.5 64567.0 15 1979 140184.0 232878.0 35569.1 65560.4 16 1980 141398.0 242131.0 38293.4 63846.2 17 1981 144257.0 250159.0 39917.0 64394.4 18 1982 150360.0 258241.0 40698.0 64297.5 19 1983 154910.0 267700.0 42711.7 63274.3 20 1984 158910.0 281399.0 47631.2 64695.8 21 1985 163395.0 293982.0 53899.5 64404.0 22 1986 169016.0 301846.0 56236.0 68598.6 23 1987 176556.0 318109.0 61897.9 74350.9 24 1988 185318.0 334969.0 72617.1 76545.4 25 1989 191233.0 351877.0 84584.1 77764.1 |
読み込んだデータdをsageのデータ形式に変換するには、_sage_() 関数を呼び出します。 Rインタフェースの_sage_関数の仕様で、変換されたデータdsの 'DATA'要素にヘッダをキーとした列データのディクショナリが セットされます。 'DATA'要素からCt, Yt, It, Gtをキーとして消費、所得、投資、 政府支出の列データを取り出します。
|
連立方程式のCt, Itの係数$a_0, a_1, a_2, b_0, b_1$の値を回帰分析を 使って求めます。
CtList, YtList, GtListには、1966年から1984年のデータをセットし、 Ct1Listには、1年ずらしたCtの値をセットします。
|
回帰分析には関数find_fitを使います。 find_fit関数の使い方は、以下の通りです。
find_fit(data, model, options) data: 変数1の値, 変数2の値, ... , 変数nの値, 関数の値をタプルとするリストをセット model: model(変数1の値, 変数2の値, ... , 変数nの値)の引数を持つ関数をセット options: solution_dict=Trueを指定すると係数名をキーとする辞書が戻されます
zip関数を使って各年度のYt, Ct1, Ctをタプルにまとめたリストを作成します。
|
変数Yt, Ct1, a0, a1, a2とモデルCtModelを定義します。CtModelの式は、 連立方程式の$C_t = a_0 + a_1 Y_t + a_2 C_{t-1}$の部分です。
{a0: 7518.3096037375299, a1: 0.21857976971146531, a2: 0.59268191617908206} {a0: 7518.3096037375299, a1: 0.21857976971146531, a2: 0.59268191617908206} |
消費と同様に、変数Yt, b0, b1とモデルItModelを定義します。 CtModelの式は、連立方程式の$I_t = b_0 + b_1 Y_t$の部分です。
{b0: -11736.272856134668, b1: 0.22718320402830394} {b0: -11736.272856134668, b1: 0.22718320402830394} |
係数a0, a1, b2, b0, b1が決まったので、solve関数を使って連立方程式 を解きます。
係数を入力して式を定義する代わりに、
CtEq = (Ct == CtModel(Yt, Ct1)).subs(CtFit)のようにsubs関数を使ってCt == a0 + a1*Yt + a2*Ct1のa0, a1, a2を代入しています。 これで、モデルを変更しても再計算が楽にできます。 解の結果、Ct, It, YtはGtとCt1からなる式に変換されています。
[Ct == 0.592681916179082*Ct1 + 0.218579769711465*Yt + 7518.30960373753, It == 0.227183204028304*Yt - 11736.2728561347, Yt == Ct + Gt + It] [Ct == 0.592681916179082*Ct1 + 0.218579769711465*Yt + 7518.30960373753, It == 0.227183204028304*Yt - 11736.2728561347, Yt == Ct + Gt + It] |
[{It: 163953670168/674867923015*Ct1 + 115022920/280610363*Gt - 872829386902217/64820993853, Yt: 721680419112/674867923015*Ct1 + 506300280/280610363*Gt - 164437808222620/21606997951, Ct: 557726748944/674867923015*Ct1 + 110666997/280610363*Gt + 379515962234357/64820993853}] [{It: 163953670168/674867923015*Ct1 + 115022920/280610363*Gt - 872829386902217/64820993853, Yt: 721680419112/674867923015*Ct1 + 506300280/280610363*Gt - 164437808222620/21606997951, Ct: 557726748944/674867923015*Ct1 + 110666997/280610363*Gt + 379515962234357/64820993853}] |
zip関数を使って前年度消費と政府支出をタプルとするリストを作成します。
|
連立方程式からCt式を取り出し、前年度消費Ct1と政府支出Gtを引数とするctFuncを定義します。
(Ct1, Gt) |--> 557726748944/674867923015*Ct1 + 110666997/280610363*Gt + 379515962234357/64820993853 (Ct1, Gt) |--> 557726748944/674867923015*Ct1 + 110666997/280610363*Gt + 379515962234357/64820993853 |
dataの各タプルに対し、リスト内包を使ってctFunc関数から消費の計算結果のリストを作成し、calCtListにセットします。
|
list_plotを使って計算結果(青い線)と実データ(赤い点)をプロットします。 単純なモデルの割には計算値と実データが合っています。
|
同様に投資データ、所得データを計算します。
|
計量経済モデルの目標である、未来の予測をしてみましょう。
問題を簡単にするために、政府支出Gtを$G_t(x) = c_0 + c_1 x$として回帰分析をします。
{c0: 35741.149903570993, c1: 1960.3166656122035} {c0: 35741.149903570993, c1: 1960.3166656122035} |
直線回帰で求めた政府支出と実データをプロットすると以下のようになります。
|
政府支出関数gtFuncを使って、1985年から1989年まで政府支出を求め、前年度の消費Ct1List の値とgtFuncの値から消費、投資、所得を順番に計算します。
予測した消費を1985年から1989年までの実データと重ねてプロットします。 ラフな政府支出のモデルを使った割にはうまく予測できています。
|
同様に投資と所得の予測と実データを重ねてプロットします。
|
このようにsageを使うと簡単に計量経済モデルを計算をすることができます。 モデルやデータの変更も容易なので、モデルの改良も短時間にできます。
また、sageのノートブックは計算だけではなく、手順などの説明も付け加えることが できるので、動く解説書のような役割もします。
本稿を執筆するにあたり、永山純一さん、戸張一夫さん、鈴木由宇さんには原稿に 対し貴重なコメントをいただきました。この場を借りて感謝申し上げます。
|