├── .gitignore ├── LICENSE ├── README.md ├── lemma2.png └── notebooks └── comparison.ipynb /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.ipynb_checkpoints -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Ritchie Ng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # New Eigenvectors from Eigenvalues Calculation 2 | This repository implements this [paper](https://arxiv.org/pdf/1908.03795.pdf) that allows us to calculate eigenvectors from eigenvalues elegantly through PyTorch that allows your code to run on your CPU, GPU, or TPU. 3 | 4 | Easily run it on your browser through Google Colab or copy the function locally. 5 | 6 | ## Run Notebook on Google Colab 7 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ritchieng/eigenvectors-from-eigenvalues/blob/master/notebooks/comparison.ipynb) 8 | 9 | ## Core Equation: Lemma 2 10 | 11 | **Lemma 2.** The norm squared of the elements of the eigenvectors are related to the eigenvalues and the submatrix eigenvalues. 12 | 13 |

14 | 15 |

16 | 17 | ``` 18 | Mathjax of Lemma 2 19 | 20 | $$| v_{i, j} | ^ 2 \prod_{k=1; k \neq i}^{n} (\lambda_i (A) - \lambda_k (A)) = \prod_{k=1}^{n - 1} (\lambda_i (A) - \lambda_k (M_j))$$ 21 | ``` 22 | 23 | 24 | ## Authors and Abstract 25 | Peter B. Denton, Stephen J. Parke, Terance Tao, and Xining Zhang 26 | ``` 27 | We present a new method of succinctly determining eigenvectors 28 | from eigenvalues. Specifically, we relate the norm squared of the elements of 29 | eigenvectors to the eigenvalues and the submatrix eigenvalues. 30 | ``` 31 | 32 | ## Dependencies 33 | - PyTorch 1.9.1 (can be most versions of PyTorch as I used very core basic PyTorch functions) 34 | - Python 3.8 (doesn't matter much as I use basic operations) 35 | 36 | ## Code Repository Citation 37 | - If you would like to give some credit to this code implementation, these are the relevant links. 38 | - [![DOI](https://zenodo.org/badge/221868248.svg)](https://zenodo.org/badge/latestdoi/221868248) 39 | - [Eigenvectors from Eigenvalues CPU and GPU Implementation](https://www.researchgate.net/publication/337322294_Eigenvectors_from_Eigenvalues_CPU_and_GPU_Implementation) 40 | 41 | ## License 42 | MIT 43 | -------------------------------------------------------------------------------- /lemma2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ritchieng/eigenvectors-from-eigenvalues/ba25a311efff03050630b962e0f81e6ef362b78b/lemma2.png -------------------------------------------------------------------------------- /notebooks/comparison.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 6, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "1.9.0.post2\n", 13 | "3.8.8 (default, Apr 13 2021, 19:58:26) \n", 14 | "[GCC 7.3.0]\n" 15 | ] 16 | } 17 | ], 18 | "source": [ 19 | "import torch\n", 20 | "print(torch.__version__)\n", 21 | "import sys\n", 22 | "print(sys.version)" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 7, 28 | "metadata": { 29 | "tags": [] 30 | }, 31 | "outputs": [], 32 | "source": [ 33 | "def subidx(n:int, remove:int) -> list:\n", 34 | " out = list(range(n))\n", 35 | " out.remove(remove)\n", 36 | " return out\n", 37 | "\n", 38 | "def submatrix(A:torch.tensor, j:int) -> torch.tensor:\n", 39 | " n = A.shape[0]\n", 40 | " s = subidx(n, j)\n", 41 | " o = A[s, :]\n", 42 | " o = o[:, s]\n", 43 | " return o\n", 44 | "\n", 45 | "def get_eigenvector_val_old(hermitian_matrix, i, j, disable_complex=False):\n", 46 | " # Old way\n", 47 | " lam, v = torch.linalg.eig(hermitian_matrix)\n", 48 | " if disable_complex:\n", 49 | " old_eigenvector_ij = torch.abs(v[j,i]**2)\n", 50 | " else:\n", 51 | " old_eigenvector_ij = v[j,i]**2\n", 52 | " \n", 53 | " return old_eigenvector_ij\n", 54 | "\n", 55 | "def get_eigenvector_val(hermitian_matrix, i, j, disable_complex=False):\n", 56 | " # Only need to calculate eigenvalues\n", 57 | " lam = torch.linalg.eigvals(hermitian_matrix)\n", 58 | " \n", 59 | " n = len(lam)\n", 60 | " M = submatrix(hermitian_matrix, j)\n", 61 | " # Only need to calculate eigenvalues\n", 62 | " lam_submatrix = torch.linalg.eigvals(M)\n", 63 | "\n", 64 | " # Left side of equation 2\n", 65 | " left = torch.prod(torch.tensor([lam[i] - lam[k] for k in range(n) if k!=i]))\n", 66 | " # Right side of equation 2\n", 67 | " right = torch.prod(torch.tensor([lam[i] - lam_submatrix[k] for k in range(n-1)]))\n", 68 | " # Right divided by left\n", 69 | " eigenvector_ij = right / left\n", 70 | " \n", 71 | " if disable_complex:\n", 72 | " eigenvector_ij = torch.abs(eigenvector_ij)\n", 73 | " \n", 74 | " return eigenvector_ij" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": 5, 80 | "metadata": {}, 81 | "outputs": [ 82 | { 83 | "name": "stdout", 84 | "output_type": "stream", 85 | "text": [ 86 | "Old Method Eigenvector ij: (0.02086203929382368-0j)\n", 87 | "--------------------------------------------------\n", 88 | "New Method Eigenvector ij: (0.020862039293826073+0j)\n" 89 | ] 90 | } 91 | ], 92 | "source": [ 93 | "'''\n", 94 | "Run with Complex Numbers\n", 95 | "'''\n", 96 | "# Random square matrix\n", 97 | "rand_square_matrix = torch.rand(50, 50, dtype=float)\n", 98 | "\n", 99 | "# Hermitian matrix\n", 100 | "hermitian_matrix = rand_square_matrix * rand_square_matrix.T\n", 101 | "\n", 102 | "# Old \n", 103 | "old_eigenvector_ij = get_eigenvector_val_old(hermitian_matrix, i=0, j=0)\n", 104 | "\n", 105 | "# New\n", 106 | "new_eigenvector_ij = get_eigenvector_val(hermitian_matrix, i=0, j=0)\n", 107 | "\n", 108 | "print(f'Old Method Eigenvector ij: {old_eigenvector_ij}')\n", 109 | "print('-'*50)\n", 110 | "print(f'New Method Eigenvector ij: {new_eigenvector_ij}')" 111 | ] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": 8, 116 | "metadata": {}, 117 | "outputs": [ 118 | { 119 | "name": "stdout", 120 | "output_type": "stream", 121 | "text": [ 122 | "Old Method Eigenvector ij: 0.0225447410369363\n", 123 | "--------------------------------------------------\n", 124 | "New Method Eigenvector ij: 0.02254474103693581\n" 125 | ] 126 | } 127 | ], 128 | "source": [ 129 | "'''\n", 130 | "Run with Complex Numbers Disabled\n", 131 | "'''\n", 132 | "# Random square matrix\n", 133 | "rand_square_matrix = torch.rand(50, 50, dtype=float)\n", 134 | "\n", 135 | "# Hermitian matrix\n", 136 | "hermitian_matrix = rand_square_matrix * rand_square_matrix.T\n", 137 | "\n", 138 | "# Old \n", 139 | "old_eigenvector_ij = get_eigenvector_val_old(hermitian_matrix, i=0, j=0, disable_complex=True)\n", 140 | "\n", 141 | "# New\n", 142 | "new_eigenvector_ij = get_eigenvector_val(hermitian_matrix, i=0, j=0, disable_complex=True)\n", 143 | "\n", 144 | "print(f'Old Method Eigenvector ij: {old_eigenvector_ij}')\n", 145 | "print('-'*50)\n", 146 | "print(f'New Method Eigenvector ij: {new_eigenvector_ij}')" 147 | ] 148 | } 149 | ], 150 | "metadata": { 151 | "kernelspec": { 152 | "display_name": "Python 3", 153 | "language": "python", 154 | "name": "python3" 155 | }, 156 | "language_info": { 157 | "codemirror_mode": { 158 | "name": "ipython", 159 | "version": 3 160 | }, 161 | "file_extension": ".py", 162 | "mimetype": "text/x-python", 163 | "name": "python", 164 | "nbconvert_exporter": "python", 165 | "pygments_lexer": "ipython3", 166 | "version": "3.8.8" 167 | } 168 | }, 169 | "nbformat": 4, 170 | "nbformat_minor": 4 171 | } 172 | --------------------------------------------------------------------------------