-- | This module provides some number-theoretic functions, -- particularly functions for solving the Diophantine equation -- -- \[center /t/[sup †]/t/ = ξ,] -- -- where ξ ∈ ℤ[√2] and /t/ ∈ ℤ[ω], or ξ ∈ [bold D][√2] and /t/ ∈ [bold D][ω]. -- -- In general, solving this equation can be hard, as it depends on the -- ability to factor the integer /n/ = ξ[sup •]ξ into primes. We -- formulate the solution as a step computation (see -- "Quantum.Synthesis.StepComp"), so that the caller can dynamically -- determine how much time to spend on solving the equation, or can -- attempt to solve several such equations in parallel. -- -- In many cases, even a partial factorization of /n/ is sufficient to -- determine that no solution exists. This implementation is written -- to take advantage of such cases. module Quantum.Synthesis.Diophantine ( -- * Diophantine solvers diophantine, diophantine_dyadic, diophantine_associate, -- * Factoring find_factor, relatively_prime_factors, -- * Computations in ℤ[sub /n/] power_mod, root_of_negative_one, root_mod, ) where import Quantum.Synthesis.StepComp import Quantum.Synthesis.EuclideanDomain import Quantum.Synthesis.Ring import System.Random import Control.Exception -- ---------------------------------------------------------------------- -- * Diophantine solvers -- | Given ξ ∈ ℤ[√2], find /t/ ∈ ℤ[ω] such that /t/[sup †]/t/ = ξ, if -- such /t/ exists, or return 'Nothing' otherwise. diophantine :: (RandomGen g) => g -> ZRootTwo -> StepComp (Maybe ZOmega) diophantine g xi | xi == 0 = return (Just 0) | xi < 0 = return Nothing | adj2 xi < 0 = return Nothing | otherwise = do t <- diophantine_associate g xi case t of Nothing -> return Nothing Just t -> do let xi_associate = zroottwo_of_zomega (adj t * t) let u = euclid_div xi xi_associate case zroottwo_root u of Nothing -> return Nothing Just v -> return (Just (fromZRootTwo v * t)) -- | Given an element ξ ∈ [bold D][√2], find /t/ ∈ [bold D][ω] such -- that /t/[sup †]/t/ = ξ, if such /t/ exists, or return 'Nothing' -- otherwise. -- Implementation note: In the reduction from [bold D][√2] to ℤ[√2], -- we can multiply by a power of λ√2 instead of √2. This has the same -- effect of reducing the denominator exponent to 0 (note that λ is a -- unit of the ring ℤ[√2]), but has the additional advantage that λ√2 -- (unlike √2) is doubly positive, thereby preserving solvability of -- the Diophantine equation. -- -- Similarly, in translating the solution back from ℤ[ω] to -- [bold D][ω], we use the fact that 1\/(λ√2) = /u/[sup †]/u/, where -- /u/ = (ω - /i/)\/√2. Also note that /u/ = δ⁻¹, where δ = 1 + ω. diophantine_dyadic :: (RandomGen g) => g -> DRootTwo -> StepComp (Maybe DOmega) diophantine_dyadic g xi = do let k = denomexp xi let (k',k'') = k `divMod` 2 let xi' = to_whole ((lambda * roottwo)^k'' * 2^k' * xi) t' <- diophantine g xi' case t' of Nothing -> return Nothing Just t' -> return (Just (u^k'' * roothalf^k' * from_whole t')) where u = roothalf * (omega - i) lambda = 1 + roottwo -- | Given ξ ∈ ℤ[√2], find /t/ ∈ ℤ[ω] such that /t/[sup †]/t/ ~ ξ, if -- such /t/ exists, or 'Nothing' otherwise. Unlike 'diophantine', the -- equation is only solved up to associates, i.e., up to a unit of the -- ring. diophantine_associate :: (RandomGen g) => g -> ZRootTwo -> StepComp (Maybe ZOmega) diophantine_associate g xi | xi == 0 = return (Just 0) | otherwise = do let d = euclid_gcd xi (adj2 xi) let xi' = euclid_div xi d res <- parallel_maybe (dioph_zroottwo_selfassociate g1 d) (dioph_zroottwo_assoc g2 xi') case res of Nothing -> return Nothing Just (t1, t2) -> return (Just (t1*t2)) where (g1, g2) = split g -- ---------------------------------------------------------------------- -- * Factoring -- | Given a positive composite integer /n/, find a non-trivial factor -- of /n/ using a simple Pollard-rho method. The expected runtime is -- O(√/p/), where /p/ is the size of the smallest prime factor. If -- /n/ is not composite (i.e., if /n/ is prime or 1), this function -- diverges. find_factor :: (RandomGen g) => g -> Integer -> StepComp Integer find_factor g n | even n && n > 2 = return 2 | otherwise = tick >> aux 2 (f 2) where (a, g2) = randomR (1, n-1) g f x = (x^2 + a) `mod` n aux x y | d == 1 = tick >> aux (f x) (f (f y)) | d == n = find_factor g2 n | otherwise = return d where d = gcd (x-y) n -- | Given a factorization /n/ = /ab/ of some element of a Euclidean domain, find a factorization of /n/ into relatively prime factors, -- -- \[center /n/ = /u/ /c/[sub 1][sup /k/[sub 1]] ⋅ … ⋅ /c/[sub /m/][sup /k/[sub /m/]],] -- -- where /m/ ≥ 2, /u/ is a unit, and /c/[sub 1], …, /c/[sub /m/] are -- pairwise relatively prime. -- -- While this is not quite a prime factorization of /n/, it can be a -- useful intermediate step for computations that proceed by recursion -- on relatively prime factors (such as Euler's φ-function, the -- solution of Diophantine equations, etc.). relatively_prime_factors :: (EuclideanDomain a) => a -> a -> (a, [(a, Integer)]) relatively_prime_factors a b = aux 1 [a,b] [] where aux u [] fs = (u, fs) aux u (h:t) fs | is_unit h = aux (h*u) t fs aux u (h:t) fs = aux (u'*u) (hs ++ t) fs' where (u', hs, fs') = aux2 h fs aux2 h [] = (1, [], [(h,1)]) aux2 h ((f,k) : fs) | euclid_associates h f = (u', [], (f,k+1) : fs) | is_unit d = (u, hs, (f,k) : fs') | otherwise = (1, [h `euclid_div` d, d] ++ replicate (fromInteger k) (f `euclid_div` d) ++ replicate (fromInteger k) d, fs) where d = euclid_gcd h f (u, hs, fs') = aux2 h fs u' = h `euclid_div` f -- ---------------------------------------------------------------------- -- * Computations in ℤ[sub /n/] -- | Modular exponentiation, using the method of repeated squaring. -- 'power_mod' /a/ /k/ /n/ computes /a/[sup /k/] (mod /n/). power_mod :: Integer -> Integer -> Integer -> Integer power_mod a k n | k == 0 = 1 | k == 1 = a `mod` n | even k = (b*b) `mod` n | otherwise = (b*b*a) `mod` n where b = power_mod a (k `div` 2) n -- | Compute a root of −1 in ℤ[sub /n/], where /n/ > 0. If /n/ is a -- positive prime satisfying /n/ ≡ 1 (mod 4), this succeeds within an -- expected number of 2 ticks. Otherwise, it probably diverges. -- -- As a special case, if this function notices that /n/ is not prime, -- then it diverges without doing any additional work. root_of_negative_one :: (RandomGen g) => g -> Integer -> StepComp Integer root_of_negative_one g n = do tick let (b, g') = randomR (1, n-1) g let h = power_mod b ((n-1) `div` 4) n let r = (h*h) `mod` n if r == n-1 then return h else if r /= 1 then diverge else root_of_negative_one g' n -- | Compute a root of /a/ in ℤ[sub /n/], where /n/ > 0. If /n/ is an -- odd prime and /a/ is a non-zero square in ℤ[sub /n/], then this -- succeeds in an expected number of 2 ticks. Otherwise, it probably -- diverges. root_mod :: (RandomGen g) => g -> Integer -> Integer -> StepComp Integer root_mod g n a | a `mod` n == -1 -- handle this special case more efficiently = root_of_negative_one g n | otherwise = tick >> res where (b, g') = randomR (0, n-1) g (r,s) = (2*b `mod` n, b^2-a `mod` n) (c,d) = pow (1,0) ((n-1) `div` 2) res = case inv_mod n c of Just c' | (t1^2 - a) `mod` n == 0 -> return t1 where t = (1-d) * c' t1 = (t+b) `mod` n _ -> root_mod g' n a -- | 'mul' performs a multiplication in the ring -- ℤ[sub /n/][t]\/(/t/²+/rt/+/s/). The elements /at/+/b/ are -- represented as pairs (/a/,/b/). mul :: (Integer, Integer) -> (Integer, Integer) -> (Integer, Integer) mul (a,b) (c,d) = (a'',b'') where (x,y,z) = (a*c, a*d+b*c, b*d) -- multiply polynomials (a',b') = (y - x*r,z - x*s) -- reduce modulo /t/²+/rt/+/s/ (a'',b'') = (a' `mod` n, b' `mod` n) -- reduce modulo /n/ -- | 'pow' takes a power in the ring -- ℤ[sub /n/][t]\/(/t/²+/rt/+/s/. The elements /at/+/b/ are -- represented as pairs (/a/,/b/). pow :: (Integer, Integer) -> Integer -> (Integer, Integer) pow x m | m <= 0 = (0,1) | odd m = x `mul` (x `pow` (m-1)) | otherwise = y `mul` y where y = x `pow` (m `div` 2) -- ---------------------------------------------------------------------- -- * Implementation details -- $ Our implementation of the top-level Diophantine equation solvers -- proceeds through a series of special cases. The following functions -- handle the special cases, and are not of independent interest. -- ---------------------------------------------------------------------- -- ** Case: ξ is an integer -- | Given an integer /n/ ∈ ℤ, attempt to find /t/ ∈ ℤ[ω] such that -- /t/[sup †]/t/ ~ /n/, or return 'Nothing' if no such /t/ exists. -- -- This function is optimized for the case when /n/ is prime, and -- succeeds in an expected number of 2 ticks in this case. If /n/ is -- not prime, this function probably diverges. dioph_int_assoc_prime :: (RandomGen g) => g -> Integer -> StepComp (Maybe ZOmega) dioph_int_assoc_prime g n | n < 0 = dioph_int_assoc_prime g (-n) | n == 0 = return (Just 0) | n == 2 = return (Just roottwo) | n_mod_4 == 1 = do h <- root_of_negative_one g n let t = euclid_gcd (fromInteger h+i) (fromInteger n) :: ZOmega assert (adj t * t == fromInteger n) $ return (Just t) | n_mod_8 == 3 = do h <- root_mod g n (-2) let t = euclid_gcd (fromInteger h+i*roottwo) (fromInteger n) :: ZOmega assert (adj t * t == fromInteger n) $ return (Just t) | n_mod_8 == 7 = do h <- root_mod g n 2 -- if n is prime, then 2 is a square. Conversely, if 2 is a -- square, even if n is not prime, it implies that the Diophantine -- equation has no solution. Because in this case, 2 is a square -- for every prime divisor of n, so each such divisor must be -- congruent to 1 or 7 (mod 8), so there must be at least one -- prime divisor that occurs as an odd power and is congruent to 7 -- mod n. return Nothing where n_mod_4 = n `mod` 4 n_mod_8 = n `mod` 8 -- | Given an integer /n/ ∈ ℤ, find /t/ ∈ ℤ[ω] such that /t/[sup †]/t/ -- ~ /n/, if such /t/ exists, or return 'Nothing' if no such /t/ -- exists. -- -- This function alternately calls 'dioph_int_assoc_prime' and -- attempts to factor /n/. Therefore, it will eventually succeed; -- however, the runtime depends on how hard it is to factor ξ. dioph_int_assoc :: (RandomGen g) => g -> Integer -> StepComp (Maybe ZOmega) dioph_int_assoc g n | n < 0 = dioph_int_assoc g (-n) | n == 0 = return (Just 0) | n == 1 = return (Just 1) | otherwise = interleave prime_solver factor_solver where interleave p f = do p <- subtask 4 p case p of Done res -> return res _ -> do f <- subtask 1000 f case f of Done (a, k) -> do let b = n `div` a let (u, facs) = relatively_prime_factors a b forward (k `div` 2) $ dioph_int_assoc_powers g3 facs _ -> interleave p f (g1, g') = split g (g2, g3) = split g' prime_solver = dioph_int_assoc_prime g1 n factor_solver = with_counter $ speedup 30 $ find_factor g2 n -- | Given a factorization /n/ = /q/[sub 1][sup /k/[sub 1]]⋅…⋅/q/[sub -- /m/][sup /k/[sub /m/]] of an integer /n/, where /q/[sub 1], …, -- /q/[sub /m/] are pairwise relatively prime, find /t/ ∈ ℤ[ω] such -- that /t/[sup †]/t/ ~ /n/, if such /t/ exists, or return 'Nothing' -- if no such /t/ exists. dioph_int_assoc_powers :: (RandomGen g) => g -> [(Integer, Integer)] -> StepComp (Maybe ZOmega) dioph_int_assoc_powers g facs = do res <- parallel_list_maybe [dioph_int_assoc_power g (n,k) | (n,k) <- facs] case res of Nothing -> return Nothing Just sols -> return (Just (product sols)) -- | Given a pair of integers (/n/, /k/), find /t/ ∈ ℤ[ω] such that -- /t/[sup †]/t/ ~ /n/[sup /k/], if such /t/ exists, or return -- 'Nothing' if no such /t/ exists. dioph_int_assoc_power :: (RandomGen g) => g -> (Integer, Integer) -> StepComp (Maybe ZOmega) dioph_int_assoc_power g (n,k) | even k = return (Just (fromInteger (n^(k `div` 2)))) | otherwise = do t <- dioph_int_assoc g n case t of Nothing -> return Nothing Just t -> return (Just (t^k)) -- ---------------------------------------------------------------------- -- ** Case: ξ ~ ξ[sup •] -- | Given ξ ∈ ℤ[√2] such that ξ ~ ξ[sup •], find /t/ ∈ ℤ[ω] such that -- /t/[sup †]/t/ ~ ξ, if such /t/ exists, or return 'Nothing' if no -- such /t/ exists. dioph_zroottwo_selfassociate :: (RandomGen g) => g -> ZRootTwo -> StepComp (Maybe ZOmega) dioph_zroottwo_selfassociate g xi | xi == 0 = return (Just 0) | otherwise = do res <- dioph_int_assoc g n case res of Nothing -> return Nothing Just t -> if euclid_divides roottwo r then return (Just ((1+omega) * t)) else return (Just t) where RootTwo a b = xi n = gcd a b r = euclid_div xi (fromInteger n) -- ---------------------------------------------------------------------- -- ** Case: gcd(ξ, ξ[sup •]) = 1 -- | Given ξ ∈ ℤ[√2] such that gcd(ξ, ξ[sup •]) = 1, attempt to find -- /t/ ∈ ℤ[ω] such that /t/[sup †]/t/ ~ ξ, or return 'Nothing' if no -- such /t/ exists. -- -- This function is optimized for the case when ξ is a prime in the -- ring ℤ[√2]. In this case, it succeeds quickly, in an expected -- number of 2 ticks. If ξ is not prime, this function probably -- diverges. dioph_zroottwo_assoc_prime :: (RandomGen g) => g -> ZRootTwo -> StepComp (Maybe ZOmega) dioph_zroottwo_assoc_prime g xi | xi == 0 = return (Just 0) | n_mod_8 == 1 = do h <- root_of_negative_one g n let t = euclid_gcd (fromInteger h+i) (fromZRootTwo xi) :: ZOmega assert ((adj t * t) `euclid_associates` fromZRootTwo xi) $ return (Just t) | n_mod_8 == 7 = return Nothing | otherwise = diverge where n_mod_8 = n `mod` 8 n = abs (norm xi) -- | Given ξ ∈ ℤ[√2] such that gcd(ξ, ξ[sup •]) = 1, find /t/ ∈ ℤ[ω] -- such that /t/[sup †]/t/ ~ ξ, if such /t/ exists, or return -- 'Nothing' if no such /t/ exists. -- -- This function alternately calls 'dioph_int_assoc_prime' and -- attempts to factor ξ. Therefore, it will eventually succeed. -- However, the runtime depends on how hard it is to factor ξ. dioph_zroottwo_assoc :: (RandomGen g) => g -> ZRootTwo -> StepComp (Maybe ZOmega) dioph_zroottwo_assoc g xi | xi == 0 = return (Just 0) | otherwise = interleave prime_solver factor_solver where interleave p f = do p <- subtask 4 p case p of Done res -> return res _ -> do f <- subtask 1000 f case f of Done (a, k) -> do let alpha = euclid_gcd xi (fromInteger a) let beta = xi `euclid_div` alpha let (u, facs) = relatively_prime_factors alpha beta forward (k `div` 2) $ dioph_zroottwo_assoc_powers g3 facs _ -> interleave p f (g1, g') = split g (g2, g3) = split g' prime_solver = dioph_zroottwo_assoc_prime g1 xi factor_solver = with_counter $ speedup 30 $ find_factor g2 n n = abs (norm xi) -- | Given a factorization ξ = /q/[sub 1][sup /k/[sub 1]]⋅…⋅/q/[sub -- /m/][sup /k/[sub /m/]] of some ξ ∈ ℤ[√2], where /q/[sub 1], …, -- /q/[sub /m/] are pairwise relatively prime, find /t/ ∈ ℤ[ω] such -- that /t/[sup †]/t/ ~ ξ, if such /t/ exists, or return 'Nothing' -- if it can be proven not to exist. dioph_zroottwo_assoc_powers :: (RandomGen g) => g -> [(ZRootTwo, Integer)] -> StepComp (Maybe ZOmega) dioph_zroottwo_assoc_powers g facs = do res <- parallel_list_maybe [dioph_zroottwo_assoc_power g (q,k) | (q,k) <- facs] case res of Nothing -> return Nothing Just sols -> return (Just (product sols)) -- | Given a pair (ξ, /k/), with ξ ∈ ℤ[√2] and /k/ ≥ 0, find /t/ ∈ -- ℤ[ω] such that /t/[sup †]/t/ ~ ξ[sup /k/], if such /t/ exists, or -- return 'Nothing' if no such /t/ exists. dioph_zroottwo_assoc_power :: (RandomGen g) => g -> (ZRootTwo, Integer) -> StepComp (Maybe ZOmega) dioph_zroottwo_assoc_power g (xi,k) | even k = return (Just (fromZRootTwo (xi^(k `div` 2)))) | otherwise = do t <- dioph_zroottwo_assoc g xi case t of Nothing -> return Nothing Just t -> return (Just (t^k))