Skip to content

Commit

Permalink
Create SupportsRealImagAsMethod, real, and imag
Browse files Browse the repository at this point in the history
Now ``sympy.core.numbers`` primitives are adequately supported (excepting general false positives; see #5).

Fixes #4.

Also renames ``SupportsNumeratorDenominatorProperties`` to ``SupportsNumeratorDenominator`` for consistency.
  • Loading branch information
posita committed Nov 15, 2021
1 parent 3600886 commit f107863
Show file tree
Hide file tree
Showing 26 changed files with 490 additions and 261 deletions.
34 changes: 25 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ False
These offer significant performance improvements, especially where protocols define many methods.

``` python
--8<-- "docs/perf_supports_complex.out"
--8<-- "docs/perf_supports_complex.txt"
```

<details>
Expand Down Expand Up @@ -370,14 +370,30 @@ True
False
>>> from numerary.types import SupportsRealImag
>>> real_imag: SupportsRealImag = pants_on_fire # fails to detect the lie
>>> real_imag.real
Traceback (most recent call last):
...
AttributeError: 'One' object has no attribute 'real'

```

In this particular case, ``numerary`` provides us with a defensive mechanism.

``` python
>>> from numerary.types import SupportsRealImagMixedU, real, imag
>>> real_imag_defense: SupportsRealImagMixedU = pants_on_fire
>>> real(real_imag_defense)
1
>>> imag(real_imag)
0

```

#### Protocols loses fidelity during runtime checking

At runtime, protocols match *names*, not *signatures*.
For example, [``SupportsNumeratorDenominatorProperties``](https://posita.github.io/numerary/latest/numerary.types/#numerary.types.SupportsNumeratorDenominatorProperties)’s ``numerator`` and ``denominator`` *properties* will match [``sage.rings.integer.Integer``](https://doc.sagemath.org/html/en/reference/rings_standard/sage/rings/integer.html#sage.rings.integer.Integer)’s similarly named *[functions](https://trac.sagemath.org/ticket/28234)*.
In other words, ``isinstance(sage_integer, SupportsNumeratorDenominatorProperties)`` will return ``True``.
For example, [``SupportsNumeratorDenominator``](https://posita.github.io/numerary/latest/numerary.types/#numerary.types.SupportsNumeratorDenominator)’s ``numerator`` and ``denominator`` *properties* will match [``sage.rings.integer.Integer``](https://doc.sagemath.org/html/en/reference/rings_standard/sage/rings/integer.html#sage.rings.integer.Integer)’s similarly named *[functions](https://trac.sagemath.org/ticket/28234)*.
In other words, ``isinstance(sage_integer, SupportsNumeratorDenominator)`` will return ``True``.
Further, if the short-circuiting approach is used, because ``sage.rings.integer.Integer`` registers itself with the numeric tower, this *may*[^5] not be caught by Mypy.

[^5]:
Expand All @@ -394,10 +410,10 @@ Further, if the short-circuiting approach is used, because ``sage.rings.integer.
... def denominator(self) -> int:
... return self._denominator

>>> from numerary.types import SupportsNumeratorDenominatorProperties
>>> frac: SupportsNumeratorDenominatorProperties = Fraction(29, 3) # no typing error
>>> sage_rational1: SupportsNumeratorDenominatorProperties = SageLikeRational(29, 3) # type: ignore [assignment] # Mypy catches this
>>> isinstance(sage_rational1, SupportsNumeratorDenominatorProperties) # isinstance does not
>>> from numerary.types import SupportsNumeratorDenominator
>>> frac: SupportsNumeratorDenominator = Fraction(29, 3) # no typing error
>>> sage_rational1: SupportsNumeratorDenominator = SageLikeRational(29, 3) # type: ignore [assignment] # Mypy catches this
>>> isinstance(sage_rational1, SupportsNumeratorDenominator) # isinstance does not
True
>>> sage_rational1.numerator
<...method...numerator...>
Expand Down Expand Up @@ -428,11 +444,11 @@ These accommodate rational implementations like Sage’s that are mostly complia

``` python
SupportsNumeratorDenominatorMixedU = Union[
SupportsNumeratorDenominatorProperties,
SupportsNumeratorDenominator,
SupportsNumeratorDenominatorMethods,
]
SupportsNumeratorDenominatorMixedT = (
SupportsNumeratorDenominatorProperties,
SupportsNumeratorDenominator,
SupportsNumeratorDenominatorMethods,
)
```
Expand Down
11 changes: 11 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
all : \
perf_rational_baseline.txt \
perf_rational_big_protocol.txt \
perf_rational_protocol.txt \
perf_supports_complex.txt

perf_%.txt : perf_%.ipy Makefile ../numerary/types.py
ipython >perf_$*.txt --no-banner --quick --LoggingMagics.quiet=True perf_$*.ipy

../numerary-encumbered.svg : Makefile
curl --output $@ 'https://img.shields.io/badge/%F0%9F%98%A3-%F0%9D%9A%97%F0%9D%9A%9E%F0%9D%9A%96%F0%9D%9A%8E%F0%9D%9A%9B%F0%9D%9A%8A%F0%9D%9A%9B%F0%9D%9A%A2--encumbered-yellowgreen'
3 changes: 3 additions & 0 deletions docs/notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@

## [0.1.2](https://github.com/posita/numerary/releases/tag/v0.1.2)

* Splits [``SupportsRealImagAsMethod``][numerary.types.SupportsRealImagAsMethod] out of [``SupportsRealImag``][numerary.types.SupportsRealImag] and provides the [``real``][numerary.types.real] and [``imag``][numerary.types.imag] helper functions for better support of ``sympy.core.numbers`` primitives.
* Renames ``SupportsNumeratorDenominatorProperties`` to [``SupportsNumeratorDenominator``][numerary.types.SupportsNumeratorDenominator] to mirror [``SupportsRealImag``][numerary.types.SupportsRealImag] and reflect that it captures the numeric tower interface.

## [0.1.1](https://github.com/posita/numerary/releases/tag/v0.1.1)

* Removes obsoleted ``…SCT`` aliases.
Expand Down
12 changes: 9 additions & 3 deletions docs/numerary.types.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ from numerary.bt import beartype # will resolve to the identity decorator if be
selection:
members:
- "CachingProtocolMeta"
- "RationalLikeProperties"
- "RationalLike"
- "RationalLikeMethods"
- "SupportsAbs"
- "SupportsComplex"
Expand All @@ -67,16 +67,19 @@ from numerary.bt import beartype # will resolve to the identity decorator if be
- "SupportsRound"
- "SupportsConjugate"
- "SupportsRealImag"
- "SupportsRealImagAsMethod"
- "SupportsTrunc"
- "SupportsFloorCeil"
- "SupportsDivmod"
- "SupportsNumeratorDenominatorProperties"
- "SupportsNumeratorDenominator"
- "SupportsNumeratorDenominatorMethods"
- "SupportsComplexOps"
- "SupportsComplexPow"
- "SupportsRealOps"
- "SupportsIntegralOps"
- "SupportsIntegralPow"
- "real"
- "imag"
- "trunc"
- "floor"
- "ceil"
Expand All @@ -103,10 +106,13 @@ from numerary.bt import beartype # will resolve to the identity decorator if be
- "_SupportsRound"
- "_SupportsConjugate"
- "_SupportsRealImag"
- "_SupportsRealImagAsMethod"
- "SupportsRealImagMixedT"
- "SupportsRealImagMixedU"
- "_SupportsTrunc"
- "_SupportsFloorCeil"
- "_SupportsDivmod"
- "_SupportsNumeratorDenominatorProperties"
- "_SupportsNumeratorDenominator"
- "_SupportsNumeratorDenominatorMethods"
- "SupportsNumeratorDenominatorMixedT"
- "SupportsNumeratorDenominatorMixedU"
Expand Down
6 changes: 0 additions & 6 deletions docs/perf_rational_baseline.out

This file was deleted.

6 changes: 6 additions & 0 deletions docs/perf_rational_baseline.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
]0;IPython: numerary/docs%timeit isinstance(builtins.int(1), Rational)
341 ns ± 5.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit isinstance(fractions.Fraction(2), Rational)
344 ns ± 5.28 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit isinstance(builtins.float(3.0), Rational)
369 ns ± 8.65 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
6 changes: 0 additions & 6 deletions docs/perf_rational_big_protocol.out

This file was deleted.

6 changes: 6 additions & 0 deletions docs/perf_rational_big_protocol.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
]0;IPython: numerary/docs%timeit isinstance(builtins.int(1), SupportsLotsOfNumberStuff)
149 µs ± 5.76 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit isinstance(fractions.Fraction(2), SupportsLotsOfNumberStuff)
152 µs ± 2.22 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit isinstance(builtins.float(3.0), SupportsLotsOfNumberStuff)
149 µs ± 11.1 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
6 changes: 0 additions & 6 deletions docs/perf_rational_protocol.out

This file was deleted.

6 changes: 6 additions & 0 deletions docs/perf_rational_protocol.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
]0;IPython: numerary/docs%timeit isinstance(builtins.int(1), SupportsNumeratorDenominator)
12.7 µs ± 133 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit isinstance(fractions.Fraction(2), SupportsNumeratorDenominator)
12.9 µs ± 158 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit isinstance(builtins.float(3.0), SupportsNumeratorDenominator)
13.9 µs ± 1.17 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
24 changes: 0 additions & 24 deletions docs/perf_supports_complex.out

This file was deleted.

24 changes: 24 additions & 0 deletions docs/perf_supports_complex.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
]0;IPython: numerary/docs%timeit isinstance(builtins.int(1), _SupportsComplexOps)
11.9 µs ± 189 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit isinstance(builtins.int(1), SupportsComplexOps)
310 ns ± 2.92 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit isinstance(builtins.float(2.0), _SupportsComplexOps)
12.4 µs ± 871 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit isinstance(builtins.float(2.0), SupportsComplexOps)
313 ns ± 0.829 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit isinstance(decimal.Decimal(3), _SupportsComplexOps)
12 µs ± 116 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit isinstance(decimal.Decimal(3), SupportsComplexOps)
315 ns ± 2.18 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit isinstance(fractions.Fraction(4), _SupportsComplexOps)
12.9 µs ± 1.63 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit isinstance(fractions.Fraction(4), SupportsComplexOps)
314 ns ± 3.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit isinstance(sympy.core.numbers.Integer(5), _SupportsComplexOps)
12.3 µs ± 692 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit isinstance(sympy.core.numbers.Integer(5), SupportsComplexOps)
312 ns ± 3.08 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
6 changes: 3 additions & 3 deletions docs/whytho.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ Let’s see how they perform.
First, let’s get a baseline.

``` python
--8<-- "docs/perf_rational_baseline.out"
--8<-- "docs/perf_rational_baseline.txt"
```

<details>
Expand All @@ -233,7 +233,7 @@ First, let’s get a baseline.
Now let’s compare that with our two-property protocol.

``` python
--8<-- "docs/perf_rational_protocol.out"
--8<-- "docs/perf_rational_protocol.txt"
```

<details>
Expand All @@ -249,7 +249,7 @@ And that’s just with a two-property protocol.
How much worse would it be if we had enumerated all the dunder methods? 😰

``` python
--8<-- "docs/perf_rational_big_protocol.out"
--8<-- "docs/perf_rational_big_protocol.txt"
```

<details>
Expand Down
Loading

0 comments on commit f107863

Please sign in to comment.