WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit c9c6314

Browse files
committed
address reviews
1 parent 76637f1 commit c9c6314

File tree

2 files changed

+170
-39
lines changed

2 files changed

+170
-39
lines changed

python/cuopt/cuopt/linear_programming/problem.py

Lines changed: 124 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,9 @@ def __mul__(self, other):
241241
qcoeffs = other.coefficients
242242
vars = [self]
243243
coeffs = [other.constant]
244-
return QuadraticExpression(qvars1, qvars2, qcoeffs, vars, coeffs, 0.0)
244+
return QuadraticExpression(
245+
qvars1, qvars2, qcoeffs, vars, coeffs, 0.0
246+
)
245247
case _:
246248
raise ValueError(
247249
"Cannot multiply type %s with variable"
@@ -287,8 +289,11 @@ def __eq__(self, other):
287289
case _:
288290
raise ValueError("Unsupported operation")
289291

292+
290293
class QuadraticExpression:
291-
def __init__(self, qvars1, qvars2, qcoefficients, vars, coefficients, constant):
294+
def __init__(
295+
self, qvars1, qvars2, qcoefficients, vars, coefficients, constant
296+
):
292297
self.qvars1 = qvars1
293298
self.qvars2 = qvars2
294299
self.qcoefficients = qcoefficients
@@ -388,19 +393,38 @@ def __add__(self, other):
388393
case int() | float():
389394
# Update just the constant value
390395
return QuadraticExpression(
391-
self.qvars1, self.qvars2, self.qcoefficients, self.vars, self.coefficients, self.constant + float(other)
396+
self.qvars1,
397+
self.qvars2,
398+
self.qcoefficients,
399+
self.vars,
400+
self.coefficients,
401+
self.constant + float(other),
392402
)
393403
case Variable():
394404
# Append just a variable with coefficient 1.0
395405
vars = self.vars + [other]
396406
coeffs = self.coefficients + [1.0]
397-
return QuadraticExpression(self.qvars1, self.qvars2, self.qcoefficients, vars, coeffs, self.constant)
407+
return QuadraticExpression(
408+
self.qvars1,
409+
self.qvars2,
410+
self.qcoefficients,
411+
vars,
412+
coeffs,
413+
self.constant,
414+
)
398415
case LinearExpression():
399416
# Append all linear variables, coefficients and constants
400417
vars = self.vars + other.vars
401418
coeffs = self.coefficients + other.coefficients
402419
constant = self.constant + other.constant
403-
return QuadraticExpression(self.qvars1, self.qvars2, self.qcoefficients, vars, coeffs, constant)
420+
return QuadraticExpression(
421+
self.qvars1,
422+
self.qvars2,
423+
self.qcoefficients,
424+
vars,
425+
coeffs,
426+
constant,
427+
)
404428
case QuadraticExpression():
405429
# Append all quadratic variables, coefficients and constants
406430
qvars1 = self.qvars1 + other.qvars1
@@ -409,8 +433,14 @@ def __add__(self, other):
409433
vars = self.vars + other.vars
410434
coeffs = self.coefficients + other.coefficients
411435
constant = self.constant + other.constant
412-
return QuadraticExpression(qvars1, qvars2, qcoeffs, vars, coeffs, constant)
413-
436+
return QuadraticExpression(
437+
qvars1, qvars2, qcoeffs, vars, coeffs, constant
438+
)
439+
case _:
440+
raise ValueError(
441+
"Can't add type %s to Quadratic Expression"
442+
% type(other).__name__
443+
)
414444

415445
def __radd__(self, other):
416446
return self + other
@@ -457,13 +487,25 @@ def __sub__(self, other):
457487
case int() | float():
458488
# Update just the constant value
459489
return QuadraticExpression(
460-
self.qvars1, self.qvars2, self.qcoefficients, self.vars, self.coefficients, self.constant - float(other)
490+
self.qvars1,
491+
self.qvars2,
492+
self.qcoefficients,
493+
self.vars,
494+
self.coefficients,
495+
self.constant - float(other),
461496
)
462497
case Variable():
463498
# Append just a variable with coefficient -1.0
464499
vars = self.vars + [other]
465500
coeffs = self.coefficients + [-1.0]
466-
return QuadraticExpression(self.qvars1, self.qvars2, self.qcoefficients, vars, coeffs, self.constant)
501+
return QuadraticExpression(
502+
self.qvars1,
503+
self.qvars2,
504+
self.qcoefficients,
505+
vars,
506+
coeffs,
507+
self.constant,
508+
)
467509
case LinearExpression():
468510
# Append all linear variables, coefficients and constants
469511
vars = self.vars + other.vars
@@ -473,7 +515,14 @@ def __sub__(self, other):
473515
for i in other.coefficients:
474516
coeffs.append(-1.0 * i)
475517
constant = self.constant - other.constant
476-
return QuadraticExpression(self.qvars1, self.qvars2, self.qcoefficients, vars, coeffs, constant)
518+
return QuadraticExpression(
519+
self.qvars1,
520+
self.qvars2,
521+
self.qcoefficients,
522+
vars,
523+
coeffs,
524+
constant,
525+
)
477526
case QuadraticExpression():
478527
# Append all quadratic variables, coefficients and constants
479528
vars = self.vars + other.vars
@@ -490,8 +539,14 @@ def __sub__(self, other):
490539
qcoeffs.append(i)
491540
for i in other.qcoefficients:
492541
qcoeffs.append(-1.0 * i)
493-
return QuadraticExpression(qvars1, qvars2, qcoeffs, vars, coeffs, constant)
494-
542+
return QuadraticExpression(
543+
qvars1, qvars2, qcoeffs, vars, coeffs, constant
544+
)
545+
case _:
546+
raise ValueError(
547+
"Can't sub type %s from Quadratic Expression"
548+
% type(other).__name__
549+
)
495550

496551
def __rsub__(self, other):
497552
# other - self -> other + self * -1.0
@@ -520,9 +575,18 @@ def __mul__(self, other):
520575
match other:
521576
case int() | float():
522577
coeffs = [coeff * float(other) for coeff in self.coefficients]
523-
qcoeffs = [qcoeff * float(other) for qcoeff in self.qcoefficients]
578+
qcoeffs = [
579+
qcoeff * float(other) for qcoeff in self.qcoefficients
580+
]
524581
constant = self.constant * float(other)
525-
return QuadraticExpression(self.qvars1, self.qvars2, qcoeffs, self.vars, coeffs, constant)
582+
return QuadraticExpression(
583+
self.qvars1,
584+
self.qvars2,
585+
qcoeffs,
586+
self.vars,
587+
coeffs,
588+
constant,
589+
)
526590
case _:
527591
raise ValueError(
528592
"Can't multiply type %s by QuadraticExpresson"
@@ -555,9 +619,18 @@ def __truediv__(self, other):
555619
match other:
556620
case int() | float():
557621
coeffs = [coeff / float(other) for coeff in self.coefficients]
558-
qcoeffs = [qcoeff / float(other) for qcoeff in self.qcoefficients]
622+
qcoeffs = [
623+
qcoeff / float(other) for qcoeff in self.qcoefficients
624+
]
559625
constant = self.constant / float(other)
560-
return QuadraticExpression(self.qvars1, self.qvars2, qcoeffs, self.vars, coeffs, constant)
626+
return QuadraticExpression(
627+
self.qvars1,
628+
self.qvars2,
629+
qcoeffs,
630+
self.vars,
631+
coeffs,
632+
constant,
633+
)
561634
case _:
562635
raise ValueError(
563636
"Can't divide LinearExpression by type %s"
@@ -783,11 +856,17 @@ def __imul__(self, other):
783856
for j in range(len(other.vars)):
784857
qvars1.append(self.vars[i])
785858
qvars2.append(other.vars[j])
786-
qcoeffs.append(self.coefficients[i] * other.coefficients[j])
859+
qcoeffs.append(
860+
self.coefficients[i] * other.coefficients[j]
861+
)
787862
vars = self.vars + other.vars
788-
coeffs = [other.constant * i for i in self.coefficients] + [self.constant * i for i in other.coefficients]
863+
coeffs = [other.constant * i for i in self.coefficients] + [
864+
self.constant * i for i in other.coefficients
865+
]
789866
constant = self.constant * other.constant
790-
return QuadraticExpression(qvars1, qvars2, qcoeffs, vars, coeffs, constant)
867+
return QuadraticExpression(
868+
qvars1, qvars2, qcoeffs, vars, coeffs, constant
869+
)
791870
case _:
792871
raise ValueError(
793872
"Can't multiply type %s by LinearExpresson"
@@ -807,11 +886,17 @@ def __mul__(self, other):
807886
for j in range(len(other.vars)):
808887
qvars1.append(self.vars[i])
809888
qvars2.append(other.vars[j])
810-
qcoeffs.append(self.coefficients[i] * other.coefficients[j])
889+
qcoeffs.append(
890+
self.coefficients[i] * other.coefficients[j]
891+
)
811892
vars = self.vars + other.vars
812-
coeffs = [other.constant * i for i in self.coefficients] + [self.constant * i for i in other.coefficients]
893+
coeffs = [other.constant * i for i in self.coefficients] + [
894+
self.constant * i for i in other.coefficients
895+
]
813896
constant = self.constant * other.constant
814-
return QuadraticExpression(qvars1, qvars2, qcoeffs, vars, coeffs, constant)
897+
return QuadraticExpression(
898+
qvars1, qvars2, qcoeffs, vars, coeffs, constant
899+
)
815900
case Variable():
816901
return other * self
817902

@@ -1361,7 +1446,11 @@ def setObjective(self, expr, sense=MINIMIZE):
13611446
sum_coeff
13621447
)
13631448
self.ObjConstant = expr.constant
1364-
self.objective_qcoo_matrix = expr.qvars1, expr.qvars2, expr.qcoefficients
1449+
self.objective_qcoo_matrix = (
1450+
expr.qvars1,
1451+
expr.qvars2,
1452+
expr.qcoefficients,
1453+
)
13651454
case _:
13661455
raise ValueError(
13671456
"Objective must be a LinearExpression or a constant"
@@ -1523,7 +1612,12 @@ def Obj(self):
15231612
if not self.objective_qcoo_matrix:
15241613
return LinearExpression(self.vars, coeffs, self.ObjConstant)
15251614
else:
1526-
return QuadraticExpression(*self.objective_qcoo_matrix, self.vars, coeffs, self.ObjConstant)
1615+
return QuadraticExpression(
1616+
*self.objective_qcoo_matrix,
1617+
self.vars,
1618+
coeffs,
1619+
self.ObjConstant,
1620+
)
15271621

15281622
def getCSR(self):
15291623
"""
@@ -1556,18 +1650,19 @@ def getQcsr(self):
15561650
row_dict = Qdict[var1.index]
15571651
var2 = vars2[i]
15581652
coeff = coeffs[i]
1559-
row_dict[var2.index] = row_dict[var2.index] + coeff if var2.index in row_dict else coeff
1653+
row_dict[var2.index] = (
1654+
row_dict[var2.index] + coeff
1655+
if var2.index in row_dict
1656+
else coeff
1657+
)
15601658
for i in range(0, self.NumVariables):
15611659
if i in Qdict:
1562-
Qcsr_dict["column_indices"].extend(
1563-
list(Qdict[i].keys())
1564-
)
1660+
Qcsr_dict["column_indices"].extend(list(Qdict[i].keys()))
15651661
Qcsr_dict["values"].extend(list(Qdict[i].values()))
15661662
Qcsr_dict["row_pointers"].append(len(Qcsr_dict["column_indices"]))
15671663
self.objective_qcsr_matrix = Qcsr_dict
15681664
return self.dict_to_object(Qcsr_dict)
15691665

1570-
15711666
def relax(self):
15721667
"""
15731668
Relax a MIP problem into an LP problem and return the relaxed model.

python/cuopt/cuopt/tests/linear_programming/test_python_API.py

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,7 @@ def test_quadratic_expression_and_matrix():
675675

676676
# Test Quadratic Expressions
677677
expr1 = x * x # var * var
678-
expr2 = expr1 + y*z + 2 # QP + QP + const
678+
expr2 = expr1 + y * z + 2 # QP + QP + const
679679
expr3 = expr2 * 4 # QP * const
680680

681681
assert expr1.getVariables() == [(x, x)]
@@ -697,10 +697,12 @@ def test_quadratic_expression_and_matrix():
697697
assert expr4.getLinearExpression().getVariables() == [y]
698698
assert expr4.getLinearExpression().getCoefficients() == [-1]
699699

700-
expr5 = z + 7*y + 1 # LP
700+
expr5 = z + 7 * y + 1 # LP
701701
expr6 = y * expr5 # var * LP
702-
expr7 = expr5 - x*x # LP - QP
703-
expr8 = expr4 - expr5 # QP - LP # x2 + yz + 2 - y - 7y -z - 1 + 7y2 + yz + y
702+
expr7 = expr5 - x * x # LP - QP
703+
expr8 = (
704+
expr4 - expr5
705+
) # QP - LP # x2 + yz + 2 - y - 7y -z - 1 + 7y2 + yz + y
704706
expr8 += expr6 # QP + QP
705707

706708
assert expr6.getVariable1(0) is y
@@ -725,7 +727,7 @@ def test_quadratic_expression_and_matrix():
725727
assert expr8.getLinearExpression().getConstant() == 1
726728
assert expr8.getLinearExpression().getCoefficients() == [-1, -7, -1, 1]
727729

728-
expr9 = expr5 * (3*x - y + z + 3) # LP * LP
730+
expr9 = expr5 * (3 * x - y + z + 3) # LP * LP
729731
# expr9 = 21*y*x + 7*y*z + 21*y - 7*y*y + 3*x + z + 3 - y + 3*z*x + z*z +3*z - z*y
730732

731733
qvariables = [(y, x), (y, y), (y, z), (z, x), (z, y), (z, z)]
@@ -757,23 +759,57 @@ def test_quadratic_expression_and_matrix():
757759
assert Qcsr.values == exp_vals
758760

759761

760-
def test_quadratic_objective():
761-
762+
def test_quadratic_objective_1():
762763
# Minimize x1 ^2 + 4 x2 ^2 - 8 x1 - 16 x2
763764
# subject to x1 + x2 >= 5
764765
# x1 >= 3
765766
# x2 >= 0
766767

767768
problem = Problem()
768769
x1 = problem.addVariable(lb=3.0, name="x")
769-
x2 = problem.addVariable(lb = 0, name="y")
770+
x2 = problem.addVariable(lb=0, name="y")
770771

771772
problem.addConstraint(x1 + x2 >= 5)
772-
problem.setObjective(x1*x1 + 4*x2*x2 - 8*x1 - 16*x2)
773+
problem.setObjective(x1 * x1 + 4 * x2 * x2 - 8 * x1 - 16 * x2)
773774

774775
problem.solve()
775776

776777
assert problem.Status.name == "Optimal"
777778
assert x1.getValue() == pytest.approx(4.0)
778779
assert x2.getValue() == pytest.approx(2.0)
779-
assert problem.ObjValue == pytest.approx(-32.0)
780+
assert problem.ObjValue == pytest.approx(-32.0)
781+
782+
783+
def test_quadratic_objective_2():
784+
# Minimize 4 x1^2 + 2 x2^2 + 3 x3^2 + 1.5 x1 x3 - 2 x1 + 0.5 x2 - x3
785+
# subject to x1 + 2*x2 + x3 <= 3
786+
# x1 >= 0
787+
# x2 >= 0
788+
# x3 >= 0
789+
790+
problem = Problem()
791+
x1 = problem.addVariable(lb=0, name="x")
792+
x2 = problem.addVariable(lb=0, name="y")
793+
x3 = problem.addVariable(lb=0, name="z")
794+
795+
problem.addConstraint(x1 + 2 * x2 + x3 <= 3)
796+
problem.setObjective(
797+
2 * x1 * x1
798+
+ 2 * x2 * x2
799+
+ 3 * x3 * x3
800+
+ 1.5 * x1 * x3
801+
- 2 * x1
802+
+ 0.5 * x2
803+
- 2 * x1 * x2
804+
- 1.0 * x3
805+
+ 2 * x1 * x1
806+
+ 2 * x1 * x2
807+
)
808+
809+
problem.solve()
810+
811+
assert problem.Status.name == "Optimal"
812+
assert x1.getValue() == pytest.approx(0.2295081)
813+
assert x2.getValue() == pytest.approx(0.0000000, abs=0.000001)
814+
assert x3.getValue() == pytest.approx(0.1092896)
815+
assert problem.ObjValue == pytest.approx(-0.284153)

0 commit comments

Comments
 (0)