def setUp(self): self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION") term_node_1 = Node(NodeType.CONSTANT, value=0.0) term_node_2 = Node(NodeType.CONSTANT, value=1.0) func_node_1 = Node(NodeType.FUNCTION, name="ADD", arity=2, branches=[term_node_1, term_node_2]) term_node_3 = Node(NodeType.CONSTANT, value=2.0) term_node_4 = Node(NodeType.INPUT, name="x") func_node_2 = Node(NodeType.FUNCTION, name="MUL", arity=2, branches=[term_node_3, term_node_4]) self.tree = Node(NodeType.FUNCTION, name="DIV", arity=2, branches=[func_node_1, func_node_2])
def setUp(self): self.config = { "max_population": 50, "tree_generation": { "method": "FULL_METHOD", "initial_max_depth": 4 }, "evaluator": { "use_cache": True }, "function_nodes": [ {"type": "FUNCTION", "name": "ADD", "arity": 2}, {"type": "FUNCTION", "name": "SUB", "arity": 2}, {"type": "FUNCTION", "name": "MUL", "arity": 2}, {"type": "FUNCTION", "name": "DIV", "arity": 2}, {"type": "FUNCTION", "name": "COS", "arity": 1}, {"type": "FUNCTION", "name": "SIN", "arity": 1} ], "terminal_nodes": [ {"type": "CONSTANT", "value": 1.0}, ], "input_variables": [ {"type": "INPUT", "name": "x"} ], "data_file": "tests/data/sine.dat", "response_variables": [{"name": "y"}] } config.load_data(self.config) self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION") self.generator = TreeGenerator(self.config)
import json import signal sys.path.append(os.path.join(os.path.dirname(__file__), "../..")) from flask import Flask from flask import request from flask import jsonify from flask import render_template from playground.gp.tree.evaluation import evaluate from playground.gp.tree.generator import TreeGenerator from playground.gp.functions import GPFunctionRegistry # GLOBAL VARS app = Flask(__name__) functions = GPFunctionRegistry("SYMBOLIC_REGRESSION") evaluate = evaluate class PlayNodeType(object): EVALUATOR = "EVALUATOR" MONITOR = "MONITOR" class PlayNodeStatus(object): OK = "OK" BUSY = "BUSY" SHUTDOWN = "SHUTDOWN" UNDEFINED = "UNDEFINED" ERROR = "ERROR"
def setUp(self): self.config = { "tree_generation": { "method": "GROW_METHOD", "initial_max_depth": 4 }, "mutation": { "methods": [ "POINT_MUTATION", "HOIST_MUTATION", "SUBTREE_MUTATION", "SHRINK_MUTATION", "EXPAND_MUTATION" ], "probability": 1.0 }, "function_nodes": [{ "type": "FUNCTION", "name": "ADD", "arity": 2 }, { "type": "FUNCTION", "name": "SUB", "arity": 2 }, { "type": "FUNCTION", "name": "MUL", "arity": 2 }, { "type": "FUNCTION", "name": "DIV", "arity": 2 }, { "type": "FUNCTION", "name": "COS", "arity": 1 }, { "type": "FUNCTION", "name": "SIN", "arity": 1 }, { "type": "FUNCTION", "name": "RAD", "arity": 1 }], "terminal_nodes": [{ "type": "CONSTANT", "value": 1.0 }, { "type": "CONSTANT", "value": 2.0 }, { "type": "INPUT", "name": "x" }], "input_variables": [{ "type": "INPUT", "name": "x" }] } self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION") self.generator = TreeGenerator(self.config) self.parser = TreeParser() self.mutation = TreeMutation(self.config) # create nodes left_node = Node(NodeType.CONSTANT, value=1.0) right_node = Node(NodeType.INPUT, name="x") cos_func = Node(NodeType.FUNCTION, name="COS", arity=1, branches=[left_node]) sin_func = Node(NodeType.FUNCTION, name="SIN", arity=1, branches=[right_node]) add_func = Node(NodeType.FUNCTION, name="ADD", arity=2, branches=[cos_func, sin_func]) # create tree self.tree = Tree() self.tree.root = add_func self.tree.update_program() self.tree.update_func_nodes() self.tree.update_term_nodes()
"data/iris.dat", "input_variables": [{ "name": "sepal_length" }, { "name": "sepal_width" }, { "name": "petal_length" }, { "name": "petal_width" }], "response_variables": [{ "name": "species" }] } load_data(config, script_path) functions = GPFunctionRegistry("CLASSIFICATION") generator = TreeGenerator(config) # genetic operators selection = Selection(config) crossover = TreeCrossover(config) mutation = TreeMutation(config) # run symbolic regression population = generator.init() start_time = time.time() details = play.play_details( population=population, evaluate=evaluate, functions=functions,
def setUp(self): self.config = { "tree_generation": { "initial_max_depth": 4 }, "crossover": { "method": "POINT_CROSSOVER", "probability": 1.0 }, "function_nodes": [{ "type": "FUNCTION", "name": "ADD", "arity": 2 }, { "type": "FUNCTION", "name": "SUB", "arity": 2 }, { "type": "FUNCTION", "name": "MUL", "arity": 2 }, { "type": "FUNCTION", "name": "DIV", "arity": 2 }, { "type": "FUNCTION", "name": "COS", "arity": 1 }, { "type": "FUNCTION", "name": "SIN", "arity": 1 }, { "type": "FUNCTION", "name": "RAD", "arity": 1 }], "terminal_nodes": [{ "type": "CONSTANT", "value": 1.0 }, { "type": "CONSTANT", "value": 2.0 }, { "type": "CONSTANT", "value": 2.0 }, { "type": "CONSTANT", "value": 3.0 }, { "type": "CONSTANT", "value": 4.0 }, { "type": "CONSTANT", "value": 5.0 }, { "type": "CONSTANT", "value": 6.0 }, { "type": "CONSTANT", "value": 7.0 }, { "type": "CONSTANT", "value": 8.0 }, { "type": "CONSTANT", "value": 9.0 }, { "type": "CONSTANT", "value": 10.0 }], "input_variables": [{ "type": "INPUT", "name": "x" }] } self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION") self.generator = TreeGenerator(self.config) self.crossover = TreeCrossover(self.config) self.parser = TreeParser() # create nodes left_node_1 = Node(NodeType.INPUT, name="x") right_node_1 = Node(NodeType.CONSTANT, value=2.0) node = Node(NodeType.CONSTANT, value=2.0) left_node_2 = Node(NodeType.CONSTANT, value=3.0) right_node_2 = Node(NodeType.CONSTANT, value=4.0) cos_func_1 = Node(NodeType.FUNCTION, name="ADD", arity=2, branches=[left_node_1, right_node_1]) sin_func_1 = Node(NodeType.FUNCTION, name="SIN", arity=1, branches=[node]) cos_func_2 = Node(NodeType.FUNCTION, name="COS", arity=1, branches=[left_node_2]) sin_func_2 = Node(NodeType.FUNCTION, name="SIN", arity=1, branches=[right_node_2]) add_func = Node(NodeType.FUNCTION, name="ADD", arity=2, branches=[cos_func_1, sin_func_1]) sub_func = Node(NodeType.FUNCTION, name="SUB", arity=2, branches=[sin_func_2, cos_func_2]) # create tree_1 self.tree_1 = Tree() self.tree_1.root = add_func self.tree_1.update() print self.tree_1 # create tree_2 self.tree_2 = Tree() self.tree_2.root = sub_func self.tree_2.update()
def setUp(self): self.config = { "max_population": 10, "tree_generation": { "method": "FULL_METHOD", "initial_max_depth": 4 }, "evaluator": { "use_cache": True }, "selection": { "method": "TOURNAMENT_SELECTION", "tournament_size": 2 }, "crossover": { "method": "POINT_CROSSOVER", "probability": 0.6 }, "mutation": { "methods": ["POINT_MUTATION"], "probability": 0.8 }, "function_nodes": [{ "type": "FUNCTION", "name": "ADD", "arity": 2 }, { "type": "FUNCTION", "name": "SUB", "arity": 2 }], "terminal_nodes": [ { "type": "CONSTANT", "value": 1.0 }, ], "input_variables": [{ "type": "INPUT", "name": "x" }], "data_file": "tests/data/sine.dat", "response_variables": [{ "name": "y" }], "recorder": { "store_file": "json_store_test.json", "compress": True } } config.load_data(self.config) self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION") self.generator = TreeGenerator(self.config) self.json_store = JSONStore(self.config) self.json_store.setup_store() self.population = self.generator.init() results = [] cache = {} evaluate(self.population.individuals, self.functions, self.config, results, cache, self.json_store) self.population.sort_individuals() self.selection = Selection(self.config, recorder=self.json_store) self.crossover = TreeCrossover(self.config, recorder=self.json_store) self.mutation = TreeMutation(self.config, recorder=self.json_store)
def gp_predict(train_data, test_data, train_cat, xx, yy): # setup config = { "max_population": 800, "max_generation": 30, "stale_limit": 10, "tree_generation": { "tree_type": "CLASSIFICATION_TREE", "method": "RAMPED_HALF_AND_HALF_METHOD", "depth_ranges": [{ "size": 1, "percentage": 1.0 }] }, "evaluator": { "use_cache": True }, "selection": { "method": "TOURNAMENT_SELECTION", "tournament_size": 100 }, "crossover": { "method": "POINT_CROSSOVER", "probability": 0.8 }, "mutation": { "methods": ["SUBTREE_MUTATION"], "probability": 0.8 }, "function_nodes": [{ "type": "CLASS_FUNCTION", "name": "GREATER_THAN", "arity": 2, "data_range": { "lower_bound": -1.0, "upper_bound": 1.0, "decimal_places": 2, } }, { "type": "CLASS_FUNCTION", "name": "LESS_THAN", "arity": 2, "data_range": { "lower_bound": -1.0, "upper_bound": 1.0, "decimal_places": 2, } }, { "type": "CLASS_FUNCTION", "name": "EQUALS", "arity": 2, "data_range": { "lower_bound": -1.0, "upper_bound": 1.0, "decimal_places": 2 } }], "terminal_nodes": [ { "type": "RANDOM_CONSTANT", "name": "category", "range": [0.0, 1.0] }, ], "class_attributes": ["x", "y"], "input_variables": [{ "name": "x" }, { "name": "y" }], "response_variables": [{ "name": "category" }] } # load data config["data"] = {} config["data"]["rows"] = len(train_data) config["data"]["x"] = [] config["data"]["y"] = [] config["data"]["category"] = train_cat for row in train_data: config["data"]["x"].append(row[0]) config["data"]["y"].append(row[1]) functions = GPFunctionRegistry("CLASSIFICATION") generator = TreeGenerator(config) # genetic operators selection = Selection(config) crossover = TreeCrossover(config) mutation = TreeMutation(config) # run symbolic regression population = generator.init() details = play.play_details( population=population, evaluate=evaluate, functions=functions, selection=selection, crossover=crossover, mutation=mutation, print_func=print_func, stop_func=default_stop_func, config=config, editor=edit_trees, ) play.play(details) best_tree = population.best_individuals[0] # gp_plot_dt(best_tree, True) # load test data config["data"] = {} config["data"]["rows"] = len(test_data) config["data"]["x"] = [] config["data"]["y"] = [] for row in test_data: config["data"]["x"].append(row[0]) config["data"]["y"].append(row[1]) # predict predicted = gp_eval.predict_tree(best_tree, functions, config) # load test data config["data"] = {} config["data"]["rows"] = xx.shape[0] * xx.shape[1] config["data"]["x"] = np.reshape(xx, xx.shape[0] * xx.shape[1]) config["data"]["y"] = np.reshape(yy, yy.shape[0] * yy.shape[1]) contour = gp_eval.predict_tree(best_tree, functions, config) contour = np.array(contour) contour = contour.reshape(xx.shape) return predicted, contour
def setUp(self): self.function = GPFunctionRegistry("SYMBOLIC_REGRESSION")
class FunctionsTests(unittest.TestCase): def setUp(self): self.function = GPFunctionRegistry("SYMBOLIC_REGRESSION") def test_symbolic_regression_functions(self): # ADD func = self.function.get_function("ADD") result = func(1, 2) self.assertEquals(result, 3) self.assertRaises(EvaluationError, func, 0, "A") # SUB func = self.function.get_function("SUB") result = func(2, 1) self.assertEquals(result, -1) self.assertRaises(EvaluationError, func, 0, "A") # MUL func = self.function.get_function("MUL") result = func(2, 1) self.assertEquals(result, 2) self.assertRaises(EvaluationError, func, "B", "A") # DIV func = self.function.get_function("DIV") result = func(4.0, 2.0) self.assertEquals(result, 0.5) self.assertRaises(EvaluationError, func, 0.0, 2.0) # POW func = self.function.get_function("POW") result = func(2.0, 2.0) self.assertEquals(result, 4.0) self.assertRaises(EvaluationError, func, "A", 2.0) # SQ func = self.function.get_function("SQ") result = func(4.0) self.assertEquals(result, 16.0) self.assertRaises(EvaluationError, func, "A") # COS func = self.function.get_function("COS") result = func(0) self.assertEquals(result, 1) self.assertRaises(EvaluationError, func, "A") # SIN func = self.function.get_function("SIN") result = func(0) self.assertEquals(result, 0) self.assertRaises(EvaluationError, func, "A") # RAD func = self.function.get_function("RAD") result = func(0) self.assertEquals(result, 0) self.assertRaises(EvaluationError, func, "A") # LN func = self.function.get_function("LN") result = func(1) self.assertEquals(result, 0.0) self.assertRaises(EvaluationError, func, "A") # LOG func = self.function.get_function("LOG") result = func(1) self.assertEquals(result, 0.0) self.assertRaises(EvaluationError, func, "A") # EXP func = self.function.get_function("EXP") result = func(0) self.assertEquals(result, 1.0) self.assertRaises(EvaluationError, func, "A") def test_logic_functions(self): # ADD self.assertEquals(0, funcs.and_function(0, 0)) self.assertEquals(0, funcs.and_function(1, 0)) self.assertEquals(0, funcs.and_function(0, 1)) self.assertEquals(1, funcs.and_function(1, 1)) # OR self.assertEquals(0, funcs.or_function(0, 0)) self.assertEquals(1, funcs.or_function(1, 0)) self.assertEquals(1, funcs.or_function(0, 1)) self.assertEquals(1, funcs.or_function(1, 1)) # NOT self.assertEquals(1, funcs.not_function(0)) self.assertEquals(0, funcs.not_function(1)) # NAND self.assertEquals(1, funcs.nand_function(0, 0)) self.assertEquals(1, funcs.nand_function(1, 0)) self.assertEquals(1, funcs.nand_function(0, 1)) self.assertEquals(0, funcs.nand_function(1, 1)) # NOR self.assertEquals(1, funcs.nor_function(0, 0)) self.assertEquals(0, funcs.nor_function(1, 0)) self.assertEquals(0, funcs.nor_function(0, 1)) self.assertEquals(0, funcs.nor_function(1, 1)) # XOR self.assertEquals(0, funcs.xor_function(0, 0)) self.assertEquals(1, funcs.xor_function(1, 0)) self.assertEquals(1, funcs.xor_function(0, 1)) self.assertEquals(0, funcs.xor_function(1, 1)) # XNOR self.assertEquals(1, funcs.xnor_function(0, 0)) self.assertEquals(0, funcs.xnor_function(1, 0)) self.assertEquals(0, funcs.xnor_function(0, 1)) self.assertEquals(1, funcs.xnor_function(1, 1)) def test_get_function(self): # ADD func = self.function.get_function("ADD") result = func(1, 2) self.assertEquals(result, 3) self.assertRaises(EvaluationError, func, 0, "A") def test_unregister(self): self.function.unregister("ADD") result = self.function.get_function("ADD") self.assertIsNone(result)
def setUp(self): random.seed(10) self.config = { "max_population": 10, "tree_generation": { "method": "FULL_METHOD", "initial_max_depth": 4 }, "function_nodes": [ {"type": "FUNCTION", "name": "ADD", "arity": 2}, {"type": "FUNCTION", "name": "SUB", "arity": 2}, {"type": "FUNCTION", "name": "MUL", "arity": 2}, {"type": "FUNCTION", "name": "DIV", "arity": 2}, {"type": "FUNCTION", "name": "COS", "arity": 1}, {"type": "FUNCTION", "name": "SIN", "arity": 1} ], "terminal_nodes": [ {"type": "CONSTANT", "value": 1.0}, {"type": "INPUT", "name": "x"}, {"type": "INPUT", "name": "y"}, {"type": "INPUT", "name": "z"} ], "input_variables": [ {"name": "x"}, {"name": "y"}, {"name": "z"} ] } self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION") self.generator = TreeGenerator(self.config) self.parser = TreeParser() # create nodes left_node = Node(NodeType.CONSTANT, value=1.0) right_node = Node(NodeType.CONSTANT, value=2.0) cos_func = Node( NodeType.FUNCTION, name="COS", arity=1, branches=[left_node] ) sin_func = Node( NodeType.FUNCTION, name="SIN", arity=1, branches=[right_node] ) add_func = Node( NodeType.FUNCTION, name="ADD", arity=2, branches=[cos_func, sin_func] ) # create tree self.tree = Tree() self.tree.root = add_func self.tree.update_program() self.tree.update_func_nodes() self.tree.update_term_nodes()
def setUp(self): random.seed(0) self.config = { "max_population": 20, "max_generation": 5, "tree_generation": { "method": "GROW_METHOD", "initial_max_depth": 4 }, "evaluator": { "use_cache": True }, "selection": { "method": "TOURNAMENT_SELECTION", "tournament_size": 2 }, "crossover": { "method": "POINT_CROSSOVER", "probability": 0.8 }, "mutation": { "methods": [ "POINT_MUTATION", "HOIST_MUTATION", "SUBTREE_MUTATION", "SHRINK_MUTATION", "EXPAND_MUTATION" ], "probability": 1.0 }, "function_nodes": [{ "type": "FUNCTION", "name": "ADD", "arity": 2 }, { "type": "FUNCTION", "name": "SUB", "arity": 2 }, { "type": "FUNCTION", "name": "MUL", "arity": 2 }, { "type": "FUNCTION", "name": "DIV", "arity": 2 }, { "type": "FUNCTION", "name": "COS", "arity": 1 }, { "type": "FUNCTION", "name": "SIN", "arity": 1 }, { "type": "FUNCTION", "name": "RAD", "arity": 1 }], "terminal_nodes": [{ "type": "CONSTANT", "value": 1.0 }, { "type": "CONSTANT", "value": 2.0 }, { "type": "CONSTANT", "value": 2.0 }, { "type": "CONSTANT", "value": 3.0 }, { "type": "CONSTANT", "value": 4.0 }, { "type": "CONSTANT", "value": 5.0 }, { "type": "CONSTANT", "value": 6.0 }, { "type": "CONSTANT", "value": 7.0 }, { "type": "CONSTANT", "value": 8.0 }, { "type": "CONSTANT", "value": 9.0 }, { "type": "CONSTANT", "value": 10.0 }], "input_variables": [{ "type": "INPUT", "name": "x" }], "data_file": "tests/data/sine.dat", "response_variables": [{ "name": "y" }] } config.load_data(self.config) self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION") self.generator = TreeGenerator(self.config) self.selection = Selection(self.config, recorder=None) self.crossover = TreeCrossover(self.config, recorder=None) self.mutation = TreeMutation(self.config, recorder=None)
"response_variables": [{ "name": "answer" }], "data_file": "arabas_et_al-f1.dat" } config["max_population"] = 100000 load_data(config, data_dir) generator = TreeGenerator(config) population = generator.init() results = [] # TREE EVALUTOR 1 start_time = time.time() tree_eval_1.evaluate(copy.deepcopy(population.individuals), GPFunctionRegistry("SYMBOLIC_REGRESSION"), config, results) end_time = time.time() time_taken = end_time - start_time print "Evaluator 1 took:", str(round(time_taken, 2)) + "s" # TREE EVALUTOR 2 # functions = { # "ADD": "+", # "SUB": "-", # "MUL": "*", # "DIV": "/", # "POW": "**", # "SIN": "math.sin", # "COS": "math.cos", # "RAD": "math.radians",
def setUp(self): self.config = { "max_population": 10, "tree_generation": { "tree_type": "SYMBOLIC_REGRESSION", "method": "RAMPED_HALF_AND_HALF_METHOD", "initial_max_depth": 3 }, "function_nodes": [{ "type": "FUNCTION", "arity": 2, "name": "ADD" }, { "type": "FUNCTION", "arity": 2, "name": "SUB" }, { "type": "FUNCTION", "arity": 2, "name": "MUL" }, { "type": "FUNCTION", "arity": 2, "name": "DIV" }, { "type": "FUNCTION", "arity": 1, "name": "COS" }, { "type": "FUNCTION", "arity": 1, "name": "SIN" }], "terminal_nodes": [{ "type": "CONSTANT", "value": 1.0 }, { "type": "INPUT", "name": "x" }, { "type": "INPUT", "name": "y" }, { "type": "RANDOM_CONSTANT", "data_range": { "upper_bound": 10.0, "lower_bound": -10.0, "decimal_places": 1 } }], "input_variables": [{ "name": "x" }, { "name": "y" }] } self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION") self.generator = TreeGenerator(self.config) self.parser = TreeParser()
def setUp(self): self.config = { "max_population": 10, "tree_generation": { "method": "FULL_METHOD", "initial_max_depth": 3 }, "selection": { "method": "ROULETTE_SELECTION" }, "function_nodes": [{ "type": "FUNCTION", "name": "ADD", "arity": 2 }, { "type": "FUNCTION", "name": "SUB", "arity": 2 }, { "type": "FUNCTION", "name": "MUL", "arity": 2 }, { "type": "FUNCTION", "name": "DIV", "arity": 2 }, { "type": "FUNCTION", "name": "COS", "arity": 1 }, { "type": "FUNCTION", "name": "SIN", "arity": 1 }, { "type": "FUNCTION", "name": "RAD", "arity": 1 }], "terminal_nodes": [{ "type": "CONSTANT", "value": 1.0 }, { "type": "CONSTANT", "value": 2.0 }, { "type": "CONSTANT", "value": 2.0 }, { "type": "CONSTANT", "value": 3.0 }, { "type": "CONSTANT", "value": 4.0 }, { "type": "CONSTANT", "value": 5.0 }, { "type": "CONSTANT", "value": 6.0 }, { "type": "CONSTANT", "value": 7.0 }, { "type": "CONSTANT", "value": 8.0 }, { "type": "CONSTANT", "value": 9.0 }, { "type": "CONSTANT", "value": 10.0 }], "input_variables": [{ "type": "INPUT", "name": "x" }] } self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION") self.generator = TreeGenerator(self.config) self.selection = Selection(self.config) self.population = self.generator.init() # give population random scores for inidividual in self.population.individuals: inidividual.score = random.triangular(1, 100)
def setUp(self): self.config = { "max_population": 5, "tree_generation": { "tree_type": "CLASSIFICATION_TREE", "method": "FULL_METHOD", "initial_max_depth": 2 }, "evaluator": { "use_cache": True }, "function_nodes": [{ "type": "CLASS_FUNCTION", "name": "GREATER_THAN", "arity": 2, "data_range": { "lower_bound": 0.0, "upper_bound": 10.0, "decimal_places": 0, } }, { "type": "CLASS_FUNCTION", "name": "LESS_THAN", "arity": 2, "data_range": { "lower_bound": 0.0, "upper_bound": 10.0, "decimal_places": 0, } }, { "type": "CLASS_FUNCTION", "name": "EQUALS", "arity": 2, "decimal_precision": 2, "data_range": { "lower_bound": 0.0, "upper_bound": 10.0, "decimal_places": 0, } }], "terminal_nodes": [ { "type": "RANDOM_CONSTANT", "name": "species", "range": [1.0, 2.0, 3.0] }, ], "input_variables": [{ "name": "sepal_length" }, { "name": "sepal_width" }, { "name": "petal_length" }, { "name": "petal_width" }], "class_attributes": ["sepal_length", "sepal_width", "petal_length", "petal_width"], "data_file": "tests/data/iris.dat", "response_variables": [{ "name": "species" }] } config.load_data(self.config) self.functions = GPFunctionRegistry("CLASSIFICATION") self.generator = TreeGenerator(self.config) self.population = self.generator.init()