{"cells":[{"metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nimport tensorflow.keras as ks\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Dense, Dropout, Conv2D, MaxPool2D, Conv2DTranspose, BatchNormalization\nfrom tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint\n\nimport torch\nfrom torch import FloatTensor\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%load_ext Cython","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"%%cython\ncimport cython\nimport numpy as np\n\n@cython.cdivision(True)\n@cython.boundscheck(False)\n@cython.nonecheck(False)\n@cython.wraparound(False)\ncdef int calc_neighs(unsigned char[:, :] field, int i, int j, int n, int k):\n    cdef:\n        int neighs = 0;\n        int i_min = i - 1;\n        int i_pl = i + 1;\n        int j_min = j - 1;\n        int j_pl = j + 1;\n    neighs = 0\n    if i_min >= 0:\n        if j_min >= 0:\n            neighs += field[i_min, j_min]\n        neighs += field[i_min, j]\n        if j_pl < k:\n            neighs += field[i_min, j_pl]\n    if j_min >= 0:\n        neighs += field[i, j_min]\n    if j_pl < k:\n        neighs += field[i, j_pl]\n    if i_pl < n:\n        if j_min >= 0:\n            neighs += field[i_pl, j_min]\n        neighs += field[i_pl, j]\n        if j_pl < k:\n            neighs += field[i_pl, j_pl]\n    return neighs\n\n@cython.cdivision(True)\n@cython.boundscheck(False)\n@cython.nonecheck(False)\n@cython.wraparound(False)\ncpdef make_move(unsigned char[:, :] field, int moves):\n    cdef:\n        int _, i, j, neighs;\n        int n, k;\n        int switch = 0;\n        unsigned char[:, :] cur_field;\n        unsigned char[:, :] next_field;\n    cur_field = np.copy(field)\n    next_field = np.zeros_like(field, 'uint8')\n    n = field.shape[0]\n    k = field.shape[1]\n    for _ in range(moves):\n        if switch == 0:\n            for i in range(n):\n                for j in range(k):\n                    neighs = calc_neighs(cur_field, i, j, n, k)\n                    if cur_field[i, j] and neighs == 2:\n                        next_field[i, j] = 1\n                    elif neighs == 3:\n                        next_field[i, j] = 1\n                    else:\n                        next_field[i, j] = 0\n        else:\n            for i in range(n):\n                for j in range(k):\n                    neighs = calc_neighs(next_field, i, j, n, k)\n                    if next_field[i, j] and neighs == 2:\n                        cur_field[i, j] = 1\n                    elif neighs == 3:\n                        cur_field[i, j] = 1\n                    else:\n                        cur_field[i, j] = 0\n        switch = (switch + 1) % 2\n    return np.array(next_field if switch else cur_field)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"NROW, NCOL = 25, 25\n\ndef generate_samples(delta=1, n=32):\n    \"\"\"\n    Generate batch of samples\n    \n    @return: (end_frames, start_frames)\n    \"\"\"\n    batch = np.split(np.random.binomial(1, 0.5, (NROW * n, NCOL)).astype('uint8'), n)\n    Yy = [life.make_move(state, 5) for state in batch]\n    Xx = [life.make_move(state, 1) for state in Yy]\n    Y = np.array([y.ravel() for y in Yy])\n    X = np.array([x.ravel() for x in Xx])\n    return X, Y\n    \n\ndef data_generator(delta=1, batch_size=32, ravel=True):\n    \"\"\"\n    Can be used along with .fit_generator to generate training samples on the fly\n    \"\"\"\n    while True:\n        batch = np.split(np.random.binomial(1, 0.5, (NROW * batch_size, NCOL)).astype('uint8'), batch_size)\n        Yy = [make_move(state, 5) for state in batch]\n        Xx = [make_move(state, delta) for state in Yy]\n\n        if ravel:\n            Y = np.array([y.ravel() for y in Yy])\n            X = np.array([x.ravel() for x in Xx])\n            yield X, Y\n        else:\n            yield np.array(Xx)[:,:, :, np.newaxis], np.array(Yy)[:, :, :, np.newaxis]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def create_model(n_hidden_convs=2, n_hidden_filters=128, kernel_size=5):\n    nn = Sequential()\n    nn.add(Conv2D(n_hidden_filters, kernel_size, padding='same', activation='relu', input_shape=(25, 25, 1)))\n    nn.add(BatchNormalization())\n    for i in range(n_hidden_convs):\n        nn.add(Conv2D(n_hidden_filters, kernel_size, padding='same', activation='relu'))\n        nn.add(BatchNormalization())\n    nn.add(Conv2D(1, kernel_size, padding='same', activation='sigmoid'))\n    nn.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])\n    return nn\n\ndef model_v2():\n    # This one performs worse then pure Conv2D\n    nn = Sequential()\n    nn.add(Conv2D(128, 5, padding='same', activation=lrelu, input_shape=(25, 25, 1)))\n    nn.add(BatchNormalization())\n    nn.add(Conv2D(128, 5, padding='valid', activation=lrelu))\n    nn.add(BatchNormalization())\n    nn.add(MaxPool2D())\n    nn.add(Conv2DTranspose(128, 2, strides=(2, 2), padding='valid', activation=lrelu))\n    nn.add(BatchNormalization())\n    nn.add(Conv2DTranspose(128, 5, strides=(1, 1), padding='valid', activation=lrelu))\n    nn.add(BatchNormalization())\n    nn.add(Conv2D(1, 5, padding='same', activation='sigmoid'))\n    nn.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])\n    return nn","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\n\nmodels = []\nfor delta in range(1, 6):\n    model = create_model(n_hidden_convs=6, n_hidden_filters=256)\n    es = EarlyStopping(monitor='loss', patience=9, min_delta=0.001)\n    model.fit_generator(data_generator(delta=delta, ravel=False), steps_per_epoch=10, epochs=20, verbose=1, callbacks=[es])\n    models.append(model)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df = pd.read_csv('../input/conways-reverse-game-of-life-2020/train.csv', index_col=0)\ntest_df = pd.read_csv('../input/conways-reverse-game-of-life-2020/test.csv', index_col=0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submit_df = pd.DataFrame(index=test_df.index, columns=['start_' + str(_) for _ in range(625)])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\n\nfor delta in range(1, 6):\n    mod = models[delta-1]\n    delta_df = test_df[test_df.delta == delta].iloc[:, 1:].values.reshape(-1, 25, 25, 1)\n    submit_df[test_df.delta == delta] = mod.predict(delta_df).reshape(-1, 625).round(0).astype('uint8')","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"submit_df.to_csv('submission.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"neighbors_roll_axes = [(i,j) for i in range(-1,2) for j in range(-1, 2) if not (i==0 and j==0)]\n\n\ndef binary_forward_iteration(grid, delta=1):\n    for _ in range(delta):\n        neighbor_sum = torch.cat([torch.roll(torch.roll(grid, i, 2), j, 3) for i,j  in neighbors_roll_axes], dim=1)\n        neighbor_sum = neighbor_sum.sum(dim=1, keepdim=True)\n        grid = ((neighbor_sum == 3) | ((grid==1)  & (neighbor_sum == 2)))\n    return grid\n\ndef evaluate_results(test, submission):\n    test = test.copy()\n    test['socre'] = 0\n    for delta in range(1,6):\n        mask = test['delta']==delta\n        data = FloatTensor(submission[mask].to_numpy()).reshape(-1,1,25,25)\n        for _ in range(delta):\n            data = binary_forward_iteration(data)\n        data = data.reshape(-1,625).numpy()\n        result = test.loc[mask].iloc[:,1:-1] == data\n        test.loc[mask, 'socre'] = result.mean(axis=1)\n        \n        print(f\"Delta {delta} score: {test.loc[mask, 'socre'].mean()}\")\n        test.loc[mask, 'socre'].hist(bins=30)\n        \n        plt.show()\n        \n    print(f\"LB : {1-test['socre'].mean()}\")\n\nevaluate_results(test_df, submit_df)","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}