From 8e78420c379cb0124e342affc6779fb38377f1c1 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Fri, 3 Nov 2023 16:48:14 +0800 Subject: [PATCH 001/199] add backend file --- src/qibotn/backends.py | 69 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/qibotn/backends.py diff --git a/src/qibotn/backends.py b/src/qibotn/backends.py new file mode 100644 index 00000000..98294ec2 --- /dev/null +++ b/src/qibotn/backends.py @@ -0,0 +1,69 @@ +from qibo.backends import NumpyBackend +from qibo.config import raise_error +from qibotn import cutn +from qibotn import quimb +from qibo.states import CircuitResult + + +class QiboTNBackend(NumpyBackend): + def __init__(self, platform): + super().__init__() + self.name = "qibotn" + if ( + platform == "cu_tensornet" + or platform == "cu_mps" + or platform == "qu_tensornet" + ): # pragma: no cover + self.platform = platform + else: + raise_error( + NotImplementedError, "QiboTN cannot support the specified backend." + ) + + def apply_gate(self, gate, state, nqubits): # pragma: no cover + raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") + + def apply_gate_density_matrix(self, gate, state, nqubits): # pragma: no cover + raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") + + def assign_measurements(self, measurement_map, circuit_result): + raise_error(NotImplementedError, "Not implemented in QiboTN.") + + def execute_circuit( + self, circuit, initial_state=None, nshots=None, return_array=False + ): # pragma: no cover + """Executes a quantum circuit. + + Args: + 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. + + """ + if initial_state is not None: + raise_error(NotImplementedError, "QiboTN cannot support initial state.") + + if self.platform == "cu_tensornet": + state = cutn.eval(circuit, self.dtype) + + if self.platform == "cu_mps": + gate_algo = { + "qr_method": False, + "svd_method": { + "partition": "UV", + "abs_cutoff": 1e-12, + }, + } # make this user input + state = cutn.eval_mps(circuit, gate_algo, self.dtype) + + if self.platform == "qu_tensornet": + state = quimb.eval(circuit.to_qasm(), initial_state, backend="numpy") + + if return_array: + return state.flatten() + else: + circuit._final_state = CircuitResult(self, circuit, state.flatten(), nshots) + return circuit._final_state From 3f046beb1ca5570e5ba8c1b7c0ad8ec0d85d9eb0 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Fri, 3 Nov 2023 17:08:03 +0800 Subject: [PATCH 002/199] Added check for initial conditions --- src/qibotn/backends.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/qibotn/backends.py b/src/qibotn/backends.py index 98294ec2..f1d0bafa 100644 --- a/src/qibotn/backends.py +++ b/src/qibotn/backends.py @@ -43,13 +43,17 @@ def execute_circuit( xxx. """ - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") if self.platform == "cu_tensornet": + if initial_state is not None: + raise_error(NotImplementedError, "QiboTN cannot support initial state.") + state = cutn.eval(circuit, self.dtype) if self.platform == "cu_mps": + if initial_state is not None: + raise_error(NotImplementedError, "QiboTN cannot support initial state.") + gate_algo = { "qr_method": False, "svd_method": { From 2d48d3ddbe1c38b5e57422284492f86cbdce416c Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 7 Nov 2023 17:08:57 +0800 Subject: [PATCH 003/199] Added initial state for Quimb --- src/qibotn/backends.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/qibotn/backends.py b/src/qibotn/backends.py index f1d0bafa..3193e6bc 100644 --- a/src/qibotn/backends.py +++ b/src/qibotn/backends.py @@ -3,6 +3,7 @@ from qibotn import cutn from qibotn import quimb from qibo.states import CircuitResult +import numpy as np class QiboTNBackend(NumpyBackend): @@ -30,7 +31,7 @@ def assign_measurements(self, measurement_map, circuit_result): raise_error(NotImplementedError, "Not implemented in QiboTN.") def execute_circuit( - self, circuit, initial_state=None, nshots=None, return_array=False + self, circuit, initial_state=None, nshots=None, return_array=True ): # pragma: no cover """Executes a quantum circuit. @@ -64,7 +65,12 @@ def execute_circuit( state = cutn.eval_mps(circuit, gate_algo, self.dtype) if self.platform == "qu_tensornet": - state = quimb.eval(circuit.to_qasm(), initial_state, backend="numpy") + + #init_state = np.random.random(2**circuit.nqubits) + 1j * np.random.random(2**circuit.nqubits) + #init_state = init_state / np.sqrt((np.abs(init_state) ** 2).sum()) + init_state = np.zeros(2**circuit.nqubits, dtype=self.dtype) + init_state[0] = 1.0 + state = quimb.eval(circuit.to_qasm(), init_state, backend="numpy") if return_array: return state.flatten() From 2a6b3a54f0fc7fd1a7881d5fd663e74013aa30d8 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Thu, 9 Nov 2023 00:18:27 +0800 Subject: [PATCH 004/199] Reverted back return_array to False --- src/qibotn/backends.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/backends.py b/src/qibotn/backends.py index 3193e6bc..9d399731 100644 --- a/src/qibotn/backends.py +++ b/src/qibotn/backends.py @@ -31,7 +31,7 @@ def assign_measurements(self, measurement_map, circuit_result): raise_error(NotImplementedError, "Not implemented in QiboTN.") def execute_circuit( - self, circuit, initial_state=None, nshots=None, return_array=True + self, circuit, initial_state=None, nshots=None, return_array=False ): # pragma: no cover """Executes a quantum circuit. From 0420cbfa520f91fbd24e8d24e7f98585e911ce41 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 24 Jan 2024 11:40:51 +0800 Subject: [PATCH 005/199] Updates to include expectation calculation --- src/qibotn/QiboCircuitConvertor.py | 113 ++++++++ src/qibotn/QiboCircuitToMPS.py | 2 +- src/qibotn/backends.py | 53 ++++ src/qibotn/cutn.py | 406 ++++++++++++++++++++++++++++- 4 files changed, 567 insertions(+), 7 deletions(-) diff --git a/src/qibotn/QiboCircuitConvertor.py b/src/qibotn/QiboCircuitConvertor.py index d72a09cb..11aaa716 100644 --- a/src/qibotn/QiboCircuitConvertor.py +++ b/src/qibotn/QiboCircuitConvertor.py @@ -21,6 +21,7 @@ def __init__(self, circuit, dtype="complex128"): self.dtype = getattr(self.backend, dtype) self.init_basis_map(self.backend, dtype) self.init_intermediate_circuit(circuit) + self.circuit = circuit def state_vector_operands(self): input_bitstring = "0" * len(self.active_qubits) @@ -109,3 +110,115 @@ def init_basis_map(self, backend, dtype): state_1 = asarray([0, 1], dtype=dtype) self.basis_map = {"0": state_0, "1": state_1} + + + def init_inverse_circuit(self, circuit): + self.gate_tensors_inverse = [] + gates_qubits_inverse = [] + + for gate in circuit.queue: + gate_qubits = gate.control_qubits + gate.target_qubits + gates_qubits_inverse.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_inverse.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_inverse = np.unique(gates_qubits_inverse) + + + def get_pauli_gates(self, pauli_map, dtype='complex128', backend=cp): + """ + Populate the gates for all pauli operators. + + Args: + pauli_map: A dictionary mapping qubits to pauli operators. + dtype: Data type for the tensor operands. + backend: The package the tensor operands belong to. + + Returns: + A sequence of pauli gates. + """ + asarray = backend.asarray + pauli_i = asarray([[1,0], [0,1]], dtype=dtype) + pauli_x = asarray([[0,1], [1,0]], dtype=dtype) + pauli_y = asarray([[0,-1j], [1j,0]], dtype=dtype) + pauli_z = asarray([[1,0], [0,-1]], dtype=dtype) + + operand_map = {'I': pauli_i, + 'X': pauli_x, + 'Y': pauli_y, + 'Z': pauli_z} + gates = [] + for qubit, pauli_char in pauli_map.items(): + operand = operand_map.get(pauli_char) + if operand is None: + raise ValueError('pauli string character must be one of I/X/Y/Z') + gates.append((operand, (qubit,))) + return gates + + def expectation_operands(self, pauli_string): + #assign pauli string to qubit + #_get_forward_inverse_metadata() + input_bitstring = "0" * self.circuit.nqubits #Need all qubits! + + input_operands = self._get_bitstring_tensors(input_bitstring) + pauli_string = dict(zip(range(self.circuit.nqubits), pauli_string)) + pauli_map = pauli_string + coned_qubits = pauli_map.keys() + + ( + mode_labels, + qubits_frontier, + next_frontier, + ) = self._init_mode_labels_from_qubits(range(self.circuit.nqubits)) + + gate_mode_labels, gate_operands = self._parse_gates_to_mode_labels_operands( + self.gate_tensors, qubits_frontier, next_frontier + ) + + operands = input_operands + gate_operands + mode_labels += gate_mode_labels + + self.init_inverse_circuit(self.circuit.invert()) + + + next_frontier = max(qubits_frontier.values()) + 1 + + #input_mode_labels, input_operands, qubits_frontier, next_frontier, inverse_gates = self._get_forward_inverse_metadata(coned_qubits) + + pauli_gates = self.get_pauli_gates(pauli_map, dtype=self.dtype, backend=self.backend) + + + gates_inverse = pauli_gates + self.gate_tensors_inverse + + gate_mode_labels_inverse, gate_operands_inverse = self._parse_gates_to_mode_labels_operands( + gates_inverse, qubits_frontier, next_frontier + ) + mode_labels = mode_labels + gate_mode_labels_inverse + [[qubits_frontier[ix]] for ix in range(self.circuit.nqubits)] + operands = operands + gate_operands_inverse + operands[:self.circuit.nqubits] + + operand_exp_interleave = [x for y in zip(operands, mode_labels) for x in y] + + #expec = contract(*operand_exp_interleave) + #print(expec) + + ''' + gate_mode_labels, gate_operands = circ_utils.parse_gates_to_mode_labels_operands(gates, + qubits_frontier, + next_frontier) + + mode_labels = input_mode_labels + gate_mode_labels + [[qubits_frontier[ix]] for ix in self.qubits] + operands = input_operands + gate_operands + input_operands[:n_qubits] + + output_mode_labels = [] + expression = circ_utils.convert_mode_labels_to_expression(mode_labels, output_mode_labels) + ''' + return operand_exp_interleave \ No newline at end of file diff --git a/src/qibotn/QiboCircuitToMPS.py b/src/qibotn/QiboCircuitToMPS.py index d51093f5..816b17c0 100644 --- a/src/qibotn/QiboCircuitToMPS.py +++ b/src/qibotn/QiboCircuitToMPS.py @@ -21,7 +21,7 @@ def __init__( self.handle = cutn.create() self.dtype = dtype self.mps_tensors = initial(self.num_qubits, dtype=dtype) - circuitconvertor = QiboCircuitToEinsum(circ_qibo) + circuitconvertor = QiboCircuitToEinsum(circ_qibo, dtype=dtype) for gate, qubits in circuitconvertor.gate_tensors: # mapping from qubits to qubit indices diff --git a/src/qibotn/backends.py b/src/qibotn/backends.py index 9d399731..4b28431d 100644 --- a/src/qibotn/backends.py +++ b/src/qibotn/backends.py @@ -14,6 +14,13 @@ def __init__(self, platform): platform == "cu_tensornet" or platform == "cu_mps" or platform == "qu_tensornet" + or platform == "cu_tensornet_mpi" + or platform == "cu_tensornet_mpi_expectation" + or platform == "cu_tensornet_expectation" + or platform == "cu_tensornet_nccl" + or platform == "cu_tensornet_nccl_expectation" + + ): # pragma: no cover self.platform = platform else: @@ -71,6 +78,52 @@ def execute_circuit( init_state = np.zeros(2**circuit.nqubits, dtype=self.dtype) init_state[0] = 1.0 state = quimb.eval(circuit.to_qasm(), init_state, backend="numpy") + + if self.platform == "cu_tensornet_mpi": + if initial_state is not None: + raise_error(NotImplementedError, "QiboTN cannot support initial state.") + + #state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) + state, rank = cutn.eval_tn_MPI_2(circuit, self.dtype,32) + if rank > 0: + state = np.array(0) + + if self.platform == "cu_tensornet_nccl": + if initial_state is not None: + raise_error(NotImplementedError, "QiboTN cannot support initial state.") + + #state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) + state, rank = cutn.eval_tn_nccl(circuit, self.dtype,32) + if rank > 0: + state = np.array(0) + + if self.platform == "cu_tensornet_expectation": + if initial_state is not None: + raise_error(NotImplementedError, "QiboTN cannot support initial state.") + + state = cutn.eval_expectation(circuit, self.dtype) + + if self.platform == "cu_tensornet_mpi_expectation": + if initial_state is not None: + raise_error(NotImplementedError, "QiboTN cannot support initial state.") + + #state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) + #state, rank = cutn.eval_tn_MPI_expectation(circuit, self.dtype,32) + state, rank = cutn.eval_tn_MPI_2_expectation(circuit, self.dtype,32) + + if rank > 0: + state = np.array(0) + + if self.platform == "cu_tensornet_nccl_expectation": + if initial_state is not None: + raise_error(NotImplementedError, "QiboTN cannot support initial state.") + + #state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) + #state, rank = cutn.eval_tn_MPI_expectation(circuit, self.dtype,32) + state, rank = cutn.eval_tn_nccl_expectation(circuit, self.dtype,32) + + if rank > 0: + state = np.array(0) if return_array: return state.flatten() diff --git a/src/qibotn/cutn.py b/src/qibotn/cutn.py index eb0e0d49..67d70c49 100644 --- a/src/qibotn/cutn.py +++ b/src/qibotn/cutn.py @@ -13,6 +13,354 @@ def eval(qibo_circ, datatype): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract(*myconvertor.state_vector_operands()) +def eval_expectation(qibo_circ, datatype): + myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) + return contract(*myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits))) + +def eval_tn_MPI_2(qibo_circ, datatype, n_samples=8): + from mpi4py import MPI # this line initializes MPI + import socket + from cuquantum import Network + + # Get the hostname + #hostname = socket.gethostname() + + root = 0 + comm = MPI.COMM_WORLD + rank = comm.Get_rank() + size = comm.Get_size() + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + device_id = rank % getDeviceCount() + + + # Perform circuit conversion + myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft convetor",mem_avail, "rank =",rank) + operands = myconvertor.state_vector_operands() + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + + # Broadcast the operand data. + #operands = comm.bcast(operands, root) + + # Assign the device for each process. + device_id = rank % getDeviceCount() + + #dev = cp.cuda.Device(device_id) + #free_mem, total_mem = dev.mem_info + #print("Mem free: ",free_mem, "Total mem: ",total_mem, "rank =",rank) + + # Create network object. + network = Network(*operands, options={'device_id' : device_id}) + + # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. + path, info = network.contract_path(optimize={'samples': 8, 'slicing': {'min_slices': max(32, size)}}) + #print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") + + # Select the best path from all ranks. + opt_cost, sender = comm.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) + + #if rank == root: + # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") + + # Broadcast info from the sender to all other ranks. + info = comm.bcast(info, sender) + + # Set path and slices. + path, info = network.contract_path(optimize={'path': info.path, 'slicing': info.slices}) + + # Calculate this process's share of the slices. + num_slices = info.num_slices + chunk, extra = num_slices // size, num_slices % size + slice_begin = rank * chunk + min(rank, extra) + slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + slices = range(slice_begin, slice_end) + + #print(f"Process {rank} is processing slice range: {slices}.") + + # Contract the group of slices the process is responsible for. + result = network.contract(slices=slices) + #print(f"Process {rank} result shape is : {result.shape}.") + #print(f"Process {rank} result size is : {result.nbytes}.") + + # Sum the partial contribution from each process on root. + result = comm.reduce(sendobj=result, op=MPI.SUM, root=root) + + return result, rank + +def eval_tn_nccl(qibo_circ, datatype, n_samples=8): + from mpi4py import MPI # this line initializes MPI + import socket + from cuquantum import Network + from cupy.cuda import nccl + + # Get the hostname + #hostname = socket.gethostname() + + root = 0 + comm_mpi = MPI.COMM_WORLD + rank = comm_mpi.Get_rank() + size = comm_mpi.Get_size() + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + device_id = rank % getDeviceCount() + + cp.cuda.Device(device_id).use() + + # Set up the NCCL communicator. + nccl_id = nccl.get_unique_id() if rank == root else None + nccl_id = comm_mpi.bcast(nccl_id, root) + comm_nccl = nccl.NcclCommunicator(size, nccl_id, rank) + + # Perform circuit conversion + myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft convetor",mem_avail, "rank =",rank) + operands = myconvertor.state_vector_operands() + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + + network = Network(*operands) + + # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. + path, info = network.contract_path(optimize={'samples': 8, 'slicing': {'min_slices': max(32, size)}}) + + #print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") + + # Select the best path from all ranks. + opt_cost, sender = comm_mpi.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) + + #if rank == root: + # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") + + # Broadcast info from the sender to all other ranks. + info = comm_mpi.bcast(info, sender) + + # Set path and slices. + path, info = network.contract_path(optimize={'path': info.path, 'slicing': info.slices}) + + # Calculate this process's share of the slices. + num_slices = info.num_slices + chunk, extra = num_slices // size, num_slices % size + slice_begin = rank * chunk + min(rank, extra) + slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + slices = range(slice_begin, slice_end) + + #print(f"Process {rank} is processing slice range: {slices}.") + + # Contract the group of slices the process is responsible for. + result = network.contract(slices=slices) + #print(f"Process {rank} result shape is : {result.shape}.") + #print(f"Process {rank} result size is : {result.nbytes}.") + + # Sum the partial contribution from each process on root. + stream_ptr = cp.cuda.get_current_stream().ptr + comm_nccl.reduce(result.data.ptr, result.data.ptr, result.size, nccl.NCCL_FLOAT64, nccl.NCCL_SUM, root, stream_ptr) + + return result, rank + +def eval_tn_nccl_expectation(qibo_circ, datatype, n_samples=8): + from mpi4py import MPI # this line initializes MPI + import socket + from cuquantum import Network + from cupy.cuda import nccl + + # Get the hostname + #hostname = socket.gethostname() + + root = 0 + comm_mpi = MPI.COMM_WORLD + rank = comm_mpi.Get_rank() + size = comm_mpi.Get_size() + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + device_id = rank % getDeviceCount() + + cp.cuda.Device(device_id).use() + + # Set up the NCCL communicator. + nccl_id = nccl.get_unique_id() if rank == root else None + nccl_id = comm_mpi.bcast(nccl_id, root) + comm_nccl = nccl.NcclCommunicator(size, nccl_id, rank) + + # Perform circuit conversion + myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft convetor",mem_avail, "rank =",rank) + operands = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) + + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + + network = Network(*operands) + + # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. + path, info = network.contract_path(optimize={'samples': 8, 'slicing': {'min_slices': max(32, size)}}) + + #print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") + + # Select the best path from all ranks. + opt_cost, sender = comm_mpi.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) + + #if rank == root: + # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") + + # Broadcast info from the sender to all other ranks. + info = comm_mpi.bcast(info, sender) + + # Set path and slices. + path, info = network.contract_path(optimize={'path': info.path, 'slicing': info.slices}) + + # Calculate this process's share of the slices. + num_slices = info.num_slices + chunk, extra = num_slices // size, num_slices % size + slice_begin = rank * chunk + min(rank, extra) + slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + slices = range(slice_begin, slice_end) + + #print(f"Process {rank} is processing slice range: {slices}.") + + # Contract the group of slices the process is responsible for. + result = network.contract(slices=slices) + #print(f"Process {rank} result shape is : {result.shape}.") + #print(f"Process {rank} result size is : {result.nbytes}.") + + # Sum the partial contribution from each process on root. + stream_ptr = cp.cuda.get_current_stream().ptr + comm_nccl.reduce(result.data.ptr, result.data.ptr, result.size, nccl.NCCL_FLOAT64, nccl.NCCL_SUM, root, stream_ptr) + + return result, rank + + +def eval_tn_MPI_2_expectation(qibo_circ, datatype, n_samples=8): + from mpi4py import MPI # this line initializes MPI + import socket + from cuquantum import Network + + # Get the hostname + #hostname = socket.gethostname() + + root = 0 + comm = MPI.COMM_WORLD + rank = comm.Get_rank() + size = comm.Get_size() + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + device_id = rank % getDeviceCount() + + + # Perform circuit conversion + myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft convetor",mem_avail, "rank =",rank) + operands = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + + # Broadcast the operand data. + #operands = comm.bcast(operands, root) + + # Assign the device for each process. + device_id = rank % getDeviceCount() + + #dev = cp.cuda.Device(device_id) + #free_mem, total_mem = dev.mem_info + #print("Mem free: ",free_mem, "Total mem: ",total_mem, "rank =",rank) + + # Create network object. + network = Network(*operands, options={'device_id' : device_id}) + + # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. + path, info = network.contract_path(optimize={'samples': 8, 'slicing': {'min_slices': max(32, size)}}) + #print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") + + # Select the best path from all ranks. + opt_cost, sender = comm.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) + + #if rank == root: + # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") + + # Broadcast info from the sender to all other ranks. + info = comm.bcast(info, sender) + + # Set path and slices. + path, info = network.contract_path(optimize={'path': info.path, 'slicing': info.slices}) + + # Calculate this process's share of the slices. + num_slices = info.num_slices + chunk, extra = num_slices // size, num_slices % size + slice_begin = rank * chunk + min(rank, extra) + slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + slices = range(slice_begin, slice_end) + + #print(f"Process {rank} is processing slice range: {slices}.") + + # Contract the group of slices the process is responsible for. + result = network.contract(slices=slices) + #print(f"Process {rank} result shape is : {result.shape}.") + #print(f"Process {rank} result size is : {result.nbytes}.") + + # Sum the partial contribution from each process on root. + result = comm.reduce(sendobj=result, op=MPI.SUM, root=root) + + return result, rank + + +def eval_tn_MPI_expectation(qibo_circ, datatype, n_samples=8): + from mpi4py import MPI # this line initializes MPI + import socket + # Get the hostname + #hostname = socket.gethostname() + + ncpu_threads = multiprocessing.cpu_count() // 2 + + comm = MPI.COMM_WORLD + rank = comm.Get_rank() + size = comm.Get_size() + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + device_id = rank % getDeviceCount() + cp.cuda.Device(device_id).use() + + handle = cutn.create() + network_opts = cutn.NetworkOptions(handle=handle, blocking="auto") + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft network opts",mem_avail, "rank =",rank) + cutn.distributed_reset_configuration(handle, *cutn.get_mpi_comm_pointer(comm)) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft distributed reset config",mem_avail, "rank =",rank) + # Perform circuit conversion + myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) + operands_interleave = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft convetor",mem_avail, "rank =",rank) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + + # Pathfinder: To search for the optimal path. Optimal path are assigned to path and info attribute of the network object. + network = cutn.Network(*operands_interleave, options=network_opts) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft cutn.Network(*operands_interleave,",mem_avail, "rank =",rank) + path, opt_info = network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads, 'slicing': {'min_slices': max(16, size)}}) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft contract path",mem_avail, "rank =",rank) + # Execution: To execute the contraction using the optimal path found previously + #print("opt_cost",opt_info.opt_cost, "Process =",rank) + + + num_slices = opt_info.num_slices#Andy + chunk, extra = num_slices // size, num_slices % size#Andy + slice_begin = rank * chunk + min(rank, extra)#Andy + slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra)#Andy + slices = range(slice_begin, slice_end)#Andy + result = network.contract(slices=slices) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft contract",mem_avail, "rank =",rank) + cutn.destroy(handle) + + return result, rank def eval_tn_MPI(qibo_circ, datatype, n_samples=8): """Convert qibo circuit to tensornet (TN) format and perform contraction using multi node and multi GPU through MPI. @@ -22,29 +370,59 @@ def eval_tn_MPI(qibo_circ, datatype, n_samples=8): """ from mpi4py import MPI # this line initializes MPI - + import socket + # Get the hostname + #hostname = socket.gethostname() + ncpu_threads = multiprocessing.cpu_count() // 2 - + comm = MPI.COMM_WORLD rank = comm.Get_rank() + size = comm.Get_size() + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) device_id = rank % getDeviceCount() cp.cuda.Device(device_id).use() handle = cutn.create() - cutn.distributed_reset_configuration(handle, *cutn.get_mpi_comm_pointer(comm)) network_opts = cutn.NetworkOptions(handle=handle, blocking="auto") - + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft network opts",mem_avail, "rank =",rank) + cutn.distributed_reset_configuration(handle, *cutn.get_mpi_comm_pointer(comm)) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft distributed reset config",mem_avail, "rank =",rank) # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft convetor",mem_avail, "rank =",rank) operands_interleave = myconvertor.state_vector_operands() + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) # Pathfinder: To search for the optimal path. Optimal path are assigned to path and info attribute of the network object. network = cutn.Network(*operands_interleave, options=network_opts) - network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads}) - + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft cutn.Network(*operands_interleave,",mem_avail, "rank =",rank) + network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads, 'slicing': {'min_slices': max(16, size)}}) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft contract path",mem_avail, "rank =",rank) # Execution: To execute the contraction using the optimal path found previously + #print("opt_cost",opt_info.opt_cost, "Process =",rank) + + ''' + path, opt_info = network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads, 'slicing': {'min_slices': max(16, size)}}) + + num_slices = opt_info.num_slices#Andy + chunk, extra = num_slices // size, num_slices % size#Andy + slice_begin = rank * chunk + min(rank, extra)#Andy + slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra)#Andy + slices = range(slice_begin, slice_end)#Andy + result = network.contract(slices=slices) + ''' result = network.contract() + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft contract",mem_avail, "rank =",rank) cutn.destroy(handle) return result, rank @@ -57,3 +435,19 @@ def eval_mps(qibo_circ, gate_algo, datatype): return mps_helper.contract_state_vector( myconvertor.mps_tensors, {"handle": myconvertor.handle} ) + +def PauliStringGen(nqubits): + + if nqubits <= 0: + return "Invalid input. N should be a positive integer." + + #characters = 'IXYZ' + characters = 'XXXZ' + + result = '' + + for i in range(nqubits): + char_to_add = characters[i % len(characters)] + result += char_to_add + + return result \ No newline at end of file From 37212a388d09bda535ea1c0d6fc1a95a0ed9b783 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 24 Jan 2024 11:47:32 +0800 Subject: [PATCH 006/199] Format with black --- src/qibotn/QiboCircuitConvertor.py | 85 +++---- src/qibotn/backends.py | 41 ++-- src/qibotn/cutn.py | 368 +++++++++++++++++------------ 3 files changed, 277 insertions(+), 217 deletions(-) diff --git a/src/qibotn/QiboCircuitConvertor.py b/src/qibotn/QiboCircuitConvertor.py index 11aaa716..d3a0569a 100644 --- a/src/qibotn/QiboCircuitConvertor.py +++ b/src/qibotn/QiboCircuitConvertor.py @@ -95,8 +95,7 @@ def init_intermediate_circuit(self, circuit): required_shape = self.op_shape_from_qubits(len(gate_qubits)) self.gate_tensors.append( ( - cp.asarray(gate.matrix(), dtype=self.dtype).reshape( - required_shape), + cp.asarray(gate.matrix(), dtype=self.dtype).reshape(required_shape), gate_qubits, ) ) @@ -111,7 +110,6 @@ def init_basis_map(self, backend, dtype): self.basis_map = {"0": state_0, "1": state_1} - def init_inverse_circuit(self, circuit): self.gate_tensors_inverse = [] gates_qubits_inverse = [] @@ -132,14 +130,13 @@ def init_inverse_circuit(self, circuit): # self.active_qubits is to identify qubits with at least 1 gate acting on it in the whole circuit. self.active_qubits_inverse = np.unique(gates_qubits_inverse) - - - def get_pauli_gates(self, pauli_map, dtype='complex128', backend=cp): + + def get_pauli_gates(self, pauli_map, dtype="complex128", backend=cp): """ Populate the gates for all pauli operators. Args: - pauli_map: A dictionary mapping qubits to pauli operators. + pauli_map: A dictionary mapping qubits to pauli operators. dtype: Data type for the tensor operands. backend: The package the tensor operands belong to. @@ -147,70 +144,74 @@ def get_pauli_gates(self, pauli_map, dtype='complex128', backend=cp): A sequence of pauli gates. """ asarray = backend.asarray - pauli_i = asarray([[1,0], [0,1]], dtype=dtype) - pauli_x = asarray([[0,1], [1,0]], dtype=dtype) - pauli_y = asarray([[0,-1j], [1j,0]], dtype=dtype) - pauli_z = asarray([[1,0], [0,-1]], dtype=dtype) - - operand_map = {'I': pauli_i, - 'X': pauli_x, - 'Y': pauli_y, - 'Z': pauli_z} + pauli_i = asarray([[1, 0], [0, 1]], dtype=dtype) + pauli_x = asarray([[0, 1], [1, 0]], dtype=dtype) + pauli_y = asarray([[0, -1j], [1j, 0]], dtype=dtype) + pauli_z = asarray([[1, 0], [0, -1]], dtype=dtype) + + operand_map = {"I": pauli_i, "X": pauli_x, "Y": pauli_y, "Z": pauli_z} gates = [] for qubit, pauli_char in pauli_map.items(): operand = operand_map.get(pauli_char) if operand is None: - raise ValueError('pauli string character must be one of I/X/Y/Z') + raise ValueError("pauli string character must be one of I/X/Y/Z") gates.append((operand, (qubit,))) return gates def expectation_operands(self, pauli_string): - #assign pauli string to qubit - #_get_forward_inverse_metadata() - input_bitstring = "0" * self.circuit.nqubits #Need all qubits! + # assign pauli string to qubit + # _get_forward_inverse_metadata() + input_bitstring = "0" * self.circuit.nqubits # Need all qubits! input_operands = self._get_bitstring_tensors(input_bitstring) - pauli_string = dict(zip(range(self.circuit.nqubits), pauli_string)) + pauli_string = dict(zip(range(self.circuit.nqubits), pauli_string)) pauli_map = pauli_string coned_qubits = pauli_map.keys() - + ( mode_labels, qubits_frontier, next_frontier, ) = self._init_mode_labels_from_qubits(range(self.circuit.nqubits)) - + gate_mode_labels, gate_operands = self._parse_gates_to_mode_labels_operands( self.gate_tensors, qubits_frontier, next_frontier ) - + operands = input_operands + gate_operands mode_labels += gate_mode_labels - + self.init_inverse_circuit(self.circuit.invert()) - - + next_frontier = max(qubits_frontier.values()) + 1 - #input_mode_labels, input_operands, qubits_frontier, next_frontier, inverse_gates = self._get_forward_inverse_metadata(coned_qubits) + # input_mode_labels, input_operands, qubits_frontier, next_frontier, inverse_gates = self._get_forward_inverse_metadata(coned_qubits) + + pauli_gates = self.get_pauli_gates( + pauli_map, dtype=self.dtype, backend=self.backend + ) - pauli_gates = self.get_pauli_gates(pauli_map, dtype=self.dtype, backend=self.backend) - - gates_inverse = pauli_gates + self.gate_tensors_inverse - - gate_mode_labels_inverse, gate_operands_inverse = self._parse_gates_to_mode_labels_operands( + + ( + gate_mode_labels_inverse, + gate_operands_inverse, + ) = self._parse_gates_to_mode_labels_operands( gates_inverse, qubits_frontier, next_frontier ) - mode_labels = mode_labels + gate_mode_labels_inverse + [[qubits_frontier[ix]] for ix in range(self.circuit.nqubits)] - operands = operands + gate_operands_inverse + operands[:self.circuit.nqubits] - + mode_labels = ( + mode_labels + + gate_mode_labels_inverse + + [[qubits_frontier[ix]] for ix in range(self.circuit.nqubits)] + ) + operands = operands + gate_operands_inverse + operands[: self.circuit.nqubits] + operand_exp_interleave = [x for y in zip(operands, mode_labels) for x in y] - - #expec = contract(*operand_exp_interleave) - #print(expec) - ''' + # expec = contract(*operand_exp_interleave) + # print(expec) + + """ gate_mode_labels, gate_operands = circ_utils.parse_gates_to_mode_labels_operands(gates, qubits_frontier, next_frontier) @@ -220,5 +221,5 @@ def expectation_operands(self, pauli_string): output_mode_labels = [] expression = circ_utils.convert_mode_labels_to_expression(mode_labels, output_mode_labels) - ''' - return operand_exp_interleave \ No newline at end of file + """ + return operand_exp_interleave diff --git a/src/qibotn/backends.py b/src/qibotn/backends.py index 4b28431d..3728a999 100644 --- a/src/qibotn/backends.py +++ b/src/qibotn/backends.py @@ -19,8 +19,6 @@ def __init__(self, platform): or platform == "cu_tensornet_expectation" or platform == "cu_tensornet_nccl" or platform == "cu_tensornet_nccl_expectation" - - ): # pragma: no cover self.platform = platform else: @@ -72,45 +70,44 @@ def execute_circuit( state = cutn.eval_mps(circuit, gate_algo, self.dtype) if self.platform == "qu_tensornet": - - #init_state = np.random.random(2**circuit.nqubits) + 1j * np.random.random(2**circuit.nqubits) - #init_state = init_state / np.sqrt((np.abs(init_state) ** 2).sum()) + # init_state = np.random.random(2**circuit.nqubits) + 1j * np.random.random(2**circuit.nqubits) + # init_state = init_state / np.sqrt((np.abs(init_state) ** 2).sum()) init_state = np.zeros(2**circuit.nqubits, dtype=self.dtype) init_state[0] = 1.0 state = quimb.eval(circuit.to_qasm(), init_state, backend="numpy") - + if self.platform == "cu_tensornet_mpi": if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - #state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) - state, rank = cutn.eval_tn_MPI_2(circuit, self.dtype,32) + # state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) + state, rank = cutn.eval_tn_MPI_2(circuit, self.dtype, 32) if rank > 0: state = np.array(0) - + if self.platform == "cu_tensornet_nccl": if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - #state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) - state, rank = cutn.eval_tn_nccl(circuit, self.dtype,32) + # state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) + state, rank = cutn.eval_tn_nccl(circuit, self.dtype, 32) if rank > 0: state = np.array(0) - + if self.platform == "cu_tensornet_expectation": if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - + state = cutn.eval_expectation(circuit, self.dtype) - + if self.platform == "cu_tensornet_mpi_expectation": if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - #state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) - #state, rank = cutn.eval_tn_MPI_expectation(circuit, self.dtype,32) - state, rank = cutn.eval_tn_MPI_2_expectation(circuit, self.dtype,32) - + # state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) + # state, rank = cutn.eval_tn_MPI_expectation(circuit, self.dtype,32) + state, rank = cutn.eval_tn_MPI_2_expectation(circuit, self.dtype, 32) + if rank > 0: state = np.array(0) @@ -118,10 +115,10 @@ def execute_circuit( if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - #state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) - #state, rank = cutn.eval_tn_MPI_expectation(circuit, self.dtype,32) - state, rank = cutn.eval_tn_nccl_expectation(circuit, self.dtype,32) - + # state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) + # state, rank = cutn.eval_tn_MPI_expectation(circuit, self.dtype,32) + state, rank = cutn.eval_tn_nccl_expectation(circuit, self.dtype, 32) + if rank > 0: state = np.array(0) diff --git a/src/qibotn/cutn.py b/src/qibotn/cutn.py index 67d70c49..aca33ff1 100644 --- a/src/qibotn/cutn.py +++ b/src/qibotn/cutn.py @@ -13,9 +13,13 @@ def eval(qibo_circ, datatype): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract(*myconvertor.state_vector_operands()) + def eval_expectation(qibo_circ, datatype): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - return contract(*myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits))) + return contract( + *myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) + ) + def eval_tn_MPI_2(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI @@ -23,73 +27,79 @@ def eval_tn_MPI_2(qibo_circ, datatype, n_samples=8): from cuquantum import Network # Get the hostname - #hostname = socket.gethostname() - + # hostname = socket.gethostname() + root = 0 comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) device_id = rank % getDeviceCount() - - + # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft convetor",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft convetor",mem_avail, "rank =",rank) operands = myconvertor.state_vector_operands() - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) - + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + # Broadcast the operand data. - #operands = comm.bcast(operands, root) - + # operands = comm.bcast(operands, root) + # Assign the device for each process. device_id = rank % getDeviceCount() - - #dev = cp.cuda.Device(device_id) - #free_mem, total_mem = dev.mem_info - #print("Mem free: ",free_mem, "Total mem: ",total_mem, "rank =",rank) + + # dev = cp.cuda.Device(device_id) + # free_mem, total_mem = dev.mem_info + # print("Mem free: ",free_mem, "Total mem: ",total_mem, "rank =",rank) # Create network object. - network = Network(*operands, options={'device_id' : device_id}) + network = Network(*operands, options={"device_id": device_id}) # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. - path, info = network.contract_path(optimize={'samples': 8, 'slicing': {'min_slices': max(32, size)}}) - #print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") + path, info = network.contract_path( + optimize={"samples": 8, "slicing": {"min_slices": max(32, size)}} + ) + # print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") # Select the best path from all ranks. opt_cost, sender = comm.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) - #if rank == root: + # if rank == root: # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") # Broadcast info from the sender to all other ranks. info = comm.bcast(info, sender) # Set path and slices. - path, info = network.contract_path(optimize={'path': info.path, 'slicing': info.slices}) + path, info = network.contract_path( + optimize={"path": info.path, "slicing": info.slices} + ) # Calculate this process's share of the slices. num_slices = info.num_slices chunk, extra = num_slices // size, num_slices % size slice_begin = rank * chunk + min(rank, extra) - slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + slice_end = ( + num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + ) slices = range(slice_begin, slice_end) - #print(f"Process {rank} is processing slice range: {slices}.") + # print(f"Process {rank} is processing slice range: {slices}.") # Contract the group of slices the process is responsible for. result = network.contract(slices=slices) - #print(f"Process {rank} result shape is : {result.shape}.") - #print(f"Process {rank} result size is : {result.nbytes}.") + # print(f"Process {rank} result shape is : {result.shape}.") + # print(f"Process {rank} result size is : {result.nbytes}.") # Sum the partial contribution from each process on root. result = comm.reduce(sendobj=result, op=MPI.SUM, root=root) - + return result, rank + def eval_tn_nccl(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket @@ -97,18 +107,18 @@ def eval_tn_nccl(qibo_circ, datatype, n_samples=8): from cupy.cuda import nccl # Get the hostname - #hostname = socket.gethostname() - + # hostname = socket.gethostname() + root = 0 comm_mpi = MPI.COMM_WORLD rank = comm_mpi.Get_rank() size = comm_mpi.Get_size() - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) device_id = rank % getDeviceCount() - + cp.cuda.Device(device_id).use() - + # Set up the NCCL communicator. nccl_id = nccl.get_unique_id() if rank == root else None nccl_id = comm_mpi.bcast(nccl_id, root) @@ -116,51 +126,66 @@ def eval_tn_nccl(qibo_circ, datatype, n_samples=8): # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft convetor",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft convetor",mem_avail, "rank =",rank) operands = myconvertor.state_vector_operands() - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) network = Network(*operands) # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. - path, info = network.contract_path(optimize={'samples': 8, 'slicing': {'min_slices': max(32, size)}}) + path, info = network.contract_path( + optimize={"samples": 8, "slicing": {"min_slices": max(32, size)}} + ) - #print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") + # print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") # Select the best path from all ranks. opt_cost, sender = comm_mpi.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) - #if rank == root: + # if rank == root: # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") # Broadcast info from the sender to all other ranks. info = comm_mpi.bcast(info, sender) # Set path and slices. - path, info = network.contract_path(optimize={'path': info.path, 'slicing': info.slices}) + path, info = network.contract_path( + optimize={"path": info.path, "slicing": info.slices} + ) # Calculate this process's share of the slices. num_slices = info.num_slices chunk, extra = num_slices // size, num_slices % size slice_begin = rank * chunk + min(rank, extra) - slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + slice_end = ( + num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + ) slices = range(slice_begin, slice_end) - #print(f"Process {rank} is processing slice range: {slices}.") + # print(f"Process {rank} is processing slice range: {slices}.") # Contract the group of slices the process is responsible for. result = network.contract(slices=slices) - #print(f"Process {rank} result shape is : {result.shape}.") - #print(f"Process {rank} result size is : {result.nbytes}.") + # print(f"Process {rank} result shape is : {result.shape}.") + # print(f"Process {rank} result size is : {result.nbytes}.") # Sum the partial contribution from each process on root. stream_ptr = cp.cuda.get_current_stream().ptr - comm_nccl.reduce(result.data.ptr, result.data.ptr, result.size, nccl.NCCL_FLOAT64, nccl.NCCL_SUM, root, stream_ptr) - + comm_nccl.reduce( + result.data.ptr, + result.data.ptr, + result.size, + nccl.NCCL_FLOAT64, + nccl.NCCL_SUM, + root, + stream_ptr, + ) + return result, rank + def eval_tn_nccl_expectation(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket @@ -168,18 +193,18 @@ def eval_tn_nccl_expectation(qibo_circ, datatype, n_samples=8): from cupy.cuda import nccl # Get the hostname - #hostname = socket.gethostname() - + # hostname = socket.gethostname() + root = 0 comm_mpi = MPI.COMM_WORLD rank = comm_mpi.Get_rank() size = comm_mpi.Get_size() - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) device_id = rank % getDeviceCount() - + cp.cuda.Device(device_id).use() - + # Set up the NCCL communicator. nccl_id = nccl.get_unique_id() if rank == root else None nccl_id = comm_mpi.bcast(nccl_id, root) @@ -187,50 +212,64 @@ def eval_tn_nccl_expectation(qibo_circ, datatype, n_samples=8): # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft convetor",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft convetor",mem_avail, "rank =",rank) operands = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) network = Network(*operands) # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. - path, info = network.contract_path(optimize={'samples': 8, 'slicing': {'min_slices': max(32, size)}}) + path, info = network.contract_path( + optimize={"samples": 8, "slicing": {"min_slices": max(32, size)}} + ) - #print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") + # print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") # Select the best path from all ranks. opt_cost, sender = comm_mpi.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) - #if rank == root: + # if rank == root: # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") # Broadcast info from the sender to all other ranks. info = comm_mpi.bcast(info, sender) # Set path and slices. - path, info = network.contract_path(optimize={'path': info.path, 'slicing': info.slices}) + path, info = network.contract_path( + optimize={"path": info.path, "slicing": info.slices} + ) # Calculate this process's share of the slices. num_slices = info.num_slices chunk, extra = num_slices // size, num_slices % size slice_begin = rank * chunk + min(rank, extra) - slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + slice_end = ( + num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + ) slices = range(slice_begin, slice_end) - #print(f"Process {rank} is processing slice range: {slices}.") + # print(f"Process {rank} is processing slice range: {slices}.") # Contract the group of slices the process is responsible for. result = network.contract(slices=slices) - #print(f"Process {rank} result shape is : {result.shape}.") - #print(f"Process {rank} result size is : {result.nbytes}.") + # print(f"Process {rank} result shape is : {result.shape}.") + # print(f"Process {rank} result size is : {result.nbytes}.") # Sum the partial contribution from each process on root. stream_ptr = cp.cuda.get_current_stream().ptr - comm_nccl.reduce(result.data.ptr, result.data.ptr, result.size, nccl.NCCL_FLOAT64, nccl.NCCL_SUM, root, stream_ptr) - + comm_nccl.reduce( + result.data.ptr, + result.data.ptr, + result.size, + nccl.NCCL_FLOAT64, + nccl.NCCL_SUM, + root, + stream_ptr, + ) + return result, rank @@ -240,128 +279,144 @@ def eval_tn_MPI_2_expectation(qibo_circ, datatype, n_samples=8): from cuquantum import Network # Get the hostname - #hostname = socket.gethostname() - + # hostname = socket.gethostname() + root = 0 comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) device_id = rank % getDeviceCount() - - + # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft convetor",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft convetor",mem_avail, "rank =",rank) operands = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) - + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + # Broadcast the operand data. - #operands = comm.bcast(operands, root) - + # operands = comm.bcast(operands, root) + # Assign the device for each process. device_id = rank % getDeviceCount() - - #dev = cp.cuda.Device(device_id) - #free_mem, total_mem = dev.mem_info - #print("Mem free: ",free_mem, "Total mem: ",total_mem, "rank =",rank) + + # dev = cp.cuda.Device(device_id) + # free_mem, total_mem = dev.mem_info + # print("Mem free: ",free_mem, "Total mem: ",total_mem, "rank =",rank) # Create network object. - network = Network(*operands, options={'device_id' : device_id}) + network = Network(*operands, options={"device_id": device_id}) # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. - path, info = network.contract_path(optimize={'samples': 8, 'slicing': {'min_slices': max(32, size)}}) - #print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") + path, info = network.contract_path( + optimize={"samples": 8, "slicing": {"min_slices": max(32, size)}} + ) + # print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") # Select the best path from all ranks. opt_cost, sender = comm.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) - #if rank == root: + # if rank == root: # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") # Broadcast info from the sender to all other ranks. info = comm.bcast(info, sender) # Set path and slices. - path, info = network.contract_path(optimize={'path': info.path, 'slicing': info.slices}) + path, info = network.contract_path( + optimize={"path": info.path, "slicing": info.slices} + ) # Calculate this process's share of the slices. num_slices = info.num_slices chunk, extra = num_slices // size, num_slices % size slice_begin = rank * chunk + min(rank, extra) - slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + slice_end = ( + num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + ) slices = range(slice_begin, slice_end) - #print(f"Process {rank} is processing slice range: {slices}.") + # print(f"Process {rank} is processing slice range: {slices}.") # Contract the group of slices the process is responsible for. result = network.contract(slices=slices) - #print(f"Process {rank} result shape is : {result.shape}.") - #print(f"Process {rank} result size is : {result.nbytes}.") + # print(f"Process {rank} result shape is : {result.shape}.") + # print(f"Process {rank} result size is : {result.nbytes}.") # Sum the partial contribution from each process on root. result = comm.reduce(sendobj=result, op=MPI.SUM, root=root) - + return result, rank def eval_tn_MPI_expectation(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket + # Get the hostname - #hostname = socket.gethostname() - + # hostname = socket.gethostname() + ncpu_threads = multiprocessing.cpu_count() // 2 - + comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) device_id = rank % getDeviceCount() cp.cuda.Device(device_id).use() handle = cutn.create() network_opts = cutn.NetworkOptions(handle=handle, blocking="auto") - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft network opts",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft network opts",mem_avail, "rank =",rank) cutn.distributed_reset_configuration(handle, *cutn.get_mpi_comm_pointer(comm)) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft distributed reset config",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft distributed reset config",mem_avail, "rank =",rank) # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - operands_interleave = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft convetor",mem_avail, "rank =",rank) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + operands_interleave = myconvertor.expectation_operands( + PauliStringGen(qibo_circ.nqubits) + ) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft convetor",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) # Pathfinder: To search for the optimal path. Optimal path are assigned to path and info attribute of the network object. network = cutn.Network(*operands_interleave, options=network_opts) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft cutn.Network(*operands_interleave,",mem_avail, "rank =",rank) - path, opt_info = network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads, 'slicing': {'min_slices': max(16, size)}}) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft contract path",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft cutn.Network(*operands_interleave,",mem_avail, "rank =",rank) + path, opt_info = network.contract_path( + optimize={ + "samples": n_samples, + "threads": ncpu_threads, + "slicing": {"min_slices": max(16, size)}, + } + ) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft contract path",mem_avail, "rank =",rank) # Execution: To execute the contraction using the optimal path found previously - #print("opt_cost",opt_info.opt_cost, "Process =",rank) - - - num_slices = opt_info.num_slices#Andy - chunk, extra = num_slices // size, num_slices % size#Andy - slice_begin = rank * chunk + min(rank, extra)#Andy - slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra)#Andy - slices = range(slice_begin, slice_end)#Andy + # print("opt_cost",opt_info.opt_cost, "Process =",rank) + + num_slices = opt_info.num_slices # Andy + chunk, extra = num_slices // size, num_slices % size # Andy + slice_begin = rank * chunk + min(rank, extra) # Andy + slice_end = ( + num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + ) # Andy + slices = range(slice_begin, slice_end) # Andy result = network.contract(slices=slices) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft contract",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft contract",mem_avail, "rank =",rank) cutn.destroy(handle) return result, rank + def eval_tn_MPI(qibo_circ, datatype, n_samples=8): """Convert qibo circuit to tensornet (TN) format and perform contraction using multi node and multi GPU through MPI. The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. @@ -371,45 +426,52 @@ def eval_tn_MPI(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket + # Get the hostname - #hostname = socket.gethostname() - + # hostname = socket.gethostname() + ncpu_threads = multiprocessing.cpu_count() // 2 - + comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) device_id = rank % getDeviceCount() cp.cuda.Device(device_id).use() handle = cutn.create() network_opts = cutn.NetworkOptions(handle=handle, blocking="auto") - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft network opts",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft network opts",mem_avail, "rank =",rank) cutn.distributed_reset_configuration(handle, *cutn.get_mpi_comm_pointer(comm)) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft distributed reset config",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft distributed reset config",mem_avail, "rank =",rank) # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft convetor",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft convetor",mem_avail, "rank =",rank) operands_interleave = myconvertor.state_vector_operands() - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) # Pathfinder: To search for the optimal path. Optimal path are assigned to path and info attribute of the network object. network = cutn.Network(*operands_interleave, options=network_opts) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft cutn.Network(*operands_interleave,",mem_avail, "rank =",rank) - network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads, 'slicing': {'min_slices': max(16, size)}}) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft contract path",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft cutn.Network(*operands_interleave,",mem_avail, "rank =",rank) + network.contract_path( + optimize={ + "samples": n_samples, + "threads": ncpu_threads, + "slicing": {"min_slices": max(16, size)}, + } + ) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft contract path",mem_avail, "rank =",rank) # Execution: To execute the contraction using the optimal path found previously - #print("opt_cost",opt_info.opt_cost, "Process =",rank) + # print("opt_cost",opt_info.opt_cost, "Process =",rank) - ''' + """ path, opt_info = network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads, 'slicing': {'min_slices': max(16, size)}}) num_slices = opt_info.num_slices#Andy @@ -418,16 +480,16 @@ def eval_tn_MPI(qibo_circ, datatype, n_samples=8): slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra)#Andy slices = range(slice_begin, slice_end)#Andy result = network.contract(slices=slices) - ''' + """ result = network.contract() - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft contract",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft contract",mem_avail, "rank =",rank) cutn.destroy(handle) return result, rank - + def eval_mps(qibo_circ, gate_algo, datatype): myconvertor = QiboCircuitToMPS(qibo_circ, gate_algo, dtype=datatype) mps_helper = MPSContractionHelper(myconvertor.num_qubits) @@ -436,18 +498,18 @@ def eval_mps(qibo_circ, gate_algo, datatype): myconvertor.mps_tensors, {"handle": myconvertor.handle} ) + def PauliStringGen(nqubits): - if nqubits <= 0: return "Invalid input. N should be a positive integer." - #characters = 'IXYZ' - characters = 'XXXZ' + # characters = 'IXYZ' + characters = "XXXZ" - result = '' + result = "" for i in range(nqubits): char_to_add = characters[i % len(characters)] result += char_to_add - return result \ No newline at end of file + return result From b4b2fec1b3f21477023ff73d7b2d6c41f67bdc89 Mon Sep 17 00:00:00 2001 From: Liwei Yang Date: Wed, 24 Jan 2024 17:50:54 +0800 Subject: [PATCH 007/199] Add CPU and GPU into backends --- src/qibotn/backends/__init__.py | 2 + src/qibotn/backends/cpu.py | 302 ++++++++++++++++++++++++++++++++ src/qibotn/backends/gpu.py | 39 +++++ 3 files changed, 343 insertions(+) create mode 100644 src/qibotn/backends/__init__.py create mode 100644 src/qibotn/backends/cpu.py create mode 100644 src/qibotn/backends/gpu.py diff --git a/src/qibotn/backends/__init__.py b/src/qibotn/backends/__init__.py new file mode 100644 index 00000000..ebc3a205 --- /dev/null +++ b/src/qibotn/backends/__init__.py @@ -0,0 +1,2 @@ +from qibotn.backends.cpu import NumbaBackend +from qibotn.backends.gpu import CuTensorNet diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py new file mode 100644 index 00000000..22afcdc1 --- /dev/null +++ b/src/qibotn/backends/cpu.py @@ -0,0 +1,302 @@ +import numpy as np +from qibo.backends.numpy import NumpyBackend +from qibo.config import log +from qibo.gates.abstract import ParametrizedGate +from qibo.gates.channels import ReadoutErrorChannel +from qibo.gates.special import FusedGate + +from qibojit.backends.matrices import CustomMatrices + +GATE_OPS = { + "X": "apply_x", + "CNOT": "apply_x", + "TOFFOLI": "apply_x", + "Y": "apply_y", + "Z": "apply_z", + "CZ": "apply_z", + "U1": "apply_z_pow", + "CU1": "apply_z_pow", + "SWAP": "apply_swap", + "fSim": "apply_fsim", + "GeneralizedfSim": "apply_fsim", +} + + +class NumbaBackend(NumpyBackend): + def __init__(self): + super().__init__() + import sys + + import psutil + from numba import __version__ as numba_version + + from qibotn import __version__ as qibotn_version + + self.name = "qibotn" + self.platform = "numba" + self.versions.update( + { + "qibotn": qibotn_version, + "numba": numba_version, + } + ) + self.numeric_types = ( + int, + float, + complex, + np.int32, + np.int64, + np.float32, + np.float64, + np.complex64, + np.complex128, + ) + self.tensor_types = (np.ndarray,) + self.device = "/CPU:0" + self.custom_matrices = CustomMatrices(self.dtype) + self.gates = gates + self.ops = ops + self.measure_frequencies_op = ops.measure_frequencies + self.multi_qubit_kernels = { + 3: self.gates.apply_three_qubit_gate_kernel, + 4: self.gates.apply_four_qubit_gate_kernel, + 5: self.gates.apply_five_qubit_gate_kernel, + } + if sys.platform == "darwin": # pragma: no cover + self.set_threads(psutil.cpu_count(logical=False)) + else: + self.set_threads(len(psutil.Process().cpu_affinity())) + + def set_precision(self, precision): + if precision != self.precision: + super().set_precision(precision) + if self.custom_matrices: + self.custom_matrices = CustomMatrices(self.dtype) + + def set_threads(self, nthreads): + import numba + + numba.set_num_threads(nthreads) + self.nthreads = nthreads + + # def cast(self, x, dtype=None, copy=False): Inherited from ``NumpyBackend`` + + # def to_numpy(self, x): Inherited from ``NumpyBackend`` + + def zero_state(self, nqubits): + size = 2**nqubits + state = np.empty((size,), dtype=self.dtype) + return self.ops.initial_state_vector(state) + + def zero_density_matrix(self, nqubits): + size = 2**nqubits + state = np.empty((size, size), dtype=self.dtype) + return self.ops.initial_density_matrix(state) + + # def plus_state(self, nqubits): Inherited from ``NumpyBackend`` + + # def plus_density_matrix(self, nqubits): Inherited from ``NumpyBackend`` + + # def asmatrix_special(self, gate): Inherited from ``NumpyBackend`` + + # def control_matrix(self, gate): Inherited from ``NumpyBackend`` + + def one_qubit_base(self, state, nqubits, target, kernel, gate, qubits): + ncontrols = len(qubits) - 1 if qubits is not None else 0 + m = nqubits - target - 1 + nstates = 1 << (nqubits - ncontrols - 1) + if ncontrols: + kernel = getattr(self.gates, "multicontrol_{}_kernel".format(kernel)) + return kernel(state, gate, qubits, nstates, m) + kernel = getattr(self.gates, "{}_kernel".format(kernel)) + return kernel(state, gate, nstates, m) + + def two_qubit_base(self, state, nqubits, target1, target2, kernel, gate, qubits): + ncontrols = len(qubits) - 2 if qubits is not None else 0 + if target1 > target2: + swap_targets = True + m1 = nqubits - target1 - 1 + m2 = nqubits - target2 - 1 + else: + swap_targets = False + m1 = nqubits - target2 - 1 + m2 = nqubits - target1 - 1 + nstates = 1 << (nqubits - 2 - ncontrols) + if ncontrols: + kernel = getattr(self.gates, "multicontrol_{}_kernel".format(kernel)) + return kernel(state, gate, qubits, nstates, m1, m2, swap_targets) + kernel = getattr(self.gates, "{}_kernel".format(kernel)) + return kernel(state, gate, nstates, m1, m2, swap_targets) + + def multi_qubit_base(self, state, nqubits, targets, gate, qubits): + if qubits is None: + qubits = np.array(sorted(nqubits - q - 1 for q in targets), dtype="int32") + nstates = 1 << (nqubits - len(qubits)) + targets = np.array( + [1 << (nqubits - t - 1) for t in targets[::-1]], dtype="int64" + ) + if len(targets) > 5: + kernel = self.gates.apply_multi_qubit_gate_kernel + else: + kernel = self.multi_qubit_kernels.get(len(targets)) + return kernel(state, gate, qubits, nstates, targets) + + @staticmethod + def _create_qubits_tensor(gate, nqubits): + # TODO: Treat density matrices + qubits = [nqubits - q - 1 for q in gate.control_qubits] + qubits.extend(nqubits - q - 1 for q in gate.target_qubits) + return np.array(sorted(qubits), dtype="int32") + + def _as_custom_matrix(self, gate): + name = gate.__class__.__name__ + if isinstance(gate, ParametrizedGate): + return getattr(self.custom_matrices, name)(*gate.parameters) + elif isinstance(gate, FusedGate): # pragma: no cover + # fusion is tested in qibo tests + return self.asmatrix_fused(gate) + else: + return getattr(self.custom_matrices, name) + + def apply_gate(self, gate, state, nqubits): + matrix = self._as_custom_matrix(gate) + qubits = self._create_qubits_tensor(gate, nqubits) + targets = gate.target_qubits + state = self.cast(state) + if len(targets) == 1: + op = GATE_OPS.get(gate.__class__.__name__, "apply_gate") + return self.one_qubit_base(state, nqubits, *targets, op, matrix, qubits) + elif len(targets) == 2: + op = GATE_OPS.get(gate.__class__.__name__, "apply_two_qubit_gate") + return self.two_qubit_base(state, nqubits, *targets, op, matrix, qubits) + else: + return self.multi_qubit_base(state, nqubits, targets, matrix, qubits) + + def apply_gate_density_matrix(self, gate, state, nqubits, inverse=False): + name = gate.__class__.__name__ + if name == "Y": + return self._apply_ygate_density_matrix(gate, state, nqubits) + if inverse: + # used to reset the state when applying channels + # see :meth:`qibojit.backend.NumpyBackend.apply_channel_density_matrix` below + matrix = np.linalg.inv(gate.asmatrix(self)) + matrix = self.cast(matrix) + else: + matrix = self._as_custom_matrix(gate) + qubits = self._create_qubits_tensor(gate, nqubits) + qubits_dm = qubits + nqubits + targets = gate.target_qubits + targets_dm = tuple(q + nqubits for q in targets) + + state = self.cast(state) + shape = state.shape + if len(targets) == 1: + op = GATE_OPS.get(name, "apply_gate") + state = self.one_qubit_base( + state.ravel(), 2 * nqubits, *targets, op, matrix, qubits_dm + ) + state = self.one_qubit_base( + state, 2 * nqubits, *targets_dm, op, np.conj(matrix), qubits + ) + elif len(targets) == 2: + op = GATE_OPS.get(name, "apply_two_qubit_gate") + state = self.two_qubit_base( + state.ravel(), 2 * nqubits, *targets, op, matrix, qubits_dm + ) + state = self.two_qubit_base( + state, 2 * nqubits, *targets_dm, op, np.conj(matrix), qubits + ) + else: + state = self.multi_qubit_base( + state.ravel(), 2 * nqubits, targets, matrix, qubits_dm + ) + state = self.multi_qubit_base( + state, 2 * nqubits, targets_dm, np.conj(matrix), qubits + ) + return np.reshape(state, shape) + + def _apply_ygate_density_matrix(self, gate, state, nqubits): + matrix = self._as_custom_matrix(gate) + qubits = self._create_qubits_tensor(gate, nqubits) + qubits_dm = qubits + nqubits + targets = gate.target_qubits + targets_dm = tuple(q + nqubits for q in targets) + state = self.cast(state) + shape = state.shape + state = self.one_qubit_base( + state.ravel(), 2 * nqubits, *targets, "apply_y", matrix, qubits_dm + ) + # force using ``apply_gate`` kernel so that conjugate is properly applied + state = self.one_qubit_base( + state, 2 * nqubits, *targets_dm, "apply_gate", np.conj(matrix), qubits + ) + return np.reshape(state, shape) + + # def apply_channel(self, gate): Inherited from ``NumpyBackend`` + + def apply_channel_density_matrix(self, channel, state, nqubits): + state = self.cast(state) + if isinstance(channel, ReadoutErrorChannel) is True: + state_copy = self.cast(state, copy=True) + new_state = (1 - channel.coefficient_sum) * state + for coeff, gate in zip(channel.coefficients, channel.gates): + state = self.apply_gate_density_matrix(gate, state, nqubits) + new_state += coeff * state + # reset the state + if isinstance(channel, ReadoutErrorChannel) is True: + state = self.cast(state_copy, copy=True) + else: + state = self.apply_gate_density_matrix( + gate, state, nqubits, inverse=True + ) + return new_state + + def collapse_state(self, state, qubits, shot, nqubits, normalize=True): + state = self.cast(state) + qubits = self.cast([nqubits - q - 1 for q in reversed(qubits)], dtype="int32") + if normalize: + return self.ops.collapse_state_normalized(state, qubits, int(shot), nqubits) + else: + return self.ops.collapse_state(state, qubits, int(shot), nqubits) + + def collapse_density_matrix(self, state, qubits, shot, nqubits, normalize=True): + state = self.cast(state) + shape = state.shape + dm_qubits = [q + nqubits for q in qubits] + state = self.collapse_state(state.ravel(), dm_qubits, shot, 2 * nqubits, False) + state = self.collapse_state(state, qubits, shot, 2 * nqubits, False) + state = self.np.reshape(state, shape) + if normalize: + state = state / self.np.trace(state) + return state + + # def calculate_probabilities(self, state, qubits, nqubits): Inherited from ``NumpyBackend`` + + # def sample_shots(self, probabilities, nshots): Inherited from ``NumpyBackend`` + + # def aggregate_shots(self, shots): Inherited from ``NumpyBackend`` + + # def samples_to_binary(self, samples, nqubits): Inherited from ``NumpyBackend`` + + # def samples_to_decimal(self, samples, nqubits): Inherited from ``NumpyBackend`` + + def sample_frequencies(self, probabilities, nshots): + from qibo.config import SHOT_METROPOLIS_THRESHOLD + + if nshots < SHOT_METROPOLIS_THRESHOLD: + return super().sample_frequencies(probabilities, nshots) + + import collections + + seed = np.random.randint(0, int(1e8), dtype="int64") + nqubits = int(np.log2(tuple(probabilities.shape)[0])) + frequencies = np.zeros(2**nqubits, dtype="int64") + # always fall back to numba CPU backend because for ops not implemented on GPU + frequencies = self.measure_frequencies_op( + frequencies, probabilities, nshots, nqubits, seed, self.nthreads + ) + return collections.Counter({i: f for i, f in enumerate(frequencies) if f > 0}) + + # def calculate_frequencies(self, samples): Inherited from ``NumpyBackend`` + + # def assert_allclose(self, value, target, rtol=1e-7, atol=0.0): Inherited from ``NumpyBackend`` diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py new file mode 100644 index 00000000..bca295dc --- /dev/null +++ b/src/qibotn/backends/gpu.py @@ -0,0 +1,39 @@ +from qibo.backends.numpy import NumpyBackend + + +class CuTensorNet(NumpyBackend): # pragma: no cover + # CI does not test for GPU + + def __init__(self): + super().__init__() + import cuquantum # pylint: disable=import-error + from cuquantum import cutensornet as cutn # pylint: disable=import-error + + self.cuquantum = cuquantum + self.cutn = cutn + self.platform = "cutensornet" + self.versions["cuquantum"] = self.cuquantum.__version__ + self.supports_multigpu = True + self.handle = self.cutn.create() + + def __del__(self): + if hasattr(self, "cutn"): + self.cutn.destroy(self.handle) + + def set_precision(self, precision): + if precision != self.precision: + super().set_precision(precision) + + def get_cuda_type(self, dtype="complex64"): + if dtype == "complex128": + return ( + self.cuquantum.cudaDataType.CUDA_C_64F, + self.cuquantum.ComputeType.COMPUTE_64F, + ) + elif dtype == "complex64": + return ( + self.cuquantum.cudaDataType.CUDA_C_32F, + self.cuquantum.ComputeType.COMPUTE_32F, + ) + else: + raise TypeError("Type can be either complex64 or complex128") From f8c2b88dc08b4171b911ec6839be21851fa9aed0 Mon Sep 17 00:00:00 2001 From: Liwei Yang Date: Fri, 26 Jan 2024 17:56:11 +0800 Subject: [PATCH 008/199] Finish CuTensorNet class for cuQuantum cutensornet --- src/qibotn/backends/gpu.py | 144 ++++++++++++++++++++++++++++++++++++- 1 file changed, 143 insertions(+), 1 deletion(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index bca295dc..e44b6669 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -1,21 +1,38 @@ +import numpy as np + from qibo.backends.numpy import NumpyBackend +from qibo.result import CircuitResult +from qibo.config import raise_error class CuTensorNet(NumpyBackend): # pragma: no cover # CI does not test for GPU - def __init__(self): + def __init__(self, MPI_enabled=False, MPS_enabled=False, NCCL_enabled=False): super().__init__() import cuquantum # pylint: disable=import-error from cuquantum import cutensornet as cutn # pylint: disable=import-error + self.name = "qibotn" self.cuquantum = cuquantum self.cutn = cutn self.platform = "cutensornet" self.versions["cuquantum"] = self.cuquantum.__version__ self.supports_multigpu = True + self.MPI_enabled = MPI_enabled + self.MPS_enabled = MPS_enabled + self.NCCL_enabled = NCCL_enabled self.handle = self.cutn.create() + def apply_gate(self, gate, state, nqubits): # pragma: no cover + raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") + + def apply_gate_density_matrix(self, gate, state, nqubits): # pragma: no cover + raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") + + def assign_measurements(self, measurement_map, circuit_result): + raise_error(NotImplementedError, "Not implemented in QiboTN.") + def __del__(self): if hasattr(self, "cutn"): self.cutn.destroy(self.handle) @@ -37,3 +54,128 @@ def get_cuda_type(self, dtype="complex64"): ) else: raise TypeError("Type can be either complex64 or complex128") + + def execute_circuit( + self, circuit, initial_state=None, nshots=None, return_array=False + ): # pragma: no cover + """Executes a quantum circuit. + + Args: + 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. + + """ + + import qibotn.cutn + + cutn = qibotn.cutn + MPI_enabled = self.MPI_enabled + MPS_enabled = self.MPS_enabled + NCCL_enabled = self.NCCL_enabled + + if ( + MPI_enabled == False + and MPS_enabled == False + and NCCL_enabled == False + ): + if initial_state is not None: + raise_error(NotImplementedError, + "QiboTN cannot support initial state.") + + state = cutn.eval(circuit, self.dtype) + + if ( + MPI_enabled == False + and MPS_enabled == True + and NCCL_enabled == False + ): + if initial_state is not None: + raise_error(NotImplementedError, + "QiboTN cannot support initial state.") + + gate_algo = { + "qr_method": False, + "svd_method": { + "partition": "UV", + "abs_cutoff": 1e-12, + }, + } # make this user input + state = cutn.eval_mps(circuit, gate_algo, self.dtype) + + if ( + MPI_enabled == True + and MPS_enabled == False + and NCCL_enabled == False + ): + if initial_state is not None: + raise_error(NotImplementedError, + "QiboTN cannot support initial state.") + + state, rank = cutn.eval_tn_MPI_2(circuit, self.dtype, 32) + if rank > 0: + state = np.array(0) + + if ( + MPI_enabled == False + and MPS_enabled == False + and NCCL_enabled == True + ): + if initial_state is not None: + raise_error(NotImplementedError, + "QiboTN cannot support initial state.") + + state, rank = cutn.eval_tn_nccl(circuit, self.dtype, 32) + if rank > 0: + state = np.array(0) + + if ( + MPI_enabled == False + and MPS_enabled == False + and NCCL_enabled == False + ): + if initial_state is not None: + raise_error(NotImplementedError, + "QiboTN cannot support initial state.") + + state = cutn.eval_expectation(circuit, self.dtype) + + if ( + MPI_enabled == True + and MPS_enabled == False + and NCCL_enabled == False + ): + if initial_state is not None: + raise_error(NotImplementedError, + "QiboTN cannot support initial state.") + + state, rank = cutn.eval_tn_MPI_2_expectation( + circuit, self.dtype, 32) + + if rank > 0: + state = np.array(0) + + if ( + MPI_enabled == False + and MPS_enabled == False + and NCCL_enabled == True + ): + if initial_state is not None: + raise_error(NotImplementedError, + "QiboTN cannot support initial state.") + + state, rank = cutn.eval_tn_nccl_expectation( + circuit, self.dtype, 32) + + if rank > 0: + state = np.array(0) + + if return_array: + return state.flatten() + else: + circuit._final_state = CircuitResult( + self, circuit, state.flatten(), nshots) + return circuit._final_state From d3c9266dcfa9fd5ad9376c11aaafe2a3d2bb67fc Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 10:12:09 +0800 Subject: [PATCH 009/199] Rename function call --- src/qibotn/backends/gpu.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index e44b6669..e2087db9 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -115,7 +115,7 @@ def execute_circuit( raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = cutn.eval_tn_MPI_2(circuit, self.dtype, 32) + state, rank = cutn.eval_tn_MPI(circuit, self.dtype, 32) if rank > 0: state = np.array(0) @@ -152,7 +152,7 @@ def execute_circuit( raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = cutn.eval_tn_MPI_2_expectation( + state, rank = cutn.eval_tn_MPI_expectation( circuit, self.dtype, 32) if rank > 0: From 293af81b43879465f1027dcd6f7ae80be85570dc Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 10:22:39 +0800 Subject: [PATCH 010/199] Remove eval_tn_mpi --- src/qibotn/cutn.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/qibotn/cutn.py b/src/qibotn/cutn.py index aca33ff1..e4ff2a69 100644 --- a/src/qibotn/cutn.py +++ b/src/qibotn/cutn.py @@ -22,6 +22,12 @@ def eval_expectation(qibo_circ, datatype): def eval_tn_MPI_2(qibo_circ, datatype, n_samples=8): + """Convert qibo circuit to tensornet (TN) format and perform contraction using multi node and multi GPU through MPI. + The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. + The pathfinder looks at user defined number of samples (n_samples) iteratively to 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 a dense vector representation of the TN. + """ + from mpi4py import MPI # this line initializes MPI import socket from cuquantum import Network @@ -96,6 +102,17 @@ def eval_tn_MPI_2(qibo_circ, datatype, n_samples=8): # Sum the partial contribution from each process on root. result = comm.reduce(sendobj=result, op=MPI.SUM, root=root) + + """ + path, opt_info = network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads, 'slicing': {'min_slices': max(16, size)}}) + + num_slices = opt_info.num_slices#Andy + chunk, extra = num_slices // size, num_slices % size#Andy + slice_begin = rank * chunk + min(rank, extra)#Andy + slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra)#Andy + slices = range(slice_begin, slice_end)#Andy + result = network.contract(slices=slices) + """ return result, rank From 1035629745341e35a50ca23b508bc375d016deaf Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 10:23:33 +0800 Subject: [PATCH 011/199] Remove eval_tn_mpi --- src/qibotn/cutn.py | 75 ---------------------------------------------- 1 file changed, 75 deletions(-) diff --git a/src/qibotn/cutn.py b/src/qibotn/cutn.py index e4ff2a69..e37785b5 100644 --- a/src/qibotn/cutn.py +++ b/src/qibotn/cutn.py @@ -433,80 +433,6 @@ def eval_tn_MPI_expectation(qibo_circ, datatype, n_samples=8): return result, rank - -def eval_tn_MPI(qibo_circ, datatype, n_samples=8): - """Convert qibo circuit to tensornet (TN) format and perform contraction using multi node and multi GPU through MPI. - The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. - The pathfinder looks at user defined number of samples (n_samples) iteratively to 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 a dense vector representation of the TN. - """ - - from mpi4py import MPI # this line initializes MPI - import socket - - # Get the hostname - # hostname = socket.gethostname() - - ncpu_threads = multiprocessing.cpu_count() // 2 - - comm = MPI.COMM_WORLD - rank = comm.Get_rank() - size = comm.Get_size() - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) - device_id = rank % getDeviceCount() - cp.cuda.Device(device_id).use() - - handle = cutn.create() - network_opts = cutn.NetworkOptions(handle=handle, blocking="auto") - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft network opts",mem_avail, "rank =",rank) - cutn.distributed_reset_configuration(handle, *cutn.get_mpi_comm_pointer(comm)) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft distributed reset config",mem_avail, "rank =",rank) - # Perform circuit conversion - myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft convetor",mem_avail, "rank =",rank) - operands_interleave = myconvertor.state_vector_operands() - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) - - # Pathfinder: To search for the optimal path. Optimal path are assigned to path and info attribute of the network object. - network = cutn.Network(*operands_interleave, options=network_opts) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft cutn.Network(*operands_interleave,",mem_avail, "rank =",rank) - network.contract_path( - optimize={ - "samples": n_samples, - "threads": ncpu_threads, - "slicing": {"min_slices": max(16, size)}, - } - ) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft contract path",mem_avail, "rank =",rank) - # Execution: To execute the contraction using the optimal path found previously - # print("opt_cost",opt_info.opt_cost, "Process =",rank) - - """ - path, opt_info = network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads, 'slicing': {'min_slices': max(16, size)}}) - - num_slices = opt_info.num_slices#Andy - chunk, extra = num_slices // size, num_slices % size#Andy - slice_begin = rank * chunk + min(rank, extra)#Andy - slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra)#Andy - slices = range(slice_begin, slice_end)#Andy - result = network.contract(slices=slices) - """ - result = network.contract() - - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft contract",mem_avail, "rank =",rank) - cutn.destroy(handle) - - return result, rank - - def eval_mps(qibo_circ, gate_algo, datatype): myconvertor = QiboCircuitToMPS(qibo_circ, gate_algo, dtype=datatype) mps_helper = MPSContractionHelper(myconvertor.num_qubits) @@ -515,7 +441,6 @@ def eval_mps(qibo_circ, gate_algo, datatype): myconvertor.mps_tensors, {"handle": myconvertor.handle} ) - def PauliStringGen(nqubits): if nqubits <= 0: return "Invalid input. N should be a positive integer." From 66aaf0e073f55ee54c0795b81ebff8d26831f24f Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 10:24:11 +0800 Subject: [PATCH 012/199] Rename eval_tn_MPI_2 to eval_tn_MPI --- src/qibotn/cutn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/cutn.py b/src/qibotn/cutn.py index e37785b5..84eda78c 100644 --- a/src/qibotn/cutn.py +++ b/src/qibotn/cutn.py @@ -21,7 +21,7 @@ def eval_expectation(qibo_circ, datatype): ) -def eval_tn_MPI_2(qibo_circ, datatype, n_samples=8): +def eval_tn_MPI(qibo_circ, datatype, n_samples=8): """Convert qibo circuit to tensornet (TN) format and perform contraction using multi node and multi GPU through MPI. The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. The pathfinder looks at user defined number of samples (n_samples) iteratively to select the least costly contraction path. This is sped up with multi thread. From fea2b1150f679a19d6cc852c85da41def0d47366 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 10:34:10 +0800 Subject: [PATCH 013/199] Remove eval_tn_MPI_expectation --- src/qibotn/cutn.py | 65 ---------------------------------------------- 1 file changed, 65 deletions(-) diff --git a/src/qibotn/cutn.py b/src/qibotn/cutn.py index 84eda78c..36d6a1c9 100644 --- a/src/qibotn/cutn.py +++ b/src/qibotn/cutn.py @@ -368,71 +368,6 @@ def eval_tn_MPI_2_expectation(qibo_circ, datatype, n_samples=8): return result, rank - -def eval_tn_MPI_expectation(qibo_circ, datatype, n_samples=8): - from mpi4py import MPI # this line initializes MPI - import socket - - # Get the hostname - # hostname = socket.gethostname() - - ncpu_threads = multiprocessing.cpu_count() // 2 - - comm = MPI.COMM_WORLD - rank = comm.Get_rank() - size = comm.Get_size() - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) - device_id = rank % getDeviceCount() - cp.cuda.Device(device_id).use() - - handle = cutn.create() - network_opts = cutn.NetworkOptions(handle=handle, blocking="auto") - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft network opts",mem_avail, "rank =",rank) - cutn.distributed_reset_configuration(handle, *cutn.get_mpi_comm_pointer(comm)) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft distributed reset config",mem_avail, "rank =",rank) - # Perform circuit conversion - myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - operands_interleave = myconvertor.expectation_operands( - PauliStringGen(qibo_circ.nqubits) - ) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft convetor",mem_avail, "rank =",rank) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) - - # Pathfinder: To search for the optimal path. Optimal path are assigned to path and info attribute of the network object. - network = cutn.Network(*operands_interleave, options=network_opts) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft cutn.Network(*operands_interleave,",mem_avail, "rank =",rank) - path, opt_info = network.contract_path( - optimize={ - "samples": n_samples, - "threads": ncpu_threads, - "slicing": {"min_slices": max(16, size)}, - } - ) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft contract path",mem_avail, "rank =",rank) - # Execution: To execute the contraction using the optimal path found previously - # print("opt_cost",opt_info.opt_cost, "Process =",rank) - - num_slices = opt_info.num_slices # Andy - chunk, extra = num_slices // size, num_slices % size # Andy - slice_begin = rank * chunk + min(rank, extra) # Andy - slice_end = ( - num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) - ) # Andy - slices = range(slice_begin, slice_end) # Andy - result = network.contract(slices=slices) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft contract",mem_avail, "rank =",rank) - cutn.destroy(handle) - - return result, rank - def eval_mps(qibo_circ, gate_algo, datatype): myconvertor = QiboCircuitToMPS(qibo_circ, gate_algo, dtype=datatype) mps_helper = MPSContractionHelper(myconvertor.num_qubits) From 827c285656580f12d50e4ab42bf76f5942dedfff Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 10:34:47 +0800 Subject: [PATCH 014/199] Rename eval_tn_MPI_2_expectation to eval_tn_MPI_expectation --- src/qibotn/cutn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/cutn.py b/src/qibotn/cutn.py index 36d6a1c9..3df40e18 100644 --- a/src/qibotn/cutn.py +++ b/src/qibotn/cutn.py @@ -290,7 +290,7 @@ def eval_tn_nccl_expectation(qibo_circ, datatype, n_samples=8): return result, rank -def eval_tn_MPI_2_expectation(qibo_circ, datatype, n_samples=8): +def eval_tn_MPI_expectation(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket from cuquantum import Network From 6181161133680ef3056ac69c95cc415089f1e6fa Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 10:41:26 +0800 Subject: [PATCH 015/199] Format with black --- src/qibotn/cutn.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/qibotn/cutn.py b/src/qibotn/cutn.py index 3df40e18..39cca9ce 100644 --- a/src/qibotn/cutn.py +++ b/src/qibotn/cutn.py @@ -27,7 +27,7 @@ def eval_tn_MPI(qibo_circ, datatype, n_samples=8): The pathfinder looks at user defined number of samples (n_samples) iteratively to 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 a dense vector representation of the TN. """ - + from mpi4py import MPI # this line initializes MPI import socket from cuquantum import Network @@ -102,7 +102,7 @@ def eval_tn_MPI(qibo_circ, datatype, n_samples=8): # Sum the partial contribution from each process on root. result = comm.reduce(sendobj=result, op=MPI.SUM, root=root) - + """ path, opt_info = network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads, 'slicing': {'min_slices': max(16, size)}}) @@ -368,6 +368,7 @@ def eval_tn_MPI_expectation(qibo_circ, datatype, n_samples=8): return result, rank + def eval_mps(qibo_circ, gate_algo, datatype): myconvertor = QiboCircuitToMPS(qibo_circ, gate_algo, dtype=datatype) mps_helper = MPSContractionHelper(myconvertor.num_qubits) @@ -376,6 +377,7 @@ def eval_mps(qibo_circ, gate_algo, datatype): myconvertor.mps_tensors, {"handle": myconvertor.handle} ) + def PauliStringGen(nqubits): if nqubits <= 0: return "Invalid input. N should be a positive integer." From 44ac0648d510b58a7934ca8590cba1474fd030ab Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 10:43:28 +0800 Subject: [PATCH 016/199] Rename to better reflect function of file --- src/qibotn/backends/gpu.py | 4 ++-- src/qibotn/{cutn.py => eval.py} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename src/qibotn/{cutn.py => eval.py} (100%) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index e2087db9..96eaab33 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -70,9 +70,9 @@ def execute_circuit( """ - import qibotn.cutn + import qibotn.src.qibotn.eval - cutn = qibotn.cutn + cutn = qibotn.eval MPI_enabled = self.MPI_enabled MPS_enabled = self.MPS_enabled NCCL_enabled = self.NCCL_enabled diff --git a/src/qibotn/cutn.py b/src/qibotn/eval.py similarity index 100% rename from src/qibotn/cutn.py rename to src/qibotn/eval.py From e11214060f1ff540443bd468d626c5eb02bdf959 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 10:55:22 +0800 Subject: [PATCH 017/199] Add condition to trigger expectation calculation --- src/qibotn/backends/gpu.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index 96eaab33..2d28987d 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -8,7 +8,7 @@ class CuTensorNet(NumpyBackend): # pragma: no cover # CI does not test for GPU - def __init__(self, MPI_enabled=False, MPS_enabled=False, NCCL_enabled=False): + def __init__(self, MPI_enabled=False, MPS_enabled=False, NCCL_enabled=False, expectation_enabled=False): super().__init__() import cuquantum # pylint: disable=import-error from cuquantum import cutensornet as cutn # pylint: disable=import-error @@ -22,6 +22,7 @@ def __init__(self, MPI_enabled=False, MPS_enabled=False, NCCL_enabled=False): self.MPI_enabled = MPI_enabled self.MPS_enabled = MPS_enabled self.NCCL_enabled = NCCL_enabled + self.expectation_enabled = expectation_enabled self.handle = self.cutn.create() def apply_gate(self, gate, state, nqubits): # pragma: no cover @@ -76,11 +77,13 @@ def execute_circuit( MPI_enabled = self.MPI_enabled MPS_enabled = self.MPS_enabled NCCL_enabled = self.NCCL_enabled + expectation_enabled = self.expectation_enabled if ( MPI_enabled == False and MPS_enabled == False and NCCL_enabled == False + and expectation_enabled == False ): if initial_state is not None: raise_error(NotImplementedError, @@ -92,6 +95,7 @@ def execute_circuit( MPI_enabled == False and MPS_enabled == True and NCCL_enabled == False + and expectation_enabled == False ): if initial_state is not None: raise_error(NotImplementedError, @@ -110,6 +114,7 @@ def execute_circuit( MPI_enabled == True and MPS_enabled == False and NCCL_enabled == False + and expectation_enabled == False ): if initial_state is not None: raise_error(NotImplementedError, @@ -123,6 +128,7 @@ def execute_circuit( MPI_enabled == False and MPS_enabled == False and NCCL_enabled == True + and expectation_enabled == False ): if initial_state is not None: raise_error(NotImplementedError, @@ -136,6 +142,7 @@ def execute_circuit( MPI_enabled == False and MPS_enabled == False and NCCL_enabled == False + and expectation_enabled == True ): if initial_state is not None: raise_error(NotImplementedError, @@ -147,6 +154,7 @@ def execute_circuit( MPI_enabled == True and MPS_enabled == False and NCCL_enabled == False + and expectation_enabled == True ): if initial_state is not None: raise_error(NotImplementedError, @@ -162,6 +170,7 @@ def execute_circuit( MPI_enabled == False and MPS_enabled == False and NCCL_enabled == True + and expectation_enabled == True ): if initial_state is not None: raise_error(NotImplementedError, From 34125d131cbee4ac0d393727e20fbe0683706e89 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 14:37:33 +0800 Subject: [PATCH 018/199] Change import from cutn to eval [skip CI] --- tests/test_cuquantum_cutensor_backend.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_cuquantum_cutensor_backend.py b/tests/test_cuquantum_cutensor_backend.py index 3a87206a..58020742 100644 --- a/tests/test_cuquantum_cutensor_backend.py +++ b/tests/test_cuquantum_cutensor_backend.py @@ -32,7 +32,7 @@ def test_eval(nqubits: int, dtype="complex128"): dtype (str): The data type for precision, 'complex64' for single, 'complex128' for double. """ - import qibotn.cutn + import qibotn.eval # Test qibo qibo.set_backend(backend=config.qibo.backend, @@ -59,7 +59,7 @@ def test_mps(nqubits: int, dtype="complex128"): dtype (str): The data type for precision, 'complex64' for single, 'complex128' for double. """ - import qibotn.cutn + import qibotn.eval # Test qibo qibo.set_backend(backend=config.qibo.backend, @@ -78,7 +78,7 @@ def test_mps(nqubits: int, dtype="complex128"): }} cutn_time, result_tn = time( - lambda: qibotn.cutn.eval_mps(circ_qibo, gate_algo, dtype).flatten()) + lambda: qibotn.eval.eval_mps(circ_qibo, gate_algo, dtype).flatten()) print( f"State vector difference: {abs(result_tn - result_sv_cp).max():0.3e}") From 93331aa12c864a4a7d53f856836efd1ecdbaec52 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 15:40:11 +0800 Subject: [PATCH 019/199] Rename function name to be more descriptive [skip CI] --- src/qibotn/eval.py | 14 +++++++------- tests/test_cuquantum_cutensor_backend.py | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/qibotn/eval.py b/src/qibotn/eval.py index 39cca9ce..a283ff34 100644 --- a/src/qibotn/eval.py +++ b/src/qibotn/eval.py @@ -9,19 +9,19 @@ from qibotn.mps_contraction_helper import MPSContractionHelper -def eval(qibo_circ, datatype): +def dense_vector_tn(qibo_circ, datatype): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract(*myconvertor.state_vector_operands()) -def eval_expectation(qibo_circ, datatype): +def expectation_tn(qibo_circ, datatype): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract( *myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) ) -def eval_tn_MPI(qibo_circ, datatype, n_samples=8): +def dense_vector_tn_MPI(qibo_circ, datatype, n_samples=8): """Convert qibo circuit to tensornet (TN) format and perform contraction using multi node and multi GPU through MPI. The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. The pathfinder looks at user defined number of samples (n_samples) iteratively to select the least costly contraction path. This is sped up with multi thread. @@ -117,7 +117,7 @@ def eval_tn_MPI(qibo_circ, datatype, n_samples=8): return result, rank -def eval_tn_nccl(qibo_circ, datatype, n_samples=8): +def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket from cuquantum import Network @@ -203,7 +203,7 @@ def eval_tn_nccl(qibo_circ, datatype, n_samples=8): return result, rank -def eval_tn_nccl_expectation(qibo_circ, datatype, n_samples=8): +def expectation_tn_nccl(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket from cuquantum import Network @@ -290,7 +290,7 @@ def eval_tn_nccl_expectation(qibo_circ, datatype, n_samples=8): return result, rank -def eval_tn_MPI_expectation(qibo_circ, datatype, n_samples=8): +def expectation_tn_MPI(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket from cuquantum import Network @@ -369,7 +369,7 @@ def eval_tn_MPI_expectation(qibo_circ, datatype, n_samples=8): return result, rank -def eval_mps(qibo_circ, gate_algo, datatype): +def dense_vector_mps(qibo_circ, gate_algo, datatype): myconvertor = QiboCircuitToMPS(qibo_circ, gate_algo, dtype=datatype) mps_helper = MPSContractionHelper(myconvertor.num_qubits) diff --git a/tests/test_cuquantum_cutensor_backend.py b/tests/test_cuquantum_cutensor_backend.py index 58020742..3de5c17b 100644 --- a/tests/test_cuquantum_cutensor_backend.py +++ b/tests/test_cuquantum_cutensor_backend.py @@ -42,7 +42,7 @@ def test_eval(nqubits: int, dtype="complex128"): # Test Cuquantum cutn_time, result_tn = time( - lambda: qibotn.cutn.eval(qibo_circ, dtype).flatten()) + lambda: qibotn.eval.dense_vector_tn(qibo_circ, dtype).flatten()) assert 1e-2 * qibo_time < cutn_time < 1e2 * qibo_time assert np.allclose( @@ -78,7 +78,7 @@ def test_mps(nqubits: int, dtype="complex128"): }} cutn_time, result_tn = time( - lambda: qibotn.eval.eval_mps(circ_qibo, gate_algo, dtype).flatten()) + lambda: qibotn.eval.dense_vector_mps(circ_qibo, gate_algo, dtype).flatten()) print( f"State vector difference: {abs(result_tn - result_sv_cp).max():0.3e}") From 391d25230706f9ea9be12315a3d96968f7c367ef Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 15:54:53 +0800 Subject: [PATCH 020/199] Update function call name [skip CI] --- src/qibotn/backends/gpu.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index 2d28987d..47f667fa 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -71,9 +71,8 @@ def execute_circuit( """ - import qibotn.src.qibotn.eval + import qibotn.eval as eval - cutn = qibotn.eval MPI_enabled = self.MPI_enabled MPS_enabled = self.MPS_enabled NCCL_enabled = self.NCCL_enabled @@ -89,7 +88,7 @@ def execute_circuit( raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state = cutn.eval(circuit, self.dtype) + state = eval.dense_vector_tn(circuit, self.dtype) if ( MPI_enabled == False @@ -108,7 +107,7 @@ def execute_circuit( "abs_cutoff": 1e-12, }, } # make this user input - state = cutn.eval_mps(circuit, gate_algo, self.dtype) + state = eval.dense_vector_mps(circuit, gate_algo, self.dtype) if ( MPI_enabled == True @@ -120,7 +119,7 @@ def execute_circuit( raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = cutn.eval_tn_MPI(circuit, self.dtype, 32) + state, rank = eval.dense_vector_tn_MPI(circuit, self.dtype, 32) if rank > 0: state = np.array(0) @@ -134,7 +133,7 @@ def execute_circuit( raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = cutn.eval_tn_nccl(circuit, self.dtype, 32) + state, rank = eval.dense_vector_tn_nccl(circuit, self.dtype, 32) if rank > 0: state = np.array(0) @@ -148,7 +147,7 @@ def execute_circuit( raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state = cutn.eval_expectation(circuit, self.dtype) + state = eval.expectation_tn(circuit, self.dtype) if ( MPI_enabled == True @@ -160,7 +159,7 @@ def execute_circuit( raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = cutn.eval_tn_MPI_expectation( + state, rank = eval.expectation_tn_MPI( circuit, self.dtype, 32) if rank > 0: @@ -176,7 +175,7 @@ def execute_circuit( raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = cutn.eval_tn_nccl_expectation( + state, rank = eval.expectation_tn_nccl( circuit, self.dtype, 32) if rank > 0: From 9ae7dc18f53adfd3e0958020cbefc440934a1a6e Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 17:49:30 +0800 Subject: [PATCH 021/199] Change execute_circuit inputs --- src/qibotn/backends/gpu.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index 47f667fa..5f413442 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -1,14 +1,14 @@ import numpy as np from qibo.backends.numpy import NumpyBackend -from qibo.result import CircuitResult +from qibo.states import CircuitResult from qibo.config import raise_error class CuTensorNet(NumpyBackend): # pragma: no cover # CI does not test for GPU - def __init__(self, MPI_enabled=False, MPS_enabled=False, NCCL_enabled=False, expectation_enabled=False): + def __init__(self): super().__init__() import cuquantum # pylint: disable=import-error from cuquantum import cutensornet as cutn # pylint: disable=import-error @@ -19,10 +19,6 @@ def __init__(self, MPI_enabled=False, MPS_enabled=False, NCCL_enabled=False, exp self.platform = "cutensornet" self.versions["cuquantum"] = self.cuquantum.__version__ self.supports_multigpu = True - self.MPI_enabled = MPI_enabled - self.MPS_enabled = MPS_enabled - self.NCCL_enabled = NCCL_enabled - self.expectation_enabled = expectation_enabled self.handle = self.cutn.create() def apply_gate(self, gate, state, nqubits): # pragma: no cover @@ -57,7 +53,7 @@ def get_cuda_type(self, dtype="complex64"): raise TypeError("Type can be either complex64 or complex128") def execute_circuit( - self, circuit, initial_state=None, nshots=None, return_array=False + self, circuit, test, MPI_enabled=False, MPS_enabled=False, NCCL_enabled=False, expectation_enabled=False, initial_state=None, nshots=None, return_array=False ): # pragma: no cover """Executes a quantum circuit. @@ -72,12 +68,7 @@ def execute_circuit( """ import qibotn.eval as eval - - MPI_enabled = self.MPI_enabled - MPS_enabled = self.MPS_enabled - NCCL_enabled = self.NCCL_enabled - expectation_enabled = self.expectation_enabled - + print("Test", test) if ( MPI_enabled == False and MPS_enabled == False From 6216a3291ea426f51ada1944550c7022667b7f3d Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 31 Jan 2024 10:02:34 +0800 Subject: [PATCH 022/199] Rename expectation function name --- src/qibotn/backends/gpu.py | 12 ++++++++---- src/qibotn/eval.py | 6 +++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index 5f413442..6301bf7c 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -53,7 +53,7 @@ def get_cuda_type(self, dtype="complex64"): raise TypeError("Type can be either complex64 or complex128") def execute_circuit( - self, circuit, test, MPI_enabled=False, MPS_enabled=False, NCCL_enabled=False, expectation_enabled=False, initial_state=None, nshots=None, return_array=False + self, circuit, MPI_enabled=False, MPS_enabled=False, NCCL_enabled=False, expectation_enabled=False, initial_state=None, nshots=None, return_array=False ): # pragma: no cover """Executes a quantum circuit. @@ -68,7 +68,11 @@ def execute_circuit( """ import qibotn.eval as eval - print("Test", test) + print("MPI_enabled", MPI_enabled) + print("MPS_enabled", MPS_enabled) + print("NCCL_enabled", NCCL_enabled) + print("expectation_enabled", expectation_enabled) + if ( MPI_enabled == False and MPS_enabled == False @@ -150,7 +154,7 @@ def execute_circuit( raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.expectation_tn_MPI( + state, rank = eval.expectation_pauli_tn_MPI( circuit, self.dtype, 32) if rank > 0: @@ -166,7 +170,7 @@ def execute_circuit( raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.expectation_tn_nccl( + state, rank = eval.expectation_pauli_tn_nccl( circuit, self.dtype, 32) if rank > 0: diff --git a/src/qibotn/eval.py b/src/qibotn/eval.py index a283ff34..5bb5ba18 100644 --- a/src/qibotn/eval.py +++ b/src/qibotn/eval.py @@ -14,7 +14,7 @@ def dense_vector_tn(qibo_circ, datatype): return contract(*myconvertor.state_vector_operands()) -def expectation_tn(qibo_circ, datatype): +def expectation_pauli_tn(qibo_circ, datatype): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract( *myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) @@ -203,7 +203,7 @@ def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8): return result, rank -def expectation_tn_nccl(qibo_circ, datatype, n_samples=8): +def expectation_pauli_tn_nccl(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket from cuquantum import Network @@ -290,7 +290,7 @@ def expectation_tn_nccl(qibo_circ, datatype, n_samples=8): return result, rank -def expectation_tn_MPI(qibo_circ, datatype, n_samples=8): +def expectation_pauli_tn_MPI(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket from cuquantum import Network From 168018593cd07eab93e4c09b76d44f45b81c5208 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 31 Jan 2024 10:52:09 +0800 Subject: [PATCH 023/199] Use runcard to select compute type as no other way to pass in para wout changing struct --- src/qibotn/backends/gpu.py | 130 +++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 62 deletions(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index 6301bf7c..ad82b75f 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -8,11 +8,24 @@ class CuTensorNet(NumpyBackend): # pragma: no cover # CI does not test for GPU - def __init__(self): + def __init__(self, runcard): super().__init__() import cuquantum # pylint: disable=import-error from cuquantum import cutensornet as cutn # pylint: disable=import-error + if runcard is not None: + print("inside runcard") + # Parse the runcard or use its values to set flags + self.MPI_enabled = runcard.get("MPI_enabled", False) + self.MPS_enabled = runcard.get("MPS_enabled", False) + self.NCCL_enabled = runcard.get("NCCL_enabled", False) + self.expectation_enabled = runcard.get("expectation_enabled", False) + else: + self.MPI_enabled = False + self.MPS_enabled = False + self.NCCL_enabled = False + self.expectation_enabled = False + self.name = "qibotn" self.cuquantum = cuquantum self.cutn = cutn @@ -53,7 +66,7 @@ def get_cuda_type(self, dtype="complex64"): raise TypeError("Type can be either complex64 or complex128") def execute_circuit( - self, circuit, MPI_enabled=False, MPS_enabled=False, NCCL_enabled=False, expectation_enabled=False, initial_state=None, nshots=None, return_array=False + self, circuit, initial_state=None, nshots=None, return_array=False ): # pragma: no cover """Executes a quantum circuit. @@ -68,32 +81,31 @@ def execute_circuit( """ import qibotn.eval as eval - print("MPI_enabled", MPI_enabled) - print("MPS_enabled", MPS_enabled) - print("NCCL_enabled", NCCL_enabled) - print("expectation_enabled", expectation_enabled) - + + print("MPI_enabled", self.MPI_enabled) + print("MPS_enabled", self.MPS_enabled) + print("NCCL_enabled", self.NCCL_enabled) + print("expectation_enabled", self.expectation_enabled) + if ( - MPI_enabled == False - and MPS_enabled == False - and NCCL_enabled == False - and expectation_enabled == False + self.MPI_enabled == False + and self.MPS_enabled == False + and self.NCCL_enabled == False + and self.expectation_enabled == False ): if initial_state is not None: - raise_error(NotImplementedError, - "QiboTN cannot support initial state.") + raise_error(NotImplementedError, "QiboTN cannot support initial state.") state = eval.dense_vector_tn(circuit, self.dtype) - if ( - MPI_enabled == False - and MPS_enabled == True - and NCCL_enabled == False - and expectation_enabled == False + elif ( + self.MPI_enabled == False + and self.MPS_enabled == True + and self.NCCL_enabled == False + and self.expectation_enabled == False ): if initial_state is not None: - raise_error(NotImplementedError, - "QiboTN cannot support initial state.") + raise_error(NotImplementedError, "QiboTN cannot support initial state.") gate_algo = { "qr_method": False, @@ -104,81 +116,75 @@ def execute_circuit( } # make this user input state = eval.dense_vector_mps(circuit, gate_algo, self.dtype) - if ( - MPI_enabled == True - and MPS_enabled == False - and NCCL_enabled == False - and expectation_enabled == False + elif ( + self.MPI_enabled == True + and self.MPS_enabled == False + and self.NCCL_enabled == False + and self.expectation_enabled == False ): if initial_state is not None: - raise_error(NotImplementedError, - "QiboTN cannot support initial state.") + raise_error(NotImplementedError, "QiboTN cannot support initial state.") state, rank = eval.dense_vector_tn_MPI(circuit, self.dtype, 32) if rank > 0: state = np.array(0) - if ( - MPI_enabled == False - and MPS_enabled == False - and NCCL_enabled == True - and expectation_enabled == False + elif ( + self.MPI_enabled == False + and self.MPS_enabled == False + and self.NCCL_enabled == True + and self.expectation_enabled == False ): if initial_state is not None: - raise_error(NotImplementedError, - "QiboTN cannot support initial state.") + raise_error(NotImplementedError, "QiboTN cannot support initial state.") state, rank = eval.dense_vector_tn_nccl(circuit, self.dtype, 32) if rank > 0: state = np.array(0) - if ( - MPI_enabled == False - and MPS_enabled == False - and NCCL_enabled == False - and expectation_enabled == True + elif ( + self.MPI_enabled == False + and self.MPS_enabled == False + and self.NCCL_enabled == False + and self.expectation_enabled == True ): if initial_state is not None: - raise_error(NotImplementedError, - "QiboTN cannot support initial state.") + raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state = eval.expectation_tn(circuit, self.dtype) + state = eval.expectation_pauli_tn(circuit, self.dtype) - if ( - MPI_enabled == True - and MPS_enabled == False - and NCCL_enabled == False - and expectation_enabled == True + elif ( + self.MPI_enabled == True + and self.MPS_enabled == False + and self.NCCL_enabled == False + and self.expectation_enabled == True ): if initial_state is not None: - raise_error(NotImplementedError, - "QiboTN cannot support initial state.") + raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.expectation_pauli_tn_MPI( - circuit, self.dtype, 32) + state, rank = eval.expectation_pauli_tn_MPI(circuit, self.dtype, 32) if rank > 0: state = np.array(0) - if ( - MPI_enabled == False - and MPS_enabled == False - and NCCL_enabled == True - and expectation_enabled == True + elif ( + self.MPI_enabled == False + and self.MPS_enabled == False + and self.NCCL_enabled == True + and self.expectation_enabled == True ): if initial_state is not None: - raise_error(NotImplementedError, - "QiboTN cannot support initial state.") + raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.expectation_pauli_tn_nccl( - circuit, self.dtype, 32) + state, rank = eval.expectation_pauli_tn_nccl(circuit, self.dtype, 32) if rank > 0: state = np.array(0) + else: + raise_error(NotImplementedError, "Backend not supported.") if return_array: return state.flatten() else: - circuit._final_state = CircuitResult( - self, circuit, state.flatten(), nshots) + circuit._final_state = CircuitResult(self, circuit, state.flatten(), nshots) return circuit._final_state From e7776f55bf61faa084e14e36579b01a1f4a93877 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 31 Jan 2024 10:56:47 +0800 Subject: [PATCH 024/199] Remove print [skip CI] --- src/qibotn/backends/gpu.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index ad82b75f..d758d5a1 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -14,8 +14,6 @@ def __init__(self, runcard): from cuquantum import cutensornet as cutn # pylint: disable=import-error if runcard is not None: - print("inside runcard") - # Parse the runcard or use its values to set flags self.MPI_enabled = runcard.get("MPI_enabled", False) self.MPS_enabled = runcard.get("MPS_enabled", False) self.NCCL_enabled = runcard.get("NCCL_enabled", False) @@ -82,11 +80,6 @@ def execute_circuit( import qibotn.eval as eval - print("MPI_enabled", self.MPI_enabled) - print("MPS_enabled", self.MPS_enabled) - print("NCCL_enabled", self.NCCL_enabled) - print("expectation_enabled", self.expectation_enabled) - if ( self.MPI_enabled == False and self.MPS_enabled == False @@ -181,7 +174,7 @@ def execute_circuit( if rank > 0: state = np.array(0) else: - raise_error(NotImplementedError, "Backend not supported.") + raise_error(NotImplementedError, "Compute type not supported.") if return_array: return state.flatten() From 9b8058d99e3824cbe1d8398c811211fb59b02c16 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 31 Jan 2024 14:56:21 +0800 Subject: [PATCH 025/199] Allow user to specify Pauli string pattern for expecation calculation [skip CI] --- src/qibotn/backends/gpu.py | 33 ++++++++++++++++++++++++++++----- src/qibotn/eval.py | 20 ++++++++++---------- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index d758d5a1..c133f4e4 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -12,12 +12,35 @@ def __init__(self, runcard): super().__init__() import cuquantum # pylint: disable=import-error from cuquantum import cutensornet as cutn # pylint: disable=import-error - + + self.pauli_string_pattern = "XXXZ" if runcard is not None: self.MPI_enabled = runcard.get("MPI_enabled", False) self.MPS_enabled = runcard.get("MPS_enabled", False) self.NCCL_enabled = runcard.get("NCCL_enabled", False) - self.expectation_enabled = runcard.get("expectation_enabled", False) + + expectation_enabled_value = runcard.get('expectation_enabled') + + if expectation_enabled_value is True: + self.expectation_enabled = True + + print("expectation_enabled is",self.expectation_enabled) + elif expectation_enabled_value is False: + self.expectation_enabled = False + + print("expectation_enabled is",self.expectation_enabled) + elif isinstance(expectation_enabled_value, dict): + self.expectation_enabled = True + expectation_enabled_dict = runcard.get('expectation_enabled', {}) + + self.pauli_string_pattern = expectation_enabled_dict.get('pauli_string_pattern', None) + + print("expectation_enabled is a dictionary",self.expectation_enabled,self.pauli_string_pattern ) + else: + raise TypeError("expectation_enabled has an unexpected type") + + + else: self.MPI_enabled = False self.MPS_enabled = False @@ -144,7 +167,7 @@ def execute_circuit( if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state = eval.expectation_pauli_tn(circuit, self.dtype) + state = eval.expectation_pauli_tn(circuit, self.dtype, self.pauli_string_pattern) elif ( self.MPI_enabled == True @@ -155,7 +178,7 @@ def execute_circuit( if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.expectation_pauli_tn_MPI(circuit, self.dtype, 32) + state, rank = eval.expectation_pauli_tn_MPI(circuit, self.dtype, self.pauli_string_pattern, 32) if rank > 0: state = np.array(0) @@ -169,7 +192,7 @@ def execute_circuit( if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.expectation_pauli_tn_nccl(circuit, self.dtype, 32) + state, rank = eval.expectation_pauli_tn_nccl(circuit, self.dtype, self.pauli_string_pattern, 32) if rank > 0: state = np.array(0) diff --git a/src/qibotn/eval.py b/src/qibotn/eval.py index 5bb5ba18..c8b56712 100644 --- a/src/qibotn/eval.py +++ b/src/qibotn/eval.py @@ -14,10 +14,10 @@ def dense_vector_tn(qibo_circ, datatype): return contract(*myconvertor.state_vector_operands()) -def expectation_pauli_tn(qibo_circ, datatype): +def expectation_pauli_tn(qibo_circ, datatype, pauli_string): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract( - *myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) + *myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits, pauli_string)) ) @@ -203,7 +203,7 @@ def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8): return result, rank -def expectation_pauli_tn_nccl(qibo_circ, datatype, n_samples=8): +def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket from cuquantum import Network @@ -231,7 +231,7 @@ def expectation_pauli_tn_nccl(qibo_circ, datatype, n_samples=8): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft convetor",mem_avail, "rank =",rank) - operands = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) + operands = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits, pauli_string)) # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) @@ -290,7 +290,7 @@ def expectation_pauli_tn_nccl(qibo_circ, datatype, n_samples=8): return result, rank -def expectation_pauli_tn_MPI(qibo_circ, datatype, n_samples=8): +def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket from cuquantum import Network @@ -310,7 +310,7 @@ def expectation_pauli_tn_MPI(qibo_circ, datatype, n_samples=8): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft convetor",mem_avail, "rank =",rank) - operands = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) + operands = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits, pauli_string)) # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) @@ -378,17 +378,17 @@ def dense_vector_mps(qibo_circ, gate_algo, datatype): ) -def PauliStringGen(nqubits): +def PauliStringGen(nqubits, pauli_string): if nqubits <= 0: return "Invalid input. N should be a positive integer." - # characters = 'IXYZ' - characters = "XXXZ" + characters = pauli_string + #characters = "XXXZ" result = "" for i in range(nqubits): char_to_add = characters[i % len(characters)] result += char_to_add - + print("pauli string", result) return result From a3f3538f04c0281a20c3036a9064ffc1bc6809a3 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 31 Jan 2024 16:47:10 +0800 Subject: [PATCH 026/199] Update to allow user to set MPS parameters and to set Pauli string pattern --- src/qibotn/backends/gpu.py | 60 +++++++++++++++++++++----------------- src/qibotn/eval.py | 14 ++++++--- 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index c133f4e4..5777fe90 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -12,34 +12,43 @@ def __init__(self, runcard): super().__init__() import cuquantum # pylint: disable=import-error from cuquantum import cutensornet as cutn # pylint: disable=import-error - - self.pauli_string_pattern = "XXXZ" + if runcard is not None: self.MPI_enabled = runcard.get("MPI_enabled", False) - self.MPS_enabled = runcard.get("MPS_enabled", False) self.NCCL_enabled = runcard.get("NCCL_enabled", False) - - expectation_enabled_value = runcard.get('expectation_enabled') + expectation_enabled_value = runcard.get("expectation_enabled") if expectation_enabled_value is True: self.expectation_enabled = True - - print("expectation_enabled is",self.expectation_enabled) + self.pauli_string_pattern = "XXXZ" elif expectation_enabled_value is False: self.expectation_enabled = False - - print("expectation_enabled is",self.expectation_enabled) elif isinstance(expectation_enabled_value, dict): self.expectation_enabled = True - expectation_enabled_dict = runcard.get('expectation_enabled', {}) - - self.pauli_string_pattern = expectation_enabled_dict.get('pauli_string_pattern', None) - - print("expectation_enabled is a dictionary",self.expectation_enabled,self.pauli_string_pattern ) + expectation_enabled_dict = runcard.get("expectation_enabled", {}) + self.pauli_string_pattern = expectation_enabled_dict.get( + "pauli_string_pattern", None + ) else: raise TypeError("expectation_enabled has an unexpected type") - + mps_enabled_value = runcard.get("MPS_enabled") + if mps_enabled_value is True: + self.MPS_enabled = True + self.gate_algo = { + "qr_method": False, + "svd_method": { + "partition": "UV", + "abs_cutoff": 1e-12, + }, + } + elif mps_enabled_value is False: + self.MPS_enabled = False + elif isinstance(mps_enabled_value, dict): + self.MPS_enabled = True + self.gate_algo = runcard.get("MPS_enabled", {}) + else: + raise TypeError("MPS_enabled has an unexpected type") else: self.MPI_enabled = False @@ -123,14 +132,7 @@ def execute_circuit( if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - gate_algo = { - "qr_method": False, - "svd_method": { - "partition": "UV", - "abs_cutoff": 1e-12, - }, - } # make this user input - state = eval.dense_vector_mps(circuit, gate_algo, self.dtype) + state = eval.dense_vector_mps(circuit, self.gate_algo, self.dtype) elif ( self.MPI_enabled == True @@ -167,7 +169,9 @@ def execute_circuit( if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state = eval.expectation_pauli_tn(circuit, self.dtype, self.pauli_string_pattern) + state = eval.expectation_pauli_tn( + circuit, self.dtype, self.pauli_string_pattern + ) elif ( self.MPI_enabled == True @@ -178,7 +182,9 @@ def execute_circuit( if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.expectation_pauli_tn_MPI(circuit, self.dtype, self.pauli_string_pattern, 32) + state, rank = eval.expectation_pauli_tn_MPI( + circuit, self.dtype, self.pauli_string_pattern, 32 + ) if rank > 0: state = np.array(0) @@ -192,7 +198,9 @@ def execute_circuit( if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.expectation_pauli_tn_nccl(circuit, self.dtype, self.pauli_string_pattern, 32) + state, rank = eval.expectation_pauli_tn_nccl( + circuit, self.dtype, self.pauli_string_pattern, 32 + ) if rank > 0: state = np.array(0) diff --git a/src/qibotn/eval.py b/src/qibotn/eval.py index c8b56712..afa6cbdd 100644 --- a/src/qibotn/eval.py +++ b/src/qibotn/eval.py @@ -17,7 +17,9 @@ def dense_vector_tn(qibo_circ, datatype): def expectation_pauli_tn(qibo_circ, datatype, pauli_string): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract( - *myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits, pauli_string)) + *myconvertor.expectation_operands( + PauliStringGen(qibo_circ.nqubits, pauli_string) + ) ) @@ -231,7 +233,9 @@ def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string, n_samples=8): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft convetor",mem_avail, "rank =",rank) - operands = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits, pauli_string)) + operands = myconvertor.expectation_operands( + PauliStringGen(qibo_circ.nqubits, pauli_string) + ) # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) @@ -310,7 +314,9 @@ def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string, n_samples=8): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft convetor",mem_avail, "rank =",rank) - operands = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits, pauli_string)) + operands = myconvertor.expectation_operands( + PauliStringGen(qibo_circ.nqubits, pauli_string) + ) # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) @@ -383,7 +389,7 @@ def PauliStringGen(nqubits, pauli_string): return "Invalid input. N should be a positive integer." characters = pauli_string - #characters = "XXXZ" + # characters = "XXXZ" result = "" From 67fdafedede0d7be5b4dec250fb595b2107bbc31 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 31 Jan 2024 16:49:34 +0800 Subject: [PATCH 027/199] Remove backend --- src/qibotn/backends.py | 129 ----------------------------------------- 1 file changed, 129 deletions(-) delete mode 100644 src/qibotn/backends.py diff --git a/src/qibotn/backends.py b/src/qibotn/backends.py deleted file mode 100644 index 3728a999..00000000 --- a/src/qibotn/backends.py +++ /dev/null @@ -1,129 +0,0 @@ -from qibo.backends import NumpyBackend -from qibo.config import raise_error -from qibotn import cutn -from qibotn import quimb -from qibo.states import CircuitResult -import numpy as np - - -class QiboTNBackend(NumpyBackend): - def __init__(self, platform): - super().__init__() - self.name = "qibotn" - if ( - platform == "cu_tensornet" - or platform == "cu_mps" - or platform == "qu_tensornet" - or platform == "cu_tensornet_mpi" - or platform == "cu_tensornet_mpi_expectation" - or platform == "cu_tensornet_expectation" - or platform == "cu_tensornet_nccl" - or platform == "cu_tensornet_nccl_expectation" - ): # pragma: no cover - self.platform = platform - else: - raise_error( - NotImplementedError, "QiboTN cannot support the specified backend." - ) - - def apply_gate(self, gate, state, nqubits): # pragma: no cover - raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") - - def apply_gate_density_matrix(self, gate, state, nqubits): # pragma: no cover - raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") - - def assign_measurements(self, measurement_map, circuit_result): - raise_error(NotImplementedError, "Not implemented in QiboTN.") - - def execute_circuit( - self, circuit, initial_state=None, nshots=None, return_array=False - ): # pragma: no cover - """Executes a quantum circuit. - - Args: - 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. - - """ - - if self.platform == "cu_tensornet": - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - - state = cutn.eval(circuit, self.dtype) - - if self.platform == "cu_mps": - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - - gate_algo = { - "qr_method": False, - "svd_method": { - "partition": "UV", - "abs_cutoff": 1e-12, - }, - } # make this user input - state = cutn.eval_mps(circuit, gate_algo, self.dtype) - - if self.platform == "qu_tensornet": - # init_state = np.random.random(2**circuit.nqubits) + 1j * np.random.random(2**circuit.nqubits) - # init_state = init_state / np.sqrt((np.abs(init_state) ** 2).sum()) - init_state = np.zeros(2**circuit.nqubits, dtype=self.dtype) - init_state[0] = 1.0 - state = quimb.eval(circuit.to_qasm(), init_state, backend="numpy") - - if self.platform == "cu_tensornet_mpi": - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - - # state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) - state, rank = cutn.eval_tn_MPI_2(circuit, self.dtype, 32) - if rank > 0: - state = np.array(0) - - if self.platform == "cu_tensornet_nccl": - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - - # state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) - state, rank = cutn.eval_tn_nccl(circuit, self.dtype, 32) - if rank > 0: - state = np.array(0) - - if self.platform == "cu_tensornet_expectation": - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - - state = cutn.eval_expectation(circuit, self.dtype) - - if self.platform == "cu_tensornet_mpi_expectation": - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - - # state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) - # state, rank = cutn.eval_tn_MPI_expectation(circuit, self.dtype,32) - state, rank = cutn.eval_tn_MPI_2_expectation(circuit, self.dtype, 32) - - if rank > 0: - state = np.array(0) - - if self.platform == "cu_tensornet_nccl_expectation": - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - - # state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) - # state, rank = cutn.eval_tn_MPI_expectation(circuit, self.dtype,32) - state, rank = cutn.eval_tn_nccl_expectation(circuit, self.dtype, 32) - - if rank > 0: - state = np.array(0) - - if return_array: - return state.flatten() - else: - circuit._final_state = CircuitResult(self, circuit, state.flatten(), nshots) - return circuit._final_state From d621eb2f6e019db07dc1fe55afe646fd1b36a73e Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 31 Jan 2024 17:34:15 +0800 Subject: [PATCH 028/199] Update sample code [skip CI] --- README.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/README.md b/README.md index 4628c1c1..a1fea8da 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,58 @@ Qibotn is the tensor-network translation module for Qibo to support large-scale simulation of quantum circuits and acceleration. To get started, `python setup.py install` to install the tools and dependencies. + +# Sample Codes +## Single Node + +
+```
+import numpy as np
+from qibo import Circuit, gates
+import qibo
+
+'''
+computation_settings = {
+    'MPI_enabled': False,
+    'MPS_enabled': False,
+    'NCCL_enabled': False,
+    'expectation_enabled': {
+        'pauli_string_pattern': "IXZ"
+    }
+}
+'''
+
+computation_settings = {
+    'MPI_enabled': False,
+    'MPS_enabled': {
+                "qr_method": False,
+                "svd_method": {
+                    "partition": "UV",
+                    "abs_cutoff": 1e-12,
+                },
+            } ,
+    'NCCL_enabled': False,
+    'expectation_enabled': False
+}
+
+# computation_settings = {
+#     'MPI_enabled': False,
+#     'MPS_enabled': True,
+#     'NCCL_enabled': False,
+#     'expectation_enabled': False
+# }
+
+qibo.set_backend(backend="qibotn", runcard=computation_settings)
+
+# Construct the circuit
+c = Circuit(2)
+# Add some gates
+c.add(gates.H(0))
+c.add(gates.H(1))
+
+# Execute the circuit and obtain the final state
+result = c()
+
+print(result.state())
+```
+
\ No newline at end of file From 83ffcfa19b6a45627c5803b4e555335a22d126e9 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 31 Jan 2024 17:42:10 +0800 Subject: [PATCH 029/199] Update --- README.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a1fea8da..1bf2f7ce 100644 --- a/README.md +++ b/README.md @@ -11,16 +11,10 @@ import numpy as np from qibo import Circuit, gates import qibo -''' -computation_settings = { - 'MPI_enabled': False, - 'MPS_enabled': False, - 'NCCL_enabled': False, - 'expectation_enabled': { - 'pauli_string_pattern': "IXZ" - } -} -''' +# Below shows 3 ways of setting the computation_settings +# Note that for MPS_enabled and expectation_enabled parameters the accepted inputs are boolean or a dictionary with the format shown below. +# If computation_settings is not specified, the default setting is used in which all booleans will be False. +# This will trigger the dense vector computation of the tensornet. computation_settings = { 'MPI_enabled': False, @@ -35,6 +29,14 @@ computation_settings = { 'expectation_enabled': False } +# computation_settings = { +# 'MPI_enabled': False, +# 'MPS_enabled': False, +# 'NCCL_enabled': False, +# 'expectation_enabled': { +# 'pauli_string_pattern': "IXZ" +# } + # computation_settings = { # 'MPI_enabled': False, # 'MPS_enabled': True, From 460f5e76210e684803b4ec8150d29f2bc6a25642 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 31 Jan 2024 17:49:56 +0800 Subject: [PATCH 030/199] Update sample codes --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1bf2f7ce..09281c87 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ To get started, `python setup.py install` to install the tools and dependencies. # Sample Codes ## Single Node - +The code below shows an example of how to activate the Cuquantum TensorNetwork backend of Qibo.
 ```
 import numpy as np
@@ -57,4 +57,13 @@ result = c()
 
 print(result.state())
 ```
+
+ +## Multi-Node +Multi-node is enabled by setting either the MPI or NCCL enabled flag to True in the computation settings. Below shows the script to launch multi node on 4 GPU in cluster. + +
+```
+mpirun -n 4 --mca opal_common_ucx_opal_mem_hooks 1 --mca orte_base_help_aggregate 0 -mca btl ^openib  -hostfile $node_list python test.py
+```
 
\ No newline at end of file From 6f4ffa777a3050582680c4a2d849c5f56e96fa7d Mon Sep 17 00:00:00 2001 From: tankya2 Date: Thu, 1 Feb 2024 11:35:55 +0800 Subject: [PATCH 031/199] Add more documentation --- README.md | 13 +++++++++++++ src/qibotn/eval.py | 18 ++++++++++-------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 09281c87..68f28300 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,19 @@ Qibotn is the tensor-network translation module for Qibo to support large-scale To get started, `python setup.py install` to install the tools and dependencies. +# Computation Supported + +- Tensornet (TN) + - TN contraction to dense vector + - TN contraction to dense vector with Message Passing Interface (MPI) + - TN contraction to dense vector with NCCL + - TN contraction to expectation of given Pauli string + - TN contraction to expectation of given Pauli string with Message Passing Interface (MPI) + - TN contraction to expectation of given Pauli string with NCCL + +- Matrix Product State (MPS) + - MPS contraction to dense vector + # Sample Codes ## Single Node The code below shows an example of how to activate the Cuquantum TensorNetwork backend of Qibo. diff --git a/src/qibotn/eval.py b/src/qibotn/eval.py index afa6cbdd..a9aeaac9 100644 --- a/src/qibotn/eval.py +++ b/src/qibotn/eval.py @@ -18,7 +18,7 @@ def expectation_pauli_tn(qibo_circ, datatype, pauli_string): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract( *myconvertor.expectation_operands( - PauliStringGen(qibo_circ.nqubits, pauli_string) + pauli_string_gen(qibo_circ.nqubits, pauli_string) ) ) @@ -234,7 +234,7 @@ def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string, n_samples=8): # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft convetor",mem_avail, "rank =",rank) operands = myconvertor.expectation_operands( - PauliStringGen(qibo_circ.nqubits, pauli_string) + pauli_string_gen(qibo_circ.nqubits, pauli_string) ) # mem_avail = cp.cuda.Device().mem_info[0] @@ -315,7 +315,7 @@ def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string, n_samples=8): # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft convetor",mem_avail, "rank =",rank) operands = myconvertor.expectation_operands( - PauliStringGen(qibo_circ.nqubits, pauli_string) + pauli_string_gen(qibo_circ.nqubits, pauli_string) ) # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) @@ -376,6 +376,8 @@ def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string, n_samples=8): def dense_vector_mps(qibo_circ, gate_algo, datatype): + """Convert qibo circuit to matrix product state (MPS) format and perform contraction to dense vector. + """ myconvertor = QiboCircuitToMPS(qibo_circ, gate_algo, dtype=datatype) mps_helper = MPSContractionHelper(myconvertor.num_qubits) @@ -384,17 +386,17 @@ def dense_vector_mps(qibo_circ, gate_algo, datatype): ) -def PauliStringGen(nqubits, pauli_string): +def pauli_string_gen(nqubits, pauli_string_pattern): + """ Used internally to generate the string based on given pattern and number of qubit. + Example: pattern: "XZ", number of qubit: 7, output = XZXZXZX + """ if nqubits <= 0: return "Invalid input. N should be a positive integer." - characters = pauli_string - # characters = "XXXZ" - result = "" for i in range(nqubits): - char_to_add = characters[i % len(characters)] + char_to_add = pauli_string_pattern[i % len(pauli_string_pattern)] result += char_to_add print("pauli string", result) return result From 31fe1ef0c91f7c1cc1ba7d02aff578a27222c32c Mon Sep 17 00:00:00 2001 From: tankya2 Date: Thu, 1 Feb 2024 14:42:17 +0800 Subject: [PATCH 032/199] Tidy up --- src/qibotn/MPSUtils.py | 2 + src/qibotn/QiboCircuitConvertor.py | 22 +--- src/qibotn/eval.py | 158 +++++++-------------------- src/qibotn/mps_contraction_helper.py | 4 +- 4 files changed, 48 insertions(+), 138 deletions(-) diff --git a/src/qibotn/MPSUtils.py b/src/qibotn/MPSUtils.py index fd1b4c73..4f84f672 100644 --- a/src/qibotn/MPSUtils.py +++ b/src/qibotn/MPSUtils.py @@ -2,6 +2,8 @@ from cuquantum.cutensornet.experimental import contract_decompose from cuquantum import contract +# Reference: https://github.com/NVIDIA/cuQuantum/blob/main/python/samples/cutensornet/tn_algorithms/mps_algorithms.ipynb + def initial(num_qubits, dtype): """ diff --git a/src/qibotn/QiboCircuitConvertor.py b/src/qibotn/QiboCircuitConvertor.py index d3a0569a..c59745bd 100644 --- a/src/qibotn/QiboCircuitConvertor.py +++ b/src/qibotn/QiboCircuitConvertor.py @@ -1,6 +1,8 @@ import cupy as cp import numpy as np +# Reference: https://github.com/NVIDIA/cuQuantum/tree/main/python/samples/cutensornet/circuit_converter + class QiboCircuitToEinsum: """Convert a circuit to a Tensor Network (TN) representation. @@ -159,9 +161,7 @@ def get_pauli_gates(self, pauli_map, dtype="complex128", backend=cp): return gates def expectation_operands(self, pauli_string): - # assign pauli string to qubit - # _get_forward_inverse_metadata() - input_bitstring = "0" * self.circuit.nqubits # Need all qubits! + input_bitstring = "0" * self.circuit.nqubits input_operands = self._get_bitstring_tensors(input_bitstring) pauli_string = dict(zip(range(self.circuit.nqubits), pauli_string)) @@ -185,8 +185,6 @@ def expectation_operands(self, pauli_string): next_frontier = max(qubits_frontier.values()) + 1 - # input_mode_labels, input_operands, qubits_frontier, next_frontier, inverse_gates = self._get_forward_inverse_metadata(coned_qubits) - pauli_gates = self.get_pauli_gates( pauli_map, dtype=self.dtype, backend=self.backend ) @@ -208,18 +206,4 @@ def expectation_operands(self, pauli_string): operand_exp_interleave = [x for y in zip(operands, mode_labels) for x in y] - # expec = contract(*operand_exp_interleave) - # print(expec) - - """ - gate_mode_labels, gate_operands = circ_utils.parse_gates_to_mode_labels_operands(gates, - qubits_frontier, - next_frontier) - - mode_labels = input_mode_labels + gate_mode_labels + [[qubits_frontier[ix]] for ix in self.qubits] - operands = input_operands + gate_operands + input_operands[:n_qubits] - - output_mode_labels = [] - expression = circ_utils.convert_mode_labels_to_expression(mode_labels, output_mode_labels) - """ return operand_exp_interleave diff --git a/src/qibotn/eval.py b/src/qibotn/eval.py index a9aeaac9..b73418a7 100644 --- a/src/qibotn/eval.py +++ b/src/qibotn/eval.py @@ -1,7 +1,5 @@ from qibotn.QiboCircuitConvertor import QiboCircuitToEinsum from cuquantum import contract -from cuquantum import cutensornet as cutn -import multiprocessing from cupy.cuda.runtime import getDeviceCount import cupy as cp @@ -10,15 +8,17 @@ def dense_vector_tn(qibo_circ, datatype): + """Convert qibo circuit to tensornet (TN) format and perform contraction to dense vector.""" myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract(*myconvertor.state_vector_operands()) -def expectation_pauli_tn(qibo_circ, datatype, pauli_string): +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.""" myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract( *myconvertor.expectation_operands( - pauli_string_gen(qibo_circ.nqubits, pauli_string) + pauli_string_gen(qibo_circ.nqubits, pauli_string_pattern) ) ) @@ -30,54 +30,35 @@ def dense_vector_tn_MPI(qibo_circ, datatype, n_samples=8): After pathfinding the optimal path is used in the actual contraction to give a dense vector representation of the TN. """ - from mpi4py import MPI # this line initializes MPI - import socket + from mpi4py import MPI from cuquantum import Network - # Get the hostname - # hostname = socket.gethostname() - root = 0 comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + device_id = rank % getDeviceCount() # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft convetor",mem_avail, "rank =",rank) - operands = myconvertor.state_vector_operands() - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) - # Broadcast the operand data. - # operands = comm.bcast(operands, root) + operands = myconvertor.state_vector_operands() # Assign the device for each process. device_id = rank % getDeviceCount() - # dev = cp.cuda.Device(device_id) - # free_mem, total_mem = dev.mem_info - # print("Mem free: ",free_mem, "Total mem: ",total_mem, "rank =",rank) - # Create network object. network = Network(*operands, options={"device_id": device_id}) # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. path, info = network.contract_path( - optimize={"samples": 8, "slicing": {"min_slices": max(32, size)}} + optimize={"samples": n_samples, "slicing": {"min_slices": max(32, size)}} ) - # print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") # Select the best path from all ranks. opt_cost, sender = comm.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) - # if rank == root: - # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") - # Broadcast info from the sender to all other ranks. info = comm.bcast(info, sender) @@ -95,45 +76,30 @@ def dense_vector_tn_MPI(qibo_circ, datatype, n_samples=8): ) slices = range(slice_begin, slice_end) - # print(f"Process {rank} is processing slice range: {slices}.") - # Contract the group of slices the process is responsible for. result = network.contract(slices=slices) - # print(f"Process {rank} result shape is : {result.shape}.") - # print(f"Process {rank} result size is : {result.nbytes}.") # Sum the partial contribution from each process on root. result = comm.reduce(sendobj=result, op=MPI.SUM, root=root) - """ - path, opt_info = network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads, 'slicing': {'min_slices': max(16, size)}}) - - num_slices = opt_info.num_slices#Andy - chunk, extra = num_slices // size, num_slices % size#Andy - slice_begin = rank * chunk + min(rank, extra)#Andy - slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra)#Andy - slices = range(slice_begin, slice_end)#Andy - result = network.contract(slices=slices) - """ - return result, rank def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8): - from mpi4py import MPI # this line initializes MPI - import socket + """Convert qibo circuit to tensornet (TN) format and perform contraction using multi node and multi GPU through NCCL. + The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. + The pathfinder looks at user defined number of samples (n_samples) iteratively to 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 a dense vector representation of the TN. + """ + from mpi4py import MPI from cuquantum import Network from cupy.cuda import nccl - # Get the hostname - # hostname = socket.gethostname() - root = 0 comm_mpi = MPI.COMM_WORLD rank = comm_mpi.Get_rank() size = comm_mpi.Get_size() - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + device_id = rank % getDeviceCount() cp.cuda.Device(device_id).use() @@ -145,27 +111,18 @@ def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8): # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft convetor",mem_avail, "rank =",rank) operands = myconvertor.state_vector_operands() - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) network = Network(*operands) # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. path, info = network.contract_path( - optimize={"samples": 8, "slicing": {"min_slices": max(32, size)}} + optimize={"samples": n_samples, "slicing": {"min_slices": max(32, size)}} ) - # print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") - # Select the best path from all ranks. opt_cost, sender = comm_mpi.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) - # if rank == root: - # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") - # Broadcast info from the sender to all other ranks. info = comm_mpi.bcast(info, sender) @@ -183,12 +140,8 @@ def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8): ) slices = range(slice_begin, slice_end) - # print(f"Process {rank} is processing slice range: {slices}.") - # Contract the group of slices the process is responsible for. result = network.contract(slices=slices) - # print(f"Process {rank} result shape is : {result.shape}.") - # print(f"Process {rank} result size is : {result.nbytes}.") # Sum the partial contribution from each process on root. stream_ptr = cp.cuda.get_current_stream().ptr @@ -205,21 +158,22 @@ def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8): return result, rank -def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string, n_samples=8): - from mpi4py import MPI # this line initializes MPI - import socket +def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string_pattern, n_samples=8): + """Convert qibo circuit to tensornet (TN) format and perform contraction to expectation of given Pauli string using multi node and multi GPU through NCCL. + The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. + The pauli_string_pattern is used to generate the pauli string corresponding to the number of qubits of the system. + The pathfinder looks at user defined number of samples (n_samples) iteratively to 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. + """ + from mpi4py import MPI from cuquantum import Network from cupy.cuda import nccl - # Get the hostname - # hostname = socket.gethostname() - root = 0 comm_mpi = MPI.COMM_WORLD rank = comm_mpi.Get_rank() size = comm_mpi.Get_size() - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + device_id = rank % getDeviceCount() cp.cuda.Device(device_id).use() @@ -231,30 +185,20 @@ def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string, n_samples=8): # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft convetor",mem_avail, "rank =",rank) operands = myconvertor.expectation_operands( - pauli_string_gen(qibo_circ.nqubits, pauli_string) + pauli_string_gen(qibo_circ.nqubits, pauli_string_pattern) ) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) - network = Network(*operands) # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. path, info = network.contract_path( - optimize={"samples": 8, "slicing": {"min_slices": max(32, size)}} + optimize={"samples": n_samples, "slicing": {"min_slices": max(32, size)}} ) - # print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") - # Select the best path from all ranks. opt_cost, sender = comm_mpi.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) - # if rank == root: - # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") - # Broadcast info from the sender to all other ranks. info = comm_mpi.bcast(info, sender) @@ -272,12 +216,8 @@ def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string, n_samples=8): ) slices = range(slice_begin, slice_end) - # print(f"Process {rank} is processing slice range: {slices}.") - # Contract the group of slices the process is responsible for. result = network.contract(slices=slices) - # print(f"Process {rank} result shape is : {result.shape}.") - # print(f"Process {rank} result size is : {result.nbytes}.") # Sum the partial contribution from each process on root. stream_ptr = cp.cuda.get_current_stream().ptr @@ -294,57 +234,44 @@ def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string, n_samples=8): return result, rank -def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string, n_samples=8): +def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string_pattern, n_samples=8): + """Convert qibo circuit to tensornet (TN) format and perform contraction to expectation of given Pauli string using multi node and multi GPU through MPI. + The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. + The pauli_string_pattern is used to generate the pauli string corresponding to the number of qubits of the system. + The pathfinder looks at user defined number of samples (n_samples) iteratively to 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. + """ from mpi4py import MPI # this line initializes MPI - import socket from cuquantum import Network - # Get the hostname - # hostname = socket.gethostname() - root = 0 comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + device_id = rank % getDeviceCount() # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft convetor",mem_avail, "rank =",rank) + operands = myconvertor.expectation_operands( - pauli_string_gen(qibo_circ.nqubits, pauli_string) + pauli_string_gen(qibo_circ.nqubits, pauli_string_pattern) ) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) - - # Broadcast the operand data. - # operands = comm.bcast(operands, root) # Assign the device for each process. device_id = rank % getDeviceCount() - # dev = cp.cuda.Device(device_id) - # free_mem, total_mem = dev.mem_info - # print("Mem free: ",free_mem, "Total mem: ",total_mem, "rank =",rank) - # Create network object. network = Network(*operands, options={"device_id": device_id}) # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. path, info = network.contract_path( - optimize={"samples": 8, "slicing": {"min_slices": max(32, size)}} + optimize={"samples": n_samples, "slicing": {"min_slices": max(32, size)}} ) - # print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") # Select the best path from all ranks. opt_cost, sender = comm.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) - # if rank == root: - # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") - # Broadcast info from the sender to all other ranks. info = comm.bcast(info, sender) @@ -362,12 +289,8 @@ def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string, n_samples=8): ) slices = range(slice_begin, slice_end) - # print(f"Process {rank} is processing slice range: {slices}.") - # Contract the group of slices the process is responsible for. result = network.contract(slices=slices) - # print(f"Process {rank} result shape is : {result.shape}.") - # print(f"Process {rank} result size is : {result.nbytes}.") # Sum the partial contribution from each process on root. result = comm.reduce(sendobj=result, op=MPI.SUM, root=root) @@ -376,8 +299,7 @@ def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string, n_samples=8): def dense_vector_mps(qibo_circ, gate_algo, datatype): - """Convert qibo circuit to matrix product state (MPS) format and perform contraction to dense vector. - """ + """Convert qibo circuit to matrix product state (MPS) format and perform contraction to dense vector.""" myconvertor = QiboCircuitToMPS(qibo_circ, gate_algo, dtype=datatype) mps_helper = MPSContractionHelper(myconvertor.num_qubits) @@ -387,7 +309,7 @@ def dense_vector_mps(qibo_circ, gate_algo, datatype): def pauli_string_gen(nqubits, pauli_string_pattern): - """ Used internally to generate the string based on given pattern and number of qubit. + """Used internally to generate the string based on given pattern and number of qubit. Example: pattern: "XZ", number of qubit: 7, output = XZXZXZX """ if nqubits <= 0: diff --git a/src/qibotn/mps_contraction_helper.py b/src/qibotn/mps_contraction_helper.py index ee8e4e42..29d5e253 100644 --- a/src/qibotn/mps_contraction_helper.py +++ b/src/qibotn/mps_contraction_helper.py @@ -1,5 +1,7 @@ from cuquantum import contract, contract_path, CircuitToEinsum, tensor +# Reference: https://github.com/NVIDIA/cuQuantum/blob/main/python/samples/cutensornet/tn_algorithms/mps_algorithms.ipynb + class MPSContractionHelper: """ @@ -85,7 +87,7 @@ def contract_expectation( self, mps_tensors, operator, qubits, options=None, normalize=False ): """ - Contract the corresponding tensor network to form the state vector representation of the MPS. + Contract the corresponding tensor network to form the expectation of the MPS. Args: mps_tensors: A list of rank-3 ndarray-like tensor objects. From 6a6cd2a90b0738413fc796373a9a8273e4903202 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Thu, 1 Feb 2024 14:45:55 +0800 Subject: [PATCH 033/199] Remove print [skip CI] --- src/qibotn/eval.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qibotn/eval.py b/src/qibotn/eval.py index b73418a7..96fd488a 100644 --- a/src/qibotn/eval.py +++ b/src/qibotn/eval.py @@ -320,5 +320,4 @@ def pauli_string_gen(nqubits, pauli_string_pattern): for i in range(nqubits): char_to_add = pauli_string_pattern[i % len(pauli_string_pattern)] result += char_to_add - print("pauli string", result) return result From 88fb4340bef10aba601bac24836c1487fc905dc0 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Thu, 1 Feb 2024 15:21:04 +0800 Subject: [PATCH 034/199] Update --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 68f28300..25b499ca 100644 --- a/README.md +++ b/README.md @@ -73,10 +73,10 @@ print(result.state()) ## Multi-Node -Multi-node is enabled by setting either the MPI or NCCL enabled flag to True in the computation settings. Below shows the script to launch multi node on 4 GPU in cluster. +Multi-node is enabled by setting either the MPI or NCCL enabled flag to True in the computation settings. Below shows the script to launch on 2 nodes with 2 GPUs each. $node_list contains the IP of the nodes assigned.
 ```
-mpirun -n 4 --mca opal_common_ucx_opal_mem_hooks 1 --mca orte_base_help_aggregate 0 -mca btl ^openib  -hostfile $node_list python test.py
+mpirun -n 4 -hostfile $node_list python test.py
 ```
 
\ No newline at end of file From 347cb354424482a0c2ae14dba2d32c37436a9069 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Fri, 2 Feb 2024 12:53:48 +0800 Subject: [PATCH 035/199] Update readme --- README.md | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 25b499ca..2e9b09c6 100644 --- a/README.md +++ b/README.md @@ -4,22 +4,22 @@ To get started, `python setup.py install` to install the tools and dependencies. # Computation Supported -- Tensornet (TN) - - TN contraction to dense vector - - TN contraction to dense vector with Message Passing Interface (MPI) - - TN contraction to dense vector with NCCL - - TN contraction to expectation of given Pauli string - - TN contraction to expectation of given Pauli string with Message Passing Interface (MPI) - - TN contraction to expectation of given Pauli string with NCCL +1. Tensornet (TN) with contractions to: + - dense vector + - expecation of given Pauli string -- Matrix Product State (MPS) - - MPS contraction to dense vector + For each TN case: + - single node + - multi node with Message Passing Interface (MPI) + - multi node with NCCL + +2. Tensornet (TN) with contractions to: + - dense vector (single node) # Sample Codes ## Single Node The code below shows an example of how to activate the Cuquantum TensorNetwork backend of Qibo. -
-```
+```py
 import numpy as np
 from qibo import Circuit, gates
 import qibo
@@ -29,7 +29,6 @@ import qibo
 # If computation_settings is not specified, the default setting is used in which all booleans will be False. 
 # This will trigger the dense vector computation of the tensornet.
 
-computation_settings = {
     'MPI_enabled': False,
     'MPS_enabled': {
                 "qr_method": False,
@@ -70,13 +69,11 @@ result = c()
 
 print(result.state())
 ```
-
## Multi-Node Multi-node is enabled by setting either the MPI or NCCL enabled flag to True in the computation settings. Below shows the script to launch on 2 nodes with 2 GPUs each. $node_list contains the IP of the nodes assigned. -
-```
+
+```sh
 mpirun -n 4 -hostfile $node_list python test.py
-```
-
\ No newline at end of file +``` \ No newline at end of file From bc487022b3646333f7005e3a6d967c8ad58d88fb Mon Sep 17 00:00:00 2001 From: tankya2 Date: Fri, 2 Feb 2024 13:02:09 +0800 Subject: [PATCH 036/199] Update computation setting --- README.md | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 2e9b09c6..a5b44002 100644 --- a/README.md +++ b/README.md @@ -24,11 +24,12 @@ import numpy as np from qibo import Circuit, gates import qibo -# Below shows 3 ways of setting the computation_settings +# Below shows how to set the computation_settings # Note that for MPS_enabled and expectation_enabled parameters the accepted inputs are boolean or a dictionary with the format shown below. # If computation_settings is not specified, the default setting is used in which all booleans will be False. # This will trigger the dense vector computation of the tensornet. +computation_settings = { 'MPI_enabled': False, 'MPS_enabled': { "qr_method": False, @@ -41,20 +42,6 @@ import qibo 'expectation_enabled': False } -# computation_settings = { -# 'MPI_enabled': False, -# 'MPS_enabled': False, -# 'NCCL_enabled': False, -# 'expectation_enabled': { -# 'pauli_string_pattern': "IXZ" -# } - -# computation_settings = { -# 'MPI_enabled': False, -# 'MPS_enabled': True, -# 'NCCL_enabled': False, -# 'expectation_enabled': False -# } qibo.set_backend(backend="qibotn", runcard=computation_settings) @@ -70,6 +57,27 @@ result = c() print(result.state()) ``` +Other examples of setting the computation_settings + +```py +# Expectation computation with specific Pauli String pattern +computation_settings = { + 'MPI_enabled': False, + 'MPS_enabled': False, + 'NCCL_enabled': False, + 'expectation_enabled': { + 'pauli_string_pattern': "IXZ" +} + +# Dense vector computation using multi node through MPI +computation_settings = { + 'MPI_enabled': False, + 'MPS_enabled': True, + 'NCCL_enabled': False, + 'expectation_enabled': False +} +``` + ## Multi-Node Multi-node is enabled by setting either the MPI or NCCL enabled flag to True in the computation settings. Below shows the script to launch on 2 nodes with 2 GPUs each. $node_list contains the IP of the nodes assigned. From 2d5fa9e520535bf701c93166e67493157dcf3ccd Mon Sep 17 00:00:00 2001 From: tankya2 Date: Mon, 5 Feb 2024 10:41:23 +0800 Subject: [PATCH 037/199] Correct computation_settings --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a5b44002..54ff75dc 100644 --- a/README.md +++ b/README.md @@ -71,8 +71,8 @@ computation_settings = { # Dense vector computation using multi node through MPI computation_settings = { - 'MPI_enabled': False, - 'MPS_enabled': True, + 'MPI_enabled': True, + 'MPS_enabled': False, 'NCCL_enabled': False, 'expectation_enabled': False } From 20f32c6c5173b09733976f4ed31126e42312dbca Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 6 Feb 2024 10:39:10 +0800 Subject: [PATCH 038/199] Remove not required Numba backend --- src/qibotn/backends/__init__.py | 1 - src/qibotn/backends/cpu.py | 302 -------------------------------- 2 files changed, 303 deletions(-) delete mode 100644 src/qibotn/backends/cpu.py diff --git a/src/qibotn/backends/__init__.py b/src/qibotn/backends/__init__.py index ebc3a205..26a0b9d4 100644 --- a/src/qibotn/backends/__init__.py +++ b/src/qibotn/backends/__init__.py @@ -1,2 +1 @@ -from qibotn.backends.cpu import NumbaBackend from qibotn.backends.gpu import CuTensorNet diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py deleted file mode 100644 index 22afcdc1..00000000 --- a/src/qibotn/backends/cpu.py +++ /dev/null @@ -1,302 +0,0 @@ -import numpy as np -from qibo.backends.numpy import NumpyBackend -from qibo.config import log -from qibo.gates.abstract import ParametrizedGate -from qibo.gates.channels import ReadoutErrorChannel -from qibo.gates.special import FusedGate - -from qibojit.backends.matrices import CustomMatrices - -GATE_OPS = { - "X": "apply_x", - "CNOT": "apply_x", - "TOFFOLI": "apply_x", - "Y": "apply_y", - "Z": "apply_z", - "CZ": "apply_z", - "U1": "apply_z_pow", - "CU1": "apply_z_pow", - "SWAP": "apply_swap", - "fSim": "apply_fsim", - "GeneralizedfSim": "apply_fsim", -} - - -class NumbaBackend(NumpyBackend): - def __init__(self): - super().__init__() - import sys - - import psutil - from numba import __version__ as numba_version - - from qibotn import __version__ as qibotn_version - - self.name = "qibotn" - self.platform = "numba" - self.versions.update( - { - "qibotn": qibotn_version, - "numba": numba_version, - } - ) - self.numeric_types = ( - int, - float, - complex, - np.int32, - np.int64, - np.float32, - np.float64, - np.complex64, - np.complex128, - ) - self.tensor_types = (np.ndarray,) - self.device = "/CPU:0" - self.custom_matrices = CustomMatrices(self.dtype) - self.gates = gates - self.ops = ops - self.measure_frequencies_op = ops.measure_frequencies - self.multi_qubit_kernels = { - 3: self.gates.apply_three_qubit_gate_kernel, - 4: self.gates.apply_four_qubit_gate_kernel, - 5: self.gates.apply_five_qubit_gate_kernel, - } - if sys.platform == "darwin": # pragma: no cover - self.set_threads(psutil.cpu_count(logical=False)) - else: - self.set_threads(len(psutil.Process().cpu_affinity())) - - def set_precision(self, precision): - if precision != self.precision: - super().set_precision(precision) - if self.custom_matrices: - self.custom_matrices = CustomMatrices(self.dtype) - - def set_threads(self, nthreads): - import numba - - numba.set_num_threads(nthreads) - self.nthreads = nthreads - - # def cast(self, x, dtype=None, copy=False): Inherited from ``NumpyBackend`` - - # def to_numpy(self, x): Inherited from ``NumpyBackend`` - - def zero_state(self, nqubits): - size = 2**nqubits - state = np.empty((size,), dtype=self.dtype) - return self.ops.initial_state_vector(state) - - def zero_density_matrix(self, nqubits): - size = 2**nqubits - state = np.empty((size, size), dtype=self.dtype) - return self.ops.initial_density_matrix(state) - - # def plus_state(self, nqubits): Inherited from ``NumpyBackend`` - - # def plus_density_matrix(self, nqubits): Inherited from ``NumpyBackend`` - - # def asmatrix_special(self, gate): Inherited from ``NumpyBackend`` - - # def control_matrix(self, gate): Inherited from ``NumpyBackend`` - - def one_qubit_base(self, state, nqubits, target, kernel, gate, qubits): - ncontrols = len(qubits) - 1 if qubits is not None else 0 - m = nqubits - target - 1 - nstates = 1 << (nqubits - ncontrols - 1) - if ncontrols: - kernel = getattr(self.gates, "multicontrol_{}_kernel".format(kernel)) - return kernel(state, gate, qubits, nstates, m) - kernel = getattr(self.gates, "{}_kernel".format(kernel)) - return kernel(state, gate, nstates, m) - - def two_qubit_base(self, state, nqubits, target1, target2, kernel, gate, qubits): - ncontrols = len(qubits) - 2 if qubits is not None else 0 - if target1 > target2: - swap_targets = True - m1 = nqubits - target1 - 1 - m2 = nqubits - target2 - 1 - else: - swap_targets = False - m1 = nqubits - target2 - 1 - m2 = nqubits - target1 - 1 - nstates = 1 << (nqubits - 2 - ncontrols) - if ncontrols: - kernel = getattr(self.gates, "multicontrol_{}_kernel".format(kernel)) - return kernel(state, gate, qubits, nstates, m1, m2, swap_targets) - kernel = getattr(self.gates, "{}_kernel".format(kernel)) - return kernel(state, gate, nstates, m1, m2, swap_targets) - - def multi_qubit_base(self, state, nqubits, targets, gate, qubits): - if qubits is None: - qubits = np.array(sorted(nqubits - q - 1 for q in targets), dtype="int32") - nstates = 1 << (nqubits - len(qubits)) - targets = np.array( - [1 << (nqubits - t - 1) for t in targets[::-1]], dtype="int64" - ) - if len(targets) > 5: - kernel = self.gates.apply_multi_qubit_gate_kernel - else: - kernel = self.multi_qubit_kernels.get(len(targets)) - return kernel(state, gate, qubits, nstates, targets) - - @staticmethod - def _create_qubits_tensor(gate, nqubits): - # TODO: Treat density matrices - qubits = [nqubits - q - 1 for q in gate.control_qubits] - qubits.extend(nqubits - q - 1 for q in gate.target_qubits) - return np.array(sorted(qubits), dtype="int32") - - def _as_custom_matrix(self, gate): - name = gate.__class__.__name__ - if isinstance(gate, ParametrizedGate): - return getattr(self.custom_matrices, name)(*gate.parameters) - elif isinstance(gate, FusedGate): # pragma: no cover - # fusion is tested in qibo tests - return self.asmatrix_fused(gate) - else: - return getattr(self.custom_matrices, name) - - def apply_gate(self, gate, state, nqubits): - matrix = self._as_custom_matrix(gate) - qubits = self._create_qubits_tensor(gate, nqubits) - targets = gate.target_qubits - state = self.cast(state) - if len(targets) == 1: - op = GATE_OPS.get(gate.__class__.__name__, "apply_gate") - return self.one_qubit_base(state, nqubits, *targets, op, matrix, qubits) - elif len(targets) == 2: - op = GATE_OPS.get(gate.__class__.__name__, "apply_two_qubit_gate") - return self.two_qubit_base(state, nqubits, *targets, op, matrix, qubits) - else: - return self.multi_qubit_base(state, nqubits, targets, matrix, qubits) - - def apply_gate_density_matrix(self, gate, state, nqubits, inverse=False): - name = gate.__class__.__name__ - if name == "Y": - return self._apply_ygate_density_matrix(gate, state, nqubits) - if inverse: - # used to reset the state when applying channels - # see :meth:`qibojit.backend.NumpyBackend.apply_channel_density_matrix` below - matrix = np.linalg.inv(gate.asmatrix(self)) - matrix = self.cast(matrix) - else: - matrix = self._as_custom_matrix(gate) - qubits = self._create_qubits_tensor(gate, nqubits) - qubits_dm = qubits + nqubits - targets = gate.target_qubits - targets_dm = tuple(q + nqubits for q in targets) - - state = self.cast(state) - shape = state.shape - if len(targets) == 1: - op = GATE_OPS.get(name, "apply_gate") - state = self.one_qubit_base( - state.ravel(), 2 * nqubits, *targets, op, matrix, qubits_dm - ) - state = self.one_qubit_base( - state, 2 * nqubits, *targets_dm, op, np.conj(matrix), qubits - ) - elif len(targets) == 2: - op = GATE_OPS.get(name, "apply_two_qubit_gate") - state = self.two_qubit_base( - state.ravel(), 2 * nqubits, *targets, op, matrix, qubits_dm - ) - state = self.two_qubit_base( - state, 2 * nqubits, *targets_dm, op, np.conj(matrix), qubits - ) - else: - state = self.multi_qubit_base( - state.ravel(), 2 * nqubits, targets, matrix, qubits_dm - ) - state = self.multi_qubit_base( - state, 2 * nqubits, targets_dm, np.conj(matrix), qubits - ) - return np.reshape(state, shape) - - def _apply_ygate_density_matrix(self, gate, state, nqubits): - matrix = self._as_custom_matrix(gate) - qubits = self._create_qubits_tensor(gate, nqubits) - qubits_dm = qubits + nqubits - targets = gate.target_qubits - targets_dm = tuple(q + nqubits for q in targets) - state = self.cast(state) - shape = state.shape - state = self.one_qubit_base( - state.ravel(), 2 * nqubits, *targets, "apply_y", matrix, qubits_dm - ) - # force using ``apply_gate`` kernel so that conjugate is properly applied - state = self.one_qubit_base( - state, 2 * nqubits, *targets_dm, "apply_gate", np.conj(matrix), qubits - ) - return np.reshape(state, shape) - - # def apply_channel(self, gate): Inherited from ``NumpyBackend`` - - def apply_channel_density_matrix(self, channel, state, nqubits): - state = self.cast(state) - if isinstance(channel, ReadoutErrorChannel) is True: - state_copy = self.cast(state, copy=True) - new_state = (1 - channel.coefficient_sum) * state - for coeff, gate in zip(channel.coefficients, channel.gates): - state = self.apply_gate_density_matrix(gate, state, nqubits) - new_state += coeff * state - # reset the state - if isinstance(channel, ReadoutErrorChannel) is True: - state = self.cast(state_copy, copy=True) - else: - state = self.apply_gate_density_matrix( - gate, state, nqubits, inverse=True - ) - return new_state - - def collapse_state(self, state, qubits, shot, nqubits, normalize=True): - state = self.cast(state) - qubits = self.cast([nqubits - q - 1 for q in reversed(qubits)], dtype="int32") - if normalize: - return self.ops.collapse_state_normalized(state, qubits, int(shot), nqubits) - else: - return self.ops.collapse_state(state, qubits, int(shot), nqubits) - - def collapse_density_matrix(self, state, qubits, shot, nqubits, normalize=True): - state = self.cast(state) - shape = state.shape - dm_qubits = [q + nqubits for q in qubits] - state = self.collapse_state(state.ravel(), dm_qubits, shot, 2 * nqubits, False) - state = self.collapse_state(state, qubits, shot, 2 * nqubits, False) - state = self.np.reshape(state, shape) - if normalize: - state = state / self.np.trace(state) - return state - - # def calculate_probabilities(self, state, qubits, nqubits): Inherited from ``NumpyBackend`` - - # def sample_shots(self, probabilities, nshots): Inherited from ``NumpyBackend`` - - # def aggregate_shots(self, shots): Inherited from ``NumpyBackend`` - - # def samples_to_binary(self, samples, nqubits): Inherited from ``NumpyBackend`` - - # def samples_to_decimal(self, samples, nqubits): Inherited from ``NumpyBackend`` - - def sample_frequencies(self, probabilities, nshots): - from qibo.config import SHOT_METROPOLIS_THRESHOLD - - if nshots < SHOT_METROPOLIS_THRESHOLD: - return super().sample_frequencies(probabilities, nshots) - - import collections - - seed = np.random.randint(0, int(1e8), dtype="int64") - nqubits = int(np.log2(tuple(probabilities.shape)[0])) - frequencies = np.zeros(2**nqubits, dtype="int64") - # always fall back to numba CPU backend because for ops not implemented on GPU - frequencies = self.measure_frequencies_op( - frequencies, probabilities, nshots, nqubits, seed, self.nthreads - ) - return collections.Counter({i: f for i, f in enumerate(frequencies) if f > 0}) - - # def calculate_frequencies(self, samples): Inherited from ``NumpyBackend`` - - # def assert_allclose(self, value, target, rtol=1e-7, atol=0.0): Inherited from ``NumpyBackend`` From 921828106a8cd46a96f33f1b10e96773dd0f23ca Mon Sep 17 00:00:00 2001 From: yangliwei Date: Tue, 6 Feb 2024 16:30:24 +0800 Subject: [PATCH 039/199] Add the tensor network library list --- README.md | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 54ff75dc..117d6a2e 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,21 @@ -Qibotn is the tensor-network translation module for Qibo to support large-scale simulation of quantum circuits and acceleration. +Qibotn is the tensor network translation module for Qibo to support large-scale simulation of quantum circuits and acceleration. To get started, `python setup.py install` to install the tools and dependencies. -# Computation Supported +# Supported Computation -1. Tensornet (TN) with contractions to: - - dense vector - - expecation of given Pauli string +Tensor network contractions to: +- dense vectors +- expecation values of given Pauli string - For each TN case: - - single node - - multi node with Message Passing Interface (MPI) - - multi node with NCCL +The supported configuration are: +- single node +- multi node with Message Passing Interface (MPI) +- multi node with NVIDIA Collective Communications Library (NCCL) + +Currently the supported libraries are: + - [cuQuantum](https://github.com/NVIDIA/cuQuantum) + - [quimb](https://quimb.readthedocs.io/en/latest/) 2. Tensornet (TN) with contractions to: - dense vector (single node) From 6a0459ee838c6a346881c847f3baf04aaf2328ae Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Tue, 6 Feb 2024 17:48:53 +0800 Subject: [PATCH 040/199] Added class for quimb backend --- src/qibotn/backends/cpu.py | 144 +++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 src/qibotn/backends/cpu.py diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py new file mode 100644 index 00000000..d826ba16 --- /dev/null +++ b/src/qibotn/backends/cpu.py @@ -0,0 +1,144 @@ +import numpy as np + +from qibo.backends.numpy import NumpyBackend +from qibo.states import CircuitResult +from qibo.config import raise_error + + +class QuTensorNet(NumpyBackend): + + def __init__(self, runcard): + super().__init__() + import quimb # pylint: disable=import-error + + if runcard is not None: + self.MPI_enabled = runcard.get("MPI_enabled", False) + self.NCCL_enabled = runcard.get("NCCL_enabled", False) + self.expectation_enabled_value = runcard.get("expectation_enabled", False) + + + mps_enabled_value = runcard.get("MPS_enabled") + if mps_enabled_value is True: + self.MPS_enabled = True + elif mps_enabled_value is False: + self.MPS_enabled = False + else: + raise TypeError("MPS_enabled has an unexpected type") + + else: + self.MPI_enabled = False + self.MPS_enabled = False + self.NCCL_enabled = False + self.expectation_enabled = False + + self.name = "qibotn" + self.quimb = quimb + self.platform = "qutensornet" + self.versions["quimb"] = self.quimb.__version__ + + + def apply_gate(self, gate, state, nqubits): # pragma: no cover + raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") + + def apply_gate_density_matrix(self, gate, state, nqubits): # pragma: no cover + raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") + + def assign_measurements(self, measurement_map, circuit_result): + raise_error(NotImplementedError, "Not implemented in QiboTN.") + + + def set_precision(self, precision): + if precision != self.precision: + super().set_precision(precision) + + + + def execute_circuit( + self, circuit, initial_state=None, nshots=None, return_array=False + ): # pragma: no cover + """Executes a quantum circuit. + + Args: + 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. + + """ + + import qibotn.eval_qu as eval_qu + + if ( + self.MPI_enabled == False + and self.MPS_enabled == False + and self.NCCL_enabled == False + and self.expectation_enabled == False + ): + + state = eval.dense_vector_tn_qu(circuit, init_state, is_mps=False, backend="numpy") + + elif ( + self.MPI_enabled == False + and self.MPS_enabled == True + and self.NCCL_enabled == False + and self.expectation_enabled == False + ): + + + state = eval.dense_vector_tn_qu(circuit, init_state, is_mps=True, backend="numpy") + + elif ( + self.MPI_enabled == True + and self.MPS_enabled == False + and self.NCCL_enabled == False + and self.expectation_enabled == False + ): + + raise_error(NotImplementedError, "QiboTN quimb backend cannot support MPI.") + + + elif ( + self.MPI_enabled == False + and self.MPS_enabled == False + and self.NCCL_enabled == True + and self.expectation_enabled == False + ): + + raise_error(NotImplementedError, "QiboTN quimb backend cannot support NCCL.") + + + elif ( + self.MPI_enabled == False + and self.MPS_enabled == False + and self.NCCL_enabled == False + and self.expectation_enabled == True + ): + + raise_error(NotImplementedError, "QiboTN quimb backend cannot support expectation") + + + elif ( + self.MPI_enabled == True + and self.MPS_enabled == False + and self.NCCL_enabled == False + and self.expectation_enabled == True + ): + raise_error(NotImplementedError, "QiboTN quimb backend cannot support expectation") + + elif ( + self.MPI_enabled == False + and self.MPS_enabled == False + and self.NCCL_enabled == True + and self.expectation_enabled == True + ): + raise_error(NotImplementedError, "QiboTN quimb backend cannot support expectation") + else: + raise_error(NotImplementedError, "Compute type not supported.") + + if return_array: + return state.flatten() + else: + circuit._final_state = CircuitResult(self, circuit, state.flatten(), nshots) + return circuit._final_state From 4fe1684144a9533a8157da183c3151d981d41416 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Tue, 6 Feb 2024 17:52:55 +0800 Subject: [PATCH 041/199] Added quimb backend --- src/qibotn/backends/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qibotn/backends/__init__.py b/src/qibotn/backends/__init__.py index 26a0b9d4..f9279323 100644 --- a/src/qibotn/backends/__init__.py +++ b/src/qibotn/backends/__init__.py @@ -1 +1,2 @@ from qibotn.backends.gpu import CuTensorNet +from qibotn.backends.cpu import QuTensorNet From a7574192b63d40c5636da5ebcce022ec75beccf1 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Tue, 6 Feb 2024 17:54:24 +0800 Subject: [PATCH 042/199] renamed function 'eval' to 'dense_vector_tn_qu' --- src/qibotn/eval_qu.py | 49 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/qibotn/eval_qu.py diff --git a/src/qibotn/eval_qu.py b/src/qibotn/eval_qu.py new file mode 100644 index 00000000..9dc639cd --- /dev/null +++ b/src/qibotn/eval_qu.py @@ -0,0 +1,49 @@ +import numpy as np +import quimb.tensor as qtn +from qibo.models import Circuit as QiboCircuit + + +def from_qibo(circuit: QiboCircuit, is_mps: False, psi0=None, method='svd', + cutoff=1e-6, cutoff_mode='abs'): + nqubits = circuit.nqubits + gate_opt = {} + if is_mps: + tncirc = qtn.CircuitMPS(nqubits, psi0=psi0) + gate_opt["method"] = method + gate_opt["cutoff"] = cutoff + gate_opt["cutoff_mode"] = cutoff_mode + else: + tncirc = qtn.Circuit(nqubits, psi0=psi0) + + for gate in circuit.queue: + tncirc.apply_gate( + gate.name, + *gate.parameters, + *gate.qubits, + parametrize=False if is_mps else (len(gate.parameters) > 0), + **gate_opt + ) + + return tncirc + + +def init_state_tn(nqubits, init_state_sv): + dims = tuple(2 * np.ones(nqubits, dtype=int)) + + return qtn.tensor_1d.MatrixProductState.from_dense(init_state_sv, dims) + + +def dense_vector_tn_qu(qasm: str, init_state, is_mps, backend="numpy"): + """Evaluate QASM with Quimb + + backend (quimb): numpy, cupy, jax. Passed to ``opt_einsum``. + + """ + circuit = QiboCircuit.from_qasm(qasm) + if init_state is not None: + init_state = init_state_tn(circuit.nqubits, init_state) + circ_quimb = from_qibo(circuit, is_mps, psi0=init_state) + interim = circ_quimb.psi.full_simplify(seq="DRC") + amplitudes = interim.to_dense(backend=backend).flatten() + + return amplitudes From 5ee13f7190030d07e49dcb2ac82077a6317a6be8 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Tue, 6 Feb 2024 17:56:29 +0800 Subject: [PATCH 043/199] renamed function 'eval' to 'dense_vector_tn_qu' in pytest --- tests/test_qasm_quimb_backend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_qasm_quimb_backend.py b/tests/test_qasm_quimb_backend.py index 54171159..e7dc6e67 100644 --- a/tests/test_qasm_quimb_backend.py +++ b/tests/test_qasm_quimb_backend.py @@ -27,7 +27,7 @@ def test_eval(nqubits: int, tolerance: float, is_mps: bool): # TODO: remove completely, or at least delegate to the backend # implementation os.environ["QUIMB_NUM_PROCS"] = str(os.cpu_count()) - import qibotn.quimb + import qibotn.eval_qu init_state = create_init_state(nqubits=nqubits) init_state_tn = copy.deepcopy(init_state) @@ -45,7 +45,7 @@ def test_eval(nqubits: int, tolerance: float, is_mps: bool): qasm_circ = qibo_circ.to_qasm() # Test quimb - result_tn = qibotn.quimb.eval( + result_tn = qibotn.eval_qu.dense_vector_tn_qu( qasm_circ, init_state_tn, is_mps, backend=config.quimb.backend ) From 8e73b8c172c15775f5c4761c6af67d74a8646e0b Mon Sep 17 00:00:00 2001 From: yangliwei Date: Wed, 7 Feb 2024 16:48:18 +0800 Subject: [PATCH 044/199] Minor update in the supported configurations and tensor network library list --- README.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 117d6a2e..74e1e3a6 100644 --- a/README.md +++ b/README.md @@ -8,17 +8,15 @@ Tensor network contractions to: - dense vectors - expecation values of given Pauli string -The supported configuration are: -- single node -- multi node with Message Passing Interface (MPI) -- multi node with NVIDIA Collective Communications Library (NCCL) - -Currently the supported libraries are: - - [cuQuantum](https://github.com/NVIDIA/cuQuantum) - - [quimb](https://quimb.readthedocs.io/en/latest/) - -2. Tensornet (TN) with contractions to: - - dense vector (single node) +The supported configurations are: +- single-node CPU +- single-node GPU or GPUs +- multi-node multi-GPU with Message Passing Interface (MPI) +- multi-node multi-GPU with NVIDIA Collective Communications Library (NCCL) + +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. # Sample Codes ## Single Node From 853f721a2ed9b02ccb7bf951bf3dad8647d83cae Mon Sep 17 00:00:00 2001 From: yangliwei Date: Wed, 7 Feb 2024 17:51:44 +0800 Subject: [PATCH 045/199] Format update using black formatter --- src/qibotn/backends/cpu.py | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py index d826ba16..b6b5cf69 100644 --- a/src/qibotn/backends/cpu.py +++ b/src/qibotn/backends/cpu.py @@ -5,7 +5,7 @@ from qibo.config import raise_error -class QuTensorNet(NumpyBackend): +class QuTensorNet(NumpyBackend): def __init__(self, runcard): super().__init__() @@ -16,7 +16,6 @@ def __init__(self, runcard): self.NCCL_enabled = runcard.get("NCCL_enabled", False) self.expectation_enabled_value = runcard.get("expectation_enabled", False) - mps_enabled_value = runcard.get("MPS_enabled") if mps_enabled_value is True: self.MPS_enabled = True @@ -35,7 +34,6 @@ def __init__(self, runcard): self.quimb = quimb self.platform = "qutensornet" self.versions["quimb"] = self.quimb.__version__ - def apply_gate(self, gate, state, nqubits): # pragma: no cover raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") @@ -46,13 +44,10 @@ def apply_gate_density_matrix(self, gate, state, nqubits): # pragma: no cover def assign_measurements(self, measurement_map, circuit_result): raise_error(NotImplementedError, "Not implemented in QiboTN.") - def set_precision(self, precision): if precision != self.precision: super().set_precision(precision) - - def execute_circuit( self, circuit, initial_state=None, nshots=None, return_array=False ): # pragma: no cover @@ -77,7 +72,9 @@ def execute_circuit( and self.expectation_enabled == False ): - state = eval.dense_vector_tn_qu(circuit, init_state, is_mps=False, backend="numpy") + state = eval.dense_vector_tn_qu( + circuit, init_state, is_mps=False, backend="numpy" + ) elif ( self.MPI_enabled == False @@ -85,9 +82,10 @@ def execute_circuit( and self.NCCL_enabled == False and self.expectation_enabled == False ): - - state = eval.dense_vector_tn_qu(circuit, init_state, is_mps=True, backend="numpy") + state = eval.dense_vector_tn_qu( + circuit, init_state, is_mps=True, backend="numpy" + ) elif ( self.MPI_enabled == True @@ -95,9 +93,8 @@ def execute_circuit( and self.NCCL_enabled == False and self.expectation_enabled == False ): - - raise_error(NotImplementedError, "QiboTN quimb backend cannot support MPI.") + raise_error(NotImplementedError, "QiboTN quimb backend cannot support MPI.") elif ( self.MPI_enabled == False @@ -105,9 +102,10 @@ def execute_circuit( and self.NCCL_enabled == True and self.expectation_enabled == False ): - - raise_error(NotImplementedError, "QiboTN quimb backend cannot support NCCL.") + raise_error( + NotImplementedError, "QiboTN quimb backend cannot support NCCL." + ) elif ( self.MPI_enabled == False @@ -115,9 +113,10 @@ def execute_circuit( and self.NCCL_enabled == False and self.expectation_enabled == True ): - - raise_error(NotImplementedError, "QiboTN quimb backend cannot support expectation") + raise_error( + NotImplementedError, "QiboTN quimb backend cannot support expectation" + ) elif ( self.MPI_enabled == True @@ -125,7 +124,9 @@ def execute_circuit( and self.NCCL_enabled == False and self.expectation_enabled == True ): - raise_error(NotImplementedError, "QiboTN quimb backend cannot support expectation") + raise_error( + NotImplementedError, "QiboTN quimb backend cannot support expectation" + ) elif ( self.MPI_enabled == False @@ -133,7 +134,9 @@ def execute_circuit( and self.NCCL_enabled == True and self.expectation_enabled == True ): - raise_error(NotImplementedError, "QiboTN quimb backend cannot support expectation") + raise_error( + NotImplementedError, "QiboTN quimb backend cannot support expectation" + ) else: raise_error(NotImplementedError, "Compute type not supported.") From c370c920a3c6c689fe2bae787ff916929dd38ebe Mon Sep 17 00:00:00 2001 From: yangliwei Date: Wed, 7 Feb 2024 17:59:10 +0800 Subject: [PATCH 046/199] Format update using black formatter --- tests/test_cuquantum_cutensor_backend.py | 36 +++++++++++------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/tests/test_cuquantum_cutensor_backend.py b/tests/test_cuquantum_cutensor_backend.py index 3de5c17b..57b3f342 100644 --- a/tests/test_cuquantum_cutensor_backend.py +++ b/tests/test_cuquantum_cutensor_backend.py @@ -35,18 +35,16 @@ def test_eval(nqubits: int, dtype="complex128"): import qibotn.eval # Test qibo - qibo.set_backend(backend=config.qibo.backend, - platform=config.qibo.platform) - qibo_time, (qibo_circ, result_sv) = time( - lambda: qibo_qft(nqubits, swaps=True)) + qibo.set_backend(backend=config.qibo.backend, platform=config.qibo.platform) + qibo_time, (qibo_circ, result_sv) = time(lambda: qibo_qft(nqubits, swaps=True)) # Test Cuquantum cutn_time, result_tn = time( - lambda: qibotn.eval.dense_vector_tn(qibo_circ, dtype).flatten()) + lambda: qibotn.eval.dense_vector_tn(qibo_circ, dtype).flatten() + ) assert 1e-2 * qibo_time < cutn_time < 1e2 * qibo_time - assert np.allclose( - result_sv, result_tn), "Resulting dense vectors do not match" + assert np.allclose(result_sv, result_tn), "Resulting dense vectors do not match" @pytest.mark.gpu @@ -62,25 +60,25 @@ def test_mps(nqubits: int, dtype="complex128"): import qibotn.eval # Test qibo - qibo.set_backend(backend=config.qibo.backend, - platform=config.qibo.platform) + qibo.set_backend(backend=config.qibo.backend, platform=config.qibo.platform) - qibo_time, (circ_qibo, result_sv) = time( - lambda: qibo_qft(nqubits, swaps=True)) + qibo_time, (circ_qibo, result_sv) = time(lambda: qibo_qft(nqubits, swaps=True)) result_sv_cp = cp.asarray(result_sv) # Test of MPS - gate_algo = {'qr_method': False, - 'svd_method': { - 'partition': 'UV', - 'abs_cutoff': 1e-12, - }} + gate_algo = { + "qr_method": False, + "svd_method": { + "partition": "UV", + "abs_cutoff": 1e-12, + }, + } cutn_time, result_tn = time( - lambda: qibotn.eval.dense_vector_mps(circ_qibo, gate_algo, dtype).flatten()) + lambda: qibotn.eval.dense_vector_mps(circ_qibo, gate_algo, dtype).flatten() + ) - print( - f"State vector difference: {abs(result_tn - result_sv_cp).max():0.3e}") + print(f"State vector difference: {abs(result_tn - result_sv_cp).max():0.3e}") assert cp.allclose(result_tn, result_sv_cp) From d4e75b94a403842e00b5b8267404571de17071e8 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 7 Feb 2024 11:10:44 +0100 Subject: [PATCH 047/199] ci: Update pre-commit configurations, introduce pre-commit ci --- .pre-commit-config.yaml | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 177c408f..9d8b3681 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,25 +1,48 @@ -# See https://pre-commit.com for more information -# See https://pre-commit.com/hooks.html for more hooks +ci: + autofix_prs: true repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-yaml - id: check-toml - - id: check-merge-conflict - id: debug-statements - repo: https://github.com/psf/black - rev: 23.1.0 + rev: 24.1.1 hooks: - id: black - repo: https://github.com/pycqa/isort - rev: 5.12.0 + rev: 5.13.2 hooks: - id: isort args: ["--profile", "black"] + - repo: https://github.com/PyCQA/docformatter + rev: v1.7.5 + hooks: + - id: docformatter + additional_dependencies: [tomli] + args: [--in-place, --config, ./pyproject.toml] - repo: https://github.com/asottile/pyupgrade - rev: v3.3.1 + rev: v3.15.0 hooks: - id: pyupgrade + - repo: https://github.com/hadialqattan/pycln + rev: v2.4.0 + hooks: + - id: pycln + args: + - --config=pyproject.toml + - --all + - repo: https://github.com/adamchainz/blacken-docs + rev: 1.16.0 + hooks: + - id: blacken-docs + - repo: https://github.com/pycqa/pydocstyle + rev: 6.3.0 + hooks: + - id: pydocstyle + args: + - --select=D103,D200,D206,D300,D301 + files: ^src/ From c46909a7466c58826ea9907f1ce8a480451d45f3 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 11:46:32 +0800 Subject: [PATCH 048/199] removed quimb.py as it is duplicate of eval_qu.py --- src/qibotn/quimb.py | 48 --------------------------------------------- 1 file changed, 48 deletions(-) delete mode 100644 src/qibotn/quimb.py diff --git a/src/qibotn/quimb.py b/src/qibotn/quimb.py deleted file mode 100644 index a9ac5107..00000000 --- a/src/qibotn/quimb.py +++ /dev/null @@ -1,48 +0,0 @@ -import numpy as np -import quimb.tensor as qtn -from qibo.models import Circuit as QiboCircuit - - -def from_qibo(circuit: QiboCircuit, is_mps: False, psi0=None, method='svd', - cutoff=1e-6, cutoff_mode='abs'): - nqubits = circuit.nqubits - gate_opt = {} - if is_mps: - tncirc = qtn.CircuitMPS(nqubits, psi0=psi0) - gate_opt["method"] = method - gate_opt["cutoff"] = cutoff - gate_opt["cutoff_mode"] = cutoff_mode - else: - tncirc = qtn.Circuit(nqubits, psi0=psi0) - - for gate in circuit.queue: - tncirc.apply_gate( - gate.name, - *gate.parameters, - *gate.qubits, - parametrize=False if is_mps else (len(gate.parameters) > 0), - **gate_opt - ) - - return tncirc - - -def init_state_tn(nqubits, init_state_sv): - dims = tuple(2 * np.ones(nqubits, dtype=int)) - - return qtn.tensor_1d.MatrixProductState.from_dense(init_state_sv, dims) - - -def eval(qasm: str, init_state, is_mps, backend="numpy"): - """Evaluate QASM with Quimb - - backend (quimb): numpy, cupy, jax. Passed to ``opt_einsum``. - - """ - circuit = QiboCircuit.from_qasm(qasm) - init_state_mps = init_state_tn(circuit.nqubits, init_state) - circ_quimb = from_qibo(circuit, is_mps, psi0=init_state_mps) - interim = circ_quimb.psi.full_simplify(seq="DRC") - amplitudes = interim.to_dense(backend=backend).flatten() - - return amplitudes From 546cac6956be1da01e6699ce0ba6713c19f738ed Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 11:47:25 +0800 Subject: [PATCH 049/199] removed __main__.py as it is no longer used --- src/qibotn/__main__.py | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 src/qibotn/__main__.py diff --git a/src/qibotn/__main__.py b/src/qibotn/__main__.py deleted file mode 100644 index 0476be5e..00000000 --- a/src/qibotn/__main__.py +++ /dev/null @@ -1,20 +0,0 @@ -import argparse - -import qibotn.quimb - - -def parser(): - parser = argparse.ArgumentParser() - parser.add_argument( - "--nqubits", default=10, type=int, help="Number of quibits in the circuits." - ) - return parser.parse_args() - - -def main(args: argparse.Namespace): - print("Testing for %d nqubits" % (args.nqubits)) - qibotn.quimb.eval(args.nqubits, args.qasm_circ, args.init_state) - - -if __name__ == "__main__": - main(parser()) From 84a6a035ba88f07129b9651df79662467de0f071 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 12:06:38 +0800 Subject: [PATCH 050/199] added missing docstring --- src/qibotn/eval_qu.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/qibotn/eval_qu.py b/src/qibotn/eval_qu.py index 9dc639cd..78d84ef1 100644 --- a/src/qibotn/eval_qu.py +++ b/src/qibotn/eval_qu.py @@ -5,6 +5,8 @@ def from_qibo(circuit: QiboCircuit, is_mps: False, psi0=None, method='svd', cutoff=1e-6, cutoff_mode='abs'): + """Create a tensornetwork representation of the circuit""" + nqubits = circuit.nqubits gate_opt = {} if is_mps: @@ -28,6 +30,9 @@ def from_qibo(circuit: QiboCircuit, is_mps: False, psi0=None, method='svd', def init_state_tn(nqubits, init_state_sv): + + """Create a matrixproductstate directly from a dense vector""" + dims = tuple(2 * np.ones(nqubits, dtype=int)) return qtn.tensor_1d.MatrixProductState.from_dense(init_state_sv, dims) From 2422f1face53e38a90d1e858f0d6628b1d6e3fd4 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 12:07:30 +0800 Subject: [PATCH 051/199] added missing docstring --- tests/test_qasm_quimb_backend.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/test_qasm_quimb_backend.py b/tests/test_qasm_quimb_backend.py index e7dc6e67..e01cde47 100644 --- a/tests/test_qasm_quimb_backend.py +++ b/tests/test_qasm_quimb_backend.py @@ -23,6 +23,14 @@ def qibo_qft(nqubits, init_state, swaps): @pytest.mark.parametrize("nqubits, tolerance, is_mps", [(1, 1e-6, True), (2, 1e-6, False), (5, 1e-3, True), (10, 1e-3, False)]) def test_eval(nqubits: int, tolerance: float, is_mps: bool): + + """Evaluate circuit with Quimb backend. + + Args: + nqubits (int): Total number of qubits in the system. + tolerance (float): Maximum limit allowed for difference in results + is_mps (bool): True if state is MPS and False for tensor network structure + """ # hack quimb to use the correct number of processes # TODO: remove completely, or at least delegate to the backend # implementation @@ -35,9 +43,7 @@ def test_eval(nqubits: int, tolerance: float, is_mps: bool): # Test qibo qibo.set_backend(backend=config.qibo.backend, platform=config.qibo.platform) - #qibo_time, (qibo_circ, result_sv) = time( - #lambda: qibo_qft(nqubits, init_state, swaps=True) - #) + qibo_circ, result_sv= qibo_qft(nqubits, init_state, swaps=True) From 336702a555c5c862ff431e9ce83b9d34375ca810 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Thu, 8 Feb 2024 13:09:19 +0800 Subject: [PATCH 052/199] Remove unused variable --- src/qibotn/QiboCircuitConvertor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qibotn/QiboCircuitConvertor.py b/src/qibotn/QiboCircuitConvertor.py index c59745bd..5ac2feed 100644 --- a/src/qibotn/QiboCircuitConvertor.py +++ b/src/qibotn/QiboCircuitConvertor.py @@ -166,7 +166,6 @@ def expectation_operands(self, pauli_string): input_operands = self._get_bitstring_tensors(input_bitstring) pauli_string = dict(zip(range(self.circuit.nqubits), pauli_string)) pauli_map = pauli_string - coned_qubits = pauli_map.keys() ( mode_labels, From 91152d87dbba92970224b6164149b07736c1c3b8 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Thu, 8 Feb 2024 13:57:10 +0800 Subject: [PATCH 053/199] Update sample codes to include Quimb --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 74e1e3a6..b7532ac4 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,9 @@ computation_settings = { } -qibo.set_backend(backend="qibotn", runcard=computation_settings) +qibo.set_backend(backend="qibotn", platform="cutensornet", runcard=computation_settings) #cuQuantum +# qibo.set_backend(backend="qibotn", platform="qutensornet", runcard=computation_settings) #quimb + # Construct the circuit c = Circuit(2) From 4937488e198b2cc40f30d9d4fb627b1c0dfb4c40 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 14:17:35 +0800 Subject: [PATCH 054/199] fixed some bugs --- src/qibotn/backends/cpu.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py index b6b5cf69..2483d981 100644 --- a/src/qibotn/backends/cpu.py +++ b/src/qibotn/backends/cpu.py @@ -14,7 +14,7 @@ def __init__(self, runcard): if runcard is not None: self.MPI_enabled = runcard.get("MPI_enabled", False) self.NCCL_enabled = runcard.get("NCCL_enabled", False) - self.expectation_enabled_value = runcard.get("expectation_enabled", False) + self.expectation_enabled = runcard.get("expectation_enabled", False) mps_enabled_value = runcard.get("MPS_enabled") if mps_enabled_value is True: @@ -63,7 +63,7 @@ def execute_circuit( """ - import qibotn.eval_qu as eval_qu + import qibotn.eval_qu as eval if ( self.MPI_enabled == False @@ -73,7 +73,7 @@ def execute_circuit( ): state = eval.dense_vector_tn_qu( - circuit, init_state, is_mps=False, backend="numpy" + circuit, initial_state=None, is_mps=False, backend="numpy" ) elif ( @@ -84,7 +84,7 @@ def execute_circuit( ): state = eval.dense_vector_tn_qu( - circuit, init_state, is_mps=True, backend="numpy" + circuit, initial_state=None, is_mps=True, backend="numpy" ) elif ( From 929df9199faf86a13153e022ee68e452ef3e44b6 Mon Sep 17 00:00:00 2001 From: nitinshivaraman Date: Thu, 8 Feb 2024 15:34:14 +0800 Subject: [PATCH 055/199] Rename test_qasm_quimb_backend.py to test_quimb_backend.py for better readability --- tests/{test_qasm_quimb_backend.py => test_quimb_backend.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{test_qasm_quimb_backend.py => test_quimb_backend.py} (100%) diff --git a/tests/test_qasm_quimb_backend.py b/tests/test_quimb_backend.py similarity index 100% rename from tests/test_qasm_quimb_backend.py rename to tests/test_quimb_backend.py From 4c692c14d0f89b390612bab12e8b0bb6871b7308 Mon Sep 17 00:00:00 2001 From: yangliwei Date: Thu, 8 Feb 2024 16:16:30 +0800 Subject: [PATCH 056/199] Minor naming update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b7532ac4..2e8dd948 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Tensor network contractions to: - dense vectors - expecation values of given Pauli string -The supported configurations are: +The supported HPC configurations are: - single-node CPU - single-node GPU or GPUs - multi-node multi-GPU with Message Passing Interface (MPI) From 1668eb7baa741948a3f224e7ebde715e4da79115 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 16:17:39 +0800 Subject: [PATCH 057/199] fixed error caused by missing qasm str error --- src/qibotn/backends/cpu.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py index 2483d981..2df78839 100644 --- a/src/qibotn/backends/cpu.py +++ b/src/qibotn/backends/cpu.py @@ -73,7 +73,7 @@ def execute_circuit( ): state = eval.dense_vector_tn_qu( - circuit, initial_state=None, is_mps=False, backend="numpy" + circuit.to_qasm(), initial_state=None, is_mps=False, backend="numpy" ) elif ( @@ -84,7 +84,7 @@ def execute_circuit( ): state = eval.dense_vector_tn_qu( - circuit, initial_state=None, is_mps=True, backend="numpy" + circuit.to_qasm(), initial_state=None, is_mps=True, backend="numpy" ) elif ( From aaabd855a2acdf630a912d8c8aa4d25332fc3fcb Mon Sep 17 00:00:00 2001 From: tankya2 Date: Thu, 8 Feb 2024 16:19:17 +0800 Subject: [PATCH 058/199] Add in tensor network types --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e8dd948..cdf65d46 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,11 @@ To get started, `python setup.py install` to install the tools and dependencies. # Supported Computation -Tensor network contractions to: +Tensor Network Types: +- Tensornet (TN) +- Matrix Product States (MPS) + +Tensor Network contractions to: - dense vectors - expecation values of given Pauli string From 604f11d2896c68e1ed5607452a51f222529af840 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 16:19:59 +0800 Subject: [PATCH 059/199] fixed error caused by initial_state --- src/qibotn/eval_qu.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/qibotn/eval_qu.py b/src/qibotn/eval_qu.py index 78d84ef1..579a42ae 100644 --- a/src/qibotn/eval_qu.py +++ b/src/qibotn/eval_qu.py @@ -38,17 +38,17 @@ def init_state_tn(nqubits, init_state_sv): return qtn.tensor_1d.MatrixProductState.from_dense(init_state_sv, dims) -def dense_vector_tn_qu(qasm: str, init_state, is_mps, backend="numpy"): +def dense_vector_tn_qu(qasm: str, initial_state, is_mps, backend="numpy"): """Evaluate QASM with Quimb backend (quimb): numpy, cupy, jax. Passed to ``opt_einsum``. """ circuit = QiboCircuit.from_qasm(qasm) - if init_state is not None: - init_state = init_state_tn(circuit.nqubits, init_state) - circ_quimb = from_qibo(circuit, is_mps, psi0=init_state) + if initial_state is not None: + initial_state = init_state_tn(circuit.nqubits, initial_state) + circ_quimb = from_qibo(circuit, is_mps, psi0=initial_state) interim = circ_quimb.psi.full_simplify(seq="DRC") - amplitudes = interim.to_dense(backend=backend).flatten() + amplitudes = interim.to_dense(backend=backend) return amplitudes From 773953e18b6c3b3ecea85f6ad3dca2e37c04d02e Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 16:22:38 +0800 Subject: [PATCH 060/199] added flatten of state in pytest instead of using within eval_qu --- tests/test_quimb_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_quimb_backend.py b/tests/test_quimb_backend.py index e01cde47..81a0e2b7 100644 --- a/tests/test_quimb_backend.py +++ b/tests/test_quimb_backend.py @@ -53,7 +53,7 @@ def test_eval(nqubits: int, tolerance: float, is_mps: bool): # Test quimb result_tn = qibotn.eval_qu.dense_vector_tn_qu( qasm_circ, init_state_tn, is_mps, backend=config.quimb.backend - ) + ).flatten() assert np.allclose(result_sv, result_tn, From 8ee168c3108ec3e9e8dac50e881795d2e4534fd9 Mon Sep 17 00:00:00 2001 From: yangliwei Date: Thu, 8 Feb 2024 16:31:22 +0800 Subject: [PATCH 061/199] Minor update for consistency --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cdf65d46..070ccb33 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Currently, the supported tensor network libraries are: - [quimb](https://quimb.readthedocs.io/en/latest/), an easy but fast python library for ‘quantum information many-body’ calculations, focusing primarily on tensor networks. # Sample Codes -## Single Node +## Single-Node Example The code below shows an example of how to activate the Cuquantum TensorNetwork backend of Qibo. ```py import numpy as np @@ -86,7 +86,7 @@ computation_settings = { } ``` -## Multi-Node +## Multi-Node Example Multi-node is enabled by setting either the MPI or NCCL enabled flag to True in the computation settings. Below shows the script to launch on 2 nodes with 2 GPUs each. $node_list contains the IP of the nodes assigned. From fbd995d0d1e734834e28346e360637939ce919a3 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 16:34:46 +0800 Subject: [PATCH 062/199] minor update of initial state --- src/qibotn/backends/cpu.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py index 2df78839..7115b395 100644 --- a/src/qibotn/backends/cpu.py +++ b/src/qibotn/backends/cpu.py @@ -73,7 +73,7 @@ def execute_circuit( ): state = eval.dense_vector_tn_qu( - circuit.to_qasm(), initial_state=None, is_mps=False, backend="numpy" + circuit.to_qasm(), initial_state, is_mps=False, backend="numpy" ) elif ( @@ -84,7 +84,7 @@ def execute_circuit( ): state = eval.dense_vector_tn_qu( - circuit.to_qasm(), initial_state=None, is_mps=True, backend="numpy" + circuit.to_qasm(), initial_state, is_mps=True, backend="numpy" ) elif ( From 4982fbf9262aaac28550b4ddc696da1989b7f04f Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 7 Feb 2024 11:13:55 +0100 Subject: [PATCH 063/199] build: Add Nix files --- .envrc | 9 ++ .gitignore | 1 + flake.lock | 323 +++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 62 ++++++++++ 4 files changed, 395 insertions(+) create mode 100644 .envrc create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..01f5f41d --- /dev/null +++ b/.envrc @@ -0,0 +1,9 @@ +if ! has nix_direnv_version || ! nix_direnv_version 2.2.1; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.2.1/direnvrc" "sha256-zelF0vLbEl5uaqrfIzbgNzJWGmLzCmYAkInj/LNxvKs=" +fi + +nix_direnv_watch_file flake.nix +nix_direnv_watch_file flake.lock +if ! use flake . --impure; then + echo "devenv could not be built. The devenv environment was not loaded. Make the necessary changes to devenv.nix and hit enter to try again." >&2 +fi diff --git a/.gitignore b/.gitignore index d903da02..7f051b80 100644 --- a/.gitignore +++ b/.gitignore @@ -159,3 +159,4 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ +.devenv diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..0f48ef05 --- /dev/null +++ b/flake.lock @@ -0,0 +1,323 @@ +{ + "nodes": { + "devenv": { + "inputs": { + "flake-compat": "flake-compat", + "nix": "nix", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": "pre-commit-hooks" + }, + "locked": { + "lastModified": 1707004164, + "narHash": "sha256-9Hr8onWtvLk5A8vCEkaE9kxA0D7PR62povFokM1oL5Q=", + "owner": "cachix", + "repo": "devenv", + "rev": "0e68853bb27981a4ffd7a7225b59ed84f7180fc7", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1685518550, + "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "id": "flake-utils", + "type": "indirect" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "devenv", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1660459072, + "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "lowdown-src": { + "flake": false, + "locked": { + "lastModified": 1633514407, + "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", + "owner": "kristapsdz", + "repo": "lowdown", + "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "type": "github" + }, + "original": { + "owner": "kristapsdz", + "repo": "lowdown", + "type": "github" + } + }, + "nix": { + "inputs": { + "lowdown-src": "lowdown-src", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression" + }, + "locked": { + "lastModified": 1676545802, + "narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=", + "owner": "domenkozar", + "repo": "nix", + "rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "relaxed-flakes", + "repo": "nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1678875422, + "narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-python": { + "inputs": { + "flake-compat": "flake-compat_2", + "flake-utils": "flake-utils_2", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1707114737, + "narHash": "sha256-ZXqv2epXAjDjfWbYn+yy4VOmW+C7SuUBoiZkkDoSqA4=", + "owner": "cachix", + "repo": "nixpkgs-python", + "rev": "f34ed02276bc08fe1c91c1bf0ef3589d68028878", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "nixpkgs-python", + "type": "github" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1685801374, + "narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "c37ca420157f4abc31e26f436c1145f8951ff373", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1707092692, + "narHash": "sha256-ZbHsm+mGk/izkWtT4xwwqz38fdlwu7nUUKXTOmm4SyE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "faf912b086576fd1a15fca610166c98d47bc667e", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "flake-utils": "flake-utils", + "gitignore": "gitignore", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1704725188, + "narHash": "sha256-qq8NbkhRZF1vVYQFt1s8Mbgo8knj+83+QlL5LBnYGpI=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "ea96f0c05924341c551a797aaba8126334c505d2", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "nixpkgs": "nixpkgs_2", + "nixpkgs-python": "nixpkgs-python", + "systems": "systems_3" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_3": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..92f53ee5 --- /dev/null +++ b/flake.nix @@ -0,0 +1,62 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + systems.url = "github:nix-systems/default"; + devenv.url = "github:cachix/devenv"; + nixpkgs-python = { + url = "github:cachix/nixpkgs-python"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { + self, + nixpkgs, + devenv, + systems, + ... + } @ inputs: let + forEachSystem = nixpkgs.lib.genAttrs (import systems); + in { + # packages = forEachSystem (system: { + # default = + # nixpkgs.legacyPackages.${system}.poetry2nix.mkPoetryApplication + # { + # projectDir = self; + # preferWheels = true; + # }; + # }); + + devShells = + forEachSystem + (system: let + pkgs = nixpkgs.legacyPackages.${system}; + in { + default = devenv.lib.mkShell { + inherit inputs pkgs; + + modules = [ + { + packages = with pkgs; [pre-commit poethepoet stdenv.cc.cc.lib]; + + languages.python = { + enable = true; + # poetry = { + # enable = true; + # install.enable = true; + # install.groups = ["dev" "tests"]; + # install.allExtras = true; + # }; + version = "3.11"; + }; + } + ]; + }; + }); + }; + + nixConfig = { + extra-trusted-public-keys = "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw="; + extra-substituters = "https://devenv.cachix.org"; + }; +} From c3a4a544b5e899af7f20c3a0f3fc5e373062515f Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 7 Feb 2024 11:20:58 +0100 Subject: [PATCH 064/199] fix: Fix dependencies issues introducing additional ones --- .pre-commit-config.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9d8b3681..b65aeedd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: rev: v1.7.5 hooks: - id: docformatter - additional_dependencies: [tomli] + additional_dependencies: [tomli, charset-normalizer] args: [--in-place, --config, ./pyproject.toml] - repo: https://github.com/asottile/pyupgrade rev: v3.15.0 @@ -39,6 +39,8 @@ repos: rev: 1.16.0 hooks: - id: blacken-docs + additional_dependencies: + - platformdirs - repo: https://github.com/pycqa/pydocstyle rev: 6.3.0 hooks: From e496f9f156d5d9bc1270cf8aebeae9811f26ce0b Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 7 Feb 2024 11:21:27 +0100 Subject: [PATCH 065/199] chore: Run pre-commit on all files --- README.md | 4 +- setup.py | 9 ++- src/qibotn/MPSUtils.py | 13 +--- src/qibotn/QiboCircuitConvertor.py | 16 ++-- src/qibotn/QiboCircuitToMPS.py | 4 +- src/qibotn/backends/__init__.py | 2 +- src/qibotn/backends/gpu.py | 4 +- src/qibotn/eval.py | 93 ++++++++++++++++-------- src/qibotn/mps_contraction_helper.py | 17 ++--- tests/test_cuquantum_cutensor_backend.py | 2 +- 10 files changed, 92 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index 070ccb33..9f60ad32 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ import qibo # Below shows how to set the computation_settings # Note that for MPS_enabled and expectation_enabled parameters the accepted inputs are boolean or a dictionary with the format shown below. -# If computation_settings is not specified, the default setting is used in which all booleans will be False. +# If computation_settings is not specified, the default setting is used in which all booleans will be False. # This will trigger the dense vector computation of the tensornet. computation_settings = { @@ -92,4 +92,4 @@ Multi-node is enabled by setting either the MPI or NCCL enabled flag to True in ```sh mpirun -n 4 -hostfile $node_list python test.py -``` \ No newline at end of file +``` diff --git a/setup.py b/setup.py index 13285eb7..0f619a5c 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,7 @@ -from setuptools import setup, find_packages -import re import pathlib +import re + +from setuptools import find_packages, setup HERE = pathlib.Path(__file__).parent.absolute() PACKAGE = "qibotn" @@ -8,8 +9,8 @@ # Returns the qibotn version def version(): - """Gets the version from the package's __init__ file - if there is some problem, let it happily fail""" + """Gets the version from the package's __init__ file if there is some + problem, let it happily fail.""" version_file = HERE / "src" / PACKAGE / "__init__.py" version_regex = r"^__version__ = ['\"]([^'\"]*)['\"]" diff --git a/src/qibotn/MPSUtils.py b/src/qibotn/MPSUtils.py index 4f84f672..e8068f74 100644 --- a/src/qibotn/MPSUtils.py +++ b/src/qibotn/MPSUtils.py @@ -1,23 +1,19 @@ import cupy as cp -from cuquantum.cutensornet.experimental import contract_decompose 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): - """ - Generate the MPS with an initial state of |00...00> - """ + """Generate the MPS with an initial state of |00...00>""" 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.""" # contraction followed by QR decomposition a, _, b = contract_decompose( "ipj,jqk->iqj,jpk", @@ -30,8 +26,7 @@ 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. + """Apply the gate operand to the MPS tensors in-place. Args: mps_tensors: A list of rank-3 ndarray-like tensor objects. diff --git a/src/qibotn/QiboCircuitConvertor.py b/src/qibotn/QiboCircuitConvertor.py index 5ac2feed..f67fb8ea 100644 --- a/src/qibotn/QiboCircuitConvertor.py +++ b/src/qibotn/QiboCircuitConvertor.py @@ -5,9 +5,9 @@ class QiboCircuitToEinsum: - """Convert a circuit to a Tensor Network (TN) representation. - The circuit is first processed to an intermediate form by grouping each gate - matrix with its corresponding qubit it is acting on to a list. It is then + """Convert a circuit to a Tensor Network (TN) representation. The circuit + is first processed to an intermediate form by grouping each gate matrix + with its corresponding qubit it is acting on to a list. It is then converted to an equivalent TN expression through the class function state_vector_operands() following the Einstein summation convention in the interleave format. @@ -79,9 +79,8 @@ def _parse_gates_to_mode_labels_operands( 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 (qubit_states,input_output) * + qubits_involved.""" return (2, 2) * nqubits def init_intermediate_circuit(self, circuit): @@ -134,8 +133,7 @@ def init_inverse_circuit(self, circuit): self.active_qubits_inverse = np.unique(gates_qubits_inverse) def get_pauli_gates(self, pauli_map, dtype="complex128", backend=cp): - """ - Populate the gates for all pauli operators. + """Populate the gates for all pauli operators. Args: pauli_map: A dictionary mapping qubits to pauli operators. @@ -161,7 +159,7 @@ def get_pauli_gates(self, pauli_map, dtype="complex128", backend=cp): return gates def expectation_operands(self, pauli_string): - input_bitstring = "0" * self.circuit.nqubits + input_bitstring = "0" * self.circuit.nqubits input_operands = self._get_bitstring_tensors(input_bitstring) pauli_string = dict(zip(range(self.circuit.nqubits), pauli_string)) diff --git a/src/qibotn/QiboCircuitToMPS.py b/src/qibotn/QiboCircuitToMPS.py index 816b17c0..b1d847f2 100644 --- a/src/qibotn/QiboCircuitToMPS.py +++ b/src/qibotn/QiboCircuitToMPS.py @@ -1,9 +1,9 @@ import cupy as cp import numpy as np - from cuquantum import cutensornet as cutn + +from qibotn.MPSUtils import apply_gate, initial from qibotn.QiboCircuitConvertor import QiboCircuitToEinsum -from qibotn.MPSUtils import initial, apply_gate class QiboCircuitToMPS: diff --git a/src/qibotn/backends/__init__.py b/src/qibotn/backends/__init__.py index f9279323..e5d68de5 100644 --- a/src/qibotn/backends/__init__.py +++ b/src/qibotn/backends/__init__.py @@ -1,2 +1,2 @@ -from qibotn.backends.gpu import CuTensorNet from qibotn.backends.cpu import QuTensorNet +from qibotn.backends.gpu import CuTensorNet diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index 5777fe90..2c3f8d41 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -1,8 +1,7 @@ import numpy as np - from qibo.backends.numpy import NumpyBackend -from qibo.states import CircuitResult from qibo.config import raise_error +from qibo.states import CircuitResult class CuTensorNet(NumpyBackend): # pragma: no cover @@ -107,7 +106,6 @@ def execute_circuit( Returns: xxx. - """ import qibotn.eval as eval diff --git a/src/qibotn/eval.py b/src/qibotn/eval.py index 96fd488a..5fcb66f0 100644 --- a/src/qibotn/eval.py +++ b/src/qibotn/eval.py @@ -1,20 +1,22 @@ -from qibotn.QiboCircuitConvertor import QiboCircuitToEinsum -from cuquantum import contract -from cupy.cuda.runtime import getDeviceCount import cupy as cp +from cupy.cuda.runtime import getDeviceCount +from cuquantum import contract -from qibotn.QiboCircuitToMPS import QiboCircuitToMPS from qibotn.mps_contraction_helper import MPSContractionHelper +from qibotn.QiboCircuitConvertor import QiboCircuitToEinsum +from qibotn.QiboCircuitToMPS import QiboCircuitToMPS def dense_vector_tn(qibo_circ, datatype): - """Convert qibo circuit to tensornet (TN) format and perform contraction to dense vector.""" + """Convert qibo circuit to tensornet (TN) format and perform contraction to + dense vector.""" 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.""" + """Convert qibo circuit to tensornet (TN) format and perform contraction to + expectation of given Pauli string.""" myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract( *myconvertor.expectation_operands( @@ -24,14 +26,19 @@ def expectation_pauli_tn(qibo_circ, datatype, pauli_string_pattern): def dense_vector_tn_MPI(qibo_circ, datatype, n_samples=8): - """Convert qibo circuit to tensornet (TN) format and perform contraction using multi node and multi GPU through MPI. - The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. - The pathfinder looks at user defined number of samples (n_samples) iteratively to 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 a dense vector representation of the TN. + """Convert qibo circuit to tensornet (TN) format and perform contraction + using multi node and multi GPU through MPI. + + The conversion is performed by QiboCircuitToEinsum(), after which it + goes through 2 steps: pathfinder and execution. The pathfinder looks + at user defined number of samples (n_samples) iteratively to 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 a dense vector representation of the TN. """ - from mpi4py import MPI from cuquantum import Network + from mpi4py import MPI root = 0 comm = MPI.COMM_WORLD @@ -86,14 +93,19 @@ def dense_vector_tn_MPI(qibo_circ, datatype, n_samples=8): def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8): - """Convert qibo circuit to tensornet (TN) format and perform contraction using multi node and multi GPU through NCCL. - The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. - The pathfinder looks at user defined number of samples (n_samples) iteratively to 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 a dense vector representation of the TN. + """Convert qibo circuit to tensornet (TN) format and perform contraction + using multi node and multi GPU through NCCL. + + The conversion is performed by QiboCircuitToEinsum(), after which it + goes through 2 steps: pathfinder and execution. The pathfinder looks + at user defined number of samples (n_samples) iteratively to 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 a dense vector representation of the TN. """ - from mpi4py import MPI - from cuquantum import Network from cupy.cuda import nccl + from cuquantum import Network + from mpi4py import MPI root = 0 comm_mpi = MPI.COMM_WORLD @@ -159,15 +171,22 @@ def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8): def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string_pattern, n_samples=8): - """Convert qibo circuit to tensornet (TN) format and perform contraction to expectation of given Pauli string using multi node and multi GPU through NCCL. - The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. - The pauli_string_pattern is used to generate the pauli string corresponding to the number of qubits of the system. - The pathfinder looks at user defined number of samples (n_samples) iteratively to 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. + """Convert qibo circuit to tensornet (TN) format and perform contraction to + expectation of given Pauli string using multi node and multi GPU through + NCCL. + + The conversion is performed by QiboCircuitToEinsum(), after which it + goes through 2 steps: pathfinder and execution. The + pauli_string_pattern is used to generate the pauli string + corresponding to the number of qubits of the system. The pathfinder + looks at user defined number of samples (n_samples) iteratively to + 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. """ - from mpi4py import MPI - from cuquantum import Network from cupy.cuda import nccl + from cuquantum import Network + from mpi4py import MPI root = 0 comm_mpi = MPI.COMM_WORLD @@ -235,14 +254,21 @@ def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string_pattern, n_sampl def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string_pattern, n_samples=8): - """Convert qibo circuit to tensornet (TN) format and perform contraction to expectation of given Pauli string using multi node and multi GPU through MPI. - The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. - The pauli_string_pattern is used to generate the pauli string corresponding to the number of qubits of the system. - The pathfinder looks at user defined number of samples (n_samples) iteratively to 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. + """Convert qibo circuit to tensornet (TN) format and perform contraction to + expectation of given Pauli string using multi node and multi GPU through + MPI. + + The conversion is performed by QiboCircuitToEinsum(), after which it + goes through 2 steps: pathfinder and execution. The + pauli_string_pattern is used to generate the pauli string + corresponding to the number of qubits of the system. The pathfinder + looks at user defined number of samples (n_samples) iteratively to + 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. """ - from mpi4py import MPI # this line initializes MPI from cuquantum import Network + from mpi4py import MPI # this line initializes MPI root = 0 comm = MPI.COMM_WORLD @@ -299,7 +325,8 @@ 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.""" + """Convert qibo circuit to matrix product state (MPS) format and perform + contraction to dense vector.""" myconvertor = QiboCircuitToMPS(qibo_circ, gate_algo, dtype=datatype) mps_helper = MPSContractionHelper(myconvertor.num_qubits) @@ -309,7 +336,9 @@ def dense_vector_mps(qibo_circ, gate_algo, datatype): def pauli_string_gen(nqubits, pauli_string_pattern): - """Used internally to generate the string based on given pattern and number of qubit. + """Used internally to generate the string based on given pattern and number + of qubit. + Example: pattern: "XZ", number of qubit: 7, output = XZXZXZX """ if nqubits <= 0: diff --git a/src/qibotn/mps_contraction_helper.py b/src/qibotn/mps_contraction_helper.py index 29d5e253..1c004de4 100644 --- a/src/qibotn/mps_contraction_helper.py +++ b/src/qibotn/mps_contraction_helper.py @@ -1,11 +1,10 @@ -from cuquantum import contract, contract_path, CircuitToEinsum, tensor +from cuquantum import contract, contract_path # Reference: https://github.com/NVIDIA/cuQuantum/blob/main/python/samples/cutensornet/tn_algorithms/mps_algorithms.ipynb class MPSContractionHelper: - """ - A helper class to compute various quantities for a given MPS. + """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: @@ -43,8 +42,8 @@ def __init__(self, num_qubits): ] def contract_norm(self, mps_tensors, options=None): - """ - Contract the corresponding tensor network to form the norm of the MPS. + """Contract the corresponding tensor network to form the norm of the + MPS. Args: mps_tensors: A list of rank-3 ndarray-like tensor objects. @@ -64,8 +63,8 @@ def contract_norm(self, mps_tensors, options=None): return self._contract(interleaved_inputs, options=options).real def contract_state_vector(self, mps_tensors, options=None): - """ - Contract the corresponding tensor network to form the state vector representation of the MPS. + """Contract the corresponding tensor network to form the state vector + representation of the MPS. Args: mps_tensors: A list of rank-3 ndarray-like tensor objects. @@ -86,8 +85,8 @@ def contract_state_vector(self, mps_tensors, options=None): def contract_expectation( self, mps_tensors, operator, qubits, options=None, normalize=False ): - """ - Contract the corresponding tensor network to form the expectation of the MPS. + """Contract the corresponding tensor network to form the expectation of + the MPS. Args: mps_tensors: A list of rank-3 ndarray-like tensor objects. diff --git a/tests/test_cuquantum_cutensor_backend.py b/tests/test_cuquantum_cutensor_backend.py index 57b3f342..c8f1e199 100644 --- a/tests/test_cuquantum_cutensor_backend.py +++ b/tests/test_cuquantum_cutensor_backend.py @@ -1,8 +1,8 @@ from timeit import default_timer as timer import config -import numpy as np import cupy as cp +import numpy as np import pytest import qibo from qibo.models import QFT From 665cec42b2903609e4c6641e611889b6c0407536 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 7 Feb 2024 18:58:01 +0100 Subject: [PATCH 066/199] chore: Revert additional deps in pre-commit hooks, apparently not needed The problem could have been caused by a corrupted cache. However, it does not persist after a force clean --- .pre-commit-config.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b65aeedd..9d8b3681 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: rev: v1.7.5 hooks: - id: docformatter - additional_dependencies: [tomli, charset-normalizer] + additional_dependencies: [tomli] args: [--in-place, --config, ./pyproject.toml] - repo: https://github.com/asottile/pyupgrade rev: v3.15.0 @@ -39,8 +39,6 @@ repos: rev: 1.16.0 hooks: - id: blacken-docs - additional_dependencies: - - platformdirs - repo: https://github.com/pycqa/pydocstyle rev: 6.3.0 hooks: From c69fd5f04520f74c7dc84eb882a5d0d2c8e6db0e Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 8 Feb 2024 10:17:22 +0100 Subject: [PATCH 067/199] chore: Pre-commit all files once more --- README.md | 56 ++++++++++++++++++++++--------------- src/qibotn/backends/cpu.py | 5 +--- src/qibotn/eval_qu.py | 20 +++++++------ tests/test_quimb_backend.py | 29 ++++++++++--------- 4 files changed, 60 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 9f60ad32..d22503ca 100644 --- a/README.md +++ b/README.md @@ -5,26 +5,33 @@ To get started, `python setup.py install` to install the tools and dependencies. # Supported Computation Tensor Network Types: + - Tensornet (TN) - Matrix Product States (MPS) Tensor Network contractions to: + - dense vectors - expecation values of given Pauli string The supported HPC configurations are: + - single-node CPU - single-node GPU or GPUs - multi-node multi-GPU with Message Passing Interface (MPI) - multi-node multi-GPU with NVIDIA Collective Communications Library (NCCL) 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. # Sample Codes + ## Single-Node Example + The code below shows an example of how to activate the Cuquantum TensorNetwork backend of Qibo. + ```py import numpy as np from qibo import Circuit, gates @@ -36,20 +43,22 @@ import qibo # This will trigger the dense vector computation of the tensornet. computation_settings = { - 'MPI_enabled': False, - 'MPS_enabled': { - "qr_method": False, - "svd_method": { - "partition": "UV", - "abs_cutoff": 1e-12, - }, - } , - 'NCCL_enabled': False, - 'expectation_enabled': False + "MPI_enabled": False, + "MPS_enabled": { + "qr_method": False, + "svd_method": { + "partition": "UV", + "abs_cutoff": 1e-12, + }, + }, + "NCCL_enabled": False, + "expectation_enabled": False, } -qibo.set_backend(backend="qibotn", platform="cutensornet", runcard=computation_settings) #cuQuantum +qibo.set_backend( + backend="qibotn", platform="cutensornet", runcard=computation_settings +) # cuQuantum # qibo.set_backend(backend="qibotn", platform="qutensornet", runcard=computation_settings) #quimb @@ -70,25 +79,26 @@ Other examples of setting the computation_settings ```py # Expectation computation with specific Pauli String pattern computation_settings = { - 'MPI_enabled': False, - 'MPS_enabled': False, - 'NCCL_enabled': False, - 'expectation_enabled': { - 'pauli_string_pattern': "IXZ" + "MPI_enabled": False, + "MPS_enabled": False, + "NCCL_enabled": False, + "expectation_enabled": { + "pauli_string_pattern": "IXZ", + }, } # Dense vector computation using multi node through MPI computation_settings = { - 'MPI_enabled': True, - 'MPS_enabled': False, - 'NCCL_enabled': False, - 'expectation_enabled': False + "MPI_enabled": True, + "MPS_enabled": False, + "NCCL_enabled": False, + "expectation_enabled": False, } ``` ## Multi-Node Example -Multi-node is enabled by setting either the MPI or NCCL enabled flag to True in the computation settings. Below shows the script to launch on 2 nodes with 2 GPUs each. $node_list contains the IP of the nodes assigned. +Multi-node is enabled by setting either the MPI or NCCL enabled flag to True in the computation settings. Below shows the script to launch on 2 nodes with 2 GPUs each. $node_list contains the IP of the nodes assigned. ```sh mpirun -n 4 -hostfile $node_list python test.py diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py index 7115b395..a85dfd9b 100644 --- a/src/qibotn/backends/cpu.py +++ b/src/qibotn/backends/cpu.py @@ -1,8 +1,6 @@ -import numpy as np - from qibo.backends.numpy import NumpyBackend -from qibo.states import CircuitResult from qibo.config import raise_error +from qibo.states import CircuitResult class QuTensorNet(NumpyBackend): @@ -60,7 +58,6 @@ def execute_circuit( Returns: xxx. - """ import qibotn.eval_qu as eval diff --git a/src/qibotn/eval_qu.py b/src/qibotn/eval_qu.py index 579a42ae..7b603b50 100644 --- a/src/qibotn/eval_qu.py +++ b/src/qibotn/eval_qu.py @@ -3,9 +3,15 @@ from qibo.models import Circuit as QiboCircuit -def from_qibo(circuit: QiboCircuit, is_mps: False, psi0=None, method='svd', - cutoff=1e-6, cutoff_mode='abs'): - """Create a tensornetwork representation of the circuit""" +def from_qibo( + circuit: QiboCircuit, + is_mps: False, + psi0=None, + method="svd", + cutoff=1e-6, + cutoff_mode="abs", +): + """Create a tensornetwork representation of the circuit.""" nqubits = circuit.nqubits gate_opt = {} @@ -30,19 +36,17 @@ def from_qibo(circuit: QiboCircuit, is_mps: False, psi0=None, method='svd', def init_state_tn(nqubits, init_state_sv): - - """Create a matrixproductstate directly from a dense vector""" + """Create a matrixproductstate directly from a dense vector.""" dims = tuple(2 * np.ones(nqubits, dtype=int)) return qtn.tensor_1d.MatrixProductState.from_dense(init_state_sv, dims) -def dense_vector_tn_qu(qasm: str, initial_state, is_mps, backend="numpy"): - """Evaluate QASM with Quimb +def dense_vector_tn_qu(qasm: str, initial_state, is_mps, backend="numpy"): + """Evaluate QASM with Quimb. backend (quimb): numpy, cupy, jax. Passed to ``opt_einsum``. - """ circuit = QiboCircuit.from_qasm(qasm) if initial_state is not None: diff --git a/tests/test_quimb_backend.py b/tests/test_quimb_backend.py index 81a0e2b7..15ba6524 100644 --- a/tests/test_quimb_backend.py +++ b/tests/test_quimb_backend.py @@ -1,5 +1,6 @@ import copy import os + import config import numpy as np import pytest @@ -8,8 +9,7 @@ def create_init_state(nqubits): - init_state = np.random.random(2**nqubits) + \ - 1j * np.random.random(2**nqubits) + init_state = np.random.random(2**nqubits) + 1j * np.random.random(2**nqubits) init_state = init_state / np.sqrt((np.abs(init_state) ** 2).sum()) return init_state @@ -20,10 +20,11 @@ def qibo_qft(nqubits, init_state, swaps): return circ_qibo, state_vec -@pytest.mark.parametrize("nqubits, tolerance, is_mps", - [(1, 1e-6, True), (2, 1e-6, False), (5, 1e-3, True), (10, 1e-3, False)]) +@pytest.mark.parametrize( + "nqubits, tolerance, is_mps", + [(1, 1e-6, True), (2, 1e-6, False), (5, 1e-3, True), (10, 1e-3, False)], +) def test_eval(nqubits: int, tolerance: float, is_mps: bool): - """Evaluate circuit with Quimb backend. Args: @@ -41,20 +42,18 @@ def test_eval(nqubits: int, tolerance: float, is_mps: bool): init_state_tn = copy.deepcopy(init_state) # Test qibo - qibo.set_backend(backend=config.qibo.backend, - platform=config.qibo.platform) - - qibo_circ, result_sv= qibo_qft(nqubits, init_state, swaps=True) - + qibo.set_backend(backend=config.qibo.backend, platform=config.qibo.platform) + + qibo_circ, result_sv = qibo_qft(nqubits, init_state, swaps=True) # Convert to qasm for other backends qasm_circ = qibo_circ.to_qasm() # Test quimb result_tn = qibotn.eval_qu.dense_vector_tn_qu( - qasm_circ, init_state_tn, is_mps, backend=config.quimb.backend - ).flatten() - + qasm_circ, init_state_tn, is_mps, backend=config.quimb.backend + ).flatten() - assert np.allclose(result_sv, result_tn, - atol=tolerance), "Resulting dense vectors do not match" + assert np.allclose( + result_sv, result_tn, atol=tolerance + ), "Resulting dense vectors do not match" From e6a28ce573b926758af191427d764c24ec1aac7e Mon Sep 17 00:00:00 2001 From: yangliwei Date: Thu, 8 Feb 2024 17:18:07 +0800 Subject: [PATCH 068/199] Minor black formatting --- src/qibotn/QiboCircuitConvertor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/QiboCircuitConvertor.py b/src/qibotn/QiboCircuitConvertor.py index 5ac2feed..e1aabea2 100644 --- a/src/qibotn/QiboCircuitConvertor.py +++ b/src/qibotn/QiboCircuitConvertor.py @@ -161,7 +161,7 @@ def get_pauli_gates(self, pauli_map, dtype="complex128", backend=cp): return gates def expectation_operands(self, pauli_string): - input_bitstring = "0" * self.circuit.nqubits + input_bitstring = "0" * self.circuit.nqubits input_operands = self._get_bitstring_tensors(input_bitstring) pauli_string = dict(zip(range(self.circuit.nqubits), pauli_string)) From 89e97b48e8595483ac962a575d7784dd218f9190 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 09:18:18 +0000 Subject: [PATCH 069/199] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- README.md | 4 +- setup.py | 9 ++- src/qibotn/MPSUtils.py | 13 +--- src/qibotn/QiboCircuitConvertor.py | 14 ++-- src/qibotn/QiboCircuitToMPS.py | 4 +- src/qibotn/backends/__init__.py | 2 +- src/qibotn/backends/cpu.py | 5 +- src/qibotn/backends/gpu.py | 4 +- src/qibotn/eval.py | 93 ++++++++++++++++-------- src/qibotn/eval_qu.py | 20 +++-- src/qibotn/mps_contraction_helper.py | 17 ++--- tests/test_cuquantum_cutensor_backend.py | 2 +- tests/test_quimb_backend.py | 29 ++++---- 13 files changed, 118 insertions(+), 98 deletions(-) diff --git a/README.md b/README.md index 070ccb33..9f60ad32 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ import qibo # Below shows how to set the computation_settings # Note that for MPS_enabled and expectation_enabled parameters the accepted inputs are boolean or a dictionary with the format shown below. -# If computation_settings is not specified, the default setting is used in which all booleans will be False. +# If computation_settings is not specified, the default setting is used in which all booleans will be False. # This will trigger the dense vector computation of the tensornet. computation_settings = { @@ -92,4 +92,4 @@ Multi-node is enabled by setting either the MPI or NCCL enabled flag to True in ```sh mpirun -n 4 -hostfile $node_list python test.py -``` \ No newline at end of file +``` diff --git a/setup.py b/setup.py index 13285eb7..0f619a5c 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,7 @@ -from setuptools import setup, find_packages -import re import pathlib +import re + +from setuptools import find_packages, setup HERE = pathlib.Path(__file__).parent.absolute() PACKAGE = "qibotn" @@ -8,8 +9,8 @@ # Returns the qibotn version def version(): - """Gets the version from the package's __init__ file - if there is some problem, let it happily fail""" + """Gets the version from the package's __init__ file if there is some + problem, let it happily fail.""" version_file = HERE / "src" / PACKAGE / "__init__.py" version_regex = r"^__version__ = ['\"]([^'\"]*)['\"]" diff --git a/src/qibotn/MPSUtils.py b/src/qibotn/MPSUtils.py index 4f84f672..e8068f74 100644 --- a/src/qibotn/MPSUtils.py +++ b/src/qibotn/MPSUtils.py @@ -1,23 +1,19 @@ import cupy as cp -from cuquantum.cutensornet.experimental import contract_decompose 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): - """ - Generate the MPS with an initial state of |00...00> - """ + """Generate the MPS with an initial state of |00...00>""" 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.""" # contraction followed by QR decomposition a, _, b = contract_decompose( "ipj,jqk->iqj,jpk", @@ -30,8 +26,7 @@ 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. + """Apply the gate operand to the MPS tensors in-place. Args: mps_tensors: A list of rank-3 ndarray-like tensor objects. diff --git a/src/qibotn/QiboCircuitConvertor.py b/src/qibotn/QiboCircuitConvertor.py index e1aabea2..f67fb8ea 100644 --- a/src/qibotn/QiboCircuitConvertor.py +++ b/src/qibotn/QiboCircuitConvertor.py @@ -5,9 +5,9 @@ class QiboCircuitToEinsum: - """Convert a circuit to a Tensor Network (TN) representation. - The circuit is first processed to an intermediate form by grouping each gate - matrix with its corresponding qubit it is acting on to a list. It is then + """Convert a circuit to a Tensor Network (TN) representation. The circuit + is first processed to an intermediate form by grouping each gate matrix + with its corresponding qubit it is acting on to a list. It is then converted to an equivalent TN expression through the class function state_vector_operands() following the Einstein summation convention in the interleave format. @@ -79,9 +79,8 @@ def _parse_gates_to_mode_labels_operands( 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 (qubit_states,input_output) * + qubits_involved.""" return (2, 2) * nqubits def init_intermediate_circuit(self, circuit): @@ -134,8 +133,7 @@ def init_inverse_circuit(self, circuit): self.active_qubits_inverse = np.unique(gates_qubits_inverse) def get_pauli_gates(self, pauli_map, dtype="complex128", backend=cp): - """ - Populate the gates for all pauli operators. + """Populate the gates for all pauli operators. Args: pauli_map: A dictionary mapping qubits to pauli operators. diff --git a/src/qibotn/QiboCircuitToMPS.py b/src/qibotn/QiboCircuitToMPS.py index 816b17c0..b1d847f2 100644 --- a/src/qibotn/QiboCircuitToMPS.py +++ b/src/qibotn/QiboCircuitToMPS.py @@ -1,9 +1,9 @@ import cupy as cp import numpy as np - from cuquantum import cutensornet as cutn + +from qibotn.MPSUtils import apply_gate, initial from qibotn.QiboCircuitConvertor import QiboCircuitToEinsum -from qibotn.MPSUtils import initial, apply_gate class QiboCircuitToMPS: diff --git a/src/qibotn/backends/__init__.py b/src/qibotn/backends/__init__.py index f9279323..e5d68de5 100644 --- a/src/qibotn/backends/__init__.py +++ b/src/qibotn/backends/__init__.py @@ -1,2 +1,2 @@ -from qibotn.backends.gpu import CuTensorNet from qibotn.backends.cpu import QuTensorNet +from qibotn.backends.gpu import CuTensorNet diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py index 7115b395..a85dfd9b 100644 --- a/src/qibotn/backends/cpu.py +++ b/src/qibotn/backends/cpu.py @@ -1,8 +1,6 @@ -import numpy as np - from qibo.backends.numpy import NumpyBackend -from qibo.states import CircuitResult from qibo.config import raise_error +from qibo.states import CircuitResult class QuTensorNet(NumpyBackend): @@ -60,7 +58,6 @@ def execute_circuit( Returns: xxx. - """ import qibotn.eval_qu as eval diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index 5777fe90..2c3f8d41 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -1,8 +1,7 @@ import numpy as np - from qibo.backends.numpy import NumpyBackend -from qibo.states import CircuitResult from qibo.config import raise_error +from qibo.states import CircuitResult class CuTensorNet(NumpyBackend): # pragma: no cover @@ -107,7 +106,6 @@ def execute_circuit( Returns: xxx. - """ import qibotn.eval as eval diff --git a/src/qibotn/eval.py b/src/qibotn/eval.py index 96fd488a..5fcb66f0 100644 --- a/src/qibotn/eval.py +++ b/src/qibotn/eval.py @@ -1,20 +1,22 @@ -from qibotn.QiboCircuitConvertor import QiboCircuitToEinsum -from cuquantum import contract -from cupy.cuda.runtime import getDeviceCount import cupy as cp +from cupy.cuda.runtime import getDeviceCount +from cuquantum import contract -from qibotn.QiboCircuitToMPS import QiboCircuitToMPS from qibotn.mps_contraction_helper import MPSContractionHelper +from qibotn.QiboCircuitConvertor import QiboCircuitToEinsum +from qibotn.QiboCircuitToMPS import QiboCircuitToMPS def dense_vector_tn(qibo_circ, datatype): - """Convert qibo circuit to tensornet (TN) format and perform contraction to dense vector.""" + """Convert qibo circuit to tensornet (TN) format and perform contraction to + dense vector.""" 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.""" + """Convert qibo circuit to tensornet (TN) format and perform contraction to + expectation of given Pauli string.""" myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract( *myconvertor.expectation_operands( @@ -24,14 +26,19 @@ def expectation_pauli_tn(qibo_circ, datatype, pauli_string_pattern): def dense_vector_tn_MPI(qibo_circ, datatype, n_samples=8): - """Convert qibo circuit to tensornet (TN) format and perform contraction using multi node and multi GPU through MPI. - The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. - The pathfinder looks at user defined number of samples (n_samples) iteratively to 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 a dense vector representation of the TN. + """Convert qibo circuit to tensornet (TN) format and perform contraction + using multi node and multi GPU through MPI. + + The conversion is performed by QiboCircuitToEinsum(), after which it + goes through 2 steps: pathfinder and execution. The pathfinder looks + at user defined number of samples (n_samples) iteratively to 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 a dense vector representation of the TN. """ - from mpi4py import MPI from cuquantum import Network + from mpi4py import MPI root = 0 comm = MPI.COMM_WORLD @@ -86,14 +93,19 @@ def dense_vector_tn_MPI(qibo_circ, datatype, n_samples=8): def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8): - """Convert qibo circuit to tensornet (TN) format and perform contraction using multi node and multi GPU through NCCL. - The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. - The pathfinder looks at user defined number of samples (n_samples) iteratively to 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 a dense vector representation of the TN. + """Convert qibo circuit to tensornet (TN) format and perform contraction + using multi node and multi GPU through NCCL. + + The conversion is performed by QiboCircuitToEinsum(), after which it + goes through 2 steps: pathfinder and execution. The pathfinder looks + at user defined number of samples (n_samples) iteratively to 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 a dense vector representation of the TN. """ - from mpi4py import MPI - from cuquantum import Network from cupy.cuda import nccl + from cuquantum import Network + from mpi4py import MPI root = 0 comm_mpi = MPI.COMM_WORLD @@ -159,15 +171,22 @@ def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8): def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string_pattern, n_samples=8): - """Convert qibo circuit to tensornet (TN) format and perform contraction to expectation of given Pauli string using multi node and multi GPU through NCCL. - The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. - The pauli_string_pattern is used to generate the pauli string corresponding to the number of qubits of the system. - The pathfinder looks at user defined number of samples (n_samples) iteratively to 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. + """Convert qibo circuit to tensornet (TN) format and perform contraction to + expectation of given Pauli string using multi node and multi GPU through + NCCL. + + The conversion is performed by QiboCircuitToEinsum(), after which it + goes through 2 steps: pathfinder and execution. The + pauli_string_pattern is used to generate the pauli string + corresponding to the number of qubits of the system. The pathfinder + looks at user defined number of samples (n_samples) iteratively to + 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. """ - from mpi4py import MPI - from cuquantum import Network from cupy.cuda import nccl + from cuquantum import Network + from mpi4py import MPI root = 0 comm_mpi = MPI.COMM_WORLD @@ -235,14 +254,21 @@ def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string_pattern, n_sampl def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string_pattern, n_samples=8): - """Convert qibo circuit to tensornet (TN) format and perform contraction to expectation of given Pauli string using multi node and multi GPU through MPI. - The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. - The pauli_string_pattern is used to generate the pauli string corresponding to the number of qubits of the system. - The pathfinder looks at user defined number of samples (n_samples) iteratively to 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. + """Convert qibo circuit to tensornet (TN) format and perform contraction to + expectation of given Pauli string using multi node and multi GPU through + MPI. + + The conversion is performed by QiboCircuitToEinsum(), after which it + goes through 2 steps: pathfinder and execution. The + pauli_string_pattern is used to generate the pauli string + corresponding to the number of qubits of the system. The pathfinder + looks at user defined number of samples (n_samples) iteratively to + 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. """ - from mpi4py import MPI # this line initializes MPI from cuquantum import Network + from mpi4py import MPI # this line initializes MPI root = 0 comm = MPI.COMM_WORLD @@ -299,7 +325,8 @@ 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.""" + """Convert qibo circuit to matrix product state (MPS) format and perform + contraction to dense vector.""" myconvertor = QiboCircuitToMPS(qibo_circ, gate_algo, dtype=datatype) mps_helper = MPSContractionHelper(myconvertor.num_qubits) @@ -309,7 +336,9 @@ def dense_vector_mps(qibo_circ, gate_algo, datatype): def pauli_string_gen(nqubits, pauli_string_pattern): - """Used internally to generate the string based on given pattern and number of qubit. + """Used internally to generate the string based on given pattern and number + of qubit. + Example: pattern: "XZ", number of qubit: 7, output = XZXZXZX """ if nqubits <= 0: diff --git a/src/qibotn/eval_qu.py b/src/qibotn/eval_qu.py index 579a42ae..7b603b50 100644 --- a/src/qibotn/eval_qu.py +++ b/src/qibotn/eval_qu.py @@ -3,9 +3,15 @@ from qibo.models import Circuit as QiboCircuit -def from_qibo(circuit: QiboCircuit, is_mps: False, psi0=None, method='svd', - cutoff=1e-6, cutoff_mode='abs'): - """Create a tensornetwork representation of the circuit""" +def from_qibo( + circuit: QiboCircuit, + is_mps: False, + psi0=None, + method="svd", + cutoff=1e-6, + cutoff_mode="abs", +): + """Create a tensornetwork representation of the circuit.""" nqubits = circuit.nqubits gate_opt = {} @@ -30,19 +36,17 @@ def from_qibo(circuit: QiboCircuit, is_mps: False, psi0=None, method='svd', def init_state_tn(nqubits, init_state_sv): - - """Create a matrixproductstate directly from a dense vector""" + """Create a matrixproductstate directly from a dense vector.""" dims = tuple(2 * np.ones(nqubits, dtype=int)) return qtn.tensor_1d.MatrixProductState.from_dense(init_state_sv, dims) -def dense_vector_tn_qu(qasm: str, initial_state, is_mps, backend="numpy"): - """Evaluate QASM with Quimb +def dense_vector_tn_qu(qasm: str, initial_state, is_mps, backend="numpy"): + """Evaluate QASM with Quimb. backend (quimb): numpy, cupy, jax. Passed to ``opt_einsum``. - """ circuit = QiboCircuit.from_qasm(qasm) if initial_state is not None: diff --git a/src/qibotn/mps_contraction_helper.py b/src/qibotn/mps_contraction_helper.py index 29d5e253..1c004de4 100644 --- a/src/qibotn/mps_contraction_helper.py +++ b/src/qibotn/mps_contraction_helper.py @@ -1,11 +1,10 @@ -from cuquantum import contract, contract_path, CircuitToEinsum, tensor +from cuquantum import contract, contract_path # Reference: https://github.com/NVIDIA/cuQuantum/blob/main/python/samples/cutensornet/tn_algorithms/mps_algorithms.ipynb class MPSContractionHelper: - """ - A helper class to compute various quantities for a given MPS. + """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: @@ -43,8 +42,8 @@ def __init__(self, num_qubits): ] def contract_norm(self, mps_tensors, options=None): - """ - Contract the corresponding tensor network to form the norm of the MPS. + """Contract the corresponding tensor network to form the norm of the + MPS. Args: mps_tensors: A list of rank-3 ndarray-like tensor objects. @@ -64,8 +63,8 @@ def contract_norm(self, mps_tensors, options=None): return self._contract(interleaved_inputs, options=options).real def contract_state_vector(self, mps_tensors, options=None): - """ - Contract the corresponding tensor network to form the state vector representation of the MPS. + """Contract the corresponding tensor network to form the state vector + representation of the MPS. Args: mps_tensors: A list of rank-3 ndarray-like tensor objects. @@ -86,8 +85,8 @@ def contract_state_vector(self, mps_tensors, options=None): def contract_expectation( self, mps_tensors, operator, qubits, options=None, normalize=False ): - """ - Contract the corresponding tensor network to form the expectation of the MPS. + """Contract the corresponding tensor network to form the expectation of + the MPS. Args: mps_tensors: A list of rank-3 ndarray-like tensor objects. diff --git a/tests/test_cuquantum_cutensor_backend.py b/tests/test_cuquantum_cutensor_backend.py index 57b3f342..c8f1e199 100644 --- a/tests/test_cuquantum_cutensor_backend.py +++ b/tests/test_cuquantum_cutensor_backend.py @@ -1,8 +1,8 @@ from timeit import default_timer as timer import config -import numpy as np import cupy as cp +import numpy as np import pytest import qibo from qibo.models import QFT diff --git a/tests/test_quimb_backend.py b/tests/test_quimb_backend.py index 81a0e2b7..15ba6524 100644 --- a/tests/test_quimb_backend.py +++ b/tests/test_quimb_backend.py @@ -1,5 +1,6 @@ import copy import os + import config import numpy as np import pytest @@ -8,8 +9,7 @@ def create_init_state(nqubits): - init_state = np.random.random(2**nqubits) + \ - 1j * np.random.random(2**nqubits) + init_state = np.random.random(2**nqubits) + 1j * np.random.random(2**nqubits) init_state = init_state / np.sqrt((np.abs(init_state) ** 2).sum()) return init_state @@ -20,10 +20,11 @@ def qibo_qft(nqubits, init_state, swaps): return circ_qibo, state_vec -@pytest.mark.parametrize("nqubits, tolerance, is_mps", - [(1, 1e-6, True), (2, 1e-6, False), (5, 1e-3, True), (10, 1e-3, False)]) +@pytest.mark.parametrize( + "nqubits, tolerance, is_mps", + [(1, 1e-6, True), (2, 1e-6, False), (5, 1e-3, True), (10, 1e-3, False)], +) def test_eval(nqubits: int, tolerance: float, is_mps: bool): - """Evaluate circuit with Quimb backend. Args: @@ -41,20 +42,18 @@ def test_eval(nqubits: int, tolerance: float, is_mps: bool): init_state_tn = copy.deepcopy(init_state) # Test qibo - qibo.set_backend(backend=config.qibo.backend, - platform=config.qibo.platform) - - qibo_circ, result_sv= qibo_qft(nqubits, init_state, swaps=True) - + qibo.set_backend(backend=config.qibo.backend, platform=config.qibo.platform) + + qibo_circ, result_sv = qibo_qft(nqubits, init_state, swaps=True) # Convert to qasm for other backends qasm_circ = qibo_circ.to_qasm() # Test quimb result_tn = qibotn.eval_qu.dense_vector_tn_qu( - qasm_circ, init_state_tn, is_mps, backend=config.quimb.backend - ).flatten() - + qasm_circ, init_state_tn, is_mps, backend=config.quimb.backend + ).flatten() - assert np.allclose(result_sv, result_tn, - atol=tolerance), "Resulting dense vectors do not match" + assert np.allclose( + result_sv, result_tn, atol=tolerance + ), "Resulting dense vectors do not match" From 890b9d1c93fc981efc2d2fee998b3f8ca2ef071f Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 8 Feb 2024 10:30:22 +0100 Subject: [PATCH 070/199] chore: Enforce naming conventions on module files --- ...{QiboCircuitConvertor.py => circuit_convertor.py} | 12 ++++++------ .../{QiboCircuitToMPS.py => circuit_to_mps.py} | 4 ++-- src/qibotn/eval.py | 4 ++-- src/qibotn/{MPSUtils.py => mps_utils.py} | 0 4 files changed, 10 insertions(+), 10 deletions(-) rename src/qibotn/{QiboCircuitConvertor.py => circuit_convertor.py} (96%) rename src/qibotn/{QiboCircuitToMPS.py => circuit_to_mps.py} (89%) rename src/qibotn/{MPSUtils.py => mps_utils.py} (100%) diff --git a/src/qibotn/QiboCircuitConvertor.py b/src/qibotn/circuit_convertor.py similarity index 96% rename from src/qibotn/QiboCircuitConvertor.py rename to src/qibotn/circuit_convertor.py index f67fb8ea..14af79de 100644 --- a/src/qibotn/QiboCircuitConvertor.py +++ b/src/qibotn/circuit_convertor.py @@ -5,12 +5,12 @@ class QiboCircuitToEinsum: - """Convert a circuit to a Tensor Network (TN) representation. The circuit - is first processed to an intermediate form by grouping each gate matrix - with its corresponding qubit it is acting on to a list. It is then - converted to an equivalent TN expression through the class function - state_vector_operands() following the Einstein summation convention in the - interleave format. + """Convert a circuit to a Tensor Network (TN) representation. + + The circuit is first processed to an intermediate form by grouping each gate matrix + with its corresponding qubit it is acting on to a list. It is then converted to an + equivalent TN expression through the class function state_vector_operands() + following the Einstein summation convention in the interleave format. See document for detail of the format: https://docs.nvidia.com/cuda/cuquantum/python/api/generated/cuquantum.contract.html diff --git a/src/qibotn/QiboCircuitToMPS.py b/src/qibotn/circuit_to_mps.py similarity index 89% rename from src/qibotn/QiboCircuitToMPS.py rename to src/qibotn/circuit_to_mps.py index b1d847f2..af8acd5a 100644 --- a/src/qibotn/QiboCircuitToMPS.py +++ b/src/qibotn/circuit_to_mps.py @@ -2,8 +2,8 @@ import numpy as np from cuquantum import cutensornet as cutn -from qibotn.MPSUtils import apply_gate, initial -from qibotn.QiboCircuitConvertor import QiboCircuitToEinsum +from qibotn.circuit_convertor import QiboCircuitToEinsum +from qibotn.mps_utils import apply_gate, initial class QiboCircuitToMPS: diff --git a/src/qibotn/eval.py b/src/qibotn/eval.py index 5fcb66f0..6375aa6a 100644 --- a/src/qibotn/eval.py +++ b/src/qibotn/eval.py @@ -2,9 +2,9 @@ from cupy.cuda.runtime import getDeviceCount from cuquantum import contract +from qibotn.circuit_convertor import QiboCircuitToEinsum +from qibotn.circuit_to_mps import QiboCircuitToMPS from qibotn.mps_contraction_helper import MPSContractionHelper -from qibotn.QiboCircuitConvertor import QiboCircuitToEinsum -from qibotn.QiboCircuitToMPS import QiboCircuitToMPS def dense_vector_tn(qibo_circ, datatype): diff --git a/src/qibotn/MPSUtils.py b/src/qibotn/mps_utils.py similarity index 100% rename from src/qibotn/MPSUtils.py rename to src/qibotn/mps_utils.py From e58d176de47d1de11083927298f0189d6266e23c Mon Sep 17 00:00:00 2001 From: Vinitha-balachandran <127284874+Vinitha-balachandran@users.noreply.github.com> Date: Thu, 8 Feb 2024 17:56:44 +0800 Subject: [PATCH 071/199] Update src/qibotn/backends/cpu.py Co-authored-by: Alessandro Candido --- src/qibotn/backends/cpu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py index a85dfd9b..542599c4 100644 --- a/src/qibotn/backends/cpu.py +++ b/src/qibotn/backends/cpu.py @@ -3,7 +3,7 @@ from qibo.states import CircuitResult -class QuTensorNet(NumpyBackend): +class QuimbBackend(NumpyBackend): def __init__(self, runcard): super().__init__() From a2e32c887389f17024947469705135d4d20594ea Mon Sep 17 00:00:00 2001 From: Vinitha-balachandran <127284874+Vinitha-balachandran@users.noreply.github.com> Date: Thu, 8 Feb 2024 17:58:53 +0800 Subject: [PATCH 072/199] Update src/qibotn/backends/cpu.py Co-authored-by: Alessandro Candido --- src/qibotn/backends/cpu.py | 72 ++++---------------------------------- 1 file changed, 6 insertions(+), 66 deletions(-) diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py index 542599c4..cb03cb81 100644 --- a/src/qibotn/backends/cpu.py +++ b/src/qibotn/backends/cpu.py @@ -62,80 +62,20 @@ def execute_circuit( import qibotn.eval_qu as eval - if ( - self.MPI_enabled == False - and self.MPS_enabled == False - and self.NCCL_enabled == False - and self.expectation_enabled == False - ): - - state = eval.dense_vector_tn_qu( - circuit.to_qasm(), initial_state, is_mps=False, backend="numpy" - ) - - elif ( - self.MPI_enabled == False - and self.MPS_enabled == True - and self.NCCL_enabled == False - and self.expectation_enabled == False - ): - - state = eval.dense_vector_tn_qu( - circuit.to_qasm(), initial_state, is_mps=True, backend="numpy" - ) - - elif ( - self.MPI_enabled == True - and self.MPS_enabled == False - and self.NCCL_enabled == False - and self.expectation_enabled == False - ): - + if self.MPI_enabled == True: raise_error(NotImplementedError, "QiboTN quimb backend cannot support MPI.") - - elif ( - self.MPI_enabled == False - and self.MPS_enabled == False - and self.NCCL_enabled == True - and self.expectation_enabled == False - ): - + if self.NCCL_enabled == True: raise_error( NotImplementedError, "QiboTN quimb backend cannot support NCCL." ) - - elif ( - self.MPI_enabled == False - and self.MPS_enabled == False - and self.NCCL_enabled == False - and self.expectation_enabled == True - ): - - raise_error( - NotImplementedError, "QiboTN quimb backend cannot support expectation" - ) - - elif ( - self.MPI_enabled == True - and self.MPS_enabled == False - and self.NCCL_enabled == False - and self.expectation_enabled == True - ): + if self.expectation_enabled == True: raise_error( NotImplementedError, "QiboTN quimb backend cannot support expectation" ) - elif ( - self.MPI_enabled == False - and self.MPS_enabled == False - and self.NCCL_enabled == True - and self.expectation_enabled == True - ): - raise_error( - NotImplementedError, "QiboTN quimb backend cannot support expectation" - ) - else: - raise_error(NotImplementedError, "Compute type not supported.") + state = eval.dense_vector_tn_qu( + circuit.to_qasm(), initial_state, is_mps=self.MPS_enabled, backend="numpy" + ) if return_array: return state.flatten() From 6f7df9d924b56821b72539757665bdc725f8991b Mon Sep 17 00:00:00 2001 From: Vinitha-balachandran <127284874+Vinitha-balachandran@users.noreply.github.com> Date: Thu, 8 Feb 2024 17:59:56 +0800 Subject: [PATCH 073/199] Update src/qibotn/eval_qu.py made changes Co-authored-by: Alessandro Candido --- src/qibotn/eval_qu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/eval_qu.py b/src/qibotn/eval_qu.py index 7b603b50..024db887 100644 --- a/src/qibotn/eval_qu.py +++ b/src/qibotn/eval_qu.py @@ -11,7 +11,7 @@ def from_qibo( cutoff=1e-6, cutoff_mode="abs", ): - """Create a tensornetwork representation of the circuit.""" + """Create a tensor network representation of the circuit.""" nqubits = circuit.nqubits gate_opt = {} From 9ea895bcfdffc4b6bf6c5d25ea8126184055ab83 Mon Sep 17 00:00:00 2001 From: Vinitha-balachandran <127284874+Vinitha-balachandran@users.noreply.github.com> Date: Thu, 8 Feb 2024 18:00:23 +0800 Subject: [PATCH 074/199] Update src/qibotn/eval_qu.py updated the changes Co-authored-by: Alessandro Candido --- src/qibotn/eval_qu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/eval_qu.py b/src/qibotn/eval_qu.py index 024db887..74fca1cd 100644 --- a/src/qibotn/eval_qu.py +++ b/src/qibotn/eval_qu.py @@ -36,7 +36,7 @@ def from_qibo( def init_state_tn(nqubits, init_state_sv): - """Create a matrixproductstate directly from a dense vector.""" + """Create a matrix product state directly from a dense vector.""" dims = tuple(2 * np.ones(nqubits, dtype=int)) From 07b4a799be9a5b6daae124687ad171333b9b0ce8 Mon Sep 17 00:00:00 2001 From: yangliwei Date: Thu, 8 Feb 2024 18:13:13 +0800 Subject: [PATCH 075/199] Use CUDA_PATH as the if conditional for running workflow jobs --- .github/workflows/rules.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rules.yml b/.github/workflows/rules.yml index d1c06323..ed8b4613 100644 --- a/.github/workflows/rules.yml +++ b/.github/workflows/rules.yml @@ -1,6 +1,9 @@ # A single CI script with github workflow name: Tests +env: + CUDA_PATH: echo $CUDA_PATH + on: push: pull_request: @@ -8,7 +11,7 @@ on: jobs: build: - if: contains(github.event.pull_request.labels.*.name, 'run-workflow') || github.event_name == 'push' && {{ $CUDA_PATH != '' }} + if: contains(github.event.pull_request.labels.*.name, 'run-workflow') || github.event_name == 'push' && github.env.CUDA_PATH != '' strategy: matrix: os: [ubuntu-latest] From b232c94fbdc46a90a927c282412de320dc3ea46a Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 8 Feb 2024 11:18:22 +0100 Subject: [PATCH 076/199] fix: Fix indentation wrong level from review suggestion --- src/qibotn/backends/cpu.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py index cb03cb81..ce4a3c7d 100644 --- a/src/qibotn/backends/cpu.py +++ b/src/qibotn/backends/cpu.py @@ -4,7 +4,6 @@ class QuimbBackend(NumpyBackend): - def __init__(self, runcard): super().__init__() import quimb # pylint: disable=import-error @@ -73,9 +72,9 @@ def execute_circuit( NotImplementedError, "QiboTN quimb backend cannot support expectation" ) - state = eval.dense_vector_tn_qu( - circuit.to_qasm(), initial_state, is_mps=self.MPS_enabled, backend="numpy" - ) + state = eval.dense_vector_tn_qu( + circuit.to_qasm(), initial_state, is_mps=self.MPS_enabled, backend="numpy" + ) if return_array: return state.flatten() From 7199dd6b0e45c30821eff13134a20d96eab5236d Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 18:25:28 +0800 Subject: [PATCH 077/199] renamed cpu to quimb and qutensornet to quimbbackend --- src/qibotn/backends/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/backends/__init__.py b/src/qibotn/backends/__init__.py index e5d68de5..36c58fcd 100644 --- a/src/qibotn/backends/__init__.py +++ b/src/qibotn/backends/__init__.py @@ -1,2 +1,2 @@ -from qibotn.backends.cpu import QuTensorNet +from qibotn.backends.quimb import QuimbBackend from qibotn.backends.gpu import CuTensorNet From f86627451a5585cc1a8597a4479d170321231df1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 10:26:30 +0000 Subject: [PATCH 078/199] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qibotn/backends/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/backends/__init__.py b/src/qibotn/backends/__init__.py index 36c58fcd..adab04ed 100644 --- a/src/qibotn/backends/__init__.py +++ b/src/qibotn/backends/__init__.py @@ -1,2 +1,2 @@ -from qibotn.backends.quimb import QuimbBackend from qibotn.backends.gpu import CuTensorNet +from qibotn.backends.quimb import QuimbBackend From b3a9abdcbde73f9aa9f85247cd882ea5dce609ce Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 18:28:07 +0800 Subject: [PATCH 079/199] renaming cpu to quimb --- src/qibotn/backends/quimb.py | 84 ++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/qibotn/backends/quimb.py diff --git a/src/qibotn/backends/quimb.py b/src/qibotn/backends/quimb.py new file mode 100644 index 00000000..f8380d09 --- /dev/null +++ b/src/qibotn/backends/quimb.py @@ -0,0 +1,84 @@ +from qibo.backends.numpy import NumpyBackend +from qibo.config import raise_error +from qibo.states import CircuitResult + + +class QuimbBackend(NumpyBackend): + + def __init__(self, runcard): + super().__init__() + import quimb # pylint: disable=import-error + + if runcard is not None: + self.MPI_enabled = runcard.get("MPI_enabled", False) + self.NCCL_enabled = runcard.get("NCCL_enabled", False) + self.expectation_enabled = runcard.get("expectation_enabled", False) + + mps_enabled_value = runcard.get("MPS_enabled") + if mps_enabled_value is True: + self.MPS_enabled = True + elif mps_enabled_value is False: + self.MPS_enabled = False + else: + raise TypeError("MPS_enabled has an unexpected type") + + else: + self.MPI_enabled = False + self.MPS_enabled = False + self.NCCL_enabled = False + self.expectation_enabled = False + + self.name = "qibotn" + self.quimb = quimb + self.platform = "QuimbBackend" + self.versions["quimb"] = self.quimb.__version__ + + def apply_gate(self, gate, state, nqubits): # pragma: no cover + raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") + + def apply_gate_density_matrix(self, gate, state, nqubits): # pragma: no cover + raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") + + def assign_measurements(self, measurement_map, circuit_result): + raise_error(NotImplementedError, "Not implemented in QiboTN.") + + def set_precision(self, precision): + if precision != self.precision: + super().set_precision(precision) + + def execute_circuit( + self, circuit, initial_state=None, nshots=None, return_array=False + ): # pragma: no cover + """Executes a quantum circuit. + + Args: + 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. + """ + + import qibotn.eval_qu as eval + + if self.MPI_enabled == True: + raise_error(NotImplementedError, "QiboTN quimb backend cannot support MPI.") + if self.NCCL_enabled == True: + raise_error( + NotImplementedError, "QiboTN quimb backend cannot support NCCL." + ) + if self.expectation_enabled == True: + raise_error( + NotImplementedError, "QiboTN quimb backend cannot support expectation" + ) + + state = eval.dense_vector_tn_qu( + circuit.to_qasm(), initial_state, is_mps=self.MPS_enabled, backend="numpy" + ) + + if return_array: + return state.flatten() + else: + circuit._final_state = CircuitResult(self, circuit, state.flatten(), nshots) + return circuit._final_state From fa0219e59f9b436307e9a1a81f502f9d8d1f7da2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 10:30:30 +0000 Subject: [PATCH 080/199] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qibotn/backends/quimb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/backends/quimb.py b/src/qibotn/backends/quimb.py index f8380d09..54dae861 100644 --- a/src/qibotn/backends/quimb.py +++ b/src/qibotn/backends/quimb.py @@ -74,7 +74,7 @@ def execute_circuit( ) state = eval.dense_vector_tn_qu( - circuit.to_qasm(), initial_state, is_mps=self.MPS_enabled, backend="numpy" + circuit.to_qasm(), initial_state, is_mps=self.MPS_enabled, backend="numpy" ) if return_array: From 9fc2c74a88030e3b440bdcbb7b624adab194f293 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 18:31:32 +0800 Subject: [PATCH 081/199] removing cpu.py as quimb.py is added --- src/qibotn/backends/cpu.py | 83 -------------------------------------- 1 file changed, 83 deletions(-) delete mode 100644 src/qibotn/backends/cpu.py diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py deleted file mode 100644 index ce4a3c7d..00000000 --- a/src/qibotn/backends/cpu.py +++ /dev/null @@ -1,83 +0,0 @@ -from qibo.backends.numpy import NumpyBackend -from qibo.config import raise_error -from qibo.states import CircuitResult - - -class QuimbBackend(NumpyBackend): - def __init__(self, runcard): - super().__init__() - import quimb # pylint: disable=import-error - - if runcard is not None: - self.MPI_enabled = runcard.get("MPI_enabled", False) - self.NCCL_enabled = runcard.get("NCCL_enabled", False) - self.expectation_enabled = runcard.get("expectation_enabled", False) - - mps_enabled_value = runcard.get("MPS_enabled") - if mps_enabled_value is True: - self.MPS_enabled = True - elif mps_enabled_value is False: - self.MPS_enabled = False - else: - raise TypeError("MPS_enabled has an unexpected type") - - else: - self.MPI_enabled = False - self.MPS_enabled = False - self.NCCL_enabled = False - self.expectation_enabled = False - - self.name = "qibotn" - self.quimb = quimb - self.platform = "qutensornet" - self.versions["quimb"] = self.quimb.__version__ - - def apply_gate(self, gate, state, nqubits): # pragma: no cover - raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") - - def apply_gate_density_matrix(self, gate, state, nqubits): # pragma: no cover - raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") - - def assign_measurements(self, measurement_map, circuit_result): - raise_error(NotImplementedError, "Not implemented in QiboTN.") - - def set_precision(self, precision): - if precision != self.precision: - super().set_precision(precision) - - def execute_circuit( - self, circuit, initial_state=None, nshots=None, return_array=False - ): # pragma: no cover - """Executes a quantum circuit. - - Args: - 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. - """ - - import qibotn.eval_qu as eval - - if self.MPI_enabled == True: - raise_error(NotImplementedError, "QiboTN quimb backend cannot support MPI.") - if self.NCCL_enabled == True: - raise_error( - NotImplementedError, "QiboTN quimb backend cannot support NCCL." - ) - if self.expectation_enabled == True: - raise_error( - NotImplementedError, "QiboTN quimb backend cannot support expectation" - ) - - state = eval.dense_vector_tn_qu( - circuit.to_qasm(), initial_state, is_mps=self.MPS_enabled, backend="numpy" - ) - - if return_array: - return state.flatten() - else: - circuit._final_state = CircuitResult(self, circuit, state.flatten(), nshots) - return circuit._final_state From 38764f7833cebbab2a97882e86317f14f19e7f75 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Fri, 9 Feb 2024 10:29:30 +0800 Subject: [PATCH 082/199] Take out repeat codes --- src/qibotn/backends/gpu.py | 32 +++----------------------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index 2c3f8d41..073ce590 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -110,96 +110,70 @@ def execute_circuit( import qibotn.eval as eval + if initial_state is not None: + raise_error(NotImplementedError, "QiboTN cannot support initial state.") + if ( self.MPI_enabled == False and self.MPS_enabled == False and self.NCCL_enabled == False and self.expectation_enabled == False ): - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state = eval.dense_vector_tn(circuit, self.dtype) - elif ( self.MPI_enabled == False and self.MPS_enabled == True and self.NCCL_enabled == False and self.expectation_enabled == False ): - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state = eval.dense_vector_mps(circuit, self.gate_algo, self.dtype) - elif ( self.MPI_enabled == True and self.MPS_enabled == False and self.NCCL_enabled == False and self.expectation_enabled == False ): - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.dense_vector_tn_MPI(circuit, self.dtype, 32) if rank > 0: state = np.array(0) - elif ( self.MPI_enabled == False and self.MPS_enabled == False and self.NCCL_enabled == True and self.expectation_enabled == False ): - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.dense_vector_tn_nccl(circuit, self.dtype, 32) if rank > 0: state = np.array(0) - elif ( self.MPI_enabled == False and self.MPS_enabled == False and self.NCCL_enabled == False and self.expectation_enabled == True ): - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state = eval.expectation_pauli_tn( circuit, self.dtype, self.pauli_string_pattern ) - elif ( self.MPI_enabled == True and self.MPS_enabled == False and self.NCCL_enabled == False and self.expectation_enabled == True ): - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.expectation_pauli_tn_MPI( circuit, self.dtype, self.pauli_string_pattern, 32 ) - if rank > 0: state = np.array(0) - elif ( self.MPI_enabled == False and self.MPS_enabled == False and self.NCCL_enabled == True and self.expectation_enabled == True ): - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.expectation_pauli_tn_nccl( circuit, self.dtype, self.pauli_string_pattern, 32 ) - if rank > 0: state = np.array(0) else: From 90b2cd6919150b986ee718a7658d95aa926c13d5 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Fri, 9 Feb 2024 10:33:00 +0800 Subject: [PATCH 083/199] Update filename --- src/qibotn/backends/{gpu.py => cutensornet.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/qibotn/backends/{gpu.py => cutensornet.py} (100%) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/cutensornet.py similarity index 100% rename from src/qibotn/backends/gpu.py rename to src/qibotn/backends/cutensornet.py From a73b9e9a9975609a85a71f71df5012e598b6d21e Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Fri, 9 Feb 2024 10:41:12 +0800 Subject: [PATCH 084/199] updated set_backend for quimb --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d22503ca..b15aab3b 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ computation_settings = { qibo.set_backend( backend="qibotn", platform="cutensornet", runcard=computation_settings ) # cuQuantum -# qibo.set_backend(backend="qibotn", platform="qutensornet", runcard=computation_settings) #quimb +# qibo.set_backend(backend="qibotn", platform="QuimbBackend", runcard=computation_settings) #quimb # Construct the circuit From 9b871794c987fa63c20f8f471d35266e365c2dc9 Mon Sep 17 00:00:00 2001 From: yangliwei Date: Wed, 14 Feb 2024 14:50:27 +0800 Subject: [PATCH 085/199] Minor fix to the use of env for CUDA_PATH in the workflow --- .github/workflows/rules.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rules.yml b/.github/workflows/rules.yml index ed8b4613..6415be0e 100644 --- a/.github/workflows/rules.yml +++ b/.github/workflows/rules.yml @@ -2,7 +2,7 @@ name: Tests env: - CUDA_PATH: echo $CUDA_PATH + CUDA_PATH: on: push: @@ -11,7 +11,7 @@ on: jobs: build: - if: contains(github.event.pull_request.labels.*.name, 'run-workflow') || github.event_name == 'push' && github.env.CUDA_PATH != '' + if: ${{ contains(github.event.pull_request.labels.*.name, 'run-workflow') || github.event_name == 'push' }} && env.CUDA_PATH != '' strategy: matrix: os: [ubuntu-latest] From 18cda1253a6d666b421ddd748844746b79e32041 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Fri, 3 Nov 2023 16:48:14 +0800 Subject: [PATCH 086/199] add backend file --- src/qibotn/backends.py | 69 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/qibotn/backends.py diff --git a/src/qibotn/backends.py b/src/qibotn/backends.py new file mode 100644 index 00000000..98294ec2 --- /dev/null +++ b/src/qibotn/backends.py @@ -0,0 +1,69 @@ +from qibo.backends import NumpyBackend +from qibo.config import raise_error +from qibotn import cutn +from qibotn import quimb +from qibo.states import CircuitResult + + +class QiboTNBackend(NumpyBackend): + def __init__(self, platform): + super().__init__() + self.name = "qibotn" + if ( + platform == "cu_tensornet" + or platform == "cu_mps" + or platform == "qu_tensornet" + ): # pragma: no cover + self.platform = platform + else: + raise_error( + NotImplementedError, "QiboTN cannot support the specified backend." + ) + + def apply_gate(self, gate, state, nqubits): # pragma: no cover + raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") + + def apply_gate_density_matrix(self, gate, state, nqubits): # pragma: no cover + raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") + + def assign_measurements(self, measurement_map, circuit_result): + raise_error(NotImplementedError, "Not implemented in QiboTN.") + + def execute_circuit( + self, circuit, initial_state=None, nshots=None, return_array=False + ): # pragma: no cover + """Executes a quantum circuit. + + Args: + 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. + + """ + if initial_state is not None: + raise_error(NotImplementedError, "QiboTN cannot support initial state.") + + if self.platform == "cu_tensornet": + state = cutn.eval(circuit, self.dtype) + + if self.platform == "cu_mps": + gate_algo = { + "qr_method": False, + "svd_method": { + "partition": "UV", + "abs_cutoff": 1e-12, + }, + } # make this user input + state = cutn.eval_mps(circuit, gate_algo, self.dtype) + + if self.platform == "qu_tensornet": + state = quimb.eval(circuit.to_qasm(), initial_state, backend="numpy") + + if return_array: + return state.flatten() + else: + circuit._final_state = CircuitResult(self, circuit, state.flatten(), nshots) + return circuit._final_state From f6f9e105a25fef2486860aca3f5cc7dc506c8451 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Fri, 3 Nov 2023 17:08:03 +0800 Subject: [PATCH 087/199] Added check for initial conditions --- src/qibotn/backends.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/qibotn/backends.py b/src/qibotn/backends.py index 98294ec2..f1d0bafa 100644 --- a/src/qibotn/backends.py +++ b/src/qibotn/backends.py @@ -43,13 +43,17 @@ def execute_circuit( xxx. """ - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") if self.platform == "cu_tensornet": + if initial_state is not None: + raise_error(NotImplementedError, "QiboTN cannot support initial state.") + state = cutn.eval(circuit, self.dtype) if self.platform == "cu_mps": + if initial_state is not None: + raise_error(NotImplementedError, "QiboTN cannot support initial state.") + gate_algo = { "qr_method": False, "svd_method": { From 8e1862eba5f73be96e40ae88f3fda5b6f712608f Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 7 Nov 2023 17:08:57 +0800 Subject: [PATCH 088/199] Added initial state for Quimb --- src/qibotn/backends.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/qibotn/backends.py b/src/qibotn/backends.py index f1d0bafa..3193e6bc 100644 --- a/src/qibotn/backends.py +++ b/src/qibotn/backends.py @@ -3,6 +3,7 @@ from qibotn import cutn from qibotn import quimb from qibo.states import CircuitResult +import numpy as np class QiboTNBackend(NumpyBackend): @@ -30,7 +31,7 @@ def assign_measurements(self, measurement_map, circuit_result): raise_error(NotImplementedError, "Not implemented in QiboTN.") def execute_circuit( - self, circuit, initial_state=None, nshots=None, return_array=False + self, circuit, initial_state=None, nshots=None, return_array=True ): # pragma: no cover """Executes a quantum circuit. @@ -64,7 +65,12 @@ def execute_circuit( state = cutn.eval_mps(circuit, gate_algo, self.dtype) if self.platform == "qu_tensornet": - state = quimb.eval(circuit.to_qasm(), initial_state, backend="numpy") + + #init_state = np.random.random(2**circuit.nqubits) + 1j * np.random.random(2**circuit.nqubits) + #init_state = init_state / np.sqrt((np.abs(init_state) ** 2).sum()) + init_state = np.zeros(2**circuit.nqubits, dtype=self.dtype) + init_state[0] = 1.0 + state = quimb.eval(circuit.to_qasm(), init_state, backend="numpy") if return_array: return state.flatten() From c0ebe8803f68e5fb85500bd416e466d0564ed04f Mon Sep 17 00:00:00 2001 From: tankya2 Date: Thu, 9 Nov 2023 00:18:27 +0800 Subject: [PATCH 089/199] Reverted back return_array to False --- src/qibotn/backends.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/backends.py b/src/qibotn/backends.py index 3193e6bc..9d399731 100644 --- a/src/qibotn/backends.py +++ b/src/qibotn/backends.py @@ -31,7 +31,7 @@ def assign_measurements(self, measurement_map, circuit_result): raise_error(NotImplementedError, "Not implemented in QiboTN.") def execute_circuit( - self, circuit, initial_state=None, nshots=None, return_array=True + self, circuit, initial_state=None, nshots=None, return_array=False ): # pragma: no cover """Executes a quantum circuit. From fe36a84e7461fa50135eb51003d19b506e89b1ec Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 24 Jan 2024 11:40:51 +0800 Subject: [PATCH 090/199] Updates to include expectation calculation --- src/qibotn/QiboCircuitConvertor.py | 113 ++++++++ src/qibotn/QiboCircuitToMPS.py | 2 +- src/qibotn/backends.py | 53 ++++ src/qibotn/cutn.py | 406 ++++++++++++++++++++++++++++- 4 files changed, 567 insertions(+), 7 deletions(-) diff --git a/src/qibotn/QiboCircuitConvertor.py b/src/qibotn/QiboCircuitConvertor.py index 8ebfe5fe..88ff144d 100644 --- a/src/qibotn/QiboCircuitConvertor.py +++ b/src/qibotn/QiboCircuitConvertor.py @@ -21,6 +21,7 @@ def __init__(self, circuit, dtype="complex128"): self.dtype = getattr(self.backend, dtype) self.init_basis_map(self.backend, dtype) self.init_intermediate_circuit(circuit) + self.circuit = circuit def state_vector_operands(self): input_bitstring = "0" * len(self.active_qubits) @@ -108,3 +109,115 @@ def init_basis_map(self, backend, dtype): state_1 = asarray([0, 1], dtype=dtype) self.basis_map = {"0": state_0, "1": state_1} + + + def init_inverse_circuit(self, circuit): + self.gate_tensors_inverse = [] + gates_qubits_inverse = [] + + for gate in circuit.queue: + gate_qubits = gate.control_qubits + gate.target_qubits + gates_qubits_inverse.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_inverse.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_inverse = np.unique(gates_qubits_inverse) + + + def get_pauli_gates(self, pauli_map, dtype='complex128', backend=cp): + """ + Populate the gates for all pauli operators. + + Args: + pauli_map: A dictionary mapping qubits to pauli operators. + dtype: Data type for the tensor operands. + backend: The package the tensor operands belong to. + + Returns: + A sequence of pauli gates. + """ + asarray = backend.asarray + pauli_i = asarray([[1,0], [0,1]], dtype=dtype) + pauli_x = asarray([[0,1], [1,0]], dtype=dtype) + pauli_y = asarray([[0,-1j], [1j,0]], dtype=dtype) + pauli_z = asarray([[1,0], [0,-1]], dtype=dtype) + + operand_map = {'I': pauli_i, + 'X': pauli_x, + 'Y': pauli_y, + 'Z': pauli_z} + gates = [] + for qubit, pauli_char in pauli_map.items(): + operand = operand_map.get(pauli_char) + if operand is None: + raise ValueError('pauli string character must be one of I/X/Y/Z') + gates.append((operand, (qubit,))) + return gates + + def expectation_operands(self, pauli_string): + #assign pauli string to qubit + #_get_forward_inverse_metadata() + input_bitstring = "0" * self.circuit.nqubits #Need all qubits! + + input_operands = self._get_bitstring_tensors(input_bitstring) + pauli_string = dict(zip(range(self.circuit.nqubits), pauli_string)) + pauli_map = pauli_string + coned_qubits = pauli_map.keys() + + ( + mode_labels, + qubits_frontier, + next_frontier, + ) = self._init_mode_labels_from_qubits(range(self.circuit.nqubits)) + + gate_mode_labels, gate_operands = self._parse_gates_to_mode_labels_operands( + self.gate_tensors, qubits_frontier, next_frontier + ) + + operands = input_operands + gate_operands + mode_labels += gate_mode_labels + + self.init_inverse_circuit(self.circuit.invert()) + + + next_frontier = max(qubits_frontier.values()) + 1 + + #input_mode_labels, input_operands, qubits_frontier, next_frontier, inverse_gates = self._get_forward_inverse_metadata(coned_qubits) + + pauli_gates = self.get_pauli_gates(pauli_map, dtype=self.dtype, backend=self.backend) + + + gates_inverse = pauli_gates + self.gate_tensors_inverse + + gate_mode_labels_inverse, gate_operands_inverse = self._parse_gates_to_mode_labels_operands( + gates_inverse, qubits_frontier, next_frontier + ) + mode_labels = mode_labels + gate_mode_labels_inverse + [[qubits_frontier[ix]] for ix in range(self.circuit.nqubits)] + operands = operands + gate_operands_inverse + operands[:self.circuit.nqubits] + + operand_exp_interleave = [x for y in zip(operands, mode_labels) for x in y] + + #expec = contract(*operand_exp_interleave) + #print(expec) + + ''' + gate_mode_labels, gate_operands = circ_utils.parse_gates_to_mode_labels_operands(gates, + qubits_frontier, + next_frontier) + + mode_labels = input_mode_labels + gate_mode_labels + [[qubits_frontier[ix]] for ix in self.qubits] + operands = input_operands + gate_operands + input_operands[:n_qubits] + + output_mode_labels = [] + expression = circ_utils.convert_mode_labels_to_expression(mode_labels, output_mode_labels) + ''' + return operand_exp_interleave \ No newline at end of file diff --git a/src/qibotn/QiboCircuitToMPS.py b/src/qibotn/QiboCircuitToMPS.py index 58fdd70e..b1d847f2 100644 --- a/src/qibotn/QiboCircuitToMPS.py +++ b/src/qibotn/QiboCircuitToMPS.py @@ -21,7 +21,7 @@ def __init__( self.handle = cutn.create() self.dtype = dtype self.mps_tensors = initial(self.num_qubits, dtype=dtype) - circuitconvertor = QiboCircuitToEinsum(circ_qibo) + circuitconvertor = QiboCircuitToEinsum(circ_qibo, dtype=dtype) for gate, qubits in circuitconvertor.gate_tensors: # mapping from qubits to qubit indices diff --git a/src/qibotn/backends.py b/src/qibotn/backends.py index 9d399731..4b28431d 100644 --- a/src/qibotn/backends.py +++ b/src/qibotn/backends.py @@ -14,6 +14,13 @@ def __init__(self, platform): platform == "cu_tensornet" or platform == "cu_mps" or platform == "qu_tensornet" + or platform == "cu_tensornet_mpi" + or platform == "cu_tensornet_mpi_expectation" + or platform == "cu_tensornet_expectation" + or platform == "cu_tensornet_nccl" + or platform == "cu_tensornet_nccl_expectation" + + ): # pragma: no cover self.platform = platform else: @@ -71,6 +78,52 @@ def execute_circuit( init_state = np.zeros(2**circuit.nqubits, dtype=self.dtype) init_state[0] = 1.0 state = quimb.eval(circuit.to_qasm(), init_state, backend="numpy") + + if self.platform == "cu_tensornet_mpi": + if initial_state is not None: + raise_error(NotImplementedError, "QiboTN cannot support initial state.") + + #state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) + state, rank = cutn.eval_tn_MPI_2(circuit, self.dtype,32) + if rank > 0: + state = np.array(0) + + if self.platform == "cu_tensornet_nccl": + if initial_state is not None: + raise_error(NotImplementedError, "QiboTN cannot support initial state.") + + #state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) + state, rank = cutn.eval_tn_nccl(circuit, self.dtype,32) + if rank > 0: + state = np.array(0) + + if self.platform == "cu_tensornet_expectation": + if initial_state is not None: + raise_error(NotImplementedError, "QiboTN cannot support initial state.") + + state = cutn.eval_expectation(circuit, self.dtype) + + if self.platform == "cu_tensornet_mpi_expectation": + if initial_state is not None: + raise_error(NotImplementedError, "QiboTN cannot support initial state.") + + #state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) + #state, rank = cutn.eval_tn_MPI_expectation(circuit, self.dtype,32) + state, rank = cutn.eval_tn_MPI_2_expectation(circuit, self.dtype,32) + + if rank > 0: + state = np.array(0) + + if self.platform == "cu_tensornet_nccl_expectation": + if initial_state is not None: + raise_error(NotImplementedError, "QiboTN cannot support initial state.") + + #state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) + #state, rank = cutn.eval_tn_MPI_expectation(circuit, self.dtype,32) + state, rank = cutn.eval_tn_nccl_expectation(circuit, self.dtype,32) + + if rank > 0: + state = np.array(0) if return_array: return state.flatten() diff --git a/src/qibotn/cutn.py b/src/qibotn/cutn.py index 6b8582fe..5cbc7ab9 100644 --- a/src/qibotn/cutn.py +++ b/src/qibotn/cutn.py @@ -14,6 +14,354 @@ def eval(qibo_circ, datatype): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract(*myconvertor.state_vector_operands()) +def eval_expectation(qibo_circ, datatype): + myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) + return contract(*myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits))) + +def eval_tn_MPI_2(qibo_circ, datatype, n_samples=8): + from mpi4py import MPI # this line initializes MPI + import socket + from cuquantum import Network + + # Get the hostname + #hostname = socket.gethostname() + + root = 0 + comm = MPI.COMM_WORLD + rank = comm.Get_rank() + size = comm.Get_size() + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + device_id = rank % getDeviceCount() + + + # Perform circuit conversion + myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft convetor",mem_avail, "rank =",rank) + operands = myconvertor.state_vector_operands() + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + + # Broadcast the operand data. + #operands = comm.bcast(operands, root) + + # Assign the device for each process. + device_id = rank % getDeviceCount() + + #dev = cp.cuda.Device(device_id) + #free_mem, total_mem = dev.mem_info + #print("Mem free: ",free_mem, "Total mem: ",total_mem, "rank =",rank) + + # Create network object. + network = Network(*operands, options={'device_id' : device_id}) + + # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. + path, info = network.contract_path(optimize={'samples': 8, 'slicing': {'min_slices': max(32, size)}}) + #print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") + + # Select the best path from all ranks. + opt_cost, sender = comm.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) + + #if rank == root: + # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") + + # Broadcast info from the sender to all other ranks. + info = comm.bcast(info, sender) + + # Set path and slices. + path, info = network.contract_path(optimize={'path': info.path, 'slicing': info.slices}) + + # Calculate this process's share of the slices. + num_slices = info.num_slices + chunk, extra = num_slices // size, num_slices % size + slice_begin = rank * chunk + min(rank, extra) + slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + slices = range(slice_begin, slice_end) + + #print(f"Process {rank} is processing slice range: {slices}.") + + # Contract the group of slices the process is responsible for. + result = network.contract(slices=slices) + #print(f"Process {rank} result shape is : {result.shape}.") + #print(f"Process {rank} result size is : {result.nbytes}.") + + # Sum the partial contribution from each process on root. + result = comm.reduce(sendobj=result, op=MPI.SUM, root=root) + + return result, rank + +def eval_tn_nccl(qibo_circ, datatype, n_samples=8): + from mpi4py import MPI # this line initializes MPI + import socket + from cuquantum import Network + from cupy.cuda import nccl + + # Get the hostname + #hostname = socket.gethostname() + + root = 0 + comm_mpi = MPI.COMM_WORLD + rank = comm_mpi.Get_rank() + size = comm_mpi.Get_size() + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + device_id = rank % getDeviceCount() + + cp.cuda.Device(device_id).use() + + # Set up the NCCL communicator. + nccl_id = nccl.get_unique_id() if rank == root else None + nccl_id = comm_mpi.bcast(nccl_id, root) + comm_nccl = nccl.NcclCommunicator(size, nccl_id, rank) + + # Perform circuit conversion + myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft convetor",mem_avail, "rank =",rank) + operands = myconvertor.state_vector_operands() + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + + network = Network(*operands) + + # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. + path, info = network.contract_path(optimize={'samples': 8, 'slicing': {'min_slices': max(32, size)}}) + + #print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") + + # Select the best path from all ranks. + opt_cost, sender = comm_mpi.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) + + #if rank == root: + # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") + + # Broadcast info from the sender to all other ranks. + info = comm_mpi.bcast(info, sender) + + # Set path and slices. + path, info = network.contract_path(optimize={'path': info.path, 'slicing': info.slices}) + + # Calculate this process's share of the slices. + num_slices = info.num_slices + chunk, extra = num_slices // size, num_slices % size + slice_begin = rank * chunk + min(rank, extra) + slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + slices = range(slice_begin, slice_end) + + #print(f"Process {rank} is processing slice range: {slices}.") + + # Contract the group of slices the process is responsible for. + result = network.contract(slices=slices) + #print(f"Process {rank} result shape is : {result.shape}.") + #print(f"Process {rank} result size is : {result.nbytes}.") + + # Sum the partial contribution from each process on root. + stream_ptr = cp.cuda.get_current_stream().ptr + comm_nccl.reduce(result.data.ptr, result.data.ptr, result.size, nccl.NCCL_FLOAT64, nccl.NCCL_SUM, root, stream_ptr) + + return result, rank + +def eval_tn_nccl_expectation(qibo_circ, datatype, n_samples=8): + from mpi4py import MPI # this line initializes MPI + import socket + from cuquantum import Network + from cupy.cuda import nccl + + # Get the hostname + #hostname = socket.gethostname() + + root = 0 + comm_mpi = MPI.COMM_WORLD + rank = comm_mpi.Get_rank() + size = comm_mpi.Get_size() + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + device_id = rank % getDeviceCount() + + cp.cuda.Device(device_id).use() + + # Set up the NCCL communicator. + nccl_id = nccl.get_unique_id() if rank == root else None + nccl_id = comm_mpi.bcast(nccl_id, root) + comm_nccl = nccl.NcclCommunicator(size, nccl_id, rank) + + # Perform circuit conversion + myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft convetor",mem_avail, "rank =",rank) + operands = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) + + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + + network = Network(*operands) + + # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. + path, info = network.contract_path(optimize={'samples': 8, 'slicing': {'min_slices': max(32, size)}}) + + #print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") + + # Select the best path from all ranks. + opt_cost, sender = comm_mpi.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) + + #if rank == root: + # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") + + # Broadcast info from the sender to all other ranks. + info = comm_mpi.bcast(info, sender) + + # Set path and slices. + path, info = network.contract_path(optimize={'path': info.path, 'slicing': info.slices}) + + # Calculate this process's share of the slices. + num_slices = info.num_slices + chunk, extra = num_slices // size, num_slices % size + slice_begin = rank * chunk + min(rank, extra) + slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + slices = range(slice_begin, slice_end) + + #print(f"Process {rank} is processing slice range: {slices}.") + + # Contract the group of slices the process is responsible for. + result = network.contract(slices=slices) + #print(f"Process {rank} result shape is : {result.shape}.") + #print(f"Process {rank} result size is : {result.nbytes}.") + + # Sum the partial contribution from each process on root. + stream_ptr = cp.cuda.get_current_stream().ptr + comm_nccl.reduce(result.data.ptr, result.data.ptr, result.size, nccl.NCCL_FLOAT64, nccl.NCCL_SUM, root, stream_ptr) + + return result, rank + + +def eval_tn_MPI_2_expectation(qibo_circ, datatype, n_samples=8): + from mpi4py import MPI # this line initializes MPI + import socket + from cuquantum import Network + + # Get the hostname + #hostname = socket.gethostname() + + root = 0 + comm = MPI.COMM_WORLD + rank = comm.Get_rank() + size = comm.Get_size() + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + device_id = rank % getDeviceCount() + + + # Perform circuit conversion + myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft convetor",mem_avail, "rank =",rank) + operands = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + + # Broadcast the operand data. + #operands = comm.bcast(operands, root) + + # Assign the device for each process. + device_id = rank % getDeviceCount() + + #dev = cp.cuda.Device(device_id) + #free_mem, total_mem = dev.mem_info + #print("Mem free: ",free_mem, "Total mem: ",total_mem, "rank =",rank) + + # Create network object. + network = Network(*operands, options={'device_id' : device_id}) + + # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. + path, info = network.contract_path(optimize={'samples': 8, 'slicing': {'min_slices': max(32, size)}}) + #print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") + + # Select the best path from all ranks. + opt_cost, sender = comm.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) + + #if rank == root: + # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") + + # Broadcast info from the sender to all other ranks. + info = comm.bcast(info, sender) + + # Set path and slices. + path, info = network.contract_path(optimize={'path': info.path, 'slicing': info.slices}) + + # Calculate this process's share of the slices. + num_slices = info.num_slices + chunk, extra = num_slices // size, num_slices % size + slice_begin = rank * chunk + min(rank, extra) + slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + slices = range(slice_begin, slice_end) + + #print(f"Process {rank} is processing slice range: {slices}.") + + # Contract the group of slices the process is responsible for. + result = network.contract(slices=slices) + #print(f"Process {rank} result shape is : {result.shape}.") + #print(f"Process {rank} result size is : {result.nbytes}.") + + # Sum the partial contribution from each process on root. + result = comm.reduce(sendobj=result, op=MPI.SUM, root=root) + + return result, rank + + +def eval_tn_MPI_expectation(qibo_circ, datatype, n_samples=8): + from mpi4py import MPI # this line initializes MPI + import socket + # Get the hostname + #hostname = socket.gethostname() + + ncpu_threads = multiprocessing.cpu_count() // 2 + + comm = MPI.COMM_WORLD + rank = comm.Get_rank() + size = comm.Get_size() + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + device_id = rank % getDeviceCount() + cp.cuda.Device(device_id).use() + + handle = cutn.create() + network_opts = cutn.NetworkOptions(handle=handle, blocking="auto") + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft network opts",mem_avail, "rank =",rank) + cutn.distributed_reset_configuration(handle, *cutn.get_mpi_comm_pointer(comm)) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft distributed reset config",mem_avail, "rank =",rank) + # Perform circuit conversion + myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) + operands_interleave = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft convetor",mem_avail, "rank =",rank) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + + # Pathfinder: To search for the optimal path. Optimal path are assigned to path and info attribute of the network object. + network = cutn.Network(*operands_interleave, options=network_opts) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft cutn.Network(*operands_interleave,",mem_avail, "rank =",rank) + path, opt_info = network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads, 'slicing': {'min_slices': max(16, size)}}) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft contract path",mem_avail, "rank =",rank) + # Execution: To execute the contraction using the optimal path found previously + #print("opt_cost",opt_info.opt_cost, "Process =",rank) + + + num_slices = opt_info.num_slices#Andy + chunk, extra = num_slices // size, num_slices % size#Andy + slice_begin = rank * chunk + min(rank, extra)#Andy + slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra)#Andy + slices = range(slice_begin, slice_end)#Andy + result = network.contract(slices=slices) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft contract",mem_avail, "rank =",rank) + cutn.destroy(handle) + + return result, rank def eval_tn_MPI(qibo_circ, datatype, n_samples=8): """Convert qibo circuit to tensornet (TN) format and perform contraction using multi node and multi GPU through MPI. @@ -23,29 +371,59 @@ def eval_tn_MPI(qibo_circ, datatype, n_samples=8): """ from mpi4py import MPI # this line initializes MPI - + import socket + # Get the hostname + #hostname = socket.gethostname() + ncpu_threads = multiprocessing.cpu_count() // 2 - + comm = MPI.COMM_WORLD rank = comm.Get_rank() + size = comm.Get_size() + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) device_id = rank % getDeviceCount() cp.cuda.Device(device_id).use() handle = cutn.create() - cutn.distributed_reset_configuration(handle, *cutn.get_mpi_comm_pointer(comm)) network_opts = cutn.NetworkOptions(handle=handle, blocking="auto") - + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft network opts",mem_avail, "rank =",rank) + cutn.distributed_reset_configuration(handle, *cutn.get_mpi_comm_pointer(comm)) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft distributed reset config",mem_avail, "rank =",rank) # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft convetor",mem_avail, "rank =",rank) operands_interleave = myconvertor.state_vector_operands() + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) # Pathfinder: To search for the optimal path. Optimal path are assigned to path and info attribute of the network object. network = cutn.Network(*operands_interleave, options=network_opts) - network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads}) - + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft cutn.Network(*operands_interleave,",mem_avail, "rank =",rank) + network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads, 'slicing': {'min_slices': max(16, size)}}) + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft contract path",mem_avail, "rank =",rank) # Execution: To execute the contraction using the optimal path found previously + #print("opt_cost",opt_info.opt_cost, "Process =",rank) + + ''' + path, opt_info = network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads, 'slicing': {'min_slices': max(16, size)}}) + + num_slices = opt_info.num_slices#Andy + chunk, extra = num_slices // size, num_slices % size#Andy + slice_begin = rank * chunk + min(rank, extra)#Andy + slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra)#Andy + slices = range(slice_begin, slice_end)#Andy + result = network.contract(slices=slices) + ''' result = network.contract() + #mem_avail = cp.cuda.Device().mem_info[0] + #print("Mem avail: aft contract",mem_avail, "rank =",rank) cutn.destroy(handle) return result, rank @@ -58,3 +436,19 @@ def eval_mps(qibo_circ, gate_algo, datatype): return mps_helper.contract_state_vector( myconvertor.mps_tensors, {"handle": myconvertor.handle} ) + +def PauliStringGen(nqubits): + + if nqubits <= 0: + return "Invalid input. N should be a positive integer." + + #characters = 'IXYZ' + characters = 'XXXZ' + + result = '' + + for i in range(nqubits): + char_to_add = characters[i % len(characters)] + result += char_to_add + + return result \ No newline at end of file From c2d2c8318fe605255bc2ac8db5b91d3c4c192fe9 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 24 Jan 2024 11:47:32 +0800 Subject: [PATCH 091/199] Format with black --- src/qibotn/QiboCircuitConvertor.py | 82 +++---- src/qibotn/backends.py | 41 ++-- src/qibotn/cutn.py | 366 +++++++++++++++++------------ 3 files changed, 275 insertions(+), 214 deletions(-) diff --git a/src/qibotn/QiboCircuitConvertor.py b/src/qibotn/QiboCircuitConvertor.py index 88ff144d..d3a0569a 100644 --- a/src/qibotn/QiboCircuitConvertor.py +++ b/src/qibotn/QiboCircuitConvertor.py @@ -110,7 +110,6 @@ def init_basis_map(self, backend, dtype): self.basis_map = {"0": state_0, "1": state_1} - def init_inverse_circuit(self, circuit): self.gate_tensors_inverse = [] gates_qubits_inverse = [] @@ -131,14 +130,13 @@ def init_inverse_circuit(self, circuit): # self.active_qubits is to identify qubits with at least 1 gate acting on it in the whole circuit. self.active_qubits_inverse = np.unique(gates_qubits_inverse) - - - def get_pauli_gates(self, pauli_map, dtype='complex128', backend=cp): + + def get_pauli_gates(self, pauli_map, dtype="complex128", backend=cp): """ Populate the gates for all pauli operators. Args: - pauli_map: A dictionary mapping qubits to pauli operators. + pauli_map: A dictionary mapping qubits to pauli operators. dtype: Data type for the tensor operands. backend: The package the tensor operands belong to. @@ -146,70 +144,74 @@ def get_pauli_gates(self, pauli_map, dtype='complex128', backend=cp): A sequence of pauli gates. """ asarray = backend.asarray - pauli_i = asarray([[1,0], [0,1]], dtype=dtype) - pauli_x = asarray([[0,1], [1,0]], dtype=dtype) - pauli_y = asarray([[0,-1j], [1j,0]], dtype=dtype) - pauli_z = asarray([[1,0], [0,-1]], dtype=dtype) - - operand_map = {'I': pauli_i, - 'X': pauli_x, - 'Y': pauli_y, - 'Z': pauli_z} + pauli_i = asarray([[1, 0], [0, 1]], dtype=dtype) + pauli_x = asarray([[0, 1], [1, 0]], dtype=dtype) + pauli_y = asarray([[0, -1j], [1j, 0]], dtype=dtype) + pauli_z = asarray([[1, 0], [0, -1]], dtype=dtype) + + operand_map = {"I": pauli_i, "X": pauli_x, "Y": pauli_y, "Z": pauli_z} gates = [] for qubit, pauli_char in pauli_map.items(): operand = operand_map.get(pauli_char) if operand is None: - raise ValueError('pauli string character must be one of I/X/Y/Z') + raise ValueError("pauli string character must be one of I/X/Y/Z") gates.append((operand, (qubit,))) return gates def expectation_operands(self, pauli_string): - #assign pauli string to qubit - #_get_forward_inverse_metadata() - input_bitstring = "0" * self.circuit.nqubits #Need all qubits! + # assign pauli string to qubit + # _get_forward_inverse_metadata() + input_bitstring = "0" * self.circuit.nqubits # Need all qubits! input_operands = self._get_bitstring_tensors(input_bitstring) - pauli_string = dict(zip(range(self.circuit.nqubits), pauli_string)) + pauli_string = dict(zip(range(self.circuit.nqubits), pauli_string)) pauli_map = pauli_string coned_qubits = pauli_map.keys() - + ( mode_labels, qubits_frontier, next_frontier, ) = self._init_mode_labels_from_qubits(range(self.circuit.nqubits)) - + gate_mode_labels, gate_operands = self._parse_gates_to_mode_labels_operands( self.gate_tensors, qubits_frontier, next_frontier ) - + operands = input_operands + gate_operands mode_labels += gate_mode_labels - + self.init_inverse_circuit(self.circuit.invert()) - - + next_frontier = max(qubits_frontier.values()) + 1 - #input_mode_labels, input_operands, qubits_frontier, next_frontier, inverse_gates = self._get_forward_inverse_metadata(coned_qubits) + # input_mode_labels, input_operands, qubits_frontier, next_frontier, inverse_gates = self._get_forward_inverse_metadata(coned_qubits) + + pauli_gates = self.get_pauli_gates( + pauli_map, dtype=self.dtype, backend=self.backend + ) - pauli_gates = self.get_pauli_gates(pauli_map, dtype=self.dtype, backend=self.backend) - - gates_inverse = pauli_gates + self.gate_tensors_inverse - - gate_mode_labels_inverse, gate_operands_inverse = self._parse_gates_to_mode_labels_operands( + + ( + gate_mode_labels_inverse, + gate_operands_inverse, + ) = self._parse_gates_to_mode_labels_operands( gates_inverse, qubits_frontier, next_frontier ) - mode_labels = mode_labels + gate_mode_labels_inverse + [[qubits_frontier[ix]] for ix in range(self.circuit.nqubits)] - operands = operands + gate_operands_inverse + operands[:self.circuit.nqubits] - + mode_labels = ( + mode_labels + + gate_mode_labels_inverse + + [[qubits_frontier[ix]] for ix in range(self.circuit.nqubits)] + ) + operands = operands + gate_operands_inverse + operands[: self.circuit.nqubits] + operand_exp_interleave = [x for y in zip(operands, mode_labels) for x in y] - - #expec = contract(*operand_exp_interleave) - #print(expec) - ''' + # expec = contract(*operand_exp_interleave) + # print(expec) + + """ gate_mode_labels, gate_operands = circ_utils.parse_gates_to_mode_labels_operands(gates, qubits_frontier, next_frontier) @@ -219,5 +221,5 @@ def expectation_operands(self, pauli_string): output_mode_labels = [] expression = circ_utils.convert_mode_labels_to_expression(mode_labels, output_mode_labels) - ''' - return operand_exp_interleave \ No newline at end of file + """ + return operand_exp_interleave diff --git a/src/qibotn/backends.py b/src/qibotn/backends.py index 4b28431d..3728a999 100644 --- a/src/qibotn/backends.py +++ b/src/qibotn/backends.py @@ -19,8 +19,6 @@ def __init__(self, platform): or platform == "cu_tensornet_expectation" or platform == "cu_tensornet_nccl" or platform == "cu_tensornet_nccl_expectation" - - ): # pragma: no cover self.platform = platform else: @@ -72,45 +70,44 @@ def execute_circuit( state = cutn.eval_mps(circuit, gate_algo, self.dtype) if self.platform == "qu_tensornet": - - #init_state = np.random.random(2**circuit.nqubits) + 1j * np.random.random(2**circuit.nqubits) - #init_state = init_state / np.sqrt((np.abs(init_state) ** 2).sum()) + # init_state = np.random.random(2**circuit.nqubits) + 1j * np.random.random(2**circuit.nqubits) + # init_state = init_state / np.sqrt((np.abs(init_state) ** 2).sum()) init_state = np.zeros(2**circuit.nqubits, dtype=self.dtype) init_state[0] = 1.0 state = quimb.eval(circuit.to_qasm(), init_state, backend="numpy") - + if self.platform == "cu_tensornet_mpi": if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - #state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) - state, rank = cutn.eval_tn_MPI_2(circuit, self.dtype,32) + # state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) + state, rank = cutn.eval_tn_MPI_2(circuit, self.dtype, 32) if rank > 0: state = np.array(0) - + if self.platform == "cu_tensornet_nccl": if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - #state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) - state, rank = cutn.eval_tn_nccl(circuit, self.dtype,32) + # state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) + state, rank = cutn.eval_tn_nccl(circuit, self.dtype, 32) if rank > 0: state = np.array(0) - + if self.platform == "cu_tensornet_expectation": if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - + state = cutn.eval_expectation(circuit, self.dtype) - + if self.platform == "cu_tensornet_mpi_expectation": if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - #state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) - #state, rank = cutn.eval_tn_MPI_expectation(circuit, self.dtype,32) - state, rank = cutn.eval_tn_MPI_2_expectation(circuit, self.dtype,32) - + # state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) + # state, rank = cutn.eval_tn_MPI_expectation(circuit, self.dtype,32) + state, rank = cutn.eval_tn_MPI_2_expectation(circuit, self.dtype, 32) + if rank > 0: state = np.array(0) @@ -118,10 +115,10 @@ def execute_circuit( if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - #state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) - #state, rank = cutn.eval_tn_MPI_expectation(circuit, self.dtype,32) - state, rank = cutn.eval_tn_nccl_expectation(circuit, self.dtype,32) - + # state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) + # state, rank = cutn.eval_tn_MPI_expectation(circuit, self.dtype,32) + state, rank = cutn.eval_tn_nccl_expectation(circuit, self.dtype, 32) + if rank > 0: state = np.array(0) diff --git a/src/qibotn/cutn.py b/src/qibotn/cutn.py index 5cbc7ab9..86419d2c 100644 --- a/src/qibotn/cutn.py +++ b/src/qibotn/cutn.py @@ -14,9 +14,13 @@ def eval(qibo_circ, datatype): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract(*myconvertor.state_vector_operands()) + def eval_expectation(qibo_circ, datatype): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - return contract(*myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits))) + return contract( + *myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) + ) + def eval_tn_MPI_2(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI @@ -24,73 +28,79 @@ def eval_tn_MPI_2(qibo_circ, datatype, n_samples=8): from cuquantum import Network # Get the hostname - #hostname = socket.gethostname() - + # hostname = socket.gethostname() + root = 0 comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) device_id = rank % getDeviceCount() - - + # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft convetor",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft convetor",mem_avail, "rank =",rank) operands = myconvertor.state_vector_operands() - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) - + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + # Broadcast the operand data. - #operands = comm.bcast(operands, root) - + # operands = comm.bcast(operands, root) + # Assign the device for each process. device_id = rank % getDeviceCount() - - #dev = cp.cuda.Device(device_id) - #free_mem, total_mem = dev.mem_info - #print("Mem free: ",free_mem, "Total mem: ",total_mem, "rank =",rank) + + # dev = cp.cuda.Device(device_id) + # free_mem, total_mem = dev.mem_info + # print("Mem free: ",free_mem, "Total mem: ",total_mem, "rank =",rank) # Create network object. - network = Network(*operands, options={'device_id' : device_id}) + network = Network(*operands, options={"device_id": device_id}) # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. - path, info = network.contract_path(optimize={'samples': 8, 'slicing': {'min_slices': max(32, size)}}) - #print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") + path, info = network.contract_path( + optimize={"samples": 8, "slicing": {"min_slices": max(32, size)}} + ) + # print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") # Select the best path from all ranks. opt_cost, sender = comm.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) - #if rank == root: + # if rank == root: # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") # Broadcast info from the sender to all other ranks. info = comm.bcast(info, sender) # Set path and slices. - path, info = network.contract_path(optimize={'path': info.path, 'slicing': info.slices}) + path, info = network.contract_path( + optimize={"path": info.path, "slicing": info.slices} + ) # Calculate this process's share of the slices. num_slices = info.num_slices chunk, extra = num_slices // size, num_slices % size slice_begin = rank * chunk + min(rank, extra) - slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + slice_end = ( + num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + ) slices = range(slice_begin, slice_end) - #print(f"Process {rank} is processing slice range: {slices}.") + # print(f"Process {rank} is processing slice range: {slices}.") # Contract the group of slices the process is responsible for. result = network.contract(slices=slices) - #print(f"Process {rank} result shape is : {result.shape}.") - #print(f"Process {rank} result size is : {result.nbytes}.") + # print(f"Process {rank} result shape is : {result.shape}.") + # print(f"Process {rank} result size is : {result.nbytes}.") # Sum the partial contribution from each process on root. result = comm.reduce(sendobj=result, op=MPI.SUM, root=root) - + return result, rank + def eval_tn_nccl(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket @@ -98,18 +108,18 @@ def eval_tn_nccl(qibo_circ, datatype, n_samples=8): from cupy.cuda import nccl # Get the hostname - #hostname = socket.gethostname() - + # hostname = socket.gethostname() + root = 0 comm_mpi = MPI.COMM_WORLD rank = comm_mpi.Get_rank() size = comm_mpi.Get_size() - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) device_id = rank % getDeviceCount() - + cp.cuda.Device(device_id).use() - + # Set up the NCCL communicator. nccl_id = nccl.get_unique_id() if rank == root else None nccl_id = comm_mpi.bcast(nccl_id, root) @@ -117,51 +127,66 @@ def eval_tn_nccl(qibo_circ, datatype, n_samples=8): # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft convetor",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft convetor",mem_avail, "rank =",rank) operands = myconvertor.state_vector_operands() - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) network = Network(*operands) # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. - path, info = network.contract_path(optimize={'samples': 8, 'slicing': {'min_slices': max(32, size)}}) + path, info = network.contract_path( + optimize={"samples": 8, "slicing": {"min_slices": max(32, size)}} + ) - #print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") + # print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") # Select the best path from all ranks. opt_cost, sender = comm_mpi.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) - #if rank == root: + # if rank == root: # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") # Broadcast info from the sender to all other ranks. info = comm_mpi.bcast(info, sender) # Set path and slices. - path, info = network.contract_path(optimize={'path': info.path, 'slicing': info.slices}) + path, info = network.contract_path( + optimize={"path": info.path, "slicing": info.slices} + ) # Calculate this process's share of the slices. num_slices = info.num_slices chunk, extra = num_slices // size, num_slices % size slice_begin = rank * chunk + min(rank, extra) - slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + slice_end = ( + num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + ) slices = range(slice_begin, slice_end) - #print(f"Process {rank} is processing slice range: {slices}.") + # print(f"Process {rank} is processing slice range: {slices}.") # Contract the group of slices the process is responsible for. result = network.contract(slices=slices) - #print(f"Process {rank} result shape is : {result.shape}.") - #print(f"Process {rank} result size is : {result.nbytes}.") + # print(f"Process {rank} result shape is : {result.shape}.") + # print(f"Process {rank} result size is : {result.nbytes}.") # Sum the partial contribution from each process on root. stream_ptr = cp.cuda.get_current_stream().ptr - comm_nccl.reduce(result.data.ptr, result.data.ptr, result.size, nccl.NCCL_FLOAT64, nccl.NCCL_SUM, root, stream_ptr) - + comm_nccl.reduce( + result.data.ptr, + result.data.ptr, + result.size, + nccl.NCCL_FLOAT64, + nccl.NCCL_SUM, + root, + stream_ptr, + ) + return result, rank + def eval_tn_nccl_expectation(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket @@ -169,18 +194,18 @@ def eval_tn_nccl_expectation(qibo_circ, datatype, n_samples=8): from cupy.cuda import nccl # Get the hostname - #hostname = socket.gethostname() - + # hostname = socket.gethostname() + root = 0 comm_mpi = MPI.COMM_WORLD rank = comm_mpi.Get_rank() size = comm_mpi.Get_size() - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) device_id = rank % getDeviceCount() - + cp.cuda.Device(device_id).use() - + # Set up the NCCL communicator. nccl_id = nccl.get_unique_id() if rank == root else None nccl_id = comm_mpi.bcast(nccl_id, root) @@ -188,50 +213,64 @@ def eval_tn_nccl_expectation(qibo_circ, datatype, n_samples=8): # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft convetor",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft convetor",mem_avail, "rank =",rank) operands = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) network = Network(*operands) # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. - path, info = network.contract_path(optimize={'samples': 8, 'slicing': {'min_slices': max(32, size)}}) + path, info = network.contract_path( + optimize={"samples": 8, "slicing": {"min_slices": max(32, size)}} + ) - #print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") + # print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") # Select the best path from all ranks. opt_cost, sender = comm_mpi.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) - #if rank == root: + # if rank == root: # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") # Broadcast info from the sender to all other ranks. info = comm_mpi.bcast(info, sender) # Set path and slices. - path, info = network.contract_path(optimize={'path': info.path, 'slicing': info.slices}) + path, info = network.contract_path( + optimize={"path": info.path, "slicing": info.slices} + ) # Calculate this process's share of the slices. num_slices = info.num_slices chunk, extra = num_slices // size, num_slices % size slice_begin = rank * chunk + min(rank, extra) - slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + slice_end = ( + num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + ) slices = range(slice_begin, slice_end) - #print(f"Process {rank} is processing slice range: {slices}.") + # print(f"Process {rank} is processing slice range: {slices}.") # Contract the group of slices the process is responsible for. result = network.contract(slices=slices) - #print(f"Process {rank} result shape is : {result.shape}.") - #print(f"Process {rank} result size is : {result.nbytes}.") + # print(f"Process {rank} result shape is : {result.shape}.") + # print(f"Process {rank} result size is : {result.nbytes}.") # Sum the partial contribution from each process on root. stream_ptr = cp.cuda.get_current_stream().ptr - comm_nccl.reduce(result.data.ptr, result.data.ptr, result.size, nccl.NCCL_FLOAT64, nccl.NCCL_SUM, root, stream_ptr) - + comm_nccl.reduce( + result.data.ptr, + result.data.ptr, + result.size, + nccl.NCCL_FLOAT64, + nccl.NCCL_SUM, + root, + stream_ptr, + ) + return result, rank @@ -241,128 +280,144 @@ def eval_tn_MPI_2_expectation(qibo_circ, datatype, n_samples=8): from cuquantum import Network # Get the hostname - #hostname = socket.gethostname() - + # hostname = socket.gethostname() + root = 0 comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) device_id = rank % getDeviceCount() - - + # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft convetor",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft convetor",mem_avail, "rank =",rank) operands = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) - + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + # Broadcast the operand data. - #operands = comm.bcast(operands, root) - + # operands = comm.bcast(operands, root) + # Assign the device for each process. device_id = rank % getDeviceCount() - - #dev = cp.cuda.Device(device_id) - #free_mem, total_mem = dev.mem_info - #print("Mem free: ",free_mem, "Total mem: ",total_mem, "rank =",rank) + + # dev = cp.cuda.Device(device_id) + # free_mem, total_mem = dev.mem_info + # print("Mem free: ",free_mem, "Total mem: ",total_mem, "rank =",rank) # Create network object. - network = Network(*operands, options={'device_id' : device_id}) + network = Network(*operands, options={"device_id": device_id}) # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. - path, info = network.contract_path(optimize={'samples': 8, 'slicing': {'min_slices': max(32, size)}}) - #print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") + path, info = network.contract_path( + optimize={"samples": 8, "slicing": {"min_slices": max(32, size)}} + ) + # print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") # Select the best path from all ranks. opt_cost, sender = comm.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) - #if rank == root: + # if rank == root: # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") # Broadcast info from the sender to all other ranks. info = comm.bcast(info, sender) # Set path and slices. - path, info = network.contract_path(optimize={'path': info.path, 'slicing': info.slices}) + path, info = network.contract_path( + optimize={"path": info.path, "slicing": info.slices} + ) # Calculate this process's share of the slices. num_slices = info.num_slices chunk, extra = num_slices // size, num_slices % size slice_begin = rank * chunk + min(rank, extra) - slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + slice_end = ( + num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + ) slices = range(slice_begin, slice_end) - #print(f"Process {rank} is processing slice range: {slices}.") + # print(f"Process {rank} is processing slice range: {slices}.") # Contract the group of slices the process is responsible for. result = network.contract(slices=slices) - #print(f"Process {rank} result shape is : {result.shape}.") - #print(f"Process {rank} result size is : {result.nbytes}.") + # print(f"Process {rank} result shape is : {result.shape}.") + # print(f"Process {rank} result size is : {result.nbytes}.") # Sum the partial contribution from each process on root. result = comm.reduce(sendobj=result, op=MPI.SUM, root=root) - + return result, rank def eval_tn_MPI_expectation(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket + # Get the hostname - #hostname = socket.gethostname() - + # hostname = socket.gethostname() + ncpu_threads = multiprocessing.cpu_count() // 2 - + comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) device_id = rank % getDeviceCount() cp.cuda.Device(device_id).use() handle = cutn.create() network_opts = cutn.NetworkOptions(handle=handle, blocking="auto") - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft network opts",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft network opts",mem_avail, "rank =",rank) cutn.distributed_reset_configuration(handle, *cutn.get_mpi_comm_pointer(comm)) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft distributed reset config",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft distributed reset config",mem_avail, "rank =",rank) # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - operands_interleave = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft convetor",mem_avail, "rank =",rank) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + operands_interleave = myconvertor.expectation_operands( + PauliStringGen(qibo_circ.nqubits) + ) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft convetor",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) # Pathfinder: To search for the optimal path. Optimal path are assigned to path and info attribute of the network object. network = cutn.Network(*operands_interleave, options=network_opts) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft cutn.Network(*operands_interleave,",mem_avail, "rank =",rank) - path, opt_info = network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads, 'slicing': {'min_slices': max(16, size)}}) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft contract path",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft cutn.Network(*operands_interleave,",mem_avail, "rank =",rank) + path, opt_info = network.contract_path( + optimize={ + "samples": n_samples, + "threads": ncpu_threads, + "slicing": {"min_slices": max(16, size)}, + } + ) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft contract path",mem_avail, "rank =",rank) # Execution: To execute the contraction using the optimal path found previously - #print("opt_cost",opt_info.opt_cost, "Process =",rank) - - - num_slices = opt_info.num_slices#Andy - chunk, extra = num_slices // size, num_slices % size#Andy - slice_begin = rank * chunk + min(rank, extra)#Andy - slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra)#Andy - slices = range(slice_begin, slice_end)#Andy + # print("opt_cost",opt_info.opt_cost, "Process =",rank) + + num_slices = opt_info.num_slices # Andy + chunk, extra = num_slices // size, num_slices % size # Andy + slice_begin = rank * chunk + min(rank, extra) # Andy + slice_end = ( + num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) + ) # Andy + slices = range(slice_begin, slice_end) # Andy result = network.contract(slices=slices) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft contract",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft contract",mem_avail, "rank =",rank) cutn.destroy(handle) return result, rank + def eval_tn_MPI(qibo_circ, datatype, n_samples=8): """Convert qibo circuit to tensornet (TN) format and perform contraction using multi node and multi GPU through MPI. The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. @@ -372,45 +427,52 @@ def eval_tn_MPI(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket + # Get the hostname - #hostname = socket.gethostname() - + # hostname = socket.gethostname() + ncpu_threads = multiprocessing.cpu_count() // 2 - + comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) device_id = rank % getDeviceCount() cp.cuda.Device(device_id).use() handle = cutn.create() network_opts = cutn.NetworkOptions(handle=handle, blocking="auto") - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft network opts",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft network opts",mem_avail, "rank =",rank) cutn.distributed_reset_configuration(handle, *cutn.get_mpi_comm_pointer(comm)) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft distributed reset config",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft distributed reset config",mem_avail, "rank =",rank) # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft convetor",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft convetor",mem_avail, "rank =",rank) operands_interleave = myconvertor.state_vector_operands() - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) # Pathfinder: To search for the optimal path. Optimal path are assigned to path and info attribute of the network object. network = cutn.Network(*operands_interleave, options=network_opts) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft cutn.Network(*operands_interleave,",mem_avail, "rank =",rank) - network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads, 'slicing': {'min_slices': max(16, size)}}) - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft contract path",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft cutn.Network(*operands_interleave,",mem_avail, "rank =",rank) + network.contract_path( + optimize={ + "samples": n_samples, + "threads": ncpu_threads, + "slicing": {"min_slices": max(16, size)}, + } + ) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft contract path",mem_avail, "rank =",rank) # Execution: To execute the contraction using the optimal path found previously - #print("opt_cost",opt_info.opt_cost, "Process =",rank) + # print("opt_cost",opt_info.opt_cost, "Process =",rank) - ''' + """ path, opt_info = network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads, 'slicing': {'min_slices': max(16, size)}}) num_slices = opt_info.num_slices#Andy @@ -419,11 +481,11 @@ def eval_tn_MPI(qibo_circ, datatype, n_samples=8): slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra)#Andy slices = range(slice_begin, slice_end)#Andy result = network.contract(slices=slices) - ''' + """ result = network.contract() - #mem_avail = cp.cuda.Device().mem_info[0] - #print("Mem avail: aft contract",mem_avail, "rank =",rank) + # mem_avail = cp.cuda.Device().mem_info[0] + # print("Mem avail: aft contract",mem_avail, "rank =",rank) cutn.destroy(handle) return result, rank @@ -437,18 +499,18 @@ def eval_mps(qibo_circ, gate_algo, datatype): myconvertor.mps_tensors, {"handle": myconvertor.handle} ) + def PauliStringGen(nqubits): - if nqubits <= 0: return "Invalid input. N should be a positive integer." - #characters = 'IXYZ' - characters = 'XXXZ' + # characters = 'IXYZ' + characters = "XXXZ" - result = '' + result = "" for i in range(nqubits): char_to_add = characters[i % len(characters)] result += char_to_add - return result \ No newline at end of file + return result From 388afffa7287ba9e33a484122244508799faea86 Mon Sep 17 00:00:00 2001 From: Liwei Yang Date: Wed, 24 Jan 2024 17:50:54 +0800 Subject: [PATCH 092/199] Add CPU and GPU into backends --- src/qibotn/backends/__init__.py | 2 + src/qibotn/backends/cpu.py | 302 ++++++++++++++++++++++++++++++++ src/qibotn/backends/gpu.py | 39 +++++ 3 files changed, 343 insertions(+) create mode 100644 src/qibotn/backends/__init__.py create mode 100644 src/qibotn/backends/cpu.py create mode 100644 src/qibotn/backends/gpu.py diff --git a/src/qibotn/backends/__init__.py b/src/qibotn/backends/__init__.py new file mode 100644 index 00000000..ebc3a205 --- /dev/null +++ b/src/qibotn/backends/__init__.py @@ -0,0 +1,2 @@ +from qibotn.backends.cpu import NumbaBackend +from qibotn.backends.gpu import CuTensorNet diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py new file mode 100644 index 00000000..22afcdc1 --- /dev/null +++ b/src/qibotn/backends/cpu.py @@ -0,0 +1,302 @@ +import numpy as np +from qibo.backends.numpy import NumpyBackend +from qibo.config import log +from qibo.gates.abstract import ParametrizedGate +from qibo.gates.channels import ReadoutErrorChannel +from qibo.gates.special import FusedGate + +from qibojit.backends.matrices import CustomMatrices + +GATE_OPS = { + "X": "apply_x", + "CNOT": "apply_x", + "TOFFOLI": "apply_x", + "Y": "apply_y", + "Z": "apply_z", + "CZ": "apply_z", + "U1": "apply_z_pow", + "CU1": "apply_z_pow", + "SWAP": "apply_swap", + "fSim": "apply_fsim", + "GeneralizedfSim": "apply_fsim", +} + + +class NumbaBackend(NumpyBackend): + def __init__(self): + super().__init__() + import sys + + import psutil + from numba import __version__ as numba_version + + from qibotn import __version__ as qibotn_version + + self.name = "qibotn" + self.platform = "numba" + self.versions.update( + { + "qibotn": qibotn_version, + "numba": numba_version, + } + ) + self.numeric_types = ( + int, + float, + complex, + np.int32, + np.int64, + np.float32, + np.float64, + np.complex64, + np.complex128, + ) + self.tensor_types = (np.ndarray,) + self.device = "/CPU:0" + self.custom_matrices = CustomMatrices(self.dtype) + self.gates = gates + self.ops = ops + self.measure_frequencies_op = ops.measure_frequencies + self.multi_qubit_kernels = { + 3: self.gates.apply_three_qubit_gate_kernel, + 4: self.gates.apply_four_qubit_gate_kernel, + 5: self.gates.apply_five_qubit_gate_kernel, + } + if sys.platform == "darwin": # pragma: no cover + self.set_threads(psutil.cpu_count(logical=False)) + else: + self.set_threads(len(psutil.Process().cpu_affinity())) + + def set_precision(self, precision): + if precision != self.precision: + super().set_precision(precision) + if self.custom_matrices: + self.custom_matrices = CustomMatrices(self.dtype) + + def set_threads(self, nthreads): + import numba + + numba.set_num_threads(nthreads) + self.nthreads = nthreads + + # def cast(self, x, dtype=None, copy=False): Inherited from ``NumpyBackend`` + + # def to_numpy(self, x): Inherited from ``NumpyBackend`` + + def zero_state(self, nqubits): + size = 2**nqubits + state = np.empty((size,), dtype=self.dtype) + return self.ops.initial_state_vector(state) + + def zero_density_matrix(self, nqubits): + size = 2**nqubits + state = np.empty((size, size), dtype=self.dtype) + return self.ops.initial_density_matrix(state) + + # def plus_state(self, nqubits): Inherited from ``NumpyBackend`` + + # def plus_density_matrix(self, nqubits): Inherited from ``NumpyBackend`` + + # def asmatrix_special(self, gate): Inherited from ``NumpyBackend`` + + # def control_matrix(self, gate): Inherited from ``NumpyBackend`` + + def one_qubit_base(self, state, nqubits, target, kernel, gate, qubits): + ncontrols = len(qubits) - 1 if qubits is not None else 0 + m = nqubits - target - 1 + nstates = 1 << (nqubits - ncontrols - 1) + if ncontrols: + kernel = getattr(self.gates, "multicontrol_{}_kernel".format(kernel)) + return kernel(state, gate, qubits, nstates, m) + kernel = getattr(self.gates, "{}_kernel".format(kernel)) + return kernel(state, gate, nstates, m) + + def two_qubit_base(self, state, nqubits, target1, target2, kernel, gate, qubits): + ncontrols = len(qubits) - 2 if qubits is not None else 0 + if target1 > target2: + swap_targets = True + m1 = nqubits - target1 - 1 + m2 = nqubits - target2 - 1 + else: + swap_targets = False + m1 = nqubits - target2 - 1 + m2 = nqubits - target1 - 1 + nstates = 1 << (nqubits - 2 - ncontrols) + if ncontrols: + kernel = getattr(self.gates, "multicontrol_{}_kernel".format(kernel)) + return kernel(state, gate, qubits, nstates, m1, m2, swap_targets) + kernel = getattr(self.gates, "{}_kernel".format(kernel)) + return kernel(state, gate, nstates, m1, m2, swap_targets) + + def multi_qubit_base(self, state, nqubits, targets, gate, qubits): + if qubits is None: + qubits = np.array(sorted(nqubits - q - 1 for q in targets), dtype="int32") + nstates = 1 << (nqubits - len(qubits)) + targets = np.array( + [1 << (nqubits - t - 1) for t in targets[::-1]], dtype="int64" + ) + if len(targets) > 5: + kernel = self.gates.apply_multi_qubit_gate_kernel + else: + kernel = self.multi_qubit_kernels.get(len(targets)) + return kernel(state, gate, qubits, nstates, targets) + + @staticmethod + def _create_qubits_tensor(gate, nqubits): + # TODO: Treat density matrices + qubits = [nqubits - q - 1 for q in gate.control_qubits] + qubits.extend(nqubits - q - 1 for q in gate.target_qubits) + return np.array(sorted(qubits), dtype="int32") + + def _as_custom_matrix(self, gate): + name = gate.__class__.__name__ + if isinstance(gate, ParametrizedGate): + return getattr(self.custom_matrices, name)(*gate.parameters) + elif isinstance(gate, FusedGate): # pragma: no cover + # fusion is tested in qibo tests + return self.asmatrix_fused(gate) + else: + return getattr(self.custom_matrices, name) + + def apply_gate(self, gate, state, nqubits): + matrix = self._as_custom_matrix(gate) + qubits = self._create_qubits_tensor(gate, nqubits) + targets = gate.target_qubits + state = self.cast(state) + if len(targets) == 1: + op = GATE_OPS.get(gate.__class__.__name__, "apply_gate") + return self.one_qubit_base(state, nqubits, *targets, op, matrix, qubits) + elif len(targets) == 2: + op = GATE_OPS.get(gate.__class__.__name__, "apply_two_qubit_gate") + return self.two_qubit_base(state, nqubits, *targets, op, matrix, qubits) + else: + return self.multi_qubit_base(state, nqubits, targets, matrix, qubits) + + def apply_gate_density_matrix(self, gate, state, nqubits, inverse=False): + name = gate.__class__.__name__ + if name == "Y": + return self._apply_ygate_density_matrix(gate, state, nqubits) + if inverse: + # used to reset the state when applying channels + # see :meth:`qibojit.backend.NumpyBackend.apply_channel_density_matrix` below + matrix = np.linalg.inv(gate.asmatrix(self)) + matrix = self.cast(matrix) + else: + matrix = self._as_custom_matrix(gate) + qubits = self._create_qubits_tensor(gate, nqubits) + qubits_dm = qubits + nqubits + targets = gate.target_qubits + targets_dm = tuple(q + nqubits for q in targets) + + state = self.cast(state) + shape = state.shape + if len(targets) == 1: + op = GATE_OPS.get(name, "apply_gate") + state = self.one_qubit_base( + state.ravel(), 2 * nqubits, *targets, op, matrix, qubits_dm + ) + state = self.one_qubit_base( + state, 2 * nqubits, *targets_dm, op, np.conj(matrix), qubits + ) + elif len(targets) == 2: + op = GATE_OPS.get(name, "apply_two_qubit_gate") + state = self.two_qubit_base( + state.ravel(), 2 * nqubits, *targets, op, matrix, qubits_dm + ) + state = self.two_qubit_base( + state, 2 * nqubits, *targets_dm, op, np.conj(matrix), qubits + ) + else: + state = self.multi_qubit_base( + state.ravel(), 2 * nqubits, targets, matrix, qubits_dm + ) + state = self.multi_qubit_base( + state, 2 * nqubits, targets_dm, np.conj(matrix), qubits + ) + return np.reshape(state, shape) + + def _apply_ygate_density_matrix(self, gate, state, nqubits): + matrix = self._as_custom_matrix(gate) + qubits = self._create_qubits_tensor(gate, nqubits) + qubits_dm = qubits + nqubits + targets = gate.target_qubits + targets_dm = tuple(q + nqubits for q in targets) + state = self.cast(state) + shape = state.shape + state = self.one_qubit_base( + state.ravel(), 2 * nqubits, *targets, "apply_y", matrix, qubits_dm + ) + # force using ``apply_gate`` kernel so that conjugate is properly applied + state = self.one_qubit_base( + state, 2 * nqubits, *targets_dm, "apply_gate", np.conj(matrix), qubits + ) + return np.reshape(state, shape) + + # def apply_channel(self, gate): Inherited from ``NumpyBackend`` + + def apply_channel_density_matrix(self, channel, state, nqubits): + state = self.cast(state) + if isinstance(channel, ReadoutErrorChannel) is True: + state_copy = self.cast(state, copy=True) + new_state = (1 - channel.coefficient_sum) * state + for coeff, gate in zip(channel.coefficients, channel.gates): + state = self.apply_gate_density_matrix(gate, state, nqubits) + new_state += coeff * state + # reset the state + if isinstance(channel, ReadoutErrorChannel) is True: + state = self.cast(state_copy, copy=True) + else: + state = self.apply_gate_density_matrix( + gate, state, nqubits, inverse=True + ) + return new_state + + def collapse_state(self, state, qubits, shot, nqubits, normalize=True): + state = self.cast(state) + qubits = self.cast([nqubits - q - 1 for q in reversed(qubits)], dtype="int32") + if normalize: + return self.ops.collapse_state_normalized(state, qubits, int(shot), nqubits) + else: + return self.ops.collapse_state(state, qubits, int(shot), nqubits) + + def collapse_density_matrix(self, state, qubits, shot, nqubits, normalize=True): + state = self.cast(state) + shape = state.shape + dm_qubits = [q + nqubits for q in qubits] + state = self.collapse_state(state.ravel(), dm_qubits, shot, 2 * nqubits, False) + state = self.collapse_state(state, qubits, shot, 2 * nqubits, False) + state = self.np.reshape(state, shape) + if normalize: + state = state / self.np.trace(state) + return state + + # def calculate_probabilities(self, state, qubits, nqubits): Inherited from ``NumpyBackend`` + + # def sample_shots(self, probabilities, nshots): Inherited from ``NumpyBackend`` + + # def aggregate_shots(self, shots): Inherited from ``NumpyBackend`` + + # def samples_to_binary(self, samples, nqubits): Inherited from ``NumpyBackend`` + + # def samples_to_decimal(self, samples, nqubits): Inherited from ``NumpyBackend`` + + def sample_frequencies(self, probabilities, nshots): + from qibo.config import SHOT_METROPOLIS_THRESHOLD + + if nshots < SHOT_METROPOLIS_THRESHOLD: + return super().sample_frequencies(probabilities, nshots) + + import collections + + seed = np.random.randint(0, int(1e8), dtype="int64") + nqubits = int(np.log2(tuple(probabilities.shape)[0])) + frequencies = np.zeros(2**nqubits, dtype="int64") + # always fall back to numba CPU backend because for ops not implemented on GPU + frequencies = self.measure_frequencies_op( + frequencies, probabilities, nshots, nqubits, seed, self.nthreads + ) + return collections.Counter({i: f for i, f in enumerate(frequencies) if f > 0}) + + # def calculate_frequencies(self, samples): Inherited from ``NumpyBackend`` + + # def assert_allclose(self, value, target, rtol=1e-7, atol=0.0): Inherited from ``NumpyBackend`` diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py new file mode 100644 index 00000000..bca295dc --- /dev/null +++ b/src/qibotn/backends/gpu.py @@ -0,0 +1,39 @@ +from qibo.backends.numpy import NumpyBackend + + +class CuTensorNet(NumpyBackend): # pragma: no cover + # CI does not test for GPU + + def __init__(self): + super().__init__() + import cuquantum # pylint: disable=import-error + from cuquantum import cutensornet as cutn # pylint: disable=import-error + + self.cuquantum = cuquantum + self.cutn = cutn + self.platform = "cutensornet" + self.versions["cuquantum"] = self.cuquantum.__version__ + self.supports_multigpu = True + self.handle = self.cutn.create() + + def __del__(self): + if hasattr(self, "cutn"): + self.cutn.destroy(self.handle) + + def set_precision(self, precision): + if precision != self.precision: + super().set_precision(precision) + + def get_cuda_type(self, dtype="complex64"): + if dtype == "complex128": + return ( + self.cuquantum.cudaDataType.CUDA_C_64F, + self.cuquantum.ComputeType.COMPUTE_64F, + ) + elif dtype == "complex64": + return ( + self.cuquantum.cudaDataType.CUDA_C_32F, + self.cuquantum.ComputeType.COMPUTE_32F, + ) + else: + raise TypeError("Type can be either complex64 or complex128") From 8d20587b209dad8c4862d7c68c30463255f2f064 Mon Sep 17 00:00:00 2001 From: Liwei Yang Date: Fri, 26 Jan 2024 17:56:11 +0800 Subject: [PATCH 093/199] Finish CuTensorNet class for cuQuantum cutensornet --- src/qibotn/backends/gpu.py | 144 ++++++++++++++++++++++++++++++++++++- 1 file changed, 143 insertions(+), 1 deletion(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index bca295dc..e44b6669 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -1,21 +1,38 @@ +import numpy as np + from qibo.backends.numpy import NumpyBackend +from qibo.result import CircuitResult +from qibo.config import raise_error class CuTensorNet(NumpyBackend): # pragma: no cover # CI does not test for GPU - def __init__(self): + def __init__(self, MPI_enabled=False, MPS_enabled=False, NCCL_enabled=False): super().__init__() import cuquantum # pylint: disable=import-error from cuquantum import cutensornet as cutn # pylint: disable=import-error + self.name = "qibotn" self.cuquantum = cuquantum self.cutn = cutn self.platform = "cutensornet" self.versions["cuquantum"] = self.cuquantum.__version__ self.supports_multigpu = True + self.MPI_enabled = MPI_enabled + self.MPS_enabled = MPS_enabled + self.NCCL_enabled = NCCL_enabled self.handle = self.cutn.create() + def apply_gate(self, gate, state, nqubits): # pragma: no cover + raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") + + def apply_gate_density_matrix(self, gate, state, nqubits): # pragma: no cover + raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") + + def assign_measurements(self, measurement_map, circuit_result): + raise_error(NotImplementedError, "Not implemented in QiboTN.") + def __del__(self): if hasattr(self, "cutn"): self.cutn.destroy(self.handle) @@ -37,3 +54,128 @@ def get_cuda_type(self, dtype="complex64"): ) else: raise TypeError("Type can be either complex64 or complex128") + + def execute_circuit( + self, circuit, initial_state=None, nshots=None, return_array=False + ): # pragma: no cover + """Executes a quantum circuit. + + Args: + 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. + + """ + + import qibotn.cutn + + cutn = qibotn.cutn + MPI_enabled = self.MPI_enabled + MPS_enabled = self.MPS_enabled + NCCL_enabled = self.NCCL_enabled + + if ( + MPI_enabled == False + and MPS_enabled == False + and NCCL_enabled == False + ): + if initial_state is not None: + raise_error(NotImplementedError, + "QiboTN cannot support initial state.") + + state = cutn.eval(circuit, self.dtype) + + if ( + MPI_enabled == False + and MPS_enabled == True + and NCCL_enabled == False + ): + if initial_state is not None: + raise_error(NotImplementedError, + "QiboTN cannot support initial state.") + + gate_algo = { + "qr_method": False, + "svd_method": { + "partition": "UV", + "abs_cutoff": 1e-12, + }, + } # make this user input + state = cutn.eval_mps(circuit, gate_algo, self.dtype) + + if ( + MPI_enabled == True + and MPS_enabled == False + and NCCL_enabled == False + ): + if initial_state is not None: + raise_error(NotImplementedError, + "QiboTN cannot support initial state.") + + state, rank = cutn.eval_tn_MPI_2(circuit, self.dtype, 32) + if rank > 0: + state = np.array(0) + + if ( + MPI_enabled == False + and MPS_enabled == False + and NCCL_enabled == True + ): + if initial_state is not None: + raise_error(NotImplementedError, + "QiboTN cannot support initial state.") + + state, rank = cutn.eval_tn_nccl(circuit, self.dtype, 32) + if rank > 0: + state = np.array(0) + + if ( + MPI_enabled == False + and MPS_enabled == False + and NCCL_enabled == False + ): + if initial_state is not None: + raise_error(NotImplementedError, + "QiboTN cannot support initial state.") + + state = cutn.eval_expectation(circuit, self.dtype) + + if ( + MPI_enabled == True + and MPS_enabled == False + and NCCL_enabled == False + ): + if initial_state is not None: + raise_error(NotImplementedError, + "QiboTN cannot support initial state.") + + state, rank = cutn.eval_tn_MPI_2_expectation( + circuit, self.dtype, 32) + + if rank > 0: + state = np.array(0) + + if ( + MPI_enabled == False + and MPS_enabled == False + and NCCL_enabled == True + ): + if initial_state is not None: + raise_error(NotImplementedError, + "QiboTN cannot support initial state.") + + state, rank = cutn.eval_tn_nccl_expectation( + circuit, self.dtype, 32) + + if rank > 0: + state = np.array(0) + + if return_array: + return state.flatten() + else: + circuit._final_state = CircuitResult( + self, circuit, state.flatten(), nshots) + return circuit._final_state From c04a009df3acbc9fd658e24d0e20deda52523b23 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 10:12:09 +0800 Subject: [PATCH 094/199] Rename function call --- src/qibotn/backends/gpu.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index e44b6669..e2087db9 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -115,7 +115,7 @@ def execute_circuit( raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = cutn.eval_tn_MPI_2(circuit, self.dtype, 32) + state, rank = cutn.eval_tn_MPI(circuit, self.dtype, 32) if rank > 0: state = np.array(0) @@ -152,7 +152,7 @@ def execute_circuit( raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = cutn.eval_tn_MPI_2_expectation( + state, rank = cutn.eval_tn_MPI_expectation( circuit, self.dtype, 32) if rank > 0: From 97fa6664a5f8f4cb1f77101dfdda321a708887f1 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 10:22:39 +0800 Subject: [PATCH 095/199] Remove eval_tn_mpi --- src/qibotn/cutn.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/qibotn/cutn.py b/src/qibotn/cutn.py index 86419d2c..353e24c7 100644 --- a/src/qibotn/cutn.py +++ b/src/qibotn/cutn.py @@ -23,6 +23,12 @@ def eval_expectation(qibo_circ, datatype): def eval_tn_MPI_2(qibo_circ, datatype, n_samples=8): + """Convert qibo circuit to tensornet (TN) format and perform contraction using multi node and multi GPU through MPI. + The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. + The pathfinder looks at user defined number of samples (n_samples) iteratively to 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 a dense vector representation of the TN. + """ + from mpi4py import MPI # this line initializes MPI import socket from cuquantum import Network @@ -97,6 +103,17 @@ def eval_tn_MPI_2(qibo_circ, datatype, n_samples=8): # Sum the partial contribution from each process on root. result = comm.reduce(sendobj=result, op=MPI.SUM, root=root) + + """ + path, opt_info = network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads, 'slicing': {'min_slices': max(16, size)}}) + + num_slices = opt_info.num_slices#Andy + chunk, extra = num_slices // size, num_slices % size#Andy + slice_begin = rank * chunk + min(rank, extra)#Andy + slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra)#Andy + slices = range(slice_begin, slice_end)#Andy + result = network.contract(slices=slices) + """ return result, rank From a6c63ede923c6f430e1a1765459c0b596a40864f Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 10:23:33 +0800 Subject: [PATCH 096/199] Remove eval_tn_mpi --- src/qibotn/cutn.py | 75 ---------------------------------------------- 1 file changed, 75 deletions(-) diff --git a/src/qibotn/cutn.py b/src/qibotn/cutn.py index 353e24c7..75c632b5 100644 --- a/src/qibotn/cutn.py +++ b/src/qibotn/cutn.py @@ -434,80 +434,6 @@ def eval_tn_MPI_expectation(qibo_circ, datatype, n_samples=8): return result, rank - -def eval_tn_MPI(qibo_circ, datatype, n_samples=8): - """Convert qibo circuit to tensornet (TN) format and perform contraction using multi node and multi GPU through MPI. - The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. - The pathfinder looks at user defined number of samples (n_samples) iteratively to 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 a dense vector representation of the TN. - """ - - from mpi4py import MPI # this line initializes MPI - import socket - - # Get the hostname - # hostname = socket.gethostname() - - ncpu_threads = multiprocessing.cpu_count() // 2 - - comm = MPI.COMM_WORLD - rank = comm.Get_rank() - size = comm.Get_size() - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) - device_id = rank % getDeviceCount() - cp.cuda.Device(device_id).use() - - handle = cutn.create() - network_opts = cutn.NetworkOptions(handle=handle, blocking="auto") - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft network opts",mem_avail, "rank =",rank) - cutn.distributed_reset_configuration(handle, *cutn.get_mpi_comm_pointer(comm)) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft distributed reset config",mem_avail, "rank =",rank) - # Perform circuit conversion - myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft convetor",mem_avail, "rank =",rank) - operands_interleave = myconvertor.state_vector_operands() - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) - - # Pathfinder: To search for the optimal path. Optimal path are assigned to path and info attribute of the network object. - network = cutn.Network(*operands_interleave, options=network_opts) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft cutn.Network(*operands_interleave,",mem_avail, "rank =",rank) - network.contract_path( - optimize={ - "samples": n_samples, - "threads": ncpu_threads, - "slicing": {"min_slices": max(16, size)}, - } - ) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft contract path",mem_avail, "rank =",rank) - # Execution: To execute the contraction using the optimal path found previously - # print("opt_cost",opt_info.opt_cost, "Process =",rank) - - """ - path, opt_info = network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads, 'slicing': {'min_slices': max(16, size)}}) - - num_slices = opt_info.num_slices#Andy - chunk, extra = num_slices // size, num_slices % size#Andy - slice_begin = rank * chunk + min(rank, extra)#Andy - slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra)#Andy - slices = range(slice_begin, slice_end)#Andy - result = network.contract(slices=slices) - """ - result = network.contract() - - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft contract",mem_avail, "rank =",rank) - cutn.destroy(handle) - - return result, rank - - def eval_mps(qibo_circ, gate_algo, datatype): myconvertor = QiboCircuitToMPS(qibo_circ, gate_algo, dtype=datatype) mps_helper = MPSContractionHelper(myconvertor.num_qubits) @@ -516,7 +442,6 @@ def eval_mps(qibo_circ, gate_algo, datatype): myconvertor.mps_tensors, {"handle": myconvertor.handle} ) - def PauliStringGen(nqubits): if nqubits <= 0: return "Invalid input. N should be a positive integer." From 34bfe159095c957555ee9c1e6b54b990b0c07de0 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 10:24:11 +0800 Subject: [PATCH 097/199] Rename eval_tn_MPI_2 to eval_tn_MPI --- src/qibotn/cutn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/cutn.py b/src/qibotn/cutn.py index 75c632b5..5c06e42e 100644 --- a/src/qibotn/cutn.py +++ b/src/qibotn/cutn.py @@ -22,7 +22,7 @@ def eval_expectation(qibo_circ, datatype): ) -def eval_tn_MPI_2(qibo_circ, datatype, n_samples=8): +def eval_tn_MPI(qibo_circ, datatype, n_samples=8): """Convert qibo circuit to tensornet (TN) format and perform contraction using multi node and multi GPU through MPI. The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. The pathfinder looks at user defined number of samples (n_samples) iteratively to select the least costly contraction path. This is sped up with multi thread. From 1c61fa4b661487f1596eea689696842bf2451ab8 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 10:34:10 +0800 Subject: [PATCH 098/199] Remove eval_tn_MPI_expectation --- src/qibotn/cutn.py | 65 ---------------------------------------------- 1 file changed, 65 deletions(-) diff --git a/src/qibotn/cutn.py b/src/qibotn/cutn.py index 5c06e42e..4d5c3f2f 100644 --- a/src/qibotn/cutn.py +++ b/src/qibotn/cutn.py @@ -369,71 +369,6 @@ def eval_tn_MPI_2_expectation(qibo_circ, datatype, n_samples=8): return result, rank - -def eval_tn_MPI_expectation(qibo_circ, datatype, n_samples=8): - from mpi4py import MPI # this line initializes MPI - import socket - - # Get the hostname - # hostname = socket.gethostname() - - ncpu_threads = multiprocessing.cpu_count() // 2 - - comm = MPI.COMM_WORLD - rank = comm.Get_rank() - size = comm.Get_size() - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) - device_id = rank % getDeviceCount() - cp.cuda.Device(device_id).use() - - handle = cutn.create() - network_opts = cutn.NetworkOptions(handle=handle, blocking="auto") - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft network opts",mem_avail, "rank =",rank) - cutn.distributed_reset_configuration(handle, *cutn.get_mpi_comm_pointer(comm)) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft distributed reset config",mem_avail, "rank =",rank) - # Perform circuit conversion - myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - operands_interleave = myconvertor.expectation_operands( - PauliStringGen(qibo_circ.nqubits) - ) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft convetor",mem_avail, "rank =",rank) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) - - # Pathfinder: To search for the optimal path. Optimal path are assigned to path and info attribute of the network object. - network = cutn.Network(*operands_interleave, options=network_opts) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft cutn.Network(*operands_interleave,",mem_avail, "rank =",rank) - path, opt_info = network.contract_path( - optimize={ - "samples": n_samples, - "threads": ncpu_threads, - "slicing": {"min_slices": max(16, size)}, - } - ) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft contract path",mem_avail, "rank =",rank) - # Execution: To execute the contraction using the optimal path found previously - # print("opt_cost",opt_info.opt_cost, "Process =",rank) - - num_slices = opt_info.num_slices # Andy - chunk, extra = num_slices // size, num_slices % size # Andy - slice_begin = rank * chunk + min(rank, extra) # Andy - slice_end = ( - num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra) - ) # Andy - slices = range(slice_begin, slice_end) # Andy - result = network.contract(slices=slices) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft contract",mem_avail, "rank =",rank) - cutn.destroy(handle) - - return result, rank - def eval_mps(qibo_circ, gate_algo, datatype): myconvertor = QiboCircuitToMPS(qibo_circ, gate_algo, dtype=datatype) mps_helper = MPSContractionHelper(myconvertor.num_qubits) From 9bca7a061472679729ff3c24a7fbaf2b14a4ec7b Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 10:34:47 +0800 Subject: [PATCH 099/199] Rename eval_tn_MPI_2_expectation to eval_tn_MPI_expectation --- src/qibotn/cutn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/cutn.py b/src/qibotn/cutn.py index 4d5c3f2f..b6213cf7 100644 --- a/src/qibotn/cutn.py +++ b/src/qibotn/cutn.py @@ -291,7 +291,7 @@ def eval_tn_nccl_expectation(qibo_circ, datatype, n_samples=8): return result, rank -def eval_tn_MPI_2_expectation(qibo_circ, datatype, n_samples=8): +def eval_tn_MPI_expectation(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket from cuquantum import Network From 15bab605a2a285962cfe0bef6d73045570fe212f Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 10:41:26 +0800 Subject: [PATCH 100/199] Format with black --- src/qibotn/cutn.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/qibotn/cutn.py b/src/qibotn/cutn.py index b6213cf7..ea5dad9a 100644 --- a/src/qibotn/cutn.py +++ b/src/qibotn/cutn.py @@ -28,7 +28,7 @@ def eval_tn_MPI(qibo_circ, datatype, n_samples=8): The pathfinder looks at user defined number of samples (n_samples) iteratively to 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 a dense vector representation of the TN. """ - + from mpi4py import MPI # this line initializes MPI import socket from cuquantum import Network @@ -103,7 +103,7 @@ def eval_tn_MPI(qibo_circ, datatype, n_samples=8): # Sum the partial contribution from each process on root. result = comm.reduce(sendobj=result, op=MPI.SUM, root=root) - + """ path, opt_info = network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads, 'slicing': {'min_slices': max(16, size)}}) @@ -369,6 +369,7 @@ def eval_tn_MPI_expectation(qibo_circ, datatype, n_samples=8): return result, rank + def eval_mps(qibo_circ, gate_algo, datatype): myconvertor = QiboCircuitToMPS(qibo_circ, gate_algo, dtype=datatype) mps_helper = MPSContractionHelper(myconvertor.num_qubits) @@ -377,6 +378,7 @@ def eval_mps(qibo_circ, gate_algo, datatype): myconvertor.mps_tensors, {"handle": myconvertor.handle} ) + def PauliStringGen(nqubits): if nqubits <= 0: return "Invalid input. N should be a positive integer." From f9b9e5f531abfe963fc94828027b4664c9689991 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 10:43:28 +0800 Subject: [PATCH 101/199] Rename to better reflect function of file --- src/qibotn/backends/gpu.py | 4 ++-- src/qibotn/{cutn.py => eval.py} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename src/qibotn/{cutn.py => eval.py} (100%) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index e2087db9..96eaab33 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -70,9 +70,9 @@ def execute_circuit( """ - import qibotn.cutn + import qibotn.src.qibotn.eval - cutn = qibotn.cutn + cutn = qibotn.eval MPI_enabled = self.MPI_enabled MPS_enabled = self.MPS_enabled NCCL_enabled = self.NCCL_enabled diff --git a/src/qibotn/cutn.py b/src/qibotn/eval.py similarity index 100% rename from src/qibotn/cutn.py rename to src/qibotn/eval.py From 8157557fdce0494807e751cea93be6144b841a96 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 10:55:22 +0800 Subject: [PATCH 102/199] Add condition to trigger expectation calculation --- src/qibotn/backends/gpu.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index 96eaab33..2d28987d 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -8,7 +8,7 @@ class CuTensorNet(NumpyBackend): # pragma: no cover # CI does not test for GPU - def __init__(self, MPI_enabled=False, MPS_enabled=False, NCCL_enabled=False): + def __init__(self, MPI_enabled=False, MPS_enabled=False, NCCL_enabled=False, expectation_enabled=False): super().__init__() import cuquantum # pylint: disable=import-error from cuquantum import cutensornet as cutn # pylint: disable=import-error @@ -22,6 +22,7 @@ def __init__(self, MPI_enabled=False, MPS_enabled=False, NCCL_enabled=False): self.MPI_enabled = MPI_enabled self.MPS_enabled = MPS_enabled self.NCCL_enabled = NCCL_enabled + self.expectation_enabled = expectation_enabled self.handle = self.cutn.create() def apply_gate(self, gate, state, nqubits): # pragma: no cover @@ -76,11 +77,13 @@ def execute_circuit( MPI_enabled = self.MPI_enabled MPS_enabled = self.MPS_enabled NCCL_enabled = self.NCCL_enabled + expectation_enabled = self.expectation_enabled if ( MPI_enabled == False and MPS_enabled == False and NCCL_enabled == False + and expectation_enabled == False ): if initial_state is not None: raise_error(NotImplementedError, @@ -92,6 +95,7 @@ def execute_circuit( MPI_enabled == False and MPS_enabled == True and NCCL_enabled == False + and expectation_enabled == False ): if initial_state is not None: raise_error(NotImplementedError, @@ -110,6 +114,7 @@ def execute_circuit( MPI_enabled == True and MPS_enabled == False and NCCL_enabled == False + and expectation_enabled == False ): if initial_state is not None: raise_error(NotImplementedError, @@ -123,6 +128,7 @@ def execute_circuit( MPI_enabled == False and MPS_enabled == False and NCCL_enabled == True + and expectation_enabled == False ): if initial_state is not None: raise_error(NotImplementedError, @@ -136,6 +142,7 @@ def execute_circuit( MPI_enabled == False and MPS_enabled == False and NCCL_enabled == False + and expectation_enabled == True ): if initial_state is not None: raise_error(NotImplementedError, @@ -147,6 +154,7 @@ def execute_circuit( MPI_enabled == True and MPS_enabled == False and NCCL_enabled == False + and expectation_enabled == True ): if initial_state is not None: raise_error(NotImplementedError, @@ -162,6 +170,7 @@ def execute_circuit( MPI_enabled == False and MPS_enabled == False and NCCL_enabled == True + and expectation_enabled == True ): if initial_state is not None: raise_error(NotImplementedError, From a0ad2af0c930ab4fec5021c4dcfca8ac6d3f1888 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 14:37:33 +0800 Subject: [PATCH 103/199] Change import from cutn to eval [skip CI] --- tests/test_cuquantum_cutensor_backend.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_cuquantum_cutensor_backend.py b/tests/test_cuquantum_cutensor_backend.py index b3a1c053..97d9828b 100644 --- a/tests/test_cuquantum_cutensor_backend.py +++ b/tests/test_cuquantum_cutensor_backend.py @@ -32,7 +32,7 @@ def test_eval(nqubits: int, dtype="complex128"): dtype (str): The data type for precision, 'complex64' for single, 'complex128' for double. """ - import qibotn.cutn + import qibotn.eval # Test qibo qibo.set_backend(backend=config.qibo.backend, platform=config.qibo.platform) @@ -55,7 +55,7 @@ def test_mps(nqubits: int, dtype="complex128"): dtype (str): The data type for precision, 'complex64' for single, 'complex128' for double. """ - import qibotn.cutn + import qibotn.eval # Test qibo qibo.set_backend(backend=config.qibo.backend, platform=config.qibo.platform) @@ -74,7 +74,7 @@ def test_mps(nqubits: int, dtype="complex128"): } cutn_time, result_tn = time( - lambda: qibotn.cutn.eval_mps(circ_qibo, gate_algo, dtype).flatten() + lambda: qibotn.eval.eval_mps(circ_qibo, gate_algo, dtype).flatten() ) print(f"State vector difference: {abs(result_tn - result_sv_cp).max():0.3e}") From 93514a51f6ca4131234b1362ae846bf1a958b544 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 15:40:11 +0800 Subject: [PATCH 104/199] Rename function name to be more descriptive [skip CI] --- src/qibotn/eval.py | 14 +++++++------- tests/test_cuquantum_cutensor_backend.py | 6 ++++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/qibotn/eval.py b/src/qibotn/eval.py index ea5dad9a..ddf2bfc3 100644 --- a/src/qibotn/eval.py +++ b/src/qibotn/eval.py @@ -10,19 +10,19 @@ from qibotn.QiboCircuitToMPS import QiboCircuitToMPS -def eval(qibo_circ, datatype): +def dense_vector_tn(qibo_circ, datatype): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract(*myconvertor.state_vector_operands()) -def eval_expectation(qibo_circ, datatype): +def expectation_tn(qibo_circ, datatype): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract( *myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) ) -def eval_tn_MPI(qibo_circ, datatype, n_samples=8): +def dense_vector_tn_MPI(qibo_circ, datatype, n_samples=8): """Convert qibo circuit to tensornet (TN) format and perform contraction using multi node and multi GPU through MPI. The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. The pathfinder looks at user defined number of samples (n_samples) iteratively to select the least costly contraction path. This is sped up with multi thread. @@ -118,7 +118,7 @@ def eval_tn_MPI(qibo_circ, datatype, n_samples=8): return result, rank -def eval_tn_nccl(qibo_circ, datatype, n_samples=8): +def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket from cuquantum import Network @@ -204,7 +204,7 @@ def eval_tn_nccl(qibo_circ, datatype, n_samples=8): return result, rank -def eval_tn_nccl_expectation(qibo_circ, datatype, n_samples=8): +def expectation_tn_nccl(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket from cuquantum import Network @@ -291,7 +291,7 @@ def eval_tn_nccl_expectation(qibo_circ, datatype, n_samples=8): return result, rank -def eval_tn_MPI_expectation(qibo_circ, datatype, n_samples=8): +def expectation_tn_MPI(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket from cuquantum import Network @@ -370,7 +370,7 @@ def eval_tn_MPI_expectation(qibo_circ, datatype, n_samples=8): return result, rank -def eval_mps(qibo_circ, gate_algo, datatype): +def dense_vector_mps(qibo_circ, gate_algo, datatype): myconvertor = QiboCircuitToMPS(qibo_circ, gate_algo, dtype=datatype) mps_helper = MPSContractionHelper(myconvertor.num_qubits) diff --git a/tests/test_cuquantum_cutensor_backend.py b/tests/test_cuquantum_cutensor_backend.py index 97d9828b..c8f1e199 100644 --- a/tests/test_cuquantum_cutensor_backend.py +++ b/tests/test_cuquantum_cutensor_backend.py @@ -39,7 +39,9 @@ def test_eval(nqubits: int, dtype="complex128"): qibo_time, (qibo_circ, result_sv) = time(lambda: qibo_qft(nqubits, swaps=True)) # Test Cuquantum - cutn_time, result_tn = time(lambda: qibotn.cutn.eval(qibo_circ, dtype).flatten()) + cutn_time, result_tn = time( + lambda: qibotn.eval.dense_vector_tn(qibo_circ, dtype).flatten() + ) assert 1e-2 * qibo_time < cutn_time < 1e2 * qibo_time assert np.allclose(result_sv, result_tn), "Resulting dense vectors do not match" @@ -74,7 +76,7 @@ def test_mps(nqubits: int, dtype="complex128"): } cutn_time, result_tn = time( - lambda: qibotn.eval.eval_mps(circ_qibo, gate_algo, dtype).flatten() + lambda: qibotn.eval.dense_vector_mps(circ_qibo, gate_algo, dtype).flatten() ) print(f"State vector difference: {abs(result_tn - result_sv_cp).max():0.3e}") From 6bdf100b3b80ad3a6533ec9fe2274d5f328f45f0 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 15:54:53 +0800 Subject: [PATCH 105/199] Update function call name [skip CI] --- src/qibotn/backends/gpu.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index 2d28987d..47f667fa 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -71,9 +71,8 @@ def execute_circuit( """ - import qibotn.src.qibotn.eval + import qibotn.eval as eval - cutn = qibotn.eval MPI_enabled = self.MPI_enabled MPS_enabled = self.MPS_enabled NCCL_enabled = self.NCCL_enabled @@ -89,7 +88,7 @@ def execute_circuit( raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state = cutn.eval(circuit, self.dtype) + state = eval.dense_vector_tn(circuit, self.dtype) if ( MPI_enabled == False @@ -108,7 +107,7 @@ def execute_circuit( "abs_cutoff": 1e-12, }, } # make this user input - state = cutn.eval_mps(circuit, gate_algo, self.dtype) + state = eval.dense_vector_mps(circuit, gate_algo, self.dtype) if ( MPI_enabled == True @@ -120,7 +119,7 @@ def execute_circuit( raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = cutn.eval_tn_MPI(circuit, self.dtype, 32) + state, rank = eval.dense_vector_tn_MPI(circuit, self.dtype, 32) if rank > 0: state = np.array(0) @@ -134,7 +133,7 @@ def execute_circuit( raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = cutn.eval_tn_nccl(circuit, self.dtype, 32) + state, rank = eval.dense_vector_tn_nccl(circuit, self.dtype, 32) if rank > 0: state = np.array(0) @@ -148,7 +147,7 @@ def execute_circuit( raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state = cutn.eval_expectation(circuit, self.dtype) + state = eval.expectation_tn(circuit, self.dtype) if ( MPI_enabled == True @@ -160,7 +159,7 @@ def execute_circuit( raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = cutn.eval_tn_MPI_expectation( + state, rank = eval.expectation_tn_MPI( circuit, self.dtype, 32) if rank > 0: @@ -176,7 +175,7 @@ def execute_circuit( raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = cutn.eval_tn_nccl_expectation( + state, rank = eval.expectation_tn_nccl( circuit, self.dtype, 32) if rank > 0: From 34acff936365dddc1af0e9e885890cf837d78e65 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 30 Jan 2024 17:49:30 +0800 Subject: [PATCH 106/199] Change execute_circuit inputs --- src/qibotn/backends/gpu.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index 47f667fa..5f413442 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -1,14 +1,14 @@ import numpy as np from qibo.backends.numpy import NumpyBackend -from qibo.result import CircuitResult +from qibo.states import CircuitResult from qibo.config import raise_error class CuTensorNet(NumpyBackend): # pragma: no cover # CI does not test for GPU - def __init__(self, MPI_enabled=False, MPS_enabled=False, NCCL_enabled=False, expectation_enabled=False): + def __init__(self): super().__init__() import cuquantum # pylint: disable=import-error from cuquantum import cutensornet as cutn # pylint: disable=import-error @@ -19,10 +19,6 @@ def __init__(self, MPI_enabled=False, MPS_enabled=False, NCCL_enabled=False, exp self.platform = "cutensornet" self.versions["cuquantum"] = self.cuquantum.__version__ self.supports_multigpu = True - self.MPI_enabled = MPI_enabled - self.MPS_enabled = MPS_enabled - self.NCCL_enabled = NCCL_enabled - self.expectation_enabled = expectation_enabled self.handle = self.cutn.create() def apply_gate(self, gate, state, nqubits): # pragma: no cover @@ -57,7 +53,7 @@ def get_cuda_type(self, dtype="complex64"): raise TypeError("Type can be either complex64 or complex128") def execute_circuit( - self, circuit, initial_state=None, nshots=None, return_array=False + self, circuit, test, MPI_enabled=False, MPS_enabled=False, NCCL_enabled=False, expectation_enabled=False, initial_state=None, nshots=None, return_array=False ): # pragma: no cover """Executes a quantum circuit. @@ -72,12 +68,7 @@ def execute_circuit( """ import qibotn.eval as eval - - MPI_enabled = self.MPI_enabled - MPS_enabled = self.MPS_enabled - NCCL_enabled = self.NCCL_enabled - expectation_enabled = self.expectation_enabled - + print("Test", test) if ( MPI_enabled == False and MPS_enabled == False From c43170ffa456660effc8fe4151b080244c4c3db9 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 31 Jan 2024 10:02:34 +0800 Subject: [PATCH 107/199] Rename expectation function name --- src/qibotn/backends/gpu.py | 12 ++++++++---- src/qibotn/eval.py | 6 +++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index 5f413442..6301bf7c 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -53,7 +53,7 @@ def get_cuda_type(self, dtype="complex64"): raise TypeError("Type can be either complex64 or complex128") def execute_circuit( - self, circuit, test, MPI_enabled=False, MPS_enabled=False, NCCL_enabled=False, expectation_enabled=False, initial_state=None, nshots=None, return_array=False + self, circuit, MPI_enabled=False, MPS_enabled=False, NCCL_enabled=False, expectation_enabled=False, initial_state=None, nshots=None, return_array=False ): # pragma: no cover """Executes a quantum circuit. @@ -68,7 +68,11 @@ def execute_circuit( """ import qibotn.eval as eval - print("Test", test) + print("MPI_enabled", MPI_enabled) + print("MPS_enabled", MPS_enabled) + print("NCCL_enabled", NCCL_enabled) + print("expectation_enabled", expectation_enabled) + if ( MPI_enabled == False and MPS_enabled == False @@ -150,7 +154,7 @@ def execute_circuit( raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.expectation_tn_MPI( + state, rank = eval.expectation_pauli_tn_MPI( circuit, self.dtype, 32) if rank > 0: @@ -166,7 +170,7 @@ def execute_circuit( raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.expectation_tn_nccl( + state, rank = eval.expectation_pauli_tn_nccl( circuit, self.dtype, 32) if rank > 0: diff --git a/src/qibotn/eval.py b/src/qibotn/eval.py index ddf2bfc3..16ae0468 100644 --- a/src/qibotn/eval.py +++ b/src/qibotn/eval.py @@ -15,7 +15,7 @@ def dense_vector_tn(qibo_circ, datatype): return contract(*myconvertor.state_vector_operands()) -def expectation_tn(qibo_circ, datatype): +def expectation_pauli_tn(qibo_circ, datatype): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract( *myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) @@ -204,7 +204,7 @@ def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8): return result, rank -def expectation_tn_nccl(qibo_circ, datatype, n_samples=8): +def expectation_pauli_tn_nccl(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket from cuquantum import Network @@ -291,7 +291,7 @@ def expectation_tn_nccl(qibo_circ, datatype, n_samples=8): return result, rank -def expectation_tn_MPI(qibo_circ, datatype, n_samples=8): +def expectation_pauli_tn_MPI(qibo_circ, datatype, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket from cuquantum import Network From 182dc4b00fffa4c2f032342eae60204a24471648 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 31 Jan 2024 10:52:09 +0800 Subject: [PATCH 108/199] Use runcard to select compute type as no other way to pass in para wout changing struct --- src/qibotn/backends/gpu.py | 130 +++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 62 deletions(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index 6301bf7c..ad82b75f 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -8,11 +8,24 @@ class CuTensorNet(NumpyBackend): # pragma: no cover # CI does not test for GPU - def __init__(self): + def __init__(self, runcard): super().__init__() import cuquantum # pylint: disable=import-error from cuquantum import cutensornet as cutn # pylint: disable=import-error + if runcard is not None: + print("inside runcard") + # Parse the runcard or use its values to set flags + self.MPI_enabled = runcard.get("MPI_enabled", False) + self.MPS_enabled = runcard.get("MPS_enabled", False) + self.NCCL_enabled = runcard.get("NCCL_enabled", False) + self.expectation_enabled = runcard.get("expectation_enabled", False) + else: + self.MPI_enabled = False + self.MPS_enabled = False + self.NCCL_enabled = False + self.expectation_enabled = False + self.name = "qibotn" self.cuquantum = cuquantum self.cutn = cutn @@ -53,7 +66,7 @@ def get_cuda_type(self, dtype="complex64"): raise TypeError("Type can be either complex64 or complex128") def execute_circuit( - self, circuit, MPI_enabled=False, MPS_enabled=False, NCCL_enabled=False, expectation_enabled=False, initial_state=None, nshots=None, return_array=False + self, circuit, initial_state=None, nshots=None, return_array=False ): # pragma: no cover """Executes a quantum circuit. @@ -68,32 +81,31 @@ def execute_circuit( """ import qibotn.eval as eval - print("MPI_enabled", MPI_enabled) - print("MPS_enabled", MPS_enabled) - print("NCCL_enabled", NCCL_enabled) - print("expectation_enabled", expectation_enabled) - + + print("MPI_enabled", self.MPI_enabled) + print("MPS_enabled", self.MPS_enabled) + print("NCCL_enabled", self.NCCL_enabled) + print("expectation_enabled", self.expectation_enabled) + if ( - MPI_enabled == False - and MPS_enabled == False - and NCCL_enabled == False - and expectation_enabled == False + self.MPI_enabled == False + and self.MPS_enabled == False + and self.NCCL_enabled == False + and self.expectation_enabled == False ): if initial_state is not None: - raise_error(NotImplementedError, - "QiboTN cannot support initial state.") + raise_error(NotImplementedError, "QiboTN cannot support initial state.") state = eval.dense_vector_tn(circuit, self.dtype) - if ( - MPI_enabled == False - and MPS_enabled == True - and NCCL_enabled == False - and expectation_enabled == False + elif ( + self.MPI_enabled == False + and self.MPS_enabled == True + and self.NCCL_enabled == False + and self.expectation_enabled == False ): if initial_state is not None: - raise_error(NotImplementedError, - "QiboTN cannot support initial state.") + raise_error(NotImplementedError, "QiboTN cannot support initial state.") gate_algo = { "qr_method": False, @@ -104,81 +116,75 @@ def execute_circuit( } # make this user input state = eval.dense_vector_mps(circuit, gate_algo, self.dtype) - if ( - MPI_enabled == True - and MPS_enabled == False - and NCCL_enabled == False - and expectation_enabled == False + elif ( + self.MPI_enabled == True + and self.MPS_enabled == False + and self.NCCL_enabled == False + and self.expectation_enabled == False ): if initial_state is not None: - raise_error(NotImplementedError, - "QiboTN cannot support initial state.") + raise_error(NotImplementedError, "QiboTN cannot support initial state.") state, rank = eval.dense_vector_tn_MPI(circuit, self.dtype, 32) if rank > 0: state = np.array(0) - if ( - MPI_enabled == False - and MPS_enabled == False - and NCCL_enabled == True - and expectation_enabled == False + elif ( + self.MPI_enabled == False + and self.MPS_enabled == False + and self.NCCL_enabled == True + and self.expectation_enabled == False ): if initial_state is not None: - raise_error(NotImplementedError, - "QiboTN cannot support initial state.") + raise_error(NotImplementedError, "QiboTN cannot support initial state.") state, rank = eval.dense_vector_tn_nccl(circuit, self.dtype, 32) if rank > 0: state = np.array(0) - if ( - MPI_enabled == False - and MPS_enabled == False - and NCCL_enabled == False - and expectation_enabled == True + elif ( + self.MPI_enabled == False + and self.MPS_enabled == False + and self.NCCL_enabled == False + and self.expectation_enabled == True ): if initial_state is not None: - raise_error(NotImplementedError, - "QiboTN cannot support initial state.") + raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state = eval.expectation_tn(circuit, self.dtype) + state = eval.expectation_pauli_tn(circuit, self.dtype) - if ( - MPI_enabled == True - and MPS_enabled == False - and NCCL_enabled == False - and expectation_enabled == True + elif ( + self.MPI_enabled == True + and self.MPS_enabled == False + and self.NCCL_enabled == False + and self.expectation_enabled == True ): if initial_state is not None: - raise_error(NotImplementedError, - "QiboTN cannot support initial state.") + raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.expectation_pauli_tn_MPI( - circuit, self.dtype, 32) + state, rank = eval.expectation_pauli_tn_MPI(circuit, self.dtype, 32) if rank > 0: state = np.array(0) - if ( - MPI_enabled == False - and MPS_enabled == False - and NCCL_enabled == True - and expectation_enabled == True + elif ( + self.MPI_enabled == False + and self.MPS_enabled == False + and self.NCCL_enabled == True + and self.expectation_enabled == True ): if initial_state is not None: - raise_error(NotImplementedError, - "QiboTN cannot support initial state.") + raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.expectation_pauli_tn_nccl( - circuit, self.dtype, 32) + state, rank = eval.expectation_pauli_tn_nccl(circuit, self.dtype, 32) if rank > 0: state = np.array(0) + else: + raise_error(NotImplementedError, "Backend not supported.") if return_array: return state.flatten() else: - circuit._final_state = CircuitResult( - self, circuit, state.flatten(), nshots) + circuit._final_state = CircuitResult(self, circuit, state.flatten(), nshots) return circuit._final_state From 928f99e336c5e335d4c4af4c2be8b155ab130e4b Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 31 Jan 2024 10:56:47 +0800 Subject: [PATCH 109/199] Remove print [skip CI] --- src/qibotn/backends/gpu.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index ad82b75f..d758d5a1 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -14,8 +14,6 @@ def __init__(self, runcard): from cuquantum import cutensornet as cutn # pylint: disable=import-error if runcard is not None: - print("inside runcard") - # Parse the runcard or use its values to set flags self.MPI_enabled = runcard.get("MPI_enabled", False) self.MPS_enabled = runcard.get("MPS_enabled", False) self.NCCL_enabled = runcard.get("NCCL_enabled", False) @@ -82,11 +80,6 @@ def execute_circuit( import qibotn.eval as eval - print("MPI_enabled", self.MPI_enabled) - print("MPS_enabled", self.MPS_enabled) - print("NCCL_enabled", self.NCCL_enabled) - print("expectation_enabled", self.expectation_enabled) - if ( self.MPI_enabled == False and self.MPS_enabled == False @@ -181,7 +174,7 @@ def execute_circuit( if rank > 0: state = np.array(0) else: - raise_error(NotImplementedError, "Backend not supported.") + raise_error(NotImplementedError, "Compute type not supported.") if return_array: return state.flatten() From 784b1a70ef70c160c92cd7e7679bcd412650037f Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 31 Jan 2024 14:56:21 +0800 Subject: [PATCH 110/199] Allow user to specify Pauli string pattern for expecation calculation [skip CI] --- src/qibotn/backends/gpu.py | 33 ++++++++++++++++++++++++++++----- src/qibotn/eval.py | 20 ++++++++++---------- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index d758d5a1..c133f4e4 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -12,12 +12,35 @@ def __init__(self, runcard): super().__init__() import cuquantum # pylint: disable=import-error from cuquantum import cutensornet as cutn # pylint: disable=import-error - + + self.pauli_string_pattern = "XXXZ" if runcard is not None: self.MPI_enabled = runcard.get("MPI_enabled", False) self.MPS_enabled = runcard.get("MPS_enabled", False) self.NCCL_enabled = runcard.get("NCCL_enabled", False) - self.expectation_enabled = runcard.get("expectation_enabled", False) + + expectation_enabled_value = runcard.get('expectation_enabled') + + if expectation_enabled_value is True: + self.expectation_enabled = True + + print("expectation_enabled is",self.expectation_enabled) + elif expectation_enabled_value is False: + self.expectation_enabled = False + + print("expectation_enabled is",self.expectation_enabled) + elif isinstance(expectation_enabled_value, dict): + self.expectation_enabled = True + expectation_enabled_dict = runcard.get('expectation_enabled', {}) + + self.pauli_string_pattern = expectation_enabled_dict.get('pauli_string_pattern', None) + + print("expectation_enabled is a dictionary",self.expectation_enabled,self.pauli_string_pattern ) + else: + raise TypeError("expectation_enabled has an unexpected type") + + + else: self.MPI_enabled = False self.MPS_enabled = False @@ -144,7 +167,7 @@ def execute_circuit( if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state = eval.expectation_pauli_tn(circuit, self.dtype) + state = eval.expectation_pauli_tn(circuit, self.dtype, self.pauli_string_pattern) elif ( self.MPI_enabled == True @@ -155,7 +178,7 @@ def execute_circuit( if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.expectation_pauli_tn_MPI(circuit, self.dtype, 32) + state, rank = eval.expectation_pauli_tn_MPI(circuit, self.dtype, self.pauli_string_pattern, 32) if rank > 0: state = np.array(0) @@ -169,7 +192,7 @@ def execute_circuit( if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.expectation_pauli_tn_nccl(circuit, self.dtype, 32) + state, rank = eval.expectation_pauli_tn_nccl(circuit, self.dtype, self.pauli_string_pattern, 32) if rank > 0: state = np.array(0) diff --git a/src/qibotn/eval.py b/src/qibotn/eval.py index 16ae0468..68ea462b 100644 --- a/src/qibotn/eval.py +++ b/src/qibotn/eval.py @@ -15,10 +15,10 @@ def dense_vector_tn(qibo_circ, datatype): return contract(*myconvertor.state_vector_operands()) -def expectation_pauli_tn(qibo_circ, datatype): +def expectation_pauli_tn(qibo_circ, datatype, pauli_string): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract( - *myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) + *myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits, pauli_string)) ) @@ -204,7 +204,7 @@ def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8): return result, rank -def expectation_pauli_tn_nccl(qibo_circ, datatype, n_samples=8): +def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket from cuquantum import Network @@ -232,7 +232,7 @@ def expectation_pauli_tn_nccl(qibo_circ, datatype, n_samples=8): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft convetor",mem_avail, "rank =",rank) - operands = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) + operands = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits, pauli_string)) # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) @@ -291,7 +291,7 @@ def expectation_pauli_tn_nccl(qibo_circ, datatype, n_samples=8): return result, rank -def expectation_pauli_tn_MPI(qibo_circ, datatype, n_samples=8): +def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string, n_samples=8): from mpi4py import MPI # this line initializes MPI import socket from cuquantum import Network @@ -311,7 +311,7 @@ def expectation_pauli_tn_MPI(qibo_circ, datatype, n_samples=8): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft convetor",mem_avail, "rank =",rank) - operands = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits)) + operands = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits, pauli_string)) # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) @@ -379,17 +379,17 @@ def dense_vector_mps(qibo_circ, gate_algo, datatype): ) -def PauliStringGen(nqubits): +def PauliStringGen(nqubits, pauli_string): if nqubits <= 0: return "Invalid input. N should be a positive integer." - # characters = 'IXYZ' - characters = "XXXZ" + characters = pauli_string + #characters = "XXXZ" result = "" for i in range(nqubits): char_to_add = characters[i % len(characters)] result += char_to_add - + print("pauli string", result) return result From 05f8523649073b7bca9ead084256f16953d21f46 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 31 Jan 2024 16:47:10 +0800 Subject: [PATCH 111/199] Update to allow user to set MPS parameters and to set Pauli string pattern --- src/qibotn/backends/gpu.py | 60 +++++++++++++++++++++----------------- src/qibotn/eval.py | 14 ++++++--- 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index c133f4e4..5777fe90 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -12,34 +12,43 @@ def __init__(self, runcard): super().__init__() import cuquantum # pylint: disable=import-error from cuquantum import cutensornet as cutn # pylint: disable=import-error - - self.pauli_string_pattern = "XXXZ" + if runcard is not None: self.MPI_enabled = runcard.get("MPI_enabled", False) - self.MPS_enabled = runcard.get("MPS_enabled", False) self.NCCL_enabled = runcard.get("NCCL_enabled", False) - - expectation_enabled_value = runcard.get('expectation_enabled') + expectation_enabled_value = runcard.get("expectation_enabled") if expectation_enabled_value is True: self.expectation_enabled = True - - print("expectation_enabled is",self.expectation_enabled) + self.pauli_string_pattern = "XXXZ" elif expectation_enabled_value is False: self.expectation_enabled = False - - print("expectation_enabled is",self.expectation_enabled) elif isinstance(expectation_enabled_value, dict): self.expectation_enabled = True - expectation_enabled_dict = runcard.get('expectation_enabled', {}) - - self.pauli_string_pattern = expectation_enabled_dict.get('pauli_string_pattern', None) - - print("expectation_enabled is a dictionary",self.expectation_enabled,self.pauli_string_pattern ) + expectation_enabled_dict = runcard.get("expectation_enabled", {}) + self.pauli_string_pattern = expectation_enabled_dict.get( + "pauli_string_pattern", None + ) else: raise TypeError("expectation_enabled has an unexpected type") - + mps_enabled_value = runcard.get("MPS_enabled") + if mps_enabled_value is True: + self.MPS_enabled = True + self.gate_algo = { + "qr_method": False, + "svd_method": { + "partition": "UV", + "abs_cutoff": 1e-12, + }, + } + elif mps_enabled_value is False: + self.MPS_enabled = False + elif isinstance(mps_enabled_value, dict): + self.MPS_enabled = True + self.gate_algo = runcard.get("MPS_enabled", {}) + else: + raise TypeError("MPS_enabled has an unexpected type") else: self.MPI_enabled = False @@ -123,14 +132,7 @@ def execute_circuit( if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - gate_algo = { - "qr_method": False, - "svd_method": { - "partition": "UV", - "abs_cutoff": 1e-12, - }, - } # make this user input - state = eval.dense_vector_mps(circuit, gate_algo, self.dtype) + state = eval.dense_vector_mps(circuit, self.gate_algo, self.dtype) elif ( self.MPI_enabled == True @@ -167,7 +169,9 @@ def execute_circuit( if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state = eval.expectation_pauli_tn(circuit, self.dtype, self.pauli_string_pattern) + state = eval.expectation_pauli_tn( + circuit, self.dtype, self.pauli_string_pattern + ) elif ( self.MPI_enabled == True @@ -178,7 +182,9 @@ def execute_circuit( if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.expectation_pauli_tn_MPI(circuit, self.dtype, self.pauli_string_pattern, 32) + state, rank = eval.expectation_pauli_tn_MPI( + circuit, self.dtype, self.pauli_string_pattern, 32 + ) if rank > 0: state = np.array(0) @@ -192,7 +198,9 @@ def execute_circuit( if initial_state is not None: raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.expectation_pauli_tn_nccl(circuit, self.dtype, self.pauli_string_pattern, 32) + state, rank = eval.expectation_pauli_tn_nccl( + circuit, self.dtype, self.pauli_string_pattern, 32 + ) if rank > 0: state = np.array(0) diff --git a/src/qibotn/eval.py b/src/qibotn/eval.py index 68ea462b..0e10d7bb 100644 --- a/src/qibotn/eval.py +++ b/src/qibotn/eval.py @@ -18,7 +18,9 @@ def dense_vector_tn(qibo_circ, datatype): def expectation_pauli_tn(qibo_circ, datatype, pauli_string): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract( - *myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits, pauli_string)) + *myconvertor.expectation_operands( + PauliStringGen(qibo_circ.nqubits, pauli_string) + ) ) @@ -232,7 +234,9 @@ def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string, n_samples=8): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft convetor",mem_avail, "rank =",rank) - operands = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits, pauli_string)) + operands = myconvertor.expectation_operands( + PauliStringGen(qibo_circ.nqubits, pauli_string) + ) # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) @@ -311,7 +315,9 @@ def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string, n_samples=8): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft convetor",mem_avail, "rank =",rank) - operands = myconvertor.expectation_operands(PauliStringGen(qibo_circ.nqubits, pauli_string)) + operands = myconvertor.expectation_operands( + PauliStringGen(qibo_circ.nqubits, pauli_string) + ) # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) @@ -384,7 +390,7 @@ def PauliStringGen(nqubits, pauli_string): return "Invalid input. N should be a positive integer." characters = pauli_string - #characters = "XXXZ" + # characters = "XXXZ" result = "" From d1888cf4a7b4772cedbfa440bcdb2600e881b29e Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 31 Jan 2024 16:49:34 +0800 Subject: [PATCH 112/199] Remove backend --- src/qibotn/backends.py | 129 ----------------------------------------- 1 file changed, 129 deletions(-) delete mode 100644 src/qibotn/backends.py diff --git a/src/qibotn/backends.py b/src/qibotn/backends.py deleted file mode 100644 index 3728a999..00000000 --- a/src/qibotn/backends.py +++ /dev/null @@ -1,129 +0,0 @@ -from qibo.backends import NumpyBackend -from qibo.config import raise_error -from qibotn import cutn -from qibotn import quimb -from qibo.states import CircuitResult -import numpy as np - - -class QiboTNBackend(NumpyBackend): - def __init__(self, platform): - super().__init__() - self.name = "qibotn" - if ( - platform == "cu_tensornet" - or platform == "cu_mps" - or platform == "qu_tensornet" - or platform == "cu_tensornet_mpi" - or platform == "cu_tensornet_mpi_expectation" - or platform == "cu_tensornet_expectation" - or platform == "cu_tensornet_nccl" - or platform == "cu_tensornet_nccl_expectation" - ): # pragma: no cover - self.platform = platform - else: - raise_error( - NotImplementedError, "QiboTN cannot support the specified backend." - ) - - def apply_gate(self, gate, state, nqubits): # pragma: no cover - raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") - - def apply_gate_density_matrix(self, gate, state, nqubits): # pragma: no cover - raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") - - def assign_measurements(self, measurement_map, circuit_result): - raise_error(NotImplementedError, "Not implemented in QiboTN.") - - def execute_circuit( - self, circuit, initial_state=None, nshots=None, return_array=False - ): # pragma: no cover - """Executes a quantum circuit. - - Args: - 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. - - """ - - if self.platform == "cu_tensornet": - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - - state = cutn.eval(circuit, self.dtype) - - if self.platform == "cu_mps": - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - - gate_algo = { - "qr_method": False, - "svd_method": { - "partition": "UV", - "abs_cutoff": 1e-12, - }, - } # make this user input - state = cutn.eval_mps(circuit, gate_algo, self.dtype) - - if self.platform == "qu_tensornet": - # init_state = np.random.random(2**circuit.nqubits) + 1j * np.random.random(2**circuit.nqubits) - # init_state = init_state / np.sqrt((np.abs(init_state) ** 2).sum()) - init_state = np.zeros(2**circuit.nqubits, dtype=self.dtype) - init_state[0] = 1.0 - state = quimb.eval(circuit.to_qasm(), init_state, backend="numpy") - - if self.platform == "cu_tensornet_mpi": - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - - # state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) - state, rank = cutn.eval_tn_MPI_2(circuit, self.dtype, 32) - if rank > 0: - state = np.array(0) - - if self.platform == "cu_tensornet_nccl": - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - - # state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) - state, rank = cutn.eval_tn_nccl(circuit, self.dtype, 32) - if rank > 0: - state = np.array(0) - - if self.platform == "cu_tensornet_expectation": - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - - state = cutn.eval_expectation(circuit, self.dtype) - - if self.platform == "cu_tensornet_mpi_expectation": - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - - # state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) - # state, rank = cutn.eval_tn_MPI_expectation(circuit, self.dtype,32) - state, rank = cutn.eval_tn_MPI_2_expectation(circuit, self.dtype, 32) - - if rank > 0: - state = np.array(0) - - if self.platform == "cu_tensornet_nccl_expectation": - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - - # state, rank = cutn.eval_tn_MPI(circuit, self.dtype,32) - # state, rank = cutn.eval_tn_MPI_expectation(circuit, self.dtype,32) - state, rank = cutn.eval_tn_nccl_expectation(circuit, self.dtype, 32) - - if rank > 0: - state = np.array(0) - - if return_array: - return state.flatten() - else: - circuit._final_state = CircuitResult(self, circuit, state.flatten(), nshots) - return circuit._final_state From 83e77dc31c8bc413a4e4c263581eb6b520bb4dc9 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 31 Jan 2024 17:34:15 +0800 Subject: [PATCH 113/199] Update sample code [skip CI] --- README.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/README.md b/README.md index 4628c1c1..a1fea8da 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,58 @@ Qibotn is the tensor-network translation module for Qibo to support large-scale simulation of quantum circuits and acceleration. To get started, `python setup.py install` to install the tools and dependencies. + +# Sample Codes +## Single Node + +
+```
+import numpy as np
+from qibo import Circuit, gates
+import qibo
+
+'''
+computation_settings = {
+    'MPI_enabled': False,
+    'MPS_enabled': False,
+    'NCCL_enabled': False,
+    'expectation_enabled': {
+        'pauli_string_pattern': "IXZ"
+    }
+}
+'''
+
+computation_settings = {
+    'MPI_enabled': False,
+    'MPS_enabled': {
+                "qr_method": False,
+                "svd_method": {
+                    "partition": "UV",
+                    "abs_cutoff": 1e-12,
+                },
+            } ,
+    'NCCL_enabled': False,
+    'expectation_enabled': False
+}
+
+# computation_settings = {
+#     'MPI_enabled': False,
+#     'MPS_enabled': True,
+#     'NCCL_enabled': False,
+#     'expectation_enabled': False
+# }
+
+qibo.set_backend(backend="qibotn", runcard=computation_settings)
+
+# Construct the circuit
+c = Circuit(2)
+# Add some gates
+c.add(gates.H(0))
+c.add(gates.H(1))
+
+# Execute the circuit and obtain the final state
+result = c()
+
+print(result.state())
+```
+
\ No newline at end of file From 3030d0c8701cd7cafa887224d58afb79c720260b Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 31 Jan 2024 17:42:10 +0800 Subject: [PATCH 114/199] Update --- README.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a1fea8da..1bf2f7ce 100644 --- a/README.md +++ b/README.md @@ -11,16 +11,10 @@ import numpy as np from qibo import Circuit, gates import qibo -''' -computation_settings = { - 'MPI_enabled': False, - 'MPS_enabled': False, - 'NCCL_enabled': False, - 'expectation_enabled': { - 'pauli_string_pattern': "IXZ" - } -} -''' +# Below shows 3 ways of setting the computation_settings +# Note that for MPS_enabled and expectation_enabled parameters the accepted inputs are boolean or a dictionary with the format shown below. +# If computation_settings is not specified, the default setting is used in which all booleans will be False. +# This will trigger the dense vector computation of the tensornet. computation_settings = { 'MPI_enabled': False, @@ -35,6 +29,14 @@ computation_settings = { 'expectation_enabled': False } +# computation_settings = { +# 'MPI_enabled': False, +# 'MPS_enabled': False, +# 'NCCL_enabled': False, +# 'expectation_enabled': { +# 'pauli_string_pattern': "IXZ" +# } + # computation_settings = { # 'MPI_enabled': False, # 'MPS_enabled': True, From 019f6cc04c55c10e77e795e0a36c25bededd7ef7 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Wed, 31 Jan 2024 17:49:56 +0800 Subject: [PATCH 115/199] Update sample codes --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1bf2f7ce..09281c87 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ To get started, `python setup.py install` to install the tools and dependencies. # Sample Codes ## Single Node - +The code below shows an example of how to activate the Cuquantum TensorNetwork backend of Qibo.
 ```
 import numpy as np
@@ -57,4 +57,13 @@ result = c()
 
 print(result.state())
 ```
+
+ +## Multi-Node +Multi-node is enabled by setting either the MPI or NCCL enabled flag to True in the computation settings. Below shows the script to launch multi node on 4 GPU in cluster. + +
+```
+mpirun -n 4 --mca opal_common_ucx_opal_mem_hooks 1 --mca orte_base_help_aggregate 0 -mca btl ^openib  -hostfile $node_list python test.py
+```
 
\ No newline at end of file From 50f1fcd6e89d59984c744539f2014d0398f1ec44 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Thu, 1 Feb 2024 11:35:55 +0800 Subject: [PATCH 116/199] Add more documentation --- README.md | 13 +++++++++++++ src/qibotn/eval.py | 18 ++++++++++-------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 09281c87..68f28300 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,19 @@ Qibotn is the tensor-network translation module for Qibo to support large-scale To get started, `python setup.py install` to install the tools and dependencies. +# Computation Supported + +- Tensornet (TN) + - TN contraction to dense vector + - TN contraction to dense vector with Message Passing Interface (MPI) + - TN contraction to dense vector with NCCL + - TN contraction to expectation of given Pauli string + - TN contraction to expectation of given Pauli string with Message Passing Interface (MPI) + - TN contraction to expectation of given Pauli string with NCCL + +- Matrix Product State (MPS) + - MPS contraction to dense vector + # Sample Codes ## Single Node The code below shows an example of how to activate the Cuquantum TensorNetwork backend of Qibo. diff --git a/src/qibotn/eval.py b/src/qibotn/eval.py index 0e10d7bb..a35a1ad8 100644 --- a/src/qibotn/eval.py +++ b/src/qibotn/eval.py @@ -19,7 +19,7 @@ def expectation_pauli_tn(qibo_circ, datatype, pauli_string): myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract( *myconvertor.expectation_operands( - PauliStringGen(qibo_circ.nqubits, pauli_string) + pauli_string_gen(qibo_circ.nqubits, pauli_string) ) ) @@ -235,7 +235,7 @@ def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string, n_samples=8): # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft convetor",mem_avail, "rank =",rank) operands = myconvertor.expectation_operands( - PauliStringGen(qibo_circ.nqubits, pauli_string) + pauli_string_gen(qibo_circ.nqubits, pauli_string) ) # mem_avail = cp.cuda.Device().mem_info[0] @@ -316,7 +316,7 @@ def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string, n_samples=8): # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft convetor",mem_avail, "rank =",rank) operands = myconvertor.expectation_operands( - PauliStringGen(qibo_circ.nqubits, pauli_string) + pauli_string_gen(qibo_circ.nqubits, pauli_string) ) # mem_avail = cp.cuda.Device().mem_info[0] # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) @@ -377,6 +377,8 @@ def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string, n_samples=8): def dense_vector_mps(qibo_circ, gate_algo, datatype): + """Convert qibo circuit to matrix product state (MPS) format and perform contraction to dense vector. + """ myconvertor = QiboCircuitToMPS(qibo_circ, gate_algo, dtype=datatype) mps_helper = MPSContractionHelper(myconvertor.num_qubits) @@ -385,17 +387,17 @@ def dense_vector_mps(qibo_circ, gate_algo, datatype): ) -def PauliStringGen(nqubits, pauli_string): +def pauli_string_gen(nqubits, pauli_string_pattern): + """ Used internally to generate the string based on given pattern and number of qubit. + Example: pattern: "XZ", number of qubit: 7, output = XZXZXZX + """ if nqubits <= 0: return "Invalid input. N should be a positive integer." - characters = pauli_string - # characters = "XXXZ" - result = "" for i in range(nqubits): - char_to_add = characters[i % len(characters)] + char_to_add = pauli_string_pattern[i % len(pauli_string_pattern)] result += char_to_add print("pauli string", result) return result From 09c741ea80f82d7476b9300481db313414064779 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Thu, 1 Feb 2024 14:42:17 +0800 Subject: [PATCH 117/199] Tidy up --- src/qibotn/MPSUtils.py | 2 + src/qibotn/QiboCircuitConvertor.py | 22 +--- src/qibotn/eval.py | 159 +++++++-------------------- src/qibotn/mps_contraction_helper.py | 4 +- 4 files changed, 50 insertions(+), 137 deletions(-) diff --git a/src/qibotn/MPSUtils.py b/src/qibotn/MPSUtils.py index 138fb965..1d37c42e 100644 --- a/src/qibotn/MPSUtils.py +++ b/src/qibotn/MPSUtils.py @@ -2,6 +2,8 @@ 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): """ diff --git a/src/qibotn/QiboCircuitConvertor.py b/src/qibotn/QiboCircuitConvertor.py index d3a0569a..c59745bd 100644 --- a/src/qibotn/QiboCircuitConvertor.py +++ b/src/qibotn/QiboCircuitConvertor.py @@ -1,6 +1,8 @@ import cupy as cp import numpy as np +# Reference: https://github.com/NVIDIA/cuQuantum/tree/main/python/samples/cutensornet/circuit_converter + class QiboCircuitToEinsum: """Convert a circuit to a Tensor Network (TN) representation. @@ -159,9 +161,7 @@ def get_pauli_gates(self, pauli_map, dtype="complex128", backend=cp): return gates def expectation_operands(self, pauli_string): - # assign pauli string to qubit - # _get_forward_inverse_metadata() - input_bitstring = "0" * self.circuit.nqubits # Need all qubits! + input_bitstring = "0" * self.circuit.nqubits input_operands = self._get_bitstring_tensors(input_bitstring) pauli_string = dict(zip(range(self.circuit.nqubits), pauli_string)) @@ -185,8 +185,6 @@ def expectation_operands(self, pauli_string): next_frontier = max(qubits_frontier.values()) + 1 - # input_mode_labels, input_operands, qubits_frontier, next_frontier, inverse_gates = self._get_forward_inverse_metadata(coned_qubits) - pauli_gates = self.get_pauli_gates( pauli_map, dtype=self.dtype, backend=self.backend ) @@ -208,18 +206,4 @@ def expectation_operands(self, pauli_string): operand_exp_interleave = [x for y in zip(operands, mode_labels) for x in y] - # expec = contract(*operand_exp_interleave) - # print(expec) - - """ - gate_mode_labels, gate_operands = circ_utils.parse_gates_to_mode_labels_operands(gates, - qubits_frontier, - next_frontier) - - mode_labels = input_mode_labels + gate_mode_labels + [[qubits_frontier[ix]] for ix in self.qubits] - operands = input_operands + gate_operands + input_operands[:n_qubits] - - output_mode_labels = [] - expression = circ_utils.convert_mode_labels_to_expression(mode_labels, output_mode_labels) - """ return operand_exp_interleave diff --git a/src/qibotn/eval.py b/src/qibotn/eval.py index a35a1ad8..9fb168cf 100644 --- a/src/qibotn/eval.py +++ b/src/qibotn/eval.py @@ -3,7 +3,8 @@ import cupy as cp from cupy.cuda.runtime import getDeviceCount from cuquantum import contract -from cuquantum import cutensornet as cutn +from cupy.cuda.runtime import getDeviceCount +import cupy as cp from qibotn.mps_contraction_helper import MPSContractionHelper from qibotn.QiboCircuitConvertor import QiboCircuitToEinsum @@ -11,15 +12,17 @@ def dense_vector_tn(qibo_circ, datatype): + """Convert qibo circuit to tensornet (TN) format and perform contraction to dense vector.""" myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract(*myconvertor.state_vector_operands()) -def expectation_pauli_tn(qibo_circ, datatype, pauli_string): +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.""" myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract( *myconvertor.expectation_operands( - pauli_string_gen(qibo_circ.nqubits, pauli_string) + pauli_string_gen(qibo_circ.nqubits, pauli_string_pattern) ) ) @@ -31,54 +34,35 @@ def dense_vector_tn_MPI(qibo_circ, datatype, n_samples=8): After pathfinding the optimal path is used in the actual contraction to give a dense vector representation of the TN. """ - from mpi4py import MPI # this line initializes MPI - import socket + from mpi4py import MPI from cuquantum import Network - # Get the hostname - # hostname = socket.gethostname() - root = 0 comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + device_id = rank % getDeviceCount() # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft convetor",mem_avail, "rank =",rank) - operands = myconvertor.state_vector_operands() - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) - # Broadcast the operand data. - # operands = comm.bcast(operands, root) + operands = myconvertor.state_vector_operands() # Assign the device for each process. device_id = rank % getDeviceCount() - # dev = cp.cuda.Device(device_id) - # free_mem, total_mem = dev.mem_info - # print("Mem free: ",free_mem, "Total mem: ",total_mem, "rank =",rank) - # Create network object. network = Network(*operands, options={"device_id": device_id}) # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. path, info = network.contract_path( - optimize={"samples": 8, "slicing": {"min_slices": max(32, size)}} + optimize={"samples": n_samples, "slicing": {"min_slices": max(32, size)}} ) - # print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") # Select the best path from all ranks. opt_cost, sender = comm.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) - # if rank == root: - # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") - # Broadcast info from the sender to all other ranks. info = comm.bcast(info, sender) @@ -96,45 +80,30 @@ def dense_vector_tn_MPI(qibo_circ, datatype, n_samples=8): ) slices = range(slice_begin, slice_end) - # print(f"Process {rank} is processing slice range: {slices}.") - # Contract the group of slices the process is responsible for. result = network.contract(slices=slices) - # print(f"Process {rank} result shape is : {result.shape}.") - # print(f"Process {rank} result size is : {result.nbytes}.") # Sum the partial contribution from each process on root. result = comm.reduce(sendobj=result, op=MPI.SUM, root=root) - """ - path, opt_info = network.contract_path(optimize={"samples": n_samples, "threads": ncpu_threads, 'slicing': {'min_slices': max(16, size)}}) - - num_slices = opt_info.num_slices#Andy - chunk, extra = num_slices // size, num_slices % size#Andy - slice_begin = rank * chunk + min(rank, extra)#Andy - slice_end = num_slices if rank == size - 1 else (rank + 1) * chunk + min(rank + 1, extra)#Andy - slices = range(slice_begin, slice_end)#Andy - result = network.contract(slices=slices) - """ - return result, rank def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8): - from mpi4py import MPI # this line initializes MPI - import socket + """Convert qibo circuit to tensornet (TN) format and perform contraction using multi node and multi GPU through NCCL. + The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. + The pathfinder looks at user defined number of samples (n_samples) iteratively to 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 a dense vector representation of the TN. + """ + from mpi4py import MPI from cuquantum import Network from cupy.cuda import nccl - # Get the hostname - # hostname = socket.gethostname() - root = 0 comm_mpi = MPI.COMM_WORLD rank = comm_mpi.Get_rank() size = comm_mpi.Get_size() - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + device_id = rank % getDeviceCount() cp.cuda.Device(device_id).use() @@ -146,27 +115,18 @@ def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8): # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft convetor",mem_avail, "rank =",rank) operands = myconvertor.state_vector_operands() - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) network = Network(*operands) # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. path, info = network.contract_path( - optimize={"samples": 8, "slicing": {"min_slices": max(32, size)}} + optimize={"samples": n_samples, "slicing": {"min_slices": max(32, size)}} ) - # print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") - # Select the best path from all ranks. opt_cost, sender = comm_mpi.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) - # if rank == root: - # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") - # Broadcast info from the sender to all other ranks. info = comm_mpi.bcast(info, sender) @@ -184,12 +144,8 @@ def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8): ) slices = range(slice_begin, slice_end) - # print(f"Process {rank} is processing slice range: {slices}.") - # Contract the group of slices the process is responsible for. result = network.contract(slices=slices) - # print(f"Process {rank} result shape is : {result.shape}.") - # print(f"Process {rank} result size is : {result.nbytes}.") # Sum the partial contribution from each process on root. stream_ptr = cp.cuda.get_current_stream().ptr @@ -206,21 +162,22 @@ def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8): return result, rank -def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string, n_samples=8): - from mpi4py import MPI # this line initializes MPI - import socket +def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string_pattern, n_samples=8): + """Convert qibo circuit to tensornet (TN) format and perform contraction to expectation of given Pauli string using multi node and multi GPU through NCCL. + The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. + The pauli_string_pattern is used to generate the pauli string corresponding to the number of qubits of the system. + The pathfinder looks at user defined number of samples (n_samples) iteratively to 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. + """ + from mpi4py import MPI from cuquantum import Network from cupy.cuda import nccl - # Get the hostname - # hostname = socket.gethostname() - root = 0 comm_mpi = MPI.COMM_WORLD rank = comm_mpi.Get_rank() size = comm_mpi.Get_size() - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + device_id = rank % getDeviceCount() cp.cuda.Device(device_id).use() @@ -232,30 +189,20 @@ def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string, n_samples=8): # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft convetor",mem_avail, "rank =",rank) operands = myconvertor.expectation_operands( - pauli_string_gen(qibo_circ.nqubits, pauli_string) + pauli_string_gen(qibo_circ.nqubits, pauli_string_pattern) ) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) - network = Network(*operands) # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. path, info = network.contract_path( - optimize={"samples": 8, "slicing": {"min_slices": max(32, size)}} + optimize={"samples": n_samples, "slicing": {"min_slices": max(32, size)}} ) - # print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") - # Select the best path from all ranks. opt_cost, sender = comm_mpi.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) - # if rank == root: - # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") - # Broadcast info from the sender to all other ranks. info = comm_mpi.bcast(info, sender) @@ -273,12 +220,8 @@ def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string, n_samples=8): ) slices = range(slice_begin, slice_end) - # print(f"Process {rank} is processing slice range: {slices}.") - # Contract the group of slices the process is responsible for. result = network.contract(slices=slices) - # print(f"Process {rank} result shape is : {result.shape}.") - # print(f"Process {rank} result size is : {result.nbytes}.") # Sum the partial contribution from each process on root. stream_ptr = cp.cuda.get_current_stream().ptr @@ -295,57 +238,44 @@ def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string, n_samples=8): return result, rank -def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string, n_samples=8): +def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string_pattern, n_samples=8): + """Convert qibo circuit to tensornet (TN) format and perform contraction to expectation of given Pauli string using multi node and multi GPU through MPI. + The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. + The pauli_string_pattern is used to generate the pauli string corresponding to the number of qubits of the system. + The pathfinder looks at user defined number of samples (n_samples) iteratively to 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. + """ from mpi4py import MPI # this line initializes MPI - import socket from cuquantum import Network - # Get the hostname - # hostname = socket.gethostname() - root = 0 comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: Start",mem_avail, "rank =",rank, "hostname =",hostname) + device_id = rank % getDeviceCount() # Perform circuit conversion myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft convetor",mem_avail, "rank =",rank) + operands = myconvertor.expectation_operands( - pauli_string_gen(qibo_circ.nqubits, pauli_string) + pauli_string_gen(qibo_circ.nqubits, pauli_string_pattern) ) - # mem_avail = cp.cuda.Device().mem_info[0] - # print("Mem avail: aft operand interleave",mem_avail, "rank =",rank) - - # Broadcast the operand data. - # operands = comm.bcast(operands, root) # Assign the device for each process. device_id = rank % getDeviceCount() - # dev = cp.cuda.Device(device_id) - # free_mem, total_mem = dev.mem_info - # print("Mem free: ",free_mem, "Total mem: ",total_mem, "rank =",rank) - # Create network object. network = Network(*operands, options={"device_id": device_id}) # Compute the path on all ranks with 8 samples for hyperoptimization. Force slicing to enable parallel contraction. path, info = network.contract_path( - optimize={"samples": 8, "slicing": {"min_slices": max(32, size)}} + optimize={"samples": n_samples, "slicing": {"min_slices": max(32, size)}} ) - # print(f"Process {rank} has the path with the FLOP count {info.opt_cost}.") # Select the best path from all ranks. opt_cost, sender = comm.allreduce(sendobj=(info.opt_cost, rank), op=MPI.MINLOC) - # if rank == root: - # print(f"Process {sender} has the path with the lowest FLOP count {opt_cost}.") - # Broadcast info from the sender to all other ranks. info = comm.bcast(info, sender) @@ -363,12 +293,8 @@ def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string, n_samples=8): ) slices = range(slice_begin, slice_end) - # print(f"Process {rank} is processing slice range: {slices}.") - # Contract the group of slices the process is responsible for. result = network.contract(slices=slices) - # print(f"Process {rank} result shape is : {result.shape}.") - # print(f"Process {rank} result size is : {result.nbytes}.") # Sum the partial contribution from each process on root. result = comm.reduce(sendobj=result, op=MPI.SUM, root=root) @@ -377,8 +303,7 @@ def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string, n_samples=8): def dense_vector_mps(qibo_circ, gate_algo, datatype): - """Convert qibo circuit to matrix product state (MPS) format and perform contraction to dense vector. - """ + """Convert qibo circuit to matrix product state (MPS) format and perform contraction to dense vector.""" myconvertor = QiboCircuitToMPS(qibo_circ, gate_algo, dtype=datatype) mps_helper = MPSContractionHelper(myconvertor.num_qubits) @@ -388,7 +313,7 @@ def dense_vector_mps(qibo_circ, gate_algo, datatype): def pauli_string_gen(nqubits, pauli_string_pattern): - """ Used internally to generate the string based on given pattern and number of qubit. + """Used internally to generate the string based on given pattern and number of qubit. Example: pattern: "XZ", number of qubit: 7, output = XZXZXZX """ if nqubits <= 0: diff --git a/src/qibotn/mps_contraction_helper.py b/src/qibotn/mps_contraction_helper.py index 0366a34a..9d667cf6 100644 --- a/src/qibotn/mps_contraction_helper.py +++ b/src/qibotn/mps_contraction_helper.py @@ -1,5 +1,7 @@ from cuquantum import CircuitToEinsum, contract, contract_path, tensor +# Reference: https://github.com/NVIDIA/cuQuantum/blob/main/python/samples/cutensornet/tn_algorithms/mps_algorithms.ipynb + class MPSContractionHelper: """ @@ -85,7 +87,7 @@ def contract_expectation( self, mps_tensors, operator, qubits, options=None, normalize=False ): """ - Contract the corresponding tensor network to form the state vector representation of the MPS. + Contract the corresponding tensor network to form the expectation of the MPS. Args: mps_tensors: A list of rank-3 ndarray-like tensor objects. From a61ac21dd4aa3eb28165b20357d2b63922710886 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Thu, 1 Feb 2024 14:45:55 +0800 Subject: [PATCH 118/199] Remove print [skip CI] --- src/qibotn/eval.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qibotn/eval.py b/src/qibotn/eval.py index 9fb168cf..98c87a2e 100644 --- a/src/qibotn/eval.py +++ b/src/qibotn/eval.py @@ -324,5 +324,4 @@ def pauli_string_gen(nqubits, pauli_string_pattern): for i in range(nqubits): char_to_add = pauli_string_pattern[i % len(pauli_string_pattern)] result += char_to_add - print("pauli string", result) return result From d852fd89e9cc02c939ddceeddcfc38f8db2bc348 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Thu, 1 Feb 2024 15:21:04 +0800 Subject: [PATCH 119/199] Update --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 68f28300..25b499ca 100644 --- a/README.md +++ b/README.md @@ -73,10 +73,10 @@ print(result.state()) ## Multi-Node -Multi-node is enabled by setting either the MPI or NCCL enabled flag to True in the computation settings. Below shows the script to launch multi node on 4 GPU in cluster. +Multi-node is enabled by setting either the MPI or NCCL enabled flag to True in the computation settings. Below shows the script to launch on 2 nodes with 2 GPUs each. $node_list contains the IP of the nodes assigned.
 ```
-mpirun -n 4 --mca opal_common_ucx_opal_mem_hooks 1 --mca orte_base_help_aggregate 0 -mca btl ^openib  -hostfile $node_list python test.py
+mpirun -n 4 -hostfile $node_list python test.py
 ```
 
\ No newline at end of file From 643dd9e19884ecc0029a3d1f8c7b0730418cc3b1 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Fri, 2 Feb 2024 12:53:48 +0800 Subject: [PATCH 120/199] Update readme --- README.md | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 25b499ca..2e9b09c6 100644 --- a/README.md +++ b/README.md @@ -4,22 +4,22 @@ To get started, `python setup.py install` to install the tools and dependencies. # Computation Supported -- Tensornet (TN) - - TN contraction to dense vector - - TN contraction to dense vector with Message Passing Interface (MPI) - - TN contraction to dense vector with NCCL - - TN contraction to expectation of given Pauli string - - TN contraction to expectation of given Pauli string with Message Passing Interface (MPI) - - TN contraction to expectation of given Pauli string with NCCL +1. Tensornet (TN) with contractions to: + - dense vector + - expecation of given Pauli string -- Matrix Product State (MPS) - - MPS contraction to dense vector + For each TN case: + - single node + - multi node with Message Passing Interface (MPI) + - multi node with NCCL + +2. Tensornet (TN) with contractions to: + - dense vector (single node) # Sample Codes ## Single Node The code below shows an example of how to activate the Cuquantum TensorNetwork backend of Qibo. -
-```
+```py
 import numpy as np
 from qibo import Circuit, gates
 import qibo
@@ -29,7 +29,6 @@ import qibo
 # If computation_settings is not specified, the default setting is used in which all booleans will be False. 
 # This will trigger the dense vector computation of the tensornet.
 
-computation_settings = {
     'MPI_enabled': False,
     'MPS_enabled': {
                 "qr_method": False,
@@ -70,13 +69,11 @@ result = c()
 
 print(result.state())
 ```
-
## Multi-Node Multi-node is enabled by setting either the MPI or NCCL enabled flag to True in the computation settings. Below shows the script to launch on 2 nodes with 2 GPUs each. $node_list contains the IP of the nodes assigned. -
-```
+
+```sh
 mpirun -n 4 -hostfile $node_list python test.py
-```
-
\ No newline at end of file +``` \ No newline at end of file From 65a2c644c973369abf8dca9dd81067a137840dfb Mon Sep 17 00:00:00 2001 From: tankya2 Date: Fri, 2 Feb 2024 13:02:09 +0800 Subject: [PATCH 121/199] Update computation setting --- README.md | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 2e9b09c6..a5b44002 100644 --- a/README.md +++ b/README.md @@ -24,11 +24,12 @@ import numpy as np from qibo import Circuit, gates import qibo -# Below shows 3 ways of setting the computation_settings +# Below shows how to set the computation_settings # Note that for MPS_enabled and expectation_enabled parameters the accepted inputs are boolean or a dictionary with the format shown below. # If computation_settings is not specified, the default setting is used in which all booleans will be False. # This will trigger the dense vector computation of the tensornet. +computation_settings = { 'MPI_enabled': False, 'MPS_enabled': { "qr_method": False, @@ -41,20 +42,6 @@ import qibo 'expectation_enabled': False } -# computation_settings = { -# 'MPI_enabled': False, -# 'MPS_enabled': False, -# 'NCCL_enabled': False, -# 'expectation_enabled': { -# 'pauli_string_pattern': "IXZ" -# } - -# computation_settings = { -# 'MPI_enabled': False, -# 'MPS_enabled': True, -# 'NCCL_enabled': False, -# 'expectation_enabled': False -# } qibo.set_backend(backend="qibotn", runcard=computation_settings) @@ -70,6 +57,27 @@ result = c() print(result.state()) ``` +Other examples of setting the computation_settings + +```py +# Expectation computation with specific Pauli String pattern +computation_settings = { + 'MPI_enabled': False, + 'MPS_enabled': False, + 'NCCL_enabled': False, + 'expectation_enabled': { + 'pauli_string_pattern': "IXZ" +} + +# Dense vector computation using multi node through MPI +computation_settings = { + 'MPI_enabled': False, + 'MPS_enabled': True, + 'NCCL_enabled': False, + 'expectation_enabled': False +} +``` + ## Multi-Node Multi-node is enabled by setting either the MPI or NCCL enabled flag to True in the computation settings. Below shows the script to launch on 2 nodes with 2 GPUs each. $node_list contains the IP of the nodes assigned. From 5385b4d941681328724be13de62aec678e36e827 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Mon, 5 Feb 2024 10:41:23 +0800 Subject: [PATCH 122/199] Correct computation_settings --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a5b44002..54ff75dc 100644 --- a/README.md +++ b/README.md @@ -71,8 +71,8 @@ computation_settings = { # Dense vector computation using multi node through MPI computation_settings = { - 'MPI_enabled': False, - 'MPS_enabled': True, + 'MPI_enabled': True, + 'MPS_enabled': False, 'NCCL_enabled': False, 'expectation_enabled': False } From 0ac65c216efd6faa6fcaf4bb7d02b8bf23db7342 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Tue, 6 Feb 2024 10:39:10 +0800 Subject: [PATCH 123/199] Remove not required Numba backend --- src/qibotn/backends/__init__.py | 1 - src/qibotn/backends/cpu.py | 302 -------------------------------- 2 files changed, 303 deletions(-) delete mode 100644 src/qibotn/backends/cpu.py diff --git a/src/qibotn/backends/__init__.py b/src/qibotn/backends/__init__.py index ebc3a205..26a0b9d4 100644 --- a/src/qibotn/backends/__init__.py +++ b/src/qibotn/backends/__init__.py @@ -1,2 +1 @@ -from qibotn.backends.cpu import NumbaBackend from qibotn.backends.gpu import CuTensorNet diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py deleted file mode 100644 index 22afcdc1..00000000 --- a/src/qibotn/backends/cpu.py +++ /dev/null @@ -1,302 +0,0 @@ -import numpy as np -from qibo.backends.numpy import NumpyBackend -from qibo.config import log -from qibo.gates.abstract import ParametrizedGate -from qibo.gates.channels import ReadoutErrorChannel -from qibo.gates.special import FusedGate - -from qibojit.backends.matrices import CustomMatrices - -GATE_OPS = { - "X": "apply_x", - "CNOT": "apply_x", - "TOFFOLI": "apply_x", - "Y": "apply_y", - "Z": "apply_z", - "CZ": "apply_z", - "U1": "apply_z_pow", - "CU1": "apply_z_pow", - "SWAP": "apply_swap", - "fSim": "apply_fsim", - "GeneralizedfSim": "apply_fsim", -} - - -class NumbaBackend(NumpyBackend): - def __init__(self): - super().__init__() - import sys - - import psutil - from numba import __version__ as numba_version - - from qibotn import __version__ as qibotn_version - - self.name = "qibotn" - self.platform = "numba" - self.versions.update( - { - "qibotn": qibotn_version, - "numba": numba_version, - } - ) - self.numeric_types = ( - int, - float, - complex, - np.int32, - np.int64, - np.float32, - np.float64, - np.complex64, - np.complex128, - ) - self.tensor_types = (np.ndarray,) - self.device = "/CPU:0" - self.custom_matrices = CustomMatrices(self.dtype) - self.gates = gates - self.ops = ops - self.measure_frequencies_op = ops.measure_frequencies - self.multi_qubit_kernels = { - 3: self.gates.apply_three_qubit_gate_kernel, - 4: self.gates.apply_four_qubit_gate_kernel, - 5: self.gates.apply_five_qubit_gate_kernel, - } - if sys.platform == "darwin": # pragma: no cover - self.set_threads(psutil.cpu_count(logical=False)) - else: - self.set_threads(len(psutil.Process().cpu_affinity())) - - def set_precision(self, precision): - if precision != self.precision: - super().set_precision(precision) - if self.custom_matrices: - self.custom_matrices = CustomMatrices(self.dtype) - - def set_threads(self, nthreads): - import numba - - numba.set_num_threads(nthreads) - self.nthreads = nthreads - - # def cast(self, x, dtype=None, copy=False): Inherited from ``NumpyBackend`` - - # def to_numpy(self, x): Inherited from ``NumpyBackend`` - - def zero_state(self, nqubits): - size = 2**nqubits - state = np.empty((size,), dtype=self.dtype) - return self.ops.initial_state_vector(state) - - def zero_density_matrix(self, nqubits): - size = 2**nqubits - state = np.empty((size, size), dtype=self.dtype) - return self.ops.initial_density_matrix(state) - - # def plus_state(self, nqubits): Inherited from ``NumpyBackend`` - - # def plus_density_matrix(self, nqubits): Inherited from ``NumpyBackend`` - - # def asmatrix_special(self, gate): Inherited from ``NumpyBackend`` - - # def control_matrix(self, gate): Inherited from ``NumpyBackend`` - - def one_qubit_base(self, state, nqubits, target, kernel, gate, qubits): - ncontrols = len(qubits) - 1 if qubits is not None else 0 - m = nqubits - target - 1 - nstates = 1 << (nqubits - ncontrols - 1) - if ncontrols: - kernel = getattr(self.gates, "multicontrol_{}_kernel".format(kernel)) - return kernel(state, gate, qubits, nstates, m) - kernel = getattr(self.gates, "{}_kernel".format(kernel)) - return kernel(state, gate, nstates, m) - - def two_qubit_base(self, state, nqubits, target1, target2, kernel, gate, qubits): - ncontrols = len(qubits) - 2 if qubits is not None else 0 - if target1 > target2: - swap_targets = True - m1 = nqubits - target1 - 1 - m2 = nqubits - target2 - 1 - else: - swap_targets = False - m1 = nqubits - target2 - 1 - m2 = nqubits - target1 - 1 - nstates = 1 << (nqubits - 2 - ncontrols) - if ncontrols: - kernel = getattr(self.gates, "multicontrol_{}_kernel".format(kernel)) - return kernel(state, gate, qubits, nstates, m1, m2, swap_targets) - kernel = getattr(self.gates, "{}_kernel".format(kernel)) - return kernel(state, gate, nstates, m1, m2, swap_targets) - - def multi_qubit_base(self, state, nqubits, targets, gate, qubits): - if qubits is None: - qubits = np.array(sorted(nqubits - q - 1 for q in targets), dtype="int32") - nstates = 1 << (nqubits - len(qubits)) - targets = np.array( - [1 << (nqubits - t - 1) for t in targets[::-1]], dtype="int64" - ) - if len(targets) > 5: - kernel = self.gates.apply_multi_qubit_gate_kernel - else: - kernel = self.multi_qubit_kernels.get(len(targets)) - return kernel(state, gate, qubits, nstates, targets) - - @staticmethod - def _create_qubits_tensor(gate, nqubits): - # TODO: Treat density matrices - qubits = [nqubits - q - 1 for q in gate.control_qubits] - qubits.extend(nqubits - q - 1 for q in gate.target_qubits) - return np.array(sorted(qubits), dtype="int32") - - def _as_custom_matrix(self, gate): - name = gate.__class__.__name__ - if isinstance(gate, ParametrizedGate): - return getattr(self.custom_matrices, name)(*gate.parameters) - elif isinstance(gate, FusedGate): # pragma: no cover - # fusion is tested in qibo tests - return self.asmatrix_fused(gate) - else: - return getattr(self.custom_matrices, name) - - def apply_gate(self, gate, state, nqubits): - matrix = self._as_custom_matrix(gate) - qubits = self._create_qubits_tensor(gate, nqubits) - targets = gate.target_qubits - state = self.cast(state) - if len(targets) == 1: - op = GATE_OPS.get(gate.__class__.__name__, "apply_gate") - return self.one_qubit_base(state, nqubits, *targets, op, matrix, qubits) - elif len(targets) == 2: - op = GATE_OPS.get(gate.__class__.__name__, "apply_two_qubit_gate") - return self.two_qubit_base(state, nqubits, *targets, op, matrix, qubits) - else: - return self.multi_qubit_base(state, nqubits, targets, matrix, qubits) - - def apply_gate_density_matrix(self, gate, state, nqubits, inverse=False): - name = gate.__class__.__name__ - if name == "Y": - return self._apply_ygate_density_matrix(gate, state, nqubits) - if inverse: - # used to reset the state when applying channels - # see :meth:`qibojit.backend.NumpyBackend.apply_channel_density_matrix` below - matrix = np.linalg.inv(gate.asmatrix(self)) - matrix = self.cast(matrix) - else: - matrix = self._as_custom_matrix(gate) - qubits = self._create_qubits_tensor(gate, nqubits) - qubits_dm = qubits + nqubits - targets = gate.target_qubits - targets_dm = tuple(q + nqubits for q in targets) - - state = self.cast(state) - shape = state.shape - if len(targets) == 1: - op = GATE_OPS.get(name, "apply_gate") - state = self.one_qubit_base( - state.ravel(), 2 * nqubits, *targets, op, matrix, qubits_dm - ) - state = self.one_qubit_base( - state, 2 * nqubits, *targets_dm, op, np.conj(matrix), qubits - ) - elif len(targets) == 2: - op = GATE_OPS.get(name, "apply_two_qubit_gate") - state = self.two_qubit_base( - state.ravel(), 2 * nqubits, *targets, op, matrix, qubits_dm - ) - state = self.two_qubit_base( - state, 2 * nqubits, *targets_dm, op, np.conj(matrix), qubits - ) - else: - state = self.multi_qubit_base( - state.ravel(), 2 * nqubits, targets, matrix, qubits_dm - ) - state = self.multi_qubit_base( - state, 2 * nqubits, targets_dm, np.conj(matrix), qubits - ) - return np.reshape(state, shape) - - def _apply_ygate_density_matrix(self, gate, state, nqubits): - matrix = self._as_custom_matrix(gate) - qubits = self._create_qubits_tensor(gate, nqubits) - qubits_dm = qubits + nqubits - targets = gate.target_qubits - targets_dm = tuple(q + nqubits for q in targets) - state = self.cast(state) - shape = state.shape - state = self.one_qubit_base( - state.ravel(), 2 * nqubits, *targets, "apply_y", matrix, qubits_dm - ) - # force using ``apply_gate`` kernel so that conjugate is properly applied - state = self.one_qubit_base( - state, 2 * nqubits, *targets_dm, "apply_gate", np.conj(matrix), qubits - ) - return np.reshape(state, shape) - - # def apply_channel(self, gate): Inherited from ``NumpyBackend`` - - def apply_channel_density_matrix(self, channel, state, nqubits): - state = self.cast(state) - if isinstance(channel, ReadoutErrorChannel) is True: - state_copy = self.cast(state, copy=True) - new_state = (1 - channel.coefficient_sum) * state - for coeff, gate in zip(channel.coefficients, channel.gates): - state = self.apply_gate_density_matrix(gate, state, nqubits) - new_state += coeff * state - # reset the state - if isinstance(channel, ReadoutErrorChannel) is True: - state = self.cast(state_copy, copy=True) - else: - state = self.apply_gate_density_matrix( - gate, state, nqubits, inverse=True - ) - return new_state - - def collapse_state(self, state, qubits, shot, nqubits, normalize=True): - state = self.cast(state) - qubits = self.cast([nqubits - q - 1 for q in reversed(qubits)], dtype="int32") - if normalize: - return self.ops.collapse_state_normalized(state, qubits, int(shot), nqubits) - else: - return self.ops.collapse_state(state, qubits, int(shot), nqubits) - - def collapse_density_matrix(self, state, qubits, shot, nqubits, normalize=True): - state = self.cast(state) - shape = state.shape - dm_qubits = [q + nqubits for q in qubits] - state = self.collapse_state(state.ravel(), dm_qubits, shot, 2 * nqubits, False) - state = self.collapse_state(state, qubits, shot, 2 * nqubits, False) - state = self.np.reshape(state, shape) - if normalize: - state = state / self.np.trace(state) - return state - - # def calculate_probabilities(self, state, qubits, nqubits): Inherited from ``NumpyBackend`` - - # def sample_shots(self, probabilities, nshots): Inherited from ``NumpyBackend`` - - # def aggregate_shots(self, shots): Inherited from ``NumpyBackend`` - - # def samples_to_binary(self, samples, nqubits): Inherited from ``NumpyBackend`` - - # def samples_to_decimal(self, samples, nqubits): Inherited from ``NumpyBackend`` - - def sample_frequencies(self, probabilities, nshots): - from qibo.config import SHOT_METROPOLIS_THRESHOLD - - if nshots < SHOT_METROPOLIS_THRESHOLD: - return super().sample_frequencies(probabilities, nshots) - - import collections - - seed = np.random.randint(0, int(1e8), dtype="int64") - nqubits = int(np.log2(tuple(probabilities.shape)[0])) - frequencies = np.zeros(2**nqubits, dtype="int64") - # always fall back to numba CPU backend because for ops not implemented on GPU - frequencies = self.measure_frequencies_op( - frequencies, probabilities, nshots, nqubits, seed, self.nthreads - ) - return collections.Counter({i: f for i, f in enumerate(frequencies) if f > 0}) - - # def calculate_frequencies(self, samples): Inherited from ``NumpyBackend`` - - # def assert_allclose(self, value, target, rtol=1e-7, atol=0.0): Inherited from ``NumpyBackend`` From 2adaf3836d1a25baa0fb31c04268c1456021bc6c Mon Sep 17 00:00:00 2001 From: yangliwei Date: Tue, 6 Feb 2024 16:30:24 +0800 Subject: [PATCH 124/199] Add the tensor network library list --- README.md | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 54ff75dc..117d6a2e 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,21 @@ -Qibotn is the tensor-network translation module for Qibo to support large-scale simulation of quantum circuits and acceleration. +Qibotn is the tensor network translation module for Qibo to support large-scale simulation of quantum circuits and acceleration. To get started, `python setup.py install` to install the tools and dependencies. -# Computation Supported +# Supported Computation -1. Tensornet (TN) with contractions to: - - dense vector - - expecation of given Pauli string +Tensor network contractions to: +- dense vectors +- expecation values of given Pauli string - For each TN case: - - single node - - multi node with Message Passing Interface (MPI) - - multi node with NCCL +The supported configuration are: +- single node +- multi node with Message Passing Interface (MPI) +- multi node with NVIDIA Collective Communications Library (NCCL) + +Currently the supported libraries are: + - [cuQuantum](https://github.com/NVIDIA/cuQuantum) + - [quimb](https://quimb.readthedocs.io/en/latest/) 2. Tensornet (TN) with contractions to: - dense vector (single node) From 802dde6e57e1a372d8395abe28fb14d84d499259 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Tue, 6 Feb 2024 17:48:53 +0800 Subject: [PATCH 125/199] Added class for quimb backend --- src/qibotn/backends/cpu.py | 144 +++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 src/qibotn/backends/cpu.py diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py new file mode 100644 index 00000000..d826ba16 --- /dev/null +++ b/src/qibotn/backends/cpu.py @@ -0,0 +1,144 @@ +import numpy as np + +from qibo.backends.numpy import NumpyBackend +from qibo.states import CircuitResult +from qibo.config import raise_error + + +class QuTensorNet(NumpyBackend): + + def __init__(self, runcard): + super().__init__() + import quimb # pylint: disable=import-error + + if runcard is not None: + self.MPI_enabled = runcard.get("MPI_enabled", False) + self.NCCL_enabled = runcard.get("NCCL_enabled", False) + self.expectation_enabled_value = runcard.get("expectation_enabled", False) + + + mps_enabled_value = runcard.get("MPS_enabled") + if mps_enabled_value is True: + self.MPS_enabled = True + elif mps_enabled_value is False: + self.MPS_enabled = False + else: + raise TypeError("MPS_enabled has an unexpected type") + + else: + self.MPI_enabled = False + self.MPS_enabled = False + self.NCCL_enabled = False + self.expectation_enabled = False + + self.name = "qibotn" + self.quimb = quimb + self.platform = "qutensornet" + self.versions["quimb"] = self.quimb.__version__ + + + def apply_gate(self, gate, state, nqubits): # pragma: no cover + raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") + + def apply_gate_density_matrix(self, gate, state, nqubits): # pragma: no cover + raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") + + def assign_measurements(self, measurement_map, circuit_result): + raise_error(NotImplementedError, "Not implemented in QiboTN.") + + + def set_precision(self, precision): + if precision != self.precision: + super().set_precision(precision) + + + + def execute_circuit( + self, circuit, initial_state=None, nshots=None, return_array=False + ): # pragma: no cover + """Executes a quantum circuit. + + Args: + 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. + + """ + + import qibotn.eval_qu as eval_qu + + if ( + self.MPI_enabled == False + and self.MPS_enabled == False + and self.NCCL_enabled == False + and self.expectation_enabled == False + ): + + state = eval.dense_vector_tn_qu(circuit, init_state, is_mps=False, backend="numpy") + + elif ( + self.MPI_enabled == False + and self.MPS_enabled == True + and self.NCCL_enabled == False + and self.expectation_enabled == False + ): + + + state = eval.dense_vector_tn_qu(circuit, init_state, is_mps=True, backend="numpy") + + elif ( + self.MPI_enabled == True + and self.MPS_enabled == False + and self.NCCL_enabled == False + and self.expectation_enabled == False + ): + + raise_error(NotImplementedError, "QiboTN quimb backend cannot support MPI.") + + + elif ( + self.MPI_enabled == False + and self.MPS_enabled == False + and self.NCCL_enabled == True + and self.expectation_enabled == False + ): + + raise_error(NotImplementedError, "QiboTN quimb backend cannot support NCCL.") + + + elif ( + self.MPI_enabled == False + and self.MPS_enabled == False + and self.NCCL_enabled == False + and self.expectation_enabled == True + ): + + raise_error(NotImplementedError, "QiboTN quimb backend cannot support expectation") + + + elif ( + self.MPI_enabled == True + and self.MPS_enabled == False + and self.NCCL_enabled == False + and self.expectation_enabled == True + ): + raise_error(NotImplementedError, "QiboTN quimb backend cannot support expectation") + + elif ( + self.MPI_enabled == False + and self.MPS_enabled == False + and self.NCCL_enabled == True + and self.expectation_enabled == True + ): + raise_error(NotImplementedError, "QiboTN quimb backend cannot support expectation") + else: + raise_error(NotImplementedError, "Compute type not supported.") + + if return_array: + return state.flatten() + else: + circuit._final_state = CircuitResult(self, circuit, state.flatten(), nshots) + return circuit._final_state From a6af0fb25a1443f4b7caf6d10f994c3841f928d0 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Tue, 6 Feb 2024 17:52:55 +0800 Subject: [PATCH 126/199] Added quimb backend --- src/qibotn/backends/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qibotn/backends/__init__.py b/src/qibotn/backends/__init__.py index 26a0b9d4..f9279323 100644 --- a/src/qibotn/backends/__init__.py +++ b/src/qibotn/backends/__init__.py @@ -1 +1,2 @@ from qibotn.backends.gpu import CuTensorNet +from qibotn.backends.cpu import QuTensorNet From 2861467328bb07080cae9c97d71823b9dfc391b0 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Tue, 6 Feb 2024 17:54:24 +0800 Subject: [PATCH 127/199] renamed function 'eval' to 'dense_vector_tn_qu' --- src/qibotn/eval_qu.py | 49 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/qibotn/eval_qu.py diff --git a/src/qibotn/eval_qu.py b/src/qibotn/eval_qu.py new file mode 100644 index 00000000..9dc639cd --- /dev/null +++ b/src/qibotn/eval_qu.py @@ -0,0 +1,49 @@ +import numpy as np +import quimb.tensor as qtn +from qibo.models import Circuit as QiboCircuit + + +def from_qibo(circuit: QiboCircuit, is_mps: False, psi0=None, method='svd', + cutoff=1e-6, cutoff_mode='abs'): + nqubits = circuit.nqubits + gate_opt = {} + if is_mps: + tncirc = qtn.CircuitMPS(nqubits, psi0=psi0) + gate_opt["method"] = method + gate_opt["cutoff"] = cutoff + gate_opt["cutoff_mode"] = cutoff_mode + else: + tncirc = qtn.Circuit(nqubits, psi0=psi0) + + for gate in circuit.queue: + tncirc.apply_gate( + gate.name, + *gate.parameters, + *gate.qubits, + parametrize=False if is_mps else (len(gate.parameters) > 0), + **gate_opt + ) + + return tncirc + + +def init_state_tn(nqubits, init_state_sv): + dims = tuple(2 * np.ones(nqubits, dtype=int)) + + return qtn.tensor_1d.MatrixProductState.from_dense(init_state_sv, dims) + + +def dense_vector_tn_qu(qasm: str, init_state, is_mps, backend="numpy"): + """Evaluate QASM with Quimb + + backend (quimb): numpy, cupy, jax. Passed to ``opt_einsum``. + + """ + circuit = QiboCircuit.from_qasm(qasm) + if init_state is not None: + init_state = init_state_tn(circuit.nqubits, init_state) + circ_quimb = from_qibo(circuit, is_mps, psi0=init_state) + interim = circ_quimb.psi.full_simplify(seq="DRC") + amplitudes = interim.to_dense(backend=backend).flatten() + + return amplitudes From cddb59f5f4cc255544edff1244e0dacac9259b67 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Tue, 6 Feb 2024 17:56:29 +0800 Subject: [PATCH 128/199] renamed function 'eval' to 'dense_vector_tn_qu' in pytest --- tests/test_qasm_quimb_backend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_qasm_quimb_backend.py b/tests/test_qasm_quimb_backend.py index 6abfb7f1..90d9f312 100644 --- a/tests/test_qasm_quimb_backend.py +++ b/tests/test_qasm_quimb_backend.py @@ -29,7 +29,7 @@ def test_eval(nqubits: int, tolerance: float, is_mps: bool): # TODO: remove completely, or at least delegate to the backend # implementation os.environ["QUIMB_NUM_PROCS"] = str(os.cpu_count()) - import qibotn.quimb + import qibotn.eval_qu init_state = create_init_state(nqubits=nqubits) init_state_tn = copy.deepcopy(init_state) @@ -45,7 +45,7 @@ def test_eval(nqubits: int, tolerance: float, is_mps: bool): qasm_circ = qibo_circ.to_qasm() # Test quimb - result_tn = qibotn.quimb.eval( + result_tn = qibotn.eval_qu.dense_vector_tn_qu( qasm_circ, init_state_tn, is_mps, backend=config.quimb.backend ) From c6d61e64f74edd421fe3aa0910f56459b3e57827 Mon Sep 17 00:00:00 2001 From: yangliwei Date: Wed, 7 Feb 2024 16:48:18 +0800 Subject: [PATCH 129/199] Minor update in the supported configurations and tensor network library list --- README.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 117d6a2e..74e1e3a6 100644 --- a/README.md +++ b/README.md @@ -8,17 +8,15 @@ Tensor network contractions to: - dense vectors - expecation values of given Pauli string -The supported configuration are: -- single node -- multi node with Message Passing Interface (MPI) -- multi node with NVIDIA Collective Communications Library (NCCL) - -Currently the supported libraries are: - - [cuQuantum](https://github.com/NVIDIA/cuQuantum) - - [quimb](https://quimb.readthedocs.io/en/latest/) - -2. Tensornet (TN) with contractions to: - - dense vector (single node) +The supported configurations are: +- single-node CPU +- single-node GPU or GPUs +- multi-node multi-GPU with Message Passing Interface (MPI) +- multi-node multi-GPU with NVIDIA Collective Communications Library (NCCL) + +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. # Sample Codes ## Single Node From c7ec0f40da659dd4dead5dea7daf31b0e0bb07ba Mon Sep 17 00:00:00 2001 From: yangliwei Date: Wed, 7 Feb 2024 17:51:44 +0800 Subject: [PATCH 130/199] Format update using black formatter --- src/qibotn/backends/cpu.py | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py index d826ba16..b6b5cf69 100644 --- a/src/qibotn/backends/cpu.py +++ b/src/qibotn/backends/cpu.py @@ -5,7 +5,7 @@ from qibo.config import raise_error -class QuTensorNet(NumpyBackend): +class QuTensorNet(NumpyBackend): def __init__(self, runcard): super().__init__() @@ -16,7 +16,6 @@ def __init__(self, runcard): self.NCCL_enabled = runcard.get("NCCL_enabled", False) self.expectation_enabled_value = runcard.get("expectation_enabled", False) - mps_enabled_value = runcard.get("MPS_enabled") if mps_enabled_value is True: self.MPS_enabled = True @@ -35,7 +34,6 @@ def __init__(self, runcard): self.quimb = quimb self.platform = "qutensornet" self.versions["quimb"] = self.quimb.__version__ - def apply_gate(self, gate, state, nqubits): # pragma: no cover raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") @@ -46,13 +44,10 @@ def apply_gate_density_matrix(self, gate, state, nqubits): # pragma: no cover def assign_measurements(self, measurement_map, circuit_result): raise_error(NotImplementedError, "Not implemented in QiboTN.") - def set_precision(self, precision): if precision != self.precision: super().set_precision(precision) - - def execute_circuit( self, circuit, initial_state=None, nshots=None, return_array=False ): # pragma: no cover @@ -77,7 +72,9 @@ def execute_circuit( and self.expectation_enabled == False ): - state = eval.dense_vector_tn_qu(circuit, init_state, is_mps=False, backend="numpy") + state = eval.dense_vector_tn_qu( + circuit, init_state, is_mps=False, backend="numpy" + ) elif ( self.MPI_enabled == False @@ -85,9 +82,10 @@ def execute_circuit( and self.NCCL_enabled == False and self.expectation_enabled == False ): - - state = eval.dense_vector_tn_qu(circuit, init_state, is_mps=True, backend="numpy") + state = eval.dense_vector_tn_qu( + circuit, init_state, is_mps=True, backend="numpy" + ) elif ( self.MPI_enabled == True @@ -95,9 +93,8 @@ def execute_circuit( and self.NCCL_enabled == False and self.expectation_enabled == False ): - - raise_error(NotImplementedError, "QiboTN quimb backend cannot support MPI.") + raise_error(NotImplementedError, "QiboTN quimb backend cannot support MPI.") elif ( self.MPI_enabled == False @@ -105,9 +102,10 @@ def execute_circuit( and self.NCCL_enabled == True and self.expectation_enabled == False ): - - raise_error(NotImplementedError, "QiboTN quimb backend cannot support NCCL.") + raise_error( + NotImplementedError, "QiboTN quimb backend cannot support NCCL." + ) elif ( self.MPI_enabled == False @@ -115,9 +113,10 @@ def execute_circuit( and self.NCCL_enabled == False and self.expectation_enabled == True ): - - raise_error(NotImplementedError, "QiboTN quimb backend cannot support expectation") + raise_error( + NotImplementedError, "QiboTN quimb backend cannot support expectation" + ) elif ( self.MPI_enabled == True @@ -125,7 +124,9 @@ def execute_circuit( and self.NCCL_enabled == False and self.expectation_enabled == True ): - raise_error(NotImplementedError, "QiboTN quimb backend cannot support expectation") + raise_error( + NotImplementedError, "QiboTN quimb backend cannot support expectation" + ) elif ( self.MPI_enabled == False @@ -133,7 +134,9 @@ def execute_circuit( and self.NCCL_enabled == True and self.expectation_enabled == True ): - raise_error(NotImplementedError, "QiboTN quimb backend cannot support expectation") + raise_error( + NotImplementedError, "QiboTN quimb backend cannot support expectation" + ) else: raise_error(NotImplementedError, "Compute type not supported.") From bbdc71b5f1d29e52f37a301ecb65e25910e97861 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 7 Feb 2024 11:10:44 +0100 Subject: [PATCH 131/199] ci: Update pre-commit configurations, introduce pre-commit ci --- .pre-commit-config.yaml | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 76230b3f..fab53d26 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,25 +1,50 @@ -# See https://pre-commit.com for more information -# See https://pre-commit.com/hooks.html for more hooks +ci: + autofix_prs: true repos: - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 rev: v4.5.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-yaml - id: check-toml - - id: check-merge-conflict - id: debug-statements - repo: https://github.com/psf/black - rev: 24.2.0 + rev: 24.1.1 hooks: - id: black - repo: https://github.com/pycqa/isort + rev: 5.13.2 rev: 5.13.2 hooks: - id: isort args: ["--profile", "black"] + - repo: https://github.com/PyCQA/docformatter + rev: v1.7.5 + hooks: + - id: docformatter + additional_dependencies: [tomli] + args: [--in-place, --config, ./pyproject.toml] - repo: https://github.com/asottile/pyupgrade rev: v3.15.0 hooks: - id: pyupgrade + - repo: https://github.com/hadialqattan/pycln + rev: v2.4.0 + hooks: + - id: pycln + args: + - --config=pyproject.toml + - --all + - repo: https://github.com/adamchainz/blacken-docs + rev: 1.16.0 + hooks: + - id: blacken-docs + - repo: https://github.com/pycqa/pydocstyle + rev: 6.3.0 + hooks: + - id: pydocstyle + args: + - --select=D103,D200,D206,D300,D301 + files: ^src/ From 91b83c22eb721905c4da87bafdcd40afdc04a4cd Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 11:46:32 +0800 Subject: [PATCH 132/199] removed quimb.py as it is duplicate of eval_qu.py --- src/qibotn/quimb.py | 54 --------------------------------------------- 1 file changed, 54 deletions(-) delete mode 100644 src/qibotn/quimb.py diff --git a/src/qibotn/quimb.py b/src/qibotn/quimb.py deleted file mode 100644 index 0349f58a..00000000 --- a/src/qibotn/quimb.py +++ /dev/null @@ -1,54 +0,0 @@ -import numpy as np -import quimb.tensor as qtn -from qibo.models import Circuit as QiboCircuit - - -def from_qibo( - circuit: QiboCircuit, - is_mps: False, - psi0=None, - method="svd", - cutoff=1e-6, - cutoff_mode="abs", -): - nqubits = circuit.nqubits - gate_opt = {} - if is_mps: - tncirc = qtn.CircuitMPS(nqubits, psi0=psi0) - gate_opt["method"] = method - gate_opt["cutoff"] = cutoff - gate_opt["cutoff_mode"] = cutoff_mode - else: - tncirc = qtn.Circuit(nqubits, psi0=psi0) - - for gate in circuit.queue: - tncirc.apply_gate( - gate.name, - *gate.parameters, - *gate.qubits, - parametrize=False if is_mps else (len(gate.parameters) > 0), - **gate_opt - ) - - return tncirc - - -def init_state_tn(nqubits, init_state_sv): - dims = tuple(2 * np.ones(nqubits, dtype=int)) - - return qtn.tensor_1d.MatrixProductState.from_dense(init_state_sv, dims) - - -def eval(qasm: str, init_state, is_mps, backend="numpy"): - """Evaluate QASM with Quimb - - backend (quimb): numpy, cupy, jax. Passed to ``opt_einsum``. - - """ - circuit = QiboCircuit.from_qasm(qasm) - init_state_mps = init_state_tn(circuit.nqubits, init_state) - circ_quimb = from_qibo(circuit, is_mps, psi0=init_state_mps) - interim = circ_quimb.psi.full_simplify(seq="DRC") - amplitudes = interim.to_dense(backend=backend).flatten() - - return amplitudes From 9b9f359363c1b207801800186f7571458a290392 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 11:47:25 +0800 Subject: [PATCH 133/199] removed __main__.py as it is no longer used --- src/qibotn/__main__.py | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 src/qibotn/__main__.py diff --git a/src/qibotn/__main__.py b/src/qibotn/__main__.py deleted file mode 100644 index 0476be5e..00000000 --- a/src/qibotn/__main__.py +++ /dev/null @@ -1,20 +0,0 @@ -import argparse - -import qibotn.quimb - - -def parser(): - parser = argparse.ArgumentParser() - parser.add_argument( - "--nqubits", default=10, type=int, help="Number of quibits in the circuits." - ) - return parser.parse_args() - - -def main(args: argparse.Namespace): - print("Testing for %d nqubits" % (args.nqubits)) - qibotn.quimb.eval(args.nqubits, args.qasm_circ, args.init_state) - - -if __name__ == "__main__": - main(parser()) From 8e9e5871635876bb86d9bd732f2c325de2f58704 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 12:06:38 +0800 Subject: [PATCH 134/199] added missing docstring --- src/qibotn/eval_qu.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/qibotn/eval_qu.py b/src/qibotn/eval_qu.py index 9dc639cd..78d84ef1 100644 --- a/src/qibotn/eval_qu.py +++ b/src/qibotn/eval_qu.py @@ -5,6 +5,8 @@ def from_qibo(circuit: QiboCircuit, is_mps: False, psi0=None, method='svd', cutoff=1e-6, cutoff_mode='abs'): + """Create a tensornetwork representation of the circuit""" + nqubits = circuit.nqubits gate_opt = {} if is_mps: @@ -28,6 +30,9 @@ def from_qibo(circuit: QiboCircuit, is_mps: False, psi0=None, method='svd', def init_state_tn(nqubits, init_state_sv): + + """Create a matrixproductstate directly from a dense vector""" + dims = tuple(2 * np.ones(nqubits, dtype=int)) return qtn.tensor_1d.MatrixProductState.from_dense(init_state_sv, dims) From 8dd2333f762d12d7362b4ad83751d9098af105b6 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 12:07:30 +0800 Subject: [PATCH 135/199] added missing docstring --- tests/test_qasm_quimb_backend.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/test_qasm_quimb_backend.py b/tests/test_qasm_quimb_backend.py index 90d9f312..da4d422e 100644 --- a/tests/test_qasm_quimb_backend.py +++ b/tests/test_qasm_quimb_backend.py @@ -25,6 +25,13 @@ def qibo_qft(nqubits, init_state, swaps): [(1, 1e-6, True), (2, 1e-6, False), (5, 1e-3, True), (10, 1e-3, False)], ) def test_eval(nqubits: int, tolerance: float, is_mps: bool): + """Evaluate circuit with Quimb backend. + + Args: + nqubits (int): Total number of qubits in the system. + tolerance (float): Maximum limit allowed for difference in results + is_mps (bool): True if state is MPS and False for tensor network structure + """ # hack quimb to use the correct number of processes # TODO: remove completely, or at least delegate to the backend # implementation @@ -36,9 +43,7 @@ def test_eval(nqubits: int, tolerance: float, is_mps: bool): # Test qibo qibo.set_backend(backend=config.qibo.backend, platform=config.qibo.platform) - # qibo_time, (qibo_circ, result_sv) = time( - # lambda: qibo_qft(nqubits, init_state, swaps=True) - # ) + qibo_circ, result_sv = qibo_qft(nqubits, init_state, swaps=True) # Convert to qasm for other backends From 028ff8528b6546b1306b5925b7c9ec22fe1bf499 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 14:17:35 +0800 Subject: [PATCH 136/199] fixed some bugs --- src/qibotn/backends/cpu.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py index b6b5cf69..2483d981 100644 --- a/src/qibotn/backends/cpu.py +++ b/src/qibotn/backends/cpu.py @@ -14,7 +14,7 @@ def __init__(self, runcard): if runcard is not None: self.MPI_enabled = runcard.get("MPI_enabled", False) self.NCCL_enabled = runcard.get("NCCL_enabled", False) - self.expectation_enabled_value = runcard.get("expectation_enabled", False) + self.expectation_enabled = runcard.get("expectation_enabled", False) mps_enabled_value = runcard.get("MPS_enabled") if mps_enabled_value is True: @@ -63,7 +63,7 @@ def execute_circuit( """ - import qibotn.eval_qu as eval_qu + import qibotn.eval_qu as eval if ( self.MPI_enabled == False @@ -73,7 +73,7 @@ def execute_circuit( ): state = eval.dense_vector_tn_qu( - circuit, init_state, is_mps=False, backend="numpy" + circuit, initial_state=None, is_mps=False, backend="numpy" ) elif ( @@ -84,7 +84,7 @@ def execute_circuit( ): state = eval.dense_vector_tn_qu( - circuit, init_state, is_mps=True, backend="numpy" + circuit, initial_state=None, is_mps=True, backend="numpy" ) elif ( From 0a013d371ea256156ffdddffce16f9db60ea637d Mon Sep 17 00:00:00 2001 From: tankya2 Date: Thu, 8 Feb 2024 13:09:19 +0800 Subject: [PATCH 137/199] Remove unused variable --- src/qibotn/QiboCircuitConvertor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qibotn/QiboCircuitConvertor.py b/src/qibotn/QiboCircuitConvertor.py index c59745bd..5ac2feed 100644 --- a/src/qibotn/QiboCircuitConvertor.py +++ b/src/qibotn/QiboCircuitConvertor.py @@ -166,7 +166,6 @@ def expectation_operands(self, pauli_string): input_operands = self._get_bitstring_tensors(input_bitstring) pauli_string = dict(zip(range(self.circuit.nqubits), pauli_string)) pauli_map = pauli_string - coned_qubits = pauli_map.keys() ( mode_labels, From 0ba6c3ec9a302b5b186be1c99ea6ffee3ec12f26 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Thu, 8 Feb 2024 13:57:10 +0800 Subject: [PATCH 138/199] Update sample codes to include Quimb --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 74e1e3a6..b7532ac4 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,9 @@ computation_settings = { } -qibo.set_backend(backend="qibotn", runcard=computation_settings) +qibo.set_backend(backend="qibotn", platform="cutensornet", runcard=computation_settings) #cuQuantum +# qibo.set_backend(backend="qibotn", platform="qutensornet", runcard=computation_settings) #quimb + # Construct the circuit c = Circuit(2) From 36d8391ff281cfd19e5f88a3704716be3cc62f33 Mon Sep 17 00:00:00 2001 From: nitinshivaraman Date: Thu, 8 Feb 2024 15:34:14 +0800 Subject: [PATCH 139/199] Rename test_qasm_quimb_backend.py to test_quimb_backend.py for better readability --- tests/{test_qasm_quimb_backend.py => test_quimb_backend.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{test_qasm_quimb_backend.py => test_quimb_backend.py} (100%) diff --git a/tests/test_qasm_quimb_backend.py b/tests/test_quimb_backend.py similarity index 100% rename from tests/test_qasm_quimb_backend.py rename to tests/test_quimb_backend.py From 94d0370408efb368e0ccd8e146641edd19a3afb6 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 16:17:39 +0800 Subject: [PATCH 140/199] fixed error caused by missing qasm str error --- src/qibotn/backends/cpu.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py index 2483d981..2df78839 100644 --- a/src/qibotn/backends/cpu.py +++ b/src/qibotn/backends/cpu.py @@ -73,7 +73,7 @@ def execute_circuit( ): state = eval.dense_vector_tn_qu( - circuit, initial_state=None, is_mps=False, backend="numpy" + circuit.to_qasm(), initial_state=None, is_mps=False, backend="numpy" ) elif ( @@ -84,7 +84,7 @@ def execute_circuit( ): state = eval.dense_vector_tn_qu( - circuit, initial_state=None, is_mps=True, backend="numpy" + circuit.to_qasm(), initial_state=None, is_mps=True, backend="numpy" ) elif ( From c861a130352bf362e76ee2618a93599efb6bd40c Mon Sep 17 00:00:00 2001 From: yangliwei Date: Thu, 8 Feb 2024 16:16:30 +0800 Subject: [PATCH 141/199] Minor naming update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b7532ac4..2e8dd948 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Tensor network contractions to: - dense vectors - expecation values of given Pauli string -The supported configurations are: +The supported HPC configurations are: - single-node CPU - single-node GPU or GPUs - multi-node multi-GPU with Message Passing Interface (MPI) From 03c9c6add70af99172ad405e205db8572b908f91 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 16:19:59 +0800 Subject: [PATCH 142/199] fixed error caused by initial_state --- src/qibotn/eval_qu.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/qibotn/eval_qu.py b/src/qibotn/eval_qu.py index 78d84ef1..579a42ae 100644 --- a/src/qibotn/eval_qu.py +++ b/src/qibotn/eval_qu.py @@ -38,17 +38,17 @@ def init_state_tn(nqubits, init_state_sv): return qtn.tensor_1d.MatrixProductState.from_dense(init_state_sv, dims) -def dense_vector_tn_qu(qasm: str, init_state, is_mps, backend="numpy"): +def dense_vector_tn_qu(qasm: str, initial_state, is_mps, backend="numpy"): """Evaluate QASM with Quimb backend (quimb): numpy, cupy, jax. Passed to ``opt_einsum``. """ circuit = QiboCircuit.from_qasm(qasm) - if init_state is not None: - init_state = init_state_tn(circuit.nqubits, init_state) - circ_quimb = from_qibo(circuit, is_mps, psi0=init_state) + if initial_state is not None: + initial_state = init_state_tn(circuit.nqubits, initial_state) + circ_quimb = from_qibo(circuit, is_mps, psi0=initial_state) interim = circ_quimb.psi.full_simplify(seq="DRC") - amplitudes = interim.to_dense(backend=backend).flatten() + amplitudes = interim.to_dense(backend=backend) return amplitudes From 51312158079d88e43951cae155feb15a0fafad3d Mon Sep 17 00:00:00 2001 From: tankya2 Date: Thu, 8 Feb 2024 16:19:17 +0800 Subject: [PATCH 143/199] Add in tensor network types --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e8dd948..cdf65d46 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,11 @@ To get started, `python setup.py install` to install the tools and dependencies. # Supported Computation -Tensor network contractions to: +Tensor Network Types: +- Tensornet (TN) +- Matrix Product States (MPS) + +Tensor Network contractions to: - dense vectors - expecation values of given Pauli string From 3b73a22e6a1d3069ce9bec899df3ccd33bc18ac1 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 16:22:38 +0800 Subject: [PATCH 144/199] added flatten of state in pytest instead of using within eval_qu --- tests/test_quimb_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_quimb_backend.py b/tests/test_quimb_backend.py index da4d422e..15ba6524 100644 --- a/tests/test_quimb_backend.py +++ b/tests/test_quimb_backend.py @@ -52,7 +52,7 @@ def test_eval(nqubits: int, tolerance: float, is_mps: bool): # Test quimb result_tn = qibotn.eval_qu.dense_vector_tn_qu( qasm_circ, init_state_tn, is_mps, backend=config.quimb.backend - ) + ).flatten() assert np.allclose( result_sv, result_tn, atol=tolerance From 3af7cd1d3d4c94b21c5f8fcbc09f9ca786054693 Mon Sep 17 00:00:00 2001 From: yangliwei Date: Thu, 8 Feb 2024 16:31:22 +0800 Subject: [PATCH 145/199] Minor update for consistency --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cdf65d46..070ccb33 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Currently, the supported tensor network libraries are: - [quimb](https://quimb.readthedocs.io/en/latest/), an easy but fast python library for ‘quantum information many-body’ calculations, focusing primarily on tensor networks. # Sample Codes -## Single Node +## Single-Node Example The code below shows an example of how to activate the Cuquantum TensorNetwork backend of Qibo. ```py import numpy as np @@ -86,7 +86,7 @@ computation_settings = { } ``` -## Multi-Node +## Multi-Node Example Multi-node is enabled by setting either the MPI or NCCL enabled flag to True in the computation settings. Below shows the script to launch on 2 nodes with 2 GPUs each. $node_list contains the IP of the nodes assigned. From 5e8c9d19ab2f8509c8a8a1d75ad66381a16a2dbf Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 16:34:46 +0800 Subject: [PATCH 146/199] minor update of initial state --- src/qibotn/backends/cpu.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py index 2df78839..7115b395 100644 --- a/src/qibotn/backends/cpu.py +++ b/src/qibotn/backends/cpu.py @@ -73,7 +73,7 @@ def execute_circuit( ): state = eval.dense_vector_tn_qu( - circuit.to_qasm(), initial_state=None, is_mps=False, backend="numpy" + circuit.to_qasm(), initial_state, is_mps=False, backend="numpy" ) elif ( @@ -84,7 +84,7 @@ def execute_circuit( ): state = eval.dense_vector_tn_qu( - circuit.to_qasm(), initial_state=None, is_mps=True, backend="numpy" + circuit.to_qasm(), initial_state, is_mps=True, backend="numpy" ) elif ( From b432be6d9cbbfb2b50d3c6341d032d5881895de2 Mon Sep 17 00:00:00 2001 From: yangliwei Date: Thu, 8 Feb 2024 17:18:07 +0800 Subject: [PATCH 147/199] Minor black formatting --- src/qibotn/QiboCircuitConvertor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/QiboCircuitConvertor.py b/src/qibotn/QiboCircuitConvertor.py index 5ac2feed..e1aabea2 100644 --- a/src/qibotn/QiboCircuitConvertor.py +++ b/src/qibotn/QiboCircuitConvertor.py @@ -161,7 +161,7 @@ def get_pauli_gates(self, pauli_map, dtype="complex128", backend=cp): return gates def expectation_operands(self, pauli_string): - input_bitstring = "0" * self.circuit.nqubits + input_bitstring = "0" * self.circuit.nqubits input_operands = self._get_bitstring_tensors(input_bitstring) pauli_string = dict(zip(range(self.circuit.nqubits), pauli_string)) From 898ca877e9a4f3d566b90f44ab903bbc8cf0647f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 09:18:18 +0000 Subject: [PATCH 148/199] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- README.md | 4 +- setup.py | 4 +- src/qibotn/MPSUtils.py | 11 +--- src/qibotn/QiboCircuitConvertor.py | 14 ++--- src/qibotn/backends/__init__.py | 2 +- src/qibotn/backends/cpu.py | 5 +- src/qibotn/backends/gpu.py | 4 +- src/qibotn/eval.py | 89 ++++++++++++++++++---------- src/qibotn/eval_qu.py | 20 ++++--- src/qibotn/mps_contraction_helper.py | 17 +++--- 10 files changed, 93 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index 070ccb33..9f60ad32 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ import qibo # Below shows how to set the computation_settings # Note that for MPS_enabled and expectation_enabled parameters the accepted inputs are boolean or a dictionary with the format shown below. -# If computation_settings is not specified, the default setting is used in which all booleans will be False. +# If computation_settings is not specified, the default setting is used in which all booleans will be False. # This will trigger the dense vector computation of the tensornet. computation_settings = { @@ -92,4 +92,4 @@ Multi-node is enabled by setting either the MPI or NCCL enabled flag to True in ```sh mpirun -n 4 -hostfile $node_list python test.py -``` \ No newline at end of file +``` diff --git a/setup.py b/setup.py index e44a9063..0f619a5c 100644 --- a/setup.py +++ b/setup.py @@ -9,8 +9,8 @@ # Returns the qibotn version def version(): - """Gets the version from the package's __init__ file - if there is some problem, let it happily fail""" + """Gets the version from the package's __init__ file if there is some + problem, let it happily fail.""" version_file = HERE / "src" / PACKAGE / "__init__.py" version_regex = r"^__version__ = ['\"]([^'\"]*)['\"]" diff --git a/src/qibotn/MPSUtils.py b/src/qibotn/MPSUtils.py index 1d37c42e..e8068f74 100644 --- a/src/qibotn/MPSUtils.py +++ b/src/qibotn/MPSUtils.py @@ -6,18 +6,14 @@ def initial(num_qubits, dtype): - """ - Generate the MPS with an initial state of |00...00> - """ + """Generate the MPS with an initial state of |00...00>""" 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.""" # contraction followed by QR decomposition a, _, b = contract_decompose( "ipj,jqk->iqj,jpk", @@ -30,8 +26,7 @@ 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. + """Apply the gate operand to the MPS tensors in-place. Args: mps_tensors: A list of rank-3 ndarray-like tensor objects. diff --git a/src/qibotn/QiboCircuitConvertor.py b/src/qibotn/QiboCircuitConvertor.py index e1aabea2..f67fb8ea 100644 --- a/src/qibotn/QiboCircuitConvertor.py +++ b/src/qibotn/QiboCircuitConvertor.py @@ -5,9 +5,9 @@ class QiboCircuitToEinsum: - """Convert a circuit to a Tensor Network (TN) representation. - The circuit is first processed to an intermediate form by grouping each gate - matrix with its corresponding qubit it is acting on to a list. It is then + """Convert a circuit to a Tensor Network (TN) representation. The circuit + is first processed to an intermediate form by grouping each gate matrix + with its corresponding qubit it is acting on to a list. It is then converted to an equivalent TN expression through the class function state_vector_operands() following the Einstein summation convention in the interleave format. @@ -79,9 +79,8 @@ def _parse_gates_to_mode_labels_operands( 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 (qubit_states,input_output) * + qubits_involved.""" return (2, 2) * nqubits def init_intermediate_circuit(self, circuit): @@ -134,8 +133,7 @@ def init_inverse_circuit(self, circuit): self.active_qubits_inverse = np.unique(gates_qubits_inverse) def get_pauli_gates(self, pauli_map, dtype="complex128", backend=cp): - """ - Populate the gates for all pauli operators. + """Populate the gates for all pauli operators. Args: pauli_map: A dictionary mapping qubits to pauli operators. diff --git a/src/qibotn/backends/__init__.py b/src/qibotn/backends/__init__.py index f9279323..e5d68de5 100644 --- a/src/qibotn/backends/__init__.py +++ b/src/qibotn/backends/__init__.py @@ -1,2 +1,2 @@ -from qibotn.backends.gpu import CuTensorNet from qibotn.backends.cpu import QuTensorNet +from qibotn.backends.gpu import CuTensorNet diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py index 7115b395..a85dfd9b 100644 --- a/src/qibotn/backends/cpu.py +++ b/src/qibotn/backends/cpu.py @@ -1,8 +1,6 @@ -import numpy as np - from qibo.backends.numpy import NumpyBackend -from qibo.states import CircuitResult from qibo.config import raise_error +from qibo.states import CircuitResult class QuTensorNet(NumpyBackend): @@ -60,7 +58,6 @@ def execute_circuit( Returns: xxx. - """ import qibotn.eval_qu as eval diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index 5777fe90..2c3f8d41 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -1,8 +1,7 @@ import numpy as np - from qibo.backends.numpy import NumpyBackend -from qibo.states import CircuitResult from qibo.config import raise_error +from qibo.states import CircuitResult class CuTensorNet(NumpyBackend): # pragma: no cover @@ -107,7 +106,6 @@ def execute_circuit( Returns: xxx. - """ import qibotn.eval as eval diff --git a/src/qibotn/eval.py b/src/qibotn/eval.py index 98c87a2e..5fcb66f0 100644 --- a/src/qibotn/eval.py +++ b/src/qibotn/eval.py @@ -1,10 +1,6 @@ -import multiprocessing - import cupy as cp from cupy.cuda.runtime import getDeviceCount from cuquantum import contract -from cupy.cuda.runtime import getDeviceCount -import cupy as cp from qibotn.mps_contraction_helper import MPSContractionHelper from qibotn.QiboCircuitConvertor import QiboCircuitToEinsum @@ -12,13 +8,15 @@ def dense_vector_tn(qibo_circ, datatype): - """Convert qibo circuit to tensornet (TN) format and perform contraction to dense vector.""" + """Convert qibo circuit to tensornet (TN) format and perform contraction to + dense vector.""" 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.""" + """Convert qibo circuit to tensornet (TN) format and perform contraction to + expectation of given Pauli string.""" myconvertor = QiboCircuitToEinsum(qibo_circ, dtype=datatype) return contract( *myconvertor.expectation_operands( @@ -28,14 +26,19 @@ def expectation_pauli_tn(qibo_circ, datatype, pauli_string_pattern): def dense_vector_tn_MPI(qibo_circ, datatype, n_samples=8): - """Convert qibo circuit to tensornet (TN) format and perform contraction using multi node and multi GPU through MPI. - The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. - The pathfinder looks at user defined number of samples (n_samples) iteratively to 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 a dense vector representation of the TN. + """Convert qibo circuit to tensornet (TN) format and perform contraction + using multi node and multi GPU through MPI. + + The conversion is performed by QiboCircuitToEinsum(), after which it + goes through 2 steps: pathfinder and execution. The pathfinder looks + at user defined number of samples (n_samples) iteratively to 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 a dense vector representation of the TN. """ - from mpi4py import MPI from cuquantum import Network + from mpi4py import MPI root = 0 comm = MPI.COMM_WORLD @@ -90,14 +93,19 @@ def dense_vector_tn_MPI(qibo_circ, datatype, n_samples=8): def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8): - """Convert qibo circuit to tensornet (TN) format and perform contraction using multi node and multi GPU through NCCL. - The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. - The pathfinder looks at user defined number of samples (n_samples) iteratively to 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 a dense vector representation of the TN. + """Convert qibo circuit to tensornet (TN) format and perform contraction + using multi node and multi GPU through NCCL. + + The conversion is performed by QiboCircuitToEinsum(), after which it + goes through 2 steps: pathfinder and execution. The pathfinder looks + at user defined number of samples (n_samples) iteratively to 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 a dense vector representation of the TN. """ - from mpi4py import MPI - from cuquantum import Network from cupy.cuda import nccl + from cuquantum import Network + from mpi4py import MPI root = 0 comm_mpi = MPI.COMM_WORLD @@ -163,15 +171,22 @@ def dense_vector_tn_nccl(qibo_circ, datatype, n_samples=8): def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string_pattern, n_samples=8): - """Convert qibo circuit to tensornet (TN) format and perform contraction to expectation of given Pauli string using multi node and multi GPU through NCCL. - The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. - The pauli_string_pattern is used to generate the pauli string corresponding to the number of qubits of the system. - The pathfinder looks at user defined number of samples (n_samples) iteratively to 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. + """Convert qibo circuit to tensornet (TN) format and perform contraction to + expectation of given Pauli string using multi node and multi GPU through + NCCL. + + The conversion is performed by QiboCircuitToEinsum(), after which it + goes through 2 steps: pathfinder and execution. The + pauli_string_pattern is used to generate the pauli string + corresponding to the number of qubits of the system. The pathfinder + looks at user defined number of samples (n_samples) iteratively to + 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. """ - from mpi4py import MPI - from cuquantum import Network from cupy.cuda import nccl + from cuquantum import Network + from mpi4py import MPI root = 0 comm_mpi = MPI.COMM_WORLD @@ -239,14 +254,21 @@ def expectation_pauli_tn_nccl(qibo_circ, datatype, pauli_string_pattern, n_sampl def expectation_pauli_tn_MPI(qibo_circ, datatype, pauli_string_pattern, n_samples=8): - """Convert qibo circuit to tensornet (TN) format and perform contraction to expectation of given Pauli string using multi node and multi GPU through MPI. - The conversion is performed by QiboCircuitToEinsum(), after which it goes through 2 steps: pathfinder and execution. - The pauli_string_pattern is used to generate the pauli string corresponding to the number of qubits of the system. - The pathfinder looks at user defined number of samples (n_samples) iteratively to 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. + """Convert qibo circuit to tensornet (TN) format and perform contraction to + expectation of given Pauli string using multi node and multi GPU through + MPI. + + The conversion is performed by QiboCircuitToEinsum(), after which it + goes through 2 steps: pathfinder and execution. The + pauli_string_pattern is used to generate the pauli string + corresponding to the number of qubits of the system. The pathfinder + looks at user defined number of samples (n_samples) iteratively to + 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. """ - from mpi4py import MPI # this line initializes MPI from cuquantum import Network + from mpi4py import MPI # this line initializes MPI root = 0 comm = MPI.COMM_WORLD @@ -303,7 +325,8 @@ 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.""" + """Convert qibo circuit to matrix product state (MPS) format and perform + contraction to dense vector.""" myconvertor = QiboCircuitToMPS(qibo_circ, gate_algo, dtype=datatype) mps_helper = MPSContractionHelper(myconvertor.num_qubits) @@ -313,7 +336,9 @@ def dense_vector_mps(qibo_circ, gate_algo, datatype): def pauli_string_gen(nqubits, pauli_string_pattern): - """Used internally to generate the string based on given pattern and number of qubit. + """Used internally to generate the string based on given pattern and number + of qubit. + Example: pattern: "XZ", number of qubit: 7, output = XZXZXZX """ if nqubits <= 0: diff --git a/src/qibotn/eval_qu.py b/src/qibotn/eval_qu.py index 579a42ae..7b603b50 100644 --- a/src/qibotn/eval_qu.py +++ b/src/qibotn/eval_qu.py @@ -3,9 +3,15 @@ from qibo.models import Circuit as QiboCircuit -def from_qibo(circuit: QiboCircuit, is_mps: False, psi0=None, method='svd', - cutoff=1e-6, cutoff_mode='abs'): - """Create a tensornetwork representation of the circuit""" +def from_qibo( + circuit: QiboCircuit, + is_mps: False, + psi0=None, + method="svd", + cutoff=1e-6, + cutoff_mode="abs", +): + """Create a tensornetwork representation of the circuit.""" nqubits = circuit.nqubits gate_opt = {} @@ -30,19 +36,17 @@ def from_qibo(circuit: QiboCircuit, is_mps: False, psi0=None, method='svd', def init_state_tn(nqubits, init_state_sv): - - """Create a matrixproductstate directly from a dense vector""" + """Create a matrixproductstate directly from a dense vector.""" dims = tuple(2 * np.ones(nqubits, dtype=int)) return qtn.tensor_1d.MatrixProductState.from_dense(init_state_sv, dims) -def dense_vector_tn_qu(qasm: str, initial_state, is_mps, backend="numpy"): - """Evaluate QASM with Quimb +def dense_vector_tn_qu(qasm: str, initial_state, is_mps, backend="numpy"): + """Evaluate QASM with Quimb. backend (quimb): numpy, cupy, jax. Passed to ``opt_einsum``. - """ circuit = QiboCircuit.from_qasm(qasm) if initial_state is not None: diff --git a/src/qibotn/mps_contraction_helper.py b/src/qibotn/mps_contraction_helper.py index 9d667cf6..1c004de4 100644 --- a/src/qibotn/mps_contraction_helper.py +++ b/src/qibotn/mps_contraction_helper.py @@ -1,11 +1,10 @@ -from cuquantum import CircuitToEinsum, contract, contract_path, tensor +from cuquantum import contract, contract_path # Reference: https://github.com/NVIDIA/cuQuantum/blob/main/python/samples/cutensornet/tn_algorithms/mps_algorithms.ipynb class MPSContractionHelper: - """ - A helper class to compute various quantities for a given MPS. + """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: @@ -43,8 +42,8 @@ def __init__(self, num_qubits): ] def contract_norm(self, mps_tensors, options=None): - """ - Contract the corresponding tensor network to form the norm of the MPS. + """Contract the corresponding tensor network to form the norm of the + MPS. Args: mps_tensors: A list of rank-3 ndarray-like tensor objects. @@ -64,8 +63,8 @@ def contract_norm(self, mps_tensors, options=None): return self._contract(interleaved_inputs, options=options).real def contract_state_vector(self, mps_tensors, options=None): - """ - Contract the corresponding tensor network to form the state vector representation of the MPS. + """Contract the corresponding tensor network to form the state vector + representation of the MPS. Args: mps_tensors: A list of rank-3 ndarray-like tensor objects. @@ -86,8 +85,8 @@ def contract_state_vector(self, mps_tensors, options=None): def contract_expectation( self, mps_tensors, operator, qubits, options=None, normalize=False ): - """ - Contract the corresponding tensor network to form the expectation of the MPS. + """Contract the corresponding tensor network to form the expectation of + the MPS. Args: mps_tensors: A list of rank-3 ndarray-like tensor objects. From 10efdad45fcda0ebdb7372e0b2da8b2bbfb15d95 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 7 Feb 2024 11:13:55 +0100 Subject: [PATCH 149/199] build: Add Nix files --- .envrc | 9 ++ .gitignore | 1 + flake.lock | 323 +++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 62 ++++++++++ 4 files changed, 395 insertions(+) create mode 100644 .envrc create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..01f5f41d --- /dev/null +++ b/.envrc @@ -0,0 +1,9 @@ +if ! has nix_direnv_version || ! nix_direnv_version 2.2.1; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.2.1/direnvrc" "sha256-zelF0vLbEl5uaqrfIzbgNzJWGmLzCmYAkInj/LNxvKs=" +fi + +nix_direnv_watch_file flake.nix +nix_direnv_watch_file flake.lock +if ! use flake . --impure; then + echo "devenv could not be built. The devenv environment was not loaded. Make the necessary changes to devenv.nix and hit enter to try again." >&2 +fi diff --git a/.gitignore b/.gitignore index d903da02..7f051b80 100644 --- a/.gitignore +++ b/.gitignore @@ -159,3 +159,4 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ +.devenv diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..0f48ef05 --- /dev/null +++ b/flake.lock @@ -0,0 +1,323 @@ +{ + "nodes": { + "devenv": { + "inputs": { + "flake-compat": "flake-compat", + "nix": "nix", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": "pre-commit-hooks" + }, + "locked": { + "lastModified": 1707004164, + "narHash": "sha256-9Hr8onWtvLk5A8vCEkaE9kxA0D7PR62povFokM1oL5Q=", + "owner": "cachix", + "repo": "devenv", + "rev": "0e68853bb27981a4ffd7a7225b59ed84f7180fc7", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1685518550, + "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "id": "flake-utils", + "type": "indirect" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "devenv", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1660459072, + "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "lowdown-src": { + "flake": false, + "locked": { + "lastModified": 1633514407, + "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", + "owner": "kristapsdz", + "repo": "lowdown", + "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "type": "github" + }, + "original": { + "owner": "kristapsdz", + "repo": "lowdown", + "type": "github" + } + }, + "nix": { + "inputs": { + "lowdown-src": "lowdown-src", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression" + }, + "locked": { + "lastModified": 1676545802, + "narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=", + "owner": "domenkozar", + "repo": "nix", + "rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "relaxed-flakes", + "repo": "nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1678875422, + "narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-python": { + "inputs": { + "flake-compat": "flake-compat_2", + "flake-utils": "flake-utils_2", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1707114737, + "narHash": "sha256-ZXqv2epXAjDjfWbYn+yy4VOmW+C7SuUBoiZkkDoSqA4=", + "owner": "cachix", + "repo": "nixpkgs-python", + "rev": "f34ed02276bc08fe1c91c1bf0ef3589d68028878", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "nixpkgs-python", + "type": "github" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1685801374, + "narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "c37ca420157f4abc31e26f436c1145f8951ff373", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1707092692, + "narHash": "sha256-ZbHsm+mGk/izkWtT4xwwqz38fdlwu7nUUKXTOmm4SyE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "faf912b086576fd1a15fca610166c98d47bc667e", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "flake-utils": "flake-utils", + "gitignore": "gitignore", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1704725188, + "narHash": "sha256-qq8NbkhRZF1vVYQFt1s8Mbgo8knj+83+QlL5LBnYGpI=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "ea96f0c05924341c551a797aaba8126334c505d2", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "nixpkgs": "nixpkgs_2", + "nixpkgs-python": "nixpkgs-python", + "systems": "systems_3" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_3": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..92f53ee5 --- /dev/null +++ b/flake.nix @@ -0,0 +1,62 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + systems.url = "github:nix-systems/default"; + devenv.url = "github:cachix/devenv"; + nixpkgs-python = { + url = "github:cachix/nixpkgs-python"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { + self, + nixpkgs, + devenv, + systems, + ... + } @ inputs: let + forEachSystem = nixpkgs.lib.genAttrs (import systems); + in { + # packages = forEachSystem (system: { + # default = + # nixpkgs.legacyPackages.${system}.poetry2nix.mkPoetryApplication + # { + # projectDir = self; + # preferWheels = true; + # }; + # }); + + devShells = + forEachSystem + (system: let + pkgs = nixpkgs.legacyPackages.${system}; + in { + default = devenv.lib.mkShell { + inherit inputs pkgs; + + modules = [ + { + packages = with pkgs; [pre-commit poethepoet stdenv.cc.cc.lib]; + + languages.python = { + enable = true; + # poetry = { + # enable = true; + # install.enable = true; + # install.groups = ["dev" "tests"]; + # install.allExtras = true; + # }; + version = "3.11"; + }; + } + ]; + }; + }); + }; + + nixConfig = { + extra-trusted-public-keys = "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw="; + extra-substituters = "https://devenv.cachix.org"; + }; +} From dd321697b0104ecc18dd4e42c01530057dde72d3 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 7 Feb 2024 11:20:58 +0100 Subject: [PATCH 150/199] fix: Fix dependencies issues introducing additional ones --- .pre-commit-config.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fab53d26..bad3ebf6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: rev: v1.7.5 hooks: - id: docformatter - additional_dependencies: [tomli] + additional_dependencies: [tomli, charset-normalizer] args: [--in-place, --config, ./pyproject.toml] - repo: https://github.com/asottile/pyupgrade rev: v3.15.0 @@ -41,6 +41,8 @@ repos: rev: 1.16.0 hooks: - id: blacken-docs + additional_dependencies: + - platformdirs - repo: https://github.com/pycqa/pydocstyle rev: 6.3.0 hooks: From 906cc8b021da594dbbca7a7ad9a4deb258a8f7da Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 7 Feb 2024 18:58:01 +0100 Subject: [PATCH 151/199] chore: Revert additional deps in pre-commit hooks, apparently not needed The problem could have been caused by a corrupted cache. However, it does not persist after a force clean --- .pre-commit-config.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bad3ebf6..fab53d26 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: rev: v1.7.5 hooks: - id: docformatter - additional_dependencies: [tomli, charset-normalizer] + additional_dependencies: [tomli] args: [--in-place, --config, ./pyproject.toml] - repo: https://github.com/asottile/pyupgrade rev: v3.15.0 @@ -41,8 +41,6 @@ repos: rev: 1.16.0 hooks: - id: blacken-docs - additional_dependencies: - - platformdirs - repo: https://github.com/pycqa/pydocstyle rev: 6.3.0 hooks: From 3943b91f21b3be41fa6e69b63ca260af4fc84936 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 8 Feb 2024 10:17:22 +0100 Subject: [PATCH 152/199] chore: Pre-commit all files once more --- README.md | 56 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 9f60ad32..d22503ca 100644 --- a/README.md +++ b/README.md @@ -5,26 +5,33 @@ To get started, `python setup.py install` to install the tools and dependencies. # Supported Computation Tensor Network Types: + - Tensornet (TN) - Matrix Product States (MPS) Tensor Network contractions to: + - dense vectors - expecation values of given Pauli string The supported HPC configurations are: + - single-node CPU - single-node GPU or GPUs - multi-node multi-GPU with Message Passing Interface (MPI) - multi-node multi-GPU with NVIDIA Collective Communications Library (NCCL) 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. # Sample Codes + ## Single-Node Example + The code below shows an example of how to activate the Cuquantum TensorNetwork backend of Qibo. + ```py import numpy as np from qibo import Circuit, gates @@ -36,20 +43,22 @@ import qibo # This will trigger the dense vector computation of the tensornet. computation_settings = { - 'MPI_enabled': False, - 'MPS_enabled': { - "qr_method": False, - "svd_method": { - "partition": "UV", - "abs_cutoff": 1e-12, - }, - } , - 'NCCL_enabled': False, - 'expectation_enabled': False + "MPI_enabled": False, + "MPS_enabled": { + "qr_method": False, + "svd_method": { + "partition": "UV", + "abs_cutoff": 1e-12, + }, + }, + "NCCL_enabled": False, + "expectation_enabled": False, } -qibo.set_backend(backend="qibotn", platform="cutensornet", runcard=computation_settings) #cuQuantum +qibo.set_backend( + backend="qibotn", platform="cutensornet", runcard=computation_settings +) # cuQuantum # qibo.set_backend(backend="qibotn", platform="qutensornet", runcard=computation_settings) #quimb @@ -70,25 +79,26 @@ Other examples of setting the computation_settings ```py # Expectation computation with specific Pauli String pattern computation_settings = { - 'MPI_enabled': False, - 'MPS_enabled': False, - 'NCCL_enabled': False, - 'expectation_enabled': { - 'pauli_string_pattern': "IXZ" + "MPI_enabled": False, + "MPS_enabled": False, + "NCCL_enabled": False, + "expectation_enabled": { + "pauli_string_pattern": "IXZ", + }, } # Dense vector computation using multi node through MPI computation_settings = { - 'MPI_enabled': True, - 'MPS_enabled': False, - 'NCCL_enabled': False, - 'expectation_enabled': False + "MPI_enabled": True, + "MPS_enabled": False, + "NCCL_enabled": False, + "expectation_enabled": False, } ``` ## Multi-Node Example -Multi-node is enabled by setting either the MPI or NCCL enabled flag to True in the computation settings. Below shows the script to launch on 2 nodes with 2 GPUs each. $node_list contains the IP of the nodes assigned. +Multi-node is enabled by setting either the MPI or NCCL enabled flag to True in the computation settings. Below shows the script to launch on 2 nodes with 2 GPUs each. $node_list contains the IP of the nodes assigned. ```sh mpirun -n 4 -hostfile $node_list python test.py From 67773df47777b1af0a904e6d343478a1cc4f788c Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 8 Feb 2024 10:30:22 +0100 Subject: [PATCH 153/199] chore: Enforce naming conventions on module files --- ...{QiboCircuitConvertor.py => circuit_convertor.py} | 12 ++++++------ .../{QiboCircuitToMPS.py => circuit_to_mps.py} | 4 ++-- src/qibotn/eval.py | 4 ++-- src/qibotn/{MPSUtils.py => mps_utils.py} | 0 4 files changed, 10 insertions(+), 10 deletions(-) rename src/qibotn/{QiboCircuitConvertor.py => circuit_convertor.py} (96%) rename src/qibotn/{QiboCircuitToMPS.py => circuit_to_mps.py} (89%) rename src/qibotn/{MPSUtils.py => mps_utils.py} (100%) diff --git a/src/qibotn/QiboCircuitConvertor.py b/src/qibotn/circuit_convertor.py similarity index 96% rename from src/qibotn/QiboCircuitConvertor.py rename to src/qibotn/circuit_convertor.py index f67fb8ea..14af79de 100644 --- a/src/qibotn/QiboCircuitConvertor.py +++ b/src/qibotn/circuit_convertor.py @@ -5,12 +5,12 @@ class QiboCircuitToEinsum: - """Convert a circuit to a Tensor Network (TN) representation. The circuit - is first processed to an intermediate form by grouping each gate matrix - with its corresponding qubit it is acting on to a list. It is then - converted to an equivalent TN expression through the class function - state_vector_operands() following the Einstein summation convention in the - interleave format. + """Convert a circuit to a Tensor Network (TN) representation. + + The circuit is first processed to an intermediate form by grouping each gate matrix + with its corresponding qubit it is acting on to a list. It is then converted to an + equivalent TN expression through the class function state_vector_operands() + following the Einstein summation convention in the interleave format. See document for detail of the format: https://docs.nvidia.com/cuda/cuquantum/python/api/generated/cuquantum.contract.html diff --git a/src/qibotn/QiboCircuitToMPS.py b/src/qibotn/circuit_to_mps.py similarity index 89% rename from src/qibotn/QiboCircuitToMPS.py rename to src/qibotn/circuit_to_mps.py index b1d847f2..af8acd5a 100644 --- a/src/qibotn/QiboCircuitToMPS.py +++ b/src/qibotn/circuit_to_mps.py @@ -2,8 +2,8 @@ import numpy as np from cuquantum import cutensornet as cutn -from qibotn.MPSUtils import apply_gate, initial -from qibotn.QiboCircuitConvertor import QiboCircuitToEinsum +from qibotn.circuit_convertor import QiboCircuitToEinsum +from qibotn.mps_utils import apply_gate, initial class QiboCircuitToMPS: diff --git a/src/qibotn/eval.py b/src/qibotn/eval.py index 5fcb66f0..6375aa6a 100644 --- a/src/qibotn/eval.py +++ b/src/qibotn/eval.py @@ -2,9 +2,9 @@ from cupy.cuda.runtime import getDeviceCount from cuquantum import contract +from qibotn.circuit_convertor import QiboCircuitToEinsum +from qibotn.circuit_to_mps import QiboCircuitToMPS from qibotn.mps_contraction_helper import MPSContractionHelper -from qibotn.QiboCircuitConvertor import QiboCircuitToEinsum -from qibotn.QiboCircuitToMPS import QiboCircuitToMPS def dense_vector_tn(qibo_circ, datatype): diff --git a/src/qibotn/MPSUtils.py b/src/qibotn/mps_utils.py similarity index 100% rename from src/qibotn/MPSUtils.py rename to src/qibotn/mps_utils.py From fb76621bc4d2a12d8226f42b1ea1cd351aef8dfa Mon Sep 17 00:00:00 2001 From: Vinitha-balachandran <127284874+Vinitha-balachandran@users.noreply.github.com> Date: Thu, 8 Feb 2024 17:56:44 +0800 Subject: [PATCH 154/199] Update src/qibotn/backends/cpu.py Co-authored-by: Alessandro Candido --- src/qibotn/backends/cpu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py index a85dfd9b..542599c4 100644 --- a/src/qibotn/backends/cpu.py +++ b/src/qibotn/backends/cpu.py @@ -3,7 +3,7 @@ from qibo.states import CircuitResult -class QuTensorNet(NumpyBackend): +class QuimbBackend(NumpyBackend): def __init__(self, runcard): super().__init__() From 253ff4add64cd5d1da970cb1b6c88b8e37ad65b4 Mon Sep 17 00:00:00 2001 From: Vinitha-balachandran <127284874+Vinitha-balachandran@users.noreply.github.com> Date: Thu, 8 Feb 2024 17:58:53 +0800 Subject: [PATCH 155/199] Update src/qibotn/backends/cpu.py Co-authored-by: Alessandro Candido --- src/qibotn/backends/cpu.py | 72 ++++---------------------------------- 1 file changed, 6 insertions(+), 66 deletions(-) diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py index 542599c4..cb03cb81 100644 --- a/src/qibotn/backends/cpu.py +++ b/src/qibotn/backends/cpu.py @@ -62,80 +62,20 @@ def execute_circuit( import qibotn.eval_qu as eval - if ( - self.MPI_enabled == False - and self.MPS_enabled == False - and self.NCCL_enabled == False - and self.expectation_enabled == False - ): - - state = eval.dense_vector_tn_qu( - circuit.to_qasm(), initial_state, is_mps=False, backend="numpy" - ) - - elif ( - self.MPI_enabled == False - and self.MPS_enabled == True - and self.NCCL_enabled == False - and self.expectation_enabled == False - ): - - state = eval.dense_vector_tn_qu( - circuit.to_qasm(), initial_state, is_mps=True, backend="numpy" - ) - - elif ( - self.MPI_enabled == True - and self.MPS_enabled == False - and self.NCCL_enabled == False - and self.expectation_enabled == False - ): - + if self.MPI_enabled == True: raise_error(NotImplementedError, "QiboTN quimb backend cannot support MPI.") - - elif ( - self.MPI_enabled == False - and self.MPS_enabled == False - and self.NCCL_enabled == True - and self.expectation_enabled == False - ): - + if self.NCCL_enabled == True: raise_error( NotImplementedError, "QiboTN quimb backend cannot support NCCL." ) - - elif ( - self.MPI_enabled == False - and self.MPS_enabled == False - and self.NCCL_enabled == False - and self.expectation_enabled == True - ): - - raise_error( - NotImplementedError, "QiboTN quimb backend cannot support expectation" - ) - - elif ( - self.MPI_enabled == True - and self.MPS_enabled == False - and self.NCCL_enabled == False - and self.expectation_enabled == True - ): + if self.expectation_enabled == True: raise_error( NotImplementedError, "QiboTN quimb backend cannot support expectation" ) - elif ( - self.MPI_enabled == False - and self.MPS_enabled == False - and self.NCCL_enabled == True - and self.expectation_enabled == True - ): - raise_error( - NotImplementedError, "QiboTN quimb backend cannot support expectation" - ) - else: - raise_error(NotImplementedError, "Compute type not supported.") + state = eval.dense_vector_tn_qu( + circuit.to_qasm(), initial_state, is_mps=self.MPS_enabled, backend="numpy" + ) if return_array: return state.flatten() From 483baa7f6e6fc4be0b56482834d458eb7f3e6a28 Mon Sep 17 00:00:00 2001 From: Vinitha-balachandran <127284874+Vinitha-balachandran@users.noreply.github.com> Date: Thu, 8 Feb 2024 17:59:56 +0800 Subject: [PATCH 156/199] Update src/qibotn/eval_qu.py made changes Co-authored-by: Alessandro Candido --- src/qibotn/eval_qu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/eval_qu.py b/src/qibotn/eval_qu.py index 7b603b50..024db887 100644 --- a/src/qibotn/eval_qu.py +++ b/src/qibotn/eval_qu.py @@ -11,7 +11,7 @@ def from_qibo( cutoff=1e-6, cutoff_mode="abs", ): - """Create a tensornetwork representation of the circuit.""" + """Create a tensor network representation of the circuit.""" nqubits = circuit.nqubits gate_opt = {} From 100c9f302a364e86fab65a1f6e938df7159157a0 Mon Sep 17 00:00:00 2001 From: Vinitha-balachandran <127284874+Vinitha-balachandran@users.noreply.github.com> Date: Thu, 8 Feb 2024 18:00:23 +0800 Subject: [PATCH 157/199] Update src/qibotn/eval_qu.py updated the changes Co-authored-by: Alessandro Candido --- src/qibotn/eval_qu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/eval_qu.py b/src/qibotn/eval_qu.py index 024db887..74fca1cd 100644 --- a/src/qibotn/eval_qu.py +++ b/src/qibotn/eval_qu.py @@ -36,7 +36,7 @@ def from_qibo( def init_state_tn(nqubits, init_state_sv): - """Create a matrixproductstate directly from a dense vector.""" + """Create a matrix product state directly from a dense vector.""" dims = tuple(2 * np.ones(nqubits, dtype=int)) From 51dc6011c7d9b1657ac7300074bbc0b466675e3b Mon Sep 17 00:00:00 2001 From: yangliwei Date: Thu, 8 Feb 2024 18:13:13 +0800 Subject: [PATCH 158/199] Use CUDA_PATH as the if conditional for running workflow jobs --- .github/workflows/rules.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rules.yml b/.github/workflows/rules.yml index d1c06323..ed8b4613 100644 --- a/.github/workflows/rules.yml +++ b/.github/workflows/rules.yml @@ -1,6 +1,9 @@ # A single CI script with github workflow name: Tests +env: + CUDA_PATH: echo $CUDA_PATH + on: push: pull_request: @@ -8,7 +11,7 @@ on: jobs: build: - if: contains(github.event.pull_request.labels.*.name, 'run-workflow') || github.event_name == 'push' && {{ $CUDA_PATH != '' }} + if: contains(github.event.pull_request.labels.*.name, 'run-workflow') || github.event_name == 'push' && github.env.CUDA_PATH != '' strategy: matrix: os: [ubuntu-latest] From 52bb0ec4e88762ebf60d6b8f3bdc40f30346daec Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 8 Feb 2024 11:18:22 +0100 Subject: [PATCH 159/199] fix: Fix indentation wrong level from review suggestion --- src/qibotn/backends/cpu.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py index cb03cb81..ce4a3c7d 100644 --- a/src/qibotn/backends/cpu.py +++ b/src/qibotn/backends/cpu.py @@ -4,7 +4,6 @@ class QuimbBackend(NumpyBackend): - def __init__(self, runcard): super().__init__() import quimb # pylint: disable=import-error @@ -73,9 +72,9 @@ def execute_circuit( NotImplementedError, "QiboTN quimb backend cannot support expectation" ) - state = eval.dense_vector_tn_qu( - circuit.to_qasm(), initial_state, is_mps=self.MPS_enabled, backend="numpy" - ) + state = eval.dense_vector_tn_qu( + circuit.to_qasm(), initial_state, is_mps=self.MPS_enabled, backend="numpy" + ) if return_array: return state.flatten() From e98d333222ea11dc9021d5c580f51a715654ecbf Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 18:25:28 +0800 Subject: [PATCH 160/199] renamed cpu to quimb and qutensornet to quimbbackend --- src/qibotn/backends/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/backends/__init__.py b/src/qibotn/backends/__init__.py index e5d68de5..36c58fcd 100644 --- a/src/qibotn/backends/__init__.py +++ b/src/qibotn/backends/__init__.py @@ -1,2 +1,2 @@ -from qibotn.backends.cpu import QuTensorNet +from qibotn.backends.quimb import QuimbBackend from qibotn.backends.gpu import CuTensorNet From 853e67a66805318edbf79e1a624a3941dc9dd13c Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 18:28:07 +0800 Subject: [PATCH 161/199] renaming cpu to quimb --- src/qibotn/backends/quimb.py | 84 ++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/qibotn/backends/quimb.py diff --git a/src/qibotn/backends/quimb.py b/src/qibotn/backends/quimb.py new file mode 100644 index 00000000..f8380d09 --- /dev/null +++ b/src/qibotn/backends/quimb.py @@ -0,0 +1,84 @@ +from qibo.backends.numpy import NumpyBackend +from qibo.config import raise_error +from qibo.states import CircuitResult + + +class QuimbBackend(NumpyBackend): + + def __init__(self, runcard): + super().__init__() + import quimb # pylint: disable=import-error + + if runcard is not None: + self.MPI_enabled = runcard.get("MPI_enabled", False) + self.NCCL_enabled = runcard.get("NCCL_enabled", False) + self.expectation_enabled = runcard.get("expectation_enabled", False) + + mps_enabled_value = runcard.get("MPS_enabled") + if mps_enabled_value is True: + self.MPS_enabled = True + elif mps_enabled_value is False: + self.MPS_enabled = False + else: + raise TypeError("MPS_enabled has an unexpected type") + + else: + self.MPI_enabled = False + self.MPS_enabled = False + self.NCCL_enabled = False + self.expectation_enabled = False + + self.name = "qibotn" + self.quimb = quimb + self.platform = "QuimbBackend" + self.versions["quimb"] = self.quimb.__version__ + + def apply_gate(self, gate, state, nqubits): # pragma: no cover + raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") + + def apply_gate_density_matrix(self, gate, state, nqubits): # pragma: no cover + raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") + + def assign_measurements(self, measurement_map, circuit_result): + raise_error(NotImplementedError, "Not implemented in QiboTN.") + + def set_precision(self, precision): + if precision != self.precision: + super().set_precision(precision) + + def execute_circuit( + self, circuit, initial_state=None, nshots=None, return_array=False + ): # pragma: no cover + """Executes a quantum circuit. + + Args: + 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. + """ + + import qibotn.eval_qu as eval + + if self.MPI_enabled == True: + raise_error(NotImplementedError, "QiboTN quimb backend cannot support MPI.") + if self.NCCL_enabled == True: + raise_error( + NotImplementedError, "QiboTN quimb backend cannot support NCCL." + ) + if self.expectation_enabled == True: + raise_error( + NotImplementedError, "QiboTN quimb backend cannot support expectation" + ) + + state = eval.dense_vector_tn_qu( + circuit.to_qasm(), initial_state, is_mps=self.MPS_enabled, backend="numpy" + ) + + if return_array: + return state.flatten() + else: + circuit._final_state = CircuitResult(self, circuit, state.flatten(), nshots) + return circuit._final_state From 8a9a45978a76b240ce474aa908ea5fa71ac198a3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 10:26:30 +0000 Subject: [PATCH 162/199] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qibotn/backends/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/backends/__init__.py b/src/qibotn/backends/__init__.py index 36c58fcd..adab04ed 100644 --- a/src/qibotn/backends/__init__.py +++ b/src/qibotn/backends/__init__.py @@ -1,2 +1,2 @@ -from qibotn.backends.quimb import QuimbBackend from qibotn.backends.gpu import CuTensorNet +from qibotn.backends.quimb import QuimbBackend From 154c0a13193cb1660a96ed6a15908d23b892c828 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 8 Feb 2024 18:31:32 +0800 Subject: [PATCH 163/199] removing cpu.py as quimb.py is added --- src/qibotn/backends/cpu.py | 83 -------------------------------------- 1 file changed, 83 deletions(-) delete mode 100644 src/qibotn/backends/cpu.py diff --git a/src/qibotn/backends/cpu.py b/src/qibotn/backends/cpu.py deleted file mode 100644 index ce4a3c7d..00000000 --- a/src/qibotn/backends/cpu.py +++ /dev/null @@ -1,83 +0,0 @@ -from qibo.backends.numpy import NumpyBackend -from qibo.config import raise_error -from qibo.states import CircuitResult - - -class QuimbBackend(NumpyBackend): - def __init__(self, runcard): - super().__init__() - import quimb # pylint: disable=import-error - - if runcard is not None: - self.MPI_enabled = runcard.get("MPI_enabled", False) - self.NCCL_enabled = runcard.get("NCCL_enabled", False) - self.expectation_enabled = runcard.get("expectation_enabled", False) - - mps_enabled_value = runcard.get("MPS_enabled") - if mps_enabled_value is True: - self.MPS_enabled = True - elif mps_enabled_value is False: - self.MPS_enabled = False - else: - raise TypeError("MPS_enabled has an unexpected type") - - else: - self.MPI_enabled = False - self.MPS_enabled = False - self.NCCL_enabled = False - self.expectation_enabled = False - - self.name = "qibotn" - self.quimb = quimb - self.platform = "qutensornet" - self.versions["quimb"] = self.quimb.__version__ - - def apply_gate(self, gate, state, nqubits): # pragma: no cover - raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") - - def apply_gate_density_matrix(self, gate, state, nqubits): # pragma: no cover - raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") - - def assign_measurements(self, measurement_map, circuit_result): - raise_error(NotImplementedError, "Not implemented in QiboTN.") - - def set_precision(self, precision): - if precision != self.precision: - super().set_precision(precision) - - def execute_circuit( - self, circuit, initial_state=None, nshots=None, return_array=False - ): # pragma: no cover - """Executes a quantum circuit. - - Args: - 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. - """ - - import qibotn.eval_qu as eval - - if self.MPI_enabled == True: - raise_error(NotImplementedError, "QiboTN quimb backend cannot support MPI.") - if self.NCCL_enabled == True: - raise_error( - NotImplementedError, "QiboTN quimb backend cannot support NCCL." - ) - if self.expectation_enabled == True: - raise_error( - NotImplementedError, "QiboTN quimb backend cannot support expectation" - ) - - state = eval.dense_vector_tn_qu( - circuit.to_qasm(), initial_state, is_mps=self.MPS_enabled, backend="numpy" - ) - - if return_array: - return state.flatten() - else: - circuit._final_state = CircuitResult(self, circuit, state.flatten(), nshots) - return circuit._final_state From 6b3c26b9346a27255954bc0e219c15b53c2f1f06 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 10:30:30 +0000 Subject: [PATCH 164/199] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qibotn/backends/quimb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/backends/quimb.py b/src/qibotn/backends/quimb.py index f8380d09..54dae861 100644 --- a/src/qibotn/backends/quimb.py +++ b/src/qibotn/backends/quimb.py @@ -74,7 +74,7 @@ def execute_circuit( ) state = eval.dense_vector_tn_qu( - circuit.to_qasm(), initial_state, is_mps=self.MPS_enabled, backend="numpy" + circuit.to_qasm(), initial_state, is_mps=self.MPS_enabled, backend="numpy" ) if return_array: From edf64ebeb3c18cd756e2f852c3de0060216ee916 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Fri, 9 Feb 2024 10:41:12 +0800 Subject: [PATCH 165/199] updated set_backend for quimb --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d22503ca..b15aab3b 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ computation_settings = { qibo.set_backend( backend="qibotn", platform="cutensornet", runcard=computation_settings ) # cuQuantum -# qibo.set_backend(backend="qibotn", platform="qutensornet", runcard=computation_settings) #quimb +# qibo.set_backend(backend="qibotn", platform="QuimbBackend", runcard=computation_settings) #quimb # Construct the circuit From 2b6345c8cafdfcf334654751db4e22c2c872e8bd Mon Sep 17 00:00:00 2001 From: tankya2 Date: Fri, 9 Feb 2024 10:29:30 +0800 Subject: [PATCH 166/199] Take out repeat codes --- src/qibotn/backends/gpu.py | 32 +++----------------------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/gpu.py index 2c3f8d41..073ce590 100644 --- a/src/qibotn/backends/gpu.py +++ b/src/qibotn/backends/gpu.py @@ -110,96 +110,70 @@ def execute_circuit( import qibotn.eval as eval + if initial_state is not None: + raise_error(NotImplementedError, "QiboTN cannot support initial state.") + if ( self.MPI_enabled == False and self.MPS_enabled == False and self.NCCL_enabled == False and self.expectation_enabled == False ): - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state = eval.dense_vector_tn(circuit, self.dtype) - elif ( self.MPI_enabled == False and self.MPS_enabled == True and self.NCCL_enabled == False and self.expectation_enabled == False ): - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state = eval.dense_vector_mps(circuit, self.gate_algo, self.dtype) - elif ( self.MPI_enabled == True and self.MPS_enabled == False and self.NCCL_enabled == False and self.expectation_enabled == False ): - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.dense_vector_tn_MPI(circuit, self.dtype, 32) if rank > 0: state = np.array(0) - elif ( self.MPI_enabled == False and self.MPS_enabled == False and self.NCCL_enabled == True and self.expectation_enabled == False ): - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.dense_vector_tn_nccl(circuit, self.dtype, 32) if rank > 0: state = np.array(0) - elif ( self.MPI_enabled == False and self.MPS_enabled == False and self.NCCL_enabled == False and self.expectation_enabled == True ): - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state = eval.expectation_pauli_tn( circuit, self.dtype, self.pauli_string_pattern ) - elif ( self.MPI_enabled == True and self.MPS_enabled == False and self.NCCL_enabled == False and self.expectation_enabled == True ): - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.expectation_pauli_tn_MPI( circuit, self.dtype, self.pauli_string_pattern, 32 ) - if rank > 0: state = np.array(0) - elif ( self.MPI_enabled == False and self.MPS_enabled == False and self.NCCL_enabled == True and self.expectation_enabled == True ): - if initial_state is not None: - raise_error(NotImplementedError, "QiboTN cannot support initial state.") - state, rank = eval.expectation_pauli_tn_nccl( circuit, self.dtype, self.pauli_string_pattern, 32 ) - if rank > 0: state = np.array(0) else: From d7f1241419d48e8a2527e35ecf61f80355b9049e Mon Sep 17 00:00:00 2001 From: tankya2 Date: Fri, 9 Feb 2024 10:33:00 +0800 Subject: [PATCH 167/199] Update filename --- src/qibotn/backends/{gpu.py => cutensornet.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/qibotn/backends/{gpu.py => cutensornet.py} (100%) diff --git a/src/qibotn/backends/gpu.py b/src/qibotn/backends/cutensornet.py similarity index 100% rename from src/qibotn/backends/gpu.py rename to src/qibotn/backends/cutensornet.py From f64c876bee8ef90e1573b02043092b91c4cf6854 Mon Sep 17 00:00:00 2001 From: yangliwei Date: Wed, 14 Feb 2024 14:50:27 +0800 Subject: [PATCH 168/199] Minor fix to the use of env for CUDA_PATH in the workflow --- .github/workflows/rules.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rules.yml b/.github/workflows/rules.yml index ed8b4613..6415be0e 100644 --- a/.github/workflows/rules.yml +++ b/.github/workflows/rules.yml @@ -2,7 +2,7 @@ name: Tests env: - CUDA_PATH: echo $CUDA_PATH + CUDA_PATH: on: push: @@ -11,7 +11,7 @@ on: jobs: build: - if: contains(github.event.pull_request.labels.*.name, 'run-workflow') || github.event_name == 'push' && github.env.CUDA_PATH != '' + if: ${{ contains(github.event.pull_request.labels.*.name, 'run-workflow') || github.event_name == 'push' }} && env.CUDA_PATH != '' strategy: matrix: os: [ubuntu-latest] From bcad37551de53fa0f1fe5220a8656b31510b1148 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 15 Feb 2024 11:17:17 +0100 Subject: [PATCH 169/199] ci: Ignore dependencies missing in CI in Pylint --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index f2546e49..82c13830 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,6 @@ +[tool.pylint.main] +ignored-modules = ["cupy", "cuquantum", "mpi4py"] + [tool.pylint.reports] output-format = "colorized" From d4e718f7a333447a0bd445c65f62045e6c15c904 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 15 Feb 2024 16:50:39 +0100 Subject: [PATCH 170/199] chore: Improve readme --- README.md | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b15aab3b..d00f3159 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -Qibotn is the tensor network translation module for Qibo to support large-scale simulation of quantum circuits and acceleration. +# Qibotn -To get started, `python setup.py install` to install the tools and dependencies. +The tensor network translation module for Qibo to support large-scale simulation of quantum circuits and acceleration. -# Supported Computation +## Supported Computation Tensor Network Types: @@ -26,9 +26,22 @@ 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. -# Sample Codes +## Installation -## Single-Node Example +To get started: + +```sh +python setup.py install +``` + +to install the tools and dependencies. A few extras are provided, check `setup.py` in +case you need them. + + + +## Sample Codes + +### Single-Node Example The code below shows an example of how to activate the Cuquantum TensorNetwork backend of Qibo. @@ -96,7 +109,7 @@ computation_settings = { } ``` -## Multi-Node Example +### Multi-Node Example Multi-node is enabled by setting either the MPI or NCCL enabled flag to True in the computation settings. Below shows the script to launch on 2 nodes with 2 GPUs each. $node_list contains the IP of the nodes assigned. From 736a8123ca3a055f78d869568fbd2117d1bc2689 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 15 Feb 2024 17:42:22 +0100 Subject: [PATCH 171/199] fix: Fix outdated imports --- src/qibotn/backends/__init__.py | 2 +- src/qibotn/backends/cutensornet.py | 2 +- src/qibotn/backends/quimb.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qibotn/backends/__init__.py b/src/qibotn/backends/__init__.py index adab04ed..6f5abdd4 100644 --- a/src/qibotn/backends/__init__.py +++ b/src/qibotn/backends/__init__.py @@ -1,2 +1,2 @@ -from qibotn.backends.gpu import CuTensorNet +from qibotn.backends.cutensornet import CuTensorNet from qibotn.backends.quimb import QuimbBackend diff --git a/src/qibotn/backends/cutensornet.py b/src/qibotn/backends/cutensornet.py index 073ce590..5f1419d6 100644 --- a/src/qibotn/backends/cutensornet.py +++ b/src/qibotn/backends/cutensornet.py @@ -1,7 +1,7 @@ import numpy as np from qibo.backends.numpy import NumpyBackend from qibo.config import raise_error -from qibo.states import CircuitResult +from qibo.result import CircuitResult class CuTensorNet(NumpyBackend): # pragma: no cover diff --git a/src/qibotn/backends/quimb.py b/src/qibotn/backends/quimb.py index 54dae861..cca67999 100644 --- a/src/qibotn/backends/quimb.py +++ b/src/qibotn/backends/quimb.py @@ -1,6 +1,6 @@ from qibo.backends.numpy import NumpyBackend from qibo.config import raise_error -from qibo.states import CircuitResult +from qibo.result import CircuitResult class QuimbBackend(NumpyBackend): From 91f46f048be51bf875865e50e2b173ea709af42c Mon Sep 17 00:00:00 2001 From: tankya2 Date: Fri, 16 Feb 2024 14:54:09 +0800 Subject: [PATCH 172/199] Improve code conciseness --- src/qibotn/backends/cutensornet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/backends/cutensornet.py b/src/qibotn/backends/cutensornet.py index 5f1419d6..08a8ced3 100644 --- a/src/qibotn/backends/cutensornet.py +++ b/src/qibotn/backends/cutensornet.py @@ -45,7 +45,7 @@ def __init__(self, runcard): self.MPS_enabled = False elif isinstance(mps_enabled_value, dict): self.MPS_enabled = True - self.gate_algo = runcard.get("MPS_enabled", {}) + self.gate_algo = mps_enabled_value else: raise TypeError("MPS_enabled has an unexpected type") From 5c24cc34c579891080df236da78054b4d7ad5adf Mon Sep 17 00:00:00 2001 From: tankya2 Date: Fri, 16 Feb 2024 14:56:07 +0800 Subject: [PATCH 173/199] Rename function --- src/qibotn/backends/cutensornet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/backends/cutensornet.py b/src/qibotn/backends/cutensornet.py index 08a8ced3..78ee5b6f 100644 --- a/src/qibotn/backends/cutensornet.py +++ b/src/qibotn/backends/cutensornet.py @@ -80,7 +80,7 @@ def set_precision(self, precision): if precision != self.precision: super().set_precision(precision) - def get_cuda_type(self, dtype="complex64"): + def cuda_type(self, dtype="complex64"): if dtype == "complex128": return ( self.cuquantum.cudaDataType.CUDA_C_64F, From eec44f062102e7775dba6340359172705fa8e94c Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 8 Feb 2024 16:29:25 +0100 Subject: [PATCH 174/199] build: Introduce Poetry --- flake.nix | 12 +- poetry.lock | 754 +++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 33 +++ 3 files changed, 793 insertions(+), 6 deletions(-) create mode 100644 poetry.lock diff --git a/flake.nix b/flake.nix index 92f53ee5..0285b22b 100644 --- a/flake.nix +++ b/flake.nix @@ -41,12 +41,12 @@ languages.python = { enable = true; - # poetry = { - # enable = true; - # install.enable = true; - # install.groups = ["dev" "tests"]; - # install.allExtras = true; - # }; + poetry = { + enable = true; + install.enable = true; + install.groups = ["dev" "tests"]; + install.allExtras = true; + }; version = "3.11"; }; } diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 00000000..188eed16 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,754 @@ +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. + +[[package]] +name = "appnope" +version = "0.1.4" +description = "Disable App Nap on macOS >= 10.9" +optional = false +python-versions = ">=3.6" +files = [ + {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, + {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, +] + +[[package]] +name = "astroid" +version = "3.0.3" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.0.3-py3-none-any.whl", hash = "sha256:92fcf218b89f449cdf9f7b39a269f8d5d617b27be68434912e11e79203963a17"}, + {file = "astroid-3.0.3.tar.gz", hash = "sha256:4148645659b08b70d72460ed1921158027a9e53ae8b7234149b1400eddacbb93"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "backcall" +version = "0.2.0" +description = "Specifications for callback functions passed in to an API" +optional = false +python-versions = "*" +files = [ + {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, + {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.4.1" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:077d366e724f24fc02dbfe9d946534357fda71af9764ff99d73c3c596001bbd7"}, + {file = "coverage-7.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0193657651f5399d433c92f8ae264aff31fc1d066deee4b831549526433f3f61"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d17bbc946f52ca67adf72a5ee783cd7cd3477f8f8796f59b4974a9b59cacc9ee"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3277f5fa7483c927fe3a7b017b39351610265308f5267ac6d4c2b64cc1d8d25"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dceb61d40cbfcf45f51e59933c784a50846dc03211054bd76b421a713dcdf19"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6008adeca04a445ea6ef31b2cbaf1d01d02986047606f7da266629afee982630"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c61f66d93d712f6e03369b6a7769233bfda880b12f417eefdd4f16d1deb2fc4c"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9bb62fac84d5f2ff523304e59e5c439955fb3b7f44e3d7b2085184db74d733b"}, + {file = "coverage-7.4.1-cp310-cp310-win32.whl", hash = "sha256:f86f368e1c7ce897bf2457b9eb61169a44e2ef797099fb5728482b8d69f3f016"}, + {file = "coverage-7.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:869b5046d41abfea3e381dd143407b0d29b8282a904a19cb908fa24d090cc018"}, + {file = "coverage-7.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b8ffb498a83d7e0305968289441914154fb0ef5d8b3157df02a90c6695978295"}, + {file = "coverage-7.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3cacfaefe6089d477264001f90f55b7881ba615953414999c46cc9713ff93c8c"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d6850e6e36e332d5511a48a251790ddc545e16e8beaf046c03985c69ccb2676"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18e961aa13b6d47f758cc5879383d27b5b3f3dcd9ce8cdbfdc2571fe86feb4dd"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfd1e1b9f0898817babf840b77ce9fe655ecbe8b1b327983df485b30df8cc011"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6b00e21f86598b6330f0019b40fb397e705135040dbedc2ca9a93c7441178e74"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:536d609c6963c50055bab766d9951b6c394759190d03311f3e9fcf194ca909e1"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7ac8f8eb153724f84885a1374999b7e45734bf93a87d8df1e7ce2146860edef6"}, + {file = "coverage-7.4.1-cp311-cp311-win32.whl", hash = "sha256:f3771b23bb3675a06f5d885c3630b1d01ea6cac9e84a01aaf5508706dba546c5"}, + {file = "coverage-7.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:9d2f9d4cc2a53b38cabc2d6d80f7f9b7e3da26b2f53d48f05876fef7956b6968"}, + {file = "coverage-7.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f68ef3660677e6624c8cace943e4765545f8191313a07288a53d3da188bd8581"}, + {file = "coverage-7.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23b27b8a698e749b61809fb637eb98ebf0e505710ec46a8aa6f1be7dc0dc43a6"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e3424c554391dc9ef4a92ad28665756566a28fecf47308f91841f6c49288e66"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0860a348bf7004c812c8368d1fc7f77fe8e4c095d661a579196a9533778e156"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe558371c1bdf3b8fa03e097c523fb9645b8730399c14fe7721ee9c9e2a545d3"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3468cc8720402af37b6c6e7e2a9cdb9f6c16c728638a2ebc768ba1ef6f26c3a1"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:02f2edb575d62172aa28fe00efe821ae31f25dc3d589055b3fb64d51e52e4ab1"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ca6e61dc52f601d1d224526360cdeab0d0712ec104a2ce6cc5ccef6ed9a233bc"}, + {file = "coverage-7.4.1-cp312-cp312-win32.whl", hash = "sha256:ca7b26a5e456a843b9b6683eada193fc1f65c761b3a473941efe5a291f604c74"}, + {file = "coverage-7.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:85ccc5fa54c2ed64bd91ed3b4a627b9cce04646a659512a051fa82a92c04a448"}, + {file = "coverage-7.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8bdb0285a0202888d19ec6b6d23d5990410decb932b709f2b0dfe216d031d218"}, + {file = "coverage-7.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:918440dea04521f499721c039863ef95433314b1db00ff826a02580c1f503e45"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:379d4c7abad5afbe9d88cc31ea8ca262296480a86af945b08214eb1a556a3e4d"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b094116f0b6155e36a304ff912f89bbb5067157aff5f94060ff20bbabdc8da06"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2f5968608b1fe2a1d00d01ad1017ee27efd99b3437e08b83ded9b7af3f6f766"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:10e88e7f41e6197ea0429ae18f21ff521d4f4490aa33048f6c6f94c6045a6a75"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a4a3907011d39dbc3e37bdc5df0a8c93853c369039b59efa33a7b6669de04c60"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d224f0c4c9c98290a6990259073f496fcec1b5cc613eecbd22786d398ded3ad"}, + {file = "coverage-7.4.1-cp38-cp38-win32.whl", hash = "sha256:23f5881362dcb0e1a92b84b3c2809bdc90db892332daab81ad8f642d8ed55042"}, + {file = "coverage-7.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:a07f61fc452c43cd5328b392e52555f7d1952400a1ad09086c4a8addccbd138d"}, + {file = "coverage-7.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8e738a492b6221f8dcf281b67129510835461132b03024830ac0e554311a5c54"}, + {file = "coverage-7.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46342fed0fff72efcda77040b14728049200cbba1279e0bf1188f1f2078c1d70"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9641e21670c68c7e57d2053ddf6c443e4f0a6e18e547e86af3fad0795414a628"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aeb2c2688ed93b027eb0d26aa188ada34acb22dceea256d76390eea135083950"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d12c923757de24e4e2110cf8832d83a886a4cf215c6e61ed506006872b43a6d1"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0491275c3b9971cdbd28a4595c2cb5838f08036bca31765bad5e17edf900b2c7"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8dfc5e195bbef80aabd81596ef52a1277ee7143fe419efc3c4d8ba2754671756"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1a78b656a4d12b0490ca72651fe4d9f5e07e3c6461063a9b6265ee45eb2bdd35"}, + {file = "coverage-7.4.1-cp39-cp39-win32.whl", hash = "sha256:f90515974b39f4dea2f27c0959688621b46d96d5a626cf9c53dbc653a895c05c"}, + {file = "coverage-7.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:64e723ca82a84053dd7bfcc986bdb34af8d9da83c521c19d6b472bc6880e191a"}, + {file = "coverage-7.4.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:32a8d985462e37cfdab611a6f95b09d7c091d07668fdc26e47a725ee575fe166"}, + {file = "coverage-7.4.1.tar.gz", hash = "sha256:1ed4b95480952b1a26d863e546fa5094564aa0065e1e5f0d4d0041f293251d04"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "cupy" +version = "11.6.0" +description = "CuPy: NumPy & SciPy for GPU" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cupy-11.6.0.tar.gz", hash = "sha256:53dbb840072bb32d4bfbaa6bfa072365a30c98b1fcd1f43e48969071ad98f1a7"}, +] + +[package.dependencies] +fastrlock = ">=0.5" +numpy = ">=1.20,<1.27" + +[package.extras] +all = ["Cython (>=0.29.22,<3)", "optuna (>=2.0)", "scipy (>=1.6,<1.12)"] +stylecheck = ["autopep8 (==1.5.5)", "flake8 (==3.8.4)", "mypy (==0.950)", "pbr (==5.5.1)", "pycodestyle (==2.6.0)", "types-setuptools (==57.4.14)"] +test = ["hypothesis (>=6.37.2,<6.55.0)", "pytest (>=7.2)"] + +[[package]] +name = "cuquantum-python-cu11" +version = "23.10.0" +description = "NVIDIA cuQuantum Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "cuquantum_python_cu11-23.10.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a0f0dfcb6239ec5fce836fa2f641820d3235ac7d83f391eac90952cf481da03f"}, + {file = "cuquantum_python_cu11-23.10.0-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:ad5e38501cb53d50ba19fc48790f2c79fbc14c22e101d51a0b338f6c6971e6a0"}, + {file = "cuquantum_python_cu11-23.10.0-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:f5bd44f0a50b38fa778836577e11515fb820c98217d2958fdedfc861a701f604"}, + {file = "cuquantum_python_cu11-23.10.0-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:01e6b210ed66a1fda172884f1eca68b4763f676f81949af8f0d6b16d798f1881"}, + {file = "cuquantum_python_cu11-23.10.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:0f658d3c83a8f05b81749a1fecc232ca23650147f53d82fe61dae987e544fb9c"}, + {file = "cuquantum_python_cu11-23.10.0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:997e47861bab2c5e183a3b7439ba7fe4dd777cbf4d24f2234ac4ad7936cbc699"}, +] + +[package.dependencies] +custatevec-cu11 = ">=1.5,<2.0" +cutensornet-cu11 = ">=2.3,<3.0" +numpy = ">=1.21,<2.0" + +[[package]] +name = "custatevec-cu11" +version = "1.5.0" +description = "cuStateVec - a component of NVIDIA cuQuantum SDK" +optional = false +python-versions = "*" +files = [ + {file = "custatevec_cu11-1.5.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:3e0cbbc487a0590d9f889ad70b9ac21d88c1f555f6fe01b18ba687a0d98d902f"}, + {file = "custatevec_cu11-1.5.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:be289f600e7361cac4acdeb8d12d443117d43ce745c9322f6b828292341b9f30"}, +] + +[[package]] +name = "cutensor-cu11" +version = "1.7.0" +description = "NVIDIA cuTENSOR" +optional = false +python-versions = "*" +files = [ + {file = "cutensor_cu11-1.7.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:c5598670f4f31906d725f5ea852f0df675522e3ff5a7bf886057eab36497062d"}, + {file = "cutensor_cu11-1.7.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:67b6c7427d9ab50cb82e01360948bd1b23d73775b5767ab92071c7afcfec4b8b"}, + {file = "cutensor_cu11-1.7.0-py3-none-win_amd64.whl", hash = "sha256:d173b3d0fd51cf761b371a4d4be9a3afd3ef230a55ae4336ae31e905336480e1"}, +] + +[[package]] +name = "cutensornet-cu11" +version = "2.3.0" +description = "cuTensorNet - a component of NVIDIA cuQuantum SDK" +optional = false +python-versions = "*" +files = [ + {file = "cutensornet_cu11-2.3.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:fd15b199b189f0501b3fdaa576ee70eedbdfec37b557f7fd56b97aaa5e618667"}, + {file = "cutensornet_cu11-2.3.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:dcb9ab26c3d6b51dfd1146c9ec28fe6768bfe539dca931d4ef462eea15205bf7"}, +] + +[package.dependencies] +cutensor-cu11 = ">=1.6.1,<2" + +[[package]] +name = "decorator" +version = "5.1.1" +description = "Decorators for Humans" +optional = false +python-versions = ">=3.5" +files = [ + {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, + {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, +] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "exceptiongroup" +version = "1.2.0" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, + {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "fastrlock" +version = "0.8.2" +description = "Fast, re-entrant optimistic lock implemented in Cython" +optional = false +python-versions = "*" +files = [ + {file = "fastrlock-0.8.2-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:94e348c72a1fd1f8191f25ea056448e4f5a87b8fbf005b39d290dcb0581a48cd"}, + {file = "fastrlock-0.8.2-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d5595903444c854b99c42122b87edfe8a37cd698a4eae32f4fd1d2a7b6c115d"}, + {file = "fastrlock-0.8.2-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e4bbde174a0aff5f6eeba75cf8c4c5d2a316316bc21f03a0bddca0fc3659a6f3"}, + {file = "fastrlock-0.8.2-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7a2ccaf88ac0db153e84305d1ef0aa138cea82c6a88309066f6eaa3bc98636cd"}, + {file = "fastrlock-0.8.2-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:31a27a2edf482df72b91fe6c6438314d2c65290aa7becc55589d156c9b91f0da"}, + {file = "fastrlock-0.8.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:e9904b5b37c3e5bb4a245c56bc4b7e497da57ffb8528f4fc39af9dcb168ee2e1"}, + {file = "fastrlock-0.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:43a241655e83e4603a152192cf022d5ca348c2f4e56dfb02e5c9c4c1a32f9cdb"}, + {file = "fastrlock-0.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9121a894d74e65557e47e777060a495ab85f4b903e80dd73a3c940ba042920d7"}, + {file = "fastrlock-0.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:11bbbbc526363955aeddb9eec4cee2a0012322b7b2f15b54f44454fcf4fd398a"}, + {file = "fastrlock-0.8.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:27786c62a400e282756ae1b090bcd7cfa35f28270cff65a9e7b27a5327a32561"}, + {file = "fastrlock-0.8.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:08315bde19d0c2e6b06593d5a418be3dc8f9b1ee721afa96867b9853fceb45cf"}, + {file = "fastrlock-0.8.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e8b49b5743ede51e0bcf6805741f39f5e0e0fd6a172ba460cb39e3097ba803bb"}, + {file = "fastrlock-0.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b443e73a4dfc7b6e0800ea4c13567b9694358e86f53bb2612a51c9e727cac67b"}, + {file = "fastrlock-0.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:b3853ed4ce522598dc886160a7bab432a093051af85891fa2f5577c1dcac8ed6"}, + {file = "fastrlock-0.8.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:790fc19bccbd39426060047e53629f171a44745613bf360a045e9f9c8c4a2cea"}, + {file = "fastrlock-0.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:dbdce852e6bb66e1b8c36679d482971d69d93acf1785657522e51b7de30c3356"}, + {file = "fastrlock-0.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d47713ffe6d4a627fbf078be9836a95ac106b4a0543e3841572c91e292a5d885"}, + {file = "fastrlock-0.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:ea96503b918fceaf40443182742b8964d47b65c5ebdea532893cb9479620000c"}, + {file = "fastrlock-0.8.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c6bffa978793bea5e1b00e677062e53a62255439339591b70e209fa1552d5ee0"}, + {file = "fastrlock-0.8.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:75c07726c8b1a52147fd7987d6baaa318c5dced1416c3f25593e40f56e10755b"}, + {file = "fastrlock-0.8.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:88f079335e9da631efa64486c8207564a7bcd0c00526bb9e842e9d5b7e50a6cc"}, + {file = "fastrlock-0.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4fb2e77ff04bc4beb71d63c8e064f052ce5a6ea1e001d528d4d7f4b37d736f2e"}, + {file = "fastrlock-0.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:b4c9083ea89ab236b06e9ef2263971db3b4b507195fc7d5eecab95828dcae325"}, + {file = "fastrlock-0.8.2-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:98195866d3a9949915935d40a88e4f1c166e82e378f622c88025f2938624a90a"}, + {file = "fastrlock-0.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b22ea9bf5f9fad2b0077e944a7813f91593a4f61adf8faf734a70aed3f2b3a40"}, + {file = "fastrlock-0.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dcc1bf0ac8a194313cf6e645e300a8a379674ceed8e0b1e910a2de3e3c28989e"}, + {file = "fastrlock-0.8.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a3dcc876050b8f5cbc0ee84ef1e7f0c1dfe7c148f10098828bc4403683c33f10"}, + {file = "fastrlock-0.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:685e656048b59d8dfde8c601f188ad53a4d719eb97080cafc8696cda6d75865e"}, + {file = "fastrlock-0.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:fb5363cf0fddd9b50525ddbf64a1e1b28ec4c6dfb28670a940cb1cf988a6786b"}, + {file = "fastrlock-0.8.2-cp35-cp35m-macosx_10_15_x86_64.whl", hash = "sha256:a74f5a92fa6e51c4f3c69b29c4662088b97be12f40652a21109605a175c81824"}, + {file = "fastrlock-0.8.2-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ccf39ad5702e33e4d335b48ef9d56e21619b529b7f7471b5211419f380329b62"}, + {file = "fastrlock-0.8.2-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:66f2662c640bb71a1016a031eea6eef9d25c2bcdf7ffd1d1ddc5a58f9a1ced04"}, + {file = "fastrlock-0.8.2-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:17734e2e5af4c07ddb0fb10bd484e062c22de3be6b67940b9cc6ec2f18fa61ba"}, + {file = "fastrlock-0.8.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:ab91b0c36e95d42e1041a4907e3eefd06c482d53af3c7a77be7e214cc7cd4a63"}, + {file = "fastrlock-0.8.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b32fdf874868326351a75b1e4c02f97e802147119ae44c52d3d9da193ec34f5b"}, + {file = "fastrlock-0.8.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:2074548a335fcf7d19ebb18d9208da9e33b06f745754466a7e001d2b1c58dd19"}, + {file = "fastrlock-0.8.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4fb04442b6d1e2b36c774919c6bcbe3339c61b337261d4bd57e27932589095af"}, + {file = "fastrlock-0.8.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:1fed2f4797ad68e9982038423018cf08bec5f4ce9fed63a94a790773ed6a795c"}, + {file = "fastrlock-0.8.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e380ec4e6d8b26e389713995a43cb7fe56baea2d25fe073d4998c4821a026211"}, + {file = "fastrlock-0.8.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:25945f962c7bd808415cfde3da624d4399d4ea71ed8918538375f16bceb79e1c"}, + {file = "fastrlock-0.8.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:2c1719ddc8218b01e82fb2e82e8451bd65076cb96d7bef4477194bbb4305a968"}, + {file = "fastrlock-0.8.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:5460c5ee6ced6d61ec8cd2324ebbe793a4960c4ffa2131ffff480e3b61c99ec5"}, + {file = "fastrlock-0.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:33145acbad8317584cd64588131c7e1e286beef6280c0009b4544c91fce171d2"}, + {file = "fastrlock-0.8.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:59344c1d46b7dec97d3f22f1cc930fafe8980b3c5bc9c9765c56738a5f1559e4"}, + {file = "fastrlock-0.8.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2a1c354f13f22b737621d914f3b4a8434ae69d3027a775e94b3e671756112f9"}, + {file = "fastrlock-0.8.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:cf81e0278b645004388873e0a1f9e3bc4c9ab8c18e377b14ed1a544be4b18c9a"}, + {file = "fastrlock-0.8.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1b15430b93d7eb3d56f6ff690d2ebecb79ed0e58248427717eba150a508d1cd7"}, + {file = "fastrlock-0.8.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:067edb0a0805bf61e17a251d5046af59f6e9d2b8ad01222e0ef7a0b7937d5548"}, + {file = "fastrlock-0.8.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb31fe390f03f7ae886dcc374f1099ec88526631a4cb891d399b68181f154ff0"}, + {file = "fastrlock-0.8.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:643e1e65b4f5b284427e61a894d876d10459820e93aa1e724dfb415117be24e0"}, + {file = "fastrlock-0.8.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5dfb78dd600a12f23fc0c3ec58f81336229fdc74501ecf378d1ce5b3f2f313ea"}, + {file = "fastrlock-0.8.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b8ca0fe21458457077e4cb2d81e1ebdb146a00b3e9e2db6180a773f7ea905032"}, + {file = "fastrlock-0.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:d918dfe473291e8bfd8e13223ea5cb9b317bd9f50c280923776c377f7c64b428"}, + {file = "fastrlock-0.8.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:c393af77c659a38bffbca215c0bcc8629ba4299568308dd7e4ff65d62cabed39"}, + {file = "fastrlock-0.8.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73426f5eb2ecc10626c67cf86bd0af9e00d53e80e5c67d5ce8e18376d6abfa09"}, + {file = "fastrlock-0.8.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:320fd55bafee3eb069cfb5d6491f811a912758387ef2193840e2663e80e16f48"}, + {file = "fastrlock-0.8.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8c1c91a68926421f5ccbc82c85f83bd3ba593b121a46a1b9a554b3f0dd67a4bf"}, + {file = "fastrlock-0.8.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ad1bc61c7f6b0e58106aaab034916b6cb041757f708b07fbcdd9d6e1ac629225"}, + {file = "fastrlock-0.8.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:87f4e01b042c84e6090dbc4fbe3415ddd69f6bc0130382323f9d3f1b8dd71b46"}, + {file = "fastrlock-0.8.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d34546ad2e4a480b94b6797bcc5a322b3c705c4c74c3e4e545c4a3841c1b2d59"}, + {file = "fastrlock-0.8.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ebb32d776b61acd49f859a1d16b9e3d84e7b46d0d92aebd58acd54dc38e96664"}, + {file = "fastrlock-0.8.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:30bdbe4662992348132d03996700e1cf910d141d629179b967b146a22942264e"}, + {file = "fastrlock-0.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:07ed3c7b3867c05a3d6be4ced200c7767000f3431b9be6da66972822dd86e8be"}, + {file = "fastrlock-0.8.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:ddf5d247f686aec853ddcc9a1234bfcc6f57b0a0670d2ad82fc25d8ae7e6a15f"}, + {file = "fastrlock-0.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7269bb3fc15587b0c191eecd95831d771a7d80f0c48929e560806b038ff3066c"}, + {file = "fastrlock-0.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adcb9e77aa132cc6c9de2ffe7cf880a20aa8cdba21d367d1da1a412f57bddd5d"}, + {file = "fastrlock-0.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:a3b8b5d2935403f1b4b25ae324560e94b59593a38c0d2e7b6c9872126a9622ed"}, + {file = "fastrlock-0.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2587cedbb36c7988e707d83f0f1175c1f882f362b5ebbee25d70218ea33d220d"}, + {file = "fastrlock-0.8.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9af691a9861027181d4de07ed74f0aee12a9650ac60d0a07f4320bff84b5d95f"}, + {file = "fastrlock-0.8.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:99dd6652bd6f730beadf74ef769d38c6bbd8ee6d1c15c8d138ea680b0594387f"}, + {file = "fastrlock-0.8.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:4d63b6596368dab9e0cc66bf047e7182a56f33b34db141816a4f21f5bf958228"}, + {file = "fastrlock-0.8.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ff75c90663d6e8996610d435e71487daa853871ad1770dd83dc0f2fc4997241e"}, + {file = "fastrlock-0.8.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e27c3cd27fbd25e5223c5c992b300cd4ee8f0a75c6f222ce65838138d853712c"}, + {file = "fastrlock-0.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:dd961a32a7182c3891cdebca417fda67496d5d5de6ae636962254d22723bdf52"}, + {file = "fastrlock-0.8.2.tar.gz", hash = "sha256:644ec9215cf9c4df8028d8511379a15d9c1af3e16d80e47f1b6fdc6ba118356a"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "ipython" +version = "7.34.0" +description = "IPython: Productive Interactive Computing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ipython-7.34.0-py3-none-any.whl", hash = "sha256:c175d2440a1caff76116eb719d40538fbb316e214eda85c5515c303aacbfb23e"}, + {file = "ipython-7.34.0.tar.gz", hash = "sha256:af3bdb46aa292bce5615b1b2ebc76c2080c5f77f54bda2ec72461317273e7cd6"}, +] + +[package.dependencies] +appnope = {version = "*", markers = "sys_platform == \"darwin\""} +backcall = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} +pickleshare = "*" +prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" +pygments = "*" +setuptools = ">=18.5" +traitlets = ">=4.2" + +[package.extras] +all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"] +doc = ["Sphinx (>=1.3)"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["ipywidgets", "notebook"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["ipykernel", "nbformat", "nose (>=0.10.1)", "numpy (>=1.17)", "pygments", "requests", "testpath"] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "jedi" +version = "0.19.1" +description = "An autocompletion tool for Python that can be used for text editors." +optional = false +python-versions = ">=3.6" +files = [ + {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, + {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, +] + +[package.dependencies] +parso = ">=0.8.3,<0.9.0" + +[package.extras] +docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] + +[[package]] +name = "matplotlib-inline" +version = "0.1.6" +description = "Inline Matplotlib backend for Jupyter" +optional = false +python-versions = ">=3.5" +files = [ + {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, + {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, +] + +[package.dependencies] +traitlets = "*" + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "numpy" +version = "1.26.4" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, +] + +[[package]] +name = "packaging" +version = "23.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, +] + +[[package]] +name = "parso" +version = "0.8.3" +description = "A Python Parser" +optional = false +python-versions = ">=3.6" +files = [ + {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, + {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, +] + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["docopt", "pytest (<6.0.0)"] + +[[package]] +name = "pexpect" +version = "4.9.0" +description = "Pexpect allows easy control of interactive console applications." +optional = false +python-versions = "*" +files = [ + {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, + {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, +] + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +name = "pickleshare" +version = "0.7.5" +description = "Tiny 'shelve'-like database with concurrency support" +optional = false +python-versions = "*" +files = [ + {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, + {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, +] + +[[package]] +name = "platformdirs" +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] + +[[package]] +name = "pluggy" +version = "1.4.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "prompt-toolkit" +version = "3.0.43" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"}, + {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"}, +] + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +optional = false +python-versions = "*" +files = [ + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, +] + +[[package]] +name = "pygments" +version = "2.17.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] + +[package.extras] +plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pylint" +version = "3.0.3" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.0.3-py3-none-any.whl", hash = "sha256:7a1585285aefc5165db81083c3e06363a27448f6b467b3b0f30dbd0ac1f73810"}, + {file = "pylint-3.0.3.tar.gz", hash = "sha256:58c2398b0301e049609a8429789ec6edf3aabe9b6c5fec916acd18639c16de8b"}, +] + +[package.dependencies] +astroid = ">=3.0.1,<=3.1.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = [ + {version = ">=0.2", markers = "python_version < \"3.11\""}, + {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, +] +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +tomlkit = ">=0.10.1" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pytest" +version = "8.0.0" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.0.0-py3-none-any.whl", hash = "sha256:50fb9cbe836c3f20f0dfa99c565201fb75dc54c8d76373cd1bde06b06657bdb6"}, + {file = "pytest-8.0.0.tar.gz", hash = "sha256:249b1b0864530ba251b7438274c4d251c58d868edaaec8762893ad4a0d71c36c"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.3.0,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "pytest-env" +version = "1.1.3" +description = "pytest plugin that allows you to add environment variables." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_env-1.1.3-py3-none-any.whl", hash = "sha256:aada77e6d09fcfb04540a6e462c58533c37df35fa853da78707b17ec04d17dfc"}, + {file = "pytest_env-1.1.3.tar.gz", hash = "sha256:fcd7dc23bb71efd3d35632bde1bbe5ee8c8dc4489d6617fb010674880d96216b"}, +] + +[package.dependencies] +pytest = ">=7.4.3" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +test = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "pytest-mock (>=3.12)"] + +[[package]] +name = "setuptools" +version = "69.0.3" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"}, + {file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tomlkit" +version = "0.12.3" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.3-py3-none-any.whl", hash = "sha256:b0a645a9156dc7cb5d3a1f0d4bab66db287fcb8e0430bdd4664a095ea16414ba"}, + {file = "tomlkit-0.12.3.tar.gz", hash = "sha256:75baf5012d06501f07bee5bf8e801b9f343e7aac5a92581f20f80ce632e6b5a4"}, +] + +[[package]] +name = "traitlets" +version = "5.14.1" +description = "Traitlets Python configuration system" +optional = false +python-versions = ">=3.8" +files = [ + {file = "traitlets-5.14.1-py3-none-any.whl", hash = "sha256:2e5a030e6eff91737c643231bfcf04a65b0132078dad75e4936700b213652e74"}, + {file = "traitlets-5.14.1.tar.gz", hash = "sha256:8585105b371a04b8316a43d5ce29c098575c2e477850b62b848b964f1444527e"}, +] + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<7.5)", "pytest-mock", "pytest-mypy-testing"] + +[[package]] +name = "typing-extensions" +version = "4.9.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, + {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, +] + +[[package]] +name = "wcwidth" +version = "0.2.13" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +files = [ + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.9,<3.13" +content-hash = "96c82bd153e6b4217355830d8104ba480cc360e2d73ec5e064c8fc3ffbe5d813" diff --git a/pyproject.toml b/pyproject.toml index 82c13830..2a8824ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,36 @@ +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry] +name = "qibotn" +version = "0.0.1" +description = "" +authors = ["Qibo Team"] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.9,<3.13" +qibojit = "^0.0.7" +quimb = { version = "^0.0.7", extras = ["tensor"] } + +[tool.poetry.group.dev.dependencies] +ipython = "^7" + + +[tool.poetry.group.tests.dependencies] +pytest = "^8.0.0" +pytest-cov = "^4.1.0" +pytest-env = "^1.1.3" + + +[tool.poetry.group.analysis.dependencies] +pylint = "^3.0.3" + +[tool.poetry.group.cuda.dependencies] +cupy = "^11.6.0" +cuquantum-python-cu11 = "^23.3.0" + [tool.pylint.main] ignored-modules = ["cupy", "cuquantum", "mpi4py"] From 2de7a65d02d0dc90b36a386b99cef73e326d905f Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 8 Feb 2024 16:38:23 +0100 Subject: [PATCH 175/199] build: Make dependency groups optional --- poetry.lock | 829 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 13 +- 2 files changed, 838 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 188eed16..d81be41d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -25,6 +25,21 @@ files = [ [package.dependencies] typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} +[[package]] +name = "autoray" +version = "0.6.8" +description = "Abstract your array operations." +optional = false +python-versions = ">=3.8" +files = [ + {file = "autoray-0.6.8-py3-none-any.whl", hash = "sha256:56ce1a1e105e14fd74e5e2d724a92421af7601b34b73f10c0cf58d678958fde4"}, + {file = "autoray-0.6.8.tar.gz", hash = "sha256:8e31832597cb2075e5f9f65894fafff9d726d9287718415d3c8b008e592f0197"}, +] + +[package.extras] +docs = ["astroid (<3)", "furo", "ipython (!=8.7.0)", "myst-nb", "setuptools-scm", "sphinx (>=2.0)", "sphinx-autoapi", "sphinx-copybutton"] +tests = ["coverage", "numpy", "pytest", "pytest-cov"] + [[package]] name = "backcall" version = "0.2.0" @@ -47,6 +62,88 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "contourpy" +version = "1.2.0" +description = "Python library for calculating contours of 2D quadrilateral grids" +optional = false +python-versions = ">=3.9" +files = [ + {file = "contourpy-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0274c1cb63625972c0c007ab14dd9ba9e199c36ae1a231ce45d725cbcbfd10a8"}, + {file = "contourpy-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab459a1cbbf18e8698399c595a01f6dcc5c138220ca3ea9e7e6126232d102bb4"}, + {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fdd887f17c2f4572ce548461e4f96396681212d858cae7bd52ba3310bc6f00f"}, + {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d16edfc3fc09968e09ddffada434b3bf989bf4911535e04eada58469873e28e"}, + {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c203f617abc0dde5792beb586f827021069fb6d403d7f4d5c2b543d87edceb9"}, + {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b69303ceb2e4d4f146bf82fda78891ef7bcd80c41bf16bfca3d0d7eb545448aa"}, + {file = "contourpy-1.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:884c3f9d42d7218304bc74a8a7693d172685c84bd7ab2bab1ee567b769696df9"}, + {file = "contourpy-1.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4a1b1208102be6e851f20066bf0e7a96b7d48a07c9b0cfe6d0d4545c2f6cadab"}, + {file = "contourpy-1.2.0-cp310-cp310-win32.whl", hash = "sha256:34b9071c040d6fe45d9826cbbe3727d20d83f1b6110d219b83eb0e2a01d79488"}, + {file = "contourpy-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:bd2f1ae63998da104f16a8b788f685e55d65760cd1929518fd94cd682bf03e41"}, + {file = "contourpy-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd10c26b4eadae44783c45ad6655220426f971c61d9b239e6f7b16d5cdaaa727"}, + {file = "contourpy-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c6b28956b7b232ae801406e529ad7b350d3f09a4fde958dfdf3c0520cdde0dd"}, + {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebeac59e9e1eb4b84940d076d9f9a6cec0064e241818bcb6e32124cc5c3e377a"}, + {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:139d8d2e1c1dd52d78682f505e980f592ba53c9f73bd6be102233e358b401063"}, + {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e9dc350fb4c58adc64df3e0703ab076f60aac06e67d48b3848c23647ae4310e"}, + {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18fc2b4ed8e4a8fe849d18dce4bd3c7ea637758c6343a1f2bae1e9bd4c9f4686"}, + {file = "contourpy-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:16a7380e943a6d52472096cb7ad5264ecee36ed60888e2a3d3814991a0107286"}, + {file = "contourpy-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8d8faf05be5ec8e02a4d86f616fc2a0322ff4a4ce26c0f09d9f7fb5330a35c95"}, + {file = "contourpy-1.2.0-cp311-cp311-win32.whl", hash = "sha256:67b7f17679fa62ec82b7e3e611c43a016b887bd64fb933b3ae8638583006c6d6"}, + {file = "contourpy-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:99ad97258985328b4f207a5e777c1b44a83bfe7cf1f87b99f9c11d4ee477c4de"}, + {file = "contourpy-1.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:575bcaf957a25d1194903a10bc9f316c136c19f24e0985a2b9b5608bdf5dbfe0"}, + {file = "contourpy-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9e6c93b5b2dbcedad20a2f18ec22cae47da0d705d454308063421a3b290d9ea4"}, + {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:464b423bc2a009088f19bdf1f232299e8b6917963e2b7e1d277da5041f33a779"}, + {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68ce4788b7d93e47f84edd3f1f95acdcd142ae60bc0e5493bfd120683d2d4316"}, + {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d7d1f8871998cdff5d2ff6a087e5e1780139abe2838e85b0b46b7ae6cc25399"}, + {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e739530c662a8d6d42c37c2ed52a6f0932c2d4a3e8c1f90692ad0ce1274abe0"}, + {file = "contourpy-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:247b9d16535acaa766d03037d8e8fb20866d054d3c7fbf6fd1f993f11fc60ca0"}, + {file = "contourpy-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:461e3ae84cd90b30f8d533f07d87c00379644205b1d33a5ea03381edc4b69431"}, + {file = "contourpy-1.2.0-cp312-cp312-win32.whl", hash = "sha256:1c2559d6cffc94890b0529ea7eeecc20d6fadc1539273aa27faf503eb4656d8f"}, + {file = "contourpy-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:491b1917afdd8638a05b611a56d46587d5a632cabead889a5440f7c638bc6ed9"}, + {file = "contourpy-1.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5fd1810973a375ca0e097dee059c407913ba35723b111df75671a1976efa04bc"}, + {file = "contourpy-1.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:999c71939aad2780f003979b25ac5b8f2df651dac7b38fb8ce6c46ba5abe6ae9"}, + {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7caf9b241464c404613512d5594a6e2ff0cc9cb5615c9475cc1d9b514218ae8"}, + {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:266270c6f6608340f6c9836a0fb9b367be61dde0c9a9a18d5ece97774105ff3e"}, + {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbd50d0a0539ae2e96e537553aff6d02c10ed165ef40c65b0e27e744a0f10af8"}, + {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11f8d2554e52f459918f7b8e6aa20ec2a3bce35ce95c1f0ef4ba36fbda306df5"}, + {file = "contourpy-1.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ce96dd400486e80ac7d195b2d800b03e3e6a787e2a522bfb83755938465a819e"}, + {file = "contourpy-1.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6d3364b999c62f539cd403f8123ae426da946e142312a514162adb2addd8d808"}, + {file = "contourpy-1.2.0-cp39-cp39-win32.whl", hash = "sha256:1c88dfb9e0c77612febebb6ac69d44a8d81e3dc60f993215425b62c1161353f4"}, + {file = "contourpy-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:78e6ad33cf2e2e80c5dfaaa0beec3d61face0fb650557100ee36db808bfa6843"}, + {file = "contourpy-1.2.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:be16975d94c320432657ad2402f6760990cb640c161ae6da1363051805fa8108"}, + {file = "contourpy-1.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b95a225d4948b26a28c08307a60ac00fb8671b14f2047fc5476613252a129776"}, + {file = "contourpy-1.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0d7e03c0f9a4f90dc18d4e77e9ef4ec7b7bbb437f7f675be8e530d65ae6ef956"}, + {file = "contourpy-1.2.0.tar.gz", hash = "sha256:171f311cb758de7da13fc53af221ae47a5877be5a0843a9fe150818c51ed276a"}, +] + +[package.dependencies] +numpy = ">=1.20,<2.0" + +[package.extras] +bokeh = ["bokeh", "selenium"] +docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"] +mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.6.1)", "types-Pillow"] +test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] +test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"] + +[[package]] +name = "cotengra" +version = "0.5.6" +description = "Hyper optimized contraction trees for large tensor networks and einsums." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cotengra-0.5.6-py3-none-any.whl", hash = "sha256:d2e1fa9f39e80e9e02fd750112d6722cc22538ce9a60d3aa247143f3db0e59f3"}, + {file = "cotengra-0.5.6.tar.gz", hash = "sha256:4e8ab61ea569eced25b602ca72aae0854f8825da76190664fe9c1414a55499cb"}, +] + +[package.dependencies] +autoray = "*" + +[package.extras] +docs = ["astroid (<3.0.0)", "furo", "ipython (!=8.7.0)", "myst-nb", "setuptools-scm", "sphinx (>=2.0)", "sphinx-autoapi", "sphinx-copybutton", "sphinx-design"] +recommended = ["cotengrust", "cytoolz", "kahypar", "networkx", "numpy", "opt-einsum", "optuna", "ray", "tqdm"] +test = ["altair", "baytune", "chocolate", "dask", "distributed", "kahypar", "matplotlib", "networkx", "nevergrad", "numpy", "opt-einsum", "pytest", "seaborn", "skopt"] + [[package]] name = "coverage" version = "7.4.1" @@ -190,6 +287,140 @@ files = [ [package.dependencies] cutensor-cu11 = ">=1.6.1,<2" +[[package]] +name = "cycler" +version = "0.12.1" +description = "Composable style cycles" +optional = false +python-versions = ">=3.8" +files = [ + {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, + {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, +] + +[package.extras] +docs = ["ipython", "matplotlib", "numpydoc", "sphinx"] +tests = ["pytest", "pytest-cov", "pytest-xdist"] + +[[package]] +name = "cytoolz" +version = "0.12.3" +description = "Cython implementation of Toolz: High performance functional utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cytoolz-0.12.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bbe58e26c84b163beba0fbeacf6b065feabc8f75c6d3fe305550d33f24a2d346"}, + {file = "cytoolz-0.12.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c51b66ada9bfdb88cf711bf350fcc46f82b83a4683cf2413e633c31a64df6201"}, + {file = "cytoolz-0.12.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e70d9c615e5c9dc10d279d1e32e846085fe1fd6f08d623ddd059a92861f4e3dd"}, + {file = "cytoolz-0.12.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a83f4532707963ae1a5108e51fdfe1278cc8724e3301fee48b9e73e1316de64f"}, + {file = "cytoolz-0.12.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d028044524ee2e815f36210a793c414551b689d4f4eda28f8bbb0883ad78bf5f"}, + {file = "cytoolz-0.12.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c2875bcd1397d0627a09a4f9172fa513185ad302c63758efc15b8eb33cc2a98"}, + {file = "cytoolz-0.12.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:131ff4820e5d64a25d7ad3c3556f2d8aa65c66b3f021b03f8a8e98e4180dd808"}, + {file = "cytoolz-0.12.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:04afa90d9d9d18394c40d9bed48c51433d08b57c042e0e50c8c0f9799735dcbd"}, + {file = "cytoolz-0.12.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:dc1ca9c610425f9854323669a671fc163300b873731584e258975adf50931164"}, + {file = "cytoolz-0.12.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:bfa3f8e01bc423a933f2e1c510cbb0632c6787865b5242857cc955cae220d1bf"}, + {file = "cytoolz-0.12.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:f702e295dddef5f8af4a456db93f114539b8dc2a7a9bc4de7c7e41d169aa6ec3"}, + {file = "cytoolz-0.12.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0fbad1fb9bb47e827d00e01992a099b0ba79facf5e5aa453be066033232ac4b5"}, + {file = "cytoolz-0.12.3-cp310-cp310-win32.whl", hash = "sha256:8587c3c3dbe78af90c5025288766ac10dc2240c1e76eb0a93a4e244c265ccefd"}, + {file = "cytoolz-0.12.3-cp310-cp310-win_amd64.whl", hash = "sha256:9e45803d9e75ef90a2f859ef8f7f77614730f4a8ce1b9244375734567299d239"}, + {file = "cytoolz-0.12.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3ac4f2fb38bbc67ff1875b7d2f0f162a247f43bd28eb7c9d15e6175a982e558d"}, + {file = "cytoolz-0.12.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0cf1e1e96dd86829a0539baf514a9c8473a58fbb415f92401a68e8e52a34ecd5"}, + {file = "cytoolz-0.12.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08a438701c6141dd34eaf92e9e9a1f66e23a22f7840ef8a371eba274477de85d"}, + {file = "cytoolz-0.12.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6b6f11b0d7ed91be53166aeef2a23a799e636625675bb30818f47f41ad31821"}, + {file = "cytoolz-0.12.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7fde09384d23048a7b4ac889063761e44b89a0b64015393e2d1d21d5c1f534a"}, + {file = "cytoolz-0.12.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d3bfe45173cc8e6c76206be3a916d8bfd2214fb2965563e288088012f1dabfc"}, + {file = "cytoolz-0.12.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27513a5d5b6624372d63313574381d3217a66e7a2626b056c695179623a5cb1a"}, + {file = "cytoolz-0.12.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d294e5e81ff094fe920fd545052ff30838ea49f9e91227a55ecd9f3ca19774a0"}, + {file = "cytoolz-0.12.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:727b01a2004ddb513496507a695e19b5c0cfebcdfcc68349d3efd92a1c297bf4"}, + {file = "cytoolz-0.12.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:fe1e1779a39dbe83f13886d2b4b02f8c4b10755e3c8d9a89b630395f49f4f406"}, + {file = "cytoolz-0.12.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:de74ef266e2679c3bf8b5fc20cee4fc0271ba13ae0d9097b1491c7a9bcadb389"}, + {file = "cytoolz-0.12.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e04d22049233394e0b08193aca9737200b4a2afa28659d957327aa780ddddf2"}, + {file = "cytoolz-0.12.3-cp311-cp311-win32.whl", hash = "sha256:20d36430d8ac809186736fda735ee7d595b6242bdb35f69b598ef809ebfa5605"}, + {file = "cytoolz-0.12.3-cp311-cp311-win_amd64.whl", hash = "sha256:780c06110f383344d537f48d9010d79fa4f75070d214fc47f389357dd4f010b6"}, + {file = "cytoolz-0.12.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:86923d823bd19ce35805953b018d436f6b862edd6a7c8b747a13d52b39ed5716"}, + {file = "cytoolz-0.12.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3e61acfd029bfb81c2c596249b508dfd2b4f72e31b7b53b62e5fb0507dd7293"}, + {file = "cytoolz-0.12.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd728f4e6051af6af234651df49319da1d813f47894d4c3c8ab7455e01703a37"}, + {file = "cytoolz-0.12.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe8c6267caa7ec67bcc37e360f0d8a26bc3bdce510b15b97f2f2e0143bdd3673"}, + {file = "cytoolz-0.12.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99462abd8323c52204a2a0ce62454ce8fa0f4e94b9af397945c12830de73f27e"}, + {file = "cytoolz-0.12.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da125221b1fa25c690fcd030a54344cecec80074df018d906fc6a99f46c1e3a6"}, + {file = "cytoolz-0.12.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c18e351956f70db9e2d04ff02f28e9a41839250d3f936a4c8a1eabd1c3094d2"}, + {file = "cytoolz-0.12.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:921e6d2440ac758c4945c587b1d1d9b781b72737ac0c0ca5d5e02ca1db8bded2"}, + {file = "cytoolz-0.12.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1651a9bd591a8326329ce1d6336f3129161a36d7061a4d5ea9e5377e033364cf"}, + {file = "cytoolz-0.12.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8893223b87c2782bd59f9c4bd5c7bf733edd8728b523c93efb91d7468b486528"}, + {file = "cytoolz-0.12.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:e4d2961644153c5ae186db964aa9f6109da81b12df0f1d3494b4e5cf2c332ee2"}, + {file = "cytoolz-0.12.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:71b6eb97f6695f7ba8ce69c49b707a351c5f46fd97f5aeb5f6f2fb0d6e72b887"}, + {file = "cytoolz-0.12.3-cp312-cp312-win32.whl", hash = "sha256:cee3de65584e915053412cd178729ff510ad5f8f585c21c5890e91028283518f"}, + {file = "cytoolz-0.12.3-cp312-cp312-win_amd64.whl", hash = "sha256:9eef0d23035fa4dcfa21e570961e86c375153a7ee605cdd11a8b088c24f707f6"}, + {file = "cytoolz-0.12.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9a38332cfad2a91e89405b7c18b3f00e2edc951c225accbc217597d3e4e9fde"}, + {file = "cytoolz-0.12.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f501ae1353071fa5d6677437bbeb1aeb5622067dce0977cedc2c5ec5843b202"}, + {file = "cytoolz-0.12.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:56f899758146a52e2f8cfb3fb6f4ca19c1e5814178c3d584de35f9e4d7166d91"}, + {file = "cytoolz-0.12.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:800f0526adf9e53d3c6acda748f4def1f048adaa780752f154da5cf22aa488a2"}, + {file = "cytoolz-0.12.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0976a3fcb81d065473173e9005848218ce03ddb2ec7d40dd6a8d2dba7f1c3ae"}, + {file = "cytoolz-0.12.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c835eab01466cb67d0ce6290601ebef2d82d8d0d0a285ed0d6e46989e4a7a71a"}, + {file = "cytoolz-0.12.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4fba0616fcd487e34b8beec1ad9911d192c62e758baa12fcb44448b9b6feae22"}, + {file = "cytoolz-0.12.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6f6e8207d732651e0204779e1ba5a4925c93081834570411f959b80681f8d333"}, + {file = "cytoolz-0.12.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:8119bf5961091cfe644784d0bae214e273b3b3a479f93ee3baab97bbd995ccfe"}, + {file = "cytoolz-0.12.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:7ad1331cb68afeec58469c31d944a2100cee14eac221553f0d5218ace1a0b25d"}, + {file = "cytoolz-0.12.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:92c53d508fb8a4463acc85b322fa24734efdc66933a5c8661bdc862103a3373d"}, + {file = "cytoolz-0.12.3-cp37-cp37m-win32.whl", hash = "sha256:2c6dd75dae3d84fa8988861ab8b1189d2488cb8a9b8653828f9cd6126b5e7abd"}, + {file = "cytoolz-0.12.3-cp37-cp37m-win_amd64.whl", hash = "sha256:caf07a97b5220e6334dd32c8b6d8b2bd255ca694eca5dfe914bb5b880ee66cdb"}, + {file = "cytoolz-0.12.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ed0cfb9326747759e2ad81cb6e45f20086a273b67ac3a4c00b19efcbab007c60"}, + {file = "cytoolz-0.12.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:96a5a0292575c3697121f97cc605baf2fd125120c7dcdf39edd1a135798482ca"}, + {file = "cytoolz-0.12.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b76f2f50a789c44d6fd7f773ec43d2a8686781cd52236da03f7f7d7998989bee"}, + {file = "cytoolz-0.12.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2905fdccacc64b4beba37f95cab9d792289c80f4d70830b70de2fc66c007ec01"}, + {file = "cytoolz-0.12.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ebe23028eac51251f22ba01dba6587d30aa9c320372ca0c14eeab67118ec3f"}, + {file = "cytoolz-0.12.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96c715404a3825e37fe3966fe84c5f8a1f036e7640b2a02dbed96cac0c933451"}, + {file = "cytoolz-0.12.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bac0adffc1b6b6a4c5f1fd1dd2161afb720bcc771a91016dc6bdba59af0a5d3"}, + {file = "cytoolz-0.12.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:37441bf4a2a4e2e0fe9c3b0ea5e72db352f5cca03903977ffc42f6f6c5467be9"}, + {file = "cytoolz-0.12.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f04037302049cb30033f7fa4e1d0e44afe35ed6bfcf9b380fc11f2a27d3ed697"}, + {file = "cytoolz-0.12.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:f37b60e66378e7a116931d7220f5352186abfcc950d64856038aa2c01944929c"}, + {file = "cytoolz-0.12.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:ec9be3e4b6f86ea8b294d34c990c99d2ba6c526ef1e8f46f1d52c263d4f32cd7"}, + {file = "cytoolz-0.12.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0e9199c9e3fbf380a92b8042c677eb9e7ed4bccb126de5e9c0d26f5888d96788"}, + {file = "cytoolz-0.12.3-cp38-cp38-win32.whl", hash = "sha256:18cd61e078bd6bffe088e40f1ed02001387c29174750abce79499d26fa57f5eb"}, + {file = "cytoolz-0.12.3-cp38-cp38-win_amd64.whl", hash = "sha256:765b8381d4003ceb1a07896a854eee2c31ebc950a4ae17d1e7a17c2a8feb2a68"}, + {file = "cytoolz-0.12.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b4a52dd2a36b0a91f7aa50ca6c8509057acc481a24255f6cb07b15d339a34e0f"}, + {file = "cytoolz-0.12.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:581f1ce479769fe7eeb9ae6d87eadb230df8c7c5fff32138162cdd99d7fb8fc3"}, + {file = "cytoolz-0.12.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46f505d4c6eb79585c8ad0b9dc140ef30a138c880e4e3b40230d642690e36366"}, + {file = "cytoolz-0.12.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59276021619b432a5c21c01cda8320b9cc7dbc40351ffc478b440bfccd5bbdd3"}, + {file = "cytoolz-0.12.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e44f4c25e1e7cf6149b499c74945a14649c8866d36371a2c2d2164e4649e7755"}, + {file = "cytoolz-0.12.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c64f8e60c1dd69e4d5e615481f2d57937746f4a6be2d0f86e9e7e3b9e2243b5e"}, + {file = "cytoolz-0.12.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33c63186f3bf9d7ef1347bc0537bb9a0b4111a0d7d6e619623cabc18fef0dc3b"}, + {file = "cytoolz-0.12.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fdddb9d988405f24035234f1e8d1653ab2e48cc2404226d21b49a129aefd1d25"}, + {file = "cytoolz-0.12.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6986632d8a969ea1e720990c818dace1a24c11015fd7c59b9fea0b65ef71f726"}, + {file = "cytoolz-0.12.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0ba1cbc4d9cd7571c917f88f4a069568e5121646eb5d82b2393b2cf84712cf2a"}, + {file = "cytoolz-0.12.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7d267ffc9a36c0a9a58c7e0adc9fa82620f22e4a72533e15dd1361f57fc9accf"}, + {file = "cytoolz-0.12.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95e878868a172a41fbf6c505a4b967309e6870e22adc7b1c3b19653d062711fa"}, + {file = "cytoolz-0.12.3-cp39-cp39-win32.whl", hash = "sha256:8e21932d6d260996f7109f2a40b2586070cb0a0cf1d65781e156326d5ebcc329"}, + {file = "cytoolz-0.12.3-cp39-cp39-win_amd64.whl", hash = "sha256:0d8edfbc694af6c9bda4db56643fb8ed3d14e47bec358c2f1417de9a12d6d1fb"}, + {file = "cytoolz-0.12.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:55f9bd1ae6c2a27eda5abe2a0b65a83029d2385c5a1da7b8ef47af5905d7e905"}, + {file = "cytoolz-0.12.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2d271393c378282727f1231d40391ae93b93ddc0997448acc21dd0cb6a1e56d"}, + {file = "cytoolz-0.12.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee98968d6a66ee83a8ceabf31182189ab5d8598998c8ce69b6d5843daeb2db60"}, + {file = "cytoolz-0.12.3-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01cfb8518828c1189200c02a5010ea404407fb18fd5589e29c126e84bbeadd36"}, + {file = "cytoolz-0.12.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:456395d7aec01db32bf9e6db191d667347c78d8d48e77234521fa1078f60dabb"}, + {file = "cytoolz-0.12.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:cd88028bb897fba99ddd84f253ca6bef73ecb7bdf3f3cf25bc493f8f97d3c7c5"}, + {file = "cytoolz-0.12.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59b19223e7f7bd7a73ec3aa6fdfb73b579ff09c2bc0b7d26857eec2d01a58c76"}, + {file = "cytoolz-0.12.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a79d72b08048a0980a59457c239555f111ac0c8bdc140c91a025f124104dbb4"}, + {file = "cytoolz-0.12.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1dd70141b32b717696a72b8876e86bc9c6f8eff995c1808e299db3541213ff82"}, + {file = "cytoolz-0.12.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:a1445c91009eb775d479e88954c51d0b4cf9a1e8ce3c503c2672d17252882647"}, + {file = "cytoolz-0.12.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ca6a9a9300d5bda417d9090107c6d2b007683efc59d63cc09aca0e7930a08a85"}, + {file = "cytoolz-0.12.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be6feb903d2a08a4ba2e70e950e862fd3be9be9a588b7c38cee4728150a52918"}, + {file = "cytoolz-0.12.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b6f43f086e5a965d33d62a145ae121b4ccb6e0789ac0acc895ce084fec8c65"}, + {file = "cytoolz-0.12.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:534fa66db8564d9b13872d81d54b6b09ae592c585eb826aac235bd6f1830f8ad"}, + {file = "cytoolz-0.12.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:fea649f979def23150680de1bd1d09682da3b54932800a0f90f29fc2a6c98ba8"}, + {file = "cytoolz-0.12.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a447247ed312dd64e3a8d9483841ecc5338ee26d6e6fbd29cd373ed030db0240"}, + {file = "cytoolz-0.12.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba3f843aa89f35467b38c398ae5b980a824fdbdb94065adc6ec7c47a0a22f4c7"}, + {file = "cytoolz-0.12.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:582c22f97a380211fb36a7b65b1beeb84ea11d82015fa84b054be78580390082"}, + {file = "cytoolz-0.12.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47feb089506fc66e1593cd9ade3945693a9d089a445fbe9a11385cab200b9f22"}, + {file = "cytoolz-0.12.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ba9002d2f043943744a9dc8e50a47362bcb6e6f360dc0a1abcb19642584d87bb"}, + {file = "cytoolz-0.12.3.tar.gz", hash = "sha256:4503dc59f4ced53a54643272c61dc305d1dbbfbd7d6bdf296948de9f34c3a282"}, +] + +[package.dependencies] +toolz = ">=0.8.0" + +[package.extras] +cython = ["cython"] + [[package]] name = "decorator" version = "5.1.1" @@ -314,6 +545,89 @@ files = [ {file = "fastrlock-0.8.2.tar.gz", hash = "sha256:644ec9215cf9c4df8028d8511379a15d9c1af3e16d80e47f1b6fdc6ba118356a"}, ] +[[package]] +name = "fonttools" +version = "4.48.1" +description = "Tools to manipulate font files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fonttools-4.48.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:702ae93058c81f46461dc4b2c79f11d3c3d8fd7296eaf8f75b4ba5bbf813cd5f"}, + {file = "fonttools-4.48.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:97f0a49fa6aa2d6205c6f72f4f98b74ef4b9bfdcb06fd78e6fe6c7af4989b63e"}, + {file = "fonttools-4.48.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3260db55f1843e57115256e91247ad9f68cb02a434b51262fe0019e95a98738"}, + {file = "fonttools-4.48.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e740a7602c2bb71e1091269b5dbe89549749a8817dc294b34628ffd8b2bf7124"}, + {file = "fonttools-4.48.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4108b1d247953dd7c90ec8f457a2dec5fceb373485973cc852b14200118a51ee"}, + {file = "fonttools-4.48.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56339ec557f0c342bddd7c175f5e41c45fc21282bee58a86bd9aa322bec715f2"}, + {file = "fonttools-4.48.1-cp310-cp310-win32.whl", hash = "sha256:bff5b38d0e76eb18e0b8abbf35d384e60b3371be92f7be36128ee3e67483b3ec"}, + {file = "fonttools-4.48.1-cp310-cp310-win_amd64.whl", hash = "sha256:f7449493886da6a17472004d3818cc050ba3f4a0aa03fb47972e4fa5578e6703"}, + {file = "fonttools-4.48.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:18b35fd1a850ed7233a99bbd6774485271756f717dac8b594958224b54118b61"}, + {file = "fonttools-4.48.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cad5cfd044ea2e306fda44482b3dd32ee47830fa82dfa4679374b41baa294f5f"}, + {file = "fonttools-4.48.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f30e605c7565d0da6f0aec75a30ec372072d016957cd8fc4469721a36ea59b7"}, + {file = "fonttools-4.48.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aee76fd81a8571c68841d6ef0da750d5ff08ff2c5f025576473016f16ac3bcf7"}, + {file = "fonttools-4.48.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5057ade278e67923000041e2b195c9ea53e87f227690d499b6a4edd3702f7f01"}, + {file = "fonttools-4.48.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b10633aafc5932995a391ec07eba5e79f52af0003a1735b2306b3dab8a056d48"}, + {file = "fonttools-4.48.1-cp311-cp311-win32.whl", hash = "sha256:0d533f89819f9b3ee2dbedf0fed3825c425850e32bdda24c558563c71be0064e"}, + {file = "fonttools-4.48.1-cp311-cp311-win_amd64.whl", hash = "sha256:d20588466367f05025bb1efdf4e5d498ca6d14bde07b6928b79199c588800f0a"}, + {file = "fonttools-4.48.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0a2417547462e468edf35b32e3dd06a6215ac26aa6316b41e03b8eeaf9f079ea"}, + {file = "fonttools-4.48.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cf5a0cd974f85a80b74785db2d5c3c1fd6cc09a2ba3c837359b2b5da629ee1b0"}, + {file = "fonttools-4.48.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0452fcbfbce752ba596737a7c5ec5cf76bc5f83847ce1781f4f90eab14ece252"}, + {file = "fonttools-4.48.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578c00f93868f64a4102ecc5aa600a03b49162c654676c3fadc33de2ddb88a81"}, + {file = "fonttools-4.48.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:63dc592a16cd08388d8c4c7502b59ac74190b23e16dfc863c69fe1ea74605b68"}, + {file = "fonttools-4.48.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9b58638d8a85e3a1b32ec0a91d9f8171a877b4b81c408d4cb3257d0dee63e092"}, + {file = "fonttools-4.48.1-cp312-cp312-win32.whl", hash = "sha256:d10979ef14a8beaaa32f613bb698743f7241d92f437a3b5e32356dfb9769c65d"}, + {file = "fonttools-4.48.1-cp312-cp312-win_amd64.whl", hash = "sha256:cdfd7557d1bd294a200bd211aa665ca3b02998dcc18f8211a5532da5b8fad5c5"}, + {file = "fonttools-4.48.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3cdb9a92521b81bf717ebccf592bd0292e853244d84115bfb4db0c426de58348"}, + {file = "fonttools-4.48.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9b4ec6d42a7555f5ae35f3b805482f0aad0f1baeeef54859492ea3b782959d4a"}, + {file = "fonttools-4.48.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:902e9c4e9928301912f34a6638741b8ae0b64824112b42aaf240e06b735774b1"}, + {file = "fonttools-4.48.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8c8b54bd1420c184a995f980f1a8076f87363e2bb24239ef8c171a369d85a31"}, + {file = "fonttools-4.48.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:12ee86abca46193359ea69216b3a724e90c66ab05ab220d39e3fc068c1eb72ac"}, + {file = "fonttools-4.48.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6978bade7b6c0335095bdd0bd97f8f3d590d2877b370f17e03e0865241694eb5"}, + {file = "fonttools-4.48.1-cp38-cp38-win32.whl", hash = "sha256:bcd77f89fc1a6b18428e7a55dde8ef56dae95640293bfb8f4e929929eba5e2a2"}, + {file = "fonttools-4.48.1-cp38-cp38-win_amd64.whl", hash = "sha256:f40441437b039930428e04fb05ac3a132e77458fb57666c808d74a556779e784"}, + {file = "fonttools-4.48.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0d2b01428f7da26f229a5656defc824427b741e454b4e210ad2b25ed6ea2aed4"}, + {file = "fonttools-4.48.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:df48798f9a4fc4c315ab46e17873436c8746f5df6eddd02fad91299b2af7af95"}, + {file = "fonttools-4.48.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2eb4167bde04e172a93cf22c875d8b0cff76a2491f67f5eb069566215302d45d"}, + {file = "fonttools-4.48.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c900508c46274d32d308ae8e82335117f11aaee1f7d369ac16502c9a78930b0a"}, + {file = "fonttools-4.48.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:594206b31c95fcfa65f484385171fabb4ec69f7d2d7f56d27f17db26b7a31814"}, + {file = "fonttools-4.48.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:292922dc356d7f11f5063b4111a8b719efb8faea92a2a88ed296408d449d8c2e"}, + {file = "fonttools-4.48.1-cp39-cp39-win32.whl", hash = "sha256:4709c5bf123ba10eac210d2d5c9027d3f472591d9f1a04262122710fa3d23199"}, + {file = "fonttools-4.48.1-cp39-cp39-win_amd64.whl", hash = "sha256:63c73b9dd56a94a3cbd2f90544b5fca83666948a9e03370888994143b8d7c070"}, + {file = "fonttools-4.48.1-py3-none-any.whl", hash = "sha256:e3e33862fc5261d46d9aae3544acb36203b1a337d00bdb5d3753aae50dac860e"}, + {file = "fonttools-4.48.1.tar.gz", hash = "sha256:8b8a45254218679c7f1127812761e7854ed5c8e34349aebf581e8c9204e7495a"}, +] + +[package.extras] +all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"] +graphite = ["lz4 (>=1.7.4.2)"] +interpolatable = ["munkres", "pycairo", "scipy"] +lxml = ["lxml (>=4.0)"] +pathops = ["skia-pathops (>=0.5.0)"] +plot = ["matplotlib"] +repacker = ["uharfbuzz (>=0.23.0)"] +symfont = ["sympy"] +type1 = ["xattr"] +ufo = ["fs (>=2.2.0,<3)"] +unicode = ["unicodedata2 (>=15.1.0)"] +woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] + +[[package]] +name = "importlib-resources" +version = "6.1.1" +description = "Read resources from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_resources-6.1.1-py3-none-any.whl", hash = "sha256:e8bf90d8213b486f428c9c39714b920041cb02c184686a3dee24905aaa8105d6"}, + {file = "importlib_resources-6.1.1.tar.gz", hash = "sha256:3893a00122eafde6894c59914446a512f728a0c1a45f9bb9b63721b6bacf0b4a"}, +] + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff", "zipp (>=3.17)"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -394,6 +708,198 @@ docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alab qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] +[[package]] +name = "kiwisolver" +version = "1.4.5" +description = "A fast implementation of the Cassowary constraint solver" +optional = false +python-versions = ">=3.7" +files = [ + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af"}, + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3"}, + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b"}, + {file = "kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238"}, + {file = "kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f"}, + {file = "kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac"}, + {file = "kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a"}, + {file = "kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20"}, + {file = "kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-win32.whl", hash = "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-win_amd64.whl", hash = "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250"}, + {file = "kiwisolver-1.4.5-cp38-cp38-win32.whl", hash = "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e"}, + {file = "kiwisolver-1.4.5-cp38-cp38-win_amd64.whl", hash = "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77"}, + {file = "kiwisolver-1.4.5-cp39-cp39-win32.whl", hash = "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f"}, + {file = "kiwisolver-1.4.5-cp39-cp39-win_amd64.whl", hash = "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee"}, + {file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"}, +] + +[[package]] +name = "llvmlite" +version = "0.42.0" +description = "lightweight wrapper around basic LLVM functionality" +optional = false +python-versions = ">=3.9" +files = [ + {file = "llvmlite-0.42.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3366938e1bf63d26c34fbfb4c8e8d2ded57d11e0567d5bb243d89aab1eb56098"}, + {file = "llvmlite-0.42.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c35da49666a21185d21b551fc3caf46a935d54d66969d32d72af109b5e7d2b6f"}, + {file = "llvmlite-0.42.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70f44ccc3c6220bd23e0ba698a63ec2a7d3205da0d848804807f37fc243e3f77"}, + {file = "llvmlite-0.42.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:763f8d8717a9073b9e0246998de89929071d15b47f254c10eef2310b9aac033d"}, + {file = "llvmlite-0.42.0-cp310-cp310-win_amd64.whl", hash = "sha256:8d90edf400b4ceb3a0e776b6c6e4656d05c7187c439587e06f86afceb66d2be5"}, + {file = "llvmlite-0.42.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ae511caed28beaf1252dbaf5f40e663f533b79ceb408c874c01754cafabb9cbf"}, + {file = "llvmlite-0.42.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81e674c2fe85576e6c4474e8c7e7aba7901ac0196e864fe7985492b737dbab65"}, + {file = "llvmlite-0.42.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb3975787f13eb97629052edb5017f6c170eebc1c14a0433e8089e5db43bcce6"}, + {file = "llvmlite-0.42.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5bece0cdf77f22379f19b1959ccd7aee518afa4afbd3656c6365865f84903f9"}, + {file = "llvmlite-0.42.0-cp311-cp311-win_amd64.whl", hash = "sha256:7e0c4c11c8c2aa9b0701f91b799cb9134a6a6de51444eff5a9087fc7c1384275"}, + {file = "llvmlite-0.42.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:08fa9ab02b0d0179c688a4216b8939138266519aaa0aa94f1195a8542faedb56"}, + {file = "llvmlite-0.42.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b2fce7d355068494d1e42202c7aff25d50c462584233013eb4470c33b995e3ee"}, + {file = "llvmlite-0.42.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebe66a86dc44634b59a3bc860c7b20d26d9aaffcd30364ebe8ba79161a9121f4"}, + {file = "llvmlite-0.42.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d47494552559e00d81bfb836cf1c4d5a5062e54102cc5767d5aa1e77ccd2505c"}, + {file = "llvmlite-0.42.0-cp312-cp312-win_amd64.whl", hash = "sha256:05cb7e9b6ce69165ce4d1b994fbdedca0c62492e537b0cc86141b6e2c78d5888"}, + {file = "llvmlite-0.42.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bdd3888544538a94d7ec99e7c62a0cdd8833609c85f0c23fcb6c5c591aec60ad"}, + {file = "llvmlite-0.42.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d0936c2067a67fb8816c908d5457d63eba3e2b17e515c5fe00e5ee2bace06040"}, + {file = "llvmlite-0.42.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a78ab89f1924fc11482209f6799a7a3fc74ddc80425a7a3e0e8174af0e9e2301"}, + {file = "llvmlite-0.42.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7599b65c7af7abbc978dbf345712c60fd596aa5670496561cc10e8a71cebfb2"}, + {file = "llvmlite-0.42.0-cp39-cp39-win_amd64.whl", hash = "sha256:43d65cc4e206c2e902c1004dd5418417c4efa6c1d04df05c6c5675a27e8ca90e"}, + {file = "llvmlite-0.42.0.tar.gz", hash = "sha256:f92b09243c0cc3f457da8b983f67bd8e1295d0f5b3746c7a1861d7a99403854a"}, +] + +[[package]] +name = "matplotlib" +version = "3.8.2" +description = "Python plotting package" +optional = false +python-versions = ">=3.9" +files = [ + {file = "matplotlib-3.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:09796f89fb71a0c0e1e2f4bdaf63fb2cefc84446bb963ecdeb40dfee7dfa98c7"}, + {file = "matplotlib-3.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f9c6976748a25e8b9be51ea028df49b8e561eed7809146da7a47dbecebab367"}, + {file = "matplotlib-3.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b78e4f2cedf303869b782071b55fdde5987fda3038e9d09e58c91cc261b5ad18"}, + {file = "matplotlib-3.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e208f46cf6576a7624195aa047cb344a7f802e113bb1a06cfd4bee431de5e31"}, + {file = "matplotlib-3.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:46a569130ff53798ea5f50afce7406e91fdc471ca1e0e26ba976a8c734c9427a"}, + {file = "matplotlib-3.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:830f00640c965c5b7f6bc32f0d4ce0c36dfe0379f7dd65b07a00c801713ec40a"}, + {file = "matplotlib-3.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d86593ccf546223eb75a39b44c32788e6f6440d13cfc4750c1c15d0fcb850b63"}, + {file = "matplotlib-3.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a5430836811b7652991939012f43d2808a2db9b64ee240387e8c43e2e5578c8"}, + {file = "matplotlib-3.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9576723858a78751d5aacd2497b8aef29ffea6d1c95981505877f7ac28215c6"}, + {file = "matplotlib-3.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ba9cbd8ac6cf422f3102622b20f8552d601bf8837e49a3afed188d560152788"}, + {file = "matplotlib-3.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:03f9d160a29e0b65c0790bb07f4f45d6a181b1ac33eb1bb0dd225986450148f0"}, + {file = "matplotlib-3.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:3773002da767f0a9323ba1a9b9b5d00d6257dbd2a93107233167cfb581f64717"}, + {file = "matplotlib-3.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:4c318c1e95e2f5926fba326f68177dee364aa791d6df022ceb91b8221bd0a627"}, + {file = "matplotlib-3.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:091275d18d942cf1ee9609c830a1bc36610607d8223b1b981c37d5c9fc3e46a4"}, + {file = "matplotlib-3.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b0f3b8ea0e99e233a4bcc44590f01604840d833c280ebb8fe5554fd3e6cfe8d"}, + {file = "matplotlib-3.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b1704a530395aaf73912be741c04d181f82ca78084fbd80bc737be04848331"}, + {file = "matplotlib-3.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533b0e3b0c6768eef8cbe4b583731ce25a91ab54a22f830db2b031e83cca9213"}, + {file = "matplotlib-3.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:0f4fc5d72b75e2c18e55eb32292659cf731d9d5b312a6eb036506304f4675630"}, + {file = "matplotlib-3.8.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:deaed9ad4da0b1aea77fe0aa0cebb9ef611c70b3177be936a95e5d01fa05094f"}, + {file = "matplotlib-3.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:172f4d0fbac3383d39164c6caafd3255ce6fa58f08fc392513a0b1d3b89c4f89"}, + {file = "matplotlib-3.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7d36c2209d9136cd8e02fab1c0ddc185ce79bc914c45054a9f514e44c787917"}, + {file = "matplotlib-3.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5864bdd7da445e4e5e011b199bb67168cdad10b501750367c496420f2ad00843"}, + {file = "matplotlib-3.8.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ef8345b48e95cee45ff25192ed1f4857273117917a4dcd48e3905619bcd9c9b8"}, + {file = "matplotlib-3.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:7c48d9e221b637c017232e3760ed30b4e8d5dfd081daf327e829bf2a72c731b4"}, + {file = "matplotlib-3.8.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aa11b3c6928a1e496c1a79917d51d4cd5d04f8a2e75f21df4949eeefdf697f4b"}, + {file = "matplotlib-3.8.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1095fecf99eeb7384dabad4bf44b965f929a5f6079654b681193edf7169ec20"}, + {file = "matplotlib-3.8.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:bddfb1db89bfaa855912261c805bd0e10218923cc262b9159a49c29a7a1c1afa"}, + {file = "matplotlib-3.8.2.tar.gz", hash = "sha256:01a978b871b881ee76017152f1f1a0cbf6bd5f7b8ff8c96df0df1bd57d8755a1"}, +] + +[package.dependencies] +contourpy = ">=1.0.1" +cycler = ">=0.10" +fonttools = ">=4.22.0" +importlib-resources = {version = ">=3.2.0", markers = "python_version < \"3.10\""} +kiwisolver = ">=1.3.1" +numpy = ">=1.21,<2" +packaging = ">=20.0" +pillow = ">=8" +pyparsing = ">=2.3.1" +python-dateutil = ">=2.7" + [[package]] name = "matplotlib-inline" version = "0.1.6" @@ -419,6 +925,58 @@ files = [ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] +[[package]] +name = "networkx" +version = "3.2.1" +description = "Python package for creating and manipulating graphs and networks" +optional = false +python-versions = ">=3.9" +files = [ + {file = "networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2"}, + {file = "networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6"}, +] + +[package.extras] +default = ["matplotlib (>=3.5)", "numpy (>=1.22)", "pandas (>=1.4)", "scipy (>=1.9,!=1.11.0,!=1.11.1)"] +developer = ["changelist (==0.4)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"] +doc = ["nb2plots (>=0.7)", "nbconvert (<7.9)", "numpydoc (>=1.6)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.14)", "sphinx (>=7)", "sphinx-gallery (>=0.14)", "texext (>=0.6.7)"] +extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.11)", "sympy (>=1.10)"] +test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] + +[[package]] +name = "numba" +version = "0.59.0" +description = "compiling Python code using LLVM" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numba-0.59.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8d061d800473fb8fef76a455221f4ad649a53f5e0f96e3f6c8b8553ee6fa98fa"}, + {file = "numba-0.59.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c086a434e7d3891ce5dfd3d1e7ee8102ac1e733962098578b507864120559ceb"}, + {file = "numba-0.59.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9e20736bf62e61f8353fb71b0d3a1efba636c7a303d511600fc57648b55823ed"}, + {file = "numba-0.59.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e86e6786aec31d2002122199486e10bbc0dc40f78d76364cded375912b13614c"}, + {file = "numba-0.59.0-cp310-cp310-win_amd64.whl", hash = "sha256:0307ee91b24500bb7e64d8a109848baf3a3905df48ce142b8ac60aaa406a0400"}, + {file = "numba-0.59.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d540f69a8245fb714419c2209e9af6104e568eb97623adc8943642e61f5d6d8e"}, + {file = "numba-0.59.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1192d6b2906bf3ff72b1d97458724d98860ab86a91abdd4cfd9328432b661e31"}, + {file = "numba-0.59.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:90efb436d3413809fcd15298c6d395cb7d98184350472588356ccf19db9e37c8"}, + {file = "numba-0.59.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cd3dac45e25d927dcb65d44fb3a973994f5add2b15add13337844afe669dd1ba"}, + {file = "numba-0.59.0-cp311-cp311-win_amd64.whl", hash = "sha256:753dc601a159861808cc3207bad5c17724d3b69552fd22768fddbf302a817a4c"}, + {file = "numba-0.59.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ce62bc0e6dd5264e7ff7f34f41786889fa81a6b860662f824aa7532537a7bee0"}, + {file = "numba-0.59.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8cbef55b73741b5eea2dbaf1b0590b14977ca95a13a07d200b794f8f6833a01c"}, + {file = "numba-0.59.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:70d26ba589f764be45ea8c272caa467dbe882b9676f6749fe6f42678091f5f21"}, + {file = "numba-0.59.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e125f7d69968118c28ec0eed9fbedd75440e64214b8d2eac033c22c04db48492"}, + {file = "numba-0.59.0-cp312-cp312-win_amd64.whl", hash = "sha256:4981659220b61a03c1e557654027d271f56f3087448967a55c79a0e5f926de62"}, + {file = "numba-0.59.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe4d7562d1eed754a7511ed7ba962067f198f86909741c5c6e18c4f1819b1f47"}, + {file = "numba-0.59.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6feb1504bb432280f900deaf4b1dadcee68812209500ed3f81c375cbceab24dc"}, + {file = "numba-0.59.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:944faad25ee23ea9dda582bfb0189fb9f4fc232359a80ab2a028b94c14ce2b1d"}, + {file = "numba-0.59.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5516a469514bfae52a9d7989db4940653a5cbfac106f44cb9c50133b7ad6224b"}, + {file = "numba-0.59.0-cp39-cp39-win_amd64.whl", hash = "sha256:32bd0a41525ec0b1b853da244808f4e5333867df3c43c30c33f89cf20b9c2b63"}, + {file = "numba-0.59.0.tar.gz", hash = "sha256:12b9b064a3e4ad00e2371fc5212ef0396c80f41caec9b5ec391c8b04b6eaf2a8"}, +] + +[package.dependencies] +llvmlite = "==0.42.*" +numpy = ">=1.22,<1.27" + [[package]] name = "numpy" version = "1.26.4" @@ -515,6 +1073,91 @@ files = [ {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, ] +[[package]] +name = "pillow" +version = "10.2.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pillow-10.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:7823bdd049099efa16e4246bdf15e5a13dbb18a51b68fa06d6c1d4d8b99a796e"}, + {file = "pillow-10.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:83b2021f2ade7d1ed556bc50a399127d7fb245e725aa0113ebd05cfe88aaf588"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fad5ff2f13d69b7e74ce5b4ecd12cc0ec530fcee76356cac6742785ff71c452"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da2b52b37dad6d9ec64e653637a096905b258d2fc2b984c41ae7d08b938a67e4"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:47c0995fc4e7f79b5cfcab1fc437ff2890b770440f7696a3ba065ee0fd496563"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:322bdf3c9b556e9ffb18f93462e5f749d3444ce081290352c6070d014c93feb2"}, + {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:51f1a1bffc50e2e9492e87d8e09a17c5eea8409cda8d3f277eb6edc82813c17c"}, + {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69ffdd6120a4737710a9eee73e1d2e37db89b620f702754b8f6e62594471dee0"}, + {file = "pillow-10.2.0-cp310-cp310-win32.whl", hash = "sha256:c6dafac9e0f2b3c78df97e79af707cdc5ef8e88208d686a4847bab8266870023"}, + {file = "pillow-10.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:aebb6044806f2e16ecc07b2a2637ee1ef67a11840a66752751714a0d924adf72"}, + {file = "pillow-10.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:7049e301399273a0136ff39b84c3678e314f2158f50f517bc50285fb5ec847ad"}, + {file = "pillow-10.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35bb52c37f256f662abdfa49d2dfa6ce5d93281d323a9af377a120e89a9eafb5"}, + {file = "pillow-10.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c23f307202661071d94b5e384e1e1dc7dfb972a28a2310e4ee16103e66ddb67"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:773efe0603db30c281521a7c0214cad7836c03b8ccff897beae9b47c0b657d61"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11fa2e5984b949b0dd6d7a94d967743d87c577ff0b83392f17cb3990d0d2fd6e"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:716d30ed977be8b37d3ef185fecb9e5a1d62d110dfbdcd1e2a122ab46fddb03f"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a086c2af425c5f62a65e12fbf385f7c9fcb8f107d0849dba5839461a129cf311"}, + {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c8de2789052ed501dd829e9cae8d3dcce7acb4777ea4a479c14521c942d395b1"}, + {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:609448742444d9290fd687940ac0b57fb35e6fd92bdb65386e08e99af60bf757"}, + {file = "pillow-10.2.0-cp311-cp311-win32.whl", hash = "sha256:823ef7a27cf86df6597fa0671066c1b596f69eba53efa3d1e1cb8b30f3533068"}, + {file = "pillow-10.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:1da3b2703afd040cf65ec97efea81cfba59cdbed9c11d8efc5ab09df9509fc56"}, + {file = "pillow-10.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:edca80cbfb2b68d7b56930b84a0e45ae1694aeba0541f798e908a49d66b837f1"}, + {file = "pillow-10.2.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:1b5e1b74d1bd1b78bc3477528919414874748dd363e6272efd5abf7654e68bef"}, + {file = "pillow-10.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0eae2073305f451d8ecacb5474997c08569fb4eb4ac231ffa4ad7d342fdc25ac"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7c2286c23cd350b80d2fc9d424fc797575fb16f854b831d16fd47ceec078f2c"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e23412b5c41e58cec602f1135c57dfcf15482013ce6e5f093a86db69646a5aa"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:52a50aa3fb3acb9cf7213573ef55d31d6eca37f5709c69e6858fe3bc04a5c2a2"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:127cee571038f252a552760076407f9cff79761c3d436a12af6000cd182a9d04"}, + {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8d12251f02d69d8310b046e82572ed486685c38f02176bd08baf216746eb947f"}, + {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54f1852cd531aa981bc0965b7d609f5f6cc8ce8c41b1139f6ed6b3c54ab82bfb"}, + {file = "pillow-10.2.0-cp312-cp312-win32.whl", hash = "sha256:257d8788df5ca62c980314053197f4d46eefedf4e6175bc9412f14412ec4ea2f"}, + {file = "pillow-10.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:154e939c5f0053a383de4fd3d3da48d9427a7e985f58af8e94d0b3c9fcfcf4f9"}, + {file = "pillow-10.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:f379abd2f1e3dddb2b61bc67977a6b5a0a3f7485538bcc6f39ec76163891ee48"}, + {file = "pillow-10.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8373c6c251f7ef8bda6675dd6d2b3a0fcc31edf1201266b5cf608b62a37407f9"}, + {file = "pillow-10.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:870ea1ada0899fd0b79643990809323b389d4d1d46c192f97342eeb6ee0b8483"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4b6b1e20608493548b1f32bce8cca185bf0480983890403d3b8753e44077129"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3031709084b6e7852d00479fd1d310b07d0ba82765f973b543c8af5061cf990e"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:3ff074fc97dd4e80543a3e91f69d58889baf2002b6be64347ea8cf5533188213"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:cb4c38abeef13c61d6916f264d4845fab99d7b711be96c326b84df9e3e0ff62d"}, + {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b1b3020d90c2d8e1dae29cf3ce54f8094f7938460fb5ce8bc5c01450b01fbaf6"}, + {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:170aeb00224ab3dc54230c797f8404507240dd868cf52066f66a41b33169bdbe"}, + {file = "pillow-10.2.0-cp38-cp38-win32.whl", hash = "sha256:c4225f5220f46b2fde568c74fca27ae9771536c2e29d7c04f4fb62c83275ac4e"}, + {file = "pillow-10.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:0689b5a8c5288bc0504d9fcee48f61a6a586b9b98514d7d29b840143d6734f39"}, + {file = "pillow-10.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b792a349405fbc0163190fde0dc7b3fef3c9268292586cf5645598b48e63dc67"}, + {file = "pillow-10.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c570f24be1e468e3f0ce7ef56a89a60f0e05b30a3669a459e419c6eac2c35364"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8ecd059fdaf60c1963c58ceb8997b32e9dc1b911f5da5307aab614f1ce5c2fb"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c365fd1703040de1ec284b176d6af5abe21b427cb3a5ff68e0759e1e313a5e7e"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:70c61d4c475835a19b3a5aa42492409878bbca7438554a1f89d20d58a7c75c01"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b6f491cdf80ae540738859d9766783e3b3c8e5bd37f5dfa0b76abdecc5081f13"}, + {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d189550615b4948f45252d7f005e53c2040cea1af5b60d6f79491a6e147eef7"}, + {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:49d9ba1ed0ef3e061088cd1e7538a0759aab559e2e0a80a36f9fd9d8c0c21591"}, + {file = "pillow-10.2.0-cp39-cp39-win32.whl", hash = "sha256:babf5acfede515f176833ed6028754cbcd0d206f7f614ea3447d67c33be12516"}, + {file = "pillow-10.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:0304004f8067386b477d20a518b50f3fa658a28d44e4116970abfcd94fac34a8"}, + {file = "pillow-10.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:0fb3e7fc88a14eacd303e90481ad983fd5b69c761e9e6ef94c983f91025da869"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:322209c642aabdd6207517e9739c704dc9f9db943015535783239022002f054a"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3eedd52442c0a5ff4f887fab0c1c0bb164d8635b32c894bc1faf4c618dd89df2"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb28c753fd5eb3dd859b4ee95de66cc62af91bcff5db5f2571d32a520baf1f04"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:33870dc4653c5017bf4c8873e5488d8f8d5f8935e2f1fb9a2208c47cdd66efd2"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3c31822339516fb3c82d03f30e22b1d038da87ef27b6a78c9549888f8ceda39a"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a2b56ba36e05f973d450582fb015594aaa78834fefe8dfb8fcd79b93e64ba4c6"}, + {file = "pillow-10.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d8e6aeb9201e655354b3ad049cb77d19813ad4ece0df1249d3c793de3774f8c7"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:2247178effb34a77c11c0e8ac355c7a741ceca0a732b27bf11e747bbc950722f"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15587643b9e5eb26c48e49a7b33659790d28f190fc514a322d55da2fb5c2950e"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753cd8f2086b2b80180d9b3010dd4ed147efc167c90d3bf593fe2af21265e5a5"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7c8f97e8e7a9009bcacbe3766a36175056c12f9a44e6e6f2d5caad06dcfbf03b"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d1b35bcd6c5543b9cb547dee3150c93008f8dd0f1fef78fc0cd2b141c5baf58a"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe4c15f6c9285dc54ce6553a3ce908ed37c8f3825b5a51a15c91442bb955b868"}, + {file = "pillow-10.2.0.tar.gz", hash = "sha256:e87f0b2c78157e12d7686b27d63c070fd65d994e8ddae6f328e0dcf4a0cd007e"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] + [[package]] name = "platformdirs" version = "4.2.0" @@ -559,6 +1202,34 @@ files = [ [package.dependencies] wcwidth = "*" +[[package]] +name = "psutil" +version = "5.9.8" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, + {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, + {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, + {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, + {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, + {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, + {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, + {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, + {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, + {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + [[package]] name = "ptyprocess" version = "0.7.0" @@ -615,6 +1286,20 @@ typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\"" spelling = ["pyenchant (>=3.2,<4.0)"] testutils = ["gitpython (>3)"] +[[package]] +name = "pyparsing" +version = "3.1.1" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb"}, + {file = "pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + [[package]] name = "pytest" version = "8.0.0" @@ -673,6 +1358,91 @@ tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} [package.extras] test = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "pytest-mock (>=3.12)"] +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "quimb" +version = "1.7.2" +description = "Quantum information and many-body library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "quimb-1.7.2-py3-none-any.whl", hash = "sha256:77dd7e70f93f4a9dca7d4c9ce5e6e2a42ba01849292bebb506c7e58aeff6db17"}, + {file = "quimb-1.7.2.tar.gz", hash = "sha256:8c04f173e010551f686cce1e02d5215e4c0770db0966161270c74663bef59128"}, +] + +[package.dependencies] +autoray = ">=0.6.7" +cotengra = ">=0.5.6" +cytoolz = ">=0.8.0" +matplotlib = {version = ">=2.0", optional = true, markers = "extra == \"tensor\""} +networkx = {version = ">=2.3", optional = true, markers = "extra == \"tensor\""} +numba = ">=0.39" +numpy = ">=1.17" +psutil = ">=4.3.1" +scipy = ">=1.0.0" +tqdm = ">=4" + +[package.extras] +advanced-solvers = ["mpi4py", "petsc4py", "slepc4py"] +docs = ["astroid (<3.0.0)", "autoray (>=0.6.7)", "cotengra (>=0.5.3)", "doc2dash (>=2.4.1)", "furo", "ipython (!=8.7.0)", "myst-nb", "setuptools-scm", "sphinx (>=2.0)", "sphinx-autoapi", "sphinx-copybutton", "sphinx-design"] +tensor = ["matplotlib (>=2.0)", "networkx (>=2.3)"] +tests = ["coverage", "pytest", "pytest-cov"] + +[[package]] +name = "scipy" +version = "1.12.0" +description = "Fundamental algorithms for scientific computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "scipy-1.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:78e4402e140879387187f7f25d91cc592b3501a2e51dfb320f48dfb73565f10b"}, + {file = "scipy-1.12.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:f5f00ebaf8de24d14b8449981a2842d404152774c1a1d880c901bf454cb8e2a1"}, + {file = "scipy-1.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e53958531a7c695ff66c2e7bb7b79560ffdc562e2051644c5576c39ff8efb563"}, + {file = "scipy-1.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e32847e08da8d895ce09d108a494d9eb78974cf6de23063f93306a3e419960c"}, + {file = "scipy-1.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4c1020cad92772bf44b8e4cdabc1df5d87376cb219742549ef69fc9fd86282dd"}, + {file = "scipy-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:75ea2a144096b5e39402e2ff53a36fecfd3b960d786b7efd3c180e29c39e53f2"}, + {file = "scipy-1.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:408c68423f9de16cb9e602528be4ce0d6312b05001f3de61fe9ec8b1263cad08"}, + {file = "scipy-1.12.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5adfad5dbf0163397beb4aca679187d24aec085343755fcdbdeb32b3679f254c"}, + {file = "scipy-1.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3003652496f6e7c387b1cf63f4bb720951cfa18907e998ea551e6de51a04467"}, + {file = "scipy-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b8066bce124ee5531d12a74b617d9ac0ea59245246410e19bca549656d9a40a"}, + {file = "scipy-1.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8bee4993817e204d761dba10dbab0774ba5a8612e57e81319ea04d84945375ba"}, + {file = "scipy-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a24024d45ce9a675c1fb8494e8e5244efea1c7a09c60beb1eeb80373d0fecc70"}, + {file = "scipy-1.12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e7e76cc48638228212c747ada851ef355c2bb5e7f939e10952bc504c11f4e372"}, + {file = "scipy-1.12.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:f7ce148dffcd64ade37b2df9315541f9adad6efcaa86866ee7dd5db0c8f041c3"}, + {file = "scipy-1.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c39f92041f490422924dfdb782527a4abddf4707616e07b021de33467f917bc"}, + {file = "scipy-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7ebda398f86e56178c2fa94cad15bf457a218a54a35c2a7b4490b9f9cb2676c"}, + {file = "scipy-1.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:95e5c750d55cf518c398a8240571b0e0782c2d5a703250872f36eaf737751338"}, + {file = "scipy-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e646d8571804a304e1da01040d21577685ce8e2db08ac58e543eaca063453e1c"}, + {file = "scipy-1.12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:913d6e7956c3a671de3b05ccb66b11bc293f56bfdef040583a7221d9e22a2e35"}, + {file = "scipy-1.12.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba1b0c7256ad75401c73e4b3cf09d1f176e9bd4248f0d3112170fb2ec4db067"}, + {file = "scipy-1.12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:730badef9b827b368f351eacae2e82da414e13cf8bd5051b4bdfd720271a5371"}, + {file = "scipy-1.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6546dc2c11a9df6926afcbdd8a3edec28566e4e785b915e849348c6dd9f3f490"}, + {file = "scipy-1.12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:196ebad3a4882081f62a5bf4aeb7326aa34b110e533aab23e4374fcccb0890dc"}, + {file = "scipy-1.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:b360f1b6b2f742781299514e99ff560d1fe9bd1bff2712894b52abe528d1fd1e"}, + {file = "scipy-1.12.0.tar.gz", hash = "sha256:4bf5abab8a36d20193c698b0f1fc282c1d083c94723902c447e5d2f1780936a3"}, +] + +[package.dependencies] +numpy = ">=1.22.4,<1.29.0" + +[package.extras] +dev = ["click", "cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] +doc = ["jupytext", "matplotlib (>2)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-design (>=0.2.0)"] +test = ["asv", "gmpy2", "hypothesis", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] + [[package]] name = "setuptools" version = "69.0.3" @@ -689,6 +1459,17 @@ docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + [[package]] name = "tomli" version = "2.0.1" @@ -711,6 +1492,37 @@ files = [ {file = "tomlkit-0.12.3.tar.gz", hash = "sha256:75baf5012d06501f07bee5bf8e801b9f343e7aac5a92581f20f80ce632e6b5a4"}, ] +[[package]] +name = "toolz" +version = "0.12.1" +description = "List processing tools and functional utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "toolz-0.12.1-py3-none-any.whl", hash = "sha256:d22731364c07d72eea0a0ad45bafb2c2937ab6fd38a3507bf55eae8744aa7d85"}, + {file = "toolz-0.12.1.tar.gz", hash = "sha256:ecca342664893f177a13dac0e6b41cbd8ac25a358e5f215316d43e2100224f4d"}, +] + +[[package]] +name = "tqdm" +version = "4.66.1" +description = "Fast, Extensible Progress Meter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tqdm-4.66.1-py3-none-any.whl", hash = "sha256:d302b3c5b53d47bce91fea46679d9c3c6508cf6332229aa1e7d8653723793386"}, + {file = "tqdm-4.66.1.tar.gz", hash = "sha256:d88e651f9db8d8551a62556d3cff9e3034274ca5d66e93197cf2490e2dcb69c7"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + [[package]] name = "traitlets" version = "5.14.1" @@ -748,7 +1560,22 @@ files = [ {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, ] +[[package]] +name = "zipp" +version = "3.17.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, + {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] + [metadata] lock-version = "2.0" python-versions = "^3.9,<3.13" -content-hash = "96c82bd153e6b4217355830d8104ba480cc360e2d73ec5e064c8fc3ffbe5d813" +content-hash = "ba2a028b834fd2459bed074b1b2769fdfbf5a6db14c3daecf80d8af115216da6" diff --git a/pyproject.toml b/pyproject.toml index 2a8824ad..5afd5642 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,22 +11,29 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.9,<3.13" -qibojit = "^0.0.7" -quimb = { version = "^0.0.7", extras = ["tensor"] } +quimb = { version = "^1.6.0", extras = ["tensor"] } [tool.poetry.group.dev.dependencies] -ipython = "^7" +ipython = "^7.0.0" +[tool.poetry.group.tests] +optional = true + [tool.poetry.group.tests.dependencies] pytest = "^8.0.0" pytest-cov = "^4.1.0" pytest-env = "^1.1.3" +[tool.poetry.group.analysis] +optional = true [tool.poetry.group.analysis.dependencies] pylint = "^3.0.3" +[tool.poetry.group.cuda] +optional = true + [tool.poetry.group.cuda.dependencies] cupy = "^11.6.0" cuquantum-python-cu11 = "^23.3.0" From b6752dd5f7b5d38df1615f9487312fe0008e1787 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 9 Feb 2024 03:07:54 +0100 Subject: [PATCH 176/199] fix: Turn cuda into a pip extra (from a Poetry group) --- flake.nix | 1 - poetry.lock | 23 +++++++++++++---------- pyproject.toml | 6 +++++- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/flake.nix b/flake.nix index 0285b22b..df6f7b5f 100644 --- a/flake.nix +++ b/flake.nix @@ -45,7 +45,6 @@ enable = true; install.enable = true; install.groups = ["dev" "tests"]; - install.allExtras = true; }; version = "3.11"; }; diff --git a/poetry.lock b/poetry.lock index d81be41d..30a8831b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -215,7 +215,7 @@ toml = ["tomli"] name = "cupy" version = "11.6.0" description = "CuPy: NumPy & SciPy for GPU" -optional = false +optional = true python-versions = ">=3.7" files = [ {file = "cupy-11.6.0.tar.gz", hash = "sha256:53dbb840072bb32d4bfbaa6bfa072365a30c98b1fcd1f43e48969071ad98f1a7"}, @@ -234,7 +234,7 @@ test = ["hypothesis (>=6.37.2,<6.55.0)", "pytest (>=7.2)"] name = "cuquantum-python-cu11" version = "23.10.0" description = "NVIDIA cuQuantum Python" -optional = false +optional = true python-versions = ">=3.9" files = [ {file = "cuquantum_python_cu11-23.10.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a0f0dfcb6239ec5fce836fa2f641820d3235ac7d83f391eac90952cf481da03f"}, @@ -254,7 +254,7 @@ numpy = ">=1.21,<2.0" name = "custatevec-cu11" version = "1.5.0" description = "cuStateVec - a component of NVIDIA cuQuantum SDK" -optional = false +optional = true python-versions = "*" files = [ {file = "custatevec_cu11-1.5.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:3e0cbbc487a0590d9f889ad70b9ac21d88c1f555f6fe01b18ba687a0d98d902f"}, @@ -265,7 +265,7 @@ files = [ name = "cutensor-cu11" version = "1.7.0" description = "NVIDIA cuTENSOR" -optional = false +optional = true python-versions = "*" files = [ {file = "cutensor_cu11-1.7.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:c5598670f4f31906d725f5ea852f0df675522e3ff5a7bf886057eab36497062d"}, @@ -277,7 +277,7 @@ files = [ name = "cutensornet-cu11" version = "2.3.0" description = "cuTensorNet - a component of NVIDIA cuQuantum SDK" -optional = false +optional = true python-versions = "*" files = [ {file = "cutensornet_cu11-2.3.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:fd15b199b189f0501b3fdaa576ee70eedbdfec37b557f7fd56b97aaa5e618667"}, @@ -465,7 +465,7 @@ test = ["pytest (>=6)"] name = "fastrlock" version = "0.8.2" description = "Fast, re-entrant optimistic lock implemented in Cython" -optional = false +optional = true python-versions = "*" files = [ {file = "fastrlock-0.8.2-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:94e348c72a1fd1f8191f25ea056448e4f5a87b8fbf005b39d290dcb0581a48cd"}, @@ -1374,13 +1374,13 @@ six = ">=1.5" [[package]] name = "quimb" -version = "1.7.2" +version = "1.7.3" description = "Quantum information and many-body library." optional = false python-versions = ">=3.8" files = [ - {file = "quimb-1.7.2-py3-none-any.whl", hash = "sha256:77dd7e70f93f4a9dca7d4c9ce5e6e2a42ba01849292bebb506c7e58aeff6db17"}, - {file = "quimb-1.7.2.tar.gz", hash = "sha256:8c04f173e010551f686cce1e02d5215e4c0770db0966161270c74663bef59128"}, + {file = "quimb-1.7.3-py3-none-any.whl", hash = "sha256:5fb94a562808712c09f4ee52f012fbf1bbab286ea4aaab5c251570d859afcbc3"}, + {file = "quimb-1.7.3.tar.gz", hash = "sha256:f7157270b9ace44aedbeb24769a5413f2b66e6df1b52fb23374c33108760b7a4"}, ] [package.dependencies] @@ -1575,7 +1575,10 @@ files = [ docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] +[extras] +cuda = ["cupy", "cuquantum-python-cu11"] + [metadata] lock-version = "2.0" python-versions = "^3.9,<3.13" -content-hash = "ba2a028b834fd2459bed074b1b2769fdfbf5a6db14c3daecf80d8af115216da6" +content-hash = "2bd83be8134c6922dcb957ec105289c83459ae2d7ea6d152d3879780e22c3857" diff --git a/pyproject.toml b/pyproject.toml index 5afd5642..e4b54482 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,11 +12,15 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.9,<3.13" quimb = { version = "^1.6.0", extras = ["tensor"] } +cupy = { version = "^11.6.0", optional = true } +cuquantum-python-cu11 = { version = "^23.3.0", optional = true } + +[tool.poetry.extras] +cuda = ["cupy", "cuquantum-python-cu11"] [tool.poetry.group.dev.dependencies] ipython = "^7.0.0" - [tool.poetry.group.tests] optional = true From 2e1d943ec3fd233a8c264b9f0d2fad3481a476b0 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 9 Feb 2024 03:09:04 +0100 Subject: [PATCH 177/199] fix: Add Qibo dependency in Poetry --- poetry.lock | 159 +++++++++++++++++++++++++++++++++++++++++++++++-- pyproject.toml | 3 +- 2 files changed, 157 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 30a8831b..6fabdcb2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -51,6 +51,35 @@ files = [ {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, ] +[[package]] +name = "cloudpickle" +version = "3.0.0" +description = "Pickler class to extend the standard pickle.Pickler functionality" +optional = false +python-versions = ">=3.8" +files = [ + {file = "cloudpickle-3.0.0-py3-none-any.whl", hash = "sha256:246ee7d0c295602a036e86369c77fecda4ab17b506496730f2f576d9016fd9c7"}, + {file = "cloudpickle-3.0.0.tar.gz", hash = "sha256:996d9a482c6fb4f33c1a35335cf8afd065d2a56e973270364840712d9131a882"}, +] + +[[package]] +name = "cma" +version = "3.3.0" +description = "CMA-ES, Covariance Matrix Adaptation Evolution Strategy for non-linear numerical optimization in Python" +optional = false +python-versions = "*" +files = [ + {file = "cma-3.3.0-py3-none-any.whl", hash = "sha256:5cc571b1e2068fcf1c538be36f8f3a870107456fed22ce81c1345a96329e61db"}, + {file = "cma-3.3.0.tar.gz", hash = "sha256:b748b8e03f4e7ae816157d7b9bb2fc6b1fb2fee1d5fd3399329b646bb75861ec"}, +] + +[package.dependencies] +numpy = "*" + +[package.extras] +constrained-solution-tracking = ["moarchiving"] +plotting = ["matplotlib"] + [[package]] name = "colorama" version = "0.4.6" @@ -610,6 +639,43 @@ ufo = ["fs (>=2.2.0,<3)"] unicode = ["unicodedata2 (>=15.1.0)"] woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] +[[package]] +name = "future" +version = "0.18.3" +description = "Clean single-source support for Python 3 and 2" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "future-0.18.3.tar.gz", hash = "sha256:34a17436ed1e96697a86f9de3d15a3b0be01d8bc8de9c1dffd59fb8234ed5307"}, +] + +[[package]] +name = "hyperopt" +version = "0.2.7" +description = "Distributed Asynchronous Hyperparameter Optimization" +optional = false +python-versions = "*" +files = [ + {file = "hyperopt-0.2.7-py2.py3-none-any.whl", hash = "sha256:f3046d91fe4167dbf104365016596856b2524a609d22f047a066fc1ac796427c"}, + {file = "hyperopt-0.2.7.tar.gz", hash = "sha256:1bf89ae58050bbd32c7307199046117feee245c2fd9ab6255c7308522b7ca149"}, +] + +[package.dependencies] +cloudpickle = "*" +future = "*" +networkx = ">=2.2" +numpy = "*" +py4j = "*" +scipy = "*" +six = "*" +tqdm = "*" + +[package.extras] +atpe = ["lightgbm", "scikit-learn"] +dev = ["black", "nose", "pre-commit", "pytest"] +mongotrials = ["pymongo"] +sparktrials = ["pyspark"] + [[package]] name = "importlib-resources" version = "6.1.1" @@ -708,6 +774,17 @@ docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alab qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] +[[package]] +name = "joblib" +version = "1.3.2" +description = "Lightweight pipelining with Python functions" +optional = false +python-versions = ">=3.7" +files = [ + {file = "joblib-1.3.2-py3-none-any.whl", hash = "sha256:ef4331c65f239985f3f2220ecc87db222f08fd22097a3dd5698f693875f8cbb9"}, + {file = "joblib-1.3.2.tar.gz", hash = "sha256:92f865e621e17784e7955080b6d042489e3b8e294949cc44c6eac304f59772b1"}, +] + [[package]] name = "kiwisolver" version = "1.4.5" @@ -925,6 +1002,23 @@ files = [ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] +[[package]] +name = "mpmath" +version = "1.3.0" +description = "Python library for arbitrary-precision floating-point arithmetic" +optional = false +python-versions = "*" +files = [ + {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, + {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, +] + +[package.extras] +develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"] +docs = ["sphinx"] +gmpy = ["gmpy2 (>=2.1.0a4)"] +tests = ["pytest (>=4.6)"] + [[package]] name = "networkx" version = "3.2.1" @@ -1241,6 +1335,17 @@ files = [ {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, ] +[[package]] +name = "py4j" +version = "0.10.9.7" +description = "Enables Python programs to dynamically access arbitrary Java objects" +optional = false +python-versions = "*" +files = [ + {file = "py4j-0.10.9.7-py2.py3-none-any.whl", hash = "sha256:85defdfd2b2376eb3abf5ca6474b51ab7e0de341c75a02f46dc9b5976f5a5c1b"}, + {file = "py4j-0.10.9.7.tar.gz", hash = "sha256:0b6e5315bb3ada5cf62ac651d107bb2ebc02def3dee9d9548e3baac644ea8dbb"}, +] + [[package]] name = "pygments" version = "2.17.2" @@ -1272,8 +1377,7 @@ astroid = ">=3.0.1,<=3.1.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ {version = ">=0.2", markers = "python_version < \"3.11\""}, - {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, ] isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" mccabe = ">=0.6,<0.8" @@ -1372,6 +1476,25 @@ files = [ [package.dependencies] six = ">=1.5" +[[package]] +name = "qibo" +version = "0.2.4" +description = "A framework for quantum computing with hardware acceleration." +optional = false +python-versions = ">=3.9,<3.12" +files = [ + {file = "qibo-0.2.4-py3-none-any.whl", hash = "sha256:5aaf7693004d8106eff3cc614e20ff03e7016742ab129e7ece76c84b3deb7366"}, + {file = "qibo-0.2.4.tar.gz", hash = "sha256:8ab8519b107fdfa57a7aa19d9243403437ceb4a776454816ce3071a00bdc15ff"}, +] + +[package.dependencies] +cma = ">=3.3.0,<4.0.0" +hyperopt = ">=0.2.7,<0.3.0" +joblib = ">=1.2.0,<2.0.0" +scipy = ">=1.10.1,<2.0.0" +sympy = ">=1.11.1,<2.0.0" +tabulate = ">=0.9.0,<0.10.0" + [[package]] name = "quimb" version = "1.7.3" @@ -1470,6 +1593,34 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] +[[package]] +name = "sympy" +version = "1.12" +description = "Computer algebra system (CAS) in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sympy-1.12-py3-none-any.whl", hash = "sha256:c3588cd4295d0c0f603d0f2ae780587e64e2efeedb3521e46b9bb1d08d184fa5"}, + {file = "sympy-1.12.tar.gz", hash = "sha256:ebf595c8dac3e0fdc4152c51878b498396ec7f30e7a914d6071e674d49420fb8"}, +] + +[package.dependencies] +mpmath = ">=0.19" + +[[package]] +name = "tabulate" +version = "0.9.0" +description = "Pretty-print tabular data" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, + {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, +] + +[package.extras] +widechars = ["wcwidth"] + [[package]] name = "tomli" version = "2.0.1" @@ -1580,5 +1731,5 @@ cuda = ["cupy", "cuquantum-python-cu11"] [metadata] lock-version = "2.0" -python-versions = "^3.9,<3.13" -content-hash = "2bd83be8134c6922dcb957ec105289c83459ae2d7ea6d152d3879780e22c3857" +python-versions = "^3.9,<3.12" +content-hash = "b912fdfb4ed36b7a8475c7915d99c52af2944305c4c5b1ad9191a4cecf6a3862" diff --git a/pyproject.toml b/pyproject.toml index e4b54482..66bed8f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,8 @@ authors = ["Qibo Team"] readme = "README.md" [tool.poetry.dependencies] -python = "^3.9,<3.13" +python = "^3.9,<3.12" +qibo = "^0.2.4" quimb = { version = "^1.6.0", extras = ["tensor"] } cupy = { version = "^11.6.0", optional = true } cuquantum-python-cu11 = { version = "^23.3.0", optional = true } From e9702c1136f0426d058e28d98768fbfa1bfd159d Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 9 Feb 2024 03:13:22 +0100 Subject: [PATCH 178/199] build: Standardize metadata --- pyproject.toml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 66bed8f0..b4124da3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,9 +5,19 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "qibotn" version = "0.0.1" -description = "" -authors = ["Qibo Team"] +description = "A tensor-network translation module for Qibo" +authors = ["The Qibo team"] +license = "Apache License 2.0" readme = "README.md" +homepage = "https://qibo.science/" +repository = "https://github.com/qiboteam/qibotn/" +documentation = "https://qibo.science/docs/qibotn/stable" +keywords = [] +classifiers = [ + "Programming Language :: Python :: 3", + "Topic :: Scientific/Engineering :: Physics", +] +packages = [{ include = "qibotn", from = "src" }] [tool.poetry.dependencies] python = "^3.9,<3.12" From 4e30c8e344efc85f1423afdbadfd6951a3cb4a62 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 16 Feb 2024 08:02:56 +0100 Subject: [PATCH 179/199] build: Remove cuda poetry group Since redundant with the corresponding extra, that is the appropriate space for it --- pyproject.toml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b4124da3..d2331d24 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,13 +46,6 @@ optional = true [tool.poetry.group.analysis.dependencies] pylint = "^3.0.3" -[tool.poetry.group.cuda] -optional = true - -[tool.poetry.group.cuda.dependencies] -cupy = "^11.6.0" -cuquantum-python-cu11 = "^23.3.0" - [tool.pylint.main] ignored-modules = ["cupy", "cuquantum", "mpi4py"] From 8daa52c6ff1e421334a5ddc49212f324738349c2 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 16 Feb 2024 08:04:00 +0100 Subject: [PATCH 180/199] build: Remove setup.py, replaced by poetry build --- setup.py | 65 -------------------------------------------------------- 1 file changed, 65 deletions(-) delete mode 100644 setup.py diff --git a/setup.py b/setup.py deleted file mode 100644 index 0f619a5c..00000000 --- a/setup.py +++ /dev/null @@ -1,65 +0,0 @@ -import pathlib -import re - -from setuptools import find_packages, setup - -HERE = pathlib.Path(__file__).parent.absolute() -PACKAGE = "qibotn" - - -# Returns the qibotn version -def version(): - """Gets the version from the package's __init__ file if there is some - problem, let it happily fail.""" - version_file = HERE / "src" / PACKAGE / "__init__.py" - version_regex = r"^__version__ = ['\"]([^'\"]*)['\"]" - - initfile = version_file.read_text(encoding="utf-8") - matched = re.search(version_regex, initfile, re.M) - - if matched is not None: - return matched.group(1) - return "0.0.0" - - -# load long description from README -setup( - name="qibotn", - version=version(), - description="A tensor-network translation module for quantum computing", - author="The Qibo team", - author_email="", - url="https://github.com/qiboteam/qibotn", - packages=find_packages("src"), - package_dir={"": "src"}, - package_data={"": ["*.out", "*.yml"]}, - include_package_data=True, - zip_safe=False, - classifiers=[ - "Programming Language :: Python :: 3", - "Topic :: Scientific/Engineering :: Physics", - ], - install_requires=[ - "qibo>=0.1.10", - "qibojit>=0.0.7", - "quimb[tensor]>=1.6.0", - ], - extras_require={ - "docs": [], - "tests": [ - "pytest>=7.2.0", - "pytest-cov>=4.0.0", - "pytest-env>=0.8.1", - ], - "analysis": [ - "pylint>=2.16.0", - ], - "cuda": [ - "cupy>=11.6.0", - "cuquantum-python-cu11>=23.3.0", - ], - }, - python_requires=">=3.8.0", - long_description=(HERE / "README.md").read_text(encoding="utf-8"), - long_description_content_type="text/markdown", -) From bea3af6f77cf03fe442158923e15b9507badd2d4 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Fri, 16 Feb 2024 15:41:46 +0800 Subject: [PATCH 181/199] Make datatype a constant dict --- src/qibotn/backends/cutensornet.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/qibotn/backends/cutensornet.py b/src/qibotn/backends/cutensornet.py index 78ee5b6f..022b8afc 100644 --- a/src/qibotn/backends/cutensornet.py +++ b/src/qibotn/backends/cutensornet.py @@ -2,6 +2,18 @@ from qibo.backends.numpy import NumpyBackend from qibo.config import raise_error from qibo.result import CircuitResult +import cuquantum # pylint: disable=import-error + +CUDA_TYPES = { + "complex64": ( + cuquantum.cudaDataType.CUDA_C_32F, + cuquantum.ComputeType.COMPUTE_32F, + ), + "complex128": ( + cuquantum.cudaDataType.CUDA_C_64F, + cuquantum.ComputeType.COMPUTE_64F, + ), +} class CuTensorNet(NumpyBackend): # pragma: no cover @@ -9,7 +21,6 @@ class CuTensorNet(NumpyBackend): # pragma: no cover def __init__(self, runcard): super().__init__() - import cuquantum # pylint: disable=import-error from cuquantum import cutensornet as cutn # pylint: disable=import-error if runcard is not None: @@ -81,16 +92,8 @@ def set_precision(self, precision): super().set_precision(precision) def cuda_type(self, dtype="complex64"): - if dtype == "complex128": - return ( - self.cuquantum.cudaDataType.CUDA_C_64F, - self.cuquantum.ComputeType.COMPUTE_64F, - ) - elif dtype == "complex64": - return ( - self.cuquantum.cudaDataType.CUDA_C_32F, - self.cuquantum.ComputeType.COMPUTE_32F, - ) + if dtype in CUDA_TYPES: + return CUDA_TYPES[dtype] else: raise TypeError("Type can be either complex64 or complex128") From c21935a71f5a95d6e929b25ff38f872d02597301 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 16 Feb 2024 07:42:08 +0000 Subject: [PATCH 182/199] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qibotn/backends/cutensornet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/backends/cutensornet.py b/src/qibotn/backends/cutensornet.py index 022b8afc..104b41cc 100644 --- a/src/qibotn/backends/cutensornet.py +++ b/src/qibotn/backends/cutensornet.py @@ -1,8 +1,8 @@ +import cuquantum # pylint: disable=import-error import numpy as np from qibo.backends.numpy import NumpyBackend from qibo.config import raise_error from qibo.result import CircuitResult -import cuquantum # pylint: disable=import-error CUDA_TYPES = { "complex64": ( From e12bc0fdc2759f394024a560477b338ac24e4f05 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 16 Feb 2024 08:57:32 +0100 Subject: [PATCH 183/199] chore: Poetry lock --- poetry.lock | 158 ++++++++++++++++++++++++++-------------------------- 1 file changed, 79 insertions(+), 79 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6fabdcb2..c8200c12 100644 --- a/poetry.lock +++ b/poetry.lock @@ -576,53 +576,53 @@ files = [ [[package]] name = "fonttools" -version = "4.48.1" +version = "4.49.0" description = "Tools to manipulate font files" optional = false python-versions = ">=3.8" files = [ - {file = "fonttools-4.48.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:702ae93058c81f46461dc4b2c79f11d3c3d8fd7296eaf8f75b4ba5bbf813cd5f"}, - {file = "fonttools-4.48.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:97f0a49fa6aa2d6205c6f72f4f98b74ef4b9bfdcb06fd78e6fe6c7af4989b63e"}, - {file = "fonttools-4.48.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3260db55f1843e57115256e91247ad9f68cb02a434b51262fe0019e95a98738"}, - {file = "fonttools-4.48.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e740a7602c2bb71e1091269b5dbe89549749a8817dc294b34628ffd8b2bf7124"}, - {file = "fonttools-4.48.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4108b1d247953dd7c90ec8f457a2dec5fceb373485973cc852b14200118a51ee"}, - {file = "fonttools-4.48.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56339ec557f0c342bddd7c175f5e41c45fc21282bee58a86bd9aa322bec715f2"}, - {file = "fonttools-4.48.1-cp310-cp310-win32.whl", hash = "sha256:bff5b38d0e76eb18e0b8abbf35d384e60b3371be92f7be36128ee3e67483b3ec"}, - {file = "fonttools-4.48.1-cp310-cp310-win_amd64.whl", hash = "sha256:f7449493886da6a17472004d3818cc050ba3f4a0aa03fb47972e4fa5578e6703"}, - {file = "fonttools-4.48.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:18b35fd1a850ed7233a99bbd6774485271756f717dac8b594958224b54118b61"}, - {file = "fonttools-4.48.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cad5cfd044ea2e306fda44482b3dd32ee47830fa82dfa4679374b41baa294f5f"}, - {file = "fonttools-4.48.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f30e605c7565d0da6f0aec75a30ec372072d016957cd8fc4469721a36ea59b7"}, - {file = "fonttools-4.48.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aee76fd81a8571c68841d6ef0da750d5ff08ff2c5f025576473016f16ac3bcf7"}, - {file = "fonttools-4.48.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5057ade278e67923000041e2b195c9ea53e87f227690d499b6a4edd3702f7f01"}, - {file = "fonttools-4.48.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b10633aafc5932995a391ec07eba5e79f52af0003a1735b2306b3dab8a056d48"}, - {file = "fonttools-4.48.1-cp311-cp311-win32.whl", hash = "sha256:0d533f89819f9b3ee2dbedf0fed3825c425850e32bdda24c558563c71be0064e"}, - {file = "fonttools-4.48.1-cp311-cp311-win_amd64.whl", hash = "sha256:d20588466367f05025bb1efdf4e5d498ca6d14bde07b6928b79199c588800f0a"}, - {file = "fonttools-4.48.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0a2417547462e468edf35b32e3dd06a6215ac26aa6316b41e03b8eeaf9f079ea"}, - {file = "fonttools-4.48.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cf5a0cd974f85a80b74785db2d5c3c1fd6cc09a2ba3c837359b2b5da629ee1b0"}, - {file = "fonttools-4.48.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0452fcbfbce752ba596737a7c5ec5cf76bc5f83847ce1781f4f90eab14ece252"}, - {file = "fonttools-4.48.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578c00f93868f64a4102ecc5aa600a03b49162c654676c3fadc33de2ddb88a81"}, - {file = "fonttools-4.48.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:63dc592a16cd08388d8c4c7502b59ac74190b23e16dfc863c69fe1ea74605b68"}, - {file = "fonttools-4.48.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9b58638d8a85e3a1b32ec0a91d9f8171a877b4b81c408d4cb3257d0dee63e092"}, - {file = "fonttools-4.48.1-cp312-cp312-win32.whl", hash = "sha256:d10979ef14a8beaaa32f613bb698743f7241d92f437a3b5e32356dfb9769c65d"}, - {file = "fonttools-4.48.1-cp312-cp312-win_amd64.whl", hash = "sha256:cdfd7557d1bd294a200bd211aa665ca3b02998dcc18f8211a5532da5b8fad5c5"}, - {file = "fonttools-4.48.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3cdb9a92521b81bf717ebccf592bd0292e853244d84115bfb4db0c426de58348"}, - {file = "fonttools-4.48.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9b4ec6d42a7555f5ae35f3b805482f0aad0f1baeeef54859492ea3b782959d4a"}, - {file = "fonttools-4.48.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:902e9c4e9928301912f34a6638741b8ae0b64824112b42aaf240e06b735774b1"}, - {file = "fonttools-4.48.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8c8b54bd1420c184a995f980f1a8076f87363e2bb24239ef8c171a369d85a31"}, - {file = "fonttools-4.48.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:12ee86abca46193359ea69216b3a724e90c66ab05ab220d39e3fc068c1eb72ac"}, - {file = "fonttools-4.48.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6978bade7b6c0335095bdd0bd97f8f3d590d2877b370f17e03e0865241694eb5"}, - {file = "fonttools-4.48.1-cp38-cp38-win32.whl", hash = "sha256:bcd77f89fc1a6b18428e7a55dde8ef56dae95640293bfb8f4e929929eba5e2a2"}, - {file = "fonttools-4.48.1-cp38-cp38-win_amd64.whl", hash = "sha256:f40441437b039930428e04fb05ac3a132e77458fb57666c808d74a556779e784"}, - {file = "fonttools-4.48.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0d2b01428f7da26f229a5656defc824427b741e454b4e210ad2b25ed6ea2aed4"}, - {file = "fonttools-4.48.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:df48798f9a4fc4c315ab46e17873436c8746f5df6eddd02fad91299b2af7af95"}, - {file = "fonttools-4.48.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2eb4167bde04e172a93cf22c875d8b0cff76a2491f67f5eb069566215302d45d"}, - {file = "fonttools-4.48.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c900508c46274d32d308ae8e82335117f11aaee1f7d369ac16502c9a78930b0a"}, - {file = "fonttools-4.48.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:594206b31c95fcfa65f484385171fabb4ec69f7d2d7f56d27f17db26b7a31814"}, - {file = "fonttools-4.48.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:292922dc356d7f11f5063b4111a8b719efb8faea92a2a88ed296408d449d8c2e"}, - {file = "fonttools-4.48.1-cp39-cp39-win32.whl", hash = "sha256:4709c5bf123ba10eac210d2d5c9027d3f472591d9f1a04262122710fa3d23199"}, - {file = "fonttools-4.48.1-cp39-cp39-win_amd64.whl", hash = "sha256:63c73b9dd56a94a3cbd2f90544b5fca83666948a9e03370888994143b8d7c070"}, - {file = "fonttools-4.48.1-py3-none-any.whl", hash = "sha256:e3e33862fc5261d46d9aae3544acb36203b1a337d00bdb5d3753aae50dac860e"}, - {file = "fonttools-4.48.1.tar.gz", hash = "sha256:8b8a45254218679c7f1127812761e7854ed5c8e34349aebf581e8c9204e7495a"}, + {file = "fonttools-4.49.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d970ecca0aac90d399e458f0b7a8a597e08f95de021f17785fb68e2dc0b99717"}, + {file = "fonttools-4.49.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac9a745b7609f489faa65e1dc842168c18530874a5f5b742ac3dd79e26bca8bc"}, + {file = "fonttools-4.49.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ba0e00620ca28d4ca11fc700806fd69144b463aa3275e1b36e56c7c09915559"}, + {file = "fonttools-4.49.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdee3ab220283057e7840d5fb768ad4c2ebe65bdba6f75d5d7bf47f4e0ed7d29"}, + {file = "fonttools-4.49.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ce7033cb61f2bb65d8849658d3786188afd80f53dad8366a7232654804529532"}, + {file = "fonttools-4.49.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:07bc5ea02bb7bc3aa40a1eb0481ce20e8d9b9642a9536cde0218290dd6085828"}, + {file = "fonttools-4.49.0-cp310-cp310-win32.whl", hash = "sha256:86eef6aab7fd7c6c8545f3ebd00fd1d6729ca1f63b0cb4d621bccb7d1d1c852b"}, + {file = "fonttools-4.49.0-cp310-cp310-win_amd64.whl", hash = "sha256:1fac1b7eebfce75ea663e860e7c5b4a8831b858c17acd68263bc156125201abf"}, + {file = "fonttools-4.49.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:edc0cce355984bb3c1d1e89d6a661934d39586bb32191ebff98c600f8957c63e"}, + {file = "fonttools-4.49.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:83a0d9336de2cba86d886507dd6e0153df333ac787377325a39a2797ec529814"}, + {file = "fonttools-4.49.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36c8865bdb5cfeec88f5028e7e592370a0657b676c6f1d84a2108e0564f90e22"}, + {file = "fonttools-4.49.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33037d9e56e2562c710c8954d0f20d25b8386b397250d65581e544edc9d6b942"}, + {file = "fonttools-4.49.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8fb022d799b96df3eaa27263e9eea306bd3d437cc9aa981820850281a02b6c9a"}, + {file = "fonttools-4.49.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33c584c0ef7dc54f5dd4f84082eabd8d09d1871a3d8ca2986b0c0c98165f8e86"}, + {file = "fonttools-4.49.0-cp311-cp311-win32.whl", hash = "sha256:cbe61b158deb09cffdd8540dc4a948d6e8f4d5b4f3bf5cd7db09bd6a61fee64e"}, + {file = "fonttools-4.49.0-cp311-cp311-win_amd64.whl", hash = "sha256:fc11e5114f3f978d0cea7e9853627935b30d451742eeb4239a81a677bdee6bf6"}, + {file = "fonttools-4.49.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d647a0e697e5daa98c87993726da8281c7233d9d4ffe410812a4896c7c57c075"}, + {file = "fonttools-4.49.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f3bbe672df03563d1f3a691ae531f2e31f84061724c319652039e5a70927167e"}, + {file = "fonttools-4.49.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bebd91041dda0d511b0d303180ed36e31f4f54b106b1259b69fade68413aa7ff"}, + {file = "fonttools-4.49.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4145f91531fd43c50f9eb893faa08399816bb0b13c425667c48475c9f3a2b9b5"}, + {file = "fonttools-4.49.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ea329dafb9670ffbdf4dbc3b0e5c264104abcd8441d56de77f06967f032943cb"}, + {file = "fonttools-4.49.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c076a9e548521ecc13d944b1d261ff3d7825048c338722a4bd126d22316087b7"}, + {file = "fonttools-4.49.0-cp312-cp312-win32.whl", hash = "sha256:b607ea1e96768d13be26d2b400d10d3ebd1456343eb5eaddd2f47d1c4bd00880"}, + {file = "fonttools-4.49.0-cp312-cp312-win_amd64.whl", hash = "sha256:a974c49a981e187381b9cc2c07c6b902d0079b88ff01aed34695ec5360767034"}, + {file = "fonttools-4.49.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b85ec0bdd7bdaa5c1946398cbb541e90a6dfc51df76dfa88e0aaa41b335940cb"}, + {file = "fonttools-4.49.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:af20acbe198a8a790618ee42db192eb128afcdcc4e96d99993aca0b60d1faeb4"}, + {file = "fonttools-4.49.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d418b1fee41a1d14931f7ab4b92dc0bc323b490e41d7a333eec82c9f1780c75"}, + {file = "fonttools-4.49.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b44a52b8e6244b6548851b03b2b377a9702b88ddc21dcaf56a15a0393d425cb9"}, + {file = "fonttools-4.49.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7c7125068e04a70739dad11857a4d47626f2b0bd54de39e8622e89701836eabd"}, + {file = "fonttools-4.49.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29e89d0e1a7f18bc30f197cfadcbef5a13d99806447c7e245f5667579a808036"}, + {file = "fonttools-4.49.0-cp38-cp38-win32.whl", hash = "sha256:9d95fa0d22bf4f12d2fb7b07a46070cdfc19ef5a7b1c98bc172bfab5bf0d6844"}, + {file = "fonttools-4.49.0-cp38-cp38-win_amd64.whl", hash = "sha256:768947008b4dc552d02772e5ebd49e71430a466e2373008ce905f953afea755a"}, + {file = "fonttools-4.49.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:08877e355d3dde1c11973bb58d4acad1981e6d1140711230a4bfb40b2b937ccc"}, + {file = "fonttools-4.49.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fdb54b076f25d6b0f0298dc706acee5052de20c83530fa165b60d1f2e9cbe3cb"}, + {file = "fonttools-4.49.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0af65c720520710cc01c293f9c70bd69684365c6015cc3671db2b7d807fe51f2"}, + {file = "fonttools-4.49.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f255ce8ed7556658f6d23f6afd22a6d9bbc3edb9b96c96682124dc487e1bf42"}, + {file = "fonttools-4.49.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d00af0884c0e65f60dfaf9340e26658836b935052fdd0439952ae42e44fdd2be"}, + {file = "fonttools-4.49.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:263832fae27481d48dfafcc43174644b6706639661e242902ceb30553557e16c"}, + {file = "fonttools-4.49.0-cp39-cp39-win32.whl", hash = "sha256:0404faea044577a01bb82d47a8fa4bc7a54067fa7e324785dd65d200d6dd1133"}, + {file = "fonttools-4.49.0-cp39-cp39-win_amd64.whl", hash = "sha256:b050d362df50fc6e38ae3954d8c29bf2da52be384649ee8245fdb5186b620836"}, + {file = "fonttools-4.49.0-py3-none-any.whl", hash = "sha256:af281525e5dd7fa0b39fb1667b8d5ca0e2a9079967e14c4bfe90fd1cd13e0f18"}, + {file = "fonttools-4.49.0.tar.gz", hash = "sha256:ebf46e7f01b7af7861310417d7c49591a85d99146fc23a5ba82fdb28af156321"}, ] [package.extras] @@ -930,39 +930,39 @@ files = [ [[package]] name = "matplotlib" -version = "3.8.2" +version = "3.8.3" description = "Python plotting package" optional = false python-versions = ">=3.9" files = [ - {file = "matplotlib-3.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:09796f89fb71a0c0e1e2f4bdaf63fb2cefc84446bb963ecdeb40dfee7dfa98c7"}, - {file = "matplotlib-3.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f9c6976748a25e8b9be51ea028df49b8e561eed7809146da7a47dbecebab367"}, - {file = "matplotlib-3.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b78e4f2cedf303869b782071b55fdde5987fda3038e9d09e58c91cc261b5ad18"}, - {file = "matplotlib-3.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e208f46cf6576a7624195aa047cb344a7f802e113bb1a06cfd4bee431de5e31"}, - {file = "matplotlib-3.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:46a569130ff53798ea5f50afce7406e91fdc471ca1e0e26ba976a8c734c9427a"}, - {file = "matplotlib-3.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:830f00640c965c5b7f6bc32f0d4ce0c36dfe0379f7dd65b07a00c801713ec40a"}, - {file = "matplotlib-3.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d86593ccf546223eb75a39b44c32788e6f6440d13cfc4750c1c15d0fcb850b63"}, - {file = "matplotlib-3.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a5430836811b7652991939012f43d2808a2db9b64ee240387e8c43e2e5578c8"}, - {file = "matplotlib-3.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9576723858a78751d5aacd2497b8aef29ffea6d1c95981505877f7ac28215c6"}, - {file = "matplotlib-3.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ba9cbd8ac6cf422f3102622b20f8552d601bf8837e49a3afed188d560152788"}, - {file = "matplotlib-3.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:03f9d160a29e0b65c0790bb07f4f45d6a181b1ac33eb1bb0dd225986450148f0"}, - {file = "matplotlib-3.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:3773002da767f0a9323ba1a9b9b5d00d6257dbd2a93107233167cfb581f64717"}, - {file = "matplotlib-3.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:4c318c1e95e2f5926fba326f68177dee364aa791d6df022ceb91b8221bd0a627"}, - {file = "matplotlib-3.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:091275d18d942cf1ee9609c830a1bc36610607d8223b1b981c37d5c9fc3e46a4"}, - {file = "matplotlib-3.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b0f3b8ea0e99e233a4bcc44590f01604840d833c280ebb8fe5554fd3e6cfe8d"}, - {file = "matplotlib-3.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b1704a530395aaf73912be741c04d181f82ca78084fbd80bc737be04848331"}, - {file = "matplotlib-3.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533b0e3b0c6768eef8cbe4b583731ce25a91ab54a22f830db2b031e83cca9213"}, - {file = "matplotlib-3.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:0f4fc5d72b75e2c18e55eb32292659cf731d9d5b312a6eb036506304f4675630"}, - {file = "matplotlib-3.8.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:deaed9ad4da0b1aea77fe0aa0cebb9ef611c70b3177be936a95e5d01fa05094f"}, - {file = "matplotlib-3.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:172f4d0fbac3383d39164c6caafd3255ce6fa58f08fc392513a0b1d3b89c4f89"}, - {file = "matplotlib-3.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7d36c2209d9136cd8e02fab1c0ddc185ce79bc914c45054a9f514e44c787917"}, - {file = "matplotlib-3.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5864bdd7da445e4e5e011b199bb67168cdad10b501750367c496420f2ad00843"}, - {file = "matplotlib-3.8.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ef8345b48e95cee45ff25192ed1f4857273117917a4dcd48e3905619bcd9c9b8"}, - {file = "matplotlib-3.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:7c48d9e221b637c017232e3760ed30b4e8d5dfd081daf327e829bf2a72c731b4"}, - {file = "matplotlib-3.8.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aa11b3c6928a1e496c1a79917d51d4cd5d04f8a2e75f21df4949eeefdf697f4b"}, - {file = "matplotlib-3.8.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1095fecf99eeb7384dabad4bf44b965f929a5f6079654b681193edf7169ec20"}, - {file = "matplotlib-3.8.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:bddfb1db89bfaa855912261c805bd0e10218923cc262b9159a49c29a7a1c1afa"}, - {file = "matplotlib-3.8.2.tar.gz", hash = "sha256:01a978b871b881ee76017152f1f1a0cbf6bd5f7b8ff8c96df0df1bd57d8755a1"}, + {file = "matplotlib-3.8.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:cf60138ccc8004f117ab2a2bad513cc4d122e55864b4fe7adf4db20ca68a078f"}, + {file = "matplotlib-3.8.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f557156f7116be3340cdeef7f128fa99b0d5d287d5f41a16e169819dcf22357"}, + {file = "matplotlib-3.8.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f386cf162b059809ecfac3bcc491a9ea17da69fa35c8ded8ad154cd4b933d5ec"}, + {file = "matplotlib-3.8.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3c5f96f57b0369c288bf6f9b5274ba45787f7e0589a34d24bdbaf6d3344632f"}, + {file = "matplotlib-3.8.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:83e0f72e2c116ca7e571c57aa29b0fe697d4c6425c4e87c6e994159e0c008635"}, + {file = "matplotlib-3.8.3-cp310-cp310-win_amd64.whl", hash = "sha256:1c5c8290074ba31a41db1dc332dc2b62def469ff33766cbe325d32a3ee291aea"}, + {file = "matplotlib-3.8.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5184e07c7e1d6d1481862ee361905b7059f7fe065fc837f7c3dc11eeb3f2f900"}, + {file = "matplotlib-3.8.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d7e7e0993d0758933b1a241a432b42c2db22dfa37d4108342ab4afb9557cbe3e"}, + {file = "matplotlib-3.8.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04b36ad07eac9740fc76c2aa16edf94e50b297d6eb4c081e3add863de4bb19a7"}, + {file = "matplotlib-3.8.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c42dae72a62f14982f1474f7e5c9959fc4bc70c9de11cc5244c6e766200ba65"}, + {file = "matplotlib-3.8.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bf5932eee0d428192c40b7eac1399d608f5d995f975cdb9d1e6b48539a5ad8d0"}, + {file = "matplotlib-3.8.3-cp311-cp311-win_amd64.whl", hash = "sha256:40321634e3a05ed02abf7c7b47a50be50b53ef3eaa3a573847431a545585b407"}, + {file = "matplotlib-3.8.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:09074f8057917d17ab52c242fdf4916f30e99959c1908958b1fc6032e2d0f6d4"}, + {file = "matplotlib-3.8.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5745f6d0fb5acfabbb2790318db03809a253096e98c91b9a31969df28ee604aa"}, + {file = "matplotlib-3.8.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97653d869a71721b639714b42d87cda4cfee0ee74b47c569e4874c7590c55c5"}, + {file = "matplotlib-3.8.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:242489efdb75b690c9c2e70bb5c6550727058c8a614e4c7716f363c27e10bba1"}, + {file = "matplotlib-3.8.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:83c0653c64b73926730bd9ea14aa0f50f202ba187c307a881673bad4985967b7"}, + {file = "matplotlib-3.8.3-cp312-cp312-win_amd64.whl", hash = "sha256:ef6c1025a570354297d6c15f7d0f296d95f88bd3850066b7f1e7b4f2f4c13a39"}, + {file = "matplotlib-3.8.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c4af3f7317f8a1009bbb2d0bf23dfaba859eb7dd4ccbd604eba146dccaaaf0a4"}, + {file = "matplotlib-3.8.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4c6e00a65d017d26009bac6808f637b75ceade3e1ff91a138576f6b3065eeeba"}, + {file = "matplotlib-3.8.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7b49ab49a3bea17802df6872f8d44f664ba8f9be0632a60c99b20b6db2165b7"}, + {file = "matplotlib-3.8.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6728dde0a3997396b053602dbd907a9bd64ec7d5cf99e728b404083698d3ca01"}, + {file = "matplotlib-3.8.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:813925d08fb86aba139f2d31864928d67511f64e5945ca909ad5bc09a96189bb"}, + {file = "matplotlib-3.8.3-cp39-cp39-win_amd64.whl", hash = "sha256:cd3a0c2be76f4e7be03d34a14d49ded6acf22ef61f88da600a18a5cd8b3c5f3c"}, + {file = "matplotlib-3.8.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fa93695d5c08544f4a0dfd0965f378e7afc410d8672816aff1e81be1f45dbf2e"}, + {file = "matplotlib-3.8.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9764df0e8778f06414b9d281a75235c1e85071f64bb5d71564b97c1306a2afc"}, + {file = "matplotlib-3.8.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:5e431a09e6fab4012b01fc155db0ce6dccacdbabe8198197f523a4ef4805eb26"}, + {file = "matplotlib-3.8.3.tar.gz", hash = "sha256:7b416239e9ae38be54b028abbf9048aff5054a9aba5416bef0bd17f9162ce161"}, ] [package.dependencies] @@ -1568,18 +1568,18 @@ test = ["asv", "gmpy2", "hypothesis", "mpmath", "pooch", "pytest", "pytest-cov", [[package]] name = "setuptools" -version = "69.0.3" +version = "69.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"}, - {file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"}, + {file = "setuptools-69.1.0-py3-none-any.whl", hash = "sha256:c054629b81b946d63a9c6e732bc8b2513a7c3ea645f11d0139a2191d735c60c6"}, + {file = "setuptools-69.1.0.tar.gz", hash = "sha256:850894c4195f09c4ed30dba56213bf7c3f21d86ed6bdaafb5df5972593bfc401"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -1656,13 +1656,13 @@ files = [ [[package]] name = "tqdm" -version = "4.66.1" +version = "4.66.2" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.1-py3-none-any.whl", hash = "sha256:d302b3c5b53d47bce91fea46679d9c3c6508cf6332229aa1e7d8653723793386"}, - {file = "tqdm-4.66.1.tar.gz", hash = "sha256:d88e651f9db8d8551a62556d3cff9e3034274ca5d66e93197cf2490e2dcb69c7"}, + {file = "tqdm-4.66.2-py3-none-any.whl", hash = "sha256:1ee4f8a893eb9bef51c6e35730cebf234d5d0b6bd112b0271e10ed7c24a02bd9"}, + {file = "tqdm-4.66.2.tar.gz", hash = "sha256:6cd52cdf0fef0e0f543299cfc96fec90d7b8a7e88745f411ec33eb44d5ed3531"}, ] [package.dependencies] From 5cb384cae3f77081ef2b8b210b488c1e295ce466 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 16 Feb 2024 10:13:26 +0100 Subject: [PATCH 184/199] ci: Move workflow to Poetry --- .github/workflows/rules.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rules.yml b/.github/workflows/rules.yml index 6415be0e..12626f61 100644 --- a/.github/workflows/rules.yml +++ b/.github/workflows/rules.yml @@ -16,7 +16,7 @@ jobs: matrix: os: [ubuntu-latest] python-version: [3.8, 3.9, "3.10"] - uses: qiboteam/workflows/.github/workflows/rules.yml@main + uses: qiboteam/workflows/.github/workflows/rules-poetry.yml@main with: os: ${{ matrix.os }} python-version: ${{ matrix.python-version }} From 66dc9ea44398957674dd232a85851b82dd71c093 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 16 Feb 2024 10:14:41 +0100 Subject: [PATCH 185/199] ci: Remove unused workflow input --- .github/workflows/rules.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/rules.yml b/.github/workflows/rules.yml index 12626f61..fcb21200 100644 --- a/.github/workflows/rules.yml +++ b/.github/workflows/rules.yml @@ -20,6 +20,5 @@ jobs: with: os: ${{ matrix.os }} python-version: ${{ matrix.python-version }} - environment: "qibotn" pip-extras: "analysis,tests" secrets: inherit From fcef3715330bcb1b50039fce09c16c3ce9f1de77 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 16 Feb 2024 10:15:46 +0100 Subject: [PATCH 186/199] ci: Rename workflow input To be compliant with the Poetry version --- .github/workflows/rules.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rules.yml b/.github/workflows/rules.yml index fcb21200..9171a548 100644 --- a/.github/workflows/rules.yml +++ b/.github/workflows/rules.yml @@ -20,5 +20,5 @@ jobs: with: os: ${{ matrix.os }} python-version: ${{ matrix.python-version }} - pip-extras: "analysis,tests" + poetry-extras: "analysis,tests" secrets: inherit From 5962c0c0dab82b15f7bd4e38e4e329a88d6f765e Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 16 Feb 2024 10:35:33 +0100 Subject: [PATCH 187/199] ci: Add explicit Poetry CLI option --- .github/workflows/rules.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rules.yml b/.github/workflows/rules.yml index 9171a548..d0de83a1 100644 --- a/.github/workflows/rules.yml +++ b/.github/workflows/rules.yml @@ -20,5 +20,5 @@ jobs: with: os: ${{ matrix.os }} python-version: ${{ matrix.python-version }} - poetry-extras: "analysis,tests" + poetry-extras: "--with analysis,tests" secrets: inherit From fa59ec81b555658d51d507a4062c5ba1ab5a7eef Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 16 Feb 2024 10:36:00 +0100 Subject: [PATCH 188/199] ci: Extend tests to 3.11 --- .github/workflows/rules.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rules.yml b/.github/workflows/rules.yml index d0de83a1..976b7841 100644 --- a/.github/workflows/rules.yml +++ b/.github/workflows/rules.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - python-version: [3.8, 3.9, "3.10"] + python-version: [3.8, 3.9, "3.10", "3.11"] uses: qiboteam/workflows/.github/workflows/rules-poetry.yml@main with: os: ${{ matrix.os }} From 665a145cfea33fc0eb95f775c7dfc4609103e6b6 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 16 Feb 2024 11:03:57 +0100 Subject: [PATCH 189/199] build: Define poethepoet tasks --- pyproject.toml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d2331d24..5a259248 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,8 +14,8 @@ repository = "https://github.com/qiboteam/qibotn/" documentation = "https://qibo.science/docs/qibotn/stable" keywords = [] classifiers = [ - "Programming Language :: Python :: 3", - "Topic :: Scientific/Engineering :: Physics", + "Programming Language :: Python :: 3", + "Topic :: Scientific/Engineering :: Physics", ] packages = [{ include = "qibotn", from = "src" }] @@ -46,6 +46,11 @@ optional = true [tool.poetry.group.analysis.dependencies] pylint = "^3.0.3" +[tool.poe.tasks] +test = "pytest" +lint = "pylint src --errors-only" +lint-warnings = "pylint src --exit-zero" + [tool.pylint.main] ignored-modules = ["cupy", "cuquantum", "mpi4py"] From 52da762e1b3f328b1da5f6e22c7a10a5d5c5ae82 Mon Sep 17 00:00:00 2001 From: tankya2 Date: Thu, 22 Feb 2024 17:19:39 +0800 Subject: [PATCH 190/199] Change output to QuantumState class --- src/qibotn/backends/cutensornet.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qibotn/backends/cutensornet.py b/src/qibotn/backends/cutensornet.py index 104b41cc..b776deca 100644 --- a/src/qibotn/backends/cutensornet.py +++ b/src/qibotn/backends/cutensornet.py @@ -2,7 +2,7 @@ import numpy as np from qibo.backends.numpy import NumpyBackend from qibo.config import raise_error -from qibo.result import CircuitResult +from qibo.result import QuantumState CUDA_TYPES = { "complex64": ( @@ -185,5 +185,5 @@ def execute_circuit( if return_array: return state.flatten() else: - circuit._final_state = CircuitResult(self, circuit, state.flatten(), nshots) - return circuit._final_state + return QuantumState(state.flatten()) + From 5f053b25e12a316e7860bad96309d3e7fe5678ba Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 09:19:53 +0000 Subject: [PATCH 191/199] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qibotn/backends/cutensornet.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qibotn/backends/cutensornet.py b/src/qibotn/backends/cutensornet.py index b776deca..fc010e97 100644 --- a/src/qibotn/backends/cutensornet.py +++ b/src/qibotn/backends/cutensornet.py @@ -186,4 +186,3 @@ def execute_circuit( return state.flatten() else: return QuantumState(state.flatten()) - From 151a5a51a1c653b33a93940a1acaf84e7418895a Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Thu, 22 Feb 2024 18:11:48 +0800 Subject: [PATCH 192/199] Change output to QuantumState class for quimb backend --- src/qibotn/backends/quimb.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/qibotn/backends/quimb.py b/src/qibotn/backends/quimb.py index cca67999..31e24c7f 100644 --- a/src/qibotn/backends/quimb.py +++ b/src/qibotn/backends/quimb.py @@ -1,6 +1,6 @@ from qibo.backends.numpy import NumpyBackend from qibo.config import raise_error -from qibo.result import CircuitResult +from qibo.result import QuantumState class QuimbBackend(NumpyBackend): @@ -80,5 +80,4 @@ def execute_circuit( if return_array: return state.flatten() else: - circuit._final_state = CircuitResult(self, circuit, state.flatten(), nshots) - return circuit._final_state + return QuantumState(state.flatten()) From b6bd9bdfd5fc94c6aaf08b8a6eb6585fa7f916f9 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Fri, 23 Feb 2024 14:22:06 +0800 Subject: [PATCH 193/199] evaluating circuit directly from quimb without qasm in qibo --- src/qibotn/eval_qu.py | 53 ++++++++++++++----------------------------- 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/src/qibotn/eval_qu.py b/src/qibotn/eval_qu.py index 74fca1cd..61241526 100644 --- a/src/qibotn/eval_qu.py +++ b/src/qibotn/eval_qu.py @@ -1,38 +1,5 @@ import numpy as np import quimb.tensor as qtn -from qibo.models import Circuit as QiboCircuit - - -def from_qibo( - circuit: QiboCircuit, - is_mps: False, - psi0=None, - method="svd", - cutoff=1e-6, - cutoff_mode="abs", -): - """Create a tensor network representation of the circuit.""" - - nqubits = circuit.nqubits - gate_opt = {} - if is_mps: - tncirc = qtn.CircuitMPS(nqubits, psi0=psi0) - gate_opt["method"] = method - gate_opt["cutoff"] = cutoff - gate_opt["cutoff_mode"] = cutoff_mode - else: - tncirc = qtn.Circuit(nqubits, psi0=psi0) - - for gate in circuit.queue: - tncirc.apply_gate( - gate.name, - *gate.parameters, - *gate.qubits, - parametrize=False if is_mps else (len(gate.parameters) > 0), - **gate_opt - ) - - return tncirc def init_state_tn(nqubits, init_state_sv): @@ -48,10 +15,24 @@ def dense_vector_tn_qu(qasm: str, initial_state, is_mps, backend="numpy"): backend (quimb): numpy, cupy, jax. Passed to ``opt_einsum``. """ - circuit = QiboCircuit.from_qasm(qasm) + if initial_state is not None: - initial_state = init_state_tn(circuit.nqubits, initial_state) - circ_quimb = from_qibo(circuit, is_mps, psi0=initial_state) + nqubits = int(np.log2(len(initial_state))) + initial_state = init_state_tn(nqubits, initial_state) + + if is_mps: + gate_opt = {} + gate_opt["method"] = "svd" + gate_opt["cutoff"] = 1e-6 + gate_opt["cutoff_mode"] = "abs" + + circ_quimb = qtn.circuit.CircuitMPS.from_openqasm2_str( + qasm, psi0=initial_state, gate_opts=gate_opt + ) + + else: + circ_quimb = qtn.circuit.Circuit.from_openqasm2_str(qasm, psi0=initial_state) + interim = circ_quimb.psi.full_simplify(seq="DRC") amplitudes = interim.to_dense(backend=backend) From de92060180a3da24d39b03636cf3a2f3dc27e956 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Fri, 23 Feb 2024 14:48:30 +0800 Subject: [PATCH 194/199] Adding feature to pass MPS parameters in quimb --- src/qibotn/backends/quimb.py | 8 +++++--- src/qibotn/eval_qu.py | 11 +++-------- tests/test_quimb_backend.py | 15 ++++++++++++--- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/qibotn/backends/quimb.py b/src/qibotn/backends/quimb.py index 31e24c7f..0a34eefe 100644 --- a/src/qibotn/backends/quimb.py +++ b/src/qibotn/backends/quimb.py @@ -16,9 +16,11 @@ def __init__(self, runcard): mps_enabled_value = runcard.get("MPS_enabled") if mps_enabled_value is True: - self.MPS_enabled = True + self.mps_opts = {"method": "svd", "cutoff": 1e-6, "cutoff_mod": "abs"} elif mps_enabled_value is False: - self.MPS_enabled = False + self.mps_opts = False + elif isinstance(mps_enabled_value, dict): + self.mps_opts = mps_enabled_value else: raise TypeError("MPS_enabled has an unexpected type") @@ -74,7 +76,7 @@ def execute_circuit( ) state = eval.dense_vector_tn_qu( - circuit.to_qasm(), initial_state, is_mps=self.MPS_enabled, backend="numpy" + circuit.to_qasm(), initial_state, self.mps_opts, backend="numpy" ) if return_array: diff --git a/src/qibotn/eval_qu.py b/src/qibotn/eval_qu.py index 61241526..0a327815 100644 --- a/src/qibotn/eval_qu.py +++ b/src/qibotn/eval_qu.py @@ -10,7 +10,7 @@ def init_state_tn(nqubits, init_state_sv): return qtn.tensor_1d.MatrixProductState.from_dense(init_state_sv, dims) -def dense_vector_tn_qu(qasm: str, initial_state, is_mps, backend="numpy"): +def dense_vector_tn_qu(qasm: str, initial_state, mps_opts, backend="numpy"): """Evaluate QASM with Quimb. backend (quimb): numpy, cupy, jax. Passed to ``opt_einsum``. @@ -20,14 +20,9 @@ def dense_vector_tn_qu(qasm: str, initial_state, is_mps, backend="numpy"): nqubits = int(np.log2(len(initial_state))) initial_state = init_state_tn(nqubits, initial_state) - if is_mps: - gate_opt = {} - gate_opt["method"] = "svd" - gate_opt["cutoff"] = 1e-6 - gate_opt["cutoff_mode"] = "abs" - + if mps_opts: circ_quimb = qtn.circuit.CircuitMPS.from_openqasm2_str( - qasm, psi0=initial_state, gate_opts=gate_opt + qasm, psi0=initial_state, gate_opts=mps_opts ) else: diff --git a/tests/test_quimb_backend.py b/tests/test_quimb_backend.py index 15ba6524..2b77ab6d 100644 --- a/tests/test_quimb_backend.py +++ b/tests/test_quimb_backend.py @@ -50,9 +50,18 @@ def test_eval(nqubits: int, tolerance: float, is_mps: bool): qasm_circ = qibo_circ.to_qasm() # Test quimb - result_tn = qibotn.eval_qu.dense_vector_tn_qu( - qasm_circ, init_state_tn, is_mps, backend=config.quimb.backend - ).flatten() + if is_mps: + gate_opt = {} + gate_opt["method"] = "svd" + gate_opt["cutoff"] = 1e-6 + gate_opt["cutoff_mode"] = "abs" + result_tn = qibotn.eval_qu.dense_vector_tn_qu( + qasm_circ, init_state_tn, gate_opt, backend=config.quimb.backend + ).flatten() + else: + result_tn = qibotn.eval_qu.dense_vector_tn_qu( + qasm_circ, init_state_tn, is_mps, backend=config.quimb.backend + ).flatten() assert np.allclose( result_sv, result_tn, atol=tolerance From a5b364dc58cfce1d51216ea32db838ac0aad1517 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Tue, 27 Feb 2024 23:56:32 +0800 Subject: [PATCH 195/199] updating cuda_path condition --- .github/workflows/rules.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rules.yml b/.github/workflows/rules.yml index 976b7841..4c0df6fd 100644 --- a/.github/workflows/rules.yml +++ b/.github/workflows/rules.yml @@ -5,13 +5,17 @@ env: CUDA_PATH: on: + workflow_dispatch: push: pull_request: types: [labeled] jobs: build: - if: ${{ contains(github.event.pull_request.labels.*.name, 'run-workflow') || github.event_name == 'push' }} && env.CUDA_PATH != '' + env: + var_a: ${{ env.CUDA_PATH != ''}} + var_b: ${{ contains(github.event.pull_request.labels.*.name, 'run-workflow') || github.event_name == 'push' }} + if: ${{ fromJSON(env.var_a) && fromJSON(env.var_b)}} strategy: matrix: os: [ubuntu-latest] From 4fbcff1b05a7a2404b705998f2e04e22f250f3cd Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Wed, 28 Feb 2024 14:24:19 +0800 Subject: [PATCH 196/199] using steps in jobs to validate cuda condition --- .github/workflows/rules.yml | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/.github/workflows/rules.yml b/.github/workflows/rules.yml index 4c0df6fd..a705dd88 100644 --- a/.github/workflows/rules.yml +++ b/.github/workflows/rules.yml @@ -11,18 +11,34 @@ on: types: [labeled] jobs: + check: + # job to check cuda availability + runs-on: ubuntu-latest + steps: + - id: step1 + run: echo "test=${{ env.CUDA_PATH != ''}}" >> "$GITHUB_OUTPUT" + - id: step2 + run: echo "test=${{ contains(github.event.pull_request.labels.*.name, 'run-workflow') || github.event_name == 'push' }}" >> "$GITHUB_OUTPUT" + outputs: + cuda_on: ${{ steps.step1.outputs.test }} + git_event: ${{ steps.step2.outputs.test }} + build: - env: - var_a: ${{ env.CUDA_PATH != ''}} - var_b: ${{ contains(github.event.pull_request.labels.*.name, 'run-workflow') || github.event_name == 'push' }} - if: ${{ fromJSON(env.var_a) && fromJSON(env.var_b)}} + # job to build + runs-on: ubuntu-latest strategy: matrix: os: [ubuntu-latest] python-version: [3.8, 3.9, "3.10", "3.11"] - uses: qiboteam/workflows/.github/workflows/rules-poetry.yml@main - with: - os: ${{ matrix.os }} - python-version: ${{ matrix.python-version }} - poetry-extras: "--with analysis,tests" - secrets: inherit + needs: check + steps: + - env: + CUDA_ON: ${{needs.check.outputs.cuda_on}} + GIT_EVENT: ${{needs.check.outputs.git_event}} + if: ${{ fromJSON(env.CUDA_ON) && fromJSON(env.GIT_EVENT)}} + uses: qiboteam/workflows/.github/workflows/rules-poetry.yml@main + with: + os: ${{ matrix.os }} + python-version: ${{ matrix.python-version }} + poetry-extras: "--with analysis,tests" + #secrets: inherit From 9604878aec2ca0f46627721ebc5e11eec1acfe8e Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Wed, 28 Feb 2024 14:45:37 +0800 Subject: [PATCH 197/199] fixing bugs --- .github/workflows/rules.yml | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/.github/workflows/rules.yml b/.github/workflows/rules.yml index a705dd88..56ecd695 100644 --- a/.github/workflows/rules.yml +++ b/.github/workflows/rules.yml @@ -22,23 +22,19 @@ jobs: outputs: cuda_on: ${{ steps.step1.outputs.test }} git_event: ${{ steps.step2.outputs.test }} + test_gpu: ${{ fromJSON(steps.step1.outputs.test) && fromJSON(steps.step2.outputs.test) }} build: # job to build - runs-on: ubuntu-latest - strategy: - matrix: - os: [ubuntu-latest] - python-version: [3.8, 3.9, "3.10", "3.11"] needs: check - steps: - - env: - CUDA_ON: ${{needs.check.outputs.cuda_on}} - GIT_EVENT: ${{needs.check.outputs.git_event}} - if: ${{ fromJSON(env.CUDA_ON) && fromJSON(env.GIT_EVENT)}} - uses: qiboteam/workflows/.github/workflows/rules-poetry.yml@main - with: - os: ${{ matrix.os }} - python-version: ${{ matrix.python-version }} - poetry-extras: "--with analysis,tests" - #secrets: inherit + if: ${{fromJSON(needs.check.outputs.test_gpu)}} + strategy: + matrix: + os: [ubuntu-latest] + python-version: [3.8, 3.9, "3.10", "3.11"] + uses: qiboteam/workflows/.github/workflows/rules-poetry.yml@main + with: + os: ${{ matrix.os }} + python-version: ${{ matrix.python-version }} + poetry-extras: "--with analysis,tests" + secrets: inherit From 69e62da1ee90af2b205ee402e02ee972275ec753 Mon Sep 17 00:00:00 2001 From: yangliwei Date: Wed, 28 Feb 2024 17:27:52 +0800 Subject: [PATCH 198/199] Remove the unused outputs --- .github/workflows/rules.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/rules.yml b/.github/workflows/rules.yml index 56ecd695..4ad1427f 100644 --- a/.github/workflows/rules.yml +++ b/.github/workflows/rules.yml @@ -20,14 +20,12 @@ jobs: - id: step2 run: echo "test=${{ contains(github.event.pull_request.labels.*.name, 'run-workflow') || github.event_name == 'push' }}" >> "$GITHUB_OUTPUT" outputs: - cuda_on: ${{ steps.step1.outputs.test }} - git_event: ${{ steps.step2.outputs.test }} - test_gpu: ${{ fromJSON(steps.step1.outputs.test) && fromJSON(steps.step2.outputs.test) }} + cuda_avail: ${{ fromJSON(steps.step1.outputs.test) && fromJSON(steps.step2.outputs.test) }} build: # job to build needs: check - if: ${{fromJSON(needs.check.outputs.test_gpu)}} + if: ${{fromJSON(needs.check.outputs.cuda_avail)}} strategy: matrix: os: [ubuntu-latest] From 891102f63801cecc86a0205f8d516471b557a765 Mon Sep 17 00:00:00 2001 From: vinitha-balachandran Date: Fri, 1 Mar 2024 07:25:08 +0800 Subject: [PATCH 199/199] more compact form for mps settings --- src/qibotn/eval_qu.py | 11 ++++------- tests/test_quimb_backend.py | 10 ++++------ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/qibotn/eval_qu.py b/src/qibotn/eval_qu.py index 0a327815..d6064e95 100644 --- a/src/qibotn/eval_qu.py +++ b/src/qibotn/eval_qu.py @@ -20,13 +20,10 @@ def dense_vector_tn_qu(qasm: str, initial_state, mps_opts, backend="numpy"): nqubits = int(np.log2(len(initial_state))) initial_state = init_state_tn(nqubits, initial_state) - if mps_opts: - circ_quimb = qtn.circuit.CircuitMPS.from_openqasm2_str( - qasm, psi0=initial_state, gate_opts=mps_opts - ) - - else: - circ_quimb = qtn.circuit.Circuit.from_openqasm2_str(qasm, psi0=initial_state) + circ_cls = qtn.circuit.CircuitMPS if mps_opts else qtn.circuit.Circuit + circ_quimb = circ_cls.from_openqasm2_str( + qasm, psi0=initial_state, gate_opts=mps_opts + ) interim = circ_quimb.psi.full_simplify(seq="DRC") amplitudes = interim.to_dense(backend=backend) diff --git a/tests/test_quimb_backend.py b/tests/test_quimb_backend.py index 2b77ab6d..e32aefe5 100644 --- a/tests/test_quimb_backend.py +++ b/tests/test_quimb_backend.py @@ -55,13 +55,11 @@ def test_eval(nqubits: int, tolerance: float, is_mps: bool): gate_opt["method"] = "svd" gate_opt["cutoff"] = 1e-6 gate_opt["cutoff_mode"] = "abs" - result_tn = qibotn.eval_qu.dense_vector_tn_qu( - qasm_circ, init_state_tn, gate_opt, backend=config.quimb.backend - ).flatten() else: - result_tn = qibotn.eval_qu.dense_vector_tn_qu( - qasm_circ, init_state_tn, is_mps, backend=config.quimb.backend - ).flatten() + gate_opt = None + result_tn = qibotn.eval_qu.dense_vector_tn_qu( + qasm_circ, init_state_tn, gate_opt, backend=config.quimb.backend + ).flatten() assert np.allclose( result_sv, result_tn, atol=tolerance