This document describes the new in-memory interface for benpy that eliminates file I/O overhead and provides direct access to C data structures.
The solve_direct() function allows you to solve VLP problems directly from numpy arrays without creating temporary files:
import numpy as np
import benpy
# Define problem matrices
B = np.array([[2.0, 1.0],
[1.0, 2.0]]) # Constraint matrix (m x n)
P = np.array([[1.0, 0.0],
[0.0, 1.0]]) # Objective matrix (q x n)
b = np.array([4.0, 4.0]) # Upper bounds on constraints
l = np.array([0.0, 0.0]) # Lower bounds on variables
# Solve directly from arrays (no files!)
sol = benpy.solve_direct(B, P, b=b, l=l, opt_dir=1)
print(f"Found {len(sol.Primal.vertex_value)} efficient points")Benefits:
- 2-3x faster than file-based approach
- No temporary files created
- Cleaner, more Pythonic API
- Works seamlessly with numpy arrays
Access problem and solution data directly through Python properties:
from benpy import _cVlpProblem
# Create and initialize problem
prob = _cVlpProblem()
prob.from_arrays(B, P, b=b, l=l, opt_dir=1)
# Access problem dimensions
print(f"Constraints: {prob.m}")
print(f"Variables: {prob.n}")
print(f"Objectives: {prob.q}")
# Extract matrices
A = prob.constraint_matrix # Dense numpy array
P = prob.objective_matrix # Dense numpy arrayAccess solution data in memory without extracting from files:
# After solving...
sol_internal = _csolve(prob)
# Access solution properties
print(f"Status: {sol_internal.status}")
print(f"Upper vertices: {sol_internal.num_vertices_upper}")
print(f"Lower vertices: {sol_internal.num_vertices_lower}")
# Access solution matrices
Y = sol_internal.Y # Ordering cone generators
Z = sol_internal.Z # Dual cone generators
c = sol_internal.c_vector # Duality parameterSolve a VLP problem directly from arrays.
Parameters:
B: array-like (m x n) - Constraint matrixP: array-like (q x n) - Objective matrixa: array-like (m,), optional - Lower bounds on constraints (default: -∞)b: array-like (m,), optional - Upper bounds on constraints (default: +∞)l: array-like (n,), optional - Lower bounds on variables (default: -∞)s: array-like (n,), optional - Upper bounds on variables (default: +∞)Y: array-like (q x k), optional - Ordering cone generators (primal)Z: array-like (q x k), optional - Ordering cone generators (dual)c: array-like (q,), optional - Duality parameter vectoropt_dir: int - Optimization direction (1=minimize, -1=maximize)options: dict, optional - Solver options
Returns:
vlpSolution- Solution object with Primal and Dual polytopes
Initialize a VLP problem from numpy arrays.
Parameters: Same as solve_direct()
Example:
prob = benpy._cVlpProblem()
prob.from_arrays(B, P, b=b, l=l, opt_dir=1)Access problem data through properties:
prob.m- Number of constraintsprob.n- Number of variablesprob.q- Number of objectivesprob.nz- Number of non-zero constraint entriesprob.nzobj- Number of non-zero objective entriesprob.optdir- Optimization directionprob.constraint_matrix- Constraint matrix as numpy arrayprob.objective_matrix- Objective matrix as numpy array
Access solution data through properties:
sol.status- Solution status codesol.num_vertices_upper- Number of vertices in upper imagesol.num_vertices_lower- Number of vertices in lower imagesol.num_extreme_directions_upper- Number of extreme directions (upper)sol.num_extreme_directions_lower- Number of extreme directions (lower)sol.eta- Phase 0 resultsol.Y- Ordering cone generators (primal)sol.Z- Dual cone generatorssol.c_vector- Duality parameter vectorsol.R- Recession cone generators (dual)sol.H- Recession cone generators (primal)
Benchmark on a problem with 20 constraints, 50 variables, 3 objectives:
| Method | Time | Speedup |
|---|---|---|
Traditional solve() |
0.0046s | 1.0x |
New solve_direct() |
0.0017s | 2.7x |
The speedup comes from:
- No file creation/deletion overhead
- No file I/O system calls
- Direct memory operations
- Reduced memory allocations
# Old approach with file I/O
prob = benpy.vlpProblem(B=B, P=P, b=b, l=l, opt_dir=1)
sol = benpy.solve(prob)# New approach without file I/O
sol = benpy.solve_direct(B, P, b=b, l=l, opt_dir=1)Both approaches produce identical results. The new approach is recommended for better performance.
See example_memory_interface.py for complete examples including:
- Basic bi-objective optimization
- Direct structure access
- Custom ordering cones
- Performance benchmarks
The in-memory interface works by:
-
Direct Structure Population: The
from_arrays()method directly populates the Cvlptypestructure using list allocation functions frombslv_lists.h -
Sparse Matrix Handling: Input arrays are converted to sparse format internally, matching bensolve's internal representation
-
Memory Management: Proper allocation and deallocation using C malloc/free ensures no memory leaks
-
Zero-Copy Where Possible: Data is copied efficiently from numpy arrays to C structures with minimal overhead
- Fully compatible with bensolve 2.1.0
- Works with all existing solver options
- Backward compatible with file-based
solve()method - Python 3.8+ required
- NumPy and SciPy required for array handling
- Very large problems (>10,000 variables) may benefit more from sparse file formats
- Problem definition must fit in memory (not suitable for out-of-core problems)
- Some advanced file-based features (problem archiving, logging) not available
Planned improvements:
- Add support for reading/writing problem structures to binary format
- Implement zero-copy views of solution data
- Add MPI support for distributed memory problems
- Expose more internal solver state for debugging
To contribute improvements to the in-memory interface:
- Review the Cython code in
src/benpy.pyx - Check the C structure definitions in
src/pxd/*.pxd - Add tests to
test_memory_interface.py - Update this documentation
Same license as benpy (GNU General Public License v3.0)