{-|
Module : Kh
Description : Implements the Khovanov "functor".
Copyright : Adam Saltz
License : BSD3
Maintainer : saltz.adam@gmail.com
Stability : experimental
Longer description to come.
-}
module Kh
where
import Util
import Complex
import Braids
import Data.Set (Set, (\\))
import Data.List (sort)
import qualified Data.Set as S
import Data.Map (Map, (!))
import qualified Data.Map as M
import Control.Monad
-- | Return the diagram underlying a 'Generator'.
gToD :: Generator -> BDiagram
gToD g = BDiagram {resolution' = resolution g, components' = components g}
-- | Compute the homological grading of a 'Generator'.
homGrading :: Generator -> Int
homGrading gen = sum . resolution $ gen
-- | Compute the q-grading of a 'Generator'. In this convention, the Khovanov differential *lowers* the q-grading by 1.
qGrading :: Generator -> Int
qGrading gen = sum . fmap signToNum . signs $ gen where
-- | Data type to represent whether a circle is trivial or not. Used to be Bool' but I got confused about which was @True@ and which was @False@.
data Triviality = Trivial | NonTrivial deriving (Ord, Eq, Show)
{- | Determine if a component is non-trivial.
To compute the mod 2 winding number about the braid axis, check how many of the \'top arcs\' live in a component. -}
nonTrivialCircle :: Int -> Component -> Triviality
nonTrivialCircle width arcs = if odd $ length (filter (`elem` [1..width]) (S.toList arcs))
then NonTrivial
else Trivial
-- | Compute the k-grading of a 'Generator'.
kGrading :: (Resolution, Set Component, Map Component Sign) -> Int -> Int
kGrading (_,_,ss) width = ks ! NonTrivial where
ks = M.mapKeysWith (+) (nonTrivialCircle width) . fmap signToNum $ ss
{- | "Apply the filtered Khovanov functor to a diagram."
We still need the braid width as input to get the k-grading. -}
khovanov :: Int -> BDiagram -> [Generator]
khovanov width d = do
ss <- generateSigns . components' $ d
return Generator {resolution = resolution' d,
components = components' d,
signs = M.fromList $ zip (S.toList $ components' d) ss,
kgrade = kGrading (resolution' d, components' d, M.fromList $ zip (S.toList $ components' d) ss) width}
where
generateSigns cs = Control.Monad.replicateM (length cs) [Minus,Plus] -- this is a slick and dumb way of making sequences of (-1) and 1
{-
-- | Returns the sign at mark.
markedSign :: Int -> Generator -> Int
markedSign mark gen = case findIndex (elem mark) (components gen) of
Just x -> (!!) (signs gen) x
Nothing -> -1 -- should thrown a exception
-- | "Apply the reduced Khovanov functor to a diagram"
reducedKhovanov :: Int -> Int -> BDiagram -> [Generator]
reducedKhovanov mark width cube = filter (\g -> (-1) == markedSign mark g) (khovanov width cube)
-- | "Apply the quotient Khovanov functor to a diagram"
quotientKhovanov :: Int -> Int -> BDiagram -> [Generator]
quotientKhovanov mark width cube = filter (\g -> (1) == markedSign mark g) (khovanov width cube)
-}
-- | The next three functions apply the Khovanov functor to the vertices of a cube of resolutions.
khovanovComplex :: Int -> [BDiagram] -> Map Int (Set Generator)
khovanovComplex width cube = M.fromListWith S.union [(homGrading g, S.singleton g) | g <- concatMap (khovanov width) cube]
{-
reducedKhovanovComplex :: Int -> Int -> [BDiagram] -> Map Int (Set Generator)
reducedKhovanovComplex mark width cube = M.fromListWith S.union [(homGrading g, S.singleton g) | g <- concatMap (reducedKhovanov mark width) cube]
quotientKhovanovComplex :: Int -> Int -> [BDiagram] -> Map Int (Set Generator)
quotientKhovanovComplex mark width cube = M.fromListWith S.union [(homGrading g, S.singleton g) | g <- concatMap (quotientKhovanov mark width) cube]
-}
-- | An elementary morphism is determined by 'Components'. We distinguish between 'Merge' and 'Split' 'ElMo's
data ElMo -- | elementary morphisms. The abbreviation is borrowed from Milatz.
= Merge (Set Component) (Set Component) -- ^ merges the first set to the second
| Split (Set Component) (Set Component) -- ^ splits the first set into the second
deriving (Show)
-- | Take two diagrams and returns the 'ElMo's between them, if there is one.
whichMorphism :: BDiagram -> BDiagram -> Maybe ElMo
whichMorphism d d' | not (succRes d d') = Nothing
| length cs2' == 2 && length cs1' == 1 = Just $ Split cs1' cs2'
| length cs1' == 2 && length cs2' == 1 = Just $ Merge cs1' cs2'
| otherwise = Nothing
where
cs2' = components' d' \\ components' d
cs1' = components' d \\ components' d'
succRes :: BDiagram -> BDiagram -> Bool
succRes e e' = all (>=0) diff && (sum diff == 1) where
diff = zipWith subtract (resolution' e) (resolution' e')
-- | Take a morphism and two generators and returns @True@ if there should be a 'Morphism from one to the other.
morphismAction :: Maybe ElMo -> Generator -> Generator -> Bool
morphismAction (Just (Merge cs12 c3)) g g' | (cs12 `isNotASubsetOf` gcs) || (c3 `isNotASubsetOf` g'cs) = False
| (g'cs \\ c3) /= (gcs \\ cs12) = False
| ss `deleteKeys` cs12 /= ss' `deleteKeys` c3 = False
| M.elems (ss `getSubmap` cs12) == [Plus,Plus] && M.elems (ss' `getSubmap` c3) == [Plus] = True
| sort (M.elems (ss `getSubmap` cs12)) == [Plus,Minus] && M.elems (ss' `getSubmap` c3) == [Minus] = True
| otherwise = False
where
gcs = components g
g'cs = components g'
ss = signs g
ss' = signs g'
morphismAction (Just (Split c1 cs23)) g g' | (c1 `isNotASubsetOf` gcs) || (cs23 `isNotASubsetOf` g'cs) = False
| (g'cs \\ cs23) /= (gcs \\ c1) = False
| ss `deleteKeys` c1 /= ss' `deleteKeys` cs23 = False
| M.elems (ss `getSubmap` c1) == [Plus] && sort (M.elems (ss' `getSubmap` cs23)) == [Plus, Minus] = True
| M.elems (ss `getSubmap` c1) == [Minus] && M.elems (ss' `getSubmap` cs23) == [Minus, Minus] = True
| otherwise = False
where
gcs = components g
g'cs = components g'
ss = signs g
ss' = signs g'
morphismAction (Nothing) _ _ = False
-- | Compute the difference in k-grading between two 'Generator's.
kDrop :: Generator -> Generator -> Int
kDrop g g' = kgrade g - kgrade g'
-- | Compute the filtration level of an 'AlgGen'.
kgrade' :: AlgGen -> Int
kgrade' (AlgGen gs) = maximum . S.map kgrade $ gs
kDrop' :: AlgGen -> AlgGen -> Int
kDrop' gs gs' = kgrade' gs - kgrade' gs'
-- | Like 'morphismAction', but only connects two 'Generators' if the drop in k-grading from one to the other is less than or equal to \a\.
filteredMorphismAction :: Int -> Maybe ElMo -> Generator -> Generator -> Bool
filteredMorphismAction k e g g' | kDrop g g' <= k = morphismAction e g g'
| otherwise = False
-- | Return 'Morphisms' from the 'Generator' into the set with 'kDrop' less than or equal to \k\.
filteredMorphismsFrom :: Int -> Generator -> Set Generator -> Morphisms
filteredMorphismsFrom k g gs = M.singleton (wrapGen g) gs' where
gs' = S.map wrapGen
. S.filter (\g' -> filteredMorphismAction k (mor g') g g')
$ gs
mor g' = whichMorphism (gToD g) (gToD g')
-- | Applies 'filteredMorphismsFrom' to every 'Generator' in a list into the same list.
filteredComplexLevel :: Int -> Map Int (Set Generator) -> Int -> Morphisms
filteredComplexLevel k gs i = case M.lookup (i+1) gs of
Nothing -> M.empty
otherwise -> M.unionsWith S.union . S.toList . S.map (\g -> filteredMorphismsFrom k g gsi1) $ gsi where
gsi = gs ! i
gsi1 = gs ! (i+1)
-- | Produces the 'Generator' corresponding to the transverse invariant of a braid.
psi :: Braid -> Generator
psi b = Generator {resolution = res, components = comps, signs = ss, kgrade = k} where
res = fmap (\x -> if x >= 0 then 0 else 1) (braidWord b)
comps = resolutionToComponents . flip resolve res . braidToPD $ b
ss = M.fromList $ zip (S.toList comps) (repeat Minus)
k = (-1)* braidWidth b
{-
-- | Produces the generator corresponding to the QUOTIENT transverse invariant of a braid
quotPsi :: Braid -> Int -> Generator
quotPsi b p = Generator {resolution = res, components = comps, signs = ss, kgrade = k} where
res = fmap (\x -> if x >= 0 then 0 else 1) (braidWord b)
comps = resolutionToComponents . flip resolve res . braidToPD $ b
Just c = findIndex (elem p) comps
ss = uncurry (++) . second (\xs -> 1:xs) . second tail . splitAt c $ replicate (length comps)(-1)
k = kGrading (res, comps, ss) (braidWidth b)
-}
{- |Produces the portion of the cube of resolutions of a braid which is relevant for computing kappa.
This means only using resolutions whose weights are less than or equal to psi's.
Note that this only uses the homological grading of psi, so we don't need a separate function for the quotient. -}
psiCube :: Braid -> [BDiagram]
psiCube b = do
res <- allRes (braidToPD b)
let diagram = resolutionToComponents . resolve (braidToPD b)
guard (sum res <= (sum . resolution . psi $ b))
return BDiagram {resolution' = res, components' = diagram res}