ayushnoori commited on
Commit
6b5a85b
1 Parent(s): 2056e9c

Complete for string DSL

Browse files
Files changed (5) hide show
  1. README.md +8 -2
  2. arithmetic.py +0 -11
  3. examples.py +7 -2
  4. strings.py +112 -0
  5. synthesis.py +17 -10
README.md CHANGED
@@ -35,13 +35,19 @@ optional arguments:
35
  Domain of synthesis (either "arithmetic" or "string").
36
  --examples {addition,subtraction,multiplication,division}
37
  Examples to synthesize program from. Must be a valid key in the "example_set" dictionary.
38
- --max_weight MAX_WEIGHT
39
  Maximum weight of programs to consider before terminating search.
40
  ```
41
 
42
  For example, to synthesize programs in the arithmetic domain from the addition input-output examples, run:
43
  ```
44
- python3 synthesis.py --domain arithmetic --examples addition
 
 
 
 
 
 
45
  ```
46
 
47
  To add additional input-output examples, modify `examples.py`. Add a new key to the dictionary `example_set` and set the value to be a list of tuples.
 
35
  Domain of synthesis (either "arithmetic" or "string").
36
  --examples {addition,subtraction,multiplication,division}
37
  Examples to synthesize program from. Must be a valid key in the "example_set" dictionary.
38
+ --max-weight MAX_WEIGHT
39
  Maximum weight of programs to consider before terminating search.
40
  ```
41
 
42
  For example, to synthesize programs in the arithmetic domain from the addition input-output examples, run:
43
  ```
44
+ python synthesis.py --domain arithmetic --examples addition
45
+ - Extracted 9 constants from examples.
46
+ - Searching level 2 with 9 primitives.
47
+ Program found in 0.0116s.
48
+ Program: (x0 + x1)
49
+ Program weight: 3
50
+ Program return type: int
51
  ```
52
 
53
  To add additional input-output examples, modify `examples.py`. Add a new key to the dictionary `example_set` and set the value to be a list of tuples.
arithmetic.py CHANGED
@@ -13,21 +13,10 @@ class IntegerVariable:
13
  For example, if the input is [4, 5, 6] and the variable is the third element (i.e., 6), then position = 2.
14
  '''
15
  def __init__(self, position):
16
- # self.value = None # value of the variable, initially None
17
  self.position = position # zero-indexed position of the variable in the arguments to program
18
  self.type = int # type of the variable
19
  self.weight = 1 # weight of the variable
20
 
21
- # def assign(self, value):
22
- # self.value = value
23
-
24
- # def evaluate(self, input = None):
25
- # # check that variable has been assigned a value
26
- # if self.value is None:
27
- # raise ValueError(f"Variable {self.position} has not been assigned a value.")
28
-
29
- # return self.value
30
-
31
  def evaluate(self, input = None):
32
 
33
  # check that input is not None
 
13
  For example, if the input is [4, 5, 6] and the variable is the third element (i.e., 6), then position = 2.
14
  '''
15
  def __init__(self, position):
 
16
  self.position = position # zero-indexed position of the variable in the arguments to program
17
  self.type = int # type of the variable
18
  self.weight = 1 # weight of the variable
19
 
 
 
 
 
 
 
 
 
 
 
20
  def evaluate(self, input = None):
21
 
22
  # check that input is not None
examples.py CHANGED
@@ -11,13 +11,18 @@ function in the synthesizer.
11
 
12
  # define examples
13
  example_set = {
14
- # arithmetic examples
15
  'addition': [([7, 2], 9), ([8, 1], 9), ([3, 9], 12), ([5, 8], 13)], # ([4, 6], 10),
16
  'subtraction': [([9, 2], 7), ([6, 1], 5), ([7, 3], 4), ([8, 4], 4), ([10, 2], 8)],
17
  'multiplication': [([2, 3], 6), ([4, 5], 20), ([7, 8], 56), ([9, 2], 18), ([3, 4], 12)],
18
- 'division': [([6, 2], 3), ([8, 4], 2), ([9, 3], 3), ([10, 5], 2), ([12, 6], 2)]
 
 
 
 
19
 
20
  # string examples
 
21
 
22
  # custom user examples
23
  }
 
11
 
12
  # define examples
13
  example_set = {
14
+ # basic arithmetic examples
15
  'addition': [([7, 2], 9), ([8, 1], 9), ([3, 9], 12), ([5, 8], 13)], # ([4, 6], 10),
16
  'subtraction': [([9, 2], 7), ([6, 1], 5), ([7, 3], 4), ([8, 4], 4), ([10, 2], 8)],
17
  'multiplication': [([2, 3], 6), ([4, 5], 20), ([7, 8], 56), ([9, 2], 18), ([3, 4], 12)],
18
+ 'division': [([6, 2], 3), ([8, 4], 2), ([9, 3], 3), ([10, 5], 2), ([12, 6], 2)],
19
+
20
+ # complex arithmetic examples
21
+ 'add_5_multiply_2': [([1, 2], 12), ([3, 4], 22), ([5, 6], 32), ([7, 8], 42), ([9, 10], 52)],
22
+ 'multiply_add_9': [([1, 2], 11), ([3, 4], 21), ([5, 6], 39), ([7, 8], 65), ([9, 10], 9)],
23
 
24
  # string examples
25
+ 'concatenate': [(["a", "b"], "ab"), (["c", "d"], "cd"), (["e", "f"], "ef")],
26
 
27
  # custom user examples
28
  }
strings.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ '''
2
+ STRING OPERATORS
3
+ This file contains Python classes that define the string operators for program synthesis.
4
+ '''
5
+
6
+ '''
7
+ CLASS DEFINITIONS
8
+ '''
9
+
10
+ class StringVariable:
11
+ '''
12
+ Class to represent an string variable. Note that position is the position of the variable in the input.
13
+ For example, if the input is ["a", "b", "c"] and the variable is the third element (i.e., "c"), then position = 2.
14
+ '''
15
+ def __init__(self, position):
16
+ self.position = position # zero-indexed position of the variable in the arguments to program
17
+ self.type = str # type of the variable
18
+ self.weight = 1 # weight of the variable
19
+
20
+ def evaluate(self, input = None):
21
+
22
+ # check that input is not None
23
+ if input is None:
24
+ raise ValueError("Input is None.")
25
+
26
+ # check that input is a list
27
+ if type(input) != list:
28
+ raise ValueError("Input is not a list.")
29
+
30
+ # check that input is not empty
31
+ if len(input) == 0:
32
+ raise ValueError("Input is empty.")
33
+
34
+ # check that position is valid
35
+ if self.position >= len(input):
36
+ raise ValueError(f"Position {self.position} is out of range for input of length {len(input)}.")
37
+
38
+ return input[self.position]
39
+
40
+ def str(self):
41
+ return f"x{self.position}"
42
+
43
+ class StringConstant:
44
+ '''
45
+ Class to represent an string constant.
46
+ '''
47
+ def __init__(self, value):
48
+ self.value = value # value of the constant
49
+ self.type = str # type of the constant
50
+ self.weight = 1 # weight of the constant
51
+
52
+ def evaluate(self, input = None):
53
+ return self.value
54
+
55
+ def str(self):
56
+ return str(self.value)
57
+
58
+ class Concatenate:
59
+ '''
60
+ Operator to concatenate two string values.
61
+ '''
62
+ def __init__(self):
63
+ self.arity = 2 # number of arguments
64
+ self.arg_types = [str, str] # argument types
65
+ self.return_type = str # return type
66
+ self.weight = 1 # weight
67
+
68
+ def evaluate(self, x, y, input = None):
69
+ return x + y
70
+
71
+ def str(self, x, y):
72
+ return f"Concat({x}, {y})"
73
+
74
+ class Left:
75
+ '''
76
+ Operator to get left substring.
77
+ '''
78
+ def __init__(self):
79
+ self.arity = 2 # number of arguments
80
+ self.arg_types = [str, int] # argument types
81
+ self.return_type = str # return type
82
+ self.weight = 1 # weight
83
+
84
+ def evaluate(self, x, y, input = None):
85
+ return x[:y]
86
+
87
+ def str(self, x, y):
88
+ return f"Left({x}, {y})"
89
+
90
+ class Right:
91
+ '''
92
+ Operator to get right substring.
93
+ '''
94
+ def __init__(self):
95
+ self.arity = 2 # number of arguments
96
+ self.arg_types = [str, int] # argument types
97
+ self.return_type = str # return type
98
+ self.weight = 1 # weight
99
+
100
+ def evaluate(self, x, y, input = None):
101
+ return x[(y * -1):]
102
+
103
+ def str(self, x, y):
104
+ return f"Right({x}, {y})"
105
+
106
+
107
+ '''
108
+ GLOBAL CONSTANTS
109
+ '''
110
+
111
+ # define operators
112
+ string_operators = [Concatenate(), Left(), Right()]
synthesis.py CHANGED
@@ -15,6 +15,7 @@ import time
15
 
16
  # import examples
17
  from arithmetic import *
 
18
  from abstract_syntax_tree import *
19
  from examples import example_set, check_examples
20
  import config
@@ -40,7 +41,7 @@ def parse_args():
40
  choices=example_set.keys(),
41
  help='Examples to synthesize program from. Must be a valid key in the "example_set" dictionary.')
42
 
43
- parser.add_argument('--max_weight', type=int, required=False, default=3,
44
  help='Maximum weight of programs to consider before terminating search.')
45
 
46
  args = parser.parse_args()
@@ -74,7 +75,7 @@ def extract_constants(examples):
74
  if type(input) == int:
75
  constants.append(IntegerConstant(input))
76
  elif type(input) == str:
77
- # constants.append(StringConstant(input))
78
  pass
79
  else:
80
  raise Exception("Input of unknown type.")
@@ -87,8 +88,7 @@ def extract_constants(examples):
87
  if arg == int:
88
  variables.append(IntegerVariable(position))
89
  elif arg == str:
90
- # variables.append(StringVariable(position))
91
- pass
92
  else:
93
  raise Exception("Input of unknown type.")
94
 
@@ -133,10 +133,16 @@ def run_synthesizer(args):
133
  # extract constants from examples
134
  program_bank = extract_constants(examples)
135
  program_bank_str = [p.str() for p in program_bank]
 
136
  print(f"- Extracted {len(program_bank)} constants from examples.")
137
 
138
  # define operators
139
- operators = arithmetic_operators
 
 
 
 
 
140
 
141
  # iterate over each level
142
  for weight in range(2, args.max_weight):
@@ -200,10 +206,11 @@ if __name__ == '__main__':
200
  elapsed_time = round(end_time - start_time, 4)
201
 
202
  # check if program was found
 
203
  if program is None:
204
- print(f"Max weight of {args.max_weight} reached, no program found in {elapsed_time}s.")
205
  else:
206
- print(f"Program found in {elapsed_time}s.")
207
- print(f"Program: {program.str()}")
208
- print(f"Program weight: {program.weight}")
209
- print(f"Program return type: {program.type.__name__}")
 
15
 
16
  # import examples
17
  from arithmetic import *
18
+ from strings import *
19
  from abstract_syntax_tree import *
20
  from examples import example_set, check_examples
21
  import config
 
41
  choices=example_set.keys(),
42
  help='Examples to synthesize program from. Must be a valid key in the "example_set" dictionary.')
43
 
44
+ parser.add_argument('--max-weight', type=int, required=False, default=3,
45
  help='Maximum weight of programs to consider before terminating search.')
46
 
47
  args = parser.parse_args()
 
75
  if type(input) == int:
76
  constants.append(IntegerConstant(input))
77
  elif type(input) == str:
78
+ constants.append(StringConstant(input))
79
  pass
80
  else:
81
  raise Exception("Input of unknown type.")
 
88
  if arg == int:
89
  variables.append(IntegerVariable(position))
90
  elif arg == str:
91
+ variables.append(StringVariable(position))
 
92
  else:
93
  raise Exception("Input of unknown type.")
94
 
 
133
  # extract constants from examples
134
  program_bank = extract_constants(examples)
135
  program_bank_str = [p.str() for p in program_bank]
136
+ print("\nSynthesis Log:")
137
  print(f"- Extracted {len(program_bank)} constants from examples.")
138
 
139
  # define operators
140
+ if args.domain == "arithmetic":
141
+ operators = arithmetic_operators
142
+ elif args.domain == "string":
143
+ operators = string_operators
144
+ else:
145
+ raise Exception('Domain not recognized. Must be either "arithmetic" or "string".')
146
 
147
  # iterate over each level
148
  for weight in range(2, args.max_weight):
 
206
  elapsed_time = round(end_time - start_time, 4)
207
 
208
  # check if program was found
209
+ print("\nSynthesis Results:")
210
  if program is None:
211
+ print(f"- Max weight of {args.max_weight} reached, no program found in {elapsed_time}s.")
212
  else:
213
+ print(f"- Program found in {elapsed_time}s.")
214
+ print(f"- Program: {program.str()}")
215
+ print(f"- Program weight: {program.weight}")
216
+ print(f"- Program return type: {program.type.__name__}")