-
Notifications
You must be signed in to change notification settings - Fork 258
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
Add type hints and type loop variables #946
base: master
Are you sure you want to change the base?
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #946 +/- ##
==========================================
+ Coverage 52.54% 52.96% +0.41%
==========================================
Files 20 21 +1
Lines 4345 4473 +128
==========================================
+ Hits 2283 2369 +86
- Misses 2062 2104 +42 ☔ View full report in Codecov by Sentry. |
@@ -23,16 +23,16 @@ cdef class Branchrule: | |||
'''informs branching rule that the branch and bound process data is being freed''' | |||
pass | |||
|
|||
def branchexeclp(self, allowaddcons): | |||
def branchexeclp(self, SCIP_Bool allowaddcons): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these methods not defined with cdef? I would say in the python world we should use bool and in the c world SCIP_Bool
. So where are we here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I said in the bigger comment, these functions should be accessible from the Python side, so we need to use def
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I guess to avoid confusion we should also use bool
instead of SCIP_Bool
and convert when entering the C world again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SCIP_Bool
does not make sense here. The user should never have access to such a type and the type hint should simply be bool
(like Dominik suggested).
Cython will handle the conversion or use a local declaration of SCIP_Bool
inside of the function.
@@ -29,7 +29,7 @@ cdef class Conshdlr: | |||
'''informs constraint handler that the branch and bound process is being started ''' | |||
pass | |||
|
|||
def consexitsol(self, constraints, restart): | |||
def consexitsol(self, constraints, SCIP_Bool restart): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about constraints
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't sure if they were allowed to be just one or not and was too lazy to check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is the list of constraints, I would not allow it to be a single constraint, can we declare it an iterable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can declare something as an iterable in Python type hints. We might need to then import the collections
library
@@ -23,7 +23,7 @@ cdef class Cutsel: | |||
'''executed before the branch-and-bound process is freed''' | |||
pass | |||
|
|||
def cutselselect(self, cuts, forcedcuts, root, maxnselectedcuts): | |||
def cutselselect(self, list[SCIP_ROW] cuts, int forcedcuts, SCIP_Bool root, int maxnselectedcuts): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess if we have def then the int means the Python int whereas SCIP_Bool is the C unsigned int, or am I wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not entirely sure how this works, because in things like addCons
I type all the options as SCIP_Bool
s, but then assign them Python booleans. I guess Cython makes some kind of automatic conversion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I am not aware of the Cython rules, but I find these type declarations confusing because
list[SCIP_ROW]
seems to be a Python-container of a C-typeint
is the C-intSCIP_Bool
is the C-uint
and since this should be a Python-callback, I would prefer if it is consistently supplied with Python-objects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also not sure about Cython, but I think there must be a definitive difference between this callback and a normal Python function. That's because using this sort of syntax (e.g. int forcedcuts
) leads to an invalid syntax
error.
Type hints in Python would use the following syntax (and not be enforced, making them somewhat useless):
def cutselselect(self, cuts: list[SCIP_ROW] , forcedcuts: int, root: bool, maxnselectedcuts: int):
Further, in these callbacks, bool
is not recognized as a type identifier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we're mixing type hints and variable type declarations here.
Regardless: list[SCIP_ROW]
would be incorrect because the callback receives list[Row]
, where one in theory could then access row._scip_row
, which has type SCIP_ROW
.
Could you maybe explain to me at first the convention across def and cdef and its influence on the interpretation of int? In general, it would be good to not mix up types from C and Python in a function signature. |
Hey @DominikKamp, thank you for being so quick! From my understanding, when you One crazy idea is to have an intermediate
We would get a slightly bigger speedup from doing this, buuut this does feel too crazy. Not sure if relevant, but using EDIT: I guess I could replace the signature types with pure Python types. I'm just worried about replacing |
I like your suggestion as it is much cleaner. The only thing to avoid is combining |
When using I cannot type booleans as The crazy idea with the intermediate function wasn't too well received, as it would complicate the codebase for a very minor gain. |
It is difficult to understand what you have tested exactly. Could you maybe push your local version, even if it fails? Using |
@DominikKamp just pushed the version that fails. My main desire with adding type hints was to make the codebase more robust and help users understand where their bugs might be coming from. In some cases, we get a |
Well, Python just does not support type declarations, see https://stackoverflow.com/a/3933210. This is only a Cython feature, so that also every type declared there is a C-type. Hence, using My desire here actually was only to revert 5e6a747 and use However, I would prefer to use only type hints in def functions since otherwise the users will never be able provide the intended Python-objects (because C-types would be required), which defeats the sense of this interface. The C-conversions should be done internally. |
My point was that these callbacks are not Python functions, precisely because they accept type declarations. I can call Regarding your middle point, I can create another PR just for the loop variables and take a breather on this one, but I would still like to see it merged one day. Would this be okay? (EDIT: Just reread, and it was basically what you said hehe I'll work on doing it today, then) |
Okay, then these functions can be defined by cdef, right? |
No, because functions defined with |
Alright, I just mean that the documentation might become difficult when functions expect types Python is not aware of. Furthermore, there is no way to detect conversion issues if an implicit conversion is enforced. This is particularly troublesome for implicit Python-int -> C-int conversions because this will just overflow silently. |
Based on Dominik's comments, I decided to try to add types again.
Needs a thorough revision before merging, as it's dangerous. All tests are passing, but of course something might have escaped.
The main benefit is not so much the speed from the looping variables, but rather the type checking in the arguments, which should improve error catching.
(For some very annoying reason we have a branch called main which is different from master)