Skip to content

Commit

Permalink
Fix issues with CVXPY default solvers (qiskit-community#422)
Browse files Browse the repository at this point in the history
- Fixes deprecated multiplication operators
- Adds a check to set default SDP solver based on installed solvers in cvxpy, preferring 'CVXOP' if installed, then 'SCS'.
 - Changes auto method of tomography fitter to only use 'cvx' when a default solver is sound.

When checking if 'SCS' is installed, this runs a short optimization problem to see if SCS is built with BLAS support, since it cannot solve problems bigger than 2x2 matrices without it.


* Change tomography solver to check for installed CVX solvers

* Fix cvxpy deprecation warnings

* Fix check for SDP solver

* Add cvxopt solver to test envs

* Fix typo

* and another

* Install cvxopt from conda-forge
  • Loading branch information
chriseclectic committed Jun 17, 2020
1 parent cca221c commit 313c9e9
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 50 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ language: python
install: pip install -U tox pip virtualenv setuptools six
script:
- tox --notest
- .tox/$TOXENV/bin/pip install --no-cache-dir --ignore-installed 'scs<2.1.2'
- .tox/$TOXENV/bin/pip install --no-cache-dir cvxopt
- tox

notifications:
Expand Down
41 changes: 2 additions & 39 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ jobs:
Python35:
python.version: '3.5'
TOXENV: py35
OPENBLAS: https://3f23b170c54c2533c070-1c8a9b3114517dc5fe17b7c3f8c63a43.ssl.cf2.rackcdn.com/openblas-5f998ef_gcc7_1_0_win64.zip
Python36:
python.version: '3.6'
TOXENV: py36
Expand All @@ -66,44 +65,8 @@ jobs:
displayName: Create Anaconda environment
- script: |
call activate qiskit-ignis
conda install --yes --quiet --name qiskit-ignis --channel conda-forge python=%PYTHON_VERSION% scs
displayName: Install Anaconda packages
condition: ne(variables['python.version'], '3.5')
- script: |
call activate qiskit-ignis
conda install --yes --quiet --name qiskit-ignis --channel conda-forge python=%PYTHON_VERSION% osqp
displayName: Install Anaconda packages
condition: eq(variables['python.version'], '3.8')
- powershell: (new-object System.Net.WebClient).DownloadFile($env:OPENBLAS, 'c:\openblas.zip')
condition: eq(variables['python.version'], '3.5')
displayName: 'Download openblas'
- powershell: Expand-Archive c:\openblas.zip c:\openblas
condition: eq(variables['python.version'], '3.5')
displayName: 'Unzip openblas'
- script: |
call activate qiskit-ignis
conda install --yes --quiet --update-all --name qiskit-ignis python=%PYTHON_VERSION% numpy scipy
displayName: Install Anaconda packages
condition: eq(variables['python.version'], '3.5')
- script: |
call activate qiskit-ignis
set PATH=%PATH%;%BINPATH%;C:\openblas\%64%\bin;
echo %PATH%
dir C:\openblas\%ARCH%\bin
make.exe CC="gcc.exe -m64" USE_LAPACK=1 BLASLDFLAGS="-LC:\openblas\64\bin -lopenblas_5f998ef_gcc7_1_0"
make.exe install
git clone --recursive https://github.com/bodono/scs-python.git
cd scs-python
python setup.py install --scs 'CC="gcc.exe -m64" USE_LAPACK=1 BLASLDFLAGS="-LC:\openblas\64\bin -lopenblas_5f998ef_gcc7_1_0"'
cd ..
condition: eq(variables['python.version'], '3.5')
displayName: 'Install scs from source for python 3.5'
- script: |
call activate qiskit-ignis
conda install --yes --quiet --name qiskit-ignis python=%PYTHON_VERSION% mkl
displayName: 'Install MKL'
- script: |
call activate qiskit-ignis
conda config --add channels conda-forge
conda install cvxopt
python -m pip install -c constraints.txt --upgrade pip virtualenv setuptools
pip install -c constraints.txt -U tox
tox --sitepackages -e%TOXENV%
Expand Down
36 changes: 32 additions & 4 deletions qiskit/ignis/verification/tomography/fitters/base_fitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
from qiskit.result import Result
from ..basis import TomographyBasis, default_basis
from ..data import marginal_counts, combine_counts, count_keys
from .cvx_fit import cvxpy, cvx_fit
from .lstsq_fit import lstsq_fit
from .cvx_fit import cvx_fit, _HAS_CVX

# Create logger
logger = logging.getLogger(__name__)
Expand All @@ -39,6 +39,8 @@
class TomographyFitter:
"""Base maximum-likelihood estimate tomography fitter class"""

_HAS_SDP_SOLVER = None

def __init__(self,
result: Result,
circuits: Union[List[QuantumCircuit], List[str]],
Expand Down Expand Up @@ -195,10 +197,11 @@ def fit(self,
beta)
# Choose automatic method
if method == 'auto':
if cvxpy is None:
method = 'lstsq'
else:
self._check_for_sdp_solver()
if self._HAS_SDP_SOLVER:
method = 'cvx'
else:
method = 'lstsq'
if method == 'lstsq':
return lstsq_fit(data, basis_matrix,
weights=weights,
Expand Down Expand Up @@ -486,3 +489,28 @@ def _measurement_ops(self,
op = np.kron(op, meas_matrix_fn(m, outcome))
meas_ops.append(op)
return meas_ops

@classmethod
def _check_for_sdp_solver(cls):
"""Check if CVXPY solver is available"""
if cls._HAS_SDP_SOLVER is None:
if _HAS_CVX:
# pylint:disable=import-error
import cvxpy
solvers = cvxpy.installed_solvers()
if 'CVXOPT' in solvers:
cls._HAS_SDP_SOLVER = True
return
if 'SCS' in solvers:
# Try example problem to see if built with BLAS
# SCS solver cannot solver larger than 2x2 matrix
# problems without BLAS
try:
var = cvxpy.Variable((4, 4), PSD=True)
obj = cvxpy.Minimize(cvxpy.norm(var))
cvxpy.Problem(obj).solve(solver='SCS')
cls._HAS_SDP_SOLVER = True
return
except cvxpy.error.SolverError:
pass
cls._HAS_SDP_SOLVER = False
17 changes: 11 additions & 6 deletions qiskit/ignis/verification/tomography/fitters/cvx_fit.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@
# Check if CVXPY package is installed
try:
import cvxpy
_HAS_CVX = True
except ImportError:
cvxpy = None
_HAS_CVX = False


def cvx_fit(data: np.array,
Expand Down Expand Up @@ -108,7 +109,7 @@ def cvx_fit(data: np.array,
"""

# Check if CVXPY package is installed
if cvxpy is None:
if not _HAS_CVX:
raise ImportError("The CVXPY package is required to use the cvx_fit() "
"function. You can install it with 'pip install "
"cvxpy' or use a `lstsq` fitter instead of cvx_fit.")
Expand All @@ -118,7 +119,7 @@ def cvx_fit(data: np.array,
# and imaginary parts of rho seperately: rho = rho_r + 1j * rho_i

dim = int(np.sqrt(basis_matrix.shape[1]))
rho_r = cvxpy.Variable((dim, dim))
rho_r = cvxpy.Variable((dim, dim), symmetric=True)
rho_i = cvxpy.Variable((dim, dim))

# CONSTRAINTS
Expand All @@ -128,7 +129,7 @@ def cvx_fit(data: np.array,
# 1. rho_r.T = rho_r.T (real part is symmetric)
# 2. rho_i.T = -rho_i.T (imaginary part is anti-symmetric)

cons = [rho_r == rho_r.T, rho_i == -rho_i.T]
cons = [rho_i == -rho_i.T]

# Trace constraint: note this should not be used at the same
# time as the trace preserving constraint.
Expand All @@ -153,7 +154,7 @@ def cvx_fit(data: np.array,
if trace_preserving is True:
sdim = int(np.sqrt(dim))
ptr = partial_trace_super(sdim, sdim)
cons.append(ptr * cvxpy.vec(rho_r) == np.identity(sdim).ravel())
cons.append(ptr @ cvxpy.vec(rho_r) == np.identity(sdim).ravel())

# Rescale input data and matrix by weights if they are provided
if weights is not None:
Expand Down Expand Up @@ -183,7 +184,7 @@ def cvx_fit(data: np.array,
bm_r = bm_r.todense()
bm_i = bm_i.todense()

arg = bm_r * cvxpy.vec(rho_r) - bm_i * cvxpy.vec(rho_i) - np.array(data)
arg = bm_r @ cvxpy.vec(rho_r) - bm_i @ cvxpy.vec(rho_i) - np.array(data)

# SDP objective function
obj = cvxpy.Minimize(cvxpy.norm(arg, p=2))
Expand All @@ -192,6 +193,10 @@ def cvx_fit(data: np.array,
prob = cvxpy.Problem(obj, cons)
iters = 5000
max_iters = kwargs.get('max_iters', 20000)
# Set default solver if none is specified
if 'solver' not in kwargs:
if 'CVXOPT' in cvxpy.installed_solvers():
kwargs['solver'] = 'CVXOPT'

problem_solved = False
while not problem_solved:
Expand Down

0 comments on commit 313c9e9

Please sign in to comment.