def circuit_from_quil(quil: str | Program) -> Circuit:
    """Convert a Quil program to a Cirq Circuit.

    Args:
        quil: The Quil program to convert.

    Returns:
        A Cirq Circuit generated from the Quil program.

    Raises:
        UnsupportedQuilInstruction: Cirq does not support the specified Quil instruction.
        UndefinedQuilGate: Cirq does not support the specified Quil gate.

    References:
        https://github.com/rigetti/pyquil
    """
    if isinstance(quil, str):
        program = Program(quil)
    else:
        program = quil
    circuit = Circuit()

    defined_gates, parameter_transformers = get_defined_gates(program)

    kraus_model: dict[tuple[QubitDesignator, ...], list[NDArray[np.complex128]]] = {}
    confusion_maps: dict[int, NDArray[np.float64]] = {}

    # Interpret the Pragmas
    for inst in program:
        if not isinstance(inst, Pragma):  # pragma: no cover
            continue

        # ADD-KRAUS provides Kraus operators that replace the gate operation
        if inst.command == "ADD-KRAUS":  # pragma: no cover
            args = inst.args
            gate_name = str(args[0])
            if gate_name in matrices.QUANTUM_GATES:
                u = matrices.QUANTUM_GATES[gate_name]
            elif gate_name in defined_gates:
                u = defined_gates[gate_name]
            else:
                raise UndefinedQuilGate(f"{gate_name} is not known.")

            entries = np.fromstring(
                inst.freeform_string.strip("()").replace("i", "j"), dtype=np.complex128, sep=" "
            )
            dim = int(np.sqrt(len(entries)))
            kraus_gate_op = entries.reshape((dim, dim))

            kraus_op = remove_gate_from_kraus([kraus_gate_op], u)[0]

            if args in kraus_model:
                kraus_model[args].append(kraus_op)
            else:
                kraus_model[args] = [kraus_op]

        # READOUT-POVM provides a confusion matrix
        elif inst.command == "READOUT-POVM":
            qubit = qubit_index(inst.args[0])
            entries = np.fromstring(
                inst.freeform_string.strip("()").replace("i", "j"), dtype=np.float64, sep=" "
            )
            confusion_matrix = entries.reshape((2, 2)).T

            # these types actually agree - both arrays are floats
            confusion_maps[qubit] = confusion_matrix  # type: ignore

        else:
            raise UnsupportedQuilInstruction(PRAGMA_ERROR)  # pragma: no cover

    # Interpret the instructions
    for inst in program:
        # Pass when encountering a DECLARE.
        if isinstance(inst, Declare):
            pass

        # Convert pyQuil gates to Cirq operations.
        elif isinstance(inst, PyQuilGate):
            quil_gate_name = inst.name
            quil_gate_params = inst.params
            line_qubits = list(LineQubit(qubit_index(q)) for q in inst.qubits)
            if quil_gate_name not in defined_gates:
                raise UndefinedQuilGate(f"Quil gate {quil_gate_name} not supported in Cirq.")
            cirq_gate_fn = defined_gates[quil_gate_name]
            if quil_gate_params:
                params = [quil_expression_to_sympy(p) for p in quil_gate_params]
                transformer = parameter_transformers[quil_gate_name]
                circuit += cast(Callable[..., Gate], cirq_gate_fn)(**transformer(*params))(
                    *line_qubits
                )
            else:
                circuit += cirq_gate_fn(*line_qubits)

        # Convert pyQuil MEASURE operations to Cirq MeasurementGate objects.
        elif isinstance(inst, PyQuilMeasurement):
            qubit = qubit_index(inst.qubit)
            line_qubit = LineQubit(qubit)
            if inst.classical_reg is None:
                raise UnsupportedQuilInstruction(
                    f"Quil measurement {inst} without classical register "
                    f"not currently supported in Cirq."
                )
            quil_memory_reference = inst.classical_reg.out()
            if qubit in confusion_maps:
                cmap: dict[tuple[int, ...], NDArray[np.float64]] = {(qubit,): confusion_maps[qubit]}
                """
                Argument "confusion_map" to "MeasurementGate" has incompatible type
                    "         dict[tuple[int],      ndarray[Any, dtype[floating[Any]]]]"
                expected
                    "Optional[dict[tuple[int, ...], ndarray[Any, Any]]]"
                """
                circuit += MeasurementGate(1, key=quil_memory_reference, confusion_map=cmap)(
                    line_qubit
                )
            else:
                circuit += MeasurementGate(1, key=quil_memory_reference)(line_qubit)

        # PRAGMAs
        elif isinstance(inst, Pragma):
            continue

        # Drop FENCE statements
        elif isinstance(inst, (Fence, FenceAll)):  # pragma: no cover
            continue

        # Drop DEFGATES
        elif isinstance(inst, (DefGate)):  # pragma: no cover
            continue

        # Raise a targeted error when encountering a RESET.
        elif isinstance(inst, (Reset, ResetQubit)):
            raise UnsupportedQuilInstruction(RESET_ERROR)

        # Raise a general error when encountering an unconsidered type.
        else:
            raise UnsupportedQuilInstruction(
                f"Quil instruction {inst} of type {type(inst)} not currently supported in Cirq."
            )

    if len(kraus_model) > 0:  # pragma: no cover
        noise_model = kraus_noise_model_to_cirq(kraus_model, defined_gates)
        circuit = circuit.with_noise(noise_model)

    return circuit
Convert a Quil program to a Cirq Circuit.

    Args:
        quil: The Quil program to convert.

    Returns:
        A Cirq Circuit generated from the Quil program.

    Raises:
        UnsupportedQuilInstruction: Cirq does not support the specified Quil instruction.
        UndefinedQuilGate: Cirq does not support the specified Quil gate.

    References:
        https://github.com/rigetti/pyquil
    
cirq-rigetti/cirq_rigetti/quil_input.py