Dieses Modul stellt die komplexen Zahlen (und nebenbei auch die reellen Zahlen) bereit.
- newtype R a = R {
- runR ∷ IO a
- unsafeRunR ∷ R a → a
- newtype Approx ex = MkApprox {
- unApprox ∷ PositiveNat → R ex
- class HasDenseSubset a where
- type DenseSubset a ∷ *
- approx ∷ PositiveNat → a → R (DenseSubset a)
- newInteractiveApprox ∷ Read ex ⇒ ([(PositiveNat, ex)] → Bool) → String → IO (Approx ex)
- newInteractiveApprox' ∷ (Read ex, NormedRing ex) ⇒ String → IO (Approx ex)
- type Complex = AST ComplexRational
- type Real = AST Rational
- data AST ex
- fromBase ∷ ex → AST ex
- data QinC
- data QinR
- data QIinC
- normUpperBoundR ∷ NormedRing ex ⇒ AST ex → R Rational
- class Ring a ⇒ HasMagnitudeZeroTest a where
- magnitudeZeroTestR ∷ PositiveNat → a → R Bool
- class Ring a ⇒ PseudoField a where
- recip' ∷ a → a
- signum' ∷ Real → Ordering
- traceEvals ∷ (Show ex, NormedRing ex) ⇒ String → AST ex → AST ex
- logMaxEval ∷ NormedRing ex ⇒ AST ex → IO (IORef PositiveNat, AST ex)
- sqrt2 ∷ Real
- goldenRatio ∷ Real
- demo ∷ IO ()
Monade fuer nicht-deterministische Rechnungen
newtype R a
Monade fuer nicht-deterministische Rechnungen von Approximationsalgorithmen.
Wir verwenden dazu einfach die IO
-Monade, weil wir beliebige externe
Kommunikation (beispielsweise Nutzereingaben oder Zufallszahlen) erlauben
wollen.
Operationen, die den Kontrollfluss beeinflussen (wie beispielsweise
forkIO
), verbieten wir. (Denn was wuerde so ein Algorithmus bedeuten?)
unsafeRunR ∷ R a → a
unsafeRunR m laesst die nicht-deterministische Operation m laufen und gibt ihr Ergebnis zurueck.
Das ist im Allgemeinen gefaehrlich, denn jedes Mal, wenn das Ergebnis dann ausgewertet wird, kann jeweils ein anderer Wert resultieren, da m ja nicht als deterministisch vorausgesetzt wird. Ausserdem werden die Nebeneffekte in m moeglicherweise mehrmals ausgefuehrt.
Verwendet man unsafeRunR
in einer Funktion, muss man daher darauf achten,
dass der Rueckgabewert nicht von den verschiedenen Ergebnissen von
unsafeRunR
abhaengt -- sonst verletzt man das Prinzip der referentiellen
Transparenz.
Approximationsalgorithmen
newtype Approx ex
Typ der Approximationsalgorithmen mit Approximationen aus ex. Dabei fordern wir folgende Bedingung (die sich aber leider in Haskell nicht auf Typebene festschreiben laesst):
Es muss eine bestimmte Zahl z geben, sodass der Algorithmus, mit einer positiven natuerlichen Zahl n aufgerufen, eine Naeherung an z produziert, deren Abstand zu z echt kleiner als n^(-1) ist.
Ansonsten ist dem Approximationsalgorithmus keinen Beschraenkungen unterworfen. Insbesondere darf er beliebige nicht-deterministische Prozesse anstossen, und kann bei der wiederholten Fragen nach einer n^(-1)-Naeherung jedes Mal ein anderes Resultat liefern.
MkApprox | |
|
class HasDenseSubset a where
Klasse fuer Raeume, deren Punkte sich durch Elemente einer gewissen Teilmenge beliebig genau approximieren lassen.
Damit man die Forderung der Approximation formulieren kann, muss man eigentlich
auf dem Typ a eine Norm (oder Metrik) gegeben haben. Dann koennten wir aber unser
wichtigstes Beispiel, die komplexen Zahlen Complex
, nicht mehr
unterstuetzen, denn die Ordnung auf Normen komplexer Zahlen (also gewissen reellen
Zahlen) ist nicht entscheidbar.
Moeglicherweise koennte man dieses Problem durch eine bessere Definition normierter Raeume beheben.
type DenseSubset a ∷ *
Dicht liegende Teilmenge, durch die approximiert werden soll
approx ∷ PositiveNat → a → R (DenseSubset a)
approx n z soll eine Naeherung von z bestimmen, die vom wahren Wert im Betrag um weniger (<) als n^(-1) abweicht. Wiederholte Auswertungen duerfen andere Naeherungen berechnen.
NormedRing ex ⇒ HasDenseSubset (AST ex) |
∷ Read ex | |
⇒ ([(PositiveNat, ex)] → Bool) | Funktion, die uebergebene Genauigkeits/Wertepaare auf Konsistenz prueft, also prueft, ob diese Approximationen einer ideellen Zahl sein koennten |
→ String | Name der Zahl (fuer den Nutzer) |
→ IO (Approx ex) |
Erzeugt einen interaktiven Approximationsalgorithmus, welcher Naeherungen dadurch produziert, indem er den Nutzer auf der Standardkonsole fragt.
newInteractiveApprox' ∷ (Read ex, NormedRing ex) ⇒ String → IO (Approx ex)
Erzeugt wie newInteractiveApprox'
einen interaktiven
Approximationsalgorithmus, wobei als Konsistenzpruefung
|x_n - x_m| <= 1/n + 1/m
verwendet wird. Diese ist noch zu schwach, aber die Methoden der
NormedRing
-Klasse erlauben keine Pruefung auf <.
Erweiterungen von Ringen durch approximativ gegebene Elemente
type Complex = AST ComplexRational
Der Typ der komplexen Zahlen.
data AST ex
Elemente in AST ex beschreiben Rechnungen (abstract syntax trees), die man mit Werten aus ex, den Ringoperationen (Addition und Multiplikation -- Negation kann man als Multiplikation mit -unit gewinnen) und zusaetzlichen nur durch Approximationsprozeduren gegebenen ideellen Elementen fuehren kann. Die Darstellung ist natuerlich nicht eindeutig, und Gleichheit nicht entscheidbar.
Beispielsweise ist AST ComplexRational der Typ der komplexen Zahlen, AST Rational der der reellen Zahlen -- zumindest wenn man aequivalente Beschreibungen miteinander identifiziert.
AST ex kann man sich auch als die freie Ringerweiterung von ex durch Approximationsprozeduren vorstellen, und man koennte auch kuerzer AST ex durch
data AST' ex = Exact ex | Ext String (Approx ex)
definieren, denn da Prozeduren vom Typ Approx ex beliebige Rechnungen durchfuehren duerfen, koennen diese die in dieser Alternativdefinition fehlenden Konstruktoren emulieren. Auch moeglich waere
data AST'' ex = Ext String (Approx ex).
Die hier gegebene Definition hat zwei Vorteile. Zum einen koennen wir so einige rudimentaere Optimierungen vornehmen: Moechte man beispielsweise x_1 + ... + x_n auf Genauigkeit N^(-1) auswerten, benoetigt man bei naiver Klammerung, x_1 + (x_2 + (x_3 + ...)), eine Approximation von x_n mit Genauigkeit (2^n N)^(-1). Bei balancierter Auswertung dagegen benoetigt man von jedem Summanden nur eine Approximation mit Genauigkeit (n N)^(-1).
Zum anderen ist die Definition in der Praxis effizienter: Denn bei den beiden Alternativdefinitionen haeufen sich schon bei kurzen Rechnungen sehr viele Approx-Objekte, welche Bezuege auf ihre Definitionsumgebung enthalten und daher automatische Speicherbereinigung verhindern.
Warnung: Wenn man sich nur fuer die syntaktische Struktur interessiert, ist AST ex natuerlich faktoriell in ex. Von einer allgemeinen Funktion f kann man aber nicht erwarten, dass
approx n (fmap f ast)
auch eine n^(-1)-Approximation an liftM f (approx n ast) liefert. Damit das garantiert ist, muss f ein lipschitzstetiger Ringhomomorphismus mit Lipschitzkonstante kleinergleich 1 sein, wie beispielsweise f = conjugate oder f = fromRational.
Exact ex | Einbettung exakter Werte |
Add [AST ex] | Addition beliebig (endlich) vieler Terme |
Mult (AST ex) (AST ex) | Multiplikation zweier Terme |
Ext String (Approx ex) | ideeller Wert, gegeben durch einen Approximationsalgorithmus. Die Zeichenkette kann als Bezeichner fuer Debugging-Zwecke dienen. |
Functor AST | |
Show ex ⇒ Show (AST ex) | |
(NormedRing ex, HasFloatingApprox ex, Eq ex) ⇒ HasFloatingApprox (AST ex) | |
(HasConjugation ex, Eq ex, Eq (RealSubring ex)) ⇒ HasConjugation (AST ex) | |
(HasRationalEmbedding ex, Eq ex) ⇒ HasRationalEmbedding (AST ex) | |
(Ring ex, Eq ex) ⇒ Ring (AST ex) | |
(Field ex, NormedRing ex) ⇒ PseudoField (AST ex) | z_n| >= |z_N| - |z_N - z_n| > 3N - (1N + 1n) >= 1N. |z| >= |z_N| - |z_N - z| > 2N - 1N = 1/N. Zu b): Sei |z| >= q > 0. Sei q >= 1/k, k >= 1. Setze N := 4k. Dann gilt: |z_N| >= |z| - |z - z_N| > 1k - 1(4k) = (4k-1)(4k) >= 3N. |
(NormedRing a, Eq a) ⇒ HasMagnitudeZeroTest (AST a) | |
NormedRing ex ⇒ HasDenseSubset (AST ex) |
data QinC
Bezeichnung fuer die Einbettung der rationalen in die komplexen Zahlen
RingMorphism QinC | |
Arbitrary (Alg QinC) | |
HasConjugation (IC QinC) | |
HasConjugation (Alg QinC) |
data QinR
Bezeichnung fuer die Einbettung der rationalen in die reellen Zahlen
RingMorphism QinR | |
Ord (Alg QinR) | |
OrderedRing (Alg QinR) |
data QIinC
Bezeichnung fuer die Einbettung der komplexrationalen in die komplexen Zahlen
normUpperBoundR ∷ NormedRing ex ⇒ AST ex → R Rational
Bestimmt eine obere Schranke (im Sinn von <
) fuer den Betrag der
gegebenen Zahl.
Mehrmalige Aufrufe dieser Funktion koennen verschiedene obere Schranken produzieren.
class Ring a ⇒ HasMagnitudeZeroTest a where
Wie bei HasDenseSubset
muessten wir eigentlich einen geeigneten
Metrik- oder Normkontext fordern.
magnitudeZeroTestR ∷ PositiveNat → a → R Bool
Sei z eine Zahl. Dann ist nicht entscheidbar, ob |z| = 0 oder ob nicht |z| = 0. Fuer festes n >= 1 gilt aber stets:
|z| > 0 oder |z| < 1/n,
wobei das oder natuerlich kein entweder oder ist. Gibt
magnitudeZeroTestR
False zurueck, so liegt der erste Fall vor,
andernfalls der zweite.
(NormedRing a, Eq a) ⇒ HasMagnitudeZeroTest (AST a) |
class Ring a ⇒ PseudoField a where
Klasse fuer Ringe, die im Sinne folgender schwaecheren Definition Koerper sind:
Fuer alle /x/, die von Null entfernt sind, gibt es ein Inverses von /x/.
Da wir das Konzept der Entferntheit nutzen, muessten wir (wie bei
HasDenseSubset
genauer erlaeutert) eigentlich einen geeigneten Metrik-
oder Normkontext fordern.
recip' ∷ a → a
Sei z von null entfernt, es existiere also eine rationale Zahl q mit
|z| >= q > 0. Dann soll recip' z die Zahl z^(-1) darstellen.
recip'
muss sonst nicht definiert sein.
(Field ex, NormedRing ex) ⇒ PseudoField (AST ex) | z_n| >= |z_N| - |z_N - z_n| > 3N - (1N + 1n) >= 1N. |z| >= |z_N| - |z_N - z| > 2N - 1N = 1/N. Zu b): Sei |z| >= q > 0. Sei q >= 1/k, k >= 1. Setze N := 4k. Dann gilt: |z_N| >= |z| - |z - z_N| > 1k - 1(4k) = (4k-1)(4k) >= 3N. |
Bestimmt das Vorzeichen einer von null entfernten reellen Zahl. Die Auswertung von signum' zero terminiert nicht.
Debugging
traceEvals ∷ (Show ex, NormedRing ex) ⇒ String → AST ex → AST ex
traceEvals name z ist semantisch nicht von z zu unterscheiden, gibt aber bei jeder Auswertung Debugging-Informationen auf die Standardfehlerkonsole aus.
logMaxEval ∷ NormedRing ex ⇒ AST ex → IO (IORef PositiveNat, AST ex)
logMaxEval z gibt ein Paar (var, z') zurueck, wobei z' semantisch nicht von z zu unterscheiden ist, aber seine maximalen Approximationsgesuche in der veraenderlichen Variablen var speichert.
Beispiele
demo ∷ IO ()