Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically register custom cvxpy method #53

Merged
merged 2 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading