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
523#[cfg(feature = "tensor")]
524impl<T> ConvertMatOps for DMatrix<T>
525where
526 T: nalgebra::RealField + Copy + candle_core::WithDType,
527{
528 type Mat = Self;
529 type Scalar = T;
530
531 fn from_tensor(tensor: &candle_core::Tensor) -> anyhow::Result<Self::Mat> {
532 if tensor.dims().len() != 2 {
533 return Err(anyhow::anyhow!("expected 2D tensor"));
534 }
535
536 let nrows = tensor.dims()[0];
537 let ncols = tensor.dims()[1];
538 let data: Vec<T> = tensor.flatten_all()?.to_vec1()?;
539 Ok(Self::from_row_iterator(nrows, ncols, data.iter().cloned()))
540 }
541
542 fn to_tensor(&self, dev: &candle_core::Device) -> anyhow::Result<candle_core::Tensor> {
543 use candle_core::Tensor;
544 let nrow = self.nrows();
547 let ncol = self.ncols();
548 Ok(Tensor::from_slice(self.as_slice(), (ncol, nrow), dev)?.transpose(0, 1)?)
549 }
550}
551
552#[cfg(feature = "tensor")]
553impl<T> ConvertMatOps for CscMatrix<T>
554where
555 T: nalgebra::RealField + Copy + candle_core::WithDType,
556{
557 type Mat = Self;
558 type Scalar = T;
559 fn from_tensor(tensor: &candle_core::Tensor) -> anyhow::Result<Self::Mat> {
560 if tensor.dims().len() != 2 {
561 return Err(anyhow::anyhow!("expected 2D tensor"));
562 }
563
564 let nrows = tensor.dims()[0];
565 let ncols = tensor.dims()[1];
566 let data: Vec<T> = tensor.flatten_all()?.to_vec1()?;
567
568 let dense_matrix = DMatrix::from_row_iterator(nrows, ncols, data.iter().cloned());
569 let csc_matrix = CscMatrix::from(&dense_matrix);
570
571 Ok(csc_matrix)
572 }
573
574 fn to_tensor(&self, dev: &candle_core::Device) -> anyhow::Result<candle_core::Tensor> {
575 use candle_core::Tensor;
576 let dense_matrix = DMatrix::from(self);
577 let nrow = dense_matrix.nrows();
578 let ncol = dense_matrix.ncols();
579 Ok(Tensor::from_slice(dense_matrix.as_slice(), (ncol, nrow), dev)?.transpose(0, 1)?)
582 }
583}
584
585impl<T> SampleOps for DMatrix<T>
586where
587 T: nalgebra::RealField + Float,
588{
589 type Mat = Self;
590 type Scalar = T;
591
592 fn runif(nrow: usize, ncol: usize) -> Self::Mat {
593 Self::runif_seeded(nrow, ncol, entropy_seed())
594 }
595
596 fn rnorm(nrow: usize, ncol: usize) -> Self::Mat {
597 Self::rnorm_seeded(nrow, ncol, entropy_seed())
598 }
599
600 fn rgamma(nrow: usize, ncol: usize, param: (f32, f32)) -> Self::Mat {
601 Self::rgamma_seeded(nrow, ncol, param, entropy_seed())
602 }
603
604 fn runif_seeded(nrow: usize, ncol: usize, seed: u64) -> Self::Mat {
605 let u01 = Uniform::<f32>::new(0., 1.).expect("failed to create uniform distribution");
606 DMatrix::<T>::from_vec(nrow, ncol, collect_seeded(nrow * ncol, u01, seed))
607 }
608
609 fn rnorm_seeded(nrow: usize, ncol: usize, seed: u64) -> Self::Mat {
610 DMatrix::<T>::from_vec(
611 nrow,
612 ncol,
613 collect_seeded(nrow * ncol, StandardNormal, seed),
614 )
615 }
616
617 fn rgamma_seeded(nrow: usize, ncol: usize, param: (f32, f32), seed: u64) -> Self::Mat {
618 let (shape, scale) = param;
619 let pdf = Gamma::new(shape, scale).unwrap();
620 DMatrix::<T>::from_vec(nrow, ncol, collect_seeded(nrow * ncol, pdf, seed))
621 }
622}
623
624impl<T> MatElemOps for CscMatrix<T>
625where
626 T: nalgebra::RealField + Copy,
627{
628 type Mat = Self;
629 type Scalar = T;
630
631 fn log1p_inplace(&mut self) {
632 for x in self.values_mut() {
633 *x = (*x).ln_1p();
634 }
635 }
636
637 fn log1p(&self) -> Self::Mat {
638 let mut ret = self.clone();
639 ret.log1p_inplace();
640 ret
641 }
642}
643
644impl<T> MatOps for CscMatrix<T>
645where
646 T: nalgebra::RealField + Copy,
647{
648 type Mat = Self;
649 type Scalar = T;
650
651 fn normalize_exp_logits_columns_inplace(&mut self) {
652 let ncol = self.ncols();
653
654 for j in 0..ncol {
655 if let Some(log_j) = self.get_col(j) {
656 let mut log_max = log_j.values()[0];
657 for &logx_ij in log_j.values() {
658 log_max = log_max.min(logx_ij);
659 }
660
661 let mut denom = T::zero();
662 for &logx_ij in log_j.values() {
663 denom += (logx_ij - log_max).exp();
664 }
665
666 if let Some(mut log_j) = self.get_col_mut(j) {
667 for x_ij in log_j.values_mut() {
668 *x_ij = (*x_ij - log_max).exp() / denom;
669 }
670 }
671 }
672 }
673 }
674
675 fn normalize_exp_logits_columns(&self) -> Self::Mat {
676 let mut ret = self.clone();
677 ret.normalize_exp_logits_columns_inplace();
678 ret
679 }
680
681 fn log_softmax_columns_inplace(&mut self) {
682 for j in 0..self.ncols() {
683 let lse = {
684 let Some(col) = self.get_col(j) else { continue };
685 let vals = col.values();
686 if vals.is_empty() {
687 continue;
688 }
689 let mut log_max = vals[0];
690 for &v in vals {
691 if v > log_max {
692 log_max = v;
693 }
694 }
695 let mut denom = T::zero();
696 for &v in vals {
697 denom += (v - log_max).exp();
698 }
699 log_max + denom.ln()
700 };
701 if let Some(mut col) = self.get_col_mut(j) {
702 for x in col.values_mut() {
703 *x -= lse;
704 }
705 }
706 }
707 }
708
709 fn log_softmax_columns(&self) -> Self::Mat {
710 let mut ret = self.clone();
711 ret.log_softmax_columns_inplace();
712 ret
713 }
714
715 fn sum_to_one_columns_inplace(&mut self) {
716 let ncol = self.ncols();
717
718 for j in 0..ncol {
719 if let Some(x_j) = self.get_col(j) {
720 let mut denom = T::zero();
721 for &x_ij in x_j.values() {
722 denom += x_ij;
723 }
724 if let Some(mut x_j) = self.get_col_mut(j) {
725 if denom > T::zero() {
726 for x_ij in x_j.values_mut() {
727 *x_ij /= denom;
728 }
729 }
730 }
731 }
732 }
733 }
734
735 fn sum_to_one_columns(&self) -> Self::Mat {
736 let mut ret = self.clone();
737 ret.sum_to_one_columns_inplace();
738 ret
739 }
740
741 fn sum_to_one_rows(&self) -> Self::Mat {
742 let mut ret = self.clone();
743 ret.sum_to_one_rows_inplace();
744 ret
745 }
746
747 fn sum_to_one_rows_inplace(&mut self) {
748 let nrow = self.nrows();
749 let mut denom = vec![T::zero(); nrow];
750
751 for col in self.col_iter() {
752 col.row_indices()
753 .iter()
754 .zip(col.values().iter())
755 .for_each(|(&i, &x_ij)| {
756 denom[i] += x_ij;
757 });
758 }
759
760 for mut col in self.col_iter_mut() {
761 let (row_indices, values) = col.rows_and_values_mut();
762 row_indices.iter().zip(values).for_each(|(&i, x_ij)| {
763 *x_ij /= denom[i];
764 });
765 }
766 }
767
768 fn normalize_columns_inplace(&mut self) {
769 let ncol = self.ncols();
770
771 for j in 0..ncol {
772 if let Some(x_j) = self.get_col(j) {
773 let mut denom = T::zero();
774 for &x_ij in x_j.values() {
775 denom += x_ij * x_ij;
776 }
777 denom = denom.sqrt().max(T::from_f64(1e-8).unwrap());
778
779 if let Some(mut x_j) = self.get_col_mut(j) {
780 for x_ij in x_j.values_mut() {
781 *x_ij /= denom;
782 }
783 }
784 }
785 }
786 }
787
788 fn normalize_columns(&self) -> Self::Mat {
789 let mut ret = self.clone();
790 ret.normalize_columns_inplace();
791 ret
792 }
793
794 fn scale_columns_inplace(&mut self) {
795 let ncol = self.ncols();
796
797 for j in 0..ncol {
798 if let Some(x_j) = self.get_col(j) {
799 let mut s0 = T::zero();
800 let mut s1 = T::zero();
801 let mut s2 = T::zero();
802
803 for &x_ij in x_j.values() {
804 s0 += T::one();
805 s1 += x_ij;
806 s2 += x_ij * x_ij;
807 }
808
809 let mu = s1 / s0.max(T::one());
810 let sig = (s2 / s0.max(T::one()) - mu * mu).sqrt();
811
812 if let Some(mut x_j) = self.get_col_mut(j) {
813 if sig > T::zero() {
814 for x_ij in x_j.values_mut() {
815 *x_ij = (*x_ij - mu) / sig;
816 }
817 } else {
818 for x_ij in x_j.values_mut() {
819 *x_ij -= mu;
820 }
821 }
822 }
823 }
824 }
825 }
826
827 fn scale_rows_inplace(&mut self) {
828 let nrow = self.nrows();
829 let ncol = self.ncols();
830 let mut s0 = vec![T::zero(); nrow];
831 let mut s1 = vec![T::zero(); nrow];
832 let mut s2 = vec![T::zero(); nrow];
833
834 for j in 0..ncol {
835 if let Some(x_j) = self.get_col(j) {
836 for (&x_ij, &i) in x_j.values().iter().zip(x_j.row_indices()) {
837 s0[i] += T::one();
838 s1[i] += x_ij;
839 s2[i] += x_ij * x_ij;
840 }
841 }
842 }
843
844 let mu = s1
845 .into_iter()
846 .zip(s0.iter())
847 .map(|(x, &n)| x / n.max(T::one()))
848 .collect::<Vec<_>>();
849
850 let sig = s2
851 .into_iter()
852 .zip(mu.iter())
853 .zip(s0.iter())
854 .map(|((s2, &mu), &s0)| (s2 / s0.max(T::one()) - mu * mu).sqrt())
855 .collect::<Vec<_>>();
856
857 for j in 0..ncol {
858 if let Some(mut x_j) = self.get_col_mut(j) {
859 let (rows, values) = x_j.rows_and_values_mut();
860
861 for (&i, x_ij) in rows.iter().zip(values) {
862 let mu_i = mu[i];
863 let sig_i = sig[i];
864 if sig_i > T::zero() {
865 *x_ij = (*x_ij - mu_i) / sig_i;
866 } else {
867 *x_ij -= mu_i;
868 }
869 }
870 }
871 }
872 }
873
874 fn scale_columns(&self) -> Self::Mat {
875 let mut ret = self.clone();
876 ret.scale_columns_inplace();
877 ret
878 }
879
880 fn scale_rows(&self) -> Self::Mat {
881 let mut ret = self.clone();
882 ret.scale_rows_inplace();
883 ret
884 }
885
886 fn centre_columns_inplace(&mut self) {
887 let ncol = self.ncols();
888
889 for j in 0..ncol {
890 if let Some(x_j) = self.get_col(j) {
891 let mut s0 = T::zero();
892 let mut s1 = T::zero();
893
894 for &x_ij in x_j.values() {
895 s0 += T::one();
896 s1 += x_ij;
897 }
898
899 let mu = s1 / s0.max(T::one());
900
901 if let Some(mut x_j) = self.get_col_mut(j) {
902 for x_ij in x_j.values_mut() {
903 *x_ij -= mu;
904 }
905 }
906 }
907 }
908 }
909
910 fn centre_columns(&self) -> Self::Mat {
911 let mut ret = self.clone();
912 ret.centre_columns_inplace();
913 ret
914 }
915}
916
917impl<T> MatOps for DMatrix<T>
918where
919 T: nalgebra::RealField,
920{
921 type Mat = Self;
922 type Scalar = T;
923
924 fn normalize_exp_logits_columns_inplace(&mut self) {
925 for mut x_j in self.column_iter_mut() {
926 let log_max = x_j.max();
927 let denom = x_j.map(|l| (l - log_max.clone()).exp()).sum();
928 x_j.iter_mut()
929 .for_each(|l| *l = (l.clone() - log_max.clone()).exp() / denom.clone());
930 }
931 }
932
933 fn normalize_exp_logits_columns(&self) -> Self::Mat {
934 let mut ret = self.clone();
935 ret.normalize_exp_logits_columns_inplace();
936 ret
937 }
938
939 fn log_softmax_columns_inplace(&mut self) {
940 for mut x_j in self.column_iter_mut() {
941 let log_max = x_j.max();
942 let denom = x_j.map(|l| (l - log_max.clone()).exp()).sum();
943 let lse = log_max + denom.ln();
944 x_j.iter_mut().for_each(|l| *l = l.clone() - lse.clone());
945 }
946 }
947
948 fn log_softmax_columns(&self) -> Self::Mat {
949 let mut ret = self.clone();
950 ret.log_softmax_columns_inplace();
951 ret
952 }
953
954 fn sum_to_one_columns_inplace(&mut self) {
955 self.column_iter_mut()
956 .for_each(|mut c| c.unscale_mut(c.sum()));
957 }
958
959 fn sum_to_one_columns(&self) -> Self::Mat {
960 let mut ret = self.clone();
961 ret.sum_to_one_columns_inplace();
962 ret
963 }
964
965 fn sum_to_one_rows_inplace(&mut self) {
966 self.row_iter_mut().for_each(|mut r| r.unscale_mut(r.sum()));
967 }
968
969 fn sum_to_one_rows(&self) -> Self::Mat {
970 let mut ret = self.clone();
971 ret.sum_to_one_rows_inplace();
972 ret
973 }
974
975 fn normalize_columns_inplace(&mut self) {
976 for mut xx_j in self.column_iter_mut() {
977 let denom = xx_j.norm().max(T::from_f64(1e-8).unwrap());
978 xx_j /= denom;
979 }
980 }
981
982 fn normalize_columns(&self) -> Self::Mat {
983 let mut ret = self.clone();
984 ret.normalize_columns_inplace();
985 ret
986 }
987
988 fn scale_columns_inplace(&mut self) {
989 for mut xx_j in self.column_iter_mut() {
990 let mu = xx_j.mean();
991 let sig = xx_j.variance().sqrt();
992 xx_j.add_scalar_mut(-mu);
993 if sig > T::zero() {
994 xx_j /= sig;
995 }
996 }
997 }
998
999 fn scale_rows_inplace(&mut self) {
1000 for mut xx_i in self.row_iter_mut() {
1001 let mu = xx_i.mean();
1002 let sig = xx_i.variance().sqrt();
1003 xx_i.add_scalar_mut(-mu);
1004 if sig > T::zero() {
1005 xx_i /= sig;
1006 }
1007 }
1008 }
1009
1010 fn scale_columns(&self) -> Self::Mat {
1011 let mut ret = self.clone();
1012 ret.scale_columns_inplace();
1013 ret
1014 }
1015
1016 fn scale_rows(&self) -> Self::Mat {
1017 let mut ret = self.clone();
1018 ret.scale_rows_inplace();
1019 ret
1020 }
1021
1022 fn centre_columns_inplace(&mut self) {
1023 for mut xx_j in self.column_iter_mut() {
1024 let mu = xx_j.mean();
1025 xx_j.add_scalar_mut(-mu);
1026 }
1027 }
1028
1029 fn centre_columns(&self) -> Self::Mat {
1030 let mut ret = self.clone();
1031 ret.centre_columns_inplace();
1032 ret
1033 }
1034}
1035
1036impl<T> TfIdfOps for DMatrix<T>
1059where
1060 T: nalgebra::RealField,
1061{
1062 type Mat = Self;
1063
1064 fn tfidf(&self) -> Self::Mat {
1065 let (nrows, ncols) = self.shape();
1066 let n_docs = T::from_usize(ncols).unwrap();
1067 let mut result = self.clone();
1068
1069 for i in 0..nrows {
1070 let df = (0..ncols).filter(|&j| self[(i, j)] != T::zero()).count();
1072 let df_t = T::from_usize(df).unwrap();
1073 let idf = (n_docs.clone() / (df_t + T::one())).ln();
1075 for j in 0..ncols {
1077 result[(i, j)] = result[(i, j)].clone() * idf.clone();
1078 }
1079 }
1080 result
1081 }
1082
1083 fn tfidf_normalize_columns(&self) -> Self::Mat {
1084 let mut result = self.tfidf();
1085 result.normalize_columns_inplace();
1086 result
1087 }
1088}
1089
1090impl<T> MatTriplets for DMatrix<T>
1095where
1096 T: nalgebra::RealField + Float,
1097{
1098 type Mat = Self;
1099 type Scalar = T;
1100
1101 fn from_nonzero_triplets<I>(
1102 nrow: usize,
1103 ncol: usize,
1104 triplets: &[(I, I, T)],
1105 ) -> anyhow::Result<Self::Mat>
1106 where
1107 I: TryInto<usize> + Copy,
1108 <I as TryInto<usize>>::Error: std::fmt::Debug,
1109 {
1110 let mut data = vec![T::zero(); ncol * nrow];
1111 for &(ii, jj, x_ij) in triplets {
1112 let ii: usize = ii.try_into().expect("failed to convert index ii");
1113 let jj: usize = jj.try_into().expect("failed to convert index jj");
1114 data[ii * ncol + jj] = x_ij;
1115 }
1116 Ok(DMatrix::from_row_slice(nrow, ncol, &data))
1117 }
1118
1119 fn to_nonzero_triplets(&self) -> anyhow::Result<NRowNColTriplets<Self::Scalar>> {
1120 if let Some(eps) = T::from(1e-6) {
1121 let nrow = self.nrows();
1122 let ncol = self.ncols();
1123 let mut triplets = vec![];
1124 for j in 0..ncol {
1125 for i in 0..nrow {
1126 let x_ij = &self[(i, j)];
1127 if x_ij.abs() > eps {
1128 triplets.push((i, j, *x_ij));
1129 }
1130 }
1131 }
1132 Ok(NRowNColTriplets {
1133 nrow,
1134 ncol,
1135 triplets,
1136 })
1137 } else {
1138 anyhow::bail!("eps is not defined")
1139 }
1140 }
1141}
1142
1143impl<T> MatTriplets for CsrMatrix<T>
1144where
1145 T: nalgebra::RealField + Float,
1146{
1147 type Mat = Self;
1148 type Scalar = T;
1149
1150 fn from_nonzero_triplets<I>(
1151 nrow: usize,
1152 ncol: usize,
1153 triplets: &[(I, I, Self::Scalar)],
1154 ) -> anyhow::Result<Self::Mat>
1155 where
1156 I: TryInto<usize> + Copy,
1157 <I as TryInto<usize>>::Error: std::fmt::Debug,
1158 {
1159 let mut coo = CooMatrix::<T>::new(nrow, ncol);
1160 for &(ii, jj, x_ij) in triplets {
1161 let ii: usize = ii.try_into().expect("failed to convert index ii");
1162 let jj: usize = jj.try_into().expect("failed to convert index jj");
1163 coo.push(ii, jj, x_ij);
1164 }
1165 Ok(CsrMatrix::from(&coo))
1166 }
1167 fn to_nonzero_triplets(&self) -> anyhow::Result<NRowNColTriplets<Self::Scalar>> {
1168 if let Some(eps) = T::from(1e-6) {
1169 let nrow = self.nrows();
1170 let ncol = self.ncols();
1171
1172 let mut triplets = Vec::new();
1173
1174 for i in 0..nrow {
1175 if let Some(x_i) = self.get_row(i) {
1176 let cols = x_i.col_indices();
1177 let vals = x_i.values();
1178 for k in 0..cols.len() {
1179 let j = cols[k];
1180 let x_ij = vals[k];
1181 if x_ij > eps {
1182 triplets.push((i, j, x_ij))
1183 };
1184 }
1185 }
1186 }
1187
1188 Ok(NRowNColTriplets {
1189 nrow,
1190 ncol,
1191 triplets,
1192 })
1193 } else {
1194 anyhow::bail!("eps is not defined")
1195 }
1196 }
1197}
1198
1199impl<T> MatTriplets for CscMatrix<T>
1200where
1201 T: nalgebra::RealField + Float,
1202{
1203 type Mat = Self;
1204 type Scalar = T;
1205
1206 fn from_nonzero_triplets<I>(
1207 nrow: usize,
1208 ncol: usize,
1209 triplets: &[(I, I, Self::Scalar)],
1210 ) -> anyhow::Result<Self::Mat>
1211 where
1212 I: TryInto<usize> + Copy,
1213 <I as TryInto<usize>>::Error: std::fmt::Debug,
1214 {
1215 let mut coo = CooMatrix::<T>::new(nrow, ncol);
1216 for &(ii, jj, x_ij) in triplets {
1217 let ii: usize = ii.try_into().expect("failed to convert index ii");
1218 let jj: usize = jj.try_into().expect("failed to convert index jj");
1219 coo.push(ii, jj, x_ij);
1220 }
1221 Ok(CscMatrix::from(&coo))
1222 }
1223
1224 fn to_nonzero_triplets(&self) -> anyhow::Result<NRowNColTriplets<Self::Scalar>> {
1225 if let Some(eps) = T::from(1e-6) {
1226 let nrow = self.nrows();
1227 let ncol = self.ncols();
1228
1229 let mut triplets = Vec::new();
1230 for j in 0..ncol {
1231 if let Some(x_j) = self.get_col(j) {
1232 let rows = x_j.row_indices();
1233 let vals = x_j.values();
1234 for k in 0..rows.len() {
1235 let i = rows[k];
1236 let x_ij = vals[k];
1237 if x_ij > eps {
1238 triplets.push((i, j, x_ij))
1239 }
1240 }
1241 }
1242 }
1243
1244 Ok(NRowNColTriplets {
1245 nrow,
1246 ncol,
1247 triplets,
1248 })
1249 } else {
1250 anyhow::bail!("eps is not defined")
1251 }
1252 }
1253}
1254
1255#[cfg(feature = "tensor")]
1256impl<T> CandleDataLoaderOps for DMatrix<T>
1257where
1258 T: nalgebra::RealField + Copy + candle_core::WithDType,
1259{
1260 type Scalar = T;
1261 type Mat = Self;
1262
1263 fn rows_to_tensor_vec(&self) -> Vec<candle_core::Tensor> {
1268 let mut idx_data = self
1269 .row_iter()
1270 .enumerate()
1271 .par_bridge()
1272 .map(|(i, row)| {
1273 let mut v =
1274 candle_core::Tensor::from_iter(row.iter().copied(), &candle_core::Device::Cpu)
1275 .expect("failed to create tensor");
1276 v = v.reshape((1, row.len())).expect("failed to reshape");
1277 (i, v)
1278 })
1279 .collect::<Vec<_>>();
1280
1281 idx_data.sort_by_key(|(i, _)| *i);
1282 idx_data.into_iter().map(|(_, t)| t).collect()
1283 }
1284
1285 fn data_shape(&self) -> (usize, usize) {
1286 (self.nrows(), self.ncols())
1287 }
1288
1289 fn row_to_f32_vec(&self, i: usize) -> Vec<f32> {
1290 let row_data: Vec<T> = self.row(i).iter().copied().collect();
1291 let t = candle_core::Tensor::from_vec(row_data, self.ncols(), &candle_core::Device::Cpu)
1292 .expect("tensor from row");
1293 t.to_dtype(candle_core::DType::F32)
1294 .expect("to f32")
1295 .to_vec1::<f32>()
1296 .expect("to vec")
1297 }
1298}