Conversation
inspired in SCIP.jl, pyomo and biogeme's expressions
| # We have different types of general expressions that in addition | ||
| # to the operation and list of children stores | ||
| # SumExpr: coefficients and constant | ||
| # ProdExpr: constant |
There was a problem hiding this comment.
Nice documentation!
But what is the meaning of the constant for ProdExpr? Is it contant * child1 * child2 * ... ?
| def _expr_richcmp(self, other, op): | ||
| if op == 1: # <= | ||
| if isinstance(other, Expr): | ||
| if isinstance(other, Expr) or isinstance(other, GenExpr): |
There was a problem hiding this comment.
If Expr and GenExpr are mostly handled the same, then maybe it would be easier to use a common base class for both of them?
There was a problem hiding this comment.
yes, maybe, but I felt uncertain. The idea was to try to keep Expr as untouched as possible, given that it has been much more tested.
| # nor we simplify common terms or do any other type of simplification. | ||
| # The `GenExpr` is pass as is to SCIP and SCIP will do what it see fits during presolving. | ||
| # | ||
| # TODO: All this is very complicated, so we might wanna unify Expr and GenExpr. |
There was a problem hiding this comment.
It seems to me like GenExpr is strictly more powerful than Expr, so Expr could be removed completely?
I don't know whether there is a difference in performance between these implementations...
There was a problem hiding this comment.
In any case, this step could also be done in the future.
There was a problem hiding this comment.
Well, this is not true, you can't ask information about GenExpr as you can with Expr, but if we do not care about that, then yes, it is more powerful.
I guess there are performance difference, I didn't really test this, but I would definitely do this in the future
| # is no longer in place, might affect performance? | ||
| # can't do `self = buildGenExprObj(self) + other` since I get | ||
| # TypeError: Cannot convert pyscipopt.scip.SumExpr to pyscipopt.scip.Expr | ||
| return buildGenExprObj(self) + other |
There was a problem hiding this comment.
what if we do this instead:
return other + buildGenExprObj(self)
Would this be inplace (but for other's place)?
There was a problem hiding this comment.
I am guessing that inplace means self's place. But yeah, this inplace stuff is a bit complicated and would also postpone it.
src/pyscipopt/expr.pxi
Outdated
| divisor = buildGenExprObj(other) | ||
| # we can't divide by 0 | ||
| if divisor.getOp() == Operator.const: | ||
| assert divisor.number != 0.0 |
There was a problem hiding this comment.
I think a Python programmer would expect a ValueError instead of an AssertionError, but I don't mind too much.
There was a problem hiding this comment.
I changed this and added a test for this. thanks
| '''turn it into a constraint''' | ||
| return _expr_richcmp(self, other, op) | ||
|
|
||
| def degree(self): |
There was a problem hiding this comment.
why even add this method?
There was a problem hiding this comment.
just to keep it compatible with the implementation of addCons in scip.pxd or whathever the termination is
src/pyscipopt/expr.pxi
Outdated
| ans.op = Operator.exp | ||
| ans.operatorIndex = Operator.operatorIndexDic[ans.op] | ||
| ans.children.append(buildGenExprObj(expr)) | ||
| return ans |
There was a problem hiding this comment.
why do set all these attributes after creating an "empty" object, instead of using the UnaryExpr.__init__ method to initialize them?
There was a problem hiding this comment.
what you suggest is way more elegant, will implement after lunch ;)
There was a problem hiding this comment.
Good, so I made one constructive suggestions ;-)
| elif deg == float('inf'): # general nonlinear | ||
| return self._addGenNonlinearCons(cons, **kwargs) | ||
| else: | ||
| return self._addNonlinearCons(cons, **kwargs) |
There was a problem hiding this comment.
so this is for polynomials only?
There was a problem hiding this comment.
_addNonlinearCons is for polynomials only, yes
There was a problem hiding this comment.
I would rather rename it to _addPolyCons() or something similar. It's quite unintuitive right now and these are only internal methods.
| free(varexprs) | ||
| return PyCons | ||
|
|
||
| def _addGenNonlinearCons(self, ExprCons cons, **kwargs): |
There was a problem hiding this comment.
I'll have to believe you on this function...
There was a problem hiding this comment.
haha, this is basically a copy-paste of the one in CSIP
Codecov Report
@@ Coverage Diff @@
## master #134 +/- ##
=========================================
+ Coverage 87.15% 88.7% +1.54%
=========================================
Files 18 19 +1
Lines 1059 1204 +145
=========================================
+ Hits 923 1068 +145
Misses 136 136
Continue to review full report at Codecov.
|
|
Shall we increase the version number to 1.4 or 2.0 after merging? |
|
I have no idea how the versioning works |
Apparently, the version is defined in |
Haha, I guess, @fserra was referring to what the meaning of the version digits was - not how we can change the version. So, the question boils down to whether we want to consider this a major or a minor version jump. I say we call it 1.4 since we don't break the interface. |
closes #41