title: How to do efficient matrix multiplication between a dense numpy matrix and a sparse scipy vector?
body: <p>Multiplying a dense numpy matrix with a sparse scipy vector using <code>@</code> is incredibly inefficient . It seems like it doesn't make use of the sparsity of the vector at all.</p>
  
  <p>Say we have </p>
  
  <pre><code>A = np.eye(5000)
  x = np.eye(5000)[333]
  x = scipy.sparse.coo_matrix(x).T # make it a sparse vector
  </code></pre>
  
  <p>Then multiplication using <code>@</code> yields:</p>
  
  <pre><code>%timeit A @ x
  8 ms ± 78.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  </code></pre>
  
  <p>Let's write an incredibly crappy sparse multiplication ourselves:</p>
  
  <pre><code>def mult_dense_with_sparse(A, x):
    return (A[:,x.nonzero()[0]] @ x.toarray()[x.nonzero()[0]]).T[0]
  </code></pre>
  
  <p>Lo and behold:</p>
  
  <pre><code>%timeit mult_dense_with_sparse(A, x)
  50.3 µs ± 45.3 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
  </code></pre>
  
  <p>It's <strong>much</strong> faster! Even though this implementation first creates a dense vector by adding all the zeros again, and then removes all the zeros again... So I'm wondering, if not <code>@</code>, how can I multiply a dense numpy matrix with a sparse scipy vector efficiently? Surely such a basic operation is part of scipy?</p>
  
  <p>Edit: provided solution in other question is not helpful as it is just as inefficient as <code>@</code>:</p>
  
  <pre><code>%timeit scipy.sparse.csr_matrix.dot(A, x)
  7.97 ms ± 113 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  </code></pre>
  
  <p>Edit 2 in responaw Hameer Abbasi:</p>
  
  <pre><code>Line #      Hits         Time  Per Hit   % Time  Line Contents
  ==============================================================
     101                                                       @profile
     102                                                       def ratio(self, n):
     103        80         51.0      0.6      0.0                  s = n + 1
     104        80   11401854.0 142523.2     16.1                  self.sfpc = self.scolPCA(s) # sparse first principal component
     106        80     351898.0   4398.7      0.5                  wSums = (self.signals[:,self.sfpc.nonzero()[0]] @ self.sfpc.toarray()[self.sfpc.nonzero()[0]]).T[0]
     108        80   56487433.0 706092.9     79.7                  wSums = self.sfpc.T.dot(self.signals.T)[0]
     110        80    2521189.0  31514.9      3.6                  self.Flag, self.threshold, self.incline, self.deltaVar = self.actFunctOpt(list(wSums))
     111        80        160.0      2.0      0.0                  return  self.deltaVar / (2 + s)
  </code></pre>
  
  <p>Here you can see that our 'hack' takes about 0.5% of the time of this function, while using <code>dot</code> takes 79.7% of the time in this function.</p>