diff --git a/src/qibotn/QiboCircuitConvertor.py b/src/qibotn/QiboCircuitConvertor.py index 18c79fd..353a054 100644 --- a/src/qibotn/QiboCircuitConvertor.py +++ b/src/qibotn/QiboCircuitConvertor.py @@ -19,33 +19,13 @@ class QiboCircuitToEinsum: def __init__(self, circuit, dtype="complex128"): self.backend = cp self.dtype = getattr(self.backend, dtype) - - self.gate_tensors = [] - gates_qubits = [] - - for gate in circuit.queue: - gate_qubits = gate.control_qubits + gate.target_qubits - gates_qubits.extend(gate_qubits) - - # self.gate_tensors is to extract into a list the gate matrix together with the qubit id that it is acting on - # https://github.com/NVIDIA/cuQuantum/blob/6b6339358f859ea930907b79854b90b2db71ab92/python/cuquantum/cutensornet/_internal/circuit_parser_utils_cirq.py#L32 - required_shape = self.op_shape_from_qubits(len(gate_qubits)) - self.gate_tensors.append( - ( - cp.asarray(gate.matrix).reshape(required_shape), - gate_qubits, - ) - ) - - # self.active_qubits is to identify qubits with at least 1 gate acting on it in the whole circuit. - self.active_qubits = np.unique(gates_qubits) + self.init_basis_map(self.backend, dtype) + self.init_intermediate_circuit(circuit) def state_vector_operands(self): - input_tensor_count = len(self.active_qubits) + input_bitstring = "0" * len(self.active_qubits) - input_operands = self._get_bitstring_tensors( - "0" * input_tensor_count, self.dtype, backend=self.backend - ) + input_operands = self._get_bitstring_tensors(input_bitstring) ( mode_labels, @@ -69,21 +49,13 @@ class QiboCircuitToEinsum: return operand_exp_interleave def _init_mode_labels_from_qubits(self, qubits): - frontier_dict = {} n = len(qubits) - for x in range(n): - frontier_dict[qubits[x]] = x - return [[i] for i in range(n)], frontier_dict, n + frontier_dict = {qubits[x]: x for x in range(n)} + mode_labels = [[i] for i in range(n)] + return mode_labels, frontier_dict, n - def _get_bitstring_tensors(self, bitstring, dtype=np.complex128, backend=cp): - asarray = backend.asarray - state_0 = asarray([1, 0], dtype=dtype) - state_1 = asarray([0, 1], dtype=dtype) - - basis_map = {"0": state_0, "1": state_1} - - operands = [basis_map[ibit] for ibit in bitstring] - return operands + def _get_bitstring_tensors(self, bitstring): + return [self.basis_map[ibit] for ibit in bitstring] def _parse_gates_to_mode_labels_operands( self, gates, qubits_frontier, next_frontier @@ -108,3 +80,31 @@ class QiboCircuitToEinsum: (qubit_states,input_output) * qubits_involved """ return (2, 2) * nqubits + + def init_intermediate_circuit(self, circuit): + self.gate_tensors = [] + gates_qubits = [] + + for gate in circuit.queue: + gate_qubits = gate.control_qubits + gate.target_qubits + gates_qubits.extend(gate_qubits) + + # self.gate_tensors is to extract into a list the gate matrix together with the qubit id that it is acting on + # https://github.com/NVIDIA/cuQuantum/blob/6b6339358f859ea930907b79854b90b2db71ab92/python/cuquantum/cutensornet/_internal/circuit_parser_utils_cirq.py#L32 + required_shape = self.op_shape_from_qubits(len(gate_qubits)) + self.gate_tensors.append( + ( + cp.asarray(gate.matrix).reshape(required_shape), + gate_qubits, + ) + ) + + # self.active_qubits is to identify qubits with at least 1 gate acting on it in the whole circuit. + self.active_qubits = np.unique(gates_qubits) + + def init_basis_map(self, backend, dtype): + asarray = backend.asarray + state_0 = asarray([1, 0], dtype=dtype) + state_1 = asarray([0, 1], dtype=dtype) + + self.basis_map = {"0": state_0, "1": state_1}