Coverage for picos/expressions/cone_nno.py: 97.83%

46 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-03-26 07:46 +0000

1# ------------------------------------------------------------------------------ 

2# Copyright (C) 2020 Maximilian Stahlberg 

3# 

4# This file is part of PICOS. 

5# 

6# PICOS is free software: you can redistribute it and/or modify it under the 

7# terms of the GNU General Public License as published by the Free Software 

8# Foundation, either version 3 of the License, or (at your option) any later 

9# version. 

10# 

11# PICOS is distributed in the hope that it will be useful, but WITHOUT ANY 

12# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 

13# A PARTICULAR PURPOSE. See the GNU General Public License for more details. 

14# 

15# You should have received a copy of the GNU General Public License along with 

16# this program. If not, see <http://www.gnu.org/licenses/>. 

17# ------------------------------------------------------------------------------ 

18 

19"""Implements the nonnegative orthant cone.""" 

20 

21import operator 

22from collections import namedtuple 

23 

24from .. import glyphs 

25from ..apidoc import api_end, api_start 

26from ..constraints import AffineConstraint 

27from ..constraints.uncertain import ConicallyUncertainAffineConstraint 

28from .cone import Cone 

29from .exp_affine import AffineExpression 

30from .uncertain.pert_conic import ConicPerturbationSet 

31from .uncertain.uexp_affine import UncertainAffineExpression 

32 

33_API_START = api_start(globals()) 

34# ------------------------------- 

35 

36 

37class NonnegativeOrthant(Cone): 

38 """The nonnegative orthant.""" 

39 

40 def __init__(self, dim=None): 

41 """Construct a :class:`NonnegativeOrthant`.""" 

42 Cone.__init__(self, dim, "Nonnegative Orthant", 

43 glyphs.set(glyphs.ge("x", glyphs.scalar(0)))) 

44 

45 def _get_mutables(self): 

46 return frozenset() 

47 

48 def _replace_mutables(self): 

49 return self 

50 

51 Subtype = namedtuple("Subtype", ("dim",)) 

52 

53 def _get_subtype(self): 

54 return self.Subtype(self.dim) 

55 

56 @classmethod 

57 def _predict(cls, subtype, relation, other): 

58 assert isinstance(subtype, cls.Subtype) 

59 

60 if relation == operator.__rshift__: 

61 if issubclass(other.clstype, AffineExpression) \ 

62 and not subtype.dim or subtype.dim == other.subtype.dim: 

63 return AffineConstraint.make_type( 

64 dim=other.subtype.dim, eq=False) 

65 elif issubclass(other.clstype, UncertainAffineExpression) \ 

66 and not subtype.dim or subtype.dim == other.subtype.dim: 

67 universe = other.subtype.universe_type 

68 

69 if issubclass(universe.clstype, ConicPerturbationSet): 

70 return ConicallyUncertainAffineConstraint.make_type( 

71 dim=other.subtype.dim, 

72 universe_subtype=universe.subtype) 

73 

74 return Cone._predict_base(cls, subtype, relation, other) 

75 

76 def _rshift_implementation(self, element): 

77 if isinstance(element, AffineExpression): 

78 self._check_dimension(element) 

79 

80 return element >= 0 

81 elif isinstance(element, UncertainAffineExpression): 

82 self._check_dimension(element) 

83 

84 if isinstance(element.universe, ConicPerturbationSet): 

85 return ConicallyUncertainAffineConstraint(-element) 

86 

87 # Handle scenario uncertainty for all cones. 

88 return Cone._rshift_base(self, element) 

89 

90 @property 

91 def dual_cone(self): 

92 """Implement :attr:`.cone.Cone.dual_cone`.""" 

93 return self 

94 

95 

96# -------------------------------------- 

97__all__ = api_end(_API_START, globals())