packages feed

jackpolynomials 1.4.0.0 → 1.4.1.0

raw patch · 9 files changed

+713/−208 lines, 9 filesdep +extradep +unordered-containersPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: extra, unordered-containers

API changes (from Hackage documentation)

- Math.Algebra.Jack.SymmetricPolynomials: calogeroSutherland :: (Eq a, C a) => a -> Spray a -> Spray a
- Math.Algebra.Jack.SymmetricPolynomials: isSymmetricSpray :: (C a, Eq a) => Spray a -> Bool
- Math.Algebra.Jack.SymmetricPolynomials: laplaceBeltrami :: (Eq a, C a) => a -> Spray a -> Spray a
- Math.Algebra.Jack.SymmetricPolynomials: msCombination :: C a => Spray a -> Map Partition a
- Math.Algebra.Jack.SymmetricPolynomials: msPolynomial :: (C a, Eq a) => Int -> Partition -> Spray a
- Math.Algebra.Jack.SymmetricPolynomials: prettySymmetricNumSpray :: (Num a, Ord a, Show a, C a) => Spray a -> String
- Math.Algebra.Jack.SymmetricPolynomials: prettySymmetricParametricQSpray :: [String] -> ParametricQSpray -> String
- Math.Algebra.Jack.SymmetricPolynomials: prettySymmetricQSpray :: QSpray -> String
- Math.Algebra.Jack.SymmetricPolynomials: prettySymmetricQSpray' :: QSpray' -> String
+ Math.Algebra.Jack: type Partition = [Int]
+ Math.Algebra.SymmetricPolynomials: calogeroSutherland :: (Eq a, C a) => a -> Spray a -> Spray a
+ Math.Algebra.SymmetricPolynomials: hallInnerProduct :: forall a. (Eq a, C a) => Spray a -> Spray a -> a -> a
+ Math.Algebra.SymmetricPolynomials: hallInnerProduct' :: forall a. (Eq a, C Rational a, C a) => Spray a -> Spray a -> a -> a
+ Math.Algebra.SymmetricPolynomials: hallInnerProduct'' :: forall a. Real a => Spray a -> Spray a -> a -> Rational
+ Math.Algebra.SymmetricPolynomials: hallInnerProduct''' :: forall b. (Eq b, C b, C (BaseRing b) b) => Spray b -> Spray b -> BaseRing b -> b
+ Math.Algebra.SymmetricPolynomials: hallInnerProduct'''' :: forall b. (Eq b, C b, C Rational b, C (BaseRing b) b) => Spray b -> Spray b -> BaseRing b -> b
+ Math.Algebra.SymmetricPolynomials: isSymmetricSpray :: (C a, Eq a) => Spray a -> Bool
+ Math.Algebra.SymmetricPolynomials: laplaceBeltrami :: (Eq a, C a) => a -> Spray a -> Spray a
+ Math.Algebra.SymmetricPolynomials: msCombination :: C a => Spray a -> Map Partition a
+ Math.Algebra.SymmetricPolynomials: msPolynomial :: (C a, Eq a) => Int -> Partition -> Spray a
+ Math.Algebra.SymmetricPolynomials: prettySymmetricNumSpray :: (Num a, Ord a, Show a, C a) => Spray a -> String
+ Math.Algebra.SymmetricPolynomials: prettySymmetricParametricQSpray :: [String] -> ParametricQSpray -> String
+ Math.Algebra.SymmetricPolynomials: prettySymmetricQSpray :: QSpray -> String
+ Math.Algebra.SymmetricPolynomials: prettySymmetricQSpray' :: QSpray' -> String
+ Math.Algebra.SymmetricPolynomials: psCombination :: forall a. (Eq a, C a) => Spray a -> Map Partition a
+ Math.Algebra.SymmetricPolynomials: psCombination' :: forall a. (Eq a, C Rational a, C a) => Spray a -> Map Partition a
+ Math.Algebra.SymmetricPolynomials: psPolynomial :: (C a, Eq a) => Int -> Partition -> Spray a
+ Math.Algebra.SymmetricPolynomials: symbolicHallInnerProduct :: (Eq a, C a) => Spray a -> Spray a -> Spray a
+ Math.Algebra.SymmetricPolynomials: symbolicHallInnerProduct' :: (Eq a, C Rational (Spray a), C a) => Spray a -> Spray a -> Spray a
+ Math.Algebra.SymmetricPolynomials: symbolicHallInnerProduct'' :: forall a. Real a => Spray a -> Spray a -> QSpray

Files

CHANGELOG.md view
@@ -57,4 +57,16 @@ version)
 
 * added the Laplace-Beltrami operator and the Calogero-Sutherland operator; 
-the Jack polynomials are eigenpolynomials of these operators+the Jack polynomials are eigenpolynomials of these operators
+
+1.4.1.0
+-------
+* new function `psPolynomial`, to get a power sum symmetric polynomial
+
+* new function `psCombination`, to get a symmetric polynomial as a linear 
+combination of some power sum polynomials
+
+* new function `hallInnerProduct`, to compute the Hall inner product between 
+two symmetric polynomials, and there is also the function 
+`symbolicHallInnerProduct`, to get the Hall inner product with a symbolic 
+parameter
README.md view
@@ -88,7 +88,7 @@ 
 ```haskell
 import Math.Algebra.JackPol
-import Math.Algebra.Jack.SymmetricPolynomials
+import Math.Algebra.SymmetricPolynomials
 jp = jackPol' 3 [3, 1, 1] 2 'J'
 putStrLn $ prettySymmetricQSpray jp
 -- 42*M[3,1,1] + 28*M[2,2,1]
@@ -98,7 +98,7 @@ 
 ```haskell
 import Math.Algebra.JackSymbolicPol
-import Math.Algebra.Jack.SymmetricPolynomials
+import Math.Algebra.SymmetricPolynomials
 jp = jackSymbolicPol' 3 [3, 1, 1] 'J'
 putStrLn $ prettySymmetricParametricQSpray ["a"] jp
 -- { [ 4*a^2 + 10*a + 6 ] }*M[3,1,1] + { [ 8*a + 12 ] }*M[2,2,1]
@@ -108,6 +108,76 @@ they do not check the symmetry. This new module provides the function 
 `isSymmetricSpray` to check the symmetry of a polynomial, much more efficient 
 than the function with the same name in the **hspray** package.
+
+
+### Hall inner product
+
+As of version 1.4.1.0, the package provides an implementation of the Hall 
+inner product with parameter. It is known that the Jack polynomials with 
+Jack parameter $\alpha$ are orthogonal for the Hall inner product with 
+parameter $\alpha$. 
+
+There is a function `hallInnerProduct` as well as a function 
+`symbolicHallInnerProduct`. The latter allows to get the Hall inner product 
+of two symmetric polynomials without substituting a value to the parameter 
+$\alpha$. The Hall inner product of two symmetric polynomials is a polynomial 
+in $\alpha$, so the result of `symbolicHallInnerProduct` is a `Spray` object.
+
+Let's see a first example with a power sum polynomial. These symmetric 
+polynomials are implemented in the package. We display the result by using 
+`alpha` to denote the parameter of the Hall product.
+
+```haskell
+import Math.Algebra.SymmetricPolynomials 
+import Math.Algebra.Hspray hiding (psPolynomial)
+psPoly = psPolynomial 4 [2, 1, 1] :: QSpray
+hip = symbolicHallInnerProduct psPoly psPoly
+putStrLn $ prettyQSprayXYZ ["alpha"] hip
+-- 4*alpha^3
+```
+
+Now let's consider the following situation. We want to get the symbolic Hall 
+inner product of a Jack polynomial with itself, and we deal with a symbolic 
+Jack parameter in this polynomial. We denote it by `t` to distinguish it from 
+the parameter of the Hall product that we still denote by `alpha`.
+
+The signature of the `symbolicHallInnerProduct` is a bit misleading:
+```haskell
+Spray a -> Spray a -> Spray a
+```
+because the `Spray a` of the output is not of the same family as the two 
+`Spray a` inputs: this is a univariate polynomial in $\alpha$. 
+
+We use the function `jackSymbolicPol'` to compute a Jack polynomial. It
+returns a `ParametricQSpray` spray, a type alias of `Spray RatioOfQSprays`.
+
+```haskell
+import Math.Algebra.JackSymbolicPol
+import Math.Algebra.SymmetricPolynomials 
+import Math.Algebra.Hspray 
+jp = jackSymbolicPol' 2 [3, 1] 'P'
+hip = symbolicHallInnerProduct jp jp
+putStrLn $ prettyParametricQSprayABCXYZ ["t"] ["alpha"] hip
+-- { [ 3*t^2 + 6*t + 11 ] %//% [ t^2 + 2*t + 1 ] }*alpha^2 + { [ 4*t^2 + 16*t + 16 ] %//% [ t^2 + 2*t + 1 ] }*alpha
+```
+
+One could be interested in computing the Hall inner product of a Jack 
+polynomial with itself when the Jack parameter and the parameter of the 
+Hall product are identical. That is, we want to take `alpha = t` in the 
+above expression. Since the symbolic Hall product is a `ParametricQSpray` 
+spray, one can substitute its variable `alpha` by a `RatioOfQSprays` 
+object. On the other hand, `t` represents a `QSpray` object, but one can 
+identify a `QSpray` to a `RatioOfQSprays` by taking the unit spray as the 
+denominator, that is, by applying the `asRatioOfSprays` function. Finally 
+we get the desired result if we evaluate the symbolic Hall product by 
+replacing `alpha` with `asRatioOfSprays (qlone 1)`, since `t` is the 
+first polynomial variable, `qlone 1`. 
+
+```haskell
+prettyRatioOfQSpraysXYZ ["t"] $ evaluate hip [asRatioOfSprays (qlone 1)]
+-- [ 3*t^4 + 10*t^3 + 27*t^2 + 16*t ] %//% [ t^2 + 2*t + 1 ]
+```
+
 
 
 ## References
jackpolynomials.cabal view
@@ -1,5 +1,5 @@ name:                jackpolynomials
-version:             1.4.0.0
+version:             1.4.1.0
 synopsis:            Jack, zonal, Schur and skew Schur polynomials
 description:         This library can evaluate Jack polynomials, zonal polynomials, Schur and skew Schur polynomials. It is also able to compute them in symbolic form.
 homepage:            https://github.com/stla/jackpolynomials#readme
@@ -17,7 +17,7 @@ library
   hs-source-dirs:      src
   exposed-modules:     Math.Algebra.Jack.HypergeoPQ
-                     , Math.Algebra.Jack.SymmetricPolynomials
+                     , Math.Algebra.SymmetricPolynomials
                      , Math.Algebra.Jack
                      , Math.Algebra.JackPol
                      , Math.Algebra.JackSymbolicPol
@@ -30,8 +30,11 @@                      , numeric-prelude >= 0.4.4 && < 0.5
                      , combinat >= 0.2.10 && < 0.3
                      , containers >= 0.6.4.1 && < 0.8
+                     , unordered-containers >= 0.2.17.0 && < 0.3
+                     , extra >= 1.7 && < 1.8
   other-extensions:    ScopedTypeVariables
                      , BangPatterns
+                     , FlexibleContexts
   default-language:    Haskell2010
   ghc-options:         -Wall
                        -Wcompat
@@ -54,6 +57,7 @@                       , hspray >= 0.5.0.0 && < 0.6.0.0
                       , hypergeomatrix >= 1.1.0.2 && < 2
                       , combinat >= 0.2.10 && < 0.3
+                      , containers >= 0.6.4.1 && < 0.8
   Default-Language:     Haskell2010
   ghc-options:         -Wall
                        -Wcompat
src/Math/Algebra/Jack.hs view
@@ -12,7 +12,7 @@ {-# LANGUAGE BangPatterns        #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 module Math.Algebra.Jack
-  (jack', zonal', schur', skewSchur', jack, zonal, schur, skewSchur)
+  (Partition, jack', zonal', schur', skewSchur', jack, zonal, schur, skewSchur)
   where
 import           Prelude 
   hiding ((*), (+), (-), (/), (^), (*>), product, sum, fromIntegral, fromInteger)
@@ -66,7 +66,7 @@       theproduct :: Int -> a
       theproduct nu0 = if nu0 <= 1
         then one
-        else product $ map (\i -> one + i .^ alpha) [1 .. nu0-1]
+        else product [one + i .^ alpha | i <- [1 .. nu0-1]]
       jac :: 
         Int -> Int -> [Int] -> [Int] -> Array (Int,Int) (Maybe a) -> a -> a
       jac m k mu nu arr beta
− src/Math/Algebra/Jack/SymmetricPolynomials.hs
@@ -1,189 +0,0 @@-{-|
-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 n lambda
-  | n < 0                     = 
-      error "msPolynomial: negative number of variables."
-  | not (_isPartition lambda) = 
-      error "msPolynomial: invalid partition."
-  | llambda > n               = zeroSpray
-  | otherwise                 = fromList $ zip permutations coefficients
-    where
-      llambda      = length lambda
-      permutations = permuteMultiset (lambda ++ replicate (n-llambda) 0)
-      coefficients = repeat 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 spray = spray == spray' 
-  where
-    assocs = msCombination' spray
-    n      = numberOfVariables spray
-    spray' = foldl1' (^+^) 
-      (
-        map (\(lambda, x) -> x *^ msPolynomial n lambda) assocs
-      )
-
--- | Symmetric polynomial as a linear combination of monomial symmetric polynomials
-msCombination :: AlgRing.C a => Spray a -> Map Partition a
-msCombination spray = DM.fromList (msCombination' spray)
-
-msCombination' :: AlgRing.C a => Spray a -> [(Partition, a)]
-msCombination' spray = 
-  map (\lambda -> (lambda, getCoefficient lambda spray)) lambdas
-  where
-    lambdas = nub $ map (fromPartition . mkPartition . fst) (toList spray)
-
--- helper function for the showing stuff
-makeMSpray :: (Eq a, AlgRing.C a) => Spray a -> Spray a
-makeMSpray = fromList . msCombination'
-
--- show symmetric monomial like M[3,2,1]
-showSymmetricMonomials :: [Seq Int] -> [String]
-showSymmetricMonomials = map showSymmetricMonomial
-  where
-    showSymmetricMonomial :: Seq Int -> String
-    showSymmetricMonomial lambda = 'M' : show (DF.toList 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 spray = 
-  showNumSpray showSymmetricMonomials show mspray
-  where
-    mspray = makeMSpray 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 spray = showQSpray showSymmetricMonomials mspray
-  where
-    mspray = makeMSpray spray
-
--- | Same as `prettySymmetricQSpray` but for a `QSpray'` symmetric spray
-prettySymmetricQSpray' :: QSpray' -> String
-prettySymmetricQSpray' spray = showQSpray' showSymmetricMonomials mspray
-  where
-    mspray = makeMSpray 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 letters spray = 
-  showSpray (prettyRatioOfQSpraysXYZ letters) ("{ ", " }") 
-            showSymmetricMonomials mspray
-  where
-    mspray = makeMSpray 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 alpha spray = 
-  if isConstant spray 
-    then zeroSpray 
-    else alpha' *^ spray1 ^+^ spray2
-  where
-    alpha' = alpha AlgField./ AlgRing.fromInteger 2
-    n = numberOfVariables spray
-    range = [1 .. n]
-    dsprays = map (`derivative` spray) range
-    op1 i = lone' i 2 ^*^ derivative i (dsprays !! (i-1))
-    spray1 = AlgAdd.sum (map op1 range)
-    spray2 = _numerator $ AlgAdd.sum 
-              [(lone' i 2 ^*^ dsprays !! (i-1)) %//% (lone i ^-^ lone j)
-                | i <- range, j <- range, i /= 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 alpha spray = 
-  if isConstant spray 
-    then zeroSpray
-    else halfSpray $ alpha *^ spray1 ^+^ spray2
-  where
-    halfSpray p = p /^ AlgRing.fromInteger 2
-    n = numberOfVariables spray
-    range = [1 .. n]
-    dsprays = map (`derivative` spray) range
-    op0 p i = lone i ^*^ derivative i p 
-    op1 p i = op0 (op0 p i) i
-    spray1 = AlgAdd.sum (map (op1 spray) range)
-    spray2 = _numerator $ AlgAdd.sum 
-      [let (xi, xj, dxi, dxj) = 
-            (lone i, lone j, dsprays !! (i-1), dsprays !! (j-1)) in 
-          (xi ^+^ xj) ^*^ (xi ^*^ dxi ^-^ xj ^*^ dxj) %//% (xi ^-^ xj)
-       | i <- range, j <- [i+1 .. n]]
src/Math/Algebra/JackPol.hs view
@@ -67,7 +67,7 @@       theproduct :: Int -> a
       theproduct nu0 = if nu0 <= 1
         then one
-        else product $ map (\i -> i .^ alpha + one) [1 .. nu0-1]
+        else product [i .^ alpha + one | i <- [1 .. nu0-1]]
       jac :: Int -> Int -> Partition -> Partition 
               -> Array (Int,Int) (Maybe (Spray a)) -> a -> Spray a
       jac m k mu nu arr beta
@@ -146,8 +146,8 @@           Int -> Int -> [Int] -> Array (Int,Int) (Maybe (Spray a)) -> Spray a
         sch m k nu arr
           | null nu || nu!!0 == 0 || m == 0 = unitSpray
-          | length nu > m && nu!!m > 0 = zeroSpray
-          | m == 1 = x!!0 ^**^ nu!!0
+          | length nu > m && nu !! m > 0 = zeroSpray
+          | m == 1 = x !! 0 ^**^ nu !! 0
           | isJust (arr ! (_N lambda nu, m)) = 
               fromJust $ arr ! (_N lambda nu, m)
           | otherwise = s
src/Math/Algebra/JackSymbolicPol.hs view
@@ -68,9 +68,8 @@       theproduct :: Int -> RatioOfSprays a
       theproduct nu0 = if nu0 <= 1
         then unitRatioOfSprays
-        else asRatioOfSprays $ product $ map 
-              (\i -> i .^ alpha ^+^ unitSpray) 
-              [1 .. nu0-1]
+        else asRatioOfSprays $ product
+              [i .^ alpha ^+^ unitSpray | i <- [1 .. nu0-1]]
       jac :: Int -> Int -> Partition -> Partition 
              -> Array (Int,Int) (Maybe (ParametricSpray a)) 
              -> RatioOfSprays a -> ParametricSpray a
+ src/Math/Algebra/SymmetricPolynomials.hs view
@@ -0,0 +1,500 @@+{-|
+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 initial 
+motivation of this module. But now it contains more stuff dealing with 
+symmetric polynomials.
+-}
+{-# LANGUAGE FlexibleContexts    #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Math.Algebra.SymmetricPolynomials
+  ( 
+  -- * Checking symmetry
+    isSymmetricSpray
+  -- * Classical symmetric polynomials
+  , msPolynomial
+  , psPolynomial
+  -- * Decomposition of symmetric polynomials
+  , msCombination
+  , psCombination
+  , psCombination'
+  -- * Printing symmetric polynomials
+  , prettySymmetricNumSpray
+  , prettySymmetricQSpray
+  , prettySymmetricQSpray'
+  , prettySymmetricParametricQSpray
+  -- * Operators on the space of symmetric polynomials
+  , laplaceBeltrami
+  , calogeroSutherland
+  -- * Hall inner product of symmetric polynomials
+  , hallInnerProduct
+  , hallInnerProduct'
+  , hallInnerProduct''
+  , hallInnerProduct'''
+  , hallInnerProduct''''
+  , symbolicHallInnerProduct
+  , symbolicHallInnerProduct'
+  , symbolicHallInnerProduct''
+  ) where
+import           Prelude hiding ( fromIntegral, fromRational )
+import qualified Algebra.Additive                 as AlgAdd
+import           Algebra.Field                    ( fromRational )
+import qualified Algebra.Field                    as AlgField
+import qualified Algebra.Module                   as AlgMod
+import qualified Algebra.Ring                     as AlgRing
+import           Algebra.ToInteger                ( fromIntegral )
+import qualified Data.Foldable                    as DF
+import qualified Data.HashMap.Strict              as HM
+import           Data.List                        ( foldl1', nub )
+import           Data.List.Extra                  ( unsnoc )
+import           Data.Map.Strict                  ( 
+                                                    Map
+                                                  , unionsWith
+                                                  , insert
+                                                  )
+import qualified Data.Map.Strict                  as DM
+import           Data.Maybe                       ( fromJust )
+import           Data.Map.Merge.Strict            ( 
+                                                    merge
+                                                  , dropMissing
+                                                  , zipWithMatched 
+                                                  )
+import           Data.Ratio                       ( (%) )
+import           Data.Sequence                    ( 
+                                                    Seq
+                                                  , (|>) 
+                                                  )
+import qualified Data.Sequence                    as S
+import           Data.Tuple.Extra                 ( second )
+import           Math.Algebra.Hspray              (
+                                                    FunctionLike (..)
+                                                  , (/^)
+                                                  , Spray
+                                                  , Powers (..)
+                                                  , QSpray
+                                                  , QSpray'
+                                                  , ParametricQSpray
+                                                  , lone
+                                                  , qlone
+                                                  , lone'
+                                                  , fromList
+                                                  , getCoefficient
+                                                  , getConstantTerm
+                                                  , isConstant
+                                                  , (%//%)
+                                                  , RatioOfSprays (..)
+                                                  , prettyRatioOfQSpraysXYZ
+                                                  , showNumSpray
+                                                  , showQSpray
+                                                  , showQSpray'
+                                                  , showSpray
+                                                  , toList
+                                                  , zeroSpray
+                                                  , unitSpray
+                                                  , productOfSprays
+                                                  , constantSpray
+                                                  )
+import           Math.Algebra.Jack.Internal       ( Partition , _isPartition )
+import           Math.Combinat.Compositions       ( compositions1 )
+import           Math.Combinat.Partitions.Integer ( 
+                                                    fromPartition
+                                                  , mkPartition
+                                                  , partitions 
+                                                  )
+import           Math.Combinat.Permutations       ( permuteMultiset )
+
+-- | Monomial symmetric polynomial
+--
+-- >>> 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 n lambda
+  | n < 0                     = 
+      error "msPolynomial: negative number of variables."
+  | not (_isPartition lambda) = 
+      error "msPolynomial: invalid partition."
+  | llambda > n               = zeroSpray
+  | otherwise                 = fromList $ zip permutations coefficients
+    where
+      llambda      = length lambda
+      permutations = permuteMultiset (lambda ++ replicate (n-llambda) 0)
+      coefficients = repeat 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 spray = spray == spray' 
+  where
+    assocs = msCombination' spray
+    n      = numberOfVariables spray
+    spray' = foldl1' (^+^) 
+      (
+        map (\(lambda, x) -> x *^ msPolynomial n lambda) assocs
+      )
+
+-- | Symmetric polynomial as a linear combination of monomial symmetric polynomials
+msCombination :: AlgRing.C a => Spray a -> Map Partition a
+msCombination spray = DM.fromList (msCombination' spray)
+
+msCombination' :: AlgRing.C a => Spray a -> [(Partition, a)]
+msCombination' spray = 
+  map (\lambda -> (lambda, getCoefficient lambda spray)) lambdas
+  where
+    lambdas = nub $ map (fromPartition . mkPartition . fst) (toList spray)
+
+-- helper function for the showing stuff
+makeMSpray :: (Eq a, AlgRing.C a) => Spray a -> Spray a
+makeMSpray = fromList . msCombination'
+
+-- show symmetric monomial like M[3,2,1]
+showSymmetricMonomials :: [Seq Int] -> [String]
+showSymmetricMonomials = map showSymmetricMonomial
+  where
+    showSymmetricMonomial :: Seq Int -> String
+    showSymmetricMonomial lambda = 'M' : show (DF.toList 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 spray = 
+  showNumSpray showSymmetricMonomials show mspray
+  where
+    mspray = makeMSpray 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 spray = showQSpray showSymmetricMonomials mspray
+  where
+    mspray = makeMSpray spray
+
+-- | Same as `prettySymmetricQSpray` but for a `QSpray'` symmetric spray
+prettySymmetricQSpray' :: QSpray' -> String
+prettySymmetricQSpray' spray = showQSpray' showSymmetricMonomials mspray
+  where
+    mspray = makeMSpray 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 letters spray = 
+  showSpray (prettyRatioOfQSpraysXYZ letters) ("{ ", " }") 
+            showSymmetricMonomials mspray
+  where
+    mspray = makeMSpray 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 alpha spray = 
+  if isConstant spray 
+    then zeroSpray 
+    else alpha' *^ spray1 ^+^ spray2
+  where
+    alpha' = alpha AlgField./ AlgRing.fromInteger 2
+    n = numberOfVariables spray
+    range = [1 .. n]
+    dsprays = map (`derivative` spray) range
+    op1 i = lone' i 2 ^*^ derivative i (dsprays !! (i-1))
+    spray1 = AlgAdd.sum (map op1 range)
+    spray2 = _numerator $ AlgAdd.sum 
+              [(lone' i 2 ^*^ dsprays !! (i-1)) %//% (lone i ^-^ lone j)
+                | i <- range, j <- range, i /= 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 alpha spray = 
+  if isConstant spray 
+    then zeroSpray
+    else halfSpray $ alpha *^ spray1 ^+^ spray2
+  where
+    halfSpray p = p /^ AlgRing.fromInteger 2
+    n = numberOfVariables spray
+    range = [1 .. n]
+    dsprays = map (`derivative` spray) range
+    op0 p i = lone i ^*^ derivative i p 
+    op1 p i = op0 (op0 p i) i
+    spray1 = AlgAdd.sum (map (op1 spray) range)
+    spray2 = _numerator $ AlgAdd.sum 
+      [let (xi, xj, dxi, dxj) = 
+            (lone i, lone j, dsprays !! (i-1), dsprays !! (j-1)) in 
+          (xi ^+^ xj) ^*^ (xi ^*^ dxi ^-^ xj ^*^ dxj) %//% (xi ^-^ xj)
+       | i <- range, j <- [i+1 .. n]]
+
+-- | Power sum polynomial
+--
+-- >>> putStrLn $ prettyQSpray (psPolynomial 3 [2, 1])
+-- x^3 + x^2.y + x^2.z + x.y^2 + x.z^2 + y^3 + y^2.z + y.z^2 + z^3
+psPolynomial :: (AlgRing.C a, Eq a) 
+  => Int       -- ^ number of variables
+  -> Partition -- ^ integer partition
+  -> Spray a
+psPolynomial n lambda
+  | n < 0                     = 
+      error "psPolynomial: negative number of variables."
+  | not (_isPartition lambda) = 
+      error "psPolynomial: invalid partition."
+  | null lambda'              = unitSpray
+  | llambda > n               = zeroSpray
+  | otherwise                 = productOfSprays sprays
+    where
+      lambda' = fromPartition $ mkPartition lambda
+      llambda = length lambda'
+      sprays = [HM.fromList $ [f i k | i <- [1 .. n]] | k <- lambda']
+      f j k = (Powers expts j, AlgRing.one)
+        where
+          expts = S.replicate (j-1) 0 |> k
+
+-- | monomial symmetric polynomial as a linear combination of 
+-- power sum polynomials
+mspInPSbasis :: Partition -> Map Partition Rational
+mspInPSbasis kappa = DM.fromList (zipWith f weights lambdas)
+  where
+    parts = map fromPartition (partitions (sum kappa))
+    (weights, lambdas) = unzip $ filter ((/= 0) . fst) 
+      [(eLambdaMu kappa lambda, lambda) | lambda <- parts]
+    f weight lambda = 
+      (lambda, weight / toRational (zlambda lambda))
+    ----
+    eLambdaMu :: Partition -> Partition -> Rational
+    eLambdaMu lambda mu 
+      | ellLambda < ellMu = 0
+      | otherwise = if even (ellLambda - ellMu) 
+          then sum xs 
+          else - sum xs
+      where
+        ellLambda = length lambda
+        ellMu     = length mu
+        compos = compositions1 ellMu ellLambda
+        lambdaPerms = permuteMultiset lambda
+        sequencesOfPartitions = filter (not . null)
+          [partitionSequences perm mu compo 
+            | perm <- lambdaPerms, compo <- compos]
+        xs = [eMuNus mu nus | nus <- sequencesOfPartitions]
+    ----
+    partitionSequences :: [Int] -> Partition -> [Int] -> [Partition]
+    partitionSequences lambda mu compo = if test then nus else []
+      where
+        headOfCompo = fst $ fromJust (unsnoc compo)
+        starts = scanl (+) 0 headOfCompo 
+        ends   = zipWith (+) starts compo
+        nus = [ 
+                [ lambda !! k | k <- [starts !! i .. ends !! i - 1] ] 
+                | i <- [0 .. length compo - 1]
+              ]
+        nuWeights = [sum nu | nu <- nus]
+        decreasing xs = 
+          and [xs !! i >= xs !! (i+1) | i <- [0 .. length xs - 2]]
+        test = and (zipWith (==) mu nuWeights) && all decreasing nus
+    ---- 
+    eMuNus :: Partition -> [Partition] -> Rational
+    eMuNus mu nus = product toMultiply
+      where
+        w :: Int -> Partition -> Rational
+        w k nu = 
+          let table = [sum [fromEnum (i == j) | i <- nu] | j <- nub nu] in
+          (toInteger $ k * factorial (length nu - 1)) % 
+            (toInteger $ product (map factorial table))
+        factorial n = product [2 .. n]
+        toMultiply = zipWith w mu nus
+
+-- | the factor in the Hall inner product
+zlambda :: Partition -> Int
+zlambda lambda = p
+  where
+    parts = nub lambda
+    table = [sum [fromEnum (k == j) | k <- lambda] | j <- parts]
+    p =  
+      product [factorial mj * part^mj | (part, mj) <- zip parts table]
+    factorial n = product [2 .. n]
+
+-- | symmetric polynomial as a linear combination of power sum polynomials
+_psCombination :: 
+  forall a. (Eq a, AlgRing.C a) => (a -> Rational -> a) -> Spray a -> Map Partition a
+_psCombination func spray =
+  if constantTerm == AlgAdd.zero 
+    then psMap
+    else insert [] constantTerm psMap
+  where
+    constantTerm = getConstantTerm spray
+    assocs = msCombination' (spray <+ (AlgAdd.negate constantTerm))
+    f :: (Partition, a) -> [(Partition, a)] 
+    f (lambda, coeff) = 
+      map (second (func coeff)) (DM.toList psCombo)
+      where
+        psCombo = mspInPSbasis lambda :: Map Partition Rational
+    psMap = DM.filter (/= AlgAdd.zero) 
+            (unionsWith (AlgAdd.+) (map (DM.fromList . f) assocs))
+
+-- | Symmetric polynomial as a linear combination of power sum polynomials. 
+-- Symmetry is not checked.
+psCombination :: 
+  forall a. (Eq a, AlgField.C a) => Spray a -> Map Partition a
+psCombination = 
+  _psCombination (\coef r -> coef AlgRing.* fromRational r)
+
+-- | Symmetric polynomial as a linear combination of power sum polynomials. 
+-- Same as @psCombination@ but with other constraints on the base ring of the spray.
+psCombination' :: 
+  forall a. (Eq a, AlgMod.C Rational a, AlgRing.C a) 
+  => Spray a -> Map Partition a
+psCombination' = _psCombination (flip (AlgMod.*>))
+
+-- | the Hall inner product with parameter
+_hallInnerProduct :: 
+  forall a b. (AlgRing.C b, AlgRing.C a)
+  => (Spray b -> Map Partition b)
+  -> (a -> b -> b)
+  -> Spray b   -- ^ spray
+  -> Spray b   -- ^ spray
+  -> a         -- ^ parameter
+  -> b 
+_hallInnerProduct psCombinationFunc multabFunc spray1 spray2 alpha = 
+  AlgAdd.sum $ DM.elems
+    (merge dropMissing dropMissing (zipWithMatched f) psCombo1 psCombo2)
+  where
+    psCombo1 = psCombinationFunc spray1 :: Map Partition b
+    psCombo2 = psCombinationFunc spray2 :: Map Partition b
+    zlambda' :: Partition -> a
+    zlambda' lambda = fromIntegral (zlambda lambda) 
+      AlgRing.* alpha AlgRing.^ (toInteger $ length lambda)
+    f :: Partition -> b -> b -> b
+    f lambda coeff1 coeff2 = 
+      multabFunc (zlambda' lambda) (coeff1 AlgRing.* coeff2)
+
+-- | Hall inner product with parameter. It makes sense only for symmetric sprays,
+-- and the symmetry is not checked. 
+hallInnerProduct :: 
+  forall a. (Eq a, AlgField.C a)
+  => Spray a   -- ^ spray
+  -> Spray a   -- ^ spray
+  -> a         -- ^ parameter
+  -> a 
+hallInnerProduct = _hallInnerProduct psCombination (AlgRing.*)
+
+-- | Hall inner product with parameter. Same as @hallInnerProduct@ but 
+-- with other constraints on the base ring of the sprays.
+hallInnerProduct' :: 
+  forall a. (Eq a, AlgMod.C Rational a, AlgRing.C a)
+  => Spray a   -- ^ spray
+  -> Spray a   -- ^ spray
+  -> a         -- ^ parameter
+  -> a 
+hallInnerProduct' = _hallInnerProduct psCombination' (AlgRing.*)
+
+-- | Hall inner product with parameter. Same as @hallInnerProduct@ but 
+-- with other constraints on the base ring of the sprays. It is applicable 
+-- to @Spray Int@ sprays.
+hallInnerProduct'' :: 
+  forall a. (Real a)
+  => Spray a   -- ^ spray
+  -> Spray a   -- ^ spray
+  -> a         -- ^ parameter
+  -> Rational 
+hallInnerProduct'' spray1 spray2 alpha = 
+  _hallInnerProduct 
+    (_psCombination (*)) (*) qspray1 qspray2 (toRational alpha)
+  where
+    asQSpray :: Spray a -> QSpray
+    asQSpray = HM.map toRational
+    qspray1 = asQSpray spray1
+    qspray2 = asQSpray spray2
+
+-- | Hall inner product with parameter for parametric sprays, because the
+-- type of the parameter in @hallInnerProduct@ is strange. For example, a
+-- @ParametricQSpray@ spray is a @Spray RatioOfQSprays@ spray, and it makes
+-- more sense to compute the Hall product with a @Rational@ parameter then 
+-- to compute the Hall product with a @RatioOfQSprays@ parameter.
+--
+-- >>> import Math.Algebra.Jack.SymmetricPolynomials
+-- >>> import Math.Algebra.JackSymbolicPol
+-- >>> import Math.Algebra.Hspray
+-- >>> jp = jackSymbolicPol 3 [2, 1] 'P'
+-- >>> hallInnerProduct''' jp jp 5 == hallInnerProduct jp jp (constantRatioOfSprays 5)
+hallInnerProduct''' :: 
+  forall b. (Eq b, AlgField.C b, AlgMod.C (BaseRing b) b)
+  => Spray b    -- ^ parametric spray
+  -> Spray b    -- ^ parametric spray
+  -> BaseRing b -- ^ parameter
+  -> b 
+hallInnerProduct''' = _hallInnerProduct psCombination (AlgMod.*>) 
+
+-- | Hall inner product with parameter for parametric sprays. Same as 
+-- @hallInnerProduct'''@ but with other constraints on the types. It is 
+-- applicable to @SimpleParametricQSpray@ sprays, while @hallInnerProduct'''@ 
+-- is not.
+hallInnerProduct'''' :: 
+  forall b. (Eq b, AlgRing.C b, AlgMod.C Rational b, AlgMod.C (BaseRing b) b)
+  => Spray b    -- ^ parametric spray
+  -> Spray b    -- ^ parametric spray
+  -> BaseRing b -- ^ parameter
+  -> b 
+hallInnerProduct'''' = _hallInnerProduct psCombination' (AlgMod.*>) 
+
+-- | the Hall inner product with symbolic parameter
+_symbolicHallInnerProduct :: 
+  (Eq a, AlgRing.C a) 
+  => (Spray (Spray a) -> Spray (Spray a) -> Spray a -> Spray a) 
+  -> Spray a -> Spray a -> Spray a
+_symbolicHallInnerProduct func spray1 spray2 = func spray1' spray2' (lone 1)
+  where
+    spray1' = HM.map constantSpray spray1
+    spray2' = HM.map constantSpray spray2
+
+-- | Hall inner product with symbolic parameter. See README for some examples.
+symbolicHallInnerProduct :: 
+  (Eq a, AlgField.C a) => Spray a -> Spray a -> Spray a
+symbolicHallInnerProduct =
+  _symbolicHallInnerProduct 
+    (
+      _hallInnerProduct 
+        (_psCombination (\spray_a r -> fromRational r *^ spray_a)) (^*^)
+    ) 
+
+-- | Hall inner product with symbolic parameter. Same as @symbolicHallInnerProduct@ 
+-- but with other type constraints.
+symbolicHallInnerProduct' :: 
+  (Eq a, AlgMod.C Rational (Spray a), AlgRing.C a) 
+  => Spray a -> Spray a -> Spray a
+symbolicHallInnerProduct' =  _symbolicHallInnerProduct (hallInnerProduct')
+
+-- | Hall inner product with symbolic parameter. Same as @symbolicHallInnerProduct@ 
+-- but with other type constraints. It is applicable to @Spray Int@ sprays.
+symbolicHallInnerProduct'' :: forall a. Real a => Spray a -> Spray a -> QSpray
+symbolicHallInnerProduct'' spray1 spray2 = 
+  _hallInnerProduct 
+    (_psCombination (\qspray r -> r *^ qspray)) (^*^)
+    qspray1' qspray2' (qlone 1)
+  where
+    asQSpray :: Spray a -> QSpray
+    asQSpray = HM.map toRational
+    qspray1' = HM.map constantSpray (asQSpray spray1)
+    qspray2' = HM.map constantSpray (asQSpray spray2)
+
+
+-- test'' :: (String, String)
+-- test'' = (prettyParametricQSpray result, prettyParametricQSprayABCXYZ ["a"] ["b"] $ result)
+--   where 
+--     jsp = jackSymbolicPol' 3 [2, 1] 'P'
+--     result = hallSymbolic'' jsp jsp
tests/Main.hs view
@@ -1,21 +1,33 @@ module Main ( main ) where
+import qualified Data.Map.Strict                as DM
 import Data.Ratio                               ( (%) )
 import Math.Algebra.Hspray                      ( FunctionLike (..)
-                                                , Spray, lone
+                                                , Spray, QSpray
+                                                , lone, qlone 
                                                 , evalSpray 
                                                 , evalParametricSpray'
                                                 , substituteParameters
                                                 , canCoerceToSimpleParametricSpray
                                                 , isHomogeneousSpray
+                                                , asRatioOfSprays
+                                                , (%//%)
+                                                , (/^)
                                                 )
 import qualified Math.Algebra.Hspray            as Hspray
 import Math.Algebra.Jack                        ( schur, skewSchur 
                                                 , jack', zonal' )
 import Math.Algebra.Jack.HypergeoPQ             ( hypergeoPQ )
-import Math.Algebra.Jack.SymmetricPolynomials   ( isSymmetricSpray
+import Math.Algebra.SymmetricPolynomials        ( isSymmetricSpray
                                                 , prettySymmetricParametricQSpray
                                                 , laplaceBeltrami
-                                                , calogeroSutherland )
+                                                , calogeroSutherland
+                                                , hallInnerProduct
+                                                , hallInnerProduct''
+                                                , symbolicHallInnerProduct
+                                                , symbolicHallInnerProduct''
+                                                , psPolynomial 
+                                                , psCombination
+                                                )
 import Math.Algebra.JackPol                     ( zonalPol, zonalPol', jackPol'
                                                 , schurPol, schurPol', skewSchurPol' )
 import Math.Algebra.JackSymbolicPol             ( jackSymbolicPol' )
@@ -130,7 +142,8 @@         sp3 = schurPol 4 [2, 2]
         sp4 = schurPol 4 [2, 1, 1]
         sp5 = schurPol 4 [1, 1, 1, 1] :: Spray Int
-        v = evalSpray (sp1 ^+^ 3 *^ sp2 ^+^ 2 *^ sp3 ^+^ 3 *^ sp4 ^+^ sp5) [2, 2, 2, 2]
+        v = evalSpray (sp1 ^+^ 3 *^ sp2 ^+^ 2 *^ sp3 ^+^ 3 *^ sp4 ^+^ sp5) 
+                        [2, 2, 2, 2]
     assertEqual "" v 4096
 
   , testCase "schurPol is symmetric (Groebner)" $ do
@@ -154,8 +167,9 @@         y = lone 2 :: Spray Rational
         z = lone 3 :: Spray Rational
         skp = skewSchurPol' 3 [2, 2, 1] [1, 1]
-        p = x^**^2 ^*^ y  ^+^  x^**^2 ^*^ z  ^+^  x ^*^ y^**^2  ^+^  3 *^ (x ^*^ y ^*^ z) 
-            ^+^  x ^*^ z^**^2  ^+^  y^**^2 ^*^ z  ^+^  y ^*^ z^**^2
+        p = x^**^2 ^*^ y  ^+^  x^**^2 ^*^ z  ^+^  x ^*^ y^**^2  
+            ^+^  3 *^ (x ^*^ y ^*^ z)  ^+^  x ^*^ z^**^2  
+            ^+^  y^**^2 ^*^ z  ^+^  y ^*^ z^**^2
     assertEqual "" skp p 
 
   , testCase "skewSchurPol is symmetric (Groebner)" $ do
@@ -182,4 +196,99 @@         h1 = hypergeoPQ 10 a b x :: Rational
     h2 <- hypergeomat 10 2 a b x
     assertEqual "" h1 h2
+
+  , testCase "Hall inner product" $ do
+    let
+      alpha = 2
+      poly1 = psPolynomial 4 [4]
+      poly2 = psPolynomial 4 [3, 1]
+      poly3 = psPolynomial 4 [2, 2]
+      poly4 = psPolynomial 4 [2, 1, 1]
+      poly5 = psPolynomial 4 [1, 1, 1, 1]
+      h1 = hallInnerProduct poly1 poly1 alpha
+      h2 = hallInnerProduct poly2 poly2 alpha
+      h3 = hallInnerProduct poly3 poly3 alpha
+      h4 = hallInnerProduct poly4 poly4 alpha
+      h5 = hallInnerProduct poly5 poly5 alpha
+      pow :: Rational -> Int -> Rational
+      pow = (^)
+    assertEqual ""
+      (h1, h2, h3, h4, h5) 
+      (
+        4 * alpha
+      , 3 * pow alpha 2
+      , 8 * pow alpha 2
+      , 4 * pow alpha 3
+      , 24 * pow alpha 4
+      )
+
+  , testCase "Hall inner product of Schur polynomials" $ do
+    let
+      sp1 = schurPol 7 [4, 2, 1] :: Spray Int
+      sp2 = schurPol 7 [2, 2, 2, 1] :: Spray Int
+      h1 = hallInnerProduct'' sp1 sp1 1
+      h2 = hallInnerProduct'' sp2 sp2 1
+      h12 = hallInnerProduct'' sp1 sp2 1
+    assertEqual "" (h1, h2, h12) (1, 1, 0)
+
+  , testCase "Symbolic Hall inner product" $ do
+    let
+      poly1 = psPolynomial 4 [4] :: QSpray
+      poly2 = psPolynomial 4 [3, 1] :: QSpray
+      poly3 = psPolynomial 4 [2, 2] :: QSpray
+      poly4 = psPolynomial 4 [2, 1, 1] :: QSpray
+      poly5 = psPolynomial 4 [1, 1, 1, 1] :: QSpray
+      h1 = symbolicHallInnerProduct poly1 poly1
+      h2 = symbolicHallInnerProduct poly2 poly2
+      h3 = symbolicHallInnerProduct poly3 poly3
+      h4 = symbolicHallInnerProduct poly4 poly4
+      h5 = symbolicHallInnerProduct poly5 poly5
+      alpha = qlone 1
+    assertEqual ""
+      (h1, h2, h3, h4, h5) 
+      (
+        4 *^ alpha
+      , 3 *^ alpha^**^2
+      , 8 *^ alpha^**^2
+      , 4 *^ alpha^**^3
+      , 24 *^ alpha^**^4
+      )
+
+  , testCase "Symbolic Hall inner product of Schur polynomials" $ do
+    let
+      sp1 = schurPol 7 [4, 2, 1] :: Spray Int
+      sp2 = schurPol 7 [2, 2, 2, 1] :: Spray Int
+      h1 = evaluateAt [1] (symbolicHallInnerProduct'' sp1 sp1)
+      h2 = evaluateAt [1] (symbolicHallInnerProduct'' sp2 sp2)
+      h12 = evaluateAt [1] (symbolicHallInnerProduct'' sp1 sp2)
+    assertEqual "" (h1, h2, h12) (1, 1, 0)
+
+  , testCase "Symbolic Hall inner product with parametric sprays" $ do
+    let
+      jp1 = jackSymbolicPol' 3 [1, 1, 1] 'P'
+      jp2 = jackSymbolicPol' 3 [2, 1] 'P'
+      jp3 = jackSymbolicPol' 3 [3] 'P'
+      t = qlone 1
+      t' = asRatioOfSprays t
+      h1 = hallInnerProduct jp1 jp1 t'
+      h2 = hallInnerProduct jp2 jp2 t'
+      h3 = hallInnerProduct jp3 jp3 t'
+    assertEqual ""
+      (h1, h2, h3) 
+      (
+        asRatioOfSprays (t^**^3 /^ 6 ^+^ t^**^2 /^ 2 ^+^ t /^ 3)
+      , (2*^t^**^3 ^+^ t^**^2) %//% (t <+ 2)
+      , (3*^t^**^3) %//% ((t^**^2 ^+^ (3%2)*^t) <+ (1%2))
+      )
+
+  , testCase "Power sum polynomial and power sum combination" $ do
+    let
+      psPoly = 3*^psPolynomial 4 [2, 1, 1] ^-^ psPolynomial 4 [2, 1] :: QSpray
+      psCombo = psCombination psPoly
+    assertEqual ""
+      psCombo 
+      (
+        DM.fromList [([2, 1, 1], 3), ([2, 1], -1)]
+      )
+
   ]