Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix default wire mapping and QPU program compilation and add support for parallel execution #124

Closed
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 20 additions & 18 deletions pennylane_rigetti/qc.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"""

from abc import ABC, abstractmethod
from typing import Dict
from typing import Dict, List
from collections import OrderedDict

from pyquil import Program
Expand Down Expand Up @@ -48,8 +48,7 @@ class QuantumComputerDevice(RigettiDevice, ABC):
wires (Iterable[Number, str]): Iterable that contains unique labels for the
qubits as numbers or strings (i.e., ``['q1', ..., 'qN']``).
The number of labels must match the number of qubits accessible on the backend.
If not provided, qubits are addressed as consecutive integers ``[0, 1, ...]``, and their number
is inferred from the backend.
If not provided, qubits are addressed by the backend.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To make sure I understand, does this mean that the user can pass any wires and they'll be mapped to the device wires, or that the user must use the appropriate wires for the device?

active_reset (bool): whether to actively reset qubits instead of waiting for
for qubits to decay to the ground state naturally.
Setting this to ``True`` results in a significantly faster expectation value
Expand Down Expand Up @@ -97,9 +96,8 @@ def __init__(self, device, *, shots=1000, wires=None, active_reset=False, **kwar
self.num_wires = len(self.qc.qubits())

if wires is None:
# infer the number of modes from the device specs
# and use consecutive integer wire labels
wires = range(self.num_wires)
# infer the wires from the device specs
wires = self.qc.qubits()

if isinstance(wires, int):
raise ValueError(
Expand All @@ -113,7 +111,7 @@ def __init__(self, device, *, shots=1000, wires=None, active_reset=False, **kwar
f"cannot be created with {len(wires)} wires."
)

self.wiring = dict(enumerate(self.qc.qubits()))
self.wiring = {q: q for q in sorted(wires)}
self.active_reset = active_reset

super().__init__(wires, shots)
Expand Down Expand Up @@ -180,10 +178,8 @@ def define_wire_map(self, wires):

def apply(self, operations, **kwargs):
"""Applies the given quantum operations."""
prag = Program(Pragma("INITIAL_REWIRING", ['"PARTIAL"']))
AlbertMitjans marked this conversation as resolved.
Show resolved Hide resolved
if self.active_reset:
prag += RESET()
self.prog = prag + self.prog
self.prog = Program(RESET()) + self.prog

if self.parametric_compilation:
self.prog += self.apply_parametric_operations(operations)
Expand All @@ -192,13 +188,14 @@ def apply(self, operations, **kwargs):

rotations = kwargs.get("rotations", [])
self.prog += self.apply_rotations(rotations)

qubits = sorted(self.wiring.values())
ro = self.prog.declare("ro", "BIT", len(qubits))
for i, q in enumerate(qubits):
self.prog.inst(MEASURE(q, ro[i]))

self.prog.wrap_in_numshots_loop(self.shots)
# Measure every qubit used by the program into a readout register.
# Devices don't always have sequentially adressed qubits, so
# we use a normalized value to index them into the readout register.
used_qubits = self.prog.get_qubits(indices=True)
ro = self.prog.declare("ro", "BIT", len(used_qubits))
for i, qubit in enumerate(used_qubits):
self.prog += MEASURE(qubit, ro[i])
MarquessV marked this conversation as resolved.
Show resolved Hide resolved

def apply_parametric_operations(self, operations):
"""Applies a parametric program by applying parametric operation with symbolic parameters.
Expand All @@ -224,7 +221,10 @@ def apply_parametric_operations(self, operations):
# Prepare for parametric compilation
par = []
for param in operation.data:
if getattr(param, "requires_grad", False) and operation.name != "BasisState":
if (
getattr(param, "requires_grad", False)
and operation.name != "BasisState"
):
# Using the idx for trainable parameter objects to specify the
# corresponding symbolic parameter
parameter_string = "theta" + str(id(param))
Expand Down Expand Up @@ -266,7 +266,9 @@ def generate_samples(self):
for region, value in self._parameter_map.items():
self.prog.write_memory(region_name=region, value=value)
# Fetch the compiled program, or compile and store it if it doesn't exist
self._compiled_program = self._compiled_program_dict.get(self.circuit_hash, None)
self._compiled_program = self._compiled_program_dict.get(
self.circuit_hash, None
)
if self._compiled_program is None:
self._compiled_program = self.compile()
self._compiled_program_dict[self.circuit_hash] = self._compiled_program
Expand Down
9 changes: 7 additions & 2 deletions pennylane_rigetti/qpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from pennylane.operation import Tensor
from pennylane.tape import QuantumTape
from pyquil import get_qc
from pyquil.api import QuantumComputer
from pyquil.api import QuantumComputer, QuantumExecutable
from pyquil.experiment import SymmetrizationLevel
from pyquil.operator_estimation import (
Experiment,
Expand Down Expand Up @@ -112,7 +112,12 @@ def __init__(
self.calibrate_readout = calibrate_readout
self._skip_generate_samples = False

super().__init__(device, wires=wires, shots=shots, active_reset=active_reset, **kwargs)
super().__init__(
device, wires=wires, shots=shots, active_reset=active_reset, **kwargs
)

def compile(self) -> QuantumExecutable:
return self.qc.compile(self.prog, protoquil=True)

def get_qc(self, device, **kwargs) -> QuantumComputer:
return get_qc(device, as_qvm=self.as_qvm, **kwargs)
Expand Down