problemreductions/models/specialized/
factoring.rs

1//! Integer Factoring problem implementation.
2//!
3//! The Factoring problem represents integer factorization as a computational problem.
4//! Given a number N, find two factors (a, b) such that a * b = N.
5
6use crate::graph_types::SimpleGraph;
7use crate::traits::Problem;
8use crate::types::{EnergyMode, ProblemSize, SolutionSize};
9use serde::{Deserialize, Serialize};
10
11/// The Integer Factoring problem.
12///
13/// Given a number to factor, find two integers that multiply to give
14/// the target number. Variables represent the bits of the two factors.
15///
16/// # Example
17///
18/// ```
19/// use problemreductions::models::specialized::Factoring;
20/// use problemreductions::{Problem, Solver, BruteForce};
21///
22/// // Factor 6 with 2-bit factors (allowing factors 0-3)
23/// let problem = Factoring::new(2, 2, 6);
24///
25/// let solver = BruteForce::new();
26/// let solutions = solver.find_best(&problem);
27///
28/// // Should find: 2*3=6 or 3*2=6
29/// for sol in &solutions {
30///     let (a, b) = problem.read_factors(sol);
31///     assert_eq!(a * b, 6);
32/// }
33/// ```
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct Factoring {
36    /// Number of bits for the first factor.
37    m: usize,
38    /// Number of bits for the second factor.
39    n: usize,
40    /// The number to factor.
41    target: u64,
42}
43
44impl Factoring {
45    /// Create a new Factoring problem.
46    ///
47    /// # Arguments
48    /// * `m` - Number of bits for the first factor
49    /// * `n` - Number of bits for the second factor
50    /// * `target` - The number to factor
51    pub fn new(m: usize, n: usize, target: u64) -> Self {
52        Self { m, n, target }
53    }
54
55    /// Get the number of bits for the first factor.
56    pub fn m(&self) -> usize {
57        self.m
58    }
59
60    /// Get the number of bits for the second factor.
61    pub fn n(&self) -> usize {
62        self.n
63    }
64
65    /// Get the target number to factor.
66    pub fn target(&self) -> u64 {
67        self.target
68    }
69
70    /// Read the two factors from a configuration.
71    ///
72    /// The first `m` bits represent the first factor,
73    /// the next `n` bits represent the second factor.
74    pub fn read_factors(&self, config: &[usize]) -> (u64, u64) {
75        let a = bits_to_int(&config[..self.m]);
76        let b = bits_to_int(&config[self.m..self.m + self.n]);
77        (a, b)
78    }
79
80    /// Check if the configuration is a valid factorization.
81    pub fn is_valid_factorization(&self, config: &[usize]) -> bool {
82        let (a, b) = self.read_factors(config);
83        a * b == self.target
84    }
85}
86
87/// Convert a bit vector (little-endian) to an integer.
88fn bits_to_int(bits: &[usize]) -> u64 {
89    bits.iter().enumerate().map(|(i, &b)| (b as u64) << i).sum()
90}
91
92/// Convert an integer to a bit vector (little-endian).
93#[allow(dead_code)]
94fn int_to_bits(n: u64, num_bits: usize) -> Vec<usize> {
95    (0..num_bits).map(|i| ((n >> i) & 1) as usize).collect()
96}
97
98impl Problem for Factoring {
99    const NAME: &'static str = "Factoring";
100    type GraphType = SimpleGraph;
101    type Weight = i32;
102    type Size = i32;
103
104    fn num_variables(&self) -> usize {
105        self.m + self.n
106    }
107
108    fn num_flavors(&self) -> usize {
109        2 // Binary
110    }
111
112    fn problem_size(&self) -> ProblemSize {
113        ProblemSize::new(vec![
114            ("num_bits_first", self.m),
115            ("num_bits_second", self.n),
116            ("target", self.target as usize),
117        ])
118    }
119
120    fn energy_mode(&self) -> EnergyMode {
121        EnergyMode::SmallerSizeIsBetter // Minimize distance from target
122    }
123
124    fn solution_size(&self, config: &[usize]) -> SolutionSize<Self::Size> {
125        let (a, b) = self.read_factors(config);
126        let product = a * b;
127
128        // Distance from target (0 means exact match)
129        let distance = if product > self.target {
130            (product - self.target) as i32
131        } else {
132            (self.target - product) as i32
133        };
134
135        let is_valid = product == self.target;
136        SolutionSize::new(distance, is_valid)
137    }
138}
139
140/// Check if the given factors correctly factorize the target.
141pub fn is_factoring(target: u64, a: u64, b: u64) -> bool {
142    a * b == target
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148    use crate::solvers::{BruteForce, Solver};
149
150    #[test]
151    fn test_factoring_creation() {
152        let problem = Factoring::new(3, 3, 15);
153        assert_eq!(problem.m(), 3);
154        assert_eq!(problem.n(), 3);
155        assert_eq!(problem.target(), 15);
156        assert_eq!(problem.num_variables(), 6);
157        assert_eq!(problem.num_flavors(), 2);
158    }
159
160    #[test]
161    fn test_bits_to_int() {
162        assert_eq!(bits_to_int(&[0, 0, 0]), 0);
163        assert_eq!(bits_to_int(&[1, 0, 0]), 1);
164        assert_eq!(bits_to_int(&[0, 1, 0]), 2);
165        assert_eq!(bits_to_int(&[1, 1, 0]), 3);
166        assert_eq!(bits_to_int(&[0, 0, 1]), 4);
167        assert_eq!(bits_to_int(&[1, 1, 1]), 7);
168    }
169
170    #[test]
171    fn test_int_to_bits() {
172        assert_eq!(int_to_bits(0, 3), vec![0, 0, 0]);
173        assert_eq!(int_to_bits(1, 3), vec![1, 0, 0]);
174        assert_eq!(int_to_bits(2, 3), vec![0, 1, 0]);
175        assert_eq!(int_to_bits(3, 3), vec![1, 1, 0]);
176        assert_eq!(int_to_bits(7, 3), vec![1, 1, 1]);
177    }
178
179    #[test]
180    fn test_read_factors() {
181        let problem = Factoring::new(2, 2, 6);
182        // bits: [a0, a1, b0, b1]
183        // a=2 (binary 10), b=3 (binary 11) -> config = [0,1,1,1]
184        let (a, b) = problem.read_factors(&[0, 1, 1, 1]);
185        assert_eq!(a, 2);
186        assert_eq!(b, 3);
187    }
188
189    #[test]
190    fn test_solution_size_valid() {
191        let problem = Factoring::new(2, 2, 6);
192        // 2 * 3 = 6
193        let sol = problem.solution_size(&[0, 1, 1, 1]);
194        assert!(sol.is_valid);
195        assert_eq!(sol.size, 0); // Exact match
196
197        // 3 * 2 = 6
198        let sol = problem.solution_size(&[1, 1, 0, 1]);
199        assert!(sol.is_valid);
200        assert_eq!(sol.size, 0);
201    }
202
203    #[test]
204    fn test_solution_size_invalid() {
205        let problem = Factoring::new(2, 2, 6);
206        // 2 * 2 = 4 != 6
207        let sol = problem.solution_size(&[0, 1, 0, 1]);
208        assert!(!sol.is_valid);
209        assert_eq!(sol.size, 2); // Distance from 6
210
211        // 1 * 1 = 1 != 6
212        let sol = problem.solution_size(&[1, 0, 1, 0]);
213        assert!(!sol.is_valid);
214        assert_eq!(sol.size, 5); // Distance from 6
215    }
216
217    #[test]
218    fn test_brute_force_factor_6() {
219        let problem = Factoring::new(2, 2, 6);
220        let solver = BruteForce::new();
221
222        let solutions = solver.find_best(&problem);
223        // Should find 2*3 and 3*2
224        assert!(!solutions.is_empty());
225        for sol in &solutions {
226            let (a, b) = problem.read_factors(sol);
227            assert_eq!(a * b, 6);
228        }
229    }
230
231    #[test]
232    fn test_brute_force_factor_15() {
233        let problem = Factoring::new(3, 3, 15);
234        let solver = BruteForce::new();
235
236        let solutions = solver.find_best(&problem);
237        // Should find 3*5, 5*3, 1*15, 15*1
238        for sol in &solutions {
239            let (a, b) = problem.read_factors(sol);
240            assert_eq!(a * b, 15);
241        }
242    }
243
244    #[test]
245    fn test_brute_force_prime() {
246        // 7 is prime, only 1*7 and 7*1 work
247        let problem = Factoring::new(3, 3, 7);
248        let solver = BruteForce::new();
249
250        let solutions = solver.find_best(&problem);
251        let factor_pairs: Vec<_> = solutions.iter().map(|s| problem.read_factors(s)).collect();
252
253        // Should find (1,7) and (7,1)
254        assert!(factor_pairs.contains(&(1, 7)) || factor_pairs.contains(&(7, 1)));
255    }
256
257    #[test]
258    fn test_is_factoring_function() {
259        assert!(is_factoring(6, 2, 3));
260        assert!(is_factoring(6, 3, 2));
261        assert!(is_factoring(15, 3, 5));
262        assert!(!is_factoring(6, 2, 2));
263    }
264
265    #[test]
266    fn test_energy_mode() {
267        let problem = Factoring::new(2, 2, 6);
268        assert!(problem.energy_mode().is_minimization());
269    }
270
271    #[test]
272    fn test_problem_size() {
273        let problem = Factoring::new(3, 4, 12);
274        let size = problem.problem_size();
275        assert_eq!(size.get("num_bits_first"), Some(3));
276        assert_eq!(size.get("num_bits_second"), Some(4));
277        assert_eq!(size.get("target"), Some(12));
278    }
279
280    #[test]
281    fn test_is_valid_factorization() {
282        let problem = Factoring::new(2, 2, 6);
283        assert!(problem.is_valid_factorization(&[0, 1, 1, 1])); // 2*3=6
284        assert!(!problem.is_valid_factorization(&[0, 1, 0, 1])); // 2*2=4
285    }
286
287    #[test]
288    fn test_factor_one() {
289        // Factor 1: only 1*1 works
290        let problem = Factoring::new(2, 2, 1);
291        let solver = BruteForce::new();
292
293        let solutions = solver.find_best(&problem);
294        for sol in &solutions {
295            let (a, b) = problem.read_factors(sol);
296            assert_eq!(a * b, 1);
297        }
298    }
299}