def _activation_convex_relaxation(bound_cls, index, inp, concretizer, act_type,
                                  lb_fun, ub_fun):
  """Builds the NonConvexBound object corresponding to after non-linearities.

  Args:
    bound_cls: Bound class to use.
    index: Index of the computation.
    inp: Input of the non-linearity.
    concretizer: Concretizer for the NonConvexBound to generate.
    act_type: Type of activation
    lb_fun: Function to evaluate the upper bound of the activation for an input.
    ub_fun: Function to evaluate the upper bound of the activation for an input.
  Returns:
    out_bounds: NonConvexBound for the output of the non-linearity
  """

  parents_var = inp.variables
  variables = parents_var.copy()
  variables[index] = inp.shape

  def eval_fn(var_set, dummy_inps, activations):
    """Evaluate the value of the primal."""
    inp_eval = inp.evaluate(var_set, dummy_inps, activations)

    lb_val = lb_fun(inp_eval)
    ub_val = ub_fun(inp_eval)

    theta = var_set[index]
    out_val = lb_val + theta * (ub_val - lb_val)

    return out_val

  shape = inp.shape
  previous_bounds = inp.previous_bounds.copy()

  new_bound_ctor = bound_cls.get_nonlinearity_activation_constructor(
      index, inp, act_type, lb_fun, ub_fun)
  return new_bound_ctor(index, shape, previous_bounds,
                        eval_fn, variables, concretizer)