':param loss: Symbolic expression for the loss function.
:param target: A parameterized object to optimize over. It should implement methods of the
:class:`rllab.core.paramerized.Parameterized` class.
:param leq_constraint: A constraint provided as a tuple (f, epsilon), of the form f(*inputs) <= epsilon.
:param inputs: A list of symbolic variables as inputs
:return: No return value.'

def update_opt(self, loss, target, leq_constraint, inputs, constraint_name='constraint', *args, **kwargs):

    (constraint_term, constraint_value) = leq_constraint
    with tf.variable_scope(self._name):
        penalty_var = tf.placeholder(tf.float32, tuple(), name='penalty')
    penalized_loss = (loss + (penalty_var * constraint_term))
    self._target = target
    self._max_constraint_val = constraint_value
    self._constraint_name = constraint_name
    def get_opt_output():
        params = target.get_params(trainable=True)
        grads = tf.gradients(penalized_loss, params)
        for (idx, (grad, param)) in enumerate(zip(grads, params)):
            if (grad is None):
                grads[idx] = tf.zeros_like(param)
        flat_grad = tensor_utils.flatten_tensor_variables(grads)
        return [tf.cast(penalized_loss, tf.float64), tf.cast(flat_grad, tf.float64)]
    self._opt_fun = ext.lazydict(f_loss=(lambda : tensor_utils.compile_function(inputs, loss, log_name='f_loss')), f_constraint=(lambda : tensor_utils.compile_function(inputs, constraint_term, log_name='f_constraint')), f_penalized_loss=(lambda : tensor_utils.compile_function(inputs=(inputs + [penalty_var]), outputs=[penalized_loss, loss, constraint_term], log_name='f_penalized_loss')), f_opt=(lambda : tensor_utils.compile_function(inputs=(inputs + [penalty_var]), outputs=get_opt_output())))
