Opened 10 years ago

# Binomial of integer (mod n) returns integer

Reported by: Owned by: scotts AlexGhitza major sage-6.4 basic arithmetic binomial coefficient modulo sd35 Sam Scott, Marco Streng Colton Pauderis, Johan Bosman, Marco Streng N/A todo

```sage: R = Integers(6)
sage: binomial(R(5), R(2))
10
sage: binomial(R(5), R(2)).parent()
Integer Ring
```

But `binomial(R(5), R(2))` is nonsense, both as an element of ZZ and as an element of R:

```sage: binomial(5, 2)
10
sage: binomial(11, 2)
55
sage: binomial(5, 8)
0
```

On input `binomial(x, y)`, what Sage should do instead is the following:

• If the parent of y is Zmod(n) rather than ZZ, a `TypeError` should be raised.
• (This seems to be fixed by #17852) If factorial(y) is zero or a zero-divisor in the parent of x, a `ZeroDivisionError` should be raised. This is automatic if one computes binomial(x, y) simply as
```x.parent()(prod([x-k for k in range(y)]) / factorial(y))
```

Apply:

### comment:1 Changed 10 years ago by scotts

• Status changed from new to needs_review

Attached patch should add this functionality with no impact on speed of standard integer/float/etc binomial calculation.

### comment:2 Changed 10 years ago by cpauderis

• Status changed from needs_review to positive_review

Looks good to me: positive review.

### comment:3 Changed 10 years ago by scotts

• Authors set to Sam Scott

### comment:4 Changed 10 years ago by scotts

Patch added to deal with the case where the input was a primitive python int.

Thanks to Colton (cpauderis) for catching that one.

### comment:5 Changed 10 years ago by johanbosman

• Status changed from positive_review to needs_work

The line

``` Test of integers modulo n:
}}{
should end in two colons (for docbuilding).  Furthermore,
{{{
return x.parent()(binomial(ZZ(x), m, **kwds))
}}}
is inefficient when computing modulo a small number n: the binomial coefficient over ZZ may be huge, whereas its reduction modulo n will be small.  It is therefore better to to the entire calculation modulo n.
```

### comment:6 follow-up: ↓ 9 Changed 10 years ago by johanbosman

The line

``` Test of integers modulo n:
```

should end in two colons (for docbuilding). Furthermore,

``` return x.parent()(binomial(ZZ(x), m, **kwds))
```

is inefficient when computing modulo a small number n: the binomial coefficient over ZZ may be huge, whereas its reduction modulo n will be small. It is therefore better to to the entire calculation modulo n.

### comment:8 Changed 10 years ago by mstreng

• Dependencies set to #11417
• Keywords coefficient added; coefficiant removed
• Reviewers set to Colton Pauderis, Johan Bosman, Marco Streng
• Work issues set to rebase on top of #11417, reST formatting issue, improve efficiency

Patch is incompatible with #11417, which is already merged.

### comment:9 in reply to: ↑ 6 ; follow-up: ↓ 11 Changed 10 years ago by scotts

Furthermore,

``` return x.parent()(binomial(ZZ(x), m, **kwds))
```

is inefficient when computing modulo a small number n: the binomial coefficient over ZZ may be huge, whereas its reduction modulo n will be small. It is therefore better to to the entire calculation modulo n.

I definitely agree, the changes I was proposing were to simply try and return a more sensible type.

Since I am new to sage, I was trying to keep as much of the existing algorithm intact, so that it wouldn't have any unexpected behaviour. For example, currently it seems that sage uses Peri to calculate the usual binomial coefficient. So I wasn't sure if the implementation for modulo n would belong here.

### comment:10 Changed 10 years ago by scotts

That should obviously be Pari, not Peri.

### comment:11 in reply to: ↑ 9 Changed 10 years ago by mstreng

I definitely agree, the changes I was proposing were to simply try and return a more sensible type.

Since I am new to sage, I was trying to keep as much of the existing algorithm intact, so that it wouldn't have any unexpected behaviour. For example, currently it seems that sage uses Peri to calculate the usual binomial coefficient. So I wasn't sure if the implementation for modulo n would belong here.

That sounds sensible, this is a bugfix patch and the inefficiency is not introduced by your patch. So if you don't want to, you don't have to improve the speed. However, since you're editing anyway, you might as well. It's up to you. The other issues

• documentation that does not build correctly
• a conflicting patch has already been merged

do need to be resolved though.

For the first issue, after building Sage, use `sage -docbuild reference html` to rebuild the documentation and see if all the changed documentation looks good (in this case, replacing : by :: will probably be enough).

As for the second issue, you should not write two independent patches editing the same part of a file. They can't be applied after each other, because the second-to-be-applied can't find the piece of code that it should change. You should write your patch for #12179 on top of a copy of Sage that has #11417 applied.

### comment:12 Changed 9 years ago by scotts

• Status changed from needs_work to needs_review

Finally got around to reinstalling sage after a hard-drive failure. Hoping to get back on track with contributing to sage. Sorry for the incredibly slow response.

Hopefully this should tie up this loose end.

While I do agree that there is room for improving the speed of calculating binomial coefficients mod N, I don't feel like it's worth bloating the binomial function with multiple extra lines of code for something which doesn't seem to be used much.

Perhaps it could be added as a feature at a later stage when the need is there.

However, I feel that this patch adequately addresses the original issue: that elements are treated as integers and returned as such, with no attempt to return them to their original class. This could potentially help with other cases.

Thanks for the advice with regards to the other issues.

### comment:13 follow-ups: ↓ 14 ↓ 22 Changed 9 years ago by mstreng

• Description modified (diff)
• Status changed from needs_review to needs_work
• Work issues rebase on top of #11417, reST formatting issue, improve efficiency deleted

Oops, I never got around to looking at the mathematics while reviewing your rest-formatting before. The problem is that binomial(x, y) is in general not defined for x and y integers modulo n. I'll update the ticket description appropriately.

Allowing y to be anything other than an integer makes no sense to me. What would the definition be? In any case, the output has to be in the ring containing x, and will definitely depend on the exact integer y, and not just on y modulo any n. For example:

```sage: binomial(5,-2) # bionomial(5, Zmod(2)(0)) = ???
0
sage: binomial(5,0)
1
sage: binomial(5,2)
10
sage: binomial(5,4)
5
sage: binomial(5,6)
0
```

Summary: if y is an element of Zmod(n), then surely a `ValueError` must be raised.

The situation when x is an element of Zmod(n) is more complicated, but cannot be always automatically allowed:

```sage: binomial(1, 3) % 3 # binomial(Zmod(3)(1), 3) = ???
0
sage: binomial(4, 3) % 3
1
sage: binomial(7, 3) % 3
2
```

So the correct answer to `binomial(Zmod(3)(1), 3)` is "any integer modulo 3". Just returning Zmod(3)(0) is wrong. It would be ok to raise an error, I'd say `ZeroDivisionError`, because of the following completely general formula:

```sage: binomial(x, 3)
1/6*x^3 - 1/2*x^2 + 1/3*x
```

There are of course some cases that we can still allow. Suppose the input is `binomial(Zmod(n)(x), y)`.

• If n is coprime to factorial(y), then all divisions are allowed, so the output should again be a well-defined element of Zmod(n). This is what happens with the code `x.parent()(prod([x-k for k in range(y)]) / factorial(y))` from the ticket description.
• In general, we could allow the output be an element of Zmod(n / gcd(factorial(y), n)) instead of Zmod(n). This would mean no `ZeroDivisionError`s are raised, but Zmod(1)(0) is returned instead. This makes sense in the theory and practice of p-adic power series and formal groups, but may not be what the user expects, so I left this out of the new ticket description.

Last edited 9 years ago by mstreng (previous) (diff)

### comment:14 in reply to: ↑ 13 Changed 9 years ago by mstreng

• Description modified (diff)

Summary: if y is an element of Zmod(n), then surely a `ValueError` must be raised.

no, wait, I meant `TypeError`

### comment:15 Changed 9 years ago by mstreng

• Description modified (diff)

### comment:16 Changed 8 years ago by mstreng

• Authors changed from Sam Scott to Sam Scott, Marco Streng
• Dependencies #11417 deleted
• Description modified (diff)

apply only 12179_new.patch

### comment:17 Changed 8 years ago by mstreng

New version, but it screws up lots of symbolic things. Perhaps a few special cases, like binomial(n, n) always returning 1, will fix this. I'm done with this for now.

### comment:18 Changed 8 years ago by jdemeyer

• Milestone changed from sage-5.11 to sage-5.12

### comment:19 Changed 8 years ago by vbraun_spam

• Milestone changed from sage-6.1 to sage-6.2

### comment:20 Changed 8 years ago by vbraun_spam

• Milestone changed from sage-6.2 to sage-6.3

### comment:21 Changed 7 years ago by vbraun_spam

• Milestone changed from sage-6.3 to sage-6.4

### comment:22 in reply to: ↑ 13 Changed 7 years ago by rws

So the correct answer to `binomial(Zmod(3)(1), 3)` is "any integer modulo 3". Just returning Zmod(3)(0) is wrong. It would be ok to raise an error, I'd say `ZeroDivisionError`

Pari does this too.

There are of course some cases that we can still allow. Suppose the input is `binomial(Zmod(n)(x), y)`.

Example with Pari:

```? binomial(Mod(7,11),3)
%3 = Mod(2, 11)
```

### comment:23 follow-up: ↓ 24 Changed 7 years ago by vdelecroix

• Milestone changed from sage-6.4 to sage-duplicate/invalid/wontfix
• Status changed from needs_work to needs_review

Hello,

I just discover this ticket. During a cleanup in `sage.rings.arith` (#17852) I took care of this case. I propose to close this one as duplicate. With the branch applied we got

```sage: from sage.rings.arith import binomial
sage: R = Integers(6)
sage: binomial(R(5), R(2))
Traceback (most recent call last):
...
ZeroDivisionError: Inverse does not exist.
sage: R = Integers(21)
sage: binomial(R(5), R(2))
10
sage: binomial(R(5), R(2)).parent()
Ring of integers modulo 21
```

Vincent

### comment:24 in reply to: ↑ 23 ; follow-up: ↓ 25 Changed 7 years ago by mstreng

• Description modified (diff)
• Milestone changed from sage-duplicate/invalid/wontfix to sage-6.4
• Status changed from needs_review to needs_work

sage: R = Integers(21) sage: binomial(R(5), R(2)) 10

This should be `TypeError`, because `binomial(x,y)` makes no sense when y is an element of `R`. It only makes sense when y is an integer. For example, binomial(5, 2) = 10, but binomial(5, 2+21) = 0.

So of the two points in the ticket description, the work in #17852 fixes the second one, but the first one is still open.

### comment:25 in reply to: ↑ 24 Changed 7 years ago by vdelecroix

sage: R = Integers(21) sage: binomial(R(5), R(2)) 10

This should be `TypeError`, because `binomial(x,y)` makes no sense when y is an element of `R`. It only makes sense when y is an integer. For example, binomial(5, 2) = 10, but binomial(5, 2+21) = 0.

So of the two points in the ticket description, the work in #17852 fixes the second one, but the first one is still open.

Right!

### comment:26 Changed 7 years ago by vdelecroix

Hi,

Ticket #17852 is in pass to be positively reviewed. Let me summarize what will change when calling `rings.arith.binomial(x,y)`:

• `y` must be an integer (actually, I only asked that `ZZ(y)` does work and the first lines of code do `y = ZZ(y)`)
• the output type is always the type of `x`
• if `factorial(y)` is not invertible a `ZeroDivisionError` is raised (I checked the behavior on many finite rings that I was able to think of)

Vincent

### comment:27 Changed 6 years ago by jakobkroeker

• Stopgaps set to todo
Note: See TracTickets for help on using tickets.