├── README.md ├── examples.py └── quantum.py /README.md: -------------------------------------------------------------------------------- 1 | # quantum 2 | Simulate reverse causality using quantum suicide. 3 | 4 | ```python 5 | import quantum 6 | x = quantum.choice([1,2,3,4,5,6]) # creates 6 universes 7 | quantum.assert_(x > 3) # destroys universes 1,2,3 8 | quantum.assert_(x < 6) # destroys universe 6 9 | print(x) # prints either 4 or 5 10 | ``` 11 | 12 | #### `quantum.choice(sequence)` 13 | 14 | Returns every element of the provided sequence, 15 | each in a different alternative universe (which this call creates). 16 | If the provided sequence is empty, this is equivalent to `quantum.fail()`. 17 | 18 | #### `quantum.fail()` 19 | 20 | Silently and instantaneously destroys the universe, 21 | preventing you from observing the timeline in which this call was made. 22 | **Warning:** Only useful if a previous call to `quantum.choice()` has created other universes. 23 | See notes below. 24 | 25 | #### `quantum.assert_(condition)` 26 | 27 | Shorthand for `if not condition: quantum.fail()`. 28 | 29 | 30 | ## [Examples](https://github.com/karldray/quantum/blob/master/examples.py) 31 | 32 | 33 | #### Quantum sort 34 | 35 | ```python 36 | def qsort(xs): 37 | # choose a permutation of the input 38 | r = quantum.choice(itertools.permutations(xs)) 39 | # assert that it's sorted 40 | quantum.assert_(all(r[i - 1] <= r[i] for i in range(1, len(r)))) 41 | # return it 42 | return r 43 | 44 | print(qsort([3, 0, 5, 1, 2])) # prints [0, 1, 2, 3, 5] 45 | ``` 46 | 47 | #### Sudoku solver 48 | 49 | ```python 50 | def solve(board): 51 | ''' 52 | Given a Sudoku puzzle as a list of 81 ints in {0,...,9} 53 | with 0 representing an empty cell, solve the puzzle in-place. 54 | ''' 55 | for i in range(81): 56 | if board[i] == 0: 57 | neighbors = set(board[j] for group in GROUPS[i] for j in group) 58 | board[i] = quantum.choice(x for x in range(1, 10) if x not in neighbors) 59 | 60 | board = [0] * 81 61 | solve(board) 62 | printboard(board) 63 | ``` 64 | 65 | Output: 66 | ``` 67 | 1 2 3 4 5 6 7 8 9 68 | 4 5 6 7 8 9 1 2 3 69 | 7 8 9 1 2 3 4 5 6 70 | 2 1 4 3 6 5 8 9 7 71 | 3 6 5 8 9 7 2 1 4 72 | 8 9 7 2 1 4 3 6 5 73 | 5 3 1 6 4 2 9 7 8 74 | 6 4 2 9 7 8 5 3 1 75 | 9 7 8 5 3 1 6 4 2 76 | ``` 77 | 78 | 79 | ## Notes 80 | 81 | *How it works:* Because you can never observe a scenario 82 | in which `fail` has been called (since it destroys the universe), 83 | you will necessarily observe a "fortuitous" timeline in which the values returned by `choice` 84 | cause your program to complete without ever calling `fail`. 85 | **Be careful to ensure that such a timeline exists!** 86 | 87 | **Do not run a program that calls fail() unconditionally or in every timeline.** 88 | Such a program destroys every universe in which it runs correctly, so 89 | the only observable outcome is one in which an extremely unlikely (and potentially dangerous) 90 | event prevents it from completing. 91 | 92 | When there are multiple possible (non-`fail`ing) executions of a quantum program, 93 | we consistently end up observing the lexicographically smallest one 94 | with respect to the iteration order of sequences passed to `choice`. 95 | This phenomenon remains unexplained. 96 | -------------------------------------------------------------------------------- /examples.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | try: xrange 4 | except NameError: pass 5 | else: range = xrange 6 | 7 | import itertools 8 | import quantum 9 | 10 | 11 | 12 | 13 | # SORT 14 | 15 | def qsort(xs): 16 | # choose a permutation of the input 17 | r = quantum.choice(itertools.permutations(xs)) 18 | # assert that it's sorted 19 | quantum.assert_(all(r[i - 1] <= r[i] for i in range(1, len(r)))) 20 | # return it 21 | return r 22 | 23 | print(qsort([3, 0, 5, 1, 2])) # prints [0, 1, 2, 3, 5] 24 | 25 | 26 | 27 | 28 | # SUDOKU 29 | 30 | # initialize board structure 31 | def _make_groups(): 32 | groups = [[] for _ in range(27)] 33 | c2g = [[] for c in range(81)] 34 | for c in range(81): 35 | row = c // 9 36 | col = c % 9 37 | box = 3 * (row // 3) + col // 3 38 | for i in (row, 9 + col, 18 + box): 39 | g = groups[i] 40 | g.append(c) 41 | c2g[c].append(g) 42 | return c2g 43 | 44 | GROUPS = _make_groups() 45 | 46 | def solve(board): 47 | ''' 48 | Given a Sudoku puzzle as a list of 81 ints in {0,...,9} 49 | with 0 representing an empty cell, solve the puzzle in-place. 50 | ''' 51 | for i in range(81): 52 | if board[i] == 0: 53 | neighbors = set(board[j] for group in GROUPS[i] for j in group) 54 | board[i] = quantum.choice(x for x in range(1, 10) if x not in neighbors) 55 | 56 | def printboard(board): 57 | for i in range(9): 58 | print(*board[9*i:9*(i+1)]) 59 | 60 | board = [0] * 81 61 | solve(board) 62 | printboard(board) 63 | 64 | 65 | 66 | 67 | # SCHEDULING 68 | 69 | def roundrobin(n): 70 | ''' 71 | Generate an (n-1)-day round-robin tournament schedule for n teams 72 | (i.e. a partition of K_n's edge set into perfect matchings). 73 | ''' 74 | assert n >= 2 and n & 1 == 0 75 | matches_used = set() 76 | 77 | sched = [] 78 | for _ in range(n - 1): 79 | team_used = [False] * n 80 | rnd = [] 81 | # ensure each team has a match this round 82 | for t in range(n): 83 | if not team_used[t]: 84 | # choose an opponent for t 85 | u = quantum.choice(range(t + 1, n)) 86 | 87 | # ensure u isn't already in a match this round 88 | quantum.assert_(not team_used[u]) 89 | 90 | # ensure t and u haven't already played each other 91 | m = (t, u) 92 | quantum.assert_(m not in matches_used) 93 | 94 | # add this match to our data structures 95 | team_used[t] = team_used[u] = True 96 | matches_used.add(m) 97 | rnd.append(m) 98 | 99 | assert len(rnd) == n // 2 # sanity check 100 | sched.append(rnd) 101 | 102 | return sched 103 | 104 | print(roundrobin(6)) 105 | -------------------------------------------------------------------------------- /quantum.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | __all__ = ["choice", "fail", "assert_"] 5 | 6 | 7 | FAILCODE = 55 8 | 9 | def choice(xs=(False, True)): 10 | for x in xs: 11 | pid = os.fork() 12 | if pid == 0: 13 | return x 14 | _, code = os.waitpid(pid, 0) 15 | code >>= 8 16 | if code != FAILCODE: 17 | os._exit(code) 18 | fail() 19 | 20 | def fail(): 21 | os._exit(FAILCODE) 22 | 23 | def assert_(cond): 24 | if not cond: fail() 25 | --------------------------------------------------------------------------------