From 9a493460be5b6a6c9a3be4cbe9c77c7a9bc406e8 Mon Sep 17 00:00:00 2001 From: Boxi Li Date: Tue, 3 Aug 2021 13:08:02 +0200 Subject: [PATCH 1/7] Fix the leaking noise objects (#89) A list of noise objects defined by the user is passed to the function. However, when adding T1 and T2 noise, the generated objects were added directly to the list. This changes the `noise_list` defined by the user and is a problem if the same `Processor` instance is used to perform more than one simulation. The T1,T2 noise objects from the previous run will accumulate in the `noise_list`. Therefore, we copy the list before add T1 T2 noise. --- src/qutip_qip/noise.py | 1 + tests/test_processor.py | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/qutip_qip/noise.py b/src/qutip_qip/noise.py index 26bebaed..91b73ddc 100644 --- a/src/qutip_qip/noise.py +++ b/src/qutip_qip/noise.py @@ -44,6 +44,7 @@ def process_noise(pulses, noise_list, dims, t1=None, t2=None, noisy_pulses: list of :class:`.Pulse` The noisy pulses, including the system noise. """ + noise_list = noise_list.copy() noisy_pulses = deepcopy(pulses) systematic_noise = Pulse(None, None, label="systematic_noise") diff --git a/tests/test_processor.py b/tests/test_processor.py index 847e8fe5..f76e3f09 100644 --- a/tests/test_processor.py +++ b/tests/test_processor.py @@ -365,3 +365,11 @@ def test_pulse_dict(self): processor = Processor(1) processor.add_control(sigmax(), 0, label="test") assert("test" in processor.get_pulse_dict()) + + def test_repeated_use_of_processor(self): + processor = Processor(1, t1=1.) + processor.add_pulse( + Pulse(sigmax(), targets=0, coeff=True)) + result1 = processor.run_state(basis(2, 0), tlist=np.linspace(0, 1, 10)) + result2 = processor.run_state(basis(2, 0), tlist=np.linspace(0, 1, 10)) + assert_allclose(result1.states[-1].full(), result2.states[-1].full()) From bc62df878030d29434c84691c7447ebce50b5e7d Mon Sep 17 00:00:00 2001 From: Boxi Li Date: Wed, 8 Sep 2021 22:20:13 +0200 Subject: [PATCH 2/7] Merge pull request #92 from BoxiLi/update_from_qutip Minor bug fix and docstring improvement from qutip.qip --- src/qutip_qip/circuit.py | 2 ++ tests/test_processor.py | 11 ++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/qutip_qip/circuit.py b/src/qutip_qip/circuit.py index e75cb591..f5c326dc 100644 --- a/src/qutip_qip/circuit.py +++ b/src/qutip_qip/circuit.py @@ -394,6 +394,8 @@ class QubitCircuit: A list of integer for the dimension of each composite system. e.g [2,2,2,2,2] for 5 qubits system. If None, qubits system will be the default option. + num_cbits : int + Number of classical bits in the system. Examples -------- diff --git a/tests/test_processor.py b/tests/test_processor.py index f76e3f09..ddbb2236 100644 --- a/tests/test_processor.py +++ b/tests/test_processor.py @@ -174,8 +174,10 @@ def testPlot(self): processor.add_control(sigmaz()) processor.pulses[0].tlist = tlist processor.pulses[0].coeff = np.array([np.sin(t) for t in tlist]) - processor.plot_pulses() - plt.clf() + fig, _ = processor.plot_pulses() + # testing under Xvfb with pytest-xvfb complains if figure windows are + # left open, so we politely close it: + plt.close(fig) # cubic spline tlist = np.linspace(0., 2*np.pi, 20) @@ -184,7 +186,10 @@ def testPlot(self): processor.pulses[0].tlist = tlist processor.pulses[0].coeff = np.array([np.sin(t) for t in tlist]) processor.plot_pulses() - plt.clf() + fig, _ = processor.plot_pulses() + # testing under Xvfb with pytest-xvfb complains if figure windows are + # left open, so we politely close it: + plt.close(fig) def testSpline(self): """ From 64d52b8630e8590eed0e2e638544272778dac776 Mon Sep 17 00:00:00 2001 From: Asier Galicia <57414022+AGaliciaMartinez@users.noreply.github.com> Date: Thu, 14 Oct 2021 14:25:36 +0200 Subject: [PATCH 3/7] Hadamard transform efficient (#103) Added tests for Hadamard and improved its construction efficiency. --- doc/source/qip-simulator.rst | 76 +++++++++++++++---------------- src/qutip_qip/operations/gates.py | 7 ++- tests/test_gates.py | 17 +++++++ 3 files changed, 58 insertions(+), 42 deletions(-) diff --git a/doc/source/qip-simulator.rst b/doc/source/qip-simulator.rst index c20bbfd1..8f6385de 100644 --- a/doc/source/qip-simulator.rst +++ b/doc/source/qip-simulator.rst @@ -39,7 +39,7 @@ method. .. testcode:: - from qutip import tensor + from qutip import tensor, basis zero_state = tensor(basis(2, 0), basis(2, 0), basis(2, 0)) result = qc.run(state=zero_state) wstate = result @@ -93,42 +93,42 @@ outputs, we can use the :meth:`.QubitCircuit.run_statistics` function: .. testoutput:: :options: +NORMALIZE_WHITESPACE - State: - Quantum object: dims = [[2, 2, 2], [1, 1, 1]], shape = (8, 1), type = ket - Qobj data = - [[0.] - [1.] - [0.] - [0.] - [0.] - [0.] - [0.] - [0.]] - with probability 0.33333257054168813 - State: - Quantum object: dims = [[2, 2, 2], [1, 1, 1]], shape = (8, 1), type = ket - Qobj data = - [[0.] - [0.] - [1.] - [0.] - [0.] - [0.] - [0.] - [0.]] - with probability 0.33333257054168813 - State: - Quantum object: dims = [[2, 2, 2], [1, 1, 1]], shape = (8, 1), type = ket - Qobj data = - [[0.] - [0.] - [0.] - [0.] - [1.] - [0.] - [0.] - [0.]] - with probability 0.33333485891662384 + State: + Quantum object: dims = [[2, 2, 2], [1, 1, 1]], shape = (8, 1), type = ket + Qobj data = + [[0.] + [1.] + [0.] + [0.] + [0.] + [0.] + [0.] + [0.]] + with probability 0.3333325705416881 + State: + Quantum object: dims = [[2, 2, 2], [1, 1, 1]], shape = (8, 1), type = ket + Qobj data = + [[0.] + [0.] + [1.] + [0.] + [0.] + [0.] + [0.] + [0.]] + with probability 0.3333325705416881 + State: + Quantum object: dims = [[2, 2, 2], [1, 1, 1]], shape = (8, 1), type = ket + Qobj data = + [[0.] + [0.] + [0.] + [0.] + [1.] + [0.] + [0.] + [0.]] + with probability 0.33333485891662384 The function returns a :class:`~.Result` object which contains the output states. @@ -186,7 +186,7 @@ The :class:`.CircuitSimulator` class also enables stepping through the circuit: [0. ] [0. ]] -This only excutes one gate in the circuit and +This only executes one gate in the circuit and allows for a better understanding of how the state evolution takes place. The method steps through both the gates and the measurements. diff --git a/src/qutip_qip/operations/gates.py b/src/qutip_qip/operations/gates.py index 09055444..292d9119 100644 --- a/src/qutip_qip/operations/gates.py +++ b/src/qutip_qip/operations/gates.py @@ -924,11 +924,10 @@ def hadamard_transform(N=1): Quantum object representation of the N-qubit Hadamard gate. """ - data = 2 ** (-N / 2) * np.array([[(-1) ** _hamming_distance(i & j) - for i in range(2 ** N)] - for j in range(2 ** N)]) + data = [[1, 1], [1, -1]] + H = Qobj(data) / np.sqrt(2) - return Qobj(data, dims=[[2] * N, [2] * N]) + return tensor([H] * N) def _flatten(lst): diff --git a/tests/test_gates.py b/tests/test_gates.py index 8363a0f2..f4811391 100644 --- a/tests/test_gates.py +++ b/tests/test_gates.py @@ -332,6 +332,23 @@ def test_cnot_explicit(self): [0, 0, 0, 1, 0, 0, 0, 0]]) np.testing.assert_allclose(test, expected, atol=1e-15) + def test_hadamard_explicit(self): + test = gates.hadamard_transform(3).full() + expected = np.array( + [ + [1, 1, 1, 1, 1, 1, 1, 1], + [1, -1, 1, -1, 1, -1, 1, -1], + [1, 1, -1, -1, 1, 1, -1, -1], + [1, -1, -1, 1, 1, -1, -1, 1], + [1, 1, 1, 1, -1, -1, -1, -1], + [1, -1, 1, -1, -1, 1, -1, 1], + [1, 1, -1, -1, -1, -1, 1, 1], + [1, -1, -1, 1, -1, 1, 1, -1], + ] + ) + expected = expected / np.sqrt(8) + np.testing.assert_allclose(test, expected) + def test_cyclic_permutation(self): operators = [qutip.sigmax(), qutip.sigmaz()] test = gates.expand_operator(qutip.tensor(*operators), N=3, From f4e7054b946316eaec2480bdd7b46e008660cd9e Mon Sep 17 00:00:00 2001 From: Boxi Li Date: Sat, 13 Nov 2021 12:01:12 +0100 Subject: [PATCH 4/7] Update VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 17e51c38..d917d3e2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.1 +0.1.2 From 61f979d98ce4a9b45f868883d1500600f77b0090 Mon Sep 17 00:00:00 2001 From: Boxi Li Date: Sat, 20 Nov 2021 11:00:50 +0100 Subject: [PATCH 5/7] Fix a bug for the wrong tlist in c_ops (#107) The get_full_tlist method only considers the tlist in the ideal pulse. If the Lindblad noise has a different time dependency, this will cause inconsistency. Use the tlist of the generated QobjEvo, which already includes all the time steps. --- src/qutip_qip/device/processor.py | 3 +-- tests/test_noise.py | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/qutip_qip/device/processor.py b/src/qutip_qip/device/processor.py index 95e13c96..37092499 100644 --- a/src/qutip_qip/device/processor.py +++ b/src/qutip_qip/device/processor.py @@ -669,10 +669,9 @@ def get_qobjevo(self, args=None, noisy=False): final_qu.args.update(args) # bring all c_ops to the same tlist, won't need it in QuTiP 5 - full_tlist = self.get_full_tlist() temp = [] for c_op in c_ops: - temp.append(_merge_qobjevo([c_op], full_tlist)) + temp.append(_merge_qobjevo([c_op], final_qu.tlist)) c_ops = temp if noisy: diff --git a/tests/test_noise.py b/tests/test_noise.py index 1236e289..7beeca57 100644 --- a/tests/test_noise.py +++ b/tests/test_noise.py @@ -4,9 +4,9 @@ from qutip import ( tensor, qeye, sigmaz, sigmax, sigmay, destroy, identity, QobjEvo, - fidelity, basis + fidelity, basis, sigmam ) -from qutip_qip.device import Processor, SCQubits +from qutip_qip.device import Processor, SCQubits, LinearSpinChain from qutip_qip.noise import ( RelaxationNoise, DecoherenceNoise, ControlAmpNoise, RandomNoise, ZZCrossTalk, Noise) @@ -50,6 +50,22 @@ def test_decoherence_noise(self): assert_allclose(c_ops[0].tlist, tlist) assert_allclose(c_ops[1].ops[0].qobj, tensor(qeye(2), sigmax())) + def test_collapse_with_different_tlist(self): + """ + Test if there are errors raised because of wrong tlist handling. + """ + qc = QubitCircuit(1) + qc.add_gate("X", 0) + proc = LinearSpinChain(1) + proc.load_circuit(qc) + tlist = np.linspace(0, 30., 100) + coeff = tlist * 0.1 + noise = DecoherenceNoise( + sigmam(), targets=0, + coeff=coeff, tlist=tlist) + proc.add_noise(noise) + proc.run_state(basis(2, 0)) + def test_relaxation_noise(self): """ Test for the relaxation noise From 5a3346d62bbda3997558e85ca0078e9615c5e63d Mon Sep 17 00:00:00 2001 From: Boxi Li Date: Sun, 21 Nov 2021 21:09:31 +0100 Subject: [PATCH 6/7] Make circuit latex code accessible (#108) - QubitCircuit.latex_code() now returns the full latex code, which can be saved as a tex file and compiled externally. - The algorithm will print the latex code if pdflatex failed. This simplifies the debugging process. --- src/qutip_qip/circuit.py | 13 ++++++++++++- src/qutip_qip/circuit_latex.py | 17 ++++++----------- tests/test_circuit.py | 12 +++++++++++- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/qutip_qip/circuit.py b/src/qutip_qip/circuit.py index f5c326dc..ce31711e 100644 --- a/src/qutip_qip/circuit.py +++ b/src/qutip_qip/circuit.py @@ -1807,7 +1807,7 @@ def latex_code(self): code += r" & %s" % rows[m][n] code += r" & \qw \\ " + "\n" - return code + return _latex_template % code # This slightly convoluted dance with the conversion formats is because # image conversion has optional dependencies. We always want the `png` and @@ -1868,6 +1868,17 @@ def _to_qasm(self, qasm_out): op._to_qasm(qasm_out) +_latex_template = r""" +\documentclass{standalone} +\usepackage[braket]{qcircuit} +\renewcommand{\qswap}{*=<0em>{\times}} +\begin{document} +\Qcircuit @C=1cm @R=1cm { +%s} +\end{document} +""" + + class CircuitResult: def __init__(self, final_states, probabilities, cbits=None): diff --git a/src/qutip_qip/circuit_latex.py b/src/qutip_qip/circuit_latex.py index 6f8405e8..a19c9e6c 100644 --- a/src/qutip_qip/circuit_latex.py +++ b/src/qutip_qip/circuit_latex.py @@ -39,16 +39,6 @@ import tempfile import warnings -_latex_template = r""" -\documentclass{standalone} -\usepackage[braket]{qcircuit} -\renewcommand{\qswap}{*=<0em>{\times}} -\begin{document} -\Qcircuit @C=1cm @R=1cm { -%s} -\end{document} -""" - def _run_command(command, *args, **kwargs): """ @@ -228,7 +218,7 @@ def image_from_latex(code, file_type="png"): try: os.chdir(temporary_dir) with open(filename + ".tex", "w") as file: - file.write(_latex_template % code) + file.write(code) try: _run_command((_pdflatex, '-interaction', 'batchmode', filename)) @@ -238,6 +228,11 @@ def image_from_latex(code, file_type="png"): " Perhaps you do not have it installed, or you are" " missing the LaTeX package 'qcircuit'." ) + message += ( + "The latex code is printed below. " + "Please try to compile locally using pdflatex:\n" + + code + ) raise RuntimeError(message) from e _crop_pdf(filename + ".pdf") if file_type in _MISSING_CONVERTERS: diff --git a/tests/test_circuit.py b/tests/test_circuit.py index 304731b2..27e8f500 100644 --- a/tests/test_circuit.py +++ b/tests/test_circuit.py @@ -655,10 +655,20 @@ def test_wstate(self): np.testing.assert_allclose(probs_initial, probs_final) assert sum(result_cbits[i]) == 1 + _latex_template = r""" +\documentclass{standalone} +\usepackage[braket]{qcircuit} +\renewcommand{\qswap}{*=<0em>{\times}} +\begin{document} +\Qcircuit @C=1cm @R=1cm { +%s} +\end{document} +""" + def test_latex_code_teleportation_circuit(self): qc = _teleportation_circuit() latex = qc.latex_code() - assert latex == "\n".join([ + assert latex == self._latex_template % "\n".join([ r" & \lstick{c1} & \qw & \qw & \qw & \qw" r" & \qw \cwx[4] & \qw & \qw & \ctrl{2} & \qw \\ ", r" & \lstick{c0} & \qw & \qw & \qw & \qw" From bf02c915b3f69fd74b2172849f724d575fc705cc Mon Sep 17 00:00:00 2001 From: Boxi Li Date: Wed, 24 Nov 2021 19:20:47 +0100 Subject: [PATCH 7/7] Update Changelog --- doc/source/changelog.rst | 57 ++++++++++++++++++++++++++++++++++++++++ doc/source/index.rst | 6 +++++ 2 files changed, 63 insertions(+) create mode 100644 doc/source/changelog.rst diff --git a/doc/source/changelog.rst b/doc/source/changelog.rst new file mode 100644 index 00000000..c0db99ee --- /dev/null +++ b/doc/source/changelog.rst @@ -0,0 +1,57 @@ +.. _changelog: + +********** +Change Log +********** + +Version 0.1.2 (Nov 25, 2021) +++++++++++++++++++++++++++++ +This micro release adds more thorough documentation for the project and fixes a few bugs in :obj:`.QubitCircuit` and :obj:`.Processor`. + +PRs are collected at `https://github.com/qutip/qutip-qip/milestone/4?closed=1 `_. + +Improvements +------------ +- Efficient Hadamard transform. (`#103 `_) +- Make circuit latex code accessible in `QubitCircuit`. (`#108 `_) + + +Bug Fixes +---------- +- Fix the leaking noise objects in `Processor`. (`#89 `_) +- Fix a bug in time-dependent collapse operators in `Processor`. (`#107 `_) + + +Version 0.1.1 (July 28, 2021) ++++++++++++++++++++++++++++++ + +This micro release adds more thorough documentation for the project and fixes a few bugs in :obj:`.QubitCircuit` and :obj:`.Processor`. + +PRs are collected `here `_. + +Improvements +------------ +- Improve the documentation. +- Workflows for releases and automatically building the documentation, migrated from ``qutip``. (`#49 `_, `#78 `_) +- The part of tex code taken from circuit is removed due to licence issue. Instead, the latex code now requires the user to install `qcircuit` in advance. (`#61 `_) +- Rename :obj:`.Noise.get_noisy_dynamics` with :obj:`.Noise.get_noisy_pulses`. The new name is more appropriate because it returns a list of :obj:`.Pulse`, not a ``QobjEvo``. The old API is deprecated. (`#76 `_) +- Add more thorough documentation for installing external dependencies for circuit plotting. (`#65 `_) + +Bug Fixes +--------- +- Add the missing drift Hamiltonian to the method :obj:`.Processor.run_analytically`. It was missing because only the control part of the Hamiltonian is added. (`#74 `_) +- Fix a few bugs in :obj:`.QubitCircuit`: Make `QubitCircuit.propagators_no_expand` private. It will be removed and replaced by :obj:`.QubitCircuit.propagators`. The attributes :obj:`.QubitCircuit.U_list` is also removed. (`#66 `_) + +Developer Changes +----------------- +- Documentation is moved from ``/docs`` to ``/doc``. (`#49 `_, `#78 `_) + + +Version 0.1.0 (May 14, 2021) +++++++++++++++++++++++++++++ + +This is the first release of qutip-qip, the Quantum Information Processing package in QuTiP. + +The qutip-qip package used to be a module ``qutip.qip`` under `QuTiP (Quantum Toolbox in Python) `_. From QuTiP 5.0, the community has decided to decrease the size of the core QuTiP package by reducing the external dependencies, in order to simplify maintenance. Hence a few modules are separated from the core QuTiP and will become QuTiP family packages. They are still maintained by the QuTiP team but hosted under different repositories in the `QuTiP organization `_. + +The qutip-qip package, QuTiP quantum information processing, aims at providing basic tools for quantum computing simulation both for simple quantum algorithm design and for experimental realization. Compared to other libraries for quantum information processing, qutip-qip puts additional emphasis on the physics layer and the interaction with the QuTiP package. The package offers two different approaches for simulating quantum circuits, one with :obj:`.QubitCircuit` calculating unitary evolution under quantum gates by matrix product, another called :obj:`.Processor` using open system solvers in QuTiP to simulate the execution of quantum circuits on a noisy quantum device. diff --git a/doc/source/index.rst b/doc/source/index.rst index 02bab36e..3092a4ab 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -36,6 +36,12 @@ qutip-qip: QuTiP quantum information processing contribution-code.rst contribution-docs.rst +.. toctree:: + :maxdepth: 2 + :caption: Changelog + + changelog.rst + .. toctree:: :maxdepth: 2 :caption: API documentation