Advanced usage — pyQuil 4.14.0rc0 documentation (2024)

Note

If you’re running locally, remember set up the QVM and quilc in server mode before trying to usethem: Setting up requisite servers for pyQuil.

pyQuil configuration

QCSClient instructs pyQuil on how to connect with the components needed to compile and runprograms (quilc, qvm, and QCS). Any APIs that take a configuration object as input(e.g. get_qc()) typically do so optionally, so that a default configuration can be loadedfor you if one is not provided. You can override this default configuration by either instantiating your ownQCSClient object and providing it as input to the function in question,or by setting the QCS_SETTINGS_FILE_PATH and/or QCS_SECRETS_FILE_PATH environment variables to havepyQuil load its settings and secrets from specific locations. By default, configuration will be loaded from$HOME/.qcs/settings.toml and $HOME/.qcs/secrets.toml.

Additionally, you can override whichever QVM and quilc URLs are loaded from settings.toml(profiles.<profile>.applications.pyquil.qvm_url and profiles.<profile>.applications.pyquil.quilc_url fields)by setting the QCS_SETTINGS_APPLICATIONS_QVM_URL and/or QCS_SETTINGS_APPLICATIONS_QUILC_URLenvironment variables. If these URLs are missing from settings.toml and are not set by environment variables,the following defaults will be used (as they correspond to the default behavior of the QVM and quilc when runninglocally):

  • QVM URL: http://127.0.0.1:5000

  • quilc URL: tcp://127.0.0.1:5555

Multithreading

QuantumComputer objects are safe to share between threads, enabling you to execute and retrieveresults for multiple programs or parameter values at once. Note that Program andEncryptedProgram are not thread-safe, and should be copied (with copy()) before use in aconcurrent context.

Note

The QVM processes incoming requests in parallel, while a QPU may process them sequentially or in parallel(depending on the qubits used). If you encounter timeouts while trying to run large numbers of programs against aQPU, try increasing the execution_timeout parameter on calls to get_qc() (specified inseconds).

Note

We suggest running jobs with a minimum of 2x parallelism, so that the QVM or QPUis fully occupied while your program runs and no time is wasted in between jobs.

Note

Because pyQuil does not currently have an asyncio API it is recommended to use ThreadPools.

Below is an example that demonstrates how to use pyQuil in a multithreading scenario:

from multiprocessing.pool import ThreadPoolfrom pyquil import get_qc, Programfrom pyquil.api import QCSClientqc = get_qc("Aspen-M-3")def run(program: Program): return qc.run(qc.compile(program)).get_register_map().get("ro")programs = [ Program( "DECLARE ro BIT", "RX(pi) 0", "MEASURE 0 ro", ).wrap_in_numshots_loop(10),] * 20with ThreadPool(5) as pool: results = pool.map(run, programs)for i, result in enumerate(results): print(f"Results for program {i}:\n{result}\n")

Alternative QPU endpoints

Rigetti QCS supports alternative endpoints for access to a QPU architecture, useful for very particular cases.Generally, this is useful to call “mock” or test endpoints, which simulate the results of execution for thepurposes of integration testing without the need for an active reservation or contention with other users.See the QCS API Docs for more information on QPU Endpoints.

To be able to call these endpoints using pyQuil, enter the endpoint_id of your desired endpoint in oneof the sites where quantum_processor_id is used:

# Option 1qc = get_qc("Aspen-M-3", endpoint_id="my_endpoint")# Option 2qam = QPU(quantum_processor_id="Aspen-M-3", endpoint_id="my_endpoint")

After doing so, for all intents and purposes - compilation, optimization, etc - your program will behave the sameas when using “default” endpoint for a given quantum processor, except that it will be executed by analternate QCS service, and the results of execution should not be treated as correct or meaningful.

Using libquil for Quilc and QVM

Note

This feature is experimental and may not work for all platforms.

libquil provides the functionality of Quilc and QVM in a librarythat can be used without having to run Quilc and QVM as servers, which can make developing with pyQuileasier.

To use libquil, first follow its installation instructions.Once libquil and its dependencies are installed, you will need to run the following command to install a compatibleversion of qcs-sdk-python:

poetry run pip install --config-settings=build-args='--features libquil' qcs-sdk-python --force-reinstall --no-binary qcs-sdk-python

You can then check that libquil is available to pyQuil by executing the following Python code

from pyquil.diagnostics import get_reportprint(get_report())

Towards the end of the output, you will see a libquil section like below

libquil: available: true quilc version: 1.27.0 qvm version: 1.17.2 (077ba23)

If you do not see available: true then re-try installation. If you continue to have issues, please report themon github.

If installation was successful, you can now use libquil in pyQuil: the get_qc function provides two keyword parameters quilc_client and qvm_client which can be set to use libquil:

from pyquil import get_qcfrom qcs_sdk.compiler.quilc import QuilcClientfrom qcs_sdk.qvm import QVMClientqc = get_qc("8q-qvm", quilc_client=QuilcClient.new_libquil(), qvm_client=QVMClient.new_libquil())

Please report issues on github.

Using qubit placeholders

Note

The functionality provided inline by QubitPlaceholders is similar to writing a function which returns aProgram, with qubit indices taken as arguments to the function.

In pyQuil, we typically use integers to identify qubits

from pyquil import Programfrom pyquil.gates import CNOT, Hprint(Program(H(0), CNOT(0, 1)))
H 0CNOT 0 1

However, when running on real, near-term QPUs we care about whatparticular physical qubits our program will run on. In fact, we may wantto run the same program on an assortment of different qubits. This iswhere using QubitPlaceholders comes in.

from pyquil.quilatom import QubitPlaceholderq0 = QubitPlaceholder()q1 = QubitPlaceholder()p = Program(H(q0), CNOT(q0, q1))print(p)
H Placeholder(QubitPlaceholder(0x600002DEB5B0))CNOT Placeholder(QubitPlaceholder(0x600002DEB5B0)) Placeholder(QubitPlaceholder(0x600002DEABB0))

Addressing qubits

If your program uses QubitPlaceholders, the placeholders must be resolved before your program canbe run. If you try to run a program with unresolved placeholders, you will get an error:

print(p.out())
RuntimeError: Qubit q4402789176 has not been assigned an index

Instead, you must explicitly map the placeholders to physical qubits. Bydefault, the function address_qubits() will address qubits from 0 toN, skipping indices that are already used in the program.

from pyquil.quil import address_qubitsprint(address_qubits(p))
H 0CNOT 0 1

The real power comes into play when you provide an explicit mapping:

print(address_qubits(p, qubit_mapping={ q0: 14, q1: 19,}))
H 14CNOT 14 19

As an alternative to a mapping, you can consider using resolve_placeholders_with_custom_resolvers().This method accepts any function that takes a placeholder as an argument, and returns a fixed value for that placeholder (orNone, if you want it to remain unresolved).

q0 = QubitPlaceholder()q1 = QubitPlaceholder()p = Program(H(q0), CNOT(q0, q1))qc = get_qc("2q-qvm")def qubit_resolver(placeholder: QubitPlaceholder) -> Optional[int]: if placeholder == q0: return 0 if placeholder == q1: return Nonep.resolve_placeholders_with_custom_resolvers(qubit_resolver=qubit_resolver)print(p)
H 0CNOT 0 Placeholder(...)

Requesting a register of qubit placeholders

Usually, your algorithm will use an assortment of qubits. You can usethe convenience function register() to request aregister of qubits to build your program.

qbyte = QubitPlaceholder.register(8)p_evens = Program(H(q) for q in qbyte)print(address_qubits(p_evens, {q: i*2 for i, q in enumerate(qbyte)}))
H 0H 2H 4H 6H 8H 10H 12H 14

Classical control flow

Here are a couple quick examples that show how much richer a Quil programcan be with classical control flow.

Warning

Dynamic control flow can have unexpected effects on readout data. See Accessing raw execution data for more information.

While loops

In this first example, we create a while loop by following these steps:

  1. Declare a register called flag_register to use as a boolean test for looping.

  2. Initialize this register to 1, so our while loop will execute. This is often called theloop preamble or loop initialization.

  3. Write the body of the loop in its own Program. This will be aprogram that applies an \(X\) gate followed by an \(H\) gate on ourqubit.

  4. Use the while_do() method to add control flow.

  5. Call resolve_label_placeholders() to resolve the label placeholders inserted by while_do.

from pyquil import Programfrom pyquil.gates import *# Initialize the Program and declare a 1 bit memory space for our boolean flagouter_loop = Program()flag_register = outer_loop.declare('flag_register', 'BIT')# Set the initial flag value to 1outer_loop += MOVE(flag_register, 1)# Define the body of the loop with a new Programinner_loop = Program()inner_loop += Program(X(0), H(0))inner_loop += MEASURE(0, flag_register)# Run inner_loop in a loop until flag_register is 0outer_loop.while_do(flag_register, inner_loop)outer_loop.resolve_label_placeholders()print(outer_loop)
DECLARE flag_register BIT[1]MOVE flag_register[0] 1LABEL @START_0JUMP-UNLESS @END_0 flag_register[0]X 0H 0MEASURE 0 flag_register[0]JUMP @START_0LABEL @END_0

Notice that the outer_loop program applied a Quil instruction directly to aclassical register. There are several classical commands that can be used in this fashion:

  • NOT which flips a classical bit

  • AND which operates on two classical bits

  • IOR which operates on two classical bits

  • MOVE which moves the value of a classical bit at one classical address into another

  • EXCHANGE which swaps the value of two classical bits

Note

The approach documented here can be used to construct a “numshots” loop in pure Quil. See thewith_loop() method and Build a fixed-count loop with Quil for moreinformation.

If, then

In this next example, we show how to do conditional branching in theform of the traditional if construct as in many programminglanguages. Much like the last example, we construct programs for eachbranch of the if, and put it all together by using the if_then()method.

# Declare our memory spacesbranching_prog = Program()ro = branching_prog.declare('ro', 'BIT')test_register = branching_prog.declare('test_register', 'BIT')# Construct each branch of our if-statement. We can have empty branches# simply by having empty programs.then_branch = Program(X(0))else_branch = Program()# Construct our program so that the result in test_register is equally likely to be a 0 or 1branching_prog += H(1)branching_prog += MEASURE(1, test_register)# Add the conditional branchingbranching_prog.if_then(test_register, then_branch, else_branch)# Measure qubit 0 into our readout registerbranching_prog += MEASURE(0, ro)branching_prog.resolve_label_placeholders()print(branching_prog)
DECLARE ro BIT[1]DECLARE test_register BIT[1]H 1MEASURE 1 test_register[0]JUMP-WHEN @THEN_0 test_register[0]JUMP @END_0LABEL @THEN_0X 0LABEL @END_0MEASURE 0 ro[0]

We can run this program a few times to see what we get in the readout register ro.

from pyquil import get_qcqc = get_qc("2q-qvm")branching_prog.wrap_in_numshots_loop(10)result = qc.run(branching_prog)print(result.get_register_map()['test_register'])
[[1] [1] [1] [0] [1] [0] [0] [1] [1] [0]]

Sentinel based loop

Now that we understand how to create loops and conditionals, we can put them together to create a sentinel controlledloop. That is, we’ll repeat the body of a program until a certain condition is met. In this example, we’ll use theclassic bell state program to demonstrate the concept. However, this technique can be applied to any program with aprobabilistic outcome that we want to repeat until we get a desired result.

To start, let’s import everything we’ll need:

# Import some types we'll usefrom typing import Optional, Tuple# We'll use numpy to help us validate our resultsimport numpy as np# We'll need to create a program and define an executorfrom pyquil import Program, get_qc# We'll use these gates in our programfrom pyquil.gates import CNOT, H, X# We'll also need the help of a few control flow instructionsfrom pyquil.quilbase import Halt, Qubit, MemoryReference, JumpTarget, Jumpfrom pyquil.quilatom import Label

Building our program

Adding control flow to a program introduces complexity, especially as we add more branches to the program. To managethis complexity we’ll use some of the methods we learned about in the previous sections as well as by breaking down theprogram into its constituent parts.

The program body

First, let’s define the body of our program. This is the part of the program that we’ll repeat until we get the resultwe desire. In this case, we’ll create a bell state between two qubits and measure them:

def body(qubits: Tuple[Qubit, Qubit], measures: MemoryReference) -> Program: """Constructs a bell state between the given qubit and measures them into the given memory reference.""" program = Program(H(qubits[0]), CNOT(*qubits)) program.measure_all(*zip(qubits, measures)) return program
Resetting state

For this program, we’ll say our desired result is that both qubits measure to 0. After an unsuccessful attempt wherethey measure to 1, we’ll want to reset the state of the qubits before trying again. To do this, we’ll create a programthat applies an \(X\) gate to the qubits if either of them measured to 1:

def reset_bell_state(qubits: Tuple[Qubit, Qubit], measures: MemoryReference) -> Program: """Resets the state of the qubits if either of them measured to 1.""" program = Program() program.if_then(measures[0], Program(X(qubits[0]))) program.if_then(measures[1], Program(X(qubits[1]))) return program
Enforcing a sentinel condition

Next, we’ll construct the part of our program that enforces the sentinel condition. In this case, we’ll end the programif the given memory reference is 0, otherwise we’ll want to reset the state of our qubits and jump back to thebeginning of the program. We’ll construct the branch that ends the program using Quil’s Halt instruction, and we’llaccept the alternative branch as an argument to our function and pass it in the next step:

def enforce_sentinel(mem_ref: MemoryReference, else_program: Program) -> Program: """Ends the program if mem_ref is 0, otherwise executes else_program.""" program = Program() # We use the `if_then` method here to help us construct our branch. As described above, # `if_then` takes a memory reference and two programs. It constructs a branch that # runs the first program if `mem_ref` is 1, otherwise it runs the second program. # Since we want to end the program if `mem_ref` is 0, we pass in our HALTing # program as the second program and the alternative branch as the first. program.if_then(mem_ref, else_program, Program(Halt())) return program
Putting it all together

With each component of our program ready, we just need to compose all the pieces:

def sentinel_program(qubits: Tuple[Qubit, Qubit]) -> Program: # Create a label to reference the start of the program start_label = Label("start-loop") # Use the label to create a jump target at the beginning of the program program = Program(JumpTarget(start_label)) # Declare a register to measure the qubits into measures = program.declare("measures", "BIT", 2) # Add the loop body to our program program += body(qubits, measures) # If the sentinel condition isn't met, then we: reset = Program( # Reset the state of our qubits reset_bell_state(qubits, measures), # Jump back to the start of the program Jump(start_label) ) # Finally, if both Qubits measured to 0 (our sentinel), then we want to end the program # Otherwise, we try again. program += enforce_sentinel(measures[0], reset) # We used pyQuil to construct some of the branches for us, those methods use label placeholders # to avoid conflicts with existing labels in the program, so we resolve those placeholders here. program.resolve_label_placeholders() return program
Testing our program

Now that we have our program, let’s test it out. We’ll use the sentinel_program function to construct the programand run it against a QVM for 1000 shots. We’ll use numpy to assert that the measures register contains only 0s. Over1000 trials, this result would be improbable if our program didn’t work as intended.

qubits = (Qubit(0), Qubit(1))qc = get_qc("2q-qvm")program = sentinel_program(qubits)program.wrap_in_numshots_loop(1000)print(program.out())results = qc.run(program)measures = results.get_register_map()["measures"]assert np.all(measures == 0)
DECLARE measures BIT[2]LABEL @start-loopH 0CNOT 0 1MEASURE 0 measures[0]MEASURE 1 measures[1]JUMP-WHEN @THEN_0 measures[0]HALTJUMP @END_0LABEL @THEN_0JUMP-WHEN @THEN_1 measures[0]JUMP @END_1LABEL @THEN_1X 0LABEL @END_1JUMP-WHEN @THEN_2 measures[1]JUMP @END_2LABEL @THEN_2X 1LABEL @END_2JUMP @start-loopLABEL @END_0

Pauli Operator Algebra

Many algorithms require manipulating sums of Pauli combinations, such as\(\sigma = \frac{1}{2}I - \frac{3}{4}X_0Y_1Z_3 + (5-2i)Z_1X_2,\) where\(G_n\) indicates the gate \(G\) acting on qubit \(n\). Wecan represent such sums by constructing PauliTerm and PauliSum.The above sum can be constructed as follows:

from pyquil.paulis import ID, sX, sY, sZ# Pauli term takes an operator "X", "Y", "Z", or "I"; a qubit to act on, and# an optional coefficient.a = 0.5 * ID()b = -0.75 * sX(0) * sY(1) * sZ(3)c = (5-2j) * sZ(1) * sX(2)# Construct a sum of Pauli terms.sigma = a + b + cprint(f"sigma = {sigma}")
sigma = (0.5+0j)*I + (-0.75+0j)*X0*Y1*Z3 + (5-2j)*Z1*X2

Right now, the primary thing one can do with Pauli terms and sums is to construct theexponential of the Pauli term, i.e., \(\exp[-i\beta\sigma]\). This isaccomplished by constructing a parameterized Quil program that is evaluatedwhen passed values for the coefficients of the angle \(\beta\).

Related to exponentiating Pauli sums, we provide utility functions for findingthe commuting subgroups of a Pauli sum and approximating the exponential with theSuzuki-Trotter approximation through fourth order.

When arithmetic is done with Pauli sums, simplification is automaticallydone.

The following shows an instructive example of all three.

from pyquil.paulis import exponential_mapsigma_cubed = sigma * sigma * sigmaprint(f"Simplified: {sigma_cubed}\n")# Produce Quil code to compute exp[iX]H = -1.0 * sX(0)print(f"Quil to compute exp[iX] on qubit 0:\n" f"{exponential_map(H)(1.0)}")
Simplified: (32.46875-30j)*I + (-16.734375+15j)*X0*Y1*Z3 + (71.5625-144.625j)*Z1*X2Quil to compute exp[iX] on qubit 0:H 0RZ(-2) 0H 0

exponential_map returns a function allowing you to fill in a multiplicativeconstant later. This commonly occurs in variational algorithms. The functionexponential_map is used to compute \(\exp[-i \alpha H]\) without explicitly filling in avalue for \(\alpha\).

expH = exponential_map(H)print(f"0:\n{expH(0.0)}\n")print(f"1:\n{expH(1.0)}\n")print(f"2:\n{expH(2.0)}")
0:H 0RZ(0) 0H 01:H 0RZ(-2) 0H 02:H 0RZ(-4) 0H 0

To take it one step further, you can use Parametric compilation with exponential_map. For instance:

ham = sZ(0) * sZ(1)prog = Program()theta = prog.declare('theta', 'REAL')prog += exponential_map(ham)(theta)
Advanced usage — pyQuil 4.14.0rc0 documentation (2024)

References

Top Articles
The Telltale Traces of the US Military’s New ‘Bladed’ Missile - bellingcat
EXPLAINER: A look at the missile that killed al-Qaida leader
Avonlea Havanese
Falgout Funeral Home Obituaries Houma
Wmu Course Offerings
Bluegabe Girlfriend
Bank Of America Appointments Near Me
Lenscrafters Westchester Mall
CHESAPEAKE WV :: Topix, Craigslist Replacement
What is international trade and explain its types?
Truist Drive Through Hours
California Department of Public Health
Washington Poe en Tilly Bradshaw 1 - Brandoffer, M.W. Craven | 9789024594917 | Boeken | bol
Dignity Nfuse
Overton Funeral Home Waterloo Iowa
Apply for a credit card
Axe Throwing Milford Nh
Long Island Jobs Craigslist
Persona 5 Royal Fusion Calculator (Fusion list with guide)
Sef2 Lewis Structure
Magic Seaweed Daytona
What Is The Lineup For Nascar Race Today
Chicago Based Pizza Chain Familiarly
Sorrento Gourmet Pizza Goshen Photos
Local Collector Buying Old Motorcycles Z1 KZ900 KZ 900 KZ1000 Kawasaki - wanted - by dealer - sale - craigslist
Craigslist Rome Ny
Egusd Lunch Menu
Grave Digger Wynncraft
Ryujinx Firmware 15
About | Swan Medical Group
Quality Tire Denver City Texas
A Small Traveling Suitcase Figgerits
Poster & 1600 Autocollants créatifs | Activité facile et ludique | Poppik Stickers
1400 Kg To Lb
Tyler Sis 360 Boonville Mo
Federal Student Aid
4083519708
Games R Us Dallas
Laurin Funeral Home | Buried In Work
Smite Builds Season 9
Jamesbonchai
Academic Notice and Subject to Dismissal
Unit 11 Homework 3 Area Of Composite Figures
Gw2 Support Specter
La Qua Brothers Funeral Home
Leland Westerlund
25100 N 104Th Way
Walmart Front Door Wreaths
Okta Hendrick Login
Deviantart Rwby
7 National Titles Forum
Latest Posts
Article information

Author: Jonah Leffler

Last Updated:

Views: 6126

Rating: 4.4 / 5 (65 voted)

Reviews: 88% of readers found this page helpful

Author information

Name: Jonah Leffler

Birthday: 1997-10-27

Address: 8987 Kieth Ports, Luettgenland, CT 54657-9808

Phone: +2611128251586

Job: Mining Supervisor

Hobby: Worldbuilding, Electronics, Amateur radio, Skiing, Cycling, Jogging, Taxidermy

Introduction: My name is Jonah Leffler, I am a determined, faithful, outstanding, inexpensive, cheerful, determined, smiling person who loves writing and wants to share my knowledge and understanding with you.