1use nalgebra::{ComplexField, DMatrix, Matrix};
2use nalgebra_sparse::{coo::CooMatrix, csc::CscMatrix, csr::CsrMatrix};
3
4use crate::matrix::rand_util::{collect_seeded, entropy_seed};
5use num_traits::Float;
6use rand_distr::{Gamma, StandardNormal, Uniform};
7use rayon::prelude::*;
8
9pub use crate::matrix::dmatrix_rsvd::nystrom_basis;
10use crate::matrix::traits::*;
11
12pub fn build_columns_par<F>(nrows: usize, ncols: usize, build_col: F) -> DMatrix<f32>
19where
20 F: Fn(usize, &mut [f32]) + Sync + Send,
21{
22 let mut out = DMatrix::<f32>::zeros(nrows, ncols);
23 if nrows > 0 && ncols > 0 {
24 out.as_mut_slice()
25 .par_chunks_mut(nrows)
26 .enumerate()
27 .for_each(|(j, col)| build_col(j, col));
28 }
29 out
30}
31
32pub fn subset_columns<T, D, S, I>(
33 matrix: &Matrix<T, nalgebra::Dyn, D, S>,
34 indices: I,
35) -> anyhow::Result<DMatrix<T>>
36where
37 T: nalgebra::RealField,
38 D: nalgebra::Dim,
39 S: nalgebra::RawStorage<T, nalgebra::Dyn, D>,
40 I: IntoIterator<Item = usize>,
41{
42 let cols = indices
43 .into_iter()
44 .map(|j| matrix.column(j))
45 .collect::<Vec<_>>();
46
47 concatenate_horizontal(&cols)
48}
49
50pub fn subset_rows<T, D, S, I>(
51 matrix: &Matrix<T, D, nalgebra::Dyn, S>,
52 indices: I,
53) -> anyhow::Result<DMatrix<T>>
54where
55 T: nalgebra::RealField,
56 D: nalgebra::Dim,
57 S: nalgebra::RawStorage<T, D, nalgebra::Dyn>,
58 I: IntoIterator<Item = usize>,
59{
60 let rows = indices
61 .into_iter()
62 .map(|j| matrix.row(j))
63 .collect::<Vec<_>>();
64
65 concatenate_vertical(&rows)
66}
67
68pub fn assign_columns<T, D, S, R, I>(
69 source: &Matrix<T, nalgebra::Dyn, D, S>,
70 indices: I,
71 target: &mut Matrix<T, nalgebra::Dyn, D, R>,
72) where
73 T: nalgebra::RealField,
74 D: nalgebra::Dim,
75 S: nalgebra::RawStorage<T, nalgebra::Dyn, D>,
76 R: nalgebra::RawStorageMut<T, nalgebra::Dyn, D>,
77 I: IntoIterator<Item = usize>,
78{
79 indices
80 .into_iter()
81 .zip(source.column_iter())
82 .for_each(|(j, x_j)| {
83 target.column_mut(j).copy_from(&x_j);
84 });
85}
86
87pub fn concatenate_vertical<T, D, S>(
89 matrices: &[Matrix<T, D, nalgebra::Dyn, S>],
90) -> anyhow::Result<DMatrix<T>>
91where
92 T: nalgebra::RealField,
93 D: nalgebra::Dim,
94 S: nalgebra::RawStorage<T, D, nalgebra::Dyn>,
95{
96 if matrices.is_empty() {
97 anyhow::bail!("empty in concatenate_vertical");
98 }
99
100 let ncols = matrices[0].ncols();
101 assert!(
102 matrices.iter().all(|m| m.ncols() == ncols),
103 "should have the same number of columns"
104 );
105
106 let rows = matrices
107 .iter()
108 .flat_map(|m| m.row_iter().map(|row| row.into_owned()))
109 .collect::<Vec<_>>();
110
111 Ok(DMatrix::from_rows(&rows))
112}
113
114pub fn concatenate_horizontal<T, D, S>(
116 matrices: &[Matrix<T, nalgebra::Dyn, D, S>],
117) -> anyhow::Result<DMatrix<T>>
118where
119 T: nalgebra::RealField,
120 D: nalgebra::Dim,
121 S: nalgebra::RawStorage<T, nalgebra::Dyn, D>,
122{
123 if matrices.is_empty() {
124 anyhow::bail!("empty in concatenate_horizontal");
125 }
126
127 let nrows = matrices[0].nrows();
128 assert!(
129 matrices.iter().all(|m| m.nrows() == nrows),
130 "should have the same number of rows"
131 );
132
133 let cols = matrices
134 .iter()
135 .flat_map(|m| m.column_iter().map(|col| col.into_owned()))
136 .collect::<Vec<_>>();
137
138 Ok(DMatrix::from_columns(&cols))
139}
140
141impl<T> AdjustByDivisionOp<nalgebra_sparse::CscMatrix<T>, T> for nalgebra_sparse::CscMatrix<T>
142where
143 T: nalgebra::RealField + Copy + std::iter::Sum<T>,
144{
145 fn adjust_by_division_inplace(&mut self, denom: &nalgebra_sparse::CscMatrix<T>) {
146 self.col_iter_mut()
147 .zip(denom.col_iter())
148 .for_each(|(mut x_j, d_j)| {
149 let dsum = d_j.values().iter().copied().sum::<T>();
150 let xsum = x_j.values().iter().copied().sum::<T>();
151 let scale = if dsum > T::zero() {
152 xsum / dsum
153 } else {
154 T::one()
155 };
156
157 let (x_rows, x_values) = x_j.rows_and_values_mut();
158
159 let mut d_j_values = vec![T::zero(); x_rows.len()];
160
161 x_rows.iter().enumerate().for_each(|(idx, &i)| {
162 if let Some(pos) = d_j.row_indices().iter().position(|&d_i| d_i == i) {
163 d_j_values[idx] = d_j.values()[pos];
164 }
165 });
166
167 x_values
168 .iter_mut()
169 .zip(d_j_values)
170 .for_each(|(x_ij, d_ij)| {
171 if d_ij > T::zero() {
172 *x_ij /= d_ij * scale;
173 }
174 });
175 });
176 }
177
178 fn adjust_by_division_of_selected_inplace(
179 &mut self,
180 denom_db: &nalgebra_sparse::CscMatrix<T>,
181 batches: &[usize],
182 ) {
183 self.col_iter_mut().zip(batches).for_each(|(mut x_j, &b)| {
184 let d_j = denom_db.col(b);
185
186 let dsum = d_j.values().iter().copied().sum::<T>();
187 let xsum = x_j.values().iter().copied().sum::<T>();
188 let scale = if dsum > T::zero() {
189 xsum / dsum
190 } else {
191 T::one()
192 };
193
194 let (x_rows, x_values) = x_j.rows_and_values_mut();
195
196 let mut d_j_values = vec![T::zero(); x_rows.len()];
197
198 x_rows.iter().enumerate().for_each(|(idx, &i)| {
199 if let Some(pos) = d_j.row_indices().iter().position(|&d_i| d_i == i) {
200 d_j_values[idx] = d_j.values()[pos];
201 }
202 });
203
204 x_values
205 .iter_mut()
206 .zip(d_j_values)
207 .for_each(|(x_ij, d_ij)| {
208 if d_ij > T::zero() {
209 *x_ij /= d_ij * scale;
210 }
211 });
212 });
213 }
214}
215
216pub fn adjust_by_poisson_ratio<T, F>(vals: &mut [T], divisor: F)
227where
228 T: nalgebra::RealField + Copy + std::iter::Sum<T>,
229 F: Fn(usize) -> T,
230{
231 let dsum: T = (0..vals.len()).map(&divisor).sum();
232 let xsum: T = vals.iter().copied().sum();
233 let scale = if dsum > T::zero() {
234 xsum / dsum
235 } else {
236 T::one()
237 };
238 for (k, x) in vals.iter_mut().enumerate() {
239 let d = divisor(k);
240 if d > T::zero() {
241 *x /= d * scale;
242 }
243 }
244}
245
246impl<T> AdjustByDivisionOp<nalgebra::DMatrix<T>, T> for nalgebra_sparse::CscMatrix<T>
247where
248 T: nalgebra::RealField + Copy + std::iter::Sum<T>,
249{
250 fn adjust_by_division_of_selected_inplace(
251 &mut self,
252 denom_db: &nalgebra::DMatrix<T>,
253 batches: &[usize],
254 ) {
255 self.col_iter_mut().zip(batches).for_each(|(mut x_j, &b)| {
256 let (rows, vals) = x_j.rows_and_values_mut();
257 adjust_by_poisson_ratio(vals, |k| denom_db[(rows[k], b)]);
258 });
259 }
260
261 fn adjust_by_division_inplace(&mut self, denom: &nalgebra::DMatrix<T>) {
262 self.col_iter_mut()
263 .zip(denom.column_iter())
264 .for_each(|(mut x_j, d_col)| {
265 let (rows, vals) = x_j.rows_and_values_mut();
266 adjust_by_poisson_ratio(vals, |k| d_col[rows[k]]);
267 });
268 }
269}
270
271pub fn row_membership_matrix<T>(row_membership: Vec<usize>) -> anyhow::Result<DMatrix<T>>
276where
277 T: nalgebra::RealField + Copy,
278{
279 let kk = match row_membership.iter().max() {
280 Some(&m) => m + 1,
281 _ => 1,
282 };
283
284 let mut ret_dm = DMatrix::zeros(row_membership.len(), kk);
285 let oneval = T::from_f32(1.).ok_or(anyhow::anyhow!("cannot find 1 value"))?;
286 for (i, k) in row_membership.into_iter().enumerate() {
287 ret_dm[(i, k)] += oneval;
288 }
289
290 Ok(ret_dm)
291}
292
293fn csc_euclidean_distance_on_select_columns<T>(
294 lhs: &CscMatrix<T>,
295 rhs: &CscMatrix<T>,
296 select_columns_in_rhs: Option<&[usize]>,
297) -> anyhow::Result<Vec<(usize, usize, T)>>
298where
299 T: nalgebra::RealField + Copy + std::iter::Sum<T>,
300{
301 let all_rhs = (0..rhs.ncols()).collect::<Vec<_>>();
302 let select_columns_in_rhs = select_columns_in_rhs.unwrap_or(all_rhs.as_slice());
303
304 if select_columns_in_rhs.len() != rhs.ncols() {
305 return Err(anyhow::anyhow!(
306 "found mismatches in `select_columns` maps: lhs -> rhs"
307 ));
308 }
309
310 let mut ret = Vec::with_capacity(select_columns_in_rhs.len() * lhs.ncols());
311
312 for (src_pos, src_col) in lhs.col_iter().enumerate() {
313 for &tgt_pos in select_columns_in_rhs {
314 let tgt_col = rhs.col(tgt_pos);
315
316 let nn = src_col.nrows();
317 let denom = T::from_usize(nn).unwrap_or(T::one());
318
319 let idx_src = src_col.row_indices();
320 let idx_tgt = tgt_col.row_indices();
321 let val_src = src_col.values();
322 let val_tgt = tgt_col.values();
323
324 let mut s: usize = 0;
327 let mut t: usize = 0;
328
329 let tgt_sq_sum = val_tgt.iter().map(|&x| x * x).sum::<T>();
330 let src_sq_sum = val_src.iter().map(|&x| x * x).sum::<T>();
331 let mut overlap = T::zero();
332 while s < idx_src.len() && t < idx_tgt.len() {
333 if idx_src[s] == idx_tgt[t] {
334 overlap += val_src[s] * val_tgt[t];
335 s += 1;
336 t += 1;
337 } else if idx_src[s] < idx_tgt[t] {
338 s += 1;
339 } else {
340 t += 1;
341 }
342 }
343
344 let dist = ((src_sq_sum + tgt_sq_sum - overlap - overlap) / denom).sqrt();
345
346 ret.push((src_pos, tgt_pos, dist));
347 }
348 }
349
350 Ok(ret)
351}
352
353impl<T> DistanceOps for CscMatrix<T>
354where
355 T: nalgebra::RealField + Copy + std::iter::Sum<T>,
356{
357 type Scalar = T;
358 type Other = CscMatrix<T>;
359
360 fn euclidean_distance(
361 &self,
362 other: &Self::Other,
363 ) -> anyhow::Result<Vec<(usize, usize, Self::Scalar)>> {
364 csc_euclidean_distance_on_select_columns(self, other, None)
365 }
366
367 fn euclidean_distance_on_select_columns(
368 &self,
369 other: &Self::Other,
370 select_columns_in_other: &[usize],
371 ) -> anyhow::Result<Vec<(usize, usize, Self::Scalar)>> {
372 csc_euclidean_distance_on_select_columns(self, other, Some(select_columns_in_other))
373 }
374}
375
376impl<T> MeltOps for DMatrix<T>
377where
378 T: nalgebra::RealField + Copy,
379{
380 type Scalar = T;
381 type Mat = Self;
382 fn melt_with_indexes(&self) -> (Vec<Self::Scalar>, Vec<Vec<usize>>) {
383 let nelem = self.nrows() * self.ncols();
384
385 let mut idx: Vec<Vec<usize>> = vec![Vec::with_capacity(nelem), Vec::with_capacity(nelem)];
386 let mut val: Vec<Self::Scalar> = Vec::with_capacity(nelem);
387
388 for c in 0..self.ncols() {
390 for r in 0..self.nrows() {
391 idx[0].push(r);
392 idx[1].push(c);
393 val.push(self[(r, c)]);
394 }
395 }
396
397 (val, idx)
398 }
399
400 fn melt(&self) -> Vec<Self::Scalar> {
401 let nelem = self.len();
402 let mut val: Vec<Self::Scalar> = Vec::with_capacity(nelem);
403 for c in 0..self.ncols() {
405 for r in 0..self.nrows() {
406 val.push(self[(r, c)]);
407 }
408 }
409 val
410 }
411
412 fn melt_many_with_indexes(
413 &self,
414 others: &[&Self],
415 ) -> (Vec<Vec<Self::Scalar>>, Vec<Vec<usize>>) {
416 let nrows = self.nrows();
417 let ncols = self.ncols();
418 let nelem = nrows * ncols;
419 let n_matrices = 1 + others.len();
420
421 let mut values: Vec<Vec<Self::Scalar>> =
423 (0..n_matrices).map(|_| Vec::with_capacity(nelem)).collect();
424 let mut idx: Vec<Vec<usize>> = vec![Vec::with_capacity(nelem), Vec::with_capacity(nelem)];
425
426 for c in 0..ncols {
428 for r in 0..nrows {
429 idx[0].push(r);
430 idx[1].push(c);
431 values[0].push(self[(r, c)]);
432 for (i, other) in others.iter().enumerate() {
433 values[i + 1].push(other[(r, c)]);
434 }
435 }
436 }
437
438 (values, idx)
439 }
440}
441
442impl<T> EncodingOps for DMatrix<T>
443where
444 T: nalgebra::RealField + Float,
445 f32: From<T>,
446{
447 type Scalar = T;
448 type Mat = Self;
449
450 fn positional_embedding_columns(&self, emb_dim: usize) -> anyhow::Result<Self::Mat> {
451 let ncols = self.ncols();
452
453 let ncodes_per_col = emb_dim * 2;
454
455 let mut output = Self::zeros(self.nrows(), ncols * ncodes_per_col);
456
457 for j in 0..ncols {
458 let x_j = self.column(j);
459
460 for i in 0..emb_dim {
461 let power = T::from(2.0 * i as f32 / emb_dim as f32).unwrap();
462 let denom = T::from(10000_f32.powf(f32::from(power))).unwrap();
463
464 let column_data = if i % 2 == 0 {
465 x_j.map(|x_ij| ComplexField::sin(x_ij / denom))
466 } else {
467 x_j.map(|x_ij| ComplexField::cos(x_ij / denom))
468 };
469
470 output
472 .column_mut(j * ncodes_per_col + i)
473 .copy_from(&column_data);
474 }
475 }
476 Ok(output)
477 }
478}
479
480impl<T> CompositeOps for DMatrix<T>
481where
482 T: nalgebra::RealField + Copy,
483{
484 type Scalar = T;
485 type Mat = Self;
486 type Other = CscMatrix<T>;
487
488 fn add_assign_column(&mut self, other: &Self::Other, j: usize) {
491 debug_assert_eq!(self.nrows(), other.nrows());
492 debug_assert_eq!(self.ncols(), other.ncols());
493 if let Some(x_j) = other.get_col(j) {
494 let vals = x_j.values();
495 let rows = x_j.row_indices();
496 for k in 0..vals.len() {
497 let i = rows[k];
498 let x_ij = vals[k];
499 self[(i, j)] += x_ij;
500 }
501 }
502 }
503
504 fn add_assign(&mut self, other: &Self::Other) {
507 debug_assert_eq!(self.nrows(), other.nrows());
508 debug_assert_eq!(self.ncols(), other.ncols());
509 for j in 0..other.ncols() {
510 if let Some(x_j) = other.get_col(j) {
511 let vals = x_j.values();
512 let rows = x_j.row_indices();
513 for k in 0..vals.len() {
514 let i = rows[k];
515 let x_ij = vals[k];
516 self[(i, j)] += x_ij;
517 }
518 }
519 }
520 }
521}
522
523impl<T> ConvertMatOps for DMatrix<T>
524where
525 T: nalgebra::RealField + Copy + candle_core::WithDType,
526{
527 type Mat = Self;
528 type Scalar = T;
529
530 fn from_tensor(tensor: &candle_core::Tensor) -> anyhow::Result<Self::Mat> {
531 if tensor.dims().len() != 2 {
532 return Err(anyhow::anyhow!("expected 2D tensor"));
533 }
534
535 let nrows = tensor.dims()[0];
536 let ncols = tensor.dims()[1];
537 let data: Vec<T> = tensor.flatten_all()?.to_vec1()?;
538 Ok(Self::from_row_iterator(nrows, ncols, data.iter().cloned()))
539 }
540
541 fn to_tensor(&self, dev: &candle_core::Device) -> anyhow::Result<candle_core::Tensor> {
542 use candle_core::Tensor;
543 let nrow = self.nrows();
546 let ncol = self.ncols();
547 Ok(Tensor::from_slice(self.as_slice(), (ncol, nrow), dev)?.transpose(0, 1)?)
548 }
549}
550
551impl<T> ConvertMatOps for CscMatrix<T>
552where
553 T: nalgebra::RealField + Copy + candle_core::WithDType,
554{
555 type Mat = Self;
556 type Scalar = T;
557 fn from_tensor(tensor: &candle_core::Tensor) -> anyhow::Result<Self::Mat> {
558 if tensor.dims().len() != 2 {
559 return Err(anyhow::anyhow!("expected 2D tensor"));
560 }
561
562 let nrows = tensor.dims()[0];
563 let ncols = tensor.dims()[1];
564 let data: Vec<T> = tensor.flatten_all()?.to_vec1()?;
565
566 let dense_matrix = DMatrix::from_row_iterator(nrows, ncols, data.iter().cloned());
567 let csc_matrix = CscMatrix::from(&dense_matrix);
568
569 Ok(csc_matrix)
570 }
571
572 fn to_tensor(&self, dev: &candle_core::Device) -> anyhow::Result<candle_core::Tensor> {
573 use candle_core::Tensor;
574 let dense_matrix = DMatrix::from(self);
575 let nrow = dense_matrix.nrows();
576 let ncol = dense_matrix.ncols();
577 Ok(Tensor::from_slice(dense_matrix.as_slice(), (ncol, nrow), dev)?.transpose(0, 1)?)
580 }
581}
582
583impl<T> SampleOps for DMatrix<T>
584where
585 T: nalgebra::RealField + Float,
586{
587 type Mat = Self;
588 type Scalar = T;
589
590 fn runif(nrow: usize, ncol: usize) -> Self::Mat {
591 Self::runif_seeded(nrow, ncol, entropy_seed())
592 }
593
594 fn rnorm(nrow: usize, ncol: usize) -> Self::Mat {
595 Self::rnorm_seeded(nrow, ncol, entropy_seed())
596 }
597
598 fn rgamma(nrow: usize, ncol: usize, param: (f32, f32)) -> Self::Mat {
599 Self::rgamma_seeded(nrow, ncol, param, entropy_seed())
600 }
601
602 fn runif_seeded(nrow: usize, ncol: usize, seed: u64) -> Self::Mat {
603 let u01 = Uniform::<f32>::new(0., 1.).expect("failed to create uniform distribution");
604 DMatrix::<T>::from_vec(nrow, ncol, collect_seeded(nrow * ncol, u01, seed))
605 }
606
607 fn rnorm_seeded(nrow: usize, ncol: usize, seed: u64) -> Self::Mat {
608 DMatrix::<T>::from_vec(
609 nrow,
610 ncol,
611 collect_seeded(nrow * ncol, StandardNormal, seed),
612 )
613 }
614
615 fn rgamma_seeded(nrow: usize, ncol: usize, param: (f32, f32), seed: u64) -> Self::Mat {
616 let (shape, scale) = param;
617 let pdf = Gamma::new(shape, scale).unwrap();
618 DMatrix::<T>::from_vec(nrow, ncol, collect_seeded(nrow * ncol, pdf, seed))
619 }
620}
621
622impl<T> MatElemOps for CscMatrix<T>
623where
624 T: nalgebra::RealField + Copy,
625{
626 type Mat = Self;
627 type Scalar = T;
628
629 fn log1p_inplace(&mut self) {
630 for x in self.values_mut() {
631 *x = (*x).ln_1p();
632 }
633 }
634
635 fn log1p(&self) -> Self::Mat {
636 let mut ret = self.clone();
637 ret.log1p_inplace();
638 ret
639 }
640}
641
642impl<T> MatOps for CscMatrix<T>
643where
644 T: nalgebra::RealField + Copy,
645{
646 type Mat = Self;
647 type Scalar = T;
648
649 fn normalize_exp_logits_columns_inplace(&mut self) {
650 let ncol = self.ncols();
651
652 for j in 0..ncol {
653 if let Some(log_j) = self.get_col(j) {
654 let mut log_max = log_j.values()[0];
655 for &logx_ij in log_j.values() {
656 log_max = log_max.min(logx_ij);
657 }
658
659 let mut denom = T::zero();
660 for &logx_ij in log_j.values() {
661 denom += (logx_ij - log_max).exp();
662 }
663
664 if let Some(mut log_j) = self.get_col_mut(j) {
665 for x_ij in log_j.values_mut() {
666 *x_ij = (*x_ij - log_max).exp() / denom;
667 }
668 }
669 }
670 }
671 }
672
673 fn normalize_exp_logits_columns(&self) -> Self::Mat {
674 let mut ret = self.clone();
675 ret.normalize_exp_logits_columns_inplace();
676 ret
677 }
678
679 fn log_softmax_columns_inplace(&mut self) {
680 for j in 0..self.ncols() {
681 let lse = {
682 let Some(col) = self.get_col(j) else { continue };
683 let vals = col.values();
684 if vals.is_empty() {
685 continue;
686 }
687 let mut log_max = vals[0];
688 for &v in vals {
689 if v > log_max {
690 log_max = v;
691 }
692 }
693 let mut denom = T::zero();
694 for &v in vals {
695 denom += (v - log_max).exp();
696 }
697 log_max + denom.ln()
698 };
699 if let Some(mut col) = self.get_col_mut(j) {
700 for x in col.values_mut() {
701 *x -= lse;
702 }
703 }
704 }
705 }
706
707 fn log_softmax_columns(&self) -> Self::Mat {
708 let mut ret = self.clone();
709 ret.log_softmax_columns_inplace();
710 ret
711 }
712
713 fn sum_to_one_columns_inplace(&mut self) {
714 let ncol = self.ncols();
715
716 for j in 0..ncol {
717 if let Some(x_j) = self.get_col(j) {
718 let mut denom = T::zero();
719 for &x_ij in x_j.values() {
720 denom += x_ij;
721 }
722 if let Some(mut x_j) = self.get_col_mut(j) {
723 if denom > T::zero() {
724 for x_ij in x_j.values_mut() {
725 *x_ij /= denom;
726 }
727 }
728 }
729 }
730 }
731 }
732
733 fn sum_to_one_columns(&self) -> Self::Mat {
734 let mut ret = self.clone();
735 ret.sum_to_one_columns_inplace();
736 ret
737 }
738
739 fn sum_to_one_rows(&self) -> Self::Mat {
740 let mut ret = self.clone();
741 ret.sum_to_one_rows_inplace();
742 ret
743 }
744
745 fn sum_to_one_rows_inplace(&mut self) {
746 let nrow = self.nrows();
747 let mut denom = vec![T::zero(); nrow];
748
749 for col in self.col_iter() {
750 col.row_indices()
751 .iter()
752 .zip(col.values().iter())
753 .for_each(|(&i, &x_ij)| {
754 denom[i] += x_ij;
755 });
756 }
757
758 for mut col in self.col_iter_mut() {
759 let (row_indices, values) = col.rows_and_values_mut();
760 row_indices.iter().zip(values).for_each(|(&i, x_ij)| {
761 *x_ij /= denom[i];
762 });
763 }
764 }
765
766 fn normalize_columns_inplace(&mut self) {
767 let ncol = self.ncols();
768
769 for j in 0..ncol {
770 if let Some(x_j) = self.get_col(j) {
771 let mut denom = T::zero();
772 for &x_ij in x_j.values() {
773 denom += x_ij * x_ij;
774 }
775 denom = denom.sqrt().max(T::from_f64(1e-8).unwrap());
776
777 if let Some(mut x_j) = self.get_col_mut(j) {
778 for x_ij in x_j.values_mut() {
779 *x_ij /= denom;
780 }
781 }
782 }
783 }
784 }
785
786 fn normalize_columns(&self) -> Self::Mat {
787 let mut ret = self.clone();
788 ret.normalize_columns_inplace();
789 ret
790 }
791
792 fn scale_columns_inplace(&mut self) {
793 let ncol = self.ncols();
794
795 for j in 0..ncol {
796 if let Some(x_j) = self.get_col(j) {
797 let mut s0 = T::zero();
798 let mut s1 = T::zero();
799 let mut s2 = T::zero();
800
801 for &x_ij in x_j.values() {
802 s0 += T::one();
803 s1 += x_ij;
804 s2 += x_ij * x_ij;
805 }
806
807 let mu = s1 / s0.max(T::one());
808 let sig = (s2 / s0.max(T::one()) - mu * mu).sqrt();
809
810 if let Some(mut x_j) = self.get_col_mut(j) {
811 if sig > T::zero() {
812 for x_ij in x_j.values_mut() {
813 *x_ij = (*x_ij - mu) / sig;
814 }
815 } else {
816 for x_ij in x_j.values_mut() {
817 *x_ij -= mu;
818 }
819 }
820 }
821 }
822 }
823 }
824
825 fn scale_rows_inplace(&mut self) {
826 let nrow = self.nrows();
827 let ncol = self.ncols();
828 let mut s0 = vec![T::zero(); nrow];
829 let mut s1 = vec![T::zero(); nrow];
830 let mut s2 = vec![T::zero(); nrow];
831
832 for j in 0..ncol {
833 if let Some(x_j) = self.get_col(j) {
834 for (&x_ij, &i) in x_j.values().iter().zip(x_j.row_indices()) {
835 s0[i] += T::one();
836 s1[i] += x_ij;
837 s2[i] += x_ij * x_ij;
838 }
839 }
840 }
841
842 let mu = s1
843 .into_iter()
844 .zip(s0.iter())
845 .map(|(x, &n)| x / n.max(T::one()))
846 .collect::<Vec<_>>();
847
848 let sig = s2
849 .into_iter()
850 .zip(mu.iter())
851 .zip(s0.iter())
852 .map(|((s2, &mu), &s0)| (s2 / s0.max(T::one()) - mu * mu).sqrt())
853 .collect::<Vec<_>>();
854
855 for j in 0..ncol {
856 if let Some(mut x_j) = self.get_col_mut(j) {
857 let (rows, values) = x_j.rows_and_values_mut();
858
859 for (&i, x_ij) in rows.iter().zip(values) {
860 let mu_i = mu[i];
861 let sig_i = sig[i];
862 if sig_i > T::zero() {
863 *x_ij = (*x_ij - mu_i) / sig_i;
864 } else {
865 *x_ij -= mu_i;
866 }
867 }
868 }
869 }
870 }
871
872 fn scale_columns(&self) -> Self::Mat {
873 let mut ret = self.clone();
874 ret.scale_columns_inplace();
875 ret
876 }
877
878 fn scale_rows(&self) -> Self::Mat {
879 let mut ret = self.clone();
880 ret.scale_rows_inplace();
881 ret
882 }
883
884 fn centre_columns_inplace(&mut self) {
885 let ncol = self.ncols();
886
887 for j in 0..ncol {
888 if let Some(x_j) = self.get_col(j) {
889 let mut s0 = T::zero();
890 let mut s1 = T::zero();
891
892 for &x_ij in x_j.values() {
893 s0 += T::one();
894 s1 += x_ij;
895 }
896
897 let mu = s1 / s0.max(T::one());
898
899 if let Some(mut x_j) = self.get_col_mut(j) {
900 for x_ij in x_j.values_mut() {
901 *x_ij -= mu;
902 }
903 }
904 }
905 }
906 }
907
908 fn centre_columns(&self) -> Self::Mat {
909 let mut ret = self.clone();
910 ret.centre_columns_inplace();
911 ret
912 }
913}
914
915impl<T> MatOps for DMatrix<T>
916where
917 T: nalgebra::RealField,
918{
919 type Mat = Self;
920 type Scalar = T;
921
922 fn normalize_exp_logits_columns_inplace(&mut self) {
923 for mut x_j in self.column_iter_mut() {
924 let log_max = x_j.max();
925 let denom = x_j.map(|l| (l - log_max.clone()).exp()).sum();
926 x_j.iter_mut()
927 .for_each(|l| *l = (l.clone() - log_max.clone()).exp() / denom.clone());
928 }
929 }
930
931 fn normalize_exp_logits_columns(&self) -> Self::Mat {
932 let mut ret = self.clone();
933 ret.normalize_exp_logits_columns_inplace();
934 ret
935 }
936
937 fn log_softmax_columns_inplace(&mut self) {
938 for mut x_j in self.column_iter_mut() {
939 let log_max = x_j.max();
940 let denom = x_j.map(|l| (l - log_max.clone()).exp()).sum();
941 let lse = log_max + denom.ln();
942 x_j.iter_mut().for_each(|l| *l = l.clone() - lse.clone());
943 }
944 }
945
946 fn log_softmax_columns(&self) -> Self::Mat {
947 let mut ret = self.clone();
948 ret.log_softmax_columns_inplace();
949 ret
950 }
951
952 fn sum_to_one_columns_inplace(&mut self) {
953 self.column_iter_mut()
954 .for_each(|mut c| c.unscale_mut(c.sum()));
955 }
956
957 fn sum_to_one_columns(&self) -> Self::Mat {
958 let mut ret = self.clone();
959 ret.sum_to_one_columns_inplace();
960 ret
961 }
962
963 fn sum_to_one_rows_inplace(&mut self) {
964 self.row_iter_mut().for_each(|mut r| r.unscale_mut(r.sum()));
965 }
966
967 fn sum_to_one_rows(&self) -> Self::Mat {
968 let mut ret = self.clone();
969 ret.sum_to_one_rows_inplace();
970 ret
971 }
972
973 fn normalize_columns_inplace(&mut self) {
974 for mut xx_j in self.column_iter_mut() {
975 let denom = xx_j.norm().max(T::from_f64(1e-8).unwrap());
976 xx_j /= denom;
977 }
978 }
979
980 fn normalize_columns(&self) -> Self::Mat {
981 let mut ret = self.clone();
982 ret.normalize_columns_inplace();
983 ret
984 }
985
986 fn scale_columns_inplace(&mut self) {
987 for mut xx_j in self.column_iter_mut() {
988 let mu = xx_j.mean();
989 let sig = xx_j.variance().sqrt();
990 xx_j.add_scalar_mut(-mu);
991 if sig > T::zero() {
992 xx_j /= sig;
993 }
994 }
995 }
996
997 fn scale_rows_inplace(&mut self) {
998 for mut xx_i in self.row_iter_mut() {
999 let mu = xx_i.mean();
1000 let sig = xx_i.variance().sqrt();
1001 xx_i.add_scalar_mut(-mu);
1002 if sig > T::zero() {
1003 xx_i /= sig;
1004 }
1005 }
1006 }
1007
1008 fn scale_columns(&self) -> Self::Mat {
1009 let mut ret = self.clone();
1010 ret.scale_columns_inplace();
1011 ret
1012 }
1013
1014 fn scale_rows(&self) -> Self::Mat {
1015 let mut ret = self.clone();
1016 ret.scale_rows_inplace();
1017 ret
1018 }
1019
1020 fn centre_columns_inplace(&mut self) {
1021 for mut xx_j in self.column_iter_mut() {
1022 let mu = xx_j.mean();
1023 xx_j.add_scalar_mut(-mu);
1024 }
1025 }
1026
1027 fn centre_columns(&self) -> Self::Mat {
1028 let mut ret = self.clone();
1029 ret.centre_columns_inplace();
1030 ret
1031 }
1032}
1033
1034impl<T> TfIdfOps for DMatrix<T>
1057where
1058 T: nalgebra::RealField,
1059{
1060 type Mat = Self;
1061
1062 fn tfidf(&self) -> Self::Mat {
1063 let (nrows, ncols) = self.shape();
1064 let n_docs = T::from_usize(ncols).unwrap();
1065 let mut result = self.clone();
1066
1067 for i in 0..nrows {
1068 let df = (0..ncols).filter(|&j| self[(i, j)] != T::zero()).count();
1070 let df_t = T::from_usize(df).unwrap();
1071 let idf = (n_docs.clone() / (df_t + T::one())).ln();
1073 for j in 0..ncols {
1075 result[(i, j)] = result[(i, j)].clone() * idf.clone();
1076 }
1077 }
1078 result
1079 }
1080
1081 fn tfidf_normalize_columns(&self) -> Self::Mat {
1082 let mut result = self.tfidf();
1083 result.normalize_columns_inplace();
1084 result
1085 }
1086}
1087
1088impl<T> MatTriplets for DMatrix<T>
1093where
1094 T: nalgebra::RealField + Float,
1095{
1096 type Mat = Self;
1097 type Scalar = T;
1098
1099 fn from_nonzero_triplets<I>(
1100 nrow: usize,
1101 ncol: usize,
1102 triplets: &[(I, I, T)],
1103 ) -> anyhow::Result<Self::Mat>
1104 where
1105 I: TryInto<usize> + Copy,
1106 <I as TryInto<usize>>::Error: std::fmt::Debug,
1107 {
1108 let mut data = vec![T::zero(); ncol * nrow];
1109 for &(ii, jj, x_ij) in triplets {
1110 let ii: usize = ii.try_into().expect("failed to convert index ii");
1111 let jj: usize = jj.try_into().expect("failed to convert index jj");
1112 data[ii * ncol + jj] = x_ij;
1113 }
1114 Ok(DMatrix::from_row_slice(nrow, ncol, &data))
1115 }
1116
1117 fn to_nonzero_triplets(&self) -> anyhow::Result<NRowNColTriplets<Self::Scalar>> {
1118 if let Some(eps) = T::from(1e-6) {
1119 let nrow = self.nrows();
1120 let ncol = self.ncols();
1121 let mut triplets = vec![];
1122 for j in 0..ncol {
1123 for i in 0..nrow {
1124 let x_ij = &self[(i, j)];
1125 if x_ij.abs() > eps {
1126 triplets.push((i, j, *x_ij));
1127 }
1128 }
1129 }
1130 Ok(NRowNColTriplets {
1131 nrow,
1132 ncol,
1133 triplets,
1134 })
1135 } else {
1136 anyhow::bail!("eps is not defined")
1137 }
1138 }
1139}
1140
1141impl<T> MatTriplets for CsrMatrix<T>
1142where
1143 T: nalgebra::RealField + Float,
1144{
1145 type Mat = Self;
1146 type Scalar = T;
1147
1148 fn from_nonzero_triplets<I>(
1149 nrow: usize,
1150 ncol: usize,
1151 triplets: &[(I, I, Self::Scalar)],
1152 ) -> anyhow::Result<Self::Mat>
1153 where
1154 I: TryInto<usize> + Copy,
1155 <I as TryInto<usize>>::Error: std::fmt::Debug,
1156 {
1157 let mut coo = CooMatrix::<T>::new(nrow, ncol);
1158 for &(ii, jj, x_ij) in triplets {
1159 let ii: usize = ii.try_into().expect("failed to convert index ii");
1160 let jj: usize = jj.try_into().expect("failed to convert index jj");
1161 coo.push(ii, jj, x_ij);
1162 }
1163 Ok(CsrMatrix::from(&coo))
1164 }
1165 fn to_nonzero_triplets(&self) -> anyhow::Result<NRowNColTriplets<Self::Scalar>> {
1166 if let Some(eps) = T::from(1e-6) {
1167 let nrow = self.nrows();
1168 let ncol = self.ncols();
1169
1170 let mut triplets = Vec::new();
1171
1172 for i in 0..nrow {
1173 if let Some(x_i) = self.get_row(i) {
1174 let cols = x_i.col_indices();
1175 let vals = x_i.values();
1176 for k in 0..cols.len() {
1177 let j = cols[k];
1178 let x_ij = vals[k];
1179 if x_ij > eps {
1180 triplets.push((i, j, x_ij))
1181 };
1182 }
1183 }
1184 }
1185
1186 Ok(NRowNColTriplets {
1187 nrow,
1188 ncol,
1189 triplets,
1190 })
1191 } else {
1192 anyhow::bail!("eps is not defined")
1193 }
1194 }
1195}
1196
1197impl<T> MatTriplets for CscMatrix<T>
1198where
1199 T: nalgebra::RealField + Float,
1200{
1201 type Mat = Self;
1202 type Scalar = T;
1203
1204 fn from_nonzero_triplets<I>(
1205 nrow: usize,
1206 ncol: usize,
1207 triplets: &[(I, I, Self::Scalar)],
1208 ) -> anyhow::Result<Self::Mat>
1209 where
1210 I: TryInto<usize> + Copy,
1211 <I as TryInto<usize>>::Error: std::fmt::Debug,
1212 {
1213 let mut coo = CooMatrix::<T>::new(nrow, ncol);
1214 for &(ii, jj, x_ij) in triplets {
1215 let ii: usize = ii.try_into().expect("failed to convert index ii");
1216 let jj: usize = jj.try_into().expect("failed to convert index jj");
1217 coo.push(ii, jj, x_ij);
1218 }
1219 Ok(CscMatrix::from(&coo))
1220 }
1221
1222 fn to_nonzero_triplets(&self) -> anyhow::Result<NRowNColTriplets<Self::Scalar>> {
1223 if let Some(eps) = T::from(1e-6) {
1224 let nrow = self.nrows();
1225 let ncol = self.ncols();
1226
1227 let mut triplets = Vec::new();
1228 for j in 0..ncol {
1229 if let Some(x_j) = self.get_col(j) {
1230 let rows = x_j.row_indices();
1231 let vals = x_j.values();
1232 for k in 0..rows.len() {
1233 let i = rows[k];
1234 let x_ij = vals[k];
1235 if x_ij > eps {
1236 triplets.push((i, j, x_ij))
1237 }
1238 }
1239 }
1240 }
1241
1242 Ok(NRowNColTriplets {
1243 nrow,
1244 ncol,
1245 triplets,
1246 })
1247 } else {
1248 anyhow::bail!("eps is not defined")
1249 }
1250 }
1251}
1252
1253impl<T> CandleDataLoaderOps for DMatrix<T>
1254where
1255 T: nalgebra::RealField + Copy + candle_core::WithDType,
1256{
1257 type Scalar = T;
1258 type Mat = Self;
1259
1260 fn rows_to_tensor_vec(&self) -> Vec<candle_core::Tensor> {
1265 let mut idx_data = self
1266 .row_iter()
1267 .enumerate()
1268 .par_bridge()
1269 .map(|(i, row)| {
1270 let mut v =
1271 candle_core::Tensor::from_iter(row.iter().copied(), &candle_core::Device::Cpu)
1272 .expect("failed to create tensor");
1273 v = v.reshape((1, row.len())).expect("failed to reshape");
1274 (i, v)
1275 })
1276 .collect::<Vec<_>>();
1277
1278 idx_data.sort_by_key(|(i, _)| *i);
1279 idx_data.into_iter().map(|(_, t)| t).collect()
1280 }
1281
1282 fn data_shape(&self) -> (usize, usize) {
1283 (self.nrows(), self.ncols())
1284 }
1285
1286 fn row_to_f32_vec(&self, i: usize) -> Vec<f32> {
1287 let row_data: Vec<T> = self.row(i).iter().copied().collect();
1288 let t = candle_core::Tensor::from_vec(row_data, self.ncols(), &candle_core::Device::Cpu)
1289 .expect("tensor from row");
1290 t.to_dtype(candle_core::DType::F32)
1291 .expect("to f32")
1292 .to_vec1::<f32>()
1293 .expect("to vec")
1294 }
1295}