{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Circuit Lists Tutorial\n", "This tutorial will show you how to create lists of `Circuit` objects. In some cases, we'll want to construct and use a list (or even a list of lists!) of `Circuit`s, so we've dedicated this tutorial to talking about \"circuit lists\".\n", "We utilize the fact that `Circuit` objects behave and compose as tuples of layer-labels (see the [Circuit tutorial](../Circuit.ipynb))." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pygsti\n", "import pygsti.construction as pc" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pygsti.modelpacks import smq1Q_XY #a standard model, packaged with related information" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## List Construction Functions: `pygsti.construction` and `create_circuits`\n", "You'll often be working with entire lists of `Circuit` objects which define some part of the experiments utilized by algorithms such as Gate Set Tomography. pyGSTi provides several functions for constructing circuit lists, which we not demonstrate.\n", "\n", "The workhorse function is `pygsti.construction.create_circuits`, which executes its positional arguments within a nested loop given by iterable keyword arguments. That's a mouthful, so let's look at a few examples:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "As = [('a1',),('a2',)]\n", "Bs = [('b1','b2'), ('b3','b4')]\n", "\n", "def rep2(x):\n", " return x+x\n", "\n", "list1 = pc.create_circuits(\"a\", a=As)\n", "list2 = pc.create_circuits(\"a+b\", a=As, b=Bs, order=['a','b'])\n", "list3 = pc.create_circuits(\"R(a)+c\", a=As, c=[('c',)], R=rep2)\n", "\n", "print(\"list1 = %s\" % list(map(tuple, list1)))\n", "print(\"list2 = %s\" % list2)\n", "print(\"list3 = %s\" % list(map(str,list3)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Many of the operation sequences used by Gate Set Tomography are composed of three parts. A \"preparation fiducial\" sequence is followed by a \"repeated germ\" sequence, which is followed by a \"measurement fiducial\" sequence. We won't get into why this structure is used, but simply use this fact to motivate looking at operation sequences of the form $f_1 + R(g) + f_2$, where the $f_1$ and $f_2$ fiducial sequences are simple short sequences are $R(g)$ is a possibly long sequence that is generated by repeating a short sequence $g$ called a \"germ\".\n", "\n", "It is possible to generate \"repeated germ\" sequences in several ways using the functions **`pygsti.construction.repeat_`*xxx* **. In modern GST, germ sequences are always repeated an *integer* number of times rather than being truncated to a precise length, so `repeat_with_max_length` is used instead of `repeat_and_truncate`. Below we demonstrate the use of these functions." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(pc.repeat_and_truncate(('A', 'B', 'C'), 5)) #args (x,N): repeat x until it is exactly length N\n", "\n", "print(pc.repeat_with_max_length(('A', 'B', 'C'), 5)) #args (x,N): repeat x the maximum integer number of times so len(x) < N\n", "\n", "print(pc.repeat_count_with_max_length(('A', 'B', 'C'), 5)) #args (x,N): the maximum integer number of times so len(x) < N" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can combine a repeated germ sequence between two fiducial sequences using `create_circuits`. This demonstrates the power of the `create_circuits` to perform nested loops. We also introduce the \"bulk-conversion\" function `circuit_list`, which creates a list of `Circuit` objects from a list of tuples." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fids = pc.to_circuits( [ ('Gf0',), ('Gf1',) ] ) #fiducial strings\n", "germs = pc.to_circuits( [ ('G0',), ('G1a','G1b')] ) #germ strings\n", "\n", "circuits1 = pc.create_circuits(\"f0+germ*e+f1\", f0=fids, f1=fids,\n", " germ=germs, e=2, order=[\"germ\",\"f0\",\"f1\"])\n", "print(\"circuits1 = \\n\", \"\\n\".join(map(str,circuits1)),\"\\n\")\n", "\n", "circuits2 = pc.create_circuits(\"f0+T(germ,N)+f1\", f0=fids, f1=fids,\n", " germ=germs, N=3, T=pc.repeat_and_truncate,\n", " order=[\"germ\",\"f0\",\"f1\"])\n", "\n", "print(\"circuits2 = \\n\", \"\\n\".join(map(str,circuits2)),\"\\n\")\n", "\n", "circuits3 = pc.create_circuits(\"f0+T(germ,N)+f1\", f0=fids, f1=fids,\n", " germ=germs, N=3, T=pc.repeat_with_max_length,\n", " order=[\"germ\",\"f0\",\"f1\"])\n", "print(\"circuits3 = \\n\", \"\\n\".join(map(str,circuits3)), \"\\n\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In addition to `create_circuits`, the **`pygsti.construction.list_`*xxx* ** functions provide ways of constructing common operation sequence lists. The example below shows how to construct all possible operation sequences within a certain length range, as well as how to construct the set of operation sequences needed to run Linear LinearOperator Set Tomography given a set of fiducial strings. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "myGates = [ 'Gx', 'Gy' ] #operation labels -- often just model.operations.keys()\n", "allStringsInLengthRange = pc.list_all_circuits(myGates, minlength=0, maxlength=2)\n", "print(\"\\nAll strings using %s up to length 2 = \\n\" \\\n", " % str(myGates), \"\\n\".join(map(str,allStringsInLengthRange)))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "myFiducialList = pc.to_circuits([ ('Gf1',), ('Gf2',) ]) #list of fiducials\n", "\n", "lgstStrings = pc.create_lgst_circuits(myFiducialList,myFiducialList,myGates)\n", "\n", "print(\"\\nLGST strings = \\n\",\"\\n\".join(map(str,lgstStrings)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Manipulating `Circuits`\n", "Sometimes it is useful to manipulate a `circuits` (or a list of them) via find & replace operations. The `manipulate_circuit` and `manipulate_circuits` functions take as input a set of replacement \"rules\" and process one or more `circuits` objects accordingly. For example, the rules\n", "\n", "- ab $\\rightarrow$ AB' (if B follows A, prime B)\n", "- BA $\\rightarrow$ B''A (if B precedes A, double-prime B)\n", "- CA $\\rightarrow$ CA' (if A follows C, prime A)\n", "- BC $\\rightarrow$ BC' (if C follows B, prime C)\n", "\n", "are specified by the dictionary:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sequenceRules = [\n", " ((\"A\", \"B\"), (\"A\", \"B'\")),\n", " ((\"B\", \"A\"), (\"B''\", \"A\")),\n", " ((\"C\", \"A\"), (\"C\", \"A'\")),\n", " ((\"B\", \"C\"), (\"B\", \"C'\"))]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Will produce the output:\n", "- BAB $\\rightarrow$ B''AB'\n", "- ABA $\\rightarrow$ AB'A (frustrated!)\n", "- CAB $\\rightarrow$ CA'B'\n", "- ABC $\\rightarrow$ AB'C'" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pygsti.objects import Circuit\n", "from pygsti.construction import manipulate_circuit\n", "\n", "print(manipulate_circuit(Circuit(tuple('BAB')), sequenceRules))\n", "print(manipulate_circuit(Circuit(tuple('ABA')), sequenceRules))\n", "print(manipulate_circuit(Circuit(tuple('CAB')), sequenceRules))\n", "print(manipulate_circuit(Circuit(tuple('ABC')), sequenceRules))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# You can also process an entire list of operation sequences in bulk\n", "orig_lst = pygsti.construction.to_circuits([ tuple('BAB'), tuple('ABA'), tuple('CAB'), tuple('ABC')])\n", "lst = pygsti.construction.manipulate_circuits(orig_lst, sequenceRules)\n", "print('\\n'.join([str(s) for s in lst]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Gate Label \"Aliases\"\n", "A similar but simpler type of manipulation called \"operation label aliasing\" is used in pyGSTi to map a operation label into another operation label **only for `DataSet` lookups**. The mapping is similar to `manipulate_circuit`'s find & replace functionality, except that (at least currently) the string to find can be only a single operation label (and so isn't even a string at all). The support for operation label aliasing within pyGSTi's algorithms aids in mapping many `Model` models onto the same data (often with simpler gate labelling). " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#TODO: remove Aliasing or provide examples?" ] } ], "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.6" } }, "nbformat": 4, "nbformat_minor": 1 }