Skip to content

Conversation

haru-44
Copy link
Member

@haru-44 haru-44 commented Mar 9, 2023

p into two patterns and made them independent as a function.

  • _primitive_root_prime_iter
    Generates the primitive roots of p. Once one primitive root g is found, the other primitive roots are represented by powers of g. In this case, the exponent and p-1 are relatively prime. Therefore, there is no need to perform power calculations to determine the primitive roots every time.

  • _primitive_root_prime_power_iter
    Generates the primitive roots of p**e. Let g (0 < g < p) be the primitive root of p. If pow(g+s*p,p-1,p**2)!=1, then g+s*p is primitive root of p**e. There is always one integer s (0 <= s < p) per g that does not satisfy this condition, and the other p-1 are primitive roots. Thus, we find totient(p)*(p-1) primitive roots of p**2, which is consistent with totient(totient(p**2)). The primitive roots of p**3 are all numbers congruent to the primitive roots of p**2 by p. The same is true for p**e (e > 3), which is consistent with the totient(totient(p**e)). From the above, we see that g+(k+m*p)*p is all of the primitive roots of p**e. Where g is primitive root of p (0<g<p), 0 <= k < p, 0 <= m < p**(e-2), g**(p-1)+(p-1)*g**(p-2)*k*p % p**2 != 1.

  • _primitive_root_prime_power2_iter
    Generates the primitive roots of 2*p**e. If g is the primitive root of p**e, then the odd one of g and g+p**e is the primitive root of 2*p**e. Since totient(totient(2*p**e))=totient(totient(p**e)), this is all the primitive roots of 2*p**e.

References to other Issues or PRs

Brief description of what is fixed or changed

Other comments

Release Notes

  • ntheory
    • Added smallest option to primitive_root

p into two patterns and made them independent as a function.
@sympy-bot
Copy link

sympy-bot commented Mar 9, 2023

Hi, I am the SymPy bot. 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.13.

Click here to see the pull request description that was parsed.
p into two patterns and made them independent as a function.

* `_primitive_root_prime_iter`
Generates the primitive roots of `p`. Once one primitive root `g` is found, the other primitive roots are represented by powers of `g`. In this case, the exponent and `p-1` are relatively prime. Therefore, there is no need to perform power calculations to determine the primitive roots every time.

* `_primitive_root_prime_power_iter`
Generates the primitive roots of `p**e`. Let `g` (`0 < g < p`) be the primitive root of `p`. If `pow(g+s*p,p-1,p**2)!=1`, then `g+s*p` is primitive root of `p**e`. There is always one integer `s` (`0 <= s < p`) per `g` that does not satisfy this condition, and the other `p-1` are primitive roots. Thus, we find `totient(p)*(p-1)` primitive roots of `p**2`, which is consistent with `totient(totient(p**2))`. The primitive roots of `p**3` are all numbers congruent to the primitive roots of `p**2` by `p`. The same is true for `p**e` (`e > 3`), which is consistent with the `totient(totient(p**e))`. From the above, we see that `g+(k+m*p)*p` is all of the primitive roots of `p**e`. Where `g` is primitive root of `p` (`0<g<p`), `0 <= k < p`, `0 <= m < p**(e-2)`, `g**(p-1)+(p-1)*g**(p-2)*k*p % p**2 != 1`.

* `_primitive_root_prime_power2_iter`
Generates the primitive roots of `2*p**e`. If `g` is the primitive root of `p**e`, then the odd one of `g` and `g+p**e` is the primitive root of `2*p**e`. Since `totient(totient(2*p**e))=totient(totient(p**e))`, this is all the primitive roots of `2*p**e`.

<!-- Your title above should be a short description of what
was changed. Do not include the issue number in the title. -->

#### References to other Issues or PRs
<!-- If this pull request fixes an issue, write "Fixes #NNNN" in that exact
format, e.g. "Fixes #1234" (see
https://tinyurl.com/auto-closing for more information). Also, please
write a comment on that issue linking back to this pull request once it is
open. -->


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


#### Other comments


#### Release Notes

<!-- Write the release notes for this release below between the BEGIN and END
statements. The basic format is a bulleted list with the name of the subpackage
and the release note for this PR. For example:

* solvers
  * Added a new solver for logarithmic equations.

* functions
  * Fixed a bug with log of integers. Formerly, `log(-x)` incorrectly gave `-log(x)`.

* physics.units
  * Corrected a semantical error in the conversion between volt and statvolt which
    reported the volt as being larger than the statvolt.

or if no release note(s) should be included use:

NO ENTRY

See https://github.com/sympy/sympy/wiki/Writing-Release-Notes for more
information on how to write release notes. The bot will check your release
notes automatically to see if they are formatted correctly. -->

<!-- BEGIN RELEASE NOTES -->
* ntheory
  * Added `smallest` option to `primitive_root`
<!-- END RELEASE NOTES -->

Update

The release notes on the wiki have been updated.

Once one primitive root g is found, the other primitive roots are represented
by powers of g. In this case, the exponent and p-1 are relatively prime.
Therefore, there is no need to perform power calculations to determine
the primitive roots every time.
@github-actions
Copy link

github-actions bot commented Mar 15, 2023

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
     [2c3de5f4]       [94fc2abc]
-         112±3ms         73.7±2ms     0.66  integrate.TimeIntegrationRisch02.time_doit(10)
-         108±2ms       70.2±0.9ms     0.65  integrate.TimeIntegrationRisch02.time_doit_risch(10)

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

@sylee957
Copy link
Member

sylee957 commented Mar 19, 2023

I'm not sure if ordered keyword is needed everywhere.
We should do that only for the first or last time, and if it is not relevant for user function, it's better not to have that feature because it looks like duplicate logic have to be tested.

@haru-44
Copy link
Member Author

haru-44 commented Mar 19, 2023

thank you for comment.
deleted ordered keyword.

@smichr
Copy link
Member

smichr commented Apr 6, 2023

I wonder if it might be better still to leave the sorting for the public facing functions so the low-level can just return what's convenient and the higher level functions sort it if necessary. As long as it is canonical it need not be sorted for purposes of testing.

@smichr
Copy link
Member

smichr commented Apr 8, 2023

Please consider whether sorting is necessary at this level.

@haru-44
Copy link
Member Author

haru-44 commented Apr 9, 2023

To begin with, primitive_root is designed to return the smallest primitive root.
If a low-level function outputs without regard to order, primitive_root can only return an answer after getting all primitive roots, which is an exaggeration for getting one primitive root.

So how about the following implementation?

  • Low-level functions output without regard to order.
  • The primitive_root optionally allows the user to choose whether to return the smallest primitive root or one primitive root of whatever.
  • If it returns one primitive root, it calls a low-level function to find the primitive root fast.
  • If returning the smallest of primitive roots, check if it is a primitive root from the smaller one, as in the current case.

@smichr
Copy link
Member

smichr commented Apr 11, 2023

If it returns one primitive root, it calls a low-level function to find the a primitive root fast.
If returning the smallest of primitive roots, check if it is a primitive root from the smaller one, as in the current case. use min instead of next.

I think that would be ok -- I don't work with ntheory much, however, and don't know what the preferences of someone would be.

@haru-44
Copy link
Member Author

haru-44 commented Apr 12, 2023

Thanks. I fixed it.

no need for full extent of trailing
@smichr
Copy link
Member

smichr commented Apr 12, 2023

If the changes look ok to you, this is ready.

@haru-44
Copy link
Member Author

haru-44 commented Apr 13, 2023

thank you.
I corrected one point, but everything else is fine.

@smichr smichr merged commit bce780e into sympy:master Apr 13, 2023
@smichr
Copy link
Member

smichr commented Apr 13, 2023

Now that I look at it, I agree that the sorting of factors is not important since the g that is returned is incremented from small to large and simply has to pass a test with all factors.

Thanks for your work on this.

@haru-44 haru-44 deleted the primitive_root branch July 28, 2023 11:24
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