rumale-manifold 0.28.0 → 0.28.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '018cc8a819b686d66641f8b66e0ffeb13a7f5351ab58cc7f7b2ee79bfe0d06c6'
4
- data.tar.gz: 8e53624cc56092803808adba9a5614eb7ac451b2b83ad9a5966d1fea9a694cd4
3
+ metadata.gz: 146643ddd999165173dcb3388d01240400288e9093d2d599aeff4348486eb8ce
4
+ data.tar.gz: bc2c0eca2e8acfa5c07a28dd7b13283ba505c69c066ae94139ac0dc4033933e5
5
5
  SHA512:
6
- metadata.gz: 67e828ffa83b84a7bb0cd9c910ea4f696ab94f245afa008ff94caa615cfc4e25ea4b057e52602a4e0953728f8720a70953d295935e13450dbdd44e5ee56e6be4
7
- data.tar.gz: 91d0247aa5e1366f0740cc6a86343591abfc8a555df023983c16794d2d746635bbbe4a5074cb34d8b2e41e2e689aac26632ad46d8271e375201981eb07d1a830
6
+ metadata.gz: 308de32a6c4870c308f8da145a5cd4f6e3558d74457ea0aeb8dbc21c06babbdbb52a97e3dbe6a53b1d01cb622aebad79c69b612737023fbef24ef2a83be422f7
7
+ data.tar.gz: 6abd9ef1208b3eee3265bc23b4d785e5e70bf34bd88cd05cd1df457dfc02dc7757de63407ee338cb77d818ca2e7d238b50d14b97d9fb76a687511366ebcf7988
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rumale/base/estimator'
4
+ require 'rumale/base/transformer'
5
+ require 'rumale/pairwise_metric'
6
+ require 'rumale/validation'
7
+
8
+ module Rumale
9
+ module Manifold
10
+ # HessianEigenmaps is a class that implements Hessian Eigenmaps.
11
+ #
12
+ # @example
13
+ # require 'numo/linalg/autoloader'
14
+ # require 'rumale/manifold/hessian_eigenmaps'
15
+ #
16
+ # hem = Rumale::Manifold::HessianEigenmaps.new(n_components: 2, n_neighbors: 15)
17
+ # z = hem.fit_transform(x)
18
+ #
19
+ # *Reference*
20
+ # - Donoho, D. L., and Grimes, C., "Hessian eigenmaps: Locally linear embedding techniques for high-dimensional data," Proc. Natl. Acad. Sci. USA, vol. 100, no. 10, pp. 5591--5596, 2003.
21
+ class HessianEigenmaps < Rumale::Base::Estimator
22
+ include Rumale::Base::Transformer
23
+
24
+ # Return the data in representation space.
25
+ # @return [Numo::DFloat] (shape: [n_samples, n_components])
26
+ attr_reader :embedding
27
+
28
+ # Create a new transformer with Hessian Eigenmaps.
29
+ #
30
+ # @param n_components [Integer] The number of dimensions on representation space.
31
+ # @param n_neighbors [Integer] The number of nearest neighbors for k-nearest neighbor graph construction.
32
+ # @param reg_param [Float] The reguralization parameter for local gram matrix in transform method.
33
+ def initialize(n_neighbors: 5, n_components: 2, reg_param: 1e-6)
34
+ super()
35
+ @params = {
36
+ n_neighbors: n_neighbors,
37
+ n_components: n_components,
38
+ reg_param: reg_param
39
+ }
40
+ end
41
+
42
+ # Fit the model with given training data.
43
+ #
44
+ # @overload fit(x) -> LocallyLinearEmbedding
45
+ # @param x [Numo::DFloat] (shape: [n_samples, n_features]) The training data to be used for fitting the model.
46
+ # @return [LocallyLinearEmbedding] The learned transformer itself.
47
+ def fit(x, _y = nil) # rubocop:disable Metrics/AbcSize
48
+ raise 'HessianEigenmaps#fit requires Numo::Linalg but that is not loaded' unless enable_linalg?(warning: false)
49
+
50
+ x = Rumale::Validation.check_convert_sample_array(x)
51
+
52
+ n_samples = x.shape[0]
53
+ distance_mat = Rumale::PairwiseMetric.squared_error(x)
54
+ neighbor_ids = neighbor_ids(distance_mat, @params[:n_neighbors], true)
55
+
56
+ tri_n_components = @params[:n_components] * (@params[:n_components] + 1) / 2
57
+ hessian_mat = Numo::DFloat.zeros(n_samples * tri_n_components, n_samples)
58
+ ones = Numo::DFloat.ones(@params[:n_neighbors], 1)
59
+ n_samples.times do |i|
60
+ tan_coords = tangent_coordinates(x[neighbor_ids[i, true], true])
61
+ xi = Numo::DFloat.zeros(@params[:n_neighbors], tri_n_components)
62
+ @params[:n_components].times do |m|
63
+ offset = Array.new(m + 1) { |v| v }.sum
64
+ (@params[:n_components] - m).times do |n|
65
+ xi[true, m * @params[:n_components] - offset + n] = tan_coords[true, m] * tan_coords[true, m + n]
66
+ end
67
+ end
68
+
69
+ xt, = Numo::Linalg.qr(Numo::DFloat.hstack([ones, tan_coords, xi]))
70
+ pii = xt[true, (@params[:n_components] + 1)..-1]
71
+ tri_n_components.times do |j|
72
+ pj_sum = pii[true, j].sum
73
+ normalizer = pj_sum <= 1e-8 ? 1 : 1.fdiv(pj_sum)
74
+ hessian_mat[i * tri_n_components + j, neighbor_ids[i, true]] = pii[true, j] * normalizer
75
+ end
76
+ end
77
+
78
+ kernel_mat = hessian_mat.transpose.dot(hessian_mat)
79
+ _, eig_vecs = Numo::Linalg.eigh(kernel_mat, vals_range: 1...(1 + @params[:n_components]))
80
+
81
+ @embedding = @params[:n_components] == 1 ? eig_vecs[true, 0].dup : eig_vecs.dup
82
+ @x_train = x.dup
83
+
84
+ self
85
+ end
86
+
87
+ # Fit the model with training data, and then transform them with the learned model.
88
+ #
89
+ # @overload fit_transform(x) -> Numo::DFloat
90
+ # @param x [Numo::DFloat] (shape: [n_samples, n_features]) The training data to be used for fitting the model.
91
+ # @return [Numo::DFloat] (shape: [n_samples, n_components]) The transformed data
92
+ def fit_transform(x, _y = nil)
93
+ unless enable_linalg?(warning: false)
94
+ raise 'HessianEigenmaps#fit_transform requires Numo::Linalg but that is not loaded'
95
+ end
96
+
97
+ fit(x)
98
+
99
+ @embedding.dup
100
+ end
101
+
102
+ # Transform the given data with the learned model.
103
+ # For out-of-sample data embedding, the same method as Locally Linear Embedding is used.
104
+ #
105
+ # @param x [Numo::DFloat] (shape: [n_samples, n_features]) The data to be transformed with the learned model.
106
+ # @return [Numo::DFloat] (shape: [n_samples, n_components]) The transformed data.
107
+ def transform(x)
108
+ x = Rumale::Validation.check_convert_sample_array(x)
109
+
110
+ n_samples = x.shape[0]
111
+ tol = @params[:reg_param].fdiv(@params[:n_neighbors])
112
+ distance_mat = Rumale::PairwiseMetric.squared_error(x, @x_train)
113
+ neighbor_ids = neighbor_ids(distance_mat, @params[:n_neighbors], false)
114
+ weight_mat = Numo::DFloat.zeros(n_samples, @x_train.shape[0])
115
+
116
+ n_samples.times do |n|
117
+ x_local = @x_train[neighbor_ids[n, true], true] - x[n, true]
118
+ gram_mat = x_local.dot(x_local.transpose)
119
+ gram_mat += tol * weight_mat.trace * Numo::DFloat.eye(@params[:n_neighbors])
120
+ weights = Numo::Linalg.solve(gram_mat, Numo::DFloat.ones(@params[:n_neighbors]))
121
+ weights /= weights.sum + 1e-8
122
+ weight_mat[n, neighbor_ids[n, true]] = weights
123
+ end
124
+
125
+ weight_mat.dot(@embedding)
126
+ end
127
+
128
+ private
129
+
130
+ def neighbor_ids(distance_mat, n_neighbors, contain_self)
131
+ n_samples = distance_mat.shape[0]
132
+ neighbor_ids = Numo::Int32.zeros(n_samples, n_neighbors)
133
+ if contain_self
134
+ n_samples.times { |n| neighbor_ids[n, true] = (distance_mat[n, true].sort_index.to_a - [n])[0...n_neighbors] }
135
+ else
136
+ n_samples.times { |n| neighbor_ids[n, true] = distance_mat[n, true].sort_index.to_a[0...n_neighbors] }
137
+ end
138
+ neighbor_ids
139
+ end
140
+
141
+ def tangent_coordinates(x)
142
+ m = x.mean(axis: 0)
143
+ cx = x - m
144
+ cov_mat = cx.transpose.dot(cx)
145
+ n_features = x.shape[1]
146
+ _, evecs = Numo::Linalg.eigh(cov_mat, vals_range: (n_features - @params[:n_components])...n_features)
147
+ cx.dot(evecs.reverse(1))
148
+ end
149
+ end
150
+ end
151
+ end
@@ -5,6 +5,6 @@ module Rumale
5
5
  # Module for data embedding algorithms.
6
6
  module Manifold
7
7
  # @!visibility private
8
- VERSION = '0.28.0'
8
+ VERSION = '0.28.1'
9
9
  end
10
10
  end
@@ -4,6 +4,7 @@ require 'numo/narray'
4
4
 
5
5
  require_relative 'manifold/laplacian_eigenmaps'
6
6
  require_relative 'manifold/locally_linear_embedding'
7
+ require_relative 'manifold/hessian_eigenmaps'
7
8
  require_relative 'manifold/mds'
8
9
  require_relative 'manifold/tsne'
9
10
  require_relative 'manifold/version'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rumale-manifold
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.28.0
4
+ version: 0.28.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - yoshoku
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-11-12 00:00:00.000000000 Z
11
+ date: 2023-12-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: numo-narray
@@ -30,31 +30,32 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.28.0
33
+ version: 0.28.1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.28.0
40
+ version: 0.28.1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rumale-decomposition
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.28.0
47
+ version: 0.28.1
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.28.0
54
+ version: 0.28.1
55
55
  description: |
56
56
  Rumale::Manifold provides data embedding algorithms,
57
- such as Multi-dimensional Scaling and t-distributed Stochastic Neighbor Embedding,
57
+ such as Multi-dimensional Scaling, Locally Linear Embedding, Laplacian Eigenmaps, Hessian Eigenmaps,
58
+ and t-distributed Stochastic Neighbor Embedding,
58
59
  with Rumale interface.
59
60
  email:
60
61
  - yoshoku@outlook.com
@@ -65,6 +66,7 @@ files:
65
66
  - LICENSE.txt
66
67
  - README.md
67
68
  - lib/rumale/manifold.rb
69
+ - lib/rumale/manifold/hessian_eigenmaps.rb
68
70
  - lib/rumale/manifold/laplacian_eigenmaps.rb
69
71
  - lib/rumale/manifold/locally_linear_embedding.rb
70
72
  - lib/rumale/manifold/mds.rb
@@ -93,7 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
93
95
  - !ruby/object:Gem::Version
94
96
  version: '0'
95
97
  requirements: []
96
- rubygems_version: 3.4.20
98
+ rubygems_version: 3.4.22
97
99
  signing_key:
98
100
  specification_version: 4
99
101
  summary: Rumale::Manifold provides data embedding algorithms with Rumale interface.