-
-
Notifications
You must be signed in to change notification settings - Fork 426
Type examples/ #819
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
Type examples/ #819
Conversation
pchtsp
left a comment
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.
Thanks! I added minor questions about the renaming of some objects. I'm curious what tool renames objects based on their implied type. It sounds a bit weird.
examples/SpongeRollProblem4.py
Outdated
| lenOpts = [5, 7, 9] | ||
|
|
||
| def __init__(self, name, lengths=None): | ||
| def __init__(self, name: str, lengths: List[int]) -> None: |
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.
shouldn't we keep the =None 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.
I removed it because the zip below on L87 fails to typecheck if lengths is None. Since this code is presumably working I took that to mean that this is never used that way in practice.
| lenOpts = ["5", "7", "9"] | ||
| numPatterns = 0 | ||
|
|
||
| def __init__(self, name, lengths=None): |
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.
also here, should we keep the =None?
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 above
| # The problem data is written to an .lp file | ||
| prob.writeLP("BeerDistributionProblem.lp") | ||
| for demand in range(500, 601, 10): | ||
| for demand_value in range(500, 601, 10): |
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.
same as with _dict, is this some kind of naming convention based on the type of the object?
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.
Similar to previously there is already a demand in scope and this was me fixing the collision.
This naming convention is in my opinion a bit outdated with modern typing. Instead of changing names to indicate type it would be better, as is being done in the linked PR, to indicate the type properly with typing. |
|
Few comments based on #807:
|
| try: # allow Python 2/3 compatibility | ||
| maketrans = str.maketrans | ||
| except AttributeError: | ||
| from string import maketrans |
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 not just change to be: from string import maketrans, Python 2 is not supported
|
|
||
| _DICT_TYPE = dict | ||
|
|
||
| if sys.platform not in ["cli"]: |
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.
There is no sys.platform which is cli any more. I presume this was IronPython. Why not simplify and just remove _DICT_TYPE as per https://github.com/coin-or/pulp/pull/807/files#diff-b2a258edfe0fa294f032a978656a4082842558faa98b0950d9f68c50f546bbe2L151
| """ | ||
| self.expr = e if isinstance(e, LpAffineExpression) else LpAffineExpression(e) | ||
| self.name = name | ||
| self.constant: float = self.expr.constant |
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.
This just hides a potential bug. Better to fix the annotations
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.
In my PR, I added some type aliases, which I then used where appropriate.
LptNumber: TypeAlias = int | float
LptItem: TypeAlias = Union[LptNumber, "LpVariable"]
LptExpr: TypeAlias = Union[LptItem, "LpAffineExpression"]
LptConstExpr: TypeAlias = Union[LptNumber, "LpAffineExpression"]
LptExprConstraint: TypeAlias = Union[LptExpr, "LpConstraint"]
LptVars: TypeAlias = "LpVariable"| def valid(self, eps: float = 0) -> bool: | ||
| val = self.value() | ||
| if self.sense == const.LpConstraintEQ: | ||
| return abs(val) <= eps |
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.
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.
My links don't seem to have worked, there needs to be a check like:
val = self.value()
if val is None:
return False| if name is not None: | ||
| self.__name = name.translate(LpAffineExpression.trans) | ||
| else: | ||
| self.__name = None |
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.
Not sure why this is needed?
|
There are some easy wins in the type hinting space, but as I found in #807, there are some architectural issues with LpElement, LpVariable and LpConstraintVar that need to be fixed. Also FractionElasticSubProblem just seems fundamentally broken. Then there is also the question of what type the coefficient should be? int, float, decimal, numpy int/float types, or what combination of these? |
| Patterns += [Pattern("P" + str(len(Patterns)), i)] | ||
|
|
||
| # This loop will be repeated until morePatterns is set to False | ||
| while morePatterns == True: |
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.
Remove the == True
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 am trying to keep the scope of this PR as limited as possible. There's a ton of less-than-ideal code here. If I started fixing things I don't know where I would be able to stop.
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.
Same problem I had in #807 :)
| while morePatterns == True: | ||
| # Solve the problem as a Relaxed LP | ||
| duals = masterSolve(Patterns, rollData) | ||
| assert isinstance(duals, dict) |
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.
Surely, the return type is a tuple as per masterSolve?
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.
that function returns a union type. this assert is necessary to tell mypy which part of the union this is.
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.
Should the return should be duals, _ = masterSolve(Patterns, rollData) then?
|
👋 thanks for all the reviews. To clear up some possible confusion - this PR is intended to be the minimum possible change to add types to the Additional responses inline:
Sorry for the confusion caused by my clunky variable names. They are not meant to convey the type, just fix the variable type change issues. These variables all actually typecheck fine without adding explicit hints.
These are all great questions. The intention of this PR is not to fix any type problems in |
Would it be better to not add the |
|
In general unit tests / CI work best if you keep them in a state where you always expect them to pass. You want their binary pass / fail state to be a useful signal of whether or not a new change introduces new bugs. If you add tests which you expect to fail it can confuse things quite a bit. The type ignore statements are a useful crutch to be able to make incremental improvements without having to fix everything at once. With their help we can do
|
Makes sense, didn't read the code in larger context. Good change then, at some point those should probably have completely different names to convey their meaning better. But a good first step. |
|
merged master to pick up #820 - looks like I have some work to do |
|
That wasn't too bad at least 😅 @pchtsp I think I have addressed all your comments. Please let me know if there is anything more I can answer or change. There are some references to these files in the documentation by line number like this: pulp/doc/source/CaseStudies/a_blending_problem.rst Lines 97 to 98 in f626d34
which may need to be updated if things have shifted around. I'm happy to have a look at this as well as part of this PR. I want to wait until the actual code changes look good first though in case the lines end up moving around more. |
|
thanks @frrad . I don't mind if we do not have a perfect state now. Anything that gets as closer is good. Thanks again |
* initial pass * black + isort * add ignores in pulp.py and pulp/__init__.py * start fixing mypy errors * fix some mypy * mypy passes * move Pattern class definition to be before use
* remove build from gitignore * commit doc build output * fix references * remove compile output * Type examples/ (#819) * initial pass * black + isort * add ignores in pulp.py and pulp/__init__.py * start fixing mypy errors * fix some mypy * mypy passes * move Pattern class definition to be before use * restore gitignore * whiskas 1 * whiskas 2
Adds typing to files in
examples/.Also removed several more
pulp/files from the mypy blacklist, addingtype: ignores where necessary.