A QUBO, Quadratic Unconstrained Binary Optimization, is a type of optimization problem where the aim is to find the best combination of binary choices (0/1) to maximize or minimize a quadratic objective function. It involves no constraints except that the variables are binary.
The QUBO model's significance in combinatorial optimization is heightened by its equivalence to the Ising model, which is prominent in physics. Consequently, the broad range of optimization problems solved effectively by state-of-the-art QUBO solution methods are joined by an important domain of problems arising in physics applications.
Note: This repository contains two versions of the QUBO tool. The GAMS implementation is in the gams_qubo folder. The Python-based GAMSPy implementation is in the gamspy_qubo folder. The GAMSPy implementation will be the primary focus of all future updates and active maintenance.
The reformulation is derived from, A Tutorial on Formulating and Using QUBO Models. Glover, F., Kochenberger, G., & Du, Y. 2019.
git clone git@git.gams.com:devel/qubo.git
cd qubo/gamspy_quboThis project uses uv for environment management. You can install the base package or include specific dependency groups for development or quantum backend access.
Base installation:
uv syncInstalling specific dependency groups:
You can include optional groups using the --group flag:
- dev: Tools for testing and linting (mypy, ruff, pytest, etc.).
- dwave: Includes
dwave-ocean-sdkfor solving on D-Wave hardware. - kipu: Includes
planqk-service-sdkfor Kipu Quantum integration.
# Example: Install with dev and dwave support
uv sync --group dev --group dwaveYou can also install all dependencies at once with uv sync --all-group.
macOS / Linux:
python3 -m venv .venv
source .venv/bin/activateWindows:
python -m venv .venv
.venv\Scripts\activateThen, install using requirements.txt:
pip install -r requirements.txtThis will not install the dependencies for the quantum backends.
gamspy_qubo/
├── src/
│ └── gamspy_qubo/
│ ├── __init__.py
│ └── qubo.py
├── examples/
│ └── ...
├── tests/
│ └── ...
├── pyproject.toml
├── requirements.txt
├── README.md
└── .gitignore
Once installed, you can use it like:
from gamspy_qubo import QuboOr run example scripts:
uv run examples/qap.pyFollowing are some examples included to test the QUBO reformulation.
- setPacking.py, Set Packing Problem (max)
- O1program.py, General 0/1 Problem (max)
- QAP.py, Quadratic Assignment Problem (min)
- setPartition.py, Set Partitioning Problem (min)
- QKP.py, Quadratic Knapsack Problem (max)
- generalIP.py, a general integer problem (max)
- qplib_5881.py, a flat/scalar py file (max)
- knights.py, A Max problem from GAMS modlib
- Q01.py, Quadratic 0/1 Problem (min)
- flightGate.py, Flight gate assignment Problem (min)
- maxColorSubgraphs.py, Maximum colorable subgraph problem (min)
- tsp.py, Traveling Salesman Problem (min)
Once your GAMSPy problem is defined and a gp.Model object is instantiated, you can solve it through the QUBO reformulation by wrapping your model in the Qubo class.
from gamspy_qubo import Qubo
# 1. Wrap your existing GAMSPy model
qubo_model = Qubo(
model=demo_model, # Your gp.Model instance
name="QUBO", # (Optional) Name for the reformulated model
penalty=10, # (Optional) Penalty factor for constraints (default: 1)
)
# 2. Solve the QUBO model
# By default, solver="cplex". You can also specify quantum backends.
q.solve(solver="dwave", num_reads=1000)(Note: Generating the API key and setting up the required python environments for dwave or kipu must be done prior to using them as a solver backend).
solver: Choice of solver backend. Supported classical solvers are standard GAMSPy MIQCP solvers (default:"cplex"). Supported quantum backends sofar are"dwave"and"kipu".**kwargs: Any additional backend-specific arguments. For example, if using"dwave", you can pass arguments likenum_reads=1000. If using a classic solver, arguments are passed natively to GAMSPy's solve method.
The GAMSPy integration automatically translates the QUBO solution back to your problem's original scope. Upon a successful run, your initial model variables are directly populated with the new levels.
Note: In order to binarize the right hand side of each constraint, the reformulation needs to generate slack variables depending on the RHS. The number of binary variables to be generated for a number
- The reformulation does not work with continuous variables with the exception of the free objective variable.
- When binarizing integer variables they must have an upper bound less than or equal to 1e4.
- The coefficients for the variables in the constraints must be integers.
- The use of quadratic terms is limited to binary variables where levels are not equal to 1.
- Although the objective function can have quadratic terms, the constraints cannot have any quadratic terms. This is due to the penalization step. The step requires the constraint to be squared which results in a polynomial of degree greater than 2. This becomes a problem with 3 or more interacting variables. For example,
$(xy + yz)^2 = (xy)^2 + (yz)^2 + 2xy^2z$ . Here, the term$2xy^2z$ is problematic since it cannot be reduced to bilinear terms. - The reformulation expects the objective function to be defined via the use of a scalar equation, i.e., the symbol
iobjmust be nonzero in the gdx file created by Convert.
The reformulation will throw appropriate exceptions if the limitations are not satisfied.
A penalty value that is too large can impede the solution process as the penalty terms overwhelm the original objective function information, making it difficult to distinguish the quality of one solution from another. On the other hand, a penalty value that is too small jeopardizes the search for feasible solutions. Generally, there is a ‘Goldilocks region’ of considerable size that contains penalty values that work well. A little preliminary thought about the model can yield a ballpark estimate of the original objective function value. Taking P to be some percentage (75% to 150%) of this estimate is often a good place to start. In the end, solutions generated can always be checked for feasibility, leading to changes in penalties and further rounds of the solution process as needed to zero in on an acceptable solution.
There are tests available that can be run using the pytest package. You can install it via,
uv sync --group devThe tests can then be run via pytest tests\test_qubo.py
The original tool was developed for the GAMS modeling language. While this repository now focuses on the GAMSPy (Python) implementation, the classic GAMS version remains available in the GAMS subfolder.
The QUBO reformulation tool has been developed under the financial support of:
- ProvideQ (BMWi project, ID: 01MQ22006D)
- QuSol (BMFTR project, ID: 13N17172)