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###############################################################################
33
34import numbers
35import numpy as np
36from numpy.testing import assert_equal, run_module_suite
37import pytest
38
39import qutip
40from qutip import (jmat, basis, destroy, create, displace, qzero, qeye,
41                    num, squeeze, charge, tunneling)
42
43
44def test_jmat_12():
45    "Spin 1/2 operators"
46    spinhalf = jmat(1 / 2.)
47
48    paulix = np.array([[0.0 + 0.j, 0.5 + 0.j], [0.5 + 0.j, 0.0 + 0.j]])
49    pauliy = np.array([[0. + 0.j, 0. - 0.5j], [0. + 0.5j, 0. + 0.j]])
50    pauliz = np.array([[0.5 + 0.j, 0.0 + 0.j], [0.0 + 0.j, -0.5 + 0.j]])
51    sigmap = np.array([[0. + 0.j, 1. + 0.j], [0. + 0.j, 0. + 0.j]])
52    sigmam = np.array([[0. + 0.j, 0. + 0.j], [1. + 0.j, 0. + 0.j]])
53
54    assert_equal(np.allclose(spinhalf[0].full(), paulix), True)
55    assert_equal(np.allclose(spinhalf[1].full(), pauliy), True)
56    assert_equal(np.allclose(spinhalf[2].full(), pauliz), True)
57    assert_equal(np.allclose(jmat(1 / 2., '+').full(), sigmap), True)
58    assert_equal(np.allclose(jmat(1 / 2., '-').full(), sigmam), True)
59
60
61def test_jmat_32():
62    "Spin 3/2 operators"
63    spin32 = jmat(3 / 2.)
64
65    paulix32 = np.array(
66        [[0.0000000 + 0.j, 0.8660254 + 0.j, 0.0000000 + 0.j, 0.0000000 + 0.j],
67         [0.8660254 + 0.j, 0.0000000 + 0.j, 1.0000000 + 0.j, 0.0000000 + 0.j],
68         [0.0000000 + 0.j, 1.0000000 + 0.j, 0.0000000 + 0.j, 0.8660254 + 0.j],
69         [0.0000000 + 0.j, 0.0000000 + 0.j, 0.8660254 + 0.j, 0.0000000 + 0.j]])
70
71    pauliy32 = np.array(
72        [[0. + 0.j, 0. - 0.8660254j, 0. + 0.j, 0. + 0.j],
73         [0. + 0.8660254j, 0. + 0.j, 0. - 1.j, 0. + 0.j],
74         [0. + 0.j, 0. + 1.j, 0. + 0.j, 0. - 0.8660254j],
75         [0. + 0.j, 0. + 0.j, 0. + 0.8660254j, 0. + 0.j]])
76
77    pauliz32 = np.array([[1.5 + 0.j, 0.0 + 0.j, 0.0 + 0.j, 0.0 + 0.j],
78                         [0.0 + 0.j, 0.5 + 0.j, 0.0 + 0.j, 0.0 + 0.j],
79                         [0.0 + 0.j, 0.0 + 0.j, -0.5 + 0.j, 0.0 + 0.j],
80                         [0.0 + 0.j, 0.0 + 0.j, 0.0 + 0.j, -1.5 + 0.j]])
81
82    assert_equal(np.allclose(spin32[0].full(), paulix32), True)
83    assert_equal(np.allclose(spin32[1].full(), pauliy32), True)
84    assert_equal(np.allclose(spin32[2].full(), pauliz32), True)
85
86
87def test_jmat_42():
88    "Spin 2 operators"
89    spin42 = jmat(4 / 2., '+')
90    assert_equal(spin42.dims == [[5], [5]], True)
91
92
93def test_jmat_52():
94    "Spin 5/2 operators"
95    spin52 = jmat(5 / 2., '+')
96    assert_equal(spin52.shape == (6, 6), True)
97
98
99def test_destroy():
100    "Destruction operator"
101    b4 = basis(5, 4)
102    d5 = destroy(5)
103    test1 = d5 * b4
104    assert_equal(np.allclose(test1.full(), 2.0 * basis(5, 3).full()), True)
105    d3 = destroy(3)
106    matrix3 = np.array(
107        [[0.00000000 + 0.j, 1.00000000 + 0.j, 0.00000000 + 0.j],
108         [0.00000000 + 0.j, 0.00000000 + 0.j, 1.41421356 + 0.j],
109         [0.00000000 + 0.j, 0.00000000 + 0.j, 0.00000000 + 0.j]])
110
111    assert_equal(np.allclose(matrix3, d3.full()), True)
112
113
114def test_create():
115    "Creation operator"
116    b3 = basis(5, 3)
117    c5 = create(5)
118    test1 = c5 * b3
119    assert_equal(np.allclose(test1.full(), 2.0 * basis(5, 4).full()), True)
120    c3 = create(3)
121    matrix3 = np.array(
122        [[0.00000000 + 0.j, 0.00000000 + 0.j, 0.00000000 + 0.j],
123         [1.00000000 + 0.j, 0.00000000 + 0.j, 0.00000000 + 0.j],
124         [0.00000000 + 0.j, 1.41421356 + 0.j, 0.00000000 + 0.j]])
125
126    assert_equal(np.allclose(matrix3, c3.full()), True)
127
128
129@pytest.mark.parametrize("to_test, expected", [
130        (qutip.qzero, lambda x: np.zeros((x, x), dtype=complex)),
131        (qutip.qeye, lambda x: np.eye(x, dtype=complex)),
132    ])
133@pytest.mark.parametrize("dimension", [1, 5, 100])
134def test_simple_operator_creation(to_test, expected, dimension):
135    qobj = to_test(dimension)
136    assert np.allclose(qobj.full(), expected(dimension))
137
138
139@pytest.mark.parametrize("to_test", [qutip.qzero, qutip.qeye, qutip.identity])
140@pytest.mark.parametrize("dimensions", [
141        2,
142        [2],
143        [2, 3, 4],
144        1,
145        [1],
146        [1, 1],
147    ])
148def test_implicit_tensor_creation(to_test, dimensions):
149    implicit = to_test(dimensions)
150    if isinstance(dimensions, numbers.Integral):
151        dimensions = [dimensions]
152    assert implicit.dims == [dimensions, dimensions]
153
154
155@pytest.mark.parametrize("to_test", [qutip.qzero, qutip.qeye, qutip.identity])
156def test_super_operator_creation(to_test):
157    size = 2
158    implicit = to_test([[size], [size]])
159    explicit = qutip.to_super(to_test(size))
160    assert implicit == explicit
161
162
163def test_num():
164    "Number operator"
165    n5 = num(5)
166    assert_equal(
167        np.allclose(n5.full(),
168                    np.diag([0 + 0j, 1 + 0j, 2 + 0j, 3 + 0j, 4 + 0j])),
169        True)
170
171
172def test_squeeze():
173    "Squeezing operator"
174    sq = squeeze(4, 0.1 + 0.1j)
175    sqmatrix = np.array([[0.99500417 + 0.j, 0.00000000 + 0.j,
176                          0.07059289 - 0.07059289j, 0.00000000 + 0.j],
177                         [0.00000000 + 0.j, 0.98503746 + 0.j,
178                          0.00000000 + 0.j, 0.12186303 - 0.12186303j],
179                         [-0.07059289 - 0.07059289j, 0.00000000 + 0.j,
180                          0.99500417 + 0.j, 0.00000000 + 0.j],
181                         [0.00000000 + 0.j, -0.12186303 - 0.12186303j,
182                          0.00000000 + 0.j, 0.98503746 + 0.j]])
183
184    assert_equal(np.allclose(sq.full(), sqmatrix), True)
185
186
187def test_displace():
188    "Displacement operator"
189    dp = displace(4, 0.25)
190    dpmatrix = np.array(
191        [[0.96923323 + 0.j, -0.24230859 + 0.j, 0.04282883 + 0.j, -
192          0.00626025 + 0.j],
193         [0.24230859 + 0.j, 0.90866411 + 0.j, -0.33183303 +
194          0.j, 0.07418172 + 0.j],
195         [0.04282883 + 0.j, 0.33183303 + 0.j, 0.84809499 +
196          0.j, -0.41083747 + 0.j],
197         [0.00626025 + 0.j, 0.07418172 + 0.j, 0.41083747 + 0.j,
198          0.90866411 + 0.j]])
199
200    assert_equal(np.allclose(dp.full(), dpmatrix), True)
201
202
203def test_charge():
204    "Charge operator"
205    N = 5
206    M = - np.random.randint(N)
207    ch = charge(N,M)
208    ch_matrix = np.diag(np.arange(M,N+1))
209    assert_equal(np.allclose(ch.full(), ch_matrix), True)
210
211
212def test_tunneling():
213    "Tunneling operator"
214    N = 5
215    tn = tunneling(2*N+1)
216    tn_matrix = np.diag(np.ones(2*N),k=-1) + np.diag(np.ones(2*N),k=1)
217    assert_equal(np.allclose(tn.full(), tn_matrix), True)
218
219    tn = tunneling(2*N+1,2)
220    tn_matrix = np.diag(np.ones(2*N-1),k=-2) + np.diag(np.ones(2*N-1),k=2)
221    assert_equal(np.allclose(tn.full(), tn_matrix), True)
222
223
224if __name__ == "__main__":
225    run_module_suite()
226