# This code is part of a Qiskit project.
#
# (C) Copyright IBM 2019, 2025.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""Quadratic Objective."""
from __future__ import annotations
from enum import Enum
from typing import Any
from numpy import ndarray
from scipy.sparse import spmatrix
from ..exceptions import QiskitOptimizationError
from .linear_constraint import LinearExpression
from .quadratic_expression import QuadraticExpression
from .quadratic_program_element import QuadraticProgramElement
class ObjSense(Enum):
"""Objective Sense Type."""
MINIMIZE = 1
MAXIMIZE = -1
[docs]
class QuadraticObjective(QuadraticProgramElement):
"""Representation of quadratic objective function of the form:
constant + linear * x + x * quadratic * x.
"""
Sense = ObjSense
def __init__( # pylint: disable=too-many-positional-arguments
self,
quadratic_program: Any,
constant: float = 0.0,
linear: ndarray | spmatrix | list[float] | dict[str | int, float] | None = None,
quadratic: (
ndarray | spmatrix | list[list[float]] | dict[tuple[int | str, int | str], float] | None
) = None,
sense: ObjSense = ObjSense.MINIMIZE,
) -> None:
"""Constructs a quadratic objective function.
Args:
quadratic_program: The parent quadratic program.
constant: The constant offset of the objective.
linear: The coefficients of the linear part of the objective.
quadratic: The coefficients of the quadratic part of the objective.
sense: The optimization sense of the objective.
"""
super().__init__(quadratic_program)
self._constant = constant
if linear is None:
linear = {}
self._linear = LinearExpression(quadratic_program, linear)
if quadratic is None:
quadratic = {}
self._quadratic = QuadraticExpression(quadratic_program, quadratic)
self._sense = sense
@property
def constant(self) -> float:
"""Returns the constant part of the objective function.
Returns:
The constant part of the objective function.
"""
return self._constant
@constant.setter
def constant(self, constant: float) -> None:
"""Sets the constant part of the objective function.
Args:
constant: The constant part of the objective function.
"""
self._constant = constant
@property
def linear(self) -> LinearExpression:
"""Returns the linear part of the objective function.
Returns:
The linear part of the objective function.
"""
return self._linear
@linear.setter
def linear(
self,
linear: ndarray | spmatrix | list[float] | dict[str | int, float],
) -> None:
"""Sets the coefficients of the linear part of the objective function.
Args:
linear: The coefficients of the linear part of the objective function.
"""
self._linear = LinearExpression(self.quadratic_program, linear)
@property
def quadratic(self) -> QuadraticExpression:
"""Returns the quadratic part of the objective function.
Returns:
The quadratic part of the objective function.
"""
return self._quadratic
@quadratic.setter
def quadratic(
self,
quadratic: (
ndarray | spmatrix | list[list[float]] | dict[tuple[int | str, int | str], float]
),
) -> None:
"""Sets the coefficients of the quadratic part of the objective function.
Args:
quadratic: The coefficients of the quadratic part of the objective function.
"""
self._quadratic = QuadraticExpression(self.quadratic_program, quadratic)
@property
def sense(self) -> ObjSense:
"""Returns the sense of the objective function.
Returns:
The sense of the objective function.
"""
return self._sense
@sense.setter
def sense(self, sense: ObjSense) -> None:
"""Sets the sense of the objective function.
Args:
sense: The sense of the objective function.
"""
self._sense = sense
[docs]
def evaluate(self, x: ndarray | list | dict[int | str, float]) -> float:
"""Evaluate the quadratic objective for given variable values.
Args:
x: The values of the variables to be evaluated.
Returns:
The value of the quadratic objective given the variable values.
Raises:
QiskitOptimizationError: if the shape of the objective function does not match with
the number of variables.
"""
n = self.quadratic_program.get_num_vars()
if self.linear.coefficients.shape != (1, n) or self.quadratic.coefficients.shape != (n, n):
raise QiskitOptimizationError(
"The shape of the objective function does not match with the number of variables. "
"Need to define the objective function after defining all variables"
)
return self.constant + self.linear.evaluate(x) + self.quadratic.evaluate(x)
[docs]
def evaluate_gradient(self, x: ndarray | list | dict[int | str, float]) -> ndarray:
"""Evaluate the gradient of the quadratic objective for given variable values.
Args:
x: The values of the variables to be evaluated.
Returns:
The value of the gradient of the quadratic objective given the variable values.
Raises:
QiskitOptimizationError: if the shape of the objective function does not match with
the number of variables.
"""
n = self.quadratic_program.get_num_vars()
if self.linear.coefficients.shape != (1, n) or self.quadratic.coefficients.shape != (n, n):
raise QiskitOptimizationError(
"The shape of the objective function does not match with the number of variables. "
"Need to define the objective function after defining all variables"
)
return self.linear.evaluate_gradient(x) + self.quadratic.evaluate_gradient(x)
def __repr__(self):
# pylint: disable=cyclic-import
from ..translators.prettyprint import expr2str, DEFAULT_TRUNCATE
expr_str = expr2str(self.constant, self.linear, self.quadratic, DEFAULT_TRUNCATE)
return f"<{self.__class__.__name__}: {self._sense.name.lower()} {expr_str}>"
def __str__(self):
# pylint: disable=cyclic-import
from ..translators.prettyprint import expr2str
expr_str = expr2str(self.constant, self.linear, self.quadratic)
return f"{self._sense.name.lower()} {expr_str}"