Skip to content

Commit

Permalink
Merge pull request #467 from shahsaurabh0605/diverged
Browse files Browse the repository at this point in the history
Issue #466 Add NMatrix#magic function
  • Loading branch information
translunar committed Mar 9, 2016
2 parents c97c43a + 1ead545 commit 0de5c43
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 2 deletions.
137 changes: 136 additions & 1 deletion lib/nmatrix/shortcuts.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,92 @@

class NMatrix

# Methods for generating magic matrix.
module MagicHelpers
class << self
def odd_magic(nm, shape)
row = shape - 1
col = shape / 2
nm[row,col] = 1
(2..shape * shape).each do |index|
if nm[(row + 1) % shape,(col + 1) % shape] == 0
row = (row + 1) % shape
col = (col + 1) % shape
else
row = (row - 1 + shape) % shape
end
nm[row,col] = index
end
end

def doubly_even_magic(nm, shape)
mini_square_num = shape / 4
count = 1
inv_count = shape * shape
shape.times do |row|
shape.times do |col|
if col >= mini_square_num and col < shape - mini_square_num
if row >= mini_square_num and row < shape - mini_square_num
nm[row,col] = count
else
nm[row,col] = inv_count
end
elsif row < mini_square_num or row >= shape - mini_square_num
nm[row,col] = count
else
nm[row,col] = inv_count
end
count += 1
inv_count -= 1
end
end
end

def singly_even_magic(nm, shape)
half_shape = shape / 2
complementary_pair = (shape - 2) / 4
swap_col = NMatrix.new([shape])
index = 0
mini_magic = NMatrix.new([half_shape,half_shape], 0, dtype: nm.dtype)
odd_magic mini_magic, half_shape
half_shape.times do |row|
half_shape.times do |col|
nm[row,col] = mini_magic[row,col]
nm[row + half_shape,col + half_shape] = mini_magic[row,col] + half_shape * half_shape
nm[row,col + half_shape] = mini_magic[row,col] + 2 * half_shape * half_shape
nm[row + half_shape,col] = mini_magic[row,col] + 3 * half_shape * half_shape
end
end

(1..complementary_pair).each do |complementary_entry|
swap_col[index] = complementary_entry
index += 1
end

(shape - complementary_pair + 2..shape).each do |center|
swap_col[index] = center
index += 1
end

(1..half_shape).each do |row|
(1..index).each do |col|
temp = nm[row - 1,swap_col[col - 1] - 1]
nm[row - 1,swap_col[col - 1] - 1] = nm[row + half_shape - 1,swap_col[col - 1] - 1]
nm[row + half_shape - 1,swap_col[col - 1] - 1] = temp
end
end

temp = nm[complementary_pair,0]
nm[complementary_pair,0] = nm[complementary_pair + half_shape,0]
nm[complementary_pair + half_shape,0] = temp

temp = nm[complementary_pair + half_shape,complementary_pair]
nm[complementary_pair + half_shape,complementary_pair] = nm[complementary_pair,complementary_pair]
nm[complementary_pair,complementary_pair] = temp
end
end
end

# call-seq:
# m.dense? -> true or false
#
Expand Down Expand Up @@ -465,7 +551,56 @@ def random(shape, opts={})
NMatrix.new(shape, random_values, {:dtype => :float64, :stype => :dense}.merge(opts))
end
alias :rand :random


#
# call-seq:
# magic(shape) -> NMatrix
# magic(shape, dtype: dtype) -> NMatrix
#
# The parameter is the dimension of the matrix.
#
# Creates a +:dense+ NMatrix with the following properties:
# - An arrangement of the numbers from 1 to n^2 (n-squared) in the matrix, with each number occurring exactly once.
# - The sum of the entries of any row, any column, or any main diagonal is the same.
# - This sum must be n(n^2+1)/2.
#
# See: http://www.mathworks.com/help/matlab/ref/magic.html
#
# * *Arguments* :
# - +shape+ -> Array (or integer for square matrix) specifying the dimensions.
# - +dtype+ -> (optional) Default is +:float64+
# * *Returns* :
# - NMatrix with the above given properties.
#
# Examples:
#
# NMatrix.magic(3) # => [ [4.0, 9.0, 2.0] [3.0, 5.0, 7.0] [8.0, 1.0, 6.0] ]
#
# NMatrix.magic(4, dtype :int32) # => [ [ 1, 15, 14, 4]
# [12, 6, 7, 9]
# [ 8, 10, 11, 5]
# [13, 3, 2, 16] ]
#
# NMatrix.magic(6,dtype: :int64) # => [ [31, 9, 2, 22, 27, 20]
# [ 3, 32, 7, 21, 23, 25]
# [35, 1, 6, 26, 19, 24]
# [ 4, 36, 29, 13, 18, 11]
# [30, 5, 34, 12, 14, 16]
# [ 8, 28, 33, 17, 10, 15] ]
#
def magic(shape, opts={})
raise(ArgumentError, "shape of two is not allowed") if shape == 2
nm = NMatrix.new([shape,shape], 0, {:dtype => :float64}.merge(opts))
if shape % 2 != 0
MagicHelpers.odd_magic nm, shape
elsif shape % 4 == 0
MagicHelpers.doubly_even_magic nm, shape
else
MagicHelpers.singly_even_magic nm, shape
end
nm
end

#
# call-seq:
# linspace(base, limit) -> 1x100 NMatrix
Expand Down
29 changes: 29 additions & 0 deletions spec/shortcuts_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,35 @@
end
end

context "::magic" do

ALL_DTYPES.each do |dtype|
context dtype do
it "creates a matrix with numbers from 1 to n^n(n squared)" do
a = NMatrix.magic(3, dtype: dtype)
magic3 = NMatrix.new([3,3], [4, 9, 2, 3, 5, 7, 8, 1, 6], dtype: dtype)
expect(a).to eq magic3

b = NMatrix.magic(4, dtype: dtype)
magic4 = NMatrix.new([4,4], [1, 15, 14, 4, 12, 6, 7, 9, 8, 10, 11, 5, 13, 3, 2, 16], dtype: dtype)
expect(b).to eq magic4

c = NMatrix.magic(6, dtype: dtype)
magic6 = NMatrix.new([6,6], [31, 9, 2, 22, 27, 20, 3, 32, 7, 21, 23, 25, 35, 1, 6, 26, 19, 24, 4, 36, 29, 13, 18, 11, 30, 5, 34, 12, 14, 16, 8, 28, 33, 17, 10, 15], dtype: dtype)
expect(c).to eq magic6
end
end
end

it "shape of two is not allowed" do
expect { NMatrix.magic(2) }.to raise_error(ArgumentError)
end

it "Only accepts an integer as dimension" do
expect { NMatrix.magic(3.0) }.to raise_error(ArgumentError)
end
end

context "::linspace" do
it "creates a row vector when given only one shape parameter" do
v = NMatrix.linspace(1, 10, 4)
Expand Down
2 changes: 1 addition & 1 deletion spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

ALL_DTYPES = [:byte,:int8,:int16,:int32,:int64, :float32,:float64, :object,
:complex64, :complex128]

NON_INTEGER_DTYPES = [:float32, :float64, :complex64, :complex128,
:object]

Expand Down

0 comments on commit 0de5c43

Please sign in to comment.