{ "cells": [ { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "# Using GST to test for context dependence\n", "This example shows how to introduce new operation labels into a GST analysis so as to test for context dependence. In particular, we'll look at the 1-qubit X, Y, I model. Suppose a usual GST analysis cannot fit the model well, and that we think this is due to the fact that a \"Gi\" gate which immediately follows a \"Gx\" gate is affected by some residual noise that isn't otherwise present. In this case, we can model the system as having two different \"Gi\" gates: \"Gi\" and \"Gi2\", and model the \"Gi\" gate as \"Gi2\" whenever it follows a \"Gx\" gate." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from __future__ import print_function\n", "import pygsti\n", "from pygsti.modelpacks.legacy import std1Q_XYI" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First we'll create a mock data set that exhibits this context dependence. To do this, we add an additional \"Gi2\" gate to the data-generating model, generate some data using \"Gi2\"-containing operation sequences, and finally replace all instances of \"Gi2\" with \"Gi\" so that it looks like data that was supposed to have just a single \"Gi\" gate. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# The usual setup: identify the target model, fiducials, germs, and max-lengths\n", "target_model = std1Q_XYI.target_model()\n", "fiducials = std1Q_XYI.fiducials\n", "germs = std1Q_XYI.germs\n", "maxLengths = [1,2,4,8,16,32]\n", "\n", "# Create a Model to generate the data - one that has two identity gates: Gi and Gi2\n", "mdl_datagen = target_model.depolarize(op_noise=0.1, spam_noise=0.001)\n", "mdl_datagen[\"Gi2\"] = mdl_datagen[\"Gi\"].copy()\n", "mdl_datagen[\"Gi2\"].depolarize(0.1) # depolarize Gi2 even further\n", "mdl_datagen[\"Gi2\"].rotate( (0,0,0.1), mdl_datagen.basis) # and rotate it slightly about the Z-axis\n", "\n", "# Create a set of operation sequences by constructing the usual set of experiments and using \n", "# \"manipulate_circuits\" to replace Gi with Gi2 whenever it follows Gx. Create a \n", "# DataSet using the resulting Gi2-containing list of sequences.\n", "listOfExperiments = pygsti.circuits.create_lsgst_circuits(target_model, fiducials, fiducials, germs, maxLengths)\n", "rules = [ ((\"Gx\",\"Gi\") , (\"Gx\",\"Gi2\")) ] # a single replacement rule: GxGi => GxGi2 \n", "listOfExperiments = pygsti.circuits.manipulate_circuits(listOfExperiments, rules)\n", "ds = pygsti.data.simulate_data(mdl_datagen, listOfExperiments, num_samples=10000,\n", " sample_error=\"binomial\", seed=1234)\n", "\n", "# Revert all the Gi2 labels back to Gi, so that the DataSet doesn't contain any Gi2 labels.\n", "rev_rules = [ ((\"Gi2\",) , (\"Gi\",)) ] # returns all Gi2's to Gi's \n", "ds = ds.copy_nonstatic()\n", "ds = ds.process_circuits(lambda opstr: pygsti.circuits.manipulate_circuit(opstr,rev_rules))\n", "ds.done_adding_data()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Running \"standard\" GST on this `DataSet` resulst in a bad fit: " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "target_model.set_all_parameterizations(\"TP\")\n", "results = pygsti.run_long_sequence_gst(ds, target_model, fiducials, fiducials,\n", " germs, maxLengths, verbosity=2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So, since we have a hunch that the reason for the bad fit is that when \"Gi\" follows \"Gx\" it looks different, we can fit that data to a model that has two identity gates, call them \"Gi\" and \"Gi2\" again, and tell GST to perform the \"GxGi => GxGi2\" manipulation rule before computing the probability of a gate sequence:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Create a target model which includes a duplicate Gi called Gi2\n", "mdl_targetB = target_model.copy()\n", "mdl_targetB['Gi2'] = target_model['Gi'].copy() # Gi2 should just be another Gi\n", "\n", "#Run GST with:\n", "# 1) replacement rules giving instructions how to process all of the operation sequences\n", "# 2) data set aliases which replace labels in the *processed* strings before querying the DataSet.\n", "rules = [ ((\"Gx\",\"Gi\") , (\"Gx\",\"Gi2\")) ] # a single replacement rule: GxGi => GxGi2 \n", "resultsB = pygsti.run_long_sequence_gst(ds, mdl_targetB, fiducials, fiducials,\n", " germs, maxLengths, \n", " advanced_options={\"op_label_aliases\": {'Gi2': pygsti.circuits.Circuit(['Gi'])},\n", " \"string_manipulation_rules\": rules},\n", " verbosity=2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This gives a much better fit. In earlier versions of pyGSTi, before certain optimizer improvements, the above cell gave a slightly better fit but not one as good as it should have (given that we know the data was generated from exactly the model being used). This was because the (default) LGST initial model wasn't a good enought starting point, and the optimizer failed to find the global minimum. This is no longer the case, but we keep the cell below as a demonstration of how to specify a custom guess as the starting point, which could be useful in other scenarios where the default starting point proves insufficient. To explicitly set the starting model, we do the following:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Create a guess, which we'll use instead of LGST - here we just\n", "# take a slightly depolarized target.\n", "mdl_start = mdl_targetB.depolarize(op_noise=0.01, spam_noise=0.01)\n", "\n", "#Run GST with the replacement rules as before.\n", "resultsC = pygsti.run_long_sequence_gst(ds, mdl_targetB, fiducials, fiducials,\n", " germs, maxLengths, \n", " advanced_options={\"op_label_aliases\": {'Gi2': pygsti.circuits.Circuit(['Gi'])},\n", " \"string_manipulation_rules\": rules,\n", " \"starting_point\": mdl_start},\n", " verbosity=2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Both results from using the model containing context dependence give a much better fit and estimate, as seen from the final `2*Delta(log(L))` numbers." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "gsA = pygsti.gaugeopt_to_target(results.estimates['GateSetTomography'].models['final iteration estimate'], mdl_datagen)\n", "gsB = pygsti.gaugeopt_to_target(resultsB.estimates['GateSetTomography'].models['final iteration estimate'], mdl_datagen)\n", "gsC = pygsti.gaugeopt_to_target(resultsC.estimates['GateSetTomography'].models['final iteration estimate'], mdl_datagen)\n", "gsA['Gi2'] = gsA['Gi'] #so gsA is comparable with mdl_datagen\n", "print(\"Diff between truth and standard GST: \", mdl_datagen.frobeniusdist(gsA))\n", "print(\"Diff between truth and context-dep GST w/LGST starting pt: \", mdl_datagen.frobeniusdist(gsB))\n", "print(\"Diff between truth and context-dep GST w/custom starting pt: \", mdl_datagen.frobeniusdist(gsC))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.8.12" } }, "nbformat": 4, "nbformat_minor": 2 }