From 6838faba33e96f03e03e9c460be5c785d1d630a0 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Fri, 10 Feb 2023 16:31:17 +0800 Subject: [PATCH] Qibo circuit convertor --- src/qibotn/QiboCircuitConvertor.py | 107 +++++++++++++++++++++++++++++ src/qibotn/__main__.py | 68 +++++++++++++++++- 2 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 src/qibotn/QiboCircuitConvertor.py diff --git a/src/qibotn/QiboCircuitConvertor.py b/src/qibotn/QiboCircuitConvertor.py new file mode 100644 index 0000000..39fb010 --- /dev/null +++ b/src/qibotn/QiboCircuitConvertor.py @@ -0,0 +1,107 @@ + +import cupy as cp +import numpy as np + +EINSUM_SYMBOLS_BASE = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + +class QiboCircuitToEinsum: + def __init__(self, circuit, dtype='complex128'): + + self.backend = cp + self.dtype = getattr(self.backend, dtype) + + self.input_tensor_counter = np. zeros((circuit.nqubits,)) + self.gates = [] + for gate in circuit.queue: + targets = list(gate.target_qubits) + for target in targets: + self.input_tensor_counter[target] = self.input_tensor_counter[target] + 1 + controls = list(gate.control_qubits) + for control in controls: + self.input_tensor_counter[control] = self.input_tensor_counter[control] + 1 + gate_qubits = controls + targets + self.gates.append((cp.asarray(gate.matrix).reshape((2,) * 2 * len(gate_qubits)), gate_qubits)) + + self.qubit_name = [indx for indx, value in enumerate(self.input_tensor_counter) if value > 0] + + def state_vector(self): + + input_tensor_count = np.count_nonzero(self.input_tensor_counter) + + input_operands = self._get_bitstring_tensors('0'*input_tensor_count, self.dtype, backend=self.backend) + + mode_labels, qubits_frontier, next_frontier = self._init_mode_labels_from_qubits(self.qubit_name) + + gate_mode_labels, gate_operands = self._parse_gates_to_mode_labels_operands(self.gates, + qubits_frontier, + next_frontier) + + operands = input_operands + gate_operands + mode_labels += gate_mode_labels + + expression = self._convert_mode_labels_to_expression(mode_labels, qubits_frontier) + + return expression, operands + + def _get_symbol(self,i): + """ + Return a Unicode as label for index. + + .. note:: This function is adopted from `opt_einsum `_ + """ + if i < 52: + return EINSUM_SYMBOLS_BASE[i] + return chr(i + 140) + + 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 + + def _get_bitstring_tensors(self, bitstring, dtype=np.complex128, backend=cp): + + asarray = backend.asarray #_get_backend_asarray_func(backend) + 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 _parse_gates_to_mode_labels_operands( + self, + gates, + qubits_frontier, + next_frontier + ): + + mode_labels = [] + operands = [] + + for tensor, gate_qubits in gates: + operands.append(tensor) + input_mode_labels = [] + output_mode_labels = [] + for q in gate_qubits: + input_mode_labels.append(qubits_frontier[q]) + output_mode_labels.append(next_frontier) + qubits_frontier[q] = next_frontier + next_frontier += 1 + mode_labels.append(output_mode_labels+input_mode_labels) + return mode_labels, operands + + def _convert_mode_labels_to_expression(self,input_mode_labels, output_mode_labels): + + out_list = [] + for key in output_mode_labels: + out_list.append(output_mode_labels[key]) + + input_symbols = [''.join(map(self._get_symbol, idx)) for idx in input_mode_labels] + expression = ','.join(input_symbols) + '->' + ''.join(map(self._get_symbol, out_list)) + + return expression diff --git a/src/qibotn/__main__.py b/src/qibotn/__main__.py index 8ed7439..b42f84d 100644 --- a/src/qibotn/__main__.py +++ b/src/qibotn/__main__.py @@ -1,5 +1,11 @@ import argparse -from qibotn import qasm_quimb +from timeit import default_timer as timer + +from qibotn import quimb as qiboquimb +from QiboCircuitConvertor import QiboCircuitToEinsum +from cuquantum import contract +import cupy as cp +from qibo.models import * def parser(): @@ -12,7 +18,65 @@ def parser(): def main(args: argparse.Namespace): print("Testing for %d nqubits" % (args.nqubits)) - qasm_quimb.eval_QI_qft(args.nqubits, args.qasm_circ, args.init_state) + qiboquimb.eval(args.nqubits, args.qasm_circ, args.init_state) + + +def parser_cuquantum(): + parser = argparse.ArgumentParser() + + parser.add_argument( + "--nqubits", default=10, type=int, help="Number of quibits in the circuits." + ) + + parser.add_argument( + "--circuit", + default="qft", + type=str, + help="Type of circuit to use. See README for the list of " + "available circuits.", + ) + + parser.add_argument( + "--precision", + default="complex128", + type=str, + help="Numerical precision of the simulation. " + "Choose between 'complex128' and 'complex64'.", + ) + + return parser.parse_args() + + +def main_cuquantum(args: argparse.Namespace): + print("Testing for %d nqubits" % (args.nqubits)) + nqubits = args.nqubits + circuit_name = args.circuit + datatype = args.precision + # Create qibo quibit + + if circuit_name in ("qft", "QFT"): + circuit = QFT(nqubits) + else: + raise NotImplementedError(f"Cannot find circuit {circuit_name}.") + + myconvertor = QiboCircuitToEinsum(circuit, dtype=datatype) + + expression, operands = myconvertor.state_vector() + + start = timer() + result_qibo = circuit() + end = timer() + circuit_eval_time = end - start + print("Simulation time: Qibo =", circuit_eval_time, "s") + + start = timer() + sv_cutn = contract(expression, *operands) + end = timer() + circuit_eval_time = end - start + print("Simulation time: cuQuantum cuTensorNet =", circuit_eval_time, "s") + + # print(f"is sv in agreement?", cp.allclose(sv_cutn.flatten(), result_qibo.state(numpy=True))) + assert cp.allclose(sv_cutn.flatten(), result_qibo.state(numpy=True)) if __name__ == "__main__":