はじめに
量子計算を行う際に初期状態を欲しい形にしたい場合があると思います。
特に量子ニューラルネットワークなどで必要かと思われます。
仮に
\frac{|000>+|001>-|010>+|011>-|100>-|101>+|110>+|111>}{2 \sqrt{2}}
を作るgateの組み合わせがすぐ分かれば便利じゃありませんか?
それを実現する考え方がQuantum Hypergraph Stateです。
詳しくは Quantum Hypergraph statesをご覧ください。
Quantum Hypergraph states
軽くQuantum Hypergraph statesの説明をしたいと思います。
\frac{i_0|000>+i_1|001>+i_2|010>+i_3|011>+i_4|100>+i_5|101>+i_6|110>+i_7|111>}{2 \sqrt{2}} \\
i_k = -1 \ or \ 1
- 係数が$-1$である$|0...1...0>$が存在するとき、$1$であるqubitに対してZ gateを作用させる。
- 係数を更新する。
- 係数が$-1$である$|0...1...1...0>$が存在するとき、$1$であるqubitに対してCZ gateを作用させる。
- 係数を更新する。
- 係数が$-1$である$|0...1...1...1...0>$が存在するとき、$1$であるqubitに対してCCZ gateを作用させる。
3qubitの場合は、これですべての係数が1になります。
例
\frac{|000>+|001>-|010>+|011>-|100>-|101>+|110>+|111>}{2 \sqrt{2}}
- $|010>$と$|100>$の係数が$-1$なので、第二qubitと第三qubitにZ gateを作用させます。その結果、
\frac{|000>+|001>+|010>-|011>+|100>+|101>+|110>+|111>}{2 \sqrt{2}}
- $|011>$の係数が$-1$なので、第一qubitと第二qubitにCZ gateを作用させます。その結果、
\frac{|000>+|001>+|010>+|011>+|100>+|101>+|110>-|111>}{2 \sqrt{2}}
- $|111>の係数が$-1$なので、第一qubitと第二qubitと第三qubitにCCZ gateを作用させます。その結果、
\frac{|000>+|001>+|010>+|011>+|100>+|101>+|110>+|111>}{2 \sqrt{2}}
以上で回路の構築は終わりです。
Code
Codeは以下の通りです。正直Pythonが得意なわけでは無いので、無駄な箇所があるかもしれません。効率的な書き方があったら教えてくださると有難いです。
ちなみに、今回は3qubitまでのものですが、同じようにすればいくらでも増やせると思います。
# matplotlib inline
# coding: utf-8
import numpy as np
from math import log2
from copy import deepcopy
from qiskit import QuantumCircuit, QuantumRegister
from qiskit.quantum_info.operators import Operator
import matplotlib.pyplot as plt
def count_ones_by_bin(num):
bin_num = bin(num)[2:]
count = 0
for i in bin_num:
count += int(i)
return count
class QuantumHypergraphState:
def __init__(self, numqubits, states, qubit_index=None):
''' make Quantum HyperGraph State in circuit
:param circuit:
:param numqubits: maximum 3
:param states:
'''
self.numqubits = numqubits
self.states = deepcopy(states)
if qubit_index is None:
self.index = [i for i in range(numqubits)]
else:
self.index = qubit_index
def bin_length(self, num):
bin_num = bin(num)[2:]
dif_len = self.numqubits - len(bin_num)
for i in range(dif_len):
bin_num = '0' + bin_num
return bin_num
def get_z_tgt(self, num):
bin_num = self.bin_length(num)[::-1]
z_tgt = []
for i in range(len(bin_num)):
if int(bin_num[i]) == 1:
z_tgt.append(i)
return z_tgt
def tgt_0(self, num, tgt):
"""
e.g. tgt = [0]
num = 011
"""
bin_num = self.bin_length(num)[::-1] # 011
count = 0
for i in range(len(bin_num)):
if i in tgt:
count += int(bin_num[i])
if count == len(tgt):
return True
else:
return False
def renew_states(self, tgt):
for st in range(len(self.states)):
if self.tgt_0(st, tgt):
self.states[st] *= -1
def get_tgt_list(self, idx_list):
tgt_list = []
for i in range(len(idx_list)):
tgt_list.append(self.index[idx_list[i]])
return tgt_list
def construct_circuit(self, circuit, inverse=False):
ccz = Operator([[1, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, -1]])
if inverse:
gate_list = []
else:
circuit.h(self.index)
for num in range(1, self.numqubits + 1):
# statesの分だけloop
if num == 1:
for st in range(len(self.states)):
if count_ones_by_bin(st) == num:
if self.states[st] == -1:
idx = int(log2(st))
tgt = self.index[idx]
if inverse:
gate_list.append(['z', [tgt]])
else:
circuit.z(tgt)
self.renew_states([idx])
elif num == 2:
for st in range(len(self.states)):
if count_ones_by_bin(st) == num:
if self.states[st] == -1:
idx_list = self.get_z_tgt(st)
tgt_list = self.get_tgt_list(idx_list)
if inverse:
gate_list.append(['cz', tgt_list])
else:
circuit.cz(tgt_list[0], tgt_list[1])
self.renew_states(idx_list)
else:
for st in range(len(self.states)):
if count_ones_by_bin(st) == num:
if self.states[st] == -1:
idx_list = self.get_z_tgt(st)
tgt_list = self.get_tgt_list(idx_list)
if inverse:
gate_list.append(['ccz', tgt_list])
else:
circuit.unitary(ccz, self.index, label='ccz')
self.renew_states(idx_list)
if inverse:
gate_list = gate_list[::-1]
for gate in gate_list:
if gate[0] == 'ccz':
circuit.unitary(ccz, self.index, label='ccz')
if gate[0] == 'cz':
circuit.cz(gate[1][0], gate[1][1])
if gate[0] == 'z':
circuit.z(gate[1][0])
# circuit.h(self.index)
circuit.x(self.index)
return circuit
else:
return circuit
def draw(self):
print(self.qc.draw(output='mpl'))
以上です。ありがとうございました。
追記
まあまあエラーを吐き出していたので更新しました:2020/04/12