{ "cells": [ { "cell_type": "markdown", "id": "82fa1d26", "metadata": {}, "source": [ "# Basics" ] }, { "cell_type": "markdown", "id": "ccca03c9", "metadata": {}, "source": [ "Qubits is the *mode* type for qubit-based types, like Pauli strings, computational basis states, and collections of such objects" ] }, { "cell_type": "code", "execution_count": 1, "id": "c4b40f78", "metadata": {}, "outputs": [], "source": [ "from zixy.qubit import Qubits\n", "qubits = Qubits.from_count(4)\n", "assert len(qubits) == 4\n", "assert qubits == Qubits.from_inds([0, 1, 2, 3])\n", "assert qubits != Qubits.from_inds([0, 1, 3, 2])" ] }, { "cell_type": "markdown", "id": "18cb7d98", "metadata": {}, "source": [ "Lists are the primary containers in Zixy.\n", "Their components are stored contiguously in Rust-owned memory buffers, and manipulated by routines written in Rust.\n", "\n", "There are a few options for instantiation; one is parsing from sparse strings." ] }, { "cell_type": "code", "execution_count": 2, "id": "f6161fa9", "metadata": {}, "outputs": [], "source": [ "from zixy.qubit.pauli import Strings\n", "inp_str = \"X0 Y1 Z3, X1 Y2 Z3, , X3, Y2 Z3\"\n", "strings = Strings.from_str(inp_str, qubits)\n", "assert len(strings) == 5\n", "assert str(strings) == inp_str" ] }, { "cell_type": "markdown", "id": "3aafe733", "metadata": {}, "source": [ "An exception is raised if any qubit index in the input string is out of bounds." ] }, { "cell_type": "code", "execution_count": 3, "id": "763f5270", "metadata": {}, "outputs": [ { "ename": "IndexError", "evalue": "Mode index 7 is out-of-bounds for component list with 4 modes per component.", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mIndexError\u001b[39m Traceback (most recent call last)", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43mStrings\u001b[49m\u001b[43m.\u001b[49m\u001b[43mfrom_str\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mX0 Y1 Z3, X1 Y2 Z3, X7, X3, Y2 Z3\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mqubits\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[36mFile \u001b[39m\u001b[32m~/Desktop/zixy/zixy-py/docs/.venv/lib/python3.14/site-packages/zixy/qubit/pauli/_strings.py:270\u001b[39m, in \u001b[36mStrings.from_str\u001b[39m\u001b[34m(cls, source, qubits)\u001b[39m\n\u001b[32m 267\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(qubits, \u001b[38;5;28mint\u001b[39m):\n\u001b[32m 268\u001b[39m qubits = Qubits.from_count(qubits)\n\u001b[32m 269\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mcls\u001b[39m._create(\n\u001b[32m--> \u001b[39m\u001b[32m270\u001b[39m \u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mcmpnt_type\u001b[49m\u001b[43m.\u001b[49m\u001b[43mimpl_type\u001b[49m\u001b[43m(\u001b[49m\u001b[43mqubits\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mqubits\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mis\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mPauliSprings\u001b[49m\u001b[43m(\u001b[49m\u001b[43msource\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 271\u001b[39m )\n", "\u001b[31mIndexError\u001b[39m: Mode index 7 is out-of-bounds for component list with 4 modes per component." ] } ], "source": [ "Strings.from_str(\"X0 Y1 Z3, X1 Y2 Z3, X7, X3, Y2 Z3\", qubits)" ] }, { "cell_type": "markdown", "id": "0d971417", "metadata": {}, "source": [ "\n", "All `Strings` instances store a qubit space, but it can be implicitly created based on the largest mode index in the sparse string. Explicit identity matrices count in determining this qubit number." ] }, { "cell_type": "code", "execution_count": 4, "id": "1c3d773f", "metadata": {}, "outputs": [], "source": [ "assert len(Strings.from_str(\"X0 Y1 Z3, X1 Y2 Z3, X7, X3, Y2 Z3\").qubits) == 8\n", "assert len(Strings.from_str(\"X0 Y1 Z3, X1 Y2 Z3, , X3 I7, Y2 Z3\").qubits) == 8" ] }, { "cell_type": "markdown", "id": "0c234687", "metadata": {}, "source": [ "The sparse string parsing functionality knows how to combine coincidently-indexed Pauli matrices" ] }, { "cell_type": "code", "execution_count": 5, "id": "d1eba7ff", "metadata": {}, "outputs": [], "source": [ "assert str(Strings.from_str(\"X0 X0 Y1 Z3\")) == \"Y1 Z3\"\n", "assert str(Strings.from_str(\"X0 Y0 Y1 X1 Z3\")) == \"Z0 Z1 Z3\"" ] }, { "cell_type": "markdown", "id": "bc6092d5", "metadata": {}, "source": [ "This `Strings` type has unit coefficient type, and so it cannot absorb phase factors different from 1." ] }, { "cell_type": "code", "execution_count": 6, "id": "857ae055", "metadata": {}, "outputs": [ { "ename": "ValueError", "evalue": "Value +i of type \"ComplexSign\" is not representable as type \"Unity\"", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mValueError\u001b[39m Traceback (most recent call last)", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43mStrings\u001b[49m\u001b[43m.\u001b[49m\u001b[43mfrom_str\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mX0 Y0 Y1 Z3\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", "\u001b[36mFile \u001b[39m\u001b[32m~/Desktop/zixy/zixy-py/docs/.venv/lib/python3.14/site-packages/zixy/qubit/pauli/_strings.py:270\u001b[39m, in \u001b[36mStrings.from_str\u001b[39m\u001b[34m(cls, source, qubits)\u001b[39m\n\u001b[32m 267\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(qubits, \u001b[38;5;28mint\u001b[39m):\n\u001b[32m 268\u001b[39m qubits = Qubits.from_count(qubits)\n\u001b[32m 269\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mcls\u001b[39m._create(\n\u001b[32m--> \u001b[39m\u001b[32m270\u001b[39m \u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mcmpnt_type\u001b[49m\u001b[43m.\u001b[49m\u001b[43mimpl_type\u001b[49m\u001b[43m(\u001b[49m\u001b[43mqubits\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mqubits\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mis\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mPauliSprings\u001b[49m\u001b[43m(\u001b[49m\u001b[43msource\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 271\u001b[39m )\n", "\u001b[31mValueError\u001b[39m: Value +i of type \"ComplexSign\" is not representable as type \"Unity\"" ] } ], "source": [ "Strings.from_str(\"X0 Y0 Y1 Z3\")" ] }, { "cell_type": "markdown", "id": "08c4c796", "metadata": {}, "source": [ "For this parse to succeed, we have to use a container type that is capable of representing the phase.\n", "\n", "`ComplexSignList` is the list type with coefficients among the fourth roots of unity." ] }, { "cell_type": "code", "execution_count": 7, "id": "4da55dcd", "metadata": {}, "outputs": [], "source": [ "from zixy.qubit.pauli import ComplexSignTerms\n", "assert str(ComplexSignTerms.from_str(\"X0 Y0 Y1 Z3\")) == \"(+i, Z0 Y1 Z3)\"" ] }, { "cell_type": "markdown", "id": "692392d0", "metadata": {}, "source": [ "A single Pauli string is realised as a List with only one element" ] }, { "cell_type": "code", "execution_count": 8, "id": "e21aefa3", "metadata": {}, "outputs": [], "source": [ "from zixy.qubit.pauli import String\n", "string = String(qubits)\n", "assert str(string) == \"\"" ] }, { "cell_type": "markdown", "id": "121257e1", "metadata": {}, "source": [ "Strings and elements of lists can be accessed and modified using Python containers such as `dict` and `list`" ] }, { "cell_type": "code", "execution_count": 9, "id": "3645e2cf", "metadata": {}, "outputs": [], "source": [ "from zixy.qubit.pauli import I, X, Y, Z\n", "string.set([X, Y, X, Z])\n", "array = Strings.from_str(\"X0, Y0, Z0 X2, X0 Y1 X2 Z3\")\n", "assert array[3] == string\n", "assert array[-1] == string\n", "assert array[2].get_dict() == {0: Z, 2: X}\n", "assert array[2].get_tuple() == (Z, I, X, I)" ] }, { "cell_type": "markdown", "id": "b571f13b", "metadata": {}, "source": [ "Multiplication of terms in-place by other terms can be attempted, but raises an exception if the coefficient is not capable of absorbing the phase." ] }, { "cell_type": "code", "execution_count": 10, "id": "de04fa5c", "metadata": {}, "outputs": [ { "ename": "ValueError", "evalue": "Cannot multiply-assign left-hand operand X0 with type by value Y0. Result with factor +i is not representable by the left-hand operand type.", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mValueError\u001b[39m Traceback (most recent call last)", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[10]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m array = Strings.from_str(\u001b[33m\"\u001b[39m\u001b[33mX0, Y0, Z0 X2, X0 Y1 X2 Z3\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m \u001b[43marray\u001b[49m\u001b[43m[\u001b[49m\u001b[32;43m0\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m=\u001b[49m\u001b[43m \u001b[49m\u001b[43marray\u001b[49m\u001b[43m[\u001b[49m\u001b[32;43m1\u001b[39;49m\u001b[43m]\u001b[49m\n", "\u001b[36mFile \u001b[39m\u001b[32m~/Desktop/zixy/zixy-py/docs/.venv/lib/python3.14/site-packages/zixy/qubit/pauli/_strings.py:185\u001b[39m, in \u001b[36mString.__imul__\u001b[39m\u001b[34m(self, rhs)\u001b[39m\n\u001b[32m 183\u001b[39m phase = \u001b[38;5;28mself\u001b[39m.phase_of_mul(rhs)\n\u001b[32m 184\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m phase != ComplexSign():\n\u001b[32m--> \u001b[39m\u001b[32m185\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m _imul_factor_error(\u001b[38;5;28mself\u001b[39m, rhs, phase)\n\u001b[32m 186\u001b[39m \u001b[38;5;28mself\u001b[39m.imul_ignore_phase(rhs)\n\u001b[32m 187\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\n", "\u001b[31mValueError\u001b[39m: Cannot multiply-assign left-hand operand X0 with type by value Y0. Result with factor +i is not representable by the left-hand operand type." ] } ], "source": [ "array = Strings.from_str(\"X0, Y0, Z0 X2, X0 Y1 X2 Z3\")\n", "array[0] *= array[1]" ] }, { "cell_type": "markdown", "id": "37fe77e2", "metadata": {}, "source": [ "Of course, `ComplexSignList` is infallible with respect to Pauli term multiplication" ] }, { "cell_type": "code", "execution_count": 11, "id": "27aa39c1", "metadata": {}, "outputs": [], "source": [ "terms = ComplexSignTerms.from_str(\"X0, Y0, Z0 X2, X0 Y1 X2 Z3\")\n", "terms[0] *= terms[1]\n", "assert str(terms) == \"(+i, Z0), (+1, Y0), (+1, Z0 X2), (+1, X0 Y1 X2 Z3)\"" ] }, { "cell_type": "markdown", "id": "fcfadfb8", "metadata": {}, "source": [ "Qubit-based objects defined on different numbers of qubits are not arithmetically compatible" ] }, { "cell_type": "code", "execution_count": 12, "id": "a9422066", "metadata": {}, "outputs": [ { "ename": "ValueError", "evalue": "Qubits-based objects are based on different qubit spaces.", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mValueError\u001b[39m Traceback (most recent call last)", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[12]\u001b[39m\u001b[32m, line 6\u001b[39m\n\u001b[32m 4\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(terms_6qbt.qubits) == \u001b[32m6\u001b[39m\n\u001b[32m 5\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(terms_4qbt.qubits) == \u001b[32m4\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m6\u001b[39m \u001b[43mterms_6qbt\u001b[49m\u001b[43m[\u001b[49m\u001b[32;43m0\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m \u001b[49m\u001b[43mterms_4qbt\u001b[49m\u001b[43m[\u001b[49m\u001b[32;43m0\u001b[39;49m\u001b[43m]\u001b[49m\n", "\u001b[36mFile \u001b[39m\u001b[32m~/Desktop/zixy/zixy-py/docs/.venv/lib/python3.14/site-packages/zixy/container/mixins.py:536\u001b[39m, in \u001b[36mTermMulMixin.__mul__\u001b[39m\u001b[34m(self, rhs)\u001b[39m\n\u001b[32m 533\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.cmpnt * rhs * \u001b[38;5;28mself\u001b[39m.coeff\n\u001b[32m 534\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 535\u001b[39m \u001b[38;5;66;03m# Undefined behaviour for base Cmpnt, but may be defined by derived classes\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m536\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mcmpnt\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m \u001b[49m\u001b[43mrhs\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcmpnt\u001b[49m * \u001b[38;5;28mself\u001b[39m.coeff * rhs.coeff\n", "\u001b[36mFile \u001b[39m\u001b[32m~/Desktop/zixy/zixy-py/docs/.venv/lib/python3.14/site-packages/zixy/qubit/pauli/_strings.py:173\u001b[39m, in \u001b[36mString.__mul__\u001b[39m\u001b[34m(self, rhs)\u001b[39m\n\u001b[32m 171\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mNotImplemented\u001b[39m\n\u001b[32m 172\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(rhs, String):\n\u001b[32m--> \u001b[39m\u001b[32m173\u001b[39m product, phase = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_impl\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcmpnt_mul\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mindex\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrhs\u001b[49m\u001b[43m.\u001b[49m\u001b[43m_impl\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrhs\u001b[49m\u001b[43m.\u001b[49m\u001b[43mindex\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 174\u001b[39m phases = ComplexSignCoeffs.from_scalar(ComplexSign(phase))\n\u001b[32m 175\u001b[39m term_type = \u001b[38;5;28mself\u001b[39m._term_registry[ComplexSign]\n", "\u001b[31mValueError\u001b[39m: Qubits-based objects are based on different qubit spaces." ] } ], "source": [ "from zixy.qubit.pauli import ComplexSignTerm\n", "terms_6qbt = ComplexSignTerms.from_str(\"X0 Z2 X5\")\n", "terms_4qbt = ComplexSignTerms.from_str(\"Y0 X2, Z0 X3, X0 Y1 Z2\")\n", "assert len(terms_6qbt.qubits) == 6\n", "assert len(terms_4qbt.qubits) == 4\n", "terms_6qbt[0] * terms_4qbt[0]" ] }, { "cell_type": "markdown", "id": "8667c425", "metadata": {}, "source": [ "Moving to a different number of qubits and/or ordering of the qubit space brings qubits-based objects into arithmetic compatibility" ] }, { "cell_type": "code", "execution_count": 13, "id": "9a378011", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(-1, Z0 Y2 X5)" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "assert len(terms_4qbt.standardized(6).qubits) == 6\n", "terms_6qbt[0] * terms_4qbt.standardized(6)[0]" ] }, { "cell_type": "markdown", "id": "70b0766b", "metadata": {}, "source": [ "`Strings` and the `Terms` types can have duplicates removed on command." ] }, { "cell_type": "markdown", "id": "ab536378", "metadata": {}, "source": [ "Whenever uniqueness is to be enforced by construction, the `Set` types are required." ] }, { "cell_type": "code", "execution_count": 14, "id": "074072fe", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Z0, Y0, Z0 X2, X0 Y1 X2 Z3, X0 Y1 Z2 X3" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from zixy.qubit.pauli import StringSet\n", "string_set = StringSet.from_strings(terms.strings)\n", "assert len(string_set) == 4\n", "string_set.insert((X, Y, Z, X))\n", "assert len(string_set) == 5\n", "string_set.insert((X, Y, Z, X))\n", "assert len(string_set) == 5\n", "string_set" ] } ], "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.14.0" } }, "nbformat": 4, "nbformat_minor": 5 }