3

I am trying to efficiently solve the following linear system in Python 3.6:

b = A x

where A is an N x N Toeplitz matrix (that is, the left-to-right diagonals are constant) and x, b are N x N matrices. The Durbin-Levinson implemented in Scipy as scipy.linalg.solve_toeplitz should efficiently solve the above system utilizing the structure of A, and indeed it does this when x,b are vectors.

However, when x, b are matrices it is much slower than both scipy.linalg.solve and numpy.linalg.solve (see output from my test-script)

Execution time is critical in my application. What are my options for speeding up the process and perhaps utilizing the Toeplitz structure?

A possible explanation for scipy.linalg.solve_toeplitz being slow is that it uses a slow for-loop for matrix inputs to solve the individual linear systems with x, b being vectors (solving each column at a time).

"""This script serves to benchmark several methods for solving a
linear equation involving a Toeplitz matrix and determine which one is
faster.

We consider the equation: b = A x, where A is Toeplitz, b and x are matrices and we wish to
solve for x.
"""
import numpy as np
import numpy.linalg
import scipy.linalg
import time

N = 500
np.random.seed(1)

# Construct random vectors/matrices
x = np.random.rand(N, N)
c,r = np.random.rand(2, N)
A = scipy.linalg.toeplitz(c, r)
b = A.dot(x)

# Validate various solutions
x_sol = []
x_sol.append(('numpy.linalg.solve(A, b)', numpy.linalg.solve(A, b)))
x_sol.append(('scipy.linalg.solve(A, b)', scipy.linalg.solve(A, b)))
x_sol.append(('scipy.linalg.solve_toeplitz((c, r), b)', scipy.linalg.solve_toeplitz((c, r), b)))
for solution in x_sol:
    print("Method: {} - error: {}".format(solution[0], numpy.linalg.norm(solution[1] - x)))

# Time the solutions
x_time = []
for solution in x_sol:
    t_start = time.time()
    eval(solution[0])
    t_end = time.time()
    x_time.append(t_end - t_start)
print("Timings:")
print(x_time)

Script output:

Method: numpy.linalg.solve(A, b) - error: 4.8794411236474704e-11
Method: scipy.linalg.solve(A, b) - error: 4.824494916488265e-11
Method: scipy.linalg.solve_toeplitz((c, r), b) - error: 2.7607739766053664e-08
Timings:
[0.08000469207763672, 0.03300189971923828, 0.6740384101867676]

Documentation: https://docs.scipy.org/doc/scipy-0.17.0/reference/generated/scipy.linalg.solve_toeplitz.html

  • Use `solve_toeplitz` with `check_finite=False`, that often makes a big difference. What is `N`? – Ahmed Fasih May 01 '17 at 10:43
  • I believe `N=600` when the script was executed. Unfortunately, speed-up from setting `check_finite=False` is negligible. – Badematician May 01 '17 at 14:57
  • I’m trying to figure out why the [benchmark](https://github.com/scipy/scipy/blob/v0.17.0/benchmarks/benchmarks/linalg_solve_toeplitz.py) that came from the original [pull request adding this function](https://github.com/scipy/scipy/pull/4302) doesn’t run on my laptop, as part of research to create an issue—this speed discrepancy might be a regression since that pull request found very good performance. It looks like `solve_toeplitz` calls [`levinson`](https://github.com/scipy/scipy/blob/v0.17.0/scipy/linalg/_solve_toeplitz.pyx) which is a Pyrex (old thing like Cython) thing. – Ahmed Fasih May 01 '17 at 16:25
  • Can you edit the question with your full script to serve as a copy-pastable [MCVE](https://stackoverflow.com/help/mcve) for Scipy developers? – Ahmed Fasih May 01 '17 at 16:26
  • I have included the full test script. Do you have any suggestions to make my code run faster, preferably using the Toeplitz structure? Or am i out of luck for the time being, unless i implement my own Durbin-Levinson algorithm? Thank you for your quick replies. – Badematician May 02 '17 at 07:13
  • I’m concerned that this is a bug or a regression because `solve_toeplitz` is supposed to be (and *was*) much faster than `solve` via Levinson. If you can, file a bug report on GitHub (try to get `linalg_solve_toeplitz.py` to run first!), otherwise I’ll try to do that in the next day. – Ahmed Fasih May 02 '17 at 08:51

0 Answers0