Skip to content

Instantly share code, notes, and snippets.

@WorldSEnder
Last active June 19, 2019 21:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save WorldSEnder/bb7f4ebbc31cee2a7a078b16e1dd8162 to your computer and use it in GitHub Desktop.
Save WorldSEnder/bb7f4ebbc31cee2a7a078b16e1dd8162 to your computer and use it in GitHub Desktop.
Simulation modular automata
{-# OPTIONS --cubical #-}
module modularAutomata where
open import Data.Maybe
open import Cubical.Data.Prod
open import Cubical.Foundations.Everything
-- first, setup a type of infinite (coinductive) lists, we will need it later for simulation
record νList (A : Set) : Set where
coinductive
constructor _,_
field
head : A
tail : Maybe (νList A)
-- next, define what we need from a queue to compute with it
-- we want to access its head and tail, and prepend must be
-- "inverse" to those.
-- we don't use the laws so far, but they would be nice to have
-- in a correctness proof I guess
record QueueType (Word : Set) : Set₁ where
field
Queue : Set
-- operations
prepend : Word Word Queue Queue
head : Queue Word
tail : Queue Queue
--laws
head-prepend : a b q head (prepend a b q) ≡ a
head-tail-prepend : a b q head (tail (prepend a b q)) ≡ b
tail-tail-prepend : a b q tail (tail (prepend a b q)) ≡ q
data LR : Set where
L R : LR
module Automata (Word : Set) (QT : QueueType Word) where
open QueueType QT
State = Queue × Queue
getLeft = proj₁
getRight = proj₂
-- group in a record for readability
record ConfigurationResult : Set where
constructor _∣_,_
field
side : LR
first second : Word
-- a configuration is a total function that maps two words
-- to either nothing (a terminal state) or two words and a side
-- to push them
Configuration = Word Word Maybe ConfigurationResult
-- helper to prepend a configuration result in front of a given state
prepend-result : State ConfigurationResult State
prepend-result state (L ∣ first , second) = prepend first second (getLeft state) , getRight state
prepend-result state (R ∣ first , second) = getLeft state , prepend first second (getRight state)
-- one step of the simulation. Evaluate the configuration at the heads, the prepend-result on the tail state
simulateStep : Configuration State Maybe State
simulateStep config state = Data.Maybe.map (prepend-result stateTail) (config firstLeft firstRight) where
firstLeft = getLeft state .head
firstRight = getRight state .head
stateTail = getLeft state .tail , getRight state .tail
-- full simulation. This just loops simulate step, gradually unfolding into a νList
simulate : Configuration State νList State
νList.head (simulate config state) = state
νList.tail (simulate config state) with simulateStep config state
... | nothing = nothing
... | just nextStep = just (simulate config nextStep)
open import Data.Nat
open import Data.Fin using (Fin)
module FiniteAutomata (m : ℕ) = Automata (Fin m)
open import Cubical.Codata.Stream
open import Data.List
-- two possible queue types. Although StreamQueue is not used later on, it's
-- nice to show off. Since a stream can't be collapsed into a single value like List
-- it's not as nice to debug, though
StreamQueue : A QueueType A
StreamQueue A = record
{ Queue = Stream A
; prepend = λ a b q a , b , q
; head = Stream.head
; tail = Stream.tail
; head-prepend = λ a b q i a
; head-tail-prepend = λ a b q i b
; tail-tail-prepend = λ a b q i q
}
-- note that for a ListQueue you need to choose a default element that head returns
-- if the list is empty
ListQueue : A A QueueType A
ListQueue A zr = record
{ Queue = List A
; prepend = λ a b ls a ∷ b ∷ ls
; head = λ { [] zr ; (x ∷ xs) x }
; tail = λ { [] [] ; (x ∷ xs) xs }
; head-prepend = λ a b q i a
; head-tail-prepend = λ a b q i b
; tail-tail-prepend = λ a b q i q
}
module Example where
open import Data.Fin using (Fin; toℕ; #_)
open import Data.Nat.DivMod
-- if we have a configuration for some type A, and two functions (not necessarily an isomorphism) between A and B
-- we can construct a configuration of type B
remapConfig : {A B P Q} (B A) (A B) Automata.Configuration A P Automata.Configuration B Q
remapConfig {A} {B} {P} {Q} f g configA a b = Data.Maybe.map mapResult (configA (f a) (f b)) where
open Automata -- to get the constructor _∣_,_ in scope
mapResult : Automata.ConfigurationResult A P Automata.ConfigurationResult B Q
mapResult (side ∣ first , second ) = (side ∣ g first , g second )
-- takeLimit: get a potentially infinite list and take at most n elements from it
takeLimit : {A} (n : ℕ) νList A List A
takeLimit zero vl = []
takeLimit (suc x) vl with vl .νList.tail
... | nothing = vl .νList.head ∷ []
... | just tl = vl .νList.head ∷ takeLimit x tl
-- for automata over lists over some finite field, we can display the state as simply integers
module _ {m : ℕ} {d : Fin m} where
open FiniteAutomata m (ListQueue _ d)
collapseToℕ : {m} List (Fin m)
collapseToℕ [] = 0
collapseToℕ {m} (x ∷ xs) = collapseToℕ xs * m + toℕ x
humanizeState : State ℕ × ℕ
humanizeState (l , r) = collapseToℕ l , collapseToℕ r
simulateStepSample : Configuration State Maybe (ℕ × ℕ)
simulateStepSample config state = Data.Maybe.map humanizeState (simulateStep config state)
simulateAndSample : Configuration State List (ℕ × ℕ)
simulateAndSample config state = Data.List.map humanizeState (takeLimit 10 (simulate config state))
-- now, let's finally construct our example
open FiniteAutomata 5 (ListQueue _ (# zero))
hiding (_∣_,_) -- hide this, we will remap the configuration from ℕ
pattern one = suc 0
pattern two = suc (suc 0)
pattern four = suc (suc (suc (suc 0)))
testConfig : Configuration
testConfig = remapConfig toℕ (_mod 5) config where
open Automata
config : Automata.Configuration ℕ (ListQueue ℕ zero)
config zero zero = just (R ∣ 2 , zero )
config two two = just (L ∣ 4 , zero )
config four one = just (R ∣ zero , zero )
config x y = nothing
testState : State
testState = # 2 ∷ # 2 ∷ [] , # 2 ∷ # 1 ∷ []
stepTest : _
stepTest = simulateStepSample testConfig testState
test : _
test = simulateAndSample testConfig testState
testResult : test ≡ (12 , 7) ∷ (54 , 1) ∷ (10 , 0) ∷ (2 , 2) ∷ (4 , 0) ∷ []
testResult = refl
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment