Created
March 8, 2014 15:24
-
-
Save MpoMp/9433134 to your computer and use it in GitHub Desktop.
First attempt @ norm implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# call-seq: | |
# norm -> Numeric | |
# | |
# Calculates the selected norm (defaults to Frobenius norm) of a 2D matrix. | |
# | |
# This should be used for small or medium sized matrices. | |
# For greater matrices, there should be a separate implementation where | |
# the norm is estimated, not computed. | |
# | |
# The norm calculation code is added to lambdas and then the corresponding one runs, depending on the input argument. | |
# | |
# Currently implemented norms are p-norm, Frobenius, Infinity. | |
# Based on this reference: https://en.wikipedia.org/wiki/Matrix_norm#.22Entrywise.22_norms | |
# | |
# * *Returns* : | |
# - The selected norm of the matrix. | |
# * *Raises* : | |
# - +NotImplementedError+ -> Must be used in 2D matrices. | |
# - +ArgumentError+ -> Argument must be positive integer or one of the valid strings. | |
# | |
def norm type = 2 | |
raise(NotImplementedError, "norm can be calculated only for 2D matrices") unless self.dim == 2 | |
str_args = [:fro, :frobenius, :inf, :infinity] | |
r = self.rows | |
c = self.cols | |
pnorm_lambda = lambda{ |p| | |
sum = 0 | |
r.times do |i| | |
sum += self.row(i).inject(0) {|vsum, n| vsum + (n**p)} | |
end | |
return sum**(1/p.to_f) | |
} | |
inorm_lambda = lambda{ | |
row_sums = [] | |
r.times do |i| | |
row_sums << self.row(i).inject(0) {|vsum, n| vsum + n} | |
end | |
return row_sums.sort!.last | |
} | |
if type.class == Fixnum | |
raise ArgumentError.new("given number has to be a positive integer") unless type.integer? && type > 0 | |
return pnorm_lambda.call(type) | |
else | |
raise ArgumentError.new("argument must be positive integer, string or symbol, found: #{type.class}") unless type.class == String || type.class == Symbol | |
type_str = type.to_s unless type.class == String | |
raise ArgumentError.new("no available norm for #{type_str}") unless str_args.include? type.downcase | |
return pnorm_lambda.call(2) if type_str.start_with? 'fro' | |
return inorm_lambda.call() if type_str.start_with? 'inf' | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#TODO: add precise, valid values for expected norms | |
context "#norm" do | |
it "should default to frobenius" do | |
n = NMatrix.new([2, 3], [1, 2, 3, 5, 6, 7]) | |
expect(n.norm).to eq(11.135528725660043) | |
end | |
it "should reject invalid arguments" do | |
n = NMatrix.new([2, 3], [1, 2, 3, 5, 6, 7]) | |
nums = [0.5, -1, 0] | |
nums.each do |num| | |
expect{n.norm(num)}.to raise_error(ArgumentError) | |
end | |
expect{n.norm([])}.to raise_error(ArgumentError) | |
expect{n.norm('fr0')}.to raise_error(ArgumentError) | |
expect{n.norm(:infiniti)}.to raise_error(ArgumentError) | |
end | |
it "should calculate p norms correctly" do | |
n = NMatrix.new([2, 3], [1, 2, 3, 5, 6, 7]) | |
expect(n.norm(2)).to eq(11.135528725660043) | |
expect(n.norm(3)).to eq(8.962809493114328) | |
expect(n.norm(4)).to eq(8.15371575138468) | |
end | |
it "should calculate infinity norms correctly" do | |
n = NMatrix.new([2, 3], [1, 2, 3, 5, 6, 7]) | |
expect(n.norm(:inf)).to eq(18) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment