//  [python]
def train(args, data):
    adj_info = data[0]
    latest_per_user_by_time = data[1]
    user_id_map = data[2]
    item_id_map = data[3]
    train_df = data[4]
    valid_df = data[5]
    test_df = data[6]
    
    args.num_items = len(item_id_map) + 1
    args.num_users = len(user_id_map)
    placeholders = construct_placeholders(args)
    if not os.path.exists(args.ckpt_dir):
        os.makedirs(args.ckpt_dir)
    ckpt_path = os.path.join(args.ckpt_dir, 'model.ckpt')

    minibatch = MinibatchIterator(adj_info,
                latest_per_user_by_time,
                [train_df, valid_df, test_df],
                placeholders,
                batch_size=args.batch_size,
                max_degree=args.max_degree,
                num_nodes=len(user_id_map),
                max_length=args.max_length,
                samples_1_2=[args.samples_1, args.samples_2])
    
    dgrec = DGRec(args, minibatch.sizes, placeholders)
    
    config = tf.ConfigProto()
    config.gpu_options.allow_growth = True
    sess = tf.Session(config=config)
    sess.run(tf.global_variables_initializer())
    saver = tf.train.Saver(tf.global_variables(), max_to_keep=3)

    total_steps = 0
    avg_time = 0.

    patience = 10
    inc = 0
    early_stopping = False

    highest_val_recall = -1.0
    start_time = time.time()
    for epoch in range(args.epochs):
        minibatch.shuffle()

        iter_cn = 0
        print('Epoch: %04d' % (epoch + 1))
        epoch_val_cost = []
        epoch_val_recall = []
        epoch_val_ndcg = []
        epoch_train_cost = []
        epoch_train_recall = []
        epoch_train_ndcg = []
        epoch_train_point = []
        
        while not minibatch.end() and not early_stopping:
            t = time.time()
            feed_dict = minibatch.next_train_minibatch_feed_dict()
            outs = sess.run([dgrec.opt_op, dgrec.loss, dgrec.sum_recall, dgrec.sum_ndcg, dgrec.point_count], feed_dict=feed_dict)
            train_cost = outs[1]
            epoch_train_cost.append(train_cost)
            epoch_train_recall.append(outs[2])
            epoch_train_ndcg.append(outs[3])
            epoch_train_point.append(outs[4])
            # Print results
            avg_time = (avg_time * total_steps + time.time() - t) / (total_steps + 1)

            if iter_cn % args.val_every == 0:
                ret = evaluate(sess, dgrec, minibatch)
                epoch_val_cost.append(ret[0])
                epoch_val_recall.append(ret[1])
                epoch_val_ndcg.append(ret[2])
                if ret[1] >= highest_val_recall:
                    saver.save(sess, ckpt_path, global_step=total_steps)
                    highest_val_recall = ret[1]
                    inc = 0
                    print("Iter:", '%d' % iter_cn, 
                          "val_loss=", "{:.5f}".format(epoch_val_cost[-1]),
                          "val_recall@20=", "{:.5f}".format(epoch_val_recall[-1]),
                          "val_ndcg=", "{:.5f}".format(epoch_val_ndcg[-1]),
                          "dump model!"
                          )
                else:
                    inc += 1
                if inc >= patience:
                    early_stopping = True
                    break

            if total_steps % args.print_every == 0:
                print("Iter:", '%d' % iter_cn, 
                      "train_loss=", "{:.5f}".format(np.mean(epoch_train_cost)),
                      "train_recall@20=", "{:.5f}".format(np.sum(epoch_train_recall)/np.sum(epoch_train_point)),
                      "train_ndcg=", "{:.5f}".format(np.sum(epoch_train_ndcg)/np.sum(epoch_train_point)),
                      "val_loss=", "{:.5f}".format(epoch_val_cost[-1]),
                      "val_recall@20=", "{:.5f}".format(epoch_val_recall[-1]),
                      "val_ndcg=", "{:.5f}".format(epoch_val_ndcg[-1]),
                      "time=", "{:.5f}s".format(avg_time))
                sys.stdout.flush()
            total_steps += 1
            iter_cn += 1
        if early_stopping:
            print('Early stop at epoch: {}, total training steps: {}'.format(epoch, total_steps))
            break
    end_time = time.time() 
    print('-----------{} seconds per batch iteration-------------'.format((end_time - start_time) / total_steps))
    print('Parameter settings: {}'.format(args.ckpt_dir))
    print('Optimization finished!\tStart testing...')
    ret = evaluate(sess, dgrec, minibatch, 'test')
    print('Test results:',
            '\tLoss:{}'.format(ret[0]),
            '\tRecall@20:{}'.format(ret[1]),
            '\tNDCG:{}'.format(ret[2]))