N-QueensパズルのQUBOをnealのシミュレーティド・アニーリングで解く

# ポイント
* D-Waveの[neal](https://docs.ocean.dwavesys.com/projects/neal/en/latest/)を使って，シミュレーティド・アニーリングでN-Queenパズルを解きます．

# D-Wave neal
[シミュレーティド・アニーリング](https://ja.wikipedia.org/wiki/%E7%84%BC%E3%81%8D%E3%81%AA%E3%81%BE%E3%81%97%E6%B3%95)は，最適化問題を解くためのすぐれた近似解法です．QUBOをシミュレーティド・アニーリングで解くツールである[neal](https://docs.ocean.dwavesys.com/projects/neal/en/latest/)がD-Waveから提供されています．そのnealを使ってN-Queensパズルを解きます．N-QueensパズルのQUBOへの変換は，[N-QueensパズルのQUBOを高速生成し，Gurobiで解く](https://qiita.com/koji_nakano/items/d873a80dc577f00c898c)の方法を用いています．この方法はバイナリ変数$X=(x_{i,j))$を使用してボードを表現し，次のルールでN-QueensパズルのQUBOを求めます．
* 全ての1次項$x_{i,j}$の係数を$-1$と設定することで，$x_{i,j}=1$となるバイナリ変数の個数を最大化します
* 座標$(i,j)$と$(i',j')$が同一行、同一列、または同一斜めライン上にある場合、2次項$x_{i,j}x_{i',j'}$の係数を$+1$と設定し，Queenが衝突することをペナルティとします 

# Pythonプログラム
辞書`qubo`にQUBOの係数を設定します．タプル`(i,j)`は$x_{i,j}$に対応します．辞書`qubo`のキーは，このようなタプルのタプルであり，キー`((i,j),(i',j'))`には$x_{i,j}x_{i',j'}$の係数が設定されます．この辞書`qubo`を引数としてnealのサンプラー使用してシミュレーティド・アニーリングを実行します．シミュレーティド・アニーリングは指定した回数実行され，ベストの解が表示しています．

```Python
#!/usr/bin/env python3

import argparse
import neal

parser = argparse.ArgumentParser(
    description="Solve N-Queens puzzle through PyQUBO using D-Wave neal"
)
parser.add_argument(
    "-n", default=32, type=int, help="Value of N of N-QUEENS (default:32)"
)
parser.add_argument(
    "-t", default=10, type=int, help="Simulated annealing count (default:10)"
)
args = parser.parse_args()

qubo = {}
for x in range(args.n):
    for y in range(args.n):
        # Reward for placing a queen
        qubo[((x, y), (x, y))] = -1
        for i in range(x + 1, args.n):
            # Penalty for placing two queens in the same row
            qubo[((x, y), (i, y))] = 1
        for j in range(y + 1, args.n):
            # Penalty for placing two queens in the same column
            qubo[((x, y), (x, j))] = 1
        for d in range(1, args.n - x):
            if y - d >= 0:
                # Penalty for placing two queens on the same anti-diagonal
                qubo[((x, y), (x + d, y - d))] = 1
            if y + d < args.n:
                # Penalty for placing two queens on the same diagonal
                qubo[((x, y), (x + d, y + d))] = 1

# Initialize the simulated annealing sampler
sa_sampler = neal.SimulatedAnnealingSampler()
# Execute simulated annealing to find a solution
response = sa_sampler.sample_qubo(qubo, num_reads=args.t)
# Extract the best solution found during the simulated annealing
solution = response.first.sample
# Obtain the energy (objective value) of the best solution
energy = int(response.first.energy)

# Print the best solution
for i in range(args.n):
    for j in range(args.n):
        print(solution[(i, j)], end="")
    print()
print(f"optimal={-args.n} obtained={energy} trials={args.t}")
```

# 実行結果
32-Queensパズルの最適解が得られています．
```
00000010000000000000000000000000
00000000000000000000010000000000
00000000000000000100000000000000
10000000000000000000000000000000
00001000000000000000000000000000
00000000000000000000000000100000
00000001000000000000000000000000
00000000000000001000000000000000
00000000000001000000000000000000
00000000000100000000000000000000
00000000100000000000000000000000
00000000000000010000000000000000
00100000000000000000000000000000
00000000000000000000000000000100
01000000000000000000000000000000
00000000000000000000000100000000
00000000000000000000000000000010
00000000000000000000000000001000
00000000001000000000000000000000
00000000000000000000001000000000
00000000000000000000000000010000
00000000000000000000000000000001
00010000000000000000000000000000
00000000000000100000000000000000
00000000000000000001000000000000
00000000000000000000000010000000
00000000010000000000000000000000
00000000000010000000000000000000
00000100000000000000000000000000
00000000000000000000000001000000
00000000000000000010000000000000
00000000000000000000100000000000
optimal=-32 obtained=-32 trials=10
```