//  [python]
def get_graph(self):

        # Placehoders

        # M users
        self.pos_sample_idx = tf.placeholder(tf.int32, [None])

        # M positive items
        self.neg_sample_idx = tf.placeholder(tf.int32, [None, None])

        # M X N positive items
        self.user_idx = tf.placeholder(tf.int32, [None])

        # Variable to learn
        self.user_embeddings = tf.Variable(tf.random_normal([self.num_users, self.embed_dim],
                                                            stddev=1 / (self.embed_dim ** 0.5), dtype=tf.float32))

        self.item_embeddings = tf.Variable(tf.random_normal([self.num_items, self.embed_dim],
                                                            stddev=1 / (self.embed_dim ** 0.5), dtype=tf.float32))

        with tf.variable_scope("covariance_loss"):
            embedding = tf.concat((self.item_embeddings, self.user_embeddings), 0)
            n_rows = tf.cast(tf.shape(embedding)[0], tf.float32)
            X = embedding - (tf.reduce_mean(embedding, axis=0))
            cov = tf.matmul(X, X, transpose_a=True) / n_rows
            cov_loss = tf.reduce_sum(tf.matrix_set_diag(cov, tf.zeros(self.embed_dim, tf.float32))
                                     ) * self.cov_loss_weight

        with tf.variable_scope("metric_loss"):
            users = tf.nn.embedding_lookup(self.user_embeddings, self.user_idx, name="users")
            pos_samples = tf.nn.embedding_lookup(self.item_embeddings, self.pos_sample_idx, name="pos_items")
            neg_samples = tf.transpose(tf.nn.embedding_lookup(self.item_embeddings, self.neg_sample_idx),
                                       (0, 2, 1), name="neg_items")
            pos_distances = tf.reduce_sum(tf.squared_difference(users, pos_samples),
                                          axis=1,
                                          name="pos_distances")
            neg_distances = tf.reduce_sum(tf.squared_difference(tf.expand_dims(users, -1), neg_samples),
                                          axis=1,
                                          name="neg_distances")
            shortest_neg_distances = tf.reduce_min(neg_distances, 1, name="shortest_neg_distances")
            hinge_loss = tf.maximum(pos_distances - shortest_neg_distances + self.margin, 0, name="pair_loss")
            impostors = (tf.expand_dims(pos_distances, -1) - neg_distances + self.margin) > 0
            rank = tf.reduce_mean(tf.cast(impostors, dtype=tf.float32), 1, name="rank_weight") * self.num_items
            metric_loss = hinge_loss * tf.log(rank + 1)

        self.loss = cov_loss + metric_loss

        with tf.variable_scope("optimizer"):
            self.optimizer = tf.train.AdamOptimizer().minimize(self.loss,
                                                               var_list=[self.user_embeddings,
                                                                         self.item_embeddings])

        with tf.variable_scope("clip"):
            self.clips = [
                tf.assign(self.user_embeddings, tf.clip_by_norm(self.user_embeddings, self.clip_norm, axes=[1])),
                tf.assign(self.item_embeddings, tf.clip_by_norm(self.item_embeddings, self.clip_norm, axes=[1]))
            ]