packages feed

moonlight-category-0.1.0.0: test/simplicial/HomotopySpec.hs

module HomotopySpec
  ( tests,
  )
where

import Data.Containers.ListUtils (nubOrd)
import Data.Function ((&))
import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import Moonlight.Category.Effect.Fixture.FinCat (sampleFinCat)
import Moonlight.Category
  ( FinCat,
    FinGeneratorId (..),
    FinMorphismId (..),
    FinObjectId (..),
    mkFinCat,
  )
import Moonlight.Category.Simplicial
  ( automorphismGroupAt,
    automorphismGroupoidOfNerve,
    automorphismGroupoidObjects,
    coreGroupoidObjects,
    coreGroupoidMorphisms,
    coreGroupoidMorphismsBetween,
    coreGroupoidOfNerve,
    forgetAutomorphismGroupoidMorphism,
    forgetCoreGroupoidMorphism,
    pi0Nerve,
  )
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (assertBool, assertEqual, testCase)

disconnectedCategory :: FinCat
disconnectedCategory =
  case mkFinCat (Set.fromList [FinObjectId 0, FinObjectId 1]) Map.empty Map.empty of
    Right categoryValue -> categoryValue
    Left _ -> sampleFinCat

generatorMorphismId :: Int -> FinMorphismId
generatorMorphismId = FinGeneratorMorphismId . FinGeneratorId

identityMorphismId :: Int -> FinMorphismId
identityMorphismId = FinIdentityId . FinObjectId

isomorphicPairCategory :: FinCat
isomorphicPairCategory =
  case
    mkFinCat
      (Set.fromList [FinObjectId 0, FinObjectId 1])
      (Map.fromList [((FinObjectId 0, FinObjectId 1), [generatorMorphismId 10]), ((FinObjectId 1, FinObjectId 0), [generatorMorphismId 11])])
      (Map.fromList [((generatorMorphismId 11, generatorMorphismId 10), identityMorphismId 0), ((generatorMorphismId 10, generatorMorphismId 11), identityMorphismId 1)]) of
    Right categoryValue -> categoryValue
    Left _ -> sampleFinCat

tests :: TestTree
tests =
  testGroup
    "Homotopy"
    [ testCase "pi0 for sample category is connected" $
        assertEqual "sample pi0 components" 1 (length (pi0Nerve sampleFinCat)),
      testCase "pi0 separates disconnected category" $
        assertEqual "disconnected pi0 components" 2 (length (pi0Nerve disconnectedCategory)),
      testCase "invertible morphisms detect isomorphism pair" $
        let invertibles =
              coreGroupoidMorphisms (coreGroupoidOfNerve isomorphicPairCategory)
                & fmap forgetCoreGroupoidMorphism
         in assertBool "expected at least two non-identity invertibles" (length invertibles >= 4),
      testCase "invertible morphisms are unique" $
        let invertibles =
              coreGroupoidMorphisms (coreGroupoidOfNerve isomorphicPairCategory)
                & fmap forgetCoreGroupoidMorphism
         in assertEqual "invertible morphisms should not repeat" invertibles (nubOrd invertibles),
      testCase "core groupoid of nerve agrees with its endpoint decomposition" $
        let coreGroupoidValue = coreGroupoidOfNerve isomorphicPairCategory
            bucketedMorphisms =
              coreGroupoidObjects coreGroupoidValue
                >>= (\sourceObject ->
                       coreGroupoidObjects coreGroupoidValue
                         >>= (\targetObject ->
                                coreGroupoidMorphismsBetween coreGroupoidValue sourceObject targetObject
                            )
                    )
         in assertEqual
              "core groupoid morphisms should be the union of endpoint buckets"
              (coreGroupoidMorphisms coreGroupoidValue)
              (nubOrd bucketedMorphisms),
      testCase "core groupoid endpoint query isolates directed isomorphisms" $
        case coreGroupoidObjects (coreGroupoidOfNerve isomorphicPairCategory) of
          sourceObject : targetObject : _ ->
            let coreGroupoidValue = coreGroupoidOfNerve isomorphicPairCategory
                forwardMorphisms =
                  coreGroupoidMorphismsBetween coreGroupoidValue sourceObject targetObject
             in assertEqual "expected exactly one forward invertible generator" 1 (length forwardMorphisms)
          _ -> assertBool "expected two objects in isomorphic pair category" False,
      testCase "automorphism groupoid on sample category is identity loop" $
        case automorphismGroupoidObjects (automorphismGroupoidOfNerve sampleFinCat) of
          [] -> assertBool "expected base object" False
          baseObject : _ ->
            let automorphismGroupoidValue = automorphismGroupoidOfNerve sampleFinCat
             in assertEqual
                  "sample core automorphism size"
                  1
                  (length (fmap forgetAutomorphismGroupoidMorphism (automorphismGroupAt automorphismGroupoidValue baseObject)))
    ]