I'm writing some kind of signal processing software, and I'm starting to write discrete convolution function ,
This works fine for the first ten thousand or so list of values, but as they get larger (say 100k), I start getting StackOverflow errors, of course.
Unfortunately, I have many problems converting the urgent convolution algorithm that I have for a recursive and lazy version that is actually fast enough to use (to have at least a modest elegance style as well).
I am also not 100% sure that I have this function completely right, but - please let me know if I missed something or did something wrong. I think it is right.
(defn convolve " Convolves xs with is. This is a discrete convolution. 'xs :: list of numbers 'is :: list of numbers " [xs is] (loop [xs xs finalacc () acc ()] (if (empty? xs) (concat finalacc acc) (recur (rest xs) (if (empty? acc) () (concat finalacc [(first acc)])) (if (empty? acc) (map #(* (first xs) %) is) (vec-add (map #(* (first xs) %) is) (rest acc)))))))
I would really appreciate any help: I still get support in Clojure, and making this elegant, lazy and / or recursive would be great.
I'm a little surprised how difficult it is to express an algorithm that is pretty easy to express in an imperative language in Lisp. But maybe I'm doing it wrong!
EDIT: Just to show how easy it is to express in an imperative language and give people an algorithm that works beautifully and is easy to read, here is the Python version. Besides being shorter, more concise, and much easier to reason about, it runs faster than Clojure code: even my imperative Clojure code using Java arrays.
from itertools import repeat def convolve(ns, ms): y = [i for i in repeat(0, len(ns)+len(ms)-1)] for n in range(len(ns)): for m in range(len(ms)): y[n+m] = y[n+m] + ns[n]*ms[m] return y
Here, on the other hand, is the imperative Clojure code. It also transfers the last, not completely submerged values from the convolution. Therefore, apart from being slow and ugly, it is not 100% functional. Not functional.
(defn imp-convolve-1 [xs is] (let [ys (into-array Double (repeat (dec (+ (count xs) (count is))) 0.0)) xs (vec xs) is (vec is)] (map #(first %) (for [i (range (count xs))] (for [j (range (count is))] (aset ys (+ ij) (+ (* (nth xs i) (nth is j)) (nth ys (+ ij)))))))))
It is so discouraging. Please show me, I just missed something obvious.
EDIT 3:
Here is another version that I came up with yesterday, showing how I would like to express it (although the other solutions are pretty elegant, I just put another one there!)
(defn convolve-2 [xs is] (reduce #(vec-add %1 (pad-l %2 (inc (count %1)))) (for [x xs] (for [i is] (* xi)))))
It uses this vec-add utility function:
(defn vec-add ([xs] (vec-add xs [])) ([xs ys] (let [lxs (count xs) lys (count ys) xs (pad-r xs lys) ys (pad-r ys lxs)] (vec (map