Exact complex-like scalars with rational real and imaginary parts.
Project description
gaussian-rational
gaussian-rational provides an exact, immutable complex-like scalar where both
components are rational numbers (fractions.Fraction).
Use GaussianRational when you want complex arithmetic without intermediate
floating-point rounding in the real and imaginary parts.
Highlights
- Exact real and imaginary components (
Fraction-backed). - Immutable value type, safe to share across threads.
- Complex-style arithmetic (
+,-,*,/, integer**). - Compatible real equality/hash behavior for purely real values.
- Flexible string parsing and formatting.
- Fully typed (PEP 561, inline annotations).
Installation
pip install gaussian-rational
Quick Start
from fractions import Fraction
from gaussian_rational import GaussianRational
z = GaussianRational(Fraction(1, 3), Fraction(2, 5))
w = GaussianRational(2, -1)
print(z + w) # 7/3-3j/5
print(z * w) # 16/15+7j/15
print(z / w) # 4/15+j/3
print(z.conjugate()) # 1/3-2j/5
print(z.real, z.imag) # Fraction(1, 3) Fraction(2, 5)
print(complex(z)) # (0.3333333333333333+0.4j)
print(z.arg()) # atan2(float(imag), float(real))
Construction
GaussianRational normalizes several input forms:
from fractions import Fraction
from gaussian_rational import GaussianRational
GaussianRational(3, 4) # a=3, b=4
GaussianRational((Fraction(1, 2), Fraction(2))) # tuple input
GaussianRational(5) # purely real, imag=0
GaussianRational(GaussianRational(1, 2)) # identity/upcast
GaussianRational("1/2+2j") # string parse
Accepted component types are int and Fraction.
API Summary
Numeric operations
+,-, unary-*,/- Integer exponentiation
**nforn: int abs(x)returnsfloatcomplex(x)returns built-incomplexusingfloatcasts of both parts
Properties and helpers
real,imag— rational components, mirroringcomplexconjugate()— complex conjugatea - biarg()— phase angle in radians (atan2(imag, real))as_tuple()— explicit(real, imag)tupleabs_squared()— exact norm-squared asFraction- Predicates:
is_real,is_imaginary,is_zero_or_imaginary,is_composite,is_zero - Parsing:
GaussianRational.parse(...) - Formatting:
format(...),str(...),repr(...)
If you want deterministic ordering, sort explicitly with as_tuple():
sorted_values = sorted(values, key=lambda z: z.as_tuple())
String Formatting
GaussianRational provides three string surfaces:
| Surface | Description |
|---|---|
str(z) |
Calls z.format(). |
z.format(...) |
Configurable exact symbolic output. |
format(z, spec) / f-strings |
Empty spec → symbolic; non-empty spec → float-based complex semantics. |
repr(z) |
Eval-safe constructor form, for example GaussianRational(Fraction(1, 2), 3). |
Formatting rules:
- Real-only values print as rational scalars (for example
3,-1/2). - Imaginary-only values print with
jattached (for examplej,-2j,j/3,-5j/7). - Composite values print as
a+bjora-bjwith no spaces. force_sign=Trueadds a leading+for non-negative outputs.parens_if_composite=Truewraps composite outputs in parentheses; for non-composite values, rational fractions are parenthesized instead.imag_charoverrides the symbol per call (for exampleimag_char="i").GaussianRational.default_imag_charsets the class-wide default.- For
format(z, spec): non-emptyspecusescomplexformatting semantics, mapping the imaginary symbol to the class default.
Examples:
from fractions import Fraction
from gaussian_rational import GaussianRational
str(GaussianRational(3, 0)) # "3"
str(GaussianRational(0, 1)) # "j"
str(GaussianRational(1, -2)) # "1-2j"
GaussianRational(1, 2).format(force_sign=True) # "+1+2j"
GaussianRational(Fraction(1, 2), Fraction(1, 3)).format(
parens_if_composite=True,
) # "(1/2+j/3)"
GaussianRational(1, 2).format(imag_char="i") # "1+2i"
repr(GaussianRational(Fraction(1, 2), Fraction(-3, 4))) # "GaussianRational(Fraction(1, 2), Fraction(-3, 4))"
f"{GaussianRational(Fraction(1, 2), Fraction(-5, 3)):.2f}" # "0.50-1.67j"
repr(z) is an eval-safe round-trip given from fractions import Fraction and
from gaussian_rational import GaussianRational in scope.
Parsing String Literals
GaussianRational.parse parses symbolic literals and is also used by
GaussianRational("...").
from fractions import Fraction
from gaussian_rational import GaussianRational
GaussianRational.parse("1+2j") # GaussianRational(1, 2)
GaussianRational.parse("(2/3)j") # GaussianRational(0, Fraction(2, 3))
GaussianRational.parse("2j/3") # GaussianRational(0, Fraction(2, 3))
GaussianRational.parse(" 1/2 + 3 i ", imag_char=("i", "j"))
GaussianRational.parse("2/3j", interpret_slash_j_as_j_slash=True)
Parsing notes:
- Embedded spaces are ignored.
imag_characcepts a single character or a tuple of aliases.- By default, ambiguous
2/3jis rejected; setinterpret_slash_j_as_j_slash=Trueto accept it as2j/3. GaussianRationalLikeintentionally excludesstr, so arithmetic dunder upcasting never treats arbitrary strings as numeric.
Examples
Conjugate product gives exact norm-squared
from fractions import Fraction
from gaussian_rational import GaussianRational
z = GaussianRational(Fraction(3, 4), Fraction(-5, 6))
prod = z * z.conjugate()
print(prod) # 181/144
print(prod.is_real) # True
print(prod.real) # Fraction(181, 144)
print(z.abs_squared()) # Fraction(181, 144)
Division remains exact
from fractions import Fraction
from gaussian_rational import GaussianRational
x = GaussianRational(Fraction(1, 2), Fraction(1, 3))
y = GaussianRational(Fraction(2, 5), Fraction(-1, 7))
q = x / y
print(q.real) # Fraction(1295, 1222)
print(q.imag) # Fraction(980, 1833)
Integer powers
from gaussian_rational import GaussianRational
z = GaussianRational(1, 1)
print(z ** 2) # 2j
print(z ** -1) # 1/2-j/2
Interop with built-in complex
from fractions import Fraction
from gaussian_rational import GaussianRational
z = GaussianRational(Fraction(1, 3), Fraction(5, 2))
c = complex(z)
print(c) # (0.3333333333333333+2.5j)
print(type(c)) # <class 'complex'>
Semantics and Compatibility
GaussianRational is designed to feel close to complex while preserving
exact rational components.
- Truthiness matches numeric convention: only
GaussianRational(0, 0)is false. - Equality accepts
GaussianRational,int, andFractionvalues. - Equality intentionally does not accept tuples:
GaussianRational(1, 2) == (1, 2)isFalse. - Hashing is aligned for purely real values so equality and hash stay consistent with compatible real scalars.
- Instances are immutable and safe to share across threads.
Current intentional limitation:
- Exponentiation supports integer powers only.
Type Hints
This package ships inline type hints and a py.typed marker (PEP 561).
Public typing aliases:
FractionLike = Fraction | intGaussianRationalLike = GaussianRational | tuple[FractionLike, FractionLike] | FractionLike
Advanced Usage
Subclassing
GaussianRational is immutable and uses __slots__ for compact instances.
Subclassing is supported with a few rules:
- Do not assign attributes normally (
self.x = ...) after construction; useobject.__setattr__in__new__instead. - If your subclass adds fields, declare its own
__slots__. - Keep
type(self)(a, b)compatible with your subclass constructor, since arithmetic results are constructed that way.
Minimal pattern:
from gaussian_rational import GaussianRational
class TaggedGaussianRational(GaussianRational):
__slots__ = ("tag",)
def __new__(cls, v, v2=None, *, tag: str = ""):
self = super().__new__(cls, v, v2)
object.__setattr__(self, "tag", tag)
return self
Subclass contract for repr round-trips:
__repr__ is implemented as f"{type(self).__name__}({self.format()!r})".
A subclass that adds state beyond the real and imag slots must override both format()
(to encode that state in the string) and parse() (to decode it), so that
eval(repr(z)) remains a valid round-trip.
Common pitfalls:
- Forgetting
__slots__on the subclass, which reintroduces__dict__. - Changing constructor semantics so
type(self)(a, b)no longer works. - Adding mutable fields while keeping hashing enabled (invalidates hash contract if mutable fields affect equality).
Supported Python Versions
Python 3.10 and later.
License
MIT. See LICENSE.
For development and release workflow documentation, see CONTRIBUTING.md.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gaussian_rational-1.0.2.tar.gz.
File metadata
- Download URL: gaussian_rational-1.0.2.tar.gz
- Upload date:
- Size: 17.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
872247ea5e76c12e7d3941fb41f70fcec1b9fcabd400340cce741b757911f373
|
|
| MD5 |
259693f223e586a30fed6d50f5a6e0dd
|
|
| BLAKE2b-256 |
570cb21b72d8e6d9c9a66ad06237e36bde8c3b26a4c43ad32a97ef4044a6b3e8
|
Provenance
The following attestation bundles were made for gaussian_rational-1.0.2.tar.gz:
Publisher:
publish.yml on mckelvie-org/gaussian-rational
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gaussian_rational-1.0.2.tar.gz -
Subject digest:
872247ea5e76c12e7d3941fb41f70fcec1b9fcabd400340cce741b757911f373 - Sigstore transparency entry: 1619377652
- Sigstore integration time:
-
Permalink:
mckelvie-org/gaussian-rational@962e291a4bd331d1e53989a5f1cc0f4f413c8db9 -
Branch / Tag:
refs/tags/v1.0.2 - Owner: https://github.com/mckelvie-org
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@962e291a4bd331d1e53989a5f1cc0f4f413c8db9 -
Trigger Event:
push
-
Statement type:
File details
Details for the file gaussian_rational-1.0.2-py3-none-any.whl.
File metadata
- Download URL: gaussian_rational-1.0.2-py3-none-any.whl
- Upload date:
- Size: 13.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
466c99d27835fd269f0060f62dc47591466f5f77d15ca5569f216fd631519b0a
|
|
| MD5 |
175fcc35b57f284f858a4711471bc8e1
|
|
| BLAKE2b-256 |
a890893ec7687e3f747c3d5dc98162ca446db33f80094a8ad8ed052c0ae299d8
|
Provenance
The following attestation bundles were made for gaussian_rational-1.0.2-py3-none-any.whl:
Publisher:
publish.yml on mckelvie-org/gaussian-rational
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gaussian_rational-1.0.2-py3-none-any.whl -
Subject digest:
466c99d27835fd269f0060f62dc47591466f5f77d15ca5569f216fd631519b0a - Sigstore transparency entry: 1619377712
- Sigstore integration time:
-
Permalink:
mckelvie-org/gaussian-rational@962e291a4bd331d1e53989a5f1cc0f4f413c8db9 -
Branch / Tag:
refs/tags/v1.0.2 - Owner: https://github.com/mckelvie-org
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@962e291a4bd331d1e53989a5f1cc0f4f413c8db9 -
Trigger Event:
push
-
Statement type: