-
Notifications
You must be signed in to change notification settings - Fork 2
Some bug fixes in trimming and isolated dft function added #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -538,6 +538,9 @@ def try_replace_fdep_by_or(dft, fdep): | |
trigger = fdep.trigger() | ||
dependent = fdep.dependent()[0] | ||
|
||
if dependent.relevant: | ||
return False | ||
|
||
# Check if both trigger and dependent are part of the top module | ||
top_module = dft.get_module(dft.top_level_element) | ||
if trigger.element_id not in top_module or dependent.element_id not in top_module: | ||
|
@@ -577,9 +580,6 @@ def try_remove_superfluous_fdep(dft, fdep): | |
trigger = fdep.trigger() | ||
dependent = fdep.dependent()[0] | ||
|
||
# Trigger is single parent of dependent (apart from fdep) | ||
if len(dependent.parents()) > 2: | ||
return False | ||
Comment on lines
-580
to
-582
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be kept. If the dependent event has another parent, the dependent event is still relevant for this other event and therefore the FDEP cannot be removed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’m not entirely sure about the original reasoning, as it was done some time ago. It’s possible that I considered the check unnecessary at the time and decided to remove it. However, your point makes sense — if a dependent event has another parent, it would still be relevant, and the FDEP should not be removed. It’s worth revisiting this with that in mind. |
||
if trigger not in dependent.parents(): | ||
return False | ||
|
||
|
@@ -628,10 +628,13 @@ def try_remove_fdep_successors(dft, fdep): | |
trigger = fdep.trigger() | ||
dependent = fdep.dependent()[0] | ||
|
||
if dependent.relevant: | ||
return False | ||
|
||
# Dependent has single parent (apart from fdep) | ||
if len(dependent.parents()) > 2: | ||
return False | ||
parent = dependent.parents()[0] if not isinstance(dependent, dft_gates.DftDependency) else dependent.parents()[1] | ||
parent = dependent.parents()[0] if not isinstance(dependent.parents()[0], dft_gates.DftDependency) else dependent.parents()[1] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch. Thanks! |
||
|
||
# Trigger has the same parent | ||
if trigger not in parent.children(): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,20 @@ | |
|
||
import dftlib.storage.dft_gates as dft_gates | ||
|
||
def get_releated_parents(current, visited, related_parents_visited): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand that this function is used to also perform an "upwards" search in order to find all connected elements. Could we not extend the original function by also considering SPARES? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Possibly, yes. The main reason I wrote this function separately was to ensure that no parent elements are missed, even if they are not directly connected. I needed a recursive approach to trace and include all relevant parent elements. That’s why I implemented it as a standalone function, rather than modifying the original one. |
||
related_parents = [] | ||
|
||
if isinstance(current, dft_gates.DftDependency) or isinstance(current, dft_gates.DftSeq) or isinstance(current, dft_gates.DftMutex) or isinstance(current, dft_gates.DftSpare): | ||
if current.element_id not in visited: | ||
related_parents.append(current) | ||
if current not in related_parents_visited: | ||
related_parents_visited.append(current) | ||
for parent in current.parents(): | ||
if (isinstance(parent, dft_gates.DftDependency) or isinstance(parent, dft_gates.DftSeq) or isinstance(parent, dft_gates.DftMutex) or isinstance(parent, dft_gates.DftSpare) ) and parent.children()[0] == current: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the significane of the first child? Why is there no similar check for the other children needed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I made this change around two years ago, so I don’t recall the exact scenarios or reasoning in full detail. However, it was likely intended to prevent unnecessary recursion, assuming that the first child had already been processed. This adjustment was part of a quick fix implemented under time constraints, and although it worked correctly in our use cases, I cannot guarantee its correctness across all scenarios. That’s why I’m sharing it with you — so you can analyze it more thoroughly and decide whether it should be incorporated, considering all relevant aspects. |
||
continue | ||
related_parents += get_releated_parents(parent, visited, related_parents_visited) | ||
|
||
return related_parents | ||
|
||
def trim(dft): | ||
""" | ||
|
@@ -25,15 +39,60 @@ def trim(dft): | |
# Element is necessary | ||
del unused[current.element_id] | ||
|
||
# Continue search | ||
if current.is_be(): | ||
# Add possible SEQ or FDEP gates | ||
for parent in current.parents(): | ||
if isinstance(parent, dft_gates.DftDependency) or isinstance(parent, dft_gates.DftSeq) or isinstance(parent, dft_gates.DftMutex): | ||
if parent.element_id not in visited: | ||
queue.append(parent) | ||
visited.add(parent.element_id) | ||
elif current.is_gate(): | ||
related_parents = get_releated_parents(current, visited, []) | ||
for related_parent in related_parents: | ||
if related_parent.element_id not in visited: | ||
queue.append(related_parent) | ||
visited.add(related_parent.element_id) | ||
|
||
if current.is_gate(): | ||
# Add children of gate | ||
for child in current.children(): | ||
if child.element_id not in visited: | ||
queue.append(child) | ||
visited.add(child.element_id) | ||
|
||
assert len(unused) == len(dft.elements) - len(visited) | ||
|
||
# Remove unused elements | ||
trimmed = False | ||
for element in unused.values(): | ||
if not element.relevant: | ||
dft.remove(element) | ||
trimmed = True | ||
return trimmed | ||
|
||
|
||
def isolated(dft): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the purpose of this function? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, you're right — there is no major difference between the two functions. The isolated function was created to extract an isolated subtree, which differs slightly from the standard trimmed tree. Specifically, in the isolated version, we do not consider the top-level event's failure due to triggers, sequences, or other control behavior. Instead, we focus solely on failures caused by its children. This function is not intended to be part of the simplification process. We use the isolated DFT structure in specific criticality analyses, such as computing the Birnbaum Index (BI). For convenience, I placed this function alongside the trimming logic since it is structurally similar, with only minor modifications. I chose not to alter the existing trim function's interface to avoid impacting existing functionality. Feel free to refactor or integrate it as you see fit. |
||
""" | ||
Trim parts of the DFT in place which do not contribute to the top level element. | ||
:param dft: DFT which will be modified. | ||
:return: True iff elements were trimmed. | ||
""" | ||
|
||
# List of possible unused elements to trim | ||
unused = {elem_id: element for elem_id, element in dft.elements.items()} | ||
visited = set() | ||
|
||
# Perform breadth-first search to find all necessary elements | ||
queue = deque() | ||
queue.append(dft.top_level_element) | ||
visited.add(dft.top_level_element.element_id) | ||
while len(queue) > 0: | ||
current = queue.popleft() | ||
|
||
if current.element_id in unused: | ||
# Element is necessary | ||
del unused[current.element_id] | ||
|
||
if current != dft.top_level_element: | ||
related_parents = get_releated_parents(current, visited, []) | ||
for related_parent in related_parents: | ||
if related_parent.element_id not in visited: | ||
queue.append(related_parent) | ||
visited.add(related_parent.element_id) | ||
|
||
if current.is_gate(): | ||
# Add children of gate | ||
for child in current.children(): | ||
if child.element_id not in visited: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this change necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change was made in collaboration with Dr. Falak to address specific scenarios encountered during fault tree simplification. Unfortunately, I don’t recall the exact details now, but the modification was likely introduced to prevent the function from getting stuck in recursion under certain conditions.