rigetti / pyquil

A Python library for quantum programming using Quil.
http://docs.rigetti.com
Apache License 2.0
1.4k stars 341 forks source link

Design proposal: support for using Quilc as a library (rather than a service) #1634

Open notmgsk opened 1 year ago

notmgsk commented 1 year ago

Issue Description

libquil-sys provides a Rust interface to the Quilc shared library. There is on-going work in qcs-sdk-rust to use libquil-sys which will allow users of qcs-sdk-rust to use Quilc via TCP (RPCQ) or FFI (libquil-sys). Providing that option means that users no longer have to manage the Quilc server, which has in the past been a source of some headaches.

I would like to make that option available in pyquil v4. The problem this poses is a design question: how do we fit this new feature in alongside what currently exists?

Requirements:

Proposed Solution

The standard way of interacting with pyquil looks something like

from pyquil import get_qc, Program

# 1. Get a QuantumComputer
qc = get_qc("Aspen-M-3", as_qvm=True)

# 2. Build the program
program = Program("H 0; CNOT 0 1; MEASURE 1")

# 3. Compile the program
compiled = qc.compile(program, protoquil=True)

# 4. Execute the program
results = qc.run(compiled)

To satisfy (c) above, I feel like the natural place for this change to be exposed is in get_qc where a new optional parameter quilc_mode is introduced:

def get_qc(
    name: str,
    *,
    as_qvm: Optional[bool] = None,
    # ...
    quilc_mode: Optional[QuilcMode] = None,
) -> QuantumComputer:

QuilcMode is Python enum

class QuilcMode(Enum):
    RPCQ = auto()
    LIBQUIL = auto()

The get_qc machinery then passes this along to the appropriate AbstractCompiler implementation, which can in turn use that information to decide (within quil_to_native_quil) whether to use RPCQ or libquil-sys. That logic will interpret a None value for the quilc_mode parameter as being equivalent to QuilcMode.RPCQ. Thus, requirement (a) above is satisfied.

If a user opts-in to using libquil-sys, the change to their program would be minimal and look like

qc = get_qc(..., quilc_mode=QuilcMode.LIBQUIL)

which (IMO) satisfies both requirements (b) and (c).

To satisfy requirement (d) we would have to allow the initial implementation of QuilcMode to contain the single RPCQ variant, and the quil_to_native_quil implementations would essentially ignore the argument. Later, when the full functionality is supported on the qcs-sdk-rust side, we can extend the enum to include LIBQUIL and amend the quil_to_native_quil code to use the QuilcMode appropriately.

MarquessV commented 1 year ago

This looks like a solid plan to me. I wonder if it would make sense to add this as an option to CompilerOpts on the qcs-sdk-rust side, then expose that to get_qc and the compiler implementation.

BatmanAoD commented 1 year ago

Is quilc used outside of the qc.compile call, and if so, how is it used?

If not, I think it might make sense to put the new optional argument on qc.compile, but I may be missing something.