test_lsq_common.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. from numpy.testing import assert_, assert_allclose, assert_equal
  2. from pytest import raises as assert_raises
  3. import numpy as np
  4. from scipy.optimize._lsq.common import (
  5. step_size_to_bound, find_active_constraints, make_strictly_feasible,
  6. CL_scaling_vector, intersect_trust_region, build_quadratic_1d,
  7. minimize_quadratic_1d, evaluate_quadratic, reflective_transformation,
  8. left_multiplied_operator, right_multiplied_operator)
  9. class TestBounds:
  10. def test_step_size_to_bounds(self):
  11. lb = np.array([-1.0, 2.5, 10.0])
  12. ub = np.array([1.0, 5.0, 100.0])
  13. x = np.array([0.0, 2.5, 12.0])
  14. s = np.array([0.1, 0.0, 0.0])
  15. step, hits = step_size_to_bound(x, s, lb, ub)
  16. assert_equal(step, 10)
  17. assert_equal(hits, [1, 0, 0])
  18. s = np.array([0.01, 0.05, -1.0])
  19. step, hits = step_size_to_bound(x, s, lb, ub)
  20. assert_equal(step, 2)
  21. assert_equal(hits, [0, 0, -1])
  22. s = np.array([10.0, -0.0001, 100.0])
  23. step, hits = step_size_to_bound(x, s, lb, ub)
  24. assert_equal(step, np.array(-0))
  25. assert_equal(hits, [0, -1, 0])
  26. s = np.array([1.0, 0.5, -2.0])
  27. step, hits = step_size_to_bound(x, s, lb, ub)
  28. assert_equal(step, 1.0)
  29. assert_equal(hits, [1, 0, -1])
  30. s = np.zeros(3)
  31. step, hits = step_size_to_bound(x, s, lb, ub)
  32. assert_equal(step, np.inf)
  33. assert_equal(hits, [0, 0, 0])
  34. def test_find_active_constraints(self):
  35. lb = np.array([0.0, -10.0, 1.0])
  36. ub = np.array([1.0, 0.0, 100.0])
  37. x = np.array([0.5, -5.0, 2.0])
  38. active = find_active_constraints(x, lb, ub)
  39. assert_equal(active, [0, 0, 0])
  40. x = np.array([0.0, 0.0, 10.0])
  41. active = find_active_constraints(x, lb, ub)
  42. assert_equal(active, [-1, 1, 0])
  43. active = find_active_constraints(x, lb, ub, rtol=0)
  44. assert_equal(active, [-1, 1, 0])
  45. x = np.array([1e-9, -1e-8, 100 - 1e-9])
  46. active = find_active_constraints(x, lb, ub)
  47. assert_equal(active, [0, 0, 1])
  48. active = find_active_constraints(x, lb, ub, rtol=1.5e-9)
  49. assert_equal(active, [-1, 0, 1])
  50. lb = np.array([1.0, -np.inf, -np.inf])
  51. ub = np.array([np.inf, 10.0, np.inf])
  52. x = np.ones(3)
  53. active = find_active_constraints(x, lb, ub)
  54. assert_equal(active, [-1, 0, 0])
  55. # Handles out-of-bound cases.
  56. x = np.array([0.0, 11.0, 0.0])
  57. active = find_active_constraints(x, lb, ub)
  58. assert_equal(active, [-1, 1, 0])
  59. active = find_active_constraints(x, lb, ub, rtol=0)
  60. assert_equal(active, [-1, 1, 0])
  61. def test_make_strictly_feasible(self):
  62. lb = np.array([-0.5, -0.8, 2.0])
  63. ub = np.array([0.8, 1.0, 3.0])
  64. x = np.array([-0.5, 0.0, 2 + 1e-10])
  65. x_new = make_strictly_feasible(x, lb, ub, rstep=0)
  66. assert_(x_new[0] > -0.5)
  67. assert_equal(x_new[1:], x[1:])
  68. x_new = make_strictly_feasible(x, lb, ub, rstep=1e-4)
  69. assert_equal(x_new, [-0.5 + 1e-4, 0.0, 2 * (1 + 1e-4)])
  70. x = np.array([-0.5, -1, 3.1])
  71. x_new = make_strictly_feasible(x, lb, ub)
  72. assert_(np.all((x_new >= lb) & (x_new <= ub)))
  73. x_new = make_strictly_feasible(x, lb, ub, rstep=0)
  74. assert_(np.all((x_new >= lb) & (x_new <= ub)))
  75. lb = np.array([-1, 100.0])
  76. ub = np.array([1, 100.0 + 1e-10])
  77. x = np.array([0, 100.0])
  78. x_new = make_strictly_feasible(x, lb, ub, rstep=1e-8)
  79. assert_equal(x_new, [0, 100.0 + 0.5e-10])
  80. def test_scaling_vector(self):
  81. lb = np.array([-np.inf, -5.0, 1.0, -np.inf])
  82. ub = np.array([1.0, np.inf, 10.0, np.inf])
  83. x = np.array([0.5, 2.0, 5.0, 0.0])
  84. g = np.array([1.0, 0.1, -10.0, 0.0])
  85. v, dv = CL_scaling_vector(x, g, lb, ub)
  86. assert_equal(v, [1.0, 7.0, 5.0, 1.0])
  87. assert_equal(dv, [0.0, 1.0, -1.0, 0.0])
  88. class TestQuadraticFunction:
  89. def setup_method(self):
  90. self.J = np.array([
  91. [0.1, 0.2],
  92. [-1.0, 1.0],
  93. [0.5, 0.2]])
  94. self.g = np.array([0.8, -2.0])
  95. self.diag = np.array([1.0, 2.0])
  96. def test_build_quadratic_1d(self):
  97. s = np.zeros(2)
  98. a, b = build_quadratic_1d(self.J, self.g, s)
  99. assert_equal(a, 0)
  100. assert_equal(b, 0)
  101. a, b = build_quadratic_1d(self.J, self.g, s, diag=self.diag)
  102. assert_equal(a, 0)
  103. assert_equal(b, 0)
  104. s = np.array([1.0, -1.0])
  105. a, b = build_quadratic_1d(self.J, self.g, s)
  106. assert_equal(a, 2.05)
  107. assert_equal(b, 2.8)
  108. a, b = build_quadratic_1d(self.J, self.g, s, diag=self.diag)
  109. assert_equal(a, 3.55)
  110. assert_equal(b, 2.8)
  111. s0 = np.array([0.5, 0.5])
  112. a, b, c = build_quadratic_1d(self.J, self.g, s, diag=self.diag, s0=s0)
  113. assert_equal(a, 3.55)
  114. assert_allclose(b, 2.39)
  115. assert_allclose(c, -0.1525)
  116. def test_minimize_quadratic_1d(self):
  117. a = 5
  118. b = -1
  119. t, y = minimize_quadratic_1d(a, b, 1, 2)
  120. assert_equal(t, 1)
  121. assert_allclose(y, a * t**2 + b * t, rtol=1e-15)
  122. t, y = minimize_quadratic_1d(a, b, -2, -1)
  123. assert_equal(t, -1)
  124. assert_allclose(y, a * t**2 + b * t, rtol=1e-15)
  125. t, y = minimize_quadratic_1d(a, b, -1, 1)
  126. assert_equal(t, 0.1)
  127. assert_allclose(y, a * t**2 + b * t, rtol=1e-15)
  128. c = 10
  129. t, y = minimize_quadratic_1d(a, b, -1, 1, c=c)
  130. assert_equal(t, 0.1)
  131. assert_allclose(y, a * t**2 + b * t + c, rtol=1e-15)
  132. t, y = minimize_quadratic_1d(a, b, -np.inf, np.inf, c=c)
  133. assert_equal(t, 0.1)
  134. assert_allclose(y, a * t ** 2 + b * t + c, rtol=1e-15)
  135. t, y = minimize_quadratic_1d(a, b, 0, np.inf, c=c)
  136. assert_equal(t, 0.1)
  137. assert_allclose(y, a * t ** 2 + b * t + c, rtol=1e-15)
  138. t, y = minimize_quadratic_1d(a, b, -np.inf, 0, c=c)
  139. assert_equal(t, 0)
  140. assert_allclose(y, a * t ** 2 + b * t + c, rtol=1e-15)
  141. a = -1
  142. b = 0.2
  143. t, y = minimize_quadratic_1d(a, b, -np.inf, np.inf)
  144. assert_equal(y, -np.inf)
  145. t, y = minimize_quadratic_1d(a, b, 0, np.inf)
  146. assert_equal(t, np.inf)
  147. assert_equal(y, -np.inf)
  148. t, y = minimize_quadratic_1d(a, b, -np.inf, 0)
  149. assert_equal(t, -np.inf)
  150. assert_equal(y, -np.inf)
  151. def test_evaluate_quadratic(self):
  152. s = np.array([1.0, -1.0])
  153. value = evaluate_quadratic(self.J, self.g, s)
  154. assert_equal(value, 4.85)
  155. value = evaluate_quadratic(self.J, self.g, s, diag=self.diag)
  156. assert_equal(value, 6.35)
  157. s = np.array([[1.0, -1.0],
  158. [1.0, 1.0],
  159. [0.0, 0.0]])
  160. values = evaluate_quadratic(self.J, self.g, s)
  161. assert_allclose(values, [4.85, -0.91, 0.0])
  162. values = evaluate_quadratic(self.J, self.g, s, diag=self.diag)
  163. assert_allclose(values, [6.35, 0.59, 0.0])
  164. class TestTrustRegion:
  165. def test_intersect(self):
  166. Delta = 1.0
  167. x = np.zeros(3)
  168. s = np.array([1.0, 0.0, 0.0])
  169. t_neg, t_pos = intersect_trust_region(x, s, Delta)
  170. assert_equal(t_neg, -1)
  171. assert_equal(t_pos, 1)
  172. s = np.array([-1.0, 1.0, -1.0])
  173. t_neg, t_pos = intersect_trust_region(x, s, Delta)
  174. assert_allclose(t_neg, -3**-0.5)
  175. assert_allclose(t_pos, 3**-0.5)
  176. x = np.array([0.5, -0.5, 0])
  177. s = np.array([0, 0, 1.0])
  178. t_neg, t_pos = intersect_trust_region(x, s, Delta)
  179. assert_allclose(t_neg, -2**-0.5)
  180. assert_allclose(t_pos, 2**-0.5)
  181. x = np.ones(3)
  182. assert_raises(ValueError, intersect_trust_region, x, s, Delta)
  183. x = np.zeros(3)
  184. s = np.zeros(3)
  185. assert_raises(ValueError, intersect_trust_region, x, s, Delta)
  186. def test_reflective_transformation():
  187. lb = np.array([-1, -2], dtype=float)
  188. ub = np.array([5, 3], dtype=float)
  189. y = np.array([0, 0])
  190. x, g = reflective_transformation(y, lb, ub)
  191. assert_equal(x, y)
  192. assert_equal(g, np.ones(2))
  193. y = np.array([-4, 4], dtype=float)
  194. x, g = reflective_transformation(y, lb, np.array([np.inf, np.inf]))
  195. assert_equal(x, [2, 4])
  196. assert_equal(g, [-1, 1])
  197. x, g = reflective_transformation(y, np.array([-np.inf, -np.inf]), ub)
  198. assert_equal(x, [-4, 2])
  199. assert_equal(g, [1, -1])
  200. x, g = reflective_transformation(y, lb, ub)
  201. assert_equal(x, [2, 2])
  202. assert_equal(g, [-1, -1])
  203. lb = np.array([-np.inf, -2])
  204. ub = np.array([5, np.inf])
  205. y = np.array([10, 10], dtype=float)
  206. x, g = reflective_transformation(y, lb, ub)
  207. assert_equal(x, [0, 10])
  208. assert_equal(g, [-1, 1])
  209. def test_linear_operators():
  210. A = np.arange(6).reshape((3, 2))
  211. d_left = np.array([-1, 2, 5])
  212. DA = np.diag(d_left).dot(A)
  213. J_left = left_multiplied_operator(A, d_left)
  214. d_right = np.array([5, 10])
  215. AD = A.dot(np.diag(d_right))
  216. J_right = right_multiplied_operator(A, d_right)
  217. x = np.array([-2, 3])
  218. X = -2 * np.arange(2, 8).reshape((2, 3))
  219. xt = np.array([0, -2, 15])
  220. assert_allclose(DA.dot(x), J_left.matvec(x))
  221. assert_allclose(DA.dot(X), J_left.matmat(X))
  222. assert_allclose(DA.T.dot(xt), J_left.rmatvec(xt))
  223. assert_allclose(AD.dot(x), J_right.matvec(x))
  224. assert_allclose(AD.dot(X), J_right.matmat(X))
  225. assert_allclose(AD.T.dot(xt), J_right.rmatvec(xt))