Source code for mpt4py.solvers.lcp

import numpy as np
from typing import Union
from dataclasses import dataclass, field
from mpt4py.base import Vector, Matrix
from .lcp_solver import lcp_solve, LcpSolution
from .plcp_solver import plcp_solve, PlcpSolution


[docs] @dataclass class LinearComplementarityProblem: r""" Encapsulates data and solutions for LCP and pLCP. .. math:: w - Mz = q + Q\theta, w,z \geq 0, w^\top z = 0 """ # LCP M: Matrix = field(default_factory=lambda: np.zeros((0, 0))) q: Vector = field(default_factory=lambda: np.zeros((0,))) Q: Matrix = field(default_factory=lambda: np.zeros((0, 0))) # parameter set: Ath*th <= bth Ath: Matrix = field(default_factory=lambda: np.zeros((0, 0))) bth: Vector = field(default_factory=lambda: np.zeros((0,))) n: int = field(init=False) # problem dimension d: int = field(init=False) # number of parameters def __post_init__(self): """ Check if the dimensions and values are valid If M is non-empty, then the problem is considered to be an LCP otherwise it is considered to be LP/QP """ # make sure the inputs are matrices and vectors for attr in ['M', 'Q', 'Ath']: if np.ndim(getattr(self, attr)) != 2: raise ValueError("Opt: " + attr + " must be a matrix") for attr in ['q', 'bth']: if np.ndim(getattr(self, attr)) != 1: raise ValueError("Opt: " + attr + " must be a vector") # validate LCP inputs if self.M.size: self.n = self.M.shape[0] self.d = max(self.Q.shape[1], self.Ath.shape[1]) if self.M.shape[0] != self.M.shape[1]: raise ValueError("Opt: M must be square. The size of provided M is {}".format(self.M.shape)) if self.q.shape != (self.n,): raise ValueError("Opt: q must be of size ({},)".format(self.n)) if self.Q.size: if self.Q.shape[0] != self.n or self.Q.shape[1] != self.d: raise ValueError("Opt: Q must be of size ({},{})".format(self.n, self.d)) else: self.Q = np.zeros((self.n, 0)) if self.Ath.size: if self.Ath.shape[1] != self.d: raise ValueError("Opt: Ath must have {} columns)".format(self.d)) else: self.Ath = np.zeros((0, self.d)) if self.bth.size != self.Ath.shape[0]: raise ValueError("Opt: bth must be of size ({},))".format(self.Ath.shape[0])) self.problem_type = 'LCP' @property def is_parametric(self) -> bool: return self.d > 0 def __str__(self): lines = [ "Linear Complementarity Problem", f"Num variables: {self.n}", ] if self.is_parametric: lines[0] = "Parametric " + lines[0] lines.append(f"Num parameters: {self.d}") max_line_length = max(len(line) for line in lines) dashed_line = "-" * max_line_length return f"\n{dashed_line}\n" + "\n".join(lines) + f"\n{dashed_line}"
[docs] def solve(self, **kwargs) -> Union[LcpSolution, PlcpSolution]: """ Solve the LCP / pLCP """ if not self.is_parametric: return lcp_solve(self.M, self.q, **kwargs) else: raise plcp_solve(self.M, self.Q, self.q, self.Ath, self.bth, **kwargs)
[docs] def solve_for_param(self, theta: Vector, **kwargs) -> LcpSolution: """ Solve the LCP / pLCP for a specific parameter value """ if not self.is_parametric: raise ValueError("Cannot solve for a specific parameter value for non-parametric LCP.") else: assert theta.shape == (self.d,), f"theta must be of size ({self.d},)" return lcp_solve(self.M, self.Q @ theta + self.q, **kwargs)
LCP = LinearComplementarityProblem