//  [python]
def mmd_penalty(self, sample_qz, sample_pz):
        opts = self.opts
        sigma2_p = opts['pz_scale'] ** 2
        kernel = opts['mmd_kernel']
        n = utils.get_batch_size(sample_qz)
        n = t.cast(n, t.int32)
        nf = t.cast(n, t.float32)
        half_size = (n * n - n) / 2

        norms_pz = t.reduce_sum(t.square(sample_pz), axis=1, keep_dims=True)
        dotprods_pz = t.matmul(sample_pz, sample_pz, transpose_b=True)
        distances_pz = norms_pz + t.transpose(norms_pz) - 2. * dotprods_pz

        norms_qz = t.reduce_sum(t.square(sample_qz), axis=1, keep_dims=True)
        dotprods_qz = t.matmul(sample_qz, sample_qz, transpose_b=True)
        distances_qz = norms_qz + t.transpose(norms_qz) - 2. * dotprods_qz

        dotprods = t.matmul(sample_qz, sample_pz, transpose_b=True)
        distances = norms_qz + t.transpose(norms_pz) - 2. * dotprods

        # if opts['verbose']:
        #     distances = t.Print(
        #         distances,
        #         [t.nn.top_k(t.reshape(distances_qz, [-1]), 1).values[0]],
        #         'Maximal Qz squared pairwise distance:')
        #     distances = t.Print(distances, [t.reduce_mean(distances_qz)],
        #                         'Average Qz squared pairwise distance:')

        #     distances = t.Print(
        #         distances,
        #         [t.nn.top_k(t.reshape(distances_pz, [-1]), 1).values[0]],
        #         'Maximal Pz squared pairwise distance:')
        #     distances = t.Print(distances, [t.reduce_mean(distances_pz)],
        #                         'Average Pz squared pairwise distance:')

        if kernel == 'RBF':
            # Median heuristic for the sigma^2 of Gaussian kernel
            sigma2_k = t.nn.top_k(
                t.reshape(distances, [-1]), half_size).values[half_size - 1]
            sigma2_k += t.nn.top_k(
                t.reshape(distances_qz, [-1]), half_size).values[half_size - 1]
            # Maximal heuristic for the sigma^2 of Gaussian kernel
            # sigma2_k = t.nn.top_k(t.reshape(distances_qz, [-1]), 1).values[0]
            # sigma2_k += t.nn.top_k(t.reshape(distances, [-1]), 1).values[0]
            # sigma2_k = opts['latent_space_dim'] * sigma2_p
            if opts['verbose']:
                sigma2_k = t.Print(sigma2_k, [sigma2_k], 'Kernel width:')
            res1 = t.exp( - distances_qz / 2. / sigma2_k)
            res1 += t.exp( - distances_pz / 2. / sigma2_k)
            res1 = t.multiply(res1, 1. - t.eye(n))
            res1 = t.reduce_sum(res1) / (nf * nf - nf)
            res2 = t.exp( - distances / 2. / sigma2_k)
            res2 = t.reduce_sum(res2) * 2. / (nf * nf)
            stat = res1 - res2
        elif kernel == 'IMQ':
            # k(x, y) = C / (C + ||x - y||^2)
            # C = t.nn.top_k(t.reshape(distances, [-1]), half_size).values[half_size - 1]
            # C += t.nn.top_k(t.reshape(distances_qz, [-1]), half_size).values[half_size - 1]
            if opts['pz'] == 'normal':
                Cbase = 2. * opts['zdim'] * sigma2_p
            elif opts['pz'] == 'sphere':
                Cbase = 2.
            elif opts['pz'] == 'uniform':
                # E ||x - y||^2 = E[sum (xi - yi)^2]
                #               = zdim E[(xi - yi)^2]
                #               = const * zdim
                Cbase = opts['zdim']
            stat = 0.
            for scale in [.1, .2, .5, 1., 2., 5., 10.]:
                C = Cbase * scale
                res1 = C / (C + distances_qz)
                res1 += C / (C + distances_pz)
                res1 = t.multiply(res1, 1. - t.eye(n))
                res1 = t.reduce_sum(res1) / (nf * nf - nf)
                res2 = C / (C + distances)
                res2 = t.reduce_sum(res2) * 2. / (nf * nf)
                stat += res1 - res2
        return stat