Skip to content

Commit 6d76d81

Browse files
committed
Made lattice/parity problems harder. Minor comment improvements on human_eval.py. Added puzzles.json to the repo
1 parent 3679858 commit 6d76d81

File tree

6 files changed

+20660
-120
lines changed

6 files changed

+20660
-120
lines changed

generators/human_eval.py

Lines changed: 31 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -7,47 +7,20 @@
77
from typing import List
88

99
"""
10-
Some that came out especially nicely as puzzles:
11-
ParenthesesPermutation
12-
Derivative
13-
Frac/ClosestInteger
14-
HeronTriangle
15-
RomanNumerals
16-
ClosestPalindrome
17-
WildSort
18-
Intersperse
19-
SimplifyProductFraction
20-
Fib4
21-
MinSquaredDeviation
22-
DiffChars
23-
RotateString
24-
EvaluateOperators
25-
Grader
26-
Median
27-
TripleZeroSum
28-
PrimeFib
29-
30-
Some that weren't such natural puzzles:
31-
CircularShiftNum
32-
ReplaceMe
33-
MinSubArraySum
34-
Buckets
35-
OddEvenSum
36-
FindStrangeSum
37-
EvenSqure
38-
StrongestExtension
39-
HungryRabbits
40-
ReverseCase
41-
MatchBrackets
42-
ListTotal
43-
BelowThreshold
44-
RemoveVowels
45-
"""
10+
Some came out especially nicely as puzzles:
11+
ParenthesesPermutation, Derivative, Frac, HeronTriangle, RomanNumerals, ClosestPalindrome, WildSort, Intersperse,
12+
SimplifyProductFraction, Fib4, DiffChars, RotateString, EvaluateOperators, Grader, TripleZeroSum, PrimeFib
4613
14+
Some weren't such natural puzzles:
15+
CircularShiftNum, ReplaceMe, MinSubArraySum, Buckets, OddEvenSum, FindStrangeSum, EvenSqure, StrongestExtension
16+
HungryRabbits, ReverseCase, MatchBrackets, ListTotal, BelowThreshold, RemoveVowels
4717
48-
# See https://github.com/microsoft/PythonProgrammingPuzzles/wiki/How-to-add-a-puzzle to learn about adding puzzles
18+
In many cases, the original problem wasn't naturally a puzzle but it inspired a nice loosely-related puzzle:
19+
ZobristCollision, EvenBetween, MinSquaredDeviation, Median
20+
"""
4921

5022

23+
# See https://github.com/microsoft/PythonProgrammingPuzzles/wiki/How-to-add-a-puzzle to learn about adding puzzles
5124

5225
class FindCloseElements(PuzzleGenerator):
5326
"""Inspired by [HumanEval](https://github.com/openai/human-eval) \\#0"""
@@ -163,10 +136,10 @@ def sat(firsts: List[int], balances=[[2, 7, -2, 4, 3, -15, 10, -45, 3], [3, 4, -
163136
Given a list of numbers which represent bank deposits and withdrawals, find the *first* negative balance.
164137
165138
Sample Input:
166-
[12, -5, 3, -99, 14, 88, -99]
139+
[[12, -5, 3, -99, 14, 88, -99], [-1, 2, 5]]
167140
168141
Sample Output:
169-
-89
142+
[-89, -1]
170143
"""
171144
for i, bals in enumerate(balances):
172145
total = 0
@@ -1716,7 +1689,8 @@ def gen_random(self):
17161689

17171690
class Median(PuzzleGenerator):
17181691
"""
1719-
One definition of the median is a number that minimizes the sum of absolute deviations.
1692+
One definition of the median is a number that minimizes the sum of absolute deviations. When there are an
1693+
even number of items, there is an interval of valid solutions.
17201694
17211695
Inspired by [HumanEval](https://github.com/openai/human-eval) \\#47
17221696
"""
@@ -4018,8 +3992,11 @@ class ParenthesesPermutation(PuzzleGenerator):
40183992
"""
40193993

40203994
@staticmethod
4021-
def sat(perm: str,
4022-
s="))( )()()() )))(( ))))((( )))))(((( ))))))))((((((( ))))))((((( )))))))(((((( )))))))))((((((( (((((((((("):
3995+
def sat(
3996+
perm: str,
3997+
s="))( )()()() )))(( ))))((( )))))(((( ))))))))((((((( ))))))((((( " +
3998+
")))))))(((((( )))))))))((((((( (((((((((("
3999+
):
40234000
"""The string s consists of groups of parentheses separated by spaces.
40244001
Permute the groups such that the parentheses match.
40254002
@@ -5391,7 +5368,11 @@ def gen_random(self):
53915368

53925369

53935370
class ZobristCollision(PuzzleGenerator):
5394-
"""Inspired by [HumanEval](https://github.com/openai/human-eval) \\#162"""
5371+
"""Inspired by [HumanEval](https://github.com/openai/human-eval) \\#162
5372+
5373+
The original problem was to compute an MD5 hash. This puzzle is a problem in the space of hashing, but of a
5374+
different nature.
5375+
"""
53955376

53965377
@staticmethod
53975378
def sat(positions: List[List[int]]):
@@ -5433,7 +5414,13 @@ def zobrist(pos):
54335414

54345415

54355416
class EvenBetween(PuzzleGenerator):
5436-
"""Inspired by [HumanEval](https://github.com/openai/human-eval) \\#163"""
5417+
"""Inspired by [HumanEval](https://github.com/openai/human-eval) \\#163
5418+
5419+
The original problem was trivial to list the even single-digit numbers between two numbers:
5420+
`a=2, b=12` => `[4, 6, 8]`. In this puzzle, we consider the string of even numbers formed when counting from
5421+
`a` to `b`, e.g., `"1618202224262830"` when counting from `15` to `30`. The puzzle is, given such a string,
5422+
find `a` and `b`.
5423+
"""
54375424

54385425
@staticmethod
54395426
def sat(ab: List[int], s="3298832990329923299432996329983300033002"):
@@ -5468,5 +5455,3 @@ def gen_random(self):
54685455

54695456
if __name__ == "__main__":
54705457
PuzzleGenerator.debug_problems()
5471-
5472-

generators/lattices.py

Lines changed: 93 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,37 @@ 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+
2123
@staticmethod
22-
def sat(inds: List[int], vecs=[169, 203, 409, 50, 37, 479, 370, 133, 53, 159, 161, 367, 474, 107, 82, 447, 385]):
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]):
2342
"""
24-
Parity learning: Given binary vectors in a subspace, find the secret set $S$ of indices such that:
25-
$$sum_{i \in S} x_i = 1 (mod 2)$$
43+
Parity learning: Given binary vectors in a subspace, find the secret set S of indices such that:
44+
$\\sum_{i \in S} x_i = 1 (mod 2)$
2645
"""
2746
return all(sum((v >> i) & 1 for i in inds) % 2 == 1 for v in vecs)
2847

2948
@staticmethod
3049
def sol(vecs):
3150
# Gaussian elimination
32-
d = 0 # decode vectors into arrays
51+
d = 0 # decode vectors into arrays
3352
m = max(vecs)
3453
while m:
3554
m >>= 1
@@ -55,16 +74,23 @@ def sol(vecs):
5574

5675
return [i for i in range(d) if pool[i][-1]]
5776

58-
def gen_random(self):
59-
d = self.random.randrange(2, self.random.choice([5, 10, 20, 100]))
77+
@staticmethod
78+
def rand_parity_problem(rand, d=63):
6079
secret = None
6180
while not secret:
62-
secret = [i for i in range(d) if self.random.randrange(2)]
63-
num_vecs = self.random.randrange(d, 5 * d)
64-
vecs = [[self.random.randrange(2) for _ in range(d)] for i in range(num_vecs)]
81+
secret = [i for i in range(d) if rand.randrange(2)]
82+
num_vecs = d + 9
83+
vecs = [[rand.randrange(2) for _ in range(d)] for i in range(num_vecs)]
6584
for v in vecs:
6685
v[secret[0]] = (1 + sum([v[i] for i in secret[1:]])) % 2
67-
vecs = [sum(1 << i for i, b in enumerate(v) if b) for v in vecs] # encode into ints
86+
vecs = [sum(1 << i for i, b in enumerate(v) if b) for v in vecs] # encode into ints
87+
return vecs
88+
89+
def gen_random(self):
90+
vecs = self.rand_parity_problem(
91+
self.random,
92+
d=self.random.randrange(2, self.random.choice([5, 10, 20, 100]))
93+
)
6894
self.add(dict(vecs=vecs))
6995

7096

@@ -75,10 +101,44 @@ class LearnParityWithNoise(PuzzleGenerator):
75101
[Parity learning problem](https://en.wikipedia.org/w/index.php?title=Parity_learning)
76102
runs in time $2^(d/(log d))$"""
77103

104+
# vecs below generated by LearnParityWithNoise.rand_parity_problem(random.Random(852352407), d=63)
105+
78106
multiplier = 40 # hard puzzle, takes longer to test
79107

80108
@staticmethod
81-
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]):
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]):
82142
"""
83143
Learning parity with noise: Given binary vectors, find the secret set $S$ of indices such that, for at least
84144
3/4 of the vectors, $$sum_{i \in S} x_i = 1 (mod 2)$$
@@ -88,7 +148,7 @@ def sat(inds: List[int], vecs=[26, 5, 32, 3, 15, 18, 31, 13, 24, 25, 34, 5, 15,
88148
@staticmethod
89149
def sol(vecs):
90150
# brute force
91-
d = 0 # decode vectors into arrays
151+
d = 0 # decode vectors into arrays
92152
m = max(vecs)
93153
while m:
94154
m >>= 1
@@ -98,26 +158,38 @@ def sol(vecs):
98158
import random
99159
rand = random.Random(0)
100160
target = (len(vecs) * 3) // 4
101-
while True:
161+
max_attempts = 10**4
162+
for _ in range(max_attempts):
102163
ans = [i for i in range(d) if rand.randrange(2)]
103164
if sum(sum(v[i] for i in ans) % 2 for v in vecs) >= len(vecs) * 3 / 4:
104165
return ans
105166

106-
def gen_random(self):
107-
d = self.random.randrange(2, self.random.choice([11, 100])) # number of dimensions
167+
@staticmethod
168+
def rand_parity_problem(rand, d=63):
108169
secret = None
109170
while not secret:
110-
secret = [i for i in range(d) if self.random.randrange(2)]
111-
num_vecs = self.random.randrange(2 * d, 10 * d)
112-
vecs = [[self.random.randrange(2) for _ in range(d)] for i in range(num_vecs)]
171+
secret = [i for i in range(d) if rand.randrange(2)]
172+
print(len(secret))
173+
num_vecs = 2 * d + 5
174+
vecs = [[rand.randrange(2) for _ in range(d)] for i in range(num_vecs)]
113175
for v in vecs:
114176
v[secret[0]] = (1 + sum([v[i] for i in secret[1:]])) % 2
115-
mistakes = self.random.sample(vecs, int(len(vecs) * self.random.random() * 1 / 4))
177+
mistakes = rand.sample(vecs, int(len(vecs) * rand.random() * 1 / 4))
116178
for v in mistakes:
117179
v[secret[0]] ^= 1 # flip bit in mistakes
118-
vecs = [sum(1 << i for i, b in enumerate(v) if b) for v in vecs] # encode into ints
119-
self.add(dict(vecs=vecs), test=d < 19)
180+
vecs = [sum(1 << i for i, b in enumerate(v) if b) for v in vecs] # encode into ints
181+
return vecs
182+
183+
def testable(self, inp: dict):
184+
return max(inp["vecs"]) < 2 ** 20
120185

186+
def gen_random(self):
187+
d = self.random.randrange(2, self.random.choice([11, 100])) # number of dimensions
188+
vecs = self.rand_parity_problem(
189+
self.random,
190+
d=d
191+
)
192+
self.add(dict(vecs=vecs), test=d < 19)
121193

122194

123195
if __name__ == "__main__":

make_dataset.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ def main(args):
223223
utils.save_json(puzzles, args.json, make_dirs_if_necessary=True, indent=2)
224224
save_readme(summaries, args.readme, args.solutions)
225225
utils.info(f"Elapsed time: {(time.perf_counter() - start_time) / 60:.2f} minutes")
226-
utils.info(f"Saved {len(puzzles)} to {args.json} and {args.readme}")
226+
utils.info(f"Saved {len(puzzles):,} to {args.json} and {args.readme}")
227227

228228

229229
if __name__ == "__main__":

puzzle_generator.py

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ def check_for_trivial_solutions(self, force, already_tested): # check for trivi
721721
f"has trivial solution `{t}`")
722722
break
723723
dur = time.perf_counter() - time0
724-
if dur > 1.0:
724+
if dur > 1.0 * self.multiplier: # warn if above one second
725725
utils.warn(f"Took {dur:.1f}s to test for trivial solutions to `{self.name}`")
726726

727727
def gen(self, target_num_instances):
@@ -730,23 +730,12 @@ def gen(self, target_num_instances):
730730
def gen_random(self):
731731
pass
732732

733-
# def check_seen_input(self, inp):
734-
# """
735-
# Returns True if the input is a duplicate of a previous puzzle, and also makes sure that the types match
736-
# """
737-
# s = str(inp)
738-
# if s in self._seen_problems:
739-
# return True # duplicate problem
740-
#
741-
# self._seen_problems.add(s)
742-
#
743-
# assert set(inp) == set(self.arg_names), f"Instance #{len(self.instances)} keys mismatch in {self.name}"
744-
# example = self.get_example()
745-
# for k in inp:
746-
# v1, v2 = example[k], inp[k]
747-
# assert same_types(v1, v2), f"Instance #{len(self.instances)} variable `{k}` type mismatch in {self.name}"
748-
#
749-
# return False
733+
def testable(self, inp: dict):
734+
"""Override this to ensure that certain examples are not tested.
735+
This is the only way to make sure the *example* is not tested.
736+
For other instances, you can also avoid testing by calling .add(inp, test=False)
737+
"""
738+
return True
750739

751740
def add(self, inp: dict, test=True):
752741
s = str(inp)
@@ -762,7 +751,7 @@ def add(self, inp: dict, test=True):
762751
if not same_types(v1, v2):
763752
utils.warn(f"Instance #{self.num_generated_so_far()} variable `{k}` type mismatch in {self.name}")
764753

765-
self._inputs.append((inp, test))
754+
self._inputs.append((inp, test and self.testable(inp)))
766755

767756
# zzzz
768757
# def add(self, inp: dict, test=True):

0 commit comments

Comments
 (0)