Пример #1
0
 def __sub__(self, other):
     "Subtract other objects."
     # NOTE: We expect expanded objects
     # symbols, if other is a product, try to let product handle the addition.
     if self._repr == other._repr:
         return create_float(0)
     elif other._prec == 2: # prod
         if other.get_vrs() == (self,):
             return create_product([create_float(1.0 - other.val), self]).expand()
     return create_sum([self, create_product([create_float(-1), other])])
Пример #2
0
 def __sub__(self, other):
     "Subtract other objects."
     # NOTE: We expect expanded objects here.
     if other._prec == 0:  # float
         return create_float(self.val - other.val)
     # Multiply other by -1
     elif self.val == 0.0:
         return create_product([create_float(-1), other])
     # Return a new sum where other is multiplied by -1
     return create_sum([self, create_product([create_float(-1), other])])
Пример #3
0
 def __sub__(self, other):
     "Subtract other objects."
     # NOTE: We expect expanded objects here.
     if other._prec == 0: # float
         return create_float(self.val-other.val)
     # Multiply other by -1
     elif self.val == 0.0:
         return create_product([create_float(-1), other])
     # Return a new sum where other is multiplied by -1
     return create_sum([self, create_product([create_float(-1), other])])
Пример #4
0
 def __sub__(self, other):
     "Subtract other objects."
     # NOTE: We expect expanded objects
     # symbols, if other is a product, try to let product handle the addition.
     if self._repr == other._repr:
         return create_float(0)
     elif other._prec == 2:  # prod
         if other.get_vrs() == (self, ):
             return create_product([create_float(1.0 - other.val),
                                    self]).expand()
     return create_sum([self, create_product([create_float(-1), other])])
Пример #5
0
 def __sub__(self, other):
     "Subtract other objects."
     if other._prec == 2 and self.get_vrs() == other.get_vrs():
         # Return expanded product, to get rid of 3*x + -2*x -> x, not 1*x.
         return create_product([create_float(self.val - other.val)] + list(self.get_vrs())).expand()
     # if self == 2*x and other == x return 3*x.
     elif other._prec == 1: # sym
         if self.get_vrs() == (other,):
             # Return expanded product, to get rid of -x + x -> 0, not product(0).
             return create_product([create_float(self.val - 1.0), other]).expand()
     # Return sum
     return create_sum([self, create_product([FloatValue(-1), other])])
Пример #6
0
    def __div__(self, other):
        "Division by other objects."
        # If division is illegal (this should definitely not happen).
        if other.val == 0.0:
            error("Division by zero")

        # TODO: Should we also support division by fraction for generality?
        # It should not be needed by this module.
        if other._prec == 4:  # frac
            error("Did not expected to divide by fraction")

        # If fraction will be zero.
        if self.val == 0.0:
            return self

        # NOTE: We expect expanded objects here i.e., Product([FloatValue])
        # should not be present.
        # Handle types appropriately.
        if other._prec == 0:  # float
            return create_float(self.val / other.val)
        # If other is a symbol, return a simple fraction.
        elif other._prec == 1:  # sym
            return create_fraction(self, other)
        # Don't handle division by sum.
        elif other._prec == 3:  # sum
            # TODO: Here we could do: 4 / (2*x + 4*y) -> 2/(x + 2*y).
            return create_fraction(self, other)

        # If other is a product, remove any float value to avoid
        # 4 / (2*x), this will return 2/x.
        val = 1.0
        for v in other.vrs:
            if v._prec == 0:  # float
                val *= v.val

        # If we had any floats, create new numerator and only use 'real' variables
        # from the product in the denominator.
        if val != 1.0:
            # Check if we need to create a new denominator.
            # TODO: Just use other.vrs[1:] instead.
            if len(other.get_vrs()) > 1:
                return create_fraction(create_float(self.val / val),
                                       create_product(other.get_vrs()))
            # TODO: Because we expect all products to be expanded we shouldn't need
            # to check for this case, just use other.vrs[1].
            elif len(other.get_vrs()) == 1:
                return create_fraction(create_float(self.val / val),
                                       other.vrs[1])
            error("No variables left in denominator")

        # Nothing left to do.
        return create_fraction(self, other)
Пример #7
0
    def __init__(self, numerator, denominator):
        """Initialise a Fraction object, it derives from Expr and contains
        the additional variables:

        num       - expr, the numerator.
        denom     - expr, the denominator.
        _expanded - object, an expanded object of self, e.g.,
                    self = 'x*y/x'-> self._expanded = y (a symbol).
        _reduced  - object, a reduced object of self, e.g.,
                    self = '(2*x + x*y)/z'-> self._reduced = x*(2 + y)/z (a fraction).
        NOTE: self._prec = 4."""

        # Check for illegal division.
        if denominator.val == 0.0:
            error("Division by zero.")

        # Initialise all variables.
        self.val = numerator.val
        self.t = min([numerator.t, denominator.t])
        self.num = numerator
        self.denom = denominator
        self._prec = 4
        self._expanded = False
        self._reduced = False

        # Only try to eliminate scalar values.
        # TODO: If we divide by a float, we could add the inverse to the
        # numerator as a product, but I don't know if this is efficient
        # since it will involve creating a new object.
        if denominator._prec == 0 and numerator._prec == 0:  # float
            self.num = create_float(numerator.val / denominator.val)
            # Remove denominator, such that it will be excluded when printing.
            self.denom = None

        # Handle zero.
        if self.val == 0.0:
            # Remove denominator, such that it will be excluded when printing
            self.denom = None

        # Compute the representation now, such that we can use it directly
        # in the __eq__ and __ne__ methods (improves performance a bit, but
        # only when objects are cached).
        if self.denom:
            self._repr = "Fraction(%s, %s)" % (self.num._repr,
                                               self.denom._repr)
        else:
            self._repr = "Fraction(%s, %s)" % (self.num._repr,
                                               create_float(1)._repr)

        # Use repr as hash value.
        self._hash = hash(self._repr)
Пример #8
0
 def __add__(self, other):
     "Addition by other objects."
     # NOTE: Assuming expanded variables.
     # If two products are equal, add their float values.
     if other._prec == 2 and self.get_vrs() == other.get_vrs():
         # Return expanded product, to get rid of 3*x + -2*x -> x, not 1*x.
         return create_product([create_float(self.val + other.val)] + list(self.get_vrs())).expand()
     # if self == 2*x and other == x return 3*x.
     elif other._prec == 1: # sym
         if self.get_vrs() == (other,):
             # Return expanded product, to get rid of -x + x -> 0, not product(0).
             return create_product([create_float(self.val + 1.0), other]).expand()
     # Return sum
     return create_sum([self, other])
Пример #9
0
 def __sub__(self, other):
     "Subtract other objects."
     if other._prec == 2 and self.get_vrs() == other.get_vrs():
         # Return expanded product, to get rid of 3*x + -2*x -> x, not 1*x.
         return create_product([create_float(self.val - other.val)] +
                               list(self.get_vrs())).expand()
     # if self == 2*x and other == x return 3*x.
     elif other._prec == 1:  # sym
         if self.get_vrs() == (other, ):
             # Return expanded product, to get rid of -x + x -> 0, not product(0).
             return create_product([create_float(self.val - 1.0),
                                    other]).expand()
     # Return sum
     return create_sum([self, create_product([FloatValue(-1), other])])
Пример #10
0
    def __div__(self, other):
        "Division by other objects."
        # If division is illegal (this should definitely not happen).
        if other.val == 0.0:
            error("Division by zero")

        # TODO: Should we also support division by fraction for generality?
        # It should not be needed by this module.
        if other._prec == 4: # frac
            error("Did not expected to divide by fraction")

        # If fraction will be zero.
        if self.val == 0.0:
            return self

        # NOTE: We expect expanded objects here i.e., Product([FloatValue])
        # should not be present.
        # Handle types appropriately.
        if other._prec == 0: # float
            return create_float(self.val/other.val)
        # If other is a symbol, return a simple fraction.
        elif other._prec == 1: # sym
            return create_fraction(self, other)
        # Don't handle division by sum.
        elif other._prec == 3: # sum
            # TODO: Here we could do: 4 / (2*x + 4*y) -> 2/(x + 2*y).
            return create_fraction(self, other)

        # If other is a product, remove any float value to avoid
        # 4 / (2*x), this will return 2/x.
        val = 1.0
        for v in other.vrs:
            if v._prec == 0: # float
                val *= v.val

        # If we had any floats, create new numerator and only use 'real' variables
        # from the product in the denominator.
        if val != 1.0:
            # Check if we need to create a new denominator.
            # TODO: Just use other.vrs[1:] instead.
            if len(other.get_vrs()) > 1:
                return create_fraction(create_float(self.val/val), create_product(other.get_vrs()))
            # TODO: Because we expect all products to be expanded we shouldn't need
            # to check for this case, just use other.vrs[1].
            elif len(other.get_vrs()) == 1:
                return create_fraction(create_float(self.val/val), other.vrs[1])
            error("No variables left in denominator")

        # Nothing left to do.
        return create_fraction(self, other)
Пример #11
0
    def __init__(self, numerator, denominator):
        """Initialise a Fraction object, it derives from Expr and contains
        the additional variables:

        num       - expr, the numerator.
        denom     - expr, the denominator.
        _expanded - object, an expanded object of self, e.g.,
                    self = 'x*y/x'-> self._expanded = y (a symbol).
        _reduced  - object, a reduced object of self, e.g.,
                    self = '(2*x + x*y)/z'-> self._reduced = x*(2 + y)/z (a fraction).
        NOTE: self._prec = 4."""

        # Check for illegal division.
        if denominator.val == 0.0:
            error("Division by zero.")

        # Initialise all variables.
        self.val = numerator.val
        self.t = min([numerator.t, denominator.t])
        self.num = numerator
        self.denom = denominator
        self._prec = 4
        self._expanded = False
        self._reduced = False

        # Only try to eliminate scalar values.
        # TODO: If we divide by a float, we could add the inverse to the
        # numerator as a product, but I don't know if this is efficient
        # since it will involve creating a new object.
        if denominator._prec == 0 and numerator._prec == 0: # float
            self.num = create_float(numerator.val/denominator.val)
            # Remove denominator, such that it will be excluded when printing.
            self.denom = None

        # Handle zero.
        if self.val == 0.0:
            # Remove denominator, such that it will be excluded when printing
            self.denom = None

        # Compute the representation now, such that we can use it directly
        # in the __eq__ and __ne__ methods (improves performance a bit, but
        # only when objects are cached).
        if self.denom:
            self._repr = "Fraction(%s, %s)" %(self.num._repr, self.denom._repr)
        else:
            self._repr = "Fraction(%s, %s)" %(self.num._repr, create_float(1)._repr)

        # Use repr as hash value.
        self._hash = hash(self._repr)
Пример #12
0
 def __add__(self, other):
     "Addition by other objects."
     # NOTE: Assuming expanded variables.
     # If two products are equal, add their float values.
     if other._prec == 2 and self.get_vrs() == other.get_vrs():
         # Return expanded product, to get rid of 3*x + -2*x -> x, not 1*x.
         return create_product([create_float(self.val + other.val)] +
                               list(self.get_vrs())).expand()
     # if self == 2*x and other == x return 3*x.
     elif other._prec == 1:  # sym
         if self.get_vrs() == (other, ):
             # Return expanded product, to get rid of -x + x -> 0, not product(0).
             return create_product([create_float(self.val + 1.0),
                                    other]).expand()
     # Return sum
     return create_sum([self, other])
Пример #13
0
    def reduce_vartype(self, var_type):
        """Reduce expression with given var_type. It returns a tuple
        (found, remain), where 'found' is an expression that only has variables
        of type == var_type. If no variables are found, found=(). The 'remain'
        part contains the leftover after division by 'found' such that:
        self = found*remain."""
        # Sort variables according to type.
        found = []
        remains = []
        for v in self.vrs:
            if v.t == var_type:
                found.append(v)
                continue
            remains.append(v)

        # Create appropriate object for found.
        if len(found) > 1:
            found = create_product(found)
        elif found:
            found = found.pop()
        # We did not find any variables.
        else:
            return [((), self)]

        # Create appropriate object for remains.
        if len(remains) > 1:
            remains = create_product(remains)
        elif remains:
            remains = remains.pop()
        # We don't have anything left.
        else:
            return [(self, create_float(1))]

        # Return whatever we found.
        return [(found, remains)]
Пример #14
0
    def reduce_vartype(self, var_type):
        """Reduce expression with given var_type. It returns a tuple
        (found, remain), where 'found' is an expression that only has variables
        of type == var_type. If no variables are found, found=(). The 'remain'
        part contains the leftover after division by 'found' such that:
        self = found*remain."""
        # Sort variables according to type.
        found = []
        remains = []
        for v in self.vrs:
            if v.t == var_type:
                found.append(v)
                continue
            remains.append(v)

        # Create appropriate object for found.
        if len(found) > 1:
            found = create_product(found)
        elif found:
            found = found.pop()
        # We did not find any variables.
        else:
            return [((), self)]

        # Create appropriate object for remains.
        if len(remains) > 1:
            remains = create_product(remains)
        elif remains:
            remains = remains.pop()
        # We don't have anything left.
        else:
            return [(self, create_float(1))]

        # Return whatever we found.
        return [(found, remains)]
Пример #15
0
 def __mul__(self, other):
     "Multiplication by other objects."
     # NOTE: We expect expanded objects here i.e., Product([FloatValue])
     # should not be present.
     # Only handle case where other is a float, else let the other
     # object handle the multiplication.
     if other._prec == 0: # float
         return create_float(self.val*other.val)
     return other.__mul__(self)
Пример #16
0
 def __mul__(self, other):
     "Multiplication by other objects."
     # NOTE: We expect expanded objects here i.e., Product([FloatValue])
     # should not be present.
     # Only handle case where other is a float, else let the other
     # object handle the multiplication.
     if other._prec == 0:  # float
         return create_float(self.val * other.val)
     return other.__mul__(self)
Пример #17
0
 def __add__(self, other):
     "Addition by other objects."
     # NOTE: We expect expanded objects
     # symbols, if other is a product, try to let product handle the addition.
     # Returns x + x -> 2*x, x + 2*x -> 3*x.
     if self._repr == other._repr:
         return create_product([create_float(2), self])
     elif other._prec == 2: # prod
         return other.__add__(self)
     return create_sum([self, other])
Пример #18
0
 def __add__(self, other):
     "Addition by other objects."
     # NOTE: We expect expanded objects here.
     # This is only well-defined if other is a float or if self.val == 0.
     if other._prec == 0:  # float
         return create_float(self.val + other.val)
     elif self.val == 0.0:
         return other
     # Return a new sum
     return create_sum([self, other])
Пример #19
0
 def __add__(self, other):
     "Addition by other objects."
     # NOTE: We expect expanded objects here.
     # This is only well-defined if other is a float or if self.val == 0.
     if other._prec == 0: # float
         return create_float(self.val+other.val)
     elif self.val == 0.0:
         return other
     # Return a new sum
     return create_sum([self, other])
Пример #20
0
 def __add__(self, other):
     "Addition by other objects."
     # NOTE: We expect expanded objects
     # symbols, if other is a product, try to let product handle the addition.
     # Returns x + x -> 2*x, x + 2*x -> 3*x.
     if self._repr == other._repr:
         return create_product([create_float(2), self])
     elif other._prec == 2:  # prod
         return other.__add__(self)
     return create_sum([self, other])
Пример #21
0
 def reduce_vartype(self, var_type):
     """Reduce expression with given var_type. It returns a tuple
     (found, remain), where 'found' is an expression that only has variables
     of type == var_type. If no variables are found, found=(). The 'remain'
     part contains the leftover after division by 'found' such that:
     self = found*remain.
     Works for FloatValue and Symbol."""
     if self.t == var_type:
         return [(self, create_float(1))]
     return [((), self)]
Пример #22
0
 def reduce_vartype(self, var_type):
     """Reduce expression with given var_type. It returns a tuple
     (found, remain), where 'found' is an expression that only has variables
     of type == var_type. If no variables are found, found=(). The 'remain'
     part contains the leftover after division by 'found' such that:
     self = found*remain.
     Works for FloatValue and Symbol."""
     if self.t == var_type:
         return [(self, create_float(1))]
     return [((), self)]
Пример #23
0
 def __mul__(self, other):
     "Multiplication by other objects."
     # NOTE: assuming that we get expanded variables.
     # If product will be zero.
     if self.val == 0.0 or other.val == 0.0:
         return create_float(0)
     # Create new expanded numerator and denominator and use '/' to reduce.
     if other._prec != 4: # frac
         return (self.num*other)/self.denom
     # If we have a fraction, create new numerator and denominator and use
     # '/' to reduce expression.
     return create_product([self.num, other.num]).expand()/create_product([self.denom, other.denom]).expand()
Пример #24
0
 def __mul__(self, other):
     "Multiplication by other objects."
     # NOTE: assuming that we get expanded variables.
     # If product will be zero.
     if self.val == 0.0 or other.val == 0.0:
         return create_float(0)
     # Create new expanded numerator and denominator and use '/' to reduce.
     if other._prec != 4:  # frac
         return (self.num * other) / self.denom
     # If we have a fraction, create new numerator and denominator and use
     # '/' to reduce expression.
     return create_product([self.num, other.num]).expand() / create_product(
         [self.denom, other.denom]).expand()
Пример #25
0
    def __mul__(self, other):
        "Multiplication by other objects."
        # If product will be zero.
        if self.val == 0.0 or other.val == 0.0:
            return create_float(0)

        # NOTE: We expect expanded sub-expressions with no nested operators.
        # Create list of new products using the '*' operator
        # TODO: Is this efficient?
        new_prods = [v * other for v in self.vrs]

        # Remove zero valued terms.
        # TODO: Can this still happen?
        new_prods = [v for v in new_prods if v.val != 0.0]

        # Create new sum.
        if not new_prods:
            return create_float(0)
        elif len(new_prods) > 1:
            # Expand sum to collect terms.
            return create_sum(new_prods).expand()
        # TODO: Is it necessary to call expand?
        return new_prods[0].expand()
Пример #26
0
    def __div__(self, other):
        "Division by other objects."
        # If division is illegal (this should definitely not happen).
        if other.val == 0.0:
            error("Division by zero.")

        # If fraction will be zero.
        if self.val == 0.0:
            return create_float(0)

        # NOTE: assuming that we get expanded variables.
        # If other is a Sum we can only return a fraction.
        # TODO: We could check for equal sums if Sum.__eq__ could be trusted.
        # As it is now (2*x + y) == (3*x + y), which works for the other things I do.
        # NOTE: Expect that other is expanded i.e., x + x -> 2*x which can be handled.
        # TODO: Fix (1 + y) / (x + x*y) -> 1 / x
        # Will this be handled when reducing operations on a fraction?
        if other._prec == 3:  # sum
            return create_fraction(self, other)

        # NOTE: We expect expanded sub-expressions with no nested operators.
        # Create list of new products using the '*' operator.
        # TODO: Is this efficient?
        new_fracs = [v / other for v in self.vrs]

        # Remove zero valued terms.
        # TODO: Can this still happen?
        new_fracs = [v for v in new_fracs if v.val != 0.0]

        # Create new sum.
        # TODO: No need to call expand here, using the '/' operator should have
        # taken care of this.
        if not new_fracs:
            return create_float(0)
        elif len(new_fracs) > 1:
            return create_sum(new_fracs)
        return new_fracs[0]
Пример #27
0
    def __mul__(self, other):
        "Multiplication by other objects."
        # If product will be zero.
        if self.val == 0.0 or other.val == 0.0:
            return create_float(0)

        # If other is a Sum or Fraction let them handle it.
        if other._prec in (3, 4):  # sum or frac
            return other.__mul__(self)

        # NOTE: We expect expanded sub-expressions with no nested operators.
        # Create new product adding float or symbol.
        if other._prec in (0, 1):  # float or sym
            return create_product(self.vrs + [other])
        # Create new product adding all variables from other Product.
        return create_product(self.vrs + other.vrs)
Пример #28
0
    def __mul__(self, other):
        "Multiplication by other objects."
        # If product will be zero.
        if self.val == 0.0 or other.val == 0.0:
            return create_float(0)

        # If other is a Sum or Fraction let them handle it.
        if other._prec in (3, 4): # sum or frac
            return other.__mul__(self)

        # NOTE: We expect expanded sub-expressions with no nested operators.
        # Create new product adding float or symbol.
        if other._prec in (0, 1): # float or sym
            return create_product(self.vrs + [other])
        # Create new product adding all variables from other Product.
        return create_product(self.vrs + other.vrs)
Пример #29
0
    def __mul__(self, other):
        "Multiplication by other objects."
        # NOTE: We assume expanded objects.
        # If product will be zero.
        if self.val == 0.0 or other.val == 0.0:
            return create_float(0)

        # If other is Sum or Fraction let them handle the multiply.
        if other._prec in (3, 4):  # sum or frac
            return other.__mul__(self)

        # If other is a float or symbol, create simple product.
        if other._prec in (0, 1):  # float or sym
            return create_product([self, other])

        # Else add variables from product.
        return create_product([self] + other.vrs)
Пример #30
0
    def __mul__(self, other):
        "Multiplication by other objects."
        # NOTE: We assume expanded objects.
        # If product will be zero.
        if self.val == 0.0 or other.val == 0.0:
            return create_float(0)

        # If other is Sum or Fraction let them handle the multiply.
        if other._prec in (3, 4): # sum or frac
            return other.__mul__(self)

        # If other is a float or symbol, create simple product.
        if other._prec in (0, 1): # float or sym
            return create_product([self, other])

        # Else add variables from product.
        return create_product([self] + other.vrs)
Пример #31
0
    def __div__(self, other):
        "Division by other objects."
        # NOTE: We assume expanded objects.
        # If division is illegal (this should definitely not happen).
        if other.val == 0.0:
            error("Division by zero.")

        # Return 1 if the two symbols are equal.
        if self._repr == other._repr:
            return create_float(1)

        # If other is a Sum we can only return a fraction.
        # TODO: Refine this later such that x / (x + x*y) -> 1 / (1 + y)?
        if other._prec == 3: # sum
            return create_fraction(self, other)

        # Handle division by FloatValue, Symbol, Product and Fraction.
        # Create numerator and list for denominator.
        num = [self]
        denom = []

        # Add floatvalue, symbol and products to the list of denominators.
        if other._prec in (0, 1): # float or sym
            denom = [other]
        elif other._prec == 2: # prod
            # Need copies, so can't just do denom = other.vrs.
            denom += other.vrs
        # fraction.
        else:
            # TODO: Should we also support division by fraction for generality?
            # It should not be needed by this module.
            error("Did not expected to divide by fraction.")

        # Remove one instance of self in numerator and denominator if
        # present in denominator i.e., x/(x*y) --> 1/y.
        if self in denom:
            denom.remove(self)
            num.remove(self)

        # Loop entries in denominator and move float value to numerator.
        for d in denom:
            # Add the inverse of a float to the numerator, remove it from
            # the denominator and continue.
            if d._prec == 0: # float
                num.append(create_float(1.0/other.val))
                denom.remove(d)
                continue

        # Create appropriate return value depending on remaining data.
        # Can only be for x / (2*y*z) -> 0.5*x / (y*z).
        if len(num) > 1:
            num = create_product(num)
        # x / (y*z) -> x/(y*z),
        elif num:
            num = num[0]
        # else x / (x*y) -> 1/y.
        else:
            num = create_float(1)

        # If we have a long denominator, create product and fraction.
        if len(denom) > 1:
            return create_fraction(num, create_product(denom))
        # If we do have a denominator, but only one variable don't create a
        # product, just return a fraction using the variable as denominator.
        elif denom:
            return create_fraction(num, denom[0])
        # If we don't have any donominator left, return the numerator.
        # x / 2.0 -> 0.5*x.
        return num.expand()
Пример #32
0
    def reduce_vartype(self, var_type):
        """Reduce expression with given var_type. It returns a tuple
        (found, remain), where 'found' is an expression that only has variables
        of type == var_type. If no variables are found, found=(). The 'remain'
        part contains the leftover after division by 'found' such that:
        self = found*remain."""

        # Reduce the numerator by the var type.
#        print "self.num._prec: ", self.num._prec
#        print "self.num: ", self.num
        if self.num._prec == 3:
            foo = self.num.reduce_vartype(var_type)
            if len(foo) == 1:
                num_found, num_remain = foo[0]
#                num_found, num_remain = self.num.reduce_vartype(var_type)[0]
            else:
                # meg: I have only a marginal idea of what I'm doing here!
#                print "here: "
                new_sum = []
                for num_found, num_remain in foo:
                    if num_found == ():
                        new_sum.append(create_fraction(num_remain, self.denom))
                    else:
                        new_sum.append(create_fraction(create_product([num_found, num_remain]), self.denom))
                return create_sum(new_sum).expand().reduce_vartype(var_type)
        else:
#            num_found, num_remain = self.num.reduce_vartype(var_type)
            foo = self.num.reduce_vartype(var_type)
            if len(foo) != 1:
                raise RuntimeError("This case is not handled")
            num_found, num_remain = foo[0]

#        # TODO: Remove this test later, expansion should have taken care of
#        # no denominator.
#        if not self.denom:
#            error("This fraction should have been expanded.")

        # If the denominator is not a Sum things are straightforward.
        denom_found = None
        denom_remain = None
#        print "self.denom: ", self.denom
#        print "self.denom._prec: ", self.denom._prec
        if self.denom._prec != 3: # sum
#            denom_found, denom_remain = self.denom.reduce_vartype(var_type)
            foo = self.denom.reduce_vartype(var_type)
            if len(foo) != 1:
                raise RuntimeError("This case is not handled")
            denom_found, denom_remain = foo[0]

        # If we have a Sum in the denominator, all terms must be reduced by
        # the same terms to make sense
        else:
            remain = []
            for m in self.denom.vrs:
#                d_found, d_remain = m.reduce_vartype(var_type)
                foo = m.reduce_vartype(var_type)
                d_found, d_remain = foo[0]
                # If we've found a denom, but the new found is different from
                # the one already found, terminate loop since it wouldn't make
                # sense to reduce the fraction.
                # TODO: handle I0/((I0 + I1)/(G0 + G1) + (I1 + I2)/(G1 + G2))
                # better than just skipping.
#                if len(foo) != 1:
#                    raise RuntimeError("This case is not handled")
                if len(foo) != 1 or (denom_found is not None and repr(d_found) != repr(denom_found)):
                    # If the denominator of the entire sum has a type which is
                    # lower than or equal to the vartype that we are currently
                    # reducing for, we have to move it outside the expression
                    # as well.
                    # TODO: This is quite application specific, but I don't see
                    # how we can do it differently at the moment.
                    if self.denom.t <= var_type:
                        if not num_found:
                            num_found = create_float(1)
                        return [(create_fraction(num_found, self.denom), num_remain)]
                    else:
                        # The remainder is always a fraction
                        return [(num_found, create_fraction(num_remain, self.denom))]

                # Update denom found and add remainder.
                denom_found = d_found
                remain.append(d_remain)

            # There is always a non-const remainder if denominator was a sum.
            denom_remain = create_sum(remain)
#        print "den f: ", denom_found
#        print "den r: ", denom_remain
        # If we have found a common denominator, but no found numerator,
        # create a constant.
        # TODO: Add more checks to avoid expansion.
        found = None
        # There is always a remainder.
        remain = create_fraction(num_remain, denom_remain).expand()
#        print "remain: ", repr(remain)

        if num_found:
            if denom_found:
                found = create_fraction(num_found, denom_found)
            else:
                found = num_found
        else:
            if denom_found:
                found = create_fraction(create_float(1), denom_found)
            else:
                found = ()
#        print "found: ", found
#        print len((found, remain))
        return [(found, remain)]
Пример #33
0
    def __init__(self, variables):
        """Initialise a Product object, it derives from Expr and contains
        the additional variables:

        vrs       - a list of variables
        _expanded - object, an expanded object of self, e.g.,
                    self = x*(2+y) -> self._expanded = (2*x + x*y) (a sum), or
                    self = 2*x -> self._expanded = 2*x (self).
        NOTE: self._prec = 2."""

        # Initialise value, list of variables, class.
        self.val = 1.0
        self.vrs = []
        self._prec = 2

        # Initially set _expanded to True.
        self._expanded = True

        # Process variables if we have any.
        if variables:
            # Remove nested Products and test for expansion.
            float_val = 1.0
            for var in variables:
                # If any value is zero the entire product is zero.
                if var.val == 0.0:
                    self.val = 0.0
                    self.vrs = [create_float(0.0)]
                    float_val = 0.0
                    break

                # Collect floats into one variable
                if var._prec == 0:  # float
                    float_val *= var.val
                    continue
                # Take care of product such that we don't create nested products.
                elif var._prec == 2:  # prod
                    #                    if var.vrs[0]._prec == 0:
                    #                        float_val *= var.vrs[0].val
                    #                        self.vrs += var.vrs[1:]
                    #                        continue
                    #                    self.vrs += var.vrs
                    #                    continue
                    # If expanded product is a float, just add it.
                    if var._expanded and var._expanded._prec == 0:
                        float_val *= var._expanded.val
                    # If expanded product is symbol, this product is still expanded and add symbol.
                    elif var._expanded and var._expanded._prec == 1:
                        self.vrs.append(var._expanded)
                    # If expanded product is still a product, add the variables.
                    elif var._expanded and var._expanded._prec == 2:
                        #                        self.vrs.append(var)
                        # Add copies of the variables of other product (collect floats).
                        if var._expanded.vrs[0]._prec == 0:
                            float_val *= var._expanded.vrs[0].val
                            self.vrs += var._expanded.vrs[1:]
                            continue
                        self.vrs += var._expanded.vrs
                    # If expanded product is a sum or fraction, we must expand this product later.
                    elif var._expanded and var._expanded._prec in (3, 4):
                        self._expanded = False
                        self.vrs.append(var._expanded)
                    # Else the product is not expanded, and we must expand this one later
                    else:
                        self._expanded = False
                        # Add copies of the variables of other product (collect floats).
                        if var.vrs[0]._prec == 0:
                            float_val *= var.vrs[0].val
                            self.vrs += var.vrs[1:]
                            continue
                        self.vrs += var.vrs
                    continue
                # If we have sums or fractions in the variables the product is not expanded.
                elif var._prec in (3, 4):  # sum or frac
                    self._expanded = False

                # Just add any variable at this point to list of new vars.
                self.vrs.append(var)

            # If value is 1 there is no need to include it, unless it is the
            # only parameter left i.e., 2*0.5 = 1.
            if float_val and float_val != 1.0:
                self.val = float_val
                self.vrs.append(create_float(float_val))
            # If we no longer have any variables add the float.
            elif not self.vrs:
                self.val = float_val
                self.vrs = [create_float(float_val)]
            # If 1.0 is the only value left, add it.
            elif abs(float_val - 1.0) < format["epsilon"] and not self.vrs:
                self.val = 1.0
                self.vrs = [create_float(1)]

        # If we don't have any variables the product is zero.
        else:
            self.val = 0.0
            self.vrs = [create_float(0)]

        # The type is equal to the lowest variable type.
        self.t = min([v.t for v in self.vrs])

        # Sort the variables such that comparisons work.
        self.vrs.sort()

        # Compute the representation now, such that we can use it directly
        # in the __eq__ and __ne__ methods (improves performance a bit, but
        # only when objects are cached).
        self._repr = "Product([%s])" % ", ".join([v._repr for v in self.vrs])

        # Use repr as hash value.
        self._hash = hash(self._repr)

        # Store self as expanded value, if we did not encounter any sums or fractions.
        if self._expanded:
            self._expanded = self
Пример #34
0
    def __init__(self, variables):
        """Initialise a Sum object, it derives from Expr and contains the
        additional variables:

        vrs       - list, a list of variables.
        _expanded - object, an expanded object of self, e.g.,
                    self = 'x + x'-> self._expanded = 2*x (a product).
        _reduced  - object, a reduced object of self, e.g.,
                    self = '2*x + x*y'-> self._reduced = x*(2 + y) (a product).
        NOTE: self._prec = 3."""

        # Initialise value, list of variables, class, expanded and reduced.
        self.val = 1.0
        self.vrs = []
        self._prec = 3
        self._expanded = False
        self._reduced = False

        # Get epsilon
        EPS = format["epsilon"]
        # Process variables if we have any.
        if variables:
            # Loop variables and remove nested Sums and collect all floats in
            # 1 variable. We don't collect [x, x, x] into 3*x to avoid creating
            # objects, instead we do this when expanding the object.
            float_val = 0.0
            for var in variables:
                # Skip zero terms.
                if abs(var.val) < EPS:
                    continue
                elif var._prec == 0:  # float
                    float_val += var.val
                    continue
                elif var._prec == 3:  # sum
                    # Loop and handle variables of nested sum.
                    for v in var.vrs:
                        if abs(v.val) < EPS:
                            continue
                        elif v._prec == 0:  # float
                            float_val += v.val
                            continue
                        self.vrs.append(v)
                    continue
                self.vrs.append(var)

            # Only create new float if value is different from 0.
            if abs(float_val) > EPS:
                self.vrs.append(create_float(float_val))

        # If we don't have any variables the sum is zero.
        else:
            self.val = 0.0
            self.vrs = [create_float(0)]

        # Handle zero value.
        if not self.vrs:
            self.val = 0.0
            self.vrs = [create_float(0)]

        # Type is equal to the smallest type in both lists.
        self.t = min([v.t for v in self.vrs])

        # Sort variables, (for representation).
        self.vrs.sort()

        # Compute the representation now, such that we can use it directly
        # in the __eq__ and __ne__ methods (improves performance a bit, but
        # only when objects are cached).
        self._repr = "Sum([%s])" % ", ".join([v._repr for v in self.vrs])

        # Use repr as hash value.
        self._hash = hash(self._repr)
Пример #35
0
    def __div__(self, other):
        "Division by other objects."
        # If division is illegal (this should definitely not happen).
        if other.val == 0.0:
            error("Division by zero.")

        # If fraction will be zero.
        if self.val == 0.0:
            return self.vrs[0]

        # If other is a Sum we can only return a fraction.
        # NOTE: Expect that other is expanded i.e., x + x -> 2*x which can be handled
        # TODO: Fix x / (x + x*y) -> 1 / (1 + y).
        # Or should this be handled when reducing a fraction?
        if other._prec == 3: # sum
            return create_fraction(self, other)

        # Handle division by FloatValue, Symbol, Product and Fraction.
        # NOTE: assuming that we get expanded variables.

        # Copy numerator, and create list for denominator.
        num = self.vrs[:]
        denom = []
        # Add floatvalue, symbol and products to the list of denominators.
        if other._prec in (0, 1): # float or sym
            denom = [other]
        elif other._prec == 2: # prod
            # Get copy.
            denom = other.vrs[:]
        # fraction.
        else:
            error("Did not expected to divide by fraction.")

        # Loop entries in denominator and remove from numerator (and denominator).
        for d in denom[:]:
            # Add the inverse of a float to the numerator and continue.
            if d._prec == 0: # float
                num.append(create_float(1.0/d.val))
                denom.remove(d)
                continue
            if d in num:
                num.remove(d)
                denom.remove(d)

        # Create appropriate return value depending on remaining data.
        if len(num) > 1:
            # TODO: Make this more efficient?
            # Create product and expand to reduce
            # Product([5, 0.2]) == Product([1]) -> Float(1).
            num = create_product(num).expand()
        elif num:
            num = num[0]
        # If all variables in the numerator has been eliminated we need to add '1'.
        else:
            num = create_float(1)

        if len(denom) > 1:
            return create_fraction(num, create_product(denom))
        elif denom:
            return create_fraction(num, denom[0])
        # If we no longer have a denominater, just return the numerator.
        return num
Пример #36
0
    def __init__(self, variables):
        """Initialise a Product object, it derives from Expr and contains
        the additional variables:

        vrs       - a list of variables
        _expanded - object, an expanded object of self, e.g.,
                    self = x*(2+y) -> self._expanded = (2*x + x*y) (a sum), or
                    self = 2*x -> self._expanded = 2*x (self).
        NOTE: self._prec = 2."""

        # Initialise value, list of variables, class.
        self.val = 1.0
        self.vrs = []
        self._prec = 2

        # Initially set _expanded to True.
        self._expanded = True

        # Process variables if we have any.
        if variables:
            # Remove nested Products and test for expansion.
            float_val = 1.0
            for var in variables:
                # If any value is zero the entire product is zero.
                if var.val == 0.0:
                    self.val = 0.0
                    self.vrs = [create_float(0.0)]
                    float_val = 0.0
                    break

                # Collect floats into one variable
                if var._prec == 0: # float
                    float_val *= var.val
                    continue
                # Take care of product such that we don't create nested products.
                elif var._prec == 2: # prod
#                    if var.vrs[0]._prec == 0:
#                        float_val *= var.vrs[0].val
#                        self.vrs += var.vrs[1:]
#                        continue
#                    self.vrs += var.vrs
#                    continue
                    # If expanded product is a float, just add it.
                    if var._expanded and var._expanded._prec == 0:
                        float_val *= var._expanded.val
                    # If expanded product is symbol, this product is still expanded and add symbol.
                    elif var._expanded and var._expanded._prec == 1:
                        self.vrs.append(var._expanded)
                    # If expanded product is still a product, add the variables.
                    elif var._expanded and var._expanded._prec == 2:
#                        self.vrs.append(var)
                        # Add copies of the variables of other product (collect floats).
                        if var._expanded.vrs[0]._prec == 0:
                            float_val *= var._expanded.vrs[0].val
                            self.vrs += var._expanded.vrs[1:]
                            continue
                        self.vrs += var._expanded.vrs
                    # If expanded product is a sum or fraction, we must expand this product later.
                    elif var._expanded and var._expanded._prec in (3, 4):
                        self._expanded = False
                        self.vrs.append(var._expanded)
                    # Else the product is not expanded, and we must expand this one later
                    else:
                        self._expanded = False
                        # Add copies of the variables of other product (collect floats).
                        if var.vrs[0]._prec == 0:
                            float_val *= var.vrs[0].val
                            self.vrs += var.vrs[1:]
                            continue
                        self.vrs += var.vrs
                    continue
                # If we have sums or fractions in the variables the product is not expanded.
                elif var._prec in (3, 4): # sum or frac
                    self._expanded = False

                # Just add any variable at this point to list of new vars.
                self.vrs.append(var)

            # If value is 1 there is no need to include it, unless it is the
            # only parameter left i.e., 2*0.5 = 1.
            if float_val and float_val != 1.0:
                self.val = float_val
                self.vrs.append(create_float(float_val))
            # If we no longer have any variables add the float.
            elif not self.vrs:
                self.val = float_val
                self.vrs = [create_float(float_val)]
            # If 1.0 is the only value left, add it.
            elif abs(float_val - 1.0) < format["epsilon"] and not self.vrs:
                self.val = 1.0
                self.vrs = [create_float(1)]

        # If we don't have any variables the product is zero.
        else:
            self.val = 0.0
            self.vrs = [create_float(0)]

        # The type is equal to the lowest variable type.
        self.t = min([v.t for v in self.vrs])

        # Sort the variables such that comparisons work.
        self.vrs.sort()

        # Compute the representation now, such that we can use it directly
        # in the __eq__ and __ne__ methods (improves performance a bit, but
        # only when objects are cached).
        self._repr = "Product([%s])" % ", ".join([v._repr for v in self.vrs])

        # Use repr as hash value.
        self._hash = hash(self._repr)

        # Store self as expanded value, if we did not encounter any sums or fractions.
        if self._expanded:
            self._expanded = self
Пример #37
0
    def __div__(self, other):
        "Division by other objects."
        # NOTE: We assume expanded objects.
        # If division is illegal (this should definitely not happen).
        if other.val == 0.0:
            error("Division by zero.")

        # Return 1 if the two symbols are equal.
        if self._repr == other._repr:
            return create_float(1)

        # If other is a Sum we can only return a fraction.
        # TODO: Refine this later such that x / (x + x*y) -> 1 / (1 + y)?
        if other._prec == 3:  # sum
            return create_fraction(self, other)

        # Handle division by FloatValue, Symbol, Product and Fraction.
        # Create numerator and list for denominator.
        num = [self]
        denom = []

        # Add floatvalue, symbol and products to the list of denominators.
        if other._prec in (0, 1):  # float or sym
            denom = [other]
        elif other._prec == 2:  # prod
            # Need copies, so can't just do denom = other.vrs.
            denom += other.vrs
        # fraction.
        else:
            # TODO: Should we also support division by fraction for generality?
            # It should not be needed by this module.
            error("Did not expected to divide by fraction.")

        # Remove one instance of self in numerator and denominator if
        # present in denominator i.e., x/(x*y) --> 1/y.
        if self in denom:
            denom.remove(self)
            num.remove(self)

        # Loop entries in denominator and move float value to numerator.
        for d in denom:
            # Add the inverse of a float to the numerator, remove it from
            # the denominator and continue.
            if d._prec == 0:  # float
                num.append(create_float(1.0 / other.val))
                denom.remove(d)
                continue

        # Create appropriate return value depending on remaining data.
        # Can only be for x / (2*y*z) -> 0.5*x / (y*z).
        if len(num) > 1:
            num = create_product(num)
        # x / (y*z) -> x/(y*z),
        elif num:
            num = num[0]
        # else x / (x*y) -> 1/y.
        else:
            num = create_float(1)

        # If we have a long denominator, create product and fraction.
        if len(denom) > 1:
            return create_fraction(num, create_product(denom))
        # If we do have a denominator, but only one variable don't create a
        # product, just return a fraction using the variable as denominator.
        elif denom:
            return create_fraction(num, denom[0])
        # If we don't have any donominator left, return the numerator.
        # x / 2.0 -> 0.5*x.
        return num.expand()
Пример #38
0
    def __div__(self, other):
        "Division by other objects."
        # If division is illegal (this should definitely not happen).
        if other.val == 0.0:
            error("Division by zero.")

        # If fraction will be zero.
        if self.val == 0.0:
            return self.vrs[0]

        # If other is a Sum we can only return a fraction.
        # NOTE: Expect that other is expanded i.e., x + x -> 2*x which can be handled
        # TODO: Fix x / (x + x*y) -> 1 / (1 + y).
        # Or should this be handled when reducing a fraction?
        if other._prec == 3:  # sum
            return create_fraction(self, other)

        # Handle division by FloatValue, Symbol, Product and Fraction.
        # NOTE: assuming that we get expanded variables.

        # Copy numerator, and create list for denominator.
        num = self.vrs[:]
        denom = []
        # Add floatvalue, symbol and products to the list of denominators.
        if other._prec in (0, 1):  # float or sym
            denom = [other]
        elif other._prec == 2:  # prod
            # Get copy.
            denom = other.vrs[:]
        # fraction.
        else:
            error("Did not expected to divide by fraction.")

        # Loop entries in denominator and remove from numerator (and denominator).
        for d in denom[:]:
            # Add the inverse of a float to the numerator and continue.
            if d._prec == 0:  # float
                num.append(create_float(1.0 / d.val))
                denom.remove(d)
                continue
            if d in num:
                num.remove(d)
                denom.remove(d)

        # Create appropriate return value depending on remaining data.
        if len(num) > 1:
            # TODO: Make this more efficient?
            # Create product and expand to reduce
            # Product([5, 0.2]) == Product([1]) -> Float(1).
            num = create_product(num).expand()
        elif num:
            num = num[0]
        # If all variables in the numerator has been eliminated we need to add '1'.
        else:
            num = create_float(1)

        if len(denom) > 1:
            return create_fraction(num, create_product(denom))
        elif denom:
            return create_fraction(num, denom[0])
        # If we no longer have a denominater, just return the numerator.
        return num
Пример #39
0
    def reduce_vartype(self, var_type):
        """Reduce expression with given var_type. It returns a tuple
        (found, remain), where 'found' is an expression that only has variables
        of type == var_type. If no variables are found, found=(). The 'remain'
        part contains the leftover after division by 'found' such that:
        self = found*remain."""

        # Reduce the numerator by the var type.
        #        print "self.num._prec: ", self.num._prec
        #        print "self.num: ", self.num
        if self.num._prec == 3:
            foo = self.num.reduce_vartype(var_type)
            if len(foo) == 1:
                num_found, num_remain = foo[0]
#                num_found, num_remain = self.num.reduce_vartype(var_type)[0]
            else:
                # meg: I have only a marginal idea of what I'm doing here!
                #                print "here: "
                new_sum = []
                for num_found, num_remain in foo:
                    if num_found == ():
                        new_sum.append(create_fraction(num_remain, self.denom))
                    else:
                        new_sum.append(
                            create_fraction(
                                create_product([num_found, num_remain]),
                                self.denom))
                return create_sum(new_sum).expand().reduce_vartype(var_type)
        else:
            #            num_found, num_remain = self.num.reduce_vartype(var_type)
            foo = self.num.reduce_vartype(var_type)
            if len(foo) != 1:
                raise RuntimeError("This case is not handled")
            num_found, num_remain = foo[0]

#        # TODO: Remove this test later, expansion should have taken care of
#        # no denominator.
#        if not self.denom:
#            error("This fraction should have been expanded.")

# If the denominator is not a Sum things are straightforward.
        denom_found = None
        denom_remain = None
        #        print "self.denom: ", self.denom
        #        print "self.denom._prec: ", self.denom._prec
        if self.denom._prec != 3:  # sum
            #            denom_found, denom_remain = self.denom.reduce_vartype(var_type)
            foo = self.denom.reduce_vartype(var_type)
            if len(foo) != 1:
                raise RuntimeError("This case is not handled")
            denom_found, denom_remain = foo[0]

        # If we have a Sum in the denominator, all terms must be reduced by
        # the same terms to make sense
        else:
            remain = []
            for m in self.denom.vrs:
                #                d_found, d_remain = m.reduce_vartype(var_type)
                foo = m.reduce_vartype(var_type)
                d_found, d_remain = foo[0]
                # If we've found a denom, but the new found is different from
                # the one already found, terminate loop since it wouldn't make
                # sense to reduce the fraction.
                # TODO: handle I0/((I0 + I1)/(G0 + G1) + (I1 + I2)/(G1 + G2))
                # better than just skipping.
                #                if len(foo) != 1:
                #                    raise RuntimeError("This case is not handled")
                if len(foo) != 1 or (denom_found is not None
                                     and repr(d_found) != repr(denom_found)):
                    # If the denominator of the entire sum has a type which is
                    # lower than or equal to the vartype that we are currently
                    # reducing for, we have to move it outside the expression
                    # as well.
                    # TODO: This is quite application specific, but I don't see
                    # how we can do it differently at the moment.
                    if self.denom.t <= var_type:
                        if not num_found:
                            num_found = create_float(1)
                        return [(create_fraction(num_found,
                                                 self.denom), num_remain)]
                    else:
                        # The remainder is always a fraction
                        return [(num_found,
                                 create_fraction(num_remain, self.denom))]

                # Update denom found and add remainder.
                denom_found = d_found
                remain.append(d_remain)

            # There is always a non-const remainder if denominator was a sum.
            denom_remain = create_sum(remain)
#        print "den f: ", denom_found
#        print "den r: ", denom_remain
# If we have found a common denominator, but no found numerator,
# create a constant.
# TODO: Add more checks to avoid expansion.
        found = None
        # There is always a remainder.
        remain = create_fraction(num_remain, denom_remain).expand()
        #        print "remain: ", repr(remain)

        if num_found:
            if denom_found:
                found = create_fraction(num_found, denom_found)
            else:
                found = num_found
        else:
            if denom_found:
                found = create_fraction(create_float(1), denom_found)
            else:
                found = ()
#        print "found: ", found
#        print len((found, remain))
        return [(found, remain)]