Skip to content

Conversation

@aravindh-krishnamoorthy
Copy link
Collaborator

@aravindh-krishnamoorthy aravindh-krishnamoorthy commented Feb 14, 2025

Introduction

Gamma[] function has the rule:

Gamma[1 + z_] :> z!

However, this rule is only valid when z is an integer.

The fix changes the rule to apply only to integral z.

Test

Compare Gamma[1+x] between MMA and Mathics.

rules = {
"Gamma[z_, x0_, x1_]": "Gamma[z, x0] - Gamma[z, x1]",
"Gamma[1 + z_]": "z!",
"Gamma[1 + z_?IntegerQ]": "z!",
Copy link
Member

@rocky rocky Feb 14, 2025

Choose a reason for hiding this comment

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

Suggested change
"Gamma[1 + z_?IntegerQ]": "z!",
"Gamma[1 + z_?NumberQ]": "z!",

Factorial is defined for numbers outside of integers. For example 2.5! is 3.32335.

For those numbers like E, Pi, and I where factorial is not defined, Mathics3 Factorial returns the expression unevaluated without an error; and this is the desired behavior we want in this case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hello, @rocky. This should be _?IntegerQ because for non-integer x factorial is just defined as Gamma[1+x] so there's no point converting back and forth... Whereas for integers, notation-wise, the having x! looks good...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

MMA, e.g., follows this.

Copy link
Member

Choose a reason for hiding this comment

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

Hello, @rocky. This should be _?IntegerQ because for non-integer x factorial is just defined as Gamma[1+x] so there's no point converting back and forth... Whereas for integers, notation-wise, the having x! looks good...

I just pulled the branch and tried Gamma[1 + 2.5] and this code works. So I don't have a strong opinion one way or the other.

But from a consistency standpoint, if x! looks good for integer x, it should also look good for real x as well in my opinion. Or remove both. It is just that having it go one way for integers and the other way for reals seems weird to me.

Copy link
Member

Choose a reason for hiding this comment

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

Hello, @rocky. This should be _?IntegerQ because for non-integer x factorial is just defined as Gamma[1+x] so there's no point converting back and forth... Whereas for integers, notation-wise, the having x! looks good...

One things I was going to bring up but didn't because I didn't think were were going to go at the end with an integer test: the preferred way to express this is more simply: _Integer. When what you are testing is defined type, you can drop the "?".

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

As you're aware, factorial was first defined for positive integers, and then analytically continued to non-integers and complex numbers via the Gamma function. To me, it feels strange to see x! notation for non-integral x. Also, MMA does not perform this simplification for arbitrary x.

In[7]:= FullSimplify[Gamma[1+x]]
Out[7]= Gamma[1+x]
In[8]:= FullSimplify[Gamma[1+x], Element[x, Integers] && x > 0]
Out[8]= x!

But, I will leave this open and wait for others to provide an opinion as well.

Copy link
Member

Choose a reason for hiding this comment

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

Ok - thanks for the further explanation. There is a lot of leeway for matters of taste. And say you say, WMA does not FuillSimplify with Gamma to factorial when Integer is replaced with Rationals or Reals.

So aside from the fact that there is a failing test, I am fine with this. @mmatera ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

So aside from the fact that there is a failing test, I am fine with this. @mmatera ?

@rocky on my end all tests are passing:

================== 2870 passed, 364 skipped, 14 xfailed, 16 xpassed, 3 warnings in 131.40s (0:02:11) ===================

Could you please let me know which test is failing? Perhaps I can fix it if it's related to this change...

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Look at the actions tab. See for example: https://github.com/Mathics3/mathics-core/actions/runs/13345211275/job/37274817229?pr=1387

This is a problem because MMA evaluates it to a factorial. I'll take a deeper look...

@rocky
Copy link
Member

rocky commented Feb 18, 2025

Introduction

Gamma[] function has the rule:

Gamma[1 + z_] :> z!

However, this rule is only valid when z is an integer.

Actually, according to WMA and the example Product[k, {k, 3, n}], it is valid always valid. So what is currently in the code base matches WMA better.

Previously, we had discussed how this could be expanded to Numbers rather than Integers. But WMA behavior takes this to be rewritten to be valid no matter what z is. Go figure.

@aravindh-krishnamoorthy
Copy link
Collaborator Author

aravindh-krishnamoorthy commented Feb 18, 2025

Introduction

Gamma[] function has the rule:

Gamma[1 + z_] :> z!

However, this rule is only valid when z is an integer.

Actually, according to WMA and the example Product[k, {k, 3, n}], it is valid always valid. So what is currently in the code base matches WMA better.

Previously, we had discussed how this could be expanded to Numbers rather than Integers. But WMA behavior takes this to be rewritten to be valid no matter what z is. Go figure.

In[7]:= FullSimplify[Gamma[1+x]]
Out[7]= Gamma[1+x]
In[8]:= FullSimplify[Gamma[1+x], Element[x, Integers] && x > 0]
Out[8]= x!

@rocky as I showed you in the comment before, Gamma[1 + z_] :> z! is not the rule used for simplifying the Product. This rule seems wrong to me because for any non-integral z, factorial is defined via the Gamma function. So this would be a back and forth. I think MMA is also wrong wrt handling Product[k, {k, 3, n}] and seems to have specialised rules for those to help schoolkids maybe.

@rocky
Copy link
Member

rocky commented Feb 18, 2025

Introduction

Gamma[] function has the rule:

Gamma[1 + z_] :> z!

However, this rule is only valid when z is an integer.

Actually, according to WMA and the example Product[k, {k, 3, n}], it is valid always valid. So what is currently in the code base matches WMA better.
Previously, we had discussed how this could be expanded to Numbers rather than Integers. But WMA behavior takes this to be rewritten to be valid no matter what z is. Go figure.

In[7]:= FullSimplify[Gamma[1+x]]
Out[7]= Gamma[1+x]
In[8]:= FullSimplify[Gamma[1+x], Element[x, Integers] && x > 0]
Out[8]= x!

@rocky as I showed you in the comment before, Gamma[1 + z_] :> z! is not the rule used for simplifying the Product.

I don't know what comment you are referring to. We are coming to see that what is done in Simplify doesn't have to match what is done in a rewrite rule.

So this would be a back and forth.

The back and forth is because we have new information. When information changes, assessments might change.

A test that someone wrote to match a particular (WMA) behavior started failing. In investigating that, I see that things are a little bit different than I had understood before. See below.

To me, it feels strange to see x! notation for non-integral x.

It may be strange, but factorial is defined for non-integral x:

>>> from sympy import factorial
>>> factorial(2.3)
2.68343738195577
>>> from mpmath import fac
>>> fac(2.3)
mpf('2.6834373819557684')

And Concrete Mathmatics by Graham, Knuth and Patashnik (the first reference in the Wiki page on factorial) defines factorial using the postfix operator "!" for non-integer numbers this way too.

This rule seems wrong to me because for any non-integral z, factorial is defined via the Gamma function.

Gamma and Factorial are related. The latter is the more specialized function though, and specialized functions are used when they apply.

I think MMA is also wrong wrt handling Product[k, {k, 3, n}] and seems to have specialised rules for those to help schoolkids maybe.

We've come across situations where WMA does things differently from convention; it might even be that WMA in some situations has changed conventional mathematics expectations, (but I don't think that is the case here).

In any event, in the past what we have invariably done is go with WMA's behavior rather than what we feel is right.

Of course, there is some leeway in how to implement that behavior, but it seems to me that right now Gamma[1 + z_] :> z! matches current WMA behavior better than Gamma[1 + z_Integer] :> z!. Perhaps one day WMA will see the light and remove this. At that time, we can make the changes to the code base.

@aravindh-krishnamoorthy
Copy link
Collaborator Author

@rocky Unfortunately, Gamma is still inconsistent with MMA (see below) and this behaviour makes my day-to-day work with Mathics a bit difficult. But, I agree that MMA has many weird quirks (another example is that using 1. causes numerical evaluation similar to older languages like Axiom), and I guess 100% compatibility means these have to be carried forward too...

(* Inconsistent *)
In[1]:= Gamma[1+x]
Out[1]= x!

(* Consistent *)
In[2]:= Product[k, {k, 3, n}]
Out[2]= n! / 2

(* Not (x+1)! *)
In[3]:= Gamma[2+x]
Out[3]= Gamma[2 + x]

I'd still vote not to have the generic rule Gamma[1 + z_] :> z! but leave the final call up to you.

@mmatera
Copy link
Contributor

mmatera commented Feb 18, 2025

I was not follow this discussion very closely, but as a general rule, except when there is a very good reason, we go for the most compatible behavior.

@rocky
Copy link
Member

rocky commented Feb 19, 2025

Unfortunately, Gamma is still inconsistent with MMA (see below) and this behaviour makes my day-to-day work with Mathics a bit difficult.

I certainly don't want to make day-to-day work with Mathics more difficult. I would appreciate it if you would give specific details here.

Here is a recap of where things stand.

This PR makes the expression Gamma[1+x] give the same results as WMA, but at the expense of making the expression Product[k, {3, n}] give different results. And this is why the tests currently fail: we are testing compatibility for Product, but not Gamma.

I'd also like to note that removing the rule Gamma[1 + z_] :> z! altogether has the same effect. There is no benefit, as far as I can see in terms of WMA compatibility, for Gamma[1 + z_Integer] :> z!

If we want to match WMA for both Product and Gamma, then we could try to do something to the Product code.

Here is a trace of an evaluation for this with SymPy tracing on:

image

We might be able to detect the Factorial via Pochhammer and it is possible that is also what WMA is doing as well.

@aravindh-krishnamoorthy
Copy link
Collaborator Author

#1395 does this better...

rocky added a commit that referenced this pull request Feb 21, 2025
We want to match the behavior of WMA for `Gamma` and `Product`.
Specifically:

```
Product[k, {k, 3, n}] == n! / 2
Gamma[1+x] == Gamma[1+x]
```

Supercedes #1387
@aravindh-krishnamoorthy aravindh-krishnamoorthy deleted the gammafix branch February 24, 2025 09:32
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.

4 participants