packages feed

symtegration-0.6.1: test/Symtegration/Polynomial/SymbolicSpec.hs

-- |
-- Description: Tests for Symtegration.Polynomial.Symbolic
-- Copyright: Copyright 2025 Yoo Chung
-- License: Apache-2.0
-- Maintainer: dev@chungyc.org
module Symtegration.Polynomial.SymbolicSpec (spec) where

import Symtegration.Polynomial
import Symtegration.Polynomial.Indexed
import Symtegration.Polynomial.Indexed.Arbitrary ()
import Symtegration.Polynomial.Symbolic
import Symtegration.Symbolic
import Symtegration.Symbolic.Arbitrary
import Test.Hspec
import Test.Hspec.QuickCheck
import Test.QuickCheck

spec :: Spec
spec = parallel $ do
  describe "fromExpression" $ do
    describe "with rational number coefficients" $ do
      prop "is inverse of toExpression" $ \p (SymbolText s) ->
        let e = toExpression s toRationalCoefficient (p :: IndexedPolynomial)
            p' = fromExpression (forVariable s) e
         in counterexample ("p = " <> show p) $
              counterexample ("p'" <> show p') $
                -- With exact rational coefficients, the polynomial representation of
                -- a particular polynomial is unique.
                p' `shouldBe` Just p

      prop "from number" $ \(SymbolText s) n ->
        fromExpression (forVariable s) (Number n)
          `shouldBe` Just (fromInteger n :: IndexedPolynomial)

      prop "from symbol" $ \(SymbolText s) ->
        fromExpression (forVariable s) (Symbol s)
          `shouldBe` Just (power 1 :: IndexedPolynomial)

      prop "from symbol with exponent" $ \(SymbolText s) (Positive n) ->
        n > 1 ==>
          fromExpression (forVariable s) (Symbol s :**: Number n)
            `shouldBe` Just (power (fromIntegral n) :: IndexedPolynomial)

    describe "with symbolic coefficients" $ do
      prop "from number" $ \(SymbolText s) n ->
        fromExpression (withSymbolicCoefficients (forVariable s)) (Number n)
          `shouldBe` Just (fromInteger n :: IndexedSymbolicPolynomial)

      prop "from symbol" $ \(SymbolText s) ->
        fromExpression (withSymbolicCoefficients (forVariable s)) (Symbol s)
          `shouldBe` Just (power 1 :: IndexedSymbolicPolynomial)

      prop "from symbol with exponent" $ \(SymbolText s) (Positive n) ->
        n > 1 ==>
          let e = Symbol s :**: Number n
              p = fromExpression (withSymbolicCoefficients $ forVariable s) e
           in reduceSymbolicCoefficients <$> p `shouldBe` Just (power $ fromIntegral n)

-- | Reduce symbolic coefficients into rational number coefficients.
-- The representation of polynomials with rational number coefficients is unique,
-- which make them easier to compare.
reduceSymbolicCoefficients :: IndexedSymbolicPolynomial -> IndexedPolynomial
reduceSymbolicCoefficients = mapCoefficients reduce
  where
    reduce e
      | (Just x) <- fractionalEvaluate e (const Nothing) = x
      | otherwise = 0