NVIDIAから量子コンピュータシミュレータのcuStateVecがでました。マルチGPUでその威力を発揮するのですが、blueqatでは今のところマルチGPU対応は考えておらず、googleのcirqやqsimを入れて使うのがおすすめですが、単体GPUでサクッと使いたいというご要望の場合にはcirqから使うのもあれだし、cuStateVec直接使うのも難しいという感じかと思います。
ちょっとcuStateVecをあまり深く追求できてないので、パフォーマンスは落ちるかもしれませんが、とりあえず使えるという意味でblueqatにつないでみます。
1,初期状態で状態ベクトルを準備
2,あとは各ゲート段階でゲートを定義してかける
のみです。簡単にできます。
import numpy as np
from blueqat.backends.backendbase import Backend
from blueqat import BlueqatGlobalSetting
from blueqat import Circuit
import cupy as cp
import cuquantum
from cuquantum import custatevec as cusv
class CuSv(Backend):
def _preprocess_run(self, gates, n_qubits, args, kwargs):
h_sv = np.asarray(np.full(2**n_qubits,0.0+0.0j), dtype=np.complex64)
h_sv[0] = 1.0+0.0j
d_sv = cp.asarray(h_sv)
return gates, (d_sv, n_qubits)
def _postprocess_run(self, ctx):
print(ctx[0])
return
def _one_qubit_gate_noargs(self, gate, ctx):
for idx in gate.target_iter(ctx[1]):
#欲しいゲートはこちらに記述
if gate.lowername == "x":
matrix = cp.asarray([0.0+0.0j, 1.0+0.0j, 1.0+0.0j, 0.0+0.0j], dtype=np.complex64)
else:
matrix = cp.asarray([1.0+0.0j, 0.0+0.0j, 0.0+0.0j, 1.0+0.0j], dtype=np.complex64)
nIndexBits = ctx[1]
nSvSize = (1 << nIndexBits)
nTargets = 1
nControls = 0
adjoint = 0
targets = np.asarray([idx], dtype=np.int32)
controls = np.asarray([], dtype=np.int32)
if isinstance(matrix, cp.ndarray):
matrix_ptr = matrix.data.ptr
elif isinstance(matrix, np.ndarray):
matrix_ptr = matrix.ctypes.data
else:
raise ValueError
# cuStateVec handle initialization
handle = cusv.create()
workspaceSize = cusv.apply_matrix_get_workspace_size(
handle, cuquantum.cudaDataType.CUDA_C_32F, nIndexBits, matrix_ptr, cuquantum.cudaDataType.CUDA_C_32F,
cusv.MatrixLayout.ROW, adjoint, nTargets, nControls, cuquantum.ComputeType.COMPUTE_32F)
# check the size of external workspace
if workspaceSize > 0:
workspace = cp.cuda.memory.alloc(workspaceSize)
workspace_ptr = workspace.ptr
else:
workspace_ptr = 0
# apply gate
cusv.apply_matrix(
handle, ctx[0].data.ptr, cuquantum.cudaDataType.CUDA_C_32F, nIndexBits, matrix_ptr, cuquantum.cudaDataType.CUDA_C_32F,
cusv.MatrixLayout.ROW, adjoint, targets.ctypes.data, nTargets, controls.ctypes.data, 0, nControls,
cuquantum.ComputeType.COMPUTE_32F, workspace_ptr, workspaceSize)
# destroy handle
cusv.destroy(handle)
return ctx
gate_x = _one_qubit_gate_noargs
gate_y = _one_qubit_gate_noargs
gate_z = _one_qubit_gate_noargs
gate_h = _one_qubit_gate_noargs
gate_t = _one_qubit_gate_noargs
gate_s = _one_qubit_gate_noargs
def _one_qubit_gate_args_theta(self, gate, ctx):
return ctx
gate_rx = _one_qubit_gate_args_theta
gate_ry = _one_qubit_gate_args_theta
gate_rz = _one_qubit_gate_args_theta
gate_phase = _one_qubit_gate_args_theta
def _two_qubit_gate_noargs(self, gate, ctx):
for control, target in gate.control_target_iter(ctx[1]):
#欲しいゲートはこちらに記述
if gate.lowername == "cx":
matrix = cp.asarray([0.0+0.0j, 1.0+0.0j, 1.0+0.0j, 0.0+0.0j], dtype=np.complex64)
else:
matrix = cp.asarray([1.0+0.0j, 0.0+0.0j, 0.0+0.0j, 1.0+0.0j], dtype=np.complex64)
nIndexBits = ctx[1]
nSvSize = (1 << nIndexBits)
nTargets = 1
nControls = 1
adjoint = 0
targets = np.asarray([target], dtype=np.int32)
controls = np.asarray([control], dtype=np.int32)
if isinstance(matrix, cp.ndarray):
matrix_ptr = matrix.data.ptr
elif isinstance(matrix, np.ndarray):
matrix_ptr = matrix.ctypes.data
else:
raise ValueError
# cuStateVec handle initialization
handle = cusv.create()
workspaceSize = cusv.apply_matrix_get_workspace_size(
handle, cuquantum.cudaDataType.CUDA_C_32F, nIndexBits, matrix_ptr, cuquantum.cudaDataType.CUDA_C_32F,
cusv.MatrixLayout.ROW, adjoint, nTargets, nControls, cuquantum.ComputeType.COMPUTE_32F)
# check the size of external workspace
if workspaceSize > 0:
workspace = cp.cuda.memory.alloc(workspaceSize)
workspace_ptr = workspace.ptr
else:
workspace_ptr = 0
# apply gate
cusv.apply_matrix(
handle, ctx[0].data.ptr, cuquantum.cudaDataType.CUDA_C_32F, nIndexBits, matrix_ptr, cuquantum.cudaDataType.CUDA_C_32F,
cusv.MatrixLayout.ROW, adjoint, targets.ctypes.data, nTargets, controls.ctypes.data, 0, nControls,
cuquantum.ComputeType.COMPUTE_32F, workspace_ptr, workspaceSize)
# destroy handle
cusv.destroy(handle)
return ctx
gate_cx = gate_cy = gate_cz = _two_qubit_gate_noargs
def _three_qubit_gate_noargs(self, gate, ctx):
return ctx
gate_ccx = _three_qubit_gate_noargs
gate_cswap = _three_qubit_gate_noargs
def gate_measure(self, gate, ctx):
return ctx
gate_reset = _one_qubit_gate_noargs
BlueqatGlobalSetting.register_backend('cusv', CuSv)
from blueqat import Circuit
Circuit(3).x[0,1,2].cx[0,1].run(backend="cusv")
[0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j]
動きました。サンプリング機能をつけたり、期待値計算機能をつけたりすれば普通にNISQ向けに使えます。これまでのblueqatのアプリケーション資産もそのまま使えますので、ぜひ試してみてください。要望あれば作りこみます。