Merge pull request #44 from qiboteam/doc-fix

Fix the rendering issue, refine the content.
This commit is contained in:
Alessandro Candido
2024-03-04 16:44:40 +01:00
committed by GitHub
10 changed files with 235 additions and 59 deletions

View File

@@ -1,29 +1,30 @@
Quick start
===========
In this section, we provide an example of two qubit ciruit simulation using qibotn package in Qibo simulator. First, the backend is to be set with appropriate run card settings, followed by the circuit simulation using Qibo documentation.
Setting the backend
"""""""""""""""""""
QiboTN supports two backends cutensornet (using CuQuantum library) and Quimbbackend (using Quimb library) for tensor network based simulations. The backend can be set using the following command line.
For CuQuantum library,
QiboTN offers two backends: cutensornet (using cuQuantum library) and qutensornet (using Quimb library) for tensor network based simulations. At present, cutensornet backend works only for GPUs whereas qutensornet for CPUs. The backend can be set using the following command line.
.. testcode::
qibo.set_backend(backend="qibotn", platform="cutensornet", runcard=computation_settings)
..
To use cuQuantum library, cutensornet can be specified as follows::
and for Quimb library
.. testcode::
qibo.set_backend(
backend="qibotn", platform="QuimbBackend", runcard=computation_settings
backend="qibotn", platform="cutensornet", runcard=computation_settings
)
Similarly, to use Quimb library, qutensornet can be set as follows::
qibo.set_backend(
backend="qibotn", platform="qutensornet", runcard=computation_settings
)
..
Setting the runcard
"""""""""""""""""""
Basic structure of runcard is
.. testcode::
The basic structure of the runcard is as follows::
computation_settings = {
"MPI_enabled": False,
"MPS_enabled": False,
@@ -32,21 +33,44 @@ Basic structure of runcard is
"pauli_string_pattern": "IXZ",
},
}
..
**MPI_enabled:** Setting this option *True* results in parallel execution of circuit using MPI (Message Passing Interface). At present, only works for cutensornet platform.
**MPS_enabled:** This option is set *True* for Matrix Product State (MPS) based calculations where as general tensor network structure is used for *False* value.
**NCCL_enabled:** This is set *True* for cutensoret interface for further acceleration while using Nvidia Collective Communication Library (NCCL).
**expectation_enabled:** This option is set *True* while calculating expecation value of the circuit. Observable whose expectation value is to be calculated is passed as a string in the dict format as {"pauli_string_pattern": "observable"}. When the option is set *False*, the dense vector state of the circuit is calculated.
Basic example
"""""""""""""
.. testcode::
# Construct the circuit
The following is a basic example to execute a two qubit circuit and print the final state in dense vector form using quimb backend::
# Set the quimb backend
qibo.set_backend(
backend="qibotn", platform="qutensornet", runcard=computation_settings
)
# Set the runcard
computation_settings = {
"MPI_enabled": False,
"MPS_enabled": False,
"NCCL_enabled": False,
"expectation_enabled": False,
}
# Construct the circuit with two qubits
c = Circuit(2)
# Add some gates
# Apply Hadamard gates on first and second qubit
c.add(gates.H(0))
c.add(gates.H(1))
# Execute the circuit and obtain the final state
result = c()
# Print the final state
print(result.state())
..

View File

@@ -25,8 +25,8 @@ The supported HPC configurations are:
Currently, the supported tensor network libraries are:
- [cuQuantum](https://github.com/NVIDIA/cuQuantum), an NVIDIA SDK of optimized libraries and tools for accelerating quantum computing workflows.
- [quimb](https://quimb.readthedocs.io/en/latest/), an easy but fast python library for quantum information many-body calculations, focusing primarily on tensor networks.
- `cuQuantum <https://github.com/NVIDIA/cuQuantum>`_, an NVIDIA SDK of optimized libraries and tools for accelerating quantum computing workflows.
- `quimb <https://quimb.readthedocs.io/en/latest/>`_, an easy but fast python library for quantum information many-body calculations, focusing primarily on tensor networks.
How to Use the Documentation
============================
@@ -50,10 +50,10 @@ Contents
getting-started/index
.. toctree::
:maxdepth: 2
:maxdepth: 1
:caption: Main documentation
api-reference/modules
api-reference/qibotn
Developer guides <https://qibo.science/qibo/stable/developer-guides/index.html>
.. toctree::
@@ -64,6 +64,9 @@ Contents
Qibolab docs <https://qibo.science/qibolab/stable/>
Qibocal docs <https://qibo.science/qibocal/stable/>
Qibosoq docs <https://qibo.science/qibosoq/stable/>
Qibochem docs <https://qibo.science/qibochem/stable/>
Qibotn docs <https://qibo.science/qibotn/stable/>
Qibo-cloud-backends docs <https://qibo.science/qibo-cloud-backends/stable/>
Indices and tables
==================

View File

@@ -18,6 +18,7 @@ CUDA_TYPES = {
class CuTensorNet(NumpyBackend): # pragma: no cover
# CI does not test for GPU
"""Creates CuQuantum backend for QiboTN."""
def __init__(self, runcard):
super().__init__()
@@ -92,6 +93,14 @@ class CuTensorNet(NumpyBackend): # pragma: no cover
super().set_precision(precision)
def cuda_type(self, dtype="complex64"):
"""Get CUDA Type.
Parameters:
dtype (str, optional): Either single ("complex64") or double (complex128) precision. Defaults to "complex64".
Returns:
CUDA Type: tuple of cuquantum.cudaDataType and cuquantum.ComputeType
"""
if dtype in CUDA_TYPES:
return CUDA_TYPES[dtype]
else:
@@ -100,15 +109,15 @@ class CuTensorNet(NumpyBackend): # pragma: no cover
def execute_circuit(
self, circuit, initial_state=None, nshots=None, return_array=False
): # pragma: no cover
"""Executes a quantum circuit.
"""Executes a quantum circuit using selected TN backend.
Args:
Parameters:
circuit (:class:`qibo.models.circuit.Circuit`): Circuit to execute.
initial_state (:class:`qibo.models.circuit.Circuit`): Circuit to prepare the initial state.
If ``None`` the default ``|00...0>`` state is used.
Returns:
xxx.
QuantumState or numpy.ndarray: If `return_array` is False, returns a QuantumState object representing the quantum state. If `return_array` is True, returns a numpy array representing the quantum state.
"""
import qibotn.eval as eval

View File

@@ -59,7 +59,7 @@ class QuimbBackend(NumpyBackend):
If ``None`` the default ``|00...0>`` state is used.
Returns:
xxx.
QuantumState or numpy.ndarray: If `return_array` is False, returns a QuantumState object representing the quantum state. If `return_array` is True, returns a numpy array representing the quantum state.
"""
import qibotn.eval_qu as eval

View File

@@ -26,6 +26,12 @@ class QiboCircuitToEinsum:
self.circuit = circuit
def state_vector_operands(self):
"""Create the operands for dense vector computation in the interleave
format.
Returns:
Operands for the contraction in the interleave format.
"""
input_bitstring = "0" * len(self.active_qubits)
input_operands = self._get_bitstring_tensors(input_bitstring)
@@ -79,11 +85,25 @@ class QiboCircuitToEinsum:
return mode_labels, operands
def op_shape_from_qubits(self, nqubits):
"""Modify tensor to cuQuantum shape (qubit_states,input_output) *
qubits_involved."""
"""Modify tensor to cuQuantum shape.
Parameters:
nqubits (int): The number of qubits in quantum circuit.
Returns:
(qubit_states,input_output) * nqubits
"""
return (2, 2) * nqubits
def init_intermediate_circuit(self, circuit):
"""Initialize the intermediate circuit representation.
This method initializes the intermediate circuit representation by extracting gate matrices and qubit IDs
from the given quantum circuit.
Parameters:
circuit (object): The quantum circuit object.
"""
self.gate_tensors = []
gates_qubits = []
@@ -105,6 +125,15 @@ class QiboCircuitToEinsum:
self.active_qubits = np.unique(gates_qubits)
def init_basis_map(self, backend, dtype):
"""Initialize the basis map for the quantum circuit.
This method initializes a basis map for the quantum circuit, which maps binary
strings representing qubit states to their corresponding quantum state vectors.
Parameters:
backend (object): The backend object providing the array conversion method.
dtype (object): The data type for the quantum state vectors.
"""
asarray = backend.asarray
state_0 = asarray([1, 0], dtype=dtype)
state_1 = asarray([0, 1], dtype=dtype)
@@ -112,6 +141,14 @@ class QiboCircuitToEinsum:
self.basis_map = {"0": state_0, "1": state_1}
def init_inverse_circuit(self, circuit):
"""Initialize the inverse circuit representation.
This method initializes the inverse circuit representation by extracting gate matrices and qubit IDs
from the given quantum circuit.
Parameters:
circuit (object): The quantum circuit object.
"""
self.gate_tensors_inverse = []
gates_qubits_inverse = []
@@ -135,7 +172,7 @@ class QiboCircuitToEinsum:
def get_pauli_gates(self, pauli_map, dtype="complex128", backend=cp):
"""Populate the gates for all pauli operators.
Args:
Parameters:
pauli_map: A dictionary mapping qubits to pauli operators.
dtype: Data type for the tensor operands.
backend: The package the tensor operands belong to.
@@ -159,6 +196,15 @@ class QiboCircuitToEinsum:
return gates
def expectation_operands(self, pauli_string):
"""Create the operands for pauli string expectation computation in the
interleave format.
Parameters:
pauli_string: A string representating the list of pauli gates.
Returns:
Operands for the contraction in the interleave format.
"""
input_bitstring = "0" * self.circuit.nqubits
input_operands = self._get_bitstring_tensors(input_bitstring)

View File

@@ -7,6 +7,15 @@ from qibotn.mps_utils import apply_gate, initial
class QiboCircuitToMPS:
"""A helper class to convert Qibo circuit to MPS.
Parameters:
circ_qibo: The quantum circuit object.
gate_algo(dict): Dictionary for SVD and QR settings.
datatype (str): Either single ("complex64") or double (complex128) precision.
rand_seed(int): Seed for random number generator.
"""
def __init__(
self,
circ_qibo,

View File

@@ -9,14 +9,31 @@ from qibotn.mps_contraction_helper import MPSContractionHelper
def dense_vector_tn(qibo_circ, datatype):
"""Convert qibo circuit to tensornet (TN) format and perform contraction to
dense vector."""
dense vector.
Parameters:
qibo_circ: The quantum circuit object.
datatype (str): Either single ("complex64") or double (complex128) precision.
Returns:
Dense vector of quantum circuit.
"""
myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype)
return contract(*myconvertor.state_vector_operands())
def expectation_pauli_tn(qibo_circ, datatype, pauli_string_pattern):
"""Convert qibo circuit to tensornet (TN) format and perform contraction to
expectation of given Pauli string."""
expectation of given Pauli string.
Parameters:
qibo_circ: The quantum circuit object.
datatype (str): Either single ("complex64") or double (complex128) precision.
pauli_string_pattern(str): pauli string pattern.
Returns:
Expectation of quantum circuit due to pauli string.
"""
myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype)
return contract(
*myconvertor.expectation_operands(
@@ -35,6 +52,14 @@ def dense_vector_tn_MPI(qibo_circ, datatype, n_samples=8):
the least costly contraction path. This is sped up with multi
thread. After pathfinding the optimal path is used in the actual
contraction to give a dense vector representation of the TN.
Parameters:
qibo_circ: The quantum circuit object.
datatype (str): Either single ("complex64") or double (complex128) precision.
n_samples(int): Number of samples for pathfinding.
Returns:
Dense vector of quantum circuit.
"""
from cuquantum import Network
@@ -102,6 +127,14 @@ def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8):
the least costly contraction path. This is sped up with multi
thread. After pathfinding the optimal path is used in the actual
contraction to give a dense vector representation of the TN.
Parameters:
qibo_circ: The quantum circuit object.
datatype (str): Either single ("complex64") or double (complex128) precision.
n_samples(int): Number of samples for pathfinding.
Returns:
Dense vector of quantum circuit.
"""
from cupy.cuda import nccl
from cuquantum import Network
@@ -183,6 +216,15 @@ def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string_pattern, n_sampl
select the least costly contraction path. This is sped up with multi
thread. After pathfinding the optimal path is used in the actual
contraction to give an expectation value.
Parameters:
qibo_circ: The quantum circuit object.
datatype (str): Either single ("complex64") or double (complex128) precision.
pauli_string_pattern(str): pauli string pattern.
n_samples(int): Number of samples for pathfinding.
Returns:
Expectation of quantum circuit due to pauli string.
"""
from cupy.cuda import nccl
from cuquantum import Network
@@ -266,6 +308,15 @@ def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string_pattern, n_sample
select the least costly contraction path. This is sped up with multi
thread. After pathfinding the optimal path is used in the actual
contraction to give an expectation value.
Parameters:
qibo_circ: The quantum circuit object.
datatype (str): Either single ("complex64") or double (complex128) precision.
pauli_string_pattern(str): pauli string pattern.
n_samples(int): Number of samples for pathfinding.
Returns:
Expectation of quantum circuit due to pauli string.
"""
from cuquantum import Network
from mpi4py import MPI # this line initializes MPI
@@ -326,7 +377,16 @@ def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string_pattern, n_sample
def dense_vector_mps(qibo_circ, gate_algo, datatype):
"""Convert qibo circuit to matrix product state (MPS) format and perform
contraction to dense vector."""
contraction to dense vector.
Parameters:
qibo_circ: The quantum circuit object.
gate_algo(dict): Dictionary for SVD and QR settings.
datatype (str): Either single ("complex64") or double (complex128) precision.
Returns:
Dense vector of quantum circuit.
"""
myconvertor = QiboCircuitToMPS(qibo_circ, gate_algo, dtype=datatype)
mps_helper = MPSContractionHelper(myconvertor.num_qubits)
@@ -339,6 +399,13 @@ def pauli_string_gen(nqubits, pauli_string_pattern):
"""Used internally to generate the string based on given pattern and number
of qubit.
Parameters:
nqubits(int): Number of qubits of Quantum Circuit
pauli_string_pattern(str): Strings representing sequence of pauli gates.
Returns:
String representation of the actual pauli string from the pattern.
Example: pattern: "XZ", number of qubit: 7, output = XZXZXZX
"""
if nqubits <= 0:

View File

@@ -3,7 +3,15 @@ import quimb.tensor as qtn
def init_state_tn(nqubits, init_state_sv):
"""Create a matrix product state directly from a dense vector."""
"""Create a matrix product state directly from a dense vector.
Args:
nqubits (int): Total number of qubits in the circuit.
init_state_sv (list): Initial state in the dense vector form.
Returns:
list: Matrix product state representation of the dense vector.
"""
dims = tuple(2 * np.ones(nqubits, dtype=int))
@@ -11,9 +19,16 @@ def init_state_tn(nqubits, init_state_sv):
def dense_vector_tn_qu(qasm: str, initial_state, mps_opts, backend="numpy"):
"""Evaluate QASM with Quimb.
"""Evaluate circuit in QASM format with Quimb.
backend (quimb): numpy, cupy, jax. Passed to ``opt_einsum``.
Args:
qasm (str): QASM program.
initial_state (list): Initial state in the dense vector form. If ``None`` the default ``|00...0>`` state is used.
mps_opts (dict): Parameters to tune the gate_opts for mps settings in ``class quimb.tensor.circuit.CircuitMPS``.
backend (str): Backend to perform the contraction with, e.g. ``numpy``, ``cupy``, ``jax``. Passed to ``opt_einsum``.
Returns:
list: Amplitudes of final state after the simulation of the circuit.
"""
if initial_state is not None:

View File

@@ -7,30 +7,17 @@ class MPSContractionHelper:
"""A helper class to compute various quantities for a given MPS.
Interleaved format is used to construct the input args for `cuquantum.contract`.
A concrete example on how the modes are populated for a 7-site MPS is provided
below::
0 2 4 6 8 10 12 14
bra -----A-----B-----C-----D-----E-----F-----G-----
| | | | | | |
1| 3| 5| 7| 9| 11| 13|
| | | | | | |
ket -----a-----b-----c-----d-----e-----f-----g-----
15 16 17 18 19 20 21 22
Reference: https://github.com/NVIDIA/cuQuantum/blob/main/python/samples/cutensornet/tn_algorithms/mps_algorithms.ipynb
The follwing compute quantities are supported:
The following compute quantities are supported:
- the norm of the MPS.
- the equivalent state vector from the MPS.
- the expectation value for a given operator.
- the equivalent state vector after multiplying an MPO to an MPS.
Note that for the nth MPS tensor (rank-3), the modes of the tensor are expected to be `(i,p,j)`
where i denotes the bonding mode with the (n-1)th tensor, p denotes the physical mode for the qubit and
j denotes the bonding mode with the (n+1)th tensor.
Args:
Parameters:
num_qubits: The number of qubits for the MPS.
"""
@@ -46,7 +33,7 @@ class MPSContractionHelper:
"""Contract the corresponding tensor network to form the norm of the
MPS.
Args:
Parameters:
mps_tensors: A list of rank-3 ndarray-like tensor objects.
The indices of the ith tensor are expected to be bonding index to the i-1 tensor,
the physical mode, and then the bonding index to the i+1th tensor.
@@ -67,7 +54,7 @@ class MPSContractionHelper:
"""Contract the corresponding tensor network to form the state vector
representation of the MPS.
Args:
Parameters:
mps_tensors: A list of rank-3 ndarray-like tensor objects.
The indices of the ith tensor are expected to be bonding index to the i-1 tensor,
the physical mode, and then the bonding index to the i+1th tensor.
@@ -89,7 +76,7 @@ class MPSContractionHelper:
"""Contract the corresponding tensor network to form the expectation of
the MPS.
Args:
Parameters:
mps_tensors: A list of rank-3 ndarray-like tensor objects.
The indices of the ith tensor are expected to be bonding index to the i-1 tensor,
the physical mode, and then the bonding index to the i+1th tensor.

View File

@@ -2,18 +2,32 @@ import cupy as cp
from cuquantum import contract
from cuquantum.cutensornet.experimental import contract_decompose
# Reference: https://github.com/NVIDIA/cuQuantum/blob/main/python/samples/cutensornet/tn_algorithms/mps_algorithms.ipynb
def initial(num_qubits, dtype):
r"""Generate the MPS with an initial state of :math:`\ket{00...00}`"""
r"""Generate the MPS with an initial state of :math:`\ket{00...00}`
Parameters:
num_qubits: Number of qubits in the Quantum Circuit.
dtype: Either single ("complex64") or double (complex128) precision.
Returns:
The initial MPS tensors.
"""
state_tensor = cp.asarray([1, 0], dtype=dtype).reshape(1, 2, 1)
mps_tensors = [state_tensor] * num_qubits
return mps_tensors
def mps_site_right_swap(mps_tensors, i, **kwargs):
"""Perform the swap operation between the ith and i+1th MPS tensors."""
"""Perform the swap operation between the ith and i+1th MPS tensors.
Parameters:
mps_tensors: Tensors representing MPS
i (int): index of the tensor to swap
Returns:
The updated MPS tensors.
"""
# contraction followed by QR decomposition
a, _, b = contract_decompose(
"ipj,jqk->iqj,jpk",
@@ -28,7 +42,9 @@ def mps_site_right_swap(mps_tensors, i, **kwargs):
def apply_gate(mps_tensors, gate, qubits, **kwargs):
"""Apply the gate operand to the MPS tensors in-place.
Args:
# Reference: https://github.com/NVIDIA/cuQuantum/blob/main/python/samples/cutensornet/tn_algorithms/mps_algorithms.ipynb
Parameters:
mps_tensors: A list of rank-3 ndarray-like tensor objects.
The indices of the ith tensor are expected to be the bonding index to the i-1 tensor,
the physical mode, and then the bonding index to the i+1th tensor.