Skip to content

Commit

Permalink
Queues: binary heap (#2074)
Browse files Browse the repository at this point in the history
* Some sketching

* Quick fixed to make it generate

* Tuplify

* Untuplify

* Tidy

* Stash

* More component-based approach

* Swap group

* Checking in aspirational yx file

* Thrashing. Need comb components

* Baby data file

* Attempt at CombComponent in ast

* Attempt to add comb comp to builder

* Test comb groups with tuplify

* Testing for comb comp

* Stray assert

* Fix slice helper, use correctly

* Stray change from merge

* Use HI instead of 1

* Smaller, tidier, py file

* Two values

* Smaller queue

* Add rudimentary cmp component

* Add three elements, no swaps

* Make cmp a comb component

* Trying to actually run algorithm. Wrong

* Little tinkering

* Parent, child indices

* Comb mem all over

* Aha. Just find_parent is wrong

* Minimize issue

* Move to seq mem

* fix

* Working thanks to #2071

* Tidy some helpers

* Move to mem of length 15

* Bubbling up works, insert works

* Simplify interface

* One more push that does not trigger a swap

* Put back the well-formedness pass I had removed

* Simplify parent-finding routine

* Tinkering

* Update runt.toml

* Tidy docstring

---------

Co-authored-by: Caleb <[email protected]>
Co-authored-by: calebmkim <[email protected]>
  • Loading branch information
3 people authored Jun 3, 2024
1 parent dd643a8 commit 4c21a1a
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 0 deletions.
26 changes: 26 additions & 0 deletions calyx-py/test/correctness/queues/binheap.data
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"mem": {
"data": [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
"format": {
"is_signed": false,
"numeric_type": "bitnum",
"width": 64
}
}
}
19 changes: 19 additions & 0 deletions calyx-py/test/correctness/queues/binheap.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"mem": [
3,
6,
9,
12,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
]
}
137 changes: 137 additions & 0 deletions calyx-py/test/correctness/queues/binheap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# pylint: disable=import-error
import calyx.builder as cb


def insert_swap(prog, name, width, len, idx_w):
"""Inserts the component `swap` into the program.
It takes two `idx_w`-bit inputs `a` and `b` and accepts a memory by reference.
The memory is a `len`-element memory of `width`-bit elements.
It swaps the values in the memory at addresses `a` and `b`.
"""

comp = prog.component(name)
a = comp.input("a", idx_w)
b = comp.input("b", idx_w)
mem = comp.seq_mem_d1("mem", width, len, idx_w, is_ref=True)

val_a = comp.reg(width)
val_b = comp.reg(width)

load_a_a = comp.mem_load_d1(mem, a, val_a, "load_a") # val_a := mem[a]
load_b_b = comp.mem_load_d1(mem, b, val_b, "load_b") # val_b := mem[b]

store_a_b = comp.mem_store_d1(mem, a, val_b.out, "store_a") # mem[a] := val_b
store_b_a = comp.mem_store_d1(mem, b, val_a.out, "store_b") # mem[b] := val_a

comp.control += [load_a_a, load_b_b, store_a_b, store_b_a]

return comp


def insert_binheap(prog, name):
"""Inserts the component `binheap` into the program.
It is a minimum binary heap, represented as an array.
The heap just supports the `push` operation.
Its only input is `value`, the value to push to the queue.
"""
comp: cb.ComponentBuilder = prog.component(name)
value = comp.input("value", 64) # The value to push to the heap

swap = comp.cell("swap", insert_swap(prog, "swap", 64, 15, 4))

mem = comp.seq_mem_d1("mem", 64, 15, 4, is_ref=True)
# The memory to store the heap, represented as an array.
# For now it has a hardcoded max length of 15, i.e., a binary heap of height 4.
# Each cell of the memory is 64 bits wide.

sub = comp.sub(4)
rsh = comp.rsh(4)

size = comp.reg(4) # Current size
parent_idx = comp.reg(4)
parent_val = comp.reg(64)
child_idx = comp.reg(4)
child_val = comp.reg(64)

read_parent = comp.mem_load_d1(mem, parent_idx.out, parent_val, "read_parent")
read_child = comp.mem_load_d1(mem, child_idx.out, child_val, "read_child")

with comp.group("find_parent_idx") as find_parent_idx:
# Find the parent of the `child_idx`th element and store it in `parent_idx`.
# parent_idx := floor((child_idx − 1) / 2)
sub.left = child_idx.out
sub.right = 1
rsh.left = sub.out
rsh.right = cb.const(4, 1)
parent_idx.in_ = rsh.out
parent_idx.write_en = cb.HI
find_parent_idx.done = parent_idx.done

set_child_idx = comp.reg_store(child_idx, size.out) # child_idx := size
store_new_val = comp.mem_store_d1(
mem, child_idx.out, value, "store_new_val"
) # mem[child_idx] := value
incr_size = comp.incr(size)
child_lt_parent = comp.lt_use(child_val.out, parent_val.out)
bubble_child_idx = comp.reg_store(child_idx, parent_idx.out, "bubble_child_idx")

comp.control += [
set_child_idx,
store_new_val,
incr_size,
find_parent_idx,
read_parent,
read_child,
cb.while_with(
child_lt_parent,
[
cb.invoke(
swap,
in_a=parent_idx.out,
in_b=child_idx.out,
ref_mem=mem,
),
bubble_child_idx,
find_parent_idx,
read_parent,
read_child,
],
),
]

return comp


def insert_main(prog, binheap):
"""Inserts the main component into the program.
Invokes the `binheap` component with 64-bit values 4 and 5,
and a 64-bit memory of length 15.
"""
comp = prog.component("main")
binheap = comp.cell("binheap", binheap)

mem = comp.seq_mem_d1("mem", 64, 15, 4, is_external=True)

comp.control += [
cb.invoke(binheap, in_value=cb.const(64, 9), ref_mem=mem),
cb.invoke(binheap, in_value=cb.const(64, 12), ref_mem=mem),
cb.invoke(binheap, in_value=cb.const(64, 6), ref_mem=mem),
cb.invoke(binheap, in_value=cb.const(64, 3), ref_mem=mem),
]

return comp


def build():
"""Top-level function to build the program."""
prog = cb.Builder()
binheap = insert_binheap(prog, "binheap")
_ = insert_main(prog, binheap)
return prog.program


if __name__ == "__main__":
build().emit()

0 comments on commit 4c21a1a

Please sign in to comment.