Coder Social home page Coder Social logo

dsp's Introduction

DSP - Disciplined Saddle Programming

build Coverage

A CVXPY extension for Disciplined Saddle Programming. DSP allows solving convex-concave saddle point problems, and more generally convex optimization problems we refer to as saddle problems, which include the partial supremum or infimum of convex-concave saddle functions. Saddle functions are functions that are (jointly) convex in a subset of their arguments, and (jointly) concave in the remaining arguments. A detailed description of the underlying method is given in our accompanying paper.

Installation

The DSP package can be installed using pip as follows

pip install dsp-cvxpy

The DSP package requires CVXPY 1.3 or later.

Minimal example

The following example creates and solves a simple saddle point problem known as a matrix game. A saddle point problem is created by specifying an objective and a list of constraints. Here, the objective is $f(x, y) = x^TCy$, which is simultaneously minimized over $x$ and maximized over $y$. The resulting saddle point is an optimal mixed strategy for both players in the matrix game. Each component is explained below in more detail.

import dsp
import cvxpy as cp
import numpy as np

x = cp.Variable(2)
y = cp.Variable(2)
C = np.array([[1, 2], [3, 1]])

f = dsp.inner(x, C @ y)
obj = dsp.MinimizeMaximize(f)

constraints = [x >= 0, cp.sum(x) == 1, y >= 0, cp.sum(y) == 1]
prob = dsp.SaddlePointProblem(obj, constraints)
prob.solve()  # solves the problem

prob.value  # 1.6666666666666667
x.value  # array([0.66666667, 0.33333333])
y.value  # array([0.33333333, 0.66666667])

New atoms

In DSP, saddle functions are created from atoms. Each atom represents a saddle function, with the convention being that the first argument is the convex argument and the second argument is the concave argument.

  • inner(x, y)
    The inner product $x^Ty$, with both arguments affine.
  • saddle_inner(Fx, Gy)
    The inner product $F(x)^TG(y)$, with $F$ convex and nonnegative, and $G$ concave. If $G$ is not nonnegative, a constraint $G \geq 0$ is added.
  • weighted_norm2(x, y)
    The weighted $\ell_2$ norm $\left(\sum_i y_i x_i^2\right)^{1/2}$. Here too, a constraint $y \geq 0$ is added if $y$ is not nonnegative.
  • weighted_log_sum_exp(x, y) The weighted log-sum-exp function $\log\left(\sum_i y_i \exp(x_i)\right)$. Again a constraint $y \geq 0$ is added if $y$ is not nonnegative.
  • quasidef_quad_form(x, y, P, Q, S)
    For a positive semidefinite matrix $P$ and a negative semidefinite matrix $S$, this atom represents the function

$$ f(x,y) = \left[\begin{array}{c} x \\ y \end{array}\right]^T \left[\begin{array}{cc} P & S \\ S^T & Q \end{array}\right] \left[\begin{array}{c} x \\ y \end{array}\right]. $$

  • saddle_quad_form(x, Y)
    The quadratic form $x^TYx$, where $Y$ a positive semindefinite matrix.

Calculus rules

Saddle functions can be scaled and composed by addition. DCP convex expressions are treated as saddle functions with no concave arguments, and DCP concave expressions are treated as saddle functions with no convex arguments. When adding two saddle functions, a variable may not appear as a convex variable in one expression and as a concave variable in the other expression.

Note that negating a saddle function switches the roles of the convex and concave arguments.
For example, -inner(x, y) is equivalent to inner(y, -x), not inner(-x, y). This might seem counterintuitive, especially for bi-affine functions, where both are DSP-compliant, but it is consistent with the fact that

$$ -\min_x\max_y x^T y = \max_x\min_y -x^T y \neq \min_x\max_y - x^T y. $$

Saddle point problems

To create a saddle point problem, a MinimizeMaximize object is created first, which represents the objective function, using

obj = dsp.MinimizeMaximize(f)

where f is a DSP-compliant expression.

The syntax for specifying saddle point problems is

problem = dsp.SaddlePointProblem(obj, constraints, cvx_vars, ccv_vars)

where obj is the MinimizeMaximize object, constraints is a list of constraints, and cvx_vars and ccv_vars are lists of variables to be minimized and maximized over, respectively.

Each constraint must be DCP, and can only involve variables that are either convex or concave. When the role of a variable can be inferred, it can be omitted from the list of convex or concave variables. The role can be inferred either from a saddle atom, a DCP atom that is convex or concave, but not affine, or from a constraint, when a variable appears in a constraint that involves variables with known roles.

Nevertheless, specifying the role of each variable can add clarity to the problem formulation, and is especially useful for debugging.

To solve the problem, call problem.solve(). This returns the optimal saddle value, which is also stored in the problem's value attribute. Further all value attribute of the variables are populated with their optimal values.

Saddle extremum functions

A saddle extremum function has one of the forms

$$ G(x) = \sup_{y \in \mathcal{Y}} f(x,y) \quad \text{or} \quad H(y) = \inf_{x \in \mathcal{X}} f(x,y), $$

where $f$ is a saddle function, and $\mathcal{X}$ and $\mathcal{Y}$ are convex. Since the functions only depend on $x$ or $y$, respectively, the other variables have to be declared as LocalVariables. Any LocalVariable can only be used in one saddle extremum function. The syntax for creating a saddle extremum function is

dsp.saddle_max(f, constraints)
dsp.saddle_min(f, constraints)

where f is a DSP-compliant scalar saddle function, and constraints is a list of constraints, which can only involve LocalVariables. DSP-compliant saddle extremum functions are DCP-convex or DCP-concave, respectively, and as such can be used in DCP optimization problems.

An example of a saddle extremum function is

# Creating variables
x = cp.Variable(2)

# Creating local variables
y_loc = dsp.LocalVariable(2)

# Convex in x, concave in y_loc
f = dsp.saddle_inner(C @ x, y_loc)

# maximizes over y_loc
G = dsp.saddle_max(f, [y_loc >= 0, cp.sum(y_loc) == 1])

Saddle problems

A saddle problem is a convex optimization problem that involves saddle extremum functions. Any DCP convex optimization can include saddle extremum functions when they are DSP-compliant. Using the saddle extremum function G from above, we can solve the following problem:

prob = cp.Problem(cp.Minimize(G), [x >= 0, cp.sum(x) == 1])
prob.solve() # solving the problem

prob.value # 1.6666666666666667
x.value # array([0.66666667, 0.33333333])

Citation

If you want to reference DSP in your research, please consider citing us by using the following BibTeX:

@misc{schiele2023dsp,
  title = {Disciplined Saddle Programming},
  author = {Schiele, Philipp and Luxenberg, Eric and Boyd, Stephen},
  year = {2023},
  doi = {10.48550/arXiv.2301.13427},
  url = {https://arxiv.org/abs/2301.13427},
}

dsp's People

Contributors

dependabot[bot] avatar ericlux avatar kurtmckee avatar phschiele avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

dsp's Issues

variables and parameters for MinimizeMaximize

It would be better if MinimizeMaximize supported more of the standard methods for cvxpy objects, such as .variables() to get all the variables and .parameters() to get all the parameters.

DOC: typo?

Should saddle_innder(Fx, Gy) be saddle_inner(Fx, Gy)?

Naming convention?

I think it would be helpful to have all cvxpy related packages in a cvx namespace. E.g. you would introduce a folder cvx (without init.py) and in there you would the dsp package. This would avoid collision if you work in projects with multiple cvx related packages. I do this for example in cvxmarkowitz or in the cvxrisk also in the simulator package. cvx is always the route of the source folders. Having a name like cvx.. makes it also nice when you poetry show (or similar)... @phschiele @PTNobel

No module named 'cvxpy.reductions.dcp2cone.atom_canonicalizers'

Hello,

Using both cvxpy-1.4.1 and cvxpy-1.4.2, trying to import dsp, I get this error:

File [~/mambaforge/envs/torch2/lib/python3.11/site-packages/dsp/__init__.py:1](https://vscode-remote+ssh-002dremote-002blaurent-002dworkstation-002elan-002eetive-002ecom.vscode-resource.vscode-cdn.net/home/laurent/code/market-downloader/~/mambaforge/envs/torch2/lib/python3.11/site-packages/dsp/__init__.py:1)
----> [1](https://vscode-remote+ssh-002dremote-002blaurent-002dworkstation-002elan-002eetive-002ecom.vscode-resource.vscode-cdn.net/home/laurent/code/market-downloader/~/mambaforge/envs/torch2/lib/python3.11/site-packages/dsp/__init__.py:1) from dsp.cvxpy_integration import add_is_dsp, extend_cone_canon_methods
      [2](https://vscode-remote+ssh-002dremote-002blaurent-002dworkstation-002elan-002eetive-002ecom.vscode-resource.vscode-cdn.net/home/laurent/code/market-downloader/~/mambaforge/envs/torch2/lib/python3.11/site-packages/dsp/__init__.py:2) from dsp.local import LocalVariable
      [3](https://vscode-remote+ssh-002dremote-002blaurent-002dworkstation-002elan-002eetive-002ecom.vscode-resource.vscode-cdn.net/home/laurent/code/market-downloader/~/mambaforge/envs/torch2/lib/python3.11/site-packages/dsp/__init__.py:3) from dsp.problem import MinimizeMaximize, SaddlePointProblem, is_dsp

File [~/mambaforge/envs/torch2/lib/python3.11/site-packages/dsp/cvxpy_integration.py:2](https://vscode-remote+ssh-002dremote-002blaurent-002dworkstation-002elan-002eetive-002ecom.vscode-resource.vscode-cdn.net/home/laurent/code/market-downloader/~/mambaforge/envs/torch2/lib/python3.11/site-packages/dsp/cvxpy_integration.py:2)
      [1](https://vscode-remote+ssh-002dremote-002blaurent-002dworkstation-002elan-002eetive-002ecom.vscode-resource.vscode-cdn.net/home/laurent/code/market-downloader/~/mambaforge/envs/torch2/lib/python3.11/site-packages/dsp/cvxpy_integration.py:1) from cvxpy import Expression, Problem
----> [2](https://vscode-remote+ssh-002dremote-002blaurent-002dworkstation-002elan-002eetive-002ecom.vscode-resource.vscode-cdn.net/home/laurent/code/market-downloader/~/mambaforge/envs/torch2/lib/python3.11/site-packages/dsp/cvxpy_integration.py:2) from cvxpy.reductions.dcp2cone.atom_canonicalizers import CANON_METHODS
      [4](https://vscode-remote+ssh-002dremote-002blaurent-002dworkstation-002elan-002eetive-002ecom.vscode-resource.vscode-cdn.net/home/laurent/code/market-downloader/~/mambaforge/envs/torch2/lib/python3.11/site-packages/dsp/cvxpy_integration.py:4) from dsp.problem import is_dsp
      [5](https://vscode-remote+ssh-002dremote-002blaurent-002dworkstation-002elan-002eetive-002ecom.vscode-resource.vscode-cdn.net/home/laurent/code/market-downloader/~/mambaforge/envs/torch2/lib/python3.11/site-packages/dsp/cvxpy_integration.py:5) from dsp.saddle_extremum import conjugate, saddle_max, saddle_min

ModuleNotFoundError: No module named 'cvxpy.reductions.dcp2cone.atom_canonicalizers'

Looking at the code of CVXPY 1.3.2, I can indeed see the required symbol: https://github.com/cvxpy/cvxpy/blob/v1.3.2/cvxpy/reductions/dcp2cone/atom_canonicalizers/__init__.py

However, CVXPY 1.4.0, renamed it as https://github.com/cvxpy/cvxpy/blob/v1.4.0/cvxpy/reductions/dcp2cone/canonicalizers/__init__.py

Negative scaling of inner() atom

Not sure I'm using it correctly, but here is a MWE:

import dsp
import cvxpy as cvx
from numpy.random import default_rng

rng = default_rng(seed=777)


def mwe():
    n = 5
    A = rng.integers(low=1, high=5, size=(n, n))
    print(A)
    x = cvx.Variable(n)
    y = cvx.Variable(n)
    constraints = [x >= 0, y >= 0, cvx.sum(x) == 1, cvx.sum(y) == 1]

    objs = [
        dsp.MinimizeMaximize(f)
        for f in (
            dsp.inner(-x, A @ y),
            dsp.inner(x, -A @ y),
            -dsp.inner(x, A @ y),
        )
    ]

    for obj in objs:
        prob = dsp.SaddlePointProblem(obj, constraints)
        prob.solve()
        print(prob.value)


if __name__ == "__main__":
    mwe()

This prints out

[[4 3 2 2 1]
 [3 2 4 2 1]
 [2 2 2 3 2]
 [3 2 3 1 2]
 [4 1 3 1 3]]
-2.00000000020348
-2.00000000020348
-2.333333332964706

Why is the third one different, and which is the right way? The docs just reference scaling in general, not restricting it to positive.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.