Class: Matrix

Inherits:
Object
  • Object
show all
Defined in:
lib/linmeric/CnGal_Matrix_class.rb

Overview

This class provides a simple representation of a matrix with the main features, operations and some useful method to manipulate it or to make its creation easier.

Author

Massimiliano Dal Mas ([email protected])

License

Distributed under MIT license

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(rws = 0, cls = 0) ⇒ Matrix

Initialization of new matrix in two different ways (chosen by the user):

  • a block can be given to create a matrix according to a function

  • the user must insert each value by hand (if block is not given)

  • requires: ‘#solve` of Calculator

  • argument: number of rows (Fixnum)

  • argument: number of columns (Fixnum)

  • block: yields a function with two arguments (optional)



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 29

def initialize(rws = 0,cls = 0)
  listener = Listener.new
  if rws == 0 or cls == 0 or rws.is_a? Float or cls.is_a? Float then 
    raise MyArgError,"   Argument Error: invalid dimension #{rws}x#{cls} for Matrix object" 
  elsif !(rws.is_a? Fixnum) or !(cls.is_a? Fixnum) then
        e = rws unless e.is_a? Fixnum
        e = cls unless cls.is_a? Fixnum
        raise MyArgError,"  Argument Error: colums and rows in Integer format expected but #{e.class} found"
  else
    @MyRws = rws; @MyCls = cls
    @mx=[]
    if block_given? then
      for i in 0...@MyRws
        for j in 0...@MyCls
          value = yield(i,j) if block_given? 
          @mx << value
        end
      end       
    else
        puts "Insert the line values (separated by space) and press return to go on"
        for i in 0...rws
          listener.reset
          cl = listener.gets.split
          raise ArgumentError, "  #{cls} element(s) expected but #{cl.size} found. Retry" unless cl.size==cls
          cl.map! { |e| 
            cv = Calculator.solve(e)
            err = e if cv == nil
            raise MyArgError, "  Argument Error: Invalid operation '#{err}' found in matrix rows" unless cv != nil
            e = cv
          }
          @mx +=cl 
      end
    end
  end
 
    
  rescue ArgumentError => message
    puts message
    @mx=[]
    retry
    
end

Class Method Details

.from_file(filename = "") ⇒ Object

Creates a new matrix loading it from a .csv file

  • argument: ‘String` of the file path

  • returns: new matrix object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 76

def Matrix.from_file(filename = "")
  @mx = []
  @MyCls = 0
  @MyRws = 0
  if !(File.exists? filename) or filename == "" then
    raise MyArgError, "  Argument Error: Invalid directory or filename"
  else
    File.open(filename, "r").each_line do |ln|
      @MyRws += 1
      ln = ln.chomp.split(',')
      ln.map! do |el| 
        cv = Calculator.solve(el)
        err = el if cv == nil
        raise InputError, "  Argument Error: Invalid operation '#{err}' found in matrix rows" unless cv != nil
        el = cv
      end
      @mx += ln
      if @mx.size % ln.size != 0 then
        raise MyArgError, "  Argument Error: Irregular matrix rows"
      end
      @MyCls = ln.length
    end
  end
  return mat = Matrix.new(@MyRws,@MyCls) {|i,j| @mx[i*@MyCls+j]}
end

.identity(n) ⇒ Object

Builds an identity matrix

  • argument: ‘Fixnum` of the matrix dimension

  • returns: ‘Matrix` object

Raises:



398
399
400
401
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 398

def Matrix.identity(n)
  raise MyArgError, "Argument Error: expecting Fixnum value, but #{n.class} found" unless n.is_a? Fixnum
  return Matrix.new(n,n) { |i,j|  (i == j) ? 1 : 0 }
end

Instance Method Details

#!=(obj) ⇒ Object

Checks if this matrix is different from another object

  • argument: ‘Object` to be compared with

  • returns: true if the matrix and the object are different; false else



230
231
232
233
234
235
236
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 230

def !=(obj)
  if obj.is_a? Matrix then
    self == obj ? (return false) : (return true)
  else
    return true
  end
end

#*(ob) ⇒ Object

Multiplies the current matrix to another

  • argument: ‘Matrix` to be multiplied to

  • returns: ‘Matrix` object



301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 301

def *(ob)
  case 
    when (ob.is_a? Numeric)
      return Matrix.new(@MyRws,@MyCls){ |i,j| self[i,j] * ob}
    when (ob.is_a? Matrix)
      if self.can_multiply? ob then
        temp = []
        for i in 0...@MyRws
          for j in 0...ob.getCls()
            temp[i*@MyCls + j] = 0
            for k in 0...@MyCls
              temp[i*@MyCls + j] += @mx[i*@MyCls + k] * ob[k,j]
            end
          end
        end
        return Matrix.new(@MyRws,ob.getCls()){ |i,j| temp[i * @MyCls + j]}
      else
        raise MyArgError, "  Argument Error: Cannot multiply #{@MyRws}x#{@MyCls} Matrix with #{ob.getRws}x#{ob.getCls}"
      end
    else
      raise MyArgError, "  Argument Error: Cannot multiply Matrix with #{ob.class}"
  end
end

#**(obj) ⇒ Object

Elevates each element of the current matrix to a ‘Numeric` exponent

  • argument: ‘Numeric`

  • returns: ‘Matrix` object

Raises:



346
347
348
349
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 346

def **(obj)
  return Matrix.new(@MyRws,@MyCls) { |i,j| self[i,j] ** obj} if obj.is_a? Numeric
  raise MyArgError, "  Argument Error: Invalid power Matrix-#{obj.class} "
end

#+(m) ⇒ Object

Sums the current matrix to another

  • argument: ‘Matrix` to be summed to

  • returns: ‘Matrix` object



255
256
257
258
259
260
261
262
263
264
265
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 255

def +(m)
  if m.is_a? Matrix then
    if m.similar_to? self then
      return Matrix.new(@MyRws, @MyCls){ |i,j| self[i,j] + m[i,j]}
    else
      raise MyArgError, "  Argument Error: invalid matrix for + operation"
    end
  else
    raise MyArgError, "  Argument Error: can't sum #{m.class} to Matrix"
  end
end

#-(m) ⇒ Object

Subtracts the current matrix to another

  • argument: ‘Matrix` to be subtracted to

  • returns: ‘Matrix` object



271
272
273
274
275
276
277
278
279
280
281
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 271

def -(m)
  if m.is_a? Matrix then
    if m.similar_to? self then
      return Matrix.new(@MyRws, @MyCls){ |i,j| self[i,j] - m[i,j]}
    else
      raise MyArgError, "  Argument Error: invalid matrix for - operation"
    end
  else
    raise MyArgError, "  Argument Error: can't subtact #{m.class} to Matrix"
  end
end

#/(obj) ⇒ Object

Divides each element of the current matrix to a ‘Numeric` value

  • argument: ‘Numeric`

  • returns: ‘Matrix` object

Raises:



337
338
339
340
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 337

def /(obj)
  return Matrix.new(@MyRws,@MyCls) { |i,j| self[i,j] / obj.to_f} if obj.is_a? Numeric
  raise MyArgError, "  Argument Error: Invalid division between Matrix and #{obj.class} "
end

#==(obj) ⇒ Object

Compares the current matrix with another object

  • argument: ‘Object` to be compared with

  • returns: true if two equal matrix are compered; false else



217
218
219
220
221
222
223
224
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 217

def ==(obj)
 if obj.is_a? Matrix then
   if (self.getCls == obj.getCls && self.getRws == obj.getRws ) then
     (@mx == obj.export) ? (return true) : (return false)
   end
 end
 return false
end

#[](x, y) ⇒ Object

Gives access to each component of a matrix. It accepts two kinds of arguments: ‘Fixnum` or `Range` to get a specific component or a collection of components (eg. a whole row)

  • argument: ‘Fixnum` or `Range` of row

  • argument: ‘Fixnum` or `Range` of column

  • returns: ‘Numeric` if an only component has been required; `Matrix` else



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 176

def [](x,y)
  if x.is_a? Numeric and y.is_a? Numeric then
    return @mx[x*@MyCls + y]
  elsif x.is_a? Range and y.is_a? Numeric
    nm = []
    x.each do |i|
      nm << @mx[i*@MyCls + y]
    end
    return Matrix.new(x.count,1){|i,j| nm[i + j]}
  elsif x.is_a? Numeric and y.is_a? Range
    nm = []
    y.each do |j|
      nm << @mx[x*@MyCls + j]
    end
    return Matrix.new(1,y.count){|i,j| nm[i + j]}
  elsif x.is_a? Range and y.is_a? Range
    nm = []
    x.each do |i|
      y.each do |j|
        nm << @mx[i*@MyCls + j]
      end
    end
    return Matrix.new(x.count,y.count){ |i,j| nm[i*y.count + j]}
  end
  return nil
end

#[]=(i, j, val) ⇒ Object

Allows direct overwriting of a component

  • argument: ‘Fixnum` of row number

  • argument: ‘Fixnum` of column number

  • argument: ‘Numeric` to overwrite the component value

Raises:

  • (ArgumentError)


208
209
210
211
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 208

def []=(i,j,val)
  raise ArgumentError, "  Argument Error: Numeric value expected but #{val.class} found" unless val.is_a? Numeric
  @mx[i*@MyCls + j] = val
end

#can_divide?(obj) ⇒ Boolean

Checks if the given object can divide the current matrix

  • argument: ‘Object`

  • returns: true if ‘obj` is a Numeric; false else

Returns:

  • (Boolean)


329
330
331
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 329

def can_divide?(obj)
  return (obj.is_a? Numeric)
end

#can_multiply?(obj) ⇒ Boolean

Checks if the given object can be multiplied to the matrix

  • argument: ‘Object`

  • returns: true+ if ‘obj` is a `Numeric` or a good `Matrix` for ’*‘ operation;

false else

Returns:

  • (Boolean)


288
289
290
291
292
293
294
295
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 288

def can_multiply?(obj)
  if obj.is_a? Matrix then
    (self.getCls == obj.getRws) ? (return true) : (return false)
  elsif obj.is_a? Numeric
    return true
  end
  return false
end

#coerce(val) ⇒ Object



403
404
405
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 403

def coerce(val)
  return [self, val]
end

#exportObject

  • returns: ‘Array` on which the matrix is saved



146
147
148
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 146

def export()
  return @mx
end

#getClsObject

  • returns: ‘Fixnum` of number of columns



136
137
138
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 136

def getCls()
  return @MyCls
end

#getRwsObject

  • returns: ‘Fixnum` of number of rows



141
142
143
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 141

def getRws()
  return @MyRws
end

#is_squared?Boolean

Checks if the matrix is squared

  • returns: true if the matrix is squared; false else

Returns:

  • (Boolean)


373
374
375
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 373

def is_squared?()
  self.getCls == self.getRws ? (return true) : (return false)
end

#laplaceObject

Calulates the determinant of the matrix using the Laplace’s algorithm

  • returns: ‘Numeric` value



380
381
382
383
384
385
386
387
388
389
390
391
392
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 380

def laplace
  if self.is_squared? then
    return (self[0,0] * self[1,1]) - (self[0,1] * self[1,0]) if self.getRws == 2 
    res = 0
    for i in 0...@MyRws
      res += (((-1) ** i) * self[i,0] * del_rwcl(self,i,0).laplace) if self[i,0] != 0
    end
    return res
  else
    raise MyArgError, '  Argument Error: Cannot calculate determinat on a non-squared matrix'
    return nil
  end
end

#normObject

Calculates the norm of a matrix, defined as sqrt(sum(m**2))

  • returns: ‘Float` value



362
363
364
365
366
367
368
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 362

def norm()
 sum=0
 @mx.map do |e|
   sum +=e**2
 end
 return Math.sqrt(sum)
end

#similar_to?(obj) ⇒ Boolean

Checks if this matrix and the given object have the same features

  • argument: ‘Object`

  • returns: true if ‘obj` is a `Matrix` and has the same rows and colum number;

false else

Returns:

  • (Boolean)


243
244
245
246
247
248
249
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 243

def similar_to?(obj)
  if obj.is_a? Matrix then
    (self.getCls == obj.getCls && 
      self.getRws == obj.getRws ) ? (return true) : (return false)
  end
  return false
end

#to_file(filename = "") ⇒ Object

Writes the matrix on a file

  • argument: ‘String` of the file path



105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 105

def to_file(filename = "")
  if !(Dir.exist? File.dirname(filename)) or filename == "" then
    raise MyArgError, "  Argument Error: Invalid directory; Directory not found"
  else
    File.open(filename,"w") do |rw|
      for i in 0...@MyRws do
        for j in 0...(@MyCls - 1) do
          rw.print "#{self[i,j]},"
        end
        rw.puts "#{self[i,j+1]}"
      end
    end
  end
end

#to_sObject

converts this matrix object to a string



121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 121

def to_s()
  max = 0
  str = ""
  @mx.each{ |n| max = "#{n.round(3)}".size if "#{n.round(3)}".size > max}
  for i in 0...@MyRws do
    str += '|'
    for j in (i*(@MyCls))...(i*(@MyCls)+@MyCls) do
      str += "  #{" "*(max-@mx[j].round(3).to_s.size).abs}#{@mx[j].round(3) }"
    end
    str+= "  |\n"
  end
  return str
end

#trObject

Trasposes a matrix

returns: ‘Matrix` object



354
355
356
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 354

def tr()
  return Matrix.new(@MyCls,@MyRws) { |i,j| @mx[j * @MyCls + i]}
end

#update(mat, rws, cls) ⇒ Object

Updates this matrix after the array in which it was saved has been manually modified

  • argument: ‘Array` on which the new matrix is saved

  • argument: ‘Fixnum` of number of rows

  • argument: ‘Fixnum` of number of columns



156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/linmeric/CnGal_Matrix_class.rb', line 156

def update(mat,rws,cls)
  if !(mat.is_a? Array) then
    raise MyArgError, "  Argument Error: invalid matrix array found"
  else
    [cls,rws].each do |e|
      raise MyArgError, "  Argument Error: colums and rows in Integer format expected but #{e.class} found" unless e.is_a? Numeric
    end
  end
   @mx = mat
   @MyRws = rws
   @MyCls = cls
end