Opened 12 years ago

Closed 12 years ago

# 0**0 = 1 for number fields, matrices, and more

Reported by: Owned by: Marco Streng Alex Ghitza major sage-4.7 basic arithmetic 0, power, pow, number field, matrix, convention, python sage-4.7.alpha2 Marco Streng Robert Bradshaw N/A

### Description

Python has the convention `x**0 = 1` even for `x=0`. Sage follows this convention in some cases, but raises an error in others. This patch generalizes Python's convention to `sage.structure.element.generic_power_c`, which includes number field elements and matrices. This fixes inconsistent behaviour for matrices and a bug for number fields (see examples below). See also this discussion.

Example 1:

```sage: K.<a> = NumberField(x^4+x+1)
sage: r = [ZZ,QQ,RR,CC,int,float,K]
sage: N = len(r)^2; N
49
sage: for a in r:
....:     for b in r:
....:         try:
....:             if a(0)^b(0) != 1:
....:                 (a,b,a(0)^b(0))
....:                 N = N - 1
....:         except (ArithmeticError, TypeError, AttributeError, RuntimeError) as e:
....:             (a,b,e)
....:             N = N - 1
....:
(Integer Ring, Complex Field with 53 bits of precision, NaN - NaN*I)
(Integer Ring, Number Field in a with defining polynomial x^4 + x + 1, ArithmeticError('0^0 is undefined.',))
(Rational Field, Complex Field with 53 bits of precision, NaN - NaN*I)
(Rational Field, Number Field in a with defining polynomial x^4 + x + 1, ArithmeticError('0^0 is undefined.',))
Exception RuntimeError: 'maximum recursion depth exceeded in __subclasscheck__' in <type 'exceptions.TypeError'> ignored
Exception RuntimeError: 'maximum recursion depth exceeded in __subclasscheck__' in <type 'exceptions.TypeError'> ignored
Exception RuntimeError: 'maximum recursion depth exceeded while calling a Python object' in <type 'exceptions.GeneratorExit'> ignored
Exception RuntimeError: 'maximum recursion depth exceeded while calling a Python object' in <type 'exceptions.GeneratorExit'> ignored
Exception GeneratorExit in <generator object <genexpr> at 0x6d2e1e0> ignored
(Real Field with 53 bits of precision, Number Field in a with defining polynomial x^4 + x + 1, RuntimeError('maximum recursion depth exceeded while calling a Python object',))
(Complex Field with 53 bits of precision, Integer Ring, ArithmeticError('0^0 is undefined.',))
(Complex Field with 53 bits of precision, Rational Field, NaN - NaN*I)
(Complex Field with 53 bits of precision, Real Field with 53 bits of precision, NaN - NaN*I)
(Complex Field with 53 bits of precision, Complex Field with 53 bits of precision, NaN - NaN*I)
(Complex Field with 53 bits of precision, <type 'int'>, ArithmeticError('0^0 is undefined.',))
(Complex Field with 53 bits of precision, <type 'float'>, NaN - NaN*I)
(Complex Field with 53 bits of precision, Number Field in a with defining polynomial x^4 + x + 1, TypeError(<type 'sage.rings.complex_number.ComplexNumber'>,))
(<type 'int'>, Real Field with 53 bits of precision, TypeError("unsupported operand type(s) for ** or pow(): 'int' and 'sage.rings.real_mpfr.RealNumber'",))
(<type 'int'>, Complex Field with 53 bits of precision, AttributeError("'int' object has no attribute 'log'",))
(<type 'int'>, Number Field in a with defining polynomial x^4 + x + 1, TypeError('An embedding into RR or CC must be specified.',))
(<type 'float'>, Complex Field with 53 bits of precision, AttributeError("'float' object has no attribute 'log'",))
(<type 'float'>, Number Field in a with defining polynomial x^4 + x + 1, TypeError('An embedding into RR or CC must be specified.',))
(Number Field in a with defining polynomial x^4 + x + 1, Integer Ring, ArithmeticError('0^0 is undefined.',))
(Number Field in a with defining polynomial x^4 + x + 1, Rational Field, ArithmeticError('0^0 is undefined.',))
(Number Field in a with defining polynomial x^4 + x + 1, Real Field with 53 bits of precision, ArithmeticError('0^0 is undefined.',))
(Number Field in a with defining polynomial x^4 + x + 1, Complex Field with 53 bits of precision, TypeError('An embedding into RR or CC must be specified.',))
(Number Field in a with defining polynomial x^4 + x + 1, <type 'int'>, ArithmeticError('0^0 is undefined.',))
(Number Field in a with defining polynomial x^4 + x + 1, <type 'float'>, ArithmeticError('0^0 is undefined.',))
(Number Field in a with defining polynomial x^4 + x + 1, Number Field in a with defining polynomial x^4 + x + 1, ArithmeticError('0^0 is undefined.',))
sage: N
25
```

So in about half of these cases (25/49) we follow Python's convention `0^0=1` and the other half of the time we get an assortment of errors and `NaN`. This patch removes the 10 occurrences of `ArithmeticError` from the list. Other tickets can deal with the other cases.

Example 2:

```sage: a = Matrix([[1,0],[0,0]]); a
[1 0]
[0 0]
b = Matrix([[0,0],[0,0]]); b
[0 0]
[0 0]
sage: a^0
[1 0]
[0 1]
sage: b^0
ArithmeticError: 0^0 is undefined.
```

Here Sage finds `a!=0`, and concludes `a^0 = a.parent()(1)`. Note that the lower right entry of `a^0` should be `0^0`, and is `1`. Next, Sage finds `b==0`, and concludes that `b^0` is illegal. Both the upper left and lower right entry of `b` are the same as the lower right entry of `a`, so the behaviour of `a^0` and `b^0` in Sage is inconsistent: either both should yield an `ArithmeticError` or neither should. The patch adopts Python's convention so that the output of `b^0` is the 2x2 identity matrix and this example becomes consistent.

Example 3:

```sage: K.<a> = NumberField(x^3+x+1)
sage: K.relativize(1, 'c')
Number Field in c0 with defining polynomial x^3 + x + 1 over its base field
sage: K.relativize(0, 'c')
ArithmeticError: 0^0 is undefined.
```

This is explained by

```sage: NumberField(x-1, 'a').power_basis()
[1]
sage: NumberField(x, 'a').power_basis()
ArithmeticError: 0^0 is undefined.
```

And it gets worse: a number field with defining polynomial `x` is part of the output of `K.subfields()`

```sage: s = K.subfields()
sage: for i in [1,0]:
....:     K.relativize(s[i][1], 'c')
....:
Number Field in c0 with defining polynomial x - a1 over its base field
ArithmeticError: 0^0 is undefined.
```

This last example is clearly a bug. An automatic consequence of generalizing Python's convention `0^0=1` is that this bug vanishes.

Interestingly, the patch adds no code.

### comment:1 Changed 12 years ago by Marco Streng

Status: new → needs_review

### comment:2 Changed 12 years ago by Robert Bradshaw

Status: needs_review → positive_review

### comment:3 Changed 12 years ago by Jeroen Demeyer

Milestone: → sage-4.7