rmds 0.2

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.
@@ -0,0 +1,75 @@
1
+ #
2
+ # RMDS - Ruby Multidimensional Scaling Library
3
+ # Copyright (c) Christoph Heindl, 2010
4
+ # http://github.com/cheind/rmds
5
+ #
6
+
7
+ module MDS
8
+ #
9
+ # Examples for RMDS
10
+ #
11
+ module Examples
12
+
13
+ #
14
+ # Visualization of {MDS::Metric} output.
15
+ #
16
+ # Visually illustrates that MDS finds an embedding
17
+ # that is unique except for any rigid transformation
18
+ # applied to it.
19
+ #
20
+ def Examples.visualization
21
+ require 'mds'
22
+ require 'mds/interfaces/stdlib_interface'
23
+ require 'gnuplot'
24
+
25
+ MDS::Backend.active = MDS::StdlibInterface
26
+
27
+ # Input
28
+ x = MDS::Matrix.create_rows(
29
+ [1.0, 2.0], # Point A
30
+ [4.0, 3.0], # Point B
31
+ [0.0, 1.0] # Point C
32
+ )
33
+
34
+ d2 = MDS::Metric.squared_distances(x)
35
+
36
+ # Embedding in R2
37
+ proj = MDS::Metric.projectd(d2, 2)
38
+
39
+ Gnuplot.open do |gp|
40
+ Gnuplot::Plot.new( gp ) do |plot|
41
+
42
+ # Uncomment the following lines to write result to image.
43
+ # plot.term 'png size'
44
+ # plot.output 'visualization.png'
45
+
46
+ plot.title "Original Input and Result of MDS in Two Dimensions"
47
+ plot.xrange "[-5:5]"
48
+ plot.yrange "[-5:5]"
49
+
50
+ plot.data << Gnuplot::DataSet.new(x.columns) do |ds|
51
+ ds.with = "points"
52
+ ds.title = "Input"
53
+ end
54
+
55
+ plot.data << Gnuplot::DataSet.new(proj.columns) do |ds|
56
+ ds.with = "points"
57
+ ds.title = "Output"
58
+ end
59
+
60
+ end
61
+ end
62
+
63
+
64
+ end
65
+ end
66
+
67
+ end
68
+
69
+ if __FILE__ == $0
70
+ require 'rubygems'
71
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
72
+ MDS::Examples.visualization
73
+ end
74
+
75
+
@@ -0,0 +1,84 @@
1
+ #
2
+ # RMDS - Ruby Multidimensional Scaling Library
3
+ # Copyright (c) Christoph Heindl, 2010
4
+ # http://github.com/cheind/rmds
5
+ #
6
+
7
+ module MDS
8
+ #
9
+ # Examples for RMDS
10
+ #
11
+ module Examples
12
+
13
+ #
14
+ # Visualization of air distances between major european cities.
15
+ #
16
+ # Compare result with http://bit.ly/bNdf0e but keep in mind that
17
+ #
18
+ # - MDS finds an embedding up to rotation and translation
19
+ # - The input matrix contains air-distances and the map shows correct goedesic distances.
20
+ #
21
+ def Examples.visualization_european_cities
22
+ require 'mds'
23
+ require 'gnuplot' # gem install gnuplot
24
+
25
+ # Load backend
26
+ MDS::Backend.try_require
27
+ MDS::Backend.active = MDS::Backend.first
28
+ puts "Using backend #{MDS::Backend.active}"
29
+
30
+ # Load matrix from file, contains city names in first column
31
+ path = File.join(File.dirname(__FILE__), 'european_city_distances.csv')
32
+
33
+ cities = []
34
+ rows = MDS::IO::read_csv(path, ';') do |entry|
35
+ begin
36
+ f = Float(entry)
37
+ f * f
38
+ rescue ArgumentError
39
+ cities << entry
40
+ nil
41
+ end
42
+ end
43
+
44
+ # Invoke MDS
45
+
46
+ d2 = MDS::Matrix.create_rows(*rows)
47
+ proj = MDS::Metric.projectk(d2, 0.9)
48
+
49
+ # Plot results
50
+
51
+ Gnuplot.open do |gp|
52
+ Gnuplot::Plot.new( gp ) do |plot|
53
+
54
+ # Uncomment the following lines to write result to image.
55
+ # plot.term 'png size'
56
+ # plot.output 'visualization.png'
57
+
58
+ plot.title "Air Distances between European Cities"
59
+ plot.xrange "[-2000:2000]"
60
+ plot.yrange "[-1500:1200]"
61
+
62
+ plot.data << Gnuplot::DataSet.new(proj.columns) do |ds|
63
+ ds.with = "points"
64
+ ds.notitle
65
+ end
66
+
67
+ cities.each_with_index do |name, i|
68
+ plot.label "'#{name}' at #{proj[i,0] + 30}, #{proj[i,1] + 30}"
69
+ end
70
+ end
71
+ end
72
+
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ if __FILE__ == $0
79
+ require 'rubygems'
80
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
81
+ MDS::Examples.visualization_european_cities
82
+ end
83
+
84
+
@@ -0,0 +1,22 @@
1
+ #
2
+ # RMDS - Ruby Multidimensional Scaling Library
3
+ # Copyright (c) Christoph Heindl, 2010
4
+ # http://github.com/cheind/rmds
5
+ #
6
+
7
+ #
8
+ # MDS - Ruby Multidimensional Scaling
9
+ #
10
+ module MDS
11
+ # Current version of MDS
12
+ VERSION = '0.2'
13
+ end
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
16
+
17
+ require 'mds/matrix_interface'
18
+ require 'mds/matrix'
19
+ require 'mds/backend'
20
+ require 'mds/metric'
21
+ require 'mds/io'
22
+
@@ -0,0 +1,105 @@
1
+ #
2
+ # RMDS - Ruby Multidimensional Scaling Library
3
+ # Copyright (c) Christoph Heindl, 2010
4
+ # http://github.com/cheind/rmds
5
+ #
6
+
7
+ module MDS
8
+
9
+ # Defines the preferrred order of interfaces.
10
+ PREFERRED_INTERFACE_ORDER = ['MDS::LinalgInterface', 'MDS::GSLInterface', 'MDS::StdlibInterface']
11
+
12
+ #
13
+ # Provides a common interface to matrix operations.
14
+ #
15
+ class Backend
16
+ # Stack of active matrix interfaces
17
+ @active = []
18
+ # Array of available interface classes
19
+ @available = []
20
+
21
+ class << self;
22
+ #
23
+ # Access the active matrix interface.
24
+ #
25
+ def active
26
+ @active.first;
27
+ end
28
+
29
+ #
30
+ # Set the active matrix interface.
31
+ #
32
+ def active=(i)
33
+ @active.pop
34
+ @active.push(i)
35
+ end
36
+
37
+ #
38
+ # Push matrix interface onto the stack and set it active.
39
+ #
40
+ def push_active(i)
41
+ @active.push(i)
42
+ end
43
+
44
+ #
45
+ # Deactivate active matrix interface by popping it from stack.
46
+ #
47
+ def pop_active
48
+ @active.pop()
49
+ end
50
+
51
+ #
52
+ # Add available interface class.
53
+ #
54
+ # Automatically called when interface is required.
55
+ #
56
+ def add(i)
57
+ @available << i
58
+ end
59
+
60
+ #
61
+ # Return available interface classes.
62
+ #
63
+ def available
64
+ @available
65
+ end
66
+
67
+ #
68
+ # Return the first interface class.
69
+ #
70
+ # If no interface class matches a string in +search_order+,
71
+ # the first available interface is returned.
72
+ #
73
+ def first(search_order = MDS::PREFERRED_INTERFACE_ORDER)
74
+ c = search_order.map do |name|
75
+ begin
76
+ name.split('::').inject(Kernel) do |scope, const_name|
77
+ scope.const_get(const_name)
78
+ end
79
+ rescue NameError
80
+ nil
81
+ end
82
+ end.compact!
83
+
84
+ (!c || c.length == 0) ? @available.first : c.first
85
+ end
86
+
87
+ #
88
+ # Require all interfaces from path and conceal possible errors.
89
+ #
90
+ # @param String path the path or globbing expression to pass to require
91
+ #
92
+ def try_require(path = File.join(File.dirname(__FILE__), 'interfaces', '*interface.rb'))
93
+ Dir.glob(File.expand_path(path)) do |path|
94
+ begin
95
+ require path
96
+ rescue LoadError
97
+ end
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+
104
+ end
105
+ end
@@ -0,0 +1,140 @@
1
+ #
2
+ # RMDS - Ruby Multidimensional Scaling Library
3
+ # Copyright (c) Christoph Heindl, 2010
4
+ # http://github.com/cheind/rmds
5
+ #
6
+
7
+ require 'mds/matrix_interface'
8
+
9
+ begin
10
+ require 'rbgsl'
11
+ rescue LoadError
12
+ warn "\n**Notice: GSLAdapter requires \'rb-gsl\' which was not found."
13
+ warn "You can install the extension from : \n http://rb-gsl.rubyforge.org/ \n"
14
+ raise $!
15
+ end
16
+
17
+ module MDS
18
+
19
+ #
20
+ # Matrix interface for the Gnu Scientific Library.
21
+ #
22
+ # To succesfully use this interface 'rbgsl' bindings for GSL are required.
23
+ # For more information and installation instructions see
24
+ # http://rb-gsl.rubyforge.org/
25
+ #
26
+ # Compatible with 'rb-gsl >= 1.14.3'
27
+ #
28
+ class GSLInterface < MatrixInterface
29
+
30
+ #
31
+ # Create a new matrix with equal elements.
32
+ #
33
+ # @param (see MDS::MatrixInterface#create)
34
+ # @return (see MDS::MatrixInterface#create)
35
+ #
36
+ def GSLInterface.create(n, m, s)
37
+ mat = ::GSL::Matrix.alloc(n, m)
38
+ mat.set_all(s)
39
+ mat
40
+ end
41
+
42
+ #
43
+ # Return the number of matrix rows
44
+ #
45
+ # @param (see MDS::MatrixInterface#nrows)
46
+ # @return (see MDS::MatrixInterface#nrows)
47
+ #
48
+ def GSLInterface.nrows(m)
49
+ m.size1
50
+ end
51
+
52
+ #
53
+ # Return the number of matrix columns
54
+ #
55
+ # @param (see MDS::MatrixInterface#ncols)
56
+ # @return (see MDS::MatrixInterface#ncols)
57
+ #
58
+ def GSLInterface.ncols(m)
59
+ m.size2
60
+ end
61
+
62
+ #
63
+ # Set matrix element.
64
+ #
65
+ # @param (see MDS::MatrixInterface#set)
66
+ #
67
+ def GSLInterface.set(m, i, j, s)
68
+ m[i,j] = s
69
+ end
70
+
71
+ #
72
+ # Get matrix element.
73
+ #
74
+ # @param (see MDS::MatrixInterface#get)
75
+ # @return (see MDS::MatrixInterface#get)
76
+ #
77
+ def GSLInterface.get(m, i, j)
78
+ m[i,j]
79
+ end
80
+
81
+ #
82
+ # Calculate the product of two matrices or
83
+ # the product of a matrix and a scalar.
84
+ #
85
+ # @param (see MDS::MatrixInterface#prod)
86
+ # @return (see MDS::MatrixInterface#prod)
87
+ #
88
+ def GSLInterface.prod(m, n)
89
+ m * n
90
+ end
91
+
92
+ #
93
+ # Transpose a matrix.
94
+ #
95
+ # @param (see MDS::MatrixInterface#t)
96
+ # @return (see MDS::MatrixInterface#t)
97
+ #
98
+ def GSLInterface.t(m)
99
+ m.transpose
100
+ end
101
+
102
+ #
103
+ # Componentwise addition of two matrices.
104
+ #
105
+ # @param (see MDS::MatrixInterface#add)
106
+ # @return (see MDS::MatrixInterface#add)
107
+ #
108
+ def GSLInterface.add(m, n)
109
+ m + n
110
+ end
111
+
112
+ #
113
+ # Componentwise subtraction of two matrices.
114
+ #
115
+ # @param (see MDS::MatrixInterface#sub)
116
+ # @return (see MDS::MatrixInterface#sub)
117
+ #
118
+ def GSLInterface.sub(m, n)
119
+ m - n
120
+ end
121
+
122
+ #
123
+ # Compute the eigen-decomposition of a real symmetric matrix.
124
+ #
125
+ # @param (see MDS::MatrixInterface#ed)
126
+ # @return (see MDS::MatrixInterface#ed)
127
+ #
128
+ def GSLInterface.ed(m)
129
+ eigen_values, eigen_vectors = ::GSL::Eigen::symmv(m)
130
+ ::GSL::Eigen::Symmv::sort(
131
+ eigen_values,
132
+ eigen_vectors,
133
+ ::GSL::Eigen::SORT_VAL_DESC
134
+ )
135
+
136
+ [eigen_values.to_m_diagonal, eigen_vectors]
137
+ end
138
+
139
+ end
140
+ end
@@ -0,0 +1,149 @@
1
+ #
2
+ # RMDS - Ruby Multidimensional Scaling Library
3
+ # Copyright (c) Christoph Heindl, 2010
4
+ # http://github.com/cheind/rmds
5
+ #
6
+
7
+ require 'mds/matrix_interface'
8
+
9
+ begin
10
+ require 'linalg'
11
+ rescue LoadError
12
+ warn "\n**Notice: This matrix interface requires \'Linalg\' which was not found."
13
+ warn "You can install the extension from: \n http://rubyforge.org/projects/linalg/ \n"
14
+ raise $!
15
+ end
16
+
17
+ module MDS
18
+
19
+ #
20
+ # Common matrix interface for Linalg - Ruby Bindings for LAPACK.
21
+ #
22
+ # To succesfully use this interface 'Linalg' bindings for LAPCK/BLAS
23
+ # are required. For more information and installation instructions see
24
+ # http://rubyforge.org/projects/linalg/
25
+ #
26
+ # Compatible with 'Linalg >= 1.0.0'
27
+ #
28
+ class LinalgInterface < MatrixInterface
29
+
30
+ #
31
+ # Create a new matrix with equal elements.
32
+ #
33
+ # @param (see MatrixInterface#create)
34
+ # @return (see MatrixInterface#create)
35
+ #
36
+ def LinalgInterface.create(n, m, s)
37
+ ::Linalg::DMatrix.new(n, m, s)
38
+ end
39
+
40
+ #
41
+ # Return the number of matrix rows
42
+ #
43
+ # @param (see MatrixInterface#nrows)
44
+ # @return (see MatrixInterface#nrows)
45
+ #
46
+ def LinalgInterface.nrows(m)
47
+ m.num_rows
48
+ end
49
+
50
+ #
51
+ # Return the number of matrix columns
52
+ #
53
+ # @param (see MatrixInterface#ncols)
54
+ # @return (see MatrixInterface#ncols)
55
+ #
56
+ def LinalgInterface.ncols(m)
57
+ m.num_columns
58
+ end
59
+
60
+ #
61
+ # Set matrix element.
62
+ #
63
+ # @param (see MatrixInterface#set)
64
+ #
65
+ def LinalgInterface.set(m, i, j, s)
66
+ m[i,j] = s
67
+ end
68
+
69
+ #
70
+ # Get matrix element.
71
+ #
72
+ # @param (see MatrixInterface#get)
73
+ # @return (see MatrixInterface#get)
74
+ #
75
+ def LinalgInterface.get(m, i, j)
76
+ m[i,j]
77
+ end
78
+
79
+ #
80
+ # Calculate the product of two matrices or
81
+ # the product of a matrix and a scalar.
82
+ #
83
+ # @param (see MatrixInterface#prod)
84
+ # @return (see MatrixInterface#prod)
85
+ #
86
+ def LinalgInterface.prod(m, n)
87
+ m * n
88
+ end
89
+
90
+ #
91
+ # Transpose a matrix.
92
+ #
93
+ # @param (see MatrixInterface#t)
94
+ # @return (see MatrixInterface#t)
95
+ #
96
+ def LinalgInterface.t(m)
97
+ m.transpose
98
+ end
99
+
100
+ #
101
+ # Componentwise addition of two matrices.
102
+ #
103
+ # @param (see MatrixInterface#add)
104
+ # @return (see MatrixInterface#add)
105
+ #
106
+ def LinalgInterface.add(m, n)
107
+ m + n
108
+ end
109
+
110
+ #
111
+ # Componentwise subtraction of two matrices.
112
+ #
113
+ # @param (see MatrixInterface#sub)
114
+ # @return (see MatrixInterface#sub)
115
+ #
116
+ def LinalgInterface.sub(m, n)
117
+ m - n
118
+ end
119
+
120
+ #
121
+ # Compute the eigen-decomposition of a real symmetric matrix.
122
+ #
123
+ # @param (see MatrixInterface#ed)
124
+ # @return (see MatrixInterface#ed)
125
+ #
126
+ def LinalgInterface.ed(m)
127
+ evecs, real, img = m.eigensystem
128
+ [Linalg::DMatrix.diagonal(real), evecs]
129
+ end
130
+
131
+ #---------------------------------------
132
+ # Optional
133
+ # Methods having default implementations.
134
+ #---------------------------------------
135
+
136
+ #
137
+ # Calculate minor matrix.
138
+ #
139
+ # @param (see MatrixInterface#minor)
140
+ # @return (see MatrixInterface#minor)
141
+ #
142
+ def LinalgInterface.minor(m, row_range, col_range)
143
+ nrows = (row_range.last - row_range.first) + 1
144
+ ncols = (col_range.last - col_range.first) + 1
145
+ m.minor(row_range.first, col_range.first, nrows, ncols)
146
+ end
147
+
148
+ end
149
+ end