//  [python]
def do_tempscale_optimization(labels, preacts, bias_positions, verbose,
                              lbfgs_kwargs):  
    if (bias_positions=='all'):
        bias_positions = np.arange(labels.shape[1])
    def eval_func(x):
        t = x[0]
        bs = np.zeros(labels.shape[1])
        for bias_pos_idx, bias_pos in enumerate(bias_positions):
            bs[bias_pos] = x[1+bias_pos_idx] 
        #tsb = temp_scaled_biased
        tsb_preacts = preacts/float(t) + bs[None,:]
        log_sum_exp = scipy.special.logsumexp(a=tsb_preacts, axis=1) 

        exp_tsb_logits = np.exp(tsb_preacts)
        sum_exp = np.sum(exp_tsb_logits, axis=1)
        sum_preact_times_exp = np.sum(preacts*exp_tsb_logits, axis=1)

        notsb_logits_trueclass =\
            np.sum(preacts*labels, axis=1)
        tsb_logits_trueclass =\
            np.sum(tsb_preacts*labels, axis=1)
        
        log_likelihoods = tsb_logits_trueclass - log_sum_exp
        nll = -np.mean(log_likelihoods)
        grads_t = ((sum_preact_times_exp/sum_exp
                    - notsb_logits_trueclass)/\
                    (float(t)**2))
        grads_b = labels - (exp_tsb_logits/(sum_exp[:,None]))
        #multiply by -1 because we care about *negative* log likelihood
        mean_grad_t = -np.mean(grads_t)
        mean_grads_b = -np.mean(grads_b, axis=0)
        #only supply the gradients for the bias positions that
        # we are allowed to optimize for
        mean_grads_b_masked = []
        for bias_pos_idx, bias_pos in enumerate(bias_positions):
            mean_grads_b_masked.append(mean_grads_b[bias_pos])
        return nll, np.array([mean_grad_t]+mean_grads_b_masked)

    if (verbose):
        original_nll = compute_nll(labels=labels, preacts=preacts,
                                   t=1.0, bs=np.zeros(labels.shape[1]))
        print("Original NLL is: ",original_nll)
        
    optimization_result = scipy.optimize.minimize(
                              fun=eval_func,
                              #fun=lambda x: eval_func(x)[0],
                              x0=np.array([1.0]+[0.0 for x in
                                                 bias_positions]),
                              bounds=[(0,None)]+[(None,None) for x in
                                                 bias_positions],
                              jac=True,
                              method='L-BFGS-B',
                              tol=1e-07,
                              **lbfgs_kwargs)
    if (verbose):
        print(optimization_result)
    assert optimization_result.success==True, optimization_result
    biases = np.zeros(labels.shape[1])
    if (hasattr(optimization_result.x, '__iter__')):
        optimal_t = optimization_result.x[0]
        for bias_pos_idx,bias_pos in enumerate(bias_positions):
           biases[bias_pos] = optimization_result.x[1+bias_pos_idx] 
        final_nll = compute_nll(labels=labels, preacts=preacts,
                                t=optimal_t, bs=biases)
    else:
        optimal_t = optimization_result.x
        final_nll = compute_nll(labels=labels, preacts=preacts,
                                t=optimal_t, bs=np.zeros(labels.shape[1]))
    if (verbose):
        print("Final NLL & grad is: ",final_nll)

    return (optimal_t, biases)