Skip to main content

Multiple and predicative dispatching library

Project description

Multiple and Predicative Dispatch

API

Let's look at an example of usage that you can find in the src/examples/ directory of the repository. The confidence argument allows us to set a threshold for demanded certainty of the primality of large numbers.

from __future__ import annotations
from math import sqrt

from dispatch.dispatch import get_dispatcher
from primes import akw_primality, mr_primality, primes_16bit
nums = get_dispatcher("nums")

@nums
def is_prime(n: int & 0 < n < 2**16) -> bool:
    "Check primes from pre-computed list"
    return n in primes_16bit

@nums
def is_prime(n: n < 2**32) -> bool:
    "Check prime factors for n < √2³²"
    ceil = sqrt(n)
    for prime in primes_16bit:
        if prime > ceil:
            return True
        if n % prime == 0:
            return False
    return True

@nums(name="is_prime")
def miller_rabin(
    n: int & n >= 2**32, 
    confidence: float = 0.999_999,
) -> bool:
    "Use Miller-Rabin pseudo-primality test"
    return mr_primality(n, confidence)

@nums(name="is_prime")
def agrawal_kayal_saxena(
    n: int & n >= 2**32,
    confidence: float & confidence == 1.0,
) -> bool:
    "Use Agrawal-Kayal-Saxena deterministic primality test"
    return aks_primality(n)

# Bind to the Gaussian prime function (which _has_ type annotation)
nums(name="is_prime")(gaussian_prime)  

nums.is_prime(64_489)        # True by direct search
nums.is_prime(64_487)        # False by direct search
nums.is_prime(262_147)       # True by trial division
nums.is_prime(262_143)       # False by trial division
nums.is_prime(4_294_967_311) # True by Miller-Rabin test
nums.is_prime(4_294_967_309) # False by Miller-Rabin test
nums.is_prime(4_294_967_311, confidence=1.0) # True by AKS test
nums.is_prime(4_294_967_309, confidence=1.0) # False by AKS test
nums.is_prime(-4 + 5j)=}")  # True by Gaussian prime test
nums.is_prime(+4 - 7j)=}")  # False by Gaussian prime test

print(nums) # -->
# nums with 1 function bound to 4 implementations
nums.describe() # -->
# nums bound implementations:
# (0) is_prime
#     n: int ∩ 0 < n < 2 ** 16
# (1) is_prime
#     n: Any ∩ n < 2 ** 32
# (2) is_prime (re-bound 'miller_rabin')
#     n: int ∩ n >= 2 ** 32
#     confidence: float ∩ True
# (3) is_prime (re-bound 'agrawal_kayal_saxena')
#     n: int ∩ n >= 2 ** 32
#     confidence: float ∩ confidence == 1.0

History

I once implemented multiple dispatch (multimethods) in an ancient 2002 package:

DON'T USE THAT!

It might not work with anything after Python 2.3. And even if it does, it's certainly not an elegant API for modern Python (it came before decorators or annotations, for example).

My article from the time is still basically correct and useful:

A great many other people have also implemented multiple dispatch (usually with the name "multimethods") in Python. See https://pypi.org/search/?q=multimethods for many of these libraries.

These implementations are probably all perfectly fine. I haven't tried most of them, and the authors might make somewhat different choices about APIs than I do here. But I'm sure that almost all of them work well.

One thing I did, back in 2002 that no one else seems to have done, is to implement a choice of what "MRO" to use in choosing an implementation function. This package may or may not do that in later versions.

Way back in the early 2000s, not too long after I first wrote about and implemented multiple dispatch in Python, a wondeful fellow Pythonista named Phillip J Eby wrote a library called PEAK (Python Enterprise Application Kit). Among the many things thrown into PEAK—in a manner much like how I threw every passing thought and article into Gnosis Utilities—was a "dispatch" module:

That nifty library makes up much of the inspiration for this one. In those post-Python-2.4 days, when we had decorators (but before print() became a function), Phillip allowed us to write things like this:

import dispatch

@dispatch.generic()
def doIt(foo, other):
    "Base generic function of 'doIt()'"

@doIt.when("isinstance(foo,int) and isinstance(other,str)")
def doIt(foo, other):
    prin  "foo is an unrestricted int |", foo, other

@doIt.when("isinstance(foo,int) and 3<=foo<=17 and isinstance(other,str)")
def doIt(foo, other):
    print "foo is between 3 and 17 |", foo, other

@doIt.when("isinstance(foo,int) and 0<=foo<=1000 and isinstance(other,str)")
def doIt(foo, other):
    print "foo is between 0 and 1000 |", foo, other

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gnosis_dispatch-0.2.0.tar.gz (48.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gnosis_dispatch-0.2.0-py3-none-any.whl (9.5 kB view details)

Uploaded Python 3

File details

Details for the file gnosis_dispatch-0.2.0.tar.gz.

File metadata

  • Download URL: gnosis_dispatch-0.2.0.tar.gz
  • Upload date:
  • Size: 48.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.0

File hashes

Hashes for gnosis_dispatch-0.2.0.tar.gz
Algorithm Hash digest
SHA256 04ae40612904ce055f3961837af23f5293fbbf5aea915921df5a4522a533975d
MD5 1969d81d16604694fb40995ced0c4d2e
BLAKE2b-256 5738bb3221b61222b7b168d02cd4dd3111a9d6042c77c3cc10af5086fb9a82ed

See more details on using hashes here.

File details

Details for the file gnosis_dispatch-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for gnosis_dispatch-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9defd7e1e9797d749473053d7ddd9ff3ac500101ffbb0d6528fb2f3c3a29fa3f
MD5 c07d5065239fb3bff2fe7b046131b0fd
BLAKE2b-256 db5be525182e9fd6ad156498d27a87f1d4213ec5208a2db1735ff098105e5ddf

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page