lib/matrix.rb in matrix-0.3.0 vs lib/matrix.rb in matrix-0.3.1
- old
+ new
@@ -530,11 +530,12 @@
end
alias map! collect!
def freeze
- @rows.freeze
+ @rows.each(&:freeze).freeze
+
super
end
#
# Yields all elements of the matrix, starting with those of the first row,
@@ -1231,33 +1232,56 @@
#
# Matrix[[7,6], [3,9]] ** 2
# # => 67 96
# # 48 99
#
- def **(other)
- case other
+ def **(exp)
+ case exp
when Integer
- x = self
- if other <= 0
- x = self.inverse
- return self.class.identity(self.column_count) if other == 0
- other = -other
+ case
+ when exp == 0
+ _make_sure_it_is_invertible = inverse
+ self.class.identity(column_count)
+ when exp < 0
+ inverse.power_int(-exp)
+ else
+ power_int(exp)
end
- z = nil
- loop do
- z = z ? z * x : x if other[0] == 1
- return z if (other >>= 1).zero?
- x *= x
- end
when Numeric
v, d, v_inv = eigensystem
- v * self.class.diagonal(*d.each(:diagonal).map{|e| e ** other}) * v_inv
+ v * self.class.diagonal(*d.each(:diagonal).map{|e| e ** exp}) * v_inv
else
- raise ErrOperationNotDefined, ["**", self.class, other.class]
+ raise ErrOperationNotDefined, ["**", self.class, exp.class]
end
end
+ protected def power_int(exp)
+ # assumes `exp` is an Integer > 0
+ #
+ # Previous algorithm:
+ # build M**2, M**4 = (M**2)**2, M**8, ... and multiplying those you need
+ # e.g. M**0b1011 = M**11 = M * M**2 * M**8
+ # ^ ^
+ # (highlighted the 2 out of 5 multiplications involving `M * x`)
+ #
+ # Current algorithm has same number of multiplications but with lower exponents:
+ # M**11 = M * (M * M**4)**2
+ # ^ ^ ^
+ # (highlighted the 3 out of 5 multiplications involving `M * x`)
+ #
+ # This should be faster for all (non nil-potent) matrices.
+ case
+ when exp == 1
+ self
+ when exp.odd?
+ self * power_int(exp - 1)
+ else
+ sqrt = power_int(exp / 2)
+ sqrt * sqrt
+ end
+ end
+
def +@
self
end
# Unary matrix negation.
@@ -2116,9 +2140,12 @@
#
def zero?
all?(&:zero?)
end
+ #
+ # Makes the matrix frozen and Ractor-shareable
+ #
def freeze
@elements.freeze
super
end