Jump to content

Mathematics with Python and Ruby/Quaternions in Ruby

From Wikibooks, open books for an open world

Complex numbers

[edit | edit source]

As was seen in the preceding chapter, a complex number is an object comprising 2 real numbers (called real and imag by Ruby). This is the Cayley-Dickson construction of the complex numbers. In a very similar manner, a quaternion can be considered as made of 2 complex numbers.

Note:
Historically, Hamilton constructed the quaternions as quadruples of real numbers. This can also be done in Ruby, but it will be left as an exercise.

In all the following, cmath will be used as it handles fractions automatically. This chapter is in some way different from the preceding ones, as it shows how to create brand new objects in Ruby, and not how to use already available objects.

Quaternions

[edit | edit source]

Definition and display

[edit | edit source]

Definition

[edit | edit source]

The definition of a quaternion finds its shelter in a class which is called Quaternion:

class Quaternion
 
end

The first method of a quaternion will be its instantiation:

Instantiation

[edit | edit source]
       def initialize(a,b)
                @a,@b = a,b
        end

From now on, a and b (which will be complex numbers) will be the 2 quaternion's attributes

Attributes a and b

[edit | edit source]

As the two numbers which define a quaternion are complex, it is not appropriate to call them the real and imaginary parts. Besides, an other stage will be necessary with the octonions later on. So the shortest names have been chosen, and they will be called the a of the quaternion, and its b.

       def a
                @a
        end
 
        def b
                @b
        end

From now on it is possible to access to the a and b part of a quaternion q with q.a and q.b.

Display

[edit | edit source]

In order that it be easy to display a quaternion q with puts(q) it is necessary to redefine a method to_s for it (a case of polymorphism). There are several choices but this one works OK:

       def to_s
                '('+a.real.to_s+')+('+a.imag.to_s+')i+('+b.real.to_s+')j+('+b.imag.to_s+')k'
        end

To read it loud it is better to read from right to left. For example, a.real denotes the real part of a and q.a.real denotes the real part of the a part of q.

Functions

[edit | edit source]

Modulus

[edit | edit source]

The absolute value of a quaternion is a (positive) real number.

       def abs
                Math.hypot(@a.abs,@b.abs)
        end

Conjugate

[edit | edit source]

The conjugate of a quaternion is another quaternion, having the same modulus.

       def conj
                Quaternion.new(@a.conj,-@b)
        end

Operations

[edit | edit source]

Addition

[edit | edit source]

To add two quaternions, just add their as together, and their bs together:

       def +(q)
                Quaternion.new(@a+q.a,@b+q.b)
        end

Subtraction

[edit | edit source]

The use of the - symbol is an other case of polymorphism, which allows to write rather simply the subtraction.

       def -(q)
                Quaternion.new(@a-q.a,@b-q.b)
        end

Multiplication

[edit | edit source]

Multiplication of the quaternions is more complex (!):

       def *(q)
                Quaternion.new(@a*q.a-@b*q.b.conj,@a*q.b+@b*q.a.conj)
        end

This multiplication is not commutative, as can be checked by the following examples:

p=Quaternion.new(Complex(2,1),Complex(3,4))
q=Quaternion.new(Complex(2,5),Complex(-3,-5))
puts(p*q)
puts(q*p)

Division

[edit | edit source]

The division can be defined as this:

       def /(q)
                d=q.abs**2
                Quaternion.new((@a*q.a.conj+@b*q.b.conj)/d,(-@a*q.b+@b*q.a)/d)
        end

As they have the same modulus, the quotient of a quaternion by its conjugate has modulus one:

p=Quaternion.new(Complex(2,1),Complex(3,4))
 
puts((p/p.conj).abs)

This last example digs that , or , which is a decomposition of as a sum of 4 squares.

Quaternion class in Ruby

[edit | edit source]

The complete class is here:

require 'cmath'
 
class Quaternion
 
        def initialize(a,b)
                @a,@b = a,b
        end
 
        def a
                @a
        end
 
        def b
                @b
        end
 
        def to_s
                '('+a.real.to_s+')+('+a.imag.to_s+')i+('+b.real.to_s+')j+('+b.imag.to_s+')k'
        end
 
        def +(q)
                Quaternion.new(@a+q.a,@b+q.b)
        end
 
        def -(q)
                Quaternion.new(@a-q.a,@b-q.b)
        end
 
        def *(q)
                Quaternion.new(@a*q.a-@b*q.b.conj,@a*q.b+@b*q.a.conj)
        end
 
        def abs
                Math.hypot(@a.abs,@b.abs)
        end
 
        def conj
                Quaternion.new(@a.conj,-@b)
        end
 
        def /(q)
                d=q.abs**2
                Quaternion.new((@a*q.a.conj+@b*q.b.conj)/d,(-@a*q.b+@b*q.a.conj)/d)
        end
 
end

If this content is saved in a text file called quaternion.rb, after require 'quaternion' one can make computations on quaternions.

Octonions

[edit | edit source]

One interesting fact about the Cayley-Dickson which has been used for the quaternions above, is that it can be generalized, for example for the octonions.

Definition and display

[edit | edit source]

Definition

[edit | edit source]

All the following methods will be enclosed in a class called Octonion:

class Octonion
 
        def initialize(a,b)
                @a,@b = a,b
        end
 
        def a
                @a
        end
 
        def b
                @b
        end

At this point, there is not much difference from the quaternion object. Only, for an octonion, a and b will be quaternions, not complex numbers. Ruby will know it when a and b will be instantiated.

Display

[edit | edit source]

The to_s method of an octonion (converting it to a string object so that it can be displayed) is very similar to the quaternion equivalent, only there are 8 real numbers to display now:

       def to_s
                '('+a.a.real.to_s+')+('+a.a.imag.to_s+')i+('+a.b.real.to_s+')j+('+a.b.imag.to_s+')k+('+b.a.real.to_s+')l+('+b.a.imag.to_s+')li+('+b.b.real.to_s+')lj+('+b.b.imag.to_s+')lk'
        end

The first of these numbers is the real part of the a part of the first quaternion, which is the octonions's a! Accessing to this real part of the a part of the octonion's a part, requires to go through a binary tree which depth is 3.

Functions

[edit | edit source]

Thanks to Cayley and Dickson, the methods needed for octonions computing are similar to the quaternion's.

Modulus

[edit | edit source]

Same than for the quaternions:

	def abs
		Math.hypot(@a.abs,@b.abs)
	end


Conjugate

[edit | edit source]
	def conj
		Octonion.new(@a.conj,Quaternion.new(0,0)-@b)
	end


Operations

[edit | edit source]

Addition

[edit | edit source]

Like for the quaternions, one has just to add the as and the bs separately (only now the a and b part are quaternions):

	def +(o)
		Octonion.new(@a+o.a,@b+o.b)
	end


Subtraction

[edit | edit source]
	def -(o)
		Octonion.new(@a-o.a,@b-o.b)
	end


Multiplication

[edit | edit source]
	def *(o)
		Octonion.new(@a*o.a-o.b*@b.conj,@a.conj*o.b+o.a*@b)
	end

This multiplication is still not commutative, but it is even not associative either!

m=Octonion.new(p,q)
n=Octonion.new(q,p)
o=Octonion.new(p,p)
puts((m*n)*o)
puts(m*(n*o))


Division

[edit | edit source]
	def /(o)
		d=1/o.abs**2
		Octonion.new((@a*o.a.conj+o.b*@b.conj)*Quaternion.new(d,0),(Quaternion.new(0,0)-@a.conj*o.b+o.a.conj*@b)*Quaternion.new(d,0))
	end

Here again, the division of an octonion by its conjugate has modulus 1:

puts(m/m.conj)
puts((m/m.conj).abs)


The octonion class in Ruby

[edit | edit source]

The file is not much heavier than the quaternion's one:

class Octonion

	def initialize(a,b)
		@a,@b = a,b
	end
	
	def a
		@a
	end
	
	def b
		@b
	end
	
	def to_s
		'('+a.a.real.to_s+')+('+a.a.imag.to_s+')i+('+a.b.real.to_s+')j+('+a.b.imag.to_s+')k+('+b.a.real.to_s+')l+('+b.a.imag.to_s+')li+('+b.b.real.to_s+')lj+('+b.b.imag.to_s+')lk'
	end
	
	def +(o)
		Octonion.new(@a+o.a,@b+o.b)
	end
	
	def -(o)
		Octonion.new(@a-o.a,@b-o.b)
	end
	
	def *(o)
		Octonion.new(@a*o.a-o.b*@b.conj,@a.conj*o.b+o.a*@b)
	end
	
	def abs
		Math.hypot(@a.abs,@b.abs)
	end
	
	def conj
		Octonion.new(@a.conj,Quaternion.new(0,0)-@b)
	end
	
	def /(o)
		d=1/o.abs**2
		Octonion.new((@a*o.a.conj+o.b*@b.conj)*Quaternion.new(d,0),(Quaternion.new(0,0)-@a.conj*o.b+o.a.conj*@b)*Quaternion.new(d,0))
	end
	

end

Saving it as octonions.rb, any script beginning by

require 'octonions'

allows computing on octonions.

Bibliography

[edit | edit source]
  • Actually, there is already a quaternion support for Ruby, but it is not (yet) native: [1]; on the same site, there is also a file for the octonions, which is interesting to compare to the above one.
  • A "best-downloader" book on octonions is John Baez's one: [2]