Skip to content

Commit

Permalink
allow circuits with mixed MP types
Browse files Browse the repository at this point in the history
  • Loading branch information
lillian542 committed Nov 22, 2023
1 parent b6af5a7 commit 61c23a5
Showing 1 changed file with 82 additions and 62 deletions.
144 changes: 82 additions & 62 deletions pennylane_qiskit/qiskit_device2.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,38 +133,38 @@ def split_measurement_types(
Qiskit Sampler, ExpectationValue and Variance will use the Estimator, and other
strictly sample-based measurements will use the standard backend.run function"""

use_sampler = [mp for mp in tape.measurements if isinstance(mp, ProbabilityMP)]
use_estimator = [mp for mp in tape.measurements if isinstance(mp, (ExpectationMP, VarianceMP))]
other = [mp for mp in tape.measurements if not isinstance(mp, (ProbabilityMP, ExpectationMP, VarianceMP))]
estimator = []
sampler = []
no_prim = []

Check warning on line 138 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L136-L138

Added lines #L136 - L138 were not covered by tests

for i, mp in enumerate(tape.measurements):
if isinstance(mp, (ExpectationMP, VarianceMP)):
estimator.append((mp, i))
elif isinstance(mp, ProbabilityMP):
sampler.append((mp, i))

Check warning on line 144 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L140-L144

Added lines #L140 - L144 were not covered by tests
else:
no_prim.append((mp, i))

Check warning on line 146 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L146

Added line #L146 was not covered by tests

output_tapes = []
if use_sampler:
output_tapes.append(qml.tape.QuantumScript(tape.operations, use_sampler, shots=tape.shots))
if use_estimator:
output_tapes.append(qml.tape.QuantumScript(tape.operations, use_estimator, shots=tape.shots))
if other:
output_tapes.append(qml.tape.QuantumScript(tape.operations, other, shots=tape.shots))
order_indices = [[i for mp, i in group] for group in [estimator, sampler, no_prim]]

Check warning on line 148 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L148

Added line #L148 was not covered by tests

return tuple(output_tapes), null_postprocessing
tapes = []
if estimator:
tapes.extend([qml.tape.QuantumScript(tape.operations, measurements=[mp for mp, i in estimator])])
if sampler:
tapes.extend([qml.tape.QuantumScript(tape.operations, measurements=[mp for mp, i in sampler])])
if no_prim:
tapes.extend([qml.tape.QuantumScript(tape.operations, measurements=[mp for mp, i in no_prim])])

Check warning on line 156 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L150-L156

Added lines #L150 - L156 were not covered by tests

@transform
def validate_measurement_types(
tape: qml.tape.QuantumTape,
) -> (Sequence[qml.tape.QuantumTape], Callable):
"""Temporary transform instead of split_measurement_types - until correct splitting of
types is implemented, only allow certain groupings of types to catch invalid circuits before executing"""
def reorder_fn(res):

Check warning on line 158 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L158

Added line #L158 was not covered by tests
"""re-order the output to the original shape and order"""
result = {}

Check warning on line 160 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L160

Added line #L160 was not covered by tests

measurement_types = set(type(mp) for mp in tape.measurements)
for idx, r in zip(order_indices, res):
result.update({k: v for k, v in zip(idx, r)})

Check notice on line 163 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

codefactor.io / CodeFactor

pennylane_qiskit/qiskit_device2.py#L163

Unnecessary use of a comprehension, use dict(zip(idx, r)) instead. (unnecessary-comprehension)

Check warning on line 163 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L162-L163

Added lines #L162 - L163 were not covered by tests

if measurement_types.issubset({ExpectationMP, VarianceMP}):
return (tape,), null_postprocessing
if measurement_types.issubset({ProbabilityMP}):
return (tape,), null_postprocessing
return tuple([result[i] for i in sorted(result.keys())])

Check notice on line 165 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

codefactor.io / CodeFactor

pennylane_qiskit/qiskit_device2.py#L165

Consider using a generator instead 'tuple(result[i] for i in sorted(result.keys()))' (consider-using-generator)

Check warning on line 165 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L165

Added line #L165 was not covered by tests

# combination of Probability, Expectation and Variance other than the ones above not allowed
if measurement_types.intersection({ProbabilityMP, ExpectationMP, VarianceMP}):
raise RuntimeError("Bad measurement combination")
return (tape,), null_postprocessing
return tapes, reorder_fn

Check warning on line 167 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L167

Added line #L167 was not covered by tests


def circuit_to_qiskit(circuit, register_size, diagonalize=True, measure=True):

Check warning on line 170 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L170

Added line #L170 was not covered by tests
Expand Down Expand Up @@ -348,12 +348,6 @@ def num_wires(self):
return len(self.wires)

Check warning on line 348 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L346-L348

Added lines #L346 - L348 were not covered by tests

def reset(self):
# Reset only internal data, not the options that are determined on
# device creation
self._reg = QuantumRegister(self.num_wires, "q")
self._creg = ClassicalRegister(self.num_wires, "c")
self._circuit = QuantumCircuit(self._reg, self._creg, name="temp")

self._current_job = None

Check warning on line 351 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L350-L351

Added lines #L350 - L351 were not covered by tests

def stopping_condition(self, op: qml.operation.Operator) -> bool:

Check warning on line 353 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L353

Added line #L353 was not covered by tests
Expand Down Expand Up @@ -401,9 +395,11 @@ def preprocess(
validate_observables, stopping_condition=self.observable_stopping_condition, name=self.name
)

transform_program.add_transform(broadcast_expand)
# transform_program.add_transform(split_measurement_types)
transform_program.add_transform(validate_measurement_types)
if self._use_primitives:
transform_program.add_transform(split_measurement_types)

Check warning on line 399 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L398-L399

Added lines #L398 - L399 were not covered by tests

# transform_program.add_transform(broadcast_expand)
# missing: split non-commuting, sum_expand, etc.

return transform_program, config

Check warning on line 404 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L404

Added line #L404 was not covered by tests

Check notice on line 405 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

codefactor.io / CodeFactor

pennylane_qiskit/qiskit_device2.py#L405

Missing function or method docstring (missing-function-docstring)
Expand Down Expand Up @@ -432,46 +428,68 @@ def execute(
execution_config: ExecutionConfig = DefaultExecutionConfig,
) -> Result_or_ResultBatch:

first_measurement = circuits[0].measurements[0]
if not self._use_primitives:
results = self._execute_runtime_service(circuits)
return results

Check warning on line 433 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L431-L433

Added lines #L431 - L433 were not covered by tests

results = []

Check warning on line 435 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L435

Added line #L435 was not covered by tests

estimator_circuits = [circ for circ in circuits if isinstance(circ.measurements[0], (ExpectationMP, VarianceMP))]
sampler_circuits = [circ for circ in circuits if isinstance(circ.measurements[0], ProbabilityMP)]
other_circuts = [circ for circ in circuits if not isinstance(circ.measurements[0], (ExpectationMP, VarianceMP, ProbabilityMP))]

Check warning on line 439 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L437-L439

Added lines #L437 - L439 were not covered by tests

if isinstance(first_measurement, (ExpectationMP, VarianceMP)) and self._use_primitives:
if estimator_circuits:

Check warning on line 441 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L441

Added line #L441 was not covered by tests
# get results from Estimator
results = self._execute_estimator(circuits)
elif isinstance(first_measurement, ProbabilityMP) and self._use_primitives:
result = self._execute_estimator(estimator_circuits)
results.append(result)
if sampler_circuits:

Check warning on line 445 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L443-L445

Added lines #L443 - L445 were not covered by tests
# get results from Sampler
results = self._execute_sampler(circuits)
else:
# the old run_service execution (this is the only option sample-based measurements)
qcirc = [circuit_to_qiskit(circ, self.num_wires, diagonalize=True, measure=True) for circ in circuits]
compiled_circuits = self.compile_circuits(qcirc)
result = self._execute_sampler(sampler_circuits)
results.append(result)
if other_circuts:

Check warning on line 449 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L447-L449

Added lines #L447 - L449 were not covered by tests
# still use the old run_service execution (this is the only option sample-based measurements)
result = self._execute_runtime_service(other_circuts)
results.append(result)

Check warning on line 452 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L451-L452

Added lines #L451 - L452 were not covered by tests

program_inputs = {"circuits": compiled_circuits, "shots": self.shots.total_shots}
return results

Check warning on line 454 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L454

Added line #L454 was not covered by tests

# for kwarg in self.kwargs:
# program_inputs[kwarg] = self.kwargs.get(kwarg)
def _execute_runtime_service(self, circuits):

Check warning on line 456 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L456

Added line #L456 was not covered by tests
"""Execution using old runtime_service (can't use runtime sessions)"""

options = {"backend": self.backend.name}
print(f"using old runtime services, with circuits {circuits}")

Check warning on line 459 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L459

Added line #L459 was not covered by tests

# Send circuits to the cloud for execution by the circuit-runner program.
job = self.runtime_service.run(
program_id="circuit-runner", options=options, inputs=program_inputs
)
self._current_job = job.result(decoder=RunnerResult)
qcirc = [circuit_to_qiskit(circ, self.num_wires, diagonalize=True, measure=True) for circ in circuits]
compiled_circuits = self.compile_circuits(qcirc)

Check warning on line 462 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L461-L462

Added lines #L461 - L462 were not covered by tests

program_inputs = {"circuits": compiled_circuits, "shots": self.shots.total_shots}

Check warning on line 464 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L464

Added line #L464 was not covered by tests

# for kwarg in self.kwargs:
# program_inputs[kwarg] = self.kwargs.get(kwarg)

options = {"backend": self.backend.name}

Check warning on line 469 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L469

Added line #L469 was not covered by tests

# Send circuits to the cloud for execution by the circuit-runner program.
job = self.runtime_service.run(

Check warning on line 472 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L472

Added line #L472 was not covered by tests
program_id="circuit-runner", options=options, inputs=program_inputs
)
self._current_job = job.result(decoder=RunnerResult)

Check warning on line 475 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L475

Added line #L475 was not covered by tests

results = []
results = []

Check warning on line 477 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L477

Added line #L477 was not covered by tests

for index, circuit in enumerate(circuits):
self._samples = self.generate_samples(index)
res = [mp.process_samples(self._samples, wire_order=self.wires) for mp in circuit.measurements]
single_measurement = len(circuit.measurements) == 1
res = res[0] if single_measurement else tuple(res)
results.append(res)
for index, circuit in enumerate(circuits):
self._samples = self.generate_samples(index)
res = [mp.process_samples(self._samples, wire_order=self.wires) for mp in circuit.measurements]
single_measurement = len(circuit.measurements) == 1
res = res[0] if single_measurement else tuple(res)
results.append(res)

Check warning on line 484 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L479-L484

Added lines #L479 - L484 were not covered by tests

return results

Check warning on line 486 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L486

Added line #L486 was not covered by tests

def _execute_sampler(self, circuits):

Check warning on line 488 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L488

Added line #L488 was not covered by tests
"""Execution for the Sampler primitive"""

print(f"using sampler, with circuits {circuits}")

Check warning on line 491 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L491

Added line #L491 was not covered by tests

qcirc = [circuit_to_qiskit(circ, self.num_wires, diagonalize=True, measure=True) for circ in circuits]
results = []

Check warning on line 494 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L493-L494

Added lines #L493 - L494 were not covered by tests

Expand All @@ -486,6 +504,8 @@ def _execute_sampler(self, circuits):

def _execute_estimator(self, circuits):

Check warning on line 505 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L505

Added line #L505 was not covered by tests

print(f"using estimator, with circuits {circuits}")

Check warning on line 507 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L507

Added line #L507 was not covered by tests

# initially we assume only one measurement per tape
qcirc = [circuit_to_qiskit(circ, self.num_wires, diagonalize=False, measure=False) for circ in circuits]
results = []

Check warning on line 511 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L510-L511

Added lines #L510 - L511 were not covered by tests
Expand All @@ -497,8 +517,8 @@ def _execute_estimator(self, circuits):
pauli_observables = [mp_to_pauli(mp, self.num_wires) for mp in circ.observables]
result = estimator.run([qc]*len(pauli_observables), pauli_observables).result()
result = self._process_estimator_job(circ.measurements, result)
results.append(result)
# raise NotImplementedError
results.extend(result)

Check warning on line 520 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L516-L520

Added lines #L516 - L520 were not covered by tests

return results

Check warning on line 522 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L522

Added line #L522 was not covered by tests

def _process_estimator_job(self, measurements, job_result):

Check warning on line 524 in pennylane_qiskit/qiskit_device2.py

View check run for this annotation

Codecov / codecov/patch

pennylane_qiskit/qiskit_device2.py#L524

Added line #L524 was not covered by tests
Expand Down

0 comments on commit 61c23a5

Please sign in to comment.