{-|
Module      : Math.Algebra.Jack.SymmetricPolynomials
Description : Some utilities for Jack polynomials.
Copyright   : (c) Stéphane Laurent, 2024
License     : GPL-3
Maintainer  : laurent_step@outlook.fr

A Jack polynomial can have a very long expression in the canonical basis. 
A considerably shorter expression is obtained by writing the polynomial as 
a linear combination of the monomial symmetric polynomials instead, which is 
always possible since Jack polynomials are symmetric. This is the motivation 
of this module.
-}

module Math.Algebra.Jack.SymmetricPolynomials
  ( isSymmetricSpray
  , msPolynomial
  , msCombination
  , prettySymmetricNumSpray
  , prettySymmetricQSpray
  , prettySymmetricQSpray'
  , prettySymmetricParametricQSpray
  , laplaceBeltrami
  , calogeroSutherland
  ) where
import qualified Algebra.Additive                 as AlgAdd
import qualified Algebra.Field                    as AlgField
import qualified Algebra.Ring                     as AlgRing
import qualified Data.Foldable                    as DF
import           Data.List                        ( foldl1', nub )
import           Data.Map.Strict                  ( Map )
import qualified Data.Map.Strict                  as DM
import           Data.Sequence                    ( Seq )
import           Math.Algebra.Hspray              (
                                                    FunctionLike (..)
                                                  , (/^)
                                                  , Spray
                                                  , QSpray
                                                  , QSpray'
                                                  , ParametricQSpray
                                                  , lone
                                                  , lone'
                                                  , fromList
                                                  , getCoefficient
                                                  , isConstant
                                                  , (%//%)
                                                  , RatioOfSprays (..)
                                                  , prettyRatioOfQSpraysXYZ
                                                  , showNumSpray
                                                  , showQSpray
                                                  , showQSpray'
                                                  , showSpray
                                                  , toList
                                                  , zeroSpray
                                                  )
import           Math.Algebra.Jack.Internal       ( Partition , _isPartition )
import           Math.Combinat.Permutations       ( permuteMultiset )
import           Math.Combinat.Partitions.Integer ( fromPartition, mkPartition )

-- | Monomial symmetric polynomials

--

-- >>> putStrLn $ prettySpray' (msPolynomial 3 [2, 1])

-- (1) x1^2.x2 + (1) x1^2.x3 + (1) x1.x2^2 + (1) x1.x3^2 + (1) x2^2.x3 + (1) x2.x3^2

msPolynomial :: (AlgRing.C a, Eq a) 
  => Int       -- ^ number of variables

  -> Partition -- ^ integer partition

  -> Spray a
msPolynomial :: forall a. (C a, Eq a) => Int -> [Int] -> Spray a
msPolynomial Int
n [Int]
lambda
  | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0                     = 
      [Char] -> Spray a
forall a. HasCallStack => [Char] -> a
error [Char]
"msPolynomial: negative number of variables."
  | Bool -> Bool
not ([Int] -> Bool
_isPartition [Int]
lambda) = 
      [Char] -> Spray a
forall a. HasCallStack => [Char] -> a
error [Char]
"msPolynomial: invalid partition."
  | Int
llambda Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
n               = Spray a
forall a. (Eq a, C a) => Spray a
zeroSpray
  | Bool
otherwise                 = [([Int], a)] -> Spray a
forall a. (C a, Eq a) => [([Int], a)] -> Spray a
fromList ([([Int], a)] -> Spray a) -> [([Int], a)] -> Spray a
forall a b. (a -> b) -> a -> b
$ [[Int]] -> [a] -> [([Int], a)]
forall a b. [a] -> [b] -> [(a, b)]
zip [[Int]]
permutations [a]
coefficients
    where
      llambda :: Int
llambda      = [Int] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Int]
lambda
      permutations :: [[Int]]
permutations = [Int] -> [[Int]]
forall a. (Eq a, Ord a) => [a] -> [[a]]
permuteMultiset ([Int]
lambda [Int] -> [Int] -> [Int]
forall a. [a] -> [a] -> [a]
++ Int -> Int -> [Int]
forall a. Int -> a -> [a]
replicate (Int
nInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
llambda) Int
0)
      coefficients :: [a]
coefficients = a -> [a]
forall a. a -> [a]
repeat a
forall a. C a => a
AlgRing.one

-- | Checks whether a spray defines a symmetric polynomial; this is useless for 

-- Jack polynomials because they always are symmetric, but this module contains 

-- everything needed to build this function which can be useful in another context

isSymmetricSpray :: (AlgRing.C a, Eq a) => Spray a -> Bool
isSymmetricSpray :: forall a. (C a, Eq a) => Spray a -> Bool
isSymmetricSpray Spray a
spray = Spray a
spray Spray a -> Spray a -> Bool
forall a. Eq a => a -> a -> Bool
== Spray a
spray' 
  where
    assocs :: [([Int], a)]
assocs = Spray a -> [([Int], a)]
forall a. C a => Spray a -> [([Int], a)]
msCombination' Spray a
spray
    n :: Int
n      = Spray a -> Int
forall b. FunctionLike b => b -> Int
numberOfVariables Spray a
spray
    spray' :: Spray a
spray' = (Spray a -> Spray a -> Spray a) -> [Spray a] -> Spray a
forall a. HasCallStack => (a -> a -> a) -> [a] -> a
foldl1' Spray a -> Spray a -> Spray a
forall b. (FunctionLike b, C b) => b -> b -> b
(^+^) 
      (
        (([Int], a) -> Spray a) -> [([Int], a)] -> [Spray a]
forall a b. (a -> b) -> [a] -> [b]
map (\([Int]
lambda, a
x) -> a
BaseRing (Spray a)
x BaseRing (Spray a) -> Spray a -> Spray a
forall b. FunctionLike b => BaseRing b -> b -> b
*^ Int -> [Int] -> Spray a
forall a. (C a, Eq a) => Int -> [Int] -> Spray a
msPolynomial Int
n [Int]
lambda) [([Int], a)]
assocs
      )

-- | Symmetric polynomial as a linear combination of monomial symmetric polynomials

msCombination :: AlgRing.C a => Spray a -> Map Partition a
msCombination :: forall a. C a => Spray a -> Map [Int] a
msCombination Spray a
spray = [([Int], a)] -> Map [Int] a
forall k a. Ord k => [(k, a)] -> Map k a
DM.fromList (Spray a -> [([Int], a)]
forall a. C a => Spray a -> [([Int], a)]
msCombination' Spray a
spray)

msCombination' :: AlgRing.C a => Spray a -> [(Partition, a)]
msCombination' :: forall a. C a => Spray a -> [([Int], a)]
msCombination' Spray a
spray = 
  ([Int] -> ([Int], a)) -> [[Int]] -> [([Int], a)]
forall a b. (a -> b) -> [a] -> [b]
map (\[Int]
lambda -> ([Int]
lambda, [Int] -> Spray a -> a
forall a. C a => [Int] -> Spray a -> a
getCoefficient [Int]
lambda Spray a
spray)) [[Int]]
lambdas
  where
    lambdas :: [[Int]]
lambdas = [[Int]] -> [[Int]]
forall a. Eq a => [a] -> [a]
nub ([[Int]] -> [[Int]]) -> [[Int]] -> [[Int]]
forall a b. (a -> b) -> a -> b
$ (([Int], a) -> [Int]) -> [([Int], a)] -> [[Int]]
forall a b. (a -> b) -> [a] -> [b]
map (Partition -> [Int]
fromPartition (Partition -> [Int])
-> (([Int], a) -> Partition) -> ([Int], a) -> [Int]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Int] -> Partition
mkPartition ([Int] -> Partition)
-> (([Int], a) -> [Int]) -> ([Int], a) -> Partition
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([Int], a) -> [Int]
forall a b. (a, b) -> a
fst) (Spray a -> [([Int], a)]
forall a. Spray a -> [([Int], a)]
toList Spray a
spray)

-- helper function for the showing stuff

makeMSpray :: (Eq a, AlgRing.C a) => Spray a -> Spray a
makeMSpray :: forall a. (Eq a, C a) => Spray a -> Spray a
makeMSpray = [([Int], a)] -> Spray a
forall a. (C a, Eq a) => [([Int], a)] -> Spray a
fromList ([([Int], a)] -> Spray a)
-> (Spray a -> [([Int], a)]) -> Spray a -> Spray a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Spray a -> [([Int], a)]
forall a. C a => Spray a -> [([Int], a)]
msCombination'

-- show symmetric monomial like M[3,2,1]

showSymmetricMonomials :: [Seq Int] -> [String]
showSymmetricMonomials :: [Seq Int] -> [[Char]]
showSymmetricMonomials = (Seq Int -> [Char]) -> [Seq Int] -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
map Seq Int -> [Char]
showSymmetricMonomial
  where
    showSymmetricMonomial :: Seq Int -> String
    showSymmetricMonomial :: Seq Int -> [Char]
showSymmetricMonomial Seq Int
lambda = Char
'M' Char -> [Char] -> [Char]
forall a. a -> [a] -> [a]
: [Int] -> [Char]
forall a. Show a => a -> [Char]
show (Seq Int -> [Int]
forall a. Seq a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
DF.toList Seq Int
lambda)

-- | Prints a symmetric spray as a linear combination of monomial symmetric polynomials

--

-- >>> putStrLn $ prettySymmetricNumSpray $ schurPol' 3 [3, 1, 1]

-- M[3,1,1] + M[2,2,1]

prettySymmetricNumSpray :: 
  (Num a, Ord a, Show a, AlgRing.C a) => Spray a -> String
prettySymmetricNumSpray :: forall a. (Num a, Ord a, Show a, C a) => Spray a -> [Char]
prettySymmetricNumSpray Spray a
spray = 
  ([Seq Int] -> [[Char]]) -> (a -> [Char]) -> Spray a -> [Char]
forall a.
(Num a, Ord a) =>
([Seq Int] -> [[Char]]) -> (a -> [Char]) -> Spray a -> [Char]
showNumSpray [Seq Int] -> [[Char]]
showSymmetricMonomials a -> [Char]
forall a. Show a => a -> [Char]
show Spray a
mspray
  where
    mspray :: Spray a
mspray = Spray a -> Spray a
forall a. (Eq a, C a) => Spray a -> Spray a
makeMSpray Spray a
spray

-- | Prints a symmetric spray as a linear combination of monomial symmetric polynomials

--

-- >>> putStrLn $ prettySymmetricQSpray $ jackPol' 3 [3, 1, 1] 2 'J'

-- 42*M[3,1,1] + 28*M[2,2,1]

prettySymmetricQSpray :: QSpray -> String
prettySymmetricQSpray :: QSpray -> [Char]
prettySymmetricQSpray QSpray
spray = ([Seq Int] -> [[Char]]) -> QSpray -> [Char]
showQSpray [Seq Int] -> [[Char]]
showSymmetricMonomials QSpray
mspray
  where
    mspray :: QSpray
mspray = QSpray -> QSpray
forall a. (Eq a, C a) => Spray a -> Spray a
makeMSpray QSpray
spray

-- | Same as `prettySymmetricQSpray` but for a `QSpray'` symmetric spray

prettySymmetricQSpray' :: QSpray' -> String
prettySymmetricQSpray' :: QSpray' -> [Char]
prettySymmetricQSpray' QSpray'
spray = ([Seq Int] -> [[Char]]) -> QSpray' -> [Char]
showQSpray' [Seq Int] -> [[Char]]
showSymmetricMonomials QSpray'
mspray
  where
    mspray :: QSpray'
mspray = QSpray' -> QSpray'
forall a. (Eq a, C a) => Spray a -> Spray a
makeMSpray QSpray'
spray

-- | Prints a symmetric parametric spray as a linear combination of monomial 

-- symmetric polynomials

--

-- >>> putStrLn $ prettySymmetricParametricQSpray ["a"] $ jackSymbolicPol' 3 [3, 1, 1] 'J'

-- { [ 4*a^2 + 10*a + 6 ] }*M[3,1,1] + { [ 8*a + 12 ] }*M[2,2,1]

prettySymmetricParametricQSpray :: [String] -> ParametricQSpray -> String
prettySymmetricParametricQSpray :: [[Char]] -> ParametricQSpray -> [Char]
prettySymmetricParametricQSpray [[Char]]
letters ParametricQSpray
spray = 
  (RatioOfQSprays -> [Char])
-> ([Char], [Char])
-> ([Seq Int] -> [[Char]])
-> ParametricQSpray
-> [Char]
forall a.
(a -> [Char])
-> ([Char], [Char]) -> ([Seq Int] -> [[Char]]) -> Spray a -> [Char]
showSpray ([[Char]] -> RatioOfQSprays -> [Char]
prettyRatioOfQSpraysXYZ [[Char]]
letters) ([Char]
"{ ", [Char]
" }") 
            [Seq Int] -> [[Char]]
showSymmetricMonomials ParametricQSpray
mspray
  where
    mspray :: ParametricQSpray
mspray = ParametricQSpray -> ParametricQSpray
forall a. (Eq a, C a) => Spray a -> Spray a
makeMSpray ParametricQSpray
spray

-- Laplace-Beltrami operator on the space of homogeneous symmetric polynomials;

-- neither symmetry and homogeneity are checked

laplaceBeltrami :: (Eq a, AlgField.C a) => a -> Spray a -> Spray a
laplaceBeltrami :: forall a. (Eq a, C a) => a -> Spray a -> Spray a
laplaceBeltrami a
alpha Spray a
spray = 
  if Spray a -> Bool
forall b. FunctionLike b => b -> Bool
isConstant Spray a
spray 
    then Spray a
forall a. (Eq a, C a) => Spray a
zeroSpray 
    else a
BaseRing (Spray a)
alpha' BaseRing (Spray a) -> Spray a -> Spray a
forall b. FunctionLike b => BaseRing b -> b -> b
*^ Spray a
spray1 Spray a -> Spray a -> Spray a
forall b. (FunctionLike b, C b) => b -> b -> b
^+^ Spray a
spray2
  where
    alpha' :: a
alpha' = a
alpha a -> a -> a
forall a. C a => a -> a -> a
AlgField./ Integer -> a
forall a. C a => Integer -> a
AlgRing.fromInteger Integer
2
    n :: Int
n = Spray a -> Int
forall b. FunctionLike b => b -> Int
numberOfVariables Spray a
spray
    range :: [Int]
range = [Int
1 .. Int
n]
    dsprays :: [Spray a]
dsprays = (Int -> Spray a) -> [Int] -> [Spray a]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> Spray a -> Spray a
forall b. FunctionLike b => Int -> b -> b
`derivative` Spray a
spray) [Int]
range
    op1 :: Int -> Spray a
op1 Int
i = Int -> Int -> Spray a
forall a. C a => Int -> Int -> Spray a
lone' Int
i Int
2 Spray a -> Spray a -> Spray a
forall b. (FunctionLike b, C b) => b -> b -> b
^*^ Int -> Spray a -> Spray a
forall b. FunctionLike b => Int -> b -> b
derivative Int
i ([Spray a]
dsprays [Spray a] -> Int -> Spray a
forall a. HasCallStack => [a] -> Int -> a
!! (Int
iInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1))
    spray1 :: Spray a
spray1 = [Spray a] -> Spray a
forall a. C a => [a] -> a
AlgAdd.sum ((Int -> Spray a) -> [Int] -> [Spray a]
forall a b. (a -> b) -> [a] -> [b]
map Int -> Spray a
op1 [Int]
range)
    spray2 :: Spray a
spray2 = RatioOfSprays a -> Spray a
forall a. RatioOfSprays a -> Spray a
_numerator (RatioOfSprays a -> Spray a) -> RatioOfSprays a -> Spray a
forall a b. (a -> b) -> a -> b
$ [RatioOfSprays a] -> RatioOfSprays a
forall a. C a => [a] -> a
AlgAdd.sum 
              [(Int -> Int -> Spray a
forall a. C a => Int -> Int -> Spray a
lone' Int
i Int
2 Spray a -> Spray a -> Spray a
forall b. (FunctionLike b, C b) => b -> b -> b
^*^ [Spray a]
dsprays [Spray a] -> Int -> Spray a
forall a. HasCallStack => [a] -> Int -> a
!! (Int
iInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1)) Spray a -> Spray a -> RatioOfSprays a
forall a. (Eq a, C a) => Spray a -> Spray a -> RatioOfSprays a
%//% (Int -> Spray a
forall a. C a => Int -> Spray a
lone Int
i Spray a -> Spray a -> Spray a
forall b. (FunctionLike b, C b) => b -> b -> b
^-^ Int -> Spray a
forall a. C a => Int -> Spray a
lone Int
j)
                | Int
i <- [Int]
range, Int
j <- [Int]
range, Int
i Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
j]

-- Calogero-Sutherland operator on the space of homogeneous symmetric polynomials;

-- neither symmetry and homogeneity are checked

calogeroSutherland :: (Eq a, AlgField.C a) => a -> Spray a -> Spray a
calogeroSutherland :: forall a. (Eq a, C a) => a -> Spray a -> Spray a
calogeroSutherland a
alpha Spray a
spray = 
  if Spray a -> Bool
forall b. FunctionLike b => b -> Bool
isConstant Spray a
spray 
    then Spray a
forall a. (Eq a, C a) => Spray a
zeroSpray
    else Spray a -> Spray a
forall {a}. (C a, Eq a) => Spray a -> Spray a
halfSpray (Spray a -> Spray a) -> Spray a -> Spray a
forall a b. (a -> b) -> a -> b
$ a
BaseRing (Spray a)
alpha BaseRing (Spray a) -> Spray a -> Spray a
forall b. FunctionLike b => BaseRing b -> b -> b
*^ Spray a
spray1 Spray a -> Spray a -> Spray a
forall b. (FunctionLike b, C b) => b -> b -> b
^+^ Spray a
spray2
  where
    halfSpray :: Spray a -> Spray a
halfSpray Spray a
p = Spray a
p Spray a -> a -> Spray a
forall a. (C a, Eq a) => Spray a -> a -> Spray a
/^ Integer -> a
forall a. C a => Integer -> a
AlgRing.fromInteger Integer
2
    n :: Int
n = Spray a -> Int
forall b. FunctionLike b => b -> Int
numberOfVariables Spray a
spray
    range :: [Int]
range = [Int
1 .. Int
n]
    dsprays :: [Spray a]
dsprays = (Int -> Spray a) -> [Int] -> [Spray a]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> Spray a -> Spray a
forall b. FunctionLike b => Int -> b -> b
`derivative` Spray a
spray) [Int]
range
    op0 :: Spray a -> Int -> Spray a
op0 Spray a
p Int
i = Int -> Spray a
forall a. C a => Int -> Spray a
lone Int
i Spray a -> Spray a -> Spray a
forall b. (FunctionLike b, C b) => b -> b -> b
^*^ Int -> Spray a -> Spray a
forall b. FunctionLike b => Int -> b -> b
derivative Int
i Spray a
p 
    op1 :: Spray a -> Int -> Spray a
op1 Spray a
p Int
i = Spray a -> Int -> Spray a
forall {a}. (Eq a, C a) => Spray a -> Int -> Spray a
op0 (Spray a -> Int -> Spray a
forall {a}. (Eq a, C a) => Spray a -> Int -> Spray a
op0 Spray a
p Int
i) Int
i
    spray1 :: Spray a
spray1 = [Spray a] -> Spray a
forall a. C a => [a] -> a
AlgAdd.sum ((Int -> Spray a) -> [Int] -> [Spray a]
forall a b. (a -> b) -> [a] -> [b]
map (Spray a -> Int -> Spray a
forall {a}. (Eq a, C a) => Spray a -> Int -> Spray a
op1 Spray a
spray) [Int]
range)
    spray2 :: Spray a
spray2 = RatioOfSprays a -> Spray a
forall a. RatioOfSprays a -> Spray a
_numerator (RatioOfSprays a -> Spray a) -> RatioOfSprays a -> Spray a
forall a b. (a -> b) -> a -> b
$ [RatioOfSprays a] -> RatioOfSprays a
forall a. C a => [a] -> a
AlgAdd.sum 
      [let (Spray a
xi, Spray a
xj, Spray a
dxi, Spray a
dxj) = 
            (Int -> Spray a
forall a. C a => Int -> Spray a
lone Int
i, Int -> Spray a
forall a. C a => Int -> Spray a
lone Int
j, [Spray a]
dsprays [Spray a] -> Int -> Spray a
forall a. HasCallStack => [a] -> Int -> a
!! (Int
iInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1), [Spray a]
dsprays [Spray a] -> Int -> Spray a
forall a. HasCallStack => [a] -> Int -> a
!! (Int
jInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1)) in 
          (Spray a
xi Spray a -> Spray a -> Spray a
forall b. (FunctionLike b, C b) => b -> b -> b
^+^ Spray a
xj) Spray a -> Spray a -> Spray a
forall b. (FunctionLike b, C b) => b -> b -> b
^*^ (Spray a
xi Spray a -> Spray a -> Spray a
forall b. (FunctionLike b, C b) => b -> b -> b
^*^ Spray a
dxi Spray a -> Spray a -> Spray a
forall b. (FunctionLike b, C b) => b -> b -> b
^-^ Spray a
xj Spray a -> Spray a -> Spray a
forall b. (FunctionLike b, C b) => b -> b -> b
^*^ Spray a
dxj) Spray a -> Spray a -> RatioOfSprays a
forall a. (Eq a, C a) => Spray a -> Spray a -> RatioOfSprays a
%//% (Spray a
xi Spray a -> Spray a -> Spray a
forall b. (FunctionLike b, C b) => b -> b -> b
^-^ Spray a
xj)
       | Int
i <- [Int]
range, Int
j <- [Int
iInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1 .. Int
n]]