FoundryProductsTechnologyCompanyInvestor relationsResource libraryNews
Contact us
Resource library
    Resource library home
    Developer resources
    Applications
    Lessons
    Research and publications
    Support
      Software packages
        eqc-direct software package
        qci-client software package
        uqrng-direct software package
        emucore-direct software package
        eqc-models software package
          Getting Started
          Basic Usage
          eqc_models
          Dependencies
      Spec sheets
      User guides

Couldn’t find what you are looking for? Reach out to technical support.

Contact support
Privacy PolicyCookie PolicyTerms of UseForward Looking StatementsAccessibility Statement
Terms and Conditions of SaleEnd User License Agreement

© 2018-2026 Quantum Computing Inc.

Download

Default

Source code for eqc_models.base.constraints

  • # (C) Quantum Computing Inc., 2024.
  • """
  • Four useful classes are provided in this module.
  • ConstraintsMixin
  • Converts equality constraints to penalties. Depends on the value provided
  • for penalty_multiplier.
  • InequalitiesMixin
  • Allows inequality constraints, converting to equality constraints before
  • penalties by adding slack variables.
  • ConstraintModel
  • An example implementation of the ConstraintsMixin.
  • InequalityConstraintModel
  • An example implementation of the InequalitiesMixin.
  • >>> lhs = np.array([[1, 1],
  • ... [2, 2]])
  • >>> rhs = np.array([1, 1])
  • >>> senses = ["LE", "GE"]
  • >>> model = InequalityConstraintModel()
  • >>> model.constraints = lhs, rhs
  • >>> model.senses = senses
  • >>> A, b = model.constraints
  • >>> A
  • array([[ 1., 1., 1., 0.],
  • [ 2., 2., 0., -1.]])
  • >>> model.penalty_multiplier = 1.0
  • >>> model.checkPenalty(np.array([1, 0, 0, 1]))
  • 0.0
  • >>> model.checkPenalty(np.array([1, 1, 0, 0]))
  • 10.0
  • """
  • from typing import (List, Tuple)
  • import numpy as np
  • from eqc_models.base.base import EqcModel
  • [docs]
  • class ConstraintsMixIn:
  • """
  • This mixin class contains methods and attributes which transform
  • linear constraints into penalties.
  • """
  • lhs = None
  • rhs = None
  • # alpha is the internal name for the penalty multiplier
  • # defaulting to 1 as a user experience enhancement
  • # while forcing the user to choose a value helps to
  • # remind of the need, it is not friendly to get a None
  • # type error when an attempt to use it is made.
  • alpha = 1
  • linear_objective = None
  • quad_objective = None
  • @property
  • def penalties(self) -> Tuple[np.ndarray, np.ndarray]:
  • """ Returns two numpy arrays, one linear and one quadratic pieces of an operator """
  • lhs, rhs = self.constraints
  • if lhs is None or rhs is None:
  • raise ValueError("Constraints lhs and/or rhs are undefined. " +
  • "Both must be instantiated numpy arrays.")
  • Pq = lhs.T @ lhs
  • Pl = -2 * rhs.T @ lhs
  • return Pl.T, Pq
  • @property
  • def penalty_multiplier(self) -> float:
  • return self.alpha
  • @penalty_multiplier.setter
  • def penalty_multiplier(self, value: float):
  • self.alpha = value
  • @property
  • def constraints(self):
  • return self.lhs, self.rhs
  • @constraints.setter
  • def constraints(self, value: Tuple[np.ndarray, np.ndarray]):
  • self.lhs, self.rhs = value
  • @property
  • def offset(self) -> float:
  • """ Calculate the offset due to the conversion of constraints to penalties """
  • lhs, rhs = self.constraints
  • return np.squeeze(rhs.T@rhs)
  • [docs]
  • def evaluate(self, solution : np.ndarray, alpha : float = None, includeoffset:bool=False):
  • """
  • Compute the objective value plus penalties for the given solution. Including
  • the offset will ensure the penalty contribution is non-negative.
  • Parameters
  • ----------
  • solution : np.array
  • The solution vector for the problem.
  • alpha : float
  • Penalty multiplier, optional. This can be used to test different
  • multipliers for determination of sufficiently large values.
  • """
  • if alpha is None:
  • alpha = self.penalty_multiplier
  • penalty = self.evaluatePenalties(solution)
  • penalty *= alpha
  • if includeoffset:
  • penalty += alpha * self.offset
  • return penalty + self.evaluateObjective(solution)
  • [docs]
  • def evaluatePenalties(self, solution) -> float:
  • """
  • Evaluate penalty function without alpha or offset
  • Parameters
  • ----------
  • solution : np.array
  • The solution vector for the problem.
  • """
  • Pl, Pq = self.penalties
  • qpart = solution.T@Pq@solution
  • lpart = Pl.T@solution
  • ttlpart = qpart + lpart
  • return ttlpart
  • [docs]
  • def checkPenalty(self, solution : np.ndarray):
  • """
  • Get the penalty of the solution.
  • Parameters
  • ----------
  • solution : np.array
  • The solution vector for the problem.
  • """
  • penalty = self.evaluatePenalties(solution)
  • penalty += self.penalty_multiplier * self.offset
  • assert penalty >= 0, "Inconsistent model, penalty cannot be less than 0."
  • return penalty
  • [docs]
  • class InequalitiesMixin:
  • """
  • This mixin enables inequality constraints by automatically
  • generating slack variables for each inequality
  • This mixin adds a `senses` attribute which has a value for each
  • constraint. The values are one of 'EQ', 'LE' or 'GE' for equal
  • to, less than or equal to or greater than or equal to. The effect
  • of the value is to control whether a slack is added and what
  • the sign of the slack variable in the constraint is. Negative
  • is used for GE, positive is used for LE and all slack variables
  • get a coefficient magnitude of 1.
  • The constraints are modified on demand, so the class members,
  • `lhs` and `rhs` remain unmodified.
  • """
  • _senses = None
  • @property
  • def senses(self) -> List[str]:
  • """ Comparison operator by constraint """
  • return self._senses
  • @senses.setter
  • def senses(self, value : List[str]):
  • self._senses = value
  • @property
  • def num_slacks(self) -> int:
  • """
  • The number of slack variables. Will match the number of inequality
  • constraints.
  • Returns
  • -------
  • number : int
  • """
  • G = self.lhs
  • m = G.shape[0]
  • senses = self.senses
  • num_slacks = sum([0 if senses[i] == "EQ" else 1 for i in range(m)])
  • return num_slacks
  • @property
  • def constraints(self) -> Tuple[np.ndarray, np.ndarray]:
  • """
  • Get the general form of the constraints, add slacks where needed
  • and return a standard, equality constraint form.
  • """
  • G = self.lhs
  • h = self.rhs
  • senses = self.senses
  • m = G.shape[0]
  • n = G.shape[1]
  • num_slacks = self.num_slacks
  • # Adjusted dimensions for slack variables
  • slack_vars = np.zeros((m, num_slacks))
  • ii = 0
  • for i in range(m):
  • rule = senses[i]
  • if rule == "LE":
  • # Add slack variable for less than or equal constraint
  • slack_vars[i, ii] = 1
  • ii += 1
  • elif rule == "GE":
  • # Add negated slack variable for greater than or equal constraint
  • slack_vars[i, ii] = -1
  • ii += 1
  • A = np.hstack((G, slack_vars))
  • b = h
  • return A, b
  • @constraints.setter
  • def constraints(self, value : Tuple[np.ndarray, np.ndarray]):
  • if len(value) != 2:
  • raise ValueError("Constraints must be specified as a 2-tuple")
  • self.lhs, self.rhs = value
  • [docs]
  • class ConstraintModel(ConstraintsMixIn, EqcModel):
  • """
  • Abstract class for representing linear constrained optimization problems as
  • EQC models.
  • """
  • [docs]
  • class InequalityConstraintModel(InequalitiesMixin, ConstraintModel):
  • """
  • Abstract class for a linear constrained optimization model with inequality constraints
  • """
  • # class MIPBinaryModel(ConstraintModel):
  • #
  • # binary_only_variables = None
  • #
  • # @property
  • # def binaries(self) -> List:
  • # return self.binary_only_variables
  • #
  • # @binaries.setter
  • # def binaries(self, value : List) -> None:
  • # for item in value:
  • # assert int(item) == item, "Index of binary variable must be integer"
  • # self.binary_only_variables = value
  • #
  • # @property
  • # def penalties(self) -> Tuple[np.ndarray, np.ndarray]:
  • # # get the explicit constraint penalties
  • # Pl, Pq = super(MIPBinaryModel, self).penalties
  • # if self.binary_only_variables is not None:
  • # # add penalties which enforce the selection of 0 or 1 for each binar variable
  • # for idx in self.binaries:
  • # # add the values x(x-1)^2 -> x^3-2x^2+x
  • # indices = [[idx+1, idx+1, idx+1], [0, idx+1, idx+1], [0, 0, idx+1]]
  • # coefficients = [1, -2, 1]
  • #
  • # return Pl, Pq
Next page

Content

  • Source code for eqc_models.base.constraints