linalg.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. from math import sqrt
  2. from array import array as _array
  3. constants = (int, float)
  4. def coerce_(types):
  5. if types is None:
  6. types = []
  7. if str in types:
  8. raise TypeError
  9. if complex in types:
  10. raise TypeError
  11. dtype = ("i", int)
  12. if float in types:
  13. dtype = ("d", float)
  14. return dtype
  15. def _mkslice(i, n):
  16. if not isinstance(i, slice):
  17. i = slice(i, i + 1, 1)
  18. start, stop, stride = i.indices(n)
  19. return slice(start, stop, stride)
  20. def make_ij_slices(f):
  21. def wrapper(self, ij, *args):
  22. # I = slice(0,self.n,1)
  23. J = slice(0, self.p, 1)
  24. if isinstance(ij, tuple):
  25. I = _mkslice(ij[0], self.n)
  26. J = _mkslice(ij[1], self.p)
  27. else:
  28. I = _mkslice(ij, self.n)
  29. return f(self, (I, J), *args)
  30. return wrapper
  31. # minimalistic numpy.array replacement class used as fallback
  32. # when numpy is not found in geometry module
  33. class array(object):
  34. def __init__(self, data, dtype=None, copy=True):
  35. self.dim = len(data)
  36. types = None
  37. if self.dim > 0:
  38. types = set([type(x) for x in data])
  39. if dtype is not None:
  40. types = (dtype,)
  41. tc, self.dtype = coerce_(types)
  42. data = [self.dtype(x) for x in data]
  43. if copy is True:
  44. self.data = _array(tc, data)
  45. else:
  46. raise NotImplementedError
  47. def coerce(self, dtype):
  48. data = [dtype(x) for x in self.data]
  49. tc, dtype = coerce_((dtype,))
  50. self.data = _array(tc, data)
  51. self.dtype = dtype
  52. @property
  53. def typecode(self):
  54. return self.data.typecode
  55. def __len__(self):
  56. return self.dim
  57. def __str__(self):
  58. s = " ".join(("%.12s" % x).ljust(12) for x in self)
  59. return "[%s]" % s.strip()
  60. def copy(self):
  61. return array(self.data, self.dtype)
  62. def __add__(self, v):
  63. if isinstance(v, constants):
  64. v = array([v] * self.dim)
  65. assert v.dim == self.dim
  66. return array([x + y for (x, y) in zip(self.data, v.data)])
  67. def __sub__(self, v):
  68. if isinstance(v, constants):
  69. v = array([v] * self.dim)
  70. assert v.dim == self.dim
  71. return array([x - y for (x, y) in zip(self.data, v.data)])
  72. def __neg__(self):
  73. return array([-x for x in self.data], dtype=self.dtype)
  74. def __radd__(self, v):
  75. return self + v
  76. def __rsub__(self, v):
  77. return (-self) + v
  78. def dot(self, v):
  79. assert v.dim == self.dim
  80. return sum([x * y for (x, y) in zip(self.data, v.data)])
  81. def __rmul__(self, k):
  82. return array([k * x for x in self.data])
  83. def __mul__(self, v):
  84. if isinstance(v, constants):
  85. v = array([v] * self.dim)
  86. assert v.dim == self.dim
  87. return array([x * y for (x, y) in zip(self.data, v.data)])
  88. def __truediv__(self, v):
  89. if isinstance(v, constants):
  90. v = array([v] * self.dim)
  91. assert v.dim == self.dim
  92. return array([x / y for (x, y) in zip(self.data, v.data)])
  93. __div__ = __truediv__
  94. def __rtruediv__(self, v):
  95. if isinstance(v, constants):
  96. v = array([v] * self.dim)
  97. assert v.dim == self.dim
  98. return array([x / y for (x, y) in zip(v.data, self.data)])
  99. __rdiv__ = __rtruediv__
  100. def __floordiv__(self, v):
  101. if isinstance(v, constants):
  102. v = array([v] * self.dim)
  103. assert v.dim == self.dim
  104. return array([x // y for (x, y) in zip(self.data, v.data)])
  105. def __rfloordiv__(self, v):
  106. if isinstance(v, constants):
  107. v = array([v] * self.dim)
  108. assert v.dim == self.dim
  109. return array([x // y for (x, y) in zip(v.data, self.data)])
  110. def norm(self):
  111. return sqrt(self.dot(self))
  112. def max(self):
  113. return max(self.data)
  114. def min(self):
  115. return min(self.data)
  116. def __iter__(self):
  117. for x in self.data:
  118. yield x
  119. def __setitem__(self, i, v):
  120. assert isinstance(i, int)
  121. self.data[i] = self.dtype(v)
  122. def __getitem__(self, i):
  123. i = _mkslice(i, self.dim)
  124. res = self.data[i]
  125. if len(res) == 1:
  126. return res[0]
  127. return array(res)
  128. def transpose(self):
  129. return matrix(self.data, self.dtype)
  130. def __float__(self):
  131. assert self.dim == 1
  132. return float(self.data[0])
  133. # ------------------------------------------------------------------------------
  134. # minimalistic numpy.matrix replacement class used as fallback
  135. # when numpy is not found in geometry module
  136. class matrix(object):
  137. def __init__(self, data, dtype=None, copy=True, transpose=False):
  138. # check input data types:
  139. types = set([type(v) for v in data])
  140. if len(types) > 1:
  141. raise TypeError
  142. t = types.pop()
  143. # import data:
  144. if t in constants:
  145. self.data = [array(data, dtype, copy)]
  146. else:
  147. if transpose:
  148. data = zip(*data)
  149. self.data = [array(v, dtype, copy) for v in data]
  150. # define matrix sizes:
  151. self.n = len(self.data)
  152. sizes = set([len(v) for v in self.data])
  153. if len(sizes) > 1:
  154. raise ValueError
  155. self.p = sizes.pop()
  156. if dtype is None:
  157. # coerce types of arrays of matrix:
  158. types = set([v.dtype for v in self.data])
  159. tc, dtype = coerce_(types)
  160. for v in self.data:
  161. v.coerce(dtype)
  162. self.dtype = dtype
  163. def __len__(self):
  164. return self.n * self.p
  165. def __str__(self):
  166. s = "\n ".join([str(v) for v in self.data])
  167. return "[%s]" % s.strip()
  168. @property
  169. def shape(self):
  170. return (self.n, self.p)
  171. def lvecs(self):
  172. return self.data
  173. def cvecs(self):
  174. return [array(v, self.dtype) for v in zip(*self.data)]
  175. def copy(self):
  176. return matrix(self.data, self.dtype)
  177. def transpose(self):
  178. return matrix(self.data, dtype=self.dtype, transpose=True)
  179. def sum(self):
  180. return sum([sum(v) for v in self.data])
  181. @make_ij_slices
  182. def __getitem__(self, ij):
  183. I, J = ij
  184. l = self.lvecs()[I]
  185. m = matrix([v[J] for v in l])
  186. if m.n == 1:
  187. v = m.data[0]
  188. if v.dim == 1:
  189. return v[0]
  190. if len(l) > 1:
  191. return v
  192. return m
  193. @make_ij_slices
  194. def __setitem__(self, ij, v):
  195. I, J = ij
  196. Ri = range(I.start, I.stop, I.step)
  197. Rj = range(J.start, J.stop, J.step)
  198. if type(v) in constants:
  199. v = (v,)
  200. value = (x for x in v)
  201. for i in Ri:
  202. for j in Rj:
  203. self.data[i][j] = next(value)
  204. def __add__(self, m):
  205. if isinstance(m, constants):
  206. return matrix([u + m for u in self.data])
  207. else:
  208. assert self.shape == m.shape
  209. return matrix([u + v for (u, v) in zip(self.data, m.data)])
  210. def __sub__(self, m):
  211. if isinstance(m, constants):
  212. return matrix([u - m for u in self.data])
  213. else:
  214. assert self.shape == m.shape
  215. return matrix([u - v for (u, v) in zip(self.data, m.data)])
  216. def __neg__(self):
  217. return matrix([-x for x in self.data], dtype=self.dtype)
  218. def __float__(self):
  219. assert self.n == 1 and self.p == 1
  220. return self[0, 0]
  221. def __radd__(self, v):
  222. return self + v
  223. def __rsub__(self, v):
  224. return (-self) + v
  225. def __rmul__(self, k):
  226. if not isinstance(k, constants):
  227. raise TypeError
  228. return matrix([k * v for v in self.data])
  229. def __mul__(self, X):
  230. if isinstance(X, constants):
  231. return X * self
  232. if isinstance(X, array):
  233. assert X.dim == self.p
  234. return array([v.dot(X) for v in self.data])
  235. if isinstance(X, matrix):
  236. assert X.n == self.p
  237. return matrix([self * v for v in X.cvecs()])
  238. def __pow__(self, v):
  239. S = [self] * v
  240. assert len(S) > 0
  241. return reduce(lambda x, y: x * y, S)
  242. def __iter__(self):
  243. for l in self.data:
  244. for v in l:
  245. yield v
  246. class SimplexMin(object):
  247. def __init__(self, A, b, c):
  248. self.A = A
  249. self.b = b
  250. self.c = c
  251. self.tableau()
  252. def tableau(self):
  253. self.T = []
  254. def setup(self):
  255. self.enter = []
  256. delf.outer = []