Class: Ignis::Sparse::SparseMatrix

Inherits:
Object
  • Object
show all
Defined in:
lib/nvruby/sparse/sparse_matrix.rb

Overview

Sparse matrix representation with multiple format support

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#col_indicesCUDA::Memory? (readonly)

Returns Column indices.

Returns:

  • (CUDA::Memory, nil)

    Column indices



27
28
29
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 27

def col_indices
  @col_indices
end

#device_indexInteger (readonly)

Returns Device index.

Returns:

  • (Integer)

    Device index



20
21
22
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 20

def device_index
  @device_index
end

#dtypeSymbol (readonly)

Returns Data type.

Returns:

  • (Symbol)

    Data type



17
18
19
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 17

def dtype
  @dtype
end

#formatSymbol (readonly)

Returns Sparse format (:csr, :csc, :coo).

Returns:

  • (Symbol)

    Sparse format (:csr, :csc, :coo)



8
9
10
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 8

def format
  @format
end

#nnzInteger (readonly)

Returns Number of non-zero elements.

Returns:

  • (Integer)

    Number of non-zero elements



14
15
16
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 14

def nnz
  @nnz
end

#row_indicesCUDA::Memory? (readonly)

Returns Row indices (for COO).

Returns:

  • (CUDA::Memory, nil)

    Row indices (for COO)



30
31
32
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 30

def row_indices
  @row_indices
end

#row_ptrCUDA::Memory? (readonly)

CSR format: row_ptr, col_indices, values

Returns:

  • (CUDA::Memory, nil)

    Row pointer array



24
25
26
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 24

def row_ptr
  @row_ptr
end

#shapeArray<Integer> (readonly)

Returns Matrix shape [rows, cols].

Returns:

  • (Array<Integer>)

    Matrix shape [rows, cols]



11
12
13
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 11

def shape
  @shape
end

#valuesCUDA::Memory? (readonly)

Returns Values array.

Returns:

  • (CUDA::Memory, nil)

    Values array



33
34
35
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 33

def values
  @values
end

Class Method Details

.coo(values:, row_indices:, col_indices:, shape:, dtype: :float32, device: nil) ⇒ SparseMatrix

Create a sparse matrix in COO format

Parameters:

  • values (Array, NvArray)

    Non-zero values

  • row_indices (Array, NvArray)

    Row indices

  • col_indices (Array, NvArray)

    Column indices

  • shape (Array<Integer>)

    Matrix shape [rows, cols]

  • dtype (Symbol) (defaults to: :float32)

    Data type

  • device (Integer, nil) (defaults to: nil)

    Device index

Returns:



57
58
59
60
61
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 57

def self.coo(values:, row_indices:, col_indices:, shape:, dtype: :float32, device: nil)
  matrix = new(format: :coo, shape: shape, nnz: values.size, dtype: dtype, device: device)
  matrix.send(:initialize_coo, values, row_indices, col_indices)
  matrix
end

.csr(values:, row_ptr:, col_indices:, shape:, dtype: :float32, device: nil) ⇒ SparseMatrix

Create a sparse matrix in CSR format

Parameters:

  • values (Array, NvArray)

    Non-zero values

  • row_ptr (Array, NvArray)

    Row pointer array

  • col_indices (Array, NvArray)

    Column indices

  • shape (Array<Integer>)

    Matrix shape [rows, cols]

  • dtype (Symbol) (defaults to: :float32)

    Data type

  • device (Integer, nil) (defaults to: nil)

    Device index

Returns:



43
44
45
46
47
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 43

def self.csr(values:, row_ptr:, col_indices:, shape:, dtype: :float32, device: nil)
  matrix = new(format: :csr, shape: shape, nnz: values.size, dtype: dtype, device: device)
  matrix.send(:initialize_csr, values, row_ptr, col_indices)
  matrix
end

.from_dense(dense, format: :csr, threshold: 0.0) ⇒ SparseMatrix

Create sparse matrix from dense NvArray

Parameters:

  • dense (NvArray)

    Dense matrix

  • format (Symbol) (defaults to: :csr)

    Output format (:csr or :coo)

  • threshold (Float) (defaults to: 0.0)

    Values below this are treated as zero

Returns:

Raises:

  • (ArgumentError)


82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 82

def self.from_dense(dense, format: :csr, threshold: 0.0)
  raise ArgumentError, "Expected NvArray, got #{dense.class}" unless dense.is_a?(NvArray)
  raise DimensionError, "Expected 2D array, got #{dense.ndim}D" unless dense.ndim == 2

  dense = dense.to_host unless dense.on_host?
  data = dense.flatten

  rows, cols = dense.shape
  row_indices = []
  col_indices_arr = []
  values_arr = []

  data.each_with_index do |val, idx|
    next if val.abs <= threshold

    row_indices << idx / cols
    col_indices_arr << idx % cols
    values_arr << val
  end

  if format == :csr
    # Convert row indices to row pointer
    row_ptr = Array.new(rows + 1, 0)
    row_indices.each { |r| row_ptr[r + 1] += 1 }
    (1..rows).each { |i| row_ptr[i] += row_ptr[i - 1] }

    csr(values: values_arr, row_ptr: row_ptr, col_indices: col_indices_arr,
        shape: [rows, cols], dtype: dense.dtype, device: dense.device_index)
  else
    coo(values: values_arr, row_indices: row_indices, col_indices: col_indices_arr,
        shape: [rows, cols], dtype: dense.dtype, device: dense.device_index)
  end
end

.identity(size, dtype: :float32, device: nil) ⇒ SparseMatrix

Create identity sparse matrix

Parameters:

  • size (Integer)

    Matrix size

  • dtype (Symbol) (defaults to: :float32)

    Data type

  • device (Integer, nil) (defaults to: nil)

    Device index

Returns:



68
69
70
71
72
73
74
75
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 68

def self.identity(size, dtype: :float32, device: nil)
  values = Array.new(size, 1.0)
  row_ptr = (0..size).to_a
  col_indices = (0...size).to_a

  csr(values: values, row_ptr: row_ptr, col_indices: col_indices,
      shape: [size, size], dtype: dtype, device: device)
end

Instance Method Details

#colsInteger

Returns Number of columns.

Returns:

  • (Integer)

    Number of columns



122
123
124
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 122

def cols
  @shape[1]
end

#densityFloat

Density ratio

Returns:

  • (Float)

    Fraction of elements that are non-zero



134
135
136
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 134

def density
  @nnz.to_f / (rows * cols)
end

#free!void

This method returns an undefined value.

Free all memory



216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 216

def free!
  @row_ptr&.free!
  @col_indices&.free!
  @row_indices&.free!
  @values&.free!

  @row_ptr = nil
  @col_indices = nil
  @row_indices = nil
  @values = nil
  @on_device = false
end

#on_device?Boolean

Check if on device

Returns:

  • (Boolean)


169
170
171
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 169

def on_device?
  @on_device
end

#rowsInteger

Returns Number of rows.

Returns:

  • (Integer)

    Number of rows



117
118
119
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 117

def rows
  @shape[0]
end

#sparsityFloat

Sparsity ratio

Returns:

  • (Float)

    Fraction of elements that are zero



128
129
130
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 128

def sparsity
  1.0 - (@nnz.to_f / (rows * cols))
end

#spmv(x, y: nil, alpha: 1.0, beta: 0.0, transpose: false) ⇒ NvArray

Sparse matrix-vector multiplication: y = alpha * A * x + beta * y

Parameters:

  • x (NvArray)

    Input vector

  • y (NvArray, nil) (defaults to: nil)

    Output vector (created if nil)

  • alpha (Float) (defaults to: 1.0)

    Scaling factor for A*x

  • beta (Float) (defaults to: 0.0)

    Scaling factor for y

  • transpose (Boolean) (defaults to: false)

    Transpose A

Returns:

Raises:

  • (ArgumentError)


195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 195

def spmv(x, y: nil, alpha: 1.0, beta: 0.0, transpose: false)
  raise ArgumentError, "Expected NvArray, got #{x.class}" unless x.is_a?(NvArray)

  out_rows = transpose ? cols : rows
  in_cols = transpose ? rows : cols

  raise DimensionError, "Vector size #{x.size} != matrix cols #{in_cols}" unless x.size == in_cols

  to_device unless on_device?
  x = x.to_device unless x.on_device?

  y ||= NvArray.zeros([out_rows], dtype: @dtype, device: @device_index)
  y = y.to_device unless y.on_device?

  execute_spmv(x, y, alpha, beta, transpose)

  y
end

#to_denseNvArray

Convert to dense NvArray

Returns:



175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 175

def to_dense
  result = NvArray.zeros(@shape, dtype: @dtype, device: nil)

  case @format
  when :csr
    expand_csr_to_dense(result)
  when :coo
    expand_coo_to_dense(result)
  end

  result
end

#to_device(device: nil) ⇒ self

Transfer to GPU

Parameters:

  • device (Integer, nil) (defaults to: nil)

    Target device

Returns:

  • (self)


141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 141

def to_device(device: nil)
  target_device = device || @device_index

  case @format
  when :csr
    transfer_csr_to_device(target_device)
  when :coo
    transfer_coo_to_device(target_device)
  end

  @device_index = target_device
  @on_device = true
  self
end

#to_hostself

Transfer data to host

Returns:

  • (self)


158
159
160
161
162
163
164
165
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 158

def to_host
  return self unless on_device?

  # Currently we don't support device-side mutation of sparse structures
  # so the host copy is already up to date. We just free device memory.
  free!
  self
end

#to_sString

Returns:

  • (String)


230
231
232
# File 'lib/nvruby/sparse/sparse_matrix.rb', line 230

def to_s
  "SparseMatrix(shape=#{@shape}, nnz=#{@nnz}, format=#{@format}, density=#{(density * 100).round(2)}%)"
end