{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# How to optimize controls with time symmetrization\n",
"**Incorporate time symmetry into optimized waveforms**\n",
"\n",
"Boulder Opal exposes a highly-flexible [optimization engine](https://docs.q-ctrl.com/boulder-opal/references/qctrl/Functions/calculate_optimization.html) for general-purpose gradient-based optimization. It can be directly applied to model-based control optimization for arbitrary-dimensional quantum systems. \n",
"\n",
"In some cases, forcing control solutions to exhibit time symmetry can yield natural noise robustness while simplifying the optimization problem (by halving the dimensionality of the search space).\n",
"In this user guide we will show an easy method to build time-symmetrized pulses.\n",
"To learn the basics about control optimization, you can follow our [robust optimization tutorial](https://docs.q-ctrl.com/boulder-opal/tutorials/design-robust-single-qubit-gates-using-computational-graphs).\n",
"\n",
"\n",
"## Summary workflow\n",
"### 1. Define time-symmetry constraint in computational graph\n",
"The flexible Boulder Opal optimization engine expresses all optimization problems as data flow graphs, which describe how optimization variables (variables that can be tuned by the optimizer) are transformed into the cost function (the objective that the optimizer attempts to minimize). \n",
"\n",
"To enforce time symmetry, we create the control signals in two steps: we first create a standard signal for the first half of the gate duration, and then we create a copy (depending on the same underlying control parameters) that is reflected and concatenated with the original signal using the `graph.symmetrize_pwc` graph operation.\n",
"```python\n",
"pwc_signal = graph.symmetrize_pwc(half_pwc_signal, name=\"pwc_signal\")\n",
"```\n",
"\n",
"### 2. Execute graph-based optimization\n",
"\n",
"With the graph object created, an optimization can be run using the `qctrl.functions.calculate_optimization` function. The function returns the results of the optimization and should include the symmetrized PWC as an output node of the graph using `output_node_names`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Example: Time-symmetrized robust pulses for a single qubit\n",
"\n",
"In this example, we perform a robust optimization of a single qubit using symmetric pulses. The single-qubit system is represented by the following Hamiltonian:\n",
"$$\n",
"H(t) = \\frac{\\nu}{2} \\sigma_{z} + \\frac{1}{2}\\left(\\gamma(t)\\sigma_{-} + \\gamma^*(t)\\sigma_{+}\\right) + \\frac{\\alpha(t)}{2} \\sigma_{z} + \\beta(t) \\sigma_{z} ,\n",
"$$\n",
"where $\\nu$ is the qubit detuning, $\\gamma(t)$ and $\\alpha(t)$ are, respectively, complex and real time-dependent pulses, and $\\beta(t)$ is a small, slowly-varying stochastic dephasing noise process. Here $\\gamma(t)$ and $\\alpha(t)$ will be optimized subject to a time-symmetry constraint."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"from qctrlvisualizer import plot_controls\n",
"\n",
"from qctrl import Qctrl\n",
"\n",
"# Start a Boulder Opal session\n",
"qctrl = Qctrl()"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
" 0%| | 0/100 [00:00"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Define physical constraints\n",
"gamma_max = 2 * np.pi * 8.5e6 # Hz\n",
"alpha_max = 2 * np.pi * 8.5e6 # Hz\n",
"nu = 2 * np.pi * 6e6 # Hz\n",
"segment_count = 50\n",
"duration = 154e-9 # s\n",
"\n",
"# Create graph object\n",
"graph = qctrl.create_graph()\n",
"\n",
"# Create detuning term.\n",
"detuning = nu * graph.pauli_matrix(\"Z\") / 2\n",
"\n",
"# Create a complex PWC signal describing the first half of gamma(t)\n",
"half_gamma = graph.utils.complex_optimizable_pwc_signal(\n",
" segment_count=segment_count, maximum=gamma_max, duration=duration / 2\n",
")\n",
"# Define gamma(t) by symmetrizing half_gamma\n",
"gamma = graph.symmetrize_pwc(half_gamma, name=\"gamma\")\n",
"# Create drive term\n",
"drive = graph.hermitian_part(gamma * graph.pauli_matrix(\"M\"))\n",
"\n",
"# Create alpha(t) similarly\n",
"alpha = graph.symmetrize_pwc(\n",
" graph.utils.real_optimizable_pwc_signal(\n",
" segment_count=segment_count,\n",
" minimum=-alpha_max,\n",
" maximum=alpha_max,\n",
" duration=duration / 2,\n",
" ),\n",
" name=\"alpha\",\n",
")\n",
"# Create clock shift term\n",
"shift = alpha * graph.pauli_matrix(\"Z\") / 2\n",
"\n",
"# Create dephasing noise term\n",
"dephasing = graph.pauli_matrix(\"Z\") / duration\n",
"\n",
"# Create target\n",
"target_operator = graph.target(operator=graph.pauli_matrix(\"Y\"))\n",
"\n",
"# Create infidelity\n",
"infidelity = graph.infidelity_pwc(\n",
" hamiltonian=detuning + drive + shift,\n",
" target=target_operator,\n",
" noise_operators=[dephasing],\n",
" name=\"infidelity\",\n",
")\n",
"\n",
"# Run the optimization\n",
"optimization_result = qctrl.functions.calculate_optimization(\n",
" cost_node_name=\"infidelity\",\n",
" output_node_names=[\"alpha\", \"gamma\"],\n",
" graph=graph,\n",
" optimization_count=4,\n",
")\n",
"\n",
"print(f\"\\nOptimized cost:\\t{optimization_result.cost:.3e}\")\n",
"\n",
"# Plot the optimized controls\n",
"plot_controls(\n",
" {\n",
" \"$\\\\alpha$\": optimization_result.output[\"alpha\"],\n",
" \"$\\\\gamma$\": optimization_result.output[\"gamma\"],\n",
" }\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"| Package | Version |\n",
"| --------------------- | ------------ |\n",
"| Python | 3.10.8 |\n",
"| matplotlib | 3.6.3 |\n",
"| numpy | 1.24.1 |\n",
"| scipy | 1.10.0 |\n",
"| qctrl | 20.1.1 |\n",
"| qctrl-commons | 17.7.0 |\n",
"| boulder-opal-toolkits | 2.0.0-beta.3 |\n",
"| qctrl-visualizer | 4.4.0 |\n"
]
}
],
"source": [
"from qctrl.utils import print_environment_related_packages\n",
"\n",
"print_environment_related_packages()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.8"
}
},
"nbformat": 4,
"nbformat_minor": 4
}