<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 RAW-CAPTURE on Aspen-8
In this we are going to show how to access "raw" measurement data with Quilt.
Step1:  Peeking at a MEASURE calibration
We first take a peek at how a measurement operation is specified. We can dot his by looking at the corresponding calibration. Below we consider the calibration for MEASURE 0.
Step2:  There are a few things note about the above
Step3:  An almost-trivial example
First, let's just run a RAW-CAPTURE instruction. We will apply this to the above CAPTURE frame, i.e. 0 "ro_rx", and for the same duration the CAPTURE. The principal difference is that rather than read-out to a memory region of length 2, we will need many more. It is easy to compute the size $n$ of the output, namely
\begin{equation}
n = \left \lceil{t \cdot f_s}\right \rceil , 
\end{equation}
where $t$ is the duration in seconds, $f_s$ is the sample rate in Hz (which is part of the frame definition), and $ \left \lceil x \right \rceil $ denotes the smallest integer not less than $x$.
Step4:  Raw capture results are by default represented as integers in the interval $[-2^{15}, 2^{15}]$. For many analyses you may prefer to normalize to the range $[-1,1]$.
Step5:  Applying a PULSE before RAW-CAPTURE
Recall how measurements are usually done
Step6:  Capturing an excited qubit
Finally, we extend the above by first exciting the qubit, by applying a RX(pi) gate.
Step7:  TODO
Discuss readout classification.
Some Restrictions May Apply
Performing a RAW-CAPTURE operation places a number of demands on the underlying hardware, and thus comes with a few constraints. We demonstrate these here.
Capture duration exceeds maximum length
A RAW-CAPTURE operation can capture at most 8192 samples per shot, which puts a limit of $\frac{8192}{f_s}$ seconds for the duration, where $f_s$ is the frame's sample rate.
Step8:  Number of samples in a job exceeds maximum
There is a total limit of $2^{24}$ samples per job, i.e. duration * sample_rate * num_shots cannot exceed $2^24$.
Step9:  RAW-CAPTURE precludes the use of other capture operations
Due to the hardware requirements associated with RAW-CAPTURE, the following limits are currently imposed
<ASSISTANT_TASK:>
Python Code:
from pyquil import Program, get_qc
qc = get_qc('Aspen-8')
cals = qc.compiler.calibration_program
Explanation: RAW-CAPTURE on Aspen-8
In this we are going to show how to access "raw" measurement data with Quilt.
End of explanation
from pyquil.quilatom import Qubit, Frame
from pyquil.quilbase import Pulse, Capture, DefMeasureCalibration
qubit = Qubit(0)
measure_defn = next(defn for defn in cals.calibrations 
                    if isinstance(defn, DefMeasureCalibration) and defn.qubit == qubit)
print(measure_defn)
Explanation: Peeking at a MEASURE calibration
We first take a peek at how a measurement operation is specified. We can dot his by looking at the corresponding calibration. Below we consider the calibration for MEASURE 0.
End of explanation
pulse = next(i for i in measure_defn.instrs if isinstance(i, Pulse))
print(pulse, "\n")
capture = next(i for i in measure_defn.instrs if isinstance(i, Capture))
print(capture, "\n")
frame = Frame([qubit], "ro_rx")
frame_defn = cals.frames[frame]
print(frame_defn)
Explanation: There are a few things note about the above:
The basic structure of MEASURE 0 addr is to apply a pulse on the "ro_tx" frame, and then perform a capture on the corresponding "ro_rx" frame.
Although the user may perform MEASURE 0 ro, the memory location required for this is a bit. Under the hood, CAPTURE writes a complex IQ value to the REAL[2] region q0_unclassified.
The wrangling in order to map from q0_unclassified to addr is controlled through PRAGMA operations. These are important for downstream processing and execution of the Quil program. Tamper with them at your own risk!
RAW-CAPTURE experiments
The value stored in q0_unclassified has already been processed on hardware: in particular, it is produced by demodulating a passband signal and then integrating against the CAPTURE waveform. What RAW-CAPTURE does is give you, the user, access to the raw values of that passband signal. In the following, execute programs with RAW-CAPTURE, and plot their results.
Before we begin, it will be useful to get some data associated with the above MEASURE calibration. In particular, the PULSE and CAPTURE operations, as well as the frame definition for 0 "ro_rx".
End of explanation
from math import ceil
duration = capture.kernel.duration
sample_rate = frame_defn.sample_rate
memory_length = ceil(duration * sample_rate)
raw_capture_no_pulse = Program(     
    f'DECLARE raw REAL[{memory_length}]',
    f'RAW-CAPTURE {frame} {duration} raw'
).wrap_in_numshots_loop(1000)
print(raw_capture_no_pulse)
exe = qc.compiler.native_quil_to_executable(raw_capture_no_pulse)
qc.run(exe)
raw_results_no_pulse = qc.qam.read_memory(region_name='raw')
Explanation: An almost-trivial example
First, let's just run a RAW-CAPTURE instruction. We will apply this to the above CAPTURE frame, i.e. 0 "ro_rx", and for the same duration the CAPTURE. The principal difference is that rather than read-out to a memory region of length 2, we will need many more. It is easy to compute the size $n$ of the output, namely
\begin{equation}
n = \left \lceil{t \cdot f_s}\right \rceil , 
\end{equation}
where $t$ is the duration in seconds, $f_s$ is the sample rate in Hz (which is part of the frame definition), and $ \left \lceil x \right \rceil $ denotes the smallest integer not less than $x$.
End of explanation
print("shape", raw_results_no_pulse.shape)
print("data", raw_results_no_pulse)
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
plt.figure()
plt.gcf().set_size_inches(20.5, 10.5)
plt.plot(np.arange(len(raw_results_no_pulse[0,:]))/sample_rate, raw_results_no_pulse[0,:])
plt.show()
avg_results_no_pulse = raw_results_no_pulse.mean(axis=0) / (2**15)
plt.psd(avg_results_no_pulse, Fs=sample_rate)
plt.show()
Explanation: Raw capture results are by default represented as integers in the interval $[-2^{15}, 2^{15}]$. For many analyses you may prefer to normalize to the range $[-1,1]$.
End of explanation
raw_capture_pulse = Program(     
    f'DECLARE raw REAL[{memory_length}]',
    pulse,
    f'RAW-CAPTURE {frame} {duration} raw'
).wrap_in_numshots_loop(1000)
print(raw_capture_pulse)
exe = qc.compiler.native_quil_to_executable(raw_capture_pulse)
qc.run(exe)
raw_results_pulse = qc.qam.read_memory(region_name='raw')
avg_results_pulse = raw_results_pulse.mean(axis=0) / 2**15
plt.psd(avg_results_pulse, Fs=sample_rate)
plt.show()
Explanation: Applying a PULSE before RAW-CAPTURE
Recall how measurements are usually done: first there is a pulse on the "ro_tx" frame, and then a capture on the "ro_rx" frame. We modify our above program by including the PULSE operation associated with the vanilla measurement.
End of explanation
raw_capture_excited = Program(     
    f'DECLARE raw REAL[{memory_length}]',
    f'RX(pi) {qubit}',
    pulse,
    f'RAW-CAPTURE {frame} {duration} raw'
).wrap_in_numshots_loop(1000)
print(raw_capture_excited)
exe = qc.compiler.native_quil_to_executable(raw_capture_excited)
qc.run(exe)
raw_results_excited = qc.qam.read_memory(region_name='raw')
avg_results_excited = raw_results_excited.mean(axis=0) / 2**15
plt.psd(avg_results_excited, Fs=sample_rate)
plt.show()
Explanation: Capturing an excited qubit
Finally, we extend the above by first exciting the qubit, by applying a RX(pi) gate.
End of explanation
duration = 5e-6
samples = ceil(sample_rate*duration)
rrr = Program(
    f'DECLARE raw REAL[{samples}]', 
    f'RAW-CAPTURE 0 "ro_rx" {duration} raw'
).wrap_in_numshots_loop(1)
try:
    exe = qc.compiler.native_quil_to_executable(rrr)
except Exception as e:
    print(e)
Explanation: TODO
Discuss readout classification.
Some Restrictions May Apply
Performing a RAW-CAPTURE operation places a number of demands on the underlying hardware, and thus comes with a few constraints. We demonstrate these here.
Capture duration exceeds maximum length
A RAW-CAPTURE operation can capture at most 8192 samples per shot, which puts a limit of $\frac{8192}{f_s}$ seconds for the duration, where $f_s$ is the frame's sample rate.
End of explanation
duration = 1e-06
samples = ceil(sample_rate*duration)
rrr = Program(
    f'DECLARE raw REAL[{samples}]', 
    f'RAW-CAPTURE 0 "ro_rx" {duration} raw'
).wrap_in_numshots_loop(100000)
try:
    exe = qc.compiler.native_quil_to_executable(rrr)
except Exception as e:
    print(e)
Explanation: Number of samples in a job exceeds maximum
There is a total limit of $2^{24}$ samples per job, i.e. duration * sample_rate * num_shots cannot exceed $2^24$.
End of explanation
duration = 1e-06
samples = ceil(sample_rate*duration)
rrr = Program(
    f'DECLARE raw REAL[{samples}]', 
    'DECLARE ro BIT',
    'MEASURE 1 ro',
    f'RAW-CAPTURE 0 "ro_rx" {duration} raw'
)
try:
    exe = qc.compiler.native_quil_to_executable(rrr)
except Exception as e:
    print(e)
Explanation: RAW-CAPTURE precludes the use of other capture operations
Due to the hardware requirements associated with RAW-CAPTURE, the following limits are currently imposed:
there can be at most one RAW-CAPTURE operation per program, and
if a program includes RAW-CAPTURE, then it cannot also include CAPTURE operations.
End of explanation
<END_TASK>