|
12 | 12 |
|
13 | 13 | """Module containing multi-controlled circuits synthesis with and without ancillary qubits.""" |
14 | 14 |
|
| 15 | +from __future__ import annotations |
15 | 16 | from math import ceil |
16 | 17 | import numpy as np |
17 | 18 |
|
18 | | -from qiskit.circuit import QuantumRegister |
19 | | -from qiskit.circuit.quantumcircuit import QuantumCircuit |
| 19 | +from qiskit.exceptions import QiskitError |
| 20 | +from qiskit.circuit.quantumcircuit import QuantumCircuit, QuantumRegister, AncillaRegister |
20 | 21 | from qiskit.circuit.library.standard_gates import ( |
21 | 22 | HGate, |
22 | 23 | CU1Gate, |
@@ -305,6 +306,350 @@ def synth_mcx_noaux_v24(num_ctrl_qubits: int) -> QuantumCircuit: |
305 | 306 | return qc |
306 | 307 |
|
307 | 308 |
|
| 309 | +def _linear_depth_ladder_ops(num_ladder_qubits: int) -> tuple[QuantumCircuit, list[int]]: |
| 310 | + r""" |
| 311 | + Helper function to create linear-depth ladder operations used in Khattar and Gidney's MCX synthesis. |
| 312 | + In particular, this implements Step-1 and Step-2 on Fig. 3 of [1] except for the first and last |
| 313 | + CCX gates. |
| 314 | +
|
| 315 | + Args: |
| 316 | + num_ladder_qubits: No. of qubits involved in the ladder operation. |
| 317 | +
|
| 318 | + Returns: |
| 319 | + A tuple consisting of the linear-depth ladder circuit and the index of control qubit to |
| 320 | + apply the final CCX gate. |
| 321 | +
|
| 322 | + Raises: |
| 323 | + QiskitError: If num_ladder_qubits <= 2. |
| 324 | +
|
| 325 | + References: |
| 326 | + 1. Khattar and Gidney, Rise of conditionally clean ancillae for optimizing quantum circuits |
| 327 | + `arXiv:2407.17966 <https://arxiv.org/abs/2407.17966>`__ |
| 328 | + """ |
| 329 | + |
| 330 | + if num_ladder_qubits <= 2: |
| 331 | + raise QiskitError("n_ctrls >= 3 to use MCX ladder. Otherwise, use CCX") |
| 332 | + |
| 333 | + n = num_ladder_qubits + 1 |
| 334 | + qc = QuantumCircuit(n) |
| 335 | + qreg = list(range(n)) |
| 336 | + |
| 337 | + # up-ladder |
| 338 | + for i in range(2, n - 2, 2): |
| 339 | + qc.ccx(qreg[i + 1], qreg[i + 2], qreg[i]) |
| 340 | + qc.x(qreg[i]) |
| 341 | + |
| 342 | + # down-ladder |
| 343 | + if n % 2 != 0: |
| 344 | + a, b, target = n - 3, n - 5, n - 6 |
| 345 | + else: |
| 346 | + a, b, target = n - 1, n - 4, n - 5 |
| 347 | + |
| 348 | + if target > 0: |
| 349 | + qc.ccx(qreg[a], qreg[b], qreg[target]) |
| 350 | + qc.x(qreg[target]) |
| 351 | + |
| 352 | + for i in range(target, 2, -2): |
| 353 | + qc.ccx(qreg[i], qreg[i - 1], qreg[i - 2]) |
| 354 | + qc.x(qreg[i - 2]) |
| 355 | + |
| 356 | + mid_second_ctrl = 1 + max(0, 6 - n) |
| 357 | + final_ctrl = qreg[mid_second_ctrl] - 1 |
| 358 | + return qc, final_ctrl |
| 359 | + |
| 360 | + |
| 361 | +def synth_mcx_1_kg24(num_ctrl_qubits: int, clean: bool = True) -> QuantumCircuit: |
| 362 | + r""" |
| 363 | + Synthesize a multi-controlled X gate with :math:`k` controls using :math:`1` ancillary qubit as |
| 364 | + described in Sec. 5 of [1]. |
| 365 | +
|
| 366 | + Args: |
| 367 | + num_ctrl_qubits: The number of control qubits. |
| 368 | + clean: If True, the ancilla is clean, otherwise it is dirty. |
| 369 | +
|
| 370 | + Returns: |
| 371 | + The synthesized quantum circuit. |
| 372 | +
|
| 373 | + Raises: |
| 374 | + QiskitError: If num_ctrl_qubits <= 2. |
| 375 | +
|
| 376 | + References: |
| 377 | + 1. Khattar and Gidney, Rise of conditionally clean ancillae for optimizing quantum circuits |
| 378 | + `arXiv:2407.17966 <https://arxiv.org/abs/2407.17966>`__ |
| 379 | + """ |
| 380 | + |
| 381 | + if num_ctrl_qubits <= 2: |
| 382 | + raise QiskitError("kg24 synthesis requires at least 3 control qubits. Use CCX directly.") |
| 383 | + |
| 384 | + q_controls = QuantumRegister(num_ctrl_qubits, name="ctrl") |
| 385 | + q_target = QuantumRegister(1, name="targ") |
| 386 | + q_ancilla = AncillaRegister(1, name="anc") |
| 387 | + qc = QuantumCircuit(q_controls, q_target, q_ancilla, name="mcx_linear_depth") |
| 388 | + |
| 389 | + ladder_ops, final_ctrl = _linear_depth_ladder_ops(num_ctrl_qubits) |
| 390 | + qc.ccx(q_controls[0], q_controls[1], q_ancilla) # # create cond. clean ancilla |
| 391 | + qc.compose(ladder_ops, q_ancilla[:] + q_controls[:], inplace=True) # up-ladder |
| 392 | + qc.ccx(q_ancilla, q_controls[final_ctrl], q_target) # # target |
| 393 | + qc.compose( # # down-ladder |
| 394 | + ladder_ops.inverse(), |
| 395 | + q_ancilla[:] + q_controls[:], |
| 396 | + inplace=True, |
| 397 | + ) |
| 398 | + qc.ccx(q_controls[0], q_controls[1], q_ancilla) |
| 399 | + |
| 400 | + if not clean: |
| 401 | + # perform toggle-detection if ancilla is dirty |
| 402 | + qc.compose(ladder_ops, q_ancilla[:] + q_controls[:], inplace=True) |
| 403 | + qc.ccx(q_ancilla, q_controls[final_ctrl], q_target) |
| 404 | + qc.compose(ladder_ops.inverse(), q_ancilla[:] + q_controls[:], inplace=True) |
| 405 | + |
| 406 | + return qc |
| 407 | + |
| 408 | + |
| 409 | +def synth_mcx_1_clean_kg24(num_ctrl_qubits: int) -> QuantumCircuit: |
| 410 | + r""" |
| 411 | + Synthesize a multi-controlled X gate with :math:`k` controls using :math:`1` clean ancillary qubit |
| 412 | + producing a circuit with :math:`2k-3` Toffoli gates and depth :math:`O(k)` as described in |
| 413 | + Sec. 5.1 of [1]. |
| 414 | +
|
| 415 | + Args: |
| 416 | + num_ctrl_qubits: The number of control qubits. |
| 417 | +
|
| 418 | + Returns: |
| 419 | + The synthesized quantum circuit. |
| 420 | +
|
| 421 | + Raises: |
| 422 | + QiskitError: If num_ctrl_qubits <= 2. |
| 423 | +
|
| 424 | + References: |
| 425 | + 1. Khattar and Gidney, Rise of conditionally clean ancillae for optimizing quantum circuits |
| 426 | + `arXiv:2407.17966 <https://arxiv.org/abs/2407.17966>`__ |
| 427 | + """ |
| 428 | + |
| 429 | + return synth_mcx_1_kg24(num_ctrl_qubits, clean=True) |
| 430 | + |
| 431 | + |
| 432 | +def synth_mcx_1_dirty_kg24(num_ctrl_qubits: int) -> QuantumCircuit: |
| 433 | + r""" |
| 434 | + Synthesize a multi-controlled X gate with :math:`k` controls using :math:`1` dirty ancillary qubit |
| 435 | + producing a circuit with :math:`4k-8` Toffoli gates and depth :math:`O(k)` as described in |
| 436 | + Sec. 5.3 of [1]. |
| 437 | +
|
| 438 | + Args: |
| 439 | + num_ctrl_qubits: The number of control qubits. |
| 440 | +
|
| 441 | + Returns: |
| 442 | + The synthesized quantum circuit. |
| 443 | +
|
| 444 | + Raises: |
| 445 | + QiskitError: If num_ctrl_qubits <= 2. |
| 446 | +
|
| 447 | + References: |
| 448 | + 1. Khattar and Gidney, Rise of conditionally clean ancillae for optimizing quantum circuits |
| 449 | + `arXiv:2407.17966 <https://arxiv.org/abs/2407.17966>`__ |
| 450 | + """ |
| 451 | + |
| 452 | + return synth_mcx_1_kg24(num_ctrl_qubits, clean=False) |
| 453 | + |
| 454 | + |
| 455 | +def _n_parallel_ccx_x(n: int) -> QuantumCircuit: |
| 456 | + r""" |
| 457 | + Construct a quantum circuit for creating n-condionally clean ancillae using 3n qubits. This |
| 458 | + implements Fig. 4a of [1]. The order of returned qubits is qr_a, qr_a, qr_target. |
| 459 | +
|
| 460 | + Args: |
| 461 | + n: Number of conditionally clean ancillae to create. |
| 462 | +
|
| 463 | + Returns: |
| 464 | + QuantumCircuit: The quantum circuit for creating n-condionally clean ancillae. |
| 465 | +
|
| 466 | + References: |
| 467 | + 1. Khattar and Gidney, Rise of conditionally clean ancillae for optimizing quantum circuits |
| 468 | + `arXiv:2407.17966 <https://arxiv.org/abs/2407.17966>`__ |
| 469 | + """ |
| 470 | + |
| 471 | + n_qubits = 3 * n |
| 472 | + q = QuantumRegister(n_qubits, name="q") |
| 473 | + qc = QuantumCircuit(q, name=f"ccxn_{n}") |
| 474 | + qr_a, qr_b, qr_target = q[:n], q[n : 2 * n], q[2 * n :] |
| 475 | + qc.x(qr_target) |
| 476 | + qc.ccx(qr_a, qr_b, qr_target) |
| 477 | + |
| 478 | + return qc |
| 479 | + |
| 480 | + |
| 481 | +def _build_logn_depth_ccx_ladder( |
| 482 | + ancilla_idx: int, ctrls: list[int], skip_cond_clean: bool = False |
| 483 | +) -> tuple[QuantumCircuit, list[int]]: |
| 484 | + r""" |
| 485 | + Helper function to build a log-depth ladder compose of CCX and X gates as shown in Fig. 4b of [1]. |
| 486 | +
|
| 487 | + Args: |
| 488 | + ancilla_idx: Index of the ancillary qubit. |
| 489 | + ctrls: List of control qubits. |
| 490 | + skip_cond_clean: If True, do not include the conditionally clean ancilla (step 1 and 5 in |
| 491 | + Fig. 4b of [1]). |
| 492 | +
|
| 493 | + Returns: |
| 494 | + A tuple consisting of the log-depth ladder circuit of conditionally clean ancillae and the |
| 495 | + list of indices of control qubit to apply the linear-depth MCX gate. |
| 496 | +
|
| 497 | + Raises: |
| 498 | + QiskitError: If no. of qubits in parallel CCX + X gates are not the same. |
| 499 | +
|
| 500 | + References: |
| 501 | + 1. Khattar and Gidney, Rise of conditionally clean ancillae for optimizing quantum circuits |
| 502 | + `arXiv:2407.17966 <https://arxiv.org/abs/2407.17966>`__ |
| 503 | + """ |
| 504 | + |
| 505 | + qc = QuantumCircuit(len(ctrls) + 1) |
| 506 | + anc = [ancilla_idx] |
| 507 | + final_ctrls = [] |
| 508 | + |
| 509 | + while len(ctrls) > 1: |
| 510 | + next_batch_len = min(len(anc) + 1, len(ctrls)) |
| 511 | + ctrls, nxt_batch = ctrls[next_batch_len:], ctrls[:next_batch_len] |
| 512 | + new_anc = [] |
| 513 | + while len(nxt_batch) > 1: |
| 514 | + ccx_n = len(nxt_batch) // 2 |
| 515 | + st = int(len(nxt_batch) % 2) |
| 516 | + ccx_x, ccx_y, ccx_t = ( |
| 517 | + nxt_batch[st : st + ccx_n], |
| 518 | + nxt_batch[st + ccx_n :], |
| 519 | + anc[-ccx_n:], |
| 520 | + ) |
| 521 | + if not len(ccx_x) == len(ccx_y) == ccx_n >= 1: |
| 522 | + raise QiskitError( |
| 523 | + f"Invalid CCX gate parameters: {len(ccx_x)=} != {len(ccx_y)=} != {len(ccx_n)=}" |
| 524 | + ) |
| 525 | + if ccx_t != [ancilla_idx]: |
| 526 | + qc.compose(_n_parallel_ccx_x(ccx_n), ccx_x + ccx_y + ccx_t, inplace=True) |
| 527 | + else: |
| 528 | + if not skip_cond_clean: |
| 529 | + qc.ccx(ccx_x[0], ccx_y[0], ccx_t[0]) # # create conditionally clean ancilla |
| 530 | + new_anc += nxt_batch[st:] # # newly created cond. clean ancilla |
| 531 | + nxt_batch = ccx_t + nxt_batch[:st] |
| 532 | + anc = anc[:-ccx_n] |
| 533 | + |
| 534 | + anc = sorted(anc + new_anc) |
| 535 | + final_ctrls += nxt_batch |
| 536 | + |
| 537 | + final_ctrls += ctrls |
| 538 | + final_ctrls = sorted(final_ctrls) |
| 539 | + return qc, final_ctrls[:-1] # exclude ancilla |
| 540 | + |
| 541 | + |
| 542 | +def synth_mcx_2_kg24(num_ctrl_qubits: int, clean: bool = True) -> QuantumCircuit: |
| 543 | + r""" |
| 544 | + Synthesize a multi-controlled X gate with :math:`k` controls using :math:`2` ancillary qubits. |
| 545 | + as described in Sec. 5 of [1]. |
| 546 | +
|
| 547 | + Args: |
| 548 | + num_ctrl_qubits: The number of control qubits. |
| 549 | + clean: If True, the ancilla is clean, otherwise it is dirty. |
| 550 | +
|
| 551 | + Returns: |
| 552 | + The synthesized quantum circuit. |
| 553 | +
|
| 554 | + Raises: |
| 555 | + QiskitError: If num_ctrl_qubits <= 2. |
| 556 | +
|
| 557 | + References: |
| 558 | + 1. Khattar and Gidney, Rise of conditionally clean ancillae for optimizing quantum circuits |
| 559 | + `arXiv:2407.17966 <https://arxiv.org/abs/2407.17966>`__ |
| 560 | + """ |
| 561 | + |
| 562 | + if num_ctrl_qubits <= 2: |
| 563 | + raise QiskitError("kg24 synthesis requires at least 3 control qubits. Use CCX directly.") |
| 564 | + |
| 565 | + q_control = QuantumRegister(num_ctrl_qubits, name="ctrl") |
| 566 | + q_target = QuantumRegister(1, name="targ") |
| 567 | + q_ancilla = AncillaRegister(2, name="anc") |
| 568 | + qc = QuantumCircuit(q_control, q_target, q_ancilla, name="mcx_logn_depth") |
| 569 | + |
| 570 | + ladder_ops, final_ctrls = _build_logn_depth_ccx_ladder( |
| 571 | + num_ctrl_qubits, list(range(num_ctrl_qubits)) |
| 572 | + ) |
| 573 | + qc.compose(ladder_ops, q_control[:] + [q_ancilla[0]], inplace=True) |
| 574 | + if len(final_ctrls) == 1: # Already a toffoli |
| 575 | + qc.ccx(q_ancilla[0], q_control[final_ctrls[0]], q_target) |
| 576 | + else: |
| 577 | + mid_mcx = synth_mcx_1_clean_kg24(len(final_ctrls) + 1) |
| 578 | + qc.compose( |
| 579 | + mid_mcx, |
| 580 | + [q_ancilla[0]] |
| 581 | + + q_control[final_ctrls] |
| 582 | + + q_target[:] |
| 583 | + + [q_ancilla[1]], # ctrls, targ, anc |
| 584 | + inplace=True, |
| 585 | + ) |
| 586 | + qc.compose(ladder_ops.inverse(), q_control[:] + [q_ancilla[0]], inplace=True) |
| 587 | + |
| 588 | + if not clean: |
| 589 | + # perform toggle-detection if ancilla is dirty |
| 590 | + ladder_ops_new, final_ctrls = _build_logn_depth_ccx_ladder( |
| 591 | + num_ctrl_qubits, list(range(num_ctrl_qubits)), skip_cond_clean=True |
| 592 | + ) |
| 593 | + qc.compose(ladder_ops_new, q_control[:] + [q_ancilla[0]], inplace=True) |
| 594 | + if len(final_ctrls) == 1: |
| 595 | + qc.ccx(q_ancilla[0], q_control[final_ctrls[0]], q_target) |
| 596 | + else: |
| 597 | + qc.compose( |
| 598 | + mid_mcx, |
| 599 | + [q_ancilla[0]] + q_control[final_ctrls] + q_target[:] + [q_ancilla[1]], |
| 600 | + inplace=True, |
| 601 | + ) |
| 602 | + qc.compose(ladder_ops_new.inverse(), q_control[:] + [q_ancilla[0]], inplace=True) |
| 603 | + |
| 604 | + return qc |
| 605 | + |
| 606 | + |
| 607 | +def synth_mcx_2_clean_kg24(num_ctrl_qubits: int) -> QuantumCircuit: |
| 608 | + r""" |
| 609 | + Synthesize a multi-controlled X gate with :math:`k` controls using :math:`2` clean ancillary qubits |
| 610 | + producing a circuit with :math:`2k-3` Toffoli gates and depth :math:`O(\log(k))` as described in |
| 611 | + Sec. 5.2 of [1]. |
| 612 | +
|
| 613 | + Args: |
| 614 | + num_ctrl_qubits: The number of control qubits. |
| 615 | +
|
| 616 | + Returns: |
| 617 | + The synthesized quantum circuit. |
| 618 | +
|
| 619 | + Raises: |
| 620 | + QiskitError: If num_ctrl_qubits <= 2. |
| 621 | +
|
| 622 | + References: |
| 623 | + 1. Khattar and Gidney, Rise of conditionally clean ancillae for optimizing quantum circuits |
| 624 | + `arXiv:2407.17966 <https://arxiv.org/abs/2407.17966>`__ |
| 625 | + """ |
| 626 | + |
| 627 | + return synth_mcx_2_kg24(num_ctrl_qubits, clean=True) |
| 628 | + |
| 629 | + |
| 630 | +def synth_mcx_2_dirty_kg24(num_ctrl_qubits: int) -> QuantumCircuit: |
| 631 | + r""" |
| 632 | + Synthesize a multi-controlled X gate with :math:`k` controls using :math:`2` dirty ancillary qubits |
| 633 | + producing a circuit with :math:`4k-8` Toffoli gates and depth :math:`O(\log(k))` as described in |
| 634 | + Sec. 5.4 of [1]. |
| 635 | +
|
| 636 | + Args: |
| 637 | + num_ctrl_qubits: The number of control qubits. |
| 638 | +
|
| 639 | + Returns: |
| 640 | + The synthesized quantum circuit. |
| 641 | +
|
| 642 | + Raises: |
| 643 | + QiskitError: If num_ctrl_qubits <= 2. |
| 644 | +
|
| 645 | + References: |
| 646 | + 1. Khattar and Gidney, Rise of conditionally clean ancillae for optimizing quantum circuits |
| 647 | + `arXiv:2407.17966 <https://arxiv.org/abs/2407.17966>`__ |
| 648 | + """ |
| 649 | + |
| 650 | + return synth_mcx_2_kg24(num_ctrl_qubits, clean=False) |
| 651 | + |
| 652 | + |
308 | 653 | def synth_c3x() -> QuantumCircuit: |
309 | 654 | """Efficient synthesis of 3-controlled X-gate.""" |
310 | 655 |
|
|
0 commit comments