"""A simple example for shifting the qubit positions."""
# Copyright © 2021-2024 HQS Quantum Simulations GmbH.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
# or implied. See the License for the specific language governing permissions and limitations under
# the License.
import numpy as np
import qoqo.operations as ops # type:ignore
from qoqo import Circuit
from qoqo_qryd import SimulatorBackend
from qoqo_qryd import pragma_operations as qrydops
from qoqo_qryd.tweezer_devices import TweezerMutableDevice # type:ignore
from utils import apply_column_square, apply_row
# ------------------------- The set-up of the device -------------------------- #
# Initializing Device with a square lattice
#
# Tweezer positions:
# (0, 0) ----- (0, 1) ----- (0, 2) ----- (0, 3)
# (1, 0) ----- (1, 1) ----- (1, 2) ----- (1, 3)
#
# Qubit positions:
# 0 --- 1
# 2 --- 3
#
rows = 2
columns = 4
device = TweezerMutableDevice()
device.add_layout("square_lattice")
for i in range(rows * columns):
for gate in ["RotateX", "PhaseShiftState1"]:
device.set_tweezer_single_qubit_gate_time(gate, i, 1.0, "square_lattice")
for row in range(rows):
for column in range(columns):
row_indices = apply_row(row, column, columns, rows)
column_indices = apply_column_square(row, column, columns, rows)
if row_indices is not None:
device.set_tweezer_two_qubit_gate_time(
"PhaseShiftedControlledZ",
row_indices[0],
row_indices[1],
1.0,
"square_lattice",
)
if column_indices is not None:
device.set_tweezer_two_qubit_gate_time(
"PhaseShiftedControlledZ",
column_indices[0],
column_indices[1],
1.0,
"square_lattice",
)
device.set_allowed_tweezer_shifts_from_rows(
[[0, 1, 2, 3], [4, 5, 6, 7]], "square_lattice"
)
device.switch_layout("square_lattice", with_trivial_map=False)
# Populate the device according to initialization explained above
device.add_qubit_tweezer_mapping(0, 0)
device.add_qubit_tweezer_mapping(1, 1)
device.add_qubit_tweezer_mapping(2, 4)
device.add_qubit_tweezer_mapping(3, 5)
backend = SimulatorBackend(device)
# ------------------------ The set-up of the circuit ------------------------ #
circuit = Circuit()
# Qubits 1 and 2 are not close enough for interaction in square lattice
circuit += ops.PhaseShiftedControlledZ(control=1, target=2, phi=0.0)
# This should fail
# result = backend.run_circuit(circuit)
# ------------------ The set-up of the circuit with device change --------------- #
circuit = Circuit()
# Qubits 1 and 2 are close enough for interaction in square lattice after shift
# Qubit positions after shift:
# 0 --- 1
# 2 --- 3
circuit += ops.DefinitionComplex("state_vector_before", 16, True)
circuit += ops.DefinitionComplex("state_vector_after", 16, True)
circuit += ops.RotateX(1, np.pi)
circuit += ops.RotateX(2, np.pi / 2)
circuit += ops.PragmaGetStateVector("state_vector_before", None)
circuit += qrydops.PragmaShiftQubitsTweezers([(5, 6)]).to_pragma_change_device()
circuit += qrydops.PragmaShiftQubitsTweezers([(4, 5)]).to_pragma_change_device()
circuit += ops.PhaseShiftedControlledZ(control=1, target=2, phi=0.0)
circuit += ops.PragmaGetStateVector("state_vector_after", None)
# This should pass
result = backend.run_circuit(circuit)
print("State vector before applying shift and two-qubit gate")
print(result[2]["state_vector_before"])
print("State vector after applying shift and two-qubit gate")
print(result[2]["state_vector_after"])
"""A simple example for switching between a square lattice and a triangular lattice layout."""
# Copyright © 2021-2024 HQS Quantum Simulations GmbH.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
# or implied. See the License for the specific language governing permissions and limitations under
# the License.
import numpy as np
import qoqo.operations as ops # type:ignore
from qoqo import Circuit
from qoqo_qryd import SimulatorBackend
from qoqo_qryd import pragma_operations as qrydops
from qoqo_qryd.tweezer_devices import TweezerMutableDevice # type:ignore
from utils import apply_column_square, apply_column_triangular, apply_row
# ------------------------- The set-up of the device -------------------------- #
# Initializing device with a square lattice
#
# Tweezer positions:
# (0, 0) ----- (0, 1) ----- (0, 2) ----- (0, 3)
# (1, 0) ----- (1, 1) ----- (1, 2) ----- (1, 3)
#
# Qubit positions:
# 0 --- 1
# 2 --- 3
#
rows = 2
columns = 4
device = TweezerMutableDevice()
device.add_layout("square_lattice")
for i in range(rows * columns):
for gate in ["RotateX", "PhaseShiftState1"]:
device.set_tweezer_single_qubit_gate_time(gate, i, 1.0, "square_lattice")
for row in range(rows):
for column in range(columns):
row_indices = apply_row(row, column, columns, rows)
column_indices = apply_column_square(row, column, columns, rows)
if row_indices is not None:
device.set_tweezer_two_qubit_gate_time(
"PhaseShiftedControlledPhase",
row_indices[0],
row_indices[1],
1.0,
"square_lattice",
)
if column_indices is not None:
device.set_tweezer_two_qubit_gate_time(
"PhaseShiftedControlledPhase",
column_indices[0],
column_indices[1],
1.0,
"square_lattice",
)
# set the tweezer per row information for the square lattice
# this is needed in order to dynamically switch the device layout later to a triangular lattice
device.set_tweezers_per_row([4, 4], "square_lattice")
# Adding a triangular lattice
#
# Tweezer positions:
# (0, 0) ----- (0, 1) ----- (0, 2) ----- (0, 3)
# (1, 0) ----- (1, 1) ----- (1, 2) ----- (1, 3)
#
# Qubit positions:
# 0 --- 1
# 2 --- 3
device.add_layout("triangular_lattice")
for i in range(rows * columns):
for gate in ["RotateX", "PhaseShiftState1"]:
device.set_tweezer_single_qubit_gate_time(gate, i, 1.0, "triangular_lattice")
for row in range(rows):
for column in range(columns):
row_indices = apply_row(row, column, columns, rows)
column_indices = apply_column_triangular(row, column, columns, rows)
if row_indices is not None:
device.set_tweezer_two_qubit_gate_time(
"PhaseShiftedControlledZ",
row_indices[0],
row_indices[1],
1.0,
"triangular_lattice",
)
if column_indices is not None:
for column_index in column_indices:
device.set_tweezer_two_qubit_gate_time(
"PhaseShiftedControlledZ",
column_index[0],
column_index[1],
1.0,
"triangular_lattice",
)
device.set_tweezers_per_row([4, 4], "triangular_lattice")
# After adding the layout info currently, we switch to the square lattice
# and populate the device
device.switch_layout("square_lattice", with_trivial_map=False)
# Populate the device according to initialization explained above
device.add_qubit_tweezer_mapping(0, 0)
device.add_qubit_tweezer_mapping(1, 1)
device.add_qubit_tweezer_mapping(2, 4)
device.add_qubit_tweezer_mapping(3, 5)
backend = SimulatorBackend(device)
# ----------------------- The set-up of the circuit -------------------------- #
circuit = Circuit()
# Qubits 0 and 3 are not close enough
# for interaction in square lattice
circuit += ops.PhaseShiftedControlledZ(control=0, target=3, phi=0.0)
# This should fail
# result = backend.run_circuit(circuit)
# ----------------- The set-up of the circuit with device change --------------- #
circuit = Circuit()
circuit += ops.DefinitionComplex("state_vector_before", 16, True)
circuit += ops.DefinitionComplex("state_vector_after", 16, True)
circuit += ops.RotateX(0, np.pi)
circuit += ops.RotateX(3, np.pi / 2)
circuit += ops.PragmaGetStateVector("state_vector_before", None)
# Qubits 0 and 2 are close enough for interaction in square lattice
circuit += qrydops.PragmaSwitchDeviceLayout(
"triangular_lattice"
).to_pragma_change_device()
circuit += ops.PhaseShiftedControlledZ(control=0, target=3, phi=0.0)
circuit += ops.PragmaGetStateVector("state_vector_after", None)
# This should pass
result = backend.run_circuit(circuit)
print("State vector before applying shift and two-qubit gate")
print(result[2]["state_vector_before"])
print("State vector after applying shift and two-qubit gate")
print(result[2]["state_vector_after"])
"""A simple example demonstrating multi qubit operations.
Kept as past reference, as there are no multi-qubit operations that are natively
supported by TweezerDevice and TweezerMutableDevice."""
# Copyright © 2021 - 2024 HQS Quantum Simulations GmbH.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
# or implied. See the License for the specific language governing permissions and limitations under
# the License.
import numpy as np
import qoqo.operations as ops # type:ignore
from qoqo import Circuit
from qoqo_qryd import SimulatorBackend, qryd_devices
from qoqo_qryd import pragma_operations as qrydops # type:ignore
# --------------------- The set-up of the device ----------------------- #
# Initializing Device with a square lattice
#
# Tweezer positions:
# (0, 0) ----- (0, 1) ----- (0, 2) ----- (0, 3)
# (1, 0) ----- (1, 1) ----- (1, 2) ----- (1, 3)
# (2, 0) ----- (2, 1) ----- (2, 2) ----- (2, 3)
#
# Qubit positions:
# 0 --- 1 --- 2 --- 3
# 4 --- 5 --- 6 --- 7
# 8 --- 9 --- 10 --- 11
#
device = qryd_devices.FirstDevice(
number_rows=3,
number_columns=4,
qubits_per_row=[4, 4, 4],
row_distance=1.0,
initial_layout=np.array(
[[0.0, 1.0, 2.0, 3.0], [0.0, 1.0, 2.0, 3.0], [0.0, 1.0, 2.0, 3.0]]
),
)
backend = SimulatorBackend(device)
# ---------------- Multi Qubit Circuits that will fail ---------------------- #
# For the Prototype we assume that only MultiQubitZZ operations
# are allowed between qubits in one row.
# This is an arbitrary limitation implemented to showcase
# how a restricted operation would be implemented
# in the final device.
# Use a MultiQubitMS Molemer-Sorensen Gate
circuit = Circuit()
# MultiQubitMS not supported natively
circuit += ops.MultiQubitMS(qubits=[0, 1, 2, 3], theta=1.0)
# This should fail
# result = backend.run_circuit(circuit)
# Use a MultiQubitZZ Gate but in the wrong direction
circuit = Circuit()
# MultiQubitZZ not supported along a column
circuit += ops.MultiQubitZZ(qubits=[0, 4, 8], theta=1.0)
# This should fail
# result = backend.run_circuit(circuit)
# --------------------- Working Multi Qubit Circuit ------------------------- #
circuit = Circuit()
circuit += ops.DefinitionBit("ro", 12, True)
circuit += ops.MultiQubitZZ(qubits=[0, 1, 2, 3], theta=1.0)
circuit += ops.PragmaRepeatedMeasurement("ro", 1, None)
# This should pass
result = backend.run_circuit(circuit)
print("Result of ZZ on first four qubits")
print(result)