Coverage for python/lsst/pex/config/comparison.py: 9%

39 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-02-02 14:19 +0000

1# This file is part of pex_config. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This software is dual licensed under the GNU General Public License and also 

10# under a 3-clause BSD license. Recipients may choose which of these licenses 

11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt, 

12# respectively. If you choose the GPL option then the following text applies 

13# (but note that there is still no warranty even if you opt for BSD instead): 

14# 

15# This program is free software: you can redistribute it and/or modify 

16# it under the terms of the GNU General Public License as published by 

17# the Free Software Foundation, either version 3 of the License, or 

18# (at your option) any later version. 

19# 

20# This program is distributed in the hope that it will be useful, 

21# but WITHOUT ANY WARRANTY; without even the implied warranty of 

22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

23# GNU General Public License for more details. 

24# 

25# You should have received a copy of the GNU General Public License 

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

27 

28"""Helper functions for comparing `lsst.pex.config.Config` instancess. 

29 

30Theses function should be use for any comparison in a `lsst.pex.Config.compare` 

31or `lsst.pex.config.Field._compare` implementation, as they take care of 

32writing messages as well as floating-point comparisons and shortcuts. 

33""" 

34 

35__all__ = ("getComparisonName", "compareScalars", "compareConfigs") 

36 

37import numpy 

38 

39 

40def getComparisonName(name1, name2): 

41 """Create a comparison name that is used for printed output of comparisons. 

42 

43 Parameters 

44 ---------- 

45 name1 : `str` 

46 Name of the first configuration. 

47 name2 : `str` 

48 Name of the second configuration. 

49 

50 Returns 

51 ------- 

52 name : `str` 

53 When ``name1`` and ``name2`` are equal, the returned name is 

54 simply one of the names. When they are different the returned name is 

55 formatted as ``"{name1} / {name2}"``. 

56 """ 

57 if name1 != name2: 

58 return "%s / %s" % (name1, name2) 

59 return name1 

60 

61 

62def compareScalars(name, v1, v2, output, rtol=1e-8, atol=1e-8, dtype=None): 

63 """Compare two scalar values for equality. 

64 

65 This function is a helper for `lsst.pex.config.Config.compare`. 

66 

67 Parameters 

68 ---------- 

69 name : `str` 

70 Name to use when reporting differences, typically created by 

71 `getComparisonName`. 

72 v1 : object 

73 Left-hand side value to compare. 

74 v2 : object 

75 Right-hand side value to compare. 

76 output : callable or `None` 

77 A callable that takes a string, used (possibly repeatedly) to report 

78 inequalities (for example, `print`). Set to `None` to disable output. 

79 rtol : `float`, optional 

80 Relative tolerance for floating point comparisons. 

81 atol : `float`, optional 

82 Absolute tolerance for floating point comparisons. 

83 dtype : class, optional 

84 Data type of values for comparison. May be `None` if values are not 

85 floating-point. 

86 

87 Returns 

88 ------- 

89 areEqual : `bool` 

90 `True` if the values are equal, `False` if they are not. 

91 

92 See also 

93 -------- 

94 lsst.pex.config.compareConfigs 

95 

96 Notes 

97 ----- 

98 Floating point comparisons are performed by `numpy.allclose`. 

99 """ 

100 if v1 is None or v2 is None: 

101 result = v1 == v2 

102 elif dtype in (float, complex): 

103 result = numpy.allclose(v1, v2, rtol=rtol, atol=atol) or (numpy.isnan(v1) and numpy.isnan(v2)) 

104 else: 

105 result = v1 == v2 

106 if not result and output is not None: 

107 output("Inequality in %s: %r != %r" % (name, v1, v2)) 

108 return result 

109 

110 

111def compareConfigs(name, c1, c2, shortcut=True, rtol=1e-8, atol=1e-8, output=None): 

112 """Compare two `lsst.pex.config.Config` instances for equality. 

113 

114 This function is a helper for `lsst.pex.config.Config.compare`. 

115 

116 Parameters 

117 ---------- 

118 name : `str` 

119 Name to use when reporting differences, typically created by 

120 `getComparisonName`. 

121 v1 : `lsst.pex.config.Config` 

122 Left-hand side config to compare. 

123 v2 : `lsst.pex.config.Config` 

124 Right-hand side config to compare. 

125 shortcut : `bool`, optional 

126 If `True`, return as soon as an inequality is found. Default is `True`. 

127 rtol : `float`, optional 

128 Relative tolerance for floating point comparisons. 

129 atol : `float`, optional 

130 Absolute tolerance for floating point comparisons. 

131 output : callable, optional 

132 A callable that takes a string, used (possibly repeatedly) to report 

133 inequalities. For example: `print`. 

134 

135 Returns 

136 ------- 

137 areEqual : `bool` 

138 `True` when the two `lsst.pex.config.Config` instances are equal. 

139 `False` if there is an inequality. 

140 

141 See also 

142 -------- 

143 lsst.pex.config.compareScalars 

144 

145 Notes 

146 ----- 

147 Floating point comparisons are performed by `numpy.allclose`. 

148 

149 If ``c1`` or ``c2`` contain `~lsst.pex.config.RegistryField` or 

150 `~lsst.pex.config.ConfigChoiceField` instances, *unselected* 

151 `~lsst.pex.config.Config` instances will not be compared. 

152 """ 

153 assert name is not None 

154 if c1 is None: 

155 if c2 is None: 

156 return True 

157 else: 

158 if output is not None: 

159 output("LHS is None for %s" % name) 

160 return False 

161 else: 

162 if c2 is None: 

163 if output is not None: 

164 output("RHS is None for %s" % name) 

165 return False 

166 if type(c1) != type(c2): 

167 if output is not None: 

168 output("Config types do not match for %s: %s != %s" % (name, type(c1), type(c2))) 

169 return False 

170 equal = True 

171 for field in c1._fields.values(): 

172 result = field._compare(c1, c2, shortcut=shortcut, rtol=rtol, atol=atol, output=output) 

173 if not result and shortcut: 

174 return False 

175 equal = equal and result 

176 return equal