//  [python]
def build_ur_model(is_training, inputs, params):
    """Compute logits of the model (output distribution)
    Args:
        mode: (string) 'train', 'eval', etc.
        inputs: (dict) contains the inputs of the graph (features, labels...)
                this can be `tf.placeholder` or outputs of `tf.data`
        params: (Params) contains hyperparameters of the model (ex: `params.learning_rate`)
    Returns:
        output: (tf.Tensor) output of the model
    Notice:
        !!! when using the build_model mdprank needs a learning_rate around 1e-5 - 1e-7
    """
    permutation_loss = tf.constant(0, dtype=tf.float32)
    features = inputs['features']
    features = tf.reshape(features, [-1, int(params.feature_dim)])
    with tf.variable_scope('ur', reuse=tf.AUTO_REUSE):
        # mlp for rnn
        out_l0 = tf.layers.dense(features, params.mlp_sizes[0], name='full_dense_l0', activation=tf.nn.relu)
        out_l1 = tf.layers.dense(out_l0, params.mlp_sizes[-1], name='full_dense_l1', activation=tf.nn.relu)
        # dropout or batch normalization did not help
        # out_l1 = tf.nn.dropout(out_l1, params.dropout_rate)
        x = tf.squeeze(out_l1)
        x = tf.reshape(x, [-1, params.mlp_sizes[-1]])
        h_1 = tf.zeros(tf.shape(x))
        h_1 = tf.cast(h_1, dtype=tf.float32)
        # ls, current = gru(x, h_1, params)
        if params.rnn == 'C2':
            ls, current = rnnC2(x, h_1, params)
        else:
            ls, current = rnnC1(x, h_1, params)
        # use ls at the 1st step directly for validation and inference
        if not is_training:
            # since we use NDCG as metric for the best weights
            # we do not have to calculate permutation_loss
            # permutation_loss is 0 in validation and inference
            return ls, permutation_loss
        # the following is only for rating
        # calculate loss
        labels = inputs['labels']
        n_data = inputs['height']
        # n_data = tf.shape(labels)[0]       
        # global minimum label
        mini_label = tf.reduce_min(labels)
        total_predicted_scores = ls - tf.reduce_min(ls)
        # total_predicted_scores = ls
        inputs['no_ndcg'] = 1
        multi_label_loss = _get_multi_label_loss(labels, total_predicted_scores)
        permutation_loss += multi_label_loss
        x, prev = _get_rnn_x_prev(labels, x, current, params.mlp_sizes[-1], params)
        keep_labels = _get_updated_labels(labels)
        unique_rating = tf.constant(1, dtype=tf.float32)
        shape_invariants = [unique_rating.get_shape(), tf.TensorShape([None, 1]), \
        tf.TensorShape([None, params.mlp_sizes[-1]]), tf.TensorShape([1, params.mlp_sizes[-1]]), \
        permutation_loss.get_shape()]

        def _cond(unique_rating, keep_labels, \
            x, prev, permutation_loss):
            return tf.greater(tf.reduce_max(keep_labels), mini_label)

        def _gen_loop_body():
            def loop_body(unique_rating, keep_labels, \
                x, prev, permutation_loss):
                tiled_shape = tf.shape(keep_labels)
                prev = tf.ones(tiled_shape) * prev
                # ls, current = gru(x, prev, params)
                if params.rnn == 'C1':
                    ls, current = rnnC1(x, prev, params)
                else:
                    ls, current = rnnC2(x, prev, params)
                total_predicted_scores = ls - tf.reduce_min(ls)
                # total_predicted_scores = ls
                multi_label_loss = _get_multi_label_loss(keep_labels, total_predicted_scores)
                permutation_loss += multi_label_loss
                # update
                x, prev = _get_rnn_x_prev(keep_labels, x, current, params.mlp_sizes[-1], params)
                keep_labels = _get_updated_labels(keep_labels)
                return tf.add(unique_rating, 1.0), keep_labels, \
                x, prev, permutation_loss
            return loop_body

        unique_rating, keep_labels, \
        x, prev, permutation_loss = tf.while_loop(_cond, _gen_loop_body(),
            [unique_rating, keep_labels, \
            x, prev, permutation_loss], shape_invariants=shape_invariants)
        permutation_loss /= unique_rating
    return total_predicted_scores, permutation_loss