-
Notifications
You must be signed in to change notification settings - Fork 74
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
Adding the unbalanced penalization method #1347
Merged
arcondello
merged 10 commits into
dwavesystems:main
from
alejomonbar:Adding_unbalanced_penalization
Aug 17, 2023
Merged
Changes from 8 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
4f62c44
Adding the unbalanced penalization method
alejomonbar 5cac8c8
Update binary_quadratic_model.py
alejomonbar d96576c
Update binary_quadratic_model.py
alejomonbar cb0668c
solving some issues
alejomonbar f2b6313
Merge pull request #1 from dwavesystems/main
alejomonbar b47e37f
Adding test and release note
alejomonbar e817a42
Merge branch 'Adding_unbalanced_penalization' of https://github.com/a…
alejomonbar 4582259
Update unbalanced-penalization-e16af2362227bcdb.yaml
alejomonbar 2fe34ae
Update binary_quadratic_model.py
alejomonbar a2668cb
Make minor edits to unbalance penalization method release notes
arcondello File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -702,105 +702,125 @@ def add_linear_equality_constraint( | |
self.offset += lagrange_multiplier * constant * constant | ||
|
||
def add_linear_inequality_constraint( | ||
self, terms: Iterable[Tuple[Variable, int]], | ||
lagrange_multiplier: Bias, | ||
label: str, | ||
constant: int = 0, | ||
lb: int = np.iinfo(np.int64).min, | ||
ub: int = 0, | ||
cross_zero: bool = False | ||
) -> Iterable[Tuple[Variable, int]]: | ||
"""Add a linear inequality constraint as a quadratic objective. | ||
|
||
The linear inequality constraint is of the form: | ||
:math:`lb <= \sum_{i,k} a_{i,k} x_{i,k} + constant <= ub`. | ||
|
||
For constraints with fractional coefficients, multiply both sides of the | ||
inequality by an appropriate factor of ten to attain or approximate | ||
integer coefficients. | ||
|
||
Args: | ||
terms: | ||
Values of the :math:`\sum_{i} a_{i} x_{i}` term as an | ||
:math:`i`--length iterable of 2-tuples, ``(variable, bias)``, with | ||
each tuple constituting a term in the summation. | ||
lagrange_multiplier: | ||
Weight or penalty strength. The linear constraint is multiplied | ||
by this value (which does not appear explicitly in the above | ||
equation) when added to the binary quadratic model. | ||
label: | ||
Prefix for labels of any slack variables used in the added | ||
objective. | ||
constant: | ||
Value of the constant term of the linear constraint. | ||
lb: | ||
Lower bound for the constraint. | ||
ub: | ||
Upper bound for the constraint. | ||
cross_zero: | ||
When True, adds zero to the domain of constraint. | ||
|
||
Returns: | ||
slack_terms: Values of :math:`\sum_{i} b_{i} slack_{i}` as an | ||
:math:`i`--length iterable of 2-tuples, ``(slack variable, bias)``, | ||
with each tuple constituting a term in the summation. | ||
""" | ||
|
||
if isinstance(terms, Iterator): | ||
terms = list(terms) | ||
|
||
if int(constant) != constant or int(lb) != lb or int(ub) != ub or any( | ||
int(bias) != bias for _, bias in terms): | ||
warnings.warn("For constraints with fractional coefficients, " | ||
"multiply both sides of the inequality by an " | ||
"appropriate factor of ten to attain or " | ||
"approximate integer coefficients. ") | ||
|
||
terms_upper_bound = sum(v for _, v in terms if v > 0) | ||
terms_lower_bound = sum(v for _, v in terms if v < 0) | ||
ub_c = min(terms_upper_bound, ub - constant) | ||
lb_c = max(terms_lower_bound, lb - constant) | ||
|
||
if terms_upper_bound <= ub_c and terms_lower_bound >= lb_c: | ||
warnings.warn( | ||
f'Did not add constraint {label}.' | ||
' This constraint is feasible' | ||
' with any value for state variables.') | ||
return [] | ||
|
||
if ub_c < lb_c: | ||
raise ValueError( | ||
f'The given constraint ({label}) is infeasible with any value' | ||
' for state variables.') | ||
|
||
slack_upper_bound = int(ub_c - lb_c) | ||
if slack_upper_bound == 0: | ||
self.add_linear_equality_constraint(terms, lagrange_multiplier, -ub_c) | ||
return [] | ||
else: | ||
slack_terms = [] | ||
zero_constraint = False | ||
if cross_zero: | ||
if lb_c > 0 or ub_c < 0: | ||
if ub_c-slack_upper_bound > 0: | ||
zero_constraint = True | ||
|
||
num_slack = int(np.floor(np.log2(slack_upper_bound))) | ||
slack_coefficients = [2 ** j for j in range(num_slack)] | ||
if slack_upper_bound - 2 ** num_slack >= 0: | ||
slack_coefficients.append(slack_upper_bound - 2 ** num_slack + 1) | ||
|
||
for j, s in enumerate(slack_coefficients): | ||
sv = self.add_variable(f'slack_{label}_{j}') | ||
slack_terms.append((sv, s)) | ||
|
||
if zero_constraint: | ||
sv = self.add_variable(f'slack_{label}_{num_slack + 1}') | ||
slack_terms.append((sv, ub_c - slack_upper_bound)) | ||
|
||
self.add_linear_equality_constraint(terms + slack_terms, | ||
lagrange_multiplier, -ub_c) | ||
return slack_terms | ||
self, terms: Iterable[Tuple[Variable, int]], | ||
lagrange_multiplier: Bias, | ||
label: str, | ||
constant: int = 0, | ||
lb: int = np.iinfo(np.int64).min, | ||
ub: int = 0, | ||
cross_zero: bool = False, | ||
penalization_method: str = "slack", | ||
) -> Iterable[Tuple[Variable, int]]: | ||
"""Add a linear inequality constraint as a quadratic objective. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like everything got indented by an additional tab? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I fixed it! @arcondello |
||
|
||
The linear inequality constraint is of the form: | ||
:math:`lb <= \sum_{i,k} a_{i,k} x_{i,k} + constant <= ub`. | ||
|
||
For constraints with fractional coefficients, multiply both sides of the | ||
inequality by an appropriate factor of ten to attain or approximate | ||
integer coefficients. | ||
|
||
Args: | ||
terms: | ||
Values of the :math:`\sum_{i} a_{i} x_{i}` term as an | ||
:math:`i`--length iterable of 2-tuples, ``(variable, bias)``, with | ||
each tuple constituting a term in the summation. | ||
lagrange_multiplier: | ||
Weight or penalty strength. The linear constraint is multiplied | ||
by this value (which does not appear explicitly in the above | ||
equation) when added to the binary quadratic model. | ||
label: | ||
Prefix for labels of any slack variables used in the added | ||
objective. | ||
constant: | ||
Value of the constant term of the linear constraint. | ||
lb: | ||
Lower bound for the constraint. | ||
ub: | ||
Upper bound for the constraint. | ||
cross_zero: | ||
When True, adds zero to the domain of constraint. | ||
penalization_method: | ||
Whether to use slack variables or the unbalanced penalization method [1]. | ||
("slack", "unbalanced") | ||
[1] https://arxiv.org/abs/2211.13914 | ||
|
||
Returns: | ||
slack_terms: Values of :math:`\sum_{i} b_{i} slack_{i}` as an | ||
:math:`i`--length iterable of 2-tuples, ``(slack variable, bias)``, | ||
with each tuple constituting a term in the summation. | ||
""" | ||
|
||
if isinstance(terms, Iterator): | ||
terms = list(terms) | ||
|
||
if int(constant) != constant or int(lb) != lb or int(ub) != ub or any( | ||
int(bias) != bias for _, bias in terms): | ||
warnings.warn("For constraints with fractional coefficients, " | ||
"multiply both sides of the inequality by an " | ||
"appropriate factor of ten to attain or " | ||
"approximate integer coefficients. ") | ||
|
||
terms_upper_bound = sum(v for _, v in terms if v > 0) | ||
terms_lower_bound = sum(v for _, v in terms if v < 0) | ||
ub_c = min(terms_upper_bound, ub - constant) | ||
lb_c = max(terms_lower_bound, lb - constant) | ||
|
||
if terms_upper_bound <= ub_c and terms_lower_bound >= lb_c: | ||
warnings.warn( | ||
f'Did not add constraint {label}.' | ||
' This constraint is feasible' | ||
' with any value for state variables.') | ||
return [] | ||
|
||
if ub_c < lb_c: | ||
raise ValueError( | ||
f'The given constraint ({label}) is infeasible with any value' | ||
' for state variables.') | ||
if penalization_method == "slack": | ||
slack_upper_bound = int(ub_c - lb_c) | ||
if slack_upper_bound == 0: | ||
self.add_linear_equality_constraint(terms, lagrange_multiplier, -ub_c) | ||
return [] | ||
else: | ||
slack_terms = [] | ||
zero_constraint = False | ||
if cross_zero: | ||
if lb_c > 0 or ub_c < 0: | ||
if ub_c-slack_upper_bound > 0: | ||
zero_constraint = True | ||
|
||
num_slack = int(np.floor(np.log2(slack_upper_bound))) | ||
slack_coefficients = [2 ** j for j in range(num_slack)] | ||
if slack_upper_bound - 2 ** num_slack >= 0: | ||
slack_coefficients.append(slack_upper_bound - 2 ** num_slack + 1) | ||
|
||
for j, s in enumerate(slack_coefficients): | ||
sv = self.add_variable(f'slack_{label}_{j}') | ||
slack_terms.append((sv, s)) | ||
|
||
if zero_constraint: | ||
sv = self.add_variable(f'slack_{label}_{num_slack + 1}') | ||
slack_terms.append((sv, ub_c - slack_upper_bound)) | ||
|
||
self.add_linear_equality_constraint(terms + slack_terms, | ||
lagrange_multiplier, -ub_c) | ||
return slack_terms | ||
|
||
elif penalization_method == "unbalanced": | ||
if not isinstance(lagrange_multiplier, Iterable): | ||
raise TypeError('A list with two lagrange_multiplier are needed' | ||
' for the unbalanced penalization method.') | ||
|
||
for v, bias in terms: | ||
self.add_linear(v, lagrange_multiplier[0] * bias) | ||
self.offset += -ub_c | ||
self.add_linear_equality_constraint(terms, lagrange_multiplier[1], -ub_c) | ||
|
||
return [] | ||
else: | ||
raise ValueError(f"The method {penalization_method} is not a valid method." | ||
' Choose between ["slack", "unbalanced"]') | ||
|
||
def add_linear_from_array(self, linear: Sequence): | ||
"""Add linear biases from an array-like to a binary quadratic model. | ||
|
3 changes: 3 additions & 0 deletions
3
releasenotes/notes/unbalanced-penalization-e16af2362227bcdb.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
--- | ||
features: | ||
- Add `BinaryQuadraticModel::add_linear_inequality_constraint:penalization_method` parameter. It allows the use of unbalanced penalization https://arxiv.org/abs/2211.13914 instead of the slack variables method for the inequality constraints. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Added @arcondello