def construct_circuit(self, measurement=False):
        """Construct circuit.

        Args:
            measurement (bool): Boolean flag to indicate if measurement should be included in the circuit.


        Returns:
            QuantumCircuit: quantum circuit.
        """

        breakpoints = []

        # Get n value used in Shor's algorithm, to know how many qubits are used
        self._n = math.ceil(math.log(self._N, 2))

        # quantum register where the sequential QFT is performed
        self._up_qreg = QuantumRegister(2 * self._n, name='up')
        # quantum register where the multiplications are made
        self._down_qreg = QuantumRegister(self._n, name='down')
        # auxilliary quantum register used in addition and multiplication
        self._aux_qreg = QuantumRegister(self._n + 2, name='aux')

        up_cqreg = ClassicalRegister(2 * self._n, name='m_up')
        down_cqreg = ClassicalRegister(self._n, name='m_down')

        # Create Quantum Circuit
        circuit = QuantumCircuit(self._up_qreg, self._down_qreg, self._aux_qreg, up_cqreg, down_cqreg)

        # Initialize down register to 1 and create maximal superposition in top register
        circuit.u2(0, np.pi, self._up_qreg)
        circuit.u3(np.pi, 0, np.pi, self._down_qreg[0])

        # validate maximal superposition in top register
        breakpoints.append(circuit.get_breakpoint_uniform(self._up_qreg, up_cqreg, 0.05))
        # validate initialize down register to 1
        breakpoints.append(circuit.get_breakpoint_classical(self._down_qreg, down_cqreg, 0.05, 1))

        # Apply the multiplication gates as showed in the report in order to create the exponentiation
        for i in range(0, 2 * self._n):
            self._controlled_multiple_mod_N(
                circuit,
                self._up_qreg[i],
                self._down_qreg,
                self._aux_qreg,
                int(pow(self._a, pow(2, i)))
            )

        # Apply inverse QFT
        ftc.construct_circuit(circuit=circuit, qubits=self._up_qreg, do_swaps=True, inverse=True)

        # validate uncomputation is complete and registers are in product state
        breakpoints.append(circuit.get_breakpoint_product(self._up_qreg[:], up_cqreg[:], self._down_qreg[:], down_cqreg[:], .05))

        if measurement:
            circuit.measure(self._up_qreg, up_cqreg)

        logger.info(summarize_circuits(circuit))

        return breakpoints, circuit
Example #2
0
    def construct_circuit(self, measurement: bool = False) -> QuantumCircuit:
        """Construct circuit.

        Args:
            measurement: Boolean flag to indicate if measurement should be included in the circuit.

        Returns:
            Quantum circuit.
        """

        # Get n value used in Shor's algorithm, to know how many qubits are used
        self._n = math.ceil(math.log(self._N, 2))
        self._qft.num_qubits = self._n + 1
        self._iqft.num_qubits = self._n + 1
        self._qft_dag = circuit_to_dag(self._qft)
        self._iqft_dag = circuit_to_dag(self._iqft)

        # quantum register where the sequential QFT is performed
        self._up_qreg = QuantumRegister(2 * self._n, name='up')
        # quantum register where the multiplications are made
        self._down_qreg = QuantumRegister(self._n, name='down')
        # auxiliary quantum register used in addition and multiplication
        self._aux_qreg = QuantumRegister(self._n + 2, name='aux')

        # Create Quantum Circuit
        circuit = QuantumCircuit(self._up_qreg, self._down_qreg,
                                 self._aux_qreg)

        # Initialize down register to 1 and create maximal superposition in top register
        circuit.u2(0, np.pi, self._up_qreg)
        circuit.u3(np.pi, 0, np.pi, self._down_qreg[0])

        tdags = []
        dag_self = circuit_to_dag(circuit)

        # Apply the multiplication gates as showed in
        # the report in order to create the exponentiation
        for i in range(0, 2 * self._n):
            tdags += self._controlled_multiple_mod_N_tdags(
                self._up_qreg[i], self._down_qreg, self._aux_qreg,
                int(pow(self._a, pow(2, i))))

        for tdag in tdags:
            dag_compose_with_tagged(dag_self, tdag)
        composed_circuit = dag_to_circuit(dag_self)
        circuit.__dict__.update(composed_circuit.__dict__)

        # Apply inverse QFT
        iqft = QFT(len(self._up_qreg), inverse=True)
        circuit.compose(iqft, qubits=self._up_qreg)

        if measurement:
            up_cqreg = ClassicalRegister(2 * self._n, name='m')
            circuit.add_register(up_cqreg)
            circuit.measure(self._up_qreg, up_cqreg)

        logger.info(summarize_circuits(circuit))

        return circuit
Example #3
0
    def construct_circuit(self):
        """Construct circuit.

        Returns:
            QuantumCircuit: quantum circuit.
        """

        # Get n value used in Shor's algorithm, to know how many qubits are used
        self._n = math.ceil(math.log(self._N, 2))

        # quantum register where the sequential QFT is performed
        self._up_qreg = QuantumRegister(2 * self._n, name='up')
        # quantum register where the multiplications are made
        self._down_qreg = QuantumRegister(self._n, name='down')
        # auxilliary quantum register used in addition and multiplication
        self._aux_qreg = QuantumRegister(self._n + 2, name='aux')

        # Create Quantum Circuit
        circuit = QuantumCircuit(self._up_qreg, self._down_qreg,
                                 self._aux_qreg)

        # Initialize down register to 1 and create maximal superposition in top register
        circuit.u2(0, np.pi, self._up_qreg)
        circuit.u3(np.pi, 0, np.pi, self._down_qreg[0])

        # Apply the multiplication gates as showed in the report in order to create the exponentiation
        for i in range(0, 2 * self._n):
            self._controlled_multiple_mod_N(circuit, self._up_qreg[i],
                                            self._down_qreg, self._aux_qreg,
                                            int(pow(self._a, pow(2, i))))

        # Apply inverse QFT
        ftc.construct_circuit(circuit=circuit,
                              qubits=self._up_qreg,
                              do_swaps=True,
                              inverse=True)

        logger.info(summarize_circuits(circuit))

        return circuit
Example #4
0
def compile_and_run_circuits(circuits,
                             backend,
                             backend_config=None,
                             compile_config=None,
                             run_config=None,
                             qjob_config=None,
                             backend_options=None,
                             noise_config=None,
                             show_circuit_summary=False,
                             has_shared_circuits=False,
                             circuit_cache=None,
                             skip_qobj_validation=False,
                             **kwargs):
    """
    An execution wrapper with Qiskit-Terra, with job auto recover capability.

    The autorecovery feature is only applied for non-simulator backend.
    This wraper will try to get the result no matter how long it costs.

    Args:
        circuits (QuantumCircuit or list[QuantumCircuit]): circuits to execute
        backend (BaseBackend): backend instance
        backend_config (dict, optional): configuration for backend
        compile_config (dict, optional): configuration for compilation
        run_config (RunConfig, optional): configuration for running a circuit
        qjob_config (dict, optional): configuration for quantum job object
        backend_options (dict, optional): configuration for simulator
        noise_config (dict, optional): configuration for noise model
        show_circuit_summary (bool, optional): showing the summary of submitted circuits.
        has_shared_circuits (bool, optional): use the 0-th circuits as initial state for other circuits.
        circuit_cache (CircuitCache, optional): A CircuitCache to use when calling compile_and_run_circuits
        skip_qobj_validation (bool, optional): Bypass Qobj validation to decrease submission time

    Returns:
        Result: Result object

    Raises:
        AquaError: Any error except for JobError raised by Qiskit Terra
    """
    backend_config = backend_config or {}
    compile_config = compile_config or {}
    run_config = run_config or {}
    qjob_config = qjob_config or {}
    backend_options = backend_options or {}
    noise_config = noise_config or {}

    if backend is None or not isinstance(backend, BaseBackend):
        raise ValueError(
            'Backend is missing or not an instance of BaseBackend')

    if not isinstance(circuits, list):
        circuits = [circuits]

    if is_simulator_backend(backend):
        circuits = _avoid_empty_circuits(circuits)

    if has_shared_circuits:
        return _reuse_shared_circuits(circuits, backend, backend_config,
                                      compile_config, run_config, qjob_config,
                                      backend_options)

    with_autorecover = False if is_simulator_backend(backend) else True

    if MAX_CIRCUITS_PER_JOB is not None:
        max_circuits_per_job = int(MAX_CIRCUITS_PER_JOB)
    else:
        if is_local_backend(backend):
            max_circuits_per_job = sys.maxsize
        else:
            max_circuits_per_job = backend.configuration().max_experiments

    if circuit_cache is not None and circuit_cache.try_reusing_qobjs:
        # Check if all circuits are the same length.
        # If not, don't try to use the same qobj.experiment for all of them.
        if len(set([len(circ.data) for circ in circuits])) > 1:
            circuit_cache.try_reusing_qobjs = False
        else:  # Try setting up the reusable qobj
            # Compile and cache first circuit if cache is empty. The load method will try to reuse it
            if circuit_cache.qobjs is None:
                qobj, _ = _compile_wrapper([circuits[0]], backend,
                                           backend_config, compile_config,
                                           run_config)
                if is_aer_provider(backend):
                    qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs)
                circuit_cache.cache_circuit(qobj, [circuits[0]], 0)

    qobjs = []
    jobs = []
    job_ids = []
    transpiled_circuits = []
    chunks = int(np.ceil(len(circuits) / max_circuits_per_job))
    for i in range(chunks):
        sub_circuits = circuits[i * max_circuits_per_job:(i + 1) *
                                max_circuits_per_job]
        if circuit_cache is not None and circuit_cache.misses < circuit_cache.allowed_misses:
            try:
                if circuit_cache.cache_transpiled_circuits:
                    transpiled_sub_circuits = compiler.transpile(
                        sub_circuits, backend, **backend_config,
                        **compile_config)
                    qobj = circuit_cache.load_qobj_from_cache(
                        transpiled_sub_circuits, i, run_config=run_config)
                else:
                    qobj = circuit_cache.load_qobj_from_cache(
                        sub_circuits, i, run_config=run_config)
                if is_aer_provider(backend):
                    qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs)
            # cache miss, fail gracefully
            except (TypeError, IndexError, FileNotFoundError, EOFError,
                    AquaError, AttributeError) as e:
                circuit_cache.try_reusing_qobjs = False  # Reusing Qobj didn't work
                if len(circuit_cache.qobjs) > 0:
                    logger.info(
                        'Circuit cache miss, recompiling. Cache miss reason: '
                        + repr(e))
                    circuit_cache.misses += 1
                else:
                    logger.info(
                        'Circuit cache is empty, compiling from scratch.')
                circuit_cache.clear_cache()
                qobj, transpiled_sub_circuits = _compile_wrapper(
                    sub_circuits, backend, backend_config, compile_config,
                    run_config)
                transpiled_circuits.extend(transpiled_sub_circuits)
                if is_aer_provider(backend):
                    qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs)
                try:
                    circuit_cache.cache_circuit(qobj, sub_circuits, i)
                except (TypeError, IndexError, AquaError, AttributeError,
                        KeyError) as e:
                    try:
                        circuit_cache.cache_transpiled_circuits = True
                        circuit_cache.cache_circuit(qobj,
                                                    transpiled_sub_circuits, i)
                    except (TypeError, IndexError, AquaError, AttributeError,
                            KeyError) as e:
                        logger.info(
                            'Circuit could not be cached for reason: ' +
                            repr(e))
                        logger.info(
                            'Transpilation may be too aggressive. Try skipping transpiler.'
                        )

        else:
            qobj, transpiled_sub_circuits = _compile_wrapper(
                sub_circuits, backend, backend_config, compile_config,
                run_config)
            transpiled_circuits.extend(transpiled_sub_circuits)
            if is_aer_provider(backend):
                qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs)

        # assure get job ids
        while True:
            job = run_on_backend(backend,
                                 qobj,
                                 backend_options=backend_options,
                                 noise_config=noise_config,
                                 skip_qobj_validation=skip_qobj_validation)
            try:
                job_id = job.job_id()
                break
            except JobError as e:
                logger.warning(
                    "FAILURE: the {}-th chunk of circuits, can not get job id, "
                    "Resubmit the qobj to get job id. "
                    "Terra job error: {} ".format(i, e))
            except Exception as e:
                logger.warning(
                    "FAILURE: the {}-th chunk of circuits, can not get job id, "
                    "Resubmit the qobj to get job id. "
                    "Error: {} ".format(i, e))
        job_ids.append(job_id)
        jobs.append(job)
        qobjs.append(qobj)

    if logger.isEnabledFor(logging.DEBUG) and show_circuit_summary:
        logger.debug("==== Before transpiler ====")
        logger.debug(summarize_circuits(circuits))
        logger.debug("====  After transpiler ====")
        logger.debug(summarize_circuits(transpiled_circuits))

    results = []
    if with_autorecover:
        logger.info("Backend status: {}".format(backend.status()))
        logger.info(
            "There are {} circuits and they are chunked into {} chunks, "
            "each with {} circutis (max.).".format(len(circuits), chunks,
                                                   max_circuits_per_job))
        logger.info("All job ids:\n{}".format(job_ids))
        for idx in range(len(jobs)):
            while True:
                job = jobs[idx]
                job_id = job_ids[idx]
                logger.info("Running {}-th chunk circuits, job id: {}".format(
                    idx, job_id))
                # try to get result if possible
                try:
                    result = job.result(**qjob_config)
                    if result.success:
                        results.append(result)
                        logger.info("COMPLETED the {}-th chunk of circuits, "
                                    "job id: {}".format(idx, job_id))
                        break
                    else:
                        logger.warning("FAILURE: the {}-th chunk of circuits, "
                                       "job id: {}".format(idx, job_id))
                except JobError as e:
                    # if terra raise any error, which means something wrong, re-run it
                    logger.warning(
                        "FAILURE: the {}-th chunk of circuits, job id: {} "
                        "Terra job error: {} ".format(idx, job_id, e))
                except Exception as e:
                    raise AquaError(
                        "FAILURE: the {}-th chunk of circuits, job id: {} "
                        "Unknown error: {} ".format(idx, job_id, e)) from e

                # something wrong here, querying the status to check how to handle it.
                # keep qeurying it until getting the status.
                while True:
                    try:
                        job_status = job.status()
                        break
                    except JobError as e:
                        logger.warning("FAILURE: job id: {}, "
                                       "status: 'FAIL_TO_GET_STATUS' "
                                       "Terra job error: {}".format(job_id, e))
                        time.sleep(5)
                    except Exception as e:
                        raise AquaError("FAILURE: job id: {}, "
                                        "status: 'FAIL_TO_GET_STATUS' "
                                        "Unknown error: ({})".format(
                                            job_id, e)) from e

                logger.info("Job status: {}".format(job_status))

                # handle the failure job based on job status
                if job_status == JobStatus.DONE:
                    logger.info(
                        "Job ({}) is completed anyway, retrieve result "
                        "from backend.".format(job_id))
                    job = backend.retrieve_job(job_id)
                elif job_status == JobStatus.RUNNING or job_status == JobStatus.QUEUED:
                    logger.info("Job ({}) is {}, but encounter an exception, "
                                "recover it from backend.".format(
                                    job_id, job_status))
                    job = backend.retrieve_job(job_id)
                else:
                    logger.info(
                        "Fail to run Job ({}), resubmit it.".format(job_id))
                    qobj = qobjs[idx]
                    #  assure job get its id
                    while True:
                        job = run_on_backend(
                            backend,
                            qobj,
                            backend_options=backend_options,
                            noise_config=noise_config,
                            skip_qobj_validation=skip_qobj_validation)
                        try:
                            job_id = job.job_id()
                            break
                        except JobError as e:
                            logger.warning(
                                "FAILURE: the {}-th chunk of circuits, "
                                "can not get job id. Resubmit the qobj to get job id. "
                                "Terra job error: {} ".format(idx, e))
                        except Exception as e:
                            logger.warning(
                                "FAILURE: the {}-th chunk of circuits, "
                                "can not get job id, Resubmit the qobj to get job id. "
                                "Unknown error: {} ".format(idx, e))
                    jobs[idx] = job
                    job_ids[idx] = job_id
    else:
        results = []
        for job in jobs:
            results.append(job.result(**qjob_config))

    result = _combine_result_objects(results) if len(results) != 0 else None

    return result
def compile_circuits(circuits, backend, backend_config=None, compile_config=None, run_config=None,
                     show_circuit_summary=False, circuit_cache=None, **kwargs):
    """
    An execution wrapper with Qiskit-Terra, with job auto recover capability.

    The autorecovery feature is only applied for non-simulator backend.
    This wrapper will try to get the result no matter how long it costs.

    Args:
        circuits (QuantumCircuit or list[QuantumCircuit]): circuits to execute
        backend (BaseBackend): backend instance
        backend_config (dict, optional): configuration for backend
        compile_config (dict, optional): configuration for compilation
        run_config (RunConfig, optional): configuration for running a circuit
        show_circuit_summary (bool, optional): showing the summary of submitted circuits.
        circuit_cache (CircuitCache, optional): A CircuitCache to use
                    when calling compile_and_run_circuits
        kwargs (optional): special aer instructions to evaluation the expectation of a hamiltonian

    Returns:
        QasmObj: compiled qobj.

    Raises:
        ValueError: backend type is wrong or not given
        ValueError: no circuit in the circuits

    """
    backend_config = backend_config or {}
    compile_config = compile_config or {}
    run_config = run_config or {}

    if backend is None or not isinstance(backend, BaseBackend):
        raise ValueError('Backend is missing or not an instance of BaseBackend')

    if not isinstance(circuits, list):
        circuits = [circuits]

    if not circuits:
        raise ValueError("The input circuit is empty.")

    if is_simulator_backend(backend):
        circuits = _avoid_empty_circuits(circuits)

    if circuit_cache is not None and circuit_cache.try_reusing_qobjs:
        # Check if all circuits are the same length.
        # If not, don't try to use the same qobj.experiment for all of them.
        if len({len(circ.data) for circ in circuits}) > 1:
            circuit_cache.try_reusing_qobjs = False
        else:  # Try setting up the reusable qobj
            # Compile and cache first circuit if cache is empty.
            # The load method will try to reuse it
            if circuit_cache.qobjs is None:
                qobj, _ = _compile_wrapper([circuits[0]], backend, backend_config,
                                           compile_config, run_config)

                if is_aer_provider(backend):
                    qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs)
                circuit_cache.cache_circuit(qobj, [circuits[0]], 0)

    transpiled_circuits = None
    if circuit_cache is not None and circuit_cache.misses < circuit_cache.allowed_misses:
        try:
            if circuit_cache.cache_transpiled_circuits:
                transpiled_circuits = compiler.transpile(circuits, backend, **backend_config,
                                                         **compile_config)
                qobj = circuit_cache.load_qobj_from_cache(transpiled_circuits,
                                                          0, run_config=run_config)
            else:
                qobj = circuit_cache.load_qobj_from_cache(circuits, 0, run_config=run_config)

            if is_aer_provider(backend):
                qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs)
        # cache miss, fail gracefully
        except (TypeError, IndexError, FileNotFoundError, EOFError, AquaError, AttributeError) as e:
            circuit_cache.try_reusing_qobjs = False  # Reusing Qobj didn't work
            if circuit_cache.qobjs:
                logger.info('Circuit cache miss, recompiling. Cache miss reason: %s', repr(e))
                circuit_cache.misses += 1
            else:
                logger.info('Circuit cache is empty, compiling from scratch.')
            circuit_cache.clear_cache()

            qobj, transpiled_circuits = _compile_wrapper(circuits, backend, backend_config,
                                                         compile_config, run_config)
            if is_aer_provider(backend):
                qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs)
            try:
                circuit_cache.cache_circuit(qobj, circuits, 0)
            except (TypeError, IndexError, AquaError, AttributeError, KeyError):
                try:
                    circuit_cache.cache_transpiled_circuits = True
                    circuit_cache.cache_circuit(qobj, transpiled_circuits, 0)
                except (TypeError, IndexError, AquaError, AttributeError, KeyError) as e:
                    logger.info('Circuit could not be cached for reason: %s', repr(e))
                    logger.info('Transpilation may be too aggressive. Try skipping transpiler.')

    else:
        qobj, transpiled_circuits = _compile_wrapper(circuits, backend,
                                                     backend_config, compile_config,
                                                     run_config)
        if is_aer_provider(backend):
            qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs)

    if logger.isEnabledFor(logging.DEBUG) and show_circuit_summary:
        logger.debug("==== Before transpiler ====")
        logger.debug(summarize_circuits(circuits))
        if transpiled_circuits is not None:
            logger.debug("====  After transpiler ====")
            logger.debug(summarize_circuits(transpiled_circuits))

    return qobj
Example #6
0
    def construct_circuit(self, measurement: bool = False) -> QuantumCircuit:
        """Construct circuit.

        Args:
            measurement: Boolean flag to indicate if measurement should be included in the circuit.

        Returns:
            Quantum circuit.
        """

        # Get n value used in Shor's algorithm, to know how many qubits are used
        self._n = math.ceil(math.log(self._N, 2))
        self._qft.num_qubits = self._n + 1
        self._iqft.num_qubits = self._n + 1

        # quantum register where the sequential QFT is performed
        self._up_qreg = QuantumRegister(2 * self._n, name='up')
        # quantum register where the multiplications are made
        self._down_qreg = QuantumRegister(self._n, name='down')
        # auxiliary quantum register used in addition and multiplication
        self._aux_qreg = QuantumRegister(self._n + 2, name='aux')

        # Create Quantum Circuit
        circuit = QuantumCircuit(self._up_qreg,
                                 self._down_qreg,
                                 self._aux_qreg,
                                 name="Shor(N={}, a={})".format(
                                     self._N, self._a))

        # Create gates to perform addition/subtraction by N in Fourier Space
        self._phi_add_N = self._phi_add_gate(self._aux_qreg.size - 1,
                                             self._get_angles(self._N))
        self._iphi_add_N = self._phi_add_N.inverse()

        # Create maximal superposition in top register
        circuit.h(self._up_qreg)

        # Initialize down register to 1
        circuit.x(self._down_qreg[0])

        # Apply the multiplication gates as showed in
        # the report in order to create the exponentiation
        for i, ctl_up in enumerate(self._up_qreg):  # type: ignore
            a = int(pow(self._a, pow(2, i)))
            controlled_multiple_mod_N = self._controlled_multiple_mod_N(
                len(self._down_qreg) + len(self._aux_qreg) + 1,
                a,
            )
            circuit.append(controlled_multiple_mod_N,
                           [ctl_up, *self._down_qreg, *self._aux_qreg])

        # Apply inverse QFT
        iqft = QFT(len(self._up_qreg)).inverse().to_instruction()
        circuit.append(iqft, self._up_qreg)

        if measurement:
            up_cqreg = ClassicalRegister(2 * self._n, name='m')
            circuit.add_register(up_cqreg)
            circuit.measure(self._up_qreg, up_cqreg)

        logger.info(summarize_circuits(circuit))

        return circuit
Example #7
0
def compile_and_run_circuits(circuits,
                             backend,
                             backend_config,
                             compile_config,
                             run_config,
                             qjob_config=None,
                             backend_options=None,
                             noise_config=None,
                             show_circuit_summary=False,
                             has_shared_circuits=False,
                             circuit_cache=None,
                             skip_qobj_validation=False,
                             **kwargs):
    """
    An execution wrapper with Qiskit-Terra, with job auto recover capability.

    The autorecovery feature is only applied for non-simulator backend.
    This wraper will try to get the result no matter how long it costs.

    Args:
        circuits (QuantumCircuit or list[QuantumCircuit]): circuits to execute
        backend (BaseBackend): backend instance
        backend_config (dict): configuration for backend
        compile_config (dict): configuration for compilation
        run_config (RunConfig): configuration for running a circuit
        qjob_config (dict): configuration for quantum job object
        backend_options (dict): configuration for simulator
        noise_config (dict): configuration for noise model
        show_circuit_summary (bool): showing the summary of submitted circuits.
        has_shared_circuits (bool): use the 0-th circuits as initial state for other circuits.

    Returns:
        Result: Result object

    Raises:
        AquaError: Any error except for JobError raised by Qiskit Terra
    """
    qjob_config = qjob_config or {}
    backend_options = backend_options or {}
    noise_config = noise_config or {}

    if backend is None or not isinstance(backend, BaseBackend):
        raise ValueError(
            'Backend is missing or not an instance of BaseBackend')

    if not isinstance(circuits, list):
        circuits = [circuits]

    if 'statevector' in backend.name():
        circuits = _avoid_empty_circuits(circuits)

    if has_shared_circuits:
        return _reuse_shared_circuits(circuits, backend, backend_config,
                                      compile_config, run_config, qjob_config,
                                      backend_options)

    with_autorecover = False if backend.configuration().simulator else True

    if MAX_CIRCUITS_PER_JOB is not None:
        max_circuits_per_job = int(MAX_CIRCUITS_PER_JOB)
    else:
        if backend.configuration().local:
            max_circuits_per_job = sys.maxsize
        else:
            max_circuits_per_job = backend.configuration().max_experiments

    if circuit_cache is not None and circuit_cache.try_reusing_qobjs:
        # Check if all circuits are the same length. If not, don't try to use the same qobj.experiment for all of them.
        if len(set([len(circ.data) for circ in circuits])) > 1:
            circuit_cache.try_reusing_qobjs = False
        else:  # Try setting up the reusable qobj
            # Compile and cache first circuit if cache is empty. The load method will try to reuse it
            if circuit_cache.try_reusing_qobjs and circuit_cache.qobjs is None:
                qobj = q_compile([circuits[0]], backend, **execute_config)
                circuit_cache.cache_circuit(qobj, [circuits[0]], 0)

    qobjs = []
    jobs = []
    job_ids = []
    chunks = int(np.ceil(len(circuits) / max_circuits_per_job))
    for i in range(chunks):
        sub_circuits = circuits[i * max_circuits_per_job:(i + 1) *
                                max_circuits_per_job]
        if circuit_cache is not None and circuit_cache.misses < circuit_cache.allowed_misses:
            try:
                qobj = circuit_cache.load_qobj_from_cache(
                    sub_circuits, i, run_config=run_config)
            # cache miss, fail gracefully
            except (TypeError, IndexError, FileNotFoundError, EOFError,
                    AquaError, AttributeError) as e:
                circuit_cache.try_reusing_qobjs = False  # Reusing Qobj didn't work
                circuit_cache.clear_cache()
                logger.debug(
                    'Circuit cache miss, recompiling. Cache miss reason: ' +
                    repr(e))
                qobj = q_compile(sub_circuits, backend, **backend_config,
                                 **compile_config, **run_config.to_dict())
                circuit_cache.cache_circuit(qobj, sub_circuits, i)
                circuit_cache.misses += 1
        else:
            qobj = q_compile(sub_circuits, backend, **backend_config,
                             **compile_config, **run_config.to_dict())

        if 'expectation' in kwargs:
            from qiskit.providers.aer.utils.qobj_utils import snapshot_instr, append_instr
            # add others, how to derive the correct used number of qubits?
            # the compiled qobj could be wrong if coupling map is used.
            # if mulitple params are provided, we assume that each circuit is corresponding one param
            # otherwise, params are used for all circuits.
            params = kwargs['expectation']['params']
            num_qubits = kwargs['expectation']['num_qubits']
            if len(params) == 1:
                new_ins = snapshot_instr('expectation_value_pauli',
                                         'test',
                                         range(num_qubits),
                                         params=params[0])
                for ii in range(len(sub_circuits)):
                    qobj = append_instr(qobj, ii, new_ins)
            else:
                for ii in range(len(sub_circuits)):
                    new_ins = snapshot_instr('expectation_value_pauli',
                                             'test',
                                             range(num_qubits),
                                             params=params[ii])
                    qobj = append_instr(qobj, ii, new_ins)
        # assure get job ids
        while True:
            job = run_on_backend(backend,
                                 qobj,
                                 backend_options=backend_options,
                                 noise_config=noise_config,
                                 skip_qobj_validation=skip_qobj_validation)
            try:
                job_id = job.job_id()
                break
            except JobError as e:
                logger.warning(
                    "FAILURE: the {}-th chunk of circuits, can not get job id, "
                    "Resubmit the qobj to get job id. "
                    "Terra job error: {} ".format(i, e))
            except Exception as e:
                logger.warning(
                    "FAILURE: the {}-th chunk of circuits, can not get job id, "
                    "Resubmit the qobj to get job id. "
                    "Error: {} ".format(i, e))
        job_ids.append(job_id)
        jobs.append(job)
        qobjs.append(qobj)

    if logger.isEnabledFor(logging.DEBUG) and show_circuit_summary:
        logger.debug(summarize_circuits(circuits))

    results = []
    if with_autorecover:
        logger.info("Backend status: {}".format(backend.status()))
        logger.info(
            "There are {} circuits and they are chunked into {} chunks, "
            "each with {} circutis (max.).".format(len(circuits), chunks,
                                                   max_circuits_per_job))
        logger.info("All job ids:\n{}".format(job_ids))
        for idx in range(len(jobs)):
            while True:
                job = jobs[idx]
                job_id = job_ids[idx]
                logger.info("Running {}-th chunk circuits, job id: {}".format(
                    idx, job_id))
                # try to get result if possible
                try:
                    result = job.result(**qjob_config)
                    if result.success:
                        results.append(result)
                        logger.info("COMPLETED the {}-th chunk of circuits, "
                                    "job id: {}".format(idx, job_id))
                        break
                    else:
                        logger.warning("FAILURE: the {}-th chunk of circuits, "
                                       "job id: {}".format(idx, job_id))
                except JobError as e:
                    # if terra raise any error, which means something wrong, re-run it
                    logger.warning(
                        "FAILURE: the {}-th chunk of circuits, job id: {} "
                        "Terra job error: {} ".format(idx, job_id, e))
                except Exception as e:
                    raise AquaError(
                        "FAILURE: the {}-th chunk of circuits, job id: {} "
                        "Unknown error: {} ".format(idx, job_id, e)) from e

                # something wrong here, querying the status to check how to handle it.
                # keep qeurying it until getting the status.
                while True:
                    try:
                        job_status = job.status()
                        break
                    except JobError as e:
                        logger.warning("FAILURE: job id: {}, "
                                       "status: 'FAIL_TO_GET_STATUS' "
                                       "Terra job error: {}".format(job_id, e))
                        time.sleep(5)
                    except Exception as e:
                        raise AquaError("FAILURE: job id: {}, "
                                        "status: 'FAIL_TO_GET_STATUS' "
                                        "Unknown error: ({})".format(
                                            job_id, e)) from e

                logger.info("Job status: {}".format(job_status))

                # handle the failure job based on job status
                if job_status == JobStatus.DONE:
                    logger.info(
                        "Job ({}) is completed anyway, retrieve result "
                        "from backend.".format(job_id))
                    job = backend.retrieve_job(job_id)
                elif job_status == JobStatus.RUNNING or job_status == JobStatus.QUEUED:
                    logger.info("Job ({}) is {}, but encounter an exception, "
                                "recover it from backend.".format(
                                    job_id, job_status))
                    job = backend.retrieve_job(job_id)
                else:
                    logger.info(
                        "Fail to run Job ({}), resubmit it.".format(job_id))
                    qobj = qobjs[idx]
                    #  assure job get its id
                    while True:
                        job = run_on_backend(
                            backend,
                            qobj,
                            backend_options=backend_options,
                            noise_config=noise_config,
                            skip_qobj_validation=skip_qobj_validation)
                        try:
                            job_id = job.job_id()
                            break
                        except JobError as e:
                            logger.warning(
                                "FAILURE: the {}-th chunk of circuits, "
                                "can not get job id. Resubmit the qobj to get job id. "
                                "Terra job error: {} ".format(idx, e))
                        except Exception as e:
                            logger.warning(
                                "FAILURE: the {}-th chunk of circuits, "
                                "can not get job id, Resubmit the qobj to get job id. "
                                "Unknown error: {} ".format(idx, e))
                    jobs[idx] = job
                    job_ids[idx] = job_id
    else:
        results = []
        for job in jobs:
            results.append(job.result(**qjob_config))

    result = _combine_result_objects(results) if len(results) != 0 else None

    return result
Example #8
0
def compile_circuits(circuits,
                     backend,
                     backend_config=None,
                     compile_config=None,
                     run_config=None,
                     show_circuit_summary=False,
                     circuit_cache=None,
                     **kwargs):
    """
    An execution wrapper with Qiskit-Terra, with job auto recover capability.

    The autorecovery feature is only applied for non-simulator backend.
    This wraper will try to get the result no matter how long it costs.

    Args:
        circuits (QuantumCircuit or list[QuantumCircuit]): circuits to execute
        backend (BaseBackend): backend instance
        backend_config (dict, optional): configuration for backend
        compile_config (dict, optional): configuration for compilation
        run_config (RunConfig, optional): configuration for running a circuit
        show_circuit_summary (bool, optional): showing the summary of submitted circuits.
        circuit_cache (CircuitCache, optional): A CircuitCache to use when calling compile_and_run_circuits

    Returns:
        QasmObj: compiled qobj.

    Raises:
        AquaError: Any error except for JobError raised by Qiskit Terra
    """
    backend_config = backend_config or {}
    compile_config = compile_config or {}
    run_config = run_config or {}

    if backend is None or not isinstance(backend, BaseBackend):
        raise ValueError(
            'Backend is missing or not an instance of BaseBackend')

    if not isinstance(circuits, list):
        circuits = [circuits]

    if is_simulator_backend(backend):
        circuits = _avoid_empty_circuits(circuits)

    if MAX_CIRCUITS_PER_JOB is not None:
        max_circuits_per_job = int(MAX_CIRCUITS_PER_JOB)
    else:
        if is_local_backend(backend):
            max_circuits_per_job = sys.maxsize
        else:
            max_circuits_per_job = backend.configuration().max_experiments

    if circuit_cache is not None and circuit_cache.try_reusing_qobjs:
        # Check if all circuits are the same length.
        # If not, don't try to use the same qobj.experiment for all of them.
        if len(set([len(circ.data) for circ in circuits])) > 1:
            circuit_cache.try_reusing_qobjs = False
        else:  # Try setting up the reusable qobj
            # Compile and cache first circuit if cache is empty. The load method will try to reuse it
            if circuit_cache.qobjs is None:
                qobj, transpiled_circuits = _compile_wrapper([circuits[0]],
                                                             backend,
                                                             backend_config,
                                                             compile_config,
                                                             run_config)

                if is_aer_provider(backend):
                    qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs)
                circuit_cache.cache_circuit(qobj, [circuits[0]], 0)

    qobjs = []
    transpiled_circuits = []
    chunks = int(np.ceil(len(circuits) / max_circuits_per_job))
    for i in range(chunks):
        sub_circuits = circuits[i * max_circuits_per_job:(i + 1) *
                                max_circuits_per_job]
        if circuit_cache is not None and circuit_cache.misses < circuit_cache.allowed_misses:
            try:
                if circuit_cache.cache_transpiled_circuits:
                    transpiled_sub_circuits = compiler.transpile(
                        sub_circuits, backend, **backend_config,
                        **compile_config)
                    qobj = circuit_cache.load_qobj_from_cache(
                        transpiled_sub_circuits, i, run_config=run_config)
                else:
                    qobj = circuit_cache.load_qobj_from_cache(
                        sub_circuits, i, run_config=run_config)
                if is_aer_provider(backend):
                    qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs)
            # cache miss, fail gracefully
            except (TypeError, IndexError, FileNotFoundError, EOFError,
                    AquaError, AttributeError) as e:
                circuit_cache.try_reusing_qobjs = False  # Reusing Qobj didn't work
                if len(circuit_cache.qobjs) > 0:
                    logger.info(
                        'Circuit cache miss, recompiling. Cache miss reason: '
                        + repr(e))
                    circuit_cache.misses += 1
                else:
                    logger.info(
                        'Circuit cache is empty, compiling from scratch.')
                circuit_cache.clear_cache()
                qobj, transpiled_sub_circuits = _compile_wrapper(
                    sub_circuits, backend, backend_config, compile_config,
                    run_config)
                transpiled_circuits.extend(transpiled_sub_circuits)
                if is_aer_provider(backend):
                    qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs)
                try:
                    circuit_cache.cache_circuit(qobj, sub_circuits, i)
                except (TypeError, IndexError, AquaError, AttributeError,
                        KeyError) as e:
                    try:
                        circuit_cache.cache_transpiled_circuits = True
                        circuit_cache.cache_circuit(qobj,
                                                    transpiled_sub_circuits, i)
                    except (TypeError, IndexError, AquaError, AttributeError,
                            KeyError) as e:
                        logger.info(
                            'Circuit could not be cached for reason: ' +
                            repr(e))
                        logger.info(
                            'Transpilation may be too aggressive. Try skipping transpiler.'
                        )

        else:
            qobj, transpiled_sub_circuits = _compile_wrapper(
                sub_circuits, backend, backend_config, compile_config,
                run_config)
            transpiled_circuits.extend(transpiled_sub_circuits)
            if is_aer_provider(backend):
                qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs)

        qobjs.append(qobj)

    if logger.isEnabledFor(logging.DEBUG) and show_circuit_summary:
        logger.debug("==== Before transpiler ====")
        logger.debug(summarize_circuits(circuits))
        logger.debug("====  After transpiler ====")
        logger.debug(summarize_circuits(transpiled_circuits))

    return qobjs