Skip to content

Commit

Permalink
Updates to include expectation calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
Tankya2 committed Jan 24, 2024
1 parent de530d0 commit 214fdf6
Show file tree
Hide file tree
Showing 4 changed files with 567 additions and 7 deletions.
113 changes: 113 additions & 0 deletions src/qibotn/QiboCircuitConvertor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
2 changes: 1 addition & 1 deletion src/qibotn/QiboCircuitToMPS.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
53 changes: 53 additions & 0 deletions src/qibotn/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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()
Expand Down
Loading

0 comments on commit 214fdf6

Please sign in to comment.