Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-35606: Implement math.prod #11359

Merged
merged 3 commits into from
Feb 7, 2019
Merged

bpo-35606: Implement math.prod #11359

merged 3 commits into from
Feb 7, 2019

Conversation

pablogsal
Copy link
Member

@pablogsal pablogsal commented Dec 29, 2018

Comparison between math.prod and reduce + operator.mul

❯ ./python ../experiment.py
.....................
math.prod: Mean +- std dev: 79.3 us +- 1.8 us
.....................
reduce+operator.mul: Mean +- std dev: 517 us +- 13 us

https://bugs.python.org/issue35606

Note: This PR is a reference implementation to further discuss the addition of prod to the mathmodule.

@pablogsal pablogsal self-assigned this Dec 29, 2018
@pablogsal pablogsal requested a review from rhettinger December 29, 2018 22:06
@pablogsal pablogsal changed the title bpo-bpo35606: Implement math.prod bpo-35606: Implement math.prod Dec 29, 2018
@@ -218,6 +229,7 @@ platform C double type), in which case any float *x* with ``abs(x) >= 2**52``
necessarily has no fractional bits.



Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary line?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@eamanu
Copy link
Contributor

eamanu commented Dec 30, 2018

Look.

>>> math.prod([3,2,1], [2,1])
[2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1]

This isn't normal, right?

@pablogsal
Copy link
Member Author

pablogsal commented Dec 30, 2018

@eamanu Is the same result you will get if you use a simple reduce algorithm:

>>> from functools import reduce
>>> reduce(lambda x,y: x * y, [3,2,1], [1,2])
[1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2]

This raises the question if we should ban certain data types as lists/dicts...etc but that behaviour is totally normal for that input.

@eamanu
Copy link
Contributor

eamanu commented Dec 30, 2018

@pablogsal If we based on sum(), list/tuples/etc should no be accepted

@pablogsal
Copy link
Member Author

pablogsal commented Dec 30, 2018

@eamanu Why not? This is perfectly valid and an analogous is in the test suite:

>>> sum([[1,2,3,4]], [1,2,3,3,4])
[1, 2, 3, 3, 4, 1, 2, 3, 4]

We can decide to limit the API to not accept list/tuples/etc but people can get away and subclass them or implement their own containers. I think the text in the doc and docstring is enough.

@eamanu
Copy link
Contributor

eamanu commented Dec 30, 2018

I think the text in the doc and docstring is enough.

Ok, yes, you are right.

A test with the form math.prod([3,2,1], [2,1]) can be added?

Copy link
Contributor

@eamanu eamanu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

return PyFloat_FromDouble(f_result);
}
if (PyFloat_CheckExact(item)) {
PyFPE_START_PROTECT("prod", Py_DECREF(item); Py_DECREF(iter); return 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Support for these macros was dropped in 3.7, so you can leave them out. We still define the macros to avoid breaking pre-existing third-party code but the macros don't do anything, they are empty.

@bedevere-bot
Copy link

When you're done making the requested changes, leave the comment: I have made the requested changes; please review again.

@rhettinger
Copy link
Contributor

Please add an entry to whatsnew/3.8.rst. Otherwise, this all looks good :-)

@pablogsal pablogsal force-pushed the bpo35606 branch 3 times, most recently from a97b7f6 to a9ffb24 Compare February 7, 2019 01:18
@rhettinger
Copy link
Contributor

rhettinger commented Feb 7, 2019

Can you please fix-up the CI bot failures. Likely, it wants the trailing whitespace cleaned-up.

@rhettinger rhettinger merged commit bc09851 into python:master Feb 7, 2019
@pablogsal pablogsal deleted the bpo35606 branch February 7, 2019 09:24
Comment on lines +1748 to +1757
self.assertRaises(TypeError, prod, ['a', 'b', 'c'], '')
self.assertRaises(TypeError, prod, [b'a', b'c'], b'')
values = [bytearray(b'a'), bytearray(b'b')]
self.assertRaises(TypeError, prod, values, bytearray(b''))
self.assertRaises(TypeError, prod, [[1], [2], [3]])
self.assertRaises(TypeError, prod, [{2:3}])
self.assertRaises(TypeError, prod, [{2:3}]*2, {2:3})
self.assertRaises(TypeError, prod, [[1], [2], [3]], [])
with self.assertRaises(TypeError):
prod([10, 20], [30, 40]) # start is a keyword-only argument
Copy link

@s-cork s-cork Sep 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rhettinger / @pablogsal - Skulpt committer here - implementing these tests in our own project.

I was wondering if the intention wasn't quite right with some of these TypeError tests?
Most of them fail because start is a keyword only argument.
Whereas it looks like - for most of them except the final test - the intention is to check multiplication across incompatible types within the prod function.

}

if (result == NULL) {
result = PyLong_FromLong(1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use _PyLong_One.

}
/* Loop over all the items in the iterable until we finish, we overflow
* or we found a non integer element */
while(result == NULL) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a space between while and (.

}
if (PyLong_CheckExact(item)) {
long b = PyLong_AsLongAndOverflow(item, &overflow);
long x = i_result * b;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Signed integer overflow is an undefined behavior.

/* Continue if there is no overflow */
if (overflow == 0
&& x < INT_MAX && x > INT_MIN
&& !(b != 0 && x / i_result != b)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible division by 0.

double f_result = PyFloat_AS_DOUBLE(result);
Py_DECREF(result);
result = NULL;
while(result == NULL) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a space between while and (.

@pablogsal
Copy link
Member Author

@serhiy-storchaka Thanks for the review. This PR is already merged so I can do a separate PR if you wish

@serhiy-storchaka
Copy link
Member

Please address @s-cork comment. Tests do not work as intended.

@serhiy-storchaka
Copy link
Member

Ah, I started reviewing after seen @s-cork comment, without noticing that this PR was merged years ago.

@pablogsal
Copy link
Member Author

Yeah, most of these things are already fixed, although is possible that some of these still remain. I will do a check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants