Blog Post

FICO® Xpress Optimization Blog
7 MIN READ

"Xpress Everywhere" Part 2: Python Ecosystem (Pyomo, CVXPY, PuLP, PyOptInterface, Linopy)

FICO's avatar
FICO
Community Manager
7 months ago
This blog is part of the "Xpress Everywhere" series, showcasing how FICO® Xpress integrates with popular open-source optimization frameworks across different languages and modeling paradigms.

Read Part 1:Google OR-Tools with FICO® Xpress

Python is widely used for data science and machine learning. But when it comes to mathematical optimization, the ecosystem offers a diverse array of choices: PuLP is often a first choice for newcomers, Pyomo for production systems, Linopy for data-driven workflows, PyOptInterface for performance, CVXPY for convex problems. How do you choose?
Here's the good news: you don't have to sacrifice solver performance to use your favorite modeling library. FICO® Xpress integrates seamlessly with all of them, bringing enterprise-grade LP, QP, and MIP solving to whatever Python workflow you prefer. The Xpress development team actively contributes to these open-source projects, ensuring robust and up-to-date integrations.

In this post, we'll solve the same portfolio optimization problem using five different Python libraries, each with Xpress as the solver backend. You'll see how each library's syntax differs while achieving the same optimal results. Complete, runnable examples demonstrating advanced features like SOS constraints, warm starts, and callbacks are available in our GitHub repository.

The Problem: Mean-Variance Portfolio Optimization

Every investor faces the same fundamental trade-off: higher returns typically come with higher risk. The classic Markowitz mean-variance model captures this mathematically:
Given:
  • *n* assets with expected returns μ_i and covariance matrix P
  • Budget constraint (weights sum to 1)
  • Sector diversification limits
  • Optional: cardinality constraints (limit number of assets)
Find: Portfolio weights wᵢ that minimize risk (variance) while achieving a target return:
w' P w
s.t.    Σ_i wᵢ = 1,    μ' w ≥ r_target,    w ≥ 0
This problem combines:
  • Quadratic objective (variance minimization) - showcases QP capabilities
  • Linear constraints (budget, return target, sector limits)
  • Binary variables (optional, for cardinality) - showcases MIP capabilities

PuLP: The Beginner-Friendly Choice

PuLP offers the gentlest learning curve. Its syntax reads almost like English, using the += operator to add constraints and objectives to the model. This makes PuLP code remarkably readable even for those new to optimization.

Since PuLP doesn't support quadratic objectives, our example maximizes expected return (a linear objective) rather than minimizing variance like the QP-capable libraries:



To use Xpress as the solver, PuLP provides the XPRESS_PY class which uses the Python API directly:



PuLP also supports MIP warmstart to accelerate solving by providing an initial feasible solution:



Key strengths: Simplicity and readability. PuLP's clean syntax makes it ideal for learning optimization, prototyping, and linear/mixed-integer problems where ease of understanding matters most.

For a complete runnable example with LP and MIP variants, see the full PuLP portfolio example.

Pyomo: The Production-Grade Workhorse

Pyomo supports a broad spectrum of optimization: LP, QP, MIP, and more. Pyomo uses a formal algebraic modeling language with explicit sets, parameters, and rule-based constraints. This higher-level abstraction pays off in large-scale production systems where structure and maintainability matter:



Pyomo offers two Xpress interfaces: xpress_direct and xpress_persistent. Both use the Xpress Python API directly. The xpress_direct interface rebuilds the entire Xpress model from the Pyomo model on each solve, while xpress_persistent keeps the model alive between solves for incremental updates.

The solver.options dictionary accepts any Xpress control name. Besides TIMELIMIT, you can use WORKLIMIT for deterministic, machine-independent stopping - useful when you need reproducible results across different hardware:



Pyomo's SOS constraints use a declarative rule-based syntax. Each SOS1 is created per sector, allowing at most one asset per sector to have non-zero weight:



Key strengths: Flexibility and structure. From simple LP to complex QP, Pyomo scales with your needs. The explicit algebraic structure makes large models easier to maintain.

For a complete runnable example with QP and SOS constraints, see the full Pyomo portfolio example.

Linopy: The Pandas-Native Approach

Linopy speaks the language of data scientists: pandas and xarray. If you're coming from a data science background, Linopy feels natural because variables and constraints use familiar pandas indexing rather than optimization-specific syntax. Originally developed for the PyPSA energy system modeling framework, it excels at multi-dimensional problems:



Linopy communicates with Xpress via LP file I/O (unlike Pyomo's direct interface). Solver parameters are passed as keyword arguments:



The .sel() method is xarray's label-based indexing - use it to query specific assets by name rather than numeric index.

Linopy offers two constraint modeling styles. For beginners, use explicit loops with labeled indexing:



For more compact code, Linopy's pandas-native design enables vectorized operations - applying constraints across entire variable arrays in a single line:



Where Linopy truly shines is multi-dimensional problems like time-indexed optimization. Compare this to the nested loops you'd need in PuLP or Pyomo:



Key strengths: Data integration. Perfect for scenario analysis, time-series optimization, and any workflow with pandas/xarray data. Linopy also supports quadratic objectives with Xpress, making it viable for mean-variance optimization.

For a complete runnable example with multi-period optimization, see the full Linopy portfolio example.

PyOptInterface: Maximum Performance

PyOptInterface takes a fundamentally different approach: instead of building a modeling abstraction, it provides thin C++ bindings that call Xpress functions directly. This eliminates the overhead of expression tree building and constraint translation that high-level APIs incur. Benchmarks show significantly faster model building compared to high-level APIs like Pyomo - critical for applications that rebuild models frequently:



PyOptInterface offers two modeling styles. For building expressions incrementally or reusing expressions, use explicit ExprBuilder objects, which provide 4-5x performance gains for large models:



For more readable code, PyOptInterface also supports a compact algebraic syntax using quicksum (similar to other Python libraries):



To solve the model and retrieve results:



Note: PyOptInterface includes Xpress support by default. The Xpress library is loaded dynamically at runtime - no special compiler flags needed.

Key strengths: Performance and control. PyOptInterface also uniquely supports NLP formulas (trigonometric and other nonlinear functions), in addition to QP, SOCP, and SOS. When you need callbacks, incremental model updates, or are rebuilding models in a loop, PyOptInterface provides direct C++ bindings to Xpress with minimal overhead.

For a complete runnable example with QP and MIP warmstart, see the full PyOptInterface portfolio example.

CVXPY: Disciplined Convex Programming

CVXPY represents a philosophically different approach to optimization modeling. Rather than giving you full control over solver-specific constructs, CVXPY enforces Disciplined Convex Programming (DCP) rules that guarantee your problem is convex. If you violate these rules, CVXPY refuses to solve - catching modeling errors at formulation time rather than after invoking the solver:



CVXPY automatically selects an appropriate solver, but you can explicitly request Xpress. CVXPY handles the translation to Xpress's QP format internally:



Where CVXPY truly excels is second-order cone programming (SOCP). Risk constraints can be expressed as norm bounds, which CVXPY automatically reformulates for Xpress:



Key strengths: Mathematical elegance and safety. CVXPY catches non-convex formulations before you waste time debugging solver failures. The tradeoff: problems must fit DCP rules, and CVXPY's MIP support with Xpress is more limited than the dedicated MIP libraries.

For a complete runnable example with QP, SOCP, and Sharpe ratio optimization, see the full CVXPY portfolio example.

Feature Comparison

Not all libraries expose the same Xpress capabilities. Here's what each supports:
FeaturePuLPPyomoLinopyPyOptInterfaceCVXPY
LP✓✓✓✓✓
QP Objective✓✓✓✓
QCQP✓✓✓
MIP✓✓✓✓✓
SOCP✓✓
NLP✓
SOS Constraints✓✓✓
Warm Start✓✓✓✓✓
Solver Parameters✓✓✓✓✓
Callbacks✓
Direct API (in memory)✓✓✓✓

Choosing Your Library

Start with PuLP if you're new to optimization. The gentle learning curve and excellent documentation make it ideal for learning.
Use Pyomo for production systems requiring LP, QP, and MIP. Its rich constraint modeling, direct solver interface, and extensive features handle industrial-scale problems.
Choose Linopy for data-driven workflows. Its pandas integration makes time-indexed and multi-dimensional models natural.
Pick PyOptInterface when performance is critical. The direct C++ bindings minimize overhead for model building and solving.
Select CVXPY for convex optimization in ML/finance contexts. Its automatic reformulation and verification catch modeling errors before they become solver errors.
Use the Xpress Python API directly when you need full access to all Xpress Solver API functionality across all problem types (LP, QP, QCQP, MIP, SOCP, NLP). The native Python interface provides complete control over all solver features, callbacks, and parameters without abstraction layers.

Getting Started

1. Install the libraries via pip:



For Xpress itself, get a free Community License at fico.com.
2. Explore the examples:
Clone the xpress-3rdparty-interfaces repository for complete, runnable examples demonstrating each library with Xpress.
Alternatively, run the examples directly on GitHub Codespaces (no local installation needed):
  1. Navigate to the repository: xpress-3rdparty-interfaces
  2. Click the green "Code" button and select "Codespaces"
  3. Create a new codespace - Xpress Community License is pre-configured
  4. All libraries including PyOptInterface work out of the box

Conclusion

Python's optimization ecosystem offers something for everyone: simplicity (PuLP), flexibility (Pyomo), data integration (Linopy), performance (PyOptInterface), and mathematical elegance (CVXPY). With FICO Xpress as the solver backend, you don't sacrifice performance for convenience.

For maximum control and access to Xpress's full capabilities—including advanced features like indicator constraints, infeasibility analysis, all callback types, multi-objective optimization, n-best solutions search and complete parameter access—use the native Xpress Python API. These third-party libraries provide convenient abstractions, but direct API access unlocks every Xpress feature.

The best library is the one that fits your workflow. And with Xpress, they all deliver the same enterprise-grade solutions.

Next in the series: Part 3 explores JuMP in Julia - where compile-time optimization meets runtime optimization.
──────────────────────────────────────────────────
Have questions or feedback? Join the discussion on the FICO Community.
Updated 7 months ago
Version 3.0
No CommentsBe the first to comment