Beispiel #1
0
    def from_backend(cls, backend: BaseBackend):
        """Construct an :class:`InstructionDurations` object from the backend.

        Args:
            backend: backend from which durations (gate lengths) and dt are extracted.

        Returns:
            InstructionDurations: The InstructionDurations constructed from backend.

        Raises:
            TranspilerError: If dt and dtm is different in the backend.
        """
        # All durations in seconds in gate_length
        instruction_durations = []
        for gate, insts in backend.properties()._gates.items():
            for qubits, props in insts.items():
                if 'gate_length' in props:
                    gate_length = props['gate_length'][
                        0]  # Throw away datetime at index 1
                    instruction_durations.append(
                        (gate, qubits, gate_length, 's'))
        for q, props in backend.properties()._qubits.items():
            if 'readout_length' in props:
                readout_length = props['readout_length'][
                    0]  # Throw away datetime at index 1
                instruction_durations.append(
                    ('measure', [q], readout_length, 's'))

        try:
            dt = backend.configuration().dt  # pylint: disable=invalid-name
        except AttributeError:
            dt = None

        return InstructionDurations(instruction_durations, dt=dt)
Beispiel #2
0
    def from_backend(cls, backend: BaseBackend, **kwargs):
        """Automatically loads a QuaC noise model given a backend of type BaseBackend. Primarily
        for speeding up definition of IBMQ hardware noise models in QuaC

        :param backend: an object of type BaseBackend
        :param kwargs: an optional dictionary mapping strings to booleans stating which types
            of noise to include (keys are t1, t2, meas, and zz)
        :return: a QuacNoiseModel object
        """
        n_qubits = len(backend.properties().qubits)
        qubits = list(range(n_qubits))

        # Set up defaults
        t1_times = [float('inf') for _ in range(n_qubits)]
        t2_times = [float('inf') for _ in range(n_qubits)]
        meas_matrices = None
        zz = None

        # Adjust defaults as appropriate
        if kwargs.get("t1"):
            t1_times = [
                backend.properties().t1(qubit) * 1e9 for qubit in qubits
            ]
        if kwargs.get("t2"):
            t2_times = [
                backend.properties().t2(qubit) * 1e9 for qubit in qubits
            ]
        if kwargs.get("meas"):
            meas_matrices = []
            # Construct probability matrix for measurement error adjustments
            for qubit in range(n_qubits):
                # Not all backends have measurement errors added
                try:
                    prob_meas0_prep1 = backend.properties().qubit_property(
                        qubit, "prob_meas0_prep1")[0]
                    prob_meas1_prep0 = backend.properties().qubit_property(
                        qubit, "prob_meas1_prep0")[0]
                except BackendPropertyError:
                    warnings.warn(
                        "Measurement error simulation not supported on this backend"
                    )
                    break

                qubit_measurement_error_matrix = np.array(
                    [[1 - prob_meas1_prep0, prob_meas0_prep1],
                     [prob_meas1_prep0, 1 - prob_meas0_prep1]])

                meas_matrices.append(qubit_measurement_error_matrix)
        if kwargs.get("zz"):
            warnings.warn("ZZ coupling not supported in automatic loading")

        return QuacNoiseModel(t1_times, t2_times, meas_matrices, zz)
Beispiel #3
0
def quac_time_qasm_transpiler(circuit: QuantumCircuit,
                              backend: BaseBackend) -> str:
    """Converts a circuit of type QuantumCircuit to a string of TIMEQASM specification

    :param circuit: a QuantumCircuit (need not be transpiled)
    :param backend: a specific backend to generate the QASM for (for tranpsilation)
    :return: a string containing necessary QASM with times for each gate
    """
    # Get original QASM
    transpiled_circuit = transpile(circuit, backend)
    original_qasm = transpiled_circuit.qasm()

    # Get body of original QASM
    start = 2 + len(circuit.qregs) + len(circuit.cregs)
    original_qasm_body = original_qasm.splitlines()[start:]

    # Formulate header
    qasm_modified = "TIMEQASM 1.0;\n"
    qasm_modified += "\n".join(original_qasm.splitlines()[1:start])
    qasm_modified += "\n"
    # Schedule circuit
    qobj = assemble(transpiled_circuit, backend)
    qexp = qobj.experiments[0]
    qschedule = list_schedule_experiment(qexp, backend.properties())

    # Formulate body
    for instruction, time in qschedule:
        # Note: ID was custom injected by the scheduler in this plugin
        qasm_modified += f"{original_qasm_body[instruction.id][:-1]} @{time};\n"

    return qasm_modified
    def from_backend(cls, backend: BaseBackend):
        """Construct an :class:`InstructionDurations` object from the backend.

        Args:
            backend: backend from which durations (gate lengths) and dt are extracted.

        Returns:
            InstructionDurations: The InstructionDurations constructed from backend.

        Raises:
            TranspilerError: If dt and dtm is different in the backend.
        """
        # All durations in seconds in gate_length
        instruction_durations = []
        for gate, insts in backend.properties()._gates.items():
            for qubits, props in insts.items():
                if 'gate_length' in props:
                    gate_length = props['gate_length'][
                        0]  # Throw away datetime at index 1
                    instruction_durations.append(
                        (gate, qubits, gate_length, 's'))

        try:
            dt = backend.configuration().dt  # pylint: disable=invalid-name
        except AttributeError:
            dt = None

        # TODO: backend.properties() should tell us durations of measurements
        # TODO: Remove the following lines after that
        try:
            dtm = backend.configuration().dtm
            if dtm != dt:
                raise TranspilerError("dtm != dt case is not supported.")
            inst_map = backend.defaults().instruction_schedule_map
            all_qubits = tuple(range(backend.configuration().num_qubits))
            meas_duration = inst_map.get('measure', all_qubits).duration
            for q in all_qubits:
                instruction_durations.append(
                    ('measure', [q], meas_duration, 'dt'))
        except AttributeError:
            pass

        return InstructionDurations(instruction_durations, dt=dt)
Beispiel #5
0
    def from_calibration_results(cls, backend: BaseBackend,
                                 t1_result: Tuple[np.array, Result],
                                 t2_result: Tuple[np.array,
                                                  Result], meas_result: Result,
                                 zz_results: Dict[Tuple[int, int],
                                                  Tuple[np.array, float,
                                                        Result]]):
        """Takes results from running calibration circuits on hardware and constructs a
        QuacNoiseModel object

        :param backend: the backend on which the circuits were run (a BaseBackend object)
        :param t1_result: a tuple with a list of delay times (in ns) as the 0th element and the T1
            calibration Result object as the 1st element
        :param t2_result: a tuple with a list of delay times (in ns) as the 0th element and the T2
            calibration Result object as the 1st element
        :param meas_result: a Result object from running measurement calibration circuits
        :param zz_results: a dictionary mapping tuples of qubit indices to a ZZ coupling calibration circuit
            Result object
        :return: a QuacNoiseModel object
        """
        n_qubits = len(backend.properties().qubits)
        qubits = list(range(n_qubits))

        # Set up defaults
        t1_times = [float('inf') for _ in range(n_qubits)]
        t2_times = [float('inf') for _ in range(n_qubits)]
        meas_matrices = None
        zz = None

        # Adjust defaults as appropriate
        if t1_result:
            t1_fit = T1Fitter(t1_result[1],
                              t1_result[0],
                              qubits,
                              fit_p0=[1, 1e5, 0],
                              fit_bounds=([0, 0, -1], [2, 1e10, 1]),
                              time_unit="nano-seconds")
            t1_times = t1_fit.time()
        if t2_result:
            t2_fit = T2Fitter(t2_result[1],
                              t2_result[0],
                              qubits,
                              fit_p0=[1, 1e4, 0],
                              fit_bounds=([0, 0, -1], [2, 1e10, 1]),
                              time_unit="nano-seconds")
            t2_times = t2_fit.time()
        if meas_result:
            meas_fit = TensoredMeasFitter(meas_result,
                                          [[qubit] for qubit in qubits])
            meas_matrices = meas_fit.cal_matrices
        if zz_results:
            zz = {}
            for qubit1 in qubits:
                for qubit2 in qubits:
                    if qubit1 < qubit2:
                        zz_information = zz_results[(qubit1, qubit2)]
                        xdata, osc_freq, zz_result = zz_information
                        zz_fit = ZZFitter(
                            zz_result,
                            xdata,
                            [qubit1],
                            [qubit2],
                            fit_p0=[1, osc_freq, -np.pi / 20, 0],
                            fit_bounds=([-0.5, 0, -np.pi,
                                         -0.5], [1.5, 1e10, np.pi, 1.5]),
                        )
                        zz[(qubit1, qubit2)] = zz_fit.ZZ_rate()[0]

        return QuacNoiseModel(t1_times, t2_times, meas_matrices, zz)
Beispiel #6
0
def process_characterisation(backend: BaseBackend) -> Dict[str, Any]:
    """Convert a :py:class:`qiskit.BaseBackend` to a dictionary
     containing device Characteristics

    :param backend: A backend to be converted
    :type backend: BaseBackend
    :return: A dictionary containing device characteristics
    :rtype: dict
    """
    gate_set = _tk_gate_set(backend)
    assert OpType.CX in gate_set
    # TODO explicitly check for and separate 1 and 2 qubit gates
    properties = cast("BackendProperties", backend.properties())

    def return_value_if_found(iterator: Iterable["Nduv"],
                              name: str) -> Optional[Any]:
        try:
            first_found = next(filter(lambda item: item.name == name,
                                      iterator))
        except StopIteration:
            return None
        if hasattr(first_found, "value"):
            return first_found.value
        return None

    config = backend.configuration()
    coupling_map = config.coupling_map
    n_qubits = config.n_qubits
    if coupling_map is None:
        # Assume full connectivity
        arc = FullyConnected(n_qubits)
        link_ers_dict = {}
    else:
        arc = Architecture(coupling_map)
        link_ers_dict = {
            tuple(pair): QubitErrorContainer({OpType.CX})
            for pair in coupling_map
        }

    node_ers_dict = {}
    supported_single_optypes = gate_set.difference({OpType.CX})

    t1_times_dict = {}
    t2_times_dict = {}
    frequencies_dict = {}
    gate_times_dict = {}

    if properties is not None:
        for index, qubit_info in enumerate(properties.qubits):
            error_cont = QubitErrorContainer(supported_single_optypes)
            error_cont.add_readout(
                return_value_if_found(qubit_info, "readout_error"))

            t1_times_dict[index] = return_value_if_found(qubit_info, "T1")
            t2_times_dict[index] = return_value_if_found(qubit_info, "T2")
            frequencies_dict[index] = return_value_if_found(
                qubit_info, "frequency")

            node_ers_dict[index] = error_cont

        for gate in properties.gates:
            name = gate.gate
            if name in _gate_str_2_optype:
                optype = _gate_str_2_optype[name]
                qubits = gate.qubits
                gate_error = return_value_if_found(gate.parameters,
                                                   "gate_error")
                gate_error = gate_error if gate_error else 0.0
                gate_length = return_value_if_found(gate.parameters,
                                                    "gate_length")
                gate_length = gate_length if gate_length else 0.0
                gate_times_dict[(optype, tuple(qubits))] = gate_length
                # add gate fidelities to their relevant lists
                if len(qubits) == 1:
                    node_ers_dict[qubits[0]].add_error((optype, gate_error))
                elif len(qubits) == 2:
                    link_ers_dict[tuple(qubits)].add_error(
                        (optype, gate_error))
                    opposite_link = tuple(qubits[::-1])
                    if opposite_link not in coupling_map:
                        # to simulate a worse reverse direction square the fidelity
                        link_ers_dict[opposite_link] = QubitErrorContainer(
                            {OpType.CX})
                        link_ers_dict[opposite_link].add_error(
                            (optype, 2 * gate_error))

    # convert qubits to architecture Nodes
    node_ers_dict = {
        Node(q_index): ers
        for q_index, ers in node_ers_dict.items()
    }
    link_ers_dict = {(Node(q_indices[0]), Node(q_indices[1])): ers
                     for q_indices, ers in link_ers_dict.items()}

    characterisation: Dict[str, Any] = dict()
    characterisation["NodeErrors"] = node_ers_dict
    characterisation["EdgeErrors"] = link_ers_dict
    characterisation["Architecture"] = arc
    characterisation["t1times"] = t1_times_dict
    characterisation["t2times"] = t2_times_dict
    characterisation["Frequencies"] = frequencies_dict
    characterisation["GateTimes"] = gate_times_dict

    return characterisation