I’m trying to do SVD on a small financial correlation matrix (16x16), and I’m getting 45 seconds+ to do this in Nx, whereas it takes like, a millisecond in Python.
Here is my Elixir code:
defmodule Svdtest do
def finurl, do: "https://raw.githubusercontent.com/vegabook/nxtest/main/priv/data/dfc.csv"
def url_to_tensor(url) do
req = HTTPoison.get!(url)
req.body
|> String.split("\n", trim: true)
|> Enum.map(fn x -> String.split(x, ",", trim: true) end)
|> Enum.map(fn x -> Enum.map(x, fn y -> String.to_float(y) end) end)
|> Nx.tensor
end
def time_svd(tensor) do
:timer.tc(&Nx.LinAlg.svd/1, [tensor])
end
def run_test(url) do
tensor = url_to_tensor(url)
{time, {u, s, vt}} = time_svd(tensor)
seconds = time / 1000000
IO.inspect(s)
IO.puts("Time taken: #{seconds} seconds")
end
end
And here is the equivalent python:
import requests
import numpy as np
import datetime as dt
finurl = "https://raw.githubusercontent.com/vegabook/nxtest/main/priv/data/dfc.csv"
def test_svd(url = finurl):
data = requests.get(finurl)
body = data.content
rows = list(filter(lambda x: x != "", body.decode().split("\n")))
numbers = [[float(i) for i in x.split(",")] for x in rows]
npa = np.array(numbers)
nowtime = dt.datetime.utcnow()
svd = np.linalg.svd(npa)
time_taken = (dt.datetime.utcnow() - nowtime).total_seconds()
print(time_taken)
seconds = time_taken
print(svd[1])
print(f"Python time taken: {seconds} seconds")
if __name__ == "__main__":
test_svd()
The results do come out the same, btw, as you will see if you run the code.
This is running on a Raspberry Pi, because it is brutal in showing slow code paths, so there is no GPU backend that I can imagine. I do know that Numpy on python is using Neon vectorised instructions though so this is a best case for it on this hardware.
Code is on github where you can see my mix.exs, etc, but here are my deps:
defp deps do
[
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
#{:axon, "~> 0.3.0"},
#{:exla, "~> 0.4.0"},
{:nx, "~> 0.4.0"},
{:explorer, "~> 0.4.0"},
{:csv, "~> 3.0"},
{:httpoison, "~> 1.8"},
]
I did see perusing through the Nx code yesterday that SVD, alone, seems to be written in native Elixir which maybe explains this? I’d really like to move to Elixir for my financial stat arb workflows but need it to be faster than this.
Is it because I’m on the “wrong” hardware, perhaps? Would x86 and or a better backend help, in which case can someone run the code above and let me know the difference you get on said hardware?
Or is it maybe that Nx’s focus is ML/AI and not traditional statistics? Thanks.