Geben Sie Python ein, um die algebraische Erweiterung zu implementieren (1) ~ Monoide, Gruppen, Ringe, ganzzahlige Ringe ~

Einführung

Python 3.5 führt die Funktion ** Typhinweis ** ein, die Sie auf Typen aufmerksam macht. Sie sehen jedoch selten Code mit Typhinweisen. Zuerst war ich skeptisch, dass "Python statisch eingegeben werden muss?", Aber als ich meinem Code einen Tipphinweis hinzufügte, "[oh my (py)!](Http: // blog) .zulip.org / 2016/10/13 / static-types-in-python-oh-mypy /) ", und ich wollte es verbreiten, also habe ich beschlossen, diesen Artikel zu schreiben.

Von nun an werden wir die Körpererweiterung in Python mithilfe von Typhinweisen in drei Teilen implementieren. Zur Implementierung habe ich auf den Artikel "Einführung in die Algebra mit Swift" von taketo1024 verwiesen.

Übrigens ist der Autor mit der Erweiterung der Algebra nicht vertraut (so viel ich gelesen habe "Mathematics Girl / Galois Theory" von Hiroshi Yuki), also wenn es irgendwelche Fehler gibt I würde es schätzen wenn du könntest.

Die Algebra-Erweiterung ist im Artikel von taketo1024 und auf anderen Webseiten sorgfältig beschrieben. Lassen Sie uns daher in diesem Artikel nicht viel auf mathematische Themen eingehen, sondern uns auf die Vorgehensweise mit Python konzentrieren. Ich denke.

Angenommene Leserschaft

Inhaltsverzeichnis

  1. Monoid / Gruppe / Ring / ganzzahliger Ring
  2. Feld, rationales Zahlenfeld, euklidischer Ring, Restring, endlicher Körper
  3. Polymerring / Koeffizientenring / Feldausdehnung

Hinweisfunktion vom Typ Python

Eine der Grundlagen für Typhinweise ist das Schreiben des Codes ** Funktionsanmerkung (PEP 3107) **. Dies wird verwendet, um die Argumente und Rückgabewerte einer Funktion zu erläutern.

Das Folgende ist ein Beispiel für eine Anmerkung aus PEP 3107.

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

Der Inhalt der Funktionsanmerkung wirkt sich nicht auf das Ausführungsergebnis des Programms aus. Es wird jedoch versucht, die Funktionsanmerkung zu verwenden, um den Code offline zu überprüfen.

Da PEP 3107 den Inhalt der Anmerkung nicht speziell spezifiziert, können verschiedene Anmerkungsstile aufgrund des Inspektionswerkzeugs verstreut sein. Dies würde jedoch die Vorteile für den Programmierer halbieren, also ab Python 3.5 ** Typhinweise (PEP 484) ** Ein standardisiertes Framework namens, wurde eingeführt, und ein zugehöriges Typisierungsmodul wird bereitgestellt.

Das Folgende ist ein Beispiel für eine Anmerkung aus PEP 484. Geben Sie anstelle der Beschreibung den Namen der Klasse oder des Typs in die Variable oder Zeichenfolge ein.

from typing import List
Vector = List[float]

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

Ich werde es noch einmal schreiben, aber das Verhalten des Programms ändert sich nicht abhängig vom Inhalt der Annotation, und die Annotationsmethode wird nicht erzwungen. Es ist auch nicht immer erforderlich, diese Anmerkungsmethode zu befolgen. Die Standardisierung vereinheitlicht jedoch die Regeln, und wir können eine Zusammenarbeit zwischen Programmierern erwarten.

Inspektionswerkzeug

Nur weil ein Typ-Hinweis-Framework vorhanden ist, bedeutet dies nicht, dass Inspektionstools als Teil von Python bereitgestellt werden. Sie müssen also eines auswählen. In diesem Artikel ist sich PEP 484 sehr bewusst und verwendet ** mypy **, den aktuellen De-facto-Standard.

Sie können mypy mit dem Befehl pip wie folgt installieren (beachten Sie, dass es sich um mypy-lang handelt, nicht um mypy).

$ sudo pip3 install mypy-lang

Die tatsächliche Verwendung erfolgt später in diesem Artikel.

Implementierung

Jetzt machen wir eine "Zahl".

Monoid

Erstens ist das Monoid. Ein Satz, der die folgenden Bedingungen erfüllt, wird als "Monoid" bezeichnet.

Wegen Binäroperation $ \ star $ geschlossen

  1. Das Gesetz der Kombination gilt ($ x \ Stern (y \ Stern z) = (x \ Stern y) \ Stern z $)
  2. Es gibt ein Einheitselement ($ e $ existiert so, dass $ x \ star e = e \ star x = x $)

Beispielsweise sind natürliche Zahlen, reelle Zahlen, quadratische Matrizen usw. Monoide für "Produkt". Um dies in Python auszudrücken, erstellen Sie zunächst eine ** abstrakte Basisklasse ** für Monoide. Fügen Sie metaclass = ABCMeta hinzu, um es zu einer abstrakten Basisklasse zu machen.

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

Da alle Monoide mindestens "Binäroperation $ \ star $", "Einheitselement" und "gleich" haben sollen, deklariert die abstrakte Basisklasse eine leere Methode mit einem Dekorator "@ abstractmethod". Ich werde. Wenn ** diese drei Methoden nicht in der Klasse definiert sind, die Group erbt, tritt daher während der Instanziierung ** ein Fehler auf.

Die Operation $ \ star $ ersetzt den Python-Operator *. Um den Operator "*" für eine Klasse zu definieren, definieren Sie die Methode "mul". Instanzen der Klasse, in der dieser Operator definiert ist, können zum Erstellen von Ausdrücken verwendet werden, indem andere Objekte mit dem Operator * verbunden werden. Zum Beispiel:

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

Bei der Auswertung eines Ausdrucks werden die verbundenen Objekte in "mul" "x" zugewiesen und berechnet, und der Rückgabewert ist das Auswertungsergebnis des Ausdrucks. __eq__ entspricht der Operation ==.

Dies allein bedeutet jedoch nicht, dass das Monoid für die Operation $ \ star $ ** geschlossen ** ist. Hier kommt der ** Typ-Hinweis ** ins Spiel. Typhinweise können die Typen von ** Funktionsargumenten und Rückgabewerten ** darstellen. Schauen wir uns ein Beispiel mit Typhinweisen an.

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

Auf jedes Argument der Funktion folgen ein : und eine Typensignatur. Es gibt auch eine Unterschrift am Ende der def -Anweisung nach dem->. Hier ist ein Tipp.

Mithilfe von Typhinweisen können Sie Funktionsargumenten, Rückgabewerten und anderen Variablen Typinformationen geben. Diese werden beim Ausführen des Programms wie Kommentare behandelt, sind jedoch nützliche Informationen bei Verwendung der Typprüfung.

An jeder Stelle wird eine Variable namens "MonoidType" deklariert und verwendet. Dies wird als ** Typvariable ** bezeichnet, und Variablen, die mit derselben Typvariablen versehen sind, müssen vom selben Typ sein.

In der Deklaration von "MonoidType" wird "bound =" Monoid "verwendet. Auf diese Weise können Variablen mit "MonoidType" auf Instanzen von Unterklassen von "Monoid" beschränkt werden.

Hier wird kein Typhinweis zu __eq__ gegeben. __eq__ ist in object definiert, der Basis aller Klassen. Wenn Sie einen anderen Typhinweis geben, tritt ein Fehler auf.

Gruppe

Eine "Gruppe" ist ein Monoid plus die umgekehrte Bedingung.

Wegen Binäroperation $ \ star $ geschlossen

  1. Das Gesetz der Kombination gilt ($ x \ Stern (y \ Stern z) = (x \ Stern y) \ Stern z $)
  2. Es gibt ein Einheitselement ($ e $ existiert so, dass $ x \ star e = e \ star x = x $)
  3. Es gibt ein inverses Element ($ x ^ {-1} $ existiert so, dass $ x \ star x ^ {-1} = x ^ {-1} \ star x = e $)

Die abstrakte Basisklasse der Gruppe erbt das Monoid, und das inverse Element wird durch die Methode "inverse" definiert. Neben der Definition des inversen Elements empfiehlt es sich, auch hier die Divisionsdefinition "truediv" zu verwenden.

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()

Additive Gruppe

Insbesondere wird eine Menge, die eine Gruppe für eine Operation namens "Addition" bildet, als "Additionsgruppe" bezeichnet. Da die additive Gruppe die Machbarkeit voraussetzt, stellen wir vier Bedingungen wie folgt ein.

Die Operation $ \ oplus $ ist geschlossen.

  1. Das Kombinationsgesetz gilt ($ x \ oplus (y \ oplus z) = (x \ oplus y) \ oplus z $)
  2. Ein Einheitselement existiert ($ 0 $ existiert so, dass $ x \ oplus 0 = 0 \ oplus x = x $, Nullelement)
  3. Inverses Element existiert ($ -x $ existiert so, dass $ x \ oplus (-x) = (-x) \ oplus x = 0 $, minus Element)
  4. Cabrio ($ x \ oplus y = y \ oplus x $)

Ähnlich wie die Gruppe, aber das Codebeispiel lautet wie folgt. Anstatt die Division in Gruppen zu definieren, definieren Sie die Subtraktion.

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

Wenn ich "self + (-x)" betrachte, merke ich noch einmal, dass ich eine Operation definiere.

Ring

Die Gruppen- und Monoid-Bedingungen für nur eine Operation. Der "Ring" definiert die Bedingungen für die beiden Operationen.

Die als Multiplikation bezeichnete Operation $ \ star $ und die als Addition bezeichnete Operation $ \ oplus $ werden geschlossen.

  1. Monoid für die Multiplikation $ \ star $
  2. Informationen zur additiven $ \ oplus $ Abel-Gruppe
  3. Das Verteilungsgesetz gilt zwischen Multiplikation und Addition ($ x \ Stern (y \ oplus z) = x \ Stern y \ oplus x \ Stern z $)

Erben Sie beim Definieren einer Ringklasse einfach die monoiden und additiven Klassen und implementieren Sie den Verteilungsgesetz-Testcode.

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

Ganzzahliger Ring

Sie können endlich einen Integer-Ring implementieren. Erben Sie die abstrakte Basisklasse "Ring" des Rings und überschreiben Sie die in "@ abstractmethod" deklarierte Methode. Definieren Sie außerdem die Methode __repr__, damit der Inhalt der Klasse Integer auf der Python-Konsole angezeigt werden kann.

Wenn es wahr ist, wäre es schön, wenn wir "int" erweitern könnten, indem wir nur "identity" und "zero" schreiben, aber leider kann Python das nicht, also werden wir jede Methode stetig definieren.

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

Verwenden wir nun Z als Alias für Integer.

Jede Methode gibt den Typ "Integer" als Zeichenfolge an. Dies ist eine Vorwärtsreferenz, da die Klasse "Integer" zum Zeitpunkt der Methodendefinition noch nicht definiert wurde. Verwenden Sie Zeichenfolgenliterale anstelle von Symbolen, wenn Sie mit Typhinweisen vorwärts referenzieren.

Analyse von mypy

Lassen Sie uns nach der Implementierung der Ganzzahl den Code mit mypy überprüfen. Geben Sie zur Überprüfung einfach den Dateinamen im Befehl mypy an und führen Sie ihn aus.

$ mypy ./field_extension.py

Wenn keine Probleme vorliegen, wird kein Fehler angezeigt. Versuchen wir nun, die Methode mul aus der Klasse Integer zu entfernen und zu überprüfen.

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

Ringe, für die keine Multiplikation definiert ist, führen zu einem Fehler. Dieser Fehler tritt auf, wenn Sie eine Instanz der Klasse "Integer" erstellen, ohne mypy zu verwenden.

Als nächstes ändern wir den Argumenttyp der Methode mul von Integer in int.

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

Wenn Sie mypy in diesem Status ausführen, wird eine Fehlermeldung angezeigt, dass die Typen nicht übereinstimmen.

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

Zu diesem Zeitpunkt haben wir die Monoide, Gruppen und Ringe abstrahiert und den ganzzahligen Ring erfolgreich implementiert.

Nächster Zeitplan

Das nächste Mal, nachdem ich den Körper berührt habe, plane ich, rationale Zahlenfelder, Restringe und endliche Körper zu erstellen.

Recommended Posts

Geben Sie Python ein, um die algebraische Erweiterung zu implementieren (1) ~ Monoide, Gruppen, Ringe, ganzzahlige Ringe ~
Geben Sie Python ein, um die algebraische Erweiterung zu implementieren (3) ~ Polygonaler Ring, Restring des Polynoms, Erweiterung des Feldes ~
Geben Sie Python ein, um die algebraische Erweiterung zu implementieren (2) ~ Feld, rationales Zahlenfeld, euklidischer Ring, Ring der Restklasse, endlicher Körper ~
Implementieren Sie die Lösung der Riccati-Algebra in Python
Implementieren Sie die Erweiterung in Python
Ich habe versucht, das Blackjack of Trump-Spiel mit Python zu implementieren
Reversibles Verwürfeln von Ganzzahlen in Python
Implementieren Sie einen deterministischen endlichen Automaten in Python, um Vielfache von 3 zu bestimmen
Ich habe versucht, PLSA in Python zu implementieren
Ich habe versucht, Permutation in Python zu implementieren
Ich habe versucht, PLSA in Python 2 zu implementieren
Ich habe versucht, PPO in Python zu implementieren
[Einführung in Python] Eine ausführliche Erklärung der in Python verwendeten Zeichenkettentypen!
Ich habe versucht, Trumps Kartenspiel in Python zu implementieren
Versuchen Sie, Oni Mai Tsuji Miserable mit Python zu implementieren
[Python] Verwendung von zwei Arten von type ()
Zusammenfassung zum Importieren von Dateien in Python 3
So implementieren Sie Shared Memory in Python (mmap.mmap)
Zusammenfassung der Verwendung von MNIST mit Python
Primfaktor-Zerlegung ver.1 der in Python eingegebenen Ganzzahlen
Ich habe versucht, TOPIC MODEL in Python zu implementieren
So erhalten Sie Elemente vom Typ Wörterbuch von Python 2.7
Primfaktor-Zerlegung Version 2 der in Python eingegebenen Ganzzahlen
Ich habe versucht, eine selektive Sortierung in Python zu implementieren
Wie man mit dem Datum / Uhrzeit-Typ in Pythons SQLite3 umgeht
Ich möchte Timeout einfach in Python implementieren
Zusammenfassung der Tools, die zum Analysieren von Daten in Python benötigt werden
So ermitteln Sie die Anzahl der Stellen in Python
So schreiben Sie einen Listen- / Wörterbuchtyp von Python3
Ich habe versucht, einen Pseudo-Pachislot in Python zu implementieren
Ich habe versucht, GA (genetischer Algorithmus) in Python zu implementieren
Konvertieren Sie den Webpay-Entitätstyp in den Dict-Typ (rekursiv in Python).
[Python] Ich habe versucht, Json von Tintenfischring 2 zu bekommen
Lesen von CSVs, die in Python nur Ganzzahlen enthalten
Um das Äquivalent von Rubys ObjectSpace._id2ref in Python zu tun
Implementieren Sie XENO mit Python
Implementieren Sie Traceroute in Python 3
Ich habe versucht, einen eindimensionalen Zellautomaten in Python zu implementieren
Python3-Verarbeitung, die in Paiza verwendbar zu sein scheint
Python - Ermitteln Sie die Anzahl der Gruppen im regulären Ausdruck
So entwickeln Sie in einer virtuellen Python-Umgebung [Memo]
Vergleich der Verwendung von Funktionen höherer Ordnung in Python 2 und 3
So implementieren Sie Python EXE für Windows mit Docker-Container
So erhalten Sie eine Liste der integrierten Ausnahmen für Python
Ich habe versucht, die Mail-Sendefunktion in Python zu implementieren
Eine Geschichte über den Versuch, private Variablen in Python zu implementieren.