跳转至

IterativePhaseEstimation

QuICT.algorithm.quantum_algorithm.IterativePhaseEstimation

IterativePhaseEstimation(precision_bits: int, work_bits: int, control_unitary: CompositeGate, work_state_prep: Optional[CompositeGate] = None)

Iterative Quantum Phase Estimation

Qubit Arrangement: |recycling precision_bits> |work_bits> |ancilla>

References

[1]: Dobsicek, M., G. Johansson, V. S. Shumeiko, and G. Wendin. “Arbitrary Accuracy Iterative Phase Estimation Algorithm as a Two Qubit Benchmark.” Physical Review A 76, no. 3 (September 19, 2007): 030306. https://doi.org/10.1103/PhysRevA.76.030306.

Parameters:

  • precision_bits (int) –

    The number of qubits for representing the phase.

  • work_bits (int) –

    The number of qubits for storing the eigenstate.

  • control_unitary (CompositeGate) –

    A controlled version of the uitary whose phase is about to be estimated. The control bit requires to be the highest bit.

  • work_state_prep (CompositeGate, default: None ) –

    Optional eigenstate prepration gate.

Raises: ValueError: If control_unitary is not a composite gate. ValueError: If work_state_prep is not a composite gate or its width is larger the number of work bits.

Source code in QuICT/algorithm/quantum_algorithm/phase_estimation/iterative_qpe.py
def __init__(
    self,
    precision_bits: int,
    work_bits: int,
    control_unitary: CompositeGate,
    work_state_prep: Optional[CompositeGate] = None
):
    """
    Args:
        precision_bits (int): The number of qubits for representing the phase.
        work_bits (int): The number of qubits for storing the eigenstate.
        control_unitary (CompositeGate): A controlled version of the uitary whose phase is about to
            be estimated. The control bit requires to be the highest bit.
        work_state_prep (CompositeGate): Optional eigenstate prepration gate.
    Raises:
        ValueError: If `control_unitary` is not a composite gate.
        ValueError: If `work_state_prep` is not a composite gate or its width is larger the number of
            work bits.
    """
    if not isinstance(control_unitary, CompositeGate):
        raise ValueError("Input `control_unitary` has to be a composite gate.")
    required_q_cu = max(control_unitary.qubits) - len(control_unitary.ancilla_qubits)
    if required_q_cu > work_bits:
        raise ValueError("Not enough work qubits for control unitary. "
                         f"Given {work_bits}, but the control unitary requires: {required_q_cu}.")

    if work_state_prep is not None:
        if not isinstance(work_state_prep, CompositeGate):
            raise ValueError("Input `work_state_prep` has to be a composite gate.")
        required_q_sp = max(work_state_prep.qubits) + 1 - len(work_state_prep.ancilla_qubits)
        if required_q_sp > work_bits:
            raise ValueError("Not enough work qubits for the state preparation. "
                             f"Given {work_bits}, but the state preparation requires: {required_q_sp}.")

    self._control_unitary = control_unitary
    self._work_state_prep = work_state_prep

    # Allocate required qubits
    reg_manager = QRegManager()
    num_ancilla = max(len(control_unitary.ancilla_qubits), len(work_state_prep.ancilla_qubits))
    self._iteration = precision_bits
    self._precision_reg = reg_manager.alloc(1)
    self._work_reg = reg_manager.alloc(work_bits)
    self._ancilla_reg = reg_manager.alloc(num_ancilla)
    self._total_qubits = reg_manager.allocated

    # Get input composite gates' application indices
    gate_mapper = QubitAligner(self._work_reg, self._ancilla_reg)
    self._sp_reg = gate_mapper.getMap(self._work_state_prep)
    self._cu_reg_without_ctrl = gate_mapper.getMap(self._control_unitary, fix_top=1)

    self._circuit = None
    valid_meas_idx = [0] + [((2 * precision_bits - i + 1) * i) >> 1 for i in range(1, precision_bits)]
    self._measurement_idx = list(reversed(valid_meas_idx))
    self._total_measure_counts = {}

circuit

circuit()

Build the iterative phase estimation circuit.

Returns:

  • Circuit

    the iterative qpe circuit.

Source code in QuICT/algorithm/quantum_algorithm/phase_estimation/iterative_qpe.py
def circuit(self):
    """ Build the iterative phase estimation circuit.

    Returns:
        Circuit: the iterative qpe circuit.
    """
    if self._circuit is not None:
        return self._circuit

    self._circuit = Circuit(self._total_qubits)

    if self._work_state_prep is not None:
        self._work_state_prep | self._circuit(self._sp_reg)

    ptr = 0
    for i in range(self._iteration):
        precision_pos = self._iteration - 1 - i

        H | self._circuit(self._precision_reg)
        self._control_unitary.exp2(precision_pos) | self._circuit(
            self._precision_reg + self._cu_reg_without_ctrl
        )
        H | self._circuit(self._precision_reg)

        offset = 0
        # q_if gate for q == 0 case
        cg_id = CompositeGate(gates=[ID & self._precision_reg])
        for j in range(precision_pos):
            # q_if gate for q == 1
            cg_rotation = CompositeGate(gates=[Rz(-np.pi / (2 << j)) & self._precision_reg])
            trig_targat = precision_pos - j + ((3 + i) * (j + 1)) + offset - 1
            Trigger(1, [cg_id, cg_rotation], position=trig_targat) | self._circuit(self._precision_reg)

            ptr += 1
            offset += (precision_pos - j + 1)

        if i == self._iteration - 1:
            Measure | self._circuit(self._precision_reg)
        else:
            # recycle and reset the qubit to 0 using q_if
            cg_reset = CompositeGate(gates=[X & self._precision_reg])
            Trigger(1, [cg_id, cg_reset]) | self._circuit(self._precision_reg)
            ptr += 1

    return self._circuit

run

run(backend=StateVectorSimulator(ignore_last_measure=False), shots: int = 1, decode_as_float: bool = True, cumulative: bool = True) -> Union[Dict[float, int], Dict[str, int]]

Run the iterative phase estimation circuit

Parameters:

  • backend (Any, default: StateVectorSimulator(ignore_last_measure=False) ) –

    a backend to run the qpe circuit.

  • shots (int, default: 1 ) –

    number of times to run the circuit.

  • decode_as_float (bool, default: True ) –

    If True, the running result will be decoded to be the phase/(2*pi) which is a float between 0 and 1. If False, the result will be presented as bit strings.

Returns:

  • Union[Dict[float, int], Dict[str, int]]

    Dict[float, int] | Dict[str, int]: result get from running the qpe circuit, a (result, shot_counts) pair.

Source code in QuICT/algorithm/quantum_algorithm/phase_estimation/iterative_qpe.py
def run(
    self,
    backend=StateVectorSimulator(ignore_last_measure=False),
    shots: int = 1,
    decode_as_float: bool = True,
    cumulative: bool = True
) -> Union[Dict[float, int], Dict[str, int]]:
    """ Run the iterative phase estimation circuit

    Args:
        backend (Any): a backend to run the qpe circuit.
        shots (int): number of times to run the circuit.
        decode_as_float (bool): If `True`, the running result will be decoded to be the phase/(2*pi)
            which is a float between 0 and 1. If `False`, the result will be presented as bit strings.

    Returns:
        Dict[float, int] | Dict[str, int]: result get from running the qpe circuit, a (result, shot_counts) pair.
    """
    if shots < 1:
        raise ValueError(f"Shots have to be positive.")

    if self._circuit is None:
        self.circuit()

    measure_dict = {}

    for _ in range(shots):
        # get measurement results on the precision qubit
        backend.run(self._circuit)
        hist_measure = self._circuit.qubits[self._precision_reg[0]].historical_measured
        res_bin = "".join(str(hist_measure[i]) for i in self._measurement_idx)
        # reset the precision qubit measurement history
        self._circuit.qubits[self._precision_reg[0]].reset()

        # decode measurement results
        if decode_as_float:
            res_key = int(res_bin, base=2) / (1 << self._iteration)
        else:
            res_key = res_bin

        measure_dict.setdefault(res_key, 0)
        measure_dict[res_key] += 1

    if cumulative:
        for key in measure_dict:
            self._total_measure_counts.setdefault(key, 0)
            self._total_measure_counts[key] += measure_dict[key]

    return measure_dict