Skip to content

add support general nonlinear cons#134

Merged
mattmilten merged 3 commits intomasterfrom
fs/general_nonlinear
Mar 2, 2018
Merged

add support general nonlinear cons#134
mattmilten merged 3 commits intomasterfrom
fs/general_nonlinear

Conversation

@fserra
Copy link
Collaborator

@fserra fserra commented Mar 2, 2018

closes #41

inspired in SCIP.jl, pyomo and biogeme's expressions
@fserra fserra requested a review from rschwarz March 2, 2018 11:20
Copy link
Collaborator

@rschwarz rschwarz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice tests!

# We have different types of general expressions that in addition
# to the operation and list of children stores
# SumExpr: coefficients and constant
# ProdExpr: constant
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice documentation!

But what is the meaning of the constant for ProdExpr? Is it contant * child1 * child2 * ... ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

def _expr_richcmp(self, other, op):
if op == 1: # <=
if isinstance(other, Expr):
if isinstance(other, Expr) or isinstance(other, GenExpr):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In any case, this step could also be done in the future.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if we do this instead:

return other + buildGenExprObj(self)

Would this be inplace (but for other's place)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am guessing that inplace means self's place. But yeah, this inplace stuff is a bit complicated and would also postpone it.

divisor = buildGenExprObj(other)
# we can't divide by 0
if divisor.getOp() == Operator.const:
assert divisor.number != 0.0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a Python programmer would expect a ValueError instead of an AssertionError, but I don't mind too much.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this and added a test for this. thanks

'''turn it into a constraint'''
return _expr_richcmp(self, other, op)

def degree(self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why even add this method?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just to keep it compatible with the implementation of addCons in scip.pxd or whathever the termination is

ans.op = Operator.exp
ans.operatorIndex = Operator.operatorIndexDic[ans.op]
ans.children.append(buildGenExprObj(expr))
return ans
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do set all these attributes after creating an "empty" object, instead of using the UnaryExpr.__init__ method to initialize them?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what you suggest is way more elegant, will implement after lunch ;)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good, so I made one constructive suggestions ;-)

elif deg == float('inf'): # general nonlinear
return self._addGenNonlinearCons(cons, **kwargs)
else:
return self._addNonlinearCons(cons, **kwargs)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so this is for polynomials only?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_addNonlinearCons is for polynomials only, yes

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll have to believe you on this function...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha, this is basically a copy-paste of the one in CSIP

@codecov
Copy link

codecov bot commented Mar 2, 2018

Codecov Report

Merging #134 into master will increase coverage by 1.54%.
The diff coverage is 100%.

Impacted file tree graph

@@            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
Impacted Files Coverage Δ
tests/test_linexpr.py 100% <ø> (ø) ⬆️
src/pyscipopt/__init__.py 100% <100%> (ø) ⬆️
tests/test_expr.py 100% <100%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 49a830d...e6227a4. Read the comment docs.

@mattmilten
Copy link
Collaborator

Shall we increase the version number to 1.4 or 2.0 after merging?

@fserra
Copy link
Collaborator Author

fserra commented Mar 2, 2018

I have no idea how the versioning works

@rschwarz
Copy link
Collaborator

rschwarz commented Mar 2, 2018

I have no idea how the versioning works

Apparently, the version is defined in __init__.py

@mattmilten
Copy link
Collaborator

Apparently, the version is defined in init.py

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.

@mattmilten mattmilten merged commit acff07c into master Mar 2, 2018
@mattmilten mattmilten deleted the fs/general_nonlinear branch July 13, 2018 07:42
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.

Non Linear expressions

3 participants