Skip to content

ENH: Add gcd and lcm ufuncs #8774

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

Merged
merged 4 commits into from
Dec 13, 2017
Merged

ENH: Add gcd and lcm ufuncs #8774

merged 4 commits into from
Dec 13, 2017

Conversation

eric-wieser
Copy link
Member

@eric-wieser eric-wieser commented Mar 12, 2017

Fixes #8772, implementing Least Common Multiple and Greatest Common Divisor as ufuncs

Since these are now both in the C++17 stdlib, it seems pretty reasonable that they should be part of numpy as well. Specification matches the C++17 one, where the result is always positive.

Defined for:

  • signed and unsigned ints
  • object
    • builtin arbitrary-precision longs defer to math.gcd (if available)
    • everything else (Decimal, Fraction, ...) defers to fraction.gcd np.core.internal._gcd, but enforcing consistency with math.gcd (returning a positive value)

Not defined for:

  • bool - doesn't seem useful, since it just decays to logical_or
  • np.inexact - not well defined

First commit is some minor cleanup to make it easier to see where to add new integer ufuncs. Githubs rendering of this diff is not very clear

U@TYPE@_absolute(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));

NPY_NO_EXPORT void
@TYPE@_absolute(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
Copy link
Member Author

Choose a reason for hiding this comment

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

This commit's diff is just combining U@TYPE@_absolute and @TYPE@_absolute into @S@@TYPE@_absolute, and the same for _sign, _divide, and _remainder. This is already the pattern we use for other functions in this file

@eric-wieser
Copy link
Member Author

eric-wieser commented Mar 12, 2017

Not the most efficient of algorithms - CPython 3.5 uses Lehmer's algorithm in math.gcd. But this is better than the python function that would otherwise be written by users of earlier versions of python.

Also, the builtin math.gcd works for arbitrary-precision integers, so actually needs the speed boost for 1000-bit integers - whereas we're limited to 64/128 anyway

@eric-wieser eric-wieser force-pushed the lcm-gcd branch 3 times, most recently from 160c2a5 to 264c1c7 Compare March 15, 2017 23:40
@eric-wieser
Copy link
Member Author

eric-wieser commented Mar 15, 2017

Ok, tests are added

One problem remains - np.gcd(Decimal(1), Decimal(1)) defers to fractions.gcd(Decimal(1), Decimal(1)). On python 3.5, this gives a warning

@eric-wieser eric-wieser force-pushed the lcm-gcd branch 2 times, most recently from f881013 to 24774f6 Compare March 16, 2017 15:18
@eric-wieser
Copy link
Member Author

All green, documented, and release note'd

@homu
Copy link
Contributor

homu commented Mar 23, 2017

☔ The latest upstream changes (presumably #8795) made this pull request unmergeable. Please resolve the merge conflicts.

/**end repeat**/

/**begin repeat
* #TYPE = UBYTE, USHORT, UINT, ULONG, ULONGLONG#
* #type = npy_ubyte, npy_ushort, npy_uint, npy_ulong, npy_ulonglong#
* #c = u,u,u,ul,ull#
Copy link
Member

Choose a reason for hiding this comment

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

The standard codes for these are B, H, I, L and Q. Shouldn't you use those instead?

Copy link
Member Author

@eric-wieser eric-wieser Mar 23, 2017

Choose a reason for hiding this comment

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

Possibly. Although I didn't see much point implementing B, H, and I separately, because arithmetic always promotes to int in C anyway, doesn't it?

Can you link me to a file that uses these standard codes? Oh, these are the struct.pack codes, right? I was trying to write this as though it were a free C function, like labs and llabs are to abs. I guess in hindsight they use a prefix though...

Copy link
Member Author

Choose a reason for hiding this comment

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

A better example of what I was imitating might be strto{l,ll,ul,ull} from the C standard library.

Copy link
Member

Choose a reason for hiding this comment

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

...these are the struct.pack codes, right?

They are also documented in numpy; see https://docs.scipy.org/doc/numpy/reference/arrays.scalars.html#built-in-scalar-types and numpy.typecodes['Integer'], numpy.typecodes['UnsignedInteger'], etc.

@homu
Copy link
Contributor

homu commented Mar 27, 2017

☔ The latest upstream changes (presumably #8847) made this pull request unmergeable. Please resolve the merge conflicts.

@mhvk
Copy link
Contributor

mhvk commented May 5, 2017

Would be quite nice to have this! To me, it looks OK, but maybe e.g. @juliantaylor can have a look?

@eric-wieser eric-wieser force-pushed the lcm-gcd branch 2 times, most recently from adca9d6 to 36d6cc7 Compare May 6, 2017 01:01
TD('O', f='npy_ObjectGCD'),
),
'lcm' :
Ufunc(2, 1, None,
Copy link
Member Author

@eric-wieser eric-wieser May 6, 2017

Choose a reason for hiding this comment

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

Got this wrong until now. The identity of gcd is 0 since by definition, gcd(a, 0) == a.

The identity for lcm is some kind of epsilon. For the integers, that epsilon is simply 1 (lcm(a, 1) = a). However, for the rationals, (ie decimal.Decimal), this is incorrect (lcm(0.2, 1) != 0.2), nor is such an epsilon really representable anyway.

@homu
Copy link
Contributor

homu commented May 8, 2017

☔ The latest upstream changes (presumably #9063) made this pull request unmergeable. Please resolve the merge conflicts.

@eric-wieser
Copy link
Member Author

Rebased for 1.14

@eric-wieser eric-wieser added the 60 - Major release Issues that need or may be better addressed in a major release label Dec 12, 2017
@mhvk
Copy link
Contributor

mhvk commented Dec 12, 2017

I looked again at this, and it all looks fine, except there was the question about which character to use. Also, I'd explicitly check that calling the functions with a float gets you an error, and maybe that it works for a python super-long integer.

@eric-wieser
Copy link
Member Author

@mhvk: Tests and release notes fixed. I don't think there's a strong argument to be made about the suffices - since they're a private implementation detail, I'd prefer to leave them

@eric-wieser eric-wieser added this to the 1.15.0 release milestone Dec 13, 2017
@eric-wieser eric-wieser removed the 60 - Major release Issues that need or may be better addressed in a major release label Dec 13, 2017
@mhvk
Copy link
Contributor

mhvk commented Dec 13, 2017

OK, that looks great. Merging!

@mhvk mhvk merged commit d407f24 into numpy:master Dec 13, 2017
@eric-wieser
Copy link
Member Author

Thanks! Glad I won't have to rebase for 1.16 too!

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

Successfully merging this pull request may close these issues.

4 participants