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

Redesign of the constructor for Hyperelliptic curves #27633

Closed
anna-somoza mannequin opened this issue Apr 10, 2019 · 38 comments
Closed

Redesign of the constructor for Hyperelliptic curves #27633

anna-somoza mannequin opened this issue Apr 10, 2019 · 38 comments

Comments

@anna-somoza
Copy link
Mannequin

anna-somoza mannequin commented Apr 10, 2019

As discussed in #22173, the current the design of the hyperelliptic curves classes was not good.

This changes create the (genus, ring) subclasses dynamically, removing the need for a class/file per case.

CC: @JRSijsling @mstreng @videlec @slel

Component: number theory

Keywords: days98, hyperelliptic curves

Author: Anna Somoza

Branch/Commit: fe002eb

Reviewer: Vincent Delecroix

Issue created by migration from https://trac.sagemath.org/ticket/27633

@anna-somoza anna-somoza mannequin added this to the sage-8.8 milestone Apr 10, 2019
@anna-somoza anna-somoza mannequin added the p: major / 3 label Apr 10, 2019
@anna-somoza
Copy link
Mannequin Author

anna-somoza mannequin commented Apr 11, 2019

@anna-somoza
Copy link
Mannequin Author

anna-somoza mannequin commented Apr 11, 2019

Author: Anna Somoza

@anna-somoza

This comment has been minimized.

@anna-somoza
Copy link
Mannequin Author

anna-somoza mannequin commented Apr 11, 2019

Changed keywords from none to days98, hyperelliptic curves

@anna-somoza
Copy link
Mannequin Author

anna-somoza mannequin commented Apr 11, 2019

Commit: e4d5db4

@videlec
Copy link
Contributor

videlec commented Apr 11, 2019

Reviewer: Vincent Delecroix

@videlec
Copy link
Contributor

videlec commented Apr 11, 2019

comment:3

This is a good move.

The logic in the following is weird

fld = map(lambda fnc : fnc(R), [is_FiniteField, is_RationalField, is_pAdicField])
fld_type = fld.index(True)

You would rather do something like

fields = [
   ("FiniteField", is_FiniteField, HyperellipticCurve_finite_field),
   ("RationalField", is_RationalField, HyperellipticCurve_rational_field),
   ("pAdicField", is_pAdicField, HyperellipticCurve_padic_field)]
for name, test, cls in fields:
    if test(R):
        ....    # treat the class
        break   # the tests are exclusive, no need to run through all cases

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 11, 2019

Branch pushed to git repo; I updated commit sha1. New commits:

1c35da7Modify field-related data treatment

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 11, 2019

Changed commit from e4d5db4 to 1c35da7

@anna-somoza
Copy link
Mannequin Author

anna-somoza mannequin commented Apr 11, 2019

comment:5

Agreed, with your approach the code is easier to understand.

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 11, 2019

Changed commit from 1c35da7 to 2863afa

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 11, 2019

Branch pushed to git repo; I updated commit sha1. New commits:

2863afaDoc now builds

@videlec
Copy link
Contributor

videlec commented Apr 11, 2019

comment:7

Similarly, why do you construct a dictionary in the following

+    genus_class = {2:HyperellipticCurve_g2}
+    if g in genus_class.keys():
+        supercls.append(genus_class[g])

This is simpler as

if g == 2:
    supercls.append(HyperellipticCurve_g2)

@anna-somoza
Copy link
Mannequin Author

anna-somoza mannequin commented Apr 11, 2019

comment:8

This I did with #22173 in mind, where I will add the class for genus 3 to the dictionary (I am working on it right now). In general, if more genus are added, then using the dictionary leads to a cleaner code than a list of ifs.

@videlec
Copy link
Contributor

videlec commented Apr 11, 2019

comment:9

I see. What about?

if g == 2:
    supercls.append(HyperellipticCurve_g2)
# elif g == 3:
#     supercls.append(HyperellipticCurve_g3)

@videlec
Copy link
Contributor

videlec commented Apr 11, 2019

comment:10

What do you think about removing the inheritance from HyperellipticCurve_generic
in HyperellipticCurve_rational_field, HyperellipticCurve_padic_field, etc? All the inheritance is handled by this new function anyway.

In this case, the function would be

cls = [HyperellipticCurve_generic]

...

if len(cls) > 1:   # contains more than just HyperellipticCurve_generic
    cls = type("HyperellipticCurve_" + "_".join(cls_name), tuple(cls), {})

return cls(PP, f, h, names=names, genus=g)

@videlec
Copy link
Contributor

videlec commented Apr 11, 2019

comment:11

And last but not the least, you definitely don't to create twice the same class. The first reason for this is that if you construct twice a hyperelliptic curves over rational you want the type to be the same. But the type function will recreate a new class each time (even if it has the same name).

Also, you don't want to create classes that are not necessary. Creating a class for each genus is a waste of time.

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 11, 2019

Changed commit from 2863afa to 5b5c570

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 11, 2019

Branch pushed to git repo; I updated commit sha1. New commits:

5b5c570Modify genus-related data treatment. Store already-created classes for future use.

@anna-somoza
Copy link
Mannequin Author

anna-somoza mannequin commented Apr 11, 2019

comment:13

Replying to @videlec:

I see. What about?

if g == 2:
    supercls.append(HyperellipticCurve_g2)
# elif g == 3:
#     supercls.append(HyperellipticCurve_g3)

How about this new version? There I just use the information of the classes that are imported in the module.

Replying to @videlec:

What do you think about removing the inheritance from HyperellipticCurve_generic
in HyperellipticCurve_rational_field, HyperellipticCurve_padic_field, etc? All the inheritance is handled by this new function anyway.

In this case, the function would be

cls = [HyperellipticCurve_generic]

...

if len(cls) > 1:   # contains more than just HyperellipticCurve_generic
    cls = type("HyperellipticCurve_" + "_".join(cls_name), tuple(cls), {})

return cls(PP, f, h, names=names, genus=g)

The methods in HyperellipticCurve_rational_field and others use methods from HyperellipticCurve_generic, so I don't think that it is a good idea.

Replying to @videlec:

And last but not the least, you definitely don't to create twice the same class. The first reason for this is that if you construct twice a hyperelliptic curves over rational you want the type to be the same. But the type function will recreate a new class each time (even if it has the same name).

Also, you don't want to create classes that are not necessary. Creating a class for each genus is a waste of time.

Agreed, I included the functionality and added a test for it.

@videlec
Copy link
Contributor

videlec commented Apr 12, 2019

comment:14

Replying to @anna-somoza:

Replying to @videlec:

I see. What about?

if g == 2:
    supercls.append(HyperellipticCurve_g2)
# elif g == 3:
#     supercls.append(HyperellipticCurve_g3)

How about this new version? There I just use the information of the classes that are imported in the module.

This is terrible. Explicit code is to my mind much better. With the current version, a curious person looking at the code would have no clue about how it works.

@videlec
Copy link
Contributor

videlec commented Apr 12, 2019

comment:15

Otherwise, the caching with the dictionary is ok.

@VivianePons
Copy link

comment:16

The new version is good as it forces new contributions to follow the same name pattern and only requires to add the new class without changing the source code of the main function.

So I don't think "terrible" is an appropriate adjective! (Unless you have an other way to catch the existing classes?) A list of ifs might just get very long and unpractical.

I do agree that it is not very reader-friendly as it is. This could easily be fixed with more comments/documentation.

@videlec
Copy link
Contributor

videlec commented Apr 12, 2019

comment:17

Replying to @VivianePons:

The new version is good as it forces new contributions to follow the same name pattern and only requires to add the new class without changing the source code of the main function.

So I don't think "terrible" is an appropriate adjective! (Unless you have an other way to catch the existing classes?) A list of ifs might just get very long and unpractical.

The list has length one and after #22173 it will have length two. I don't understand this objection.

I do agree that it is not very reader-friendly as it is. This could easily be fixed with more comments/documentation.

"Terrible design" is appropriate.

  1. As I said it is unreadable.

  2. The code is sensitive to code injection

sage: import sage.schemes.hyperelliptic_curves.constructor as constructor
sage: constructor.HyperellipticCurve_g3 = int

You might consider this as a super feature, but it is very much error prone.

  1. The way it is written goes through several dictionary accesses and a try/except each time a hyperelliptic curve is created. This is a waste of time as you want constructors to be as light as possible.

@VivianePons
Copy link

comment:18

I understand the objection about security and efficiency. The argument that the list will be of length 2 is not very convincing though: the very purpose of this ticket is to write an architecture which allows for easy extensions.

I would suggest we go back to the initial solution that was proposed: use a dictionary associating the class name to the actual class. I find it much more readable than the list of ifs and easier to extend.

@slel

This comment has been minimized.

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 16, 2019

Branch pushed to git repo; I updated commit sha1. New commits:

9295319Back to using a dictionary to deal with genus-related classes

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 16, 2019

Changed commit from 5b5c570 to 9295319

@anna-somoza
Copy link
Mannequin Author

anna-somoza mannequin commented Apr 16, 2019

comment:21

Following Viviane's comment, I went back to using a dictionary.

I am not sure of the meaning of the error that the bot gives, I think it comes from the fact that some files where removed and is otherwise green.

@videlec
Copy link
Contributor

videlec commented Apr 16, 2019

comment:22

You might want to test that inheritance is actually correct in the doctests

sage: R.<t> = PolynomialRing(GF(next_prime(10^9)))
sage: C = HyperellipticCurve(t^5 + t + 1)
sage: import inspect
sage: inspect.getmro(type(C))
(<class 'sage.schemes.hyperelliptic_curves.constructor.HyperellipticCurve_g2_FiniteField_with_category'>,
 <class 'sage.schemes.hyperelliptic_curves.constructor.HyperellipticCurve_g2_FiniteField'>,
 <class 'sage.schemes.hyperelliptic_curves.hyperelliptic_g2.HyperellipticCurve_g2'>,
 <class 'sage.schemes.hyperelliptic_curves.hyperelliptic_finite_field.HyperellipticCurve_finite_field'>,
 <class 'sage.schemes.hyperelliptic_curves.hyperelliptic_generic.HyperellipticCurve_generic'>,
 ...)

@anna-somoza
Copy link
Mannequin Author

anna-somoza mannequin commented Apr 16, 2019

comment:23

Is it better to add a test for every case or with the one you gave it's enough?

Also, while looking into this I realized that I am duplicating the classes with only one superclass.

sage: R.<t> = PolynomialRing(GF(next_prime(10^9)))
sage: C = HyperellipticCurve(t^7 + t + 1)
sage: import inspect
sage: inspect.getmro(type(C))
(<class 'sage.schemes.hyperelliptic_curves.constructor.HyperellipticCurve_FiniteField_with_category'>,
 <class 'sage.schemes.hyperelliptic_curves.constructor.HyperellipticCurve_FiniteField'>,
 <class 'sage.schemes.hyperelliptic_curves.hyperelliptic_finite_field.HyperellipticCurve_finite_field'>,
 <class 'sage.schemes.hyperelliptic_curves.hyperelliptic_generic.HyperellipticCurve_generic'>,
...)

Would it be preferable to initiate the created_classes dictionary with the classes HyperellipticCurve_g2, HyperellipticCurve_FiniteField, etc to avoid that? That could also simplify the last part of the code if HyperellipticCurve_generic was added too.

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 18, 2019

Changed commit from 9295319 to 12069c7

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 18, 2019

Branch pushed to git repo; I updated commit sha1. New commits:

12069c7Test added. Dynamic creation of classes is now done with `dynamic_class` function.

@videlec
Copy link
Contributor

videlec commented Apr 18, 2019

comment:25

Even nicer.

One last little thing. In the documentation, you need a linebreak after ::.

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 18, 2019

Changed commit from 12069c7 to fe002eb

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 18, 2019

Branch pushed to git repo; I updated commit sha1. New commits:

fe002ebAdd missing endline in documentation

@videlec
Copy link
Contributor

videlec commented Apr 18, 2019

comment:27

Perfect! Thanks for your patience.

@vbraun
Copy link
Member

vbraun commented Apr 27, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants