Go Back

Lisp and Linear Algebra

While learning Linear Algebra and calculating a bunch of vector operations by hand I was getting a bit tired, that’s when I thought about making my life easier with a bit of Common Lisp code.

Vectors

For this little experiment I’m going to represent vectors using regular lists.

Operations

Scalar Multiplication

The simplest operation I can think of is scalar multiplication, where you multiply all components in a vector by a scalar value, that is, just a real number.

(defun v@ (u alpha)
  "Returns scalar multiplication of U by ALPHA."
  (map 'list #'(lambda (x)
                 (* x alpha))
        u))

map expects a return type (here we’re returning a list), a function that will run on each element of the list (here we’re using simple lambda that multiplies x by alpha), and the sequence we want to apply our function to, which is u.

Calling v@ gives us the expected answer:

* (v@ '(1 2 3) 5)
(5 10 15)

Operations on Two Vectors

Next we will implement adding, subtracting, and multiplying vectors.

The v+ function is pretty similar to the v@ function we defined earlier:

(defun v+ (u v)
  "Returns the addition of the vectors U and V."
  (mapcar #'(lambda (x y)
              (+ x y))
          u
          v))

mapcar expects a function that will run on each pair, with the first value being the nth component in a vector U, and second value being the nth component in a vector V.

Calling v+ should give you the following output:

* (v+ '(1 2 3) '(4 5 6))
(5 7 9)

The functions for substraction and multiplication are pretty much the same.

Vector Substraction

(defun v- (u v)
  "Returns the subtraction of the vectors U and V."
  (mapcar #'(lambda (x y)
              (- x y))
          u
          v))

Vector Multiplication

(defun v* (u v)
  "Returns the multiplication of the vectors U and V."
  (mapcar #'(lambda (x y)
              (* x y))
          u
          v))

Dot Product

The dot product of two vectors is calculated by multiplying the nth component in a vector U with the nth component in a vector V, and returning the sum of those products.

(defun v. (u v)
  "Returns the dot product between the vectors U and V."
  (apply #'+
         (mapcar #'(lambda (x y)
                     (* x y))
                 u
                 v)))

The only difference between v. and the previous functions is the use of apply. apply applies some function, in this case +, to all items in the list.

It’s like running:

(+ 1 2 3)

But using the elements from the list mapcar returns.

Calling v. should give you the following output:

* (v. '(1 2 3) '(4 5 6))
32

Modulus, or Magnitude

To get the modulus, or magnitude of a vector we get the square root of the sum of all components squared.

(defun v-modulus (u)
  "Returns the modulus, or magnitude of the vector U."
  (sqrt (apply #'+
               (map 'list #'(lambda (n)
                              (expt n 2))
                    u))))

Here we see the use of the expt function, it expects a base and a exponent.

Calling v-modulus should give you the following output:

* (v-modulus '(1 2))
2.236068

Getting The Angle Between Two Vectors

Getting the angle between two vectors is a bit more complex.

We need to get the arccosine of the dot product of U and V over the product of the modulus of U and V. A bit long-winded isn’t it, I find the code a bit easier to read.

(defun v0 (u v)
  "Returns the angle between the vectors U and V."
  (acos (/ (v. u v)
           (*
            (v-modulus u)
            (v-modulus v)))))

The code is almost a translation of the explanation above, luckily for us, Common Lisp provides an arccosine function called acos.

Calling v0 should give you the following output:

* (v0 '(1 0) '(0 3))
1.5707964

And that’s probably not what you expected, right? That’s because it’s returning the angle in radians, so all that’s left to do is create a function to convert radians to degrees.

(defun radians-to-degress (radians)
  "Converts RADIANS to degrees."
  (nth-value 0 (round (* radians (/ 180 PI)))))

We can do the conversion by multiplying the angle in radians by 180 over PI.

The round function rounds the resulting value, round returns multiple values, the rounded number and the remainder.

Since we only care about the rounded number we can ignore the remainder, the nth-value function let’s do exactly that! Getting the zeroth return value from round.

Let’s modify v0 to make use of the radians-to-degrees function:

(defun v0 (u v)
  "Returns the angle between the vectors U and V."
  (radians-to-degress
   (acos (/ (v. u v)
            (*
             (v-modulus u)
             (v-modulus v))))))

Now v0 should give the answer in degrees, like we expected:

* (v0 '(1 0) '(0 3))
90

Conclusion

To me that was a pretty good exercise, making me understand both Linear Algebra and Common Lisp a bit better. I hope it was useful for you too!