Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

A small edit to tmonteil's first anser does the trick. Need to define reverse multiplication and division (http://www.rafekettler.com/magicmethods.pdf):

class MYExp(sage.symbolic.expression.Expression):
def __sub__(self,other):
    if self.convert() == other.convert():
        return self
    else:
        raise TypeError('Subtraction of non-matching units')

def __add__(self,other):
    if self.convert() == other.convert():
        return self
    else:
        raise TypeError('Sum of non-matching units')
def __mul__(self,other):
        return MYExp(SR, super(MYExp, self).__mul__(other))
def __rmul__(self,other):
        return MYExp(SR, super(MYExp, self).__mul__(other))
def __div__(self,other):
    return MYExp(SR, super(MYExp, self).__div__(other))
def __rdiv__(self,other):
    return MYExp(SR, super(MYExp, self).__div__(other))
def __pow__(self,other):
    return MYExp(SR, super(MYExp, self).__pow__(other))

U = lambda x: MYExp(SR,x)
for s in units.length.trait_names():
       globals()[s] = U(getattr(units.length,s))

Now we can write:

meter - meter

meter

1*meter - 1*meter

meter

meter^2 - meter^2

meter^2

A small edit to tmonteil's first anser does the trick. Need to define reverse multiplication and division (http://www.rafekettler.com/magicmethods.pdf):

class MYExp(sage.symbolic.expression.Expression):
def __sub__(self,other):
    if self.convert() == other.convert():
        return self
    else:
        raise TypeError('Subtraction of non-matching units')

def __add__(self,other):
    if self.convert() == other.convert():
        return self
    else:
        raise TypeError('Sum of non-matching units')
def __mul__(self,other):
        return MYExp(SR, super(MYExp, self).__mul__(other))
def __rmul__(self,other):
        return MYExp(SR, super(MYExp, self).__mul__(other))
def __div__(self,other):
    return MYExp(SR, super(MYExp, self).__div__(other))
def __rdiv__(self,other):
    return MYExp(SR, super(MYExp, self).__div__(other))
def __pow__(self,other):
    return MYExp(SR, super(MYExp, self).__pow__(other))

U = lambda x: MYExp(SR,x)
for s in units.length.trait_names():
       globals()[s] = U(getattr(units.length,s))

Now we can write:

meter - meter

meter

1*meter - 1*meter

meter

meter^2 - meter^2

meter^2

However, note that external functions can still result in problems, e.g.

sqrt(meter) - sqrt(meter)

0

sin(meter) - sin(meter)

0

A small edit to tmonteil's first anser does answer allows handling multiplications and divisions in the trick. above framework. Need to define reverse multiplication and division (http://www.rafekettler.com/magicmethods.pdf):

class MYExp(sage.symbolic.expression.Expression):
def __sub__(self,other):
    if self.convert() == other.convert():
        return self
    else:
        raise TypeError('Subtraction of non-matching units')

def __add__(self,other):
    if self.convert() == other.convert():
        return self
    else:
        raise TypeError('Sum of non-matching units')
def __mul__(self,other):
        return MYExp(SR, super(MYExp, self).__mul__(other))
def __rmul__(self,other):
        return MYExp(SR, super(MYExp, self).__mul__(other))
def __div__(self,other):
    return MYExp(SR, super(MYExp, self).__div__(other))
def __rdiv__(self,other):
    return MYExp(SR, super(MYExp, self).__div__(other))
def __pow__(self,other):
    return MYExp(SR, super(MYExp, self).__pow__(other))

U = lambda x: MYExp(SR,x)
for s in units.length.trait_names():
       globals()[s] = U(getattr(units.length,s))

Now we can write:

meter - meter

meter

1*meter - 1*meter

meter

meter^2 - meter^2

meter^2

However, note that external functions can still result in problems, e.g.

sqrt(meter) - sqrt(meter)

0

sin(meter) - sin(meter)

0

Actually, I found a more practical way to automatically check for consistent units. Provided a dictionary containing all variables of eq as keys and each variable multiplied by its units as entries:

def units_check(eq, i0 = 1):
'''
Checks whether all arguments are keys in udict and returns simplified units
If units cancel out on one side while they should not, set i0=2 or something other than 1.
Example:
sage: var('a b c d')
sage: udict = {a: a*units.length.meter^3, b: b*units.length.meter, c: c*units.length.meter, d: d*units.length.meter}
sage: eq = a == b*c*d
sage: units_check(eq)
meter^3 == meter^3
'''
valdict = {}
i = i0
for arg in eq.arguments():
    valdict[arg] = i
    i = i + 1
    udict[arg]
eq_val = eq.subs(valdict)
return (eq.subs(udict).subs(valdict))/eq_val