diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c046f02a4e..f758c46f1d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -69,6 +69,7 @@ jobs: python-version: "3.10" scipy-requirement: ">=1.10,<1.11" numpy-requirement: ">=1.24,<1.25" + oldcython: 1 nocython: 1 # Python 3.11 and recent numpy @@ -128,15 +129,19 @@ jobs: # In the run, first we handle any special cases. We do this in bash # rather than in the GitHub Actions file directly, because bash gives us # a proper programming language to use. + # We install without build isolation so qutip is compiled with the + # version of cython, scipy, numpy in the test matrix, not a temporary + # version use in the installation virtual environment. run: | - QUTIP_TARGET="tests,graphics,semidefinite,ipython,extras" - if [[ -z "${{ matrix.nocython }}" ]]; then - QUTIP_TARGET="$QUTIP_TARGET,runtime_compilation" - fi - if [[ "${{ matrix.oldcython }}" ]]; then - pip install cython==0.29.36 - fi - export CI_QUTIP_WITH_OPENMP=${{ matrix.openmp }} + # Install the extra requirement + python -m pip install pytest>=5.2 pytest-rerunfailures # tests + python -m pip install matplotlib>=1.2.1 # graphics + python -m pip install cvxpy>=1.0 cvxopt # semidefinite + python -m pip install ipython # ipython + python -m pip install loky tqdm # extras + python -m pip install "coverage${{ matrix.coverage-requirement }}" chardet + python -m pip install pytest-cov coveralls pytest-fail-slow + if [[ -z "${{ matrix.nomkl }}" ]]; then conda install blas=*=mkl "numpy${{ matrix.numpy-requirement }}" "scipy${{ matrix.scipy-requirement }}" elif [[ "${{ matrix.os }}" =~ ^windows.*$ ]]; then @@ -153,9 +158,17 @@ jobs: # Use openmpi because mpich causes problems. Note, environment variable names change in v5 conda install "openmpi<5" mpi4py fi - python -m pip install -e .[$QUTIP_TARGET] - python -m pip install "coverage${{ matrix.coverage-requirement }}" - python -m pip install pytest-cov coveralls pytest-fail-slow + if [[ "${{ matrix.oldcython }}" ]]; then + python -m pip install cython==0.29.36 filelock + else + python -m pip install cython filelock + fi + + python -m pip install -e . -v --no-build-isolation + + if [[ "${{ matrix.nocython }}" ]]; then + python -m pip uninstall cython -y + fi - name: Package information run: | diff --git a/doc/changelog.rst b/doc/changelog.rst index a916a657c5..c4125aeb41 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -6,6 +6,463 @@ Change Log .. towncrier release notes start + +QuTiP 5.0.0 (2024-03-26) +======================== + + +QuTiP 5 is a redesign of many of the core components of QuTiP (``Qobj``, +``QobjEvo``, solvers) to make them more consistent and more flexible. + +``Qobj`` may now be stored in either sparse or dense representations, +and the two may be mixed sensibly as needed. ``QobjEvo`` is now used +consistently throughout QuTiP, and the implementation has been +substantially cleaned up. A new ``Coefficient`` class is used to +represent the time-dependent factors inside ``QobjEvo``. + +The solvers have been rewritten to work well with the new data layer +and the concept of ``Integrators`` which solve ODEs has been introduced. +In future, new data layers may provide their own ``Integrators`` +specialized to their representation of the underlying data. + +Much of the user-facing API of QuTiP remains familiar, but there have +had to be many small breaking changes. If we can make changes to +easy migrating code from QuTiP 4 to QuTiP 5, please let us know. + +An extensive list of changes follows. + +Contributors +------------ + +QuTiP 5 has been a large effort by many people over the last three years. + +In particular: + +- Jake Lishman led the implementation of the new data layer and coefficients. +- Eric Giguère led the implementation of the new QobjEvo interface and solvers. +- Boxi Li led the updating of QuTiP's QIP support and the creation of ``qutip_qip``. + +Other members of the QuTiP Admin team have been heavily involved in reviewing, +testing and designing QuTiP 5: + +- Alexander Pitchford +- Asier Galicia +- Nathan Shammah +- Shahnawaz Ahmed +- Neill Lambert +- Simon Cross +- Paul Menczel + +Two Google Summer of Code contributors updated the tutorials and benchmarks to +QuTiP 5: + +- Christian Staufenbiel updated many of the tutorials (``). +- Xavier Sproken update the benchmarks (``). + +During an internship at RIKEN, Patrick Hopf created a new quantum control method and +improved the existing methods interface: + +- Patrick Hopf created new quantum control package (``). + +Four experimental data layers backends were written either as part of Google Summer +of Code or as separate projects. While these are still alpha quality, they helped +significantly to test the data layer API: + +- ``qutip-tensorflow``: a TensorFlow backend by Asier Galicia (``) +- ``qutip-cupy``: a CuPy GPU backend by Felipe Bivort Haiek (``)` +- ``qutip-tensornetwork``: a TensorNetwork backend by Asier Galicia (``) +- ``qutip-jax``: a JAX backend by Eric Giguère (``) + +Finally, Yuji Tamakoshi updated the visualization function and added animation +functions as part of Google Summer of Code project. + +We have also had many other contributors, whose specific contributions are +detailed below: + +- Pieter Eendebak (updated the required SciPy to 1.5+, `#1982 `). +- Pieter Eendebak (reduced import times by setting logger names, `#1981 `) +- Pieter Eendebak (Allow scipy 1.12 to be used with qutip, `#2354 `) +- Xavier Sproken (included C header files in the source distribution, `#1971 `) +- Christian Staufenbiel (added support for multiple collapse operators to the Floquet solver, `#1962 `) +- Christian Staufenbiel (fixed the basis used in the Floquet Master Equation solver, `#1952 `) +- Christian Staufenbiel (allowed the ``bloch_redfield_tensor`` function to accept strings and callables for `a_ops`, `#1951 `) +- Christian Staufenbiel (Add a guide on Superoperators, Pauli Basis and Channel Contraction, `#1984 `) +- Henrique Silvéro (allowed ``qutip_qip`` to be imported as ``qutip.qip``, `#1920 `) +- Florian Hopfmueller (added a vastly improved implementations of ``process_fidelity`` and ``average_gate_fidelity``, `#1712 `, `#1748 `, `#1788 `) +- Felipe Bivort Haiek (fixed inaccuracy in docstring of the dense implementation of negation, `#1608 `) +- Rajath Shetty (added support for specifying colors for individual points, vectors and states display by `qutip.Bloch`, `#1335 `) +- Rochisha Agarwal (Add dtype to printed ouput of qobj, `#2352 `) +- Kosuke Mizuno (Add arguments of plot_wigner() and plot_wigner_fock_distribution() to specify parameters for wigner(), `#2057 `) +- Matt Ord (Only pre-compute density matrices if keep_runs_results is False, `#2303 `) +- Daniel Moreno Galán (Add the possibility to customize point colors as in V4 and fix point plot behavior for 'l' style, `#2303 `) +- Sola85 (Fixed simdiag not returning orthonormal eigenvectors, `#2269 `) +- Edward Thomas (Fix LaTeX display of Qobj state in Jupyter cell outputs, `#2272 `) +- Bogdan Reznychenko (Rework `kraus_to_choi` making it faster, `#2284 `) +- gabbence95 (Fix typos in `expect` documentation, `#2331 `) +- lklivingstone (Added __repr__ to QobjEvo, `#2111 `) +- Yuji Tamakoshi (Improve print(qutip.settings) by make it shorter, `#2113 `) +- khnikhil (Added fermionic annihilation and creation operators, `#2166 `) +- Daniel Weiss (Improved sampling algorithm for mcsolve, `#2218 `) +- SJUW (Increase missing colorbar padding for matrix_histogram_complex() from 0 to 0.05, `#2181 `) +- Valan Baptist Mathuranayagam (Changed qutip-notebooks to qutip-tutorials and fixed the typo in the link redirecting to the changelog section in the PR template, `#2107 `) +- Gerardo Jose Suarez (Added information on sec_cutoff to the documentation, `#2136 `) +- Cristian Emiliano Godinez Ramirez (Added inherited members to API doc of MESolver, SMESolver, SSESolver, NonMarkovianMCSolver, `#2167 `) +- Andrey Rakhubovsky (Corrected grammar in Bloch-Redfield master equation documentation, `#2174 `) +- Rushiraj Gadhvi (qutip.ipynbtools.version_table() can now be called without Cython installed, `#2110 `) +- Harsh Khilawala (Moved HTMLProgressBar from qutip/ipynbtools.py to qutip/ui/progressbar.py, `#2112 `) +- Avatar Srinidhi P V (Added new argument bc_type to take boundary conditions when creating QobjEvo, `#2114 `) +- Andrey Rakhubovsky (Fix types in docstring of projection(), `#2363 `) + + +Qobj changes +------------ + +Previously ``Qobj`` data was stored in a SciPy-like sparse matrix. Now the +representation is flexible. Implementations for dense and sparse formats are +included in QuTiP and custom implementations are possible. QuTiP's performance +on dense states and operators is significantly improved as a result. + +Some highlights: + +- The data is still acessible via the ``.data`` attribute, but is now an + instance of the underlying data type instead of a SciPy-like sparse matrix. + The operations available in ``qutip.core.data`` may be used on ``.data``, + regardless of the data type. +- ``Qobj`` with different data types may be mixed in arithmetic and other + operations. A sensible output type will be automatically determined. +- The new ``.to(...)`` method may be used to convert a ``Qobj`` from one data type + to another. E.g. ``.to("dense")`` will convert to the dense representation and + ``.to("csr")`` will convert to the sparse type. +- Many ``Qobj`` methods and methods that create ``Qobj`` now accepted a ``dtype`` + parameter that allows the data type of the returned ``Qobj`` to specified. +- The new ``&`` operator may be used to obtain the tensor product. +- The new ``@`` operator may be used to obtain the matrix / operator product. + ``bar @ ket`` returns a scalar. +- The new ``.contract()`` method will collapse 1D subspaces of the dimensions of + the ``Qobj``. +- The new ``.logm()`` method returns the matrix logarithm of an operator. +- The methods ``.set_data``, ``.get_data``, ``.extract_state``, ``.eliminate_states``, + ``.evaluate`` and ``.check_isunitary`` have been removed. +- The property ``dtype`` return the representation of the data used. +- The new ``data_as`` allow to obtain the data as a common python formats: + numpy array, scipy sparse matrix, JAX Array, etc. + +QobjEvo changes +--------------- + +The ``QobjEvo`` type for storing time-dependent quantum objects has been +significantly expanded, standardized and extended. The time-dependent +coefficients are now represented using a new ``Coefficient`` type that +may be independently created and manipulated if required. + +Some highlights: + +- The ``.compile()`` method has been removed. Coefficients specified as + strings are automatically compiled if possible and the compilation is + cached across different Python runs and instances. +- Mixing coefficient types within a single ``Qobj`` is now supported. +- Many new attributes were added to ``QobjEvo`` for convenience. Examples + include ``.dims``, ``.shape``, ``.superrep`` and ``.isconstant``. +- Many old attributes such as ``.cte``, ``.use_cython``, ``.type``, ``.const``, + and ``.coeff_file`` were removed. +- A new ``Spline`` coefficient supports spline interpolations of different + orders. The old ``Cubic_Spline`` coefficient has been removed. +- The new ``.arguments(...)`` method allows additional arguments to the + underlying coefficient functions to be updated. +- The ``_step_func_coeff`` argument has been replaced by the ``order`` + parameter. ``_step_func_coeff=False`` is equivalent to ``order=3``. + ``_step_func_coeff=True`` is equivalent to ``order=0``. Higher values + of ``order`` gives spline interpolations of higher orders. +- The spline type can take ``bc_type`` to control the boundary conditions. +- QobjEvo can be creating from the multiplication of a Qobj with a coefficient: + ``oper * qutip.coefficient(f, args=args)`` is equivalent to + ``qutip.QobjEvo([[oper, f]], args=args)``. +- Coefficient function can be defined in a pythonic manner: ``def f(t, A, w)``. + The dictionary ``args`` second argument is no longer needed. + Function using the exact ``f(t, args)`` signature will use the old method for + backward compatibility. + +Solver changes +-------------- + +The solvers in QuTiP have been heavily reworked and standardized. +Under the hood solvers now make use of swappable ODE ``Integrators``. +Many ``Integrators`` are included (see the list below) and +custom implementations are possible. Solvers now consistently +accept a ``QobjEvo`` instance at the Hamiltonian or Liouvillian, or +any object which can be passed to the ``QobjEvo`` constructor. + +A breakdown of highlights follows. + +All solvers: + +- Solver options are now supplied in an ordinary Python dict. + ``qutip.Options`` is deprecated and returns a dict for backwards + compatibility. +- A specific ODE integrator may be selected by supplying a + ``method`` option. +- Each solver provides a class interface. Creating an instance + of the class allows a solver to be run multiple times for the + same system without having to repeatedly reconstruct the + right-hand side of the ODE to be integrated. +- A ``QobjEvo`` instance is accepted for most operators, e.g., + ``H``, ``c_ops``, ``e_ops``, ``a_ops``. +- The progress bar is now selected using the ``progress_bar`` option. + A new progess bar using the ``tqdm`` Python library is provided. +- Dynamic arguments, where the value of an operator depends on + the current state of the evolution interface reworked. Now a property of the + solver is to be used as an arguments: + ``args={"state": MESolver.StateFeedback(default=rho0)}`` + +Integrators: + +- The SciPy zvode integrator is available with the BDF and + Adams methods as ``bdf`` and ``adams``. +- The SciPy dop853 integrator (an eighth order Runge-Kutta method by + Dormand & Prince) is available as ``dop853``. +- The SciPy lsoda integrator is available as ``lsoda``. +- QuTiP's own implementation of Verner's "most efficient" Runge-Kutta methods + of order 7 and 9 are available as ``vern7`` and ``vern9``. See + http://people.math.sfu.ca/~jverner/ for a description of the methods. +- QuTiP's own implementation of a solver that directly diagonalizes the + the system to be integrated is available as ``diag``. It only works on + time-independent systems and is slow to setup, but once the diagonalization + is complete, it generates solutions very quickly. +- QuTiP's own implementatoin of an approximate Krylov subspace integrator is + available as ``krylov``. This integrator is only usable with ``sesolve``. + +Result class: + +- A new ``.e_data`` attribute provides expectation values as a dictionary. + Unlike ``.expect``, the values are provided in a Python list rather than + a numpy array, which better supports non-numeric types. +- The contents of the ``.stats`` attribute changed significantly and is + now more consistent across solvers. + +Monte-Carlo Solver (mcsolve): + +- The system, H, may now be a super-operator. +- The ``seed`` parameter now supports supplying numpy ``SeedSequence`` or + ``Generator`` types. +- The new ``timeout`` and ``target_tol`` parameters allow the solver to exit + early if a timeout or target tolerance is reached. +- The ntraj option no longer supports a list of numbers of trajectories. + Instead, just run the solver multiple times and use the class ``MCSolver`` + if setting up the solver uses a significant amount of time. +- The ``map_func`` parameter has been replaced by the ``map`` option. +- A loky based parallel map as been added. +- A mpi based parallel map as been added. +- The result returned by ``mcsolve`` now supports calculating photocurrents + and calculating the steady state over N trajectories. +- The old ``parfor`` parallel execution function has been removed from + ``qutip.parallel``. Use ``parallel_map``, ``loky_map`` or ``mpi_pmap`` instead. +- Added improved sampling options which converge much faster when the + probability of collapse is small. + +Non Markovian Monte-Carlo Solver (nm_mcsolve): + +- New Monte-Carlo Solver supporting negative decay rates. +- Based on the influence martingale approach, Donvil et al., Nat Commun 13, 4140 (2022). +- Most of the improvements made to the regular Monte-Carlo solver are also available here. +- The value of the influence martingale is available through the ``.trace`` attribute of the result. + +Stochastic Equation Solvers (ssesolve, smesolve) + +- Function call greatly changed: many keyword arguments are now options. +- m_ops and dW_factors are now changed from the default from the new class interface only. +- Use the same parallel maps as mcsolve: support for loky and mpi map added. +- End conditions ``timeout`` and ``target_tol`` added. +- The ``seed`` parameter now supports supplying numpy ``SeedSequence``. +- Wiener function is now available as a feedback. + +Bloch-Redfield Master Equation Solver (brmesolve): + +- The ``a_ops`` and ``spectra`` support implementations been heavily reworked to + reuse the techniques from the new Coefficient and QobjEvo classes. +- The ``use_secular`` parameter has been removed. Use ``sec_cutoff=-1`` instead. +- The required tolerance is now read from ``qutip.settings``. + +Krylov Subspace Solver (krylovsolve): + +- The Krylov solver is now implemented using ``SESolver`` and the ``krylov`` + ODE integrator. The function ``krylovsolve`` is maintained for convenience + and now supports many more options. +- The ``sparse`` parameter has been removed. Supply a sparse ``Qobj`` for the + Hamiltonian instead. + +Floquet Solver (fsesolve and fmmesolve): + +- The Floquet solver has been rewritten to use a new ``FloquetBasis`` class + which manages the transformations from lab to Floquet basis and back. +- Many of the internal methods used by the old Floquet solvers have + been removed. The Floquet tensor may still be retried using + the function ``floquet_tensor``. +- The Floquet Markov Master Equation solver has had many changes and + new options added. The environment temperature may be specified using + ``w_th``, and the result states are stored in the lab basis and optionally + in the Floquet basis using ``store_floquet_state``. +- The spectra functions supplied to ``fmmesolve`` must now be vectorized + (i.e. accept and return numpy arrays for frequencies and densities) and + must accept negative frequence (i.e. usually include a ``w > 0`` factor + so that the returned densities are zero for negative frequencies). +- The number of sidebands to keep, ``kmax`` may only be supplied when using + the ``FMESolver`` +- The ``Tsteps`` parameter has been removed from both ``fsesolve`` and + ``fmmesolve``. The ``precompute`` option to ``FloquetBasis`` may be used + instead. + +Evolution of State Solver (essovle): + +- The function ``essolve`` has been removed. Use the ``diag`` integration + method with ``sesolve`` or ``mesolve`` instead. + +Steady-state solvers (steadystate module): + +- The ``method`` parameter and ``solver`` parameters have been separated. Previously + they were mixed together in the ``method`` parameter. +- The previous options are now passed as parameters to the steady state + solver and mostly passed through to the underlying SciPy functions. +- The logging and statistics have been removed. + +Correlation functions (correlation module): + +- A new ``correlation_3op`` function has been added. It supports ``MESolver`` + or ``BRMESolver``. +- The ``correlation``, ``correlation_4op``, and ``correlation_ss`` functions have been + removed. +- Support for calculating correlation with ``mcsolve`` has been removed. + +Propagators (propagator module): + +- A class interface, ``qutip.Propagator``, has been added for propagators. +- Propagation of time-dependent systems is now supported using ``QobjEvo``. +- The ``unitary_mode`` and ``parallel`` options have been removed. + +Correlation spectra (spectrum module): + +- The functions ``spectrum_ss`` and ``spectrum_pi`` have been removed and + are now internal functions. +- The ``use_pinv`` parameter for ``spectrum`` has been removed and the + functionality merged into the ``solver`` parameter. Use ``solver="pi"`` + instead. + +Hierarchical Equation of Motion Solver (HEOM) + +- Updated the solver to use the new QuTiP integrators and data layer. +- Updated all the HEOM tutorials to QuTiP 5. +- Added support for combining bosonic and fermionic baths. +- Sped up the construction of the RHS of the HEOM solver by a factor of 4x. +- As in QuTiP 4, the HEOM supports arbitrary spectral densities, bosonic and fermionic baths, Páde and Matsubara expansions of the correlation functions, calculating the Matsubara terminator and inspection of the ADOs (auxiliary density operators). + + +QuTiP core +---------- + +There have been numerous other small changes to core QuTiP features: + +- ``qft(...)`` the function that returns the quantum Fourier + transform operator was moved from ``qutip.qip.algorithm`` into ``qutip``. +- The Bloch-Redfield solver tensor, ``brtensor``, has been moved into + ``qutip.core``. See the section above on the Bloch-Redfield solver + for details. +- The functions ``mat2vec`` and ``vec2mat`` for transforming states to and + from super-operator states have been renamed to ``stack_columns`` and + ``unstack_columns``. +- The function ``liouvillian_ref`` has been removed. Used ``liouvillian`` + instead. +- The superoperator transforms ``super_to_choi``, ``choi_to_super``, + ``choi_to_kraus``, ``choi_to_chi`` and ``chi_to_choi`` have been removed. + Used ``to_choi``, ``to_super``, ``to_kraus`` and ``to_chi`` instead. +- All of the random object creation functions now accepted a + numpy ``Generator`` as a seed. +- The ``dims`` parameter of all random object creation functions has + been removed. Supply the dimensions as the first parameter if + explicit dimensions are required. +- The function ``rand_unitary_haar`` has been removed. Use + ``rand_unitary(distribution="haar")`` instead. +- The functions ``rand_dm_hs`` and ``rand_dm_ginibre`` have been removed. + Use ``rand_dm(distribution="hs")`` and ``rand_dm(distribution="ginibre")`` + instead. +- The function ``rand_ket_haar`` has been removed. Use + ``rand_ket(distribution="haar")`` instead. +- The measurement functions have had the ``target`` parameter for + expanding the measurement operator removed. Used ``expand_operator`` + to expand the operator instead. +- ``qutip.Bloch`` now supports applying colours per-point, state or vector in + ``add_point``, ``add_states``, and ``add_vectors``. +- Dimensions use a class instead of layered lists. +- Allow measurement functions to support degenerate operators. +- Add ``qeye_like`` and ``qzero_like``. +- Added fermionic annihilation and creation operators. + +QuTiP settings +-------------- + +Previously ``qutip.settings`` was an ordinary module. Now ``qutip.settings`` is +an instance of a settings class. All the runtime modifiable settings for +core operations are in ``qutip.settings.core``. The other settings are not +modifiable at runtime. + +- Removed ``load``. ``reset`` and ``save`` functions. +- Removed ``.debug``, ``.fortran``, ``.openmp_thresh``. +- New ``.compile`` stores the compilation options for compiled coefficients. +- New ``.core["rtol"]`` core option gives the default relative tolerance used by QuTiP. +- The absolute tolerance setting ``.atol`` has been moved to ``.core["atol"]``. + +Visualization +------------- + +- Added arguments to ``plot_wigner`` and ``plot_wigner_fock_distribution`` to specify parameters for ``wigner``. +- Removed ``Bloch3D``. The same functionality is provided by ``Bloch``. +- Added ``fig``, ``ax`` and ``cmap`` keyword arguments to all visualization functions. +- Most visualization functions now respect the ``colorblind_safe`` setting. +- Added new functions to create animations from a list of ``Qobj`` or directly from solver results with saved states. + + +Package reorganization +---------------------- + +- ``qutip.qip`` has been moved into its own package, qutip-qip. Once installed, qutip-qip is available as either ``qutip.qip`` or ``qutip_qip``. Some widely useful gates have been retained in ``qutip.gates``. +- ``qutip.control`` has been moved to qutip-qtrl and once installed qutip-qtrl is available as either ``qutip.control`` or ``qutip_qtrl``. Note that ``quitp_qtrl`` is provided primarily for backwards compatibility. Improvements to optimal control will take place in the new ``qutip_qoc`` package. +- ``qutip.lattice`` has been moved into its own package, qutip-lattice. It is available from ``. +- ``qutip.sparse`` has been removed. It contained the old sparse matrix representation and is replaced by the new implementation in ``qutip.data``. +- ``qutip.piqs`` functions are no longer available from the ``qutip`` namespace. They are accessible from ``qutip.piqs`` instead. + +Miscellaneous +------------- + +- Support has been added for 64-bit integer sparse matrix indices, allowing + sparse matrices with up to 2**63 rows and columns. This support needs to + be enabled at compilation time by calling ``setup.py`` and passing + ``--with-idxint-64``. + +Feature removals +---------------- + +- Support for OpenMP has been removed. If there is enough demand and a good plan for how to organize it, OpenMP support may return in a future QuTiP release. +- The ``qutip.parfor`` function has been removed. Use ``qutip.parallel_map`` instead. +- ``qutip.graph`` has been removed and replaced by SciPy's graph functions. +- ``qutip.topology`` has been removed. It contained only one function ``berry_curvature``. +- The ``~/.qutip/qutiprc`` config file is no longer supported. It contained settings for the OpenMP support. +- Deprecate ``three_level_atom`` +- Deprecate ``orbital`` + + +Changes from QuTiP 5.0.0b1: +-------------------------- + +Features +-------- + +- Add dtype to printed ouput of qobj (#2352 by Rochisha Agarwal) + + +Miscellaneous +------------- + +- Allow scipy 1.12 to be used with qutip. (#2354 by Pieter Eendebak) + + QuTiP 5.0.0b1 (2024-03-04) ========================== @@ -58,7 +515,7 @@ Features - Change the order of parameters in expand_operator (#1991) - Add `svn` and `solve` to dispatched (#2002) - Added nm_mcsolve to provide support for Monte-Carlo simulations of master equations with possibly negative rates. The method implemented here is described in arXiv:2209.08958 [quant-ph]. (#2070 by pmenczel) -- Add support for combining bosinic and fermionic HEOM baths (#2089) +- Add support for combining bosonic and fermionic HEOM baths (#2089) - Added __repr__ to QobjEvo (#2111 by lklivingstone) - Improve print(qutip.settings) by make it shorter (#2113 by tamakoshi2001) - Create the `trace_oper_ket` operation (#2126) diff --git a/doc/changes/2352.feature b/doc/changes/2352.feature deleted file mode 100644 index 96b9a87c15..0000000000 --- a/doc/changes/2352.feature +++ /dev/null @@ -1 +0,0 @@ -Add dtype to printed ouput of qobj \ No newline at end of file diff --git a/doc/changes/2354.misc b/doc/changes/2354.misc deleted file mode 100644 index e3cfa9de56..0000000000 --- a/doc/changes/2354.misc +++ /dev/null @@ -1 +0,0 @@ -Allow scipy 1.12 to be used with qutip. \ No newline at end of file diff --git a/qutip/core/states.py b/qutip/core/states.py index 2075dfe7b6..2ca6754d50 100644 --- a/qutip/core/states.py +++ b/qutip/core/states.py @@ -571,7 +571,7 @@ def projection(dimensions, n, m, offset=None, *, dtype=None): Number of basis states in Hilbert space. If a list, then the resultant object will be a tensor product over spaces with those dimensions. - n, m : float + n, m : int The number states in the projection. offset : int, default: 0 diff --git a/qutip/solver/mcsolve.py b/qutip/solver/mcsolve.py index 3c100906d4..43dc60893f 100644 --- a/qutip/solver/mcsolve.py +++ b/qutip/solver/mcsolve.py @@ -2,7 +2,7 @@ import numpy as np from ..core import QobjEvo, spre, spost, Qobj, unstack_columns -from .multitraj import MultiTrajSolver, _MTSystem +from .multitraj import MultiTrajSolver, _MultiTrajRHS from .solver_base import Solver, Integrator, _solver_deprecation from .result import McResult, McTrajectoryResult, McResultImprovedSampling from .mesolve import mesolve, MESolver @@ -167,27 +167,19 @@ def mcsolve(H, state, tlist, c_ops=(), e_ops=None, ntraj=500, *, return result -class _MCSystem(_MTSystem): +class _MCRHS(_MultiTrajRHS): """ Container for the operators of the solver. """ - def __init__(self, rhs, c_ops, n_ops): - self.rhs = rhs + def __init__(self, H, c_ops, n_ops): + self.rhs = H self.c_ops = c_ops self.n_ops = n_ops - self._collapse_key = "" def __call__(self): return self.rhs - def __getattr__(self, attr): - if attr == "rhs": - raise AttributeError - if hasattr(self.rhs, attr): - return getattr(self.rhs, attr) - raise AttributeError - def arguments(self, args): self.rhs.arguments(args) for c_op in self.c_ops: @@ -456,13 +448,15 @@ def __init__(self, H, c_ops, *, options=None): self._num_collapse = len(self._c_ops) self.options = options - system = _MCSystem(rhs, self._c_ops, self._n_ops) + system = _MCRHS(rhs, self._c_ops, self._n_ops) super().__init__(system, options=options) def _restore_state(self, data, *, copy=True): """ Retore the Qobj state from its data. """ + # Duplicated from the Solver class, but removed the check for the + # normalize_output option, since MCSolver doesn't have that option. if self._state_metadata['dims'] == self.rhs._dims[1]: state = Qobj(unstack_columns(data), **self._state_metadata, copy=False) @@ -480,37 +474,20 @@ def _initialize_stats(self): }) return stats - def _initialize_run_one_traj(self, seed, state, tlist, e_ops, - no_jump=False, jump_prob_floor=0.0): - result = self._trajectory_resultclass(e_ops, self.options) - generator = self._get_generator(seed) - self._integrator.set_state(tlist[0], state, generator, - no_jump=no_jump, - jump_prob_floor=jump_prob_floor) - result.add(tlist[0], self._restore_state(state, copy=False)) - return result - - def _run_one_traj(self, seed, state, tlist, e_ops, no_jump=False, - jump_prob_floor=0.0): + def _run_one_traj(self, seed, state, tlist, e_ops, **integrator_kwargs): """ Run one trajectory and return the result. """ - result = self._initialize_run_one_traj(seed, state, tlist, e_ops, - no_jump=no_jump, - jump_prob_floor=jump_prob_floor) - seed, result = self._integrate_one_traj(seed, tlist, result) + seed, result = super()._run_one_traj(seed, state, tlist, e_ops, + **integrator_kwargs) result.collapse = self._integrator.collapses return seed, result def run(self, state, tlist, ntraj=1, *, args=None, e_ops=(), timeout=None, target_tol=None, seeds=None): - """ - Do the evolution of the Quantum system. - See the overridden method for further details. The modification - here is to sample the no-jump trajectory first. Then, the no-jump - probability is used as a lower-bound for random numbers in future - monte carlo runs - """ + # Overridden to sample the no-jump trajectory first. Then, the no-jump + # probability is used as a lower-bound for random numbers in future + # monte carlo runs if not self.options.get("improved_sampling", False): return super().run(state, tlist, ntraj=ntraj, args=args, e_ops=e_ops, timeout=timeout, @@ -540,7 +517,8 @@ def run(self, state, tlist, ntraj=1, *, start_time = time() map_func( self._run_one_traj, seeds[1:], - (state0, tlist, e_ops, False, no_jump_prob), + task_args=(state0, tlist, e_ops), + task_kwargs={'no_jump': False, 'jump_prob_floor': no_jump_prob}, reduce_func=result.add, map_kw=map_kw, progress_bar=self.options["progress_bar"], progress_bar_kwargs=self.options["progress_kwargs"] @@ -557,9 +535,9 @@ def _get_integrator(self): integrator = method else: raise ValueError("Integrator method not supported.") - integrator_instance = integrator(self.system(), self.options) + integrator_instance = integrator(self.rhs(), self.options) mc_integrator = self._mc_integrator_class( - integrator_instance, self.system, self.options + integrator_instance, self.rhs, self.options ) self._init_integrator_time = time() - _time_start return mc_integrator diff --git a/qutip/solver/multitraj.py b/qutip/solver/multitraj.py index 3ca7930a96..3d1cf6ee95 100644 --- a/qutip/solver/multitraj.py +++ b/qutip/solver/multitraj.py @@ -8,16 +8,13 @@ __all__ = ["MultiTrajSolver"] -class _MTSystem: +class _MultiTrajRHS: """ Container for the operators of the solver. """ def __init__(self, rhs): self.rhs = rhs - def __call__(self): - return self.rhs - def arguments(self, args): self.rhs.arguments(args) @@ -25,6 +22,8 @@ def _register_feedback(self, type, val): pass def __getattr__(self, attr): + if attr == "rhs": + raise AttributeError if hasattr(self.rhs, attr): return getattr(self.rhs, attr) raise AttributeError @@ -71,12 +70,11 @@ class MultiTrajSolver(Solver): def __init__(self, rhs, *, options=None): if isinstance(rhs, QobjEvo): - self.system = _MTSystem(rhs) - elif isinstance(rhs, _MTSystem): - self.system = rhs + self.rhs = _MultiTrajRHS(rhs) + elif isinstance(rhs, _MultiTrajRHS): + self.rhs = rhs else: raise TypeError("The system should be a QobjEvo") - self.rhs = self.system() self.options = options self.seed_sequence = np.random.SeedSequence() self._integrator = self._get_integrator() @@ -233,23 +231,25 @@ def run(self, state, tlist, ntraj=1, *, result.stats['run time'] = time() - start_time return result - def _initialize_run_one_traj(self, seed, state, tlist, e_ops): + def _initialize_run_one_traj(self, seed, state, tlist, e_ops, + **integrator_kwargs): result = self._trajectory_resultclass(e_ops, self.options) generator = self._get_generator(seed) - self._integrator.set_state(tlist[0], state, generator) + self._integrator.set_state(tlist[0], state, generator, + **integrator_kwargs) result.add(tlist[0], self._restore_state(state, copy=False)) return result - def _run_one_traj(self, seed, state, tlist, e_ops): + def _run_one_traj(self, seed, state, tlist, e_ops, **integrator_kwargs): """ Run one trajectory and return the result. """ - result = self._initialize_run_one_traj(seed, state, tlist, e_ops) + result = self._initialize_run_one_traj(seed, state, tlist, e_ops, + **integrator_kwargs) return self._integrate_one_traj(seed, tlist, result) def _integrate_one_traj(self, seed, tlist, result): - for t in tlist[1:]: - t, state = self._integrator.integrate(t, copy=False) + for t, state in self._integrator.run(tlist): result.add(t, self._restore_state(state, copy=False)) return seed, result @@ -278,7 +278,8 @@ def _read_seed(self, seed, ntraj): def _argument(self, args): """Update the args, for the `rhs` and `c_ops` and other operators.""" if args: - self.system.arguments(args) + self.rhs.arguments(args) + self._integrator.arguments(args) def _get_generator(self, seed): """ diff --git a/qutip/solver/stochastic.py b/qutip/solver/stochastic.py index e999e2098a..ee35c63f57 100644 --- a/qutip/solver/stochastic.py +++ b/qutip/solver/stochastic.py @@ -2,7 +2,7 @@ from .sode.ssystem import StochasticOpenSystem, StochasticClosedSystem from .result import MultiTrajResult, Result, ExpectOp -from .multitraj import MultiTrajSolver +from .multitraj import _MultiTrajRHS, MultiTrajSolver from .. import Qobj, QobjEvo from ..core.dimensions import Dimensions import numpy as np @@ -26,7 +26,7 @@ def _post_init(self, m_ops=(), dw_factor=(), heterodyne=False): self.m_ops.append(ExpectOp(op, f, self.m_expect[-1].append)) self.add_processor(self.m_ops[-1]._store) - def add(self, t, state, noise): + def add(self, t, state, noise=None): super().add(t, state) if noise is not None and self.options["store_measurement"]: for i, dW in enumerate(noise): @@ -166,7 +166,7 @@ def wiener_process(self): return self._trajectories_attr("wiener_process") -class _StochasticRHS: +class _StochasticRHS(_MultiTrajRHS): """ In between object to store the stochastic system. @@ -181,7 +181,7 @@ class _StochasticRHS: def __init__(self, issuper, H, sc_ops, c_ops, heterodyne): if not isinstance(H, (Qobj, QobjEvo)) or not H.isoper: - raise TypeError("The Hamiltonian must be am operator") + raise TypeError("The Hamiltonian must be an operator") self.H = QobjEvo(H) if isinstance(sc_ops, (Qobj, QobjEvo)): @@ -500,8 +500,8 @@ class StochasticSolver(MultiTrajSolver): name = "StochasticSolver" _resultclass = StochasticResult _avail_integrators = {} - system = None _open = None + solver_options = { "progress_bar": "text", "progress_kwargs": {"chunk_size": 10}, @@ -517,20 +517,22 @@ class StochasticSolver(MultiTrajSolver): "store_measurement": False, } + def _trajectory_resultclass(self, e_ops, options): + return StochasticTrajResult( + e_ops, + options, + m_ops=self.m_ops, + dw_factor=self.dW_factors, + heterodyne=self.heterodyne, + ) + def __init__(self, H, sc_ops, heterodyne, *, c_ops=(), options=None): - self.options = options self._heterodyne = heterodyne if self.name == "ssesolve" and c_ops: raise ValueError("c_ops are not supported by ssesolve.") rhs = _StochasticRHS(self._open, H, sc_ops, c_ops, heterodyne) - self.rhs = rhs - self.system = rhs - self.options = options - self.seed_sequence = np.random.SeedSequence() - self._integrator = self._get_integrator() - self._state_metadata = {} - self.stats = self._initialize_stats() + super().__init__(rhs, options=options) if heterodyne: self._m_ops = [] @@ -619,25 +621,9 @@ def dW_factors(self, new_dW_factors): ) self._dW_factors = new_dW_factors - def _run_one_traj(self, seed, state, tlist, e_ops): - """ - Run one trajectory and return the result. - """ - result = StochasticTrajResult( - e_ops, - self.options, - m_ops=self.m_ops, - dw_factor=self.dW_factors, - heterodyne=self.heterodyne, - ) - generator = self._get_generator(seed) - self._integrator.set_state(tlist[0], state, generator) - state_t = self._restore_state(state, copy=False) - result.add(tlist[0], state_t, None) - for t in tlist[1:]: - t, state, noise = self._integrator.integrate(t, copy=False) - state_t = self._restore_state(state, copy=False) - result.add(t, state_t, noise) + def _integrate_one_traj(self, seed, tlist, result): + for t, state, noise in self._integrator.run(tlist): + result.add(t, self._restore_state(state, copy=False), noise) return seed, result @classmethod