The Structure of Matrix Product State.
Q0 -- O -- Q1 -- O -- ... -- O -- Qn-1 -- O -- Qn
| | | |
U U ... ---- U ----
| | | |
Q0-- O -- Q1-- O -- ... -- O -- Qn-1-- O -- Qn`
Initial the MPSSiteStructure Class
Parameters:
-
device
(str, default:
'CPU'
)
–
The device type, one of [CPU, GPU]. Defaults to "CPU".
-
precision
(str, default:
'double'
)
–
The precision type, one of [single, double]. Defaults to "double".
Source code in QuICT/simulation/matrix_product_state/mps_site.py
| def __init__(
self,
device: str = "CPU",
precision: str = "double"
):
""" Initial the MPSSiteStructure Class
Args:
device (str, optional): The device type, one of [CPU, GPU]. Defaults to "CPU".
precision (str, optional): The precision type, one of [single, double]. Defaults to "double".
"""
# based properties
assert device in ["CPU", "GPU"]
self._device = device
assert precision in ["double", "single"]
self._precision = precision
self._dtype = np.complex128 if self._precision == "double" else np.complex64
self._qubits = 0
# matrix product state structure
self._product_state = []
self._norms = []
self._groups = [] # List of the interval of entangle states, e.g. [(0, 3), (5, 7)]
# algorithm Module
self._linear_algorithm = LinAlgLoader(device=device)
if self._device == "GPU":
import cupy as cp
self._array_helper = cp
else:
self._array_helper = np
|
apply_double_gate
apply_double_gate(qubit_indexes: list, gate_matrix: ndarray)
Apply bi-qubits quantum gate into MPS.
Parameters:
-
qubit_indexes
(list)
–
The list of qubits' indexes
-
gate_matrix
(ndarray)
–
The matrix of the quantum gate
-
inverse
(bool)
–
Need Extra inverse? only for CRx and Unitary. Defaults to False.
Source code in QuICT/simulation/matrix_product_state/mps_site.py
| def apply_double_gate(self, qubit_indexes: list, gate_matrix: np.ndarray):
""" Apply bi-qubits quantum gate into MPS.
Args:
qubit_indexes (list): The list of qubits' indexes
gate_matrix (np.ndarray): The matrix of the quantum gate
inverse (bool, optional): Need Extra inverse? only for CRx and Unitary. Defaults to False.
"""
q0, q1 = qubit_indexes
if abs(q0 - q1) == 1:
# Apply consecutive bi-qubits gate
self._apply_consecutive_double_gate(qubit_indexes, gate_matrix)
else:
# Apply swap gate for un-consecutive bi-qubits gate
min_q = min(qubit_indexes)
max_q = max(qubit_indexes)
for i in range(min_q, max_q - 1):
self._apply_consecutive_double_gate([i, i + 1], self.__SWAP_MATRIX)
qubit_indexes = [max_q, max_q - 1] if q0 > q1 else [max_q - 1, max_q]
self._apply_consecutive_double_gate(qubit_indexes, gate_matrix)
for i in range(max_q - 1, min_q, -1):
self._apply_consecutive_double_gate([i - 1, i], self.__SWAP_MATRIX)
self._update_groups()
|
apply_single_gate
apply_single_gate(qubit_index: int, gate_matrix: ndarray)
Apply single gate into MPS
Parameters:
-
qubit_index
(int)
–
-
gate_matrix
(ndarray)
–
The matrix of the quantum gate
Source code in QuICT/simulation/matrix_product_state/mps_site.py
| def apply_single_gate(self, qubit_index: int, gate_matrix: np.ndarray):
""" Apply single gate into MPS
Args:
qubit_index (int): The index of qubit
gate_matrix (np.ndarray): The matrix of the quantum gate
"""
assert qubit_index < self.qubits
target_qubit = self._product_state[qubit_index]
target_qubit.tensor_data = self._array_helper.tensordot(
target_qubit.tensor_data, self._array_helper.array(gate_matrix, dtype=self._dtype), [[1], [1]]
).transpose([0, 2, 1])
|
initial_mps
initial_mps(qubits: int, quantum_state: Union[str, ndarray] = None)
Generate the initial state for MPS by given quantum state or default state.
Parameters:
-
qubits
(int)
–
-
quantum_state
(Union[str, ndarray], default:
None
)
–
The initial quantum state. Defaults to None.
Source code in QuICT/simulation/matrix_product_state/mps_site.py
| def initial_mps(self, qubits: int, quantum_state: Union[str, np.ndarray] = None):
""" Generate the initial state for MPS by given quantum state or default state.
Args:
qubits (int): The number of qubits.
quantum_state (Union[str, np.ndarray], optional): The initial quantum state. Defaults to None.
"""
self._qubits = qubits
if quantum_state is not None:
# TODO: add special quantum state like GHZ or ... later
if not isinstance(quantum_state, str):
quantum_state = self._array_helper.array(quantum_state)
self._quantum_state_schmidt_decomposition(quantum_state)
else:
self._product_state = [QubitTensor(self._initial_qtensor()) for _ in range(self._qubits)]
self._norms = [Normalize(self._initial_normalize())for _ in range(1, self._qubits)] if qubits > 1 else []
self._groups = []
|
sample
Sample the measured result from current Matrix Product State.
Parameters:
Source code in QuICT/simulation/matrix_product_state/mps_site.py
| def sample(self, shots: int):
""" Sample the measured result from current Matrix Product State.
Args:
shots (int): The sample times.
"""
# Get State Vector
state_vector = self.to_statevector()
state_list = [0] * (1 << self._qubits)
# Sample from State Vector
measured_prob = self._array_helper.square(self._array_helper.abs(state_vector))
if self._device == "GPU":
measured_prob = measured_prob.get()
sample_result = np.random.choice(
np.arange(1 << self._qubits), shots, p=measured_prob
)
for res in sample_result:
state_list[res] += 1
return state_list
|
single_amplitude_calculate
single_amplitude_calculate(state_index: int)
Calculate single amplitude for current MPS.
Parameters:
-
state_index
(int)
–
The index of quantum state which want to be calculate.
Source code in QuICT/simulation/matrix_product_state/mps_site.py
| def single_amplitude_calculate(self, state_index: int):
""" Calculate single amplitude for current MPS.
Args:
state_index (int): The index of quantum state which want to be calculate.
"""
assert state_index < (1 << self._qubits)
# Index divided by qubits per each circuit blocks
bit_index = "{0:0b}".format(state_index)
bit_index = bit_index.zfill(self._qubits)
# bit_index = [bit_index[i] for i in range(0, self._qubits, 1)]
q0_state = int(bit_index[0], 2)
single_state = self._product_state[0].tensor_data[:, q0_state, :]
for i in range(1, self._qubits):
# multiply the norm
single_state = np.einsum('ij,j->ij', single_state, self._norms[i - 1].matrix_data)
# choice qubits' state
qmps = self._product_state[i]
q_state = int(bit_index[i], 2)
target_value = qmps.tensor_data[:, q_state, :]
single_state = np.einsum('ij,jk->ik', single_state, target_value)
return single_state.flatten()
|
to_statevector
to_statevector(interval: list = None) -> np.ndarray
Transfer MPS into State Vector. WARNING: it will generate an vector with size 2**n, where n is the
number of qubits.
Returns:
-
ndarray
–
np.ndarray: The state vector from MPS
Source code in QuICT/simulation/matrix_product_state/mps_site.py
| def to_statevector(self, interval: list = None) -> np.ndarray:
""" Transfer MPS into State Vector. WARNING: it will generate an vector with size 2**n, where n is the
number of qubits.
Returns:
np.ndarray: The state vector from MPS
"""
if not interval:
start, end = 0, self.qubits
else:
assert len(interval) == 2
start, end = interval
state_vector = self._product_state[start].tensor_data
for i in range(start + 1, end):
state_vector = self._array_helper.tensordot(
state_vector, self._array_helper.diag(self._norms[i - 1].matrix_data), [[-1], [0]]
)
state_vector = self._array_helper.tensordot(state_vector, self._product_state[i].tensor_data, [[-1], [0]])
ldim, rdim = state_vector.shape[0], state_vector.shape[-1]
state_vector = state_vector.reshape([ldim, -1, rdim])
return state_vector.flatten('C')
|