Skip to content

Commit ce49827

Browse files
committedJul 6, 2024·
Minor fix to define QUnfoldQUBO gurobi solver methods only if gurobipy is explicitly installed
1 parent d92c7db commit ce49827

File tree

6 files changed

+68
-65
lines changed

6 files changed

+68
-65
lines changed
 

‎CITATION.cff

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ authors:
66
- family-names: "Gasperini"
77
given-names: "Simone"
88
title: "QUnfold"
9-
version: 0.3.3
9+
version: 0.3.4
1010
doi: https://zenodo.org/badge/latestdoi/652649177
11-
date-released: 2024-07-04
11+
date-released: 2024-07-07
1212
url: "https://github.com/JustWhit3/QUnfold"

‎README.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ To install the `QUnfold` latest version released on [PyPI](https://pypi.org/proj
5050
pip install QUnfold
5151
```
5252

53+
If you also want to enable the classical [Gurobi](https://www.gurobi.com/) solver ([Python API](https://www.gurobi.com/documentation/current/refman/py_python_api_details.html)) both for the integer optimization and QUBO problem, you need to install `QUnfold` including extra requirements:
54+
```shell
55+
pip install QUnfold[gurobi]
56+
```
57+
5358
### *Developer-mode*
5459
To create a dedicated [`conda`](https://docs.conda.io/en/latest/) environment and install `QUnfold` in developer-mode you can do:
5560
```shell
@@ -58,7 +63,7 @@ conda activate qunfold-dev
5863
git clone https://github.com/JustWhit3/QUnfold.git
5964
cd QUnfold
6065
pip install -r requirements-dev.txt
61-
pip install -e .
66+
pip install -e .[gurobi]
6267
```
6368

6469
## Usage example

‎docs/source/conf.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
project = "QUnfold"
1010
copyright = "2024, Gianluca Bianco, Simone Gasperini"
1111
author = "Gianluca Bianco, Simone Gasperini"
12-
release = "0.3.3" # version
12+
release = "0.3.4" # version
1313
master_doc = "index"
1414

1515

‎setup.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = QUnfold
3-
version = 0.3.3
3+
version = 0.3.4
44
url = https://github.com/JustWhit3/QUnfold
55
author = Gianluca Bianco, Simone Gasperini
66
author_email = biancogianluca9@gmail.com, simone.gasperini4@unibo.it

‎src/QUnfold/QUnfoldQUBO.py

+42-51
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
import os
2+
import sys
23
import numpy as np
34
import scipy as sp
45
import dimod
56
import minorminer
6-
try:
7-
import gurobipy
8-
except ImportError:
9-
pass
107
from tqdm import tqdm
118
from concurrent.futures import ThreadPoolExecutor
129
from dwave.samplers import SimulatedAnnealingSampler
1310
from dwave.system import LeapHybridSampler
1411
from dwave.system import DWaveSampler, FixedEmbeddingComposite
1512

13+
try:
14+
import gurobipy
15+
except ImportError:
16+
pass
17+
1618

1719
class QUnfoldQUBO:
1820
def __init__(self, response, measured, lam=0.0):
@@ -180,53 +182,42 @@ def compute_energy(self, x):
180182
energy = self.dwave_bqm.energy(sample=xbin)
181183
return energy
182184

183-
def solve_gurobi_integer(self):
184-
try:
185+
if "gurobipy" in sys.modules:
186+
187+
def solve_gurobi_integer(self):
185188
model = gurobipy.Model()
186-
except Exception as e:
187-
import traceback
188-
import sys
189-
print(traceback.format_exc())
190-
print("\n\nGurobi not installed in this env, to install use pip install QUnfold[guro]\n\n")
191-
sys.exit(1)
192-
model.setParam("OutputFlag", 0)
193-
x = [
194-
model.addVar(vtype=gurobipy.GRB.INTEGER, lb=0, ub=2**b - 1)
195-
for b in self.num_bits
196-
]
197-
a = self.linear_coeffs
198-
B = self.quadratic_coeffs
199-
model.setObjective(a @ x + x @ B @ x, sense=gurobipy.GRB.MINIMIZE)
200-
model.optimize()
201-
sol = np.array([var.x for var in x])
202-
err = np.sqrt(sol)
203-
cov = np.diag(sol)
204-
return sol, err, cov
189+
model.setParam("OutputFlag", 0)
190+
x = [
191+
model.addVar(vtype=gurobipy.GRB.INTEGER, lb=0, ub=2**b - 1)
192+
for b in self.num_bits
193+
]
194+
a = self.linear_coeffs
195+
B = self.quadratic_coeffs
196+
model.setObjective(a @ x + x @ B @ x, sense=gurobipy.GRB.MINIMIZE)
197+
model.optimize()
198+
sol = np.array([var.x for var in x])
199+
err = np.sqrt(sol)
200+
cov = np.diag(sol)
201+
return sol, err, cov
205202

206-
def solve_gurobi_binary(self):
207-
try:
203+
def solve_gurobi_binary(self):
208204
model = gurobipy.Model()
209-
except Exception as e:
210-
import traceback
211-
import sys
212-
print(traceback.format_exc())
213-
print("\n\nGurobi not installed in this env, to install use pip install QUnfold[guro]\n\n")
214-
sys.exit(1)
215-
model.setParam("OutputFlag", 0)
216-
num_bits = self.num_bits
217-
x = [
218-
model.addVar(vtype=gurobipy.GRB.BINARY)
219-
for i in range(self.num_bins)
220-
for _ in range(num_bits[i])
221-
]
222-
Q = self.qubo_matrix
223-
model.setObjective(x @ Q @ x, sense=gurobipy.GRB.MINIMIZE)
224-
model.optimize()
225-
bitstr = np.array([var.x for var in x], dtype=int)
226-
arrays = np.split(bitstr, np.cumsum(num_bits[:-1]))
227-
sol = np.array(
228-
[int("".join(arr.astype(str))[::-1], base=2) for arr in arrays], dtype=float
229-
)
230-
err = np.sqrt(sol)
231-
cov = np.diag(sol)
232-
return sol, err, cov
205+
model.setParam("OutputFlag", 0)
206+
num_bits = self.num_bits
207+
x = [
208+
model.addVar(vtype=gurobipy.GRB.BINARY)
209+
for i in range(self.num_bins)
210+
for _ in range(num_bits[i])
211+
]
212+
Q = self.qubo_matrix
213+
model.setObjective(x @ Q @ x, sense=gurobipy.GRB.MINIMIZE)
214+
model.optimize()
215+
bitstr = np.array([var.x for var in x], dtype=int)
216+
arrays = np.split(bitstr, np.cumsum(num_bits[:-1]))
217+
sol = np.array(
218+
[int("".join(arr.astype(str))[::-1], base=2) for arr in arrays],
219+
dtype=float,
220+
)
221+
err = np.sqrt(sol)
222+
cov = np.diag(sol)
223+
return sol, err, cov

‎src/QUnfold/utils.py

+16-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import numpy as np
2+
import scipy as sp
3+
from QUnfold import QUnfoldQUBO
24

35

46
def normalize_response(response, truth_mc):
@@ -31,20 +33,25 @@ def compute_chi2(observed, expected, covariance):
3133
chi2 = residuals.T @ np.linalg.pinv(covariance) @ residuals
3234
chi2_red = chi2 / len(expected)
3335
return chi2_red
34-
3536

36-
from QUnfold import QUnfoldQUBO
37-
from QUnfold.utils import compute_chi2
38-
from scipy.optimize import minimize_scalar
39-
from scipy.optimize import minimize_scalar
4037

41-
def lambda_optimizer(response, measured, truth, maxlam = 1, minlam = 0):
42-
def lamba_func(lam = .1):
38+
def lambda_optimizer(
39+
response, measured, truth, min_lam=0.0, max_lam=1.0, verbose=False
40+
):
41+
def lambda_func(lam):
4342
unfolder = QUnfoldQUBO(response, measured, lam=lam)
4443
unfolder.initialize_qubo_model()
45-
sol, err, cov = unfolder.solve_gurobi_integer()
44+
sol, _, cov = unfolder.solve_gurobi_integer()
4645
chi2 = compute_chi2(observed=sol, expected=truth, covariance=cov)
4746
return chi2
4847

49-
minimizer = minimize_scalar(lamba_func,bracket=(minlam,maxlam),method='brent' ,options={'disp':3})
48+
try:
49+
options = {"disp": 3 if verbose else 0}
50+
minimizer = sp.optimize.minimize_scalar(
51+
lambda_func, bracket=(min_lam, max_lam), method="brent", options=options
52+
)
53+
except AttributeError:
54+
raise ModuleNotFoundError(
55+
"Function 'lambda_optimizer' requires Gurobi solver: 'pip install gurobipy'"
56+
)
5057
return minimizer.x

0 commit comments

Comments
 (0)
Please sign in to comment.