Skip to content

Commit

Permalink
Merge pull request #136 from BoxiLi/qutip5
Browse files Browse the repository at this point in the history
Support qutip-v5

- Add support of qutip=5.0 to `setup.cfg`
- New GitHub actions are added to test qutip-qip against the dev.major version of qutip
- Fix the importation errors for `qutip.Options`, use `qutip.SolverOptions` instead.
- Use `Qobj.full()` to transfer the matrix to NumPy arrays before using `assert_all_close`, since `Qobj.__array__` is removed.
- Tests are adapted for the new QobjEvo in qutip.
  - Replace the `QobjEvo.ops` and `QobjEvo.cte` as they are removed. Create separate test functions to compare `QobjEvo`s.
  - Remove the use of `QobjEvo.tlist`.
  - Slightly modify the pulse and noise module to correctly handle the continuous/discrete pulse in `QobjEvo`.
- Fix/Improve some other unstable/wrong tests.
  • Loading branch information
BoxiLi committed May 15, 2022
2 parents 2fb741a + 8b373ad commit 5662577
Show file tree
Hide file tree
Showing 21 changed files with 410 additions and 202 deletions.
58 changes: 44 additions & 14 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,54 +10,71 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: [3.6, 3.9]
os: [ubuntu-latest, windows-latest, macOS-latest]

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
python-version: 3.9
- name: Install qutip-qip
run: |
python -m pip install --upgrade pip
pip install -e .[tests]
- name: Test with pytest
- name: Test with pytest and generate coverage report
run: |
pytest tests --strict-config --strict-markers --verbosity=1
pip install pytest-cov coveralls
pytest tests --strict-markers --cov=qutip_qip --cov-report=
- name: Upload to Coveralls
env:
GITHUB_TOKEN: ${{ secrets.github_token }}
COVERALLS_FLAG_NAME: ${{ matrix.qutip-version }}
COVERALLS_PARALLEL: true
run: coveralls --service=github

test-qutip-support:
# test for qutip master branch
runs-on: ubuntu-latest

strategy:
matrix:
# TODO: add dev.major and minimal supported qutip version v4.6
qutip-version: ['master']

matrix:
qutip-version: ['==4.6.*', '==4.7.*', '@dev.major']

steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.8
python-version: 3.6
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install numpy scipy cython matplotlib pytest pytest-cov coveralls
- name: Install qutip
python -m pip install pytest pytest-cov coveralls
- name: Install QuTiP from PyPI
if: ${{ ! startsWith( matrix.qutip-version, '@') }}
run: python -m pip install 'qutip${{ matrix.qutip-version }}'

- name: Install QuTiP from GitHub
if: ${{ startsWith( matrix.qutip-version, '@') }}
run: |
pip install git+https://github.com/qutip/qutip.git
python -m pip install numpy scipy cython
python -m pip install 'git+https://github.com/qutip/qutip.git${{ matrix.qutip-version }}'
- name: Install qutip-qip
# Installing in-place so that coveralls can locate the source code.
run: |
pip install -e .
pip install -e .[full]
- name: Test with pytest and generate coverage report
run: |
pip install pytest-cov coveralls
pytest tests --strict-markers --cov=qutip_qip --cov-report=
- name: Upload to Coveralls
env:
GITHUB_TOKEN: ${{ secrets.github_token }}
COVERALLS_FLAG_NAME: ${{ matrix.qutip-version }}
COVERALLS_PARALLEL: true
run: coveralls --service=github

doctest:
Expand All @@ -83,3 +100,16 @@ jobs:
python -m pip install joblib pytest pytest-custom_exit_code
cd doc/pulse-paper
pytest *.py --suppress-no-test-exit-code
finalise:
name: Finalise coverage reporting
needs: [test, test-qutip-support]
runs-on: ubuntu-latest
container: python:3-slim
steps:
- name: Finalise coverage reporting
env:
GITHUB_TOKEN: ${{ secrets.github_token }}
run: |
python -m pip install coveralls
coveralls --service=github --finish
12 changes: 9 additions & 3 deletions doc/pulse-paper/customize.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@
plt.rcParams.update({"text.usetex": False, "font.size": 10})
from joblib import Parallel, delayed # for parallel simulations
import numpy as np
from qutip import sigmax, sigmay, sigmaz, basis, qeye, tensor, Qobj, fock_dm
from qutip import (
fidelity, sigmax, sigmay, sigmaz, basis, qeye, tensor, Qobj, fock_dm)
from qutip_qip.circuit import QubitCircuit, Gate
from qutip_qip.device import ModelProcessor, Model
from qutip_qip.compiler import GateCompiler, Instruction
from qutip import Options, fidelity
import qutip
from packaging.version import parse as parse_version
if parse_version(qutip.__version__) < parse_version('5.dev'):
from qutip import Options as SolverOptions
else:
from qutip import SolverOptions
from qutip_qip.noise import Noise


Expand Down Expand Up @@ -213,7 +219,7 @@ def single_crosstalk_simulation(num_gates):
init_state = tensor(
[Qobj([[init_fid, 0], [0, 0.025]]), Qobj([[init_fid, 0], [0, 0.025]])]
)
options = Options(nsteps=10000) # increase the maximal allowed steps
options = SolverOptions(nsteps=10000) # increase the maximal allowed steps
e_ops = [tensor([qeye(2), fock_dm(2)])] # observable

# compute results of the run using a solver of choice with custom options
Expand Down
3 changes: 2 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ include_package_data = True
install_requires =
numpy>=1.16.6
scipy>=1.0
qutip>=4.6,<5
qutip>=4.6
packaging
setup_requires =
packaging

Expand Down
8 changes: 4 additions & 4 deletions src/qutip_qip/compiler/cavityqedcompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ class CavityQEDCompiler(GateCompiler):
... qc, compiler=compiler) # doctest: +NORMALIZE_WHITESPACE
({'sz0': array([ 0. , 2500. , 2500.01315789]),
'sz1': array([ 0. , 2500. , 2500.01315789]),
'g0': array([ 0., 2500.]),
'g1': array([ 0., 2500.])},
'g0': array([ 0. , 2500. , 2500.01315789]),
'g1': array([ 0. , 2500. , 2500.01315789])},
{'sz0': array([-0.5, -9.5]),
'sz1': array([-0.5, -9.5]),
'g0': array([0.01]),
'g1': array([0.01])})
'g0': array([0.01, 0. ]),
'g1': array([0.01, 0. ])})
Notice that the above example is equivalent to using directly
the :obj:`.DispersiveCavityQED`.
Expand Down
14 changes: 14 additions & 0 deletions src/qutip_qip/compiler/gatecompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ def _concatenate_pulses(
Concatenate compiled pulses coefficients and tlist for each pulse.
If there is idling time, add zeros properly to prevent wrong spline.
"""
min_step_size = np.inf
# Concatenate tlist and coeffs for each control pulses
compiled_tlist = [[] for tmp in range(num_controls)]
compiled_coeffs = [[] for tmp in range(num_controls)]
Expand All @@ -223,6 +224,7 @@ def _concatenate_pulses(
step_size,
pulse_mode,
) = self._process_gate_pulse(start_time, tlist, coeff)
min_step_size = min(step_size, min_step_size)

if abs(last_pulse_time) < step_size * 1.0e-6: # if first pulse
compiled_tlist[pulse_ind].append([0.0])
Expand All @@ -247,6 +249,18 @@ def _concatenate_pulses(
compiled_tlist[pulse_ind].append(execution_time)
compiled_coeffs[pulse_ind].append(coeffs)

final_time = np.max([tlist[-1] for tlist in compiled_tlist])
for pulse_ind in range(num_controls):
if not compiled_tlist[pulse_ind]:
continue
last_pulse_time = compiled_tlist[pulse_ind][-1][-1]
if np.abs(final_time - last_pulse_time) > min_step_size * 1.0e-6:
idling_tlist = self._process_idling_tlist(
pulse_mode, final_time, last_pulse_time, min_step_size
)
compiled_tlist[pulse_ind].append(idling_tlist)
compiled_coeffs[pulse_ind].append(np.zeros(len(idling_tlist)))

for i in range(num_controls):
if not compiled_coeffs[i]:
compiled_tlist[i] = None
Expand Down
4 changes: 2 additions & 2 deletions src/qutip_qip/compiler/spinchaincompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ class SpinChainCompiler(GateCompiler):
>>> compiler = SpinChainCompiler(2, params=model.params, setup="linear")
>>> processor.load_circuit(
... qc, compiler=compiler) # doctest: +NORMALIZE_WHITESPACE
({'sx0': array([0., 1.]), 'sz1': array([0. , 0.25])},
{'sx0': array([0.25]), 'sz1': array([1.])})
({'sx0': array([0., 1.]), 'sz1': array([0. , 0.25, 1. ])},
{'sx0': array([0.25]), 'sz1': array([1., 0.])})
Notice that the above example is equivalent to using directly
the :obj:`.LinearSpinChain`.
Expand Down
2 changes: 1 addition & 1 deletion src/qutip_qip/decompose/_utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def check_gate(gate, num_qubits):
"""
if not isinstance(gate, Qobj):
raise TypeError("The input matrix is not a Qobj.")
if not gate.check_isunitary():
if not gate.isunitary:
raise ValueError("Input is not unitary.")
if gate.dims != [[2] * num_qubits] * 2:
raise ValueError(f"Input is not a unitary on {num_qubits} qubits.")
9 changes: 8 additions & 1 deletion src/qutip_qip/device/cavityqed.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,14 @@ def eliminate_auxillary_modes(self, U):
[basis(self.num_levels, 0)]
+ [identity(2) for n in range(self.num_qubits)]
)
return psi_proj.dag() * U * psi_proj
result = psi_proj.dag() * U * psi_proj
# In qutip 5 multiplication of matrices
# with dims [[1, 2], [2, 2]] and [[2, 2], [1, 2]]
# will give a result of
# dims [[1, 2], [1, 2]] instead of [[2], [2]].
if result.dims[0][0] == 1:
result = result.ptrace(list(range(len(self.dims)))[1:])
return result

def load_circuit(self, qc, schedule_mode="ASAP", compiler=None):
if compiler is None:
Expand Down
28 changes: 21 additions & 7 deletions src/qutip_qip/device/processor.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from packaging.version import parse as parse_version
from collections.abc import Iterable
import warnings
from copy import deepcopy
Expand All @@ -21,6 +22,12 @@
from ..pulse import Pulse, Drift, _merge_qobjevo, _fill_coeff


if parse_version(qutip.__version__) >= parse_version("5.dev"):
is_qutip5 = True
else:
is_qutip5 = False


__all__ = ["Processor"]


Expand Down Expand Up @@ -536,7 +543,7 @@ def get_full_coeffs(self, full_tlist=None):
)
elif self.spline_kind == "cubic":
coeffs_list.append(
_fill_coeff(pulse.coeff, pulse.tlist, full_tlist, {})
_fill_coeff(pulse.coeff, pulse.tlist, full_tlist)
)
else:
raise ValueError("Unknown spline kind.")
Expand Down Expand Up @@ -936,6 +943,7 @@ def get_noisy_pulses(self, device_noise=False, drift=False):
t1=self.t1,
t2=self.t2,
device_noise=device_noise,
spline_kind=self.spline_kind,
)
if drift:
drift_obj = self._get_drift_obj()
Expand Down Expand Up @@ -990,13 +998,17 @@ def get_qobjevo(self, args=None, noisy=False):
qu_list.append(qu)

final_qu = _merge_qobjevo(qu_list)
final_qu.args.update(args)
if is_qutip5:
final_qu.arguments(args)
else:
final_qu.args.update(args)

# bring all c_ops to the same tlist, won't need it in QuTiP 5
temp = []
for c_op in c_ops:
temp.append(_merge_qobjevo([c_op], final_qu.tlist))
c_ops = temp
if not parse_version(qutip.__version__) >= parse_version("5.dev"):
temp = []
for c_op in c_ops:
temp.append(_merge_qobjevo([c_op], final_qu.tlist))
c_ops = temp

if noisy:
return final_qu, c_ops
Expand Down Expand Up @@ -1181,7 +1193,9 @@ def run_state(
tlist = kwargs["tlist"]
del kwargs["tlist"]
else:
tlist = noisy_qobjevo.tlist
# TODO, this can be simplified further, tlist in the solver only
# determines the time step for intermediate result.
tlist = self.get_full_tlist()
if solver == "mesolve":
evo_result = mesolve(
H=noisy_qobjevo, rho0=init_state, tlist=tlist, **kwargs
Expand Down
12 changes: 10 additions & 2 deletions src/qutip_qip/noise.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@


def process_noise(
pulses, noise_list, dims, t1=None, t2=None, device_noise=False
pulses,
noise_list,
dims,
t1=None,
t2=None,
device_noise=False,
spline_kind=None,
):
"""
Apply noise to the input list of pulses. It does not modify the input
Expand Down Expand Up @@ -55,7 +61,9 @@ def process_noise(
"""
noise_list = noise_list.copy()
noisy_pulses = deepcopy(pulses)
systematic_noise = Pulse(None, None, label="systematic_noise")
systematic_noise = Pulse(
None, None, label="systematic_noise", spline_kind=spline_kind
)

if (t1 is not None) or (t2 is not None):
noise_list.append(RelaxationNoise(t1, t2))
Expand Down

0 comments on commit 5662577

Please sign in to comment.