Notice

This is a raw view of the Python source code due to an error in generating the documentation.

Date of Conversion: 2025-02-20 21:48:16

   def eval(self, groupname, group_op):
        """
        Evaluates the expressions like o1+o2+o3-o4+o5+o6 and stores the result in a new group.

        Parameters:
        -----------
        groupname : str
            The name of the group that will store the result.
        group_op : GroupOperation or Operation
            The operation or group of operations to evaluate.
        """
        if isinstance(group_op, Operation):
            # For Operation, simply add it with the specified name
            group_op.name = groupname
            self.add_operation(group_op)

        elif isinstance(group_op, GroupOperation):
            # Start with the first operation
            result_group = group_op.operations[0].name

            # Iterate through the remaining operations in this GroupOperation
            for op in group_op.operations[1:]:
                if group_op.operator == '+':
                    self.union(groupname, result_group, op.name)
                elif group_op.operator == '-':
                    self.subtract(groupname, result_group, op.name)
                elif group_op.operator == '*':
                    self.intersect(groupname, result_group, op.name)
                result_group = groupname

        else:
            raise TypeError("Expected an instance of GroupOperation or Operation.")




# %% debug section - generic code to test methods (press F5)
if __name__ == '__main__':

    # Example Usage
    # G = group()
    # G.variable("groupname","variablename","myexpression myexpression myexpression myexpression and again 1234")
    # print(G.disp("groupname"))

    # G.region("regiongroup","myregionID")
    # print(G.disp("regiongroup"))

    # G.union("uniongroup","group1","group2","group3")
    # print(G.disp("uniongroup"))

    # Advanced Usage
    G = group()
    G.create('o1')
    G.create('o2')
    G.create('o3')
    G.create('o4')
    G.create('o5')
    G.create('o6')
    G.create('o7')
    #G.eval("debug",G.o1+G.o2+G.o3 + G.o4 + (G.o5 +G.o6) + G.o7)
    G.eval("debug",G.o1+G.o2+G.o3)
    print(repr(G))

# class for multiple heterogenerous operations
class GroupOperation:
    """
        class to represent a sequence of operations combined using a specific operator.

        Attributes
        ----------
            operations : list of Operation or GroupOperation
                The list of operations involved in this GroupOperation.
            operators : list of str
                The list of operators used to combine the operations.
                Operators are represented as strings, e.g., '+', '-', '*'.

        Methods
        -------
            __init__(operation1, operation2, operator):
                Initialize a GroupOperation instance with two Operation or GroupOperation instances and an operator.
            __add__(other):
                Overloads the '+' operator to support adding two GroupOperation or Operation instances.
            __sub__(other):
                Overloads the '-' operator to support subtracting two GroupOperation or Operation instances.
            __mul__(other):
                Overloads the '*' operator to support multiplying two GroupOperation or Operation instances.

    """

    def __init__(self, operation1, operation2, operator,name=""):
        # error check
        if not isinstance(operation1, (Operation, GroupOperation)) or not isinstance(operation2, (Operation, GroupOperation)):
            raise TypeError("Expected instances of Operation or GroupOperation.")
        # Check whether operation1 needs to be collapsed
        if isinstance(operation1, GroupOperation):
            if operation1.operator != operator:
                operation1 = self.collapse(operation1)

        # Check whether operation2 needs to be collapsed
        if isinstance(operation2, GroupOperation):
            if operation2.operator != operator:
                operation2 = self.collapse(operation2)
        # Store the (potentially collapsed) operations
        self.operations = [operation1, operation2]
        self.operator = operator if operator is not None else ""
        self.name = name if name else self.generate_hashname()
        self.flatten()

    def generate_hashname(self):
        """
            Generate a hash name by concatenating the names of all operations inside
            and hashing the result, keeping only the first 10 characters.
        """
        concatenated_names = self.operator.join(op.name for op in self.operations)
        return "GRP"+hashlib.sha256(concatenated_names.encode()).hexdigest()[:6]

    @staticmethod
    def collapse(group_operation):
        """
            Collapse a group of operations of the same kind
        """
        collapsed_name = group_operation.generate_hashname()
        available_names = group_operation.flatten_names()
        if group_operation.operator == '+':
            group.union(collapsed_name, available_names[0],*available_names[1:])
        elif group_operation.operator == '-':
            group.subtract(collapsed_name, available_names[0],*available_names[1:])
        elif group_operation.operator == '*':
            group.intersect(collapsed_name, available_names[0],*available_names[1:])
        return group._operations[-1]

    def flatten(self):
        """
        Recursively flattens this GroupOperation to reduce unnecessary nesting.
        """
        flat_operations = []
        for operation in self.operations:
            if isinstance(operation, GroupOperation):
                if operation.operator == self.operator:
                    flat_operations.extend(operation.operations)
                else:
                    flat_operations.append(operation)
            else:
                flat_operations.append(operation)
        return flat_operations

    def __add__(self, other):
        return GroupOperation(self, other, '+')

    def __sub__(self, other):
        return GroupOperation(self, other, '-')

    def __mul__(self, other):
        return GroupOperation(self, other, '*')

    def __repr__(self):
          return f"{self.name} = " + f" {self.operator} ".join(map(str, self.operations))


    def flatten_names(self):
        """
        Recursively collect the names of all Operation instances inside
        this GroupOperation, including any nested GroupOperation instances.
        """
        names = []
        for op in self.operations:
            if isinstance(op, GroupOperation):
                names.extend(op.flatten_names())
            else:
                names.append(op.name)
        return names


    def extend(self, new_operator, new_operation, force_collapse=False):
        """
        Extend the current GroupOperation with a new operation.
        If the new operator is different from the current operator,
        it merges the current operations into a single operation
        using the method corresponding to the current operator.

        Parameters:
        -----------
        new_operator : str
            The new operator to be applied. It can be '+' (union), '-' (subtract), or '*' (intersect).
        new_operation : Operation
            The new operation object to be added to this GroupOperation.
        force_collapse : bool
            Whether to force a collapse of the current operations into a single operation
            using the current operator, without adding a new operation.
        """
        if new_operator == self.operator:
            # If the new operator is the same, just add the new operation to this GroupOperation
            if isinstance(new_operation, GroupOperation) and new_operation.operator == self.operator:
                self.operations.extend(new_operation.operations)
            else:
                self.operations.append(new_operation)
        else:
            # If the new operator is different, start a new GroupOperation
            return GroupOperation(self, new_operation, new_operator)