def _execute_and_read_result(
    quantum_computer: QuantumComputer,
    executable: QuantumExecutable,
    measurement_id_map: Dict[str, str],
    resolver: cirq.ParamResolverOrSimilarType,
    memory_map: Optional[Dict[str, Union[int, float, Sequence[int],
                                         Sequence[float]]]] = None,
) -> cirq.Result:
    """Execute the `pyquil.api.QuantumExecutable` and parse the measurements into
    a `cirq.Result`.

    Args:
        quantum_computer: The `pyquil.api.QuantumComputer` on which to execute
            and from which to read results.
        executable: The fully compiled `pyquil.api.QuantumExecutable` to run.
        measurement_id_map: A dict mapping cirq measurement keys to pyQuil
            read out regions.
        resolver: The `cirq.ParamResolverOrSimilarType` to include on
            the returned `cirq.Result`.
        memory_map: A dict of values to write to memory values on the
            `quantum_computer`. The `pyquil.api.QuantumAbstractMachine` reads these
            values into memory regions on the pre-compiled `executable` during execution.

    Returns:
        A `cirq.Result` with measurements read from the `quantum_computer`.

    Raises:
        ValueError: measurement_id_map references an undefined pyQuil readout region.
    """
    if memory_map is None:
        memory_map = {}

    for region_name, values in memory_map.items():
        executable.write_memory(region_name=region_name, value=values)
    qam_execution_result = quantum_computer.qam.run(executable)

    measurements = {}
    # For every key, value in QuilOutput#measurement_id_map, use the value to read
    # Rigetti QCS results and assign to measurements by key.
    for cirq_memory_key, pyquil_region in measurement_id_map.items():
        readout = qam_execution_result.readout_data.get(pyquil_region)
        if readout is None:
            raise ValueError(
                f'readout data does not have values for region "{pyquil_region}"'
            )
        measurements[cirq_memory_key] = readout
    logger.debug(f"measurement_id_map {measurement_id_map}")
    logger.debug(f"measurements {measurements}")

    # collect results in a cirq.Result.
    result = cirq.Result(
        params=cast(cirq.ParamResolver, resolver or cirq.ParamResolver({})),
        measurements=measurements,
    )  # noqa
    return result
def with_quilc_parametric_compilation(
    *,
    quantum_computer: QuantumComputer,
    circuit: cirq.Circuit,
    resolvers: Sequence[cirq.ParamResolverOrSimilarType],
    repetitions: int,
    transformer: transformers.CircuitTransformer = transformers.default,
) -> List[cirq.Result]:
    """This `CircuitSweepExecutor` will compile the `circuit` using quilc as a
    parameterized `pyquil.api.QuantumExecutable` and on each iteration of
    `resolvers`, rather than resolving the `circuit` with `cirq.protocols.resolve_parameters`,
    it will attempt to cast the resolver to a dict and pass it as a memory map to
    to `pyquil.api.QuantumComputer`.

    Args:
        quantum_computer: The `pyquil.api.QuantumComputer` against which to execute the circuit.
        circuit: The `cirq.Circuit` to transform into a `pyquil.Program` and executed on the
            `quantum_computer`.
        resolvers: A sequence of parameter resolvers that this executor will write to memory
            on a copy of the `pyquil.api.QuantumExecutable` for each parameter sweep.
        repetitions: Number of times to run each iteration through the `resolvers`. For a given
            resolver, the `cirq.Result` will include a measurement for each repetition.
        transformer: A callable that transforms the `cirq.Circuit` into a `pyquil.Program`.
            You may pass your own callable or any function from `cirq_rigetti.circuit_transformers`.

    Returns:
        A list of `cirq.Result`, each corresponding to a resolver in `resolvers`.
    """

    program, measurement_id_map = transformer(circuit=circuit)
    program = _prepend_real_declarations(program=program, resolvers=resolvers)
    program.wrap_in_numshots_loop(repetitions)
    executable = quantum_computer.compile(program)

    cirq_results = []
    for resolver in resolvers:
        memory_map = _get_param_dict(resolver)
        logger.debug(
            f"running pre-compiled parametric circuit with parameters {memory_map}"
        )
        result = _execute_and_read_result(
            quantum_computer,
            executable.copy(),
            measurement_id_map,
            resolver,
            memory_map=memory_map,
        )
        cirq_results.append(result)

    return cirq_results
def _prepend_real_declarations(
        *, program: Program,
        resolvers: Sequence[cirq.ParamResolverOrSimilarType]) -> Program:
    """Adds memory declarations for all variables in each of the `resolver`'s
    param dict. Note, this function assumes that the first parameter resolver
    will contain all variables subsequently referenced in `resolvers`.

    Args:
        program: The program that the quantum computer will execute.
        resolvers: A sequence of parameters resolvers that provide values
            for parameter references in the original `cirq.Circuit`.
    Returns:
        A program that includes the QUIL memory declarations as described above.
    """
    if len(resolvers) > 0:
        resolver = resolvers[0]
        param_dict = _get_param_dict(resolver)
        for key in param_dict.keys():
            declaration = Declare(str(key), "REAL")
            program._instructions.insert(0, declaration)
            program._synthesized_instructions = None
            program.declarations[declaration.name] = declaration
            logger.debug(f"prepended declaration {declaration}")
    return program