# SPDX-License-Identifier: MPL-2.0
#
# ODAT-SE -- an open framework for data analysis
# Copyright (C) 2020- The University of Tokyo
#
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
from abc import ABCMeta, abstractmethod
import numpy as np
from .read_matrix import read_matrix, read_vector
[docs]
class LimitationBase(metaclass=ABCMeta):
"""
Abstract base class for limitations.
"""
[docs]
@abstractmethod
def __init__(self, is_limitary: bool):
"""
Initialize the limitation.
Parameters
----------
is_limitary : bool
Boolean indicating if the limitation is active.
"""
self.is_limitary = is_limitary
[docs]
@abstractmethod
def judge(self, x: np.ndarray) -> bool:
"""
Abstract method to judge if the limitation is satisfied.
Parameters
----------
x : np.ndarray
Input array to be judged.
Returns
-------
bool
Boolean indicating if the limitation is satisfied.
"""
raise NotImplementedError
[docs]
class Unlimited(LimitationBase):
"""
Class representing an unlimited (no limitation) condition.
"""
[docs]
def __init__(self):
"""
Initialize the unlimited condition.
"""
super().__init__(False)
[docs]
def judge(self, x: np.ndarray) -> bool:
"""
Always returns True as there is no limitation.
Parameters
----------
x : np.ndarray
Input array to be judged.
Returns
-------
bool
Always True.
"""
return True
[docs]
class Inequality(LimitationBase):
"""
Class representing an inequality limitation.
"""
[docs]
def __init__(self, a: np.ndarray, b: np.ndarray, is_limitary: bool):
"""
Initialize the inequality limitation.
Parameters
----------
a : np.ndarray
Coefficient matrix.
b : np.ndarray
Constant vector.
is_limitary : bool
Boolean indicating if the limitation is active.
"""
super().__init__(is_limitary)
if self.is_limitary:
self.a = np.array(a)
self.b = np.array(b)
self.minusb = -np.array(b)
self.n_formula = a.shape[0]
self.ndim = a.shape[1]
[docs]
def judge(self, x: np.ndarray) -> bool:
"""
Judge if the inequality limitation is satisfied.
Parameters
----------
x : np.ndarray
Input array to be judged.
Returns
-------
bool
Boolean indicating if the limitation is satisfied.
"""
if self.is_limitary:
Ax_b = np.dot(self.a, x) + self.b
judge_result = np.all(Ax_b > 0)
else:
judge_result = True
return judge_result
[docs]
@classmethod
def from_dict(cls, d):
"""
Create an Inequality instance from a dictionary.
Parameters
----------
d
Dictionary containing 'co_a' and 'co_b' keys.
Returns
-------
Inequality
an Inequality instance.
"""
co_a: np.ndarray = read_matrix(d.get("co_a", []))
co_b: np.ndarray = read_matrix(d.get("co_b", []))
if co_a.size == 0:
is_set_co_a = False
else:
if co_a.ndim == 2:
is_set_co_a = True
else:
raise ValueError("co_a should be a matrix of size equal to number of constraints times dimension")
if co_b.size == 0:
is_set_co_b = False
else:
if co_b.ndim == 2 and co_b.shape == (co_a.shape[0], 1):
is_set_co_b = True
else:
raise ValueError("co_b should be a column vector of size equal to number of constraints")
if is_set_co_a and is_set_co_b:
is_limitary = True
elif (not is_set_co_a) and (not is_set_co_b):
is_limitary = False
else:
msg = "ERROR: Both co_a and co_b must be defined."
raise ValueError(msg)
return cls(co_a, co_b.reshape(-1), is_limitary)