1# This file is part of QuTiP: Quantum Toolbox in Python.
2#
3#    Copyright (c) 2011 and later, Paul D. Nation and Robert J. Johansson.
4#    All rights reserved.
5#
6#    Redistribution and use in source and binary forms, with or without
7#    modification, are permitted provided that the following conditions are
8#    met:
9#
10#    1. Redistributions of source code must retain the above copyright notice,
11#       this list of conditions and the following disclaimer.
12#
13#    2. Redistributions in binary form must reproduce the above copyright
14#       notice, this list of conditions and the following disclaimer in the
15#       documentation and/or other materials provided with the distribution.
16#
17#    3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names
18#       of its contributors may be used to endorse or promote products derived
19#       from this software without specific prior written permission.
20#
21#    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22#    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23#    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24#    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25#    HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26#    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27#    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28#    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29#    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30#    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31#    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32###############################################################################
33import os
34
35from numpy.testing import (
36    assert_, run_module_suite, assert_allclose, assert_equal)
37import numpy as np
38
39from qutip.qip.device.processor import Processor
40from qutip.states import basis
41from qutip.operators import sigmaz, sigmax, sigmay, identity, destroy
42from qutip.qip.operations.gates import hadamard_transform
43from qutip.tensor import tensor
44from qutip.solver import Options
45from qutip.random_objects import rand_ket, rand_dm
46from qutip.qip.noise import (
47    DecoherenceNoise, RandomNoise, ControlAmpNoise)
48from qutip.qip.qubits import qubit_states
49from qutip.metrics import fidelity
50from qutip.qip.pulse import Pulse
51
52
53class TestCircuitProcessor:
54    def test_modify_ctrls(self):
55        """
56        Test for modifying Hamiltonian, add_control, remove_pulse
57        """
58        N = 2
59        proc = Processor(N=N)
60        proc.ctrls
61        proc.add_control(sigmaz())
62        assert_(tensor([sigmaz(), identity(2)]), proc.ctrls[0])
63        proc.add_control(sigmax(), cyclic_permutation=True)
64        assert_allclose(len(proc.ctrls), 3)
65        assert_allclose(tensor([sigmax(), identity(2)]), proc.ctrls[1])
66        assert_allclose(tensor([identity(2), sigmax()]), proc.ctrls[2])
67        proc.add_control(sigmay(), targets=1)
68        assert_allclose(tensor([identity(2), sigmay()]), proc.ctrls[3])
69        proc.remove_pulse([0, 1, 2])
70        assert_allclose(tensor([identity(2), sigmay()]), proc.ctrls[0])
71        proc.remove_pulse(0)
72        assert_allclose(len(proc.ctrls), 0)
73
74    def test_save_read(self):
75        """
76        Test for saving and reading a pulse matrix
77        """
78        proc = Processor(N=2)
79        proc.add_control(sigmaz(), cyclic_permutation=True)
80        proc1 = Processor(N=2)
81        proc1.add_control(sigmaz(), cyclic_permutation=True)
82        proc2 = Processor(N=2)
83        proc2.add_control(sigmaz(), cyclic_permutation=True)
84        # TODO generalize to different tlist
85        tlist = [0., 0.1, 0.2, 0.3, 0.4, 0.5]
86        amp1 = np.arange(0, 5, 1)
87        amp2 = np.arange(5, 0, -1)
88
89        proc.pulses[0].tlist = tlist
90        proc.pulses[0].coeff = amp1
91        proc.pulses[1].tlist = tlist
92        proc.pulses[1].coeff = amp2
93        proc.save_coeff("qutip_test_CircuitProcessor.txt")
94        proc1.read_coeff("qutip_test_CircuitProcessor.txt")
95        os.remove("qutip_test_CircuitProcessor.txt")
96        assert_allclose(proc1.get_full_coeffs(), proc.get_full_coeffs())
97        assert_allclose(proc1.get_full_tlist(), proc.get_full_tlist())
98        proc.save_coeff("qutip_test_CircuitProcessor.txt", inctime=False)
99        proc2.read_coeff("qutip_test_CircuitProcessor.txt", inctime=False)
100        proc2.set_all_tlist(tlist)
101        os.remove("qutip_test_CircuitProcessor.txt")
102        assert_allclose(proc2.get_full_coeffs(), proc.get_full_coeffs())
103
104    def test_id_evolution(self):
105        """
106        Test for identity evolution
107        """
108        N = 1
109        proc = Processor(N=N)
110        init_state = rand_ket(2)
111        tlist = [0., 1., 2.]
112        proc.add_pulse(Pulse(identity(2), 0, tlist, False))
113        result = proc.run_state(
114            init_state, options=Options(store_final_state=True))
115        global_phase = init_state.data[0, 0]/result.final_state.data[0, 0]
116        assert_allclose(global_phase*result.final_state, init_state)
117
118    def test_id_with_T1_T2(self):
119        """
120        Test for identity evolution with relaxation t1 and t2
121        """
122        # setup
123        a = destroy(2)
124        Hadamard = hadamard_transform(1)
125        ex_state = basis(2, 1)
126        mines_state = (basis(2, 1)-basis(2, 0)).unit()
127        end_time = 2.
128        tlist = np.arange(0, end_time + 0.02, 0.02)
129        t1 = 1.
130        t2 = 0.5
131
132        # test t1
133        test = Processor(1, t1=t1)
134        # zero ham evolution
135        test.add_pulse(Pulse(identity(2), 0, tlist, False))
136        result = test.run_state(ex_state, e_ops=[a.dag()*a])
137        assert_allclose(
138            result.expect[0][-1], np.exp(-1./t1*end_time),
139            rtol=1e-5, err_msg="Error in t1 time simulation")
140
141        # test t2
142        test = Processor(1, t2=t2)
143        test.add_pulse(Pulse(identity(2), 0, tlist, False))
144        result = test.run_state(
145            init_state=mines_state, e_ops=[Hadamard*a.dag()*a*Hadamard])
146        assert_allclose(
147            result.expect[0][-1], np.exp(-1./t2*end_time)*0.5+0.5,
148            rtol=1e-5, err_msg="Error in t2 time simulation")
149
150        # test t1 and t2
151        t1 = np.random.rand(1) + 0.5
152        t2 = np.random.rand(1) * 0.5 + 0.5
153        test = Processor(1, t1=t1, t2=t2)
154        test.add_pulse(Pulse(identity(2), 0, tlist, False))
155        result = test.run_state(
156            init_state=mines_state, e_ops=[Hadamard*a.dag()*a*Hadamard])
157        assert_allclose(
158            result.expect[0][-1], np.exp(-1./t2*end_time)*0.5+0.5,
159            rtol=1e-5,
160            err_msg="Error in t1 & t2 simulation, "
161                    "with t1={} and t2={}".format(t1, t2))
162
163    def testPlot(self):
164        """
165        Test for plotting functions
166        """
167        try:
168            import matplotlib.pyplot as plt
169        except Exception:
170            return True
171        # step_func
172        tlist = np.linspace(0., 2*np.pi, 20)
173        processor = Processor(N=1, spline_kind="step_func")
174        processor.add_control(sigmaz())
175        processor.pulses[0].tlist = tlist
176        processor.pulses[0].coeff = np.array([np.sin(t) for t in tlist])
177        processor.plot_pulses()
178        plt.clf()
179
180        # cubic spline
181        tlist = np.linspace(0., 2*np.pi, 20)
182        processor = Processor(N=1, spline_kind="cubic")
183        processor.add_control(sigmaz())
184        processor.pulses[0].tlist = tlist
185        processor.pulses[0].coeff = np.array([np.sin(t) for t in tlist])
186        processor.plot_pulses()
187        plt.clf()
188
189    def testSpline(self):
190        """
191        Test if the spline kind is correctly transfered into
192        the arguments in QobjEvo
193        """
194        tlist = np.array([1, 2, 3, 4, 5, 6], dtype=float)
195        coeff = np.array([1, 1, 1, 1, 1, 1], dtype=float)
196        processor = Processor(N=1, spline_kind="step_func")
197        processor.add_control(sigmaz())
198        processor.pulses[0].tlist = tlist
199        processor.pulses[0].coeff = coeff
200
201        ideal_qobjevo, _ = processor.get_qobjevo(noisy=False)
202        assert_(ideal_qobjevo.args["_step_func_coeff"])
203        noisy_qobjevo, c_ops = processor.get_qobjevo(noisy=True)
204        assert_(noisy_qobjevo.args["_step_func_coeff"])
205        processor.T1 = 100.
206        processor.add_noise(ControlAmpNoise(coeff=coeff, tlist=tlist))
207        noisy_qobjevo, c_ops = processor.get_qobjevo(noisy=True)
208        assert_(noisy_qobjevo.args["_step_func_coeff"])
209
210        tlist = np.array([1, 2, 3, 4, 5, 6], dtype=float)
211        coeff = np.array([1, 1, 1, 1, 1, 1], dtype=float)
212        processor = Processor(N=1, spline_kind="cubic")
213        processor.add_control(sigmaz())
214        processor.pulses[0].tlist = tlist
215        processor.pulses[0].coeff = coeff
216
217        ideal_qobjevo, _ = processor.get_qobjevo(noisy=False)
218        assert_(not ideal_qobjevo.args["_step_func_coeff"])
219        noisy_qobjevo, c_ops = processor.get_qobjevo(noisy=True)
220        assert_(not noisy_qobjevo.args["_step_func_coeff"])
221        processor.T1 = 100.
222        processor.add_noise(ControlAmpNoise(coeff=coeff, tlist=tlist))
223        noisy_qobjevo, c_ops = processor.get_qobjevo(noisy=True)
224        assert_(not noisy_qobjevo.args["_step_func_coeff"])
225
226    def testGetObjevo(self):
227        tlist = np.array([1, 2, 3, 4, 5, 6], dtype=float)
228        coeff = np.array([1, 1, 1, 1, 1, 1], dtype=float)
229        processor = Processor(N=1)
230        processor.add_control(sigmaz())
231        processor.pulses[0].tlist = tlist
232        processor.pulses[0].coeff = coeff
233
234        # without noise
235        unitary_qobjevo, _ = processor.get_qobjevo(
236            args={"test": True}, noisy=False)
237        assert_allclose(unitary_qobjevo.ops[0].qobj, sigmaz())
238        assert_allclose(unitary_qobjevo.tlist, tlist)
239        assert_allclose(unitary_qobjevo.ops[0].coeff, coeff[0])
240        assert_(unitary_qobjevo.args["test"],
241                msg="Arguments not correctly passed on")
242
243        # with decoherence noise
244        dec_noise = DecoherenceNoise(
245            c_ops=sigmax(), coeff=coeff, tlist=tlist)
246        processor.add_noise(dec_noise)
247        assert_equal(unitary_qobjevo.to_list(),
248                     processor.get_qobjevo(noisy=False)[0].to_list())
249
250        noisy_qobjevo, c_ops = processor.get_qobjevo(
251            args={"test": True}, noisy=True)
252        assert_(noisy_qobjevo.args["_step_func_coeff"],
253                msg="Spline type not correctly passed on")
254        assert_(noisy_qobjevo.args["test"],
255                msg="Arguments not correctly passed on")
256        assert_(sigmaz() in [pair[0] for pair in noisy_qobjevo.to_list()])
257        assert_equal(c_ops[0].ops[0].qobj, sigmax())
258        assert_equal(c_ops[0].tlist, tlist)
259
260        # with amplitude noise
261        processor = Processor(N=1, spline_kind="cubic")
262        processor.add_control(sigmaz())
263        tlist = np.linspace(1, 6, int(5/0.2))
264        coeff = np.random.rand(len(tlist))
265        processor.pulses[0].tlist = tlist
266        processor.pulses[0].coeff = coeff
267
268        amp_noise = ControlAmpNoise(coeff=coeff, tlist=tlist)
269        processor.add_noise(amp_noise)
270        noisy_qobjevo, c_ops = processor.get_qobjevo(
271            args={"test": True}, noisy=True)
272        assert_(not noisy_qobjevo.args["_step_func_coeff"],
273                msg="Spline type not correctly passed on")
274        assert_(noisy_qobjevo.args["test"],
275                msg="Arguments not correctly passed on")
276        assert_equal(len(noisy_qobjevo.ops), 2)
277        assert_equal(sigmaz(), noisy_qobjevo.ops[0].qobj)
278        assert_allclose(coeff, noisy_qobjevo.ops[0].coeff, rtol=1.e-10)
279
280    def testNoise(self):
281        """
282        Test for Processor with noise
283        """
284        # setup and fidelity without noise
285        init_state = qubit_states(2, [0, 0, 0, 0])
286        tlist = np.array([0., np.pi/2.])
287        a = destroy(2)
288        proc = Processor(N=2)
289        proc.add_control(sigmax(), targets=1)
290        proc.pulses[0].tlist = tlist
291        proc.pulses[0].coeff = np.array([1])
292        result = proc.run_state(init_state=init_state)
293        assert_allclose(
294            fidelity(result.states[-1], qubit_states(2, [0, 1, 0, 0])),
295            1, rtol=1.e-7)
296
297        # decoherence noise
298        dec_noise = DecoherenceNoise([0.25*a], targets=1)
299        proc.add_noise(dec_noise)
300        result = proc.run_state(init_state=init_state)
301        assert_allclose(
302            fidelity(result.states[-1], qubit_states(2, [0, 1, 0, 0])),
303            0.981852, rtol=1.e-3)
304
305        # white random noise
306        proc.noise = []
307        white_noise = RandomNoise(0.2, np.random.normal, loc=0.1, scale=0.1)
308        proc.add_noise(white_noise)
309        result = proc.run_state(init_state=init_state)
310
311    def testMultiLevelSystem(self):
312        """
313        Test for processor with multi-level system
314        """
315        N = 2
316        proc = Processor(N=N, dims=[2, 3])
317        proc.add_control(tensor(sigmaz(), rand_dm(3, density=1.)))
318        proc.pulses[0].coeff = np.array([1, 2])
319        proc.pulses[0].tlist = np.array([0., 1., 2])
320        proc.run_state(init_state=tensor([basis(2, 0), basis(3, 1)]))
321
322    def testDrift(self):
323        """
324        Test for the drift Hamiltonian
325        """
326        processor = Processor(N=1)
327        processor.add_drift(sigmaz(), 0)
328        tlist = np.array([0., 1., 2.])
329        processor.add_pulse(Pulse(identity(2), 0, tlist, False))
330        ideal_qobjevo, _ = processor.get_qobjevo(noisy=True)
331        assert_equal(ideal_qobjevo.cte, sigmaz())
332
333    def testChooseSolver(self):
334        # setup and fidelity without noise
335        init_state = qubit_states(2, [0, 0, 0, 0])
336        tlist = np.array([0., np.pi/2.])
337        a = destroy(2)
338        proc = Processor(N=2)
339        proc.add_control(sigmax(), targets=1)
340        proc.pulses[0].tlist = tlist
341        proc.pulses[0].coeff = np.array([1])
342        result = proc.run_state(init_state=init_state, solver="mcsolve")
343        assert_allclose(
344            fidelity(result.states[-1], qubit_states(2, [0, 1, 0, 0])),
345            1, rtol=1.e-7)
346
347if __name__ == "__main__":
348    run_module_suite()
349