Polinômios¶
Nesta seção vamos ilustrar como criar e usar polinômios no Sage.
Polinômios em Uma Variável¶
Existem três formas de criar anéis de polinômios.
sage: R = PolynomialRing(QQ, 't')
sage: R
Univariate Polynomial Ring in t over Rational Field
>>> from sage.all import *
>>> R = PolynomialRing(QQ, 't')
>>> R
Univariate Polynomial Ring in t over Rational Field
R = PolynomialRing(QQ, 't') R
Esse comando cria um anel de polinômios e diz para o Sage usar a letra
‘t’ para representar a variável indeterminada quando imprimir na tela.
Todavia, isso não define o símbolo t
para uso no Sage, logo você
não pode usá-lo para definir um polinômio (como \(t^2+1\))
pertencente a R
.
Uma forma alternativa é
sage: S = QQ['t']
sage: S == R
True
>>> from sage.all import *
>>> S = QQ['t']
>>> S == R
True
S = QQ['t'] S == R
As mesmas observações com respeito a t
valem também nesse caso.
Uma terceira e conveniente forma de definir polinômios é
sage: R.<t> = PolynomialRing(QQ)
>>> from sage.all import *
>>> R = PolynomialRing(QQ, names=('t',)); (t,) = R._first_ngens(1)
R.<t> = PolynomialRing(QQ)
ou
sage: R.<t> = QQ['t']
>>> from sage.all import *
>>> R = QQ['t']; (t,) = R._first_ngens(1)
R.<t> = QQ['t']
ou ainda
sage: R.<t> = QQ[]
>>> from sage.all import *
>>> R = QQ['t']; (t,) = R._first_ngens(1)
R.<t> = QQ[]
Isso tem o efeito colateral de definir a variável t
como a
variável indeterminada do anel de polinômios, logo você pode
facilmente construir elementos de R
da seguinte forma. (Note que
essa terceira alternativa é muito semelhante à notação usada em Magma,
e da mesma forma que no Magma ela pode ser usada para diversos tipos
de objetos.)
sage: poly = (t+1) * (t+2); poly
t^2 + 3*t + 2
sage: poly in R
True
>>> from sage.all import *
>>> poly = (t+Integer(1)) * (t+Integer(2)); poly
t^2 + 3*t + 2
>>> poly in R
True
poly = (t+1) * (t+2); poly poly in R
Qualquer que seja o método usado para definir um anel de polinômios, você pode recuperar a variável indeterminada como o \(0\)-ésimo gerador:
sage: R = PolynomialRing(QQ, 't')
sage: t = R.0
sage: t in R
True
>>> from sage.all import *
>>> R = PolynomialRing(QQ, 't')
>>> t = R.gen(0)
>>> t in R
True
R = PolynomialRing(QQ, 't') t = R.0 t in R
Note que uma construção similar funciona com os números complexos: os
números complexos podem ser vistos como sendo gerados pelo símbolo
i
sobre os números reais; logo temos o seguinte:
sage: CC
Complex Field with 53 bits of precision
sage: CC.0 # 0th generator of CC
1.00000000000000*I
>>> from sage.all import *
>>> CC
Complex Field with 53 bits of precision
>>> CC.gen(0) # 0th generator of CC
1.00000000000000*I
CC CC.0 # 0th generator of CC
Para anel de polinômios, você pode obter tanto o anel como o seu gerador, ou somente o gerador, no momento em que o anel for criado, da seguinte forma:
sage: R, t = QQ['t'].objgen()
sage: t = QQ['t'].gen()
sage: R, t = objgen(QQ['t'])
sage: t = gen(QQ['t'])
>>> from sage.all import *
>>> R, t = QQ['t'].objgen()
>>> t = QQ['t'].gen()
>>> R, t = objgen(QQ['t'])
>>> t = gen(QQ['t'])
R, t = QQ['t'].objgen() t = QQ['t'].gen() R, t = objgen(QQ['t']) t = gen(QQ['t'])
Finalmente apresentamos um pouco de aritmética em \(\QQ[t]\).
sage: R, t = QQ['t'].objgen()
sage: f = 2*t^7 + 3*t^2 - 15/19
sage: f^2
4*t^14 + 12*t^9 - 60/19*t^7 + 9*t^4 - 90/19*t^2 + 225/361
sage: cyclo = R.cyclotomic_polynomial(7); cyclo
t^6 + t^5 + t^4 + t^3 + t^2 + t + 1
sage: g = 7 * cyclo * t^5 * (t^5 + 10*t + 2)
sage: g
7*t^16 + 7*t^15 + 7*t^14 + 7*t^13 + 77*t^12 + 91*t^11 + 91*t^10 + 84*t^9
+ 84*t^8 + 84*t^7 + 84*t^6 + 14*t^5
sage: F = factor(g); F
(7) * t^5 * (t^5 + 10*t + 2) * (t^6 + t^5 + t^4 + t^3 + t^2 + t + 1)
sage: F.unit()
7
sage: list(F)
[(t, 5), (t^5 + 10*t + 2, 1), (t^6 + t^5 + t^4 + t^3 + t^2 + t + 1, 1)]
>>> from sage.all import *
>>> R, t = QQ['t'].objgen()
>>> f = Integer(2)*t**Integer(7) + Integer(3)*t**Integer(2) - Integer(15)/Integer(19)
>>> f**Integer(2)
4*t^14 + 12*t^9 - 60/19*t^7 + 9*t^4 - 90/19*t^2 + 225/361
>>> cyclo = R.cyclotomic_polynomial(Integer(7)); cyclo
t^6 + t^5 + t^4 + t^3 + t^2 + t + 1
>>> g = Integer(7) * cyclo * t**Integer(5) * (t**Integer(5) + Integer(10)*t + Integer(2))
>>> g
7*t^16 + 7*t^15 + 7*t^14 + 7*t^13 + 77*t^12 + 91*t^11 + 91*t^10 + 84*t^9
+ 84*t^8 + 84*t^7 + 84*t^6 + 14*t^5
>>> F = factor(g); F
(7) * t^5 * (t^5 + 10*t + 2) * (t^6 + t^5 + t^4 + t^3 + t^2 + t + 1)
>>> F.unit()
7
>>> list(F)
[(t, 5), (t^5 + 10*t + 2, 1), (t^6 + t^5 + t^4 + t^3 + t^2 + t + 1, 1)]
R, t = QQ['t'].objgen() f = 2*t^7 + 3*t^2 - 15/19 f^2 cyclo = R.cyclotomic_polynomial(7); cyclo g = 7 * cyclo * t^5 * (t^5 + 10*t + 2) g F = factor(g); F F.unit() list(F)
Note que a fatorização corretamente leva em conta e armazena a parte unitária.
Se você fosse usar, por exemplo, a função R.cyclotomic_polynomial
intensamente para algum projeto de pesquisa, além de citar o Sage,
você deveria tentar descobrir qual componente do Sage é de fato usado
para calcular esses polinômios, e citá-lo também. Nesse caso, se você
digitar R.cyclotomic_polynomial??
para ver o código fonte, você
irá facilmente ver uma linha f = pari.polcyclo(n)
o que significa
que o PARI é usado para o cálculo dos polinômios ciclotrômicos. Cite o
PARI também no seu trabalho.
Dividindo dois polinômios cria-se um elemento do corpo de frações (o qual o Sage cria automaticamente).
sage: x = QQ['x'].0
sage: f = x^3 + 1; g = x^2 - 17
sage: h = f/g; h
(x^3 + 1)/(x^2 - 17)
sage: h.parent()
Fraction Field of Univariate Polynomial Ring in x over Rational Field
>>> from sage.all import *
>>> x = QQ['x'].gen(0)
>>> f = x**Integer(3) + Integer(1); g = x**Integer(2) - Integer(17)
>>> h = f/g; h
(x^3 + 1)/(x^2 - 17)
>>> h.parent()
Fraction Field of Univariate Polynomial Ring in x over Rational Field
x = QQ['x'].0 f = x^3 + 1; g = x^2 - 17 h = f/g; h h.parent()
Usando-se a série de Laurent, pode-se calcular a expansão em série no
corpo de frações de QQ[x]
:
sage: R.<x> = LaurentSeriesRing(QQ); R
Laurent Series Ring in x over Rational Field
sage: 1/(1-x) + O(x^10)
1 + x + x^2 + x^3 + x^4 + x^5 + x^6 + x^7 + x^8 + x^9 + O(x^10)
>>> from sage.all import *
>>> R = LaurentSeriesRing(QQ, names=('x',)); (x,) = R._first_ngens(1); R
Laurent Series Ring in x over Rational Field
>>> Integer(1)/(Integer(1)-x) + O(x**Integer(10))
1 + x + x^2 + x^3 + x^4 + x^5 + x^6 + x^7 + x^8 + x^9 + O(x^10)
R.<x> = LaurentSeriesRing(QQ); R 1/(1-x) + O(x^10)
Se nomearmos a variável de outra forma, obtemos um anel de polinômios em uma variável diferente.
sage: R.<x> = PolynomialRing(QQ)
sage: S.<y> = PolynomialRing(QQ)
sage: x == y
False
sage: R == S
False
sage: R(y)
x
sage: R(y^2 - 17)
x^2 - 17
>>> from sage.all import *
>>> R = PolynomialRing(QQ, names=('x',)); (x,) = R._first_ngens(1)
>>> S = PolynomialRing(QQ, names=('y',)); (y,) = S._first_ngens(1)
>>> x == y
False
>>> R == S
False
>>> R(y)
x
>>> R(y**Integer(2) - Integer(17))
x^2 - 17
R.<x> = PolynomialRing(QQ) S.<y> = PolynomialRing(QQ) x == y R == S R(y) R(y^2 - 17)
O anel é determinado pela variável. Note que criar um outro anel com
variável indeterminada x
não retorna um anel diferente.
sage: R = PolynomialRing(QQ, "x")
sage: T = PolynomialRing(QQ, "x")
sage: R == T
True
sage: R is T
True
sage: R.0 == T.0
True
>>> from sage.all import *
>>> R = PolynomialRing(QQ, "x")
>>> T = PolynomialRing(QQ, "x")
>>> R == T
True
>>> R is T
True
>>> R.gen(0) == T.gen(0)
True
R = PolynomialRing(QQ, "x") T = PolynomialRing(QQ, "x") R == T R is T R.0 == T.0
O Sage também possui suporte para séries de potências e séries de Laurent sobre um anel arbitrário. No seguinte exemplo, nós criamos um elemento de \(\GF{7}[[T]]\) e dividimos para criar um elemento de \(\GF{7}((T))\).
sage: R.<T> = PowerSeriesRing(GF(7)); R
Power Series Ring in T over Finite Field of size 7
sage: f = T + 3*T^2 + T^3 + O(T^4)
sage: f^3
T^3 + 2*T^4 + 2*T^5 + O(T^6)
sage: 1/f
T^-1 + 4 + T + O(T^2)
sage: parent(1/f)
Laurent Series Ring in T over Finite Field of size 7
>>> from sage.all import *
>>> R = PowerSeriesRing(GF(Integer(7)), names=('T',)); (T,) = R._first_ngens(1); R
Power Series Ring in T over Finite Field of size 7
>>> f = T + Integer(3)*T**Integer(2) + T**Integer(3) + O(T**Integer(4))
>>> f**Integer(3)
T^3 + 2*T^4 + 2*T^5 + O(T^6)
>>> Integer(1)/f
T^-1 + 4 + T + O(T^2)
>>> parent(Integer(1)/f)
Laurent Series Ring in T over Finite Field of size 7
R.<T> = PowerSeriesRing(GF(7)); R f = T + 3*T^2 + T^3 + O(T^4) f^3 1/f parent(1/f)
Você também pode criar anéis de polinômios usando a notação de colchetes duplos:
sage: GF(7)[['T']]
Power Series Ring in T over Finite Field of size 7
>>> from sage.all import *
>>> GF(Integer(7))[['T']]
Power Series Ring in T over Finite Field of size 7
GF(7)[['T']]
Polinômios em Mais De Uma Variável¶
Para trabalhar com polinômios em várias variáveis, nós primeiro declaramos o anel de polinômios e as variáveis.
sage: R = PolynomialRing(GF(5),3,"z") # here, 3 = number of variables
sage: R
Multivariate Polynomial Ring in z0, z1, z2 over Finite Field of size 5
>>> from sage.all import *
>>> R = PolynomialRing(GF(Integer(5)),Integer(3),"z") # here, 3 = number of variables
>>> R
Multivariate Polynomial Ring in z0, z1, z2 over Finite Field of size 5
R = PolynomialRing(GF(5),3,"z") # here, 3 = number of variables R
Da mesma forma como ocorre com polinômios em uma variável, existem três maneiras de fazer isso:
sage: GF(5)['z0, z1, z2']
Multivariate Polynomial Ring in z0, z1, z2 over Finite Field of size 5
sage: R.<z0,z1,z2> = GF(5)[]; R
Multivariate Polynomial Ring in z0, z1, z2 over Finite Field of size 5
>>> from sage.all import *
>>> GF(Integer(5))['z0, z1, z2']
Multivariate Polynomial Ring in z0, z1, z2 over Finite Field of size 5
>>> R = GF(Integer(5))['z0, z1, z2']; (z0, z1, z2,) = R._first_ngens(3); R
Multivariate Polynomial Ring in z0, z1, z2 over Finite Field of size 5
GF(5)['z0, z1, z2'] R.<z0,z1,z2> = GF(5)[]; R
Se você quiser usar os nomes das variáveis com apenas uma letra, então você pode usar os seguinte comando:
sage: PolynomialRing(GF(5), 3, 'xyz')
Multivariate Polynomial Ring in x, y, z over Finite Field of size 5
>>> from sage.all import *
>>> PolynomialRing(GF(Integer(5)), Integer(3), 'xyz')
Multivariate Polynomial Ring in x, y, z over Finite Field of size 5
PolynomialRing(GF(5), 3, 'xyz')
A seguir fazemos um pouco de aritmética.
sage: z = GF(5)['z0, z1, z2'].gens()
sage: z
(z0, z1, z2)
sage: (z[0]+z[1]+z[2])^2
z0^2 + 2*z0*z1 + z1^2 + 2*z0*z2 + 2*z1*z2 + z2^2
>>> from sage.all import *
>>> z = GF(Integer(5))['z0, z1, z2'].gens()
>>> z
(z0, z1, z2)
>>> (z[Integer(0)]+z[Integer(1)]+z[Integer(2)])**Integer(2)
z0^2 + 2*z0*z1 + z1^2 + 2*z0*z2 + 2*z1*z2 + z2^2
z = GF(5)['z0, z1, z2'].gens() z (z[0]+z[1]+z[2])^2
Você também pode usar uma notação mais matemática para criar um anel de polinômios.
sage: R = GF(5)['x,y,z']
sage: x,y,z = R.gens()
sage: QQ['x']
Univariate Polynomial Ring in x over Rational Field
sage: QQ['x,y'].gens()
(x, y)
sage: QQ['x'].objgens()
(Univariate Polynomial Ring in x over Rational Field, (x,))
>>> from sage.all import *
>>> R = GF(Integer(5))['x,y,z']
>>> x,y,z = R.gens()
>>> QQ['x']
Univariate Polynomial Ring in x over Rational Field
>>> QQ['x,y'].gens()
(x, y)
>>> QQ['x'].objgens()
(Univariate Polynomial Ring in x over Rational Field, (x,))
R = GF(5)['x,y,z'] x,y,z = R.gens() QQ['x'] QQ['x,y'].gens() QQ['x'].objgens()
Polinômios em mais de uma variável são implementados no Sage usando dicionários em Python e a “representação distribuída” de um polinômio. O Sage usa o Singular [Si], por exemplo, para o cálculo do maior divisor comum e bases de Gröbner para ideais algébricos.
sage: R, (x, y) = PolynomialRing(RationalField(), 2, 'xy').objgens()
sage: f = (x^3 + 2*y^2*x)^2
sage: g = x^2*y^2
sage: f.gcd(g)
x^2
>>> from sage.all import *
>>> R, (x, y) = PolynomialRing(RationalField(), Integer(2), 'xy').objgens()
>>> f = (x**Integer(3) + Integer(2)*y**Integer(2)*x)**Integer(2)
>>> g = x**Integer(2)*y**Integer(2)
>>> f.gcd(g)
x^2
R, (x, y) = PolynomialRing(RationalField(), 2, 'xy').objgens() f = (x^3 + 2*y^2*x)^2 g = x^2*y^2 f.gcd(g)
A seguir criamos o ideal \((f,g)\) gerado por \(f\) e
\(g\), simplesmente multiplicando (f,g)
por R
(nós
poderíamos também escrever ideal([f,g])
ou ideal(f,g)
).
sage: I = (f, g)*R; I
Ideal (x^6 + 4*x^4*y^2 + 4*x^2*y^4, x^2*y^2) of Multivariate Polynomial
Ring in x, y over Rational Field
sage: B = I.groebner_basis(); B
[x^6, x^2*y^2]
sage: x^2 in I
False
>>> from sage.all import *
>>> I = (f, g)*R; I
Ideal (x^6 + 4*x^4*y^2 + 4*x^2*y^4, x^2*y^2) of Multivariate Polynomial
Ring in x, y over Rational Field
>>> B = I.groebner_basis(); B
[x^6, x^2*y^2]
>>> x**Integer(2) in I
False
I = (f, g)*R; I B = I.groebner_basis(); B x^2 in I
A base de Gröbner acima não é uma lista mas sim uma sequência imutável. Isso implica que ela possui universo (universe) e parente (parent), e não pode ser modificada (o que é bom pois ocasionaria erros em outras rotinas que usam bases de Gröbner).
sage: B.universe()
Multivariate Polynomial Ring in x, y over Rational Field
sage: B[1] = x
Traceback (most recent call last):
...
ValueError: object is immutable; please change a copy instead.
>>> from sage.all import *
>>> B.universe()
Multivariate Polynomial Ring in x, y over Rational Field
>>> B[Integer(1)] = x
Traceback (most recent call last):
...
ValueError: object is immutable; please change a copy instead.
B.universe() B[1] = x
Um pouco (não tanto quanto gostaríamos) de álgebra comutativa está disponível no Sage, implementado via Singular. Por exemplo, podemos calcular a decomposição primaria e primos associados de \(I\):
sage: I.primary_decomposition()
[Ideal (x^2) of Multivariate Polynomial Ring in x, y over Rational Field,
Ideal (y^2, x^6) of Multivariate Polynomial Ring in x, y over Rational Field]
sage: I.associated_primes()
[Ideal (x) of Multivariate Polynomial Ring in x, y over Rational Field,
Ideal (y, x) of Multivariate Polynomial Ring in x, y over Rational Field]
>>> from sage.all import *
>>> I.primary_decomposition()
[Ideal (x^2) of Multivariate Polynomial Ring in x, y over Rational Field,
Ideal (y^2, x^6) of Multivariate Polynomial Ring in x, y over Rational Field]
>>> I.associated_primes()
[Ideal (x) of Multivariate Polynomial Ring in x, y over Rational Field,
Ideal (y, x) of Multivariate Polynomial Ring in x, y over Rational Field]
I.primary_decomposition() I.associated_primes()