proarrow-0.1.0.0: test/Props/Sheaf/Chain.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# OPTIONS_GHC -Wno-orphans #-}
-- | __A site whose covers nest.__ In the other coverages here
-- @'Proarrow.Category.Sheaf.HasFiniteCovers'@'s Composition law (if @c@ covers @a@ and every leg of
-- @c@ is covered, the composites cover @a@) holds trivially. Either no leg of a cover is itself
-- covered (the two points of the discrete space, the bottom of the walking arrow), or every cover
-- of a leg contains its identity (the image of a functor). So the idempotence of 'closure' that
-- the law buys is never tested.
--
-- On the three-element chain @0 -> 1 -> 2@, 'Atomic' lets every arrow cover, so the law holds for
-- a reason: @1 '~>' 2@ covers @2@, its leg is covered by @0 '~>' 1@, and the composite @0 '~>' 2@
-- covers @2@ too. 'Pred' covers each object only by its immediate predecessor and breaks the law:
-- @0 '~>' 2@ is not a cover, so 'closure' is not idempotent, as the last test asserts. 'Pred'
-- still passes the three @testSiteLaws@ checks and the other two Lawvere-Tierney laws, so
-- idempotence is the only law separating the two coverages.
module Props.Sheaf.Chain (test) where
import Props.Bool ()
import Props.Ordinal ()
import Props.Sheaf ()
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.Falsify (testProperty)
import Prelude hiding (id, (.))
import Proarrow.Category.Enriched.Finitary (Finitary (..), sizes)
import Proarrow.Category.Enriched.Finitary.Sheaf (ClosedSieve, closure, isClosed, isSheaf, lawvereTierney)
import Proarrow.Category.Enriched.Finitary.Topos (FIN, FINITARY, glueBySearch)
import Proarrow.Category.Instance.Bool (BOOL (..), Booleans (..))
import Proarrow.Category.Instance.Opposite (OPPOSITE (..))
import Proarrow.Category.Instance.Ordinal (LTE (..), ORDINAL (..), ORDINAL3)
import Proarrow.Category.Instance.Prof (Prof)
import Proarrow.Category.Instance.Sub (SUBCAT (..), Sub)
import Proarrow.Category.Sheaf
( Atomic
, Cover (..)
, Coverage
, Factors (..)
, HasFiniteCovers (..)
, Induced
, Joins
, Leg (..)
, PulledBack (..)
, Sheaf (..)
, Site (..)
, SomeCover (..)
, SomeLeg (..)
, StableSite (..)
, glueExtension
, pullbackAlongId
)
import Proarrow.Core (CAT, Hom, Kind, Promonad (..), obj, type (+->))
import Proarrow.Functor (FunctorForRep (..), Presheaf)
import Proarrow.Limit.BinaryProduct (PROD)
import Proarrow.Profunctor.Corepresentable (Corep)
import Proarrow.Profunctor.Instance.Composition ((:.:))
import Proarrow.Profunctor.Instance.Coproduct ((:+:))
import Proarrow.Profunctor.Instance.Rift (Rift)
import Proarrow.Profunctor.Instance.Sieve (Sieve (..))
import Proarrow.Profunctor.Instance.Terminal (TerminalProfunctor)
import Proarrow.Profunctor.Instance.Yoneda (Yo)
import Proarrow.Testing (Testable (..), TestableProfunctor, expect, genSomeDef)
import Proarrow.Testing.Laws
( testAtomicIsDoubleNegation
, testCoveredByImage
, testGluesBack
, testLawvereTierney_
, testRanFullyFaithful
, testRiftFullyFaithful
, testSiteLaws
)
-- | The bottom of the chain.
type O0 :: ORDINAL3
type O0 = OZ
-- | The middle.
type O1 :: ORDINAL3
type O1 = OS OZ
-- | The top.
type O2 :: ORDINAL3
type O2 = OS (OS OZ)
-- | The kind of finitary presheaves on the chain, as a testable kind: the Lawvere--Tierney laws
-- are stated on the presheaf classifier, so this is what carries them.
type PshChain :: Kind
type PshChain = FINITARY () ORDINAL3
instance TestableProfunctor (Sub Prof :: CAT PshChain)
instance Testable PshChain where
showOb @(SUB p) = show (sizes @p)
genSome =
genSomeDef
@'[ FIN TerminalProfunctor
, FIN (Yo O2 (OP '()))
, FIN (Yo O0 (OP '()))
, FIN (Sieve :: Presheaf ORDINAL3)
]
-- * The chain covered by its immediate predecessors
-- | The coverage that covers each object by the one below it, and nothing else. Stable, and not
-- composing: see the module header.
type Pred :: Coverage
type data Pred
-- | The name of both of 'Pred'\'s covers: @TwoByOne@ covers 'O2' by its one leg @FromOne@, and
-- @OneByZero@ covers 'O1' by @FromZero@.
type data ByPredecessor
instance Site Pred ORDINAL3 where
data Cover Pred ORDINAL3 a c where
TwoByOne :: Cover Pred ORDINAL3 O2 ByPredecessor
OneByZero :: Cover Pred ORDINAL3 O1 ByPredecessor
data Leg Pred ORDINAL3 a c x where
FromOne :: Leg Pred ORDINAL3 O2 ByPredecessor O1
FromZero :: Leg Pred ORDINAL3 O1 ByPredecessor O0
legArrow FromOne = SLT (ZLT ZEQ)
legArrow FromZero = ZLT ZEQ
legs TwoByOne = [SomeLeg FromOne]
legs OneByZero = [SomeLeg FromZero]
instance HasFiniteCovers Pred ORDINAL3 where
covers @a = case obj @a of
ZEQ -> []
SLT ZEQ -> [SomeCover OneByZero]
SLT (SLT ZEQ) -> [SomeCover TwoByOne]
-- | Stable: every arrow into 'O2' other than the identity already factors through @1 '~>' 2@,
-- because the chain is thin and @0 <= 1@.
instance StableSite Pred ORDINAL3 where
pullbackCover TwoByOne (ZLT (ZLT ZEQ)) = AlreadyFactors (Factors FromOne (ZLT ZEQ))
pullbackCover TwoByOne (SLT (ZLT ZEQ)) = AlreadyFactors (Factors FromOne id)
pullbackCover TwoByOne (SLT (SLT ZEQ)) = pullbackAlongId TwoByOne
pullbackCover OneByZero (ZLT ZEQ) = AlreadyFactors (Factors FromZero id)
pullbackCover OneByZero (SLT ZEQ) = pullbackAlongId OneByZero
-- * The comparison lemma
-- | The chain's two ends, @0 '~>' 2@, as a functor from the walking arrow. Fully faithful, and
-- under 'Atomic' the middle is covered by @0 '~>' 1@, so the sheaves on the chain are the sheaves on
-- the walking arrow for the induced coverage. That coverage covers 'TRU' by @'FLS' '~>' 'TRU'@,
-- which is 'Atomic' again.
data family Ends :: BOOL +-> ORDINAL3
instance FunctorForRep Ends where
type Ends @ FLS = O0
type Ends @ TRU = O2
fmap Fls = ZEQ
fmap Tru = SLT (SLT ZEQ)
fmap F2T = ZLT (ZLT ZEQ)
type EndsInc :: ORDINAL3 +-> BOOL
type EndsInc = Corep Ends
-- | The top two, @1 '~>' 2@. Dense in the categorical sense, since the bottom is the empty colimit,
-- but nothing from the image covers the bottom, and the lemma fails: the induced coverage gives
-- 'TRU' an empty cover, and its only sheaf is the terminal one.
data family Upper :: BOOL +-> ORDINAL3
instance FunctorForRep Upper where
type Upper @ FLS = O1
type Upper @ TRU = O2
fmap Fls = SLT ZEQ
fmap Tru = SLT (SLT ZEQ)
fmap F2T = SLT (ZLT ZEQ)
type UpperInc :: ORDINAL3 +-> BOOL
type UpperInc = Corep Upper
-- | Two elements everywhere, on the walking arrow.
type Two :: Presheaf BOOL
type Two = TerminalProfunctor :+: TerminalProfunctor
-- | Two elements everywhere, on the chain.
type TwoC :: Presheaf ORDINAL3
type TwoC = TerminalProfunctor :+: TerminalProfunctor
instance Sheaf (Induced Atomic EndsInc) Two where
glue = glueBySearch @(Induced Atomic EndsInc)
instance Sheaf Atomic (Rift (OP EndsInc) Two) where
glue = glueExtension @Atomic
-- | The sieve at the top of the arrows out of the bottom: the one generated by @0 '~>' 2@, and the
-- witness that 'Pred' does not compose.
fromBottom :: Sieve (O2 :: ORDINAL3) '()
fromBottom = Sieve \g _ -> case g of
ZLT _ -> True
_ -> False
test :: TestTree
test =
testGroup
"Chain"
[ testGroup
"Atomic"
[ testSiteLaws @Atomic @() @ORDINAL3
, -- the payoff: idempotence and meet preservation on a site where composing covers
-- actually produces a cover that was not one of the two being composed
testLawvereTierney_ @(PROD PshChain) (lawvereTierney @Atomic)
, -- a chain has pullbacks, so here too the atomic topology is the double-negation one --
-- on a site where, unlike the walking arrow, covers nest
testAtomicIsDoubleNegation @ORDINAL3
, testProperty "isSheaf" $ do
expect
"the terminal presheaf is a sheaf: every restriction of it is a bijection"
True
(isSheaf @Atomic @(TerminalProfunctor :: Presheaf ORDINAL3))
expect
"the representable at the top is a sheaf -- it is the terminal presheaf"
True
(isSheaf @Atomic @(Yo O2 (OP '())))
expect
"the representable at the bottom is not: nothing over 1, one thing over 0"
False
(isSheaf @Atomic @(Yo O0 (OP '())))
expect
"the sieves are not: four at the top, three at the middle"
False
(isSheaf @Atomic @(Sieve :: Presheaf ORDINAL3))
, testProperty "the truth values" $ do
expect
"the sieves: two at the bottom, three at the middle, four at the top"
[2, 3, 4]
(sizes @(Sieve :: Presheaf ORDINAL3))
expect
"the closed ones: the empty sieve and the maximal one, at every object"
[2, 2, 2]
(sizes @(ClosedSieve Atomic :: Presheaf ORDINAL3))
expect
"so the truth values are constant, and a sheaf -- as the classifier of a topos of sheaves must be"
True
(isSheaf @Atomic @(ClosedSieve Atomic :: Presheaf ORDINAL3))
]
, testGroup
"Joins"
[ -- on a chain no element but the bottom is the join of the ones below it, so the one
-- cover is the bottom's empty one, and a sheaf is a presheaf with one element there
testSiteLaws @Joins @() @ORDINAL3
, testLawvereTierney_ @(PROD PshChain) (lawvereTierney @Joins)
, testProperty "isSheaf" $ do
expect "the terminal presheaf is a sheaf" True (isSheaf @Joins @(TerminalProfunctor :: Presheaf ORDINAL3))
expect "the representable at the top is a sheaf" True (isSheaf @Joins @(Yo O2 (OP '())))
expect "and so is the one at the bottom: subcanonical" True (isSheaf @Joins @(Yo O0 (OP '())))
expect "the sieves are not: two at the bottom" False (isSheaf @Joins @(Sieve :: Presheaf ORDINAL3))
]
, testGroup
"restricted to its ends"
[ testSiteLaws @(Induced Atomic EndsInc) @() @BOOL
, testLawvereTierney_ @(PROD (FINITARY () BOOL)) (lawvereTierney @(Induced Atomic EndsInc))
, testRanFullyFaithful @EndsInc
, testCoveredByImage @Atomic @EndsInc
, testGluesBack @Atomic @(Rift (OP EndsInc) Two)
, testProperty "the comparison lemma" $ do
expect
"restriction keeps a sheaf"
(True, True)
(isSheaf @Atomic @TwoC, isSheaf @(Induced Atomic EndsInc) @(EndsInc :.: TwoC))
expect
"extension makes one"
(True, True)
(isSheaf @(Induced Atomic EndsInc) @Two, isSheaf @Atomic @(Rift (OP EndsInc) Two))
expect "the extension" [2, 2, 2] (sizes @(Rift (OP EndsInc) Two))
expect
"the induced coverage has the sheaves of Atomic"
(isSheaf @Atomic @Two, isSheaf @Atomic @(Yo FLS (OP '())), isSheaf @Atomic @(Yo TRU (OP '())))
( isSheaf @(Induced Atomic EndsInc) @Two
, isSheaf @(Induced Atomic EndsInc) @(Yo FLS (OP '()))
, isSheaf @(Induced Atomic EndsInc) @(Yo TRU (OP '()))
)
, testProperty "the ends are not dense" $
expect
"transformations 1 -> 0, arrows 1 -> 0"
(1, 0)
(size @(Rift (OP EndsInc) EndsInc) @O1 @O0, size @(Hom ORDINAL3) @O1 @O0)
, testRiftFullyFaithful @UpperInc
, testLawvereTierney_ @(PROD (FINITARY () BOOL)) (lawvereTierney @(Induced Atomic UpperInc))
, testProperty "the top two are dense, but do not cover the bottom" $ do
expect
"restriction loses a sheaf"
(True, False)
(isSheaf @Atomic @TwoC, isSheaf @(Induced Atomic UpperInc) @(UpperInc :.: TwoC))
expect "the induced coverage has no sheaf with two elements" False (isSheaf @(Induced Atomic UpperInc) @Two)
]
, testGroup
"Pred"
[ -- stability, generated sieves, and dense = covering all hold; only composition fails,
-- and none of the three sees it
testSiteLaws @Pred @() @ORDINAL3
, testProperty "the covers do not compose, so closure is not idempotent" $ do
expect
"{0 -> 2} is not closed: its closure adds 1 -> 2"
False
(isClosed @Pred fromBottom)
expect
"its closure adds 1 -> 2, whose own closure is maximal"
False
(isClosed @Pred (closure @Pred fromBottom))
]
]