Previous ToC Up Next

## 15.1. Vector Multiplication

Carol: Which means it is time to move on to multiplication. But here we have another problem: there is multiplication and then there is multiplication.

Dan: You mean?

Carol: We can multiply a vector with a scalar number; for a vector

```  v = [1, 2, 3]
```
we would like to see multiplication by two giving us:

```  2 * v = [2, 4, 6]
```
But in addition (no pun intended) we want to form an inner product of two vectors. In particular, we would like to get the square of the length of a vector by forming the inner product with itself:

```  v * v = 2*2 + 4*4 + 6*6 = 56
```
Or more generally, for

```  w = [10, 10, 10]
```
we want to be able to take the inner product of v and w to give us:

```  v * w = 2*10 + 4*10 + 6*10 = 120
```
We could of course define different method names for these two operations, like multiply_scalar and inner_product, but something tells me that we will be happier using * for both.

Dan: Certainly I will be happier that way!

```   def *(a)
if a.class == Vector              # inner product
product = 0
self.each_index{|k| product += self[k]*a[k]}
else
product = Vector.new            # scalar product
self.each_index{|k| product[k] = self[k]*a}
end
product
end
```

Time for a workout:

``` |gravity> irb
require "vector_try.rb"
true
v1 = Vector[1, 2, 3]
[1, 2, 3]
v2 = Vector[5, 6, 7]
[5, 6, 7]
v1 * 3
[3, 6, 9]
v1 * v1
14
v1 * v2
38
v1 * v2 * 3
114
v1 * 3 * v2
114
quit
```

## 15.2. An Unnatural Asymmetry

Dan: So far so good, but why do you put the number 3 only at the end and in the middle, why not in front? That would seem more natural! Let me try:

``` |gravity> irb
require "vector_try.rb"
true
v1 = Vector[1, 2, 3]
[1, 2, 3]
v1 * 3
[3, 6, 9]
3 * v1
TypeError: Vector can't be coerced into Fixnum
from (irb):4:in `*'
from (irb):4
from :0
quit
```
Hmmm, I guess not.

Carol: Yes, it would be more natural, but it doesn't work. Do you see why? Remember, when we write v2 * 3 we invoke the * method of the vector object v2. I understand that you would like to write 3 * v2, but in that case you have a problem: you would be trying to invoke the * method of the number object 3 . . . .

Erica: That's all and well, as a formal explanation, but if you can only write [1, 2, 3] * 3 and never 3 * [1, 2, 3], I'm not very happy with it. The whole point was to make our life easier, and to make the software notation more natural, more close to what you would write with pen and paper . . .

Dan: I agree. Half a solution is often worse than no solution at all. Perhaps we should just return to our earlier component notation.

Carol: I can't argue with that. But I'm not yet willing to give up. I wonder whether there is not some sort of way out. Let me see whether I can find something.

## 15.3. Vector Division

Erica: For now at least, let's finish the job we started . . .

Carol: . . . which means that we have to add a division method as well. In the case of division, we only have to deal with scalar division.

Erica: Ah, yes, of course, you have an inner product, but not an inner quotient.

Carol: Well, not completely `of course', there is something called `geometric algebra'. If you're curious, you can search for it on the internet.

Dan: I'm not curious, let's move on.

Carol: Okay, so I will punish attempts to divide two vectors by letting an error message appear. In Ruby you can use the command raise to halt execution and display an error message:

```   def /(a)
if a.class == Vector
raise
else
quotient = Vector.new           # scalar quotient
self.each_index{|k| quotient[k] = self[k]/a}
end
quotient
end
```

A quick check:

``` |gravity> irb
require "vector_try.rb"
true
v1 = Vector[1, 2, 3]
[1, 2, 3]
v2 = Vector[5, 6, 7]
[5, 6, 7]
v1 / 3.0
[0.333333333333333, 0.666666666666667, 1.0]
v2 / 0.5
[10.0, 12.0, 14.0]
v1 / v2
RuntimeError:
from ./vector_try.rb:41:in `/'
from (irb):6
from :0
quit
```
You see, we got a run time error, because the raise statement had the effect of raising an error, as it is called in Ruby.

## 15.4. The Score: Six to One

Erica: Let me see whether I can sum up what we've learned. We've successfully implemented seven legal operations, unary/binary addition/multiplication, scalar/vector multiplication and scalar division.

Of these seven, six are okay. It is only with scalar multiplication that we encounter a problem, namely a lack of symmetry.

Dan: Can you define "okay" for the other six?

Erica: I will be explicit. I will use a notation where v1 and v2 are vectors, and s is a scalar number, which could be either an integer or a floating point number. I will call something "well defined" if it gives a specific answer, and not an error message.

Here is the whole list of the six operations that I am now considering "okay":

1) unary plus: +v1 is okay, because it is well defined.

2) unary minus: -v1 is okay, because it is well defined.

3) binary plus: v1 + v2 is okay, because both v1 + v2 and v2 + v1 are well defined, and both give the exact same answer, as they should.

4) binary minus: v1 - v2 is okay, because both v1 - v2 and v2 - v1 are well defined, and both give the exact opposite answer, as they should.

5) vector times: v1 * v2 is okay, because both v1 * v2 and v2 * v1 are well defined, and both give the exact same answer, as they should.

6) scalar slash: v1 / s is okay, because it is well defined. The alternative, s / v1 is not defined, and will give an error message. However, that's perfectly okay too, because we have decided that we don't allow division by vectors.

So far, the score is six to zero, and we seem to be winning. The problem is that we are losing in the case of number 7:

7) scalar times: v1 * s is okay, because it is well defined. The alternative, s * v1 should give the same answer too, but unfortunately, in our implementation it is not defined, and in fact, as we have seen, it gives an error message.

Dan: Thanks, Erica, that's a very clear summary. So all we have to do, to save the day, is to repair s * v1, so that it gives a well defined result, and in fact the same result as v1 * s.

## 15.5. A Solution

Carol: Ah, of course! How stupid, we should have thought about it right away!

Carol: Class augmentation! We can just augment the number classes, telling them how to multiply with vectors! Just as we have already augmented the array class, telling it how to convert an array into a vector.

Dan: I don't see that. What exactly are you trying to repair?

Carol: Let's go back to square one. We wanted to make our scalar-vector multiplication symmetric. The problem was, when you have a vector v and you want to write

3 * v

you are effectively asking the number 3 to provide a method that knows how to handle multiplication of 3 with a vector. But the poor number 3 has never heard of vectors! No wonder we got an error message. Let me show again explicitly what the error message says:

``` |gravity> irb
require "vector_try.rb"
true
v = Vector[1, 2, 3]
[1, 2, 3]
v * 3
[3, 6, 9]
3 * v
TypeError: Vector can't be coerced into Fixnum
from (irb):4:in `*'
from (irb):4
from :0
quit
```
You see: it tells us that the number 3 expects to make a multiplication with something like another number. It tries to convert a vector into such an other number, but doesn't succeed.

Erica: So, what we really would like to do is to augment the rules for the class Fixnum, which is the class of integers, to explain how to multiply with an object of class Vector. We can then do the same for the class Float, the class of floating point numbers.

## 15.6. Augmenting the Fixnum Class

``` class Fixnum
alias :original_mult :*
def *(a)
if a.class == Vector
a*self
else
original_mult(a)
end
end
end
```

Dan: Whoa! That's quite a bit more complicated than I had expected. You must have been reading Ruby manuals lately!

Carol: I admit, I did. Here is what happens. If we start with the number 3, and take a vector v, and write

```  3 * v
```
then the * method of the fixed point number 3 first checks whether v is a Vector. In our case, it is, so then it returns the result

```  v * self
```
which in the case of 3 is simply

```  v * 3
```
and that is something we have already defined, in the Vector class.

If, on the other hand we write anything else, such as

```  3 * 8.5
```
then the * method of the fixed point number 3 finds that 8.5 is not a Vector, so it applies the original definition of multiplication, to the number 8.5, as it should.

Dan: So the alias notion means that whatever the original * did is now assigned to original_mult instead?

Carol: Exactly. Writing alias :x :y means that x becomes an alias for y. Or so the theory goes. Let's see what the practice tells us:

``` |gravity> irb
require "vector.rb"
true
v = [1, 2, 3].to_v
[1, 2, 3]
v * 3
[3, 6, 9]
(v * 3).class
Vector
3 * v
[3, 6, 9]
(3 * v).class
Vector
quit
```

## 15.7. Augmenting the Float Class

Dan: Not bad! Great! 3 * v now produces the exact same thing as v * 3. Congratulations! Another hope fulfilled. And I guess you should do the same thing for floating point numbers, right?

Carol: Right. All very similar:

``` class Float
alias :original_mult :*
def *(a)
if a.class == Vector
a*self
else
original_mult(a)
end
end
end
```

This time my hope for success will be quite justified:

``` |gravity> irb
require "vector.rb"
true
v = [1, 2, 3].to_v
[1, 2, 3]
v * 3.14
[3.14, 6.28, 9.42]
(v * 3.14).class
Vector
3.14 * v
[3.14, 6.28, 9.42]
(3.14 * v).class
Vector
quit
```
And indeed, it all comes out the way it should. So I declare victory: I'm very pleased with our vector class.

## 15.8. Vector Class Listing

Erica: Let's get the whole listing on the screen. Here it is. In fact, it is shorter than I thought, for all the things it does:

``` class Vector < Array
def +(a)
sum = Vector.new
self.each_index{|k| sum[k] = self[k]+a[k]}
sum
end
def -(a)
diff = Vector.new
self.each_index{|k| diff[k] = self[k]-a[k]}
diff
end
def +@
self
end
def -@
self.map{|x| -x}.to_v
end
def *(a)
if a.class == Vector              # inner product
product = 0
self.each_index{|k| product += self[k]*a[k]}
else
product = Vector.new            # scalar product
self.each_index{|k| product[k] = self[k]*a}
end
product
end
def /(a)
if a.class == Vector
raise
else
quotient = Vector.new           # scalar quotient
self.each_index{|k| quotient[k] = self[k]/a}
end
quotient
end
end

class Array
def to_v
Vector[*self]
end
end

class Fixnum
alias :original_mult :*
def *(a)
if a.class == Vector
a*self
else
original_mult(a)
end
end
end

class Float
alias :original_mult :*
def *(a)
if a.class == Vector
a*self
else
original_mult(a)
end
end
end
```

## 15.9. Forward Euler in Vector Form

``` include Math

def print_pos_vel(r,v)
r.each{|x| print(x, "  ")}
v.each{|x| print(x, "  ")}
print "\n"
end

r = [1, 0, 0]
v = [0, 0.5, 0]
dt = 0.01

print_pos_vel(r,v)
1000.times{
r2 = 0
r.each{|x| r2 += x*x}
r3 = r2 * sqrt(r2)
a = r.map{|x| -x/r3}
r.each_index{|k| r[k] += v[k]*dt}
v.each_index{|k| v[k] += a[k]*dt}
print_pos_vel(r,v)
}
```

What would that look like in vector form?

Carol: To start with, we'll have to require the file vector.rb. We also have to convert the arrays into vectors, using .to_v. Then, in the loop, we can use our vector versions of addition, unary minus, multiplication and division. Let me write it all in file euler_vector.rb:

``` require "vector.rb"
include Math

def print_pos_vel(r,v)
r.each{|x| print(x, "  ")}
v.each{|x| print(x, "  ")}
print "\n"
end

r = [1, 0, 0].to_v
v = [0, 0.5, 0].to_v
dt = 0.01

print_pos_vel(r,v)
1000.times{
r2 = r*r
r3 = r2 * sqrt(r2)
a = -r/r3
r += v*dt
v += a*dt
print_pos_vel(r,v)
}
```

Dan: Wait a minute, we haven't define += yet!

Carol: Ah, you see, that's a nice feature of operator overloading in Ruby: once you redefine + the same redefinition applies to derivative expressions such as +=.

Dan: That's nice, if it really works. Let's compare the run with the old results:

``` |gravity> ruby euler_array_each_def.rb | tail -1
7.6937453936572  -6.27772005661599  0.0  0.812206830641815  -0.574200201239989  0.0

|gravity> ruby euler_vector.rb | tail -1
7.6937453936572  -6.27772005661599  0.0  0.812206830641815  -0.574200201239989  0.0
```
Wonderful. And I must say, the code does look a lot cleaner now.

Erica: Definitely. This is a lot more pretty. What a difference! None of all those ugly [k] occurrences anymore. I think it was worth all the work we put in, defining a Vector class.
 Previous ToC Up Next