Tapez Python pour implémenter l'expansion algébrique (1) ~ Monoïdes, groupes, anneaux, anneaux entiers ~

introduction

Python 3.5 a introduit la fonctionnalité ** Type Hint ** pour vous informer des types. Cependant, vous voyez rarement du code avec des indices de type. Au début, j'étais sceptique sur le fait que "Python a-t-il besoin d'un typage statique?", Mais quand j'ai ajouté un indice de type à mon code, "[oh my (py)!](Http: // blog) .zulip.org / 2016/10/13 / static-types-in-python-oh-mypy /) ", et je voulais le diffuser, j'ai donc décidé d'écrire cet article.

À partir de maintenant, nous allons implémenter l'expansion du corps en Python en trois parties, en utilisant des indices de type. Pour la mise en œuvre, je me suis référé à l'article "Introduction à l'algèbre avec Swift" de taketo1024.

D'ailleurs, l'auteur n'est pas familier avec l'expansion de l'algèbre (autant que j'ai lu "Mathematics Girl / Galois Theory" par Hiroshi Yuki), donc je vous dirai s'il y a des erreurs. J'apprécierais si vous le pouviez.

L'expansion de l'algèbre est soigneusement écrite dans l'article de taketo1024 et d'autres pages Web, alors ne touchons pas beaucoup aux sujets mathématiques dans cet article, mais concentrons-nous sur ** comment le faire avec Python **. Je pense.

Lectorat présumé

table des matières

  1. Monoïde / groupe / anneau / anneau entier
  2. Champ, champ de nombres rationnels, anneau euclidien, anneau de reste, corps fini
  3. Anneau polymère / Anneau de coefficient / Expansion du champ

Fonction d'indication de type Python

L'une des bases des conseils de type est d'écrire le code ** Annotation de fonction (PEP 3107) **. Ceci est utilisé pour expliquer les arguments et les valeurs de retour d'une fonction.

Voici un exemple d'annotation tirée de PEP 3107.

def compile(source: "something compilable",
            filename: "where the compilable thing comes from",
            mode: "is this a single statement or a suite?"):

Le contenu de l'annotation de fonction n'affecte pas le résultat de l'exécution du programme, mais il y a des tentatives d'utilisation de l'annotation de fonction pour inspecter le code hors ligne.

Puisque PEP 3107 ne spécifie pas spécifiquement le contenu de l'annotation, divers styles d'annotation peuvent être dispersés en raison de l'outil d'inspection. Cependant, cela réduirait de moitié les avantages pour le programmeur, donc à partir de Python 3.5 ** type hints (PEP 484) ** Un cadre normalisé appelé, a été introduit, et un module de «typage» associé est fourni.

Voici un exemple d'annotation tirée de PEP 484. Spécifiez le nom de la classe ou tapez dans la variable ou la chaîne de caractères au lieu de la description.

from typing import List
Vector = List[float]

def scale(scalar: float, vector: Vector) -> Vector:
    return [scalar * num for num in vector]

Je vais l'écrire à nouveau, mais le comportement du programme ne change pas en fonction du contenu de l'annotation, et la méthode d'annotation n'est pas forcée. De plus, il n'est pas toujours nécessaire de suivre cette méthode d'annotation. Cependant, la normalisation unifie les règles et on peut s'attendre à une coopération entre les programmeurs.

Outil d'inspection

Ce n'est pas parce qu'un cadre d'indication de type est en place que des outils d'inspection sont fournis dans le cadre de Python, vous devez donc en choisir un. Dans cet article, nous utiliserons ** mypy **, qui est actuellement le standard de facto pour PEP 484.

Vous pouvez installer mypy avec la commande pip comme suit (notez que c'est mypy-lang, pas mypy).

$ sudo pip3 install mypy-lang

L'utilisation réelle sera effectuée plus loin dans cet article.

la mise en oeuvre

Maintenant, faisons un "nombre".

Monoïde

Le premier est le monoïde. Un ensemble qui remplit les conditions suivantes est appelé un "monoïde".

Fermé pour opération binaire $ \ star $

  1. La loi de combinaison tient ($ x \ star (y \ star z) = (x \ star y) \ star z $)
  2. Il y a un élément unitaire ($ e $ existe tel que $ x \ star e = e \ star x = x $)

Par exemple, les nombres naturels, les nombres réels, les matrices carrées, etc. sont des monoïdes pour «produit». Pour exprimer cela en Python, créez d'abord une ** classe de base abstraite ** pour les monoïdes. Ajoutez metaclass = ABCMeta pour en faire une classe de base abstraite.

from abc import ABCMeta, abstractmethod

class Monoid(metaclass=ABCMeta):
    @abstractmethod
    def __mul__(self, x):
        pass

    @abstractmethod
    def identity(self):
        pass

    @abstractmethod
    def __eq__(self, x):
        pass

    def testAssociativity(self, a, b):
        return (self * a) * b == self * (a * b)

    def testIdentity(self):
        return self * self.identity() == self

Puisque nous voulons que tous les monoïdes aient au moins "opération binaire $ \ star $", "élément unitaire" et "égal" implémentés, la classe de base abstraite déclare une méthode vide avec un décorateur @ abstractmethod. Je vais. En conséquence, ** si ces trois méthodes ne sont pas définies dans la classe qui hérite de Group, une erreur se produira lors de l'instanciation **.

L'opération $ \ star $ remplace l'opérateur Python *. Pour définir l'opérateur * pour une classe, définissez la méthode __mul__. Les instances de la classe dans laquelle cet opérateur est défini peuvent être utilisées pour créer des expressions en connectant d'autres objets avec l'opérateur «*». Par exemple:

class A(object):
    def __init__(self, data):
        self.data

    def __mul__(self, x):
        return self.data * x.data

a = A(5)
b = A(10)
print((a * b).data) # -> 50

Lors de l'évaluation d'une expression, les objets connectés sont affectés à «x» dans «mul» et calculés, et la valeur de retour est le résultat de l'évaluation de l'expression. __eq__ correspond à l'opération ==.

Cependant, cela seul ne signifie pas que le monoïde est ** fermé ** pour l'opération $ \ star $. C'est là que le ** indice de type ** entre en jeu. Les indicateurs de type peuvent représenter les types ** d'arguments de fonction et de valeurs de retour **. Regardons un exemple avec des indices de type.

from typing import TypeVar

MonoidType = TypeVar("MonoidType", bound="Monoid")

class Monoid(metaclass=ABCMeta):
    @abstractmethod
    def __mul__(self: MonoidType, x: MonoidType) -> MonoidType:
        pass

    @abstractmethod
    def identity(self: MonoidType) -> MonoidType:
        pass

    @abstractmethod
    def __eq__(self, x): 
        pass

    def testAssociativity(self: MonoidType, a: MonoidType, b: MonoidType) -> bool:
        return (self * a) * b == self * (a * b)

    def testIdentity(self: MonoidType) -> bool:
        return self * self.identity() == self

Chaque argument de la fonction est suivi d'un «:» et d'une signature de type. Il y a aussi une signature à la fin de l'instruction def, après le->. Voici un indice de type.

Les conseils de type vous permettent de donner des informations de type aux arguments de fonction, aux valeurs de retour et à d'autres variables. Ceux-ci sont traités de la même manière que les commentaires lors de l'exécution réelle du programme, mais ce sont des informations utiles lors de l'utilisation du vérificateur de type.

A chaque endroit, une variable appelée «MonoidType» est déclarée et utilisée. C'est ce qu'on appelle une ** variable de type **, et les variables annotées avec le même type de variable doivent être du même type.

Dans la déclaration de MonoidType,` bound = "Monoid" ʻest utilisé. De cette manière, les variables avec "MonoidType" peuvent être limitées aux instances de sous-classes de "Monoid".

Ici, aucun indice de type n'est donné à «eq». __eq__ est défini dans ʻobject`, qui est la base de toutes les classes, et si vous donnez un indice de type différent, une erreur se produira.

groupe

Un «groupe» est un monoïde plus la condition inverse.

Fermé pour opération binaire $ \ star $

  1. La loi de combinaison tient ($ x \ star (y \ star z) = (x \ star y) \ star z $)
  2. Il y a un élément unitaire ($ e $ existe tel que $ x \ star e = e \ star x = x $)
  3. Il existe un élément inverse ($ x ^ {-1} $ existe tel que $ x \ star x ^ {-1} = x ^ {-1} \ star x = e $)

La classe de base abstraite du groupe hérite du monoïde, et l'élément inverse est défini par la méthode «inverse». En plus de définir l'élément inverse, c'est une bonne idée de faire la définition de division __truediv__ ici aussi.

GroupType = TypeVar("GroupType", bound="Group")

class Group(Monoid, metaclass=ABCMeta):
    @abstractmethod
    def inverse(self: GroupType) -> GroupType:
        pass

    def __truediv__(self: GroupType, x: GroupType) -> GroupType:
        return self * x.inverse()

    def testInverse(self: GroupType) -> bool:
        return self * self.inverse() == self.identity()

Groupe additif

En particulier, un ensemble qui forme un groupe pour une opération appelée «addition» est appelé «groupe d'addition». Puisque le groupe additif suppose la convertibilité, nous fixons quatre conditions comme suit.

L'opération $ \ oplus $ est fermée.

  1. La loi de combinaison tient ($ x \ oplus (y \ oplus z) = (x \ oplus y) \ oplus z $)
  2. Un élément unitaire existe ($ 0 $ existe tel que $ x \ oplus 0 = 0 \ oplus x = x $, zéro élément)
  3. L'élément inverse existe ($ -x $ existe tel que $ x \ oplus (-x) = (-x) \ oplus x = 0 $, élément moins)
  4. Convertible ($ x \ oplus y = y \ oplus x $)

Similaire au groupe, mais l'exemple de code est le suivant. Au lieu de définir la division en groupes, définissez la soustraction.

AdditiveGroupType = TypeVar("AdditiveGroupType", bound="AdditiveGroup")

class AdditiveGroup(metaclass=ABCMeta):

    @abstractmethod
    def __add__(self: AdditiveGroupType, x: AdditiveGroupType) -> AdditiveGroupType:
        pass

    @abstractmethod
    def zero(self: AdditiveGroupType) -> AdditiveGroupType:
        pass

    @abstractmethod
    def __neg__(self: AdditiveGroupType) -> AdditiveGroupType:
        pass

    def __sub__(self: AdditiveGroupType, x: AdditiveGroupType) -> AdditiveGroupType:
        return self + (-x)

    @abstractmethod
    def __eq__(self, x):
        pass

    def testAdditiveAssociativity(self: AdditiveGroupType, a: AdditiveGroupType, b: AdditiveGroupType) -> bool:
        return (self + a) + b == self + (a + b)

    def testZero(self: AdditiveGroupType) -> bool:
        return self + self.zero() == self

    def testNegative(self: AdditiveGroupType) -> bool:
        return self + (-self) == self.zero()

    def testAbelian(self: AdditiveGroupType, a: AdditiveGroupType) -> bool:
        return self + a == a + self

En regardant self + (-x), je me rends compte une fois de plus que je définis une opération.

bague

Le groupe et le monoïde stipulaient des conditions pour une seule opération. L '«anneau» définit les conditions des deux opérations.

L'opération $ \ star $ appelée multiplication et l'opération $ \ oplus $ appelée addition sont fermées.

  1. Monoïde pour multiplication $ \ star $
  2. À propos du groupe additif $ \ oplus $ Abel
  3. La loi de distribution est valable entre multiplication et addition ($ x \ star (y \ oplus z) = x \ star y \ oplus x \ star z $)

Lors de la définition d'une classe en anneau, héritez simplement des classes monoïde et additive et implémentez le code de test de loi de distribution.

RingType = TypeVar("RingType", bound="Ring")

class Ring(Monoid, AdditiveGroup):
    def testDistributive(self: RingType, a: RingType, b: RingType) -> bool:
        return self * (a + b) == self * a + self * b

Anneau entier

Vous pouvez enfin implémenter un anneau entier. Hérite de la classe de base abstraite Ring de l'anneau et remplace la méthode déclarée dans @ abstractmethod. Aussi, définissez la méthode __repr__ pour que le contenu de la classe ʻInteger` puisse être affiché sur la console Python.

Si c'est vrai, ce serait bien si nous pouvions étendre ʻint en écrivant uniquement ʻidentity et zero, mais malheureusement Python ne peut pas le faire, donc nous définirons régulièrement chaque méthode.

class Integer(Ring):
    def __init__(self: "Integer", v: int) -> None:
        self.v = v

    def __repr__(self: "Integer") -> str:
        return str(self.v)

    def __mul__(self: "Integer", x: "Integer") -> "Integer":
        return Integer(self.v * x.v)

    def __add__(self: "Integer", x: "Integer") -> "Integer":
        return Integer(self.v + x.v)

    def __neg__(self: "Integer") -> "Integer":
        return Integer(-self.v)

    def __eq__(self, x):
        if not isinstance(x, Integer):
            return NotImplemented
        return self.v == x.v

    def identity(self: "Integer") -> "Integer":
        return Integer(1)

    def zero(self: "Integer") -> "Integer":
        return Integer(0)

Z = Integer

Ici, utilisons Z comme alias pour ʻInteger`.

Chaque méthode spécifie le type ʻInteger sous forme de chaîne. Il s'agit d'une référence directe car la définition de la classe ʻInteger n'est pas terminée au moment de la définition de la méthode. Utilisez des chaînes littérales au lieu de symboles lors du référencement direct avec des indicateurs de type.

Analyse par mypy

Après avoir implémenté l'entier, vérifions le code avec mypy. Pour vérifier, spécifiez simplement le nom du fichier dans la commande mypy et exécutez-le.

$ mypy ./field_extension.py

S'il n'y a aucun problème, aucune erreur ne sera affichée. Maintenant, essayons de supprimer la méthode __mul__ de la classe ʻInteger` et de la vérifier.

$ mypy ./field_extension.py
field_extension.py:174: error: Cannot instantiate abstract class 'Integer' with abstract attribute '__mul__'
...

Les anneaux pour lesquels la multiplication n'est pas définie entraîneront une erreur. Cette erreur se produira au stade de la création d'une instance de la classe ʻInteger` sans utiliser mypy.

Ensuite, changeons le type d'argument de la méthode __mul__ de ʻInteger en ʻint.

def __mul__(self: "Integer", x: int) -> "Integer":
    return Integer(self.v * x)

Si vous exécutez mypy dans cet état, vous obtiendrez une erreur indiquant que les types ne correspondent pas.

$ mypy ./field_extension.py
field_extension.py: note: In class "Integer":
field_extension.py:91: error: Argument 1 of "__mul__" incompatible with supertype "Monoid"

À ce stade, nous avons résumé les monoïdes, les groupes et les anneaux et mis en œuvre avec succès l'anneau entier.

Calendrier suivant

La prochaine fois, après avoir touché le corps, je prévois de créer des champs de nombres rationnels, des anneaux de résidus et des corps finis.

Recommended Posts

Tapez Python pour implémenter l'expansion algébrique (1) ~ Monoïdes, groupes, anneaux, anneaux entiers ~
Tapez Python pour implémenter l'expansion algébrique (3) ~ Anneau polygonal, anneau résiduel du polynôme, expansion du champ ~
Tapez Python pour implémenter l'expansion algébrique (2) ~ Champ, champ de nombres rationnels, anneau euclidien, anneau de classe de reste, corps fini ~
Implémenter la solution de l'algèbre de Riccati en Python
Implémenter l'extension en Python
J'ai essayé d'implémenter le blackjack du jeu Trump en Python
Brouillage réversible d'entiers en Python
Implémenter un automate fini déterministe en Python pour déterminer des multiples de 3
J'ai essayé d'implémenter PLSA en Python
J'ai essayé d'implémenter la permutation en Python
J'ai essayé d'implémenter PLSA dans Python 2
J'ai essayé d'implémenter PPO en Python
[Introduction à Python] Une explication approfondie des types de chaînes de caractères utilisés dans Python!
J'ai essayé d'implémenter le jeu de cartes de Trump en Python
Essayez d'implémenter Oni Mai Tsuji Miserable avec python
[Python] Comment utiliser deux types de type ()
Résumé de la façon d'importer des fichiers dans Python 3
Comment implémenter la mémoire partagée en Python (mmap.mmap)
Résumé de l'utilisation de MNIST avec Python
Décomposition en facteurs premiers ver.1 des entiers entrés en Python
J'ai essayé d'implémenter TOPIC MODEL en Python
Comment obtenir des éléments de type dictionnaire de Python 2.7
Décomposition en facteurs premiers ver.2 des entiers entrés en Python
J'ai essayé d'implémenter le tri sélectif en python
Comment gérer le type datetime dans sqlite3 de python
Je veux facilement implémenter le délai d'expiration en python
Résumé des outils nécessaires pour analyser les données en Python
Comment obtenir le nombre de chiffres en Python
Comment écrire un type liste / dictionnaire de Python3
J'ai essayé d'implémenter un pseudo pachislot en Python
J'ai essayé d'implémenter GA (algorithme génétique) en Python
Convertir le type d'entité Webpay en type Dict (récursivement en Python)
[Python] J'ai essayé d'obtenir Json de squid ring 2
Comment lire un csv contenant uniquement des entiers en Python
Pour faire l'équivalent de Ruby ObjectSpace._id2ref en Python
Implémenter XENO avec python
Implémenter Traceroute dans Python 3
J'ai essayé d'implémenter un automate cellulaire unidimensionnel en Python
traitement python3 qui semble utilisable dans paiza
Python --Trouvez le nombre de groupes dans l'expression regex
Comment développer dans un environnement virtuel Python [Memo]
Comparaison de l'utilisation des fonctions d'ordre supérieur dans Python 2 et 3
Comment implémenter Python EXE pour Windows avec le conteneur Docker
Comment obtenir une liste d'exceptions intégrées pour python
J'ai essayé d'implémenter la fonction d'envoi de courrier en Python
Une histoire sur la tentative d'implémentation de variables privées en Python.