packages feed

MiniAgda-0.2016.12.19: src/Polarity.hs

{- In the context of polarities, we use "recursive" in the sense of
"computable" rather than syntactic recursion. -}

module Polarity where

import Util
import Warshall

import Data.Map (Map)
import qualified Data.Map as Map
import qualified Data.List as List

{- 2010-10-09 Fusing polarity and irrelevance

     .      constant (= irrelevant) function
    / \
  ++   |    strictly positive function (types only)
   |   |
   +   -    positive/negative function (types only)
    \ /
     ^      parametric function (lambda cube), default for types
     |
     *      recursive function (pattern matching), default for terms


 Composition (AC)

  . p = .
  * p = *  (p not .)
  ^ p = ^  (p not .,*)
 ++ p = p
  + p = p  (p not ++)
  - - = +

Equality/subtyping <=p

  x <=. y  iff  true
  x <=- y  iff  x >= y
  x <=^ y  iff  x == y
  x <=* y  iff  x == y
 -}

-- polarities and strict positivity ----------------------------------

class Polarity pol where
  erased    :: pol -> Bool
  compose   :: pol -> pol -> pol
  neutral   :: pol                 -- ^ neutral for compose.
  promote   :: pol -> pol
  demote    :: pol -> pol
  hidden    :: pol                 -- ^ corresponding to hidden quantification

type PVarId = Int

data Pol
  = Const -- non-occurring, irrelevant
  | SPos  -- strictly positive
  | Pos   -- positive
  | Neg   -- negative, used internally for contravariance of sized codata
  | Param -- parametric (lambda) function
  | Rec   -- recursive (takes decision)
  | Default     -- no polarity given (for parsing)
  | PVar PVarId -- flexible polarity variable
         deriving (Eq,Ord)

mixed = Rec
defaultPol = Rec
{-
mixed = Param -- TODO: Rec
defaultPol = Param -- TODO: Rec
-}
instance Polarity Pol where
  erased  = (==) Const
  compose = polComp
  neutral = SPos
  promote = invComp Const
  demote  = invComp Rec
  hidden  = Const

instance Show Pol where
  show Const = "."
  show SPos  = "++"
  show Pos   = "+"
  show Neg   = "-"
  show Param = "^"
  show Rec   = "*"
  show Default = "{default polarity}"
  show (PVar i) = showPVar i

showPVar i = "?p" ++ show i

isPVar (PVar{}) = True
isPVar _ = False

-- information ordering
leqPol :: Pol -> Pol -> Bool
leqPol x Const  = True   -- Const is top
leqPol Const x  = False
leqPol Rec y    = True   -- Rec is bottom
leqPol x Rec    = False
leqPol Param y  = True   -- Param is second bottom
leqPol x Param  = False
leqPol Pos SPos = True
leqPol x   y    = x == y

{- RETIRED
isSPos :: Pol -> Bool
isSPos SPos = True
isSPos Const = True
isSPos _ = False
-}

{- NOT USED
isPos :: Pol -> Bool
isPos Pos = True
isPos x = isSPos x
-}

-- polarity negation
-- used in Eval.hs leqVals' for switching sides
-- this means it is only applied to Pos, Neg, Param,
-- never to SPos, Const, or polarity expressions
polNeg :: Pol -> Pol
polNeg Const  = Const
polNeg SPos  = Neg
polNeg Pos   = Neg
polNeg Neg   = Pos
polNeg Param = Param
polNeg Rec   = Rec

-- polarity composition
-- used in Eval.hs leqVals'
polComp :: Pol -> Pol -> Pol
polComp Const  x  = Const   -- most dominant
polComp x Const   = Const
polComp Rec x     = Rec  -- dominant except for Const
polComp x Rec     = Rec
polComp Param x   = Param  -- dominant except for Const, Rec
polComp x Param   = Param
polComp SPos  x   = x      -- neutral
polComp x SPos    = x
polComp Pos  x    = x      -- neutral except for SPos
polComp x Pos     = x
polComp Neg Neg   = Pos    -- order 2
{- pol.comp. is ass., comm., with neutral ++, and infinity Const
   cancellation does not hold, since composition with anything by ++ is
   information loss:
     q p <= q p' ==> p <= p'
   only if q = ++ (then it is trivial anyway) -}

-- polarity inverse composition (see Abel, MSCS 2008)
-- invComp p q1 <= q2  <==> q1 <= polComp p q2
-- used in TCM.hs cxtApplyDec
invComp :: Pol -> Pol -> Pol
invComp Rec   Rec   = Rec       -- in rec. arg. keep only rec. vars
invComp Rec   x     = Const     -- all others are declared unusable
invComp Param Param = Param     -- in parametric mixed arg, keep only mixed vars
invComp Param x     = Const
invComp Const x     = Param     -- a constant function can take any argument
invComp SPos  x     = x         -- SPos is the identity
invComp p     SPos  = Const     -- SPos preserved only under SPos
invComp Pos   x     = x         -- x not SPos
invComp Neg   x     = polNeg x  -- x not SPos

{- UNUSED
invCompExpr :: Pol -> PExpr -> PExpr
invCompExpr q (PValue p)   = PValue $ invComp q p
invCompExpr q (PExpr q' i) = PExpr (polComp q q') i
-}

-- polarity conjuction (infimum)
-- used in comparing spines
polAnd :: Pol -> Pol -> Pol
polAnd Const x = x      -- most information
polAnd x Const = x
polAnd Rec   x = Rec   -- least information
polAnd x   Rec = Rec
{-
polAnd Param x  = Param   -- 2nd least information
polAnd x Param  = Param
-}
polAnd x y | x == y = x       -- same information
polAnd SPos Pos = Pos     -- SPos is more informative than Pos
polAnd Pos SPos = Pos
{-
polAnd SPos Neg = Param
polAnd Neg SPos = Param
-}
polAnd _ _      = Param     -- remaining cases: conflicting info or Param

instance SemiRing Pol where
  oplus  = polAnd
  otimes = polComp
  ozero  = Const    -- dominant for composition, neutral for infimum
  oone   = SPos     -- neutral  for composition

-- computing a relation from <=
relPol :: Pol -> (a -> a -> Bool) -> (a -> a -> Bool)
relPol Const r a b = True
relPol Rec   r a b = r a b && r b a
relPol Param r a b = r a b && r b a
relPol Neg   r a b = r b a
relPol Pos   r a b = r a b
relPol SPos  r a b = r a b

relPolM :: (Monad m) => Pol -> (a -> a -> m ()) -> (a -> a -> m ())
relPolM Const r a b = return ()
relPolM Rec   r a b = r a b >> r b a
relPolM Param r a b = r a b >> r b a
relPolM Neg   r a b = r b a
relPolM Pos   r a b = r a b
relPolM SPos  r a b = r a b

-- polarity product (composition of polarities) ----------------------

data Multiplicity = POne | PTwo deriving (Eq, Ord)

instance Show Multiplicity where
  show POne = "1"
  show PTwo = "2"

-- addition modulo 2
addMultiplicity :: Multiplicity -> Multiplicity -> Multiplicity
addMultiplicity PTwo y = y
addMultiplicity x PTwo = x
addMultiplicity POne POne = PTwo

type VarMults = Map PVarId Multiplicity -- multiplicity of variables (1 or 2)

showMults :: VarMults -> String
showMults mults =
  let ml = Map.toList mults  -- get list of (key,value) pairs
      l  = concat $ map f ml where
             f (k, POne) = [k]
             f (k, PTwo) = [k,k]
  in Util.showList "." showPVar l

multsEmpty = Map.empty

multsSingle :: Int -> VarMults
multsSingle i = Map.insert i POne multsEmpty


data PProd = PProd
  { coeff    :: Pol      -- a coefficient, excluding PVar
  , varMults :: VarMults -- multiplicity of variables (1 or 2)
  } deriving (Eq,Ord)

instance Polarity PProd where
  erased  = erased . coeff
  compose = polProd
  neutral = PProd SPos multsEmpty
  demote  = undefined
  promote = undefined
  hidden  = PProd hidden multsEmpty

instance Show PProd where
  show (PProd Const _) = show Const
  show (PProd SPos m) = if Map.null m then show SPos else showMults m
  show (PProd q m) = separate "." (show q) (showMults m)

pprod :: Pol -> PProd
pprod (PVar i) = PProd SPos (multsSingle i)
pprod q = PProd q multsEmpty

-- | fails if not a simple polarity
fromPProd :: PProd -> Maybe Pol
fromPProd (PProd Const _)          = Just Const
fromPProd (PProd p m) | Map.null m = Just p
fromPProd _                        = Nothing

isSPos :: PProd -> Bool
isSPos (PProd Const _) = True
isSPos (PProd SPos m) = Map.null m
isSPos _ = False

-- multiply two products

polProd :: PProd -> PProd -> PProd
polProd (PProd q1 m1) (PProd q2 m2) = PProd (polComp q1 q2) $
  Map.unionWith addMultiplicity m1 m2

-- polarity expressions are polynomials ------------------------------

data PPoly = PPoly { monomials :: [PProd] } deriving (Eq,Ord)

instance Show PPoly where
  show (PPoly []) = show Const
  show (PPoly [m]) = show m
  show (PPoly l)   = Util.showList "/\\" show l

ppoly :: PProd -> PPoly
ppoly (PProd Const _) = PPoly []
ppoly pp = PPoly [pp]

polSum :: PPoly -> PPoly -> PPoly
polSum (PPoly x) (PPoly y) = PPoly $ List.nub $ x ++ y

polProduct :: PPoly -> PPoly -> PPoly
polProduct (PPoly l1) (PPoly l2) =
  let ps = [ polProd x y | x <- l1, y <- l2]
  in PPoly $ List.nub $ ps

instance SemiRing PPoly where
  oplus  = polSum
  otimes = polProduct
  ozero  = PPoly []
  oone   = PPoly [PProd SPos Map.empty]

{-
data PExpr
  = PValue Pol     -- constant polarity
  | PExpr Pol Int  -- PExpr q pi means q^_1 pi  (pi is the number of the var)

-- a polarity variable
pvar :: Int -> PExpr
pvar = PExpr SPos  -- ++ is the neutral element of inverse polarity composition

instance Show PExpr where
  show (PValue p) = show p
  show (PExpr SPos i) = "?p" ++ show i
  show (PExpr q i) = show q ++ "^-1(?p" ++ show i ++ ")"
-}


{- ML-style Polarity inference

Preliminaries:
1. constructor types are mixed-variant function types only
2. matching is only allowed on mixed-variant arguments
  1+2 are both consequences that only type-valued functions have variance
  and 1. data constructors are not types, 2. types are not matched on

Concrete syntax

  f : (xs : As) -> C   (C not a Pi-type)
  f = t

is parsed as abstract syntax

  f : pis(xs : As) -> C
  f = t

where pi_1..n are fresh polarity variables

Then t is type-checked to infer the polarity variables, e.g.

  f xs = t

  pis(xs : As) |- t : C

Now what can happen?

Variable:  t = x_i.  Then we add a constraint  pi_i <= ++

Application t = u v  where u : q(x:B) -> D

  q^-1(pis(xs: As)) |- v : B

  A term q^-1 pi arises where q is a polarity constant (!, ML-inference)
  or a polarity variable (recursion!, e.g. u = f)
  and pi is a polarity expression

In the context, keep SOLL and HABEN

  SOLL  is the original polarity (variable or constant)
  HABEN is a (ordered) list of pol.vars. and a pol.const. (default: ++)

Variable   : add constraint SOLL <= HABEN
Application: add q to HABEN by polarity multiplication (q is a var or const)
Abstraction: \xt : q(x:A) -> B:  continue with x (SOLL = q, HABEN = ++)

What kind of constraints do arise
1) q  <= pi    [ from variables , pi is a Pol-product ]
2) ++ <= pis  [ from positivity graph, pis is a sum of Pol-products ]
   this means ++ <= pi for all pi in pis

Solving constraints

- discard  o <= pi  and q <= /  (do not even need to add them)
- all pvars which are not bounded below (appearing in one q in 1)
  can be instantiated to /  which will remove some constraints


-}

{- Mutual recursion

In mutual declarations, use the following Ansatz:  data/codata ++, functions o

  A = B -> A
  B = A -> B

A (B) is positive in its own body and negative in the body of B (A)

  F A B = B -> A   F(-,++)
  G A B = A -> B   G(-,++)

  F A B = G A B -> F A B
  G A B = F A B -> G A B

  Polarities:
  F : fa * -> fb * -> *
  G : ga * -> gb * -> *

  A : -fa, B : -fb |- G A B : *  ==> -fa <= ga, -fb <= gb
  A : -ga, B : -gb |- F A B : *  ==> -ga <= fa, -gb <= fb

-}

{- Pure polarity inference

Judgement:  pis(xs:As) |- t : B ---> C

Variable:   pis(xs:As) |- xi : Ai ---> pi_i <= ++

Application: Delta |- u : q(x:A) -> B ---> C1
             Delta |- v : A           ---> C2
             --------------------------------------------------
             Delta |- u v : B[u/x] ---> C1,C2,q(Delta) <= Delta
-}