💩

【欠陥あり】イジングモデルで作るQRコード【強引】

2024/10/03に公開

はじめに

1年ほど前にQRコードについてのポッドキャストを聴き、それ以降の人生ではたびたびQRコードについて考えることがありました。そして、学部時代に学び最近になってもう一度出てきたイジングモデルをぼーっと眺めていた時に、「イジングモデルを使ってQRコード作れるんじゃね?」と思ったので作ってみました。

物理とPython、そしてQRコードを浅く知っているZenn初投稿者なので、改善点があれば優しく教えてください🥳
https://www.youtube.com/watch?v=Zu3DUeKNHec

結論

結果としてはうまく読み込めるQRコードを生成することができなかった。

イジングモデルについて理解したこと⚡

概要
統計物理学において磁性体などを記述するために用いられる格子模型。格子点上に配置されたスピンが相互作用することで全体としてどのような振る舞いを見せるかを解析するモデル。系全体が低エネルギー状態に向かうようになっている。

基本的な要素:

  • スピン: 各格子点には、上向き(+1)または下向き(-1)のいずれかの状態をとるスピンが存在。
  • 相互作用: 隣り合うスピン同士は相互作用し、同じ方向を向いているとエネルギーが低くなり、逆向きだとエネルギーが高くなる。
  • 温度: 系の温度は、スピンの向きがランダムに反転する度合いを表す。温度が高いほど、スピンの向きはランダムになり、低いほど、スピンはエネルギーの低い状態、つまり同じ方向を向きやすくなる。

エネルギーの計算方法

E = -J\sum_{\langle i,j \rangle} s_i s_j - h\sum_i s_i

  • E: システム全体のエネルギー

  • J: 隣接スピン間の相互作用の強さを表す結合定数

  • s_i, s_j: i番目とj番目のサイトのスピン(+1または-1の値をとる)

  • h: 外部磁場の強さ

  • \sum_{\langle i,j \rangle}: 最近接サイトのペアについての和

  • \sum_i: すべてのサイトについての和

メトロポリス法

イジングモデルの振る舞いを解析する手法の一つに、メトロポリス法を用いたモンテカルロシミュレーションがある。

メトロポリス法のアルゴリズム:

  1. ランダムに格子点を選び、そのスピンを反転させる。
  2. スピン反転前後のエネルギー差\Delta Eを計算する。
  3. \Delta E < 0 ならば、スピン反転を受け入れる。
  4. \Delta E > 0 ならば、確率 e^{-\frac{\Delta E}{k_B T}} でスピン反転を受け入れる。(k_Bはボルツマン定数、Tは温度)
  5. 上記の手順を繰り返し行うことで、系の平衡状態をシミュレートする。

メトロポリス法を用いることで、様々な温度や外部磁場におけるイジングモデルの性質を調べることができる。

今回のコードでは外部磁場については考えてはいません(面倒臭かった)

QRコードについて理解したこと👁️‍🗨️

QRコードのモデルとバージョン:

  • モデル: QRコードには、モデル1とモデル2の2つのモデルがある。モデル1は初期のバージョンで、モデル2はエラー訂正能力やデータ容量が向上したバージョン。
  • バージョン: QRコードのバージョンは、データ容量とコードのサイズを表す指標。バージョン1は21x21セル、バージョン40は177x177セルまで、バージョンが上がるにつれてセル数が増加し、より多くのデータを格納できる。

QRコード必要4要素:

  1. 位置検出パターン❤️: QRコードの3つの角に配置され、QRコードの位置と向きを認識するために使用。白セルと黒セルの比率が1:1:3:1:1の正方形。
  2. アライメントパターン🌲: モデル2のQRコードでは必須。位置検出パターン以外の場所に配置され、QRコードの歪みを補正するために使用。右下から斜めに7マス目を中心に白セルと黒セルの比率が1:1:1の正方形。
  3. タイミングパターン🌀: 位置検出パターンを結ぶように配置され、QRコードのサイズを認識するために使用。位置検出パターンの内側を繋ぐように白黒を交互に配置。
  4. クワイエットゾーン: 2次元コードの周りの空白。4セル必要。

QRコード例

実装

コードの読みやすさとかは考えられていないので少し冗長かも。
1. 初期化🏖️: Model,Versionを入力しなければただのイジングモデルとして使えるようにする。Modelが入力されたらinitiate()を実行。QRコードとして必須な部分はset()で管理され、反転されないようにする。

class Ising:
    def __init__(self, L, T, Model=0, Version=None):
        self.L = L
        if Version:
            self.L = 17 + 4 * Version
        self.T = T
        self.spins = np.random.choice([-1, 1], size=(self.L, self.L))
        self.essence = set()
        self.model = Model
        self.version = Version
        if Model:
            self.initiate()
    def initiate(self):
        for i in range(3):
            self.make_eye(i)
        for i in range(2):
            self.make_timingpatern(i)
        for i in range(3):
            self.make_separater(i)
        if self.model == 2:
            self.make_alignment()
  1. 位置検出パターンの作成❤️: 3つの位置検出パターンをそれぞれ配置する。パターンの見た目が👀ぽかったので関数名はeyeにした。
    def make_eye(self, n):
        if n == 0:
            x, y = 0, 0
        elif n == 1:
            x, y = 0, self.L - 7
        elif n == 2:
            x, y = self.L - 7, 0

        for i in range(x, x + 7):
            for j in range(y, y + 7):
                self.essence.add((i, j))
                self.spins[i, j] = -1
        for i in range(x + 1, x + 6):
            for j in range(y + 1, y + 6):
                self.spins[i, j] = 1
        for i in range(x + 2, x + 5):
            for j in range(y + 2, y + 5):
                self.spins[i, j] = -1
  1. アライメントパターン🌲: そのまま
    def make_alignment(self):
        x, y = self.L - 9, self.L - 9
        for i in range(5):
            for j in range(5):
                self.essence.add((x + i, y + j))
                self.spins[x + i, y + j] = -1
        for i in range(3):
            for j in range(3):
                self.spins[x + i + 1, y + j + 1] = 1
        self.spins[x + 2, y + 2] = -1
  1. タイミングパターン🌀: そのまま
    def make_timingpatern(self, n):
        if n == 0:
            x, y = 7, 6
            for i in range(x, x + self.L - 14):
                self.essence.add((i, y))
                if i % 2 == 0:
                    self.spins[i, y] = -1
                else:
                    self.spins[i, y] = 1
        elif n == 1:
            x, y = 6, 7
            for i in range(y, y + self.L - 14):
                self.essence.add((x, i))
                if i % 2 == 0:
                    self.spins[x, i] = -1
                else:
                    self.spins[x, i] = 1
  1. 電子を1つ選択し、反転させる💞: ランダムに格子点を1つ選択する。その際選択した格子点がQRコードのエッセンスであった場合反転操作はせずにそのままにする。
    def choice(self):
        i, j = np.random.randint(self.L, size=2)
        if (i, j) in self.essence:
            return
        now_energy = self.calc_energy(i, j)
        self.spins[i, j] *= -1
        new_energy = self.calc_energy(i, j)
        dE = new_energy - now_energy
        if dE < 0:
            return
        if np.random.rand() < np.exp(-dE / self.T):
            return
        self.spins[i, j] *= -1

    def calc_energy(self, i, j):
        energy = 0
        for di, dj in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
            energy -= (
                self.spins[i, j] * self.spins[(i + di) % self.L, (j + dj) % self.L]
            )
        return energy

生成されたQRコード🏭

実際に出力された画像は上に示したQRコードの4つのエッセンスを保ちながら生成されていることがわかる。(読み込めないけど)
モデル2,バージョン3(読み込めないやつ)
温度をいい感じにすると臨界状態を揺らぎ続けるのでいつかいい感じのQRコードが生成されるのですかne?🍿

もちろん温度を下げると巨大なクラスターが生成され、秩序的❓️なQRコードが生成されるのがわかる。🙉

おわりに

今回のコードだとうまくイジングモデルからQRコードを生成することができなかったので、QRコード、Pythonに詳しいお兄様お姉様方がいらっしゃいましいたら、こうした方がいいよなど指摘していただけると喜びます😌

参考にしたサイト

https://www.keyence.co.jp/ss/products/autoid/codereader/basic2d_qr.jsp
https://time-space.kddi.com/mobile/20190425/2624.html

Discussion