Exemplo n.º 1
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)]
Exemplo n.º 2
0
 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])])
Exemplo n.º 3
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)]
Exemplo n.º 4
0
 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])])
Exemplo n.º 5
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])])
Exemplo n.º 6
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])])
Exemplo n.º 7
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])])
Exemplo n.º 8
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])])
Exemplo n.º 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])])
Exemplo n.º 10
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()
Exemplo n.º 11
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()
Exemplo n.º 12
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])])
Exemplo n.º 13
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])
Exemplo n.º 14
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])
Exemplo n.º 15
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)
Exemplo n.º 16
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)
Exemplo n.º 17
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)
Exemplo n.º 18
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)
Exemplo n.º 19
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])
Exemplo n.º 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])
Exemplo n.º 21
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)
Exemplo n.º 22
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)
Exemplo n.º 23
0
    def expand(self):
        "Expand the fraction expression."

        # If fraction is already expanded, simply return the expansion.
        if self._expanded:
            return self._expanded

        # If we don't have a denominator just return expansion of numerator.
        if not self.denom:
            return self.num.expand()

        # Expand numerator and denominator.
        num = self.num.expand()
        denom = self.denom.expand()

        # TODO: Is it too expensive to call expand in the below?
        # If both the numerator and denominator are fractions, create new
        # numerator and denominator and use division to possibly reduce the
        # expression.
        if num._prec == 4 and denom._prec == 4:  # frac
            new_num = create_product([num.num, denom.denom]).expand()
            new_denom = create_product([num.denom, denom.num]).expand()
            self._expanded = new_num / new_denom
        # If the numerator is a fraction, multiply denominators and use
        # division to reduce expression.
        elif num._prec == 4:  # frac
            new_denom = create_product([num.denom, denom]).expand()
            self._expanded = num.num / new_denom
        # If the denominator is a fraction multiply by the inverse and
        # use division to reduce expression.
        elif denom._prec == 4:  # frac
            new_num = create_product([num, denom.denom]).expand()
            self._expanded = new_num / denom.num
        # Use division to reduce the expression, no need to call expand().
        else:
            self._expanded = num / denom
        return self._expanded
Exemplo n.º 24
0
    def expand(self):
        "Expand the fraction expression."

        # If fraction is already expanded, simply return the expansion.
        if self._expanded:
            return self._expanded

        # If we don't have a denominator just return expansion of numerator.
        if not self.denom:
            return self.num.expand()

        # Expand numerator and denominator.
        num = self.num.expand()
        denom = self.denom.expand()

        # TODO: Is it too expensive to call expand in the below?
        # If both the numerator and denominator are fractions, create new
        # numerator and denominator and use division to possibly reduce the
        # expression.
        if num._prec == 4 and denom._prec == 4: # frac
            new_num = create_product([num.num, denom.denom]).expand()
            new_denom = create_product([num.denom, denom.num]).expand()
            self._expanded = new_num/new_denom
        # If the numerator is a fraction, multiply denominators and use
        # division to reduce expression.
        elif num._prec == 4: # frac
            new_denom = create_product([num.denom, denom]).expand()
            self._expanded = num.num/new_denom
        # If the denominator is a fraction multiply by the inverse and
        # use division to reduce expression.
        elif denom._prec == 4: # frac
            new_num = create_product([num, denom.denom]).expand()
            self._expanded = new_num/denom.num
        # Use division to reduce the expression, no need to call expand().
        else:
            self._expanded = num/denom
        return self._expanded
Exemplo n.º 25
0
    def expand(self):
        "Expand all members of the product."
        # If we just have one variable, compute the expansion of it
        # (it is not a Product, so it should be safe). We need this to get
        # rid of Product([Symbol]) type expressions.
        if len(self.vrs) == 1:
            self._expanded = self.vrs[0].expand()
            return self._expanded

        # If product is already expanded, simply return the expansion.
        if self._expanded:
            return self._expanded

        # Sort variables such that we don't call the '*' operator more than we have to.
        float_syms = []
        sum_fracs = []
        for v in self.vrs:
            if v._prec in (0, 1):  # float or sym
                float_syms.append(v)
                continue
            exp = v.expand()

            # If the expanded expression is a float, sym or product,
            # we can add the variables.
            if exp._prec in (0, 1):  # float or sym
                float_syms.append(exp)
            elif exp._prec == 2:  # prod
                float_syms += exp.vrs
            else:
                sum_fracs.append(exp)
        # If we have floats or symbols add the symbols to the rest as a single
        # product (for speed).
        if len(float_syms) > 1:
            sum_fracs.append(create_product(float_syms))
        elif float_syms:
            sum_fracs.append(float_syms[0])

        # Use __mult__ to reduce list to one single variable.
        # TODO: Can this be done more efficiently without creating all the
        # intermediate variables?
        self._expanded = reduce(lambda x, y: x * y, sum_fracs)
        return self._expanded
Exemplo n.º 26
0
    def expand(self):
        "Expand all members of the product."
        # If we just have one variable, compute the expansion of it
        # (it is not a Product, so it should be safe). We need this to get
        # rid of Product([Symbol]) type expressions.
        if len(self.vrs) == 1:
            self._expanded = self.vrs[0].expand()
            return self._expanded

        # If product is already expanded, simply return the expansion.
        if self._expanded:
            return self._expanded

        # Sort variables such that we don't call the '*' operator more than we have to.
        float_syms = []
        sum_fracs = []
        for v in self.vrs:
            if v._prec in (0, 1): # float or sym
                float_syms.append(v)
                continue
            exp = v.expand()

            # If the expanded expression is a float, sym or product,
            # we can add the variables.
            if exp._prec in (0, 1): # float or sym
                float_syms.append(exp)
            elif exp._prec == 2: # prod
                float_syms += exp.vrs
            else:
                sum_fracs.append(exp)
        # If we have floats or symbols add the symbols to the rest as a single
        # product (for speed).
        if len(float_syms) > 1:
            sum_fracs.append( create_product(float_syms) )
        elif float_syms:
            sum_fracs.append(float_syms[0])

        # Use __mult__ to reduce list to one single variable.
        # TODO: Can this be done more efficiently without creating all the
        # intermediate variables?
        self._expanded = reduce(lambda x,y: x*y, sum_fracs)
        return self._expanded
Exemplo n.º 27
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)]
Exemplo n.º 28
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
Exemplo n.º 29
0
    def reduce_ops(self):
        "Reduce the number of operations needed to evaluate the sum."
        #        global ind
        #        ind += " "
        #        print "\n%sreduce_ops, start" % ind

        if self._reduced:
            return self._reduced
        # NOTE: Assuming that sum has already been expanded.
        # TODO: Add test for this and handle case if it is not.

        # TODO: The entire function looks expensive, can it be optimised?

        # TODO: It is not necessary to create a new Sum if we do not have more
        # than one Fraction.
        # First group all fractions in the sum.
        new_sum = _group_fractions(self)
        if new_sum._prec != 3:  # sum
            self._reduced = new_sum.reduce_ops()
            return self._reduced
        # Loop all variables of the sum and collect the number of common
        # variables that can be factored out.
        common_vars = {}
        for var in new_sum.vrs:
            # Get dictonary of occurrences and add the variable and the number
            # of occurrences to common dictionary.
            for k, v in var.get_var_occurrences().iteritems():
                #                print
                #                print ind + "var: ", var
                #                print ind + "k: ", k
                #                print ind + "v: ", v
                if k in common_vars:
                    common_vars[k].append((v, var))
                else:
                    common_vars[k] = [(v, var)]
#        print
#        print "common vars: "
#        for k,v in common_vars.items():
#            print "k: ", k
#            print "v: ", v
#        print
# Determine the maximum reduction for each variable
# sorted as: {(x*x*y, x*y*z, 2*y):[2, [y]]}.
        terms_reductions = {}
        for k, v in sorted(common_vars.iteritems()):
            #            print
            #            print ind + "k: ", k
            #            print ind + "v: ", v
            # If the number of expressions that can be reduced is only one
            # there is nothing to be done.
            if len(v) > 1:
                # TODO: Is there a better way to compute the reduction gain
                # and the number of occurrences we should remove?

                # Get the list of number of occurences of 'k' in expressions
                # in 'v'.
                occurrences = [t[0] for t in v]

                # Determine the favorable number of occurences and an estimate
                # of the maximum reduction for current variable.
                fav_occur = 0
                reduc = 0
                for i in set(occurrences):
                    # Get number of terms that has a number of occcurences equal
                    # to or higher than the current number.
                    num_terms = len([o for o in occurrences if o >= i])

                    # An estimate of the reduction in operations is:
                    # (number_of_terms - 1) * number_occurrences.
                    new_reduc = (num_terms - 1) * i
                    if new_reduc > reduc:
                        reduc = new_reduc
                        fav_occur = i

                # Extract the terms of v where the number of occurrences is
                # equal to or higher than the most favorable number of occurrences.
                terms = sorted([t[1] for t in v if t[0] >= fav_occur])

                # We need to reduce the expression with the favorable number of
                # occurrences of the current variable.
                red_vars = [k] * fav_occur

                # If the list of terms is already present in the dictionary,
                # add the reduction count and the variables.
                if tuple(terms) in terms_reductions:
                    terms_reductions[tuple(terms)][0] += reduc
                    terms_reductions[tuple(terms)][1] += red_vars
                else:
                    terms_reductions[tuple(terms)] = [reduc, red_vars]
#        print "\nterms_reductions: "
#        for k,v in terms_reductions.items():
#            print "k: ", create_sum(k)
#            print "v: ", v
#        print "red: self: ", self
        if terms_reductions:
            # Invert dictionary of terms.
            reductions_terms = dict([((v[0], tuple(v[1])), k)
                                     for k, v in terms_reductions.iteritems()])

            # Create a sorted list of those variables that give the highest
            # reduction.
            sorted_reduc_var = [k for k, v in reductions_terms.iteritems()]
            #            print
            #            print ind + "raw"
            #            for k in sorted_reduc_var:
            #                print ind, k[0], k[1]
            sorted_reduc_var.sort()
            #            sorted_reduc_var.sort(lambda x, y: cmp(x[0], y[0]))
            sorted_reduc_var.reverse()
            #            print ind + "sorted"
            #            for k in sorted_reduc_var:
            #                print ind, k[0], k[1]

            # Create a new dictionary of terms that should be reduced, if some
            # terms overlap, only pick the one which give the highest reduction to
            # ensure that a*x*x + b*x*x + x*x*y + 2*y -> x*x*(a + b + y) + 2*y NOT
            # x*x*(a + b) + y*(2 + x*x).
            reduction_vars = {}
            rejections = {}
            for var in sorted_reduc_var:
                terms = reductions_terms[var]
                if _overlap(terms, reduction_vars) or _overlap(
                        terms, rejections):
                    rejections[var[1]] = terms
                else:
                    reduction_vars[var[1]] = terms

#            print "\nreduction_vars: "
#            for k,v in reduction_vars.items():
#                print "k: ", k
#                print "v: ", v

# Reduce each set of terms with appropriate variables.
            all_reduced_terms = []
            reduced_expressions = []
            for reduc_var, terms in sorted(reduction_vars.iteritems()):

                # Add current terms to list of all variables that have been reduced.
                all_reduced_terms += list(terms)

                # Create variable that we will use to reduce the terms.
                reduction_var = None
                if len(reduc_var) > 1:
                    reduction_var = create_product(list(reduc_var))
                else:
                    reduction_var = reduc_var[0]

                # Reduce all terms that need to be reduced.
                reduced_terms = [t.reduce_var(reduction_var) for t in terms]

                # Create reduced expression.
                reduced_expr = None
                if len(reduced_terms) > 1:
                    # Try to reduce the reduced terms further.
                    reduced_expr = create_product([
                        reduction_var,
                        create_sum(reduced_terms).reduce_ops()
                    ])
                else:
                    reduced_expr = create_product(reduction_var,
                                                  reduced_terms[0])

                # Add reduced expression to list of reduced expressions.
                reduced_expressions.append(reduced_expr)

            # Create list of terms that should not be reduced.
            dont_reduce_terms = []
            for v in new_sum.vrs:
                if not v in all_reduced_terms:
                    dont_reduce_terms.append(v)

            # Create expression from terms that was not reduced.
            not_reduced_expr = None
            if dont_reduce_terms and len(dont_reduce_terms) > 1:
                # Try to reduce the remaining terms that were not reduced at first.
                not_reduced_expr = create_sum(dont_reduce_terms).reduce_ops()
            elif dont_reduce_terms:
                not_reduced_expr = dont_reduce_terms[0]

            # Create return expression.
            if not_reduced_expr:
                self._reduced = create_sum(reduced_expressions +
                                           [not_reduced_expr])
            elif len(reduced_expressions) > 1:
                self._reduced = create_sum(reduced_expressions)
            else:
                self._reduced = reduced_expressions[0]


#            # NOTE: Only switch on for debugging.
#            if not self._reduced.expand() == self.expand():
#                print reduced_expressions[0]
#                print reduced_expressions[0].expand()
#                print "self: ", self
#                print "red:  ", repr(self._reduced)
#                print "self.exp: ", self.expand()
#                print "red.exp:  ", self._reduced.expand()
#                error("Reduced expression is not equal to original expression.")
            return self._reduced

        # Return self if we don't have any variables for which we can reduce
        # the sum.
        self._reduced = self
        return self._reduced
Exemplo n.º 30
0
 def __sub__(self, other):
     "Subtract other objects."
     # Return a new sum
     return create_sum([self, create_product([FloatValue(-1), other])])
Exemplo n.º 31
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)]
Exemplo n.º 32
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()
Exemplo n.º 33
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
Exemplo n.º 34
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()