Denoising Diffusion Probabilistic Models
はじめに
2020年の以下の論文の式変形を記載する。 arxiv.org
拡散モデル
この論文で考える「拡散モデル」はマルコフ連鎖モデル(未来の状態は現在の状態だけで決まるモデル)である(下図参照)。
1番左の状態はガウスノイズ画像、1番右の状態は物体が明確に写っている画像である。いま、の状態にわずかなガウスノイズを載せた画像を作り、それを状態とする。これを回繰り返すとになるとする。からに至る過程をForward過程(拡散過程)と呼ぶ。一方、からに至る逆向きの過程も考えることができる。この過程は、ガウスノイズ画像が徐々にきれいな画像に変化していく過程である。この過程をReverse過程(生成過程)と呼ぶ。
拡散過程の各ステップでは画像にガウスノイズを載せるので、を次式で与えることができる。
ここで、はベクトル、は単位行列、である。本論文では(ハイパーパラメータ)として程度の値を与えている。が小さな値のとき生成過程もガウス分布関数で近似することができる。
ここで、はとから計算される平均ベクトル、(ハイパーパラメータ)は分散である。拡散モデルでは、をニューラルネットワークを用いて学習する。
変分推論
学習を行うには最小化する関数が必要である。拡散モデルでは次式を最小化する。
ここで、はを表し、はを表す。マルコフ連鎖モデルなので
と書くことができる。これを\eqref{eq1}に代入すると
を得る。ここで、を用いて上式を書き換える。
3行目でイェンセンの不等式を用いた。左辺を最小化するには右辺()を最小化すれば良いので、これ以降の議論ではの最小化を考える。を変形すると
は以下のように書くことができる。
これを式\eqref{eq2}に代入すると
を得る。
上式の両辺にをかけてで積分すると
となる。いま、期待値
を定義すると、上式の右辺は
と書くことができる。を少し変形して
を得る。ここで
が成り立つので(後半の等式はベイズの定理)これを式\eqref{eq3}の右辺第3項に代入すると
となる。式\eqref{eq4}の第2項は
と変形される。従って
を得る。右辺第2項と第4項をまとめて
右辺第1項と第2項を変形して
を得る。式\eqref{eq5}の右辺第2項を変形すると
式\eqref{eq5}の右辺第3項を変形すると
を得る。いま
を定義すると式\eqref{eq5}は次式となる。
この式が論文中の式(5)である。
の計算
式\eqref{eq0}でとおく。
いま
を考え、次式を計算する。
3行目はに依存する項だけを残した。指数関数の肩の]に注目して
ここで
と置いた。はスカラー、はベクトルである。さらに計算すると
を得る。上式を式\eqref{eq6}に代入し、について積分すると
となる。2行目への変形ではに依存する項だけを残した。以上より
が成り立つことがわかる。ここで
とした。同じ計算を繰り返していけば
を得る。これが論文の式(4)である。また、を大きくしていくとは小さくなるのでは標準正規分布に近づいていくことが分かる。
の計算
ベイズの定理より
ここに\eqref{eq0}と\eqref{eq9}を代入する。
2行目ではに依存する項だけを残した。指数関数の肩の]に注目して
ここで
と置いた。はスカラー、はベクトルである。さらに変形すると
ここに上のとを代入すると
となる。ここで
と置いた。上式を指数関数の肩に戻すと
となる。従って
を得る。これらが論文中の式(6)と式(7)に相当する。
最小化すべき関数の計算
最小化すべき関数\eqref{eq7}は以下であった。
右辺第2項は学習から決めるパラメータに依存しないので無視できる。
最初にの第2項を考える。期待値の中のKullback Leibler divergenceを取り出して
ここに
を代入すると
となる。はに依存しない項である。の場合を考えての中を計算すると
上式を\eqref{eq10}に戻してについて積分すると、上式の第1項と第2項はガウス関数と掛け算されて奇関数となるので積分の寄与はゼロになる。以上より
を得る。ただし定数項は落とした。の第2項に戻すと
を得る。これが論文中の式(8)である。先に求めた式
に再パラメータ化トリックを適用して
これをについて解くと
となる。ここで、上で求めた式\eqref{eq11}を考える。
右辺のに式\eqref{eq12}を代入すると
となる。従っては
となる。これが論文中の式(10)である。上式が最小になるのは
のときである。は学習で決めるパラメータであるから上式右辺には学習で決める項がなければならない。そこで、新たな量を導入する。
はとから計算される関数である。上式を式\eqref{eq13}に代入すると
を得る。ここで、は式\eqref{eq14}で与えられていることに注意する(次式)。
式\eqref{eq15}は、標準正規分布で生成したノイズをとを用いて予測する目的関数になっている。式\eqref{eq15}を実際に計算する際は、とでサンプリングして期待値を取る。
さらに、ノルムの係数も落とし、ステップについてもサンプリングを取る。
これが論文中の式(14)である。最後に式\eqref{eq16}の第1項を考える。
は、生成過程の一番最後のステップである。を各画素から構成される次元のベクトルと考える。
最終行では積分範囲を平均値の近傍に制限した(下図参照)。これが論文中の式(13)である(恐らく)。
まとめ
拡散モデルの2020年の論文Denoising Diffusion Probabilistic Modelsの式変形を追ってみた。恐らく誤りや勘違いがあると思うので指摘いただければ幸いです。
QSのAcademic Reputationについて
はじめに
世界大学ランキングのひとつに「QS World University Rankings」がある。 www.topuniversities.com このランキングは6個の指標(後述)の総合点を用いて作られている。今回は6個の中の1つであるAcademic Reputation(学者からの評価)だけを用いて国内の大学を順位付けする。
Academic Reputation
QSに使われる6個の指標の内訳は以下の通り。 今回はこれらの指標のうち、「Academic Reputation」に注目する。世界中の大学の教員に自分の専門分野でトップと目される研究をしている大学名を上げてもらい、その結果をスコア化したものが「Academic Reputation」である。100点満点のスコアである。
国内の大学の順位
Academic Reputationだけで並べ替えたときの国内大学の順位は以下の通り。
上のグラフを見て分かることは以下の通り。
2022年のアジア地域の順位
Academic Reputationだけで並べ替えたときのアジア地域の順位は以下の通り。
2022年の満点大学
2022年のAcademic Reputationで満点をとった大学は以下の通り。
まとめ
どの世界大学ランキングを見ても、日本の大学の地位が低下している。しかし、QSのAcademic Reputationを見る限り学術的評価は高い。旧帝や早慶は、研究大学として学術的評価を伸ばすことを目標にしてほしい。Academic Reputation以外の指標は気にする必要はないと思う。
cpplinqサンプル集
はじめに
ずいぶん昔にGoogleのbloggerに作ったcpplinqのサンプル集が、bloggerの仕様が変わって表示されなくなりました。こちらに転載します。
サンプル集
#include "cpplinq_samples.hpp" #include <cpplinq.hpp> #include <iostream> #include <array> #include <boost/range/algorithm.hpp> #include <random> #include <boost/format.hpp> void sample_where() { std::cout << "* cpplinq::where\n"; auto vs = std::array<int, 5>{}; // make an array of [1,2,3,4,5] std::iota(std::begin(vs), std::end(vs), 1); std::cout << " even: "; cpplinq::from(vs) >> cpplinq::where([](const auto& v){ return v % 2 == 0; }) >> cpplinq::for_each([](const auto& v){ std::cout << v << " "; }); std::cout << std::endl; std::cout << " odd: "; cpplinq::from(vs) >> cpplinq::where([](const auto& v){ return v % 2 == 1; }) >> cpplinq::for_each([](const auto& v){ std::cout << v << " "; }); std::cout << std::endl; } void sample_reference_wrapper() { std::cout << "* std::reference_wrapper/cpplinq::ref\n"; auto ls = std::list<int>(10); // make a list of [-4,-3,...] std::iota(std::begin(ls), std::end(ls), -4); std::cout << " 0: "; boost::for_each(ls, [](const auto& l){ std::cout << l << " "; }); std::cout << std::endl; // Two expressions have the same meaning. auto vs = std::vector<std::reference_wrapper<int>>{ std::begin(ls), std::end(ls)}; // auto vs = cpplinq::from(ls) >> cpplinq::ref() // >> cpplinq::to_vector(); std::shuffle(std::begin(vs), std::end(vs), std::mt19937{std::random_device{}()}); std::cout << " 1: "; boost::for_each(vs, [](const auto& v){ std::cout << v << " "; }); std::cout << std::endl; boost::for_each(ls, [](auto& l){ l *= 2; }); std::cout << " 2: "; cpplinq::from(ls) >> cpplinq::for_each([](const auto& l){ std::cout << l << " "; }); std::cout << std::endl; std::cout << " 3: "; boost::for_each(vs, [](const auto& v){ std::cout << v << " "; }); std::cout << std::endl; } void sample_select() { std::cout << "* cpplinq::select\n"; auto ps = std::vector<std::pair<int, std::string>>{ std::make_pair(1, "sabaton"), std::make_pair(2, "metallica"), std::make_pair(3, "slayer"), }; std::cout << " 0: "; auto vs = cpplinq::from(ps) >> cpplinq::select([](const auto& p){ return p.second; }) >> cpplinq::to_vector(); boost::for_each(vs, [](const auto& v){ std::cout << v << " "; }); std::cout << std::endl; } void sample_select_many() { std::cout << "* cpplinq::select_many\n"; auto vs = std::array<int, 3>{1, 2, 3}; std::cout << " 0: "; cpplinq::from(vs) >> cpplinq::select_many([](const auto& v){ auto dst = std::vector<int>{}; dst.reserve(3); dst.emplace_back(v); dst.emplace_back(10 * v); dst.emplace_back(100 * v); return cpplinq::from(dst);}) >> cpplinq::for_each([](const auto& u){ std::cout << u << " "; }); std::cout << std::endl; } void sample_select_many_flatten() { std::cout << "* cpplinq::select_many(flatten)\n"; auto vss = std::array<std::array<int, 3>, 3>{ {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}} }; std::cout << " 0: "; cpplinq::from(vss) >> cpplinq::select_many([](const auto& vs){ return cpplinq::from(vs);}) >> cpplinq::for_each([](const auto& u){ std::cout << u << " "; }); std::cout << std::endl; } void sample_take() { std::cout << "* cpplinq::take\n"; auto vs = std::array<int, 100>{}; std::iota(std::begin(vs), std::end(vs), 1); std::cout << " 0: "; cpplinq::from(vs) >> cpplinq::take(5) >> cpplinq::for_each([](const auto& v){ std::cout << v << " "; }); std::cout << std::endl; } void sample_orderby_descending() { std::cout << "* cpplinq::orderby_descending\n"; auto vs = std::array<std::pair<int, std::string>, 3>{{ {std::make_pair(3, "tokyo" )}, {std::make_pair(1, "osaka" )}, {std::make_pair(2, "nagoya")}, }}; std::cout << " 0: "; cpplinq::from(vs) >> cpplinq::orderby_descending([](const auto& v){ return v.first; }) >> cpplinq::for_each([](const auto& u){ std::cout << boost::format("(%1%, %2%) ") %u.first %u.second; }); std::cout << std::endl; } void sample_orderby_ascending() { std::cout << "* cpplinq::orderby_ascending\n"; auto vs = std::array<std::pair<int, std::string>, 3>{{ {std::make_pair(3, "tokyo" )}, {std::make_pair(1, "osaka" )}, {std::make_pair(2, "nagoya")}, }}; std::cout << " 0: "; cpplinq::from(vs) >> cpplinq::orderby_ascending([](const auto& v){ return v.first; }) >> cpplinq::for_each([](const auto& u){ std::cout << boost::format("(%1%, %2%) ") %u.first %u.second; }); std::cout << std::endl; } void sample_orderby_true() { std::cout << "* cpplinq::orderby(true)\n"; auto vs = std::array<std::pair<int, std::string>, 3>{{ {std::make_pair(3, "tokyo" )}, {std::make_pair(1, "osaka" )}, {std::make_pair(2, "nagoya")}, }}; // The result is equal to that of orderby_ascending. std::cout << " 0: "; cpplinq::from(vs) >> cpplinq::orderby([](const auto& v){ return v.first; }, true) >> cpplinq::for_each([](const auto& u){ std::cout << boost::format("(%1%, %2%) ") %u.first %u.second; }); std::cout << std::endl; } void sample_orderby_false() { std::cout << "* cpplinq::orderby(false)\n"; auto vs = std::array<std::pair<int, std::string>, 3>{{ {std::make_pair(3, "tokyo" )}, {std::make_pair(1, "osaka" )}, {std::make_pair(2, "nagoya")}, }}; // The result is equal to that of orderby_descending. std::cout << " 0: "; cpplinq::from(vs) >> cpplinq::orderby([](const auto& v){ return v.first; }, false) >> cpplinq::for_each([](const auto& u){ std::cout << boost::format("(%1%, %2%) ") %u.first %u.second; }); std::cout << std::endl; } void sample_thenby() { std::cout << "* cpplinq::thenby\n"; auto vs = std::array<std::pair<int, std::string>, 4>{{ {std::make_pair(3, "tokyo" )}, {std::make_pair(1, "osaka" )}, {std::make_pair(2, "nagoya")}, {std::make_pair(3, "tanaka")}, }}; // The result is equal to that of orderby_descending. std::cout << " 0: "; cpplinq::from(vs) >> cpplinq::orderby([](const auto& v){ return v.first; }, false) >> cpplinq::for_each([](const auto& u){ std::cout << boost::format("(%1%, %2%) ") %u.first %u.second; }); std::cout << std::endl; std::cout << " 1: "; cpplinq::from(vs) >> cpplinq::orderby([](const auto& v){ return v.first; }, false) >> cpplinq::thenby( [](const auto& v){ return v.second; }, true) >> cpplinq::for_each([](const auto& u){ std::cout << boost::format("(%1%, %2%) ") %u.first %u.second; }); std::cout << std::endl; } void sample_take_while() { std::cout << "* cpplinq::take_while\n"; auto vs = std::array<int, 100>{}; std::iota(std::begin(vs), std::end(vs), 1); std::cout << " 0: "; cpplinq::from(vs) >> cpplinq::take_while([](const auto& v){ return v < 5; }) >> cpplinq::for_each([](const auto& v){ std::cout << v << " "; }); std::cout << std::endl; } void sample_skip_while() { std::cout << "* cpplinq::skip_while\n"; auto vs = std::array<int, 10>{}; std::iota(std::begin(vs), std::end(vs), 1); std::cout << " 0: "; cpplinq::from(vs) >> cpplinq::skip_while([](const auto& v){ return v < 5; }) >> cpplinq::for_each([](const auto& v){ std::cout << v << " "; }); std::cout << std::endl; } void sample_join() { std::cout << "* cpplinq::join\n"; struct vocal { int id_; std::string name_; }; struct band { int id_; std::string name_; }; auto bands = std::array<band, 4> {{ {1, "Nightwish"}, {2, "Amaranthe"}, {3, "Epica"}, {4, "Arch Enemy"}, }}; auto vocals = std::array<vocal, 3> {{ {3, "Simone"}, {1, "Floor"}, {4, "Alissa"}, }}; std::cout << " 0: "; cpplinq::from(bands) >> cpplinq::join( cpplinq::from(vocals), [](const auto& band) {return band.id_;}, [](const auto& vocal) {return vocal.id_;}, [](const auto& band, const auto& vocal) { return std::make_pair(band, vocal); }) >> cpplinq::for_each([](const auto& p){ std::cout << boost::format("(%1%, %2%) ") %p.first.name_ %p.second.name_; }); std::cout << std::endl; std::cout << " 1: "; cpplinq::from(vocals) >> cpplinq::join( cpplinq::from(bands), [](const auto& vocal) {return vocal.id_;}, [](const auto& band) {return band.id_;}, [](const auto& vocal, const auto& band) { return std::make_pair(vocal, band); }) >> cpplinq::for_each([](const auto& p){ std::cout << boost::format("(%1%, %2%) ") %p.first.name_ %p.second.name_; }); std::cout << std::endl; } void sample_concat() { std::cout << "* concat\n"; auto vs = cpplinq::range(0, 5); auto us = cpplinq::range(5, 5); std::cout << " 0: "; vs >> cpplinq::concat(us) >> cpplinq::for_each([](const auto& v){ std::cout << v << " "; }); std::cout << std::endl; } void sample_reverse() { std::cout << "* reverse\n"; auto vs = cpplinq::range(0, 5); std::cout << " 0: "; vs >> cpplinq::reverse() >> cpplinq::for_each([](const auto& p){ std::cout << p << " "; }); std::cout << std::endl; } void sample_distinct() { std::cout << "* distinct\n"; auto vs = std::array<int, 9>{5, 4, 3, 2, 1, 2, 3, 4, 5}; std::cout << " 0: "; cpplinq::from(vs) >> cpplinq::distinct() >> cpplinq::for_each([](const auto& p){ std::cout << p << " "; }); std::cout << std::endl; } void sample_union_with() { std::cout << "* union_with\n"; auto vs = std::array<int, 3>{5, 4, 3}; auto us = std::array<int, 3>{3, 5, 1}; std::cout << " 0: "; cpplinq::from(vs) >> cpplinq::union_with(cpplinq::from(us)) >> cpplinq::for_each([](const auto& p){ std::cout << p << " "; }); std::cout << std::endl; std::cout << " 1: "; cpplinq::from(us) >> cpplinq::union_with(cpplinq::from(vs)) >> cpplinq::for_each([](const auto& p){ std::cout << p << " "; }); std::cout << std::endl; } void sample_intersect_with() { std::cout << "* intersect_with\n"; auto vs = std::array<int, 3>{5, 4, 3}; auto us = std::array<int, 3>{3, 5, 1}; std::cout << " 0: "; cpplinq::from(vs) >> cpplinq::intersect_with(cpplinq::from(us)) >> cpplinq::for_each([](const auto& p){ std::cout << p << " "; }); std::cout << std::endl; std::cout << " 1: "; cpplinq::from(us) >> cpplinq::intersect_with(cpplinq::from(vs)) >> cpplinq::for_each([](const auto& p){ std::cout << p << " "; }); std::cout << std::endl; } void sample_except() { std::cout << "* except\n"; auto vs = std::array<int, 3>{5, 4, 3}; auto us = std::array<int, 3>{3, 5, 1}; std::cout << " 0: "; cpplinq::from(vs) >> cpplinq::except(cpplinq::from(us)) >> cpplinq::for_each([](const auto& p){ std::cout << p << " "; }); std::cout << std::endl; std::cout << " 1: "; cpplinq::from(us) >> cpplinq::except(cpplinq::from(vs)) >> cpplinq::for_each([](const auto& p){ std::cout << p << " "; }); std::cout << std::endl; } void sample_to_vector() { std::cout << "* to_vector\n"; std::vector<int> vs = cpplinq::range(0, 3) >> cpplinq::to_vector(); std::cout << " 0: "; boost::for_each(vs, [](const auto& v){ std::cout << v << " "; }); std::cout << std::endl; } void sample_to_list() { std::cout << "* to_list\n"; std::list<int> vs = cpplinq::range(0, 3) >> cpplinq::to_list(); std::cout << " 0: "; boost::for_each(vs, [](const auto& v){ std::cout << v << " "; }); std::cout << std::endl; } void sample_to_map() { std::cout << "* to_map\n"; auto vs = std::vector<std::pair<int, std::string>>{ std::make_pair(1, "tarja"), std::make_pair(2, "annette"), std::make_pair(3, "floor"), }; std::map<int, std::pair<int, std::string>> ms = cpplinq::from(vs) >> cpplinq::to_map([](const auto& v){ return v.first; }); std::cout << " 0: "; boost::for_each(ms, [](const auto& p){ std::cout << "(" << p.first << " " << p.second.second << ") "; }); std::cout << std::endl; } void sample_to_lookup() { std::cout << "* to_lookup\n"; auto vs = std::vector<std::pair<int, std::string>>{ std::make_pair(1, "fear factory"), std::make_pair(2, "soilwork"), std::make_pair(3, "arch enemy"), std::make_pair(3, "scar symmetry"), }; auto ms = cpplinq::from(vs) >> cpplinq::to_lookup([](const auto& v){ return v.first; }); std::cout << " 0: "; ms[3] >> cpplinq::select([](const auto& m){ return m.second; }) >> cpplinq::for_each([](const auto& n){ std::cout << n << " "; }); std::cout << std::endl; } void sample_sequence_equal() { std::cout << "* sequence_equal\n"; auto vs0 = std::vector<int>{1, 2, 3}; auto vs1 = std::vector<int>{1, 2, 3}; auto is_same = cpplinq::from(vs0) >> cpplinq::sequence_equal(cpplinq::from(vs1)); std::cout << " 0: "; std::cout << std::boolalpha << is_same << std::endl; auto vs2 = std::vector<int>{3, 2, 1}; is_same = cpplinq::from(vs0) >> cpplinq::sequence_equal(cpplinq::from(vs2)); std::cout << " 1: "; std::cout << is_same << std::endl; } void sample_first() { std::cout << "* first\n"; auto vs = std::vector<int>{1, 2, 3}; auto f = cpplinq::from(vs) >> cpplinq::first(); std::cout << " 0: "; std::cout << f << std::endl; auto g = cpplinq::from(vs) >> cpplinq::first([](const auto& v){ return v % 2 == 0; }); std::cout << " 1: "; std::cout << g << std::endl; try { auto h = cpplinq::from(vs) >> cpplinq::first([](const auto& v){ return v > 9; }); std::cout << " 2: "; std::cout << h << std::endl; } catch (const std::exception& error) { std::cout << " 3: "; std::cout << error.what() << std::endl; } } void sample_first_or_default() { std::cout << "* first_or_default\n"; auto vs = std::vector<int>{1, 2, 3}; auto f = cpplinq::from(vs) >> cpplinq::first_or_default(); std::cout << " 0: "; std::cout << f << std::endl; auto g = cpplinq::from(vs) >> cpplinq::first_or_default([](const auto& v){ return v % 2 == 0; }); std::cout << " 1: "; std::cout << g << std::endl; auto h = cpplinq::from(vs) >> cpplinq::first_or_default([](const auto& v){ return v > 9; }); std::cout << " 2: "; std::cout << h << std::endl; } void sample_last_or_default() { std::cout << "* last_or_default\n"; auto vs = std::vector<int>{1, 2, 3, 4, 5}; auto f = cpplinq::from(vs) >> cpplinq::last_or_default(); std::cout << " 0: "; std::cout << f << std::endl; auto g = cpplinq::from(vs) >> cpplinq::last_or_default([](const auto& v){ return v % 2 == 0; }); std::cout << " 1: "; std::cout << g << std::endl; auto h = cpplinq::from(vs) >> cpplinq::last_or_default([](const auto& v){ return v > 9; }); std::cout << " 2: "; std::cout << h << std::endl; } void sample_element_at_or_default() { std::cout << "* element_at_or_default\n"; auto vs = std::vector<int>{1, 2, 3, 4, 5}; auto f = cpplinq::from(vs) >> cpplinq::element_at_or_default(0); std::cout << " 0: "; std::cout << f << std::endl; auto g = cpplinq::from(vs) >> cpplinq::element_at_or_default(3); std::cout << " 1: "; std::cout << g << std::endl; auto h = cpplinq::from(vs) >> cpplinq::element_at_or_default(100); std::cout << " 2: "; std::cout << h << std::endl; } void sample_range() { std::cout << "* range\n"; auto start = 10; auto count = 3; std::cout << " 0: "; cpplinq::range(start, count) >> cpplinq::for_each([](const auto& v){ std::cout << v << " "; }); std::cout << std::endl; } void sample_repeat() { std::cout << "* repeat\n"; auto element = "tokyo"; auto count = 3; std::cout << " 0: "; cpplinq::repeat(element, count) >> cpplinq::for_each([](const auto& v){ std::cout << v << " "; }); std::cout << std::endl; } void sample_empty() { std::cout << "* empty\n"; auto is = cpplinq::empty<int>() >> cpplinq::to_vector(); std::cout << " 0: "; std::cout << std::boolalpha << is.empty() << std::endl; auto ss = cpplinq::empty<std::string>() >> cpplinq::to_list(); std::cout << " 1: "; std::cout << std::boolalpha << is.empty() << std::endl; } void sample_singleton() { std::cout << "* singleton\n"; std::cout << " 0: "; cpplinq::singleton(1) >> cpplinq::for_each([](const auto& v){ std::cout << v << std::endl; }); } void sample_generate() { std::cout << "* generate\n"; auto mt = std::mt19937{std::random_device{}()}; auto rnd = std::uniform_int_distribution<>{0, 10}; std::cout << " 0: "; cpplinq::generate([&rnd, &mt](){ auto value = rnd(mt); return (value != 0) ? cpplinq::to_opt(value) : cpplinq::to_opt<int>(); }) >> cpplinq::for_each([](const auto& x){ std::cout << x << " "; }); std::cout << std::endl; } void sample_any() { std::cout << "* any\n"; auto vs = std::array<int, 3>{1, 4, 8}; std::cout << " 0: "; bool flag = cpplinq::from(vs) >> cpplinq::any([](const auto& v){ return v % 2 == 1; }); std::cout << std::boolalpha << flag << std::endl; } void sample_all() { std::cout << "* all\n"; auto vs = std::array<int, 3>{2, 4, 8}; std::cout << " 0: "; bool flag = cpplinq::from(vs) >> cpplinq::all([](const auto& v){ return v % 2 == 0; }); std::cout << std::boolalpha << flag << std::endl; std::cout << " 1: "; flag = cpplinq::from(vs) >> cpplinq::all([](const auto& v){ return v % 2 == 1; }); std::cout << std::boolalpha << flag << std::endl; } void sample_contains() { std::cout << "* contains\n"; auto vs = std::array<int, 3>{2, 4, 8}; std::cout << " 0: "; bool flag = cpplinq::from(vs) >> cpplinq::contains(2); std::cout << std::boolalpha << flag << std::endl; struct info { int id_; std::string name_; }; auto infos = std::vector<info>{ info{3, "tokyo"}, info{2, "osaka"}, info{5, "nagoay"}, }; flag = cpplinq::from(infos) >> cpplinq::contains(infos[0], [](const auto& x, const auto& y){ return x.id_ == y.id_; }); std::cout << " 1: "; std::cout << flag << std::endl; } void sample_count() { std::cout << "* count\n"; auto vs = std::array<int, 5>{1, 2, 3, 4, 8}; auto c = cpplinq::from(vs) >> cpplinq::count(); std::cout << " 0: "; std::cout << c << std::endl; c = cpplinq::from(vs) >> cpplinq::count([](const auto& v){ return v % 2 == 0; }); std::cout << " 1: "; std::cout << c << std::endl; } void sample_sum() { std::cout << "* sum\n"; auto vs = std::array<int, 5>{1, 2, 3, 4, 8}; auto s = cpplinq::from(vs) >> cpplinq::sum(); std::cout << " 0: "; std::cout << s << std::endl; s = cpplinq::from(vs) >> cpplinq::sum([](const auto& v){ return v * v; }); std::cout << " 1: "; std::cout << s << std::endl; } void sample_min() { std::cout << "* min\n"; auto vs = std::array<int, 5>{1, 2, 3, 4, 8}; auto m = cpplinq::from(vs) >> cpplinq::min(); std::cout << " 0: "; std::cout << m << std::endl; m = cpplinq::from(vs) >> cpplinq::min([](const auto& v){ return 2 * v; }); std::cout << " 1: "; std::cout << m << std::endl; } void sample_max() { std::cout << "* max\n"; auto vs = std::array<int, 5>{1, 2, 3, 4, 8}; auto m = cpplinq::from(vs) >> cpplinq::max(); std::cout << " 0: "; std::cout << m << std::endl; m = cpplinq::from(vs) >> cpplinq::max([](const auto& v){ return 2 * v; }); std::cout << " 1: "; std::cout << m << std::endl; } void sample_avg() { std::cout << "* avg\n"; auto vs = std::array<double, 5>{1, 2, 3, 4, 8}; auto m = cpplinq::from(vs) >> cpplinq::avg(); std::cout << " 0: "; std::cout << m << std::endl; m = cpplinq::from(vs) >> cpplinq::avg([](const auto& v){ return 2 * v; }); std::cout << " 1: "; std::cout << m << std::endl; } void sample_pairwise() { std::cout << "* pairwise\n"; auto vs = std::array<double, 5>{1, 2, 3, 4, 8}; std::cout << " 0: "; cpplinq::from(vs) >> cpplinq::pairwise() >> cpplinq::for_each( [](const auto& p){ std::cout << boost::format("(%1%, %2%) ") %p.first %p.second; }); std::cout << std::endl; } void sample_zip_with() { std::cout << "* zip_with\n"; auto vs = std::array<std::string, 3>{"nightwish", "amaranthe", "kamelot"}; auto us = std::array<std::string, 5>{"finland", "sweden", "america", "japan", "canada"}; std::cout << " 0: "; cpplinq::from(vs) >> cpplinq::zip_with(cpplinq::from(us)) >> cpplinq::for_each( [](const auto& p){ std::cout << boost::format("(%1%, %2%) ") %p.first %p.second; }); std::cout << std::endl; }
boost/range/adaptorサンプル集
はじめに
ずいぶん昔にGoogleのbloggerに作ったboost/range/adaptorのサンプル集が、bloggerの仕様が変わって表示されなくなりました。こちらに転載します。
サンプル集
#include <boost/range/irange.hpp> #include <boost/range/adaptor/adjacent_filtered.hpp> #include <boost/range/adaptor/copied.hpp> #include <boost/range/adaptor/filtered.hpp> #include <boost/range/adaptor/indexed.hpp> #include <boost/range/adaptor/indirected.hpp> #include <boost/range/adaptor/map.hpp> #include <boost/range/adaptor/replaced.hpp> #include <boost/range/adaptor/replaced_if.hpp> #include <boost/range/adaptor/reversed.hpp> #include <boost/range/adaptor/sliced.hpp> #include <boost/range/adaptor/strided.hpp> #include <boost/range/adaptor/tokenized.hpp> #include <boost/range/adaptor/type_erased.hpp> #include <boost/range/adaptor/transformed.hpp> #include <boost/range/adaptor/uniqued.hpp> #include <boost/range/algorithm/copy.hpp> #include <boost/range/combine.hpp> #include <boost/range/algorithm/for_each.hpp> #include <iostream> #include <boost/regex.h> void test_adjacent_filtered() { const auto range = {1, 1, 1, 2, 2, 3, 4, 5, 5}; const auto result = range | boost::adaptors::adjacent_filtered([](auto a, auto b){ return a != b; }); boost::copy(result, std::ostream_iterator<int>(std::cout, ", ")); std::cout << std::endl; } void test_copied() { const auto range = std::vector<int>{10, 20, 30, 40, 50}; const auto result = range | boost::adaptors::copied(1, 3); boost::copy(result, std::ostream_iterator<int>(std::cout, ", ")); std::cout << std::endl; } void test_filtered() { const auto range = boost::irange(0, 10); const auto result = range | boost::adaptors::filtered([](auto v){ return v % 2 == 0; }); boost::copy(result, std::ostream_iterator<int>(std::cout, ", ")); std::cout << std::endl; } void test_indexed() { const auto range = boost::irange(100, 110); for ( const auto& v : range | boost::adaptors::indexed(3) ) { std::cout << v.index() << " --- " << v.value() << std::endl; } } void test_indirected() { const auto range = std::vector<std::shared_ptr<int>> { std::make_shared<int>(10), std::make_shared<int>(20), std::make_shared<int>(30), }; boost::copy(range | boost::adaptors::indirected, std::ostream_iterator<int>(std::cout, ", ")); std::cout << std::endl; } void test_map() { const auto range = std::map<int, std::string> { {1, "tokyo"}, {2, "osaka"}, {3, "nagoya"}, }; boost::copy(range | boost::adaptors::map_keys, std::ostream_iterator<int>(std::cout, ", ")); std::cout << std::endl; boost::copy(range | boost::adaptors::map_values, std::ostream_iterator<std::string>(std::cout, ", ")); std::cout << std::endl; } void test_replaced() { const auto range = std::string{"tokyo"}; boost::copy(range | boost::adaptors::replaced('o', 'O'), std::ostream_iterator<char>(std::cout, "")); std::cout << std::endl; } void test_replaced_if() { const auto range = std::string{"tokyo"}; boost::copy(range | boost::adaptors::replaced_if([](auto c){ return c == 'o'; }, 'O'), std::ostream_iterator<char>(std::cout, "")); std::cout << std::endl; } void test_reversed() { const auto range = std::vector<int>{1, 2, 3, 4, 5}; boost::copy(range | boost::adaptors::reversed, std::ostream_iterator<int>(std::cout, " ")); std::cout << std::endl; } void test_sliced() { const auto range = std::string{"tokyo"}; boost::copy(range | boost::adaptors::sliced(1, 3), std::ostream_iterator<char>(std::cout, "")); std::cout << std::endl; } void test_strided() { const auto range = std::string{"!o!y!"}; boost::copy(range | boost::adaptors::strided(2), std::ostream_iterator<char>(std::cout, "")); std::cout << std::endl; } void test_tokenized() { auto range = std::string{"[100,200];[300,400];[500,600]"}; for ( const auto& m : range | boost::adaptors::tokenized(boost::regex(R"(\[\d+,\d+\])")) ) { std::cout << m << std::endl; } } void test_transformed() { auto range = std::vector<int> {1, 2, 3, 4, 5}; boost::copy(range | boost::adaptors::transformed([](auto i){ return 2 * i; }), std::ostream_iterator<int>(std::cout, " ")); std::cout << std::endl; } void test_uniqued() { auto range = std::vector<int> {1, 1, 3, 4, 4}; boost::copy(range | boost::adaptors::uniqued, std::ostream_iterator<int>(std::cout, " ")); std::cout << std::endl; } void test_combine() { std::cout << "> combine\n"; auto v1 = std::vector<int>{1, 2, 3}; auto v2 = std::vector<std::string>{"tokyo", "osaka", "nagoya"}; boost::for_each(boost::combine(v1, v2), [](const auto& p){ std::cout << boost::get<0>(p) << " " << boost::get<1>(p) << std::endl;} ); } int main(int, char* argv[]) { test_adjacent_filtered(); test_copied(); test_filtered(); test_indexed(); test_indirected(); test_map(); test_replaced(); test_replaced_if(); test_reversed(); test_sliced(); test_strided(); test_tokenized(); test_transformed(); test_uniqued(); test_combine(); return 0; }
出力は以下の通り。
> adjacent_filtered 1, 2, 3, 4, 5, > copied 20, 30, > filtered 0, 2, 4, 6, 8, > indexed 3 --- 100 4 --- 101 5 --- 102 6 --- 103 7 --- 104 8 --- 105 9 --- 106 10 --- 107 11 --- 108 12 --- 109 > indirected 10, 20, 30, > map 1, 2, 3, tokyo, osaka, nagoya, > replaced tOkyO > replaced_if tOkyO > reversed 5 4 3 2 1 > sliced ok > strided !!! > tokenized [100,200] [300,400] [500,600] > transformed 2 4 6 8 10 > uniqued 1 3 4 > combine 1 tokyo 2 osaka 3 nagoya
2つの自然数が互いに素である確率
はじめに
Youtubeで面白い数学の問題が紹介されていたのでまとめておく。
問題
問題文は以下の通り。
任意の2つの自然数が互いに素である確率を求めよ。
解答
ある自然数を考えたとき、それがの倍数である確率はである。いま、2つの自然数を取り出したとき、それらが同時にの倍数にならない確率は
となる。これを用いると、が互いに素になる確率は
と書くことができる。ここで、は素数である。式(1)の計算をさらに進める。
を得る。この分母を考える。
式(3)から式(4)への変形にゼータ関数のオイラー積表示を用いた。また、式(4)から式(5)への変形はバーゼル問題と呼ばれる計算である。この結果を式(2)へ代入して
を得る。
まとめ
問題文はシンプルであるが、その解答には
が現れるとても興味深い問題である。
参考動画
量子線形回帰
はじめに
線形回帰
, との組 が個与えられたとき
を満たすベクトルを求めることが目的である(としてあり、バイアス項は考慮されているとする)。最小化すべき損失関数は
である。で偏微分した値を0と置くと
を得る。いま行列を定義すると上式より
を得る。任意のについて上式が成り立つから
となり、これより
を得る。である。ところで、任意の行列は特異値分解できるから(特異値分解の詳細はこちらの説明を見てほしい)
と書くことができる。ここで、である。とは直交行列であり
を満たす。また、として
と書くことができる()。式(2)を使うと
を示すことができる。であり、(3)式右辺のに挟まれた行列は行列なので、上の行列は全体として行列の行列になる。式(3)を式(1)に代入すればの解析解を得ることができる。
未知のデータが与えられたときの出力値は以下のように計算される。
ここで、の成分をとした。また、式(2)より
成分をとると
ここで、行列の第j列の列ベクトルをと書くと
を得る。すなわち、の固有値はであり、それに対応する固有ベクトルはである。
線形回帰の量子アルゴリズム
ここでは、前節の定式化を量子コンピュータ上で行う手順を示す。まず最初に、入力データを振幅エンコーディングした状態を用意する。
ここで、添字のは量子ビットを収めるレジスタであり、独立した2つの状態空間であることを示す。式(2)より
を得る。ここで、はそれぞれ行列の第列の列ベクトルである。の密度行列は
となり、Bレジスタの空間でトレースを取ると以下のようになる。
すなわち、行列を埋め込んだ密度行列を得ることできる。式(5)より次式が成り立つことも分かる。
さて、式(6)に、複数ビットからなる3つ目のレジスタを加える。
上の状態のレジスタにユニタリー行列を施し、の固有値をレジスタに入れる(量子位相推定)。
4つ目のレジスタを加えレジスタの値を入力とし算術演算を行う(算術演算や次の回転ゲートの詳細な手順はここを参照のこと)。
さらに、1ビットからなるレジスタを加え、レジスタの各ビットを制御ビットとしレジスタに回転ゲートを施す。
レジスタを測定し0が観測されたとき、上の状態は次の状態に収縮する。
は規格化定数
である。各種演算の逆演算を施しレジスタを初期化する。
レジスタ部分を取り出し、これをと置く。
ここまでの計算で式(3)を振幅に埋め込んだ状態が得られたことになる。さて、未知データが与えられたときの出力値を計算するため次の状態を用意する。
内積を計算すると
を得る。これは式(4)で得た出力値を倍したものである。は求めることができる値なので、内積を計算できれば、未知データに対する出力を得ることができる。今回は、内積計算の量子アルゴリズムには言及しない。
まとめ
線形回帰の量子アルゴリズムについて説明した。上の量子位相推定を行う際にをに施す必要がある。このとき、ここで説明したDensity Matrix Exponentiationを利用する。
参考文献
Density Matrix Exponentiation
はじめに
Density Matrix Exponentiationは、密度演算子をハミルトニアンとする時間発展演算子を量子状態に作用させる際に使われるテクニックである。
微小時間への分解
を十分大きな数として、と置くと
と書ける。つまり、微小時間の時間発展を回繰り返すことを考える。時間発展させる量子状態をとすると、後の状態は
となる。この状態に対応する密度演算子は
である。、と置くと
を得る。を級数展開して上式に代入すると
となる。は微小量であるから、これの1次までとり近似する。
この近似式をユニタリー演算子だけを用いて作る手順が、Density Matrix Exponentiationである。
Density Matrix Exponentiation
最初に、同じ数の量子ビットからなら2つのレジスタA,Bを考える。Aレジスタに密度演算子を、Bレジスタに密度演算子を振幅エンコーディングする。
を考える。これが2つのレジスタ間の状態を交換することは以下のように確認することができる。いま
とすると
となり、確かにAレジスタとBレジスタの内容が交換されたことが分かる。同じ計算を繰り返せば
を示すことができる(元の状態に戻るということ)。
さて、式(2)と(3)を用いた次式を考える。
式(4)から
が成り立つので、これらをに代入すると
を得る。いま
が成り立つので
を得る。レジスタAの空間だけでトレースを取る。
いま
が成り立つ。右辺はすべてレジスタBの空間での量である。従って
を得る(添字は省いた)。を十分小さな量としているので、の1次までの量で近似すると
となる。これは求めたかった式(1)である。