Skip to content

Commit

Permalink
Merge pull request #53 from cvxgrp/ms/register-module
Browse files Browse the repository at this point in the history
Automatically register custom cvxpy method
  • Loading branch information
maxschaller authored Oct 10, 2024
2 parents bac4c2e + 0a1053d commit 2ea24a4
Show file tree
Hide file tree
Showing 8 changed files with 20 additions and 19 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This generated solver is specific to the problem family and accepts different pa
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 [manuscript](https://web.stanford.edu/~boyd/papers/cvxpygen.html).
An overview of CVXPYgen can be found in our [slides and manuscript](https://web.stanford.edu/~boyd/papers/cvxpygen.html).

CVXPYgen accepts CVXPY problems that are compliant with [Disciplined Convex Programming (DCP)](https://www.cvxpy.org/tutorial/dcp/index.html).
DCP is a system for constructing mathematical expressions with known curvature from a given library of base functions.
Expand Down Expand Up @@ -118,7 +118,7 @@ import sys

# import extension module and register custom CVXPY solve method
from nonneg_LS.cpg_solver import cpg_solve
problem.register_solve('cpg', cpg_solve)
problem.register_solve('CPG', cpg_solve)

# solve problem conventionally
t0 = time.time()
Expand All @@ -131,7 +131,7 @@ sys.stdout.write('Objective function value: %.6f\n' % val)

# solve problem with C code via python wrapper
t0 = time.time()
val = problem.solve(method='cpg', updated_params=['A', 'b'], verbose=False)
val = problem.solve(method='CPG', updated_params=['A', 'b'], verbose=False)
t1 = time.time()
sys.stdout.write('\nCVXPYgen\nSolve time: %.3f ms\n' % (1000 * (t1 - t0)))
sys.stdout.write('Primal solution: x = [%.6f, %.6f]\n' % tuple(x.value))
Expand All @@ -148,7 +148,7 @@ 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.
Especially for smaller problems like this, the new solve method ``'CPG'`` is significantly faster than solving without code generation.

### 3. Executable

Expand Down
4 changes: 4 additions & 0 deletions cvxpygen/cpg.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import shutil
import pickle
import warnings
import importlib

from cvxpygen import utils
from cvxpygen.utils import write_file, read_write_file, write_example_def, write_module_prot, write_module_def, \
Expand Down Expand Up @@ -89,6 +90,9 @@ def generate_code(problem, code_dir='CPG_code', solver=None, solver_opts=None,

if wrapper:
compile_python_module(code_dir)
module = importlib.import_module(f'{code_dir}.cpg_solver')
cpg_solve = getattr(module, 'cpg_solve')
problem.register_solve('CPG', cpg_solve)


def get_quad_obj(problem, solver_type, solver_opts, solver_class) -> bool:
Expand Down
7 changes: 4 additions & 3 deletions cvxpygen/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def write_vec_def(f, vec, name, typ):
"""
Write vector to file
"""
vec = replace_inf(vec)
f.write(f'{typ} {name}[{len(vec)}] = {{\n')

# Write vector components
Expand Down Expand Up @@ -260,7 +261,7 @@ def write_param_def(f, param, name, prefix, suffix):
if name.isupper():
write_mat_def(f, param, f'{prefix}canon_{name}{suffix}')
elif name == 'd':
f.write(f'cpg_float {prefix}canon_d{suffix} = %.20f;\n' % param[0])
f.write(f'cpg_float {prefix}canon_d{suffix} = %.20f;\n' % replace_inf(param[0]))
else:
write_vec_def(f, param, f'{prefix}canon_{name}{suffix}', 'cpg_float')
f.write('\n')
Expand Down Expand Up @@ -463,9 +464,9 @@ def write_workspace_def(f, configuration, variable_info, dual_variable_info, par
if p_id == 'd':
canon_casts.append('')
else:
write_param_def(f, replace_inf(p), p_id, configuration.prefix, '')
write_param_def(f, p, p_id, configuration.prefix, '')
if solver_interface.inmemory_preconditioning:
write_param_def(f, replace_inf(p), p_id, configuration.prefix, '_conditioning')
write_param_def(f, p, p_id, configuration.prefix, '_conditioning')
if p_id.isupper():
canon_casts.append('')
else:
Expand Down
6 changes: 1 addition & 5 deletions examples/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@
2. Solve & Compare
'''

# import extension module and register custom CVXPY solve method
from nonneg_LS.cpg_solver import cpg_solve
problem.register_solve('cpg', cpg_solve)

# solve problem conventionally
t0 = time.time()
val = problem.solve(solver='SCS')
Expand All @@ -49,7 +45,7 @@

# solve problem with C code via python wrapper
t0 = time.time()
val = problem.solve(method='cpg', updated_params=['A', 'b'], verbose=False)
val = problem.solve(method='CPG', updated_params=['A', 'b'], verbose=False)
t1 = time.time()
sys.stdout.write('\nCVXPYgen\nSolve time: %.3f ms\n' % (1000 * (t1 - t0)))
sys.stdout.write('Primal solution: x = [%.6f, %.6f]\n' % tuple(x.value))
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

MAJOR = 0
MINOR = 3
MICRO = 5
MICRO = 6
VERSION = '%d.%d.%d' % (MAJOR, MINOR, MICRO)


Expand Down
4 changes: 2 additions & 2 deletions tests/test_E2E_LP.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ def test(name, solver, style, seed):
with open('test_%s_%s_%s/problem.pickle' % (name, solver, style), 'rb') as f:
prob = pickle.load(f)

module = importlib.import_module('test_%s_%s_%s.cpg_solver' % (name, solver, style))
prob.register_solve('CPG', module.cpg_solve)
#module = importlib.import_module('test_%s_%s_%s.cpg_solver' % (name, solver, style))
#prob.register_solve('CPG', module.cpg_solve)

prob = assign_data(prob, name, seed)

Expand Down
4 changes: 2 additions & 2 deletions tests/test_E2E_QP.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,8 @@ def test(name, solver, style, seed):
with open('test_%s_%s_%s/problem.pickle' % (name, solver, style), 'rb') as f:
prob = pickle.load(f)

module = importlib.import_module('test_%s_%s_%s.cpg_solver' % (name, solver, style))
prob.register_solve('CPG', module.cpg_solve)
#module = importlib.import_module('test_%s_%s_%s.cpg_solver' % (name, solver, style))
#prob.register_solve('CPG', module.cpg_solve)

prob = assign_data(prob, name, seed)

Expand Down
4 changes: 2 additions & 2 deletions tests/test_E2E_SOCP.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ def test(name, solver, style, seed):
with open('test_%s_%s_%s/problem.pickle' % (name, solver, style), 'rb') as f:
prob = pickle.load(f)

module = importlib.import_module('test_%s_%s_%s.cpg_solver' % (name, solver, style))
prob.register_solve('CPG', module.cpg_solve)
#module = importlib.import_module('test_%s_%s_%s.cpg_solver' % (name, solver, style))
#prob.register_solve('CPG', module.cpg_solve)

prob = assign_data(prob, name, seed)

Expand Down

0 comments on commit 2ea24a4

Please sign in to comment.