https://github.com/cvxgrp/cvxpygen

Code generation with CVXPY

https://github.com/cvxgrp/cvxpygen

Science Score: 36.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
    Found .zenodo.json file
  • DOI references
  • Academic publication links
  • Committers with academic emails
    1 of 9 committers (11.1%) from academic institutions
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (14.6%) to scientific vocabulary

Keywords from Contributors

convex-optimization numerical-optimization cvxpy mathematical-optimization modeling-language optimization-modeling portfolio-optimization quadratic-programming
Last synced: 10 months ago · JSON representation

Repository

Code generation with CVXPY

Basic Info
  • Host: GitHub
  • Owner: cvxgrp
  • License: apache-2.0
  • Language: Python
  • Default Branch: master
  • Size: 974 KB
Statistics
  • Stars: 158
  • Watchers: 13
  • Forks: 23
  • Open Issues: 13
  • Releases: 33
Created over 5 years ago · Last pushed 12 months ago
Metadata Files
Readme License

README.md

CVXPYgen: Code generation with CVXPY

CVXPYgen takes a convex optimization problem family modeled with CVXPY and generates a custom solver implementation in C. This generated solver is specific to the problem family and accepts different parameter values. In particular, this solver is suitable for deployment on embedded systems. In addition, CVXPYgen creates a Python wrapper for prototyping and desktop (non-embedded) applications.

An overview of CVXPYgen can be found in our slides and manuscript.

CVXPYgen accepts CVXPY problems that are compliant with Disciplined Convex Programming (DCP). DCP is a system for constructing mathematical expressions with known curvature from a given library of base functions. CVXPY uses DCP to ensure that the specified optimization problems are convex. In addition, problems need to be modeled according to Disciplined Parametrized Programming (DPP). Solving a DPP-compliant problem repeatedly for different values of the parameters can be much faster than repeatedly solving a new problem.

For now, CVXPYgen is a separate module, until it will be integrated into CVXPY. As of today, CVXPYgen works with linear, quadratic, and second-order cone programs. It also supports differentiating through quadratic programs and computing an explicit solution to linear and quadratic programs.

This package has similar functionality as the package cvxpy_codegen, which appears to be unsupported.

Installation

pip install cvxpygen

If you wish to use the Clarabel solver, you need to install Rust and Eigen.

The example notebooks located in examples/ require matplotlib.

Windows users: CVXPYgen is tested with Visual Studio 2019 and 2022, newer and older versions might work as well.

Example

We define a simple 'nonnegative least squares' problem, generate code for it, and solve the problem with example parameter values.

1. Generate Code

Let's step through the first part of examples/main.py. Define a convex optimization problem the way you are used to with CVXPY. Everything that is described as cp.Parameter() is assumed to be changing between multiple solves. For constant properties, use cp.Constant().

```python import cvxpy as cp

m, n = 3, 2 x = cp.Variable(n, name='x') A = cp.Parameter((m, n), name='A', sparsity=((0, 0, 1), (0, 1, 1))) b = cp.Parameter(m, name='b') problem = cp.Problem(cp.Minimize(cp.sum_squares(A @ x - b)), [x >= 0]) ```

Specify the name attribute for variables and parameters to recognize them after generating code. The attribute sparsity is a tuple of row and column indices of the nonzero entries of matrix A. Parameter sparsity is only taken into account for matrices.

Assign parameter values and test-solve.

```python import numpy as np

np.random.seed(0) A.value = np.zeros((m, n)) A.value[0, 0] = np.random.randn() A.value[0, 1] = np.random.randn() A.value[1, 1] = np.random.randn() b.value = np.random.randn(m) problem.solve() ```

Generating C code for this problem is as simple as,

```python from cvxpygen import cpg

cpg.generatecode(problem, codedir='nonneg_LS', solver='SCS') ```

where the generated code is stored inside nonneg_LS and the SCS solver is used. Next to the positional argument problem, all keyword arguments for the generate_code() method are summarized below.

| Argument | Meaning | Type | Default | | ------------- | ------------- | ------------- | ------------- | | code_dir | directory for code to be stored in | String | 'CPG_code' | | solver | canonical solver to generate code with | String | CVXPY default | | solver_opts | options passed to canonical solver | Dict | None | | enable_settings| enabled settings that are otherwise locked by embedded solver | List of Strings | [] | | unroll | unroll loops in canonicalization code | Bool | False | | prefix | prefix for unique code symbols when dealing with multiple problems | String | '' | | wrapper | compile Python wrapper for CVXPY interface | Bool | True | | gradient | enable differentiation (works for linear and quadratic programs) | Bool | False |

You can find an overview of the code generation result in nonneg_LS/README.html.

2. Solve & Compare

As summarized in the second part of examples/main.py, after assigning parameter values, you can solve the problem both conventionally and via the generated code, which is wrapped inside the custom CVXPY solve method cpg_solve.

```python import time import sys

import extension module and register custom CVXPY solve method

from nonnegLS.cpgsolver import cpgsolve problem.registersolve('CPG', cpg_solve)

solve problem conventionally

t0 = time.time() val = problem.solve(solver='SCS') t1 = time.time() print('\nCVXPY\nSolve time: %.3f ms\n' % (1000*(t1-t0))) print('Primal solution: x = [%.6f, %.6f]\n' % tuple(x.value)) print('Dual solution: d0 = [%.6f, %.6f]\n' % tuple(problem.constraints[0].dual_value)) print('Objective function value: %.6f\n' % val)

solve problem with C code via python wrapper

t0 = time.time() val = problem.solve(method='CPG', updatedparams=['A', 'b'], verbose=False) t1 = time.time() print('\nCVXPYgen\nSolve time: %.3f ms\n' % (1000 * (t1 - t0))) print('Primal solution: x = [%.6f, %.6f]\n' % tuple(x.value)) print('Dual solution: d0 = [%.6f, %.6f]\n' % tuple(problem.constraints[0].dualvalue)) print('Objective function value: %.6f\n' % val) ```

The argument updated_params specifies which user-defined parameter values are new. If the argument is omitted, all parameter values are assumed to be new. If only a subset of the user-defined parameters have new values, use this argument to speed up the solver.

Most solver settings can be specified as keyword arguments like without code generation. Here, we use verbose=False to suppress printing. The list of changeable settings differs by solver and is documented in <code_dir>/README.html after code generation.

Comparing the standard and codegen methods for this example, both the solutions and objective values are close. Especially for smaller problems like this, the new solve method 'CPG' is significantly faster than solving without code generation.

3. Executable

In the C code, all of your parameters and variables are stored as vectors via Fortran-style flattening (vertical index moves fastest). For example, the (i, j)-th entry of the original matrix with height h will be the i+j*h-th entry of the flattened matrix in C. For sparse parameters, i.e. matrices, the k-th entry of the C array is the k-th nonzero entry encountered when proceeding through the parameter column by column.

Before compiling the example executable, make sure that CMake 3.5 or newer is installed.

On Unix platforms, run the following commands in your terminal to compile and run the program:

bash cd nonneg_LS/c/build cmake .. cmake --build . --target cpg_example ./cpg_example

On Windows, type:

bash cd nonneg_LS\c\build cmake .. cmake --build . --target cpg_example --config release Release\cpg_example

Differentiating through problems

CVXPYgen supports differentiating through quadratic programs. To enable this feature, set gradient=True when generating code. You can use the generated code together with CVXPYlayers as

```python cpg.generatecode(problem, codedir='code_diff', gradient=True)

from codediff.cpgsolver import forward, backward from cvxpylayers.torch import CvxpyLayer

layer = CvxpyLayer(problem, parameters=[A, b], variables=[x], custom_method=(forward, backward)) ```

See our manuscript for more details and examples/paper_grad for three practical examples (from our manuscript), in the areas of machine learning, control, and finance.

Explicitly solving problems

For quadratic programs in which the coefficients of the linear objective terms and the righthand side of the constraints are affine functions of a parameter, the solution is a piecewise affine function of the parameter.

The number of (polyhedral) regions in the solution map can grow exponentially in problem size (specifically, the number of inequality constraints), but when the number of regions is moderate, a so-called explicit solver is practical. Such a solver computes the coefficients of the affine functions and the linear inequalities defining the polyhedral regions offline; to solve a problem instance online it simply evaluates this explicit solution map. Potential advantages of an explicit solver over a more general purpose iterative solver can include transparency, interpretability, reliability, and speed.

CVXPYgen can generate such explicit solvers. To enable this feature, set solver='explicit' when generating code. By default, only the primal solution is computed. To also compute the dual solution, pass solver_opts={'dual': True}. You can choose to store the explicit solution in half precision (instead of single precision), by setting 'fp16': True in solver_opts. Limits on parameters are encouraged and can be represented as standard CVXPY constraints. As of now, we support simple bounds of the form [l <= p, p <= u] where p is a cp.Parameter() and l and u are constants.

See our manuscript for more details. You can control the maximum number of floating point numbers in the explicit solution via 'max_floats' (default is 1e6) and the maximum number of regions via 'max_regions' (default is 500) in the solver_opts dict.

CVXPYgen uses PDAQP to construct explicit solutions.

Tests

To run tests, install pytest via

bash conda install pytest

and execute:

bash cd tests pytest

Owner

  • Name: Stanford University Convex Optimization Group
  • Login: cvxgrp
  • Kind: organization
  • Location: Stanford, CA

GitHub Events

Total
  • Create event: 23
  • Release event: 12
  • Issues event: 20
  • Watch event: 27
  • Delete event: 2
  • Issue comment event: 52
  • Push event: 72
  • Pull request review comment event: 13
  • Pull request review event: 12
  • Pull request event: 36
  • Fork event: 6
Last Year
  • Create event: 23
  • Release event: 12
  • Issues event: 20
  • Watch event: 27
  • Delete event: 2
  • Issue comment event: 52
  • Push event: 72
  • Pull request review comment event: 13
  • Pull request review event: 12
  • Pull request event: 36
  • Fork event: 6

Committers

Last synced: about 1 year ago

All Time
  • Total Commits: 426
  • Total Committers: 9
  • Avg Commits per committer: 47.333
  • Development Distribution Score (DDS): 0.223
Past Year
  • Commits: 129
  • Committers: 2
  • Avg Commits per committer: 64.5
  • Development Distribution Score (DDS): 0.465
Top Committers
Name Email Commits
maximilianschaller 7****r 331
govindchari g****1@g****m 60
maximilianschaller 7****r 17
Steven Diamond d****d@c****u 12
jpreiss j****s@g****m 2
phschiele p****e@g****m 1
Greg Horn g****d@g****m 1
Alan Yang a****7@g****m 1
Akshay Agrawal a****7@g****m 1
Committer Domains (Top 20 + Academic)

Issues and Pull Requests

Last synced: 10 months ago

All Time
  • Total issues: 40
  • Total pull requests: 64
  • Average time to close issues: about 1 month
  • Average time to close pull requests: 4 days
  • Total issue authors: 31
  • Total pull request authors: 11
  • Average comments per issue: 2.5
  • Average comments per pull request: 0.42
  • Merged pull requests: 53
  • Bot issues: 0
  • Bot pull requests: 0
Past Year
  • Issues: 14
  • Pull requests: 42
  • Average time to close issues: 11 days
  • Average time to close pull requests: 3 days
  • Issue authors: 10
  • Pull request authors: 4
  • Average comments per issue: 2.64
  • Average comments per pull request: 0.4
  • Merged pull requests: 35
  • Bot issues: 0
  • Bot pull requests: 0
Top Authors
Issue Authors
  • langestefan (3)
  • leogrillo (2)
  • tpbarron (2)
  • The-SS (2)
  • estevaoms (2)
  • gibor1992 (2)
  • ghorn (2)
  • ilyanoskov (2)
  • Charlescai123 (1)
  • maxschaller (1)
  • chenyx09 (1)
  • scout222 (1)
  • karthyyy (1)
  • htrocks (1)
  • jfpios (1)
Pull Request Authors
  • maxschaller (35)
  • govindchari (14)
  • darnstrom (6)
  • Feiyang472 (2)
  • zdryan (1)
  • SteveDiamond (1)
  • phschiele (1)
  • syanga (1)
  • langestefan (1)
  • jpreiss (1)
  • ghorn (1)
Top Labels
Issue Labels
Pull Request Labels

Dependencies

environment.yml conda
  • cmake >=3.5
  • cvxpy >=1.1.18
  • pybind11 >=2.8.0
  • python >=3.6
cvxpygen/template/setup.py pypi
  • cvxpy *
setup.py pypi
  • cmake *
  • cvxpy *
  • numpy *
  • osqp *
  • pybind11 *
  • scipy *
.github/workflows/CI.yml actions
  • actions/checkout v2 composite
  • actions/setup-python v2 composite