From 3c3079e6acf7d96b67de7e1635e26d9c1ae477ff Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Tue, 7 Nov 2023 13:31:19 -0500 Subject: [PATCH 01/17] Oracle for FIFOs --- calyx-py/test/correctness/fifo_data_gen.py | 52 +++++++++++++++++++++ calyx-py/test/correctness/fifo_oracle.py | 54 ++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 calyx-py/test/correctness/fifo_data_gen.py create mode 100644 calyx-py/test/correctness/fifo_oracle.py diff --git a/calyx-py/test/correctness/fifo_data_gen.py b/calyx-py/test/correctness/fifo_data_gen.py new file mode 100644 index 000000000..636b95a3c --- /dev/null +++ b/calyx-py/test/correctness/fifo_data_gen.py @@ -0,0 +1,52 @@ +import random +import json +from typing import Dict, Union + +MAX_CMDS = 15 +ANS_MEM_LEN = 10 + +FormatType = Dict[str, Union[bool, str, int]] + + +def format_gen(width: int) -> FormatType: + """Generates a format object for a bitvector of length `width`.""" + return {"is_signed": False, "numeric_type": "bitnum", "width": width} + + +def dump_json(): + """Prints a JSON representation of the data to stdout. + The data itself is populated randomly, following certain rules: + - It has three "memories": `commands`, `values`, and `ans_mem`. + - The `commands` array has MAX_CMDS items, which are 0, 1, or 2. + - The `values` array has MAX_CMDS items: random values between 0 and 100. + - The `ans_mem` array has ANS_MEM_LEN items, all zeroes. + - Each memory has a `format` field, which is a format object for a bitvector. + """ + commands = { + "commands": { + "data": [random.randint(0, 2) for _ in range(MAX_CMDS)], + # The `commands` array has MAX_CMDS items, which are 0, 1, or 2. + "format": format_gen(2), + } + } + values = { + "values": { + "data": [random.randint(0, 100) for _ in range(MAX_CMDS)], + # The `values` array has MAX_CMDS items: random values between 0 and 100. + "format": format_gen(32), + } + } + ans_mem = { + "ans_mem": { + "data": [0 for _ in range(ANS_MEM_LEN)], + # The `ans_mem` array has ANS_MEM_LEN items, all zeroes. + "format": format_gen(32), + } + } + + print(json.dumps(commands | values | ans_mem, indent=2)) + + +if __name__ == "__main__": + random.seed(4) + dump_json() diff --git a/calyx-py/test/correctness/fifo_oracle.py b/calyx-py/test/correctness/fifo_oracle.py new file mode 100644 index 000000000..dee3d8076 --- /dev/null +++ b/calyx-py/test/correctness/fifo_oracle.py @@ -0,0 +1,54 @@ +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` array, which has MAX_CMDS items. + - The `values` array, which has MAX_CMDS items. + + Returns the three lists. + """ + with open("fifo.data", "r") as f: + data = json.load(f) + 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: + ans.append(fifo.pop(0)) + elif cmd == 1: + 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): + payload = { + "ans_mem": ans_mem, + "commands": commands, + "values": values, + } + print(json.dumps(payload, indent=2)) + + +if __name__ == "__main__": + commands, values = parse_json() + ans = operate_fifo(commands, values) + dump_json(commands, values, ans) From ab0408d3600d6039c06fd8e81b02909449f48d69 Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Tue, 7 Nov 2023 13:43:03 -0500 Subject: [PATCH 02/17] Good pseudorandom seed --- calyx-py/test/correctness/fifo_data_gen.py | 3 ++- calyx-py/test/correctness/fifo_oracle.py | 10 ++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/calyx-py/test/correctness/fifo_data_gen.py b/calyx-py/test/correctness/fifo_data_gen.py index 636b95a3c..d35de6086 100644 --- a/calyx-py/test/correctness/fifo_data_gen.py +++ b/calyx-py/test/correctness/fifo_data_gen.py @@ -1,4 +1,5 @@ import random +import time import json from typing import Dict, Union @@ -48,5 +49,5 @@ def dump_json(): if __name__ == "__main__": - random.seed(4) + random.seed(5) dump_json() diff --git a/calyx-py/test/correctness/fifo_oracle.py b/calyx-py/test/correctness/fifo_oracle.py index dee3d8076..aecda22a0 100644 --- a/calyx-py/test/correctness/fifo_oracle.py +++ b/calyx-py/test/correctness/fifo_oracle.py @@ -1,3 +1,4 @@ +import sys import json @@ -9,8 +10,9 @@ def parse_json(): Returns the three lists. """ - with open("fifo.data", "r") as f: - data = json.load(f) + + # 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 @@ -29,8 +31,12 @@ def operate_fifo(commands, values): 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) From 0b1e60317bf73e1b4744871ecd386aec469ffddb Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Tue, 7 Nov 2023 13:46:17 -0500 Subject: [PATCH 03/17] Move two files out of runt's way --- calyx-py/{test/correctness => calyx}/fifo_data_gen.py | 0 calyx-py/{test/correctness => calyx}/fifo_oracle.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename calyx-py/{test/correctness => calyx}/fifo_data_gen.py (100%) rename calyx-py/{test/correctness => calyx}/fifo_oracle.py (100%) diff --git a/calyx-py/test/correctness/fifo_data_gen.py b/calyx-py/calyx/fifo_data_gen.py similarity index 100% rename from calyx-py/test/correctness/fifo_data_gen.py rename to calyx-py/calyx/fifo_data_gen.py diff --git a/calyx-py/test/correctness/fifo_oracle.py b/calyx-py/calyx/fifo_oracle.py similarity index 100% rename from calyx-py/test/correctness/fifo_oracle.py rename to calyx-py/calyx/fifo_oracle.py From 7a8bbb928c2daa48623c33582e134683a0c90948 Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Tue, 7 Nov 2023 13:46:47 -0500 Subject: [PATCH 04/17] Use new, randomly generated data and expect files for actual testing --- calyx-py/test/correctness/fifo.data | 40 ++++++++++---------- calyx-py/test/correctness/fifo.expect | 54 +++++++++++++-------------- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/calyx-py/test/correctness/fifo.data b/calyx-py/test/correctness/fifo.data index f0c8359a9..65ab3d659 100644 --- a/calyx-py/test/correctness/fifo.data +++ b/calyx-py/test/correctness/fifo.data @@ -4,15 +4,15 @@ 2, 1, 2, - 2, - 0, - 2, - 2, + 1, 2, 2, 2, 2, 0, + 1, + 0, + 2, 0, 0, 0 @@ -25,21 +25,21 @@ }, "values": { "data": [ - 100, - 0, - 101, - 102, - 0, - 103, - 104, - 105, - 106, - 107, - 108, - 0, - 0, - 0, - 0 + 47, + 60, + 31, + 48, + 69, + 13, + 73, + 31, + 1, + 93, + 27, + 52, + 35, + 23, + 98 ], "format": { "is_signed": false, @@ -66,4 +66,4 @@ "width": 32 } } -} \ No newline at end of file +} diff --git a/calyx-py/test/correctness/fifo.expect b/calyx-py/test/correctness/fifo.expect index 72d5cf8bd..20595ecca 100644 --- a/calyx-py/test/correctness/fifo.expect +++ b/calyx-py/test/correctness/fifo.expect @@ -1,13 +1,13 @@ { "ans_mem": [ - 100, - 100, - 101, - 102, - 103, - 104, - 0, - 0, + 47, + 47, + 47, + 31, + 31, + 69, + 13, + 73, 0, 0 ], @@ -15,34 +15,34 @@ 2, 1, 2, - 2, - 0, - 2, - 2, + 1, 2, 2, 2, 2, 0, + 1, + 0, + 2, 0, 0, 0 ], "values": [ - 100, - 0, - 101, - 102, - 0, - 103, - 104, - 105, - 106, - 107, - 108, - 0, - 0, - 0, - 0 + 47, + 60, + 31, + 48, + 69, + 13, + 73, + 31, + 1, + 93, + 27, + 52, + 35, + 23, + 98 ] } From d7eca6eb36805e501b53f63c8a7a6595d267b69b Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Tue, 7 Nov 2023 14:20:10 -0500 Subject: [PATCH 05/17] Nits --- calyx-py/calyx/fifo_data_gen.py | 12 ++++++------ calyx-py/calyx/fifo_oracle.py | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/calyx-py/calyx/fifo_data_gen.py b/calyx-py/calyx/fifo_data_gen.py index d35de6086..da07f57f4 100644 --- a/calyx-py/calyx/fifo_data_gen.py +++ b/calyx-py/calyx/fifo_data_gen.py @@ -18,29 +18,29 @@ def dump_json(): """Prints a JSON representation of the data to stdout. The data itself is populated randomly, following certain rules: - It has three "memories": `commands`, `values`, and `ans_mem`. - - The `commands` array has MAX_CMDS items, which are 0, 1, or 2. - - The `values` array has MAX_CMDS items: random values between 0 and 100. - - The `ans_mem` array has ANS_MEM_LEN items, all zeroes. + - 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 `ans_mem` memory has ANS_MEM_LEN items, all zeroes. - Each memory has a `format` field, which is a format object for a bitvector. """ commands = { "commands": { "data": [random.randint(0, 2) for _ in range(MAX_CMDS)], - # The `commands` array has MAX_CMDS items, which are 0, 1, or 2. + # The `commands` memory has MAX_CMDS items, which are 0, 1, or 2. "format": format_gen(2), } } values = { "values": { "data": [random.randint(0, 100) for _ in range(MAX_CMDS)], - # The `values` array has MAX_CMDS items: random values between 0 and 100. + # The `values` memory has MAX_CMDS items: random values between 0 and 100. "format": format_gen(32), } } ans_mem = { "ans_mem": { "data": [0 for _ in range(ANS_MEM_LEN)], - # The `ans_mem` array has ANS_MEM_LEN items, all zeroes. + # The `ans_mem` memory has ANS_MEM_LEN items, all zeroes. "format": format_gen(32), } } diff --git a/calyx-py/calyx/fifo_oracle.py b/calyx-py/calyx/fifo_oracle.py index aecda22a0..6ca4fbd54 100644 --- a/calyx-py/calyx/fifo_oracle.py +++ b/calyx-py/calyx/fifo_oracle.py @@ -5,10 +5,9 @@ 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` array, which has MAX_CMDS items. - - The `values` array, which has MAX_CMDS items. - - Returns the three 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. @@ -46,6 +45,7 @@ def operate_fifo(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, From 70a69ee83d89fcee1bae46d6a1c9ac74b58de308 Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Tue, 7 Nov 2023 18:40:01 -0500 Subject: [PATCH 06/17] Generalize fifo_data_gen to all kinds of queues --- calyx-py/calyx/{fifo_data_gen.py => queue_data_gen.py} | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) rename calyx-py/calyx/{fifo_data_gen.py => queue_data_gen.py} (93%) diff --git a/calyx-py/calyx/fifo_data_gen.py b/calyx-py/calyx/queue_data_gen.py similarity index 93% rename from calyx-py/calyx/fifo_data_gen.py rename to calyx-py/calyx/queue_data_gen.py index da07f57f4..13f5cf211 100644 --- a/calyx-py/calyx/fifo_data_gen.py +++ b/calyx-py/calyx/queue_data_gen.py @@ -1,5 +1,4 @@ import random -import time import json from typing import Dict, Union @@ -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. """ @@ -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), } } From 61b43e5a91bcd3c48923cdb3c573503b4d090ec3 Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Tue, 7 Nov 2023 19:03:29 -0500 Subject: [PATCH 07/17] Automatic generation of PIFO data and expect --- calyx-py/calyx/fifo_oracle.py | 4 +- calyx-py/calyx/pifo_oracle.py | 79 +++++++++++++++++++++++++++ calyx-py/test/correctness/pifo.data | 38 ++++++------- calyx-py/test/correctness/pifo.expect | 54 +++++++++--------- 4 files changed, 127 insertions(+), 48 deletions(-) create mode 100644 calyx-py/calyx/pifo_oracle.py diff --git a/calyx-py/calyx/fifo_oracle.py b/calyx-py/calyx/fifo_oracle.py index 6ca4fbd54..66cfdebdb 100644 --- a/calyx-py/calyx/fifo_oracle.py +++ b/calyx-py/calyx/fifo_oracle.py @@ -18,8 +18,8 @@ def parse_json(): def operate_fifo(commands, values): - """Given the three lists, operate a FIFO routine. - - Read the comammands list in order. + """Given the two lists, operate a FIFO routine. + - Read the commands 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. diff --git a/calyx-py/calyx/pifo_oracle.py b/calyx-py/calyx/pifo_oracle.py new file mode 100644 index 000000000..282ccd079 --- /dev/null +++ b/calyx-py/calyx/pifo_oracle.py @@ -0,0 +1,79 @@ +import fifo_oracle + + +def operate_pifo(commands, values): + """Given the three lists, operate a PIFO routine. + We do this by maintaining two FIFOs and toggling between them when popping. + We have a variable called `hot` that indicates which FIFO is to be popped next. + `hot` starts at 1. + + - Read the commands list in order. + - When the value is 0, we "pop" the PIFO and write the value to the answer memory. + + This is a little complicated since we are actually popping from two FIFOs. + + If `len(FIFO_1) + len(FIFO_2)` = 0, break. + + Try `pop(FIFO_{hot})`. + * If it succeeds it will return a value `v`; just put `v` in + the answer memory. + Also flip `hot` so it points to the other sub-queue. + * If it fails because of underflow, return `pop(queue_{not-hot})`. + Leave `hot` as it was. + - When it is 1, we "peek" into the PIFO and write the value to the answer memory. + - When it is 2, we push the coressponding item in the `values` list into + one of our two FIFOs. + + In particular, if the value is less than 200, it goes into the first FIFO. + + If it is greater than 200, it goes into the second FIFO. + + In the end, we return the answer memory. + """ + fifo_1 = [] + fifo_2 = [] + ans = [] + hot = 1 + for cmd, val in zip(commands, values): + pifo_len = len(fifo_1) + len(fifo_2) + if cmd == 0: + if pifo_len == 0: + break + # We have to pop from the PIFO. + if hot == 1: + try: + ans.append(fifo_1.pop(0)) # Suceess. + hot = 2 # Flip hot. + except IndexError: + ans.append(fifo_2.pop(0)) # Recovery. Leave hot as it was. + else: + try: + ans.append(fifo_2.pop(0)) # Suceess. + hot = 1 # Flip hot. + except IndexError: + ans.append(fifo_1.pop(0)) # Recovery. Leave hot as it was. + elif cmd == 1: + if pifo_len == 0: + break + # We have to peek into the PIFO. + if hot == 1: + try: + ans.append(fifo_1[0]) # Suceess. + except IndexError: + ans.append(fifo_2[0]) # Recovery. + else: + try: + ans.append(fifo_2[0]) # Suceess. + except IndexError: + ans.append(fifo_1[0]) # Recovery. + elif cmd == 2: + # We have to push into the PIFO. + if val < 200: + fifo_1.append(val) + else: + fifo_2.append(val) + + # Pad the answer memory with zeroes until it is of length ANS_MEM_LEN. + ans += [0] * (10 - len(ans)) + return ans + + +if __name__ == "__main__": + commands, values = fifo_oracle.parse_json() + ans = operate_pifo(commands, values) + fifo_oracle.dump_json(commands, values, ans) diff --git a/calyx-py/test/correctness/pifo.data b/calyx-py/test/correctness/pifo.data index fc06886e7..5f72a3db8 100644 --- a/calyx-py/test/correctness/pifo.data +++ b/calyx-py/test/correctness/pifo.data @@ -2,19 +2,19 @@ "commands": { "data": [ 2, + 1, 2, - 0, - 0, + 1, 2, 2, 2, 2, - 1, 0, + 1, 0, + 2, 0, 0, - 2, 0 ], "format": { @@ -25,21 +25,21 @@ }, "values": { "data": [ - 101, - 102, - 0, - 0, - 103, - 104, - 201, - 202, - 0, - 0, - 0, - 0, - 0, - 105, - 0 + 190, + 240, + 126, + 194, + 278, + 52, + 293, + 127, + 6, + 374, + 110, + 208, + 143, + 93, + 392 ], "format": { "is_signed": false, diff --git a/calyx-py/test/correctness/pifo.expect b/calyx-py/test/correctness/pifo.expect index 0631155c8..e29484e3f 100644 --- a/calyx-py/test/correctness/pifo.expect +++ b/calyx-py/test/correctness/pifo.expect @@ -1,48 +1,48 @@ { "ans_mem": [ - 101, - 102, - 201, - 201, - 103, - 202, - 104, - 105, + 190, + 190, + 190, + 278, + 278, + 126, + 293, + 52, 0, 0 ], "commands": [ 2, + 1, 2, - 0, - 0, + 1, 2, 2, 2, 2, - 1, 0, + 1, 0, + 2, 0, 0, - 2, 0 ], "values": [ - 101, - 102, - 0, - 0, - 103, - 104, - 201, - 202, - 0, - 0, - 0, - 0, - 0, - 105, - 0 + 190, + 240, + 126, + 194, + 278, + 52, + 293, + 127, + 6, + 374, + 110, + 208, + 143, + 93, + 392 ] } From 481f8189aa480a0e01258a7775a9a1cc0ebb3ede Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Wed, 8 Nov 2023 11:17:14 -0500 Subject: [PATCH 08/17] OO style for FIFO --- calyx-py/calyx/fifo_oracle.py | 59 ++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/calyx-py/calyx/fifo_oracle.py b/calyx-py/calyx/fifo_oracle.py index 66cfdebdb..8c106f93a 100644 --- a/calyx-py/calyx/fifo_oracle.py +++ b/calyx-py/calyx/fifo_oracle.py @@ -1,6 +1,11 @@ import sys import json +from dataclasses import dataclass +from typing import List + +ANS_MEM_LEN = 10 + def parse_json(): """Effectively the opposite of `data_gen`: @@ -17,6 +22,37 @@ def parse_json(): 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)) + + +@dataclass +class Fifo: + """A FIFO data structure. + Supports the operations `push`, `pop`, and `peek`. + """ + + data: List[int] + + def push(self, val: int): + """Pushes `val` to the FIFO.""" + self.data.append(val) + + def pop(self) -> int: + """Pops the FIFO.""" + return self.data.pop(0) + + def peek(self) -> int: + """Peeks into the FIFO.""" + return self.data[0] + + def operate_fifo(commands, values): """Given the two lists, operate a FIFO routine. - Read the commands list in order. @@ -26,34 +62,27 @@ def operate_fifo(commands, values): In the end, we return the answer memory. """ - fifo = [] + fifo = Fifo([]) ans = [] for cmd, val in zip(commands, values): if cmd == 0: if len(fifo) == 0: break - ans.append(fifo.pop(0)) + ans.append(fifo.pop()) + elif cmd == 1: if len(fifo) == 0: break - ans.append(fifo[0]) + ans.append(fifo.peek()) + elif cmd == 2: - fifo.append(val) + fifo.push(val) + # Pad the answer memory with zeroes until it is of length ANS_MEM_LEN. - ans += [0] * (10 - len(ans)) + ans += [0] * (ANS_MEM_LEN - 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)) - - if __name__ == "__main__": commands, values = parse_json() ans = operate_fifo(commands, values) From ca732ec1317bf57ac732c3cf4506e8a8aa928e30 Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Wed, 8 Nov 2023 11:28:26 -0500 Subject: [PATCH 09/17] PIFOs in OO style --- calyx-py/calyx/pifo_oracle.py | 150 ++++++++++++++++++++++------------ 1 file changed, 97 insertions(+), 53 deletions(-) diff --git a/calyx-py/calyx/pifo_oracle.py b/calyx-py/calyx/pifo_oracle.py index 282ccd079..eec86a4e6 100644 --- a/calyx-py/calyx/pifo_oracle.py +++ b/calyx-py/calyx/pifo_oracle.py @@ -1,75 +1,119 @@ import fifo_oracle +from dataclasses import dataclass +from typing import List, Tuple + +ANS_MEM_LEN = 10 + + +@dataclass +class Pifo: + """A PIFO data structure. + Supports the operations `push`, `pop`, and `peek`. -def operate_pifo(commands, values): - """Given the three lists, operate a PIFO routine. We do this by maintaining two FIFOs and toggling between them when popping. We have a variable called `hot` that indicates which FIFO is to be popped next. `hot` starts at 1. - - Read the commands list in order. - - When the value is 0, we "pop" the PIFO and write the value to the answer memory. - + This is a little complicated since we are actually popping from two FIFOs. - + If `len(FIFO_1) + len(FIFO_2)` = 0, break. - + Try `pop(FIFO_{hot})`. - * If it succeeds it will return a value `v`; just put `v` in - the answer memory. - Also flip `hot` so it points to the other sub-queue. - * If it fails because of underflow, return `pop(queue_{not-hot})`. - Leave `hot` as it was. - - When it is 1, we "peek" into the PIFO and write the value to the answer memory. - - When it is 2, we push the coressponding item in the `values` list into - one of our two FIFOs. - + In particular, if the value is less than 200, it goes into the first FIFO. - + If it is greater than 200, it goes into the second FIFO. + We maintain a variable called `pifo_len`: the sum of the lengths of the two FIFOs. + + When asked to pop: + - If `pifo_len` is 0, we raise an error. + - Else, if `hot` is 1, we try to pop from FIFO_1. + + If it succeeds, we flip `hot` to 2 and return the value we got. + + If it fails, we pop from FIFO_2 and return the value we got. + We leave `hot` as it was. + - If `hot` is 2, 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 200, we push it into FIFO_1. + - Else, we push it into FIFO_2. + - We increment `pifo_len` by 1. + """ + + data = Tuple[fifo_oracle.Fifo, fifo_oracle.Fifo] + def __init__(self): + self.data = (fifo_oracle.Fifo([]), fifo_oracle.Fifo([])) + self.hot = 1 + self.pifo_len = 0 + + def push(self, val: int): + """Pushes `val` to the PIFO.""" + if val < 200: + 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.") + if self.hot == 1: + try: + self.pifo_len -= 1 + self.hot = 2 + return self.data[0].pop() + except IndexError: + self.pifo_len -= 1 + return self.data[1].pop() + else: + try: + self.pifo_len -= 1 + self.hot = 1 + return self.data[1].pop() + except IndexError: + self.pifo_len -= 1 + 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 == 1: + 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 operate_pifo(commands, values): + """Given the three lists, operate a PIFO routine. In the end, we return the answer memory. """ - fifo_1 = [] - fifo_2 = [] + + pifo = Pifo() ans = [] - hot = 1 for cmd, val in zip(commands, values): - pifo_len = len(fifo_1) + len(fifo_2) if cmd == 0: - if pifo_len == 0: + try: + ans.append(pifo.pop()) + except IndexError: break - # We have to pop from the PIFO. - if hot == 1: - try: - ans.append(fifo_1.pop(0)) # Suceess. - hot = 2 # Flip hot. - except IndexError: - ans.append(fifo_2.pop(0)) # Recovery. Leave hot as it was. - else: - try: - ans.append(fifo_2.pop(0)) # Suceess. - hot = 1 # Flip hot. - except IndexError: - ans.append(fifo_1.pop(0)) # Recovery. Leave hot as it was. + elif cmd == 1: - if pifo_len == 0: + try: + ans.append(pifo.peek()) + except IndexError: break - # We have to peek into the PIFO. - if hot == 1: - try: - ans.append(fifo_1[0]) # Suceess. - except IndexError: - ans.append(fifo_2[0]) # Recovery. - else: - try: - ans.append(fifo_2[0]) # Suceess. - except IndexError: - ans.append(fifo_1[0]) # Recovery. + elif cmd == 2: - # We have to push into the PIFO. - if val < 200: - fifo_1.append(val) - else: - fifo_2.append(val) + pifo.push(val) # Pad the answer memory with zeroes until it is of length ANS_MEM_LEN. - ans += [0] * (10 - len(ans)) + ans += [0] * (ANS_MEM_LEN - len(ans)) return ans From 1a03b1ec129429e1c31898969bcf7362728ad17f Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Wed, 8 Nov 2023 12:03:13 -0500 Subject: [PATCH 10/17] Generalize PIFO to take any two queues --- calyx-py/calyx/pifo_oracle.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/calyx-py/calyx/pifo_oracle.py b/calyx-py/calyx/pifo_oracle.py index eec86a4e6..16d214d61 100644 --- a/calyx-py/calyx/pifo_oracle.py +++ b/calyx-py/calyx/pifo_oracle.py @@ -11,17 +11,18 @@ class Pifo: """A PIFO data structure. Supports the operations `push`, `pop`, and `peek`. - We do this by maintaining two FIFOs and toggling between them when popping. - We have a variable called `hot` that indicates which FIFO is to be popped next. + 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 1. - We maintain a variable called `pifo_len`: the sum of the lengths of the two FIFOs. + We maintain 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 1, we try to pop from FIFO_1. + - Else, if `hot` is 1, we try to pop from queue_1. + If it succeeds, we flip `hot` to 2 and return the value we got. - + If it fails, we pop from FIFO_2 and return the value we got. + + If it fails, we pop from queue_2 and return the value we got. We leave `hot` as it was. - If `hot` is 2, we proceed symmetrically. - We decrement `pifo_len` by 1. @@ -32,15 +33,13 @@ class Pifo: - We don't flip `hot`. When asked to push: - - If the value to be pushed is less than 200, we push it into FIFO_1. - - Else, we push it into FIFO_2. + - If the value to be pushed is less than 200, we push it into queue_1. + - Else, we push it into queue_2. - We increment `pifo_len` by 1. """ - data = Tuple[fifo_oracle.Fifo, fifo_oracle.Fifo] - - def __init__(self): - self.data = (fifo_oracle.Fifo([]), fifo_oracle.Fifo([])) + def __init__(self, queue_1, queue_2): + self.data = (queue_1, queue_2) self.hot = 1 self.pifo_len = 0 @@ -91,10 +90,13 @@ def peek(self) -> int: def operate_pifo(commands, values): """Given the three lists, operate a PIFO routine. + In this case, we have our PIFO just orchestrate two FIFOs. In the end, we return the answer memory. """ - pifo = Pifo() + pifo = Pifo(fifo_oracle.Fifo([]), fifo_oracle.Fifo([])) + # Our PIFO is simple: it just orchestrates two FIFOs. + ans = [] for cmd, val in zip(commands, values): if cmd == 0: From 5f9ca75f6e340562e4b48fc397277f34135ff5ec Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Wed, 8 Nov 2023 12:19:06 -0500 Subject: [PATCH 11/17] Lift the different queues into their own file --- calyx-py/calyx/fifo_oracle.py | 37 ++------- calyx-py/calyx/pifo_oracle.py | 88 +------------------- calyx-py/calyx/queues.py | 114 ++++++++++++++++++++++++++ calyx-py/test/correctness/fifo.data | 28 +++---- calyx-py/test/correctness/fifo.expect | 44 +++++----- 5 files changed, 160 insertions(+), 151 deletions(-) create mode 100644 calyx-py/calyx/queues.py diff --git a/calyx-py/calyx/fifo_oracle.py b/calyx-py/calyx/fifo_oracle.py index 8749e247e..da21a12d4 100644 --- a/calyx-py/calyx/fifo_oracle.py +++ b/calyx-py/calyx/fifo_oracle.py @@ -1,8 +1,6 @@ import sys import json - -from dataclasses import dataclass -from typing import List +import queues ANS_MEM_LEN = 10 @@ -32,27 +30,6 @@ def dump_json(commands, values, ans_mem): print(json.dumps(payload, indent=2)) -@dataclass -class Fifo: - """A FIFO data structure. - Supports the operations `push`, `pop`, and `peek`. - """ - - data: List[int] - - def push(self, val: int): - """Pushes `val` to the FIFO.""" - self.data.append(val) - - def pop(self) -> int: - """Pops the FIFO.""" - return self.data.pop(0) - - def peek(self) -> int: - """Peeks into the FIFO.""" - return self.data[0] - - def operate_fifo(commands, values): """Given the two lists, operate a FIFO routine. - Read the commands list in order. @@ -62,18 +39,20 @@ def operate_fifo(commands, values): In the end, we return the answer memory. """ - fifo = Fifo([]) + fifo = queues.Fifo([]) ans = [] for cmd, val in zip(commands, values): if cmd == 0: - if len(fifo) == 0: + try: + ans.append(fifo.pop()) + except IndexError: break - ans.append(fifo.pop()) elif cmd == 1: - if len(fifo) == 0: + try: + ans.append(fifo.peek()) + except IndexError: break - ans.append(fifo.peek()) elif cmd == 2: fifo.push(val) diff --git a/calyx-py/calyx/pifo_oracle.py b/calyx-py/calyx/pifo_oracle.py index 16d214d61..12edb3d98 100644 --- a/calyx-py/calyx/pifo_oracle.py +++ b/calyx-py/calyx/pifo_oracle.py @@ -1,100 +1,16 @@ +import queues import fifo_oracle -from dataclasses import dataclass -from typing import List, Tuple - ANS_MEM_LEN = 10 -@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 1. - - We maintain 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 1, we try to pop from queue_1. - + If it succeeds, we flip `hot` to 2 and return the value we got. - + If it fails, we pop from queue_2 and return the value we got. - We leave `hot` as it was. - - If `hot` is 2, 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 200, 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): - self.data = (queue_1, queue_2) - self.hot = 1 - self.pifo_len = 0 - - def push(self, val: int): - """Pushes `val` to the PIFO.""" - if val < 200: - 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.") - if self.hot == 1: - try: - self.pifo_len -= 1 - self.hot = 2 - return self.data[0].pop() - except IndexError: - self.pifo_len -= 1 - return self.data[1].pop() - else: - try: - self.pifo_len -= 1 - self.hot = 1 - return self.data[1].pop() - except IndexError: - self.pifo_len -= 1 - 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 == 1: - 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 operate_pifo(commands, values): """Given the three lists, operate a PIFO routine. In this case, we have our PIFO just orchestrate two FIFOs. In the end, we return the answer memory. """ - pifo = Pifo(fifo_oracle.Fifo([]), fifo_oracle.Fifo([])) + pifo = queues.Pifo(queues.Fifo([]), queues.Fifo([])) # Our PIFO is simple: it just orchestrates two FIFOs. ans = [] diff --git a/calyx-py/calyx/queues.py b/calyx-py/calyx/queues.py new file mode 100644 index 000000000..ae5b00f13 --- /dev/null +++ b/calyx-py/calyx/queues.py @@ -0,0 +1,114 @@ +from dataclasses import dataclass +from typing import List, Tuple + + +@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 1. + + 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 1, we try to pop from queue_1. + + If it succeeds, we flip `hot` to 2 and return the value we got. + + If it fails, we pop from queue_2 and return the value we got. + We leave `hot` as it was. + - If `hot` is 2, 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 200, 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): + self.data = (queue_1, queue_2) + self.hot = 1 + self.pifo_len = len(queue_1) + len(queue_2) + + def push(self, val: int): + """Pushes `val` to the PIFO.""" + if val < 200: + 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 + if self.hot == 1: + try: + self.hot = 2 + return self.data[0].pop() + except IndexError: + return self.data[1].pop() + else: + try: + self.hot = 1 + 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 == 1: + 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 diff --git a/calyx-py/test/correctness/fifo.data b/calyx-py/test/correctness/fifo.data index 65ab3d659..5f72a3db8 100644 --- a/calyx-py/test/correctness/fifo.data +++ b/calyx-py/test/correctness/fifo.data @@ -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, diff --git a/calyx-py/test/correctness/fifo.expect b/calyx-py/test/correctness/fifo.expect index 20595ecca..d55133390 100644 --- a/calyx-py/test/correctness/fifo.expect +++ b/calyx-py/test/correctness/fifo.expect @@ -1,13 +1,13 @@ { "ans_mem": [ - 47, - 47, - 47, - 31, - 31, - 69, - 13, - 73, + 190, + 190, + 190, + 126, + 126, + 278, + 52, + 293, 0, 0 ], @@ -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 ] } From d02103ccd9a8825749534974c3b50b1702e185b6 Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Wed, 8 Nov 2023 12:31:10 -0500 Subject: [PATCH 12/17] Lift the runner logic too --- calyx-py/calyx/fifo_oracle.py | 79 +++-------------------------------- calyx-py/calyx/pifo_oracle.py | 40 +++--------------- calyx-py/calyx/queues.py | 64 ++++++++++++++++++++++++++-- 3 files changed, 72 insertions(+), 111 deletions(-) diff --git a/calyx-py/calyx/fifo_oracle.py b/calyx-py/calyx/fifo_oracle.py index da21a12d4..c862694e7 100644 --- a/calyx-py/calyx/fifo_oracle.py +++ b/calyx-py/calyx/fifo_oracle.py @@ -1,78 +1,11 @@ -import sys -import json import queues -ANS_MEM_LEN = 10 +if __name__ == "__main__": + commands, values = queues.parse_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 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_fifo(commands, values): - """Given the two lists, operate a FIFO routine. - - Read the commands 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 = queues.Fifo([]) - ans = [] - for cmd, val in zip(commands, values): - if cmd == 0: - try: - ans.append(fifo.pop()) - except IndexError: - break - - elif cmd == 1: - try: - ans.append(fifo.peek()) - except IndexError: - break - - elif cmd == 2: - fifo.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 - - -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)) - + pifo = queues.Fifo([]) + # Our PIFO is simple: it just orchestrates two FIFOs. -if __name__ == "__main__": - commands, values = parse_json() - ans = operate_fifo(commands, values) - dump_json(commands, values, ans) + ans = queues.operate_queue(commands, values, pifo) + queues.dump_json(commands, values, ans) diff --git a/calyx-py/calyx/pifo_oracle.py b/calyx-py/calyx/pifo_oracle.py index 12edb3d98..aef0fd470 100644 --- a/calyx-py/calyx/pifo_oracle.py +++ b/calyx-py/calyx/pifo_oracle.py @@ -1,41 +1,11 @@ import queues -import fifo_oracle -ANS_MEM_LEN = 10 +if __name__ == "__main__": + commands, values = queues.parse_json() -def operate_pifo(commands, values): - """Given the three lists, operate a PIFO routine. - In this case, we have our PIFO just orchestrate two FIFOs. - In the end, we return the answer memory. - """ - - pifo = queues.Pifo(queues.Fifo([]), queues.Fifo([])) + pifo = queues.Pifo(queues.Fifo([]), queues.Fifo([]), 200) # Our PIFO is simple: it just orchestrates two FIFOs. - ans = [] - for cmd, val in zip(commands, values): - if cmd == 0: - try: - ans.append(pifo.pop()) - except IndexError: - break - - elif cmd == 1: - try: - ans.append(pifo.peek()) - except IndexError: - break - - elif cmd == 2: - pifo.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 - - -if __name__ == "__main__": - commands, values = fifo_oracle.parse_json() - ans = operate_pifo(commands, values) - fifo_oracle.dump_json(commands, values, ans) + ans = queues.operate_queue(commands, values, pifo) + queues.dump_json(commands, values, ans) diff --git a/calyx-py/calyx/queues.py b/calyx-py/calyx/queues.py index ae5b00f13..836da890d 100644 --- a/calyx-py/calyx/queues.py +++ b/calyx-py/calyx/queues.py @@ -1,6 +1,10 @@ +import sys +import json from dataclasses import dataclass from typing import List, Tuple +ANS_MEM_LEN = 10 + @dataclass class Fifo: @@ -40,6 +44,7 @@ class Pifo: 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 1. + 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. @@ -59,19 +64,20 @@ class Pifo: - We don't flip `hot`. When asked to push: - - If the value to be pushed is less than 200, we push it into queue_1. + - 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): + def __init__(self, queue_1, queue_2, boundary): self.data = (queue_1, queue_2) self.hot = 1 self.pifo_len = len(queue_1) + len(queue_2) + self.boundary = boundary def push(self, val: int): """Pushes `val` to the PIFO.""" - if val < 200: + if val < self.boundary: self.data[0].push(val) else: self.data[1].push(val) @@ -112,3 +118,55 @@ def peek(self) -> int: 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. + """ + + # 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 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 From 0b9c6004cc942d35fb7e61752b8cd2a7c91d4911 Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Wed, 8 Nov 2023 12:32:49 -0500 Subject: [PATCH 13/17] Tidying --- calyx-py/calyx/queues.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/calyx-py/calyx/queues.py b/calyx-py/calyx/queues.py index 836da890d..da1948fea 100644 --- a/calyx-py/calyx/queues.py +++ b/calyx-py/calyx/queues.py @@ -1,7 +1,7 @@ import sys import json from dataclasses import dataclass -from typing import List, Tuple +from typing import List ANS_MEM_LEN = 10 @@ -128,7 +128,6 @@ def parse_json(): 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"] From 8c32e723efd974d906541edcab69e02d7f10c3e4 Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Wed, 8 Nov 2023 12:33:15 -0500 Subject: [PATCH 14/17] Remove stray file --- calyx-py/calyx/fifo_data_gen.py | 53 --------------------------------- 1 file changed, 53 deletions(-) delete mode 100644 calyx-py/calyx/fifo_data_gen.py diff --git a/calyx-py/calyx/fifo_data_gen.py b/calyx-py/calyx/fifo_data_gen.py deleted file mode 100644 index da07f57f4..000000000 --- a/calyx-py/calyx/fifo_data_gen.py +++ /dev/null @@ -1,53 +0,0 @@ -import random -import time -import json -from typing import Dict, Union - -MAX_CMDS = 15 -ANS_MEM_LEN = 10 - -FormatType = Dict[str, Union[bool, str, int]] - - -def format_gen(width: int) -> FormatType: - """Generates a format object for a bitvector of length `width`.""" - return {"is_signed": False, "numeric_type": "bitnum", "width": width} - - -def dump_json(): - """Prints a JSON representation of the data to stdout. - 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 `ans_mem` memory has ANS_MEM_LEN items, all zeroes. - - Each memory has a `format` field, which is a format object for a bitvector. - """ - commands = { - "commands": { - "data": [random.randint(0, 2) for _ in range(MAX_CMDS)], - # The `commands` memory has MAX_CMDS items, which are 0, 1, or 2. - "format": format_gen(2), - } - } - 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. - "format": format_gen(32), - } - } - ans_mem = { - "ans_mem": { - "data": [0 for _ in range(ANS_MEM_LEN)], - # The `ans_mem` memory has ANS_MEM_LEN items, all zeroes. - "format": format_gen(32), - } - } - - print(json.dumps(commands | values | ans_mem, indent=2)) - - -if __name__ == "__main__": - random.seed(5) - dump_json() From 84cf9c5f83d61782530d2a18f7ba812e696f7a89 Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Wed, 8 Nov 2023 12:42:06 -0500 Subject: [PATCH 15/17] Tidying --- calyx-py/calyx/fifo_oracle.py | 3 --- calyx-py/calyx/pifo_oracle.py | 2 +- calyx-py/calyx/queues.py | 22 +++++++++++----------- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/calyx-py/calyx/fifo_oracle.py b/calyx-py/calyx/fifo_oracle.py index c862694e7..8cc4104f5 100644 --- a/calyx-py/calyx/fifo_oracle.py +++ b/calyx-py/calyx/fifo_oracle.py @@ -3,9 +3,6 @@ if __name__ == "__main__": commands, values = queues.parse_json() - pifo = queues.Fifo([]) - # Our PIFO is simple: it just orchestrates two FIFOs. - ans = queues.operate_queue(commands, values, pifo) queues.dump_json(commands, values, ans) diff --git a/calyx-py/calyx/pifo_oracle.py b/calyx-py/calyx/pifo_oracle.py index aef0fd470..55d7a81bc 100644 --- a/calyx-py/calyx/pifo_oracle.py +++ b/calyx-py/calyx/pifo_oracle.py @@ -4,8 +4,8 @@ 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) - # Our PIFO is simple: it just orchestrates two FIFOs. ans = queues.operate_queue(commands, values, pifo) queues.dump_json(commands, values, ans) diff --git a/calyx-py/calyx/queues.py b/calyx-py/calyx/queues.py index da1948fea..c9c493b9e 100644 --- a/calyx-py/calyx/queues.py +++ b/calyx-py/calyx/queues.py @@ -43,7 +43,7 @@ class Pifo: 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 1. + `hot` starts at 0. We also take at initialization a `boundary` value. We maintain internally a variable called `pifo_len`: @@ -51,11 +51,11 @@ class Pifo: When asked to pop: - If `pifo_len` is 0, we raise an error. - - Else, if `hot` is 1, we try to pop from queue_1. - + If it succeeds, we flip `hot` to 2 and return the value we got. - + If it fails, we pop from queue_2 and return the value we got. + - 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 2, we proceed symmetrically. + - If `hot` is 1, we proceed symmetrically. - We decrement `pifo_len` by 1. When asked to peek: @@ -71,7 +71,7 @@ class Pifo: def __init__(self, queue_1, queue_2, boundary): self.data = (queue_1, queue_2) - self.hot = 1 + self.hot = 0 self.pifo_len = len(queue_1) + len(queue_2) self.boundary = boundary @@ -87,16 +87,16 @@ def pop(self) -> int: """Pops the PIFO.""" if self.pifo_len == 0: raise IndexError("Cannot pop from empty PIFO.") - self.pifo_len -= 1 - if self.hot == 1: + self.pifo_len -= 1 # We decrement `pifo_len` by 1. + if self.hot == 0: try: - self.hot = 2 + self.hot = 1 return self.data[0].pop() except IndexError: return self.data[1].pop() else: try: - self.hot = 1 + self.hot = 0 return self.data[1].pop() except IndexError: return self.data[0].pop() @@ -105,7 +105,7 @@ def peek(self) -> int: """Peeks into the PIFO.""" if self.pifo_len == 0: raise IndexError("Cannot peek into empty PIFO.") - if self.hot == 1: + if self.hot == 0: try: return self.data[0].peek() except IndexError: From e388fdb9bcc65bb00261d79d0cf056f5e3ff8ff7 Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Wed, 8 Nov 2023 12:53:48 -0500 Subject: [PATCH 16/17] New data for PIFO tree --- calyx-py/test/correctness/pifo_tree.data | 40 ++++++++++++------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/calyx-py/test/correctness/pifo_tree.data b/calyx-py/test/correctness/pifo_tree.data index 254f74da6..5f72a3db8 100644 --- a/calyx-py/test/correctness/pifo_tree.data +++ b/calyx-py/test/correctness/pifo_tree.data @@ -2,17 +2,17 @@ "commands": { "data": [ 2, + 1, 2, + 1, 2, 2, 2, 2, 0, + 1, 0, - 0, - 0, - 0, - 0, + 2, 0, 0, 0 @@ -25,21 +25,21 @@ }, "values": { "data": [ - 11, - 12, - 201, - 202, - 203, - 101, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 + 190, + 240, + 126, + 194, + 278, + 52, + 293, + 127, + 6, + 374, + 110, + 208, + 143, + 93, + 392 ], "format": { "is_signed": false, @@ -66,4 +66,4 @@ "width": 32 } } -} \ No newline at end of file +} From d29de942a457c3c995abdabb47b1ee806ff2d41f Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Wed, 8 Nov 2023 13:00:33 -0500 Subject: [PATCH 17/17] PIFO tree oracle! --- calyx-py/calyx/pifotree_oracle.py | 22 +++++++++ calyx-py/test/correctness/pifo_tree.expect | 54 +++++++++++----------- 2 files changed, 49 insertions(+), 27 deletions(-) create mode 100644 calyx-py/calyx/pifotree_oracle.py diff --git a/calyx-py/calyx/pifotree_oracle.py b/calyx-py/calyx/pifotree_oracle.py new file mode 100644 index 000000000..9f83b83fc --- /dev/null +++ b/calyx-py/calyx/pifotree_oracle.py @@ -0,0 +1,22 @@ +import queues + + +if __name__ == "__main__": + commands, values = queues.parse_json() + + # Our PIFO is a little complicated: it is a tree of queues. + # The root has two children, which are PIFOs. + # - PIFO_red is the left child. + # + PIFO_red itself has two children, which are FIFOs. + # * FIFO_purple is the left child. + # * FIFO_tangerine is the right child. + # * The boundary for this is 100. + # - FIFO_blue is the right child. + # - The boundary for this is 200. + + pifo = queues.Pifo( + queues.Pifo(queues.Fifo([]), queues.Fifo([]), 100), queues.Fifo([]), 200 + ) + + ans = queues.operate_queue(commands, values, pifo) + queues.dump_json(commands, values, ans) diff --git a/calyx-py/test/correctness/pifo_tree.expect b/calyx-py/test/correctness/pifo_tree.expect index e72076fcf..c0040e323 100644 --- a/calyx-py/test/correctness/pifo_tree.expect +++ b/calyx-py/test/correctness/pifo_tree.expect @@ -1,48 +1,48 @@ { "ans_mem": [ - 11, - 201, - 101, - 202, - 12, - 203, - 0, - 0, + 190, + 190, + 52, + 278, + 278, + 190, + 293, + 126, 0, 0 ], "commands": [ 2, + 1, 2, + 1, 2, 2, 2, 2, 0, + 1, 0, - 0, - 0, - 0, - 0, + 2, 0, 0, 0 ], "values": [ - 11, - 12, - 201, - 202, - 203, - 101, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 + 190, + 240, + 126, + 194, + 278, + 52, + 293, + 127, + 6, + 374, + 110, + 208, + 143, + 93, + 392 ] }