{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "%env MKL_NUM_THREADS=12\n",
    "%env OMP_NUM_THREADS=12"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import defaultdict\n",
    "\n",
    "import numpy as np\n",
    "import scipy as sp\n",
    "import pandas as pd\n",
    "from ipypb import track\n",
    "\n",
    "from polara.evaluation import evaluation_engine as ee\n",
    "from polara.evaluation.pipelines import random_grid, find_optimal_config\n",
    "\n",
    "from lce import LCEModel, LCEModelItemColdStart\n",
    "from data_preprocessing import (get_yahoo_music_data,\n",
    "                                get_similarity_data,\n",
    "                                prepare_data_model,\n",
    "                                prepare_cold_start_data_model)\n",
    "from utils import (report_results, save_results,\n",
    "                   apply_config, print_data_stats,\n",
    "                   save_training_time, save_cv_training_time)\n",
    "\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from polara.recommender import defaults\n",
    "defaults.memory_hard_limit = 15 # allowed memory usage during recommendations generationa\n",
    "defaults.max_test_workers = 6 # use this manyparallel thread for evaluation each using up to {memory_hard_limit} Gb of RAM"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "seed = 42"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "experiment_name = 'lce'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Experiment setup"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "data_labels = ['YaMus']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "init_config = dict(seed = seed,\n",
    "                   max_iterations = 75,\n",
    "                   alpha = 0.1,\n",
    "                   beta = 0.05,\n",
    "                   max_neighbours=10,\n",
    "                   )\n",
    "lce_init_config = dict.fromkeys(data_labels, {'LCE': init_config,  # standard scenario\n",
    "                                              'LCE(cs)': init_config})  # cold start"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "params = {\n",
    "          'regularization': [1, 3, 10, 30],\n",
    "          'rank': [100] # for initial tuning (exploration)\n",
    "         }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "coeffs = {\n",
    "    'alpha': [0.1, 0.3, 0.5, 0.7, 0.9],\n",
    "    'beta': [0, 0.05, 0.1, 0.3]\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "ranks_grid = [1, 50, 100, 500, 750, 1000, 1500, 2000, 2500, 3000]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "lce_ranks = {'YaMus': ranks_grid}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "topk_values = [1, 3, 10, 20, 30]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "target_metric = 'mrr'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "data_dict = dict.fromkeys(data_labels)\n",
    "meta_dict = dict.fromkeys(data_labels)\n",
    "similarities = dict.fromkeys(data_labels)\n",
    "feature_idx = dict.fromkeys(data_labels)\n",
    "sim_indices = dict.fromkeys(data_labels)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "all_data = [data_dict, similarities, sim_indices, meta_dict]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Yahoo Music"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "lbl = 'YaMus'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "data_dict[lbl], meta_dict[lbl] = get_yahoo_music_data('/data/recsys/yahoo_music/yamus_train0_rating5.gz',\n",
    "                                                      meta_path='/data/recsys/yahoo_music/yamus_attrs.gz',\n",
    "                                                      implicit=True,\n",
    "                                                      pcore=5,\n",
    "                                                      filter_data={'genreid': [0]}, # filter unknown genre\n",
    "                                                      filter_no_meta=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "similarities[lbl], sim_indices[lbl], feature_idx[lbl] = get_similarity_data(meta_dict[lbl])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.0"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(meta_dict[lbl].applymap(len).sum(axis=1)==0).mean()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data stats"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "YaMus\n",
      "{'userid': 183003, 'songid': 134059}\n",
      "density 0.09740952587383789\n",
      "similarity matrix density 0.4576464914574314\n"
     ]
    }
   ],
   "source": [
    "print_data_stats(data_labels, all_data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Standard experiment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "def prepare_recommender_models(data_label, data_models, config):\n",
    "    data_model = data_models[data_label]\n",
    "    lce = LCEModel(data_model, item_features=meta_dict[data_label])\n",
    "    lce.method = 'LCE'\n",
    "    models = [lce]\n",
    "    apply_config(models, config, data_label)\n",
    "    return models\n",
    "\n",
    "def fine_tune_lce(model, params, label, ntrials=60, record_time_as=None):\n",
    "    param_grid, param_names = random_grid(params, n=ntrials)\n",
    "    best_lce_config, lce_scores = find_optimal_config(model, param_grid, param_names,\n",
    "                                                      target_metric,\n",
    "                                                      return_scores=True,\n",
    "                                                      force_build=True,\n",
    "                                                      iterator=lambda x: track(x, label=label))\n",
    "    model_config = {model.method: dict(zip(param_names, best_lce_config))}\n",
    "    model_scores = {model.method: lce_scores}\n",
    "    try:\n",
    "        if record_time_as:\n",
    "            save_training_time(f'{experiment_name}_{record_time_as}', model, lce_scores.index, label)\n",
    "    finally:\n",
    "        return model_config, model_scores"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## tuning"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "config = {}\n",
    "scores = {}\n",
    "data_models = {}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'seed': 42,\n",
       " 'max_iterations': 75,\n",
       " 'alpha': 0.1,\n",
       " 'beta': 0.05,\n",
       " 'max_neighbours': 10}"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "lce_init_config['YaMus']['LCE']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### regularization"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div><span class=\"Text-label\" style=\"display:inline-block; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; min-width:0; max-width:15ex; vertical-align:middle; text-align:right\"></span>\n",
       "<progress style=\"width:60ex\" max=\"1\" value=\"1\" class=\"Progress-main\"/></progress>\n",
       "<span class=\"Progress-label\"><strong>100%</strong></span>\n",
       "<span class=\"Iteration-label\">1/1</span>\n",
       "<span class=\"Time-label\">[47:14<47:14, 2833.77s/it]</span></div>"
      ],
      "text/plain": [
       "\u001b[A\u001b[A\u001b[2K\r",
       " [████████████████████████████████████████████████████████████] 1/1 [47:14<47:14, 2833.77s/it]\u001b[B"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div><span class=\"Text-label\" style=\"display:inline-block; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; min-width:15ex; max-width:15ex; vertical-align:middle; text-align:right\">YaMus</span>\n",
       "<progress style=\"width:45ex\" max=\"4\" value=\"4\" class=\"Progress-main\"/></progress>\n",
       "<span class=\"Progress-label\"><strong>100%</strong></span>\n",
       "<span class=\"Iteration-label\">4/4</span>\n",
       "<span class=\"Time-label\">[46:45<12:04, 701.19s/it]</span></div>"
      ],
      "text/plain": [
       "\u001b[A\u001b[2K\r",
       "          YaMus [█████████████████████████████████████████████] 4/4 [46:45<12:04, 701.19s/it]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "_config = {}\n",
    "_scores = {}\n",
    "for label in track(data_labels):\n",
    "    data_models[label] = prepare_data_model(label, *all_data, seed)\n",
    "    model, = prepare_recommender_models(label, data_models, lce_init_config)\n",
    "    _config[label], _ = fine_tune_lce(model, params, label)\n",
    "del model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'YaMus': {'LCE': {'regularization': 10, 'rank': 40}}}"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "_config # will also reuse it in coldstart"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### $\\alpha, \\beta$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div><span class=\"Text-label\" style=\"display:inline-block; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; min-width:0; max-width:15ex; vertical-align:middle; text-align:right\"></span>\n",
       "<progress style=\"width:60ex\" max=\"1\" value=\"0\" class=\"Progress-main\"/></progress>\n",
       "<span class=\"Progress-label\"><strong>0%</strong></span>\n",
       "<span class=\"Iteration-label\">0/1</span>\n",
       "<span class=\"Time-label\">[0<0, 0.00s/it]</span></div>"
      ],
      "text/plain": [
       "\u001b[2K\r",
       " [############################################################] 0/1 [0<0, 0.00s/it]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div><span class=\"Text-label\" style=\"display:inline-block; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; min-width:15ex; max-width:15ex; vertical-align:middle; text-align:right\">YaMus</span>\n",
       "<progress style=\"width:45ex\" max=\"20\" value=\"2\" class=\"Progress-main\"/></progress>\n",
       "<span class=\"Progress-label\"><strong>10%</strong></span>\n",
       "<span class=\"Iteration-label\">2/20</span>\n",
       "<span class=\"Time-label\">[23:48<11:39, 713.76s/it]</span></div>"
      ],
      "text/plain": [
       "\u001b[A\u001b[2K\r",
       "          YaMus [████#########################################] 2/20 [23:48<11:39, 713.76s/it]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "for label in track(data_labels):\n",
    "    data_models[label] = prepare_data_model(label, *all_data, seed)\n",
    "    model, = prepare_recommender_models(label, data_models, [lce_init_config, _config])\n",
    "    config[label], scores[label] = fine_tune_lce(model, coeffs, label)\n",
    "    # make sure to save all parameters\n",
    "    config[label][model.method].update(_config[label][model.method])\n",
    "del model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "report_results('tuning', scores);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "config"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### saving data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "save_results(f'{experiment_name}_param', config=config, tuning=scores)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## rank estimation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "rank_config = {}\n",
    "rank_scores = {}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for label in track(data_labels):\n",
    "    model, = prepare_recommender_models(label, data_models,\n",
    "                                        [lce_init_config, config]) # initiate with optimal config\n",
    "    rank_config[label], rank_scores[label] = fine_tune_lce(model, {'rank': lce_ranks[label]},\n",
    "                                                          label, ntrials=0, record_time_as='rank')\n",
    "del model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "report_results('rank', {lbl: v.sort_index() for lbl, scr in rank_scores.items() for k, v in scr.items()});"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "rank_config"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### saving data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "save_results(f'{experiment_name}_rank', config=rank_config, tuning=rank_scores)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## cross-validation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "result = {}\n",
    "for label in track(data_labels):\n",
    "    models = prepare_recommender_models(label, data_models, [lce_init_config, config, rank_config])\n",
    "    result[label] = ee.run_cv_experiment(models,\n",
    "                                         fold_experiment=ee.topk_test,\n",
    "                                         topk_list=topk_values,\n",
    "                                         ignore_feedback=True,\n",
    "                                         iterator=lambda x: track(x, label=label))\n",
    "    save_cv_training_time(experiment_name, models, label)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "report_results('topn', result, target_metric);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pd.concat({lbl: res.mean(level='top-n').loc[10, :'ranking'] for lbl, res in result.items()}, axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### saving data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "save_results(experiment_name, cv=result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Cold start"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "import gc\n",
    "del data_models, models\n",
    "gc.collect()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [],
   "source": [
    "def prepare_cold_start_recommender_models(data_label, data_models, config):\n",
    "    data_model = data_models[data_label]\n",
    "    lce = LCEModelItemColdStart(data_model, item_features=meta_dict[data_label])\n",
    "    lce.method = 'LCE(cs)'\n",
    "    models = [lce]\n",
    "    apply_config(models, config, data_label)\n",
    "    return models"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## tuning"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [],
   "source": [
    "config_cold = {}\n",
    "scores_cold = {}\n",
    "data_models_cold = {}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'seed': 42,\n",
       " 'max_iterations': 75,\n",
       " 'alpha': 0.1,\n",
       " 'beta': 0.05,\n",
       " 'max_neighbours': 10}"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "lce_init_config['YaMus']['LCE(cs)']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### $\\alpha, \\beta$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div><span class=\"Text-label\" style=\"display:inline-block; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; min-width:0; max-width:15ex; vertical-align:middle; text-align:right\"></span>\n",
       "<progress style=\"width:60ex\" max=\"1\" value=\"1\" class=\"Progress-main\"/></progress>\n",
       "<span class=\"Progress-label\"><strong>100%</strong></span>\n",
       "<span class=\"Iteration-label\">1/1</span>\n",
       "<span class=\"Time-label\">[03:58:60<03:58:60, 14339.53s/it]</span></div>"
      ],
      "text/plain": [
       "\u001b[A\u001b[A\u001b[2K\r",
       " [████████████████████████████████████████████████████████████] 1/1 [03:58:60<03:58:60, 14339.53s/it]\u001b[B"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div><span class=\"Text-label\" style=\"display:inline-block; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; min-width:15ex; max-width:15ex; vertical-align:middle; text-align:right\">YaMus</span>\n",
       "<progress style=\"width:45ex\" max=\"20\" value=\"20\" class=\"Progress-main\"/></progress>\n",
       "<span class=\"Progress-label\"><strong>100%</strong></span>\n",
       "<span class=\"Iteration-label\">20/20</span>\n",
       "<span class=\"Time-label\">[03:58:34<12:07, 715.69s/it]</span></div>"
      ],
      "text/plain": [
       "\u001b[A\u001b[2K\r",
       "          YaMus [█████████████████████████████████████████████] 20/20 [03:58:34<12:07, 715.69s/it]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "_config_cold = {}\n",
    "for label in track(data_labels):\n",
    "    # reuse regularization param from standard scenario\n",
    "    _config_cold[label] = {f'{k}(cs)' if k=='LCE' else k: v for k, v in _config[label].items()}\n",
    "    data_models_cold[label] = prepare_cold_start_data_model(label, *all_data, seed)\n",
    "    model, = prepare_cold_start_recommender_models(label, data_models_cold, [lce_init_config, _config_cold])\n",
    "    config_cold[label], scores_cold[label] = fine_tune_lce(model, coeffs, label, record_time_as=None)\n",
    "    # make sure to save all parameters\n",
    "    config_cold[label][model.method].update(_config_cold[label][model.method])\n",
    "del model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/conda/envs/py36/lib/python3.6/site-packages/pandas/plotting/_core.py:1001: UserWarning: Attempted to set non-positive left xlim on a log-scaled axis.\n",
      "Invalid limit will be ignored.\n",
      "  ax.set_xlim(left, right)\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "report_results('tuning', scores_cold);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'YaMus': {'LCE(cs)': {'alpha': 0.1,\n",
       "   'beta': 0.1,\n",
       "   'regularization': 10,\n",
       "   'rank': 40}}}"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "config_cold"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### saving data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "save_results(f'{experiment_name}_coldstart_param', config=config_cold, tuning=scores_cold)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## rank estimation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "rank_config_cold = {}\n",
    "rank_scores_cold = {}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div><span class=\"Text-label\" style=\"display:inline-block; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; min-width:0; max-width:15ex; vertical-align:middle; text-align:right\"></span>\n",
       "<progress style=\"width:60ex\" max=\"1\" value=\"0\" class=\"Progress-main\"/></progress>\n",
       "<span class=\"Progress-label\"><strong>0%</strong></span>\n",
       "<span class=\"Iteration-label\">0/1</span>\n",
       "<span class=\"Time-label\">[0<0, 0.00s/it]</span></div>"
      ],
      "text/plain": [
       "\u001b[2K\r",
       " [############################################################] 0/1 [0<0, 0.00s/it]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div><span class=\"Text-label\" style=\"display:inline-block; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; min-width:15ex; max-width:15ex; vertical-align:middle; text-align:right\">YaMus</span>\n",
       "<progress style=\"width:45ex\" max=\"10\" value=\"1\" class=\"Progress-main\"/></progress>\n",
       "<span class=\"Progress-label\"><strong>10%</strong></span>\n",
       "<span class=\"Iteration-label\">1/10</span>\n",
       "<span class=\"Time-label\">[06:43<06:43, 402.93s/it]</span></div>"
      ],
      "text/plain": [
       "\u001b[A\u001b[2K\r",
       "          YaMus [████#########################################] 1/10 [06:43<06:43, 402.93s/it]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "for label in track(data_labels):\n",
    "    model, = prepare_cold_start_recommender_models(label, data_models_cold,\n",
    "                                                   [lce_init_config, config_cold]) # initiate with optimal config\n",
    "    rank_config_cold[label], rank_scores_cold[label] = fine_tune_lce(model, {'rank': lce_ranks[label]},\n",
    "                                                                     label, ntrials=0)\n",
    "del model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "report_results('rank', {lbl: v.sort_index() for lbl, scr in rank_scores_cold.items() for k, v in scr.items()});"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "rank_config_cold"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### saving data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "save_results(f'{experiment_name}_coldstart_rank', config=rank_config_cold, tuning=rank_scores_cold)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## cross validation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "result_cold = {}\n",
    "for label in track(data_labels):\n",
    "    models_cold = prepare_cold_start_recommender_models(label, data_models_cold,\n",
    "                                                        [lce_init_config, config_cold, rank_config_cold])\n",
    "    result_cold[label] = ee.run_cv_experiment(models_cold,\n",
    "                                              fold_experiment=ee.topk_test,\n",
    "                                              topk_list=topk_values,\n",
    "                                              ignore_feedback=True,\n",
    "                                              iterator=lambda x: track(x, label=label))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "report_results('topn', result_cold, target_metric);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "report_results('topn', result_cold, 'coverage');"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### saving data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "save_results(f'{experiment_name}_coldstart', cv=result_cold)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}