Coverage for picos/constraints/con_oprelentr.py: 92.31%

91 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-04-12 07:53 +0000

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

2# Copyright (C) 2024 Kerry He 

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"""Operator relative entropy constraints.""" 

20 

21from collections import namedtuple 

22 

23from .. import glyphs 

24from ..apidoc import api_end, api_start 

25from ..caching import cached_property 

26from .constraint import Constraint 

27 

28_API_START = api_start(globals()) 

29# ------------------------------- 

30 

31 

32class OpRelEntropyConstraint(Constraint): 

33 """Epigraph of an operator relative entropy. 

34 

35 This is the upper bound, in the Loewner order, of an operator relative 

36 entropy, represented by :class:`~picos.expressions.OperatorRelativeEntropy`. 

37 """ 

38 

39 def __init__(self, divergence, upperBound): 

40 """Construct a :class:`OpRelEntropyConstraint`. 

41 

42 :param ~picos.expressions.OperatorRelativeEntropy divergence: 

43 Constrained expression. 

44 :param ~picos.expressions.AffineExpression upperBound: 

45 Upper bound on the expression. 

46 """ 

47 from ..expressions import OperatorRelativeEntropy 

48 

49 required_type = self._required_type() 

50 

51 assert isinstance(divergence, OperatorRelativeEntropy) 

52 assert isinstance(upperBound, required_type) 

53 assert divergence.shape == upperBound.shape 

54 

55 self.divergence = divergence 

56 self.upperBound = upperBound 

57 

58 assert isinstance(divergence.X, required_type) 

59 assert isinstance(divergence.Y, required_type) 

60 

61 super(OpRelEntropyConstraint, self).__init__(divergence._typeStr) 

62 

63 def _required_type(self): 

64 from ..expressions import AffineExpression 

65 

66 return AffineExpression 

67 

68 @property 

69 def X(self): 

70 """The :math:`X` of the divergence.""" 

71 return self.divergence.X 

72 

73 @cached_property 

74 def Y(self): 

75 """The :math:`Y` of the divergence.""" 

76 return self.divergence.Y 

77 

78 Subtype = namedtuple("Subtype", ("argdim",)) 

79 

80 def _subtype(self): 

81 return self.Subtype(self.X.shape[0] ** 2) 

82 

83 @classmethod 

84 def _cost(cls, subtype): 

85 n = subtype.argdim 

86 return n * (n + 1) // 2 * 3 

87 

88 def _expression_names(self): 

89 yield "divergence" 

90 yield "upperBound" 

91 

92 def _str(self): 

93 return glyphs.psdle(self.divergence.string, self.upperBound.string) 

94 

95 def _get_size(self): 

96 n = self.X.shape[0] 

97 return (3 * n * n, 1) 

98 

99 def _get_slack(self): 

100 return self.upperBound.safe_value - self.divergence.safe_value 

101 

102 

103class ComplexOpRelEntropyConstraint(OpRelEntropyConstraint): 

104 """Epigraph of a complex operator relative entropy.""" 

105 

106 # TODO: Implement real conversion of operator relative entropy epigraph 

107 

108 def _required_type(self): 

109 from ..expressions import ComplexAffineExpression 

110 

111 return ComplexAffineExpression 

112 

113 

114class TrOpRelEntropyConstraint(Constraint): 

115 """Upper bound of trace of operator relative entropy. 

116 

117 This is the upper bound on the trace of an operator relative entropy, 

118 represented by :class:`~picos.expressions.TrOperatorRelativeEntropy`. 

119 """ 

120 

121 def __init__(self, divergence, upperBound): 

122 """Construct a :class:`TrOpRelEntropyConstraint`. 

123 

124 :param ~picos.expressions.TrOperatorRelativeEntropy divergence: 

125 Constrained expression. 

126 :param ~picos.expressions.AffineExpression upperBound: 

127 Upper bound on the expression. 

128 """ 

129 from ..expressions import AffineExpression, TrOperatorRelativeEntropy 

130 

131 assert isinstance(divergence, TrOperatorRelativeEntropy) 

132 assert isinstance(upperBound, AffineExpression) 

133 assert len(upperBound) == 1 

134 

135 self.divergence = divergence 

136 self.upperBound = upperBound 

137 

138 required_type = self._required_type() 

139 

140 assert isinstance(divergence.X, required_type) 

141 assert isinstance(divergence.Y, required_type) 

142 

143 super(TrOpRelEntropyConstraint, self).__init__(divergence._typeStr) 

144 

145 def _required_type(self): 

146 from ..expressions import AffineExpression 

147 

148 return AffineExpression 

149 

150 @property 

151 def X(self): 

152 """The :math:`X` of the divergence.""" 

153 return self.divergence.X 

154 

155 @cached_property 

156 def Y(self): 

157 """The :math:`Y` of the divergence.""" 

158 return self.divergence.Y 

159 

160 Subtype = namedtuple("Subtype", ("argdim",)) 

161 

162 def _subtype(self): 

163 return self.Subtype(self.X.shape[0] ** 2) 

164 

165 @classmethod 

166 def _cost(cls, subtype): 

167 n = subtype.argdim 

168 return n * (n + 1) + 1 

169 

170 def _expression_names(self): 

171 yield "divergence" 

172 yield "upperBound" 

173 

174 def _str(self): 

175 return glyphs.le(self.divergence.string, self.upperBound.string) 

176 

177 def _get_size(self): 

178 n = self.X.shape[0] 

179 return (2 * n * n + 1, 1) 

180 

181 def _get_slack(self): 

182 return self.upperBound.safe_value - self.divergence.safe_value 

183 

184 

185class ComplexTrOpRelEntropyConstraint(TrOpRelEntropyConstraint): 

186 """Upper bound of trace of complex operator relative entropy.""" 

187 

188 # TODO: Implement real conversion of operator relative entropy cone 

189 

190 def _required_type(self): 

191 from ..expressions import ComplexAffineExpression 

192 

193 return ComplexAffineExpression 

194 

195 

196# -------------------------------------- 

197__all__ = api_end(_API_START, globals())