diff --git a/EvolutionaryAlgorithm/EvolutionaryAlgorithm.ipynb b/EvolutionaryAlgorithm/EvolutionaryAlgorithm.ipynb
index bb24db7..b4a0ab9 100644
--- a/EvolutionaryAlgorithm/EvolutionaryAlgorithm.ipynb
+++ b/EvolutionaryAlgorithm/EvolutionaryAlgorithm.ipynb
@@ -91,7 +91,7 @@
"name": "stderr",
"output_type": "stream",
"text": [
- "/home/jonas/.local/lib/python3.7/site-packages/ipykernel_launcher.py:37: TqdmExperimentalWarning:\n",
+ "/home/jonas/.local/lib/python3.7/site-packages/ipykernel_launcher.py:39: TqdmExperimentalWarning:\n",
"\n",
"Using `tqdm.autonotebook.tqdm` in notebook mode. Use `tqdm.tqdm` instead to force console mode (e.g. in jupyter console)\n",
"\n"
@@ -119,6 +119,8 @@
"\n",
"import numpy as np\n",
"\n",
+ "import ActionGroups as AG\n",
+ "\n",
"import plotly.graph_objs as go\n",
"from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot\n",
"from plotly.subplots import make_subplots\n",
@@ -459,14 +461,92 @@
},
{
"cell_type": "code",
- "execution_count": 18,
+ "execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"def prepare_ratio(ing:str):\n",
" keys, values = m_grouped_act.get_backward_adjacent(Ingredient(ing).to_json())\n",
" action_dict = dict(zip(keys,values))\n",
- " return action_dict['prepare'] / action_dict['heat']"
+ " return action_dict['prepare'] / action_dict['heat']\n",
+ "\n",
+ "def random_prepare(ing:str):\n",
+ " \"\"\"\n",
+ " returns randomly a boolean value if ing should be prepared, w.r.t. the prepare_ration function\n",
+ " \"\"\"\n",
+ " \n",
+ " return prepare_ratio(ing) > np.random.normal(0.35,0.1)\n",
+ "\n",
+ "def random_heated(ingredient:str):\n",
+ " action_set, action_weights = m_grouped_base_act.get_backward_adjacent(ingredient)\n",
+ " d = dict(zip(action_set, action_weights))\n",
+ " ratio = 1 - d['prepare'] / d['heat']\n",
+ " \n",
+ " return ratio > np.random.normal(0.65,0.15)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def relative_action_rank(ingredient:str, action:str):\n",
+ " action_set, action_weights = m_base_act.get_backward_adjacent(ingredient)\n",
+ " if action not in action_set or len(action_set) <= 1:\n",
+ " return 0\n",
+ " return 1 - action_set.tolist().index(action) / (len(action_set) - 1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def filter_set_by_group(act_set, act_w, group):\n",
+ " new_act_set = []\n",
+ " new_act_w = []\n",
+ " for i in range(len(act_set)):\n",
+ " if act_set[i] in AG.inverse_groups[group]:\n",
+ " new_act_set.append(act_set[i])\n",
+ " new_act_w.append(act_w[i])\n",
+ " return np.array(new_act_set), np.array(new_act_w)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## better normalized scores:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 31,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def normalized_score(key, matrix):\n",
+ " sum_key = matrix.get_sum(key)\n",
+ " keys, values = matrix.get_adjacent(key)\n",
+ " normalized_values = np.array([(values[i] / matrix.get_sum(keys[i])) * (values[i] / sum_key) for i in range(len(keys))])\n",
+ " sort = np.argsort(-normalized_values)\n",
+ " return keys[sort], normalized_values[sort]\n",
+ "\n",
+ "def forward_normalized_score(key, matrix):\n",
+ " sum_key = matrix.get_fw_sum(key)\n",
+ " keys, values = matrix.get_forward_adjacent(key)\n",
+ " normalized_values = np.array([(values[i] / matrix.get_bw_sum(keys[i])) * (values[i] / sum_key) for i in range(len(keys))])\n",
+ " sort = np.argsort(-normalized_values)\n",
+ " return keys[sort], normalized_values[sort]\n",
+ "\n",
+ "def backward_normalized_score(key, matrix):\n",
+ " sum_key = matrix.get_bw_sum(key)\n",
+ " keys, values = matrix.get_backward_adjacent(key)\n",
+ " normalized_values = np.array([(values[i] / matrix.get_fw_sum(keys[i])) * (values[i] / sum_key) for i in range(len(keys))])\n",
+ " sort = np.argsort(-normalized_values)\n",
+ " return keys[sort], normalized_values[sort]"
]
},
{
@@ -479,7 +559,7 @@
},
{
"cell_type": "code",
- "execution_count": 19,
+ "execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
@@ -681,7 +761,7 @@
},
{
"cell_type": "code",
- "execution_count": 20,
+ "execution_count": 21,
"metadata": {},
"outputs": [],
"source": [
@@ -803,7 +883,7 @@
},
{
"cell_type": "code",
- "execution_count": 21,
+ "execution_count": 22,
"metadata": {},
"outputs": [],
"source": [
@@ -861,7 +941,7 @@
},
{
"cell_type": "code",
- "execution_count": 22,
+ "execution_count": 23,
"metadata": {},
"outputs": [],
"source": [
@@ -919,7 +999,7 @@
},
{
"cell_type": "code",
- "execution_count": 28,
+ "execution_count": 24,
"metadata": {},
"outputs": [],
"source": [
@@ -946,6 +1026,7 @@
" ings_for_acts[a].add(ing)\n",
"\n",
" '''\n",
+ " \n",
" # choose randomly an action for each ingredient by the \"wheel of fortune\" method\n",
" actions_for_ing = {}\n",
" for ing in ingredients:\n",
@@ -1267,6 +1348,441 @@
" return Tree.from_serialization(self.serialize())\n"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ingredients = [\"onion\", \"tomato\", \"rice\", \"salt\"] \n",
+ "main_ingredients = [\"rice\"]\n",
+ "\n",
+ "assert set(main_ingredients).issubset(set(ingredients))\n",
+ "\n",
+ "max_n = 5\n",
+ "wheel_turns = 2\n",
+ "\n",
+ "def does_action_match(ingredient:str, action:str, t = 0.6):\n",
+ " return relative_action_rank(ingredient, action) > t\n",
+ "\n",
+ "\n",
+ "# choose randomly an action for each ingredient by the \"wheel of fortune\" method\n",
+ "actions_for_ing = {}\n",
+ "for ing in ingredients:\n",
+ " actions_for_ing[ing] = set()\n",
+ " action_set, action_weights = m_base_act.get_backward_adjacent(ing)\n",
+ " if random_heated(ing):\n",
+ " #print(action_set)\n",
+ " action_set, action_weights = filter_set_by_group(action_set, action_weights, \"heat\")\n",
+ " #print(action_set)\n",
+ " for i in range(wheel_turns):\n",
+ " if ing in main_ingredients:\n",
+ " # if main ingredient: choose by action probability\n",
+ " w = np.array(list(action_weights), dtype=float)\n",
+ " w *= (1.0 / np.sum(w))\n",
+ " action = np.random.choice(list(action_set), size=1, replace=False, p=w)[0]\n",
+ " else:\n",
+ " # else: choose rank based\n",
+ " action = ea_tools.wheel_of_fortune_selection(action_set[:max_n], action_weights[:max_n])\n",
+ " actions_for_ing[ing].add(action)\n",
+ " #print(f\"action {action} for ing {ing}\")\n",
+ " #print(ing, action)\n",
+ " \n",
+ "# create ingredient nodes:\n",
+ "ingredient_nodes = {}\n",
+ "\n",
+ "# create ingredient nodes:\n",
+ "for ing in ingredients:\n",
+ " new_node = IngredientNode(ing, constant=True)\n",
+ " \n",
+ " # check if we should do a preparation step\n",
+ " if random_prepare(ing):\n",
+ " # choose a preparation cooking action\n",
+ " action_set, action_weights = m_act.get_backward_adjacent(Ingredient(ing).to_json())\n",
+ " action_set, action_weights = filter_set_by_group(action_set, action_weights, \"prepare\")\n",
+ " if len(action_set) > 0:\n",
+ " action = ea_tools.wheel_of_fortune_selection(action_set[:max_n], action_weights[:max_n])\n",
+ " act_node = ActionNode(action)\n",
+ " act_node.add_child(new_node)\n",
+ " new_node = act_node\n",
+ " \n",
+ " \n",
+ " ingredient_nodes[ing] = new_node\n",
+ "\n",
+ "# starting now with the actions found for the main ingredients and try to match all ingredients together\n",
+ "# with that:\n",
+ "\n",
+ "unprocessed_ings = set(filter(lambda x: len(actions_for_ing[x]) > 0, ingredients))\n",
+ "unprocessed_main_ings = set(filter(lambda x: len(actions_for_ing[x]) > 0, main_ingredients))\n",
+ "\n",
+ "while len(unprocessed_main_ings) > 0:\n",
+ " main_ing = unprocessed_main_ings.pop()\n",
+ " \n",
+ " # random action for that ing:\n",
+ " act = actions_for_ing[main_ing].pop()\n",
+ " \n",
+ " act_node = ActionNode(act)\n",
+ " mix_node = MixNode()\n",
+ " mix_node.add_child(ingredient_nodes[main_ing])\n",
+ " act_node.add_child(mix_node)\n",
+ " ingredient_nodes[main_ing] = act_node\n",
+ " \n",
+ " unprocessed_ings.remove(main_ing)\n",
+ " \n",
+ " for ing in unprocessed_ings.copy():\n",
+ " if does_action_match(ing, act):\n",
+ " mix_node.add_child(ingredient_nodes[ing])\n",
+ " ingredient_nodes[ing] = act_node\n",
+ " unprocessed_ings.remove(ing)\n",
+ " if ing in unprocessed_main_ings:\n",
+ " unprocessed_main_ings.remove(ing)\n",
+ " \n",
+ " if len(mix_node.childs()) == 1:\n",
+ " mix_node.remove()\n",
+ "\n",
+ "# now make the same with all remaining ingredients:\n",
+ "while len(unprocessed_ings) > 0:\n",
+ " current_ing = unprocessed_ings.pop() \n",
+ " \n",
+ " # random action for that ing:\n",
+ " act = actions_for_ing[current_ing].pop()\n",
+ " \n",
+ " act_node = ActionNode(act)\n",
+ " mix_node = MixNode()\n",
+ " mix_node.add_child(ingredient_nodes[current_ing])\n",
+ " act_node.add_child(mix_node)\n",
+ " \n",
+ " ingredient_nodes[current_ing] = act_node\n",
+ " \n",
+ " \n",
+ " for ing in unprocessed_ings.copy():\n",
+ " if does_action_match(ing, act):\n",
+ " mix_node.add_child(ingredient_nodes[ing])\n",
+ " ingredient_nodes[ing] = act_node\n",
+ " unprocessed_ings.remove(ing)\n",
+ " \n",
+ " if len(mix_node.childs()) == 1:\n",
+ " mix_node.remove()\n",
+ "\n",
+ "\n",
+ "root_layer = set([n.root() for n in ingredient_nodes.values()])\n",
+ "\n",
+ "root_layer_without_parents = []\n",
+ "for node in root_layer:\n",
+ " if node.parent() is None:\n",
+ " root_layer_without_parents.append(node)\n",
+ "\n",
+ "if len(root_layer_without_parents) == 1:\n",
+ " root_node = root_layer_without_parents[0]\n",
+ "\n",
+ "else:\n",
+ " root_node = MixNode()\n",
+ " for r in root_layer_without_parents:\n",
+ " root_node.add_child(r)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 26,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "root_node.dot()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "constant_ingredients = [\"noodle\", \"onion\", \"tomato\"]\n",
+ "main_ingredients = ['noodle']\n",
+ "min_additional = 3\n",
+ "max_additional = 6\n",
+ "top_ings =3"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['noodle',\n",
+ " 'onion',\n",
+ " 'tomato',\n",
+ " 'salt',\n",
+ " 'cheese',\n",
+ " 'garlic clove',\n",
+ " 'olive oil',\n",
+ " 'mozzarella cheese']"
+ ]
+ },
+ "execution_count": 36,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "seen_items = set(constant_ingredients)\n",
+ "\n",
+ "items = []\n",
+ "scores = []\n",
+ "\n",
+ "assert set(main_ingredients).issubset(set(constant_ingredients))\n",
+ "\n",
+ "# additional ingredients are choosen w.r.t all given ingredients\n",
+ "n_additional_ings = np.random.randint(min_additional, max_additional + 1)\n",
+ "\n",
+ "# extra ings are ingredients choosen specially for the main ingredient\n",
+ "n_extra_ings = int((len(main_ingredients) / len(constant_ingredients)) * n_additional_ings)\n",
+ "\n",
+ "if n_extra_ings > n_additional_ings:\n",
+ " n_extra_ings = n_additional_ings\n",
+ "\n",
+ " \n",
+ "# choose extra ingredients\n",
+ "extra_candidates = []\n",
+ "extra_weights = []\n",
+ "\n",
+ "for ing in main_ingredients:\n",
+ " candidates, weights = normalized_score(ing, m_base_mix)\n",
+ " extra_candidates.append(candidates[:10])\n",
+ " extra_weights.append(weights[:10])\n",
+ "\n",
+ "extra_ingredients = ea_tools.combined_wheel_of_fortune_selection(extra_candidates,\n",
+ " extra_weights,\n",
+ " n_extra_ings)\n",
+ "\n",
+ "for ing in constant_ingredients:\n",
+ " # find best matching ingredients\n",
+ " best_items = []\n",
+ " best_scores = []\n",
+ "\n",
+ " candidates, weights = m_base_mix.get_adjacent(ing)\n",
+ " i = 0\n",
+ " while i < len(candidates) and len(best_items) < top_ings:\n",
+ " if candidates[i] not in seen_items:\n",
+ " best_items.append(candidates[i])\n",
+ " best_scores.append(weights[i])\n",
+ " i += 1\n",
+ "\n",
+ " items.append(best_items)\n",
+ " scores.append(best_scores)\n",
+ "\n",
+ "#TODO: error handling if too few options are availabale!\n",
+ "\n",
+ "additional_ingredients = ea_tools.combined_wheel_of_fortune_selection(items,\n",
+ " scores,\n",
+ " n_additional_ings - n_extra_ings)\n",
+ "list(constant_ingredients) + list(additional_ingredients) + list(extra_ingredients)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(array(['ricotta cheese', 'spaghetti sauce', 'mozzarella cheese', 'cheese',\n",
+ " 'spinach', 'sausage', 'ground beef', 'tomato sauce', 'onion',\n",
+ " 'basil', 'broccoli', 'parsley', 'mushroom soup', 'mushroom',\n",
+ " 'sauce', 'egg', 'garlic clove', 'chicken', 'milk', 'water', 'salt',\n",
+ " 'zucchini', 'tomato', 'pepper', 'seasoning', 'green pepper',\n",
+ " 'shrimp', 'soy sauce', 'butter', 'red pepper', 'oregano',\n",
+ " 'clove garlic', 'olive oil', 'pork', 'carrot', 'green onion',\n",
+ " 'cream cheese', 'garlic', 'chicken broth', 'tablespoon butter',\n",
+ " 'red bell pepper', 'flour', 'cream', 'black pepper',\n",
+ " 'vegetable oil', 'chicken breast', 'sugar'], dtype='\n",
" \n",
" \n",
- " \n",
+ " \n",
"