{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# StateEnsemble and state_ensemble_typical"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "np.set_printoptions(linewidth=200)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## StateEnsemble\n",
    "StateEnsemble is a list of State with corresponding probabilities(MultinomialDistribution).  \n",
    "The property `states` is a list of State.\n",
    "The property `ps` is probabilities.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Example.  \n",
    "If StateEnsemble is a ensemble of $(1/2, z_0)$ and $(1/2, z_1)$, then `states` = $[z_0, z_1]$ and `ps` = MultinomialDistribution of $[1/2, 1/2]$."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The methods for generating a StateEnsemble includes the following:\n",
    "\n",
    "- Generate from `state_ensemble_typical` module\n",
    "- Generate StateEnsemble object directly\n",
    "\n",
    "Generate from `state_ensemble_typical` module by specifying CompositeSystem and state name (ex. “z0”)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Type:\n",
      "StateEnsemble\n",
      "\n",
      "States:\n",
      "states[0]: [0.70710678 0.         0.         0.70710678]\n",
      "states[1]: [ 0.70710678  0.          0.         -0.70710678]\n",
      "\n",
      "MultinomialDistribution:\n",
      "shape = (2,)\n",
      "ps = [1. 0.]\n"
     ]
    }
   ],
   "source": [
    "from quara.objects.composite_system_typical import generate_composite_system\n",
    "from quara.objects.state_ensemble_typical import generate_state_ensemble_from_name\n",
    "\n",
    "c_sys = generate_composite_system(\"qubit\", 1)\n",
    "ensemble = generate_state_ensemble_from_name(c_sys, \"z0\")\n",
    "print(ensemble)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Generate StateEnsemble object directly using a list of State and MultinomialDistribution."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Type:\n",
      "StateEnsemble\n",
      "\n",
      "States:\n",
      "states[0]: [0.70710678 0.         0.         0.70710678]\n",
      "states[1]: [ 0.70710678  0.          0.         -0.70710678]\n",
      "\n",
      "MultinomialDistribution:\n",
      "shape = (2,)\n",
      "ps = [1. 0.]\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",
    "from quara.objects.state import State\n",
    "from quara.objects.multinomial_distribution import MultinomialDistribution\n",
    "from quara.objects.state_ensemble import StateEnsemble\n",
    "\n",
    "basis = get_normalized_pauli_basis(1)\n",
    "e_sys = ElementalSystem(0, basis)\n",
    "c_sys = CompositeSystem([e_sys])\n",
    "vec1 = np.array([1, 0, 0, 1]) / np.sqrt(2)\n",
    "state1 = State(c_sys, vec1)\n",
    "vec2 = np.array([1, 0, 0, -1]) / np.sqrt(2)\n",
    "state2 = State(c_sys, vec2)\n",
    "\n",
    "ps = [1.0, 0.0]\n",
    "dist = MultinomialDistribution(ps)\n",
    "ensemble = StateEnsemble([state1, state2], dist)\n",
    "print(ensemble)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### specific properties\n",
    "The property vec of `states` is a list of State specified by the constructor argument `states`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "states: \n",
      "[<quara.objects.state.State object at 0x000001E734C6F8C8>, <quara.objects.state.State object at 0x000001E734C6FFC8>]\n"
     ]
    }
   ],
   "source": [
    "ensemble = StateEnsemble([state1, state2], dist)\n",
    "print(f\"states: \\n{ensemble.states}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `state()` function returns a State specified by the constructor argument `state`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "state(0): \n",
      "Type:\n",
      "State\n",
      "\n",
      "Dim:\n",
      "2\n",
      "\n",
      "Vec:\n",
      "[0.70710678 0.         0.         0.70710678]\n",
      "state(1): \n",
      "Type:\n",
      "State\n",
      "\n",
      "Dim:\n",
      "2\n",
      "\n",
      "Vec:\n",
      "[ 0.70710678  0.          0.         -0.70710678]\n"
     ]
    }
   ],
   "source": [
    "ensemble = StateEnsemble([state1, state2], dist)\n",
    "print(f\"state(0): \\n{ensemble.state(0)}\")\n",
    "print(f\"state(1): \\n{ensemble.state(1)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The property vec of `prob_dist` is a MultinomialDistribution specified by the constructor argument `prob_dist`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "prob_dist: shape = (2,)\n",
      "ps = [1. 0.]\n"
     ]
    }
   ],
   "source": [
    "ensemble = StateEnsemble([state1, state2], dist)\n",
    "print(f\"prob_dist: {ensemble.prob_dist}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### other functions\n",
    "Many functions that other subclasses of QOperation have are not supported in StateEnsemble.\n",
    "If an unsupported function is executed, a NotImplementedError will be raised."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## state_ensemble_typical\n",
    "`generate_state_ensemble_object_from_state_ensemble_name_object_name()` function in `state_ensemble_typical` module can easily generate objects related to MProcess.  \n",
    "The `generate_state_ensemble_object_from_state_ensemble_name_object_name()` function has the following arguments:\n",
    "\n",
    "- The string that can be specified for `mprocess_name` can be checked by executing the `get_state_ensemble_names()` function. The tensor product of state_ensemble_names \"a\", \"b\" is written \"a_b\".\n",
    "- `object_name` can be the following string:\n",
    "  - \"state_ensemble\" - StateEnsemble object.\n",
    "- `c_sys` - CompositeSystem of objects related to MProcess. Specify when `object_name` is \"state_ensemble\".\n",
    "- `is_physicality_required` - Whether the generated object is physicality required, by default True."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "get_state_ensemble_names(): \n",
      "['x0', 'x1', 'y0', 'y1', 'z0', 'z1', 'a']\n"
     ]
    }
   ],
   "source": [
    "from quara.objects.state_ensemble_typical import (\n",
    "    get_state_ensemble_names,\n",
    "    generate_state_ensemble_object_from_state_ensemble_name_object_name,\n",
    ")\n",
    "\n",
    "print(f\"get_state_ensemble_names(): \\n{get_state_ensemble_names()}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### object_name = “state_ensemble”"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Type:\n",
      "StateEnsemble\n",
      "\n",
      "States:\n",
      "states[0]: [0.70710678 0.         0.         0.70710678]\n",
      "states[1]: [ 0.70710678  0.          0.         -0.70710678]\n",
      "\n",
      "MultinomialDistribution:\n",
      "shape = (2,)\n",
      "ps = [1. 0.]\n"
     ]
    }
   ],
   "source": [
    "c_sys = generate_composite_system(\"qubit\", 1)\n",
    "ensemble = generate_state_ensemble_object_from_state_ensemble_name_object_name(\"z0\", \"state_ensemble\", c_sys=c_sys)\n",
    "print(ensemble)"
   ]
  }
 ],
 "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
}
