Skip to content

Commit

Permalink
pifo: better testing (#1763)
Browse files Browse the repository at this point in the history
  • Loading branch information
anshumanmohan authored Nov 16, 2023
1 parent ffc56a7 commit 2df97f1
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 143 deletions.
62 changes: 5 additions & 57 deletions calyx-py/calyx/fifo_oracle.py
Original file line number Diff line number Diff line change
@@ -1,60 +1,8 @@
import sys
import json


def parse_json():
"""Effectively the opposite of `data_gen`:
Given a JSON file formatted for Calyx purposes, parse it into its two lists:
- The `commands` memory, which has MAX_CMDS items.
- The `values` memory, which has MAX_CMDS items.
Returns the two lists.
"""

# The JSON file is piped to us in stdin.
data = json.load(sys.stdin)
commands = data["commands"]["data"]
values = data["values"]["data"]
return commands, values


def operate_fifo(commands, values):
"""Given the three lists, operate a FIFO routine.
- Read the comammands list in order.
- When the value is 0, we "pop" the FIFO and write the value to the answer memory.
- When it is 1, we "peek" into the FIFO and write the value to the answer memory.
- When it is 2, we push the coressponding item in the `values` list to the FIFO.
In the end, we return the answer memory.
"""
fifo = []
ans = []
for cmd, val in zip(commands, values):
if cmd == 0:
if len(fifo) == 0:
break
ans.append(fifo.pop(0))
elif cmd == 1:
if len(fifo) == 0:
break
ans.append(fifo[0])
elif cmd == 2:
fifo.append(val)
# Pad the answer memory with zeroes until it is of length ANS_MEM_LEN.
ans += [0] * (10 - len(ans))
return ans


def dump_json(commands, values, ans_mem):
"""Prints a JSON representation of the data to stdout."""
payload = {
"ans_mem": ans_mem,
"commands": commands,
"values": values,
}
print(json.dumps(payload, indent=2))
import queues


if __name__ == "__main__":
commands, values = parse_json()
ans = operate_fifo(commands, values)
dump_json(commands, values, ans)
commands, values = queues.parse_json()
pifo = queues.Fifo([])
ans = queues.operate_queue(commands, values, pifo)
queues.dump_json(commands, values, ans)
11 changes: 11 additions & 0 deletions calyx-py/calyx/pifo_oracle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import queues


if __name__ == "__main__":
commands, values = queues.parse_json()

# Our PIFO is simple: it just orchestrates two FIFOs. The boundary is 200.
pifo = queues.Pifo(queues.Fifo([]), queues.Fifo([]), 200)

ans = queues.operate_queue(commands, values, pifo)
queues.dump_json(commands, values, ans)
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import random
import time
import json
from typing import Dict, Union

Expand All @@ -19,7 +18,7 @@ def dump_json():
The data itself is populated randomly, following certain rules:
- It has three "memories": `commands`, `values`, and `ans_mem`.
- The `commands` memory has MAX_CMDS items, which are 0, 1, or 2.
- The `values` memory has MAX_CMDS items: random values between 0 and 100.
- The `values` memory has MAX_CMDS items: random values between 0 and 400.
- The `ans_mem` memory has ANS_MEM_LEN items, all zeroes.
- Each memory has a `format` field, which is a format object for a bitvector.
"""
Expand All @@ -32,8 +31,8 @@ def dump_json():
}
values = {
"values": {
"data": [random.randint(0, 100) for _ in range(MAX_CMDS)],
# The `values` memory has MAX_CMDS items: random values between 0 and 100.
"data": [random.randint(0, 400) for _ in range(MAX_CMDS)],
# The `values` memory has MAX_CMDS items: random values between 0 and 00.
"format": format_gen(32),
}
}
Expand Down
171 changes: 171 additions & 0 deletions calyx-py/calyx/queues.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import sys
import json
from dataclasses import dataclass
from typing import List

ANS_MEM_LEN = 10


@dataclass
class Fifo:
"""A FIFO data structure.
Supports the operations `push`, `pop`, and `peek`.
"""

def __init__(self, data: List[int]):
self.data = data

def push(self, val: int):
"""Pushes `val` to the FIFO."""
self.data.append(val)

def pop(self) -> int:
"""Pops the FIFO."""
if len(self.data) == 0:
raise IndexError("Cannot pop from empty FIFO.")
return self.data.pop(0)

def peek(self) -> int:
"""Peeks into the FIFO."""
if len(self.data) == 0:
raise IndexError("Cannot peek into empty FIFO.")
return self.data[0]

def __len__(self) -> int:
return len(self.data)


@dataclass
class Pifo:
"""A PIFO data structure.
Supports the operations `push`, `pop`, and `peek`.
We do this by maintaining two queues that are given to us at initialization.
We toggle between these queues when popping/peeking.
We have a variable called `hot` that says which queue is to be popped/peeked next.
`hot` starts at 0.
We also take at initialization a `boundary` value.
We maintain internally a variable called `pifo_len`:
the sum of the lengths of the two queues.
When asked to pop:
- If `pifo_len` is 0, we raise an error.
- Else, if `hot` is 0, we try to pop from queue_0.
+ If it succeeds, we flip `hot` to 1 and return the value we got.
+ If it fails, we pop from queue_1 and return the value we got.
We leave `hot` as it was.
- If `hot` is 1, we proceed symmetrically.
- We decrement `pifo_len` by 1.
When asked to peek:
We do the same thing as above, except:
- We peek instead of popping.
- We don't flip `hot`.
When asked to push:
- If the value to be pushed is less than `boundary`, we push it into queue_1.
- Else, we push it into queue_2.
- We increment `pifo_len` by 1.
"""

def __init__(self, queue_1, queue_2, boundary):
self.data = (queue_1, queue_2)
self.hot = 0
self.pifo_len = len(queue_1) + len(queue_2)
self.boundary = boundary

def push(self, val: int):
"""Pushes `val` to the PIFO."""
if val < self.boundary:
self.data[0].push(val)
else:
self.data[1].push(val)
self.pifo_len += 1

def pop(self) -> int:
"""Pops the PIFO."""
if self.pifo_len == 0:
raise IndexError("Cannot pop from empty PIFO.")
self.pifo_len -= 1 # We decrement `pifo_len` by 1.
if self.hot == 0:
try:
self.hot = 1
return self.data[0].pop()
except IndexError:
return self.data[1].pop()
else:
try:
self.hot = 0
return self.data[1].pop()
except IndexError:
return self.data[0].pop()

def peek(self) -> int:
"""Peeks into the PIFO."""
if self.pifo_len == 0:
raise IndexError("Cannot peek into empty PIFO.")
if self.hot == 0:
try:
return self.data[0].peek()
except IndexError:
return self.data[1].peek()
else:
try:
return self.data[1].peek()
except IndexError:
return self.data[0].peek()

def __len__(self) -> int:
return self.pifo_len


def parse_json():
"""Effectively the opposite of `data_gen`:
Given a JSON file formatted for Calyx purposes, parse it into its two lists:
- The `commands` memory, which has MAX_CMDS items.
- The `values` memory, which has MAX_CMDS items.
Returns the two lists.
"""

data = json.load(sys.stdin)
commands = data["commands"]["data"]
values = data["values"]["data"]
return commands, values


def dump_json(commands, values, ans_mem):
"""Prints a JSON representation of the data to stdout."""
payload = {
"ans_mem": ans_mem,
"commands": commands,
"values": values,
}
print(json.dumps(payload, indent=2))


def operate_queue(commands, values, queue):
"""Given the two lists, one of commands and one of values.
Feed these into our queue, and return the answer memory.
"""

ans = []
for cmd, val in zip(commands, values):
if cmd == 0:
try:
ans.append(queue.pop())
except IndexError:
break

elif cmd == 1:
try:
ans.append(queue.peek())
except IndexError:
break

elif cmd == 2:
queue.push(val)

# Pad the answer memory with zeroes until it is of length ANS_MEM_LEN.
ans += [0] * (ANS_MEM_LEN - len(ans))
return ans
28 changes: 14 additions & 14 deletions calyx-py/test/correctness/fifo.data
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,21 @@
},
"values": {
"data": [
47,
60,
31,
48,
69,
13,
73,
31,
1,
93,
27,
190,
240,
126,
194,
278,
52,
35,
23,
98
293,
127,
6,
374,
110,
208,
143,
93,
392
],
"format": {
"is_signed": false,
Expand Down
44 changes: 22 additions & 22 deletions calyx-py/test/correctness/fifo.expect
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"ans_mem": [
47,
47,
47,
31,
31,
69,
13,
73,
190,
190,
190,
126,
126,
278,
52,
293,
0,
0
],
Expand All @@ -29,20 +29,20 @@
0
],
"values": [
47,
60,
31,
48,
69,
13,
73,
31,
1,
93,
27,
190,
240,
126,
194,
278,
52,
35,
23,
98
293,
127,
6,
374,
110,
208,
143,
93,
392
]
}
Loading

0 comments on commit 2df97f1

Please sign in to comment.