Search code examples
pythonsumproductnotation

Can summation and product notations be used in a single line of Python code?


I am currently working with a few mathematical problems. Some involve double summations while others involve double products. Some include a combination summation with a product.

Python permits double summation in a single line of code. Take the example:

\sum_{x=1}^{2}\left ( \sum_{y=1}^{3} \left ( x+y \right )\right )= 21

This can be simply solved with a single code line:

>>> sum((x + y) for y in range(1,4) for x in range(1,3))
21

Similarly, double products can be calculated with a single line of code. Example:

\prod_{x=1}^{2}\left ( \prod_{y=1}^{3} \left ( x+y \right )\right )= 1440

And the coded solution using Python 3.8:

>>> import math
>>> math.prod((x + y) for y in range(1,4) for x in range(1,3))
1440

How can I complete a similar problem that includes a single summation and a single product? Is it possible using a single line of code? For example:

\sum_{x=1}^{2}\left ( \prod_{y=1}^{3} \left ( x+y \right )\right )= 84

I thought this may be as simple as:

>>> sum((math.prod(x + y)) for y in range(1,4) for x in range(1,3))
TypeError: 'int' object is not iterable

but print(dir(sum)) and print(dir(math.prod)) show the magic method __iter__ is absent from both sum and math.prod, hence the TypeError.

I know I can use a for loop like this:

>>> ans = 0
>>> for x in range(1,3):
...         ans += math.prod((x + y) for y in range(1,4))
>>> ans
84

but this gets a little untidy when I have more summations and products in the same equation (up to four, occasionally more).

Is there a simple single code line approach for combining summations and products I’m missing? Or is the for loop method the only way? Any thoughts?


Solution

  • The parentheses in the mathematical notation are your guide. What we need to do is to consider each summation/product step separately.

    When we perform the sum of products, each product is of the form

    math.prod(x + y for y in range(1, 4))
    

    (Note that parentheses around x + y are not necessary.)

    Notice that x is undefined here - so far, it is a free variable (in math-speak).

    So, we bind it when we perform the summation:

    sum(math.prod(x + y for y in range(1, 4)) for x in range(1, 3))
    

    which evaluates to the desired 84.

    It helps here to understand the theory underlying the syntactic constructs. When we write sum(f(x) for x in xs), we are passing a generator expression to the built-in sum function. This functions similarly to a list comprehension (which in turn is built upon the mathematical concept of set-builder notation), except that the elements will be determined on-demand as sum iterates over them, rather than being eagerly evaluated and stored in memory all at once. Normally, a generator expression would be written using an enclosing pair of parentheses, thus (f(x) for x in xs); Python has a special syntax rule such that, when you call a function with a single argument which is a generator expression, you can omit this pair of parentheses (since they would be redundant with the ones used for the function call).

    For more detail on list comprehensions, please see Trey Hunner's excellent guide. The basic technique applies equally to all sorts of comprehensions in Python as well as to generator expressions.