Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add signature for the :func:`~math.typot`.
48 changes: 47 additions & 1 deletion Modules/clinic/mathmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 28 additions & 21 deletions Modules/mathmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -2761,26 +2761,49 @@ math_dist_impl(PyObject *module, PyObject *p, PyObject *q)
return NULL;
}

/* AC: cannot convert yet, waiting for *args support */

/*[clinic input]
math.hypot

*coordinates as args: object

Multidimensional Euclidean distance from the origin to a point.

Roughly equivalent to:
sqrt(sum(x**2 for x in coordinates))

For a two dimensional point (x, y), gives the hypotenuse
using the Pythagorean theorem: sqrt(x*x + y*y).

For example, the hypotenuse of a 3/4/5 right triangle is:

>>> hypot(3.0, 4.0)
5.0

[clinic start generated code]*/

static PyObject *
math_hypot(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
math_hypot_impl(PyObject *module, PyObject *args)
/*[clinic end generated code: output=ace3a23188b6798e input=ff33f88cd8b1f06d]*/
{
Py_ssize_t i;
Py_ssize_t i, nargs;
PyObject *item;
double max = 0.0;
double x, result;
int found_nan = 0;
double coord_on_stack[NUM_STACK_ELEMS];
double *coordinates = coord_on_stack;

nargs = PyTuple_GET_SIZE(args);

if (nargs > NUM_STACK_ELEMS) {
coordinates = (double *) PyObject_Malloc(nargs * sizeof(double));
if (coordinates == NULL) {
return PyErr_NoMemory();
}
}
for (i = 0; i < nargs; i++) {
item = args[i];
item = PyTuple_GetItem(args, i);
Copy link
Member

Choose a reason for hiding this comment

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

This might affect performance. Can you please measure it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, there is a difference.

$ ./python -m timeit -s 'from math import hypot as h' -s 'a = list(range(1, 10))' 'h(*a)'
500000 loops, best of 5: 650 nsec per loop
$ ./python -m timeit -s 'from math import hypot as h' -s 'a = list(range(1, 100))' 'h(*a)'
50000 loops, best of 5: 4.54 usec per loop
$ ./python -m timeit -s 'from math import hypot as h' -s 'a = list(range(1, 1000))' 'h(*a)'
5000 loops, best of 5: 45.1 usec per loop
$ git checkout fix-101123 
Switched to branch 'fix-101123'
Your branch is up to date with 'github/fix-101123'.
$ make -s
Checked 111 modules (30 built-in, 80 shared, 1 n/a on linux-x86_64, 0 disabled, 0 missing, 0 failed on import)
$ ./python -m timeit -s 'from math import hypot as h' -s 'a = list(range(1, 10))' 'h(*a)'
500000 loops, best of 5: 950 nsec per loop
$ ./python -m timeit -s 'from math import hypot as h' -s 'a = list(range(1, 100))' 'h(*a)'
50000 loops, best of 5: 6.43 usec per loop
$ ./python -m timeit -s 'from math import hypot as h' -s 'a = list(range(1, 1000))' 'h(*a)'
5000 loops, best of 5: 65.6 usec per loop

So, probably we should use the patch from the issue. On another hand, the comment ("AC: cannot convert yet, waiting for *args support") - seems to be misleading and must be removed.

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 string-based docs to populate __text_signature__. It will fix your problem with inspect.signature with 0 runtime cost. Example from dict.get:

PyDoc_STRVAR(dict_get__doc__,
"get($self, key, default=None, /)\n"
"--\n"
"\n"
"Return the value for key if key is in the dictionary, else default.");

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You can use string-based docs to populate text_signature.

Yes, I understand. That seems to be the solution, presented in the issue description, isn't?

The question was: should we keep the mentioned comment for math_hypot? It seems to be misleading for me (issue reference would be more helpful here, if it is an open problem), as there is no principal problems to convert this function to the Argument Clinic.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, I think it should be changed to: Not converted to AC because it affects performance

Copy link
Contributor Author

@skirpichev skirpichev Jan 18, 2023

Choose a reason for hiding this comment

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

Could @rhettinger bless this change? The comment is coming from #8474 and I'm not sure he meant performance issues with AC (rather something like #64490).
PS: AC stuff reverted.
Timings with PyTuple_GET_ITEM:

$ ./python -m timeit -s 'from math import hypot as h' -s 'a = list(range(1, 10))' 'h(*a)'
500000 loops, best of 5: 814 nsec per loop
$ ./python -m timeit -s 'from math import hypot as h' -s 'a = list(range(1, 100))' 'h(*a)'
50000 loops, best of 5: 5.16 usec per loop
$ ./python -m timeit -s 'from math import hypot as h' -s 'a = list(range(1, 1000))' 'h(*a)'
5000 loops, best of 5: 53.2 usec per loop

ASSIGN_DOUBLE(x, item, error_exit);
x = fabs(x);
coordinates[i] = x;
Expand All @@ -2804,22 +2827,6 @@ math_hypot(PyObject *self, PyObject *const *args, Py_ssize_t nargs)

#undef NUM_STACK_ELEMS

PyDoc_STRVAR(math_hypot_doc,
"hypot(*coordinates) -> value\n\n\
Multidimensional Euclidean distance from the origin to a point.\n\
\n\
Roughly equivalent to:\n\
sqrt(sum(x**2 for x in coordinates))\n\
\n\
For a two dimensional point (x, y), gives the hypotenuse\n\
using the Pythagorean theorem: sqrt(x*x + y*y).\n\
\n\
For example, the hypotenuse of a 3/4/5 right triangle is:\n\
\n\
>>> hypot(3.0, 4.0)\n\
5.0\n\
");

/** sumprod() ***************************************************************/

/* Forward declaration */
Expand Down Expand Up @@ -4234,6 +4241,7 @@ static PyMethodDef math_methods[] = {
{"cosh", math_cosh, METH_O, math_cosh_doc},
MATH_DEGREES_METHODDEF
MATH_DIST_METHODDEF
MATH_HYPOT_METHODDEF
{"erf", math_erf, METH_O, math_erf_doc},
{"erfc", math_erfc, METH_O, math_erfc_doc},
{"exp", math_exp, METH_O, math_exp_doc},
Expand All @@ -4247,7 +4255,6 @@ static PyMethodDef math_methods[] = {
MATH_FSUM_METHODDEF
{"gamma", math_gamma, METH_O, math_gamma_doc},
{"gcd", _PyCFunction_CAST(math_gcd), METH_FASTCALL, math_gcd_doc},
{"hypot", _PyCFunction_CAST(math_hypot), METH_FASTCALL, math_hypot_doc},
MATH_ISCLOSE_METHODDEF
MATH_ISFINITE_METHODDEF
MATH_ISINF_METHODDEF
Expand Down