{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# System\n",
    "This notebook describes the concept of system in Quara and the related classes.\n",
    "\n",
    "Quara supports operations such as State, Gate, Povm, etc.\n",
    "The system in which these operations are performed is called a CompositeSystem in Quara.\n",
    "To perform these operations, first generate a CompositeSystem.\n",
    "The methods for generating a CompositeSystem includes the following:\n",
    "\n",
    "- Generate from `composite_system_typical` module\n",
    "- Generate CompositeSystem object directly\n",
    "\n",
    "Both generate the same CompositeSystem."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Generate from `composite_system_typical` module by specifying mode (\"qubit\" or \"qutrit\") and number of qubits."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CompositeSystem: \n",
      "elemental_systems:\n",
      "[0] 0 (system_id=2245191296776)\n",
      "[1] 1 (system_id=2245191296840)\n",
      "\n",
      "dim: 4\n",
      "basis:\n",
      "(<4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>)\n"
     ]
    }
   ],
   "source": [
    "from quara.objects.composite_system_typical import generate_composite_system\n",
    "\n",
    "c_sys = generate_composite_system(\"qubit\", 2)\n",
    "print(f\"CompositeSystem: \\n{c_sys}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Generate CompositeSystem object directly using classes called MatrixBasis, ElementalSystem."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "elemental_systems:\n",
      "[0] 0 (system_id=2245192950920)\n",
      "[1] 1 (system_id=2245192951112)\n",
      "\n",
      "dim: 4\n",
      "basis:\n",
      "(<4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>, <4x4 sparse matrix of type '<class 'numpy.complex128'>'\n",
      "\twith 4 stored elements in Compressed Sparse Row format>)\n"
     ]
    }
   ],
   "source": [
    "from quara.objects.composite_system import CompositeSystem\n",
    "from quara.objects.elemental_system import ElementalSystem\n",
    "from quara.objects.matrix_basis import get_normalized_pauli_basis\n",
    "\n",
    "basis = get_normalized_pauli_basis(1)\n",
    "e_sys0 = ElementalSystem(0, basis)\n",
    "e_sys1 = ElementalSystem(1, basis)\n",
    "c_sys = CompositeSystem([e_sys0, e_sys1])\n",
    "print(c_sys)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Fundamental Rules\n",
    "- Two methods to generate objects of Quara.\n",
    "  - The typical objects can be generated from a module called `xxx_typical`. (\"xxx\" is a object name.)\n",
    "  - Users can also generate objects directly.\n",
    "- The structure of CompositeSystem is nested as follows:\n",
    "  - CompositeSystem corresponds to a whole system.\n",
    "  - CompositeSystem has a list of ElementalSystems sorted in ascending order by name.\n",
    "    - ElementalSystem corresponds to the unit of operation of qubits (or qutrits, qudits).\n",
    "    - ElementalSystem has a tuple of name (int) and MatrixBasis.\n",
    "    - The name is recommended to be named 0, 1, 2...\n",
    "      - MatrixBasis corresponds to the basis of the system.\n",
    "      - MatrixBasis has a basis represented by a list of `np.ndarray`.\n",
    "- To execute tomography in Quara, you must use MatrixBasis with the following properties:\n",
    "  - All matrices of MatrixBasis are normalized.\n",
    "  - All matrices of MatrixBasis are Hermitian.\n",
    "  - The 0th matrix of MatrixBasis is a multiple of the identity matrix."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Notice\n",
    "To notify a time-consuming process, a message `16it [00:00, 296.30it/s]` like below image may be output.  \n",
    "![](img/tqdm.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## CompositeSystem\n",
    "The constructor of CompositeSystem rearranges the argument `List[ElementalSystem]` in ascending order by name."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(ElementalSystem(name=0,             basis=MatrixBasis(basis=[array([[0.70710678+0.j, 0.        +0.j],\n",
      "       [0.        +0.j, 0.70710678+0.j]]), array([[0.        +0.j, 0.70710678+0.j],\n",
      "       [0.70710678+0.j, 0.        +0.j]]), array([[0.+0.j        , 0.-0.70710678j],\n",
      "       [0.+0.70710678j, 0.+0.j        ]]), array([[ 0.70710678+0.j,  0.        +0.j],\n",
      "       [ 0.        +0.j, -0.70710678+0.j]])])), ElementalSystem(name=1,             basis=MatrixBasis(basis=[array([[0.70710678+0.j, 0.        +0.j],\n",
      "       [0.        +0.j, 0.70710678+0.j]]), array([[0.        +0.j, 0.70710678+0.j],\n",
      "       [0.70710678+0.j, 0.        +0.j]]), array([[0.+0.j        , 0.-0.70710678j],\n",
      "       [0.+0.70710678j, 0.+0.j        ]]), array([[ 0.70710678+0.j,  0.        +0.j],\n",
      "       [ 0.        +0.j, -0.70710678+0.j]])])))\n"
     ]
    }
   ],
   "source": [
    "basis = get_normalized_pauli_basis(1)\n",
    "e_sys0 = ElementalSystem(0, basis)\n",
    "e_sys1 = ElementalSystem(1, basis)\n",
    "c_sys = CompositeSystem([e_sys0, e_sys1])\n",
    "\n",
    "print(c_sys.elemental_systems)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The property `dim` of CompositeSystem is the dimension of the matrices of MatrixBasis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dim: 2\n",
      "shape: 2\n"
     ]
    }
   ],
   "source": [
    "basis = get_normalized_pauli_basis(1)\n",
    "e_sys = ElementalSystem(0, basis)\n",
    "c_sys = CompositeSystem([e_sys])\n",
    "\n",
    "print(f\"dim: {c_sys.dim}\")\n",
    "print(f\"shape: {c_sys.basis()[0].shape[0]}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "CompositeSystem has properties to check properties of MatrixBasis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "is_orthonormal_hermitian_0thprop_identity: True\n",
      "is_basis_hermitian: True\n"
     ]
    }
   ],
   "source": [
    "print(f\"is_orthonormal_hermitian_0thprop_identity: {c_sys.is_orthonormal_hermitian_0thprop_identity}\")\n",
    "print(f\"is_basis_hermitian: {c_sys.is_basis_hermitian}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "CompositeSystem has properties for fast computation using sparsity.\n",
    "\n",
    "- basis_basisconjugate\n",
    "- dict_from_hs_to_choi\n",
    "- dict_from_choi_to_hs\n",
    "- basis_T_sparse, \n",
    "- basisconjugate_sparse\n",
    "- basisconjugate_basis_sparse\n",
    "- basis_basisconjugate_T_sparse\n",
    "- basis_basisconjugate_T_sparse_from_1\n",
    "- basishermitian_basis_T_from_1\n",
    "\n",
    "These properties are calculated only once when accessed."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## composite_system_typical module\n",
    "`composite_system_typical` module makes it easy to generate a CompositeSystem.\n",
    "Call the `generate_composite_system()` function with `mode` and `num` to generate CompositeSystem.\n",
    "\n",
    "- `mode` - \"qubit\" or \"qutrit\"\n",
    "- `num` - number of qubits or qutrits\n",
    "\n",
    "To learn more about optional parameters, please see to the API reference."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# CompositeSystem with one qubit\n",
    "c_sys = generate_composite_system(\"qubit\", 1)\n",
    "# CompositeSystem with two qutrits\n",
    "c_sys = generate_composite_system(\"qutrit\", 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## ElementalSystem\n",
    "ElementalSystem has a tuple of name (int) and MatrixBasis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name: 0\n",
      "basis: (array([[0.70710678+0.j, 0.        +0.j],\n",
      "       [0.        +0.j, 0.70710678+0.j]]), array([[0.        +0.j, 0.70710678+0.j],\n",
      "       [0.70710678+0.j, 0.        +0.j]]), array([[0.+0.j        , 0.-0.70710678j],\n",
      "       [0.+0.70710678j, 0.+0.j        ]]), array([[ 0.70710678+0.j,  0.        +0.j],\n",
      "       [ 0.        +0.j, -0.70710678+0.j]]))\n"
     ]
    }
   ],
   "source": [
    "basis = get_normalized_pauli_basis(1)\n",
    "e_sys = ElementalSystem(0, basis)\n",
    "\n",
    "print(f\"name: {e_sys.name}\")\n",
    "print(f\"basis: {e_sys.basis}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The property `dim` of ElementalSystem is the dimension of the matrices of MatrixBasis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dim: 2\n",
      "shape: 2\n"
     ]
    }
   ],
   "source": [
    "print(f\"dim: {e_sys.dim}\")\n",
    "print(f\"shape: {e_sys.basis[0].shape[0]}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "ElementalSystem has properties to check properties of MatrixBasis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "is_orthonormal_hermitian_0thprop_identity: True\n",
      "is_hermitian: True\n"
     ]
    }
   ],
   "source": [
    "print(f\"is_orthonormal_hermitian_0thprop_identity: {e_sys.is_orthonormal_hermitian_0thprop_identity}\")\n",
    "print(f\"is_hermitian: {e_sys.is_hermitian}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## MatrixBasis\n",
    "MatrixBasis has a basis represented by a list of `np.ndarray`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "basis[0]: \n",
      "[[0.70710678+0.j 0.        +0.j]\n",
      " [0.        +0.j 0.70710678+0.j]]\n",
      "basis[1]: \n",
      "[[0.        +0.j 0.70710678+0.j]\n",
      " [0.70710678+0.j 0.        +0.j]]\n",
      "basis[2]: \n",
      "[[0.+0.j         0.-0.70710678j]\n",
      " [0.+0.70710678j 0.+0.j        ]]\n",
      "basis[3]: \n",
      "[[ 0.70710678+0.j  0.        +0.j]\n",
      " [ 0.        +0.j -0.70710678+0.j]]\n"
     ]
    }
   ],
   "source": [
    "basis = get_normalized_pauli_basis(1)\n",
    "print(f\"basis[0]: \\n{basis[0]}\")\n",
    "print(f\"basis[1]: \\n{basis[1]}\")\n",
    "print(f\"basis[2]: \\n{basis[2]}\")\n",
    "print(f\"basis[3]: \\n{basis[3]}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The property dim of MatrixBasis is the dimension of the matrices of basis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dim: 2\n",
      "shape: 2\n"
     ]
    }
   ],
   "source": [
    "print(f\"dim: {basis.dim}\")\n",
    "print(f\"shape: {basis[0].shape[0]}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "MatrixBasis has functions to check properties."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "is_orthogonal(): True\n",
      "is_normal(): True\n",
      "is_hermitian(): True\n",
      "is_0thpropI(): True\n",
      "is_trace_less(): True\n",
      "size(): (2, 2)\n"
     ]
    }
   ],
   "source": [
    "print(f\"is_orthogonal(): {basis.is_orthogonal()}\")\n",
    "print(f\"is_normal(): {basis.is_normal()}\")\n",
    "print(f\"is_hermitian(): {basis.is_hermitian()}\")\n",
    "print(f\"is_0thpropI(): {basis.is_0thpropI()}\")\n",
    "print(f\"is_trace_less(): {basis.is_trace_less()}\")\n",
    "print(f\"size(): {basis.size()}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.7.7 64-bit ('quara': pipenv)",
   "metadata": {
    "interpreter": {
     "hash": "e0c99f005b0837bce79602fafa142c135e273dc311d0671484e4c834ee0e9c24"
    }
   },
   "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.7-final"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
