-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added XEB script #221
Added XEB script #221
Conversation
This script implements Cross-Entropy Benchmarking which leverages real-time processsing features of the OPX. Namely, the random circuit generation and execution is done on the OPX directly. We also propose tools from Qiskit (should be installed) and Cirq (no need to be installed) for enriching and strengthening the post-processing.
...cting/Two-Flux-Tunable-Transmons/Use Case 3 - Two-Qubit Cross-Entropy Benchmarking/README.md
Outdated
Show resolved
Hide resolved
Since I'm unable to comment on the notebook itself, I will add comments roughly cell-by-cell. |
Cell 1 # !pip install qiskit
from qiskit.circuit import QuantumCircuit
from qiskit.circuit.library import get_standard_gate_name_mapping as gate_map, UnitaryGate, RYGate |
Cell 3 @dataclass
class XEBConfig:
save_dir: str = "" # Directory where the data will be saved
should_save_data: bool = True # Whether to save the data
generate_new_data: bool = True # Whether to generate new data
seqs: int = 100 # Number of random sequences to run per depth
max_depth: int = 3 # Max depth of the XEB sequence
step: int = 1 # Step for choosing which depths to run up to max_depth
depths: np.ndarray = field(init=False) # Array of depths to iterate through
n_shots: int = 101 # Number of averages per sequence
impose_0_cycle: bool = False # Whether to impose the first gate at 0-cycle
apply_two_qb_gate: bool = False
def __post_init__(self):
self.depths = np.arange(1, self.max_depth + 1, self.step) The benefit to doing this is that you can easily save the config parameters to file, or serialize into a dictionary, allowing you to add it directly to the experiment results in e.g., part of the params in an from dataclasses import asdict
config = XEBConfig(...)
config_dict = config.to_dict()
...
np.savez(
config.save_dir / filename,
...
**config_dict,
...
) This isn't usually done in ML, but can be very handy in physics where you share around data for portable analysis by other researchers, or where you do so many runs and forget with what parameters you performed the run and might want to publish the results. For now it's fine, but I thought I'd mention it because this is what I'd do if I was writing a protocol from scratch. |
Cell 4 Path().cwd() is this needed? |
Cell 5 gatesets.py gateset = generate_gateset(gateset="supremacy") The notebook is currently designed to be very transparent which is good, but for a new user looking to just run the notebook and see the results, this cell gives too much detail. If they want more detail, it is only behind a single function call. I also suggest putting the gateset choices as a string to make make things more explicit. Following from my previous comment, the gateset choice/type can also be made a config parameter, such that you can write e.g., gateset = generate_gateset(gateset=config.gateset_choice) |
Cell 6 # change this so that it follows the name of your qubit elements.
# for example, this is how it would look if your one of your elements was "qubit0$xy"
qubit_elements = [f"qubit{i}$xy" for i in qubits]
# change this so that it follows the name of your readout elements.
readout_elements = [f"qubit{i}$rr" for i in qubits]
# change this to configure CZ operations on different qubit pairs
# key: tuple of qubit indices to perform CZ
# value: tuple containing (element name, operation name)
CZ_operations = {
(0, 2): ("qubit2$z", "Cz$flux_pulse_control_qubit0"),
# (1, 2): ("qubit2$z", "Cz$flux_pulse_control_qubit1"),
# (2, 1): ("qubit2$z", "Cz$flux_pulse_control_qubit1"),
# (2, 0): ("qubit2$z", "Cz$flux_pulse_control_qubit0"),
# (2, 3): ("qubit3$z", "Cz$flux_pulse_control_qubit2"),
# (3, 2): ("qubit3$z", "Cz$flux_pulse_control_qubit2"),
} I also commented out those which I think are unused, given that the default is |
Cell 8 def align_qubit(qubit: int):
base_qubit = f"qubit{qubit}$"
elements = [base_qubit + el for el in ["xy", "rr", "z"]]
align(*elements) assumes that your elements have a dollar sign at the end. Instead, the macros should use a template similar to what you define in Cell 6, for example, qubit_element_template = "qubit{}"
...
def align_qubit(qubit: int, qubit_element_template: str):
base_qubit = qubit_element_template.format(qubit)
elements = [base_qubit + el for el in ["xy", "rr", "z"]]
align(*elements)
...
align_qubit(qubit, qubit_element_template) The same assumption is made in the Next, I think you should separate out all of the macros that don't require user input into a separate file called |
Cell 9 I also wanted to comment on the code that looks like this: with while_(g[q][d_] == g[q][d_ - 1]) With the comment you have, it makes it obvious what this code is doing, but in general, one-letter variables can be hard to track because you have to map in your head what the variable is referring to. Since in this case, each variable can be represented with a short word Otherwise I haven't tried to hard to verify the logic of your code, but I trust that it's right if the results look good in a real experiment. |
General Comments
Otherwise, it looks really good! |
Most of the comments have been addressed here. There is now a dataclass for XEBConfig as suggested, however we leave the user to exploit this structure for saving hyperparameters. Moreover, macros contain both QUA and Python functions used throughout the notebook.
...ting/Two-Flux-Tunable-Transmons/Use Case 3 - Two-Qubit Cross-Entropy Benchmarking/gateset.py
Outdated
Show resolved
Hide resolved
Cell 6 # Change this so that it follows the name of your qubit elements.
# This is how it would look if your elements start with "qubit0$",
# since the qubit index will eventually fill in the {} curly braces
qubit_element_template = "qubit{}$"
# And this is how it would look if one of your elements was "qubit0$xy"
qubit_elements = [f"{qubit_element_template.format(i)}xy" for i in qubits]
readout_elements = [f"{qubit_element_template.format(i)}rr" for i in qubits] |
This script implements Cross-Entropy Benchmarking which leverages real-time processsing features of the OPX. Namely, the random circuit generation and execution is done on the OPX directly. We also propose tools from Qiskit (should be installed) and Cirq (no need to be installed) for enriching and strengthening the post-processing.