How to enumerate the elements of a list by `Fin`s in linear time? - time-complexity

We can enumerate the elements of a list like this:
-- enumerate-ℕ = zip [0..]
enumerate-ℕ : ∀ {α} {A : Set α} -> List A -> List (ℕ × A)
enumerate-ℕ = go 0 where
go : ∀ {α} {A : Set α} -> ℕ -> List A -> List (ℕ × A)
go n [] = []
go n (x ∷ xs) = (n , x) ∷ go (ℕ.suc n) xs
E.g. enumerate-ℕ (1 ∷ 3 ∷ 2 ∷ 5 ∷ []) is equal to (0 , 1) ∷ (1 , 3) ∷ (2 , 2) ∷ (3 , 5) ∷ []. Assuming there is sharing in Agda, the function is linear.
However if we try to enumerate the elements of a list by Fins rather than ℕs, the function becomes quadratic:
enumerate-Fin : ∀ {α} {A : Set α} -> (xs : List A) -> List (Fin (length xs) × A)
enumerate-Fin [] = []
enumerate-Fin (x ∷ xs) = (zero , x) ∷ map (pmap suc id) (enumerate-Fin xs)
Is it possible to enumerate by Fins in linear time?

Consider this as a first attempt:
go : ∀ {m α} {A : Set α} -> Fin m -> (xs : List A) -> List (Fin (m + length xs) × A)
go i [] = []
go i (x ∷ xs) = (inject+ _ i , x) ∷ {!go (suc i) xs!}
i grows at each recursive call as it should, but there is a mismatch:
the type of the goal is List (Fin (.m + suc (length xs)) × .A)
the type of the expression insise the hole is List (Fin (suc (.m + length xs)) × .A)
It's easy to prove that two types are equal, but it's also dirty. This is a common problem: one argument grows and the other lowers, so we need definitionally commutative _+_ to handle both the cases, but there is no way to define it. The solution is to use CPS:
go : ∀ {α} {A : Set α} -> (k : ℕ -> ℕ) -> (xs : List A) -> List (Fin (k (length xs)) × A)
go k [] = []
go k (x ∷ xs) = ({!!} , x) ∷ go (k ∘ suc) xs
(k ∘ suc) (length xs) is the same thing as k (length (x ∷ xs)), hence the mismatch is fixed, but what is i now? The type of the hole is Fin (k (suc (length xs))) and it's uninhabited in the current context, so let's introduce some inhabitant:
go : ∀ {α} {A : Set α}
-> (k : ℕ -> ℕ)
-> (∀ {n} -> Fin (k (suc n)))
-> (xs : List A)
-> List (Fin (k (length xs)) × A)
go k i [] = []
go k i (x ∷ xs) = (i , x) ∷ go (k ∘ suc) {!!} xs
The type of the new hole is {n : ℕ} → Fin (k (suc (suc n))). We can fill it with i, but i must grow at each recursive call. However suc and k doesn't commute, so suc i is Fin (suc (k (suc (_n_65 k i x xs)))). So we add another argument that sucs under k, and the final definition is
enumerate-Fin : ∀ {α} {A : Set α} -> (xs : List A) -> List (Fin (length xs) × A)
enumerate-Fin = go id suc zero where
go : ∀ {α} {A : Set α}
-> (k : ℕ -> ℕ)
-> (∀ {n} -> Fin (k n) -> Fin (k (suc n)))
-> (∀ {n} -> Fin (k (suc n)))
-> (xs : List A)
-> List (Fin (k (length xs)) × A)
go k s i [] = []
go k s i (x ∷ xs) = (i , x) ∷ go (k ∘ suc) s (s i) xs
which works, because s : {n : ℕ} → Fin (k n) → Fin (k (suc n)) can be treated as {n : ℕ} → Fin (k (suc n)) → Fin (k (suc (suc n))).
A test: C-c C-n enumerate-Fin (1 ∷ 3 ∷ 2 ∷ 5 ∷ []) gives
(zero , 1) ∷
(suc zero , 3) ∷
(suc (suc zero) , 2) ∷ (suc (suc (suc zero)) , 5) ∷ []
Now note that in enumerate-Fin k always follows Fin in the types. Hence we can abstract Fin ∘ k and get a generic version of the function that works with both ℕ and Fin:
genumerate : ∀ {α β} {A : Set α}
-> (B : ℕ -> Set β)
-> (∀ {n} -> B n -> B (suc n))
-> (∀ {n} -> B (suc n))
-> (xs : List A)
-> List (B (length xs) × A)
genumerate B s i [] = []
genumerate B s i (x ∷ xs) = (i , x) ∷ genumerate (B ∘ suc) s (s i) xs
enumerate-ℕ : ∀ {α} {A : Set α} -> List A -> List (ℕ × A)
enumerate-ℕ = genumerate _ suc 0
enumerate-Fin : ∀ {α} {A : Set α} -> (xs : List A) -> List (Fin (length xs) × A)
enumerate-Fin = genumerate Fin suc zero

Related

How to rewrite: Vect (S (S (n + m))) a -> Vect (S (plus n (S m))) a

I am stuck with Idris (again, sigh). I am doing an exercise on merge sort from the type driven development with Idris book on chapter 10. I have this:
import Data.Vect
import Data.Vect.Views
sort2 : Ord a => (l: a) -> (r: a) -> (a, a)
sort2 l r = if l <= r then (l, r) else (r, l)
needHelp : Vect (S (S (n + m))) a -> Vect (S (plus n (S m))) a
needHelp {n=(S n)} {m=(S m)} (x :: xs) = ?help
vectMerge : Ord a => Vect n a -> Vect m a -> Vect (n + m) a
vectMerge [] ys = ys
vectMerge {n} xs [] = rewrite plusZeroRightNeutral n in xs
vectMerge {n=(S n)} {m=(S m)} (x :: xs) (y :: ys) =
let (f, s) = sort2 x y in
needHelp (f :: s :: (vectMerge xs ys))
I have isolated the needHelp function so you can see the rewrite that I want to achieve. I tried this:
vectMerge : Ord a => Vect n a -> Vect m a -> Vect (n + m) a
vectMerge [] ys = ys
vectMerge {n} xs [] = rewrite plusZeroRightNeutral n in xs
vectMerge {n=(S n)} {m=(S m)} (x :: xs) (y :: ys) =
let (f, s) = sort2 x y in
let tail = (rewrite plusSuccRightSucc n m in s :: vectMerge xs ys) in
f :: tail
But Idris complains:
When checking right hand side of Main.case block in vectMerge with expected type
Vect (S (plus n (S m))) a
rewriting S (plus n m) to plus n (S m) did not change type letty
I don't understand why this doesn't work. Help much appreciated.
rewrite works with respect to your current goal, not wrt to the term you are trying to use to solve the goal (I tried to illustrate it in this answer).
So, here is a possible solution:
import Data.Vect
sort2 : Ord a => (l: a) -> (r: a) -> (a, a)
sort2 l r = if l <= r then (l, r) else (r, l)
vectMerge : Ord a => Vect n a -> Vect m a -> Vect (n + m) a
vectMerge [] ys = ys
vectMerge {n} xs [] = rewrite plusZeroRightNeutral n in xs
vectMerge {n=(S n)} {m=(S m)} (x :: xs) (y :: ys) =
let (f, s) = sort2 x y in
rewrite sym $ plusSuccRightSucc n m in
(f :: s :: (vectMerge xs ys))
sym in sym $ plusSuccRightSucc n m reverses the direction of rewrite.

Understanding `decEq`

Given:
*section3> :module Data.Vect
*section3> :let e = the (Vect 0 Int) []
*section3> :let xs = the (Vect _ _) [1,2]
*section3> decEq xs e
(input):1:7:When checking argument x2 to function Decidable.Equality.decEq:
Type mismatch between
Vect 0 Int (Type of e)
and
Vect 2 Integer (Expected type)
Specifically:
Type mismatch between
0
and
2
Why must the Nat arguments equal each other for DecEq?
Note - posted in https://groups.google.com/forum/#!topic/idris-lang/qgtImCLka3I originally
decEq is for homogenous propositional equality:
||| Decision procedures for propositional equality
interface DecEq t where
||| Decide whether two elements of `t` are propositionally equal
total decEq : (x1 : t) -> (x2 : t) -> Dec (x1 = x2)
As you can see, x1 and x2 are both of type t. In your case, you have x1 : Vect 2 Integer and x2 : Vect 0 Int. These are two different types.
You can write your own heterogenous equality decider for Vectors of the same element type by first checking their lengths, then delegating to the homogenous version:
import Data.Vect
vectLength : {xs : Vect n a} -> {ys : Vect m a} -> xs = ys -> n = m
vectLength {n = n} {m = n} Refl = Refl
decEqVect : (DecEq a) => (xs : Vect n a) -> (ys : Vect m a) -> Dec (xs = ys)
decEqVect {n = n} {m = m} xs ys with (decEq n m)
decEqVect xs ys | Yes Refl = decEq xs ys
decEqVect xs ys | No notEq = No (notEq . vectLength)

Distributivity of `subst`

Suppose I have a transitive relation ~with two endomaps f and g.
Assuming f and g agree everywhere and f a ~ f b ~ f c
then there are two ways to show g a ~ g c:
transform each f into a g by the given equality then apply
transitivity,
or apply transitivity then transform along the equality.
Are the resulting proofs identical? Apparently so,
open import Relation.Binary.PropositionalEquality
postulate A : Set
postulate _~_ : A → A → Set
postulate _⟨~~⟩_ : ∀{a b c} → a ~ b → b ~ c → a ~ c
postulate f g : A → A
subst-dist : ∀{a b c}{ef : f a ~ f b}{psf : f b ~ f c} → (eq : ∀ {z} → f z ≡ g z)
→
subst₂ _~_ eq eq ef ⟨~~⟩ subst₂ _~_ eq eq psf
≡ subst₂ _~_ eq eq (ef ⟨~~⟩ psf)
subst-dist {a} {b} {c} {ef} {psf} eq rewrite eq {a} | eq {b} | eq {c} = refl
I just recently learned about the rewrite keyword and thought it might help here; apparently it does. However, I honestly do not understand what is going on here. I've used rewrite other times, with comprehension. However, all these substs are confusing me.
I'd like to know
if is there a simplier way to obtain subst-dist? Maybe something similar in the libraries?
what is going on with this particular usage of rewrite
an alternate proof of subst-dist without using rewrite (most important)
is there another way to obtain g a ~ g c without using subst?
what are some of the downsides of using heterogeneous equality, it doesn't seem like most people are fond of it. (also important)
Any help is appreciated.
rewrite is just a sugared with, which is just sugared "top-level" pattern matching. See in Agda’s documentation.
what are some of the downsides of using heterogeneous equality, it
doesn't seem like most people are fond of it. (also important)
This is OK
types-equal : ∀ {α} {A B : Set α} {x : A} {y : B} -> x ≅ y -> A ≡ B
types-equal refl = refl
this is OK as well
A-is-Bool : {A : Set} {x : A} -> x ≅ true -> A ≡ Bool
A-is-Bool refl = refl
This is an error
fail : ∀ {n m} {i : Fin n} {j : Fin m} -> i ≅ j -> n ≡ m
fail refl = {!!}
-- n != m of type ℕ
-- when checking that the pattern refl has type i ≅ j
because Fin n ≡ Fin m doesn't immediately imply n ≡ m (you can make it so by enabling --injective-type-constructors, but that makes Agda anti-classical) (Fin n ≡ Fin m -> n ≡ m is provable though).
Originally Agda permitted to pattern match on x ≅ y when x and y have non-unifiable types, but that allows to write weird things like (quoting from this thread)
P : Set -> Set
P S = Σ S (\s → s ≅ true)
pbool : P Bool
pbool = true , refl
¬pfin : ¬ P (Fin 2)
¬pfin ( zero , () )
¬pfin ( suc zero , () )
¬pfin ( suc (suc ()) , () )
tada : ¬ (Bool ≡ Fin 2)
tada eq = ⊥-elim ( ¬pfin (subst (\ S → P S) eq pbool ) )
Saizan or maybe it's just ignoring the types and comparing the constructor names?
pigworker Saizan: that's exactly what I think is happening
Andread Abel:
If I slighly modify the code, I can prove Bool unequal Bool2, where true2, false2 : Bool2 (see file ..22.agda)
However, if I rename the constructors to true, false : Bool2, then suddenly I cannot prove that Bool is unequal to Bool2 anymore (see
other file). So, at the moment Agda2 compares apples and oranges in
certain situations. ;-)
So in order to pattern match on i ≅ j, where i : Fin n, j : Fin m, you first need to unify n with m
OK : ∀ {n m} {i : Fin n} {j : Fin m} -> n ≡ m -> i ≅ j -> ...
OK refl refl = ...
That's the main drawback of heteregeneous equality: you need to provide proofs of equality of indices everywhere. Usual cong and subst are non-indexed, so you also have to provide indexed versions of them (or use even more annoying cong₂ and subst₂).
There is no such problem with "heteroindexed" (I don't know if it has a proper name) equality
data [_]_≅_ {ι α} {I : Set ι} {i} (A : I -> Set α) (x : A i) : ∀ {j} -> A j -> Set where
refl : [ A ] x ≅ x
e.g.
OK : ∀ {n m} {i : Fin n} {j : Fin m} -> [ Fin ] i ≅ j -> n ≡ m
OK refl = refl
More generally, whenever you have x : A i, y : A j, p : [ A ] x ≅ y, you can pattern match on p and j will be unified with i, so you don't need to carry an additional proof of n ≡ m.
Heterogeneous equality, as it presented in Agda, is also inconsistent with the univalence axiom.
EDIT
Pattern matching on x : A, y : B, x ≅ y is equal to pattern matching on A ≡ B and then changing every y in a context to x. So when you write
fail : ∀ {n m} {i : Fin n} {j : Fin m} -> i ≅ j -> n ≡ m
fail refl = {!!}
it's the same as
fail' : ∀ {n m} {i : Fin n} {j : Fin m} -> Fin n ≡ Fin m -> i ≅ j -> n ≡ m
fail' refl refl = {!!}
but you can't pattern match on Fin n ≡ Fin m
fail-coerce : ∀ {n m} -> Fin n ≡ Fin m -> Fin n -> Fin m
fail-coerce refl = {!!}
-- n != m of type ℕ
-- when checking that the pattern refl has type Fin n ≡ Fin m
like you cannot pattern match on
fail'' : ∀ {n m} -> Nat.pred n ≡ Nat.pred m -> n ≡ m
fail'' refl = {!!}
-- n != m of type ℕ
-- when checking that the pattern refl has type Nat.pred n ≡ Nat.pred m
In general
f-inj : ∀ {n m} -> f n ≡ f m -> ...
f-inj refl = ...
works only if f is obviously injective. I.e. if f is a series of constructors (e.g. suc (suc n) ≡ suc (suc m)) or computes to it (e.g. 2 + n ≡ 2 + m). Type constructors (which Fin is) are not injective because that would make Agda anti-classical, so you cannot pattern on Fin n ≡ Fin m unless you enable --injective-type-constructors.
Indices unify for
data [_]_≅_ {ι α} {I : Set ι} {i} (A : I -> Set α) (x : A i) : ∀ {j} -> A j -> Set where
refl : [ A ] x ≅ x
because you don't try to unify A i with A j, but instead explicitly carry indices in the type of [_]_≅_, which make them available for unification. When indices are unified, both types become the same A i and it's possible to proceed like with propositional equality.
EDIT
One another problem with heterogeneous equality is that it's not fully heterogeneous: in x : A, y : B, x ≅ y A and B must be in the same universe. The treatment of universe levels in data definitions has been changed recently and now we can define fully heterogeneous equality:
data _≅_ {α} {A : Set α} (x : A) : ∀ {β} {B : Set β} -> B -> Set where
refl : x ≅ x
But this doesn't work
levels-equal : ∀ {α β} -> Set α ≅ Set β -> α ≅ β
levels-equal refl = refl
-- Refuse to solve heterogeneous constraint Set α : Set (suc α) =?=
-- Set β : Set (suc β)
because Agda doesn't think suc is injective
suc-inj : {α β : Level} -> suc α ≅ suc β -> α ≅ β
suc-inj refl = refl
-- α != β of type Level
-- when checking that the pattern refl has type suc α ≅ suc β
If we postulate it, then we can prove levels-equal:
hcong : ∀ {α β δ} {A : Set α} {B : Set β} {D : Set δ} {x : A} {y : B}
-> (f : ∀ {γ} {C : Set γ} -> C -> D) -> x ≅ y -> f x ≅ f y
hcong f refl = refl
levelOf : ∀ {α} {A : Set α} -> A -> Level
levelOf {α} _ = α
postulate
suc-inj : {α β : Level} -> suc α ≅ suc β -> α ≅ β
levels-equal : ∀ {α β} -> Set α ≅ Set β -> α ≅ β
levels-equal p = suc-inj (suc-inj (hcong levelOf p))

In Idris, how to write a "vect generator" function that take a function of index in parameter

I'm trying to write in Idris a function that create a Vect by passing the size of the Vect and a function taking the index in parameter.
So far, I've this :
import Data.Fin
import Data.Vect
generate: (n:Nat) -> (Nat -> a) ->Vect n a
generate n f = generate' 0 n f where
generate': (idx:Nat) -> (n:Nat) -> (Nat -> a) -> Vect n a
generate' idx Z f = []
generate' idx (S k) f = (f idx) :: generate' (idx + 1) k f
But I would like to ensure that the function passed in parameter is only taking index lesser than the size of the Vect.
I tried that :
generate: (n:Nat) -> (Fin n -> a) ->Vect n a
generate n f = generate' 0 n f where
generate': (idx:Fin n) -> (n:Nat) -> (Fin n -> a) -> Vect n a
generate' idx Z f = []
generate' idx (S k) f = (f idx) :: generate' (idx + 1) k f
But it doesn't compile with the error
Can't convert
Fin n
with
Fin (S k)
My question is : is what I want to do possible and how ?
The key idea is that the first element of the vector is f 0, and for the tail, if you have k : Fin n, then FS k : Fin (S n) is a "shift" of the finite number that increments its value and its type at the same time.
Using this observation, we can rewrite generate as
generate : {n : Nat} -> (f : Fin n -> a) -> Vect n a
generate {n = Z} f = []
generate {n = S _} f = f 0 :: generate (f . FS)
Another possibility is to do what #dfeuer suggested and generate a vector of Fins, then map f over it:
fins : (n : Nat) -> Vect n (Fin n)
fins Z = []
fins (S n) = FZ :: map FS (fins n)
generate' : {n : Nat} -> (f : Fin n -> a) -> Vect n a
generate' f = map f $ fins _
Proving generate f = generate' f is left as en exercise to the reader.
Cactus's answer appears to be about the best way to get what you asked for, but if you want something that can be used at runtime, it will be quite inefficient. The essential reason for this is that to weaken a Fin n to a Fin n+m requires that you completely deconstruct it to change the type of its FZ, and then build it back up again. So there can be no sharing at all between the Fin values produced for each vector element. An alternative is to combine a Nat with a proof that it is below a given bound, which leads to the possibility of erasure:
data NFin : Nat -> Type where
MkNFin : (m : Nat) -> .(LT m n) -> NFin n
lteSuccLeft : LTE (S n) m -> LTE n m
lteSuccLeft {n = Z} prf = LTEZero
lteSuccLeft {n = (S k)} {m = Z} prf = absurd (succNotLTEzero prf)
lteSuccLeft {n = (S k)} {m = (S j)} (LTESucc prf) = LTESucc (lteSuccLeft prf)
countDown' : (m : Nat) -> .(m `LTE` n) -> Vect m (NFin n)
countDown' Z mLTEn = []
countDown' (S k) mLTEn = MkNFin k mLTEn :: countDown' k (lteSuccLeft mLTEn)
countDown : (n : Nat) -> Vect n (NFin n)
countDown n = countDown' n lteRefl
countUp : (n : Nat) -> Vect n (NFin n)
countUp n = reverse $ countDown n
generate : (n : Nat) -> (NFin n -> a) -> Vect n a
generate n f = map f (countUp n)
As in the Fin approach, the function you pass to generate does not need to work on all naturals; it only needs to handle ones less than n.
I used the NFin type to explicitly indicate that I want the LT m n proof to be erased in all cases. If I didn't want/need that, I could just use (m ** LT m n) instead.

How can I arrange to pattern match on a dependent view?

I've written some simple types for viewing Vect values:
data SnocVect : Vect n a -> Type where
SnocNil : SnocVect []
Snoc : (xs : Vect n a) -> (x : a) -> SnocVect (xs ++ [x])
data Split : (m : Nat) -> Vect n a -> Type where
MkSplit : (xs : Vect j a) -> (ys : Vect k a) ->
Split j (xs ++ ys)
Now it seems to me entirely reasonable that if I have a Split separating the last element of a vector, I should be able to convert that into a SnocVect:
splitToSnocVect : .{xs : Vect (S n) a} -> Split n xs ->
SnocVect xs
Unfortunately, I can't seem to find any way to implement this thing. In particular, I haven't found any way whatsoever to get it to let me pattern match on the Split n xs argument, without which I obviously can't get anywhere. I think the basic problem is that I have something of type
Split j (ps ++ [p])
and since ++ isn't injective, I need to work some sort of magic to convince the type checker that things make sense. But I don't understand this well enough to say for sure.
I finally got it! I imagine there must be a better way, but this works.
vectLengthConv : {auto a : Type} -> m = n ->
Vect m a = Vect n a
vectLengthConv prf = rewrite prf in Refl
splitToSnocVect' : .(n : Nat) -> .(xs : Vect m a) ->
.(m = n+1) -> Split n xs -> SnocVect xs
splitToSnocVect' n (ys ++ zs) prf (MkSplit {k} ys zs)
with (vectLengthConv (plusLeftCancel n k 1 prf))
splitToSnocVect' n (ys ++ []) prf
(MkSplit {k = Z} ys []) | Refl impossible
splitToSnocVect' n (ys ++ (z :: [])) prf
(MkSplit {k = (S Z)} ys (z :: [])) | lenconv =
Snoc ys z
splitToSnocVect' n (ys ++ zs) prf
(MkSplit {k = (S (S k))} ys zs) | Refl impossible
splitToSnocVect : .{n : Nat} -> .{xs : Vect (S n) a} ->
Split n xs -> SnocVect xs
splitToSnocVect {n} {xs} splt =
splitToSnocVect' n xs (plusCommutative 1 n) splt
Edit
David Christiansen suggests nixing vectLengthConv and instead using cong {f=\len=>Vect len a} (plusLeftCancel n k 1 prf) in the with clause. This helps a little.