Idiomatic Python implementation for finite sets, images, and preimages of finite functions?

Question:

Suppose I have a finite concrete function between finite sets. Of course, Python has sets implemented natively.

But what is the best way to implement the idea of a finite function between sets, precisely? A wrapped dictionary? Also, how would one implement the calculations of images and preimages? [See below.]

For images I can use map, clearly. For preimages I can loop over the domain set and filter. Still, I’m wondering about the more pythonic idiomatic solution.

Wikipedia: Images (mathematics)

Asked By: Rex Butler

||

Answers:

For arbitrary images from a set to another, I would use a Python dictionary. For example, f(n)=n^2 on a set of {1,2,3,4}, I can do this:

preimage = set([1,2,3,4])
mapping = {x: x*x for x in preimage}
image = set(mapping.values())
assert set(mapping.keys()) == preimage
function = lambda x: mapping[x] # so you can now have y = function(x)
check_image = set([function(x) for x in preimage])
assert check_image == image

Of course, this only works if your finite set is really finite with respect to the memory you have.

The above is the most generic case that you define the function as mapping. But in case for simpler function which you can have a Python expression to represent it, you can skip the dictionary:

preimage = set([1,2,3,4])
function = lambda x: x*x
image = set([function(x) for x in preimage])
check_preimage = set([y for x in image for y in preimage if function(y)==x])
assert check_preimage == preimage

And if further, you have an inverse function available for the domain:

import math
preimage = set([1,2,3,4])
function = lambda x: x*x
inv_func = lambda x: int(math.sqrt(x))
image = set([function(x) for x in preimage])
check_preimage = set([inv_func(x) for x in image])
assert check_preimage == preimage

Note that, the three different code snippet above, only the first will guarantee your function(x) to allow only those x in the predefined preimage.

Speaking of the idiomic python: I don’t think Python is really such a mathematical language (compare to, say, Wolfram’s mathematica) so we do not have the concept of image, mapping, etc. built-in. However, you can see my code above with the list comprehension. Indeed, I just made the thing more explicit to use set keyword as in set([function(x) for x in preimage]) but you can save a few keystroke with {function(x) for x in preimage}.

Answered By: adrtam
def preimage(b: B, f: Dict[A, B]) -> Set[A]: 
    ''' get the preimage of one item. '''
    return set(a for a in f.keys() if f[a]==b)

This assumes TypeVar and other things from pep484

from typing import TypeVar, Dict, Set
A = TypeVar('A')
B = TypeVar('B')

Without type annotation it looks like this

def preimage(b, f): 
  ...

or a subset of the codomain

from typing import Collection
from functools import reduce

def preimage_(bs: Collection[B], f: Dict[A, B]) -> Set[A]: 
    ''' get the preimage of a collection of items '''
    return reduce(lambda a, b: a.union(b), [preimage(b, f) for b in bs])
Answered By: Quinn Dougherty

Inspired by @Quinn Dougherty’s answer, here’s a full implementation:

from dataclasses import dataclass
import functools
from typing import (Collection, Dict, FrozenSet, Set, TypeVar)

A = TypeVar('A')
B = TypeVar('B')


@dataclass
class FiniteFunction:
    f: Dict[A, B]
    domain: FrozenSet = None
    f_range: FrozenSet = None

    def __post_init__(self):
        self.domain, self.f_range = (frozenset(x) for x in (self.f.keys(), self.f.values()))

    def image(self, a: A) -> B:
        ''' get the image of one item. '''
        return self.f[a]

    # https://stackoverflow.com/a/56350794
    def preimage(self, b: B) -> Set[A]:
        ''' get the preimage of one item. '''
        return set(a for a in self.f.keys() if self.f[a] == b)

    def image_(self, aa: Collection[A]) -> Set[B]:
        ''' get the image of a collection of items '''
        return functools.reduce(lambda a, b: a.union(b), [self.image(a) for a in aa])

    # https://stackoverflow.com/a/56350794
    def preimage_(self, bs: Collection[B]) -> Set[A]:
        ''' get the preimage of a collection of items '''
        return functools.reduce(lambda a, b: a.union(b), [self.preimage(b) for b in bs])
Answered By: Attila the Fun
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.