Feat adjust measure#198
Conversation
| qiskit_observables.append(translated) | ||
| qiskit_observables = [ | ||
| obs.apply_layout(ibm_circuit.layout) for obs in qiskit_observables | ||
| ] |
There was a problem hiding this comment.
can we do this in a single step ?
| from mpqp.gates import H | ||
|
|
||
| for i in range(nb_qubits): | ||
| qcircuit.add(H(i)) |
There was a problem hiding this comment.
maybe random_gate instead of H ?
| Example: | ||
| >>> I = np.eye(2) | ||
| >>> X = np.array([[0,1], [1,0]]) | ||
| >>> matrix = np.kron(I, X) | ||
| >>> pprint(matrix) | ||
| [[0, 1, 0, 0], | ||
| [1, 0, 0, 0], | ||
| [0, 0, 0, 1], | ||
| [0, 0, 1, 0]] | ||
| >>> pprint(rearrange_matrix(matrix, [1,0])) | ||
| [[0, 0, 1, 0], | ||
| [0, 0, 0, 1], | ||
| [1, 0, 0, 0], | ||
| [0, 1, 0, 0]] |
There was a problem hiding this comment.
starting with a diagonal matrix might be more pedagogical/easier to understand what happens
| @@ -484,47 +481,8 @@ def _check_targets_order(self): | |||
| """Ensures target qubits are ordered and contiguous, rearranging them if | |||
| necessary (private).""" | |||
|
|
|||
| if len(self.targets) == 0: | |||
| self._pre_measure: list[Gate] = [] | |||
| return | |||
|
|
|||
| if self.nb_qubits != self.observables[0].nb_qubits: | |||
| raise NumberQubitsError( | |||
| f"Target size {self.nb_qubits} doesn't match observable size " | |||
| f"{self.observables[0].nb_qubits}." | |||
| ) | |||
|
|
|||
| self.rearranged_targets = list(self.targets) | |||
| self._pre_measure: list[Gate] = [] | |||
There was a problem hiding this comment.
is this function still needed ?
There was a problem hiding this comment.
Done 59fd820
Removed rearranged_targets, it was only used by the old swap path. Also removed pre_measure since Measure.pre_measure already returns [ ]
| ] | ||
| for circuit in pre_measure: | ||
| for instr in circuit.instructions: | ||
| instr.targets[0] = job.measure.targets[instr.targets[0]] |
There was a problem hiding this comment.
why are you changing just the first target ? the logic looks a bit dodgy here
There was a problem hiding this comment.
Yeah, instr.targets[0] was originally used because find_qubitwise_rotations(...) return single qubit instructions., so each generated instruction has one target. But yeah, this assumption is not obvious, so I agree it looks fragile
| f"{self.observables[0].nb_qubits}." | ||
| ) | ||
|
|
||
| self.rearranged_targets = list(self.targets) |
There was a problem hiding this comment.
I guess the idea here is than now you support arbitrary order, so all the logic
that was here before is not needed anymore, can you confirm ?
There was a problem hiding this comment.
Yes I confirm your suspicions.
| assert isinstance(result, Result) | ||
| from mpqp.tools import pprint | ||
|
|
||
| pprint(result.amplitudes) |
There was a problem hiding this comment.
Did you leave a print?
| ] | ||
| for circuit in pre_measure: | ||
| for instr in circuit.instructions: | ||
| instr.targets[0] = job.measure.targets[instr.targets[0]] |
| def rearrange_pauli_string( | ||
| ps: PauliString, targets: list[int], copy: bool = True | ||
| ) -> PauliString: | ||
| """ | ||
| This function aims at reorderring a pauli string's monomials according to unordered targets. | ||
|
|
||
| Note: The targets must be contiguous, otherwise the algorithm won't work. | ||
| Args: | ||
| ps: The PauliString to reorder. | ||
| targets: The list of unordered targets. | ||
| copy: If set at True will deepcopy the initial string ps. | ||
|
|
||
| Examples: | ||
| >>> ps = pX @ pI | ||
| >>> print(rearrange_pauli_string(ps, [1,0])) | ||
| pI@pX | ||
| >>> ps2 = pX @ pI + pI @ pX | ||
| >>> print(rearrange_pauli_string(ps2, [1,0])) | ||
| pI@pX + pX@pI | ||
| """ | ||
| if copy: | ||
| from copy import deepcopy | ||
|
|
||
| pauli = deepcopy(ps) | ||
| else: | ||
| pauli = ps | ||
|
|
||
| l = len(targets) | ||
| shuffled = sorted(targets) | ||
| for index in range(l): | ||
| if targets[index] == index: | ||
| continue | ||
| shuffled_index = shuffled.index(targets[index]) | ||
| for monom in pauli.monomials: | ||
| atoms = monom.atoms | ||
| atoms[shuffled_index], atoms[shuffled[index]] = ( | ||
| atoms[shuffled[index]], | ||
| atoms[shuffled_index], | ||
| ) | ||
| shuffled[index], shuffled[targets[index]] = ( | ||
| shuffled[targets[index]], | ||
| shuffled[index], | ||
| ) | ||
|
|
||
| i = targets.index(index) | ||
| targets[i], targets[index] = ( | ||
| targets[index], | ||
| targets[i], | ||
| ) | ||
| return pauli |
There was a problem hiding this comment.
I would move this to be a method of PauliString
| pauli = ps | ||
|
|
||
| l = len(targets) | ||
| shuffled = sorted(targets) |
There was a problem hiding this comment.
"shuffled" means "mélangé", "rearranged" or "reordered" might be clearer
…nto feat-adjust-measure
Updated the way to treat expectation measures.
If the measure doesn't cover the whole registry we pad the pauli string with Identities, or is not ordered (instead of swap gates reorganize the matrix or pauli string).
Isn't necessary for braket with optimize_measure set at true (does the pauli grouping).