qfall_math/integer/mat_z/
properties.rs

1// Copyright © 2023 Phil Milewski
2//
3// This file is part of qFALL-math.
4//
5// qFALL-math is free software: you can redistribute it and/or modify it under
6// the terms of the Mozilla Public License Version 2.0 as published by the
7// Mozilla Foundation. See <https://mozilla.org/en-US/MPL/2.0/>.
8
9//! This module includes functionality about properties of [`MatZ`] instances.
10
11use super::MatZ;
12use crate::{
13    integer::Z,
14    traits::{MatrixDimensions, MatrixGetEntry},
15};
16use flint_sys::fmpz_mat::{fmpz_mat_is_one, fmpz_mat_is_square, fmpz_mat_is_zero, fmpz_mat_rank};
17
18impl MatZ {
19    /// Checks if a [`MatZ`] is the identity matrix.
20    ///
21    /// Returns `true` if every diagonal entry of the matrix is `1`
22    /// and every other entry is `0`.
23    ///
24    /// # Examples
25    /// ```
26    /// use qfall_math::integer::MatZ;
27    ///
28    /// let value = MatZ::identity(2, 2);
29    /// assert!(value.is_identity());
30    /// ```
31    pub fn is_identity(&self) -> bool {
32        1 == unsafe { fmpz_mat_is_one(&self.matrix) }
33    }
34
35    /// Checks if a [`MatZ`] is a square matrix.
36    ///
37    /// Returns `true` if the number of rows and columns is identical.
38    ///
39    /// # Examples
40    /// ```
41    /// use qfall_math::integer::MatZ;
42    /// use std::str::FromStr;
43    ///
44    /// let value = MatZ::from_str("[[4, 0],[0, 1]]").unwrap();
45    /// assert!(value.is_square());
46    /// ```
47    pub fn is_square(&self) -> bool {
48        1 == unsafe { fmpz_mat_is_square(&self.matrix) }
49    }
50
51    /// Checks if every entry of a [`MatZ`] is `0`.
52    ///
53    /// Returns `true` if every entry of the matrix is `0`.
54    ///
55    /// # Examples
56    /// ```
57    /// use qfall_math::integer::MatZ;
58    /// use std::str::FromStr;
59    ///
60    /// let value = MatZ::from_str("[[0, 0],[0, 0]]").unwrap();
61    /// assert!(value.is_zero());
62    /// ```
63    pub fn is_zero(&self) -> bool {
64        1 == unsafe { fmpz_mat_is_zero(&self.matrix) }
65    }
66
67    /// Checks if a [`MatZ`] is symmetric.
68    ///
69    /// Returns `true` if we have `a_ij == a_ji` for all i,j.
70    ///
71    /// # Examples
72    /// ```
73    /// use qfall_math::integer::MatZ;
74    ///
75    /// let value = MatZ::identity(2,2);
76    /// assert!(value.is_symmetric());
77    /// ```
78    pub fn is_symmetric(&self) -> bool {
79        if !self.is_square() {
80            return false;
81        }
82        for row in 0..self.get_num_rows() {
83            for column in 0..row {
84                if unsafe {
85                    self.get_entry_unchecked(row, column) != self.get_entry_unchecked(column, row)
86                } {
87                    return false;
88                }
89            }
90        }
91        true
92    }
93
94    /// Returns the rank of the matrix.
95    ///
96    /// # Examples
97    /// ```
98    /// use qfall_math::integer::MatZ;
99    /// use std::str::FromStr;
100    ///
101    /// let matrix = MatZ::from_str("[[1, 2, 3],[4, 5, 6]]").unwrap();
102    ///
103    /// let rank = matrix.rank();
104    /// ```
105    pub fn rank(&self) -> Z {
106        Z::from(unsafe { fmpz_mat_rank(&self.matrix) })
107    }
108}
109
110#[cfg(test)]
111mod test_is_identity {
112    use super::MatZ;
113    use std::str::FromStr;
114
115    /// Ensure that is_identity returns `true` for identity matrices.
116    #[test]
117    fn identity_detection() {
118        let ident = MatZ::identity(2, 2);
119        let nosquare = MatZ::from_str("[[1, 0],[0, 1],[0, 0]]").unwrap();
120
121        assert!(ident.is_identity());
122        assert!(nosquare.is_identity());
123        assert!(MatZ::identity(1, 1).is_identity());
124        assert!(MatZ::identity(2, 4).is_identity());
125        assert!(MatZ::identity(4, 4).is_identity());
126    }
127
128    /// Ensure that is_identity returns `false` for non-identity matrices.
129    #[test]
130    fn identity_rejection() {
131        let small = MatZ::from_str("[[0, 0],[2, 0]]").unwrap();
132        let large = MatZ::from_str(&format!("[[1, 0],[0, {}]]", (u128::MAX - 1) / 2 + 2)).unwrap();
133
134        assert!(!(small.is_identity()));
135        assert!(!(large.is_identity()));
136    }
137}
138
139#[cfg(test)]
140mod test_is_zero {
141    use super::MatZ;
142    use std::str::FromStr;
143
144    /// Ensure that is_zero returns `true` for all zero matrices.
145    #[test]
146    fn zero_detection() {
147        let zero_1 = MatZ::from_str("[[0, 0],[0, 0],[0, 0]]").unwrap();
148        let zero_2 = MatZ::from_str("[[0, 0, 0, 0],[0, 0, 0, 0]]").unwrap();
149        let zero_3 = MatZ::from_str("[[0, 0],[0, 0]]").unwrap();
150
151        assert!(zero_1.is_zero());
152        assert!(zero_2.is_zero());
153        assert!(zero_3.is_zero());
154    }
155
156    /// Ensure that is_zero returns `false` for non-zero matrices.
157    #[test]
158    fn zero_rejection() {
159        let small = MatZ::from_str("[[0, 0],[2, 0]]").unwrap();
160        let large = MatZ::from_str(&format!("[[0, 0],[{}, 0]]", (u128::MAX - 1) / 2 + 1)).unwrap();
161
162        assert!(!small.is_zero());
163        assert!(!large.is_zero());
164    }
165}
166
167#[cfg(test)]
168mod test_is_square {
169    use super::MatZ;
170    use std::str::FromStr;
171
172    /// Ensure that is_square returns `true` for square matrices.
173    #[test]
174    fn square_detection() {
175        let square_1 = MatZ::from_str("[[0, 4],[0, 0]]").unwrap();
176        let square_2 = MatZ::from_str("[[0, 6, 4],[0, 0, 1],[4, 6, 1]]").unwrap();
177
178        assert!(square_1.is_square());
179        assert!(square_2.is_square());
180    }
181
182    /// Ensure that is_square returns `false` for non-square matrices.
183    #[test]
184    fn square_rejection() {
185        let small = MatZ::from_str("[[0, 0, 4],[2, 0, 1]]").unwrap();
186        let large =
187            MatZ::from_str(&format!("[[9, 0],[{}, 0],[1, 4]]", (u128::MAX - 1) / 2 + 1)).unwrap();
188
189        assert!(!small.is_square());
190        assert!(!large.is_square());
191    }
192}
193
194#[cfg(test)]
195mod test_is_symmetric {
196    use super::MatZ;
197    use std::str::FromStr;
198
199    /// Ensure that is_symmetric returns `false` for non-symmetric matrices.
200    #[test]
201    fn symmetric_rejection() {
202        let mat_2x3 = MatZ::from_str("[[0, 5, 4],[2, 0, 1]]").unwrap();
203        let mat_2x2 = MatZ::from_str("[[9, 0],[71, 0]]").unwrap();
204
205        assert!(!mat_2x3.is_symmetric());
206        assert!(!mat_2x2.is_symmetric());
207    }
208
209    /// Ensure that is_symmetric returns `true` for symmetric matrices.
210    #[test]
211    fn symmetric_detection() {
212        let mat_2x2 = MatZ::from_str(&format!(
213            "[[{}, {}],[{}, {}]]",
214            u64::MIN,
215            u64::MAX,
216            u64::MAX,
217            i64::MAX
218        ))
219        .unwrap();
220
221        assert!(mat_2x2.is_symmetric());
222    }
223}
224
225#[cfg(test)]
226mod test_rank {
227    use crate::integer::{MatZ, Z};
228    use std::str::FromStr;
229
230    /// Test whether the rank is correctly computed
231    #[test]
232    fn rank_works() {
233        let mat_1 = MatZ::from_str("[[5, 2],[2, 1]]").unwrap();
234        let mat_2 = MatZ::from_str(&format!("[[{}, 0, 2, 8],[0, 1, 5, 7]]", i64::MIN)).unwrap();
235        let mat_3 = MatZ::from_str("[[0],[0]]").unwrap();
236        let mat_4 = MatZ::from_str("[[0, 0],[0, 1]]").unwrap();
237        let mat_5 = MatZ::from_str("[[0, 1],[0, 5]]").unwrap();
238        let mat_6 = MatZ::from_str("[[6, 0, 1],[0, 1, 0],[1, 2, 3]]").unwrap();
239
240        let rank_1 = mat_1.rank();
241        let rank_2 = mat_2.rank();
242        let rank_3 = mat_3.rank();
243        let rank_4 = mat_4.rank();
244        let rank_5 = mat_5.rank();
245        let rank_6 = mat_6.rank();
246
247        assert_eq!(Z::from(2), rank_1);
248        assert_eq!(Z::from(2), rank_2);
249        assert_eq!(Z::ZERO, rank_3);
250        assert_eq!(Z::ONE, rank_4);
251        assert_eq!(Z::ONE, rank_5);
252        assert_eq!(Z::from(3), rank_6);
253    }
254}