'Reduces the atom to an affine expression and list of constraints.
Parameters
arg_objs : list
LinExpr for each argument.
size : tuple
The size of the resulting expression.
data :
Additional data required by the atom.
Returns
tuple
(LinOp for objective, list of constraints)
Notes
Implementation notes.
- For general :math:`p \geq 1`, the inequality :math:`\|x\|_p \leq t`
is equivalent to the following convex inequalities:
.. math::
|x_i| &\leq r_i^{1/p} t^{1 - 1/p}\\
\sum_i r_i &= t.
These inequalities happen to also be correct for :math:`p = +\infty`,
if we interpret :math:`1/\infty` as :math:`0`.
- For general :math:`0 < p < 1`, the inequality :math:`\|x\|_p \geq t`
is equivalent to the following convex inequalities:
.. math::
r_i &\leq x_i^{p} t^{1 - p}\\
\sum_i r_i &= t.
- For general :math:`p < 0`, the inequality :math:`\|x\|_p \geq t`
is equivalent to the following convex inequalities:
.. math::
t &\leq x_i^{-p/(1-p)} r_i^{1/(1 - p)}\\
\sum_i r_i &= t.
Although the inequalities above are correct, for a few special cases,
we can represent the p-norm more efficiently and with fewer variables and inequalities.
- For :math:`p = 1`, we use the representation
.. math::
x_i &\leq r_i\\
-x_i &\leq r_i\\
\sum_i r_i &= t
- For :math:`p = \infty`, we use the representation
.. math::
x_i &\leq t\\
-x_i &\leq t
Note that we don\'t need the :math:`r` variable or the sum inequality.
- For :math:`p = 2`, we use the natural second-order cone representation
.. math::
\|x\|_2 \leq t
Note that we could have used the set of inequalities given above if we wanted
an alternate decomposition of a large second-order cone into into several
smaller inequalities.'

@staticmethod
def graph_implementation(arg_objs, size, data=None):

    p = data[0]
    axis = data[1]
    x = arg_objs[0]
    t = lu.create_var((1, 1))
    constraints = []
    if (p == 2):
        if (axis is None):
            return (t, [SOC(t, [x])])
        else:
            t = lu.create_var(size)
            return (t, [SOC_Axis(lu.reshape(t, ((t.size[0] * t.size[1]), 1)), x, axis)])
    if (p == np.inf):
        t_ = lu.promote(t, x.size)
        return (t, [lu.create_leq(x, t_), lu.create_geq(lu.sum_expr([x, t_]))])
    if (p >= 1):
        absx = lu.create_var(x.size)
        constraints += [lu.create_leq(x, absx), lu.create_geq(lu.sum_expr([x, absx]))]
        x = absx
    if (p == 1):
        return (lu.sum_entries(x), constraints)
    r = lu.create_var(x.size)
    t_ = lu.promote(t, x.size)
    constraints += [lu.create_eq(lu.sum_entries(r), t)]
    p = Fraction(p)
    if (p < 0):
        constraints += gm_constrs(t_, [x, r], (((- p) / (1 - p)), (1 / (1 - p))))
    if (0 < p < 1):
        constraints += gm_constrs(r, [x, t_], (p, (1 - p)))
    if (p > 1):
        constraints += gm_constrs(x, [r, t_], ((1 / p), (1 - (1 / p))))
    return (t, constraints)
