.. -*- coding: utf-8 -*- .. _section-coercion: ============================ FamÃlias, Conversão e Coação ============================ Esta seção pode parecer mais técnica do que as anteriores, mas acreditamos que é importante entender o significado de famÃlias e coação de modo a usar anéis e outras estruturas algébricas no Sage de forma efetiva e eficiente. Note que vamos explicar algumas noções, mas não vamos mostrar aqui como implementá-las. Um tutorial voltado à implementação está disponÃvel (em inglês) como um `arquivo Sage <http://flask.sagenb.org/home/pub/82/>`_. Elementos --------- Caso se queira implementar um anel em Python, uma primeira aproximação seria criar uma classe para os elementos ``X`` do anel e adicionar os requeridos métodos (com underscores duplos) ``__add__``, ``__sub``, ``__mul__``, obviamente garantindo que os axiomas de anel são verificados. Como o Python é uma linguagem de tipagem forte (ainda que de tipagem dinâmica), poderia-se, pelo menos a princÃpio, esperar-se que fosse implementado em Python uma classe para cada anel. No final das contas, o Python contém um tipo ``<int>`` para os inteiros, um tipo ``<float>`` para os reais, e assim por diante. Mas essa estratégia logo encontra uma limitação: Existe um número infinito de anéis, e não se pode implementar um número infinito de classes. Em vez disso, poderia-se criar uma hierarquia de classes projetada para implementar elementos de estruturas algébricas ubÃquas, tais como grupos, anéis, anéis comutativos, corpos, álgebras, e assim por diante. Mas isso significa que elementos de anéis bastante diferentes podem ter o mesmo tipo. :: sage: P.<x,y> = GF(3)[] sage: Q.<a,b> = GF(4,'z')[] sage: type(x)==type(a) True Por outro lado, poderia-se ter também classes diferentes em Python fornecendo implementações diferentes da mesma estrutura matemática (por exemplo, matrizes densas versus matrizes esparsas). :: sage: P.<a> = PolynomialRing(ZZ) sage: Q.<b> = PolynomialRing(ZZ, sparse=True) sage: R.<c> = PolynomialRing(ZZ, implementation='NTL') sage: type(a); type(b); type(c) <type 'sage.rings.polynomial.polynomial_integer_dense_flint.Polynomial_integer_dense_flint'> <class 'sage.rings.polynomial.polynomial_element_generic.Polynomial_generic_sparse'> <type 'sage.rings.polynomial.polynomial_integer_dense_ntl.Polynomial_integer_dense_ntl'> Isso apresenta dois problemas: Por um lado, se tivéssemos elementos que são duas instancias da mesma classe, então poderia-se esperar que o método ``__add__`` dessas classes permitisse somá-los; mas não se deseja isso, se os elementos pertencem a anéis bastante diferentes. Por outro lado, se possui-se elementos que pertencem a implementações diferentes do mesmo anel, então gostaria-se de somá-los, mas isso não pode ser feito diretamente se eles pertencem a classes diferentes em Python. A solução para esses problemas é chamada coação e será explicada a seguir. Todavia, é essencial que cada elemento saiba a qual pertence. Isso está disponÃvel através método ``parent()``: .. link :: sage: a.parent(); b.parent(); c.parent() Univariate Polynomial Ring in a over Integer Ring Sparse Univariate Polynomial Ring in b over Integer Ring Univariate Polynomial Ring in c over Integer Ring (using NTL) FamÃlias e Categorias --------------------- De forma similar à hierarquia de classes em Python voltada para elementos de estruturas algébricas, o Sage também fornece classes para as estruturas algébricas que contém esses elementos. Estruturas contendo elementos são chamadas "estruturas parente" no Sage, e existe uma classe básica para elas. Paralelamente à hierarquia de noções matemáticas, tem-se uma hierarquia de classes, a saber, para conjuntos, anéis, corpos e assim por diante: :: sage: isinstance(QQ,Field) True sage: isinstance(QQ, Ring) True sage: isinstance(ZZ,Field) False sage: isinstance(ZZ, Ring) True Em álgebra, objetos que compartilham o mesmo tipo de estruturas algébricas são agrupados nas assim chamadas "categorias". Logo, existe uma analogia aproximada entre a hierarquia de classes em Sage e a hierarquia de categorias. Todavia, essa analogia de classes em Python e categorias não deve ser enfatizada demais. No final das contas, categorias matemáticas também são implementadas no Sage: :: sage: Rings() Category of rings sage: ZZ.category() Category of euclidean domains sage: ZZ.category().is_subcategory(Rings()) True sage: ZZ in Rings() True sage: ZZ in Fields() False sage: QQ in Fields() True Enquanto a hierarquia de classes no Sage é centrada nos detalhes de implementação, a construção de categorias em Sage é mais centrada na estrutura matemática. à possÃvel implementar métodos e testes gerais independentemente de uma implementação especÃfica nas categorias. Estruturas da mesma famÃlia em Sage são supostamente objetos únicos em Python. Por exemplo, uma vez que um anel de polinômios sobre um certo anel base e com uma certa lista de geradores é criada, o resultado é arquivado: :: sage: RR['x','y'] is RR['x','y'] True Tipos versus Parentes --------------------- O tipo ``RingElement`` não deve ser confundido com a noção matemática de elemento de anel; por razões práticas, as vezes um objeto é uma instancia de ``RingElement`` embora ele não pertence a um anel: :: sage: M = Matrix(ZZ,2,3); M [0 0 0] [0 0 0] sage: isinstance(M, RingElement) True Enquanto *famÃlias* são únicas, elementos iguais de uma famÃlia em Sage não são necessariamente idênticos. Isso contrasta com o comportamento do Python para alguns (embora não todos) inteiros: :: sage: int(1) is int(1) # Python int True sage: int(-15) is int(-15) False sage: 1 is 1 # Sage Integer False à importante observar que elementos de anéis diferentes em geral não podem ser distinguidos pelos seus tipos, mas sim por sua famÃlia: :: sage: a = GF(2)(1) sage: b = GF(5)(1) sage: type(a) is type(b) True sage: parent(a) Finite Field of size 2 sage: parent(b) Finite Field of size 5 Logo, de um ponto de vista algébrico, **o parente de um elemento é mais importante do que seu tipo.** Conversão versus Coação ----------------------- Em alguns casos é possÃvel converter um elemento de uma estrutura parente em um elemento de uma outra estrutura parente. Tal conversão pode ser tanto explÃcita como implÃcita (essa é chamada *coação*). O leitor pode conhecer as noções de *conversão de tipo* e *coação de tipo* como na linguagem C, por exemplo. Existem noções de *conversão* e *coação* em Sage também. Mas as noções em Sage são centradas em *famÃlia*, não em tipos. Então, por favor não confunda conversão de tipo em C com conversão em Sage! Aqui se encontra uma breve apresentação. Para uma descrição detalhada e informações sobre a implementação, referimos à seção sobre coação no manual de referência e para o `arquivo tutorial <http://flask.sagenb.org/home/pub/82/>`_. Existem duas possibilidades extremas com respeito à possibilidade de fazer aritmética com elementos de *anéis diferentes*: * Anéis diferentes são mundos diferentes, e não faz nenhum sentido somar ou multiplicar elementos de anéis diferentes; mesmo ``1 + 1/2`` não faz sentido, pois o primeiro somando é um inteiro e o segundo um racional. Ou * Se um elemento ``r1`` de uma aner ``R1`` pode de alguma forma ser interpretado em um outro anel ``R2``, então todas as operações aritméticas envolvendo ``r1`` e qualquer elemento de ``R2`` são permitidas. O elemento neutro da multiplicação existe em todos os corpos e em vários anéis, e eles devem ser todos iguais. O Sage faz uma concessão. Se ``P1`` e ``P2`` são estruturas da mesma famÃlia e ``p1`` é um elemento de ``P1``, então o usuário pode explicitamente perguntar por uma interpretação de ``p1`` em ``P2``. Isso pode não fazer sentido em todos os casos ou não estar definido para todos os elementos de ``P1``, e fica a cargo do usuário assegurar que isso faz sentido. Nos referimos a isso como **conversão**: :: sage: a = GF(2)(1) sage: b = GF(5)(1) sage: GF(5)(a) == b True sage: GF(2)(b) == a True Todavia, uma conversão *implÃcita* (ou automática) ocorrerá apenas se puder ser feita *completamente* e *consistentemente*. Rigor matemático é essencial nesse ponto. Uma tal conversão implÃcita é chamada **coação**. Se coação for definida, então deve coincidir com conversão. Duas condições devem ser satisfeitas para uma coação ser definida: #. Uma coação de ``P1`` para ``P2`` deve ser dada por uma estrutura que preserva mapeamentos (por exemplo, um homomorfismo de anéis). Não é suficiente que *alguns* elementos de ``P1`` possam ser mapeados em ``P2``, e o mapa deve respeitar a estrutura algébrica de ``P1``. #. A escolha desses mapas de coação deve ser consistente: Se ``P3`` é uma terceira estrutura parente, então a composição da coação adotada de ``P1`` para ``P2`` com a coação de ``P2`` para ``P3`` deve coincidir com a coação adotada de ``P1`` para ``P3``. Em particular, se existir uma coação de ``P1`` para ``P2`` e ``P2`` para ``P1``, a composição deve ser o mapa identidade em ``P1``. Logo, embora é possÃvel converter cada elemento de ``GF(2)`` para ``GF(5)``, não há coação, pois não existe homomorfismo de anel entre ``GF(2)`` e ``GF(5)``. O segundo aspecto - consistência - é um pouco mais difÃcil de explicar. Vamos ilustrá-lo usando anéis de polinômios em mais de uma variável. Em aplicações, certamente faz mais sentido ter coações que preservam nomes. Então temos: :: sage: R1.<x,y> = ZZ[] sage: R2 = ZZ['y','x'] sage: R2.has_coerce_map_from(R1) True sage: R2(x) x sage: R2(y) y Se não existir homomorfismo de anel que preserve nomes, coação não é definida. Todavia, conversão pode ainda ser possÃvel, a saber, mapeando geradores de anel de acordo com sua posição da lista de geradores: .. link :: sage: R3 = ZZ['z','x'] sage: R3.has_coerce_map_from(R1) False sage: R3(x) z sage: R3(y) x Mas essas conversões que preservam a posição não se qualificam como coação: Compondo um mapa que preserva nomes de ``ZZ['x','y']`` para ``ZZ['y','x']``, com um mapa que preserva nomes de ``ZZ['y','x']`` para ``ZZ['a','b']``, resultaria em um mapa que não preserva nomes nem posição, violando a consistência. Se houver coação, ela será usada para comparar elementos de anéis diferentes ou fazer aritmética. Isso é frequentemente conveniente, mas o usuário deve estar ciente que estender a relação ``==`` além das fronteiras de famÃlias diferentes pode facilmente resultar em problemas. Por exemplo, enquanto ``==`` é supostamente uma relação de equivalência sobre os elementos de *um* anel, isso não é necessariamente o caso se anéis *diferentes* estão envolvidos. Por exemplo, ``1`` em ``ZZ`` e em um corpo finito são considerados iguais, pois existe uma coação canônica dos inteiros em qualquer corpo finito. Todavia, em geral não existe coação entre dois corpos finitos diferentes. Portanto temos .. link :: sage: GF(5)(1) == 1 True sage: 1 == GF(2)(1) True sage: GF(5)(1) == GF(2)(1) False sage: GF(5)(1) != GF(2)(1) True Similarmente, .. link :: sage: R3(R1.1) == R3.1 True sage: R1.1 == R3.1 False sage: R1.1 != R3.1 True Uma outra consequência da condição de consistência é que coação pode apenas ir de anéis exatos (por exemplo, os racionais ``QQ``) para anéis não-exatos (por exemplo, os números reais com uma precisão fixa ``RR``), mas não na outra direção. A razão é que a composição da coação de ``QQ`` em ``RR`` com a conversão de ``RR`` para ``QQ`` deveria ser a identidade em ``QQ``. Mas isso é impossÃvel, pois alguns números racionais distintos podem ser tratados como iguais em ``RR``, como no seguinte exemplo: :: sage: RR(1/10^200+1/10^100) == RR(1/10^100) True sage: 1/10^200+1/10^100 == 1/10^100 False Quando se compara elementos de duas famÃlias ``P1`` e ``P2``, é possÃvel que não haja coação entre os dois anéis, mas existe uma escolha canônica de um parente ``P3`` de modo que tanto ``P1`` como ``P2`` são coagidos em ``P3``. Nesse caso, coação vai ocorrer também. Um caso de uso tÃpico é na soma de um número racional com um polinômio com coeficientes inteiros, resultando em um polinômio com coeficientes racionais: :: sage: P1.<x> = ZZ[] sage: p = 2*x+3 sage: q = 1/2 sage: parent(p) Univariate Polynomial Ring in x over Integer Ring sage: parent(p+q) Univariate Polynomial Ring in x over Rational Field Note que a princÃpio o resultado deveria também fazer sentido no corpo de frações de ``ZZ['x']``. Todavia, o Sage tenta escolher um parente *canônico* comum que parece ser o mais natural (``QQ['x']`` no nosso exemplo). Se várias famÃlias potencialmente comuns parecem igualmente naturais, o Sage *não* vai escolher um deles aleatoriamente. Os mecanismos sobre os quais essa escolha se baseia é explicado em um `arquivo tutorial <http://flask.sagenb.org/home/pub/82/>`_ Nenhuma coação para um parente comum vai ocorrer no seguinte exemplo: :: sage: R.<x> = QQ[] sage: S.<y> = QQ[] sage: x+y Traceback (most recent call last): ... TypeError: unsupported operand parent(s) for '+': 'Univariate Polynomial Ring in x over Rational Field' and 'Univariate Polynomial Ring in y over Rational Field' A razão é que o Sage não escolhe um dos potenciais candidatos ``QQ['x']['y']``, ``QQ['y']['x']``, ``QQ['x','y']`` ou ``QQ['y','x']``, porque todas essas estruturas combinadas em pares diferentes parecem ser de famÃlias comuns naturais, e não existe escolha canônica aparente.