Skip to content

Commit 013bacf

Browse files
committed
Added skip_example feature. This enables per-instance multipliers and also so we do not *have* to test the example. Also updated readme format
1 parent 6d76d81 commit 013bacf

File tree

9 files changed

+20590
-9912
lines changed

9 files changed

+20590
-9912
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ A more challenging puzzle that requires [dynamic programming](https://en.wikiped
115115
[longest increasing subsequence](https://en.wikipedia.org/wiki/Longest_increasing_subsequence) problem
116116
which we can also describe with strings:
117117
```python
118-
def f(x: List[int], length=20, s="Dynamic programming solves this classic job-interview puzzle!!!"):
118+
def sat(x: List[int], length=20, s="Dynamic programming solves this classic job-interview puzzle!!!"):
119119
"""Find the indices of the longest substring with characters in sorted order"""
120120
return all(s[x[i]] <= s[x[i + 1]] and x[i + 1] > x[i] for i in range(length - 1))
121121

@@ -179,7 +179,7 @@ During a Microsoft hackathon July 27-29, 2020, several people completed 30 user
179179
[Hackathon_puzzles.ipynb](/notebooks/Hackathon_puzzles.ipynb). These are of a somewhat
180180
different flavor as they are more often `hacks` like
181181
```python
182-
def f(x):
182+
def sat(x):
183183
return x > x
184184
```
185185
where the type of `x` is clearly non-standard. The creators of these puzzles include github users:

generators/IMO.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class ExponentialCoinMoves(PuzzleGenerator):
2020
2121
Inspired by [IMO 2010 Problem 5](https://www.imo-official.org/problems.aspx)"""
2222

23-
multiplier = 10 # worth 10 times normal so that it can run 10 times longer than normal
23+
skip_example = True # so that we can add a multiplier in gen method below
2424

2525
@staticmethod
2626
def sat(states: List[List[int]], n=16385):
@@ -88,9 +88,10 @@ def exp_move(): # shifts last 3 [..., a, 0, 0] to [..., 0, 2^a, 0] for a>0
8888
return ans
8989

9090
def gen(self, target_num_instances):
91+
self.add(dict(n=2**14 + 1), multiplier=10) # n=16385 will be first instance because of skip_example
9192
for i in range(10):
9293
n = 2 ** i
93-
self.add(dict(n=n))
94+
self.add(dict(n=n), multiplier=1 if n <= 4 else n)
9495

9596

9697
class NoRelativePrimes(PuzzleGenerator):

generators/classic_puzzles.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
"""Classic puzzles
2-
"""
1+
"""Classic puzzles"""
32

43
from puzzle_generator import PuzzleGenerator
54
from typing import List

generators/games.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class Nim(PuzzleGenerator):
1919
them to determine winning states or beat a certain opponent.
2020
"""
2121

22-
value_multiplier = 10 # harder than most problems, worth more
22+
skip_example = True # so we can add multiplier in gen method below
2323

2424
@staticmethod
2525
def sat(moves: List[List[int]], initial_state=[5, 9, 3, 11, 18, 25, 1, 2, 4, 1]):
@@ -79,6 +79,9 @@ def optimal_move():
7979
return moves
8080
bot_move()
8181

82+
def gen(self, target_num_instances):
83+
self.add(self.get_example(), multiplier=10)
84+
8285
def gen_random(self):
8386
def losing(h): # return True if h is a losing state
8487
xor = 0
@@ -94,7 +97,7 @@ def losing(h): # return True if h is a losing state
9497
for i in initial_state:
9598
prod *= i + 1
9699
if prod < 10 ** 6:
97-
self.add(dict(initial_state=initial_state))
100+
self.add(dict(initial_state=initial_state), multiplier=10 if prod > 1000 else 1)
98101

99102

100103
class Mastermind(PuzzleGenerator):
@@ -105,7 +108,7 @@ class Mastermind(PuzzleGenerator):
105108
them to provide a provable winning game tree.
106109
"""
107110

108-
multiplier = 10 # hard puzzle, takes longer to test
111+
skip_example = True # so we can add multiplier in gen method below
109112

110113
@staticmethod
111114
def sat(transcripts: List[str], max_moves=10):
@@ -191,8 +194,8 @@ def max_ambiguity(guess):
191194
return transcripts
192195

193196
def gen(self, target_num_instances):
194-
for max_moves in [6, 8, 10]:
195-
self.add(dict(max_moves=max_moves))
197+
for max_moves in [10, 8, 6]:
198+
self.add(dict(max_moves=max_moves), multiplier=30 - 2 * max_moves)
196199

197200

198201
class TicTacToeX(PuzzleGenerator):
@@ -315,17 +318,16 @@ def sol():
315318
return [1 / 3] * 3
316319

317320

318-
319321
class Nash(PuzzleGenerator):
320322
"""Computing a [Nash equilibrium](https://en.wikipedia.org/wiki/Nash_equilibrium) for a given
321323
[bimatrix game](https://en.wikipedia.org/wiki/Bimatrix_game) is known to be
322324
PPAD-hard in general. However, the challenge is be much easier for an approximate
323325
[eps-equilibrium](https://en.wikipedia.org/wiki/Epsilon-equilibrium) and of course for small games."""
324326

325-
multiplier = 10
327+
skip_example = True # so we can add multiplier in gen method below
326328

327329
@staticmethod
328-
def sat(strategies: List[List[float]], A = [[1.0, -1.0], [-1.3, 0.8]], B = [[-0.9, 1.1], [0.7, -0.8]], eps=0.01):
330+
def sat(strategies: List[List[float]], A=[[1.0, -1.0], [-1.3, 0.8]], B=[[-0.9, 1.1], [0.7, -0.8]], eps=0.01):
329331
"""
330332
Find an eps-Nash-equilibrium for a given two-player game with payoffs described by matrices A, B.
331333
For example, for the classic Prisoner dilemma:
@@ -373,13 +375,16 @@ def sat(strategies: List[List[float]], A, B, eps):
373375
if sat(strategies, A, B, eps):
374376
return strategies
375377

378+
def gen(self, target_num_instances):
379+
self.add(self.get_example(), multiplier=5)
380+
376381
def gen_random(self):
377382
m = self.random.randrange(2, 10)
378383
n = self.random.randrange(2, 10)
379384
A, B = [[[self.random.random() for _i in range(m)] for _j in range(n)] for _k in range(2)]
380385
eps = self.random.choice([0.5, 0.1, 0.01])
381386
solved = self.sol(A, B, eps) is not None
382-
self.add(dict(A=A, B=B, eps=eps), test=solved)
387+
self.add(dict(A=A, B=B, eps=eps), test=solved, multiplier=5)
383388

384389

385390
class ZeroSum(PuzzleGenerator):
@@ -390,7 +395,7 @@ class ZeroSum(PuzzleGenerator):
390395
more efficient algorithms would be needed."""
391396

392397
@staticmethod
393-
def sat(strategies: List[List[float]], A = [[0., -0.5, 1.], [0.75, 0., -1.], [-1., 0.4, 0.]], eps=0.01):
398+
def sat(strategies: List[List[float]], A=[[0., -0.5, 1.], [0.75, 0., -1.], [-1., 0.4, 0.]], eps=0.01):
394399
"""
395400
Compute minimax optimal strategies for a given zero-sum game up to error tolerance eps.
396401
For example, rock paper scissors has
@@ -407,7 +412,7 @@ def sat(strategies: List[List[float]], A = [[0., -0.5, 1.], [0.75, 0., -1.], [-1
407412

408413
@staticmethod
409414
def sol(A, eps):
410-
MAX_ITER = 10**4
415+
MAX_ITER = 10 ** 4
411416
m, n = len(A), len(A[0])
412417
a = [0 for _i in range(m)]
413418
b = [0 for _j in range(n)]

generators/lattices.py

Lines changed: 18 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,8 @@ class LearnParity(PuzzleGenerator):
1818
The vectors are encoded as binary integers for succinctness.
1919
"""
2020

21-
# vecs below generated by vecs = LearnParity.rand_parity_problem(random.Random(28562407), d=63)
22-
2321
@staticmethod
24-
def sat(inds: List[int], vecs=[8543342634111025532, 8335192666369313368, 2359039407982105779, 4172548441791366513,
25-
1256349095522986569, 3754463859322679595, 1562879970152915618, 1933016518061876369,
26-
5920060919607788629, 8545759471656960221, 2934241949774725291, 559495833580308526,
27-
5239436672544732707, 5865707252111994906, 8310678944230832071, 4595527784831581592,
28-
4348871153851862010, 5198370132175169882, 3748480974791545460, 1215135748294622536,
29-
4321487173746421746, 9012812639700145153, 588387599697000986, 5003829835901037543,
30-
7754881381173342129, 2635789994388296837, 3222773777603033590, 5790284924977099989,
31-
7540575369379211274, 7898971930608516039, 27260728996582582, 1792453914477410383,
32-
8726418386455953809, 9193001185022172125, 3515388340741601364, 6217726337930929836,
33-
1038687698871580494, 1892601486162604802, 3633356355444530940, 108334555669330693,
34-
1955821183884414243, 5681081121990060330, 5791800194327455183, 8459367068223249929,
35-
4271428016720060690, 913733008909519396, 2233236350093301187, 6538503022239131288,
36-
5292485269677307644, 4615671355181378169, 2605305508625596241, 4954529961471509975,
37-
2312963580097644831, 888555840551788245, 4152336321587083789, 8978251650218883651,
38-
2567641184250287470, 2168893575221172018, 4358821646257958779, 3102433300308778243,
39-
4185793889128296420, 6687096428156463254, 4143873353280484310, 8454616559174688585,
40-
6589014033410725016, 5903549622062684554, 2388718494916838667, 8850145667696469408,
41-
5068285804151890745, 2981241929741282230, 79408177335937724, 1711542430102927280]):
22+
def sat(inds: List[int], vecs=[169, 203, 409, 50, 37, 479, 370, 133, 53, 159, 161, 367, 474, 107, 82, 447, 385]):
4223
"""
4324
Parity learning: Given binary vectors in a subspace, find the secret set S of indices such that:
4425
$\\sum_{i \in S} x_i = 1 (mod 2)$
@@ -76,22 +57,25 @@ def sol(vecs):
7657

7758
@staticmethod
7859
def rand_parity_problem(rand, d=63):
79-
secret = None
80-
while not secret:
81-
secret = [i for i in range(d) if rand.randrange(2)]
60+
secret = rand.sample(range(d), d // 2)
8261
num_vecs = d + 9
8362
vecs = [[rand.randrange(2) for _ in range(d)] for i in range(num_vecs)]
8463
for v in vecs:
8564
v[secret[0]] = (1 + sum([v[i] for i in secret[1:]])) % 2
8665
vecs = [sum(1 << i for i, b in enumerate(v) if b) for v in vecs] # encode into ints
8766
return vecs
8867

68+
def gen(self, target_num_instances):
69+
vecs = self.rand_parity_problem(self.random, d=63)
70+
self.add(dict(vecs=vecs), multiplier=10)
71+
8972
def gen_random(self):
73+
d = self.random.randrange(2, self.random.choice([5, 10, 20, 100]))
9074
vecs = self.rand_parity_problem(
9175
self.random,
92-
d=self.random.randrange(2, self.random.choice([5, 10, 20, 100]))
76+
d=d,
9377
)
94-
self.add(dict(vecs=vecs))
78+
self.add(dict(vecs=vecs), multiplier=10 if d > 9 else 1)
9579

9680

9781
class LearnParityWithNoise(PuzzleGenerator):
@@ -101,44 +85,8 @@ class LearnParityWithNoise(PuzzleGenerator):
10185
[Parity learning problem](https://en.wikipedia.org/w/index.php?title=Parity_learning)
10286
runs in time $2^(d/(log d))$"""
10387

104-
# vecs below generated by LearnParityWithNoise.rand_parity_problem(random.Random(852352407), d=63)
105-
106-
multiplier = 40 # hard puzzle, takes longer to test
107-
10888
@staticmethod
109-
def sat(inds: List[int], vecs=[2874444459419665109, 3571416480966091062, 3627516422625241827, 2417762213996395207,
110-
4371357242721531635, 1396026910505373292, 6671557086560014752, 9066082518122683098,
111-
5240053591369828114, 8556210480838058892, 7302977584273736381, 8938278934736014411,
112-
4398671200512032996, 6147375266514044469, 6609538006889421793, 2297823643430705118,
113-
7583979108118079257, 2498392101379258437, 7893501751515236283, 2027235323873165116,
114-
925357965000140266, 9009345166609418406, 5689450111800001849, 2079746314404416253,
115-
4228649029862868917, 5819371323838727219, 102386757609774316, 5480808186035115654,
116-
3001738569073502536, 9059061077086189682, 681271298018419415, 5616731111115463763,
117-
2722737236861682531, 4918075690687573998, 7125583379861998376, 7968096465923567867,
118-
898679944061110348, 1140358409311167922, 6077294650144352445, 587776882127248609,
119-
2018954969823094844, 1618480274277140739, 8884189689498565225, 4084721521520724931,
120-
4718438135662169666, 8411612063174086200, 8726374275365985960, 3135872851883336005,
121-
1091802941995014823, 4944178741300545327, 6970959994566965947, 2911632933598497473,
122-
8638215954009823387, 7438975146059987571, 3486356869336916018, 4935404783245269300,
123-
3492912640500734004, 7903591215281799872, 4616161610863395412, 875020887047334808,
124-
2721628497281503934, 6882639287577667047, 6274957618887284536, 3575443754501116278,
125-
2031604067526359716, 4433373641914130623, 6204772769819600658, 8509292558066435714,
126-
1857073904365563798, 7875287918949902618, 5205034693823928900, 4943396962875355147,
127-
2805601192218759148, 8976171820624983460, 5930936964665834653, 949687393644726240,
128-
6466615045398392331, 423404729770342491, 2720698610804800422, 7479269416044676778,
129-
7869290888646534505, 6327163107872545492, 476579447640475544, 1218066186129904051,
130-
7630726053076756205, 7741086216563432736, 5225376670650457287, 7040078265943665053,
131-
2162853338175426448, 5633819254572300801, 92334600849454176, 9098183941628882647,
132-
3481731752092062852, 5473741255745389738, 7266470290696653678, 3090338455353169956,
133-
4358343354422765853, 3623553173494979282, 8328390749513844747, 2287762878756609646,
134-
4126189061710502597, 5829472669961813184, 7342395882491704275, 5030578088617810038,
135-
2210525427289006508, 6161187897225224000, 5601573223749212224, 6539026784581543793,
136-
3571032801838391198, 4813662449014287760, 6577243754700968179, 4401899289452367605,
137-
305529480505303551, 1548494450097231731, 6926707725781258948, 6357305518384676781,
138-
6357665620505806556, 1554358231697328409, 7871587375269472810, 2094942344314098945,
139-
1452972368095860063, 3210274450167364491, 6901356410911155351, 7609098874470545378,
140-
6955802737127492446, 6919896432783547538, 5423154486785623318, 3105394980859157674,
141-
8438962979748731599, 4110730383299136510, 6718356757580670867]):
89+
def sat(inds: List[int], vecs=[26, 5, 32, 3, 15, 18, 31, 13, 24, 25, 34, 5, 15, 24, 16, 13, 0, 27, 37]):
14290
"""
14391
Learning parity with noise: Given binary vectors, find the secret set $S$ of indices such that, for at least
14492
3/4 of the vectors, $$sum_{i \in S} x_i = 1 (mod 2)$$
@@ -158,18 +106,15 @@ def sol(vecs):
158106
import random
159107
rand = random.Random(0)
160108
target = (len(vecs) * 3) // 4
161-
max_attempts = 10**4
109+
max_attempts = 10 ** 5
162110
for _ in range(max_attempts):
163111
ans = [i for i in range(d) if rand.randrange(2)]
164112
if sum(sum(v[i] for i in ans) % 2 for v in vecs) >= len(vecs) * 3 / 4:
165113
return ans
166114

167115
@staticmethod
168116
def rand_parity_problem(rand, d=63):
169-
secret = None
170-
while not secret:
171-
secret = [i for i in range(d) if rand.randrange(2)]
172-
print(len(secret))
117+
secret = rand.sample(range(d), d // 2)
173118
num_vecs = 2 * d + 5
174119
vecs = [[rand.randrange(2) for _ in range(d)] for i in range(num_vecs)]
175120
for v in vecs:
@@ -180,17 +125,17 @@ def rand_parity_problem(rand, d=63):
180125
vecs = [sum(1 << i for i, b in enumerate(v) if b) for v in vecs] # encode into ints
181126
return vecs
182127

183-
def testable(self, inp: dict):
184-
return max(inp["vecs"]) < 2 ** 20
128+
def gen(self, target_num_instances):
129+
vecs = self.rand_parity_problem(self.random, d=63)
130+
self.add(dict(vecs=vecs), test=False, multiplier=1000)
185131

186132
def gen_random(self):
187133
d = self.random.randrange(2, self.random.choice([11, 100])) # number of dimensions
188134
vecs = self.rand_parity_problem(
189135
self.random,
190136
d=d
191137
)
192-
self.add(dict(vecs=vecs), test=d < 19)
193-
138+
self.add(dict(vecs=vecs), test=d < 19, multiplier=1000 if d > 40 else 30 if d >= 19 else 1)
194139

195-
if __name__ == "__main__":
196-
PuzzleGenerator.debug_problems()
140+
if __name__ == "__main__":
141+
PuzzleGenerator.debug_problems()

0 commit comments

Comments
 (0)