NVIDIA has released a quantum computer simulator, cuStateVec. It shows its power on multiple GPUs, but blueqat does not currently consider multi-GPU support, and we recommend using it with cirq + qsim from google. However, if you want to use cirq or qsim on a standalone GPU, I think it is difficult to use cirq or cuStateVec directly.
Since I have not been able to explore cuStateVec in depth (as we are working on cuTensorNet a lot), but I will try to connect it to blueqat in the sense that it can be used for the time being.
1,Prepare the state vector in the initial state.
2, Define and apply gates at each gate stage.
This is all. It is easy to do.
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 you want a new gate please write down here.
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)
it's done.
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]
It worked. If you add a sampling function or an expectation value calculation function, you can use it for NISQ as usual. You can use the existing blueqat application assets so please try it out. We will make more if there are any requests.