{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Matrix Basis Tutorial\n", "\n", "Consider the space of density matrices corresponding to a Hilbert space $\\mathcal{H}$ of dimension $d$. The basis used for this Hilbert-Schmidt space, $B(\\mathcal{H})$, can be any set of $d\\times d$ matrices which span the density matrix space. pyGSTi supports arbitrary bases by deriving from the `pygsti.tools.Basis` class, and constains built-in support for the following basis sets:\n", "\n", "- the matrix unit, or \"standard\" basis, consisting of the matrices with a single unit (1.0) element and otherwise zero. This basis is selected by passing `\"std\"` to appropriate function arguments.\n", "- the Pauli-product basis, consisting of tensor products of the four Pauli matrices {I, X, Y, Z} normalized so that $Tr(B_i B_j) = \\delta_{ij}$. All of these matrices are Hermitian, so that Hilbert-Schmidt vectors and matrices are real when this basis is used. This basis can only be used when the $d = 4^i$ for integer $i$, and is selected using the string `\"pp\"`.\n", "- the Gell-Mann basis, consisting of the normalized Gell-Mann matrices (see Wikipedia if you don't know what these are). Similar to the Pauli-product case, these matrices are also Hermitian, so that Hilbert-Schmidt vectors and matrices are real when this basis is used. Unlike the Pauli-product case, since Gell-Mann matrices are well defined in any dimension, the Gell-Mann basis is *not* restricted to cases when $d=4^i$. This basis is selected using the string `\"gm\"`.\n", "- a special basis of $3 \\times 3$ matricies designed for Qutrit systems formed by taking the symmetric subspace of a 2-qubit system. This basis is selected using the string `\"qt\"`. \n", "\n", "Various functions and objects within pyGSTi require knowledge of what Hilbert-Schmidt basis is being used. The `pygsti.objects.Basis` object encapsulates a basis, and is the most flexible way of specifying a basis in pyGSTi. Alternatively, many functions also accept the short strings `\"std\"`, `\"gm\"`, `\"pp\"`, and `\"qt\"` to select one of the standard bases. In this tutorial, we'll demonstrate how to create a `Basis` object and use it and related functions to obtain and change the basis of the operation matrices and SPAM vectors stored in a `Model`.\n", "\n", "The most straightforward way to create a `Basis` object is to provide its short name and dimension to the `Basis.cast` function, which \"casts\" various things as a basis object. PyGSTi contains built-in support for bases consisting of the tensor product of Pauli matrices (or just the Pauli matrices in the case of 1 qubit), named `\"pp\"`, as well as the Gell-Mann matrices, named `\"gm\"`. It also contains a special \"qutrit\" basis, named `\"qt\"`, for the case of 3-level quantum systems. In cases when there are an integral number of qubits, and the dimension equals $4^N$, the `\"pp\"` basis is usually preferred since it is more intuitive. In other cases, where the Hilbert space includes non-qubit (e.g. environmental) degrees of freedom, the Gell-Mann basis may be useful since it can be used in any dimension. Note that both the Gell-Mann and Pauli-Product bases reduce to the usual Pauli matrices plus identity in when the dimension equals 4 (1 qubit).\n", "\n", "Here are some examples:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import pygsti\n", "from pygsti import Basis\n", "pp = Basis.cast('pp', 4) # Pauli-product (in this dim=4 case, just the Paulis)\n", "std = Basis.cast('std', 4) # \"standard\" basis of matrix units\n", "gm = Basis.cast('gm', 4) # Gell-Mann\n", "qt = Basis.cast('qt', 9) # qutrit - must be dim 9\n", "bases = [pp, std, gm, qt]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Each of the `pp`, `std`, and `gm` bases created will have $4$ $2x2$ matrices each. The `qt` basis has $9$ $3x3$ matrices instead:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "for basis in bases:\n", " print('\\n{} basis (dim {}):'.format(basis.name, basis.dim))\n", " print('{} elements:'.format(len(basis)))\n", " for element in basis:\n", " pygsti.tools.print_mx(element)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, custom \"explicit\" bases, which expicitly hold a set of basis-element matrices, can be easily created by supplying a list of the elements:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from pygsti.objects import ExplicitBasis\n", "std2x2Matrices = [\n", " np.array([[1, 0],\n", " [0, 0]]),\n", " np.array([[0, 1],\n", " [0, 0]]),\n", " np.array([[0, 0],\n", " [1, 0]]),\n", " np.array([[0, 0],\n", " [0, 1]])]\n", "\n", "alt_standard = ExplicitBasis(std2x2Matrices, [\"myElement%d\" % i for i in range(4)],\n", " name='std', longname='Standard')\n", "print(alt_standard)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "More complex bases can be created by chaining the elements of other bases together along the diagonal. This yields a basis for the direct-sum of the spaces spanned by the original basis. For example, a composition of the $2x2$ `std` basis with the $1x1$ `std` basis leads to a basis with state vectors of length $5$, or $5x5$ matrices:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "from pygsti.objects import DirectSumBasis\n", "comp = Basis.cast('std', [4, 1])\n", "comp = Basis.cast([('std', 4), ('std', 1)])\n", "comp = DirectSumBasis([ Basis.cast('std', 4), Basis.cast('std', 1)])\n", "\n", "#All three comps above give the same final basis\n", "print(comp)\n", "for element in comp.elements:\n", " print(element)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Basis usage\n", "Once created, bases are used to manipulate matrices and vectors within pygsti:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "from pygsti.tools import change_basis, flexible_change_basis\n", "\n", "mx = np.array([[1, 0, 0, 1],\n", " [0, 1, 2, 0],\n", " [0, 2, 1, 0],\n", " [1, 0, 0, 1]])\n", "\n", "change_basis(mx, 'std', 'gm') # shortname lookup\n", "change_basis(mx, std, gm) # object only\n", "change_basis(mx, std, 'gm') # combination" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Composite bases can be converted between expanded and contracted forms:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "mxInStdBasis = np.array([[1,0,0,2],\n", " [0,0,0,0],\n", " [0,0,0,0],\n", " [3,0,0,4]],'d')\n", "\n", "begin = Basis.cast('std', 4)\n", "end = Basis.cast('std', [1,1])\n", "mxInReducedBasis = flexible_change_basis(mxInStdBasis, begin, end)\n", "print(mxInReducedBasis)\n", "original = flexible_change_basis(mxInReducedBasis, end, begin)" ] }, { "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.7.0" } }, "nbformat": 4, "nbformat_minor": 2 }