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

Math module - unifying argument names #19005

Closed
lydia-duncan opened this issue Jan 14, 2022 · 10 comments · Fixed by #23077
Closed

Math module - unifying argument names #19005

lydia-duncan opened this issue Jan 14, 2022 · 10 comments · Fixed by #23077

Comments

@lydia-duncan
Copy link
Member

lydia-duncan commented Jan 14, 2022

Argument name for a lot of the functions vary depending on the type of the argument - I see i for integer, x for real and z for complex but not always. I think these argument names should be unified (probably to x?).

There are three categories of functions that use a different strategy than the above. I think they should also be unified, but it seemed worth calling out how they are different in case they should stay as exceptions:

  • divceil, etc use m and n for their argument names. Should it be x and y instead? They at least seem to be consistent with each other. mod also mostly uses the same names, but not always.
  • log uses x for the argument name except for the log2 variants which use val. These should be unified (to x, I believe)
  • gcd uses a and b for its argument names. Should it use x and y?
@damianmoz
Copy link

damianmoz commented Jan 15, 2022

I would need to see the complete list. Let's sort out the default and non-default names first.

But generally a good idea. While some people use x and y when there are two parameters , others use u and v in this two parameter case because the size of the letters in most fonts is approximately the same and there is no trailing tail as there is with y.
That approach means you can also use w for a third parameter which gives you a third tail-less letter in the mix and avoids the need to use z which as you say, is often used for a single complex number.

Even so, I tend to use 'u' .. 'z' or even 'f' .. 'h' for anything of real(w) type, and 'i' .. 'n' for things integral.

I thought that divceil worked on integers, so it probably should not use x and y.

Exactly what you do with say max and min which apply to both integral and real(w) types, I do not have a perfect answer.

@bradcray
Copy link
Member

I feel disinclined to have the argument names try to encode the types of the arguments, or at least would need to hear a better argument for why this is a good/valuable direction. Specifically, I'm not excited about trying to encode types into identifiers in general (preferring to rely on the documentation, code, or tools to provide that information) and wouldn't want to set an expectation that specific argument names suggest specific types that might apply to other modules beyond Math.

@lydia-duncan
Copy link
Member Author

lydia-duncan commented Jun 22, 2023

Prep summary for an ad-hoc subteam discussion, anticipated for starting the week of July 11th, 2023.

1. What is the API being presented?

I've done a pass over all the functions in the Math and AutoMath modules as of this writing. Here is the summary of the argument names that are used and how widely:

Math:
One arg:

  • x
    • acos (2 versions, see also z)
    • acosh(2 versions, see also z)
    • asin (2 versions, see also z)
    • asinh (2 versions, see also z)
    • atan (2 versions, see also z)
    • atanh (2 versions, see also z)
    • cos (2 versions, see also z)
    • cosh (2 versions, see also z)
    • erf (all versions)
    • erfc (all versions)
    • exp (2 versions, see also z)
    • exp2 (all versions)
    • expm1 (all versions)
    • lgamma (all versions)
    • log (2 versions, see also z)
    • log10 (all versions)
    • log1p (all versions)
    • log2 (2 versions, see also val)
    • sin (2 versions, see also z)
    • sinh (2 versions, see also z)
    • tan (2 versions, see also z)
    • tanh (2 versions, see also z)
    • tgamma (all versions)
    • j0 (all versions)
    • j1 (all versions)
    • y0 (all versions)
    • y1 (all versions)
  • z
    • acos (2 versions, see also x)
    • acosh(2 versions, see also x)
    • asin (2 versions, see also x)
    • asinh (2 versions, see also x)
    • atan (2 versions, see also x)
    • atanh (2 versions, see also x)
    • cos (2 versions, see also x)
    • cosh (2 versions, see also x)
    • exp (2 versions, see also x)
    • log (2 versions, see also x)
    • sin (2 versions, see also x)
    • sinh (2 versions, see also x)
    • tan (2 versions, see also x)
    • tanh (2 versions, see also x)
  • val
    • log2 (2 versions, see also x)

Two args:

  • y, x
    • atan2 (all versions)
  • x, n
    • ldexp (all versions)
  • val, baseLog2
    • logBasePow2 (all versions)
  • a, b
    • gcd
  • n, x
    • jn (all versions)
    • yn (all versions)

AutoMath:
One arg:

  • i
    • abs (3 versions, see also r, x, im, z)
    • sgn (3 versions, see also x)
  • r
    • abs (1 version, see also i, x, im, z)
      • Note: this is the real(64) version
  • x
    • abs (1 version, see also i, r, im, z)
      • Note: this is the real(32) version
    • cbrt (all versions)
    • ceil (all versions)
    • floor (all versions)
    • isfinite (all versions)
    • isinf (all versions)
    • isnan (all versions)
    • nearbyint (all versions)
    • rint (all versions)
    • round (all versions)
    • sgn (1 version, see also i)
    • sqrt (2 versions, see also z)
    • trunc (all versions)
    • signbit (all versions)
  • im
    • abs (2 versions, see also i, r, x, z)
  • z
    • abs (1 version, see also i, r, x, im)
    • carg
    • conjg (all versions)
    • cproj
    • sqrt (2 versions, see also x)

Two args:

  • m, n
    • divceil (all versions)
    • divceilpos
    • divfloor (all versions)
    • divfloorpos
    • mod (2 versions, see also x,y)
  • x, y
    • max (2 versions - but all versions with 2 arguments)
    • min (2 versions - but all versions with 2 arguments)
    • mod (2 versions, see also m,n)

Three args (var args):

  • x, y, z .. ?k
    • max (1 version, but all versions with 3+ args)
    • min (1 version, but all versions with 3+ args)

Four args:

  • x, y, rtol, atol
    • isclose

How is it being used in Arkouda and CHAMPS?

Arkouda:

  • no uses with named arguments that I saw
    • Relies on round, mod, sqrt, log, cos, exp, abs, isnan, potentially more

CHAMPS:

  • no uses with named arguments that I saw
    • relies on log, exp, cos, sin, atan2, acos, log10, max, sqrt, tanh, min, abs, potentially more

Especially for the single argument case, it seems likely that users are not relying on named arguments to call these functions, so changing the argument names will hopefully not be too burdensome of a change.

2. What's the history of the feature, if it already exists?

Math is one of the earliest modules. Not all symbols were added immediately, but they were added in a relatively short span of time as compared to how long this module has been in existence. We recently shuffled symbols around to impact what was made available to all Chapel programs by default.

Most of what is provided is based on symbols available in C, although isclose was inspired by Python. It does not seem like we followed C's argument naming convention when we added them (though my information is based on C++'s references to the C functions as of today, rather than looking at the pages from the original date they were added, so the argument names may have changed).

3. What's the precedent in other languages, if they support it? As relevant, we especially care about:

a. Python

Quick summary: because Python doesn't care as much about type strictness, it does not have argument names that vary based on the type that is provided nor multiple overloads from the same function. My information was taken from the math package.

All one arg functions in the math package that we care about in the context of this issue use x as the name of their argument. ldexp and atan2 are both two argument functions and use different argument names, though the names are used to encode different meaning (so it makes sense that they differ). atan2 matches our name and argument ordering.

- one arg:
    - x
        - ceil
        - floor
        - isfinite
        - isinf
        - isnan
        - isqrt
        - trunc
        - cbrt
        - exp
        - exp2
        - expm1
        - log
        - log1p
        - log2
        - log10
        - sqrt
        - acos
        - asin
        - atan
        - cos
        - sin
        - tan
        - acosh
        - asinh
        - atanh
        - cosh
        - sinh
        - tanh
        - erf
        - erfc
        - gamma
        - lgamma
- two args:
    - x, i
        - ldexp
    - y, x
        - atan2
- five args:
    - a, b, *, rel_tol, abs_tol
        - isclose (I’m not sure how * is used here)
- many args:
    - *integers
        - gcd

b. C/C++

Quick summary: C/C++ is the least consistent on the names that it uses of the languages that I surveyed and I wasn't always sure about the reason for using one name or the other. My information was taken from this reference page

The most common name for the single argument case was arg. The most common names used in the two argument case was x and y. Their atan2 overloads use the same names and ordering as ours.

- one arg:
    - n
        - abs, labs, llabs
        - exp2, exp2f, exp2l
    - arg
        - fabs, fabsf, fabsl
        - expf, exp, expl
        - expm1f, expm1, expm1l
        - logf, log, logl
        - log10, log10f, log10l
        - log2, log2f, log2l
        - log1p, log1pf, log1pl
        - sqrt, sqrtf, sqrtl
        - cbrt, cbrtf, cbrtl
        - sin, sinf, sinl
        - cos, cosf, cosl
        - tan, tanf, tanl
        - asin, asinf, asinl
        - acos, acosf, acosl
        - atan, atanf, atanl
        - sinh, sinhf, sinhl
        - cosh, coshf, coshl
        - tanh, tanhf, tanhl
        - asinh, asinhf, asinhl
        - acosh, acoshf, acoshl
        - atanh, atanhf, atanhl
        - erf, erff, erfl
        - erfc, erfcf, erfcl
        - tgamma, tgammaf, tgammal
        - lgamma, lgammaf, lgammal
        - ceil, ceilf, ceill
        - floor, floorf, floorl
        - trunc, truncf, truncl
        - round, roundf, roundl, lround, lroundf, lroundl, llround, llroundf, llroundl
        - nearbyint, nearbyintf, nearbyintl
        - rint, rintf, rintl, lrint, lrintf, lrintl, llrint, llrintf, llrintl
        - isfinite (macro)
        - isinf (macro)
        - isnan (macro)
        - signbit (macro)
- two args:
    - x, y
        - fmodf, fmod, fmodl
        - fmax, fmaxf, fmaxl
        - fmin, fminf, fminl
    - y, x
        - atan2, atan2f, atan2l
    - arg, exp
        - ldexp, ldexpf, ldexpl
    - arg, iptr (out argument)
        - modf, modff, modfl

c. Rust

Quick summary: My information is mostly taken from the f64 page, though I did quickly check the f32 page and didn't initially spot any differences.

Rust defines these functions as methods on their first argument, so all of the first arguments are named self. When there are two arguments, the second argument is either named other or base, depending on what the operation is (and it seemed logically connected to the behavior of the function).

- 1 arg:
    - self
        - floor
        - ceil
        - round
        - trunc
        - abs
        - signum
        - sqrt
        - exp
        - exp2
        - ln
        - log2
        - log10
        - cbrt
        - sin
        - cos
        - tan
        - asin
        - acos
        - atan
        - sin_cos
        - exp_m1
        - ln_1p
        - sinh
        - cosh
        - tanh
        - asinh
        - acosh
        - atanh
        - is_nan
        - is_infinite
        - is_finite
- 2 args:
    - self, other
        - atan2
        - max
        - min
    - self, base
        - log

d. Swift

Quick summary: I was able to find most of these functions on the Core Graphics Functions page, which a stackoverflow post linked to, so my information is taken from there. That location seems a little unintuitive to me, so if there's a better place I should be looking, I'm all ears!

For the single argument case, these functions use x. For the two argument case, they either use lhs and rhs (including for atan2), or n and x.

- 1 arg:
    - x
        - acos
        - acosh
        - asin
        - asinh
        - atan
        - atanh
        - cbrt
        - cos
        - cosh
        - erf
        - erfc
        - exp
        - exp2
        - expm1
        - j0
        - j1
        - lgamma
        - log
        - log10
        - log1p
        - log2
        - logb
        - nearbyint
        - rint
        - sin
        - sinh
        - tan
        - tanh
        - tgamma
        - y0
        - y1
- 2 args:
    - lhs, rhs
        - atan2
        - fmax
        - fmin
    - n, x
        - jn
        - yn

e. Julia

Quick summary: I took this information from Julia's base math page. The handling for their rounding functions is not as easily comparable to ours so I haven't included it directly in this data, but the curious can take a look at it in this page. There is some variance in argument names otherwise, but it is mostly unified.

The most common argument name in the single argument case is x. The most common pair for the two argument case is x and y. Julia's atan2 uses the same argument names and ordering as we do.

- 1 arg:
    - x
        - sin
        - cos
        - sincos
        - tan
        - sind
        - cosd
        - tand
        - sincosd
        - sinh
        - cosh
        - tanh
        - asin
        - acos
        - atan (1 version, but only version with 1 argument)
        - asind
        - acosd
        - atand
        - asinh
        - acosh
        - atanh
        - log (1 version, but only version with 1 argument)
        - log2
        - log10
        - log1p
        - exp
        - exp2
        - exp10
        - modf
        - expm1
        - abs
        - abs2
        - sign
        - signbit
        - sqrt
        - cbrt
    - z
        - conj
- 2 args:
    - x, y
        - fld
        - cld
        - mod (1 version, see also x, r, and x, T)
        - fld1
        - mod1
        - fldmod1
        - minmax
    - x, r
        - mod (1 version - takes a range of values, see also x, y and x, T)
    - x, T
        - mod (1 version - takes a type, kinda a type conversion, see also x, y and x, r)
    - y, x
        - atan (1 version, but only version with 2 arguments)
    - b, x
        - log (1 version, but only version with 2 arguments)
    - x, n
        - ldexp
- varargs
    - x, y, …
        - min
        - max
        - gcd
- Round
    - The rounding functions are special and complicated.  There’s a few combinations, and some variance in the argument name based on the type

f. Go

Quick summary: I got this information from Go's math package.

The most common name in the single argument case is x. The two argument case has a lot of variability that seems to make sense based on the operation being performed, the most common pair is x and y but not by much. Go's Atan2 uses the same argument name and ordering we do.

- 1 arg:
    - x
        - Abs
        - Acos
        - Acosh
        - Asin
        - Asinh
        - Atan
        - Atanh
        - Cbrt
        - Ceil
        - Cos
        - Cosh
        - Erf
        - Erfc
        - Exp
        - Exp2
        - Expm1
        - Floor
        - Gamma
        - J0
        - J1
        - Lgamma
        - Log
        - Log10
        - Log1p
        - Log2
        - Round
        - Signbit
        - Sin
        - Sincos
        - Sinh
        - Sqrt
        - Tan
        - Tanh
        - Trunc
        - Y0
        - Y1
    - f
        - IsNaN
        - Modf
- 2 args:
    - y, x
        - Atan2
    - f, sign
        - IsInf
    - n, x
        - Jn
        - Yn
    - frac, exp
        - Ldexp
    - x, y
        - Max
        - Min
        - Mod

4. Are there known Github issues with the feature?

The most relevant known Github issue is #19025, which talks about isclose and its arguments.

Because this is covering almost the entire Math and AutoMath modules, there's a lot of side issues related to the functions themselves rather than the argument names specifically. I'll list them here for completeness, but they're mostly a distraction:

5. Are there features it is related to?

The naming of operator arguments issue is related but less important because there's no way for the user to use the argument names when calling the operator.

We might feel the need to rename arguments to similar functions in the BigInteger module.

6. How do you propose to solve the problem?

A. Leave things as is

  • Pros:
    • not all the languages surveyed are unified, we don't necessarily need to be
    • Less work
  • Cons:
    • Makes the module look a little more slapdash

B. All i, r, z, im, val arguments get turned into x, including val in logBasePow2. m,n and a,b pairs get transformed into x,y pairs. Other n uses, baseLog2, rtol, and atol will be left alone, and the order of the atan2 arguments will be unchanged.

  • Pros:
    • Provides more unity, making the module look more polished
    • Encoding the type in the argument name was unnecessary given Chapel's overload capabilities.
    • x is short, sweet, and pretty understandable as representing "a number".
    • Already has a majority, so the changes needed will be less than choosing any other name.
    • In keeping with Python, Swift, Julia and Go (for the most part)
  • Cons:
    • More work than leaving things as they are
    • Doesn't match C/C++ or Rust
    • Arguably an unimportant change in the long term
    • Theoretically could impact user codes (though that seems less likely given the sampling above)

C. Ditto B, but use arg or val instead of x.

  • Pros:
    • Provides more unity, making the module look more polished
    • Encoding the type in the argument name was unnecessary given Chapel's overload capabilities
    • arg matches a good portion of C/C++, val is already in use
  • Cons:
    • Requires changing more functions than x for little additional value
    • Doesn't match Python, Swift, Julia, Rust or Go
    • Theoretically could impact user codes (though that seems less likely given the sampling above)

D. Ditto B/C but reverse the order of the atan2 arguments (I don't support this option, putting it here for completeness, though)

  • Pros:
    • x and y will be alphabetical?
  • Cons:
    • Doesn't match any other language
    • Going to be an annoying change to implement and for users to deal with

E. Ditto B/C but rename atol and rtol for a little more clarity. Options include absTol/relTol, absoluteTolerance/relativeTolerance

  • Pros:
    • These arguments already will need a docs update to clarify them, the other names may make it more obvious what they are even without looking at the docs
  • Cons:
    • These arguments are the most likely to be named in calls of any in the module, so are the most likely to impact user code
    • Doc update could be sufficient
    • Current names match the numpy version, and Julia's isapprox

Other combinations or ideas welcome.

@lydia-duncan
Copy link
Member Author

Upon looking at logBasePow2 more closely, I think the argument baseLog2 could do with a better name. I would propose exp or expOf2

@lydia-duncan
Copy link
Member Author

Summary:
In our meeting today, we decided:

  • All i, r, z, im, val arguments will get turned into x, including val in logBasePow2.
  • m,n and a,b pairs get transformed into x,y pairs.
  • rename atol to absTol and rtol to relTol in isclose
  • Rename baseLog2 to exp in logBasePow2
  • Rename ldexp's n argument to exp

@lydia-duncan
Copy link
Member Author

We also decided that BigInteger will have similar renamings

@damianmoz
Copy link

What type does each of x or y have or does that vary?

What has Chapel traditionally called a complex argument?

Ditto for an imag type

@lydia-duncan
Copy link
Member Author

What type does each of x or y have or does that vary?

It varies. But the intention is that it will use a unified name across the types.

What has Chapel traditionally called a complex argument?

Ditto for an imag type

There are not very many places where Chapel has explicitly complex or imag arguments. The main other place aside from the Math/AutoMath modules is modules/internal/ChapelBase.chpl, where the operator definitions live. These operators all use the same argument name, regardless of the type of the argument. There are not many places not involving complex/imag where we differentiate the type of the argument using the argument name, either.

Another point to consider when thinking about this is the difference between explicit overloads for the various types and defining a single method with a generic argument. In the latter case, we get the same name for all types by default. We generally find this beneficial, allowing the user to not worry about the type being used as long as the function supports that type. It wouldn't be beneficial in the case of writeln, say, to explicitly give the argument a different name when the type was different.

When we have inconsistencies in how things are handled across modules, it makes it easy for users to notice patterns that were never intended. It's fiddly and annoying to make changes that feel so minor, but in the long run, it will help the language feel more intentional and polished, and give users an actual foundation for pattern recognition.

jabraham17 added a commit that referenced this issue Jul 20, 2023
Adds a divide by zero check to `BigInteger.mod`, `BigInteger.%` and
`BigInteger.%=` to match the divide functions (#19384). While doing this
work, implemented part of the change for argument names
(#19005 (comment)).

## Summary of changes
- add a divide by zero check to `mod`
- reimplement `mod` in terms of the `divR` with the proper rounding
argument
- add helper methods for `modTrunc` by a `integral` to reduce code
duplication
- deprecate argument names `a`/`b` on `mod` in favor of `x`/`y`

## New tests
- adds `test/library/standard/BigInteger/modByZero.chpl`
- adds `test/deprecated/BigInteger/argumentNames.chpl`

## Testing
- paratest with futures
- paratest + gasnet
- built and checked docs

[Reviewed by @bmcdonald3]

closes #19384
closes Cray/chapel-private#5094
@lydia-duncan
Copy link
Member Author

#22837 will resolve 3 of the five decisions. Will handle ldexp and isclose in separate PRs.

lydia-duncan added a commit that referenced this issue Jul 31, 2023
[reviewed by @jabraham17]

We decided in a recent discussion that we wanted to fix the argument
names in the Math and AutoMath modules so that they were consistent
with each other and most other modules.  This means that:
- All `i`, `r`, `z`, `im`, and `val` arguments will get turned into `x`,
  including `val` in `logBasePow2`.
- All `m`, `n` and `a`, `b` pairs will get transformed into `x`, `y`
pairs.
- `logBasePow2` will also change its `baseLog2` argument to `exp`

Implements part of the decision in #19005.

Deprecates all the arguments that would need to be changed to comply
with this decision in the AutoMath module (except in functions that are
already deprecated due to moving to the Math module).  Deprecates the
arguments in `logBasePow2` because it was moved more than 1 release
ago.

Note: this PR does not add deprecation messages for the functions that
were recently moved to the Math module so they would no longer be
included by default.  This is because doing so results in resolution
conflicts with the deprecations in AutoMath: we want to maintain
deprecations for two releases and it seems unlikely that any of these
functions are being called with named arguments in the first place,
due to only having a single argument (in the case of things like `cos`)
or the argument ordering not mattering (in the case of `gcd`).  The only
exception is `ldexp`, which may get renamed entirely as a result of
discussion in #19021 and so will be put off until we know the result of
that discussion (because then the version with the old arguments won't
need to be marked as "last resort" to generate the deprecation message
and so won't conflict with the version in AutoMath)

Note: also does not rename the arguments to `isclose`.  This is because
`isclose` itself is also getting renamed, so will do that at the same
time in a separate PR.

Removes the old deprecation for `logBasePow2` since it has been
deprecated for a few releases and leaving it would cause conflicts. In
doing so, left the helper function implementing it in place due to the
deprecated version of `log2` relying on it (and added a note to those
functions to ensure it will get cleaned up when they are removed).

Adds deprecation tests when deprecations were added.  Only two tests
ended up needing modifications as a result of this change - the
submitted mandelbrots now have a slightly different deprecation
message due to extending the message for divceil/etc, which we
stopped including by default in the current release cycle.

Passed a full paratest with futures
@lydia-duncan
Copy link
Member Author

#22847 resolved the isclose decision, only waiting on discussion in #19021 for handling ldexp

jabraham17 added a commit that referenced this issue Aug 7, 2023
Renames remaining BigInteger arguments to `x` and `y`, unless they
imparted a special meaning of their functionality (like `n` or `numer`).
This unifies argument names for differing types in overloaded functions.

Implements changes from
#19005 (comment)

## Summary of changes
- change arguments of `init(num)` and `init(str)` to `init(x)`
  - also changed `init=`, but that should not be a user facing change 
- change arguments of `root(result, a, b)` to `root(result, x, y)`
- change arguments of `sqrt(result, a)` to `sqrt(result, x)`
- change arguments of `invert(result, a)` to `invert(result, x)`
- change arguments of `add(result, a, b)` to `add(result, x, y)`
- change arguments of `sub(result, a, b)` to `sub(result, x, y)`
- change arguments of `mul(result, a, b)` to `mul(result, x, y)`
- change arguments of `neg(result, a)` to `neg(result, x)`
- change arguments of `abs(result, a)` to `abs(result, x)`
- change arguments of `cmp(b)` to `cmp(x)`
- change arguments of `cmpabs(b)` to `cmp(x)`
- change arguments of `and(result, a, b)` to `and(result, x, y)`
- change arguments of `com(result, a)` to `com(result, x)`
- change arguments of `xor(result, a, b)` to `xor(result, x, y)`
- change arguments of `set(num)`, `set(a)`, and `set(str)` to `set(x)`
- change arguments of `swap(a)` to `swap(x)`
- updates and extends documentation for all affected functions

## New tests
- extends `test/deprecated/BigInteger/argumentNames.chpl`

## Testing
- [x] paratest with futures
- [x] paratest + gasnet
- [x] built and checked docs

[Reviewed by @bmcdonald3]

closes #22161
closes Cray/chapel-private#5100
lydia-duncan added a commit that referenced this issue Aug 28, 2023
[reviewed by @jabraham17]

In recent discussions, we decided to:
- rename `ldexp` to `ldExp`
- rename its `n` argument to `exp`

This will make the function name easier to understand (the
word boundaries are much more clear with camelCasing)
and more accurately describe the argument.

Resolves #19005
Resolves #19021 

Updated the Random module to use the new version instead
of the deprecated one.

Updated the deprecation message in AutoMath to mention
that the version in Math has a different name now.  Updated
the test tracking that error as well

Updated the doc link reference to use the new name.

Added a test of the deprecation message.

Updated a GPU test that was using the old name.

Passed a full paratest with futures
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants