nmatrix 0.2.1 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
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: