From d842842bcd672b73a85b7abb9452424309f83882 Mon Sep 17 00:00:00 2001 From: mori Date: Fri, 23 Feb 2024 00:44:40 -0800 Subject: [PATCH 01/12] add causal order generator --- src/c3py/history.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/c3py/history.py b/src/c3py/history.py index 8c210f2..1451035 100644 --- a/src/c3py/history.py +++ b/src/c3py/history.py @@ -61,6 +61,20 @@ def __init__(self, data: dict[str, list[Operation]]): for process, ops in data.items(): for i in range(len(ops) - 1): self.poset.order_try(f"{process}.{i + 1}", f"{process}.{i + 2}") + + # co = (po U wr)^+ + def causal_order(self): + ch = deepcopy(self) + for id1, op1 in self.label.items(): + if op1.method == "wr": + continue + + arg = op1.arg + ret = op1.ret + for id2, op2 in self.label.items(): + if op2.method == "wr" and op2.arg == (arg, ret): + ch.poset.link(id2, id1) + return ch def causal_hist(self, op_id: str, ret_set: set[str]) -> Self: ch = deepcopy(self) From 144ce4c7eebe2b7ebc178a25e0144ae263a6167c Mon Sep 17 00:00:00 2001 From: mori Date: Fri, 23 Feb 2024 18:09:01 -0800 Subject: [PATCH 02/12] create WRMemoryHistory --- src/c3py/history.py | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/src/c3py/history.py b/src/c3py/history.py index 1451035..bb426d6 100644 --- a/src/c3py/history.py +++ b/src/c3py/history.py @@ -61,20 +61,6 @@ def __init__(self, data: dict[str, list[Operation]]): for process, ops in data.items(): for i in range(len(ops) - 1): self.poset.order_try(f"{process}.{i + 1}", f"{process}.{i + 2}") - - # co = (po U wr)^+ - def causal_order(self): - ch = deepcopy(self) - for id1, op1 in self.label.items(): - if op1.method == "wr": - continue - - arg = op1.arg - ret = op1.ret - for id2, op2 in self.label.items(): - if op2.method == "wr" and op2.arg == (arg, ret): - ch.poset.link(id2, id1) - return ch def causal_hist(self, op_id: str, ret_set: set[str]) -> Self: ch = deepcopy(self) @@ -108,6 +94,37 @@ def visualize(self, include_label: bool = True) -> pydot.Dot: return dot +class WRMemoryHistory(History): + def check_differentiated_h(self) -> bool: + wr = set[Operation.arg]() + for id, op in self.label.items(): + if op.method == "wr": + if op.arg in wr: + return False + wr.add(op.arg) + return True + + def make_co(self) -> Self | None: + """ + co = (po U wr)^+ + po is checked in History.__init__ + so just check wr here + """ + wr = dict[tuple[Any, Any], str]() + for id, op in self.label.items(): + if op.method == "wr": + wr[op.arg] = id + + ch = deepcopy(self) + for id, op in self.label.items(): + if op.method == "rd": + src = wr.get((op.arg, op.ret)) + if not src: + continue + ch.poset.link(src, id) + return ch + + class Specification(ABC): @abstractmethod def start(self): From 7d5c0f35301e9361c90ada006161ab233a1d1834 Mon Sep 17 00:00:00 2001 From: mori Date: Fri, 23 Feb 2024 18:34:46 -0800 Subject: [PATCH 03/12] check if co is acyclic --- src/c3py/history.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/c3py/history.py b/src/c3py/history.py index bb426d6..7e20b25 100644 --- a/src/c3py/history.py +++ b/src/c3py/history.py @@ -3,6 +3,7 @@ from copy import deepcopy from types import MappingProxyType from typing import Any, NamedTuple, Self +import networkx as nx import pydot @@ -122,6 +123,9 @@ def make_co(self) -> Self | None: if not src: continue ch.poset.link(src, id) + + if not nx.is_directed_acyclic_graph(ch.poset.G): + return None return ch From d6614ab635a531a71747adb3b752fde2bba22eed Mon Sep 17 00:00:00 2001 From: mori Date: Sat, 24 Feb 2024 14:34:12 -0800 Subject: [PATCH 04/12] add test for WRMemoryHistory --- src/c3py/history.py | 2 +- src/c3py/history_test.py | 66 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/c3py/history.py b/src/c3py/history.py index 7e20b25..74b0ce5 100644 --- a/src/c3py/history.py +++ b/src/c3py/history.py @@ -3,8 +3,8 @@ from copy import deepcopy from types import MappingProxyType from typing import Any, NamedTuple, Self -import networkx as nx +import networkx as nx import pydot from c3py.poset import Poset diff --git a/src/c3py/history_test.py b/src/c3py/history_test.py index 976492c..22f3042 100644 --- a/src/c3py/history_test.py +++ b/src/c3py/history_test.py @@ -1,3 +1,4 @@ +import networkx as nx import pytest from c3py.history import ( @@ -5,6 +6,7 @@ Instruction, Operation, RWMemorySpecification, + WRMemoryHistory, check_CC, check_CCv, check_CM, @@ -195,3 +197,67 @@ def test_cm_history_e(self): def test_cv_history_e(self): h = self.make_history_e() assert check_CCv(h, RWMemorySpecification()).is_CCv is False + + +class TestWRMemoryHistory: + def make_wrhistory_a(self): + h = WRMemoryHistory( + { + "a": [Operation("wr", ("x", 1)), Operation("rd", "x", 2)], + "b": [Operation("wr", ("x", 2)), Operation("rd", "x", 1)], + } + ) + return h + + def make_wrhistory_b(self): + h = WRMemoryHistory( + { + "a": [ + Operation("wr", ("z", 1)), + Operation("wr", ("x", 1)), + Operation("wr", ("y", 1)), + ], + "b": [ + Operation("wr", ("x", 2)), + Operation("rd", "z", None), # default value (0 in paper) + Operation("rd", "y", 1), + Operation("rd", "x", 2), + ], + } + ) + return h + + def test_check_differentiated_h(self): + h_b = self.make_wrhistory_b() + assert h_b.check_differentiated_h() + + not_diff_h = WRMemoryHistory( + { + "a": [Operation("wr", ("x", 1)), Operation("rd", "x", 2)], + "b": [Operation("wr", ("x", 1)), Operation("rd", "x", 1)], + } + ) + assert not not_diff_h.check_differentiated_h() + + def test_make_co(self): + h_a = self.make_wrhistory_a() + h_a = h_a.make_co() + tc_a = nx.transitive_closure(h_a.poset.G) + correct_tc = set( + ( + ("a.1", "a.2"), + ("b.1", "b.2"), + ("a.1", "b.2"), + ("b.1", "a.2"), + ) + ) + assert set(tc_a.edges(tc_a)) == correct_tc + + cyclic_h = WRMemoryHistory( + { + "a": [Operation("rd", "x", 1), Operation("wr", ("x", 1))], + "b": [Operation("wr", ("x", 2)), Operation("rd", "x", 2)], + } + ) + cyclic_h = cyclic_h.make_co() + assert not cyclic_h From 30c1dda2a29401ea4a3b4f08cc71ac66781d911f Mon Sep 17 00:00:00 2001 From: mori Date: Tue, 27 Feb 2024 17:44:04 -0800 Subject: [PATCH 05/12] moved bad pattern things to bad_pattern.py and bad_pattern_test.py add is_write_co_init_read method to WRMemoryHistory class --- src/c3py/bad_pattern.py | 80 +++++++++++++++++++++++++++ src/c3py/bad_pattern_test.py | 104 +++++++++++++++++++++++++++++++++++ src/c3py/history.py | 35 ------------ src/c3py/history_test.py | 66 ---------------------- 4 files changed, 184 insertions(+), 101 deletions(-) create mode 100644 src/c3py/bad_pattern.py create mode 100644 src/c3py/bad_pattern_test.py diff --git a/src/c3py/bad_pattern.py b/src/c3py/bad_pattern.py new file mode 100644 index 0000000..c37ba3c --- /dev/null +++ b/src/c3py/bad_pattern.py @@ -0,0 +1,80 @@ +from copy import deepcopy +from enum import Enum +from typing import Any, Self + +import networkx as nx + +from c3py.history import History, Operation + + +class BadPattern(Enum): + CyclicCO = 1 + WriteCOInitRead = 2 + ThinAirRead = 3 + WriteCORead = 4 + CyclicCF = 5 + WriteHBInitRead = 6 + CyclicHB = 7 + + +class WRMemoryHistory(History): + def check_differentiated_h(self) -> bool: + wr = set[Operation.arg]() + for id, op in self.label.items(): + if op.method == "wr": + if op.arg in wr: + return False + wr.add(op.arg) + return True + + def make_co(self) -> Self | BadPattern: + """ + co = (po U wr)^+ + po is checked in History.__init__ + so just check wr here + """ + wr = dict[tuple[Any, Any], str]() + for id, op in self.label.items(): + if op.method == "wr": + wr[op.arg] = id + + ch = deepcopy(self) + for id, op in self.label.items(): + if op.method == "rd": + src = wr.get((op.arg, op.ret)) + if not src: + continue + ch.poset.link(src, id) + + if not nx.is_directed_acyclic_graph(ch.poset.G): + return BadPattern.CyclicCO + + # ensure transitivity of poset + tc = nx.transitive_closure(ch.poset.G, reflexive=False) + missing_es = tc.edges() - ch.poset.G.edges() + for e in missing_es: + ch.poset.link(e[0], e[1]) + + return ch + + def is_write_co_init_read(self) -> bool: + """ + This method has to be preceded by self.make_co + :returns True if WriteCoInitRead + """ + rd_init: list(tuple[str, Operation]) = [] # (id, Operation) + for id, op in self.label.items(): + if op.method == "rd" and op.ret is None: + rd_init.append((id, op)) + + if len(rd_init) == 0: + return False + + for id1, op1 in rd_init: + anc = nx.ancestors(self.poset.G, id1) + for id2 in anc: + op2 = self.label[id2] + if op2.method == "wr" and op2.arg[0] == op1.arg: + return True + + return False diff --git a/src/c3py/bad_pattern_test.py b/src/c3py/bad_pattern_test.py new file mode 100644 index 0000000..e26a0a1 --- /dev/null +++ b/src/c3py/bad_pattern_test.py @@ -0,0 +1,104 @@ +from c3py.bad_pattern import BadPattern, WRMemoryHistory +from c3py.history import Operation + + +class TestWRMemoryHistory: + def make_wrhistory_a(self): + h = WRMemoryHistory( + { + "a": [Operation("wr", ("x", 1)), Operation("rd", "x", 2)], + "b": [Operation("wr", ("x", 2)), Operation("rd", "x", 1)], + } + ) + return h + + def make_wrhistory_b(self): + h = WRMemoryHistory( + { + "a": [ + Operation("wr", ("z", 1)), + Operation("wr", ("x", 1)), + Operation("wr", ("y", 1)), + ], + "b": [ + Operation("wr", ("x", 2)), + Operation("rd", "z", None), # default value (0 in paper) + Operation("rd", "y", 1), + Operation("rd", "x", 2), + ], + } + ) + return h + + def test_check_differentiated_h(self): + h_b = self.make_wrhistory_b() + assert h_b.check_differentiated_h() + + not_diff_h = WRMemoryHistory( + { + "a": [Operation("wr", ("x", 1)), Operation("rd", "x", 2)], + "b": [Operation("wr", ("x", 1)), Operation("rd", "x", 1)], + } + ) + assert not not_diff_h.check_differentiated_h() + + def test_make_co_a(self): + h = self.make_wrhistory_a() + h = h.make_co() + correct_co = set( + ( + ("a.1", "a.2"), + ("b.1", "b.2"), + ("a.1", "b.2"), + ("b.1", "a.2"), + ) + ) + assert h.poset.G.edges() == correct_co + + def test_make_co_b(self): + h = self.make_wrhistory_b() + h = h.make_co() + correct_co = set( + ( + ("a.1", "a.2"), + ("a.1", "a.3"), + ("a.1", "b.3"), + ("a.1", "b.4"), + ("a.2", "a.3"), + ("a.2", "b.3"), + ("a.2", "b.4"), + ("a.3", "b.3"), + ("a.3", "b.4"), + ("b.1", "b.2"), + ("b.1", "b.3"), + ("b.1", "b.4"), + ("b.2", "b.3"), + ("b.2", "b.4"), + ("b.3", "b.4"), + ) + ) + assert h.poset.G.edges() == correct_co + + def test_make_co_cyclic(self): + h = WRMemoryHistory( + { + "a": [Operation("rd", "x", 1), Operation("wr", ("x", 1))], + "b": [Operation("wr", ("x", 2)), Operation("rd", "x", 2)], + } + ) + h = h.make_co() + assert h == BadPattern.CyclicCO + + def test_is_write_co_init_read_false(self): + h_b = self.make_wrhistory_b() + h_b = h_b.make_co() + assert not h_b.is_write_co_init_read() + + def test_is_write_co_init_read_true(self): + h = WRMemoryHistory( + { + "a": [Operation("wr", ("x", 1)), Operation("rd", "x", None)] + } + ) + h = h.make_co() + assert h.is_write_co_init_read() diff --git a/src/c3py/history.py b/src/c3py/history.py index 74b0ce5..8c210f2 100644 --- a/src/c3py/history.py +++ b/src/c3py/history.py @@ -4,7 +4,6 @@ from types import MappingProxyType from typing import Any, NamedTuple, Self -import networkx as nx import pydot from c3py.poset import Poset @@ -95,40 +94,6 @@ def visualize(self, include_label: bool = True) -> pydot.Dot: return dot -class WRMemoryHistory(History): - def check_differentiated_h(self) -> bool: - wr = set[Operation.arg]() - for id, op in self.label.items(): - if op.method == "wr": - if op.arg in wr: - return False - wr.add(op.arg) - return True - - def make_co(self) -> Self | None: - """ - co = (po U wr)^+ - po is checked in History.__init__ - so just check wr here - """ - wr = dict[tuple[Any, Any], str]() - for id, op in self.label.items(): - if op.method == "wr": - wr[op.arg] = id - - ch = deepcopy(self) - for id, op in self.label.items(): - if op.method == "rd": - src = wr.get((op.arg, op.ret)) - if not src: - continue - ch.poset.link(src, id) - - if not nx.is_directed_acyclic_graph(ch.poset.G): - return None - return ch - - class Specification(ABC): @abstractmethod def start(self): diff --git a/src/c3py/history_test.py b/src/c3py/history_test.py index 22f3042..976492c 100644 --- a/src/c3py/history_test.py +++ b/src/c3py/history_test.py @@ -1,4 +1,3 @@ -import networkx as nx import pytest from c3py.history import ( @@ -6,7 +5,6 @@ Instruction, Operation, RWMemorySpecification, - WRMemoryHistory, check_CC, check_CCv, check_CM, @@ -197,67 +195,3 @@ def test_cm_history_e(self): def test_cv_history_e(self): h = self.make_history_e() assert check_CCv(h, RWMemorySpecification()).is_CCv is False - - -class TestWRMemoryHistory: - def make_wrhistory_a(self): - h = WRMemoryHistory( - { - "a": [Operation("wr", ("x", 1)), Operation("rd", "x", 2)], - "b": [Operation("wr", ("x", 2)), Operation("rd", "x", 1)], - } - ) - return h - - def make_wrhistory_b(self): - h = WRMemoryHistory( - { - "a": [ - Operation("wr", ("z", 1)), - Operation("wr", ("x", 1)), - Operation("wr", ("y", 1)), - ], - "b": [ - Operation("wr", ("x", 2)), - Operation("rd", "z", None), # default value (0 in paper) - Operation("rd", "y", 1), - Operation("rd", "x", 2), - ], - } - ) - return h - - def test_check_differentiated_h(self): - h_b = self.make_wrhistory_b() - assert h_b.check_differentiated_h() - - not_diff_h = WRMemoryHistory( - { - "a": [Operation("wr", ("x", 1)), Operation("rd", "x", 2)], - "b": [Operation("wr", ("x", 1)), Operation("rd", "x", 1)], - } - ) - assert not not_diff_h.check_differentiated_h() - - def test_make_co(self): - h_a = self.make_wrhistory_a() - h_a = h_a.make_co() - tc_a = nx.transitive_closure(h_a.poset.G) - correct_tc = set( - ( - ("a.1", "a.2"), - ("b.1", "b.2"), - ("a.1", "b.2"), - ("b.1", "a.2"), - ) - ) - assert set(tc_a.edges(tc_a)) == correct_tc - - cyclic_h = WRMemoryHistory( - { - "a": [Operation("rd", "x", 1), Operation("wr", ("x", 1))], - "b": [Operation("wr", ("x", 2)), Operation("rd", "x", 2)], - } - ) - cyclic_h = cyclic_h.make_co() - assert not cyclic_h From 7d943baa121518ae33d1309f3e14a894402ce4d0 Mon Sep 17 00:00:00 2001 From: mori Date: Tue, 27 Feb 2024 18:32:26 -0800 Subject: [PATCH 06/12] add ThinAirRead checker --- src/c3py/bad_pattern.py | 14 ++++++++++++++ src/c3py/bad_pattern_test.py | 15 +++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/c3py/bad_pattern.py b/src/c3py/bad_pattern.py index c37ba3c..16cbfb3 100644 --- a/src/c3py/bad_pattern.py +++ b/src/c3py/bad_pattern.py @@ -78,3 +78,17 @@ def is_write_co_init_read(self) -> bool: return True return False + + def is_thin_air_read(self) -> bool: + wrs: list[Operation.arg] = [] + rds = set[(Operation.arg, Operation.ret)]() + for _, op in self.label.items(): + if op.method == "wr": + wrs.append(op.arg) + else: + rds.add((op.arg, op.ret)) + + for wr in wrs: + rds.remove(wr) + + return rds != set() diff --git a/src/c3py/bad_pattern_test.py b/src/c3py/bad_pattern_test.py index e26a0a1..f9a6a9a 100644 --- a/src/c3py/bad_pattern_test.py +++ b/src/c3py/bad_pattern_test.py @@ -102,3 +102,18 @@ def test_is_write_co_init_read_true(self): ) h = h.make_co() assert h.is_write_co_init_read() + + def test_is_thin_air_read_false(self): + h = self.make_wrhistory_a() + h = h.make_co() + assert not h.is_thin_air_read() + + def test_is_thin_air_read_true(self): + h = WRMemoryHistory( + { + "a": [Operation("wr", ("x", 1)), Operation("rd", "x", 2)], + "b": [Operation("wr", ("x", 2)), Operation("rd", "y", 1)], + } + ) + h.make_co() + assert h.is_thin_air_read From 65c0afba5cf582ecc11ac9290125a880a692e6bb Mon Sep 17 00:00:00 2001 From: mori Date: Wed, 28 Feb 2024 18:43:53 -0800 Subject: [PATCH 07/12] add WriteCORead checker --- src/c3py/bad_pattern.py | 25 +++++++++++++++++++++++++ src/c3py/bad_pattern_test.py | 23 +++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/c3py/bad_pattern.py b/src/c3py/bad_pattern.py index 16cbfb3..e95e9b4 100644 --- a/src/c3py/bad_pattern.py +++ b/src/c3py/bad_pattern.py @@ -92,3 +92,28 @@ def is_thin_air_read(self) -> bool: rds.remove(wr) return rds != set() + + def is_write_co_read(self) -> bool: + # ToDo do this in make_co + wr = dict[tuple[Any, Any], str]() + for id, op in self.label.items(): + if op.method == "wr": + wr[op.arg] = id + + wr_relations = list[tuple[str, str, str]]() # [(var, wr1, rd1), (var, wr2, rd2), ...] + for id, op in self.label.items(): + if op.method == "rd": + src = wr.get((op.arg, op.ret)) + if not src: + continue + wr_relations.append((op.arg, src, id)) + + for v, wr, rd in wr_relations: + wr_des = nx.descendants(self.poset.G, wr) + rd_anc = nx.ancestors(self.poset.G, rd) + intermediates = wr_des.intersection(rd_anc) + for i in intermediates: + if self.label[i].method == "wr" and self.label[i].arg[0] == v: + return True + + return False diff --git a/src/c3py/bad_pattern_test.py b/src/c3py/bad_pattern_test.py index f9a6a9a..7c3611f 100644 --- a/src/c3py/bad_pattern_test.py +++ b/src/c3py/bad_pattern_test.py @@ -30,6 +30,19 @@ def make_wrhistory_b(self): ) return h + def make_wrhistory_e(self): + h = WRMemoryHistory( + { + "a": [Operation("wr", ("x", 1)), Operation("wr", ("y", 1))], + "b": [Operation("rd", "y", 1), Operation("wr", ("x", 2))], + "c": [ + Operation("rd", "x", 2), + Operation("rd", "x", 1), + ], + } + ) + return h + def test_check_differentiated_h(self): h_b = self.make_wrhistory_b() assert h_b.check_differentiated_h() @@ -117,3 +130,13 @@ def test_is_thin_air_read_true(self): ) h.make_co() assert h.is_thin_air_read + + def test_is_write_co_read_true(self): + h = self.make_wrhistory_e() + h = h.make_co() + assert h.is_write_co_read() + + def test_is_write_co_read_false(self): + h = self.make_wrhistory_a() + h = h.make_co() + assert not h.is_write_co_read() From f4c9e690d590e24f35a6a5133d0c2d7f34ba018d Mon Sep 17 00:00:00 2001 From: mori Date: Wed, 28 Feb 2024 19:22:15 -0800 Subject: [PATCH 08/12] more tests and modify ThinAirRead checker - add more tests for WRMemoryHistory class - check ThinAirRead in WRMemoryHistory.make_co --- src/c3py/bad_pattern.py | 18 +----- src/c3py/bad_pattern_test.py | 107 +++++++++++++++++++++++++++++------ 2 files changed, 93 insertions(+), 32 deletions(-) diff --git a/src/c3py/bad_pattern.py b/src/c3py/bad_pattern.py index e95e9b4..dc569a5 100644 --- a/src/c3py/bad_pattern.py +++ b/src/c3py/bad_pattern.py @@ -40,10 +40,10 @@ def make_co(self) -> Self | BadPattern: ch = deepcopy(self) for id, op in self.label.items(): - if op.method == "rd": + if op.method == "rd" and op.ret is not None: src = wr.get((op.arg, op.ret)) if not src: - continue + return BadPattern.ThinAirRead ch.poset.link(src, id) if not nx.is_directed_acyclic_graph(ch.poset.G): @@ -79,20 +79,6 @@ def is_write_co_init_read(self) -> bool: return False - def is_thin_air_read(self) -> bool: - wrs: list[Operation.arg] = [] - rds = set[(Operation.arg, Operation.ret)]() - for _, op in self.label.items(): - if op.method == "wr": - wrs.append(op.arg) - else: - rds.add((op.arg, op.ret)) - - for wr in wrs: - rds.remove(wr) - - return rds != set() - def is_write_co_read(self) -> bool: # ToDo do this in make_co wr = dict[tuple[Any, Any], str]() diff --git a/src/c3py/bad_pattern_test.py b/src/c3py/bad_pattern_test.py index 7c3611f..ce28b8e 100644 --- a/src/c3py/bad_pattern_test.py +++ b/src/c3py/bad_pattern_test.py @@ -30,6 +30,38 @@ def make_wrhistory_b(self): ) return h + def make_wrhistory_c(self): + h = WRMemoryHistory( + { + "a": [Operation("wr", ("x", 1))], + "b": [ + Operation("wr", ("x", 2)), + Operation("rd", "x", 1), + Operation("rd", "x", 2), + ], + } + ) + return h + + def make_wrhistory_d(self): + h = WRMemoryHistory( + { + "a": [ + Operation("wr", ("x", 1)), + Operation("rd", "y", None), # default value + Operation("wr", ("y", 1)), + Operation("rd", "x", 1), + ], + "b": [ + Operation("wr", ("x", 2)), + Operation("rd", "y", None), # default value + Operation("wr", ("y", 2)), + Operation("rd", "x", 2), + ], + } + ) + return h + def make_wrhistory_e(self): h = WRMemoryHistory( { @@ -58,6 +90,8 @@ def test_check_differentiated_h(self): def test_make_co_a(self): h = self.make_wrhistory_a() h = h.make_co() + assert h != BadPattern.CyclicCO + assert h != BadPattern.ThinAirRead correct_co = set( ( ("a.1", "a.2"), @@ -71,6 +105,8 @@ def test_make_co_a(self): def test_make_co_b(self): h = self.make_wrhistory_b() h = h.make_co() + assert h != BadPattern.CyclicCO + assert h != BadPattern.ThinAirRead correct_co = set( ( ("a.1", "a.2"), @@ -102,12 +138,21 @@ def test_make_co_cyclic(self): h = h.make_co() assert h == BadPattern.CyclicCO + def test_make_co_thin_air_read(self): + h = WRMemoryHistory( + { + "a": [Operation("wr", ("x", 1)), Operation("rd", "x", 2)], + "b": [Operation("wr", ("x", 2)), Operation("rd", "y", 1)], + } + ) + assert h.make_co() == BadPattern.ThinAirRead + def test_is_write_co_init_read_false(self): h_b = self.make_wrhistory_b() h_b = h_b.make_co() assert not h_b.is_write_co_init_read() - def test_is_write_co_init_read_true(self): + def test_write_co_init_read(self): h = WRMemoryHistory( { "a": [Operation("wr", ("x", 1)), Operation("rd", "x", None)] @@ -116,21 +161,6 @@ def test_is_write_co_init_read_true(self): h = h.make_co() assert h.is_write_co_init_read() - def test_is_thin_air_read_false(self): - h = self.make_wrhistory_a() - h = h.make_co() - assert not h.is_thin_air_read() - - def test_is_thin_air_read_true(self): - h = WRMemoryHistory( - { - "a": [Operation("wr", ("x", 1)), Operation("rd", "x", 2)], - "b": [Operation("wr", ("x", 2)), Operation("rd", "y", 1)], - } - ) - h.make_co() - assert h.is_thin_air_read - def test_is_write_co_read_true(self): h = self.make_wrhistory_e() h = h.make_co() @@ -140,3 +170,48 @@ def test_is_write_co_read_false(self): h = self.make_wrhistory_a() h = h.make_co() assert not h.is_write_co_read() + + def test_bad_pattern_a(self): + h = self.make_wrhistory_a() + assert h.check_differentiated_h() + h = h.make_co() + assert h != BadPattern.CyclicCO + assert h != BadPattern.ThinAirRead + assert not h.is_write_co_init_read() + assert not h.is_write_co_read() + + def test_bad_pattern_b(self): + h = self.make_wrhistory_b() + assert h.check_differentiated_h() + h = h.make_co() + assert h != BadPattern.CyclicCO + assert h != BadPattern.ThinAirRead + assert not h.is_write_co_init_read() + assert not h.is_write_co_read() + + def test_bad_pattern_c(self): + h = self.make_wrhistory_c() + assert h.check_differentiated_h() + h = h.make_co() + assert h != BadPattern.CyclicCO + assert h != BadPattern.ThinAirRead + assert not h.is_write_co_init_read() + assert not h.is_write_co_read() + + def test_bad_pattern_d(self): + h = self.make_wrhistory_a() + assert h.check_differentiated_h() + h = h.make_co() + assert h != BadPattern.CyclicCO + assert h != BadPattern.ThinAirRead + assert not h.is_write_co_init_read() + assert not h.is_write_co_read() + + def test_bad_pattern_e(self): + h = self.make_wrhistory_e() + assert h.check_differentiated_h() + h = h.make_co() + assert h != BadPattern.CyclicCO + assert h != BadPattern.ThinAirRead + assert not h.is_write_co_init_read() + assert h.is_write_co_read() From 72ad50215660ef197174382a4d01144adba11e7b Mon Sep 17 00:00:00 2001 From: mori Date: Thu, 29 Feb 2024 17:59:54 -0800 Subject: [PATCH 09/12] create find_cc_bad_pattern --- src/c3py/bad_pattern.py | 42 ++++++++++++++++++++------- src/c3py/bad_pattern_test.py | 56 ++++++++++++++---------------------- 2 files changed, 52 insertions(+), 46 deletions(-) diff --git a/src/c3py/bad_pattern.py b/src/c3py/bad_pattern.py index dc569a5..ef1de7c 100644 --- a/src/c3py/bad_pattern.py +++ b/src/c3py/bad_pattern.py @@ -1,6 +1,6 @@ from copy import deepcopy from enum import Enum -from typing import Any, Self +from typing import Any, NamedTuple, Self import networkx as nx @@ -18,6 +18,11 @@ class BadPattern(Enum): class WRMemoryHistory(History): + def __init__(self, data): + super().__init__(data) + assert self.check_differentiated_h() + self.poset, self.bp = self.make_co() + def check_differentiated_h(self) -> bool: wr = set[Operation.arg]() for id, op in self.label.items(): @@ -27,7 +32,7 @@ def check_differentiated_h(self) -> bool: wr.add(op.arg) return True - def make_co(self) -> Self | BadPattern: + def make_co(self) -> tuple[Self, BadPattern]: """ co = (po U wr)^+ po is checked in History.__init__ @@ -38,24 +43,24 @@ def make_co(self) -> Self | BadPattern: if op.method == "wr": wr[op.arg] = id - ch = deepcopy(self) + cp = deepcopy(self.poset) for id, op in self.label.items(): if op.method == "rd" and op.ret is not None: src = wr.get((op.arg, op.ret)) if not src: - return BadPattern.ThinAirRead - ch.poset.link(src, id) + return cp, BadPattern.ThinAirRead + cp.link(src, id) - if not nx.is_directed_acyclic_graph(ch.poset.G): - return BadPattern.CyclicCO + if not nx.is_directed_acyclic_graph(cp.G): + return cp, BadPattern.CyclicCO # ensure transitivity of poset - tc = nx.transitive_closure(ch.poset.G, reflexive=False) - missing_es = tc.edges() - ch.poset.G.edges() + tc = nx.transitive_closure(cp.G, reflexive=False) + missing_es = tc.edges() - cp.G.edges() for e in missing_es: - ch.poset.link(e[0], e[1]) + cp.link(e[0], e[1]) - return ch + return cp, None def is_write_co_init_read(self) -> bool: """ @@ -103,3 +108,18 @@ def is_write_co_read(self) -> bool: return True return False + + +class CCBPResult(NamedTuple): + is_CC: bool + bad_pattern: BadPattern + + +def find_cc_bad_pattern(h: WRMemoryHistory) -> CCBPResult: + if h.bp: + return CCBPResult(False, h.bp) + if h.is_write_co_init_read(): + return CCBPResult(False, BadPattern.WriteCOInitRead) + if h.is_write_co_read(): + return CCBPResult(False, BadPattern.WriteCORead) + return CCBPResult(True, None) diff --git a/src/c3py/bad_pattern_test.py b/src/c3py/bad_pattern_test.py index ce28b8e..690198b 100644 --- a/src/c3py/bad_pattern_test.py +++ b/src/c3py/bad_pattern_test.py @@ -1,4 +1,4 @@ -from c3py.bad_pattern import BadPattern, WRMemoryHistory +from c3py.bad_pattern import BadPattern, find_cc_bad_pattern, WRMemoryHistory from c3py.history import Operation @@ -79,6 +79,7 @@ def test_check_differentiated_h(self): h_b = self.make_wrhistory_b() assert h_b.check_differentiated_h() + # Todo not_diff_h = WRMemoryHistory( { "a": [Operation("wr", ("x", 1)), Operation("rd", "x", 2)], @@ -89,7 +90,7 @@ def test_check_differentiated_h(self): def test_make_co_a(self): h = self.make_wrhistory_a() - h = h.make_co() + assert not h.bp assert h != BadPattern.CyclicCO assert h != BadPattern.ThinAirRead correct_co = set( @@ -104,7 +105,7 @@ def test_make_co_a(self): def test_make_co_b(self): h = self.make_wrhistory_b() - h = h.make_co() + assert not h.bp assert h != BadPattern.CyclicCO assert h != BadPattern.ThinAirRead correct_co = set( @@ -135,8 +136,7 @@ def test_make_co_cyclic(self): "b": [Operation("wr", ("x", 2)), Operation("rd", "x", 2)], } ) - h = h.make_co() - assert h == BadPattern.CyclicCO + assert h.bp == BadPattern.CyclicCO def test_make_co_thin_air_read(self): h = WRMemoryHistory( @@ -145,11 +145,10 @@ def test_make_co_thin_air_read(self): "b": [Operation("wr", ("x", 2)), Operation("rd", "y", 1)], } ) - assert h.make_co() == BadPattern.ThinAirRead + assert h.bp == BadPattern.ThinAirRead def test_is_write_co_init_read_false(self): h_b = self.make_wrhistory_b() - h_b = h_b.make_co() assert not h_b.is_write_co_init_read() def test_write_co_init_read(self): @@ -158,60 +157,47 @@ def test_write_co_init_read(self): "a": [Operation("wr", ("x", 1)), Operation("rd", "x", None)] } ) - h = h.make_co() assert h.is_write_co_init_read() def test_is_write_co_read_true(self): h = self.make_wrhistory_e() - h = h.make_co() assert h.is_write_co_read() def test_is_write_co_read_false(self): h = self.make_wrhistory_a() - h = h.make_co() assert not h.is_write_co_read() def test_bad_pattern_a(self): h = self.make_wrhistory_a() assert h.check_differentiated_h() - h = h.make_co() - assert h != BadPattern.CyclicCO - assert h != BadPattern.ThinAirRead - assert not h.is_write_co_init_read() - assert not h.is_write_co_read() + res = find_cc_bad_pattern(h) + assert res.is_CC + assert not res.bad_pattern def test_bad_pattern_b(self): h = self.make_wrhistory_b() assert h.check_differentiated_h() - h = h.make_co() - assert h != BadPattern.CyclicCO - assert h != BadPattern.ThinAirRead - assert not h.is_write_co_init_read() - assert not h.is_write_co_read() + res = find_cc_bad_pattern(h) + assert res.is_CC + assert not res.bad_pattern def test_bad_pattern_c(self): h = self.make_wrhistory_c() assert h.check_differentiated_h() - h = h.make_co() - assert h != BadPattern.CyclicCO - assert h != BadPattern.ThinAirRead - assert not h.is_write_co_init_read() - assert not h.is_write_co_read() + res = find_cc_bad_pattern(h) + assert res.is_CC + assert not res.bad_pattern def test_bad_pattern_d(self): h = self.make_wrhistory_a() assert h.check_differentiated_h() - h = h.make_co() - assert h != BadPattern.CyclicCO - assert h != BadPattern.ThinAirRead - assert not h.is_write_co_init_read() - assert not h.is_write_co_read() + res = find_cc_bad_pattern(h) + assert res.is_CC + assert not res.bad_pattern def test_bad_pattern_e(self): h = self.make_wrhistory_e() assert h.check_differentiated_h() - h = h.make_co() - assert h != BadPattern.CyclicCO - assert h != BadPattern.ThinAirRead - assert not h.is_write_co_init_read() - assert h.is_write_co_read() + res = find_cc_bad_pattern(h) + assert not res.is_CC + assert res.bad_pattern == BadPattern.WriteCORead From 1dd2c1e3e88ab34e9b6242041517df5be05b232e Mon Sep 17 00:00:00 2001 From: mori Date: Tue, 12 Mar 2024 02:50:27 -0700 Subject: [PATCH 10/12] create find_ccv_bad_pattern --- src/c3py/bad_pattern.py | 52 ++++++++++++++++++++++++++++++++---- src/c3py/bad_pattern_test.py | 47 +++++++++++++++++++++++++------- 2 files changed, 84 insertions(+), 15 deletions(-) diff --git a/src/c3py/bad_pattern.py b/src/c3py/bad_pattern.py index ef1de7c..a5219d7 100644 --- a/src/c3py/bad_pattern.py +++ b/src/c3py/bad_pattern.py @@ -21,6 +21,8 @@ class WRMemoryHistory(History): def __init__(self, data): super().__init__(data) assert self.check_differentiated_h() + self.writes = dict[tuple[Any, Any], str]() + self.write_read_relations = dict[str, str]() self.poset, self.bp = self.make_co() def check_differentiated_h(self) -> bool: @@ -34,22 +36,25 @@ def check_differentiated_h(self) -> bool: def make_co(self) -> tuple[Self, BadPattern]: """ + :returns + tuple[self.poset, BadPattern] + co = (po U wr)^+ po is checked in History.__init__ so just check wr here """ - wr = dict[tuple[Any, Any], str]() for id, op in self.label.items(): if op.method == "wr": - wr[op.arg] = id + self.writes[op.arg] = id cp = deepcopy(self.poset) for id, op in self.label.items(): if op.method == "rd" and op.ret is not None: - src = wr.get((op.arg, op.ret)) + src = self.writes.get((op.arg, op.ret)) if not src: return cp, BadPattern.ThinAirRead cp.link(src, id) + self.write_read_relations[src] = id if not nx.is_directed_acyclic_graph(cp.G): return cp, BadPattern.CyclicCO @@ -67,7 +72,7 @@ def is_write_co_init_read(self) -> bool: This method has to be preceded by self.make_co :returns True if WriteCoInitRead """ - rd_init: list(tuple[str, Operation]) = [] # (id, Operation) + rd_init: list(tuple[str, Operation]) = [] # (id, Operation) for id, op in self.label.items(): if op.method == "rd" and op.ret is None: rd_init.append((id, op)) @@ -91,7 +96,9 @@ def is_write_co_read(self) -> bool: if op.method == "wr": wr[op.arg] = id - wr_relations = list[tuple[str, str, str]]() # [(var, wr1, rd1), (var, wr2, rd2), ...] + wr_relations = list[ + tuple[str, str, str] + ]() # [(var, wr1, rd1), (var, wr2, rd2), ...] for id, op in self.label.items(): if op.method == "rd": src = wr.get((op.arg, op.ret)) @@ -109,6 +116,24 @@ def is_write_co_read(self) -> bool: return False + def is_cyclic_cf(self) -> bool: + cf = list[tuple[str, str]]() + for w, r in self.write_read_relations.items(): + v = self.label[r].arg + r_anc = nx.ancestors(self.poset.G, r) + for id in r_anc: + if id == w: + continue + op = self.label[id] + if op.method == "wr" and op.arg[0] == v: + cf.append((id, w)) + + cp = deepcopy(self.poset) + for s, d in cf: + cp.link(s, d) + + return not nx.is_directed_acyclic_graph(cp.G) + class CCBPResult(NamedTuple): is_CC: bool @@ -123,3 +148,20 @@ def find_cc_bad_pattern(h: WRMemoryHistory) -> CCBPResult: if h.is_write_co_read(): return CCBPResult(False, BadPattern.WriteCORead) return CCBPResult(True, None) + + +class CCvBPResult(NamedTuple): + is_CCv: bool + bad_pattern: BadPattern + + +def find_ccv_bad_pattern(h: WRMemoryHistory) -> CCvBPResult: + if h.bp: + return CCvBPResult(False, h.bp) + if h.is_write_co_init_read(): + return CCvBPResult(False, BadPattern.WriteCOInitRead) + if h.is_write_co_read(): + return CCvBPResult(False, BadPattern.WriteCORead) + if h.is_cyclic_cf(): + return CCvBPResult(False, BadPattern.CyclicCF) + return CCvBPResult(True, None) diff --git a/src/c3py/bad_pattern_test.py b/src/c3py/bad_pattern_test.py index 690198b..6d244da 100644 --- a/src/c3py/bad_pattern_test.py +++ b/src/c3py/bad_pattern_test.py @@ -1,4 +1,9 @@ -from c3py.bad_pattern import BadPattern, find_cc_bad_pattern, WRMemoryHistory +from c3py.bad_pattern import ( + BadPattern, + WRMemoryHistory, + find_cc_bad_pattern, + find_ccv_bad_pattern, +) from c3py.history import Operation @@ -153,9 +158,7 @@ def test_is_write_co_init_read_false(self): def test_write_co_init_read(self): h = WRMemoryHistory( - { - "a": [Operation("wr", ("x", 1)), Operation("rd", "x", None)] - } + {"a": [Operation("wr", ("x", 1)), Operation("rd", "x", None)]} ) assert h.is_write_co_init_read() @@ -167,37 +170,61 @@ def test_is_write_co_read_false(self): h = self.make_wrhistory_a() assert not h.is_write_co_read() - def test_bad_pattern_a(self): + def test_cc_bad_pattern_a(self): h = self.make_wrhistory_a() assert h.check_differentiated_h() res = find_cc_bad_pattern(h) assert res.is_CC assert not res.bad_pattern - def test_bad_pattern_b(self): + def test_cc_bad_pattern_b(self): h = self.make_wrhistory_b() assert h.check_differentiated_h() res = find_cc_bad_pattern(h) assert res.is_CC assert not res.bad_pattern - def test_bad_pattern_c(self): + def test_cc_bad_pattern_c(self): h = self.make_wrhistory_c() assert h.check_differentiated_h() res = find_cc_bad_pattern(h) assert res.is_CC assert not res.bad_pattern - def test_bad_pattern_d(self): - h = self.make_wrhistory_a() + def test_cc_bad_pattern_d(self): + h = self.make_wrhistory_d() assert h.check_differentiated_h() res = find_cc_bad_pattern(h) assert res.is_CC assert not res.bad_pattern - def test_bad_pattern_e(self): + def test_cc_bad_pattern_e(self): h = self.make_wrhistory_e() assert h.check_differentiated_h() res = find_cc_bad_pattern(h) assert not res.is_CC assert res.bad_pattern == BadPattern.WriteCORead + + def test_ccv_bad_pattern_a(self): + h = self.make_wrhistory_a() + res = find_ccv_bad_pattern(h) + assert not res.is_CCv + assert res.bad_pattern == BadPattern.CyclicCF + + def test_ccv_bad_pattern_b(self): + h = self.make_wrhistory_b() + res = find_ccv_bad_pattern(h) + assert res.is_CCv + assert not res.bad_pattern + + def test_ccv_bad_pattern_c(self): + h = self.make_wrhistory_c() + res = find_ccv_bad_pattern(h) + assert not res.is_CCv + assert res.bad_pattern == BadPattern.CyclicCF + + def test_ccv_bad_pattern_d(self): + h = self.make_wrhistory_d() + res = find_ccv_bad_pattern(h) + assert res.is_CCv + assert not res.bad_pattern From 3fdb1d61878467a0400f972b20a0f22fba9fed53 Mon Sep 17 00:00:00 2001 From: mori Date: Thu, 21 Mar 2024 15:59:23 -0700 Subject: [PATCH 11/12] implement cm checker --- src/c3py/bad_pattern.py | 73 ++++++++++++++++++++++++++++++++++++ src/c3py/bad_pattern_test.py | 25 ++++++++++++ 2 files changed, 98 insertions(+) diff --git a/src/c3py/bad_pattern.py b/src/c3py/bad_pattern.py index a5219d7..37793d5 100644 --- a/src/c3py/bad_pattern.py +++ b/src/c3py/bad_pattern.py @@ -25,6 +25,12 @@ def __init__(self, data): self.write_read_relations = dict[str, str]() self.poset, self.bp = self.make_co() + self.process_last_op = dict[str, str]() + for process, ops in data.items(): + self.process_last_op[process] = f"{process}.{ops[-1]}" + + self.hb: dict[list[tuple[str, str]]] = self.make_hb() + def check_differentiated_h(self) -> bool: wr = set[Operation.arg]() for id, op in self.label.items(): @@ -67,6 +73,24 @@ def make_co(self) -> tuple[Self, BadPattern]: return cp, None + def make_hb(self) -> dict[list[tuple[str, str]]]: + hb = dict[list[tuple[str, str]]]() + for process, _ in self.process_last_op.items(): + hb[process] = [] + + for wr, rd in self.write_read_relations.items(): + rd_process = rd[0] + v = self.label[rd].arg + rd_ret = self.label[rd].ret + + r_anc = nx.ancestors(self.poset.G, rd) + for id in r_anc: + op = self.label[id] + if op.method == "wr" and op.arg[0] == v and op.arg[1] != rd_ret: + hb[rd_process].append((id, wr)) + + return hb + def is_write_co_init_read(self) -> bool: """ This method has to be preceded by self.make_co @@ -134,6 +158,36 @@ def is_cyclic_cf(self) -> bool: return not nx.is_directed_acyclic_graph(cp.G) + def is_write_hb_init_read(self) -> bool: + init_rds = list[Operation]() + for id, op in self.label.items(): + if op.method == "rd" and not op.ret: + init_rds.append(op) + + for op in init_rds: + process = op.op_id[0] + cp = deepcopy(self.poset) + for s, d in self.hb[process]: + cp.link(s, d) + + anc_rd = nx.ancestors(cp.G, op.op_id) + for id in anc_rd: + op1 = self.label[id] + if op1.method == "wr" and op1.arg[0] == op.arg: + return True + + return False + + def is_cyclic_hb(self) -> bool: + for _, hbo in self.hb.items(): + cp = deepcopy(self.poset) + for tup in hbo: + cp.link(tup[0], tup[1]) + if not nx.is_directed_acyclic_graph(cp.G): + return True + + return False + class CCBPResult(NamedTuple): is_CC: bool @@ -165,3 +219,22 @@ def find_ccv_bad_pattern(h: WRMemoryHistory) -> CCvBPResult: if h.is_cyclic_cf(): return CCvBPResult(False, BadPattern.CyclicCF) return CCvBPResult(True, None) + + +class CMBPResult(NamedTuple): + is_CM: bool + bad_pattern: BadPattern + + +def find_cm_bad_pattern(h: WRMemoryHistory) -> CMBPResult: + if h.bp: + return CMBPResult(False, h.bp) + if h.is_write_co_init_read(): + return CMBPResult(False, BadPattern.WriteCOInitRead) + if h.is_write_co_read(): + return CMBPResult(False, BadPattern.WriteCORead) + if h.is_write_hb_init_read(): + return CMBPResult(False, BadPattern.WriteHBInitRead) + if h.is_cyclic_hb(): + return CMBPResult(False, BadPattern.CyclicHB) + return CMBPResult(True, None) diff --git a/src/c3py/bad_pattern_test.py b/src/c3py/bad_pattern_test.py index 6d244da..c09e68b 100644 --- a/src/c3py/bad_pattern_test.py +++ b/src/c3py/bad_pattern_test.py @@ -3,6 +3,7 @@ WRMemoryHistory, find_cc_bad_pattern, find_ccv_bad_pattern, + find_cm_bad_pattern, ) from c3py.history import Operation @@ -228,3 +229,27 @@ def test_ccv_bad_pattern_d(self): res = find_ccv_bad_pattern(h) assert res.is_CCv assert not res.bad_pattern + + def test_cm_bad_pattern_a(self): + h = self.make_wrhistory_a() + res = find_cm_bad_pattern(h) + assert res.is_CM + assert not res.bad_pattern + + def test_cm_bad_pattern_b(self): + h = self.make_wrhistory_b() + res = find_cm_bad_pattern(h) + assert not res.is_CM + assert res.bad_pattern == BadPattern.WriteHBInitRead + + def test_cm_bad_pattern_c(self): + h = self.make_wrhistory_c() + res = find_cm_bad_pattern(h) + assert not res.is_CM + assert res.bad_pattern == BadPattern.CyclicHB + + def test_cm_bad_pattern_d(self): + h = self.make_wrhistory_d() + res = find_cm_bad_pattern(h) + assert res.is_CM + assert not res.bad_pattern From 8f0fb43fe6f6b928488792ad114b4bb1e9796837 Mon Sep 17 00:00:00 2001 From: mori Date: Fri, 22 Mar 2024 10:55:35 -0700 Subject: [PATCH 12/12] minor change remove unnecessary if statement --- src/c3py/bad_pattern.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/c3py/bad_pattern.py b/src/c3py/bad_pattern.py index 37793d5..ea96677 100644 --- a/src/c3py/bad_pattern.py +++ b/src/c3py/bad_pattern.py @@ -101,9 +101,6 @@ def is_write_co_init_read(self) -> bool: if op.method == "rd" and op.ret is None: rd_init.append((id, op)) - if len(rd_init) == 0: - return False - for id1, op1 in rd_init: anc = nx.ancestors(self.poset.G, id1) for id2 in anc: