graycode.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. from sympy.core import Basic, Integer
  2. import random
  3. class GrayCode(Basic):
  4. """
  5. A Gray code is essentially a Hamiltonian walk on
  6. a n-dimensional cube with edge length of one.
  7. The vertices of the cube are represented by vectors
  8. whose values are binary. The Hamilton walk visits
  9. each vertex exactly once. The Gray code for a 3d
  10. cube is ['000','100','110','010','011','111','101',
  11. '001'].
  12. A Gray code solves the problem of sequentially
  13. generating all possible subsets of n objects in such
  14. a way that each subset is obtained from the previous
  15. one by either deleting or adding a single object.
  16. In the above example, 1 indicates that the object is
  17. present, and 0 indicates that its absent.
  18. Gray codes have applications in statistics as well when
  19. we want to compute various statistics related to subsets
  20. in an efficient manner.
  21. Examples
  22. ========
  23. >>> from sympy.combinatorics import GrayCode
  24. >>> a = GrayCode(3)
  25. >>> list(a.generate_gray())
  26. ['000', '001', '011', '010', '110', '111', '101', '100']
  27. >>> a = GrayCode(4)
  28. >>> list(a.generate_gray())
  29. ['0000', '0001', '0011', '0010', '0110', '0111', '0101', '0100', \
  30. '1100', '1101', '1111', '1110', '1010', '1011', '1001', '1000']
  31. References
  32. ==========
  33. .. [1] Nijenhuis,A. and Wilf,H.S.(1978).
  34. Combinatorial Algorithms. Academic Press.
  35. .. [2] Knuth, D. (2011). The Art of Computer Programming, Vol 4
  36. Addison Wesley
  37. """
  38. _skip = False
  39. _current = 0
  40. _rank = None
  41. def __new__(cls, n, *args, **kw_args):
  42. """
  43. Default constructor.
  44. It takes a single argument ``n`` which gives the dimension of the Gray
  45. code. The starting Gray code string (``start``) or the starting ``rank``
  46. may also be given; the default is to start at rank = 0 ('0...0').
  47. Examples
  48. ========
  49. >>> from sympy.combinatorics import GrayCode
  50. >>> a = GrayCode(3)
  51. >>> a
  52. GrayCode(3)
  53. >>> a.n
  54. 3
  55. >>> a = GrayCode(3, start='100')
  56. >>> a.current
  57. '100'
  58. >>> a = GrayCode(4, rank=4)
  59. >>> a.current
  60. '0110'
  61. >>> a.rank
  62. 4
  63. """
  64. if n < 1 or int(n) != n:
  65. raise ValueError(
  66. 'Gray code dimension must be a positive integer, not %i' % n)
  67. n = Integer(n)
  68. args = (n,) + args
  69. obj = Basic.__new__(cls, *args)
  70. if 'start' in kw_args:
  71. obj._current = kw_args["start"]
  72. if len(obj._current) > n:
  73. raise ValueError('Gray code start has length %i but '
  74. 'should not be greater than %i' % (len(obj._current), n))
  75. elif 'rank' in kw_args:
  76. if int(kw_args["rank"]) != kw_args["rank"]:
  77. raise ValueError('Gray code rank must be a positive integer, '
  78. 'not %i' % kw_args["rank"])
  79. obj._rank = int(kw_args["rank"]) % obj.selections
  80. obj._current = obj.unrank(n, obj._rank)
  81. return obj
  82. def next(self, delta=1):
  83. """
  84. Returns the Gray code a distance ``delta`` (default = 1) from the
  85. current value in canonical order.
  86. Examples
  87. ========
  88. >>> from sympy.combinatorics import GrayCode
  89. >>> a = GrayCode(3, start='110')
  90. >>> a.next().current
  91. '111'
  92. >>> a.next(-1).current
  93. '010'
  94. """
  95. return GrayCode(self.n, rank=(self.rank + delta) % self.selections)
  96. @property
  97. def selections(self):
  98. """
  99. Returns the number of bit vectors in the Gray code.
  100. Examples
  101. ========
  102. >>> from sympy.combinatorics import GrayCode
  103. >>> a = GrayCode(3)
  104. >>> a.selections
  105. 8
  106. """
  107. return 2**self.n
  108. @property
  109. def n(self):
  110. """
  111. Returns the dimension of the Gray code.
  112. Examples
  113. ========
  114. >>> from sympy.combinatorics import GrayCode
  115. >>> a = GrayCode(5)
  116. >>> a.n
  117. 5
  118. """
  119. return self.args[0]
  120. def generate_gray(self, **hints):
  121. """
  122. Generates the sequence of bit vectors of a Gray Code.
  123. Examples
  124. ========
  125. >>> from sympy.combinatorics import GrayCode
  126. >>> a = GrayCode(3)
  127. >>> list(a.generate_gray())
  128. ['000', '001', '011', '010', '110', '111', '101', '100']
  129. >>> list(a.generate_gray(start='011'))
  130. ['011', '010', '110', '111', '101', '100']
  131. >>> list(a.generate_gray(rank=4))
  132. ['110', '111', '101', '100']
  133. See Also
  134. ========
  135. skip
  136. References
  137. ==========
  138. .. [1] Knuth, D. (2011). The Art of Computer Programming,
  139. Vol 4, Addison Wesley
  140. """
  141. bits = self.n
  142. start = None
  143. if "start" in hints:
  144. start = hints["start"]
  145. elif "rank" in hints:
  146. start = GrayCode.unrank(self.n, hints["rank"])
  147. if start is not None:
  148. self._current = start
  149. current = self.current
  150. graycode_bin = gray_to_bin(current)
  151. if len(graycode_bin) > self.n:
  152. raise ValueError('Gray code start has length %i but should '
  153. 'not be greater than %i' % (len(graycode_bin), bits))
  154. self._current = int(current, 2)
  155. graycode_int = int(''.join(graycode_bin), 2)
  156. for i in range(graycode_int, 1 << bits):
  157. if self._skip:
  158. self._skip = False
  159. else:
  160. yield self.current
  161. bbtc = (i ^ (i + 1))
  162. gbtc = (bbtc ^ (bbtc >> 1))
  163. self._current = (self._current ^ gbtc)
  164. self._current = 0
  165. def skip(self):
  166. """
  167. Skips the bit generation.
  168. Examples
  169. ========
  170. >>> from sympy.combinatorics import GrayCode
  171. >>> a = GrayCode(3)
  172. >>> for i in a.generate_gray():
  173. ... if i == '010':
  174. ... a.skip()
  175. ... print(i)
  176. ...
  177. 000
  178. 001
  179. 011
  180. 010
  181. 111
  182. 101
  183. 100
  184. See Also
  185. ========
  186. generate_gray
  187. """
  188. self._skip = True
  189. @property
  190. def rank(self):
  191. """
  192. Ranks the Gray code.
  193. A ranking algorithm determines the position (or rank)
  194. of a combinatorial object among all the objects w.r.t.
  195. a given order. For example, the 4 bit binary reflected
  196. Gray code (BRGC) '0101' has a rank of 6 as it appears in
  197. the 6th position in the canonical ordering of the family
  198. of 4 bit Gray codes.
  199. Examples
  200. ========
  201. >>> from sympy.combinatorics import GrayCode
  202. >>> a = GrayCode(3)
  203. >>> list(a.generate_gray())
  204. ['000', '001', '011', '010', '110', '111', '101', '100']
  205. >>> GrayCode(3, start='100').rank
  206. 7
  207. >>> GrayCode(3, rank=7).current
  208. '100'
  209. See Also
  210. ========
  211. unrank
  212. References
  213. ==========
  214. .. [1] http://statweb.stanford.edu/~susan/courses/s208/node12.html
  215. """
  216. if self._rank is None:
  217. self._rank = int(gray_to_bin(self.current), 2)
  218. return self._rank
  219. @property
  220. def current(self):
  221. """
  222. Returns the currently referenced Gray code as a bit string.
  223. Examples
  224. ========
  225. >>> from sympy.combinatorics import GrayCode
  226. >>> GrayCode(3, start='100').current
  227. '100'
  228. """
  229. rv = self._current or '0'
  230. if not isinstance(rv, str):
  231. rv = bin(rv)[2:]
  232. return rv.rjust(self.n, '0')
  233. @classmethod
  234. def unrank(self, n, rank):
  235. """
  236. Unranks an n-bit sized Gray code of rank k. This method exists
  237. so that a derivative GrayCode class can define its own code of
  238. a given rank.
  239. The string here is generated in reverse order to allow for tail-call
  240. optimization.
  241. Examples
  242. ========
  243. >>> from sympy.combinatorics import GrayCode
  244. >>> GrayCode(5, rank=3).current
  245. '00010'
  246. >>> GrayCode.unrank(5, 3)
  247. '00010'
  248. See Also
  249. ========
  250. rank
  251. """
  252. def _unrank(k, n):
  253. if n == 1:
  254. return str(k % 2)
  255. m = 2**(n - 1)
  256. if k < m:
  257. return '0' + _unrank(k, n - 1)
  258. return '1' + _unrank(m - (k % m) - 1, n - 1)
  259. return _unrank(rank, n)
  260. def random_bitstring(n):
  261. """
  262. Generates a random bitlist of length n.
  263. Examples
  264. ========
  265. >>> from sympy.combinatorics.graycode import random_bitstring
  266. >>> random_bitstring(3) # doctest: +SKIP
  267. 100
  268. """
  269. return ''.join([random.choice('01') for i in range(n)])
  270. def gray_to_bin(bin_list):
  271. """
  272. Convert from Gray coding to binary coding.
  273. We assume big endian encoding.
  274. Examples
  275. ========
  276. >>> from sympy.combinatorics.graycode import gray_to_bin
  277. >>> gray_to_bin('100')
  278. '111'
  279. See Also
  280. ========
  281. bin_to_gray
  282. """
  283. b = [bin_list[0]]
  284. for i in range(1, len(bin_list)):
  285. b += str(int(b[i - 1] != bin_list[i]))
  286. return ''.join(b)
  287. def bin_to_gray(bin_list):
  288. """
  289. Convert from binary coding to gray coding.
  290. We assume big endian encoding.
  291. Examples
  292. ========
  293. >>> from sympy.combinatorics.graycode import bin_to_gray
  294. >>> bin_to_gray('111')
  295. '100'
  296. See Also
  297. ========
  298. gray_to_bin
  299. """
  300. b = [bin_list[0]]
  301. for i in range(1, len(bin_list)):
  302. b += str(int(bin_list[i]) ^ int(bin_list[i - 1]))
  303. return ''.join(b)
  304. def get_subset_from_bitstring(super_set, bitstring):
  305. """
  306. Gets the subset defined by the bitstring.
  307. Examples
  308. ========
  309. >>> from sympy.combinatorics.graycode import get_subset_from_bitstring
  310. >>> get_subset_from_bitstring(['a', 'b', 'c', 'd'], '0011')
  311. ['c', 'd']
  312. >>> get_subset_from_bitstring(['c', 'a', 'c', 'c'], '1100')
  313. ['c', 'a']
  314. See Also
  315. ========
  316. graycode_subsets
  317. """
  318. if len(super_set) != len(bitstring):
  319. raise ValueError("The sizes of the lists are not equal")
  320. return [super_set[i] for i, j in enumerate(bitstring)
  321. if bitstring[i] == '1']
  322. def graycode_subsets(gray_code_set):
  323. """
  324. Generates the subsets as enumerated by a Gray code.
  325. Examples
  326. ========
  327. >>> from sympy.combinatorics.graycode import graycode_subsets
  328. >>> list(graycode_subsets(['a', 'b', 'c']))
  329. [[], ['c'], ['b', 'c'], ['b'], ['a', 'b'], ['a', 'b', 'c'], \
  330. ['a', 'c'], ['a']]
  331. >>> list(graycode_subsets(['a', 'b', 'c', 'c']))
  332. [[], ['c'], ['c', 'c'], ['c'], ['b', 'c'], ['b', 'c', 'c'], \
  333. ['b', 'c'], ['b'], ['a', 'b'], ['a', 'b', 'c'], ['a', 'b', 'c', 'c'], \
  334. ['a', 'b', 'c'], ['a', 'c'], ['a', 'c', 'c'], ['a', 'c'], ['a']]
  335. See Also
  336. ========
  337. get_subset_from_bitstring
  338. """
  339. for bitstring in list(GrayCode(len(gray_code_set)).generate_gray()):
  340. yield get_subset_from_bitstring(gray_code_set, bitstring)