熱力学 読書記録 (導入~等温操作、断熱操作)

田崎先生著の熱力学を読んでいるのでそのメモ書きを記そうと思う。中身の詳細は本を読んだ方が早いので、大事だと思ったところを書き残していくつもりでいる。飽きたらやめるかもしれない。

www.amazon.co.jp

物質についての知見を取り入れようと思い、少しずつ学んでいる。大学基礎レベルの力学、電磁気学は一通りさらったつもりでいるので次は熱力学かと思ってこの本を手に取った。

導入

高校でも熱力学は学ぶ。自分は何もかも忘れてしまっていたが、大学受験対策サイトなどを見て思い出すと「理想気体」「比熱」「熱サイクルの効率」あたりがキーワードのように見受けられる。高校過程では気体は分子の総体によって構成されていることを前提として気体の持つエネルギーについて論じていた。一方でこの本では気体を分子の総体として見ずに、少数の(当たり前と思える)前提から数理的な理論を組み立てている。歴史からすると熱力学自体は19世紀に完成し、その時代では原子論は現代とは異なり異端的であったとされるから、分子などの考えを導入しなくても熱力学が成り立つのは確かにそうなのだろう。気体を分子の総体として見たときの分析は統計物理という発展的な分野の範囲になってくるのだと思っている。

熱力学では物質を閉じた容器の中に幽閉して色々な操作 (押しつぶしたり、引き伸ばしたり、仕切りで区切ったり、混ぜたりなど) を施したときにどのような変化が生じるのかを論じる。特に、エネルギーが考察の中心的存在となるように見える。物質の状態としては気体の方がイメージしやすいが、理論としては液体や固体でも良いようである。熱力学では平衡状態にある静的なもののみを対象とする。これは容器内の物質はどこを取っても同じような状態であることを意味する。逆に言うと容器の中でいろんな乱流や化学反応が起こっているような状態の物質は(平衡)熱力学の対象からは外れる。

平衡状態にある物質は温度T、体積V、物質量n、の3つの物理量によって特徴づけられるものと考える。物質が外部に働きかける圧力はこれらの関数であり p(T; V, n) のように表されるものとする。理想気体は p=nRT/V となるようなモデルである。実際にはこれが成り立つ範囲は限定的である。熱力学では気体のモデルを具体的に定めず、一般的な立場からどのような性質が成り立つかを分析していく。理想気体は特殊ケースとして考える。熱力学では考察対象を容器内の物質とは言わず、系という言い方をする。

ところで、容器に閉じ込めた系を考える意義はなんだろうか。歴史的には産業面からの要請が大きかったようである。何かを燃やすことで力学的エネルギーを得る装置は熱機関と呼ばれるが、その効率を高めることは産業的な意義が大きく、装置の開発にはいろいろな努力がされていたのだろう。燃やしたら燃やした分だけ人間にとって有益な力学的エネルギーに換えられればベストだが、実際には限界があるのである。これを数理的に示すことに一つの意義があると見ている。昨今では CO2 排出削減のため熱機関は冷ややかな目で見られつつある気もするが、しかしだからこそ大事なのだろうか。また、化学反応を考える際にも重要なようである。

等温操作

温度T, 体積V, 物質量n の組を系の状態量だと見なし、状態量をいろいろ変化させたときのエネルギーを分析するのがこの本の中心的な話題となる。

熱力学では等温操作と断熱操作を基礎に理論を積み上げていく。なぜだろうか。

等温操作は系外部の温度が一定であり、操作の始状態と終状態で温度が一致する操作を指す。系の温度自体を常に固定しているのではわけではないことに注意する。操作の過程で系を断熱したりしてもよい。

この本ではまず等温操作で色々な操作を行ったときの系が外部に行う仕事を分析するところから話が始まる。等温操作を考える動機は第二種永久機関が存在しないことを前提にするためであると捉えている。等温環境下で、ある状態から何らかの操作を行って同じ状態に戻ってきたとき、その系が外界に対して行う仕事は正ではないという前提である。この前提をもとに系が外部にする仕事について考えると、等温環境下では力学で言うポテンシャルエネルギーに相当する概念を作ることができるようになる。ヘルムホルツの自由エネルギーと呼ばれる量である。

断熱操作

一方で、断熱操作は容器が断熱されていて系が外部と熱のやり取りを行えない状態での操作を指す。断熱操作は等温操作と対になる関係というわけではなく、例えば断熱しつつ何かの操作をした結果始状態と終状態で同じ温度になっていれば、それは断熱操作でもあり等温操作でもあると見なせる。ここで重要になってくることは、系が外部にする仕事についてである。等温操作の場合はどんな操作を施したのか、例えば気体を高速に伸縮させた場合とゆっくり伸縮させた場合では、始状態と終状態が同じでもその過程によって外部への力学的仕事の量が変わってくる。一方で断熱操作の場合には経路には依存せず始状態と終状態が決まれば外部への力学的仕事は一意に定まるという前提を置く。これは熱が遮断されていればエネルギーは保存されるということを言っているので、熱力学上でのエネルギー保存則であると見なせる。この前提をベースに、系そのものが持つ内部エネルギーを定義することが可能になってくる。系の正体が何なのかを触れることなく、である。

ここまでは特殊な操作を考えたときに何かのエネルギー量を定義しただけで、なんだか特別な世界の話をしているようであった。より一般的な世界に入るのはここからだが長くなったので一旦区切る。

余談:どうやって実験するの?

本だと特に触れられていないが、熱力学に関する実験をする場合どうするのだろうと思った。容器の中で気体の容積を変えたりすると容器が破裂したりしそうで大変危険そうに見える。大学実験などで熱力学に関する実験を行うときはどのような装置を使うのだろうと思い少し調べたところ、等容変化に関する実験であれば以下のような装置で比較的簡潔な構成で行えるようである。それでもある程度の高温や高圧を扱うことになるし、諸々の測定器も必要になるからなかなか重厚な印象を受ける。

以下は東京都市大学の講義資料で見かけた図: https://www.comm.tcu.ac.jp/~mse/uploads/2016/07/00846a981b418da93bddfb62a6af2971.pdf

2022

久々に日記を書きます。出遅れましたが前の年の振り返り。題目は「住居」「健康」「仕事」「ボランティア(競プロ)」「本」「ゲーム」の6点です。

住居: 素敵そうな物件があったので買った

これまで賃貸マンションで暮らしていたところから引っ越しました。春頃に大学時代のサークルメンバーと同窓会のような何かがあったのでふらっと参加したところ、ある者が不動産の話を熱心にしており、不動産のどこにそんなに熱くなる要素があるのだろうと奇妙に感じ、そのときはあまり真面目に聞かなかったというかそういう概念もあるんだな程度に思って聞き流していました。帰宅してからふとそのことを思い出して不動産について色々調べると、どうも物件を見つけたら仲介業者経由でいくつかのステップを踏んで売買契約をして、住宅ローンなるものを組めば住居というものは買えるのだと知りました。思っていたよりも簡単そうだし、知り合いも既にやっているので自分でも出来るんではないかという気がしてきます。そこからしばらくスーモを毎日見て、どこか自分に合う物件がないかを探しました。3~4週間くらいずっと凝視し続けていたのではないかと思います。自分がこれまで行ったことがない土地にも足を運んで、ここに住んだらどんな気持ちになるのだろうかとイメージしてみる日もありました。自分の見知らぬ街にもいろいろな活力があるのだと勝手に感じ取ったりしました。結局自分にとって良い物件が見つかったので、若干のハプニングを乗り越えつつも手に入れることができました。雰囲気の良い地域で、部屋も広くなったので満足しています。シンプルに、広いということは心の余裕にも繋がるように思えます。

住居は一応自分の所有物ということになってますが、所有物といっていいのかよくわからない感覚があります。ローンは気が遠くなるほど長い返済期間がありますし、そもそも自分が東京にどれくらい居続けることになるのかもよくわからないのです。もしかすると10年くらい住んでみて気が変わったら買い替えたりするのかもしれない程度の気持ちでいますが、実際はそんなことは面倒なのでしないのかもしれません。

健康: 眠ってばかりいるがそうしたいわけではない

睡眠の質は以前から慢性的に悪かったのですがいよいよどうにかしないと日常生活が危ういと感じたので2022年のはじめくらいから睡眠薬や安定剤を処方してもらいながら生活することにしました。結果としては"多少マシになった"とは思います。自分の抱えていた眠りに関する症状としては

  • 入眠にかなり時間がかかる。1~2時間くらいはザラ
  • 寝ても謎のタイミングで目が覚める
  • 眠りが浅い中起きて始業するけど夕方くらいに眠くなるので1~2時間くらい昼寝する
  • 意識が低迷してる中仕事をするので精神も余分に消耗する、ような気がする

というあまり良くないサイクルが繰り返されていました。1つ目と4つ目は薬を飲むと多少マシになりました。しかし2つ目は改善せず、4~5時間くらいしか寝てないのに尿意で起きたりすることはよくあり、3つ目の夕方に眠いのは相変わらずで生活リズムは結構めちゃくちゃな感じが続いています。という感じで1年を通して質の悪い眠りを取りまくっていたように思います。

睡眠以外はどうかというとやはり身体が良くない感じに向かっているようで、多少は日常的に運動しようと試みたものの結局継続できていません。健康のために短時間とはいえど時間使うって全然おもしろくないのですがどうするといいのでしょうか。今後の課題です。

仕事: 人生で最も時間を費やしているにもかかわらず振り返るのが最も難しい

仕事について。オープンにできることは限られていますが、可能な限り書いてみます。

まず、何をやっていたのかについて。今年はある機械系メーカー様と共同で研究開発や製品開発をしていました。今年は、と書きましたがもう3年くらいやっているようです。不幸にもコロナ禍がちょうど重なりコミュニケーションが難しいタイミングで自分は参加したのですが、今年は最も密接に異なる組織の方々と一緒に仕事ができたように感じます。日頃は技術者として技術の問題に取り組んで小さな一歩を積み重ねつつ、最終的には経営上の課題に結びつく貢献を意識していたように思います。自社の製品の使い勝手を良くすることでシェアを伸ばしたいから新技術の検討をしたい、他社と比べられる要素を追加したいから新技術の検討をしたい、新規事業を伸ばしたい、…など。技術的なところでいうと今年はほぼ windows 環境上で python3 を書いていました。機械系のドメイン固有技術については知らないこともあったのでいくつか学んだりもしました。プロの方々には到底及びませんが、それでも学部レベルくらいの知識は抑えておくといいこともあると思えました。

仕事をしていて、自分の中でのモチベーションの濃淡が割と分かってきた年でもあったように思います。モチベーションが湧くのは目標を前に進めるにあたって必要となる要素を考え込み、それをチームメンバーと合意が取れるようにまとめ上げてフィードバックを得ているとき。あるいは、既にあるゴールをより適切な方向に導くにはどうすればいいか考え、それについて問うているとき、など。目標を達成することが何らかの価値を見いだせると信じ切っている状態で考え尽くし、それをいろんな人にぶつけてみるというサイクルは大いに楽しめるものです。逆にモチベーションが湧かないのはゴールそのものに疑問を抱いているとき。こういうときは極端な話、ズルズルと続けても誰のためにもならないので撤退した方がいいのかもしれません。実際にはそれは簡単ではなく、前線で踏みとどまらなければならないこともあるようにみえるのですが。だから仕事について振り返るのは難しいのかもしれません。

それを踏まえて2023年はどうしたいのかというと、自分にとって魅力のあるゴールに自分の時間を捧げたいように思えます。しかしそれが何なのかは日常を漫然と過ごしていても分からないようです。マクロに見れば社会は問題だらけでパッチを当てなければならないところだらけなのでしょう。少子化で日本や地方はどうなっちゃうのとか、気候変動で云々とか… ただそれらはあまりに気が遠くなる話であり、なるようにしかならん気がします。では目の前で湧いてくるものを漫然と捌いていれば良いのかというとそれも釈然としません。2023年は誰に何を提供すれば自分にとって喜びとなるのかを考える年になるのかもしれません。

ボランティア: 競プロの文化的意義を無視できなかった結果ICPCのジャッジに復帰した

自分は ICPCという競プロの大会のジャッジ(作問者)を2014年から2019年までやっていました。それ以降は競技とは一切無縁の生活をしていたのですが、また復帰しないかとのご連絡を頂きました。そのときはもう自分の中で競プロ自体への興味がかなり薄くなっていたため復帰したくない気持ちがありました。しかし話を聞くと運営で色々苦難があるのだとわかりました。それを知り、さすがに大げさかもしれませんが、仮にこの大会が準備不足とかなんとかで開催不能になり消滅してしまったら自分はどう思うのかと少し想像しました。ICPCは様々な人たちが長い世代に渡って情熱をぶつけてきた場所であることを自分は知っています。それがなくなるのは寂しいのでは?と思いました。結果として復帰することにしました。自分は相変わらず競プロ自体の興味は薄い状態だったので、他のメンバーがコンテストの準備が行いやすくなるように色んなところを見ることに役目を置いたつもりでした。問題提案もして採用されました。

  • 国内予選ではF問題, H問題の原案担当でした、一応…。準備は他の方におまかせしました。
  • 地区予選ではB問題の原案・準備を担当しました。今回は若干の挑戦心をもってインタラクティブ問題を出したのですが事前の見通しがやや甘く、システム面でトラブルの温床になってしまったと個人的に反省しています。

今思い返すと、僕はこの大会が持つ文化的な面を尊重したかったのだと思います。つまりコンテストを通して得られる喜びや楽しさ、悔しさ、葛藤は大学生活のような短い期間の中で意義深いものになると信じているのです。実際は分かりません。コンテストに集中する時期が終わって日常へと戻り、学業だったり仕事だったりに向かう生活になると求められるものは大抵の場合全く変わるわけで、徐々に記憶からは消えていくものなのかもしれません。そうは言っても無視しがたいものを自分は感じ取っていました。

最後に一つ。コンテストには華々しい面だけでなくネガティブな面もあることを自分は知っているつもりです。過熱した戦いは人生に不要な劣等感や優越感を与えてしまう要因になるかもしれません。短時間コンテストという形態は、IT産業向けエリート養成所のような役割を必ずしも果たせないように見えます。産業的意義の結実については他の方々におまかせしたいと考えている次第です。

読書: 色々漁った記憶だけが残っている

ビジネス書のようなものは一時期色々読んでいた気がするのですがなんか似たようなこと書いてることが多い気がして今年はほとんど読みませんでした。漫画はいくつか読んだ気がしますがあまり印象に残っているものがありません。結構古いですが日出処の天子が面白かったです。太古の昔の世界をこんなに優美に、それでいて簡素に描けるものなのかと驚きを感じました。繊細に人間関係が描かれているのも好きです。

去年からの続きですが物理の学部レベル相当の本を若干読みました。メーカーの方々を相手に仕事してるのにその業界の常識っぽいことも全然知らなくていいのか?と思ったのがきっかけですが普通に数理的な考え方が面白いので読んでいます。考えの幅を広げたかったのです。今年は解析力学の本を読み、演習問題を解きました。変分原理の理屈が見えたのが収穫でした。記憶はだいぶ風化していますが。今は田崎先生の熱力学を読んでいます。語り口から熱が伝わってくる感じが好きです。

ソフトウェアについてはソフトウェアアーキテクチャの基礎を読んで最近の開発スタイルに関するキーワードを拾ったり、またいわゆるITシステム開発に関する基本 (仕様書の書き方など) を拾ったりしていました。

自身のための学びはもっとやりたい気もするのですが体力とかの問題があってそんなにいろいろはできないというのが正直な感想です。

ゲーム: 漫然と遊んだ

今年やったゲームで記憶に残っているものだけ。

  • Cult of the Lamb: 周回で何度か遊んで100時間くらい。今年一番気に入りました。まだまだこの世界を味わいたい気もするのですがExtraまでクリアしてしまい、クリア後のダンジョン潜りも敵の体力が高すぎて面倒なだけであまりおもしろくないので、さすがにやることがなくなってきました。しかし今後アップデートで色々入るらしいので楽しみにしています。
  • Splatoon 3: 楽しみにしていたタイトルです。今のところ200時間くらいやっているっぽいです。ネットゲームは他人の存在無しに自分は成り立たないのだということを認識させてくれます。漫然と長時間やってるとストレスで脳に悪いので週一くらいでエンジョイするのがちょうどいい付き合い方なんじゃないかという気がしてきました。
  • Satisfactory: 一瞬だけ狂ったように遊んでいたのですが謎のバグを踏んで苦労して作った生産設備が動かなくなったあたりで「こんなゲームにマジになってどうすんの?」みたいな気持ちになってセーブデータごと消してやめました。
  • テトリス: Tetris Effect とか Tetris 99 をやっていましたがイライラすることが結構多くて精神・身体等にあんまりよくないんじゃないかと思ってあまりプレイしなくなりました。
  • Apex: 年始くらいに80時間くらいプレイしましたが豆粒みたいに遠くにいる敵に弾を当てる方法が全く分からずやめました。

エンジニアから見た NeurIPS 2019 参加記

会社で書いていた論文が機械学習トップ国際会議の NeurIPS 2019 に採択されたので12月8日から14日にかけて開催地のバンクーバーに発表のために行っていました。近況報告も兼ねてその様子をメモ書きしてみようと思います。

f:id:ir5:20191210081010j:plain
会場の Vancouver Convention Center

自分について

エンジニアとして働いています。研究を行い論文を書くことを主として行う研究者ではなく、実用上の問題を解決するための仕事をしています。今回論文を書いて発表を行うことになったのは、解きたかった問題の解決方法がまだ世の中に知られていなかったため自分で新たに編みだす必要があり、それが論文としてアウトプットする価値があったという事情に基づきます。

発表した内容としては「ニューラルネットの学習ではメモリ消費が非常に大きくなりGPUのメモリから溢れることがあるが、その消費量を再計算という手法によって削減する方法を提案した」というものになります。以前書いた記事もあるので大雑把にはそちらを参照していただければと思います。詳細には以下があります。

この取り組みは一旦落ち着いたこともあり、最近はより実用に近い課題に取り組んでいます。こういった事情があり最近は研究から少し心が遠のき、実用の課題解決に関心が寄ってきている中での参加になった感はあります。

何にせよ、以下に参加して印象に残ったことを記していきます。

多数の参加者

機械学習やAIトップ会議への参加者は年々増え続けていると言われています。今回の NeurIPS には約12000人の参加登録者がおり、それに合わせて会場もかなり広々とした空間が容易されていました。チュートリアルや招待講演が行われていた場所は実質ライブ会場みたいな感じでした。

f:id:ir5:20191209144035j:plain
招待講演などが行われた会場

それでもポスターセッションでは人が一気に集まるため移動が困難になるほど混雑していました。実際、ポスターのときにまともに発表を聞いたり質問したりできたのは開始前の人があまり集まっていない休憩時間くらいで、それ以降はかなり厳しい状況でした。

f:id:ir5:20191212172424j:plain

それほどまでに機械学習やAIの技術が世の中から注目を浴びてきているということなのだと思います。少なくとも、現在に関しては。

f:id:ir5:20191214122305j:plain
採用活動も盛んに行われている

コネクション作りの場としての学会

NeurIPS の講演動画や講演資料のほとんどがウェブに上がっています。

こうなるともう単に発表を聞いて知識を仕入れるという目的のためだけならわざわざ遠方まで出かける必要もなさそうに見えます。が、学会の目的はそれだけではなくむしろそれよりも人と交流して将来のためのつながりを作ることが大事だとされます。(関連記事: "Preparing for NeurIPS")

今回の参加で印象的だったのは、運営から Whova というスマートフォン向けの交流用アプリが提供されており、それを通じて特定のトピックに興味のある人や共通する背景(国籍とか)を持つ人で集まりやすくなるように配慮されていました。これはかなり活発に使われていたように見えます。僕はこのアプリを通じて普段はあまり会う機会が無さそうな日本人参加者や海外参加者と繋がることができて有意義でした。

f:id:ir5:20191218010500p:plain:w200
Whova のスクリーンショット

広い企業ブース

NeurIPS にはスポンサー企業が多数あり、それぞれの企業がブースを出展して自社製品のデモや採用活動を行っていました。企業ブースはかなり広いスペースの中にあり、展示会のような雰囲気がしました。*1

f:id:ir5:20191209165006j:plain

会社勤めの社会人としては足を運びづらい気がしてあまり回らなかったのですが、今思い返すとあまり気にせず色々回った方が各社の思惑などが見れて良かったのかもしれません。

デザインを意識したポスター

発表ではデザインを十分に作り込んでいるポスターが多かったように感じました。デザインを意識することは単に第一印象を良くしたり組織のブランディングを創出するだけでなく自分の研究の要点を初見の聴衆にも伝わりやすくする役割があるので、昨今では多くの研究者がその重要性を認識しているということなのかもしれません。

f:id:ir5:20191212173509j:plain

特に今回は参加者が多かったことに配慮してなのか、遠くから見てその内容がひと目で分かるデザインのものがいくつかありました。特に、中央にでかい要約が書かれていてその傍に詳細が書かれているようなものが複数の研究機関のポスターで見られ、印象的でした。

論文やソースコードへのURLのQRコードをポスターに載せているグループを結構見ました。参加者と発表件数が非常に多いこともあり、参加者からするとポスターはその場で全部じっくり聞くというより写真だけ撮影して後で見直すという風にした方が効率がいいこともあるのですが、そのときに論文そのものやコードにアクセスしやすくなっていると良い場面は多いのだろうと思いました。実際、僕自身も発表していて「ソースコード公開してる?」と聞かれる場面は想像していたより多かったです。

日本のプレゼンス

日本人の参加者は(あてにならない体感ですが)おそらく1~2%くらいだったのではないかと思います。数字にすると微小なのですが、これは通路を歩いているとたまに名札を見て日本人っぽい人がいると認知できる程度の数であり、決して少なくはないと思います。これよりさらにプレゼンスを上げるには、本会議論文なりワークショップなりスポンサーブースで何か発表する場を作るのが有効なのかもしれません。

ジェンダーダイバーシティ

自分にとって海外に行くのは3年ぶり、国際学会に行くのは5年ぶりでした。別に NeurIPS に限ったことでもない気がしますがが学会の女性の参加率の高さが印象的でした。これもあてにならない体感ですが、参加者や発表者の3割くらいは女性だったのではないでしょうか。このあたりは日本の情報系分野の状況とはとても違っているわけですが、少なくとも色んな人々が活躍している場は活気があるように感じるし、また個人個人の生きやすさにも関わって来そうに思います。

発表は楽しい

上述したように常に人が多く、ポスター発表は聞いている分には辛いのですが、発表者からすると(今回僕が発表したような、全体からするとニッチ気味な研究でも)常に一定数の人がひっきりなしにやってくるので意見の交換ができて楽しかったです。また、トップ会議であるため分野の詳しい人が来てコメントをくれることがあったり、関連する分野の研究者と交流することもできました。

面白かった発表

有名な人はやはり世の中で重要な問題を広い視点から捉えた上でそれらにどう取り組むべきなのかについて洞察が深いこと多いので、講演を聞いていて面白いことが多かったです。

全体を振り返って

f:id:ir5:20191210174719j:plain

とにかく海外の熱気を強く感じる場所でした。周りで大手研究機関や大企業の研究者たちが盛んにディスカッションをしてそうに見える中で僕は英語が苦手でかつ初対面の人と話すのも得意でなく、苦戦しました。とにかく、face-to-face のコミュニケーションは適当に妥協するとしても少なくとも海外の情報は日頃から意識的に何らかの形で取り入れたほうが良いのだろうという感が芽生えました。

*1:しかし聞くところによるとこれでも規模は抑えられている方らしい…

論文紹介: The Ramanujan Machine: Automatically Generated Conjectures on Fundamental Constants

2019/06/30 に arXiv にアップロードされた論文である The Ramanujan Machine: Automatically Generated Conjectures on Fundamental Constants を読んだのでその概要をメモしようと思います。

概要

円周率 $\pi$ や自然対数の底 $e$ といった数学の定数を含む式は規則的な項を持つ連分数で表現できることが知られています。例えば wikipedia からの引用ですが以下のような等式が成り立つようです。

f:id:ir5:20190707122436p:plain:w220

論文ではこのような形の等式に関する予想をコンピューターに自動で発見させる手法を提案しています。結果として、これまでに知られていなかった予想を複数個得られたと著者は報告しています。以下は見つかったとされる予想の一例です。

f:id:ir5:20190707122529p:plain

著者らの発見したこれらの等式には数学的な証明はありません ("予想"であるため) が、数値計算によって小数点以下50桁以上が一致することを確認しているようです。発見された予想は著者らのプロジェクトページにまとまっています。

論文タイトルは数学者のラマヌジャンから来ており、著者らのアプローチでは数学的な証明を一切経ずに予想を得ることから取っているのだと思います。

問題設定

$c$ を関心の対象となる定数 ($e$, $\pi$, ...) とします。求めたいのは整数係数多項式 $\alpha, \beta, \gamma, \delta$ で以下を満たすようなものです。

$\gamma(c)/\delta(c) = \mathrm{GCF}(\alpha, \beta)$.

ここで、$\mathrm{GCF}(\alpha, \beta)$ は $a_n = \alpha(n)$ と $b_n = \beta(n)$ を一般項として持つ以下のような連分数を表すとします。

f:id:ir5:20190707122627p:plain:w200

やりたいことは等式を満たす多項式の組をいろいろ見つけることです。

手法

おもしろ等式予想を見つける方法として、両側探索的な方法と勾配法をベースにした方法の2種類を提案しています。

両側探索

等式の左辺 $\gamma(c) / \delta(c)$ と右辺 $\mathrm{GCF}(\alpha, \beta)$ として考えられる候補をそれぞれ独立に列挙して、数値計算による値が一致しているものを選び出す方法です。

最初の列挙では精度の低い状態で計算して、一致していた場合はさらに精度を上げて検算することで計算効率を上げているようです。

割と普通っぽい(?)方法です。これによって $c=\zeta(3)=\sum_{n=1}^{\infty} 1 / n ^ 3$ (アペリーの定数) に対して新規の予想を得られたと報告しています。

f:id:ir5:20190707122811p:plain

勾配法のような何か

元の解きたい問題を最適化問題とみなします。

f:id:ir5:20190707123007p:plain:w320

この問題では変数の取りうる範囲は離散領域ですが、取りうる範囲を実数に連続緩和することで勾配法のようなアプローチを取り入れようとしています。

具体的には、以下のようにしています。

  • 初期値は複数個の候補を取ります。論文では1つのパラメータをあらかじめ決めた領域から等間隔に取っているようです。
  • まずは勾配に沿ってそれぞれの候補を更新します。
  • 次に、(恐らく候補が同じようなところに収束するのを防ぐために) 候補同士に斥力をかけて分離させることをします。
  • 勾配による更新と斥力による更新を繰り返して、最終的には候補のすべてのパラメータが整数になるように調整します。損失関数を 0 にするような候補が存在すればそれはおもしろ等式ということになり、無事ハッピーな気持ちになります。

以下は論文の図の引用です。損失関数を 0 にする領域(図の水色の領域)が曲線の和集合のようになっているのが特徴的な感じがします。

f:id:ir5:20190707123115p:plain

その他

著者らはここからコミュニティを形成していくことに積極的なようで、プロジェクトページでは発見した予想に証明をつけてくれる人や、このプログラムを GIMPS のようなノリで動かしてさらに多くの予想を発見するのに貢献してくれる人を募集しているようです。(証明付けるのとかメッチャ大変そうなのにそんな気楽にできるもんなのか?という気はしますが…。)

連分数の収束率は多項式 $\alpha, \beta$ の次数の比によって大きく変わるようで、論文の Appendix にはその証明が記されています (僕はちゃんと読んでませんが)。

論文だと詳細がかなり端折られている (学習率や初期値のサンプル方法や、勾配とか反発の計算方法など) 箇所があるので本当に細部を知りたいなら公開されているコードを追う必要があるのだと思います。また、勾配法によって発見された等式があったのかどうかについては特に記述がないので、勾配法の良し悪しも論文だけからだとあまりよくわかりません。

有名な数学の定数で、今回の手法で試したけど等式の予想が見つからなかったものも数多くあるようです。例えばオイラーの定数 $\gamma$ などが今回そうだったようです。これらに対しても何らかの表現方法を自動で探す方法があるといいね、といった話をして論文は締めくくられています。

感想

まず問題設定の仕方がうまいと思いました。今回のような定数を含む等式の発見というタスクは伝統的なアプローチのように証明を構成することから入ろうとするとメチャクチャ大変(というか今の技術だと無理?)ですが、一方で適当に思いついた式が合ってそうかどうかはコンピューターなら一瞬で検証できるので、証明の構成を完全に回避して探索を走らせるのはなかなかコンピューター向けな気がします。

一方でこのようなことを取り組みにあたってはリスクもあったと思います。僕が想像できるのは、探索してもすでによく知られているような式しか発見できず、新しい発見に繋がらない可能性です。そのようなリスクがあった上でも実装を突き進めてちゃんと新しい発見をしたのは賞賛に値するのではないでしょうか。

とはいえ、見つかった式が本当にこれまで未知だったものなのかというのは十分に調べきれているのか(というかどこまで調べれば新しい発見だと胸を張って言えるのか?)は少し心配ではあります。が、reddit に議論スレがあって誰も新規性についての指摘などをしていないのを見るに、別に大丈夫なのかもしれません。

数学的定数に関する等式以外の分野でも未知の予想などを発見しうるアルゴリズムはもしかしたら作れるのかもしれません。誰かにやってみてほしいです。(?)

連続値版ライフゲーム SmoothLife について

f:id:ir5:20180602152845p:plain

SmoothLife はライフゲームを連続的に拡張したもの。6年くらい前に出て、少し話題になっていたりした。

普通のライフゲーム(復習)

f:id:ir5:20180602152804g:plain

ライフゲームでは、無限の広さをもつグリッド盤面で、各セルに生と死のどちらかの状態が割り当てられているものを考える。 セルの状態は時刻を経るごとに遷移していく。遷移のルールは単純で、次のように表される。

  • あるセルが生のとき、周囲8近傍のセルで生のものがちょうど2つか3つならば次も生である。そうでないなら次は死になる。
  • あるセルが死のとき、周囲8近傍のセルで生のものがちょうど3つならば次は生である。そうでないなら次は死のままである。

初期のセルの盤面の状態を決めると、この遷移ルールによって先の全ての時刻での状態が決まる。ライフゲームの興味深い点は、生と死の状態をうまく配置することで多様なパターンを作り出せることにある。斜めに移動し続けるグライダーはその有名な例である。

SmoothLife

f:id:ir5:20180602152819g:plain

ライフゲームの世界観は"離散的"であった。セルは生と死という0/1であったし、遷移ルールにしても周囲8方向しかみないので、移動体の実装にしてもどうしても移動方向などが縦横斜め45度などに限定されてしまう。 SmoothLife では元のライフゲームのルールを拡張し、任意の方向に飛ばせるグライダー(下図)を実装している。

f:id:ir5:20180602152711g:plain

SmoothLife のルールの概要はこうである。まず、セルの値は0/1という離散値ではなく、[0,1]の連続値であるとする。盤面も本来なら連続的な感じにしたいというのが理想であるが、結局計算機上で実装するためグリッドで区切られるという点は変わらない。時間経過による遷移も1ステップごとで、離散的である。

遷移後のセルの値を決めるにあたっては、周囲8近傍ではなくもっと広範囲:具体的にはセルから半径21以内にあるマスを見る。 その広範囲のマスの中でも、半径7以内にあるマスの値の総和を n、そこから外にあるドーナツ領域にあるマスの値の総和を m とする。 セルの次の値は、この n と m を受け取って [0,1] の値を返す関数によって決まる。この関数の定義は少し複雑なので省略する。細かいことは元論文を見るとまぁいろいろ書いてある。

遷移ルールから回転不変性のようなものがあることが見てわかる。つまり、盤面をあるセルを中心に任意角度に回転させても次の状態でのそのセルの値は回転させる前のそれと大体同じになる。

また、周囲8近傍ではなく広範囲を見るようにすることで、擬似的に連続的な範囲を見ているような感じにしているのだと思う。

設定を変えて観察

SmoothLife は以下のような実装がある。せっかくなので試しに手で少し遊んでみることにした。

  • duckythescientist さんによる numpy 実装
    • 最近公開された。パラメータは元論文に近い。比較的世界観がマイルド。nunpy を使って実装されている。
  • ionreq さんによる C++ 実装
    • 元論文とパラメータや実装が結構違う。鎖のようなオブジェクトが発生したり塊が膨れ上がったりすることがあり、キモい。
    • 今回の実験ではこちらを使った。

遷移後の値を決める関数にはパラメータ(ライフゲームでいう、生と死を決める閾値みたいなもの)がいくつかある。パラメータの1つである b1 を変化させた場合に遷移の雰囲気がどのように変わるかを見ることにした。

デフォルト値(b1=0.257)

f:id:ir5:20180602155915g:plain

まずはデフォルト設定での世界。 円形の微妙に動く物体と鎖のようなオブジェクトが存在し、ゆっくりと変化していく世界が構築されている。

少し減らした場合(b1=0.247)

f:id:ir5:20180602155758g:plain

次に、閾値パラメータ b1 を少しだけ減らして世界がどう変わるかを見た。 ↑はランダム状態からスタートしてしばらく経った状態であるが、鎖のようなオブジェクトだけになって世界が安定してしまった。 何度か初期状態を変えつつリスタートしたものの、すべてこのような鎖状のオブジェが発生して世界が安定するという結果に終わった。

少し増やした場合(b1=0.267):

f:id:ir5:20180602160009g:plain

今度は逆に増やしてみた場合。 こちらでは円形のオブジェクトだけになって、やはり世界が安定してしまった。

「カオスの縁」という言葉があるらしい。 僕はこのへんの分野の専門家ではないので誤解があるかもしれないが、雰囲気としては「観測していて興味深い挙動を示す世界というのは、観測していてつまらない世界(即座に安定状態になったり、単なるランダムに近い挙動をする世界)の相転移となるような位置にある」ということを言っているように見える。 これは上記の観察にまさに一致しているように思う。 b1 という遷移ルールのパラメータをわずかに変えただけですぐにつまらない世界に変わってしまった。

裏を返せば、ゲームの設計者はこのような相転移となるパラメータを見つけるためにかなりの試行錯誤をしなければならないということでもあるだろう。

感想

アメーバ・微生物・カエルの卵っぽい見た目の世界は気持ち悪くて面白い。グライダーを任意の方向に飛ばせるようになったのは改善点として良い。パラメータをうまく調整して無事いい感じのルールを見つけ出せたのも、職人芸的な観点ですごい。が、課題は多い。

ライフゲームがヒットしたのがなぜかというと、単純な数理モデルから信じられないほど複雑で豊かな世界―例えば、固定物体や振動子のような基本素子から、グライダーや宇宙船のような移動物体、さらにはグライダーガンなどといった複雑な構造物―を構築できることにあった。

SmoothLife のモデルの複雑さは、残念ながらライフゲームと比べると跳ね上がっている。実装されているものはグライダーだけで、その他の多くのおもしろオブジェクトは実現できていない。 ライフゲームではアセンブラのような感覚で基本要素となるオブジェクトを配置して、さらに複雑な挙動をするおもしろオブジェクトを生み出すといった楽しみ方をユーザーに提供していたが、モデルが複雑になるとこういうことはしづらいかもしれない。連続方向への拡張というコンセプト自体にそれなりの難がある感じは否めない。

それでも、もし連続系のセル・オートマトンで、ライフゲームのと同じくらい多様なオブジェクトを生み出せるモデルがもしできたらどんな世界が広がっているのだろうかと想像すると、夢があるような気がする。

Rust で競プロ

Rust で競プロする話について書こうと思います。ちなみにこの記事は競プロアドベントカレンダーの7日目の記事です。

Rust とは何だったか

Rustは速度、安全性、並行性の3つのゴールにフォーカスしたシステムプログラミング言語です。 (公式サイトより引用)

Mozilla が開発しているコンパイル型のプログラミング言語です。 ムーブセマンティクスや変数の寿命といった独特な機能によってメモリの安全性やデータ競合の安全性をコンパイラが保証している点や、プログラミングする上で便利な構文が用意されているのが特徴的です。 言語の機能としては例えば imos さんの記事とかが詳しいです。

開発の世界では最近注目が集まってきているような気がしますが競プロではまだまだ使われている場面が少ないので、布教も兼ねてどういう感じか紹介したいと思います。

競プロで C++ 使ってるけど、Rust 使う利点/欠点は何?

利点としては、後述するような優れた構文やツールチェーンのおかげでプログラミングが柔軟にできる点は大きいのではないでしょうか。標準ライブラリも結構充実しているため少なくとも C++ と同等のことは出来るのではないかと思います。実行速度に関しても C++ に引けを取らない速さを達成しており安心できます。

欠点としてはマイナー言語にありがちなことですが、競プロでは使ってる人がそこまで多くないので競プロ用の便利ライブラリや問題の正解コードなどを探すのは難しくなるでしょう。また、日々コンパイラがバージョンアップしていくせいで最新の機能を使いたくてもオンラインジャッジが対応していないせいで使えないといった状況は覚悟したほうが良いです。 残念ながら日本語の情報はそこまで多くなく詰まった時にググって出て来る情報は大抵が英語なので、技術英語を読めるレベルの語学力は必須になるでしょう。

というわけなので、初心者の人は普通に C++ 使ったほうが良いと思います。逆に C++ をある程度使いこなせるけど C++ に飽きてきたから他の言語試してみたいみたいな場合には、使ってみると楽しいかもしれません。

どういうオンラインジャッジ/コンテストサイトで使えるの?

ちょっと調べました。意外にも結構対応してるところが多いですね。ちなみに現時点(2017/12/9)での最新バージョンは1.22.1です。

  • AtCoder : 1.15.1
  • Codeforces : 1.21
  • Aizu Online Judge : (バージョン不明?)
  • Sphere Online Judge : 1.14.0
  • HackerRank : 1.21.0 + いくつかの外部ライブラリが付属

以下は未対応のようです。…頑張れ!

どういった環境で使うべき?

Rust はクロスプラットフォームな言語なので Windows でも MacOS でも Linux でも使えます。インストールは容易です。

エディタについては、IDE はできればあった方が良いと思います。Rust and IDEs · The Rust Forge を見ると Vim, Emacs, Sublime, IntelliJ Idea, VSCode などの主要なエディタにプラグインが揃っているので、普段使用しているエディタにプラグインを入れれば快適な環境が整いそうです。

どうやって勉強したら良いの?

公式チュートリアルRust Book を読むのが良いです。

特徴的な言語機能

色々ありますがここでは競プロで役立ちそうな Rust の言語機能を C++ と比較しつつ紹介したいと思います。

for 文

とても基本的ですが、単に n 回ループを回したい時に REP マクロのような怪しいものに頼らずに済みます。

let n = 10;
for i in 0..n {
    solve(i);
}
型推論

型推論が強力で、宣言時に型を指定していなくても途中で型が決まるようなコードをコンパイルできます。

以下のコードだと、1行目では Vec<T> (C++ でいう std::vector<T> 相当のもの。vec![] は要素が空の Vec<T> を宣言するためのマクロ) を宣言しており、1行目の段階では T が何か決まっていません。 2行目を見ると要素に i32 (32ビット整数) を追加しているので、ここで T=i32 が決定されます。

let mut v = vec![];
v.push(1i32);

少ないタイプ数で書けるので便利。

タプル

C++ の std::pair などに比べ、値の組(タプル)についてかなり自然に扱うことができます。

以下のコードでは、値の組を返す some_function という関数に対し、その返り値を別々の変数で受け取る方法を示しています。C++std::tie などを使うより自然に受け取れるのが見て取れます。

fn some_function() -> (i32, i32) {
    (123, 456)
}

fn main() {
    let (x, y) = some_function();
    println!("x={}, y={}", x, y);  // => "x=123, y=456"
}
構造体

構造体のコンストラクタは key: value の形で行います。タイプ数が若干増えますが意味はより明示的になります。以下は円を表す構造体の定義とそのインスタンスを作るコードです。

struct Circle {
    x: f64,
    y: f64,
    r: f64,
}

fn main() {
    let my_circle = Circle {
        x: 1.23,
        y: 4.56,
        r: 7.89,
    };
}

毎回 key: value の形で書くのは冗長ですが、もし value が変数で、その変数名が key と一致しているなら、"key: " 部分の記述を省略することができます。(ただしコンパイラのバージョンがある程度新しくないと使えない模様…。)

    let x = 1.23;
    let y = 4.56;
    let r = 7.89;
    let my_circle = Circle { x, y, z, };

便利機能として、構造体の宣言時に #[derive(Debug)] というアノテーションを付けておくと、println! などで変数のインスタンスを指定した時にそのインスタンスの内容をそのまま出力できるようになります。 この機能はデバッグ時にかなり便利です。

#[derive(Debug)]
struct Circle {
    x: f64,
    y: f64,
    r: f64,
}

fn main() {
    let my_circle = Circle {
        x: 1.23,
        y: 4.56,
        r: 7.89,
    };
    println!("{:?}", my_circle);  // => "Circle { x: 1.23, y: 4.56, r: 7.89 }"
}
配列のスライス

部分列への参照に簡単にアクセスできます。以下は v という可変長配列の1番目から2番目までの部分列への参照を取っています。

let v = vec![0, 100, 200, 300, 400];
let subseq = &v[1..3];
println!("{:?}", subseq);  // => "[100, 200]"
列挙型

C++ やその他の多くの言語の列挙型 (enum) は単に定数を並べて定義するものに過ぎませんが、それに比べて Rust の列挙型は強力なものになっています。列挙型の項はそれぞれ独自の値の組を持つことができます。

以下はよく使われる Option<T> 型です。何か値を指定したいときには Some(T) を持つ(T はその値の型)けど、何も指定したくないときには None にしておくといった使い方をします。

enum Option<T> {
    None,
    Some(T),
}

競プロの文脈で使うなら、例えばメモ化計算の際に、メモ配列に上記Option<T> 型を用いて、計算済みの要素は Some(T) を割り当て、まだ計算が済んでいない要素には None を割り当てると言った用途がありえそうです。

パターンマッチ

列挙型の項の値 (T) は if letmatch といった構文で取り出せます。以下は Option<T> 型を用いてフィボナッチ数をメモ化再帰で計算する例。

fn fib(i: usize, memo: &mut Vec<Option<i64>>) -> i64 {
    if i <= 1 {
        return 1;
    }
    if let Some(x) = memo[i] {  // もし memo[i] が Some(T) だったら、
                                // x に T を代入して if ステートメント内を実行する
        return x;
    }
    let retval = fib(i - 1, memo) + fib(i - 2, memo);
    memo[i] = Some(retval);
    retval
}
変数名の再利用

同じスコープ内であっても変数名の上書きができます。これにより、似たような機能の変数だけど名前が被っているせいで別の名前を付けないといけない、みたいなことがなくなります。

let mut data;  // 何かのデータを表す変数 data
data.push(100);
// ...
let data = convert(data); // data に変換をかけたものを data という名前にする
代入文での if

代入文で if が使えます。三項演算子と違って、if ステートメントの内部で処理を行えるのが特徴的です。 以下では変数 some_important_flag が真のときには1つ目の if ステートメントの返り値(12345)がxに代入され、そうでないときは2つ目のものが代入されます。

let x = if some_important_flag {
    12345
} else {
  let mut val = 0;
  for i in 0..10 { val += i; }
  val
}

使用頻度は多くないですがいざというときに便利かもしれない。

ツールチェーン

言語の機能だけでなく、どういうツールが使えるのかもプログラミング言語の重要な要素です。

外部ライブラリのインストール

ローカル実行のできるコンテスト(Google Code Jam, Facebook Hacker Cup 等)に限られますが、Cargo というインストール時に標準で入ってくるバイナリを使うと外部ライブラリを簡単にインストールできます。python 使っている人なら pip のようなものだと思うとわかりやすいです。

例えば多倍長整数ライブラリである num をインストールするには、Cargo.toml というプロジェクトファイルに以下のような文字列を1行追加して cargo build のようなコマンドを叩くだけで良いです。

num = "0.1.41"
コードのフォーマット

Rust にはコードを自動で整形するツールが標準で入っています。rustfmtcargo fmt がそれに相当します。例えば以下のようなコードがあったとして、

let x=3 as f64;
let y=4 as f64;
let r=(x*x+y*y).sqrt();
println!("{}",r);

整形すると以下のようになってくれます。

let x = 3 as f64;
let y = 4 as f64;
let r = (x * x + y * y).sqrt();
println!("{}", r);

こういった標準の整形ツールが存在することで、スペースの空け方や改行位置の調整といったような表面的なことにあまり拘らなくて済むようになるのはありがたいかもしれない。

入力処理

Rust には C++ と違って scanf や std::cin のような手軽に競プロっぽいフォーマットの入力を標準入力で受け取る関数が無いため、ある程度自分でスキャナー相当のものを書いておいたほうが便利になります。

ここでは自分が使っているものを紹介します。read を呼び出すとトークンを文字列から所望の型に変換して返して暮れます。トークンの前後に余分なスペースや改行などがあっても使えます。

use std::io::{stdin, Read, StdinLock};
use std::str::FromStr;

struct Scanner<'a> {
    cin: StdinLock<'a>,
}
 
impl<'a> Scanner<'a> {
    fn new(cin: StdinLock<'a>) -> Scanner<'a> {
        Scanner { cin: cin }
    }
 
    fn read1<T: FromStr>(&mut self) -> Option<T> {
        let token = self.cin.by_ref().bytes().map(|c| c.unwrap() as char)
            .skip_while(|c| c.is_whitespace())
            .take_while(|c| !c.is_whitespace())
            .collect::<String>();
        token.parse::<T>().ok()
    }
 
    fn read<T: FromStr>(&mut self) -> T {
        self.read1().unwrap()
    }
}

以下のような感じで使えます。

fn main(){
    let cin = stdin();
    let cin = cin.lock();
    let mut sc = Scanner::new(cin);  // スキャナの定義

    let n: i32 = sc.read();  // 普通にトークンを1つ読みたいとき
    while let Some(n) = sc.read1() {  // こっちはICPC形式などでEOFで入力が終わるとき用
        // ...
    }
}

もしかすると他にももっと効率の良い実装方法があるかもしれません。

標準ライブラリ

入力が取れれば後はなんでもできます。C++の標準関数やSTLで代表的なものと Rust の標準ライブラリとの対応を貼っておきます。

ライブラリ C++ Rust
入出力関数 cstdio std::io
数学関数 cmath std::f64 など
可変長配列 std::vector Vec
キュー std::queue VecDeque
優先度付きキュー std::priority_queue BinaryHeap
文字列 std::string String から Vec<char> などに変換して扱うのが無難そう
連想配列 std::map std::collections::BTreeMap
集合 std::set std::collections::BTreeSet
ペア、タプル std::pair, std::tuple タプル
アルゴリズム std::algorithm 色んな所に散らばっている(next_permutation, lower_bound など一部関数は未実装)
関数オブジェクト std::functional クロージャ
ビットセット std::bitset BitSet, BitVec (ただし Unstable)
乱数 rand, std::random 標準では未実装、外部ライブラリの rand が有名
複素数 std::complex 標準では未実装、外部ライブラリの num が有名

このように、主要なものはだいたい標準ライブラリでカバーしているものの、一部は未実装だったり外部ライブラリが必要だったりするのは注意が必要かもしれません。

その他

let mut とか毎回書くのは面倒そう

競プロでもコードを書いてると変数が実は定数でいい(let だけでよい)ことは結構あるので、毎回 let mut と書かないといけないわけではないです。

コンパイラが厳格で大変そう

所有権まわりの制約は結構厳しそうに見えますが、理不尽なコンパイルエラーに遭遇することはそこまで多くない印象です。 もちろん、コードを書いてて何度か意味を理解するのが難しいコンパイルエラーに遭遇したことはあります。ただ、ちょっとややこしい状況でややこしいコンパイルエラーが出るのは C++ でも同じなので、Rust だけ理不尽かというとあまりそんなことはない気がします。

とはいえもちろん Rust もまだまだ開発中の言語なので、かなりトリッキーな例は存在するようです。例えば Rust Book の最後の章ではそういった例が紹介されています。注意をするに越したことはありません。

Rust で書かれた競プロのライブラリとかって無いの?

ちょっと調べた感じだと EbTech 氏のライブラリはとてもまとまっている印象を受けました。 ただ、これもまだまだ実装は部分的で、例えば Spaghetti source ほどの充実性は無いように見えます。

逆に言えば、今なら Rust で充実したライブラリ集を出せば大いに注目を浴びる可能性はあります。

まとめ

Rust で競プロする話について書きました。面白い言語だと思うので競プロでも普及すると良いですね。

京都旅行

ちょうど木曜が祝日で良い感じだったので、金曜と月曜を有給にして5連休を取ったので京都旅行をエンジョイした。以下はローカルな話が多い日記。

1日目

  • 朝は比較的だらだらしていたので、昼過ぎに東京を出発する。駅でいくら丼を買ったので移動中に新幹線で食べる。美味しい。
  • 京都に着くともう日が暮れていたので宿に荷物を置いて、北白川の四川料理屋である駱駝に行く。辛くて美味しい。
  • この日は特に何もせず終了。

2日目

  • 昼頃に起きたので鴨川沿いのオシャレっぽいカフェで朝飯を食べる。昨日はあまりちゃんと見れなかったが、この時期は葉っぱが赤色、黄色、緑色などのグラデーションになっていて、綺麗。
  • 京大がちょうど学園祭中だったので、中を通り過ぎて雰囲気だけ味わう。途中で生協に寄ってなんとなく教科書とかを眺める。昔読んでた本の改訂版などが置いてあったりして懐かしい。
  • 京都動物園に行く。モルモットの群れが一心不乱に餌を食べている様子が可愛かったので結構長時間眺める。
  • 知り合いの家に行って協力ゲームの Overcooked を遊ぶ。どうすれば効率をよく出来るかを話し合いながら進めるのが面白いけど、微妙に仕事っぽさがあって疲れるような気もする。

3日目

  • 昼頃に起きる。目当てのレストランが開いてなかったので仕方ないので近くにあったハイライトに入る。なぜか突然チャレンジ精神が芽生え、カラフルジャンボチキンカツを頼む。しかしもう僕はハイライト適正の年齢では無いので、食事を楽しめたとは言い難かった。
  • 銀閣寺付近の通りを経て大文字山を登った。観光シーズンだったので人が多い。大文字山は結構気軽に登れる割に達成感もあるので良い。この日を含めて2回しか登ったことないけど。
  • 夕方以降はサークルのOB/OG会があったので宴会に行った。

4日目

  • 昼頃に起きる。百万遍モダン焼きフジに行く。
  • 宇治に向かう。特に下調べなしで行ったので何があるのかよく知らなかったのだが、街は観光地としてかなり賑わっていた。神社、喫茶店、土産屋、平等院鳳凰堂あたりを周る。全体的に抹茶推しが強くて、抹茶たこ焼きなんてのもあった。

5日目

  • 10時チェックアウトなので早起きした。下鴨神社をうろうろした後でカナート洛北に行った。
  • カナート洛北付近は数年前あたりからパチンコ店の建設を進めようとする業者とそれに猛反対する近隣住民達の闘いが繰り広げられていたようなのだが、最近になって住民側の勝利で終わったらしく、建設予定地だった広大な土地が高いフェンスで囲われただけの何もない場所となっていた。
  • 荷物が重かったためあまり遠出する気にはならなかったのでその後は百万遍付近の松ノ助で昼飯を食べて新幹線に乗って帰宅した。