Cooley-TurkeyのFFTについて

始めに

FFTについて、勉強する機会があったため備忘録に記事を書こうと思いました。

問題設定

離散フーリエ変換を高速に行うアルゴリズムです。 しかし、今回は次の問題を考えることにし信号処理的な背景をすべて忘れることにします。

2つの数列$A_0,\ldots,A_{N - 1}$と$B_0,\ldots,B_{M - 1}$が与えられるとする。 この時、次の数列$C_k,(0\leq k \leq N + M - 2)$を求めよ。

\begin{align} C_k = \sum_{i + j = k} A_i B_j \end{align}

実は、次の多項式を使った表現と同じ問題になります。

入力として、2つの多項式

\begin{align} F(x) = \sum_{k = 0}^{N - 1} A_k x^k, G(x) = \sum_{k = 0}^{M - 1} B_k x^k \end{align}

が与えられるとする。この時、$(F\cdot G)(x)$としてできる多項式の係数$C_k$を求めよ。$F \cdot G$を多項式の掛け算とします。

これは、多項式の $x^k $ の係数は、$i + j = k$ となる $F(x)$ の $x^i$ の係数と $G(x)$ の $x^j $の係数に関する積和になるからです。

計算の概要

多項式の積は、ナイーブに計算を行えば$O(NM)$で計算できますが、これを$O((N + M) \log{(N + M)})$で高速化に計算を行うのがFFTです。

では、どう高速に計算を行うのでしょうか。次のステップを踏むことにより高速に計算を行うことができます。 ここで$L = N + M - 1$とし、$\omega = \exp(2\pi i / L)$とします。$i$は虚数単位です。

  1. 多項式から$F(\omega^0), F(\omega^1), \ldots, F(\omega^{L - 1})$と、$G(\omega^0), G(\omega^1), \ldots, G(\omega^{L - 1})$を計算する。
  2. $F(\omega^0), F(\omega^1), \ldots, F(\omega^{L - 1})$と、$G(\omega^0), G(\omega^1), \ldots, G(\omega^{L - 1})$から、$(F\cdot G)(\omega^0), (F\cdot G)(\omega^1), \ldots, (F\cdot G)(\omega^{L - 1})$を計算する。
  3. $(F\cdot G)(\omega^0), (F\cdot G)(\omega^1), \ldots, (F\cdot G)(\omega^{L - 1})$から、$F\cdot G$(の係数)を計算する。

ここで、1, 3のステップで、FFTと逆FFTを用いることができます。ここの計算量は$O(L\log L)$となります。 また、2のステップは$O(L)$で計算が可能です。

ステップ2に関しては$(F\cdot G)(\omega^k) = F(\omega^k)\cdot G(\omega^k)$と計算が可能です。これは、次の式から計算が可能です。

\begin{align} (F\cdot G)(\omega^k) &= \sum_{k = 0}^{L - 1}\sum_{i + j = k}A_i B_j \omega^k\\ &= \sum_{k = 0}^{L - 1}\sum_{j = 0}^kA_j \omega^j B_{k - j} \omega^{k - j}\\ &= \sum_{j = 0}^{L - 1} \sum_{k = j}^{L - 1} A_j \omega^j B_{k - j} \omega^{k - j}\\ &= \sum_{j = 0}^{L - 1} A_j \omega^j \sum_{k = j}^{L - 1} B_{k - j} \omega^{k - j}\\ &= \sum_{j = 0}^{L - 1} A_j \omega^j \sum_{l = 0}^{L - j - 1} B_l\omega^l\\ &= \sum_{j = 0}^{L - 1} A_j \omega^j \sum_{l = 0}^{L - 1} B_l\omega^l \end{align}

最後の行では、

\begin{align} \sum_{j = 0}^{L - 1} A_j \omega^j \sum_{l = L - j}^{L - 1} B_l\omega^l \end{align}

という値を加算しています。この加算した値について、$L = N + M - 1$と設定したため、$j \geq N$または$l \geq M $のいずれかが成り立ちます。 ここで元の多項式の定義から、$A_j$または$B_l$が$0$になります。

FFTについて

残っているステップとして、 多項式から$F(\omega^0), F(\omega^1), \ldots, F(\omega^{L - 1})$を計算する部分が残っています。 ここで計算の都合上$L = 2^h$とします。 これは必要なら$L$を大きくすることで、今までの議論の一般性を失わせないです。

FFTでは、これを分割統治法によって解くことができます。

$L=1$の時は、多項式の係数$A_0$が答えです。以下では、$L>1$の時を考えます。

次のように多項式を定義します。

\begin{align} E(x) = \sum_{j = 0}^{L/2 - 1}A_{2j}x^j\\ O(x) = \sum_{j = 0}^{L / 2 - 1} A_{2j+1}x^j\\ \end{align}

これに対して、$E(\omega^{0}), E(\omega^{2}),\ldots, E(\omega^{L - 2})$と$O(\omega^{0}), O(\omega^{2}),\ldots, O(\omega^{L - 2})$が計算できたと仮定します。

これらの2つの数列を用いて、$F(\omega^0), F(\omega^1), \ldots, F(\omega^{L - 1})$を計算することを考えます。 実は、この計算は次のように計算できます。

\begin{align} F(\omega^k) = \left\{\begin{matrix} E(\omega^{2k}) + \omega^kO(\omega^{2k}) & (k\leq L /2 - 1\text{のとき})\\ E(\omega^{2k - L}) - \omega^{k - L/2}O(\omega^{2k - L}) & (k\geq L /2\text{のとき})\\ \end{matrix}\right. \end{align}

これは下の式によって確かめることができます。

$k \leq L / 2 - 1$の時、

\begin{align} F(\omega^k) &= \sum_{j = 0}^{L - 1} A_j (\omega^k)^j\\ &= \sum_{j = 0}^{L/2 - 1}A_{2j}(\omega^k)^{2j} + \sum_{j = 0}^{L / 2 - 1} A_{2j+1}(\omega^k)^{2j + 1}\\ &= \sum_{j = 0}^{L/2 - 1}A_{2j}(\omega^{2k})^j + \omega^k \sum_{j = 0}^{L / 2 - 1} A_{2j+1}(\omega^{2k})^j\\ &= E(\omega^{2k}) + \omega^k O(\omega^{2k}) \end{align}

となります。

また、 $k \geq L / 2 $の時、

\begin{align} F(\omega^k) &= \sum_{j = 0}^{L - 1} A_j (\omega^k)^j\\ &= \sum_{j = 0}^{L/2 - 1}A_{2j}(\omega^k)^{2j} + \sum_{j = 0}^{L / 2 - 1} A_{2j+1}(\omega^k)^{2j + 1}\\ &= \sum_{j = 0}^{L/2 - 1}A_{2j}(\omega^{2k})^j + \omega^k \sum_{j = 0}^{L / 2 - 1} A_{2j+1}(\omega^{2k})^j\\ &= \sum_{j = 0}^{L/2 - 1}A_{2j}(\omega^k)^{2j}\omega^{-Lj} -\omega^{-L/2} \omega^k \sum_{j = 0}^{L / 2 - 1} A_{2j+1}(\omega^k)^{2j}\omega^{-Lj}\\ &= \sum_{j = 0}^{L/2 - 1}A_{2j}(\omega^{2k - L})^j -\omega^{k - L/2} \sum_{j = 0}^{L / 2 - 1} A_{2j+1}(\omega^{2k - L})^j\\ &= E(\omega^{2k - L}) - \omega^{k - L/2} O(\omega^{2k - L}) \end{align}

となります。ここで、$\omega^{-L/2} = -1$であり$\omega^{-L}=1$を用いました。

まとめると、$L > 1$の時は次のように計算が可能です。

  1. $F(x)$から$E(x)$と$O(x)$に多項式を分割する。
  2. $E(x)$と$O(x)$に対して、FFTを行う。
  3. 2で得られた結果から$F(x)$のFFTの計算結果を作成する。(マージする。)

FFT

FFTは、$(F\cdot G)(\omega^0), (F\cdot G)(\omega^1), \ldots, (F\cdot G)(\omega^{L - 1})$から、$F\cdot G$(の係数$C_k$)を計算することでした。

実は、この計算はFFTと同じ計算によって解くことができます。 多項式

\begin{align} \tilde{F}(x) = \sum_{j = 0}^{L - 1} (F\cdot G)(\omega^j) x^j \end{align}

と定義します。 この多項式に対して$\tilde{\omega} := \omega^{-1}$としてFFTを計算すると、 $L(F\cdot G)(x)$(の係数)を計算が可能です。$\tilde{\omega}$を用いてFFTができることは上の議論を追うとわかります。

したがって、後は得られた答えに対して、$L$で割った値が求めたい多項式$(F\cdot G)(x)$となっています。

FFTFFTの計算と同じであることは、次の計算で確かめられます。

\begin{align} \tilde{F}(\omega^{-k}) &= \sum_{j = 0}^{L - 1} (F\cdot G)(\omega^j) (\omega^{-k})^j\\ &= \sum_{j = 0}^{L - 1} \left(\sum_{l = 0}^{L - 1}C_l(\omega^j)^l\right) (\omega^{-k})^j\\ &= \sum_{j = 0}^{L - 1}\sum_{l = 0}^{L - 1}C_l\omega^{(l - k)j}\\ &= \sum_{j = 0}^{L - 1}C_k 1 ^j\\ &= LC_k \end{align}

ここで、下から2行目に関する和は、次のことより導けます。

\begin{align} \sum_{j = 0}^{L - 1} \omega^{kj} = \left\{\begin{matrix} L& (k = 0\text{の時})\\ 0 & (\text{otherwise}) \end{matrix}\right. \end{align}

Cooler-TurkeyのFFTについて

以上で積和についての問題を解くことができました。 しかし、FFTの計算において$E,O$という2つの多項式を定義する必要がありました。 これはナイーブな実装上メモリ確保が必要となり、ここをなくしたいという気持ちが残ります。 すなわち、サイズ$L(=2^h)$の配列$A$を用いて、$F$のFFTを計算したいです。

2つの多項式をまとめる際には、

\begin{align} F(\omega^k) = \left\{\begin{matrix} E(\omega^{2k}) + \omega^kO(\omega^{2k}) & (k\leq L /2 - 1\text{のとき})\\ E(\omega^{2k - L}) - \omega^{k - L/2}O(\omega^{2k - L}) & (k\geq L /2\text{のとき})\\ \end{matrix}\right. \end{align}

という更新式に従って更新を行いました。これを見ると、2つの値を使って2つの値を導出する形になっています。 具体的には、すべての$k (0\leq k\leq L/2 - 1)$に対して$F(\omega^k), F(\omega^{k + L/2})$を

\begin{align} F(\omega^k) &= E(\omega^{2k}) + \omega^k O(\omega^{2k})\\ F(\omega^{k + L / 2}) &= E(\omega^{2k}) - \omega^kO(\omega^{2k}) \end{align}

と求めることになります。 また、用いる2つの値は1度使われるとその後は用いることがないです。

したがって、うまいこと計算の順番を考えることで、$O,E$用に新たに配列を確保することなく使いまわすことができます。

ここでは、分割統治法においてマージする段階で簡単になるように配置を考えます。 具体的には、$E,O$のFFTの結果が

\begin{align} A[0:L/2 - 1] = [ E(\omega^{0}), E(\omega^{2}), \ldots, E(\omega^{L - 2})]\\ A[L/2:L - 1] = [ O(\omega^{0}), O(\omega^2), \ldots, O(\omega^{L - 2})] \end{align}

と配置されるように配列を分割することを考えます。 一旦このように配置されれば、次のように$F$のFFTを計算することが可能です。

int b = L / 2;
std::complex<double> z =1;
for (int j = 0; j < L / 2; j++) {
    auto s = A[i];
    auto t = A[i + b] * z;
    A[k] = s + t;
    A[k + b] = s - t;
    z *= omega;
}

さて、上は分割統治法において、最後にマージする部分を考えました。 次は、上のような$O,E$の分割が再帰的にできると仮定して、$A$がいくつか分割されている場合のコードを考えます。ここでは仮に$A$が$2^{k+1}$個に分割されており、$2^{k}$個にまとめることを考えます。

この処理を行うコードは、

int b = L / 2**(k+1);
for (int j = 0; j < b * 2; j += 2*b) {
    std::complex<double> z = 1;
    for (int k = 0; k < b; k++) {
        auto s = A[j + k];
        auto t = A[j + k + b] * z;
        A[j + k] = s + t;
        A[j + k + b] = s - t;
        z *= omega;
    }
}

となります。上のfor文においてomegaが共通する部分の計算を、一括で行うと、

int b = L / 2**(k+1);
std::complex<double> z = 1;
for (int j = 0; j < b; j++) {
    for (int k = 0; k < b*2; k += 2*b) {
        auto s = A[i];
        auto t = A[i + b] * z;
        A[k] = s + t;
        A[k + b] = s - t;
    }
    z *= omega;
}

となります。

最終的に考えないといけない部分は、$F$の係数をどのように配列$A$に配置しなおすかということです。 ここで初めの多項式$F(x)$の係数を$A_0,A_1,\ldots, A_{L - 1}$とする。 これを分割していくと、

\begin{align} \{A_0,A_2,\ldots, A_{L - 2}\}, &\{A_1,A_3,\ldots, A_{L - 1}\}\\ \{A_0,A_4,\ldots, A_{L - 4}\}, \{A_2,A_6,\ldots, A_{L - 2}\},& \{A_1,A_5,\ldots, A_{L - 3}\}, \{A_3,A_7,\ldots, A_{L - 1}\}\\ \vdots& \end{align}

となります。 係数の添字の2進数表記を見ると、1の位が$0,1$かで分け、次は2の位が$0,1$かで分け、……という形になっています。 具体的には、$L = 8$の場合で考えると、次のように係数が分かれています。

\begin{align} \{000, 010, 100, 110 \},& \{001, 011, 101, 111\}\\ \{000, 100\}, \{010, 110 \},& \{001, 101\}, \{011, 111\} \end{align}

この配置する前のindexと配置した後のindexはちょうどbit反転の関係になっています。具体的に$L = 8$の場合では、

0 = 000 -> 000
1 = 001 -> 100
2 = 010 -> 010
3 = 011 -> 110
4 = 100 -> 001
5 = 101 -> 101
6 = 110 -> 011
7 = 111 -> 111

このように係数を配置しなおすコードを書くと

for (int i = 0; i < L; i++) {
    int j = 0;
    for (int k = 0; k < h ;k++) j |= (i >> k & 1) << (h - 1 - k);
    if (i < j) swap(A[i], A[j]);
}

となります。

以上で、分割が終わり、先ほどのマージのコードを、分割の大きさが2の状態から計算を行えば求まります。

終わりに

本来は、もう少し図を入れたほうがわかりやすいだろうけど、今回はこれで終わりにしておきます。

参考

www.creativ.xyz

tiramister.net


残りは気分が向けば書きます。

キャンパス周りを歩こう

はじめに

これはISer Advent Calendar 2022の12/12の記事として書かれました。

この記事では私がキャンパスを歩いて撮った写真を紹介しようと思います。スマホで撮った写真なので特にきれいということはないです。

中身

まずは情報学環のきれいな建物です。現在休業中ですが、一階にはおしゃれそうなカフェがあります。

情報学環のデザイン性のある建物

次に鉄門です。赤門はどんなんか知っているよという人も多いとは思うのですが、鉄門を知っている人は少ないのではないでしょうか。

鉄門

工学部広場の大きな銀杏の木です。この近くにスタバがあります。この店では、スタバの新作は売れ残っている場合が多いです。

大きな銀杏の木

ここが観光客一押しのスポットです。銀杏の落ち葉を手にもって投げた写真を撮っている人が連日見られます。

観光客大人気の場所

三四郎池の滝です。三四郎池の水位が低くないとこのアングルでは取れないと思います。

三四郎池の滝

おそらく三四郎池で最も景色がいい場所です。夜中くる場合、この辺りは明かりがないので池に嵌らないようにしましょう。

三四郎池で最も景色がいい場所 (個人比)

三角広場です。通気性抜群です。自分はときどき、この場所の椅子に座ってコーヒーを飲みながら空を眺めています。

三角広場

図書館前広場です。日が沈んでからのほうが幻想的です。

図書館前広場

思いがけなく紅葉がきれいなところがありました。山上会館の裏手にあります。

紅葉

キャンパスを出まして湯島天満宮です。卒業祈願をしておきました。おみくじも引いたところ大吉だったので大丈夫でしょう。*1

湯島天満宮

枯れたハスがたくさんの池、不忍池(しのばずいけ)です。夏は青々と茂っています。また、隣の池はスワンボートの貸し出しを行っています。いつかは乗ってみたいと思っています。

不忍池

おわりに

いかがでしたか。モニターを見るのに疲れたら、散歩をしてみるのとかはどうでしょうか。

*1:なお、学業の欄には「人に頼ることなかれ」と書かれていました。どうしましょう。

Twitterの罠の一つ

はじめに

これは ISer Advent Calendar 2022 の12/5の記事として書かれました。

アドベントカレンダーの埋まりが悪く、みんなまじめな記事を書こうとして書けなくて嫌がっているのかなあと思っているため、ここでカスみたいな記事を書いてハードルを下げようかなと思いました。*1

これはくそ記事です。この記事を読んだ時間を返せ等の文句は受け付けません。

シチュエーション

次のようなシチュエーションを考えます。

  • 登場人物はA,B,Cさん3人
  • Aは鍵アカウント、Bは公開アカウント
  • AさんBさんは、Cさんに通知を送りたくない。
  • Aさんは、BさんにCさんへの通知を送らせたい。

状況

次の行為を考えます。

  1. AさんがCさんをメンションします。
  2. Bさんが、AさんがCさんをメンションしたツイートに返信します。

この状況でBさんが不注意だとCさんに通知がいきます。これが今回この記事でいう「罠」に当たります。

状況2

つまり、メンションを含んだツイートに対するリプでは通常メンション先も巻き込まれるが、このことが鍵垢に対するリプに対しても行われるということです。さらに、これがAさんが鍵アカウントだと自分のメンションによる通知を送らない状態でできるため、悪質さがあがるという形です。

対策として、Bさんはリプを送る前にちゃんとリプ先を見てCさんを除きましょう。

返信先を選ぶ画面

おわりに

いかがでしたか。Twitterの返信先は、意識しないと気にしない仕様なので、リプする際には気を付けましょう。

また情報科学に限らず、ぜひ、気軽にアドベントカレンダーを書いてみてくださいね。

*1:本当は「latexmkの仕様を備忘録的にまとめる」とか「自分がよく使うMakefileの仕様を備忘録的にまとめる」とか「院試について」とかを考えていましたがやめました。

噂のCPU実験

はじめに

これは ISer Advent Calendar 2021 - Adventar の記事として,大遅刻して書かれました. 基本的には、コア係に関することが書かれています。

何をするのか

レイトレのプログラムをコンパイルして,FPGAに送って実行結果を得ることです.

これを3,4人班で行います.それぞれ係があり,

  • 神 (シミュレータ係ともいう)
  • コンパイラ
  • FPU/メモリ係
  • コア係

となっています.行うべきタスクは次のようなものがあります.

  • ISAの決定
  • アセンブリをシミュレートするシミュレータの作成
  • バイナリの受け取り,結果を返すUARTモジュールの作成
  • ISAを実行するユニットの作成
  • プログラムをコンパイルするコンパイラの作成
  • FPUモジュールの作成
  • MIGの使い方の調査とその使用
  • アセンブリの作成

以上をそれっぽい係の人が担います.

班全体の仕事

ISAの仕様

これはコア係だけでは決定が無理です.班全体で決めるとよさそうですが,主張がある班員が決めればいいと思います.RISCV風にするのが簡単だと思います. (RISCVが簡単なので)

アセンブリの作成

誰が作ってもいいですが,テスト用のバイナリ*1がコア係が1番最初に欲しくなったので骨組みを作りました.しばらくしたら,自分の班はシミュレータ係が拡張やデアセンブリを作っていました.

班によってはコンパイラ係が作ったりしているようです.

コア係の仕事

ISAを実行するユニットの作成

これがメインのお仕事です. 基本的には,シングルサイクル方式,マルチサイクル方式,パイプライン化等の順番に構成していく人が多いですが,始めからパイプラインプロセッサを作っている人もいました.OoOは論文があるらしいですが,余裕がなさそうです.

これは適切にverilogシミュレーションをすればほとんどのバグは消えると思います.

バイナリの受け取り,結果を返すUARTモジュールの作成

これ,私はFIFOの仕様書をちゃんと読まなくて2週間苦しんでいましたが,3Sで作ったやつの使いまわしをすればいいです.

全部をくっつける

メモリについては,配線を指定してメモリ係にキャッシュ回りを作ってもらい,自分がそれを拡張しました. シミュレーションで動かせるようにすると,バグがいくつか取れてよかったです.

おわりに

参考になったのでしょうか.今年から立派なFPGAではないものを使っているので動いている班を見てもあんまり早くはないですね.自分はまだ動いていないので早く動かしたいです.

*1:正確にはバイナリのテキストファイル

TexをVSCodeで使うために

はじめに

これは, ISer Advent Calendar 2021 - Adventarの記事として書かれました.*1

n番煎じの記事です。有用なことを書く気力出ない.

環境

自分はWindows上でTeX LiveとOverleafというサイトを使っています.Macで行うと,Homebrewというパッケージマネージャから簡単に,Linux系だとaptでTeXエンジンを入れられるという噂を聞きました.

Windowsをお使いの方はTeX Liveをインストールすることになると思いますが,どうやらWSLでインストールすると爆速!!!!らしいことをうわさで聞きました.

Windowsに入れる!

TeX Liveでググって*2入れましょう.インストール用の空き容量を作らないと入れられないです!注意!

TeX Live/Windows - TeX Wiki を見ると,isoイメージかネットワークインストーラの選択肢を与えられますが,通信環境と時間の問題で前者がよさそうという気持ちです.

VSCodeで使う!

デフォルトのエディタであるTeXworks editorで記述し ても十分いいと思います.

しかし,私はコードの記述,すべてをVSCodeで行いたいがため,VSCodeTeXを記述するために苦労します.

ステップその1:LaTeX Workshopを入れる

LaTeX Workshop - Visual Studio Marketplace を入れてください.*3シンタックスハイライトやコンパイルの実行をいろいろやってくれます.

ステップその2:LaTeX Workshopの設定

おそらくこれを読んでいる人は日本語でTeXを使いたいと思っている方だと思います.TeXにはいろいろなエンジンが存在して,それぞれいろいろな特徴があります(あるそうです). 興味ある人は,https://oku.edu.mie-u.ac.jp/texconf10/presentations/yato.pdfがよさそうでした.

とりあえず,LuaLaTeXを使うことを目標にします.*4 リファレンスが読める人は Compile · James-Yu/LaTeX-Workshop Wiki · GitHub を読むといいと思います.

VSCodeのsettings.jsonを開きます. 次の設定を加えます.

以下は, Visual Studio Code/LaTeX - TeX Wiki を参考にしました.

    "latex-workshop.latex.autoBuild.run": "never", // これは保存したときに自動でビルドするかどうか
    "latex-workshop.latex.recipe.default": "lastUsed", // どのレシピ(コンパイルオプション)をデフォルトで使用するか
    "latex-workshop.latex.recipe.tools": [ // latex workshop内でのコマンドの定義 ほかのエンジンを使いたいならばここに追加しrecipeをいじる
        {
            "name": "lualatexmk",  // recipeで参照する名前
            "command": "latexmk", // 実際のコマンド
            "args": [ // コマンドオプション
                "-e",
                "$lualatex=q/lualatex %O -synctex=1 -interaction=nonstopmode -file-line-error %S/",
                "-e",
                "$bibtex=q/upbibtex %O %B/",
                "-e",
                "$biber=q/biber %O --bblencoding=utf8 -u -U --output_safechars %B/",
                "-e",
                "$makeindex=q/upmendex %O -o %D %S/",
                "-norc",
                "-gg",
                "-pdflua",
                "%DOC%"
            ]
        }
    ],
    "latex-workshop.latex.recipes": [  // toolsを組み合わせたコンパイルのコマンド
        {
            "name": "lualatex twice", // レシピの名前
            "tools": [
                "lualatexmk",
                "lualatexmk"
            ]
        },
        {
            "name": "lualatex",
            "tools": [
                "lualatexmk",
            ]
        },
    ],

latexmk*5を使って,いろいろしている感じです!!!*6 これで実際につかうことができるはずです.適当なtex拡張子を開いて,次を書き込んでください.

\documentclass[a4paper]{ltjsarticle}

\begin{document}
こんにちは! \LaTeX
\end{document}

ショートカットは,Ctrl+Alt+BでBuild,Ctrl+Alt+VでViewです!ほかのレシピを使いたい場合は,VSCodeLaTeX Workshopのタブを開いてポチポチすると使用できます. これでTeXを使う環境が整いました.

終わりに

TeXなんもわからない

*1:いけなーい!遅刻!遅刻!

*2:死語

*3:おい,TeX Workshopという似たやつを入れかけた

*4:upLaTeXやpLaTeXは,近い将来, 和文文字コードの問題で動かないみたいな話を聞きました.

*5:なんか便利機能らしいが難しいいいい!!!!

*6:なんもわからん

二部グラフとマトロイド

はじめに

ISer Advent Calendar 2021 - Adventarの記事として書かれました.*1

授業で扱わなかったけど,ちゃんと知っておきたいなということで記事にしようと思った.ちゃんと読みたい人は下に載せた参考文献を読んでくれー

マトロイド

マトロイドの定義

マトロイドとは,有限集合 $E$ に対して,有限集合族 $\mathcal{I}\subseteq 2^{E}$ が次を満たすとき,$\mathcal{I}$ はマトロイドという.

  1. $\emptyset \in \mathcal{I} $
  2. $X\in \mathcal{I}\land Y \subseteq X $ ならば, $Y\in\mathcal{I}$
  3. $X,Y\in \mathcal{I}\land |X|>|Y| $ ならば,$ \exists e\in X-Y~s.t.~Y\cup\{e\}\in \mathcal{I} $

このほかにも同値な定義の方法があるのですが,割愛します.

マトロイドの交わり

ここで,マトロイドの交わりを次のように定義する. $\mathcal{I}_1, \mathcal{I}_2$ をマトロイドとする.次の集合をマトロイドの交わりと呼ぶ.

\begin{align} \mathcal{I}_1\cap \mathcal{I}_2 = \{X| X\in \mathcal{I}_1, X\in \mathcal{I}_2 \} \end{align}

これはマトロイドになるとは限らない.

\begin{align} E &= \{1,2,3,4\}\\ \mathcal{I}_1 &= \{\emptyset, \{1\}, \{2\}, \{3\}, \{1,2\}, \{2,3\} \}\\ \mathcal{I}_2 &= \{\emptyset, \{1\}, \{2\},\{3\}, \{4\}, \{1,2\}, \{1,3\}, \{1,4\} \}\\ \mathcal{I}_1\cap \mathcal{I}_2 &= \{\emptyset, \{1\}, \{2\},\{3\},\{1,2\}\} \end{align}

上において,交わりの $\{3\}, \{1,2\}$ にマトロイドの3つ目の定義が成り立たない.

分割マトロイド

次の部分集合族はマトロイドになり,分割マトロイドと呼ぶ.

$E$を有限集合として,$E$ の分割 $\{E_1,E_2,E_3,\dots, E_n \}$ と自然数列 $r_1,\dots, r_n\geq 0$ を考える. この時,次の $\mathcal{I}$ を分割マトロイドと呼ぶ.

\begin{align} \mathcal{I} = \{X\subseteq E | \forall i \in\{1,\dots, n\}.~|X\cap E_i|\leq r_i \} \end{align}

マトロイドになることの証明

分割に関して帰納的に証明する. $\mathcal{I}_1,\mathcal{I}_2$をそれぞれdisjointな有限集合$E_1,E_2$上のマトロイドとする.この時, $\mathcal{I}_1\oplus \mathcal{I}_2=\{X_1\cup X_2 | X_1\in\mathcal{I}_1, X_2\in\mathcal{I}_2 \}$ は$E_1\cup E_2$ 上のマトロイドとなる.

  • $\emptyset$ は$\emptyset\in \mathcal{I}_1,\emptyset\in \mathcal{I}_2$より, $\emptyset \in \mathcal{I}_1\oplus \mathcal{I}_2$

よって,1つ目の性質は示された.

  • $X\in\mathcal{I}_1\oplus \mathcal{I}_2$として,$Y\subseteq X$ とする.
  • この時,$E_1,E_2$は互いにdisjointな集合であるので,$X=X_1\cup X_2, ~(X_1\in E_1, X_2\in E_2)$ が存在する.
  • 同様に,$Y=Y_1\cup Y_2 ~(Y_1\in E_1, Y_2\in E_2)$が存在する.
  • $E_1,E_2$は互いにdisjointな集合であるので,$Y\subseteq X$ より $Y_1\subseteq X_1, Y_2\subseteq X_2$が成り立つ.
  • $\mathcal{I}_1,\mathcal{I}_2$はそれぞれマトロイドであるため,$Y_1\in E_1, Y_2\in E_2$が成り立つ.したがって,$Y=Y_1\cup Y_2 \in \mathcal{I}_1\oplus \mathcal{I}_2$である.

2つ目の性質は示された.

  • $X,Y\in\mathcal{I}_1 \oplus \mathcal{I}_2$ として,$|Y|<|X|$ とする.この時,先ほどと同様の議論より,$X_1,X_2, Y_1, Y_2$が存在し,$X_1,Y_1\in\mathcal{I}_1, X_2,Y_2\in\mathcal{I}_2$を満たす.
  • $E_1,E_2$は互いにdisjointな集合より,$|Y|=|Y_1|+|Y_2|, |X|=|X_1|+|X_2|$ が成り立つ.
  • $|Y|<|X|$ より, $|X_1|>|Y_1|$ または $|X_2|>|Y_2|$のどちらかは成り立つ.ここでは $|X_1|>|Y_1|$ が成り立つとする.
  • マトロイド$\mathcal{I}_1$の3つ目の定義より,$e\in X_1 - Y_1$が存在し,$Y_1\cup \{e\}\in\mathcal{I}_1$である.
  • 以上より,$e\in (X_1\cup X_2) - (Y_1\cup Y_2)$であり,$(Y_1\cup Y_2)\cup\{e\}\in\mathcal{I}_1\oplus \mathcal{I}_2$が成り立つ.

3つ目の性質が示された.

以上より,$\mathcal{I}_1\oplus \mathcal{I}_2$はマトロイドである.この直和を帰納的に使えば,分割マトロイドはマトロイドであることがわかる.

マトロイドによる2部グラフの最大マッチングのモデル化

二部グラフの最大マッチングとは,次の問題である.

グラフ $G$ について頂点集合$V=V_1\cup V_2$,辺集合$E$とする. 問題は,$E$ の部分集合であって端点を共有することはない最大の部分集合を見つけることである.

この問題を言い換えると,$V_1, V_2$ それぞれにおいて各頂点は高々1つの辺にしか接続しないという制約の下での最大共通部分集合である.

$\delta(u)$は頂点$u$に接続する辺集合を表すとする.$V_1, V_2$に関する制約を定式化すると,

\begin{align} \mathcal{I}_1 = \{X\subseteq E | | X\cup \delta(u)|\leq 1~(u \in V_1) \}\\ \mathcal{I}_2 = \{X\subseteq E | | X\cup \delta(u)|\leq 1~(u \in V_2) \} \end{align}

となり,上は分割マトロイドである.

2部グラフの最大マッチングは,マトロイドの交わりを用いて,$\max |X| s.t. X \in \mathcal{I}_1\cap \mathcal{I}_2$と表すことができる.

おわりに

リファレンスならともかく何かを参考にしてまとめることはあんまり好きではない.

参考文献

離散最適化基礎論 (2015年度後学期) 組合せ最適化におけるマトロイドの役割 http://bigdata.nii.ac.jp/eratokansyasai3/wp-content/uploads/2016/09/0810_10_kakimura.pdf

*1:大遅刻

Vivadoで回路を記述する,書き込む

はじめに

初めてVivadoに回路を吐かせるのに2時間ぐらいかかりました.そのとき難しいなと思ったので,記事にしようと思います.

Vivadoで行えること

次の3つを行うのがメインなような気がします.

  • シミュレーション
  • 回路合成と書き込み

今回は下についてのみ書いていきます. また,既存の回路を使うためのIP Catalogや配線をGUIで行えるBlock Design等があります. クロック等を引き込む際には,clock wizardというIPを使うことが多い*1ですが,それは抜きで書いていきます.

回路を記述する

プロジェクトを作った画面は次のようになっていると思います.

プロジェクト制作後の画面

  • Flow Navigator

いろいろな機能を使うためのボタンがたくさんあります.Vivadoの機能を使うためにはここをクリックしたり,右クリックしたりして使うことになります.

  • Sources

ここにVivadoの論理的なディレクトリ構造が書かれます. Verilog等でモジュールを制作していく際は,ここに階層的に現れます.

まず,回路を描くためのファイルを追加することになります.これは,Flow NavigatorのAdd Sourcesから行けます.この後,Add or create design sourceを選択するようにしましょう.

次の画面では,ファイルを加える場合はAdd Filesを選択して,ファイルを作る場合はCreate Fileを選択しましょう.Create Fileする際にいろいろ設定が出てきますが,無視してOkを連打すればいいです.

Add or create Design Sourcesの画面

そして,Finishを押せば,先ほどのSourcesのタブに追加したファイルが表示されると思います.

回路の記述はそのほかの記事や本を見てください.

制約を追加する

英語が読めてDigilentのボードを使っている場合は,Digilentのページを読むといいです.

作ったデザイン(Verilogのモジュール) とボードのIOピン(LED,Clock)等を接続する必要があります. 接続しないと,回路を生成する際にVivadoに作った回路が不要と判断されます.

制約ファイルの追加自体は,Flow Navigator->Add Sources->Add or create constraint fileとすればよいです.

制約の中身ですが,Digilentのボードを使っている場合は,制約のチートファイルみたいなのが,公開されているのでそこからとってきましょう.

このファイルで指定したピンとトップモジュール*2のピンが接続されます.

回路を書き込む

回路を書き込むためには次の手順を踏みます.

  1. Run Synthesis
  2. Run Implementation
  3. Generate Bitstream

これらは,Flow Navigationに対応するボタンをポチポチ押せばできます.

エラーが出なければ,ボードをPCにmicro usb type-b等で接続して,最後にOpen Hardware ManagerでAuto Connectし,Program deviceで.bitファイルを書き込めば終了です.少し雑?

Open Hard managerでAuto Connectしようとしている図

ちなみに,Synthesisした段階で「Open Synthesis Design」で回路図を見ることができます. Implementationした段階では,「Open impemantation design」でFPGAの配線が見れます.

おわりに

あんまり有用な情報ではないかもしれん.

*1:経験が浅くて実際に多いのかは知らない

*2:デザインでトップとなるもの.SourcesのタブでファイルをクリックしてSet as Topをするとトップになる.