nmatrix 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/ext/nmatrix/data/data.cpp +9 -9
  3. data/ext/nmatrix/data/data.h +7 -8
  4. data/ext/nmatrix/data/ruby_object.h +1 -4
  5. data/ext/nmatrix/extconf.rb +9 -127
  6. data/ext/nmatrix/math.cpp +25 -25
  7. data/ext/nmatrix/math/asum.h +10 -31
  8. data/ext/nmatrix/math/cblas_templates_core.h +10 -10
  9. data/ext/nmatrix/math/getrf.h +2 -2
  10. data/ext/nmatrix/math/imax.h +12 -9
  11. data/ext/nmatrix/math/laswp.h +3 -3
  12. data/ext/nmatrix/math/long_dtype.h +16 -3
  13. data/ext/nmatrix/math/magnitude.h +54 -0
  14. data/ext/nmatrix/math/nrm2.h +19 -14
  15. data/ext/nmatrix/math/trsm.h +40 -36
  16. data/ext/nmatrix/math/util.h +14 -0
  17. data/ext/nmatrix/nmatrix.h +39 -1
  18. data/ext/nmatrix/ruby_nmatrix.c +45 -83
  19. data/ext/nmatrix/storage/common.h +9 -3
  20. data/ext/nmatrix/storage/dense/dense.cpp +4 -4
  21. data/ext/nmatrix/storage/list/list.cpp +2 -2
  22. data/ext/nmatrix/storage/yale/class.h +1 -1
  23. data/lib/nmatrix/blas.rb +103 -34
  24. data/lib/nmatrix/io/fortran_format.rb +8 -5
  25. data/lib/nmatrix/io/harwell_boeing.rb +11 -10
  26. data/lib/nmatrix/io/market.rb +9 -6
  27. data/lib/nmatrix/io/mat5_reader.rb +54 -29
  28. data/lib/nmatrix/io/mat_reader.rb +26 -14
  29. data/lib/nmatrix/io/point_cloud.rb +19 -11
  30. data/lib/nmatrix/math.rb +224 -5
  31. data/lib/nmatrix/mkmf.rb +103 -0
  32. data/lib/nmatrix/nmatrix.rb +20 -6
  33. data/lib/nmatrix/shortcuts.rb +415 -0
  34. data/lib/nmatrix/version.rb +1 -1
  35. data/spec/00_nmatrix_spec.rb +50 -1
  36. data/spec/02_slice_spec.rb +21 -21
  37. data/spec/blas_spec.rb +25 -3
  38. data/spec/math_spec.rb +233 -5
  39. data/spec/shortcuts_spec.rb +145 -5
  40. data/spec/spec_helper.rb +24 -1
  41. metadata +20 -4
@@ -534,6 +534,16 @@ class NMatrix
534
534
  rank(0, row_number, get_by)
535
535
  end
536
536
 
537
+ #
538
+ # call-seq:
539
+ # last -> Element of self.dtype
540
+ #
541
+ # Returns the last element stored in an NMatrix
542
+ #
543
+ def last
544
+ self[*Array.new(self.dim, -1)]
545
+ end
546
+
537
547
 
538
548
  #
539
549
  # call-seq:
@@ -700,16 +710,20 @@ class NMatrix
700
710
  # Do the actual construction.
701
711
  n = NMatrix.new(new_shape, opts)
702
712
 
703
- # Figure out where to start and stop the concatenation. We'll use NMatrices instead of
704
- # Arrays because then we can do elementwise addition.
705
- ranges = self.shape.map.with_index { |s,i| 0...self.shape[i] }
713
+ # Figure out where to start concatenation. We don't know where it will end,
714
+ # because each matrix may have own size along concat dimension.
715
+ pos = Array.new(self.dim) { 0 }
706
716
 
707
717
  matrices.unshift(self)
708
718
  matrices.each do |m|
719
+ # Figure out where to start and stop the concatenation. We'll use
720
+ # NMatrices instead of Arrays because then we can do elementwise addition.
721
+ ranges = m.shape.map.with_index { |s,i| pos[i]...(pos[i] + s) }
722
+
709
723
  n[*ranges] = m
710
724
 
711
- # move over by the requisite amount
712
- ranges[rank] = (ranges[rank].first + m.shape[rank])...(ranges[rank].last + m.shape[rank])
725
+ # Move over by the requisite amount
726
+ pos[rank] = pos[rank] + m.shape[rank]
713
727
  end
714
728
 
715
729
  n
@@ -1011,7 +1025,7 @@ class NMatrix
1011
1025
  raise(ArgumentError, 'Matrix should be repeated at least 2 times.') if count < 2
1012
1026
  new_shape = shape
1013
1027
  new_shape[axis] *= count
1014
- new_matrix = NMatrix.new(new_shape)
1028
+ new_matrix = NMatrix.new(new_shape, dtype: dtype)
1015
1029
  slice = new_shape.map { |axis_size| 0...axis_size }
1016
1030
  start = 0
1017
1031
  count.times do
@@ -34,6 +34,92 @@
34
34
 
35
35
  class NMatrix
36
36
 
37
+ # Methods for generating magic matrix.
38
+ module MagicHelpers
39
+ class << self
40
+ def odd_magic(nm, shape)
41
+ row = shape - 1
42
+ col = shape / 2
43
+ nm[row,col] = 1
44
+ (2..shape * shape).each do |index|
45
+ if nm[(row + 1) % shape,(col + 1) % shape] == 0
46
+ row = (row + 1) % shape
47
+ col = (col + 1) % shape
48
+ else
49
+ row = (row - 1 + shape) % shape
50
+ end
51
+ nm[row,col] = index
52
+ end
53
+ end
54
+
55
+ def doubly_even_magic(nm, shape)
56
+ mini_square_num = shape / 4
57
+ count = 1
58
+ inv_count = shape * shape
59
+ shape.times do |row|
60
+ shape.times do |col|
61
+ if col >= mini_square_num and col < shape - mini_square_num
62
+ if row >= mini_square_num and row < shape - mini_square_num
63
+ nm[row,col] = count
64
+ else
65
+ nm[row,col] = inv_count
66
+ end
67
+ elsif row < mini_square_num or row >= shape - mini_square_num
68
+ nm[row,col] = count
69
+ else
70
+ nm[row,col] = inv_count
71
+ end
72
+ count += 1
73
+ inv_count -= 1
74
+ end
75
+ end
76
+ end
77
+
78
+ def singly_even_magic(nm, shape)
79
+ half_shape = shape / 2
80
+ complementary_pair = (shape - 2) / 4
81
+ swap_col = NMatrix.new([shape])
82
+ index = 0
83
+ mini_magic = NMatrix.new([half_shape,half_shape], 0, dtype: nm.dtype)
84
+ odd_magic mini_magic, half_shape
85
+ half_shape.times do |row|
86
+ half_shape.times do |col|
87
+ nm[row,col] = mini_magic[row,col]
88
+ nm[row + half_shape,col + half_shape] = mini_magic[row,col] + half_shape * half_shape
89
+ nm[row,col + half_shape] = mini_magic[row,col] + 2 * half_shape * half_shape
90
+ nm[row + half_shape,col] = mini_magic[row,col] + 3 * half_shape * half_shape
91
+ end
92
+ end
93
+
94
+ (1..complementary_pair).each do |complementary_entry|
95
+ swap_col[index] = complementary_entry
96
+ index += 1
97
+ end
98
+
99
+ (shape - complementary_pair + 2..shape).each do |center|
100
+ swap_col[index] = center
101
+ index += 1
102
+ end
103
+
104
+ (1..half_shape).each do |row|
105
+ (1..index).each do |col|
106
+ temp = nm[row - 1,swap_col[col - 1] - 1]
107
+ nm[row - 1,swap_col[col - 1] - 1] = nm[row + half_shape - 1,swap_col[col - 1] - 1]
108
+ nm[row + half_shape - 1,swap_col[col - 1] - 1] = temp
109
+ end
110
+ end
111
+
112
+ temp = nm[complementary_pair,0]
113
+ nm[complementary_pair,0] = nm[complementary_pair + half_shape,0]
114
+ nm[complementary_pair + half_shape,0] = temp
115
+
116
+ temp = nm[complementary_pair + half_shape,complementary_pair]
117
+ nm[complementary_pair + half_shape,complementary_pair] = nm[complementary_pair,complementary_pair]
118
+ nm[complementary_pair,complementary_pair] = temp
119
+ end
120
+ end
121
+ end
122
+
37
123
  # call-seq:
38
124
  # m.dense? -> true or false
39
125
  #
@@ -236,6 +322,86 @@ class NMatrix
236
322
  m
237
323
  end
238
324
  alias :identity :eye
325
+
326
+ #
327
+ # call-seq:
328
+ # hilbert(shape) -> NMatrix
329
+ # hilbert(shape, dtype: dtype) -> NMatrix
330
+ # hilbert(shape, stype: stype, dtype: dtype) -> NMatrix
331
+ #
332
+ # Creates an hilbert matrix (square matrix).
333
+ #
334
+ # * *Arguments* :
335
+ # - +size+ -> integer ( for square matrix) specifying the dimensions.
336
+ # - +dtype+ -> (optional) Default is +:float64+
337
+ # - +stype+ -> (optional) Default is +:dense+.
338
+ # * *Returns* :
339
+ # - A hilbert matrix.
340
+ #
341
+ # Examples:
342
+ #
343
+ # NMatrix.hilbert(3) # => 1.0 0.5 0.3333333333333333
344
+ # 0.5 0.3333333333333333 0.25
345
+ # 0.3333333333333333 0.25 0.2
346
+ #
347
+ def hilbert(shape, opts={})
348
+ m = NMatrix.new([shape,shape], {:dtype => :float64}.merge(opts))
349
+ 0.upto(shape - 1) do |i|
350
+ 0.upto(i) do |j|
351
+ m[i,j] = 1.0 / (j + i + 1)
352
+ m[j,i] = m[i,j] if i != j
353
+ end
354
+ end
355
+ m
356
+ end
357
+
358
+ #
359
+ # call-seq:
360
+ # inv_hilbert(shape) -> NMatrix
361
+ # inv_hilbert(shape, dtype: dtype) -> NMatrix
362
+ # inv_hilbert(shape, stype: stype, dtype: dtype) -> NMatrix
363
+ #
364
+ # Creates an inverse hilbert matrix (square matrix rank 2).
365
+ #
366
+ # * *Arguments* :
367
+ # - +size+ -> Array (or integer for square matrix) specifying the dimensions.
368
+ # - +dtype+ -> (optional) Default is +:float64+
369
+ # - +stype+ -> (optional) Default is +:dense+.
370
+ # * *Returns* :
371
+ # - A hilbert matrix.
372
+ #
373
+ # Examples:
374
+ # NMatrix.inv_hilbert(3) # => 9.0, -36.0, 30.0
375
+ # -36.0, 192.0, -180.0
376
+ # 30.0, -180.0, 180.0
377
+ #
378
+ #
379
+ def inv_hilbert(shape, opts={})
380
+ opts = {:dtype => :float64}.merge(opts)
381
+ m = NMatrix.new([shape,shape],opts)
382
+ combination = NMatrix.new([2*shape,2*shape],opts)
383
+ #combinations refers to the combination of n things taken k at a time
384
+ 0.upto(2*shape-1) do |i|
385
+ 0.upto(i) do |j|
386
+ if j != 0 and j != i
387
+ combination[i,j] = combination[i-1,j] + combination[i-1,j-1]
388
+ else
389
+ combination[i,j] = 1
390
+ end
391
+ end
392
+ end
393
+
394
+ 0.upto(shape-1) do |i|
395
+ 0.upto(i) do |j|
396
+ m[i,j] = combination[shape + j,shape - i - 1] * ((i+j)+1) * \
397
+ combination[shape + i,shape - j - 1] * (-1) ** ((i+j)) * \
398
+ combination[(i+j),i] * combination[(i+j),i]
399
+ m[j,i] = m[i,j] if i != j
400
+ end
401
+ end
402
+ m
403
+ end
404
+
239
405
  #
240
406
  # call-seq:
241
407
  # diagonals(array) -> NMatrix
@@ -385,6 +551,255 @@ class NMatrix
385
551
  NMatrix.new(shape, random_values, {:dtype => :float64, :stype => :dense}.merge(opts))
386
552
  end
387
553
  alias :rand :random
554
+
555
+ #
556
+ # call-seq:
557
+ # magic(shape) -> NMatrix
558
+ # magic(shape, dtype: dtype) -> NMatrix
559
+ #
560
+ # The parameter is the dimension of the matrix.
561
+ #
562
+ # Creates a +:dense+ NMatrix with the following properties:
563
+ # - An arrangement of the numbers from 1 to n^2 (n-squared) in the matrix, with each number occurring exactly once.
564
+ # - The sum of the entries of any row, any column, or any main diagonal is the same.
565
+ # - This sum must be n(n^2+1)/2.
566
+ #
567
+ # See: http://www.mathworks.com/help/matlab/ref/magic.html
568
+ #
569
+ # * *Arguments* :
570
+ # - +shape+ -> Array (or integer for square matrix) specifying the dimensions.
571
+ # - +dtype+ -> (optional) Default is +:float64+
572
+ # * *Returns* :
573
+ # - NMatrix with the above given properties.
574
+ #
575
+ # Examples:
576
+ #
577
+ # NMatrix.magic(3) # => [ [4.0, 9.0, 2.0] [3.0, 5.0, 7.0] [8.0, 1.0, 6.0] ]
578
+ #
579
+ # NMatrix.magic(4, dtype :int32) # => [ [ 1, 15, 14, 4]
580
+ # [12, 6, 7, 9]
581
+ # [ 8, 10, 11, 5]
582
+ # [13, 3, 2, 16] ]
583
+ #
584
+ # NMatrix.magic(6,dtype: :int64) # => [ [31, 9, 2, 22, 27, 20]
585
+ # [ 3, 32, 7, 21, 23, 25]
586
+ # [35, 1, 6, 26, 19, 24]
587
+ # [ 4, 36, 29, 13, 18, 11]
588
+ # [30, 5, 34, 12, 14, 16]
589
+ # [ 8, 28, 33, 17, 10, 15] ]
590
+ #
591
+ def magic(shape, opts={})
592
+ raise(ArgumentError, "shape of two is not allowed") if shape == 2
593
+ nm = NMatrix.new([shape,shape], 0, {:dtype => :float64}.merge(opts))
594
+ if shape % 2 != 0
595
+ MagicHelpers.odd_magic nm, shape
596
+ elsif shape % 4 == 0
597
+ MagicHelpers.doubly_even_magic nm, shape
598
+ else
599
+ MagicHelpers.singly_even_magic nm, shape
600
+ end
601
+ nm
602
+ end
603
+
604
+ #
605
+ # call-seq:
606
+ # linspace(base, limit) -> 1x100 NMatrix
607
+ # linspace(base, limit, *shape) -> NMatrix
608
+ #
609
+ # Returns an NMatrix with +[shape[0] x shape[1] x .. x shape[dim-1]]+ values of dtype +:float64+ equally spaced from
610
+ # +base+ to +limit+, inclusive.
611
+ #
612
+ # See: http://www.mathworks.com/help/matlab/ref/linspace.html
613
+ #
614
+ # * *Arguments* :
615
+ # - +base+ -> The first value in the sequence.
616
+ # - +limit+ -> The last value in the sequence.
617
+ # - +shape+ -> Desired output shape. Default returns a 1x100 row vector.
618
+ # * *Returns* :
619
+ # - NMatrix with +:float64+ values.
620
+ #
621
+ # Examples :-
622
+ #
623
+ # NMatrix.linspace(1,Math::PI, 6)
624
+ # =>[1.0,
625
+ # 1.4283185005187988,
626
+ # 1.8566370010375977,
627
+ # 2.2849555015563965,
628
+ # 2.7132740020751953,
629
+ # 3.1415927410125732
630
+ # ]
631
+ #
632
+ # NMatrix.linspace(1,10, [3,2])
633
+ # =>[
634
+ # [ 1.0, 2.799999952316284]
635
+ # [4.599999904632568, 6.400000095367432]
636
+ # [8.199999809265137, 10.0]
637
+ # ]
638
+ #
639
+ def linspace(base, limit, shape = [100])
640
+
641
+ # Convert shape to array format
642
+ shape = [shape] if shape.is_a? Integer
643
+
644
+ #Calculate number of elements
645
+ count = shape.inject(:*)
646
+
647
+ # Linear spacing between elements calculated in step
648
+ # step = limit - base / (count - 1)
649
+ # [Result Sequence] = [0->N sequence] * step + [Base]
650
+ step = (limit - base) * (1.0 / (count - 1))
651
+ result = NMatrix.seq(shape, {:dtype => :float64}) * step
652
+ result += NMatrix.new(shape, base)
653
+ result
654
+ end
655
+
656
+ # call-seq:
657
+ # logspace(base, limit) -> 1x50 NMatrix with exponent_base = 10
658
+ # logspace(base, limit, shape , exponent_base:) -> NMatrix
659
+ # logspace(base, :pi, n) -> 1xn NMatrix with interval [10 ^ base, Math::PI]
660
+ #
661
+ # Returns an NMatrix with +[shape[0] x shape[1] x .. x shape[dim-1]]+ values of dtype +:float64+ logarithmically spaced from
662
+ # +exponent_base ^ base+ to +exponent_base ^ limit+, inclusive.
663
+ #
664
+ # See: http://www.mathworks.com/help/matlab/ref/logspace.html
665
+ #
666
+ # * *Arguments* :
667
+ # - +base+ -> exponent_base ** base is the first value in the sequence
668
+ # - +limit+ -> exponent_base ** limit is the last value in the sequence.
669
+ # - +shape+ -> Desired output shape. Default returns a 1x50 row vector.
670
+ # * *Returns* :
671
+ # - NMatrix with +:float64+ values.
672
+ #
673
+ # Examples :-
674
+ #
675
+ # NMatrix.logspace(1,:pi,7)
676
+ # =>[
677
+ # 10.0000,
678
+ # 8.2450,
679
+ # 6.7980,
680
+ # 5.6050,
681
+ # 4.6213,
682
+ # 3.8103,
683
+ # 3.1416
684
+ # ]
685
+ #
686
+ # NMatrix.logspace(1,2,[3,2])
687
+ # =>[
688
+ # [10.0, 15.8489]
689
+ # [25.1189, 39.8107]
690
+ # [63.0957, 100.0]
691
+ # ]
692
+ #
693
+ def logspace(base, limit, shape = [50], exponent_base: 10)
694
+
695
+ #Calculate limit for [10 ^ base ... Math::PI] if limit = :pi
696
+ limit = Math.log(Math::PI, exponent_base = 10) if limit == :pi
697
+ shape = [shape] if shape.is_a? Integer
698
+
699
+ #[base...limit] -> [exponent_base ** base ... exponent_base ** limit]
700
+ result = NMatrix.linspace(base, limit, shape)
701
+ result.map {|element| exponent_base ** element}
702
+ end
703
+
704
+ #
705
+ # call-seq:
706
+ # linspace(base, limit) -> 1x100 NMatrix
707
+ # linspace(base, limit, *shape) -> NMatrix
708
+ #
709
+ # Returns an NMatrix with +[shape[0] x shape[1] x .. x shape[dim-1]]+ values of dtype +:float64+ equally spaced from
710
+ # +base+ to +limit+, inclusive.
711
+ #
712
+ # See: http://www.mathworks.com/help/matlab/ref/linspace.html
713
+ #
714
+ # * *Arguments* :
715
+ # - +base+ -> The first value in the sequence.
716
+ # - +limit+ -> The last value in the sequence.
717
+ # - +shape+ -> Desired output shape. Default returns a 1x100 row vector.
718
+ # * *Returns* :
719
+ # - NMatrix with +:float64+ values.
720
+ #
721
+ # Examples :-
722
+ #
723
+ # NMatrix.linspace(1,Math::PI, 6)
724
+ # =>[1.0,
725
+ # 1.4283185005187988,
726
+ # 1.8566370010375977,
727
+ # 2.2849555015563965,
728
+ # 2.7132740020751953,
729
+ # 3.1415927410125732
730
+ # ]
731
+ #
732
+ # NMatrix.linspace(1,10, [3,2])
733
+ # =>[
734
+ # [ 1.0, 2.799999952316284]
735
+ # [4.599999904632568, 6.400000095367432]
736
+ # [8.199999809265137, 10.0]
737
+ # ]
738
+ #
739
+ def linspace(base, limit, shape = [100])
740
+
741
+ # Convert shape to array format
742
+ shape = [shape] if shape.is_a? Integer
743
+
744
+ #Calculate number of elements
745
+ count = shape.inject(:*)
746
+
747
+ # Linear spacing between elements calculated in step
748
+ # step = limit - base / (count - 1)
749
+ # [Result Sequence] = [0->N sequence] * step + [Base]
750
+ step = (limit - base) * (1.0 / (count - 1))
751
+ result = NMatrix.seq(shape, {:dtype => :float64}) * step
752
+ result += NMatrix.new(shape, base)
753
+ result
754
+ end
755
+
756
+ # call-seq:
757
+ # logspace(base, limit) -> 1x50 NMatrix with exponent_base = 10
758
+ # logspace(base, limit, shape , exponent_base:) -> NMatrix
759
+ # logspace(base, :pi, n) -> 1xn NMatrix with interval [10 ^ base, Math::PI]
760
+ #
761
+ # Returns an NMatrix with +[shape[0] x shape[1] x .. x shape[dim-1]]+ values of dtype +:float64+ logarithmically spaced from
762
+ # +exponent_base ^ base+ to +exponent_base ^ limit+, inclusive.
763
+ #
764
+ # See: http://www.mathworks.com/help/matlab/ref/logspace.html
765
+ #
766
+ # * *Arguments* :
767
+ # - +base+ -> exponent_base ** base is the first value in the sequence
768
+ # - +limit+ -> exponent_base ** limit is the last value in the sequence.
769
+ # - +shape+ -> Desired output shape. Default returns a 1x50 row vector.
770
+ # * *Returns* :
771
+ # - NMatrix with +:float64+ values.
772
+ #
773
+ # Examples :-
774
+ #
775
+ # NMatrix.logspace(1,:pi,7)
776
+ # =>[
777
+ # 10.0000,
778
+ # 8.2450,
779
+ # 6.7980,
780
+ # 5.6050,
781
+ # 4.6213,
782
+ # 3.8103,
783
+ # 3.1416
784
+ # ]
785
+ #
786
+ # NMatrix.logspace(1,2,[3,2])
787
+ # =>[
788
+ # [10.0, 15.8489]
789
+ # [25.1189, 39.8107]
790
+ # [63.0957, 100.0]
791
+ # ]
792
+ #
793
+ def logspace(base, limit, shape = [50], exponent_base: 10)
794
+
795
+ #Calculate limit for [10 ^ base ... Math::PI] if limit = :pi
796
+ limit = Math.log(Math::PI, exponent_base = 10) if limit == :pi
797
+ shape = [shape] if shape.is_a? Integer
798
+
799
+ #[base...limit] -> [exponent_base ** base ... exponent_base ** limit]
800
+ result = NMatrix.linspace(base, limit, shape)
801
+ result.map {|element| exponent_base ** element}
802
+ end
388
803
 
389
804
  #
390
805
  # call-seq: