{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "# The Utilities Module\n",
    "\n",
    "The `utilities` module of the `repytah` package holds functions commonly called by other modules in order for the entire package to run smoothly.\n",
    "`utilities` includes the following functions:\n",
    "- `create_sdm`: Creates a self-dissimilarity matrix; this matrix is found by creating audio shingles from feature vectors, and finding the cosine distance between shingles.\n",
    "\n",
    "- `find_initial_repeats`: Finds all diagonals present in **thresh\\_mat**, removing each diagonal as it is found.\n",
    "\n",
    "- `stretch_diags`: Fills out diagonals in binary self-dissimilarity matrix from diagonal starts and lengths.\n",
    "\n",
    "- `add_annotations`: Adds annotations to each pair of repeated structures according to their length and order of occurrence. \n",
    "\n",
    "- `get_annotation_list`: Gets one annotation marker vector, given vector of lengths **key\\_lst**.\n",
    "\n",
    "- `get_y_labels`: Generates the labels for visualization with **width\\_vec** and **anno\\_vec**.\n",
    "\n",
    "- `reformat` *(only used for creating test examples)*: Transforms a binary matrix representation of when repeats occur in a song into a list of repeated structures detailing the length and occurrence of each repeat.  \n",
    "    \n",
    "These functions are called multiple times throughout the package to reformat the outputs of various functions. Functions from `utilities` are shown in yellow in the example function pipeline below.\n",
    "<img src=\"pictures/function_pipeline.jpg\" width=\"380\">"
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "### Importing necessary modules"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "source": [
    "# NumPy is used for mathematical calculations\n",
    "import numpy as np\n",
    "\n",
    "# Matplotlib is used to display output\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# Import utilities\n",
    "from repytah.utilities import *"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "## create_sdm\n",
    "\n",
    "This function creates a self-dissimilarity matrix. This matrix is found by creating audio shingles from feature vectors, and finding the cosine distance between shingles.\n",
    "\n",
    "The inputs for the function are:\n",
    "- __fv_mat__ (np.ndarray): A matrix of feature vectors where each column is a timestep and each row includes feature information. i.e. an array of 144 columns/beats and 12 rows corresponding to chroma values.\n",
    "- __num_fv_per_shingle__ (int): The number of feature vectors per audio shingle\n",
    "\n",
    "The output for the function is:\n",
    "- __self_dissim_mat__ (np.ndarray): A self-dissimilarity matrix with paired cosine distances between shingles"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "source": [
    "fv_mat = np.array([[0, 0.5, 0, 0, 0, 1, 0, 0],\n",
    "                   [0,   2, 0, 0, 0, 0, 0, 0],\n",
    "                   [0,   0, 0, 0, 0, 0, 3, 0],\n",
    "                   [0,   3, 0, 0, 2, 0, 0, 0],\n",
    "                   [0, 1.5, 0, 0, 5, 0, 0, 0]])\n",
    "\n",
    "num_fv_per_shingle = 3\n",
    "\n",
    "print('The input matrix of feature vectors is:\\n', fv_mat)\n",
    "print('The number of feature vectors per audio shingles is:', num_fv_per_shingle)"
   ],
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "The input matrix of feature vectors is:\n",
      " [[0.  0.5 0.  0.  0.  1.  0.  0. ]\n",
      " [0.  2.  0.  0.  0.  0.  0.  0. ]\n",
      " [0.  0.  0.  0.  0.  0.  3.  0. ]\n",
      " [0.  3.  0.  0.  2.  0.  0.  0. ]\n",
      " [0.  1.5 0.  0.  5.  0.  0.  0. ]]\n",
      "The number of feature vectors per audio shingles is: 3\n"
     ]
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "source": [
    "self_dissim_mat  = create_sdm(fv_mat, num_fv_per_shingle)\n",
    "print('The resulting self-dissimilarity matrix is:\\n', self_dissim_mat)"
   ],
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "The resulting self-dissimilarity matrix is:\n",
      " [[0.         1.         1.         0.37395249 0.9796637  1.        ]\n",
      " [1.         0.         1.         1.         0.45092001 0.95983903]\n",
      " [1.         1.         0.         1.         1.         1.        ]\n",
      " [0.37395249 1.         1.         0.         1.         1.        ]\n",
      " [0.9796637  0.45092001 1.         1.         0.         1.        ]\n",
      " [1.         0.95983903 1.         1.         1.         0.        ]]\n"
     ]
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "The resulting self-dissimilarity can be shown as a picture in which the color represents the similarity between two shingles. The darker the color of a pixel, the more similar the two shingles are.  "
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "source": [
    "plt.imshow(self_dissim_mat,cmap='gray')\n",
    "plt.title(\"The self-dissimilarity matrix\")\n",
    "plt.show()"
   ],
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ],
      "image/png": "[encoded data, 5976 characters]"
     },
     "metadata": {
      "needs_background": "light"
     }
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "## find_initial_repeats\n",
    "\n",
    "This function identifies all repeated structures in a sequential data stream which are represented as diagonals in **thresh\\_mat** and then stores the pairs of repeats that correspond to each repeated structure in a list. The function finds the diagonals of lengths from the largest to the smallest, searches for non-overlapping shingles first, and cuts the overlapping shingles to up to 3 non-overlapping parts. As each diagonal is found, they are removed to avoid identifying repeated sub-structures. \n",
    "\n",
    "The image below illustrates the overlapping parts given two repeats starting with time steps $i$ and $j$ and having length $k$. The left and right segments will always exist because $i \\neq j$, but the middle segment will only exist if $k > 2(j - i)$.\n",
    "\n",
    "![alt text](pictures/find_initial_repeats.png)\n",
    "\n",
    "The inputs for the function are:\n",
    "- __thresh_mat__ (np.ndarray): A thresholded matrix from which diagonals are extracted\n",
    "- __bandwidth_vec__ (np.ndarray): An array of lengths of diagonals to be found\n",
    "- __thresh_bw__ (int): The smallest allowed diagonal length\n",
    "\n",
    "The output for the function is:\n",
    "- __all_lst__ (np.ndarray): Pairs of repeats that correspond to diagonals in **thresh\\_mat**"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "source": [
    "thresh_mat = np.array([[1, 0, 0, 1, 0, 0, 0, 1, 0, 0],\n",
    "                       [0, 1, 0, 0, 1, 1, 0, 0, 1, 0],\n",
    "                       [0, 0, 1, 0, 0, 1, 1, 0, 0, 1],\n",
    "                       [1, 0, 0, 1, 0, 0, 1, 1, 0, 0],\n",
    "                       [0, 1, 0, 0, 1, 0, 1, 0, 0, 0],\n",
    "                       [0, 1, 1, 0, 0, 1, 0, 1, 1, 0],\n",
    "                       [0, 0, 1, 1, 1, 0, 1, 0, 1, 0],\n",
    "                       [1, 0, 0, 1, 0, 1, 0, 1, 0, 1],\n",
    "                       [0, 1, 0, 0, 0, 1, 1, 0, 1, 0],\n",
    "                       [0, 0, 1, 0, 0, 0, 0, 1, 0, 1]])\n",
    "\n",
    "bandwidth_vec = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])\n",
    "thresh_bw = 0\n",
    "\n",
    "print('The thresholded matrix is:\\n', thresh_mat)\n",
    "print('The lengths of diagonals to be found are:', bandwidth_vec)\n",
    "print('The smalled allowed diagonal length is:', thresh_bw)"
   ],
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "The thresholded matrix is:\n",
      " [[1 0 0 1 0 0 0 1 0 0]\n",
      " [0 1 0 0 1 1 0 0 1 0]\n",
      " [0 0 1 0 0 1 1 0 0 1]\n",
      " [1 0 0 1 0 0 1 1 0 0]\n",
      " [0 1 0 0 1 0 1 0 0 0]\n",
      " [0 1 1 0 0 1 0 1 1 0]\n",
      " [0 0 1 1 1 0 1 0 1 0]\n",
      " [1 0 0 1 0 1 0 1 0 1]\n",
      " [0 1 0 0 0 1 1 0 1 0]\n",
      " [0 0 1 0 0 0 0 1 0 1]]\n",
      "The lengths of diagonals to be found are: [ 1  2  3  4  5  6  7  8  9 10]\n",
      "The smalled allowed diagonal length is: 0\n"
     ]
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "source": [
    "all_lst = find_initial_repeats(thresh_mat, bandwidth_vec, thresh_bw)\n",
    "\n",
    "print(\"The pairs of repeats are:\\n\", all_lst)"
   ],
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "The pairs of repeats are:\n",
      " [[ 6  6  9  9  1]\n",
      " [ 5  6  7  8  2]\n",
      " [ 7  8  9 10  2]\n",
      " [ 1  3  4  6  3]\n",
      " [ 1  3  8 10  3]\n",
      " [ 2  4  5  7  3]\n",
      " [ 2  4  6  8  3]\n",
      " [ 1 10  1 10 10]]\n"
     ]
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "## stretch_diags\n",
    "\n",
    "This function creates a binary matrix with full-length diagonals from a binary matrix of diagonal starts and length of diagonals.\n",
    "        \n",
    "The inputs for the function are:\n",
    "- __thresh_diags__ (np.ndarray): A binary matrix where entries equal to 1 signals the existence of a diagonal\n",
    "- __band_width__ (int): The length of encoded diagonals\n",
    "\n",
    "The output for the function is:\n",
    "- __stretch_diag_mat__ (np.ndarray): A logical matrix with diagonals of length __band\\_width__ starting at each entry prescribed in __thresh\\_diag__"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "source": [
    "thresh_diags = np.matrix([[0, 0, 1, 0, 0],\n",
    "                          [0, 1, 0, 0, 0],\n",
    "                          [0, 0, 1, 0, 0],\n",
    "                          [0, 0, 0, 0, 0],\n",
    "                          [0, 0, 0, 0, 0]])\n",
    "\n",
    "band_width = 3\n",
    "\n",
    "print(\"The input matrix is:\\n\", thresh_diags)\n",
    "print(\"The length of the encoded diagonals is:\", band_width)"
   ],
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "The input matrix is:\n",
      " [[0 0 1 0 0]\n",
      " [0 1 0 0 0]\n",
      " [0 0 1 0 0]\n",
      " [0 0 0 0 0]\n",
      " [0 0 0 0 0]]\n",
      "The length of the encoded diagonals is: 3\n"
     ]
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "source": [
    "stretched_diagonal = stretch_diags(thresh_diags, band_width)\n",
    "\n",
    "print(\"The output matrix is:\\n\", stretched_diagonal)"
   ],
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "The output matrix is:\n",
      " [[False False False False False False False]\n",
      " [False  True False False False False False]\n",
      " [ True False  True False False False False]\n",
      " [False  True False  True False False False]\n",
      " [False False  True False  True False False]\n",
      " [False False False False False False False]\n",
      " [False False False False False False False]]\n"
     ]
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "## add_annotations\n",
    "\n",
    "This function adds annotations to each pair of repeated structures in **input\\_mat** according to their length and order of occurrence to differentiate between different repeats of the same length. \n",
    "\n",
    "The inputs for the function are:\n",
    "- __input_mat__ (np.ndarray): Pairs of repeats. The first two columns refer to the first repeat of the pair. The third and fourth columns refer to the second repeat of the pair. The fifth column refers to the repeat lengths. The sixth column contains any previous annotations, which will be removed.\n",
    "- __song_length__ (int): The number of shingles in the song\n",
    "\n",
    "The output for the function is:\n",
    "- __anno_list__ (np.ndarray): Pairs of repeats with annotations marked"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "source": [
    "input_mat = np.array([[2,  5,  8, 11, 4, 0],\n",
    "                      [7, 10, 14, 17, 4, 0],\n",
    "                      [2,  5, 15, 18, 4, 0],\n",
    "                      [8, 11, 15, 18, 4, 0],\n",
    "                      [9, 12, 16, 19, 4, 0]])\n",
    "\n",
    "song_length = 19\n",
    "\n",
    "print(\"The input array is: \\n\", input_mat)\n",
    "print(\"The number of shingles is:\", song_length)"
   ],
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "The input array is: \n",
      " [[ 2  5  8 11  4  0]\n",
      " [ 7 10 14 17  4  0]\n",
      " [ 2  5 15 18  4  0]\n",
      " [ 8 11 15 18  4  0]\n",
      " [ 9 12 16 19  4  0]]\n",
      "The number of shingles is: 19\n"
     ]
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "source": [
    "annotated_array = add_annotations(input_mat, song_length)\n",
    "print(\"The array of repeats with annotations is:\\n\", annotated_array)"
   ],
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "The array of repeats with annotations is:\n",
      " [[ 2  5  8 11  4  1]\n",
      " [ 2  5 15 18  4  1]\n",
      " [ 8 11 15 18  4  1]\n",
      " [ 7 10 14 17  4  2]\n",
      " [ 9 12 16 19  4  3]]\n"
     ]
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "## reconstruct_full_block\n",
    "\n",
    "This function creates a record of when pairs of repeated structures occur, from the first beat in the song to the end. This record is a binary matrix with a block of 1's for each repeat encoded in **pattern\\_mat** whose length is encoded in **pattern\\_key**. By looping over all rows of **pattern\\_mat**, `reconstruct_full_block` reconstructs each row using the **pattern\\_key**. \n",
    "\n",
    "For each row of **pattern\\_mat**, a new row is created for **pattern\\_block** by looping over the same row of **pattern\\_mat** and shifting the position of 1's the number of times equivalent to the length of the repeat, storing each unique row with shifted values in a separate array. The sum of all of the shifted rows is then taken along the x-axis, thus creating a row that represents where each repeat occurs with blocks of 1's.\n",
    "\n",
    "For example, if the row in **pattern\\_mat** is [0 0 1 0 0 0 0 0 1 0 0 0 1 0 0], with a repeat length of 3, then new rows created by the for loop are:\n",
    "<br><br>\n",
    "<center>[0 0 1 0 0 0 0 0 1 0 0 0 1 0 0]<br>\n",
    "[0 0 0 1 0 0 0 0 0 1 0 0 0 1 0]<br>\n",
    "[0 0 0 0 1 0 0 0 0 0 1 0 0 0 1]<br></center><br> \n",
    "\n",
    "These rows are then summed along the y-axis to become: [0 0 1 1 1 0 0 0 1 1 1 0 1 1 1] This is then appended to the output **pattern\\_block**. \n",
    "\n",
    "The inputs for the function are:\n",
    "- __pattern_mat__ (np.ndarray): A binary matrix with 1's where repeats begin and 0's otherwise\n",
    "- __pattern_key__ (np.ndarray): The number of feature vectors per audio shingle\n",
    "\n",
    "The output for the function is:\n",
    "- __pattern_block__ (np.ndarray): A binary matrix representation for **pattern\\_mat** with blocks of 1's equal to the length's prescribed in **pattern\\_key**"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "source": [
    "pattern_mat  = np.array([[0, 0, 0, 0, 1, 0, 0, 0, 0, 1],\n",
    "                         [0, 1, 0, 0, 0, 0, 0, 1, 0, 0],\n",
    "                         [0, 0, 1, 0, 0, 0, 0, 0, 1, 0],\n",
    "                         [1, 0, 0, 0, 0, 0, 1, 0, 0, 0],\n",
    "                         [1, 0, 0, 0, 0, 0, 1, 0, 0, 0]])\n",
    "\n",
    "pattern_key  = np.array([1, 2, 2, 3, 4])\n",
    "\n",
    "print(\"The input binary matrix is:\\n\", pattern_mat)\n",
    "print(\"The input pattern key is:\\n\", pattern_key)"
   ],
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "The input binary matrix is:\n",
      " [[0 0 0 0 1 0 0 0 0 1]\n",
      " [0 1 0 0 0 0 0 1 0 0]\n",
      " [0 0 1 0 0 0 0 0 1 0]\n",
      " [1 0 0 0 0 0 1 0 0 0]\n",
      " [1 0 0 0 0 0 1 0 0 0]]\n",
      "The input pattern key is:\n",
      " [1 2 2 3 4]\n"
     ]
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "source": [
    "pattern_block  = reconstruct_full_block(pattern_mat, pattern_key)\n",
    "print(\"The reconstructed full block is:\\n\", pattern_block)"
   ],
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "The reconstructed full block is:\n",
      " [[0 0 0 0 1 0 0 0 0 1]\n",
      " [0 1 1 0 0 0 0 1 1 0]\n",
      " [0 0 1 1 0 0 0 0 1 1]\n",
      " [1 1 1 0 0 0 1 1 1 0]\n",
      " [1 1 1 1 0 0 1 1 1 1]]\n"
     ]
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "## get_annotation_list\n",
    "\n",
    "This function takes in a vector of lengths **key\\_lst**, counts the time each length occurs as its annotation maker, and outputs all annotation markers as a vector.\n",
    "    \n",
    "The input for the function is:\n",
    "- **key_lst** (np.ndarray): Array of lengths in ascending order\n",
    "\n",
    "The output for the function is:\n",
    "- **anno_lst_out** (np.ndarray): Array of one possible set of annotation markers for **key\\_lst**"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "source": [
    "key_lst = np.array([1, 2, 2, 3, 3, 3, 5, 7, 8, 8, 9])\n",
    "\n",
    "print(\"The input array of lengths is: \\n\", key_lst)"
   ],
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "The input array of lengths is: \n",
      " [1 2 2 3 3 3 5 7 8 8 9]\n"
     ]
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "source": [
    "anno_lst_out  = get_annotation_lst(key_lst)\n",
    "\n",
    "print(\"The output array is: \\n\", anno_lst_out )"
   ],
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "The output array is: \n",
      " [1 1 2 1 2 3 1 1 1 2 1]\n"
     ]
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "## get_y_labels\n",
    "This function generates labels for visualization with repeat lengths and annotations of each length. Note that the first 0 is always printed out because it acts as the hidden origin label for the later visualization.\n",
    "\n",
    "The inputs for the function are:\n",
    "\n",
    "- **width_vec** (np.ndarray): Vector of widths for a visualization\n",
    "- **anno_vec** (np.ndarray): Array of annotations for a visualization\n",
    "\n",
    "The output for the function is:\n",
    "- **y_labels**(str): Labels for the y-axis of a visualization"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "source": [
    "width_vec = np.array([[1], [2], [3], [4], [4], [5], [5], [6]])\n",
    "anno_vec = np.array([1, 1, 1, 1, 2, 1, 1, 1])\n",
    "\n",
    "print(\"The vector of widths is: \\n\", width_vec)\n",
    "print(\"The array of annotation makers is: \\n\", anno_vec)"
   ],
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "The vector of widths is: \n",
      " [[1]\n",
      " [2]\n",
      " [3]\n",
      " [4]\n",
      " [4]\n",
      " [5]\n",
      " [5]\n",
      " [6]]\n",
      "The array of annotation makers is: \n",
      " [1 1 1 1 2 1 1 1]\n"
     ]
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "source": [
    "y_labels = get_y_labels(width_vec, anno_vec)\n",
    "\n",
    "print(\"The output string is: \\n\", y_labels)"
   ],
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "The output string is: \n",
      " ['0' 'w = 1, a = 1' 'w = 2, a = 1' 'w = 3, a = 1' 'w = 4, a = 1'\n",
      " 'w = 4, a = 2' 'w = 5, a = 1' 'w = 5, a = 1' 'w = 6, a = 1']\n"
     ]
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "## reformat\n",
    "\n",
    "This function is helpful when writing example inputs for aligned hierarchies. It is not used in the main functions for creating the aligned hierarchies. It first finds the starting indices of the repeated structures row by row, and assigns the time steps of the repeated structures based on starting indices.\n",
    "        \n",
    "The inputs for the function are:\n",
    "- __pattern_mat__ (np.ndarray): Binary array with 1's where repeats start and 0's otherwise \n",
    "- __pattern_key__ (np.ndarray): Array with the lengths of each repeated structure in **pattern\\_mat**\n",
    "\n",
    "The output for the function is:\n",
    "- __info_mat__ (np.ndarray): Array with the time steps of when the pairs of repeated structures start and end organized"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "source": [
    "pattern_mat = np.array([[0, 0, 0, 0, 1, 0, 0, 0, 0, 1],\n",
    "                        [0, 1, 0, 0, 0, 0, 0, 1, 0, 0],\n",
    "                        [0, 0, 1, 0, 0, 0, 0, 0, 1, 0],\n",
    "                        [1, 0, 0, 0, 0, 0, 1, 0, 0, 0],\n",
    "                        [1, 0, 0, 0, 0, 0, 1, 0, 0, 0]])\n",
    "\n",
    "pattern_key = np.array([1, 2, 2, 3, 4])\n",
    "\n",
    "print(\"The input matrix is:\\n\", pattern_mat)\n",
    "print(\"The length of repeated structure is:\", pattern_key)"
   ],
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "The input matrix is:\n",
      " [[0 0 0 0 1 0 0 0 0 1]\n",
      " [0 1 0 0 0 0 0 1 0 0]\n",
      " [0 0 1 0 0 0 0 0 1 0]\n",
      " [1 0 0 0 0 0 1 0 0 0]\n",
      " [1 0 0 0 0 0 1 0 0 0]]\n",
      "The length of repeated structure is: [1 2 2 3 4]\n"
     ]
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "source": [
    "info_mat  = reformat(pattern_mat, pattern_key)\n",
    "\n",
    "print(\"The output matrix is:\\n\", info_mat)"
   ],
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "The output matrix is:\n",
      " [[ 5  5 10 10  1]\n",
      " [ 2  3  8  9  2]\n",
      " [ 3  4  9 10  2]\n",
      " [ 1  3  7  9  3]\n",
      " [ 1  4  7 10  4]]\n"
     ]
    }
   ],
   "metadata": {}
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "f9558a7a9c0790c2334a9072034da5d30cf10fd46d803cfe3584dbca0180200c"
  },
  "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.8.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}