# DGP fundamentals

This notebook will introduce you to the fundamentals of **disciplined geometric programming** (**DGP**), which lets you formulate and solve *log-log convex programs* (LLCPs) in CVXPY.

LLCPs are problems that become convex after the variables, objective functions, and constraint functions are replaced with their logs, an operation that we refer to as a log-log transformation. LLCPs generalize geometric programming.


```python
import cvxpy as cp
```

# 1. Log-log curvature

Just as every Expression in CVXPY has a curvature (constant, affine, convex, concave, or unknown), every Expression also has a log-log curvature.

A function $f : D \subseteq \mathbf{R}^{n}_{++} \to \mathbf{R}$ is said to be **log-log convex** if the function $F(u)=\log f(e^u)$, with domain $\{u \in \mathbf{R}^n : e^u \in D\}$, is convex (where $\mathbf{R}^{n}_{++}$ denotes the set of positive reals and the logarithm and exponential are meant elementwise); the function $F$ is called the log-log transformation of $f$. The function $f$ is **log-log concave** if $F$ is concave, and it is **log-log affine** if $F$ is affine.

Notice that if a function has log-log curvature, then its domain and range can only include positive numbers.

The log-log curvature of an `Expression` can be accessed via its `.log_log_curvature` attribute. For an `Expression` to have known log-log curvature, all of the `Constant`s, `Variable`s, and `Parameter`s it refers to must be elementwise positive.


```python
# Only elementwise positive constants are allowed in DGP.
c = cp.Constant(1.0)
print(c, c.log_log_curvature)

c = cp.Constant([1.0, 2.0])
print(c, c.log_log_curvature)

c = cp.Constant([1.0, 0.0])
print(c, c.log_log_curvature)

c = cp.Constant(-2.0)
print(c, c.log_log_curvature)
```

    1.0 LOG-LOG CONSTANT
    [1. 2.] LOG-LOG CONSTANT
    [1. 0.] UNKNOWN
    -2.0 UNKNOWN



```python
# Variables and parameters must be positive, ie, they must be constructed with the option `pos=True`
v = cp.Variable(pos=True)
print(v, v.log_log_curvature)

v = cp.Variable()
print(v, v.log_log_curvature)

p = cp.Parameter(pos=True)
print(p, p.log_log_curvature)

p = cp.Parameter()
print(p, p.log_log_curvature)
```

    var0 LOG-LOG AFFINE
    var1 UNKNOWN
    param2 LOG-LOG CONSTANT
    param3 UNKNOWN


## Functions from geometric programming

A function $f(x)$ is log-log affine if and only if it is given by

$$
f(x) = cx_1^{a_1}x_2^{a_2} \ldots x_n^{a_n},
$$

where $c > 0$ and the $a_i$ are real numbers. In the context of geometric programming, such a function is called
a monomial.


```python
x = cp.Variable(shape=(3,), pos=True, name="x")
c = 2.0
a = [0.5, 2.0, 1.8]

monomial = c * x[0] ** a[0] * x[1] ** a[1] * x[2] ** a[2]
# Monomials are not convex.
assert not monomial.is_convex()

# They are, however, log-log affine.
print(monomial, ":", monomial.log_log_curvature)
assert monomial.is_log_log_affine()
```

    2.0 * power(x[0], 1/2) * power(x[1], 2) * power(x[2], 9/5) : LOG-LOG AFFINE


A sum of monomial functions is log-log convex; in the context of geometric programming, such a function is called a posynomial. There are functions that are not posynomials that are still log-log convex.


```python
x = cp.Variable(pos=True, name="x")
y = cp.Variable(pos=True, name="y")

constant = cp.Constant(2.0)
monomial = constant * x * y
posynomial = monomial + (x ** 1.5) * (y ** -1)
reciprocal = posynomial ** -1
unknown = reciprocal + posynomial

print(constant, ":", constant.log_log_curvature)
print(monomial, ":", monomial.log_log_curvature)
print(posynomial, ":", posynomial.log_log_curvature)
print(reciprocal, ":", reciprocal.log_log_curvature)
print(unknown, ":", unknown.log_log_curvature)
```

    2.0 : LOG-LOG CONSTANT
    2.0 * x * y : LOG-LOG AFFINE
    2.0 * x * y + power(x, 3/2) * power(y, -1) : LOG-LOG CONVEX
    power(2.0 * x * y + power(x, 3/2) * power(y, -1), -1) : LOG-LOG CONCAVE
    power(2.0 * x * y + power(x, 3/2) * power(y, -1), -1) + 2.0 * x * y + power(x, 3/2) * power(y, -1) : UNKNOWN


# 2. Log-log curvature ruleset

CVXPY has a library of atomic functions with known log-log curvature and monotonicity. It uses this information to
tag every `Expression`, i.e., every composition of atomic functions, with a log-log curvature. In particular, 

A function $f(expr_1,expr_2,...,expr_n)$ is log-log convex if  $f$ is a log-log convex function and for each expri one of the following conditions holds:

$f$ is increasing in argument i and $expr_i$ is log-log convex.
$f$ is decreasing in argument $i$ and $expr_i$ is log-log concave.
$expr_i$ is log-log affine.
A function $f(expr_1,expr_2,...,expr_n)$ is log-log concave if  $f$ is a log-log concave function and for each $expr_i$ one of the following conditions holds:

$f$ is increasing in argument $i$ and $expr_i$ is log-log concave.
$f$ is decreasing in argument $i$ and $expr_i$ is log-log convex.
$expr_i$ is log-log affine.
A function $f(expr_1,expr_2,...,expr_n)$ is log-log affine if $f$ is an log-log affine function and each $expr_i$ is log-log affine.

If none of the three rules apply, the expression $f(expr_1,expr_2,...,expr_n)$ is marked as having unknown curvature.

If an Expression satisfies the composition rule, we colloquially say that the `Expression` “is DGP.” You can check whether an `Expression` is DGP by calling the method `is_dgp()`.


```python
x = cp.Variable(pos=True, name="x")
y = cp.Variable(pos=True, name="y")

monomial = 2.0 * x * y
posynomial = monomial + (x ** 1.5) * (y ** -1)

print(monomial, "is dgp?", monomial.is_dgp())
print(posynomial, "is dgp?", posynomial.is_dgp())
```

    2.0 * x * y is dgp? True
    2.0 * x * y + power(x, 3/2) * power(y, -1) is dgp? True


# 3. DGP problems

An LLCP is an optimization problem of the form

$$
\begin{equation}
\begin{array}{ll}
\mbox{minimize} & f_0(x) \\
\mbox{subject to} & f_i(x) \leq \tilde{f_i}, \quad i=1, \ldots, m\\
& g_i(x) = \tilde{g_i}, \quad i=1, \ldots, p,
\end{array}
\end{equation}
$$

where the functions $f_i$ are log-log convex, $\tilde{f_i}$ are log-log concave, and the functions $g_i$ and $\tilde{g_i}$ are log-log affine. An optimization problem with constraints of the above form in which the goal is to maximize a log-log concave function is also an LLCP.

A problem is DGP if additionally all the functions are DGP. You can check whether a CVXPY `Problem` is DGP by calling
its `.is_dgp()` method.



```python
x = cp.Variable(pos=True, name="x")
y = cp.Variable(pos=True, name="y")
z = cp.Variable(pos=True, name="z")

objective_fn = x * y * z
constraints = [
  4 * x * y * z + 2 * x * z <= 10, x <= 2*y, y <= 2*x, z >= 1]
assert objective_fn.is_log_log_concave()
assert all(constraint.is_dgp() for constraint in constraints)
problem = cp.Problem(cp.Maximize(objective_fn), constraints)

print(problem)
print("Is this problem DGP?", problem.is_dgp())
```

    maximize x * y * z
    subject to 4.0 * x * y * z + 2.0 * x * z <= 10.0
               x <= 2.0 * y
               y <= 2.0 * x
               1.0 <= z
    Is this problem DGP? True


## Solving DGP problems

You can solve a DGP `Problem` by calling its `solve` method with `gp=True`.


```python
problem.solve(gp=True)
print("Optimal value:", problem.value)
print(x, ":", x.value)
print(y, ":", y.value)
print(z, ":", z.value)
print("Dual values: ", list(c.dual_value for c in constraints))
```

    Optimal value: 1.9999999926890524
    x : 0.9999999989968756
    y : 1.9999999529045318
    z : 1.000000020895385
    Dual values:  [1.1111111199586956, 1.94877846244994e-09, 0.1111111217156332, 0.11111112214962586]


If you forget to supply `gp=True`, an error will be raised.


```python
try:
    problem.solve()
except cp.DCPError as e:
    print(e)
```

    Problem does not follow DCP rules. However, the problem does follow DGP rules. Consider calling this function with `gp=True`.


# 4. Next steps

## Atoms
CVXPY has a large library of log-log convex functions, including common functions like $\exp$, $\log$, and the difference between two numbers. Check out the tutorial on our website for the full list of atoms: https://www.cvxpy.org/tutorial/dgp/index.html

## References
For a reference on DGP, consult the following paper: https://web.stanford.edu/~boyd/papers/dgp.html

25a78438a273096c4ebb01eafa97d738a2ecf9f4
ipynb
Jupyter Notebook
examples/notebooks/dgp/dgp_fundamentals.ipynb
jasondark/cvxpy
56aaa01b0e9d98ae5a91a923708129a7b37a6f18
ECL-2.0 Apache-2.0
2015-01-03T04:02:29.000Z
2021-04-19T14:51:29.000Z
examples/notebooks/dgp/dgp_fundamentals.ipynb
h-vetinari/cvxpy
86307f271819bb78fcdf64a9c3a424773e8269fa
ECL-2.0 Apache-2.0
2015-01-01T19:40:14.000Z
2021-04-18T23:37:31.000Z
examples/notebooks/dgp/dgp_fundamentals.ipynb
h-vetinari/cvxpy
86307f271819bb78fcdf64a9c3a424773e8269fa
ECL-2.0 Apache-2.0
2015-01-02T19:29:39.000Z
2021-04-20T00:50:43.000Z
Qwen/Qwen-72B
1. YES
2. YES