def check_program(name: str, inputs: list, outputs: list, sig: ProgramSignature, iset: InstructionSet) -> PushState: """Returns the PushState for further validation.""" interpreter = PushInterpreter(iset) prog = get_program(name, sig, interpreter) assert interpreter.run(prog, inputs) == outputs return interpreter.state
def test_program_relu_2(interpreter: PushInterpreter): prog = load_program("relu_via_if") result = interpreter.run(prog, [-5], ["float"]) assert result == [0.0] result = interpreter.run(prog, [5.6], ["float"]) assert result == [5.6]
def test_program_point_dist(point_instr_set): interpreter = PushInterpreter(point_instr_set) prog = load_program("point_distance", interpreter) interpreter.run(prog, [1.0, 3.0, 3.0, 3.0], ["float"]) assert list(interpreter.state["float"]) == [2.0] interpreter.run(prog, [3.0, 2.5, 3.0, -3.0], ["float"]) assert list(interpreter.state["float"]) == [5.5]
def test_program_relu_2(): interpreter = PushInterpreter(InstructionSet(register_core=True).register_n_inputs(1)) prog = load_program("relu_via_if", interpreter) result = interpreter.run(prog, [-5], ["float"]) assert result == [0.0] result = interpreter.run(prog, [5.6], ["float"]) assert result == [5.6]
def test_interpreter_constraints(push_config: PushConfig, instr_set: InstructionSet): name = "infinite_growth" sig = ProgramSignature(arity=0, output_stacks=["int"], push_config=push_config) interpreter = PushInterpreter(instr_set) cb = load_code(name, interpreter) program = Program(code=cb, signature=sig) output = interpreter.run(program, [0], print_trace=True) assert output[0] == int(push_config.numeric_magnitude_limit) assert interpreter.status == PushInterpreterStatus.step_limit_exceeded
class TestPushInterpreterMethods(unittest.TestCase): def setUp(self): self.i = PushInterpreter() def test_reset(self): self.i.reset() def test_eval_atom_literal(self): self.i.eval_atom(5) self.assertEqual(self.i.state['_integer'][0], 5) def test_eval_atom_list(self): self.i.eval_atom([1, 2, 3, 4, 5]) self.assertEqual(len(self.i.state['_exec']), 5) def test_eval_push(self): self.i.state['_exec'].push([1, 2, [3, 4], 5]) self.i.eval_push() self.assertEqual(len(self.i.state['_integer']), 5) self.assertEqual(len(self.i.state['_exec']), 0) def test_run(self): outputs = self.i.run([7, 'hello'], inputs=["a", "b", "c"], output_types=['_string']) self.assertEqual(outputs, ['hello'])
def error_func(program): errors = [] for x in range(10): # Create the push interpreter interpreter = PushInterpreter() y_hat = interpreter.run(program, [x], ['_integer'])[0] # Get output if y_hat is None: errors.append(1e5) else: # compare to target output y_target = target_function(x) # calculate error errors.append(abs(y_hat - y_target)) return errors
def iris_error_func(program): error_vec = [] for i in range(X_train.shape[0]): interpreter = PushInterpreter() outputs = interpreter.run(program, X_train[i], ['_float', '_float', '_float']) not_none = [x for x in outputs if x is not None] if len(not_none) == 0: error_vec.append(1000000) else: y_hat = outputs.index(max(not_none)) if y_hat == y_train[i]: error_vec.append(0) else: error_vec.append(1) return error_vec
def error_func(program): errors = [] for x in range(10): # Create the push interpreter and run program interpreter = PushInterpreter() y_hat = interpreter.run(program, inputs=[x], output_types=['_integer'])[0] # Get output if y_hat is not None: # compare to target output target_int = target_function(x) # calculate error errors.append((y_hat - target_int)**2) else: errors.append(1e5) return errors
def error_function(program): errors = [] for case in cases: interpreter = PushInterpreter() outputs = interpreter.run(program, case, ['_boolean', '_boolean', '_boolean']) targets = two_bit_control_shift(case) e = 0 if outputs[0] is None: e += 1e4 elif outputs[0] != targets[0]: e += 1 if outputs[1] is None: e += 1e4 elif outputs[1] != targets[1]: e += 1 if outputs[2] is None: e += 1e4 elif outputs[2] != targets[2]: e += 1 errors.append(e) return errors
def __init__(self, interpreter: PushInterpreter = "default", penalty: float = 1e6): self.penalty = penalty if interpreter == "default": self.interpreter = PushInterpreter() else: self.interpreter = interpreter
def __init__(self, spawner: GeneSpawner, search: str = "GA", selector: Union[sl.Selector, str] = "lexicase", variation_strategy: Union[vr.VariationStrategy, dict, str] = "umad", population_size: int = 300, max_generations: int = 100, initial_genome_size: Tuple[int, int] = (20, 100), simplification_steps: int = 2000, last_str_from_stdout: bool = False, interpreter: PushInterpreter = "default", parallelism: Union[int, bool] = False, push_config: PushConfig = "default", verbose: int = 0, **kwargs): self._search_name = search self.spawner = spawner self.selector = selector self.variation_strategy = variation_strategy self.population_size = population_size self.max_generations = max_generations self.initial_genome_size = initial_genome_size self.simplification_steps = simplification_steps self.last_str_from_stdout = last_str_from_stdout self.parallelism = parallelism self.verbose = verbose self.ext = kwargs set_verbosity(self.verbose) # Initialize attributes that will be set later. self.evaluator = None self.signature = None self.search = None self.solution = None if interpreter == "default": self.interpreter = PushInterpreter() else: self.interpreter = interpreter if push_config == "default": self.push_config = PushConfig() else: self.push_config = push_config
def odd_error_func(program, debug=False): errors = [] for i in range(10): # Create the push interpreter interpreter = PushInterpreter() interpreter.state['_integer'].push(i) # Run program y_hat = interpreter.run(program, [i], ['_boolean'], debug)[0] # Get output if y_hat is None: errors.append(1e5) else: # compare to target output y = bool(i % 2) if y_hat == y: errors.append(0) else: errors.append(1) return errors
def error_function(program, debug=False): errors = [] for case in training_set: interpreter = PushInterpreter() result = interpreter.run(program, [case[0]], ['_integer'], debug)[0] if result is None: errors.append(1e5) else: errors.append((case[1] - result)**2) return errors
def test_point_distance(self, push_config: PushConfig, point_instr_set: InstructionSet): name = "point_distance" sig = ProgramSignature(arity=4, output_stacks=["float"], push_config=push_config) interpreter = PushInterpreter(point_instr_set) prog = get_program(name, sig, interpreter) assert prog.pretty_str( ) == "(input_0 input_1 point_from_floats input_2 input_3 point_from_floats point_dist)"
def error_function(program): errors = [] for case in cases: interpreter = PushInterpreter() output = interpreter.run(program, [case], ['_vector_boolean'])[0] if output is None: errors.append(1e5) else: target = binary_decrement(case) errors.append(levenshtein_distance(output, target)) return errors
def error_function(program, debug=False): errors = [] for case in training_set: interpreter = PushInterpreter() result = interpreter.run(program, [case[0]], ['_vector_integer'], debug)[0] if result is None: errors.append(1e5) else: errors.append(levenshtein_distance(case[1], result)) return errors
def string_error_func(program): inputs = [ "abcde", "", "E", "Hi", "Tom", "leprechaun", "zoomzoomzoom", "qwertyuiopasd", "GallopTrotCanter", "Quinona", "_abc" ] errors = [] for inpt in inputs: # Create the push interpreter interpreter = PushInterpreter() y_hat = interpreter.run(program, [inpt], ['_string'])[0] if y_hat is None: errors.append(1e5) else: # compare to target output target_output = inpt[:-2] + inpt[:-2] errors.append( string_difference(y_hat, target_output) + string_char_counts_difference(y_hat, target_output)) return errors
def error_function(program, debug=False): errors = [] for case in training_set: interpreter = PushInterpreter() interpreter.run(program, case[0], [], debug) result = interpreter.state.stdout if result is None: errors.append(1e5) else: errors.append(int(result != case[1])) return errors
def test_instructions(core_type_lib, push_config): debug = False interp = PushInterpreter() iset = interp.instruction_set for spec in SPECS: in_state = PushState.from_dict(spec["in"], core_type_lib, push_config) ex_state = PushState.from_dict(spec["ex"], core_type_lib, push_config) interp.state = in_state instruction_name = spec["instr"] if debug: print(instruction_name,) in_state.pretty_print() print("---") ex_state.pretty_print() print("---") interp.evaluate_atom(iset[instruction_name].meta(), push_config) ac_state = interp.state if debug: ac_state.pretty_print() print() assert ex_state == ac_state
def test_program_fibonacci(): interpreter = PushInterpreter(InstructionSet(register_core=True).register_n_inputs(1)) prog = load_program("fibonacci", interpreter) interpreter.run(prog, [5], []) assert list(interpreter.state["int"]) == [1, 1, 2, 3, 5] interpreter.run(prog, [1], []) assert list(interpreter.state["int"]) == [1] interpreter.run(prog, [-3], []) assert list(interpreter.state["int"]) == []
def error_function(program, debug=False): errors = [] for case in cases: interpreter = PushInterpreter() output = interpreter.run(program, [case], ['_vector_boolean'], debug)[0] target = invert_bitstring(case) if output is None: errors.append(1e5) if not len(output) == len(target): errors.append(1e4) else: errors.append(levenshtein_distance(output, target)) return errors
def error_function(program, debug=False): errors = [] for case in training_set: interpreter = PushInterpreter() result = interpreter.run(program, case[0], ['_vector_integer'], debug)[0] if result is None: errors.append(1e5) else: e = [] for i in range(min(len(result), len(case[1]))): e.append(abs(result[i] - case[1][i])) e.append(abs(len(result) - len(case[1])) * 100) errors.append(sum(e))
def error_function(program): errors = [] for case in cases: interpreter = PushInterpreter() output = interpreter.run(program, [case], ['_vector_integer'])[0] target = [x - 1 for x in case] if output is None: errors.append(1e5) elif len(output) != len(target): errors.append(1e4) else: rmse = np.linalg.norm(np.array(output) - np.array(target)) / np.sqrt(len(output)) errors.append(rmse) return errors
def test_program_fibonacci(interpreter: PushInterpreter): prog = load_program("fibonacci") interpreter.run(prog, [5], []) assert list(interpreter.state["int"]) == [1, 1, 2, 3, 5] interpreter.run(prog, [1], []) assert list(interpreter.state["int"]) == [1] interpreter.run(prog, [-3], []) assert list(interpreter.state["int"]) == []
def test_program_rswn(): interpreter = PushInterpreter(InstructionSet(register_core=True).register_n_inputs(1)) prog = load_program("replace_space_with_newline", interpreter) interpreter.run(prog, ["hello world"], []) assert list(interpreter.state["int"]) == [10] assert interpreter.state.stdout == "hello\nworld" interpreter.run(prog, ["nospace"], []) assert list(interpreter.state["int"]) == [7] assert interpreter.state.stdout == "nospace" interpreter.run(prog, [" "], []) assert list(interpreter.state["int"]) == [0] assert interpreter.state.stdout == "\n\n\n"
def test_inputs(core_type_lib, push_config): interp = PushInterpreter() in_state = PushState.from_dict({"inputs": [7, "x"], "int": []}, core_type_lib, push_config) ex_state = PushState.from_dict({"inputs": [7, "x"], "int": [7]}, core_type_lib, push_config) interp.state = in_state interp.evaluate_atom(Input(input_index=0), push_config) ac_state = interp.state assert ex_state == ac_state assert len(in_state.inputs) == 2 in_state = PushState.from_dict({"inputs": [7, "x"], "str": []}, core_type_lib, push_config) ex_state = PushState.from_dict({"inputs": [7, "x"], "str": ["x"]}, core_type_lib, push_config) interp.state = in_state interp.evaluate_atom(Input(input_index=1), push_config) ac_state = interp.state assert ex_state == ac_state assert len(in_state.inputs) == 2
def setUp(self): self.i = PushInterpreter() self.d = { '_auxiliary': [], '_boolean': [], '_char': [], '_code': [], '_exec': [], '_float': [], '_input': ['a', 'b', 'c'], '_integer': [], '_string': [], '_vector_boolean': [], '_vector_float': [], '_vector_integer': [], '_vector_string': [] } self.i.state.from_dict(self.d)
def test_program_rswn(interpreter: PushInterpreter): prog = load_program("replace_space_with_newline") interpreter.run(prog, ["hello world"], []) assert list(interpreter.state["int"]) == [10] assert interpreter.state.stdout == "hello\nworld" interpreter.run(prog, ["nospace"], []) assert list(interpreter.state["int"]) == [7] assert interpreter.state.stdout == "nospace" interpreter.run(prog, [" "], []) assert list(interpreter.state["int"]) == [0] assert interpreter.state.stdout == "\n\n\n"
def error_function(program): errors = [] for case in cases: interpreter = PushInterpreter() output = interpreter.run(program, case, ['_boolean', '_boolean']) targets = full_adder(case) e = 0 if output[0] is None: e += 1000 elif output[0] != targets[0]: e += 1 if output[1] is None: e += 1000 elif output[1] != targets[1]: e += 1 errors.append(e) return errors
def test_estimator_with_custom_types(point_cls, point_instr_set): X = np.arange(-1.0, 1.0, 0.05).reshape(-1, 4) y = [[point_distance(point_cls(x[0], x[1]), point_cls(x[2], x[3]))] for x in X] spawner = GeneSpawner(n_inputs=1, instruction_set=point_instr_set, literals=[], erc_generators=[]) est = PushEstimator( spawner=spawner, population_size=30, max_generations=3, simplification_steps=2, interpreter=PushInterpreter(point_instr_set), ) est.fit(X, y) assert isinstance(est.solution, Individual) assert len(est.solution.program.code) > 0
def test_genome_point_dist(point_instr_set): check_translation("point_distance", PushInterpreter(point_instr_set))
class PushEstimator: """Simple estimator that synthesizes Push programs. Parameters ---------- spawner : Union[GeneSpawner, str], optional The GeneSpawner to use when producing Genomes during initialization and variation. Default is all core instructions, no literals, and no ERC Generators. search : Union[SearchAlgorithm, str], optional The search algorithm, or its abbreviation, to use to when synthesizing Push programs. selector : Union[Selector, str], optional The selector, or name of selector, to use when selecting parents. The default is lexicase selection. variation_strategy : Union[VariationStrategy, dict, str] A VariationStrategy describing a collection of VariationOperators and how frequently to use them. If a dict is supplied, keys should be operator names and values should be the probability distribution. If a string is provided, the VariationOperators with that name will always be used. Default is ``"umad""``. population_size : int, optional The number of individuals hold in the population each generation. Default is 300. max_generations : int, optional The number of generations to run the search algorithm. Default is 100. initial_genome_size : Tuple[int, int], optional The range of genome sizes to produce during initialization. Default is (20, 100) simplification_steps : int The number of simplification iterations to apply to the best Push program produced by the search algorithm. Default 2000. interpreter : PushInterpreter, optional The PushInterpreter to use when making predictions. Also holds the instruction set to use parallelism : Union[Int, bool], optional Set the number of processes to spawn for use when performing embarrassingly parallel tasks. If false, no processes will spawn and compuation will be serial. Default is true, which spawns one process per available cpu. verbose : int, optional Indicates if verbose printing should be used during searching. Default is 0. Options are 0, 1, or 2. **kwargs Arbitrary keyword arguments. Examples of supported arguments are `epsilon` (bool or float) when using Lexicase as the selector, and `tournament_size` (int) when using tournament selection. """ def __init__(self, spawner: GeneSpawner, search: str = "GA", selector: Union[sl.Selector, str] = "lexicase", variation_strategy: Union[vr.VariationStrategy, dict, str] = "umad", population_size: int = 300, max_generations: int = 100, initial_genome_size: Tuple[int, int] = (20, 100), simplification_steps: int = 2000, last_str_from_stdout: bool = False, interpreter: PushInterpreter = "default", parallelism: Union[int, bool] = False, push_config: PushConfig = "default", verbose: int = 0, **kwargs): self._search_name = search self.spawner = spawner self.selector = selector self.variation_strategy = variation_strategy self.population_size = population_size self.max_generations = max_generations self.initial_genome_size = initial_genome_size self.simplification_steps = simplification_steps self.last_str_from_stdout = last_str_from_stdout self.parallelism = parallelism self.verbose = verbose self.ext = kwargs set_verbosity(self.verbose) # Initialize attributes that will be set later. self.evaluator = None self.signature = None self.search = None self.solution = None if interpreter == "default": self.interpreter = PushInterpreter() else: self.interpreter = interpreter if push_config == "default": self.push_config = PushConfig() else: self.push_config = push_config def _build_search_algo(self): if isinstance(self.variation_strategy, dict): var_strat = vr.VariationStrategy() for op_name, prob in self.variation_strategy.items(): var_op = vr.get_variation_operator(op_name) var_strat.add(var_op, prob) self.variation_strategy = var_strat search_config = sr.SearchConfiguration( signature=self.signature, spawner=self.spawner, evaluator=self.evaluator, selection=self.selector, variation=self.variation_strategy, population_size=self.population_size, max_generations=self.max_generations, initial_genome_size=self.initial_genome_size, simplification_steps=self.simplification_steps, parallelism=self.parallelism, push_config=self.push_config) self.search = sr.get_search_algo(self._search_name, config=search_config, **self.ext) @tap def fit(self, X, y): """Run the search algorithm to synthesize a push program. Parameters ---------- X : pandas dataframe of shape = [n_samples, n_features] The training input samples. y : list, array-like, or pandas dataframe. The target values (class labels in classification, real numbers in regression). Shape = [n_samples] or [n_samples, n_outputs] """ # arity is the number of inputs that the program takes. X, y, arity, y_types = check_X_y(X, y) output_types = [ self.interpreter.type_library.push_type_for_type(t).name for t in y_types ] if self.last_str_from_stdout: ndx = list_rindex(output_types, "str") if ndx is not None: output_types[ndx] = "stdout" self.signature = ProgramSignature(arity=arity, output_stacks=output_types, push_config=self.push_config) self.evaluator = DatasetEvaluator(X, y, interpreter=self.interpreter) self._build_search_algo() self.solution = self.search.run() self.search.config.tear_down() def predict(self, X): """Execute the synthesized push program on a dataset. Parameters ---------- X : pandas dataframe of shape = [n_samples, n_features] The set of cases to predict. Returns ------- y_hat : pandas dataframe of shape = [n_samples, n_outputs] """ check_is_fitted(self, "solution") return [ self.interpreter.run(self.solution.program, inputs) for inputs in X ] def score(self, X, y): """Run the search algorithm to synthesize a push program. Parameters ---------- X : pandas dataframe of shape = [n_samples, n_features] The training input samples. y : list, array-like, or pandas dataframe. The target values (class labels in classification, real numbers in regression). Shape = [n_samples] or [n_samples, n_outputs] """ check_is_fitted(self, "solution") X, y, arity, y_types = check_X_y(X, y) self.evaluator = DatasetEvaluator(X, y, interpreter=self.interpreter) return self.evaluator.evaluate(self.solution.program) def save(self, filepath: str): """Load the found solution to a JSON file. Parameters ---------- filepath Filepath to write the serialized search result to. """ check_is_fitted(self, "solution") self.solution.save(filepath) def load(self, filepath: str): """Load a found solution from a JSON file. Parameters ---------- filepath Filepath to read the serialized search result from. """ self.solution = Individual.load(filepath)
.register(point_distance_insrt) .register(point_from_floats_instr) ) print(instruction_set.keys()) spawner = GeneSpawner( n_inputs=2, instruction_set=instruction_set, literals=[2.0], erc_generators=[] ) # Our estimator with a custom interpreter defined. est = PushEstimator( spawner=spawner, population_size=300, max_generations=20, simplification_steps=500, interpreter=PushInterpreter(instruction_set), verbose=2 ) if __name__ == "__main__": est.fit(X, y) print(est.solution.program) print(est.predict(X)) print(est.score(X, y))