{ "cells": [ { "cell_type": "markdown", "metadata": { "deletable": true, "editable": true }, "source": [ "# Creating a 2-qubit gate set with a custom 2-qubit gate\n", "While pyGSTi is able to support several common types of 2-qubit gates, the space of all possible 2-qubit gates is so large that some users will need to construct their own particular 2-qubit gate \"from scratch\". In this example, we look at how to construct a 2-qubit gateset manually. We'll use `pygsti.construction.build_gateset` to construct the single-qubit gates and we'll create a \"non-standard\" 2-qubit gate by specifying the unitary operation it performs as a $4\\times 4$ matrix. \n", "\n", "To perform GST on such a gate set, one may need to compute new sets of fiducials and/or germ sequences. Once these are obtained, 2-qubit GST can be run just as in the example which uses one of pyGSTi's built-in 2Q gate sets." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true, "deletable": true, "editable": true }, "outputs": [], "source": [ "import pygsti" ] }, { "cell_type": "markdown", "metadata": { "deletable": true, "editable": true }, "source": [ "## Step 1: Create a gateset with only single-qubit gates\n", "Since the space of single-qubit gates is relatively small, we'll assume that the single-qubit gates in our gateset are able to be specified using `pygsti.construction.build_gateset`. So we'll start by construct a `GateSet` object containing all but the two-qubit gate(s) using `build_gateset`. " ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": true, "deletable": true, "editable": true }, "outputs": [], "source": [ "gs_target = pygsti.construction.build_gateset( \n", " [4], [('Q0','Q1')],['Gii','Gix','Giy','Gxi','Gyi'], \n", " [ \"I(Q0):I(Q1)\", \"X(pi/2,Q1)\", \"Y(pi/2,Q1)\", \"X(pi/2,Q0)\", \"Y(pi/2,Q0)\" ],\n", " effectLabels=['00','01','10','11'], effectExpressions=[\"0\",\"1\",\"2\",\"3\"])" ] }, { "cell_type": "markdown", "metadata": { "deletable": true, "editable": true }, "source": [ "There are lots of arguments to this function, so let's review what they mean:\n", "- `[4]` = a 4-dimensional Hilbert (state) space\n", "- `[('Q0','Q1')]` = interpret this 4-d space as that of two qubits 'Q0', and 'Q1' (note these labels *must* begin with 'Q'!)\n", "- `\"Gix\"` = gate label; can be anything that begins with 'G' and is followed by lowercase letters\n", "- `\"X(pi/2,Q1)\"` = pi/2 single-qubit x-rotation gate on the qubit labeled Q1\n", "- `\"rho0\"` = prep label; can be anything that begins with \"rho\"\n", "- `'10'` = designates a POVM effect label whose corresponding vector is given by the `effectExpressions` argument.\n", "- `\"2\"` = a prep or effect expression indicating a projection/preparation of the 3rd (b/c 0-based) computational basis element\n", "\n", "You can also explicity add identity operations, e.g. `\"I(Q0)\"`, to the rotation gates to get the same gate set (see `gs_targetB` below), and this same syntax can be used for non-entangling 2-qubit gates, e.g. `\"X(pi/2,Q0):X(pi/2,Q1)\"`." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false, "deletable": true, "editable": true }, "outputs": [], "source": [ "gs_targetB = pygsti.construction.build_gateset( \n", " [4], [('Q0','Q1')],['Gii','Gix','Giy','Gxi','Gyi','Gcnot'], \n", " [ \"I(Q0):I(Q1)\", \"I(Q0):X(pi/2,Q1)\", \"I(Q0):Y(pi/2,Q1)\", \"X(pi/2,Q0):I(Q1)\", \"Y(pi/2,Q0):I(Q1)\", \"CNOT(Q0,Q1)\" ],\n", " effectLabels=['00','01','10','11'], effectExpressions=[\"0\",\"1\",\"2\",\"3\"])\n", "assert(abs(gs_target.frobeniusdist(gs_targetB)) < 1e-6)" ] }, { "cell_type": "markdown", "metadata": { "deletable": true, "editable": true }, "source": [ "If our 2-qubit gate happens to be one that *can* be specified using `build_gateset` then we can just use it to construct the entire `GateSet` and be done. Currently, `build_gateset` can create any controlled $X$, $Y$, or $Z$ rotation using `CX`, `CY` and `CZ`, as well as the standard `CNOT` and `CPHASE` gates. Below we demonstrate creation with the CNOT gate. The resulting `GateSet` is then identical to `pygsti.construction.std2Q_XYCNOT.gs_target`." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false, "deletable": true, "editable": true }, "outputs": [], "source": [ "gs_withCNOT = pygsti.construction.build_gateset( \n", " [4], [('Q0','Q1')],['Gii','Gix','Giy','Gxi','Gyi','Gcnot'], \n", " [ \"I(Q0):I(Q1)\", \"I(Q0):X(pi/2,Q1)\", \"I(Q0):Y(pi/2,Q1)\", \"X(pi/2,Q0):I(Q1)\", \"Y(pi/2,Q0):I(Q1)\", \"CNOT(Q0,Q1)\" ],\n", " effectLabels=['00','01','10','11'], effectExpressions=[\"0\",\"1\",\"2\",\"3\"])\n", "\n", "#Note this is the same gateset as one of pyGSTi's standard gate sets:\n", "from pygsti.construction import std2Q_XYICNOT\n", "assert(abs(gs_withCNOT.frobeniusdist(std2Q_XYICNOT.gs_target)) < 1e-6)" ] }, { "cell_type": "markdown", "metadata": { "deletable": true, "editable": true }, "source": [ "Thus, since our `gs_target` just contains the 1-qubit gates of `std2Q_XYCNOT.gs_target`, we could also create has all the 1-qubit gates are the same a third way to obtain `gs_target` is to just remove `Gcnot` from the standard gate set:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false, "deletable": true, "editable": true }, "outputs": [], "source": [ "gs_targetC = std2Q_XYICNOT.gs_target.copy()\n", "del gs_targetC.gates['Gcnot']\n", "assert(abs(gs_target.frobeniusdist(gs_targetC)) < 1e-6)" ] }, { "cell_type": "markdown", "metadata": { "deletable": true, "editable": true }, "source": [ "## Step 2: Create a custom 2-qubit gate\n", "We're assuming that `build_gateset` can't make the 2-qubit gate we want, so we'll need to create our own. Below we demonstrate how to create a 2-qubit gate from a given unitary which acts on the 2-qubit, 4-dimensional, state space." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false, "deletable": true, "editable": true }, "outputs": [], "source": [ "import numpy as np\n", "\n", "#Unitary in acting on the state-space { |A>, |B>, |C>, |D> } == { |00>, |01>, |10>, |11> }.\n", "# This unitary rotates the second qubit by pi/2 in either the (+) or (-) direction based on \n", "# the state of the first qubit.\n", "myUnitary = 1./np.sqrt(2) * np.array([[1,-1j,0,0],\n", " [-1j,1,0,0],\n", " [0,0,1,1j],\n", " [0,0,1j,1]])\n", "\n", "#Convert this unitary into a \"superoperator\", which acts on the \n", "# space of vectorized density matrices instead of just the state space.\n", "# These superoperators are what GST calls \"gates\".\n", "mySuperOp_stdbasis = pygsti.unitary_to_process_mx(myUnitary)\n", "\n", "#After the call to unitary_to_process_mx, the superoperator is a complex matrix\n", "# in the \"standard\" or \"matrix unit\" basis given by { |A>