|
12 | 12 | from enum import Enum |
13 | 13 | from dataclasses import dataclass |
14 | 14 | from collections.abc import Iterator |
15 | | -from typing import Any |
| 15 | +from typing import Any, Optional |
16 | 16 | import math |
17 | 17 |
|
18 | 18 | from .event_hook import EventHook |
@@ -300,6 +300,133 @@ def from_iterator(it: Iterator): |
300 | 300 | return FutureRead(qubit=next(it)) |
301 | 301 |
|
302 | 302 |
|
| 303 | +RPP_PYTKET_DEFINITION: Optional["pytket.circuit.CustomGateDef"] = None |
| 304 | + |
| 305 | + |
| 306 | +@dataclass |
| 307 | +class RPP(Operation): |
| 308 | + qubit0: int |
| 309 | + qubit1: int |
| 310 | + theta: float |
| 311 | + phi: float |
| 312 | + |
| 313 | + @staticmethod |
| 314 | + def get_gate_definition() -> "pytket.circuit.CustomGateDef": |
| 315 | + assert PYTKET_AVAILABLE, "pytket is not available" |
| 316 | + import sympy |
| 317 | + |
| 318 | + global RPP_PYTKET_DEFINITION |
| 319 | + if RPP_PYTKET_DEFINITION is not None: |
| 320 | + return RPP_PYTKET_DEFINITION |
| 321 | + theta, phi = sympy.symbols("theta phi") |
| 322 | + subcircuit = pytket.Circuit(2) |
| 323 | + subcircuit.Rz(angle=-phi, qubit=0) |
| 324 | + subcircuit.Rz(angle=-phi, qubit=1) |
| 325 | + subcircuit.XXPhase(angle=theta, qubit0=0, qubit1=1) |
| 326 | + subcircuit.Rz(angle=phi, qubit=0) |
| 327 | + subcircuit.Rz(angle=phi, qubit=1) |
| 328 | + RPP_PYTKET_DEFINITION = pytket.circuit.CustomGateDef.define( |
| 329 | + name="PhasedXX", circ=subcircuit, args=[theta, phi] |
| 330 | + ) |
| 331 | + return RPP_PYTKET_DEFINITION |
| 332 | + |
| 333 | + def append_to_circuit(self, circuit: "pytket.Circuit"): |
| 334 | + assert PYTKET_AVAILABLE, "pytket is not available" |
| 335 | + circuit.add_custom_gate( |
| 336 | + definition=self.get_gate_definition(), |
| 337 | + params=[self.theta / math.pi, self.phi / math.pi], |
| 338 | + qubits=[self.qubit0, self.qubit1], |
| 339 | + ) |
| 340 | + |
| 341 | + def to_dict(self) -> dict: |
| 342 | + return { |
| 343 | + "op": "RPP", |
| 344 | + "qubit0": self.qubit0, |
| 345 | + "qubit1": self.qubit1, |
| 346 | + "theta": self.theta, |
| 347 | + "phi": self.phi, |
| 348 | + } |
| 349 | + |
| 350 | + @staticmethod |
| 351 | + def from_iterator(it: Iterator): |
| 352 | + qubit0 = next(it) |
| 353 | + qubit1 = next(it) |
| 354 | + theta = next(it) |
| 355 | + phi = next(it) |
| 356 | + return RPP(qubit0=qubit0, qubit1=qubit1, theta=theta, phi=phi) |
| 357 | + |
| 358 | + |
| 359 | +@dataclass |
| 360 | +class TK2(Operation): |
| 361 | + qubit0: int |
| 362 | + qubit1: int |
| 363 | + alpha: float |
| 364 | + beta: float |
| 365 | + gamma: float |
| 366 | + |
| 367 | + def append_to_circuit(self, circuit: "pytket.Circuit"): |
| 368 | + assert PYTKET_AVAILABLE, "pytket is not available" |
| 369 | + circuit.tk2( |
| 370 | + angle0=self.alpha / math.pi, |
| 371 | + angle1=self.beta / math.pi, |
| 372 | + angle2=self.gamma / math.pi, |
| 373 | + qubit0=self.qubit0, |
| 374 | + qubit1=self.qubit1, |
| 375 | + ) |
| 376 | + |
| 377 | + def to_dict(self) -> dict: |
| 378 | + return { |
| 379 | + "op": "TK2", |
| 380 | + "qubit0": self.qubit0, |
| 381 | + "qubit1": self.qubit1, |
| 382 | + "alpha": self.alpha, |
| 383 | + "beta": self.beta, |
| 384 | + "gamma": self.gamma, |
| 385 | + } |
| 386 | + |
| 387 | + @staticmethod |
| 388 | + def from_iterator(it: Iterator): |
| 389 | + qubit0 = next(it) |
| 390 | + qubit1 = next(it) |
| 391 | + alpha = next(it) |
| 392 | + beta = next(it) |
| 393 | + gamma = next(it) |
| 394 | + return TK2(qubit0=qubit0, qubit1=qubit1, alpha=alpha, beta=beta, gamma=gamma) |
| 395 | + |
| 396 | + |
| 397 | +@dataclass |
| 398 | +class TwinRXY(Operation): |
| 399 | + qubit0: int |
| 400 | + qubit1: int |
| 401 | + theta: float |
| 402 | + phi: float |
| 403 | + |
| 404 | + def append_to_circuit(self, circuit: "pytket.Circuit"): |
| 405 | + assert PYTKET_AVAILABLE, "pytket is not available" |
| 406 | + circuit.add_gate( |
| 407 | + pytket.OpType.NPhasedX, |
| 408 | + angles=[self.theta / math.pi, self.phi / math.pi], |
| 409 | + qubits=[self.qubit0, self.qubit1], |
| 410 | + ) |
| 411 | + |
| 412 | + def to_dict(self) -> dict: |
| 413 | + return { |
| 414 | + "op": "TwinRXY", |
| 415 | + "qubit0": self.qubit0, |
| 416 | + "qubit1": self.qubit1, |
| 417 | + "theta": self.theta, |
| 418 | + "phi": self.phi, |
| 419 | + } |
| 420 | + |
| 421 | + @staticmethod |
| 422 | + def from_iterator(it: Iterator): |
| 423 | + qubit0 = next(it) |
| 424 | + qubit1 = next(it) |
| 425 | + theta = next(it) |
| 426 | + phi = next(it) |
| 427 | + return TwinRXY(qubit0=qubit0, qubit1=qubit1, theta=theta, phi=phi) |
| 428 | + |
| 429 | + |
303 | 430 | class Source(Enum): |
304 | 431 | """ |
305 | 432 | Selene provides the source of each instruction as an |
@@ -370,6 +497,12 @@ class itself, and it is responsible for advancing the iterator |
370 | 497 | operation = GlobalBarrier.from_iterator(it) |
371 | 498 | case 12: |
372 | 499 | operation = MeasureLeakedRequest.from_iterator(it) |
| 500 | + case 13: |
| 501 | + operation = RPP.from_iterator(it) |
| 502 | + case 14: |
| 503 | + operation = TK2.from_iterator(it) |
| 504 | + case 15: |
| 505 | + operation = TwinRXY.from_iterator(it) |
373 | 506 | if operation is None: |
374 | 507 | raise ValueError(f"Unknown instruction operation index {operation_idx}") |
375 | 508 | return Instruction(source=source, operation=operation) |
|
0 commit comments