Skip to content

Instantly share code, notes, and snippets.

@MpoMp
Created March 8, 2014 15:24
Show Gist options
  • Save MpoMp/9433134 to your computer and use it in GitHub Desktop.
Save MpoMp/9433134 to your computer and use it in GitHub Desktop.
First attempt @ norm implementation
#
# 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
#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