






"""simple docstring"""







from typing import Any

import numpy as np





class      UpperCamelCase_       :



  def __init__(					self       ,						snake_case__       ,						snake_case__ )	->	None:



         """simple docstring"""





         if np.any(tableau[:, -1]       ,						where=tableau[:, -1] < 0 ):
                raise ValueError("""RHS must be > 0""" )

         UpperCAmelCase					=	tableau
         UpperCAmelCase			,				UpperCAmelCase					=	tableau.shape

         # Number of decision variables x1, x2, x3...
         UpperCAmelCase					=	n_vars

         # Number of artificial variables to be minimised
         UpperCAmelCase					=	len(np.where(tableau[self.n_vars : -1] == -1 )[0] )

         # 2 if there are >= or == constraints (nonstandard), 1 otherwise (std)
         UpperCAmelCase					=	(self.n_art_vars > 0) + 1

         # Number of slack variables added to make inequalities into equalities
         UpperCAmelCase					=	self.n_rows - self.n_stages

         # Objectives for each stage
         UpperCAmelCase					=	["""max"""]

         # In two stage simplex, first minimise then maximise
         if self.n_art_vars:
                self.objectives.append("""min""" )

         UpperCAmelCase					=	[""""""]

         # Index of current pivot row and column
         UpperCAmelCase					=	None
         UpperCAmelCase					=	None

         # Does objective row only contain (non)-negative values?
         UpperCAmelCase					=	False



  @staticmethod
  def 						UpperCamelCase_ (					*snake_case__ )	->	list[str]:



         """simple docstring"""





         if len(snake_case__ ) != 3:
                raise ValueError("""Must provide n_vars, n_slack, and n_art_vars""" )

         if not all(x >= 0 and isinstance(snake_case__       ,						snake_case__ ) for x in args ):
                raise ValueError("""All arguments must be non-negative integers""" )

         # decision | slack | artificial
         UpperCAmelCase					=	["""x""", """s""", """a"""]
         UpperCAmelCase					=	[]
         for i in range(3 ):
                for j in range(args[i] ):
                       titles.append(string_starts[i] + str(j + 1 ) )
         titles.append("""RHS""" )
         return titles



  def 						UpperCamelCase_ (					self       ,						snake_case__ )	->	tuple[Any, Any]:



         """simple docstring"""





         UpperCAmelCase					=	self.objectives[-1]

         # Find entries of highest magnitude in objective rows
         UpperCAmelCase					=	(objective == """min""") - (objective == """max""")
         UpperCAmelCase					=	np.argmax(sign * tableau[0, : self.n_vars] )

         # Choice is only valid if below 0 for maximise, and above for minimise
         if sign * self.tableau[0, col_idx] <= 0:
                UpperCAmelCase					=	True
                return 0, 0

         # Pivot row is chosen as having the lowest quotient when elements of
         # the pivot column divide the right-hand side

         # Slice excluding the objective rows
         UpperCAmelCase					=	slice(self.n_stages       ,						self.n_rows )

         # RHS
         UpperCAmelCase					=	tableau[s, -1]

         # Elements of pivot column within slice
         UpperCAmelCase					=	tableau[s, col_idx]

         # Array filled with nans
         UpperCAmelCase					=	np.full(self.n_rows - self.n_stages       ,						np.nan )

         # If element in pivot column is greater than zeron_stages, return
         # quotient or nan otherwise
         UpperCAmelCase					=	np.divide(snake_case__       ,						snake_case__       ,						out=snake_case__       ,						where=divisor > 0 )

         # Arg of minimum quotient excluding the nan values. n_stages is added
         # to compensate for earlier exclusion of objective columns
         UpperCAmelCase					=	np.nanargmin(snake_case__ ) + self.n_stages
         return row_idx, col_idx



  def 						UpperCamelCase_ (					self       ,						snake_case__       ,						snake_case__       ,						snake_case__ )	->	np.ndarray:



         """simple docstring"""





         UpperCAmelCase					=	tableau[row_idx].copy()

         UpperCAmelCase					=	piv_row[col_idx]

         # Entry becomes 1
         piv_row *= 1 / piv_val

         # Variable in pivot column becomes basic, ie the only non-zero entry
         for idx, coeff in enumerate(tableau[:, col_idx] ):
                tableau[idx] += -coeff * piv_row
         UpperCAmelCase					=	piv_row
         return tableau



  def 						UpperCamelCase_ (					self       ,						snake_case__ )	->	np.ndarray:



         """simple docstring"""





         self.objectives.pop()

         if not self.objectives:
                return tableau

         # Slice containing ids for artificial columns
         UpperCAmelCase					=	slice(-self.n_art_vars - 1       ,						-1 )

         # Delete the artificial variable columns
         UpperCAmelCase					=	np.delete(snake_case__       ,						snake_case__       ,						axis=1 )

         # Delete the objective row of the first stage
         UpperCAmelCase					=	np.delete(snake_case__       ,						0       ,						axis=0 )

         UpperCAmelCase					=	1
         self.n_rows -= 1
         UpperCAmelCase					=	0
         UpperCAmelCase					=	False
         return tableau



  def 						UpperCamelCase_ (					self )	->	dict[Any, Any]:



         """simple docstring"""





         for _ in range(1_00 ):
                # Completion of each stage removes an objective. If both stages
                # are complete, then no objectives are left
                if not self.objectives:
                       UpperCAmelCase					=	self.generate_col_titles(
                           self.n_vars       ,						self.n_slack       ,						self.n_art_vars )

                       # Find the values of each variable at optimal solution
                       return self.interpret_tableau(self.tableau       ,						self.col_titles )

                UpperCAmelCase			,				UpperCAmelCase					=	self.find_pivot(self.tableau )

                # If there are no more negative values in objective row
                if self.stop_iter:
                       # Delete artificial variable columns and rows. Update attributes
                       UpperCAmelCase					=	self.change_stage(self.tableau )
                else:
                       UpperCAmelCase					=	self.pivot(self.tableau       ,						snake_case__       ,						snake_case__ )
         return {}



  def 						UpperCamelCase_ (					self       ,						snake_case__       ,						snake_case__ )	->	dict[str, float]:



         """simple docstring"""





         UpperCAmelCase					=	{"""P""": abs(tableau[0, -1] )}

         for i in range(self.n_vars ):
                # Gives ids of nonzero entries in the ith column
                UpperCAmelCase					=	np.nonzero(tableau[:, i] )
                UpperCAmelCase					=	len(nonzero[0] )

                # First entry in the nonzero ids
                UpperCAmelCase					=	nonzero[0][0]
                UpperCAmelCase					=	tableau[nonzero_rowidx, i]

                # If there is only one nonzero value in column, which is one
                if n_nonzero == nonzero_val == 1:
                       UpperCAmelCase					=	tableau[nonzero_rowidx, -1]
                       UpperCAmelCase					=	rhs_val

        # Check for basic variables
         for title in col_titles:
                # Don't add RHS or slack variables to output dict
                if title[0] not in "R-s-a":
                       output_dict.setdefault(snake_case__       ,						0 )
         return output_dict


if __name__ == "__main__":
 import doctest

 doctest.testmod()








"""simple docstring"""







from collections import OrderedDict
from dataclasses import fields
from typing import Any, Tuple

import numpy as np

from .import_utils import is_torch_available






def 					_lowerCAmelCase				(					lowerCAmelCase						):







       '''simple docstring'''
       if is_torch_available():
              import torch

              if isinstance(lowerCAmelCase		,					torch.Tensor						):
                     return True

       return isinstance(lowerCAmelCase		,					np.ndarray						)





class      UpperCamelCase_       (				a_     ):



  def 						UpperCamelCase_ (					self )	->	Dict:



         """simple docstring"""





         UpperCAmelCase					=	fields(self )

         # Safety and consistency checks
         if not len(snake_case__ ):
                raise ValueError(f'''{self.__class__.__name__} has no fields.''' )

         UpperCAmelCase					=	getattr(self       ,						class_fields[0].name )
         UpperCAmelCase					=	all(getattr(self       ,						field.name ) is None for field in class_fields[1:] )

         if other_fields_are_none and isinstance(snake_case__       ,						snake_case__ ):
                for key, value in first_field.items():
                       UpperCAmelCase					=	value
         else:
                for field in class_fields:
                       UpperCAmelCase					=	getattr(self       ,						field.name )
                       if v is not None:
                              UpperCAmelCase					=	v



  def __delitem__(					self       ,						*snake_case__       ,						**snake_case__ )	->	Optional[int]:



         """simple docstring"""





         raise Exception(f'''You cannot use ``__delitem__`` on a {self.__class__.__name__} instance.''' )



  def 						UpperCamelCase_ (					self       ,						*snake_case__       ,						**snake_case__ )	->	List[str]:



         """simple docstring"""





         raise Exception(f'''You cannot use ``setdefault`` on a {self.__class__.__name__} instance.''' )



  def 						UpperCamelCase_ (					self       ,						*snake_case__       ,						**snake_case__ )	->	Optional[int]:



         """simple docstring"""





         raise Exception(f'''You cannot use ``pop`` on a {self.__class__.__name__} instance.''' )



  def 						UpperCamelCase_ (					self       ,						*snake_case__       ,						**snake_case__ )	->	Optional[int]:



         """simple docstring"""





         raise Exception(f'''You cannot use ``update`` on a {self.__class__.__name__} instance.''' )



  def __getitem__(					self       ,						snake_case__ )	->	List[Any]:



         """simple docstring"""





         if isinstance(snake_case__       ,						snake_case__ ):
                UpperCAmelCase					=	dict(self.items() )
                return inner_dict[k]
         else:
                return self.to_tuple()[k]



  def __setattr__(					self       ,						snake_case__       ,						snake_case__ )	->	Union[str, Any]:



         """simple docstring"""





         if name in self.keys() and value is not None:
                # Don't call self.__setitem__ to avoid recursion errors
                super().__setitem__(snake_case__       ,						snake_case__ )
         super().__setattr__(snake_case__       ,						snake_case__ )



  def __setitem__(					self       ,						snake_case__       ,						snake_case__ )	->	Tuple:



         """simple docstring"""





         super().__setitem__(snake_case__       ,						snake_case__ )
         # Don't call self.__setattr__ to avoid recursion errors
         super().__setattr__(snake_case__       ,						snake_case__ )



  def 						UpperCamelCase_ (					self )	->	Tuple[Any]:



         """simple docstring"""





         return tuple(self[k] for k in self.keys() )
