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

B(1) = +½ set 2: generalised Bernoulli function #23984

Merged
merged 2 commits into from Aug 28, 2022

Conversation

Parcly-Taxel
Copy link
Contributor

@Parcly-Taxel Parcly-Taxel commented Aug 28, 2022

References to other Issues or PRs

Part 2 of the changes formerly included in #23926, building upon #23973. The changes here are also the actual changes that will fix #23866.

Brief description of what is fixed or changed

This PR generalises bernoulli() to the generalised Bernoulli function described in Peter Luschny's "An introduction to the Bernoulli function":
$$B(s,a)=\begin{cases}1&s=0\\-s\zeta(1-s,a)&s\ne0\end{cases}$$
In the process it also changes bernoulli(1) to +1/2.

Other comments

Since the Genocchi numbers are currently defined in terms of the Bernoulli numbers, this also changes genocchi(1) to -1; a later PR will generalise this integer sequence to the two-complex-argument generalised Genocchi function in the same vein as the generalised Bernoulli function, though.

Luschny also defines the central Bernoulli numbers and polynomials, but I have not made a new SymPy function out of that because its generalisation to arbitrary complex arguments can be expressed as $2^s B(s,\frac{a+1}2)$.

Release Notes

  • functions
    • BREAKING: bernoulli(1) is now 1/2 instead of -1/2 and genocchi(1) is now -1 instead of 1.
    • bernoulli() now accepts complex arguments, implementing Luschny (2020).

In Peter Luschny's "An introduction to the Bernoulli function"
(https://arxiv.org/abs/2009.06743, will just be called Luschny in future
commits) a generalised (two-argument) Bernoulli function is defined. One
of the given representations is

          ⎧      1         for s = 0
B(s, a) = ⎨
          ⎩-s⋅ζ(1 - s, a)  otherwise

When s is a nonnegative integer this function reproduces the Bernoulli
polynomials; when in addition a = 1 (the default choice for the Hurwitz
zeta function's second argument, yielding Riemann's zeta) the Bernoulli
numbers are obtained. Hence B(1) = +1/2, which is how Jakob Bernoulli
originally defined his numbers in Ars Conjectandi (1713); this choice
gives the simpler form/wider range of validity than B(1) = -1/2 for very
many equations like Faulhaber's formula and the Euler–Maclaurin
summation formula.

We use the zeta representation of the Bernoulli function, despite
Luschny striving to avoid such dependence by defining it as an integral,
since mpmath provides a correct implementation of the Hurwitz zeta
function; SymPy's zeta() was also aligned to Hurwitz zeta in sympy#23973.
@sympy-bot
Copy link

sympy-bot commented Aug 28, 2022

Hi, I am the SymPy bot (v167). I'm here to help you write a release notes entry. Please read the guide on how to write release notes.

Your release notes are in good order.

Here is what the release notes will look like:

This will be added to https://github.com/sympy/sympy/wiki/Release-Notes-for-1.12.

Click here to see the pull request description that was parsed.
#### References to other Issues or PRs

Part 2 of the changes formerly included in #23926, building upon #23973. The changes here are also the actual changes that will fix #23866.

#### Brief description of what is fixed or changed

This PR generalises `bernoulli()` to the generalised Bernoulli function described in Peter Luschny's "[An introduction to the Bernoulli function](https://arxiv.org/abs/2009.06743)":
$$B(s,a)=\begin{cases}1&s=0\\\\-s\zeta(1-s,a)&s\ne0\end{cases}$$
In the process it also changes `bernoulli(1)` to `+1/2`.

#### Other comments

Since the Genocchi numbers are currently defined in terms of the Bernoulli numbers, this also changes `genocchi(1)` to `-1`; a later PR will generalise this integer sequence to the two-complex-argument generalised Genocchi function in the same vein as the generalised Bernoulli function, though.

Luschny also defines the _central Bernoulli numbers_ and _polynomials_, but I have not made a new SymPy function out of that because its generalisation to arbitrary complex arguments can be expressed as $2^s B(s,\frac{a+1}2)$.

#### Release Notes

<!-- BEGIN RELEASE NOTES -->
* functions
    * BREAKING: `bernoulli(1)` is now `1/2` instead of `-1/2` and `genocchi(1)` is now `-1` instead of `1`.
    * `bernoulli()` now accepts complex arguments, implementing [Luschny (2020)](https://arxiv.org/abs/2009.06743).
<!-- END RELEASE NOTES -->

Update

The release notes on the wiki have been updated.

@Parcly-Taxel Parcly-Taxel mentioned this pull request Aug 28, 2022
10 tasks
@github-actions
Copy link

github-actions bot commented Aug 28, 2022

Benchmark results from GitHub Actions

Lower numbers are good, higher numbers are bad. A ratio less than 1
means a speed up and greater than 1 means a slowdown. Green lines
beginning with + are slowdowns (the PR is slower then master or
master is slower than the previous release). Red lines beginning
with - are speedups.

Significantly changed benchmark results (PR vs master)

Significantly changed benchmark results (master vs previous release)

       before           after         ratio
     [41d90958]       [a3826118]
-         987±3μs          626±2μs     0.63  solve.TimeSparseSystem.time_linear_eq_to_matrix(10)
-     2.84±0.01ms         1.16±0ms     0.41  solve.TimeSparseSystem.time_linear_eq_to_matrix(20)
-     5.62±0.01ms         1.70±0ms     0.30  solve.TimeSparseSystem.time_linear_eq_to_matrix(30)

Full benchmark results can be found as artifacts in GitHub Actions
(click on checks at the top of the PR).

@oscarbenjamin
Copy link
Contributor

I think this looks good. Does anyone else have any opinions about this?

The discussion for the rationale for this change is in the linked issue and reference cited above.

Not a new problem but this should also get fixed:

In [1]: lambdify(x, bernoulli(x))(1.2)
Out[1]: array([ 1. , -0.5])

That's scipy's bernoulli function which is inconsistent with SymPy's.

@Parcly-Taxel
Copy link
Contributor Author

Not a new problem but this should also get fixed: … That's scipy's bernoulli function which is inconsistent with SymPy's.

I think I've got the solution for that. Put the following code in sympy/printing/numpy.py:

    def _print_bernoulli(self, expr):
        return self._print(expr._eval_rewrite_as_zeta(*expr.args))

Does this make sense and should I commit it in this PR?

@oscargus
Copy link
Contributor

I think that makes sense and could go into this. Possibly with a small comment of the type # scipy's bernoulli in inconsistent with SymPy's so rewrite and a test. Thanks!

How surprising that it's a one-liner – but manipulating an expression in
a printing module is very sneaky.
@oscarbenjamin
Copy link
Contributor

Okay let's get this in.

Thanks @Parcly-Taxel!

@oscarbenjamin oscarbenjamin merged commit 930eb70 into sympy:master Aug 28, 2022
@Parcly-Taxel Parcly-Taxel deleted the plushalf branch August 28, 2022 23:35
Parcly-Taxel added a commit to Parcly-Taxel/sympy that referenced this pull request Aug 29, 2022
Luschny defines the generalised Genocchi function in terms of the
generalised Bernoulli function (implemented in sympy#23984) as

G(s, a) = 2^s (B(s, a/2) - B(s, (a+1) / 2))

which can be rather easily rewritten as

G(s, a) = 2 (B(s, a) - 2^s B(s, (a+1) / 2))

which is the form used here. When s is a nonnegative integer this
function reproduces the (so-called) Genocchi polynomials, which have
integer coefficients; when in addition a = 1 the Genocchi numbers are
obtained, with G(1) = -1.

The second term in the rewritten expression (2^s B(s, (a+1)/2)) is what
Luschny calls the central Bernoulli function, which reduces to the
central Bernoulli numbers when a = 0.
@asmeurer
Copy link
Member

The lambdified bernoulli was already wrong anyway. scipy.special.bernoulli(n) returns the first n Bernoulli numbers. The equivalent for SymPy would be scipy.special.bernoulli(n)[-1].

@fchapoton
Copy link
Contributor

This is a very wrong move and should be undone. You have been tricked to do that by people with no authority whatsoever. This is not good to try changing the stable convention used by the large majority of mathematical papers.

@Parcly-Taxel
Copy link
Contributor Author

Parcly-Taxel commented Sep 12, 2022

This is a very wrong move and should be undone. You have been tricked to do that by people with no authority whatsoever. This is not good to try changing the stable convention used by the large majority of mathematical papers.

You have no authority here to decide what $B_1$ is. Meurer, Benjamin and I have already decided that it will change to $+\frac12$ in 1.12 and that such change is entirely OK; I layered my PRs in such a way to lock this change in. It's too late to turn back.

@edgarcosta
Copy link

@Parcly-Taxel, I think you have just agreed that you tricked them:

I layered my PRs in such a way to lock this change in. It's too late to turn back.

@oscarbenjamin
Copy link
Contributor

I layered my PRs in such a way to lock this change in. It's too late to turn back.

It's not too late to revert anything. Nothing has been released yet.

@Parcly-Taxel
Copy link
Contributor Author

I think you have just agreed that you tricked them:

There was no trickery involved here. There is also no authority defining what the Bernoulli numbers are, just as there is no authority defining $\pi$ or the real numbers. I merely wrote code illuminating mathematical truth, which every CAS should strive to do.

@asmeurer
Copy link
Member

Can we please keep the discussions here civil and within our code of conduct. Keep the discussion to the technical merits and specific mathematical details. Claiming that people are "playing tricks", especially without any arguments to back it up, is not a helpful avenue of discussion and is unnecessarily antagonistic.

Also, it would be best to have this discussion at #23866. There's a lot of PRs relating to this issue in SymPy, but that's the main issue for bernoulli(1) and related changes. And as I requested elsewhere, if there are relevant discussions happening on other forums (such as Sage forums), please cross-link them there as many of us are not subscribed to them.

As Oscar stated, this is not yet "locked in", because it hasn't been in a release. That was done on purpose because we knew this could be controversial.

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.

The value of bernoulli(1)
7 participants