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

Make BooleanPolynomial.variables() way faster #35510

Merged
merged 1 commit into from
Apr 23, 2023

Conversation

tornaria
Copy link
Contributor

📚 Description

The implementation of BooleanMonomialVariableIterator is a bit slow. It is based in polybori's PBMonomVarIter which implements an iterator over the variables of a boolean monomial which is slow.

However there is PBMonomIter which implements an iterator over the indices of the variables which seems to be faster; the variables can then be retrieved from the ring itself.

This is what we do in this commit. Performance is way better.

I don't know why this difference, maybe because the variables are strings and this suffers from allocation, while indices are just 32 bit ints.

Before this commit:

sage: R = BooleanPolynomialRing(512)
sage: f = sum(R.gen(randrange(512)) for _ in range(20))
sage: timeit('vs = f.variables()')
125 loops, best of 3: 6.1 ms per loop

After this commit:

sage: R = BooleanPolynomialRing(512)
sage: f = sum(R.gen(randrange(512)) for _ in range(20))
sage: timeit('vs = f.variables()')
625 loops, best of 3: 13.7 μs per loop

Another example taken from a doctest for the method PolynomialSequence_generic.connected_components() in src/sage/rings/polynomial/multi_polynomial_sequence.py:

Before this commit:

sage: sr = mq.SR(2,4,4,8,gf2=True,polybori=True)
sage: F, s = sr.polynomial_system()
sage: Fz = Sequence(F.part(2))
sage: %time vs = Fz.variables()
CPU times: user 603 ms, sys: 2.42 s, total: 3.03 s
Wall time: 3.03 s
sage: %time len(Fz.connected_components())
CPU times: user 2.59 s, sys: 10.9 s, total: 13.5 s
Wall time: 13.5 s
4

After this commit:

sage: sr = mq.SR(2,4,4,8,gf2=True,polybori=True)
sage: F, s = sr.polynomial_system()
sage: Fz = Sequence(F.part(2))
sage: %time vs = Fz.variables()
CPU times: user 9.77 ms, sys: 994 µs, total: 10.8 ms
Wall time: 13.7 ms
sage: %time len(Fz.connected_components())
CPU times: user 70.8 ms, sys: 8.27 ms, total: 79.1 ms
Wall time: 82.5 ms
4

📝 Checklist

  • The title is concise, informative, and self-explanatory.
  • The description explains in detail what this PR is about.

The implementation of `BooleanMonomialVariableIterator` is a bit slow.
It is based in polybori's `PBMonomVarIter` which implements an iterator
over the variables of a boolean monomial which is slow.

However there is `PBMonomIter` which implements an iterator over the
indices of the variables which seems to be faster; the variables can
then be retrieved from the ring itself.

This is what we do in this commit. Performance is way better.

I don't know why this difference, maybe because the variables are strings
and this suffers from allocation, while indices are just 32 bit ints.

Before this commit:
```
sage: R = BooleanPolynomialRing(512)
sage: f = sum(R.gen(randrange(512)) for _ in range(20))
sage: timeit('vs = f.variables()')
125 loops, best of 3: 6.1 ms per loop
```

After this commit:
```
sage: R = BooleanPolynomialRing(512)
sage: f = sum(R.gen(randrange(512)) for _ in range(20))
sage: timeit('vs = f.variables()')
625 loops, best of 3: 13.7 μs per loop
```

Another example taken from a doctest for the method
`PolynomialSequence_generic.connected_components()` in
`src/sage/rings/polynomial/multi_polynomial_sequence.py`:

Before this commit:
```
sage: sr = mq.SR(2,4,4,8,gf2=True,polybori=True)
sage: F, s = sr.polynomial_system()
sage: Fz = Sequence(F.part(2))
sage: %time vs = Fz.variables()
CPU times: user 603 ms, sys: 2.42 s, total: 3.03 s
Wall time: 3.03 s
sage: %time len(Fz.connected_components())
CPU times: user 2.59 s, sys: 10.9 s, total: 13.5 s
Wall time: 13.5 s
4
```

After this commit:
```
sage: sr = mq.SR(2,4,4,8,gf2=True,polybori=True)
sage: F, s = sr.polynomial_system()
sage: Fz = Sequence(F.part(2))
sage: %time vs = Fz.variables()
CPU times: user 9.77 ms, sys: 994 µs, total: 10.8 ms
Wall time: 13.7 ms
sage: %time len(Fz.connected_components())
CPU times: user 70.8 ms, sys: 8.27 ms, total: 79.1 ms
Wall time: 82.5 ms
4
```
@tornaria tornaria requested a review from fchapoton April 15, 2023 03:14
tornaria added a commit to tornaria/sage that referenced this pull request Apr 15, 2023
Simplify the code; also, the previous code has to iterate over variables
of the sequence twice (this is really bad before sagemath#35510)
@github-actions
Copy link

Documentation preview for this PR is ready! 🎉
Built with commit: 3cea241

tornaria added a commit to tornaria/sage that referenced this pull request Apr 15, 2023
Simplify the code; also, the previous code has to iterate over variables
of the sequence twice (this is really bad before sagemath#35510)

Moreover, now we add a clique between the variables of each polynomial,
so it agrees with the description (the code before used to add just a
spanning tree of the clique -- a star).

This makes this method a little bit slower for the purposes of
`connected_components()` (for which adding a star is equivalent).

However, sagemath#35518 will rewrite `connected_components()` without using
`connection_graph()` so this is not really a problem.
@vbraun vbraun merged commit 0ff23f6 into sagemath:develop Apr 23, 2023
vbraun pushed a commit that referenced this pull request Apr 23, 2023
    
Simplify the code; also, the previous code has to iterate over variables
of the sequence twice (this is really bad before #35510).

This affects mainly the method `connected_components()`.

EDIT:

Moreover, now we add a clique between the variables of each polynomial,
so it agrees with the description (the code before used to add just a
spanning tree of the clique -- a star).

This makes this method a little bit slower for the purposes of
`connected_components()` (for which adding a star is equivalent).

However, #35518 will rewrite `connected_components()` without using
`connection_graph()` so this is not really a problem.


### 📝 Checklist

- [x] The title is concise, informative, and self-explanatory.
- [x] The description explains in detail what this PR is about.
- [x] New tests added to check that a clique is added for the variables
in each polynomial.

### Dependencies
 - #35511
    
URL: #35512
Reported by: Gonzalo Tornaría
Reviewer(s): David Coudert, Gonzalo Tornaría, Vincent Delecroix
@mkoeppe mkoeppe added this to the sage-10.0 milestone Apr 23, 2023
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