Skip to content

Commit

Permalink
Typical NISQ devices as DeviceProperty
Browse files Browse the repository at this point in the history
  • Loading branch information
kwkbtr authored Oct 24, 2024
2 parents 92d0a8c + c9ad227 commit 4ca1ee8
Show file tree
Hide file tree
Showing 5 changed files with 292 additions and 8 deletions.
8 changes: 7 additions & 1 deletion packages/circuit/quri_parts/circuit/gate_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
U1: Literal["U1"] = "U1"
U2: Literal["U2"] = "U2"
U3: Literal["U3"] = "U3"
Measurement: Literal["Measurement"] = "Measurement"

SINGLE_QUBIT_GATE_NAMES: set[SingleQubitGateNameType] = {
Identity,
Expand Down Expand Up @@ -183,6 +182,11 @@ def is_parametric_gate_name(gate_name: str) -> TypeGuard[ParametricGateNameType]
return gate_name in PARAMETRIC_GATE_NAMES


MeasurementGateNameType: TypeAlias = Literal["Measurement"]
Measurement: Literal["Measurement"] = "Measurement"
MEASUREMENT_GATE_NAMES: set[MeasurementGateNameType] = {Measurement}


#: Valid Pauli gate names.
PauliNameType: TypeAlias = Literal["X", "Y", "Z", "Pauli"]

Expand Down Expand Up @@ -242,6 +246,7 @@ def is_pauli_name(gate_name: str) -> TypeGuard[PauliNameType]:
MultiQubitGateNameType,
UnitaryMatrixGateNameType,
ParametricGateNameType,
MeasurementGateNameType,
]

GATE_NAMES: set[GateNameType] = (
Expand All @@ -251,6 +256,7 @@ def is_pauli_name(gate_name: str) -> TypeGuard[PauliNameType]:
| MULTI_QUBIT_GATE_NAMES
| UNITARY_MATRIX_GATE_NAMES
| PARAMETRIC_GATE_NAMES
| MEASUREMENT_GATE_NAMES
)


Expand Down
15 changes: 14 additions & 1 deletion packages/circuit/quri_parts/circuit/topology/square_lattice.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from collections.abc import Mapping, Sequence
from collections.abc import Collection, Mapping, Sequence
from typing import Callable, Optional

from typing_extensions import TypeAlias
Expand Down Expand Up @@ -46,6 +46,19 @@ def __init__(
f"The same coordinates were specified multiple times: {v}."
)
self._coord_to_qubit[v] = k
self._xsize, self._ysize = xsize, ysize

@property
def xsize(self) -> int:
return self._xsize

@property
def ysize(self) -> int:
return self._ysize

@property
def qubits(self) -> Collection[int]:
return self._qubit_to_coord.keys()

def get_qubit(self, coord: Coordinate) -> int:
"""Returns qubit index corresponding to the given coordinate."""
Expand Down
86 changes: 86 additions & 0 deletions packages/core/quri_parts/backend/devices/nisq_iontrap_device.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from collections.abc import Collection
from typing import Optional

import networkx as nx

from quri_parts.backend.device import DeviceProperty, GateProperty, QubitProperty
from quri_parts.backend.units import TimeValue
from quri_parts.circuit import gate_names
from quri_parts.circuit.gate_names import GateNameType
from quri_parts.circuit.transpile import GateSetConversionTranspiler


def generate_device_property(
qubit_count: int,
native_gates: Collection[GateNameType],
gate_error_1q: float,
gate_error_2q: float,
gate_error_meas: float,
gate_time_1q: TimeValue,
gate_time_2q: TimeValue,
gate_time_meas: TimeValue,
t1: Optional[TimeValue] = None,
t2: Optional[TimeValue] = None,
) -> DeviceProperty:
"""Generate DeviceProperty object for a typical NISQ trapped ion device.
Assumes that the device's qubits are all-to-all connected and that a subset of
the gates natively supported by QURI Parts can be used as the native gates.
Args:
qubit_count: Number of qubits.
native_gates: Native gates supported by the device.
gate_error_1q: Error rate of single qubit gate operations.
gate_error_2q: Error rate of two qubit gate operations.
gate_error_meas: Error rate of readout operations.
gate_time_1q: Latency of single qubit gate operations.
gate_time_2q: Latency of two qubit gate operations.
gate_time_meas: Latency of readout operations.
t1: T1 coherence time.
t2: T2 coherence time.
"""

native_gate_set = set(native_gates)
gates_1q = native_gate_set & gate_names.SINGLE_QUBIT_GATE_NAMES
gates_2q = native_gate_set & gate_names.TWO_QUBIT_GATE_NAMES

if gates_1q | gates_2q != native_gate_set:
raise ValueError(
"Only single and two qubit gates are supported as native gates"
)

qubits = list(range(qubit_count))
qubit_properties = {q: QubitProperty() for q in qubits}
gate_properties = [
GateProperty(
gate_names.Measurement,
(),
gate_error=gate_error_meas,
gate_time=gate_time_meas,
)
]
gate_properties.extend(
[
GateProperty(name, (), gate_error=gate_error_1q, gate_time=gate_time_1q)
for name in gates_1q
]
)
gate_properties.extend(
[
GateProperty(name, (), gate_error=gate_error_2q, gate_time=gate_time_2q)
for name in gates_2q
]
)

return DeviceProperty(
qubit_count=qubit_count,
qubits=qubits,
qubit_graph=nx.complete_graph(qubit_count),
qubit_properties=qubit_properties,
native_gates=native_gates,
gate_properties=gate_properties,
physical_qubit_count=qubit_count,
# TODO Calculate backgraound error from t1 and t2
background_error=None,
transpiler=GateSetConversionTranspiler(native_gates),
)
111 changes: 111 additions & 0 deletions packages/core/quri_parts/backend/devices/nisq_spcond_lattice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
from collections.abc import Collection
from typing import Optional

import networkx as nx

from quri_parts.backend.device import DeviceProperty, GateProperty, QubitProperty
from quri_parts.backend.units import TimeValue
from quri_parts.circuit import gate_names
from quri_parts.circuit.gate_names import GateNameType
from quri_parts.circuit.topology import (
SquareLattice,
SquareLatticeSWAPInsertionTranspiler,
)
from quri_parts.circuit.transpile import (
GateSetConversionTranspiler,
SequentialTranspiler,
)


def generate_device_property(
lattice: SquareLattice,
native_gates: Collection[GateNameType],
gate_error_1q: float,
gate_error_2q: float,
gate_error_meas: float,
gate_time_1q: TimeValue,
gate_time_2q: TimeValue,
gate_time_meas: TimeValue,
t1: Optional[TimeValue] = None,
t2: Optional[TimeValue] = None,
) -> DeviceProperty:
"""Generate DeviceProperty object for a typical NISQ superconducting qubit
device.
Assumes that the device's qubits are connected as square lattice with no defects
and that a subset of the gates natively supported by QURI Parts can be used as
the native gates.
Args:
lattice: SquareLattice instance representing the device qubits connectivity.
native_gates: Native gates supported by the device.
gate_error_1q: Error rate of single qubit gate operations.
gate_error_2q: Error rate of two qubit gate operations.
gate_error_meas: Error rate of readout operations.
gate_time_1q: Latency of single qubit gate operations.
gate_time_2q: Latency of two qubit gate operations.
gate_time_meas: Latency of readout operations.
t1: T1 coherence time.
t2: T2 coherence time.
"""

native_gate_set = set(native_gates)
gates_1q = native_gate_set & gate_names.SINGLE_QUBIT_GATE_NAMES
gates_2q = native_gate_set & gate_names.TWO_QUBIT_GATE_NAMES

if gates_1q | gates_2q != native_gate_set:
raise ValueError(
"Only single and two qubit gates are supported as native gates"
)

qubits = list(lattice.qubits)
qubit_count = len(qubits)
qubit_properties = {q: QubitProperty() for q in qubits}
gate_properties = [
GateProperty(
gate_names.Measurement,
(),
gate_error=gate_error_meas,
gate_time=gate_time_meas,
)
]
gate_properties.extend(
[
GateProperty(name, (), gate_error=gate_error_1q, gate_time=gate_time_1q)
for name in gates_1q
]
)
gate_properties.extend(
[
GateProperty(name, (), gate_error=gate_error_2q, gate_time=gate_time_2q)
for name in gates_2q
]
)

graph = nx.grid_2d_graph(lattice.xsize, lattice.ysize)
mapping = {
(x, y): lattice.get_qubit((x, y))
for y in range(lattice.ysize)
for x in range(lattice.xsize)
}
graph = nx.relabel_nodes(graph, mapping)

trans = SequentialTranspiler(
[
SquareLatticeSWAPInsertionTranspiler(lattice),
GateSetConversionTranspiler(native_gates),
]
)

return DeviceProperty(
qubit_count=qubit_count,
qubits=qubits,
qubit_graph=graph,
qubit_properties=qubit_properties,
native_gates=native_gates,
gate_properties=gate_properties,
physical_qubit_count=qubit_count,
# TODO Calculate backgraound error from t1 and t2
background_error=None,
transpiler=trans,
)
80 changes: 74 additions & 6 deletions packages/core/tests/backend/test_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@
estimate_circuit_fidelity,
estimate_circuit_latency,
)
from quri_parts.backend.devices import clifford_t_device, star_device
from quri_parts.backend.devices import (
clifford_t_device,
nisq_iontrap_device,
nisq_spcond_lattice,
star_device,
)
from quri_parts.backend.units import TimeUnit, TimeValue
from quri_parts.circuit import QuantumCircuit, gate_names, gates
from quri_parts.circuit.topology import SquareLattice


def _native_circuit() -> QuantumCircuit:
def _star_native_circuit() -> QuantumCircuit:
circuit = QuantumCircuit(2)
circuit.extend(
[
Expand All @@ -23,7 +29,7 @@ def _native_circuit() -> QuantumCircuit:
return circuit


def _non_native_circuit() -> QuantumCircuit:
def _non_star_native_circuit() -> QuantumCircuit:
circuit = QuantumCircuit(2)
circuit.extend(
[
Expand Down Expand Up @@ -56,13 +62,13 @@ def test_star_device() -> None:
assert math.isclose(2.3302081608722602e-07, error)
assert math.isclose(1.0e3, time.in_ns())

circuit = _native_circuit()
circuit = _star_native_circuit()
assert 0.0 < estimate_circuit_fidelity(circuit, device, background_error=True) < 1.0
assert 0.0 < estimate_circuit_latency(circuit, device).value

assert device.transpiler is not None
assert {
gate.name for gate in device.transpiler(_non_native_circuit()).gates
gate.name for gate in device.transpiler(_non_star_native_circuit()).gates
} <= set(device.native_gates)

assert device.noise_model is not None
Expand Down Expand Up @@ -90,6 +96,68 @@ def test_clifford_t_device() -> None:
assert math.isclose(2.512255650177764e-07, error)
assert math.isclose(1.0e3, time.in_ns())

circuit = _native_circuit()
circuit = _star_native_circuit()
assert 0.0 < estimate_circuit_fidelity(circuit, device, background_error=True) < 1.0
assert 0.0 < estimate_circuit_latency(circuit, device).value


def test_nisq_iontrap_device() -> None:
native_gates: set[gate_names.GateNameType] = {
gate_names.RX,
gate_names.RY,
gate_names.RZ,
gate_names.CZ,
}

device = nisq_iontrap_device.generate_device_property(
qubit_count=16,
native_gates=native_gates,
gate_error_1q=1.0e-5,
gate_error_2q=1.0e-3,
gate_error_meas=1.0e-3,
gate_time_1q=TimeValue(value=100.0, unit=TimeUnit.MICROSECOND),
gate_time_2q=TimeValue(value=500.0, unit=TimeUnit.MICROSECOND),
gate_time_meas=TimeValue(value=1.0, unit=TimeUnit.MILLISECOND), # TODO check
t1=TimeValue(value=10.0, unit=TimeUnit.SECOND),
t2=TimeValue(value=1.0, unit=TimeUnit.SECOND),
)
assert device.qubit_count == 16
assert set(device.native_gates) == native_gates

assert device.transpiler is not None
circuit = device.transpiler(_star_native_circuit())
assert (
0.0 < estimate_circuit_fidelity(circuit, device, background_error=False) < 1.0
)
assert 0.0 < estimate_circuit_latency(circuit, device).value


def test_nisq_spcond_device() -> None:
native_gates: set[gate_names.GateNameType] = {
gate_names.RX,
gate_names.RY,
gate_names.RZ,
gate_names.CZ,
}

device = nisq_spcond_lattice.generate_device_property(
lattice=SquareLattice(4, 4),
native_gates=native_gates,
gate_error_1q=1.0e-4,
gate_error_2q=1.0e-2,
gate_error_meas=1.0e-2,
gate_time_1q=TimeValue(value=500.0, unit=TimeUnit.NANOSECOND),
gate_time_2q=TimeValue(value=500.0, unit=TimeUnit.NANOSECOND),
gate_time_meas=TimeValue(value=500.0, unit=TimeUnit.NANOSECOND), # TODO check
t1=TimeValue(value=200.0, unit=TimeUnit.MICROSECOND),
t2=TimeValue(value=100.0, unit=TimeUnit.MICROSECOND),
)
assert device.qubit_count == 16
assert set(device.native_gates) == native_gates

assert device.transpiler is not None
circuit = device.transpiler(_star_native_circuit())
assert (
0.0 < estimate_circuit_fidelity(circuit, device, background_error=False) < 1.0
)
assert 0.0 < estimate_circuit_latency(circuit, device).value

0 comments on commit 4ca1ee8

Please sign in to comment.