Skip to main content

faer/perm/
mod.rs

1use crate::Idx;
2use crate::internal_prelude::*;
3use dyn_stack::StackReq;
4use linalg::zip::{Last, Zip};
5use reborrow::*;
6/// swaps the values in the columns `a` and `b`
7///
8/// # panics
9///
10/// panics if `a` and `b` don't have the same number of columns
11///
12/// # example
13///
14/// ```
15/// use faer::{mat, perm};
16/// let mut m = mat![
17/// 	[1.0, 2.0, 3.0], //
18/// 	[4.0, 5.0, 6.0],
19/// 	[7.0, 8.0, 9.0],
20/// 	[10.0, 14.0, 12.0],
21/// ];
22/// let (a, b) = m.two_cols_mut(0, 2);
23/// perm::swap_cols(a, b);
24/// let swapped = mat![
25/// 	[3.0, 2.0, 1.0], //
26/// 	[6.0, 5.0, 4.0],
27/// 	[9.0, 8.0, 7.0],
28/// 	[12.0, 14.0, 10.0],
29/// ];
30/// assert_eq!(m, swapped);
31/// ```
32#[track_caller]
33#[inline(never)]
34pub fn swap_cols<N: Shape, T>(a: ColMut<'_, T, N>, b: ColMut<'_, T, N>) {
35	fn swap<T>() -> impl FnMut(Zip<&mut T, Last<&mut T>>) {
36		|unzip!(a, b)| core::mem::swap(a, b)
37	}
38	zip!(a, b).for_each(swap::<T>());
39}
40/// swaps the values in the rows `a` and `b`
41///
42/// # panics
43///
44/// panics if `a` and `b` don't have the same number of columns
45///
46/// # example
47///
48/// ```
49/// use faer::{mat, perm};
50/// let mut m = mat![
51/// 	[1.0, 2.0, 3.0], //
52/// 	[4.0, 5.0, 6.0],
53/// 	[7.0, 8.0, 9.0],
54/// 	[10.0, 14.0, 12.0],
55/// ];
56/// let (a, b) = m.two_rows_mut(0, 2);
57/// perm::swap_rows(a, b);
58/// let swapped = mat![
59/// 	[7.0, 8.0, 9.0], //
60/// 	[4.0, 5.0, 6.0],
61/// 	[1.0, 2.0, 3.0],
62/// 	[10.0, 14.0, 12.0],
63/// ];
64/// assert_eq!(m, swapped);
65/// ```
66#[track_caller]
67#[inline]
68pub fn swap_rows<N: Shape, T>(a: RowMut<'_, T, N>, b: RowMut<'_, T, N>) {
69	swap_cols(a.transpose_mut(), b.transpose_mut())
70}
71/// swaps the two rows at indices `a` and `b` in the given matrix
72///
73/// # panics
74///
75/// panics if either `a` or `b` is out of bounds
76///
77/// # example
78///
79/// ```
80/// use faer::{mat, perm};
81/// let mut m = mat![
82/// 	[1.0, 2.0, 3.0], //
83/// 	[4.0, 5.0, 6.0],
84/// 	[7.0, 8.0, 9.0],
85/// 	[10.0, 14.0, 12.0],
86/// ];
87/// perm::swap_rows_idx(m.as_mut(), 0, 2);
88/// let swapped = mat![
89/// 	[7.0, 8.0, 9.0], //
90/// 	[4.0, 5.0, 6.0],
91/// 	[1.0, 2.0, 3.0],
92/// 	[10.0, 14.0, 12.0],
93/// ];
94/// assert_eq!(m, swapped);
95/// ```
96#[track_caller]
97#[inline]
98pub fn swap_rows_idx<M: Shape, N: Shape, T>(
99	mat: MatMut<'_, T, M, N>,
100	a: Idx<M>,
101	b: Idx<M>,
102) {
103	if a != b {
104		let (a, b) = mat.two_rows_mut(a, b);
105		swap_rows(a, b);
106	}
107}
108/// swaps the two columns at indices `a` and `b` in the given matrix
109///
110/// # panics
111///
112/// panics if either `a` or `b` is out of bounds
113///
114/// # example
115///
116/// ```
117/// use faer::{mat, perm};
118/// let mut m = mat![
119/// 	[1.0, 2.0, 3.0], //
120/// 	[4.0, 5.0, 6.0],
121/// 	[7.0, 8.0, 9.0],
122/// 	[10.0, 14.0, 12.0],
123/// ];
124/// perm::swap_cols_idx(m.as_mut(), 0, 2);
125/// let swapped = mat![
126/// 	[3.0, 2.0, 1.0], //
127/// 	[6.0, 5.0, 4.0],
128/// 	[9.0, 8.0, 7.0],
129/// 	[12.0, 14.0, 10.0],
130/// ];
131/// assert_eq!(m, swapped);
132/// ```
133#[track_caller]
134#[inline]
135pub fn swap_cols_idx<M: Shape, N: Shape, T>(
136	mat: MatMut<'_, T, M, N>,
137	a: Idx<N>,
138	b: Idx<N>,
139) {
140	if a != b {
141		let (a, b) = mat.two_cols_mut(a, b);
142		swap_cols(a, b);
143	}
144}
145mod permown;
146mod permref;
147/// permutation matrix
148pub type Perm<I, N = usize> = generic::Perm<Own<I, N>>;
149/// immutable permutation matrix view
150pub type PermRef<'a, I, N = usize> = generic::Perm<Ref<'a, I, N>>;
151pub use permown::Own;
152pub use permref::Ref;
153/// generic `Perm` wrapper
154pub mod generic {
155	use core::fmt::Debug;
156	use reborrow::*;
157	/// generic `Perm` wrapper
158	#[derive(Copy, Clone)]
159	#[repr(transparent)]
160	pub struct Perm<Inner>(pub Inner);
161	impl<Inner: Debug> Debug for Perm<Inner> {
162		#[inline(always)]
163		fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
164			self.0.fmt(f)
165		}
166	}
167	impl<Inner> Perm<Inner> {
168		/// wrap by reference
169		#[inline(always)]
170		pub fn from_inner_ref(inner: &Inner) -> &Self {
171			unsafe { &*(inner as *const Inner as *const Self) }
172		}
173
174		/// wrap by mutable reference
175		#[inline(always)]
176		pub fn from_inner_mut(inner: &mut Inner) -> &mut Self {
177			unsafe { &mut *(inner as *mut Inner as *mut Self) }
178		}
179	}
180	impl<Inner> core::ops::Deref for Perm<Inner> {
181		type Target = Inner;
182
183		#[inline(always)]
184		fn deref(&self) -> &Self::Target {
185			&self.0
186		}
187	}
188	impl<Inner> core::ops::DerefMut for Perm<Inner> {
189		#[inline(always)]
190		fn deref_mut(&mut self) -> &mut Self::Target {
191			&mut self.0
192		}
193	}
194	impl<'short, Inner: Reborrow<'short>> Reborrow<'short> for Perm<Inner> {
195		type Target = Perm<Inner::Target>;
196
197		#[inline(always)]
198		fn rb(&'short self) -> Self::Target {
199			Perm(self.0.rb())
200		}
201	}
202	impl<'short, Inner: ReborrowMut<'short>> ReborrowMut<'short> for Perm<Inner> {
203		type Target = Perm<Inner::Target>;
204
205		#[inline(always)]
206		fn rb_mut(&'short mut self) -> Self::Target {
207			Perm(self.0.rb_mut())
208		}
209	}
210	impl<Inner: IntoConst> IntoConst for Perm<Inner> {
211		type Target = Perm<Inner::Target>;
212
213		#[inline(always)]
214		fn into_const(self) -> Self::Target {
215			Perm(self.0.into_const())
216		}
217	}
218}
219use self::linalg::temp_mat_scratch;
220/// computes a permutation of the columns of the source matrix using the given
221/// permutation, and stores the result in the destination matrix
222///
223/// # panics
224///
225/// - panics if the matrices do not have the same shape
226/// - panics if the size of the permutation doesn't match the number of columns
227///   of the matrices
228#[inline]
229#[track_caller]
230pub fn permute_cols<I: Index, T: ComplexField>(
231	dst: MatMut<'_, T>,
232	src: MatRef<'_, T>,
233	perm_indices: PermRef<'_, I>,
234) {
235	Assert!(all(
236		src.nrows() == dst.nrows(),
237		src.ncols() == dst.ncols(),
238		perm_indices.arrays().0.len() == src.ncols(),
239	));
240	permute_rows(
241		dst.transpose_mut(),
242		src.transpose(),
243		perm_indices.canonicalized(),
244	);
245}
246/// computes a permutation of the rows of the source matrix using the given
247/// permutation, and stores the result in the destination matrix
248///
249/// # panics
250///
251/// - panics if the matrices do not have the same shape
252/// - panics if the size of the permutation doesn't match the number of rows of
253///   the matrices
254#[inline]
255#[track_caller]
256pub fn permute_rows<I: Index, T: ComplexField>(
257	dst: MatMut<'_, T>,
258	src: MatRef<'_, T>,
259	perm_indices: PermRef<'_, I>,
260) {
261	#[track_caller]
262	fn implementation<I: Index, T: ComplexField>(
263		dst: MatMut<'_, T>,
264		src: MatRef<'_, T>,
265		perm_indices: PermRef<'_, I>,
266	) {
267		Assert!(all(
268			src.nrows() == dst.nrows(),
269			src.ncols() == dst.ncols(),
270			perm_indices.len() == src.nrows(),
271		));
272		with_dim!(m, src.nrows());
273		with_dim!(n, src.ncols());
274		let mut dst = dst.as_shape_mut(m, n);
275		let src = src.as_shape(m, n);
276		let perm = perm_indices.as_shape(m).bound_arrays().0;
277		if dst.rb().row_stride().unsigned_abs()
278			< dst.rb().col_stride().unsigned_abs()
279		{
280			for j in n.indices() {
281				for i in m.indices() {
282					dst[(i, j)] = src[(perm[i].zx(), j)].copy();
283				}
284			}
285		} else {
286			for i in m.indices() {
287				let src_i = src.row(perm[i].zx());
288				let mut dst_i = dst.rb_mut().row_mut(i);
289				dst_i.copy_from(src_i);
290			}
291		}
292	}
293	implementation(dst, src, perm_indices.canonicalized())
294}
295/// computes the layout of required workspace for applying a row permutation to
296/// a matrix in place
297pub fn permute_rows_in_place_scratch<I: Index, T: ComplexField>(
298	nrows: usize,
299	ncols: usize,
300) -> StackReq {
301	temp_mat_scratch::<T>(nrows, ncols)
302}
303/// computes the layout of required workspace for applying a column permutation
304/// to a matrix in place
305pub fn permute_cols_in_place_scratch<I: Index, T: ComplexField>(
306	nrows: usize,
307	ncols: usize,
308) -> StackReq {
309	temp_mat_scratch::<T>(nrows, ncols)
310}
311/// computes a permutation of the rows of the matrix using the given
312/// permutation, and stores the result in the same matrix
313///
314/// # panics
315///
316/// - panics if the size of the permutation doesn't match the number of rows of
317///   the matrix
318#[inline]
319#[track_caller]
320pub fn permute_rows_in_place<I: Index, T: ComplexField>(
321	matrix: MatMut<'_, T>,
322	perm_indices: PermRef<'_, I>,
323	stack: &mut MemStack,
324) {
325	#[inline]
326	#[track_caller]
327	fn implementation<T: ComplexField, I: Index>(
328		matrix: MatMut<'_, T>,
329		perm_indices: PermRef<'_, I>,
330		stack: &mut MemStack,
331	) {
332		let mut matrix = matrix;
333		let (mut tmp, _) =
334			unsafe { temp_mat_uninit(matrix.nrows(), matrix.ncols(), stack) };
335		let mut tmp = tmp.as_mat_mut();
336		tmp.copy_from(matrix.rb());
337		permute_rows(matrix.rb_mut(), tmp.rb(), perm_indices);
338	}
339	implementation(matrix, perm_indices.canonicalized(), stack)
340}
341/// computes a permutation of the columns of the matrix using the given
342/// permutation, and stores the result in the same matrix.
343///
344/// # panics
345///
346/// - panics if the size of the permutation doesn't match the number of columns
347///   of the matrix
348#[inline]
349#[track_caller]
350pub fn permute_cols_in_place<I: Index, T: ComplexField>(
351	matrix: MatMut<'_, T>,
352	perm_indices: PermRef<'_, I>,
353	stack: &mut MemStack,
354) {
355	#[inline]
356	#[track_caller]
357	fn implementation<I: Index, T: ComplexField>(
358		matrix: MatMut<'_, T>,
359		perm_indices: PermRef<'_, I>,
360		stack: &mut MemStack,
361	) {
362		let mut matrix = matrix;
363		let (mut tmp, _) =
364			unsafe { temp_mat_uninit(matrix.nrows(), matrix.ncols(), stack) };
365		let mut tmp = tmp.as_mat_mut();
366		tmp.copy_from(matrix.rb());
367		permute_cols(matrix.rb_mut(), tmp.rb(), perm_indices);
368	}
369	implementation(matrix, perm_indices.canonicalized(), stack)
370}