def do_add_subtract_comparison(gate, comparand_reg, target_bit, controls):
    """
    N: len(query_reg)
    C: len(controls)
    Size: O(N + C)
    Depth: O(N + C)
    Args:
        gate (LessThanConstantGate):
            The gate to decompose, containing the constant to compare against.
        comparand_reg (projectq.types.Qureg):
            The register that we want to compare against a constant.
        target_bit (projectq.types.Qureg):
            The bit to toggle if the comparison is satisfied.
        controls (list[projectq.types.Qubit]):
            Conditions the operation.
    """
    # Trivial case: can't be satisfied.
    if gate.comparand <= 0:
        return

    # Trivial case: always satisfied.
    overflow_val = 1 << len(comparand_reg)
    if gate.comparand >= overflow_val:
        X & controls | target_bit
        return

    OffsetGate(-gate.comparand) & controls | comparand_reg + target_bit
    OffsetGate(gate.comparand) & controls | comparand_reg
Ejemplo n.º 2
0
def do_recursive_offset(gate, target_reg, dirty_qubit, controls):
    """
    N: len(target_reg)
    C: len(controls)
    Size: O(N*C)
    Depth: O(N*C)
    Source:
        Thomas Häner, Martin Roetteler, and Krysta M. Svore, 2016.
        "Factoring using 2n+2 qubits with Toffoli based modular multiplication"
    Args:
        gate (OffsetGate):
            The gate being applied (contains offset info).
        target_reg (projectq.types.Qureg):
            The destination register, whose value is increased by the offset
            (with wraparound).
        dirty_qubit (projectq.types.Qubit):
            Workspace.
        controls (list[Qubit]):
            Control qubits.
    """
    offset = gate.offset & ~(~0 << len(target_reg))

    # Trivial case: adding zero does nothing.
    if offset == 0:
        return

    # Trivial case: adding a power of 2 is a shifted increment.
    if (offset - 1) & offset == 0:
        power = int(math.floor(0.5 + math.log(offset, 2)))
        Increment & controls | target_reg[power:]
        return

    h = len(target_reg) // 2
    low = target_reg[:h]
    high = target_reg[h:]
    mask = (~0 << h)
    low_offset = offset & ~mask
    high_offset = (offset & mask) >> h

    # Increment high part based on predicted carry signal from low part.
    Increment & dirty_qubit | high
    MultiNot & dirty_qubit | high
    PredictOffsetOverflowGate(low_offset) & controls | (low, dirty_qubit)
    Increment & dirty_qubit | high
    PredictOffsetOverflowGate(low_offset) & controls | (low, dirty_qubit)
    MultiNot & dirty_qubit | high

    # Separately recurse on low and high parts.
    OffsetGate(low_offset) & controls | low
    OffsetGate(high_offset) & controls | high
def test_diagram_decompose_into_recursion():
    text_diagram = decomposition_to_ascii(
        gate=OffsetGate(0b110111001),
        decomposition_rule=decompose_into_recursion,
        register_sizes=[9],
        workspace=1)
    print(text_diagram)
    assert text_diagram == """
                   .-------------------.          .-------------------.   .------.
|0>----------------|                   |----------|                   |---|      |-----------
                   |                   |          |                   |   |      |
|0>----------------|                   |----------|                   |---|      |-----------
                   |                   |          |                   |   |      |
|0>----------------|         A         |----------|         A         |---|  +9  |-----------
                   |                   |          |                   |   |      |
|0>----------------|                   |----------|                   |---|      |-----------
        .------.   `-------------------` .------. `-------------------`   `------` .-------.
|0>-----|      |-X-----------------------|      |-----------------------X----------|       |-
        |      | |                       |      |                       |          |       |
|0>-----|      |-X-----------------------|      |-----------------------X----------|       |-
        |      | |                       |      |                       |          |       |
|0>-----|  +1  |-X-----------------------|  +1  |-----------------------X----------|  +27  |-
        |      | |                       |      |                       |          |       |
|0>-----|      |-X-----------------------|      |-----------------------X----------|       |-
        |      | |                       |      |                       |          |       |
|0>-----|      |-X-----------------------|      |-----------------------X----------|       |-
        `--|---` | .-------------------. `--|---` .-------------------. |          `-------`
|0>-???----@-----@-|  Xoverflow(A+=9)  |----@-----|  Xoverflow(A+=9)  |-@--------------------
                   `-------------------`          `-------------------`
        """.lstrip('\n').rstrip()
def test_diagram_decompose_into_range_increments():
    text_diagram = decomposition_to_ascii(
        gate=OffsetGate(0b110111001),
        decomposition_rule=decompose_into_range_increments,
        register_sizes=[9])
    print(text_diagram)
    assert text_diagram == """
    .------.
|0>-|      |----------------------------
    |      |
|0>-|      |----------------------------
    |      |
|0>-|      |----------------------------
    |      | .------.
|0>-|      |-|      |-------------------
    |      | |      |
|0>-|  +1  |-|      |-------------------
    |      | |      |
|0>-|      |-|      |-------------------
    |      | |      | .------.
|0>-|      |-|  −1  |-|      |----------
    |      | |      | |      | .------.
|0>-|      |-|      |-|  +1  |-|      |-
    |      | |      | |      | |      |
|0>-|      |-|      |-|      |-|  −1  |-
    `------` `------` `------` `------`
        """.lstrip('\n').rstrip()
def test_decompose_into_recursion():
    for register_size in range(1, 10):
        for offset in cover(1 << (register_size - 1)):
            check_permutation_decomposition(
                decomposition_rule=decompose_into_recursion,
                gate=OffsetGate(offset),
                register_sizes=[register_size],
                workspace=1)
Ejemplo n.º 6
0
def do_modular_double(gate, target_reg, controls):
    """
    Args:
        gate (ModularBimultiplicationGate):
            The gate being decomposed.
        target_reg (projectq.types.Qureg):
            The register to mod-multiply by the inverse factor.
        controls (list[Qubit]):
            Control qubits.
    """
    assert 0 < gate.modulus <= 1 << len(target_reg)

    h = (gate.modulus + 1) // 2

    OffsetGate(-h) & controls | target_reg
    OffsetGate(h) & target_reg[-1] & controls | target_reg[:-1]
    X & controls | target_reg[-1]
    LeftRotateBits & controls | target_reg
Ejemplo n.º 7
0
def do_decrease_size(offset, target_reg, controls):
    jump = 0
    while offset & 1 == 0:
        if not offset:
            return
        offset >>= 1
        jump += 1

    OffsetGate(offset) & controls | target_reg[jump:]
def test_decompose_decrease_size():
    for register_size in cover(100):
        for offset in cover(1 << register_size):
            for control_size in cover(2):
                check_permutation_decomposition(
                    decomposition_rule=decompose_decrease_size,
                    gate=OffsetGate(offset * 2),
                    register_sizes=[register_size],
                    control_size=control_size)
def test_decompose_remove_controls():
    for register_size in cover(100):
        for offset in cover(1 << register_size):
            for controls in range(1, 4):
                check_permutation_decomposition(
                    decomposition_rule=decompose_remove_controls,
                    gate=OffsetGate(offset),
                    register_sizes=[register_size],
                    workspace=1,
                    control_size=controls)
def test_decompose_into_range_increments():
    for register_size in cover(100):
        for offset in cover(1 << register_size):
            if estimate_cost_of_bitrange_offset(offset, register_size) > 4:
                continue

            check_permutation_decomposition(
                decomposition_rule=decompose_into_range_increments,
                gate=OffsetGate(offset),
                register_sizes=[register_size])
def do_scaled_addition_via_shifted_adds(gate, input_reg, target_reg, controls):
    """
    Reversibly adds one register, times a constant, into another of the same
    size, modulo a constant.

    N: len(input_reg) + len(target_reg)
    C: len(controls)
    Size: O(N² lg N + NC)
    Depth: O(N² + NC)
    Diagram:
        c              c
       ━/━━━━●━━━     ━/━━━━●━━━━━●━━━━━━━━●━━━━━━━
             │              │     │        │
          ┌──┴──┐           │     │        │
       ───┤     ├  =  ──────●─────┼────────┼───────
       n-2│     │     n-2   │   `.         │
       ━/━┥  A  ┝  =  ━/━━━━┿━━━━━.━━━━━━━━┿━━━━━━━
          │     │           │      `.      │
       ───┤     ├  =  ──────┼─────┼────────●───────
          └──┬──┘           │     │        │
        n ┌──┴──┐      n ┌──┴─┐   │  ┌─────┴─────┐
       ━/━┥ +AK ┝     ━/━┥ +K ┝━ ... ┥ +K<<(n-1) ┝━
          └─────┘        └────┘      └───────────┘
    Args:
        gate (ScaledAdditionGate):
            The gate being decomposed (contains factor info).
        input_reg (projectq.types.Qureg):
            The register to scaled-add from.
        target_reg (projectq.types.Qureg):
            The register to scaled-add into.
        controls (list[Qubit]):
            Control qubits.
    """
    n = min(len(input_reg), len(target_reg))

    plus_scale = OffsetGate(gate.factor) & controls
    for i in range(n):
        plus_scale & input_reg[i] | target_reg[i:]
Ejemplo n.º 12
0
def do_const_pivot_flip(gate, target_reg, controls, dirty_qubit):
    """
    Args:
        gate (ConstPivotFlipGate):
            The gate being decomposed (contains pivot info).
        target_reg (projectq.types.Qureg):
            The register where states are reversed up to the pivot.
        controls (list[Qubit]):
            Control qubits.
        dirty_qubit (Qubit):
            Workspace.
    """
    # Trivial case: no-op.
    if gate.pivot <= 1:
        return

    for _ in range(2):
        # Compare.
        LessThanConstantGate(gate.pivot) | (target_reg, dirty_qubit)

        # Conditioned double flip.
        OffsetGate(-gate.pivot) & dirty_qubit & controls | target_reg
        MultiNot & dirty_qubit & controls | target_reg
Ejemplo n.º 13
0
def do_scale(gate, target_reg, controls):
    """
    Args:
        gate (ScaleGate):
            The gate being decomposed (contains factor/modulus info).
        target_reg (projectq.types.Qureg):
            The register to scaled-add into.
        controls (list[Qubit]):
            Control qubits.
    """

    n = len(target_reg)
    factor = gate.net_factor_for_size(n)

    # Trivial cases.
    if factor == 1:
        return
    if factor == -1:
        Negate & controls | target_reg
        return

    plus_scale = OffsetGate(factor >> 1) & controls
    for i in range(n)[::-1]:
        plus_scale & target_reg[i] | target_reg[i + 1:]
def test_diagram_decompose_decrease_size():
    text_diagram = decomposition_to_ascii(
        gate=OffsetGate(0b110111000),
        decomposition_rule=decompose_decrease_size,
        register_sizes=[9])
    print(text_diagram)
    assert text_diagram == """
|0>-----------
|0>-----------
|0>-----------
    .-------.
|0>-|       |-
    |       |
|0>-|       |-
    |       |
|0>-|       |-
    |       |
|0>-|  +55  |-
    |       |
|0>-|       |-
    |       |
|0>-|       |-
    `-------`
        """.strip()
def test_diagram_decompose_remove_controls():
    text_diagram = decomposition_to_ascii(
        gate=OffsetGate(0b110111001),
        decomposition_rule=decompose_remove_controls,
        register_sizes=[9],
        workspace=1,
        control_size=3)
    print(text_diagram)
    assert text_diagram == """
|0>----------------@------------@-
                   |            |
|0>----------------@------------@-
                   |            |
|0>----------------@------------@-
        .--------. | .--------. |
|0>-----|1       |-X-|1       |-X-
        |        | | |        | |
|0>-----|2       |-X-|2       |-X-
        |        | | |        | |
|0>-----|3       |-X-|3       |-X-
        |        | | |        | |
|0>-----|4       |-X-|4       |-X-
        |        | | |        | |
|0>-----|5       |-X-|5       |-X-
        |        | | |        | |
|0>-----|6 +441  |-X-|6 -441  |-X-
        |        | | |        | |
|0>-----|7       |-X-|7       |-X-
        |        | | |        | |
|0>-----|8       |-X-|8       |-X-
        |        | | |        | |
|0>-----|9       |-X-|9       |-X-
        |        | | |        | |
|0>-???-|0       |-X-|0       |-X-
        `--------`   `--------`
        """.strip()
def test_offset_operation():
    assert OffsetGate(4).do_operation(3) == (7, )