Skip to content
Snippets Groups Projects

Add is_commutative and is_distributive

Open Robier Al Kaadi requested to merge commutative into master
2 files
+ 84
19
Compare changes
  • Side-by-side
  • Inline
Files
2
+ 54
19
@@ -2114,31 +2114,38 @@ class SFG(AbstractOperation):
@property
def is_commutative(self) -> bool:
"""
Checks if all operations in the sfg are commutative.
Checks if all operations in the SFG are commutative.
An operation is considered commutative if it is not an 'in' or 'out' operation,
An operation is considered commutative if it is not in B-ASIC Special Operations Module,
and its `is_commutative` property is `True`.
Returns:
bool: `True` if all operations are commutative, `False` otherwise.
Returns
-------
bool: `True` if all operations are commutative, `False` otherwise.
"""
return all(
(op.is_commutative if op.type_name() not in ['in', 'out'] else True)
(
op.is_commutative
if op.type_name() not in ["in", "out", "t", "c"]
else True
)
for op in self.split()
)
@property
def is_distributive(self) -> bool:
"""
Checks if the sfg is distributive.
Checks if the SFG is distributive.
An operation is considered distributive if it can be applied to each element of a set separately.
For example, multiplication is distributive over addition, meaning that `a * (b + c)` is equivalent to `a * b + a * c`.
Returns:
bool: True if the sfg is distributive, False otherwise.
Returns
-------
bool: True if the SFG is distributive, False otherwise.
Example:
Examples
--------
>>> Mad_op = MAD(Input(), Input(), Input()) # Creates an instance of the Mad operation, MAD is defined in b_asic.core_operations
>>> Mad_sfg = Mad_op.to_sfg() # The operation is turned into a sfg
>>> Mad_sfg.is_distributive # True # if the distributive property holds, False otherwise
@@ -2162,17 +2169,22 @@ class SFG(AbstractOperation):
def has_distributive_structure(self, op: Operation) -> bool:
"""
Checks if the sfg contains distributive structures.
Parameters
==========
Checks if the SFG contains distributive structures.
NOTE: a*b + c = a*(b + c/a) is considered distributive. Meaning that an algorithm transformation would require an additionat operation.
Parameters:
----------
op : Operation
The operation that is the start of the structure to check for distributivity.
Returns:
-------
bool: True if a distributive structures is found, False otherwise.
"""
# TODO Butterfly and SymmetricTwoportAdaptor needs to be converted to a SF using to_sfg() in order to be evaluated
if op.type_name() == 'mac':
return True
elif op.type_name() in ['cmul', 'mul', 'div']:
elif op.type_name() in ['mul', 'div']:
for subsequent_op in op.subsequent_operations:
if subsequent_op.type_name() in [
'add',
@@ -2181,11 +2193,32 @@ class SFG(AbstractOperation):
'min',
'max',
'sqrt',
'abs',
'rec',
'out',
't',
]:
return True
elif subsequent_op.type_name() in ['mul', 'div']:
for subsequent_op in subsequent_op.subsequent_operations:
return self.has_distributive_structure(subsequent_op)
else:
return False
elif op.type_name() in ['cmul', 'shift', 'rshift', 'lshift']:
for subsequent_op in op.subsequent_operations:
if subsequent_op.type_name() in [
'add',
'sub',
'addsub',
'min',
'max',
'out',
't',
]:
return True
elif subsequent_op.type_name() in ['cmul', 'shift', 'rshift', 'lshift']:
for subsequent_op in subsequent_op.subsequent_operations:
return self.has_distributive_structure(subsequent_op)
else:
return False
elif op.type_name() in ['add', 'sub', 'addsub']:
@@ -2200,6 +2233,9 @@ class SFG(AbstractOperation):
't',
]:
return True
elif subsequent_op.type_name() in ['add', 'sub', 'addsub']:
for subsequent_op in subsequent_op.subsequent_operations:
return self.has_distributive_structure(subsequent_op)
else:
return False
elif op.type_name() in ['min', 'max']:
@@ -2208,19 +2244,18 @@ class SFG(AbstractOperation):
'add',
'sub',
'addsub',
'mul',
'div',
'cmul',
'out',
't',
]:
return True
elif subsequent_op.type_name() in ['min', 'max']:
for subsequent_op in subsequent_op.subsequent_operations:
return self.has_distributive_structure(subsequent_op)
else:
return False
# elif op.type_name() == 'cmul':
# for subsequent_op in op.subsequent_operations:
# if subsequent_op.type_name() in ['max', 'min', 'out', 't']:
# return True
# else: return False
return False
def get_used_type_names(self) -> List[TypeName]:
Loading