Skip to content

Conversation

@tannerduve
Copy link
Contributor

This PR introduces a generic query-based computation model, and reuses the TimeM monad from #165 to give it a simple cost semantics

@tannerduve tannerduve force-pushed the query-complexity-freeM branch from 7fafeed to a32b00d Compare December 5, 2025 23:39
Comment on lines +45 to +48
def evalQuery : {ι : Type} → QueryF ι → ι
| _, .read _ => 0
| _, .write _ _ => PUnit.unit
| _, .cmp x y => x ≤ y

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be more like

def evalQuery [LE α] [Inhabited α] : {ι : Type} → QueryF ι → StateT (Array α) ι
  | _, .read i      => return (← get)[i]!
  | _, .write i x   => do modify fun a => a.set! i x
  | _, .cmp i j     => do let a ← get; return a[i]! ≤ a[j]!

You need a real model if you want to prove correctness

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Inhabited here flags that this query API is not great, as it allows illegal queries. You might want to parametrize QueryF by the array length.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks. That's a valid point. Fwiw, this entire example has several points where it is still possible to sneak in flawed code. Currently I am trying to get the typeclasses and composition theorems right, then I can redo this example.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do note that evalQuery is meant to execute the computations. The time complexity evaluation will be done by translation to the time monad with ticks

Comment on lines +35 to +36
/-- Write value `v` at index `i`. -/
| write : Nat → Nat → QueryF PUnit

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you refer to names in the docstring they should exist in the code

  /-- Write value `v` at index `i`. -/
  | write (i : Nat) (v : Nat) : QueryF PUnit

etc

open Cslib.Algorithms


/-- The Model for comparison sorting natural-number registers.-/

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not generalize to arbitrary registers?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still WIP. I will make this change once I have generalized the query type, and I am instantiating it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR started from a proof-of-concept in the zulip, posted by Tanner. It has some way to go.

Comment on lines +33 to +36
/-- Read the value stored at index `i`. -/
| read : Nat → QueryF Nat
/-- Write value `v` at index `i`. -/
| write : Nat → Nat → QueryF PUnit

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fact your sorting implementation doesn't use these at all suggests that they're not really useful as query operators. Even if you did force the API to manually read and write, I don't think you would be able to prevent an implementation that:

  • reads every entry in sequence into a List
  • does a mergesort
  • writes the outputs back

which makes counting reads and writes pretty uninteresting.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The correct model would have compare and swap operations.

Comment on lines +47 to +65
namespace Prog



/-- Conditional branching on a boolean program. -/
def cond {QType} {α} (b : Prog QType Bool) (t e : Prog QType α) : Prog QType α :=
b.bind (fun b' => if b' then t else e)

/-- A counting loop from `0` to `n - 1`, sequencing the body. -/
def forLoop {QType} (n : Nat) (body : Nat → Prog QType PUnit) : Prog QType PUnit :=
go n
where
/-- Auxiliary recursive worker for `forLoop`. -/
go : Nat → Prog QType PUnit
| 0 => pure ()
| i + 1 =>
body i >>= fun _ => go i

end Prog

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed on zulip, this is do if and do for, no need for new defs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants