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)
    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)
 def __sub__(self, other):
     "Subtract other objects."
     # Return a new sum
     if other._prec == 4 and self.denom == other.denom: # frac
         num = create_sum([self.num, create_product([FloatValue(-1), other.num])]).expand()
         return create_fraction(num, self.denom)
     return create_sum([self, create_product([FloatValue(-1), other])])
 def __add__(self, other):
     "Addition by other objects."
     # Add two fractions if their denominators are equal by creating
     # (expanded) sum of their numerators.
     if other._prec == 4 and self.denom == other.denom: # frac
         return create_fraction(create_sum([self.num, other.num]).expand(), self.denom)
     return create_sum([self, other])
def _group_fractions(expr):
    "Group Fractions in a Sum: 2/x + y/x -> (2 + y)/x."
    if expr._prec != 3:  # sum
        return expr

    # Loop variables and group those with common denominator.
    not_frac = []
    fracs = {}
    for v in expr.vrs:
        if v._prec == 4:  # frac
            if v.denom in fracs:
                fracs[v.denom][1].append(v.num)
                fracs[v.denom][0] += 1
            else:
                fracs[v.denom] = [1, [v.num], v]
            continue
        not_frac.append(v)
    if not fracs:
        return expr

    # Loop all fractions and create new ones using an appropriate numerator.
    for k, v in sorted(fracs.iteritems()):
        if v[0] > 1:
            # TODO: Is it possible to avoid expanding the Sum?
            # I think we have to because x/a + 2*x/a -> 3*x/a.
            not_frac.append(create_fraction(create_sum(v[1]).expand(), k))
        else:
            not_frac.append(v[2])

    # Create return value.
    if len(not_frac) > 1:
        return create_sum(not_frac)
    return not_frac[0]
 def __add__(self, other):
     "Addition by other objects."
     # Add two fractions if their denominators are equal by creating
     # (expanded) sum of their numerators.
     if other._prec == 4 and self.denom == other.denom:  # frac
         return create_fraction(
             create_sum([self.num, other.num]).expand(), self.denom)
     return create_sum([self, other])
 def __sub__(self, other):
     "Subtract other objects."
     # Return a new sum
     if other._prec == 4 and self.denom == other.denom:  # frac
         num = create_sum(
             [self.num,
              create_product([FloatValue(-1), other.num])]).expand()
         return create_fraction(num, self.denom)
     return create_sum([self, create_product([FloatValue(-1), other])])
 def reduce_ops(self):
     # Try to reduce operations by reducing the numerator and denominator.
     # FIXME: We assume expanded variables here, so any common variables in
     # the numerator and denominator are already removed i.e, there is no
     # risk of encountering (x + x*y) / x -> x*(1 + y)/x -> (1 + y).
     if self._reduced:
         return self._reduced
     num = self.num.reduce_ops()
     # Only return a new Fraction if we still have a denominator.
     if self.denom:
         self._reduced = create_fraction(num, self.denom.reduce_ops())
     else:
         self._reduced = num
     return self._reduced
 def reduce_ops(self):
     # Try to reduce operations by reducing the numerator and denominator.
     # FIXME: We assume expanded variables here, so any common variables in
     # the numerator and denominator are already removed i.e, there is no
     # risk of encountering (x + x*y) / x -> x*(1 + y)/x -> (1 + y).
     if self._reduced:
         return self._reduced
     num = self.num.reduce_ops()
     # Only return a new Fraction if we still have a denominator.
     if self.denom:
         self._reduced = create_fraction(num, self.denom.reduce_ops())
     else:
         self._reduced = num
     return self._reduced
Beispiel #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.")

        # 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]
Beispiel #11
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()
    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
    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
    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)]
 def reduce_var(self, var):
     "Reduce the fraction by another variable through division of numerator."
     # We assume that this function is only called by reduce_ops, such that
     # we just need to consider the numerator.
     return create_fraction(self.num / var, self.denom)
 def reduce_var(self, var):
     "Reduce the fraction by another variable through division of numerator."
     # We assume that this function is only called by reduce_ops, such that
     # we just need to consider the numerator.
     return create_fraction(self.num/var, self.denom)
    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)]
Beispiel #18
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()