Giter Club home page Giter Club logo

qutip-qip's Introduction

QuTiP: Quantum Toolbox in Python

A. Pitchford, C. Granade, A. Grimsmo, N. Shammah, S. Ahmed, N. Lambert, E. Giguère, B. Li, J. Lishman, S. Cross, A. Galicia, P. Menczel, P. Hopf, P. D. Nation, and J. R. Johansson

Build Status Coverage Status Maintainability license PyPi Downloads Conda-Forge Downloads

QuTiP is open-source software for simulating the dynamics of closed and open quantum systems. It uses the excellent Numpy, Scipy, and Cython packages as numerical backends, and graphical output is provided by Matplotlib. QuTiP aims to provide user-friendly and efficient numerical simulations of a wide variety of quantum mechanical problems, including those with Hamiltonians and/or collapse operators with arbitrary time-dependence, commonly found in a wide range of physics applications. QuTiP is freely available for use and/or modification, and it can be used on all Unix-based platforms and on Windows. Being free of any licensing fees, QuTiP is ideal for exploring quantum mechanics in research as well as in the classroom.

Support

Unitary Fund Powered by NumFOCUS

We are proud to be affiliated with Unitary Fund and numFOCUS.

We are grateful for Nori's lab at RIKEN and Blais' lab at the Institut Quantique for providing developer positions to work on QuTiP.

We also thank Google for supporting us by financing GSoC students to work on the QuTiP as well as other supporting organizations that have been supporting QuTiP over the years.

Installation

Pip Package Conda-Forge Package

QuTiP is available on both pip and conda (the latter in the conda-forge channel). You can install QuTiP from pip by doing

pip install qutip

to get the minimal installation. You can instead use the target qutip[full] to install QuTiP with all its optional dependencies. For more details, including instructions on how to build from source, see the detailed installation guide in the documentation.

All back releases are also available for download in the releases section of this repository, where you can also find per-version changelogs. For the most complete set of release notes and changelogs for historic versions, see the changelog section in the documentation.

The pre-release of QuTiP 5.0 is available on PyPI and can be installed using pip:

pip install --pre qutip

This version breaks compatibility with QuTiP 4.7 in many small ways. Please see the changelog for a list of changes, new features and deprecations. This version should be fully working. If you find any bugs, confusing documentation or missing features, please create a GitHub issue.

Documentation

Documentation Status - Latest

The documentation for the latest stable release and the master branch is available for reading on Read The Docs.

The documentation for official releases, in HTML and PDF formats, can be found in the documentation section of the QuTiP website.

The latest development documentation is available in this repository in the doc folder.

A selection of demonstration notebooks is available, which demonstrate some of the many features of QuTiP. These are stored in the qutip/qutip-tutorials repository here on GitHub.

Contribute

You are most welcome to contribute to QuTiP development by forking this repository and sending pull requests, or filing bug reports at the issues page. You can also help out with users' questions, or discuss proposed changes in the QuTiP discussion group. All code contributions are acknowledged in the contributors section in the documentation.

For more information, including technical advice, please see the "contributing to QuTiP development" section of the documentation.

Citing QuTiP

If you use QuTiP in your research, please cite the original QuTiP papers that are available here.

qutip-qip's People

Contributors

abhisekupadhyaya avatar ajgpitch avatar alexbrc avatar anubhavvardhan avatar arnelg avatar avandeursen avatar boxili avatar canoming avatar cgranade avatar christian512 avatar claretgrace0801 avatar dependabot[bot] avatar enbr55 avatar ericgig avatar gecrooks avatar hodgestar avatar jakelishman avatar jrjohansson avatar kafischer avatar ldes89150 avatar martinsandecosta avatar nathanshammah avatar nonhermitian avatar nwlambert avatar purva-thakre avatar quantshah avatar rum1887 avatar sbisw002 avatar vutshi avatar wrzadkow avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

qutip-qip's Issues

Add information on the qutip-qip pulse paper

We should add information on the preprint of the paper introducing the pulse features in qutip-qip.
My suggestion is to add:

  • [ ] 1. a badge with the arXiv link in the readme, next to the other shields. Similarly to what done here.
  • [ ] 2. A "Cite" section that shows up in the Readme
  • [ ] 3. A "Cite" section that shows up in the documentation
  • [ ] 4. Similarly to what done in QuTiP, include a .bib bibliography file somewhere in the documentation, and also have the snippet show up in the docs, for easy copy-paste, as done here.

Warning on gates when running basic code block from documentation

I created a fresh conda environment with python==3.8 and run
pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.python.org/simple/ qutip-qip
It installed qutip-qip very fast. Do we have some sort of qutip.about() for qutip.qip? Or some basic test to test the installation?

I ran the code blocks at
and all worked fine. I got the following Warning when plotting the circuit: https://qutip-qip.readthedocs.io/en/latest/qip-basics.html

/opt/miniconda3/envs/qutip-qip/lib/python3.8/site-packages/qutip_qip/circuit.py:274: UserWarning: Unknown gate H
  warnings.warn("Unknown gate %s" % name)

Align the signatures of gate functions with qutip-v5

In qutip-v5 the gate functions no longer take parameters N , targets and control, which permute the qubits if needed and generate gate operators in a larger Hilbert space. Although many of the gates are moved to qutip.core.gates, these additional parameters are removed in qutip-v5. Also, the signature of expand_operator is slightly changed to be more general.

To avoid confusion and code duplication, we should adapt the functions in qutip-qip accordingly.

Proposals:

  • Import the gate functions from qutip when v5 is used. Otherwise still use the one defined in qutip-qip.
  • Align the signatures of gate functions and expand_operator with qutip-v5, and issue deprecation warnings when needed.

Optional:

  • In principle, we can try to keep the compatibility and define a wrapper that expands the obtained Qobj. This will need some complications but should be feasible.
  • Do we also want to add some deprecation warning for this to the gates in qutip-v4 in the module qutip.qip. Some people are still using it. It would make the migration from 4 to 5 easier.

@hodgestar Do you think we should implement the optional ones?

Add a TrappedIon ModelProcessor

Trapped ions need more detailed gate analysis (at the laser pulse level) in open source packages. Adding a TrappedIon ModelProcessor class would make qutip a great place to do detailed noise analysis on quantum gates in trapped ions--maybe the only place that isn't behind a pay wall.

There is already a nice list of ModelProcessors, but trapped ions don't quite fit any on the list. A SpinChain is not detailed enough. For example, it ignores the phonon modes of the ions which mediate the multi-qubit Molmer Sorensen gate used in most experiments. These modes are important to keep track of in error analysis. The CavityQED is actually bit closer with the inclusion of a phonon mode, but there is only one phonon mode while for N ions there are N phonon modes.

I propose that a TrappedIon ModelProcessor should be added.

I'd be happy to add it. This would be my first contribution to qutip-qip, so I'd appreciate some guidance.

Running tests and src folder

Is there some preferred way to run tests locally with src/qutip_qip path ? I wanted to check before I made a large number of changes.

Usually, I will navigate to the tests folder cd qutip-project/qutip-qip/tests and then run pytest some_test.py. This does appear to be the preferred method in documentation except the documentation example specifies the directory for pytest exactly whereas I navigate to tests folder (as stated above). I have found two different methods to make sure modules are imported properly. I have used first one successfully and others are some options not yet tried (but do seem reasonable).

  1. Just add src to the module's path.
from src.qutip_qip.qasm import read_qasm
  1. Specify PYTHONPATH manually in file of some_test.py or let Python handle PYTHONPATH
python pytest tests/some_test.py

The automatically generated tutorial summary is not rendered on readthedoc

As reported by @rum1887, the generated tutorial summary is missing on the official version of the doc, https://qutip-qip.readthedocs.io/en/stable/. It is clearly successfully generated by GitHub action, which e.g. can be downloaded and verified here https://github.com/qutip/qutip-qip/actions/runs/6448237594.

Attached is the logging from readthedoc of the most recent build. According to it, the tutorial_v5 file is not successfully generated.
log.txt

Processor run_state is not reproducible due to accumulation of noise channel

There is an after effect to running a simulation. I would expect processors to be reusable and simulations to be repeatable.

Set up a processor for aT1 simulation.

processor = Processor(1, t1=t1, t2=None)
processor.add_pulse(Pulse(None, None, tlist=t_eval, coeff=False))
print(processor.noise)
[]

Run the processor.

a = qt.destroy(2)
psi0 = qt.basis(2,1)
result = processor.run_state(init_state=psi0, e_ops=[a.dag()*a])
print(processor.noise)
<qutip.qip.noise.RelaxationNoise object...>

Now, the next simulation has double the decoherence processes.

The after effect is noted in the docstring of processor.py

    def get_noisy_pulses(self, device_noise=False, drift=False):
        """
        It takes the pulses defined in the `Processor` and
        adds noise according to `Processor.noise`. It does not modify the
        pulses saved in `Processor.pulses` but returns a new list.
        The length of the new list of noisy pulses might be longer
        because of drift Hamiltonian and device noise. They will be
        added to the end of the pulses list.
...

due to calling

    noisy_pulses = process_noise(
        pulses, self.noise, self.dims, t1=self.t1, t2=self.t2,
        device_noise=device_noise)

instead of

    noisy_pulses = process_noise(
        pulses, deepcopy(self.noise), self.dims, t1=self.t1, t2=self.t2,
        device_noise=device_noise)

or modifying the process_noise not to add noise channels to the processor. Is there a reason why there is an append to self.noise inside process_noise?

Pulse class: consider changing description or code refactoring

It has been brought up that the Pulse class can be confusing for the user, as it brings together control pulses, the relative Hamiltonian, and noise added to the control pulses. This issue is opened to discuss this point and see what action to take.

There is also some conceptual overlap between qutip-qip.Pulse and qutip.QobjEvo (and qutip.Qobj for the time-constant part).

Right now the Pulse class is also discussed in https://qutip-qip.readthedocs.io/en/latest/qip-processor.html#modelling-quantum-hardware-with-processor.

The actions to be taken can be discussed here, for example:

  • A. Code refactoring.
  • B. Remove pulse from the public APIs
  • C. Reframing the presentation of Pulse in the documentation (and white paper)

Add Variational Quantum Algorithms Module

QuTiP currently lacks any functionality directly related to VQAs. It would be useful to have a class-based implementation that allows for problem specification down to the details of parametrized/fixed Hamiltonians and the optimisation method.

To outline a possible structure for this module, I have implemented something along these lines in my fork of this repository. This includes:

  1. Optimization_Result class
  • A convenient interface for results of the VQA optimisation method.
  1. VQA_Block class
  • A kind of pseudo-circuit-element. Holds one of either a Hamiltonian, a Unitary, or a pre-defined QuTiP gate (e.g. qutip_qip.operations.x_gate).
  • Takes a list of qubit targets
  • Can be either an "initial" element or not - this will determine if the block is repeated in the circuit layers.
  • In the case that a Hamiltonian or Unitary is specified, the action of the block is implemented by adding a new entry to the QubitCircuit's user_gates dictionary.
  1. VQA class
  • Initialised with n_qubits, n_layers
  • Can specify a cost function in terms of:
    • An observable
    • A state
    • A bitstring (single measurement outcome sampled from the circuit)
  • add_block(block: VQA_Block) appends VQA_Block to the circuit and, if necessary, updates the circuit's user_gates dictionary to include the new gate.
  • optimise_parameters method performs optimisation on the free parameters of the circuit and returns an Optimization_Result instance.
  • Methods that serve as a wrapper to QubitCircuit to simulate the circuit

A usage example can be found at https://github.com/EnBr55/qutip-vqa-examples/blob/main/notebooks/QAOA.ipynb.

I'm currently working on a Parameterized_Hamiltonian model as a VQA_Block input that will allow for defining more general quantum control problems.

Any discussion/suggestions as to these proposed models would be great!

Keep the information of gate duration after the compilation

Currently, the compiler only returns the compiled pulse cofficients and tlist. It is sufficient for the solvers, but a few options in the solver may need more information. For instance, max_step is needed to prevent the solver from skipping pulses after a long idling period or initial states that are invariant under the given Hamiltonian (qutip/qutip#2003, qutip/qutip#2040).

This can be realized in 2 different ways:

  • The Compiler returns more information. On top of the compiled pulses, we could let the compiler return also additional information such as the minimum gate time.
  • Include the information in e.g. in the Coefficents class of qutip-v5 and return the Coefficient Object. This may also provide the opportunely to support different max step sizes for different gates. But it needs considerably more work and may need changes in the core qutip.

problem with the usage of "IDLE" gate

Hello,
I have a question regarding the usage of "IDLE" gate.

%pip install scipy
%pip install qutip
from scipy.constants import constants
import numpy as np
import scipy.sparse as sp
import scipy.sparse.linalg as spla
import scipy.linalg as la
import matplotlib as mpl
import matplotlib.pyplot as plt
import qutip as qu
#from qutip import *
from IPython.display import Math, HTML
%pip install git+https://github.com/qutip/qutip-qip
from qutip_qip.device import ModelProcessor, Model
from qutip_qip.circuit import QubitCircuit
from qutip_qip.compiler import GateCompiler, Instruction, SpinChainCompiler
from qutip import sigmax, sigmay, sigmaz, tensor, fidelity
from qutip import basis
class MyModel(Model):
    def __init__(self, num_qubits, dims=None, h_x=1., h_y=1.,h_z=1.,t1=None, t2=None):
        super().__init__(num_qubits, dims=dims)
        self.params = {
            "sy": [h_y] * num_qubits,
            "sx": [h_x] * num_qubits,
            "sz": [h_z] * num_qubits,
            "t1": t1, #  Will be accessed by the noise module.
            "t2": t2,
        }
        # Control Hamiltonians
        self.controls = {}
        self.controls.update(
            {f"sx{n}": (2 * np.pi * sigmax()/2, n) for n in range(num_qubits)})
        self.controls.update(
            {f"sy{n}": (2 * np.pi * sigmay()/2, n) for n in range(num_qubits)}),
        self.controls.update(
            {f"sz{n}": (2 * np.pi * sigmaz()/2, n) for n in range(num_qubits)}),
        
    def get_control(self, label):
        """
        The mandatory method. It Returns a pair of Qobj and int representing the control Hamiltonian and the target qubit.
        """
        return self.controls[label]

    def get_control_labels(self):
        """
        It returns all the labels of availble controls.
        """
        return self.controls.keys()

class MyCompiler(GateCompiler):
    """Custom compiler for generating pulses from gates using the base class 
    GateCompiler.
    Args:
        num_qubits (int): The number of qubits in the processor
        params (dict): A dictionary of parameters for gate pulses such as
                       the pulse amplitude.
    """

    def __init__(self, num_qubits, params):
        super().__init__(num_qubits, params=params)
        self.params = params
        self.gate_compiler.update({
            "ROT": self.rotation_with_phase_compiler,
            "RX": self.single_qubit_gate_compiler,
            "RY": self.single_qubit_gate_compiler,
        })

    def generate_pulse(self, gate, tlist, coeff, phase=0.0):
        """Generates the pulses.
        Args:
            gate (qutip_qip.circuit.Gate): A qutip Gate object.
            tlist (array): A list of times for the evolution.
            coeff (array): An array of coefficients for the gate pulses
            phase (float): The value of the phase for the gate.
        Returns:
            Instruction (qutip_qip.compiler.instruction.Instruction): An instruction
            to implement a gate containing the control pulses.                                               
        """
        pulse_info = [
            # (control label, coeff)
            ("sx" + str(gate.targets[0]), np.cos(phase) * coeff),
            ("sy" + str(gate.targets[0]), np.sin(phase) * coeff),
        ]
        return [Instruction(gate, tlist=tlist, pulse_info=pulse_info)]

    def single_qubit_gate_compiler(self, gate, args):
        """Compiles single qubit gates to pulses.
        
        Args:
            gate (qutip_qip.circuit.Gate): A qutip Gate object.
        
        Returns:
            Instruction (qutip_qip.compiler.instruction.Instruction): An instruction
            to implement a gate containing the control pulses.
        """
        # gate.arg_value is the rotation angle
        tlist = np.abs(gate.arg_value) / self.params["pulse_amplitude"]
        coeff = self.params["pulse_amplitude"] * np.sign(gate.arg_value)
        coeff /= 2 * np.pi
        if gate.name == "RX":
            return self.generate_pulse(gate, tlist, coeff, phase=0.0)
        elif gate.name == "RY":
            return self.generate_pulse(gate, tlist, coeff, phase=np.pi / 2)

    def rotation_with_phase_compiler(self, gate, args):
        """Compiles gates with a phase term.
        Args:
            gate (qutip_qip.circuit.Gate): A qutip Gate object.
        
        Returns:
            Instruction (qutip_qip.compiler.instruction.Instruction): An instruction
            to implement a gate containing the control pulses.
        """
        # gate.arg_value is the pulse phase
        tlist = self.params["duration"]
        coeff = self.params["pulse_amplitude"]
        coeff /= 2 * np.pi
        return self.generate_pulse(gate, tlist, coeff, phase=gate.arg_value)


#ideal
print('ideal')
print(basis(2, 0).full)
circuit = QubitCircuit(1)
circuit.add_gate("RX", targets=0, arg_value=np.pi/2)
wait_time=0.1
circuit.add_gate("IDLE", 0, arg_value=wait_time)
circuit.add_gate("RX", targets=0, arg_value=np.pi/2)
result_no_relax = circuit.run(basis(2, 0))
print(result_no_relax)

#real
print('real')
myprocessor = ModelProcessor(model=MyModel(num_qubits=1, h_x=1.0, h_y=1.0, h_z=1.0))
myprocessor.native_gates = ["RX", "RY"]
mycompiler = MyCompiler(1, {"pulse_amplitude": 2})

pulses=myprocessor.load_circuit(circuit, compiler=mycompiler)
print('times')
print(pulses[0])
print('amplitudes')
print(pulses[1])

print(basis(2, 0).full)
result2 = myprocessor.run_state(basis(2, 0))
print(result2.states)

With the above codes, I obtained the following result:

ideal
<bound method Qobj.full of Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket
Qobj data =
[[1.]
[0.]]>
Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket
Qobj data =
[[0.+0.j]
[0.-1.j]]
real
times
{'sx0': array([0. , 0.78539816, 0.88539816, 1.67079633]), 'sy0': array([0. , 0.78539816, 0.88539816, 1.67079633])}
amplitudes
{'sx0': array([0.31830989, 0. , 0.31830989]), 'sy0': array([0., 0., 0.])}
<bound method Qobj.full of Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket
Qobj data =
[[1.]
[0.]]>
[Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket
Qobj data =
[[1.]
[0.]], Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket
Qobj data =
[[0.70710798+0.j ]
[0. -0.70710559j]], Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket
Qobj data =
[[0.70710732+0.j ]
[0. -0.70710624j]], Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket
Qobj data =
[[8.55710936e-07+0.j]
[0.00000000e+00-1.j]]]

, which is fine.

However when I set "waittime=0.2", I obtained a wrong result:

ideal
<bound method Qobj.full of Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket
Qobj data =
[[1.]
[0.]]>
Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket
Qobj data =
[[0.+0.j]
[0.-1.j]]
real
times
{'sx0': array([0. , 0.78539816, 0.98539816, 1.77079633]), 'sy0': array([0. , 0.78539816, 0.98539816, 1.77079633])}
amplitudes
{'sx0': array([0.31830989, 0. , 0.31830989]), 'sy0': array([0., 0., 0.])}
<bound method Qobj.full of Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket
Qobj data =
[[1.]
[0.]]>
[Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket
Qobj data =
[[1.]
[0.]], Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket
Qobj data =
[[0.70710798+0.j ]
[0. -0.70710559j]], Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket
Qobj data =
[[0.70710798+0.j ]
[0. -0.70710559j]], Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket
Qobj data =
[[0.70710798+0.j ]
[0. -0.70710559j]]]

It is strange...

It looks like any gate cannot be applied the "IDLE" gate...

Add `about()` function

Since we are branching out and making qutip_qip a separate library, it might be beneficial to have something like qutip.about for qip. The about() function could list out the QuTiP version that the code is run with and help in debugging in the future.

Here is how the about function in QuTiP is written:

"""
Command line output of information on QuTiP and dependencies.
"""
__all__ = ['about']

import sys
import os
import platform
import numpy
import scipy
import inspect
from qutip.utilities import _blas_info
import qutip.settings
from qutip.hardware_info import hardware_info


def about():
    """
    About box for QuTiP. Gives version numbers for QuTiP, NumPy, SciPy, Cython,
    and MatPlotLib.
    """
    print("")
    print("QuTiP: Quantum Toolbox in Python")
    print("================================")
    print("Copyright (c) QuTiP team 2011 and later.")
    print(
        "Current admin team: Alexander Pitchford, "
        "Nathan Shammah, Shahnawaz Ahmed, Neill Lambert, Eric Giguère, "
        "Boxi Li, Jake Lishman and Simon Cross."
    )
    print(
        "Board members: Daniel Burgarth, Robert Johansson, Anton F. Kockum, "
        "Franco Nori and Will Zeng."
    )
    print("Original developers: R. J. Johansson & P. D. Nation.")
    print("Previous lead developers: Chris Granade & A. Grimsmo.")
    print("Currently developed through wide collaboration. "
          "See https://github.com/qutip for details.")
    print("")
    print("QuTiP Version:      %s" % qutip.__version__)
    print("Numpy Version:      %s" % numpy.__version__)
    print("Scipy Version:      %s" % scipy.__version__)
    try:
        import Cython
        cython_ver = Cython.__version__
    except ImportError:
        cython_ver = 'None'
    print("Cython Version:     %s" % cython_ver)
    try:
        import matplotlib
        matplotlib_ver = matplotlib.__version__
    except ImportError:
        matplotlib_ver = 'None'
    print("Matplotlib Version: %s" % matplotlib_ver)
    print("Python Version:     %d.%d.%d" % sys.version_info[0:3])
    print("Number of CPUs:     %s" % hardware_info()['cpus'])
    print("BLAS Info:          %s" % _blas_info())
    print("OPENMP Installed:   %s" % str(qutip.settings.has_openmp))
    print("INTEL MKL Ext:      %s" % str(qutip.settings.has_mkl))
    print("Platform Info:      %s (%s)" % (platform.system(),
                                           platform.machine()))
    qutip_install_path = os.path.dirname(inspect.getsourcefile(qutip))
    print("Installation path:  %s" % qutip_install_path)

    # citation
    longbar = "=" * 80
    cite_msg = "For your convenience a bibtex reference can be easily"
    cite_msg += " generated using `qutip.cite()`"
    print(longbar)
    print("Please cite QuTiP in your publication.")
    print(longbar)
    print(cite_msg)


if __name__ == "__main__":
    about()

Add qutip-qip tutorials to documentation on Read the docs

Issue description

Add links for exisiting qutip-qip notebooks on the read the docs documentation at https://qutip-qip.readthedocs.io/en/stable/, similarly to what is done for Mitiq. Right now there is a tutorials section (see screenshot below)

qutipqip-doc

but it only contains a link to qutip.org, as the tutorials appear only on the https://qutip.org/qutip-tutorials/ under the QIP section, see screenshot below.

qutip-qip

Proposed Solution

The tricky part is that we don't want to duplicate the files, the source files should remain from qutip-tutorials, such as for the
"Decomposition of the Toffoli gate in terms of CNOT and single-qubit rotations" that is rendered from

https://nbviewer.org/urls/qutip.org/qutip-tutorials/tutorials-v4/quantum-circuits/qip-toffoli-cnot.ipynb

The tutorials should be kept also on the https://qutip.org/qutip-tutorials/#quantum-information-processing as they currently exist.

References

Steps to Maintaining Commit History

It seems like maintaining the commit history is slightly more tricky than anticipated. I am trying to see if there is a clean way to do it but in the meantime it would be nice to decide on the name for the folder where all our source files should sit in. I think @BoxiLi proposed src. We could also go with something like qutip_qip if we want to stick to that convention of naming.

There is some guidance if we go down the src path:

https://blog.ionelmc.ro/2014/05/25/python-packaging/#the-structure

but I am not sure how well this will all gel with core qutip if we do entrypoints.

One test fails

========================================================================================== FAILURES ===========================================================================================
________________________________________________________________________________________ test session _________________________________________________________________________________________

cls = <class '_pytest.runner.CallInfo'>, func = <function call_runtest_hook.<locals>.<lambda> at 0xa5c74c280>, when = 'call'
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)

    @classmethod
    def from_call(
        cls,
        func: "Callable[[], TResult]",
        when: "Literal['collect', 'setup', 'call', 'teardown']",
        reraise: Optional[
            Union[Type[BaseException], Tuple[Type[BaseException], ...]]
        ] = None,
    ) -> "CallInfo[TResult]":
        """Call func, wrapping the result in a CallInfo.
    
        :param func:
            The function to call. Called without arguments.
        :param when:
            The phase in which the function is called.
        :param reraise:
            Exception or exceptions that shall propagate if raised by the
            function, instead of being wrapped in the CallInfo.
        """
        excinfo = None
        start = timing.time()
        precise_start = timing.perf_counter()
        try:
>           result: Optional[TResult] = func()

/usr/local/lib/python3.9/site-packages/_pytest/runner.py:341: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

>       lambda: ihook(item=item, **kwds), when=when, reraise=reraise
    )

/usr/local/lib/python3.9/site-packages/_pytest/runner.py:262: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <_HookCaller 'pytest_runtest_call'>, kwargs = {'item': <CheckdocsItem project>}, firstresult = False

    def __call__(self, **kwargs: object) -> Any:
        assert (
            not self.is_historic()
        ), "Cannot directly call a historic hook - use call_historic instead."
        self._verify_all_args_are_provided(kwargs)
        firstresult = self.spec.opts.get("firstresult", False) if self.spec else False
>       return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)

/usr/local/lib/python3.9/site-packages/pluggy/_hooks.py:433: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <_pytest.config.PytestPluginManager object at 0x8291a0610>, hook_name = 'pytest_runtest_call'
methods = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from '/usr/local/lib/python3.9/site-packages/_pytest/...'trio', plugin=<module 'pytest_trio.plugin' from '/usr/local/lib/python3.9/site-packages/pytest_trio/plugin.py'>>, ...]
kwargs = {'item': <CheckdocsItem project>}, firstresult = False

    def _hookexec(
        self,
        hook_name: str,
        methods: Sequence[HookImpl],
        kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        # called from all hookcaller instances.
        # enable_tracing will set its own wrapping function at self._inner_hookexec
>       return self._inner_hookexec(hook_name, methods, kwargs, firstresult)

/usr/local/lib/python3.9/site-packages/pluggy/_manager.py:112: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

hook_name = 'pytest_runtest_call'
hook_impls = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from '/usr/local/lib/python3.9/site-packages/_pytest/...'trio', plugin=<module 'pytest_trio.plugin' from '/usr/local/lib/python3.9/site-packages/pytest_trio/plugin.py'>>, ...]
caller_kwargs = {'item': <CheckdocsItem project>}, firstresult = False

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from _HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        only_new_style_wrappers = True
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError:
                        for argname in hook_impl.argnames:
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                )
    
                    if hook_impl.hookwrapper:
                        only_new_style_wrappers = False
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            wrapper_gen = cast(Generator[None, _Result[object], None], res)
                            next(wrapper_gen)  # first yield
                            teardowns.append((wrapper_gen,))
                        except StopIteration:
                            _raise_wrapfail(wrapper_gen, "did not yield")
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
                        res = hook_impl.function(*args)
                        if res is not None:
                            results.append(res)
                            if firstresult:  # halt further impl calls
                                break
            except BaseException as exc:
                exception = exc
        finally:
            # Fast path - only new-style wrappers, no _Result.
            if only_new_style_wrappers:
                if firstresult:  # first result hooks return a single value
                    result = results[0] if results else None
                else:
                    result = results
    
                # run all wrapper post-yield blocks
                for teardown in reversed(teardowns):
                    try:
                        if exception is not None:
                            teardown.throw(exception)  # type: ignore[union-attr]
                        else:
                            teardown.send(result)  # type: ignore[union-attr]
                        # Following is unreachable for a well behaved hook wrapper.
                        # Try to force finalizers otherwise postponed till GC action.
                        # Note: close() may raise if generator handles GeneratorExit.
                        teardown.close()  # type: ignore[union-attr]
                    except StopIteration as si:
                        result = si.value
                        exception = None
                        continue
                    except BaseException as e:
                        exception = e
                        continue
                    _raise_wrapfail(teardown, "has second yield")  # type: ignore[arg-type]
    
                if exception is not None:
                    raise exception.with_traceback(exception.__traceback__)
                else:
                    return result
    
            # Slow path - need to support old-style wrappers.
            else:
                if firstresult:  # first result hooks return a single value
                    outcome: _Result[object | list[object]] = _Result(
                        results[0] if results else None, exception
                    )
                else:
                    outcome = _Result(results, exception)
    
                # run all wrapper post-yield blocks
                for teardown in reversed(teardowns):
                    if isinstance(teardown, tuple):
                        try:
                            teardown[0].send(outcome)
                            _raise_wrapfail(teardown[0], "has second yield")
                        except StopIteration:
                            pass
                    else:
                        try:
                            if outcome._exception is not None:
                                teardown.throw(outcome._exception)
                            else:
                                teardown.send(outcome._result)
                            # Following is unreachable for a well behaved hook wrapper.
                            # Try to force finalizers otherwise postponed till GC action.
                            # Note: close() may raise if generator handles GeneratorExit.
                            teardown.close()
                        except StopIteration as si:
                            outcome.force_result(si.value)
                            continue
                        except BaseException as e:
                            outcome.force_exception(e)
                            continue
                        _raise_wrapfail(teardown, "has second yield")
    
>               return outcome.get_result()

/usr/local/lib/python3.9/site-packages/pluggy/_callers.py:155: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pluggy._result._Result object at 0xa4f470430>

    def get_result(self) -> _T:
        """Get the result(s) for this hook call.
    
        If the hook was marked as a ``firstresult`` only a single value
        will be returned, otherwise a list of results.
        """
        __tracebackhide__ = True
        exc = self._exception
        if exc is None:
            return cast(_T, self._result)
        else:
>           raise exc.with_traceback(exc.__traceback__)

/usr/local/lib/python3.9/site-packages/pluggy/_result.py:108: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

hook_name = 'pytest_runtest_call'
hook_impls = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from '/usr/local/lib/python3.9/site-packages/_pytest/...'trio', plugin=<module 'pytest_trio.plugin' from '/usr/local/lib/python3.9/site-packages/pytest_trio/plugin.py'>>, ...]
caller_kwargs = {'item': <CheckdocsItem project>}, firstresult = False

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from _HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        only_new_style_wrappers = True
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError:
                        for argname in hook_impl.argnames:
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                )
    
                    if hook_impl.hookwrapper:
                        only_new_style_wrappers = False
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            wrapper_gen = cast(Generator[None, _Result[object], None], res)
                            next(wrapper_gen)  # first yield
                            teardowns.append((wrapper_gen,))
                        except StopIteration:
                            _raise_wrapfail(wrapper_gen, "did not yield")
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
>                       res = hook_impl.function(*args)

/usr/local/lib/python3.9/site-packages/pluggy/_callers.py:80: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

item = <CheckdocsItem project>

    def pytest_runtest_call(item: Item) -> None:
        _update_current_test_var(item, "call")
        try:
            del sys.last_type
            del sys.last_value
            del sys.last_traceback
        except AttributeError:
            pass
        try:
            item.runtest()
        except Exception as e:
            # Store trace info to allow postmortem debugging
            sys.last_type = type(e)
            sys.last_value = e
            assert e.__traceback__ is not None
            # Skip *this* frame
            sys.last_traceback = e.__traceback__.tb_next
>           raise e

/usr/local/lib/python3.9/site-packages/_pytest/runner.py:177: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

item = <CheckdocsItem project>

    def pytest_runtest_call(item: Item) -> None:
        _update_current_test_var(item, "call")
        try:
            del sys.last_type
            del sys.last_value
            del sys.last_traceback
        except AttributeError:
            pass
        try:
>           item.runtest()

/usr/local/lib/python3.9/site-packages/_pytest/runner.py:169: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <CheckdocsItem project>

    def runtest(self):
        desc = self.get_long_description()
        method_name = f"run_{re.sub('[-/]', '_', desc.content_type)}"
>       getattr(self, method_name)(desc)
E       AttributeError: 'CheckdocsItem' object has no attribute 'run_text_markdown; variant=GFM'

/usr/local/lib/python3.9/site-packages/pytest_checkdocs/__init__.py:43: AttributeError
====================================================================================== warnings summary =======================================================================================
tests/test_circuit.py:505
  /usr/ports/devel/py-qutip-qip/work-py39/qutip-qip-0.3.0/tests/test_circuit.py:505: PytestUnknownMarkWarning: Unknown pytest.mark.repeat - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.repeat(10)

tests/test_measurement.py:14
  /usr/ports/devel/py-qutip-qip/work-py39/qutip-qip-0.3.0/tests/test_measurement.py:14: PytestUnknownMarkWarning: Unknown pytest.mark.repeat - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.repeat(10)

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=================================================================================== short test summary info ===================================================================================
SKIPPED [1] tests/test_qir.py:10: could not import 'pyqir.generator': No module named 'pyqir.generator'
SKIPPED [3] tests/test_device.py:86: unconditional skip
SKIPPED [3] tests/test_device.py:72: unconditional skip
SKIPPED [4] tests/test_device.py:101: unconditional skip
SKIPPED [1] tests/test_circuit.py:739: requires pdflatex
============================================================== 1 failed, 505 passed, 12 skipped, 2 warnings in 85.69s (0:01:25) ===============================================================

Version: 0.3.0
Python-3.9
FreeBSD 13.2

Error when building or testing the docs

From terminal, I get this error:

  ~/gi/qutip-qip   master ❯ cd docs                              mitiqdev
  ~/gi/qutip-qip/docs   master ❯ make doctest                    mitiqdev
Running Sphinx v3.5.4

Extension error:
Could not import extension numpydoc (exception: No module named 'numpydoc')
make: *** [doctest] Error 2
  ~/gi/qutip-qip/docs   master ❯ pip install sphinx numpydoc sphinx_rtd_theme doctest

Requirement already satisfied: sphinx in /opt/miniconda3/envs/mitiqdev/lib/python3.8/site-packages (3.5.4)
Collecting numpydoc
  Using cached numpydoc-1.1.0-py3-none-any.whl (47 kB)
Collecting sphinx_rtd_theme
  Downloading sphinx_rtd_theme-0.5.2-py2.py3-none-any.whl (9.1 MB)
     |████████████████████████████████| 9.1 MB 7.5 MB/s
ERROR: Could not find a version that satisfies the requirement doctest (from versions: none)
ERROR: No matching distribution found for doctest
  ~/gi/qutip-qip/docs   master ❯ make html                       mitiqdev
Running Sphinx v3.5.4

Extension error:
Could not import extension numpydoc (exception: No module named 'numpydoc')
make: *** [html] Error 2

Is the following wrong?

pip install sphinx numpydoc sphinx_rtd_theme doctest

Make changes to installation order for circuit diagrams

As stated here, ImageMagick and latex have to be installed in order to get the circuit diagrams.

When trying to install ImageMagick, conda gets stuck trying to solve the environment (force stopped the process after +6 hours). The environment has all of the development dependencies for qutip-qip as well as the ones for documentation.

Regarding the types of the parameters of QubitCircuit.add_gate

Currently the parameters for QubitCircuit.add_gate require:

  1. targets and controls to be a list
  2. arg_value to be a float

1. is fine, however just integers work as in the method it's straight up put in a Gate object which does accept both integers and lists.
In one of your examples in Scheduler.schedule the targets and controls are also inputted as plain ints, which some IDEs will mark as errors or for example mypy just straight up gives it an error in CI/CD if you run a type checker.
So my suggestion is to update the type hint in add_gate to also include integers inline with the Gate object.

My main issue is with 2. arg_value is an object that is put raw in a (potentially) user defined method for the gate. Two issues with that:

  1. It doesn't have to be a float. Might be some situations were you don't input an angle?
  2. It can be more than one argument

For example if you have a gate that implements a general single qubit you need two angles to define that: def r_gate(theta: float, phi: float) -> Qobj. However at most one parameter is allowed

So for now I have done something like qc.add_gate("r_gate", targets=[q], arg_values=(np.pi, 0)), (which gives a type error because (np.pi, 0) is a tuple, not a float). And then in the method I need to add theta, phi = args.
Whereas ideally I can just do qc.add_gate("r_gate", targets=q, theta=np.pi, phi=0) or qc.add_gate("r_gate", targets=q, args={"theta": np.pi, "phi": 0}.
So I request to change the arg_value with a proper **args that are directly inputted into the gate function as arguments.
Or at least change the type of arg_value to from float Any

How can i generate the quantum circuit image with qutip?

Dear Respected Members of the Group,

I am trying to generate the quantum circuit with qutip,following the qutip document

https://nbviewer.org/urls/qutip.org/qutip-tutorials/tutorials-v4/quantum-circuits/quantum-gates.ipynb
The doucument said we need four packages to achieve generating the quantum circuit image.
asdhajkshd

But I am sure about I definitly did pip install pdflatex,and I can find "pdflatex" in my folder.But when I want to find "pdflatex" in Command(cmd), using where pdflatex, some troubles happend.

sada

The chinese of the above picture means: INFO: Could not find the file with the provided pattern.

My computer system is windows 11,and already has installed Imagemagick, perl, Ghostscript ,latex and pdflatex

I kindly ask for your help with many many thanks in advance.
Thank you from the bottom of my deep heart
Yours sincerely.

5 tests fail

===>  Testing for py39-qutip-qip-0.2.3
===>   py39-qutip-qip-0.2.3 depends on file: /usr/local/bin/python3.9 - found
========================================================================================== test session starts ==========================================================================================
platform freebsd13 -- Python 3.9.16, pytest-7.2.1, pluggy-1.0.0
Using --randomly-seed=401725657
rootdir: /usr/ports/devel/py-qutip-qip/work-py39
plugins: forked-1.4.0, hypothesis-6.65.2, mypy-plugins-1.10.1, cov-2.9.0, randomly-3.12.0, timeout-2.1.0, rerunfailures-10.1, flaky-3.7.0, xdist-2.5.0, env-0.6.2, mock-3.10.0, freezegun-0.4.2, typeguard-3.0.2
collected 479 items                                                                                                                                                                                     

qutip-qip-0.2.3/tests/test_model.py ......                                                                                                                                                        [  1%]
qutip-qip-0.2.3/tests/decomposition_functions/test_single_qubit_gate_decompositions.py ................................................................................................           [ 21%]
qutip-qip-0.2.3/tests/test_circuit.py ................................s..................                                                                                                         [ 31%]
qutip-qip-0.2.3/tests/test_gates.py ........................................................................................................                                                      [ 53%]
qutip-qip-0.2.3/tests/decomposition_functions/test_utility.py ...........                                                                                                                         [ 55%]
qutip-qip-0.2.3/tests/test_pulse.py .....                                                                                                                                                         [ 56%]
qutip-qip-0.2.3/tests/test_compiler.py .......................                                                                                                                                    [ 61%]
qutip-qip-0.2.3/tests/test_noise.py ..........                                                                                                                                                    [ 63%]
qutip-qip-0.2.3/tests/test_processor.py ..........F....                                                                                                                                           [ 67%]
qutip-qip-0.2.3/tests/test_scheduler.py ..........                                                                                                                                                [ 69%]
qutip-qip-0.2.3/tests/test_qft.py ....                                                                                                                                                            [ 69%]
qutip-qip-0.2.3/tests/test_qubits.py .                                                                                                                                                            [ 70%]
qutip-qip-0.2.3/tests/test_measurement.py .............                                                                                                                                           [ 72%]
qutip-qip-0.2.3/tests/test_optpulseprocessor.py ....                                                                                                                                              [ 73%]
qutip-qip-0.2.3/tests/test_device.py s.......................F.......s.................s...s............sF.F........s.s...............F............s...s..s                                       [ 98%]
qutip-qip-0.2.3/tests/test_qasm.py ........                                                                                                                                                       [100%]

=============================================================================================== FAILURES ================================================================================================
____________________________________________________________________________________ TestCircuitProcessor.test_plot _____________________________________________________________________________________

self = <tests.test_processor.TestCircuitProcessor object at 0x9b8702cd0>

    def test_plot(self):
        """
        Test for plotting functions
        """
        try:
            import matplotlib.pyplot as plt
        except Exception:
            return True
        # step_func
        tlist = np.linspace(0., 2*np.pi, 20)
        processor = Processor(N=1, spline_kind="step_func")
        processor.add_control(sigmaz(), label="sz")
        processor.set_all_coeffs({"sz": np.array([np.sin(t) for t in tlist])})
        processor.set_all_tlist(tlist)
>       fig, _ = processor.plot_pulses(use_control_latex=False)

qutip-qip-0.2.3/tests/test_processor.py:163: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
stage/usr/local/lib/python3.9/site-packages/qutip_qip/device/processor.py:865: in plot_pulses
    fig = plt.figure(figsize=figsize, dpi=dpi)
/usr/local/lib/python3.9/site-packages/matplotlib/pyplot.py:797: in figure
    manager = new_figure_manager(
/usr/local/lib/python3.9/site-packages/matplotlib/pyplot.py:316: in new_figure_manager
    return _backend_mod.new_figure_manager(*args, **kwargs)
/usr/local/lib/python3.9/site-packages/matplotlib/backend_bases.py:3544: in new_figure_manager
    fig = fig_cls(*args, **kwargs)
/usr/local/lib/python3.9/site-packages/matplotlib/figure.py:2272: in __init__
    FigureCanvasBase(self)  # Set self.canvas.
/usr/local/lib/python3.9/site-packages/matplotlib/backend_bases.py:1717: in __init__
    self._fix_ipython_backend2gui()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

cls = <class 'matplotlib.backend_bases.FigureCanvasBase'>

    @classmethod
    @functools.lru_cache()
    def _fix_ipython_backend2gui(cls):
        # Fix hard-coded module -> toolkit mapping in IPython (used for
        # `ipython --auto`).  This cannot be done at import time due to
        # ordering issues, so we do it when creating a canvas, and should only
        # be done once per class (hence the `lru_cache(1)`).
        if sys.modules.get("IPython") is None:
            return
        import IPython
>       ip = IPython.get_ipython()
E       AttributeError: module 'IPython' has no attribute 'get_ipython'

/usr/local/lib/python3.9/site-packages/matplotlib/backend_bases.py:1747: AttributeError
_______________________________________________________________________________ test_pulse_plotting[DispersiveCavityQED] ________________________________________________________________________________

processor_class = <class 'qutip_qip.device.cavityqed.DispersiveCavityQED'>

    @pytest.mark.parametrize(
        "processor_class",
        [DispersiveCavityQED, LinearSpinChain, CircularSpinChain, SCQubits])
    def test_pulse_plotting(processor_class):
        try:
            import matplotlib.pyplot as plt
        except Exception:
            return True
        qc = QubitCircuit(3)
        qc.add_gate("CNOT", 1, 0)
        qc.add_gate("X", 1)
    
        processor = processor_class(3)
        processor.load_circuit(qc)
>       fig, ax = processor.plot_pulses()

qutip-qip-0.2.3/tests/test_device.py:211: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
stage/usr/local/lib/python3.9/site-packages/qutip_qip/device/processor.py:865: in plot_pulses
    fig = plt.figure(figsize=figsize, dpi=dpi)
/usr/local/lib/python3.9/site-packages/matplotlib/pyplot.py:797: in figure
    manager = new_figure_manager(
/usr/local/lib/python3.9/site-packages/matplotlib/pyplot.py:316: in new_figure_manager
    return _backend_mod.new_figure_manager(*args, **kwargs)
/usr/local/lib/python3.9/site-packages/matplotlib/backend_bases.py:3544: in new_figure_manager
    fig = fig_cls(*args, **kwargs)
/usr/local/lib/python3.9/site-packages/matplotlib/figure.py:2272: in __init__
    FigureCanvasBase(self)  # Set self.canvas.
/usr/local/lib/python3.9/site-packages/matplotlib/backend_bases.py:1717: in __init__
    self._fix_ipython_backend2gui()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

cls = <class 'matplotlib.backend_bases.FigureCanvasBase'>

    @classmethod
    @functools.lru_cache()
    def _fix_ipython_backend2gui(cls):
        # Fix hard-coded module -> toolkit mapping in IPython (used for
        # `ipython --auto`).  This cannot be done at import time due to
        # ordering issues, so we do it when creating a canvas, and should only
        # be done once per class (hence the `lru_cache(1)`).
        if sys.modules.get("IPython") is None:
            return
        import IPython
>       ip = IPython.get_ipython()
E       AttributeError: module 'IPython' has no attribute 'get_ipython'

/usr/local/lib/python3.9/site-packages/matplotlib/backend_bases.py:1747: AttributeError
________________________________________________________________________________ test_pulse_plotting[CircularSpinChain] _________________________________________________________________________________

processor_class = <class 'qutip_qip.device.spinchain.CircularSpinChain'>

    @pytest.mark.parametrize(
        "processor_class",
        [DispersiveCavityQED, LinearSpinChain, CircularSpinChain, SCQubits])
    def test_pulse_plotting(processor_class):
        try:
            import matplotlib.pyplot as plt
        except Exception:
            return True
        qc = QubitCircuit(3)
        qc.add_gate("CNOT", 1, 0)
        qc.add_gate("X", 1)
    
        processor = processor_class(3)
        processor.load_circuit(qc)
>       fig, ax = processor.plot_pulses()

qutip-qip-0.2.3/tests/test_device.py:211: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
stage/usr/local/lib/python3.9/site-packages/qutip_qip/device/processor.py:865: in plot_pulses
    fig = plt.figure(figsize=figsize, dpi=dpi)
/usr/local/lib/python3.9/site-packages/matplotlib/pyplot.py:797: in figure
    manager = new_figure_manager(
/usr/local/lib/python3.9/site-packages/matplotlib/pyplot.py:316: in new_figure_manager
    return _backend_mod.new_figure_manager(*args, **kwargs)
/usr/local/lib/python3.9/site-packages/matplotlib/backend_bases.py:3544: in new_figure_manager
    fig = fig_cls(*args, **kwargs)
/usr/local/lib/python3.9/site-packages/matplotlib/figure.py:2272: in __init__
    FigureCanvasBase(self)  # Set self.canvas.
/usr/local/lib/python3.9/site-packages/matplotlib/backend_bases.py:1717: in __init__
    self._fix_ipython_backend2gui()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

cls = <class 'matplotlib.backend_bases.FigureCanvasBase'>

    @classmethod
    @functools.lru_cache()
    def _fix_ipython_backend2gui(cls):
        # Fix hard-coded module -> toolkit mapping in IPython (used for
        # `ipython --auto`).  This cannot be done at import time due to
        # ordering issues, so we do it when creating a canvas, and should only
        # be done once per class (hence the `lru_cache(1)`).
        if sys.modules.get("IPython") is None:
            return
        import IPython
>       ip = IPython.get_ipython()
E       AttributeError: module 'IPython' has no attribute 'get_ipython'

/usr/local/lib/python3.9/site-packages/matplotlib/backend_bases.py:1747: AttributeError
_________________________________________________________________________________ test_pulse_plotting[LinearSpinChain] __________________________________________________________________________________

processor_class = <class 'qutip_qip.device.spinchain.LinearSpinChain'>

    @pytest.mark.parametrize(
        "processor_class",
        [DispersiveCavityQED, LinearSpinChain, CircularSpinChain, SCQubits])
    def test_pulse_plotting(processor_class):
        try:
            import matplotlib.pyplot as plt
        except Exception:
            return True
        qc = QubitCircuit(3)
        qc.add_gate("CNOT", 1, 0)
        qc.add_gate("X", 1)
    
        processor = processor_class(3)
        processor.load_circuit(qc)
>       fig, ax = processor.plot_pulses()

qutip-qip-0.2.3/tests/test_device.py:211: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
stage/usr/local/lib/python3.9/site-packages/qutip_qip/device/processor.py:865: in plot_pulses
    fig = plt.figure(figsize=figsize, dpi=dpi)
/usr/local/lib/python3.9/site-packages/matplotlib/pyplot.py:797: in figure
    manager = new_figure_manager(
/usr/local/lib/python3.9/site-packages/matplotlib/pyplot.py:316: in new_figure_manager
    return _backend_mod.new_figure_manager(*args, **kwargs)
/usr/local/lib/python3.9/site-packages/matplotlib/backend_bases.py:3544: in new_figure_manager
    fig = fig_cls(*args, **kwargs)
/usr/local/lib/python3.9/site-packages/matplotlib/figure.py:2272: in __init__
    FigureCanvasBase(self)  # Set self.canvas.
/usr/local/lib/python3.9/site-packages/matplotlib/backend_bases.py:1717: in __init__
    self._fix_ipython_backend2gui()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

cls = <class 'matplotlib.backend_bases.FigureCanvasBase'>

    @classmethod
    @functools.lru_cache()
    def _fix_ipython_backend2gui(cls):
        # Fix hard-coded module -> toolkit mapping in IPython (used for
        # `ipython --auto`).  This cannot be done at import time due to
        # ordering issues, so we do it when creating a canvas, and should only
        # be done once per class (hence the `lru_cache(1)`).
        if sys.modules.get("IPython") is None:
            return
        import IPython
>       ip = IPython.get_ipython()
E       AttributeError: module 'IPython' has no attribute 'get_ipython'

/usr/local/lib/python3.9/site-packages/matplotlib/backend_bases.py:1747: AttributeError
_____________________________________________________________________________________ test_pulse_plotting[SCQubits] _____________________________________________________________________________________

processor_class = <class 'qutip_qip.device.circuitqed.SCQubits'>

    @pytest.mark.parametrize(
        "processor_class",
        [DispersiveCavityQED, LinearSpinChain, CircularSpinChain, SCQubits])
    def test_pulse_plotting(processor_class):
        try:
            import matplotlib.pyplot as plt
        except Exception:
            return True
        qc = QubitCircuit(3)
        qc.add_gate("CNOT", 1, 0)
        qc.add_gate("X", 1)
    
        processor = processor_class(3)
        processor.load_circuit(qc)
>       fig, ax = processor.plot_pulses()

qutip-qip-0.2.3/tests/test_device.py:211: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
stage/usr/local/lib/python3.9/site-packages/qutip_qip/device/processor.py:865: in plot_pulses
    fig = plt.figure(figsize=figsize, dpi=dpi)
/usr/local/lib/python3.9/site-packages/matplotlib/pyplot.py:797: in figure
    manager = new_figure_manager(
/usr/local/lib/python3.9/site-packages/matplotlib/pyplot.py:316: in new_figure_manager
    return _backend_mod.new_figure_manager(*args, **kwargs)
/usr/local/lib/python3.9/site-packages/matplotlib/backend_bases.py:3544: in new_figure_manager
    fig = fig_cls(*args, **kwargs)
/usr/local/lib/python3.9/site-packages/matplotlib/figure.py:2272: in __init__
    FigureCanvasBase(self)  # Set self.canvas.
/usr/local/lib/python3.9/site-packages/matplotlib/backend_bases.py:1717: in __init__
    self._fix_ipython_backend2gui()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

cls = <class 'matplotlib.backend_bases.FigureCanvasBase'>

    @classmethod
    @functools.lru_cache()
    def _fix_ipython_backend2gui(cls):
        # Fix hard-coded module -> toolkit mapping in IPython (used for
        # `ipython --auto`).  This cannot be done at import time due to
        # ordering issues, so we do it when creating a canvas, and should only
        # be done once per class (hence the `lru_cache(1)`).
        if sys.modules.get("IPython") is None:
            return
        import IPython
>       ip = IPython.get_ipython()
E       AttributeError: module 'IPython' has no attribute 'get_ipython'

/usr/local/lib/python3.9/site-packages/matplotlib/backend_bases.py:1747: AttributeError
=========================================================================================== warnings summary ============================================================================================
../../../../local/lib/python3.9/site-packages/matplotlib/__init__.py:152
../../../../local/lib/python3.9/site-packages/matplotlib/__init__.py:152
../../../../local/lib/python3.9/site-packages/matplotlib/__init__.py:152
../../../../local/lib/python3.9/site-packages/matplotlib/__init__.py:152
../../../../local/lib/python3.9/site-packages/matplotlib/__init__.py:152
  /usr/local/lib/python3.9/site-packages/matplotlib/__init__.py:152: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
    if LooseVersion(module.__version__) < minver:

../../../../local/lib/python3.9/site-packages/setuptools/_distutils/version.py:346
../../../../local/lib/python3.9/site-packages/setuptools/_distutils/version.py:346
../../../../local/lib/python3.9/site-packages/setuptools/_distutils/version.py:346
../../../../local/lib/python3.9/site-packages/setuptools/_distutils/version.py:346
../../../../local/lib/python3.9/site-packages/setuptools/_distutils/version.py:346
  /usr/local/lib/python3.9/site-packages/setuptools/_distutils/version.py:346: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
    other = LooseVersion(other)

qutip-qip-0.2.3/tests/test_circuit.py:572
  /usr/ports/devel/py-qutip-qip/work-py39/qutip-qip-0.2.3/tests/test_circuit.py:572: PytestUnknownMarkWarning: Unknown pytest.mark.repeat - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.repeat(10)

qutip-qip-0.2.3/tests/test_measurement.py:14
  /usr/ports/devel/py-qutip-qip/work-py39/qutip-qip-0.2.3/tests/test_measurement.py:14: PytestUnknownMarkWarning: Unknown pytest.mark.repeat - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.repeat(10)

../../../../local/lib/python3.9/site-packages/pytest_freezegun.py:17: 958 warnings
  /usr/local/lib/python3.9/site-packages/pytest_freezegun.py:17: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
    if LooseVersion(pytest.__version__) < LooseVersion('3.6.0'):

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
======================================================================================== short test summary info ========================================================================================
FAILED qutip-qip-0.2.3/tests/test_processor.py::TestCircuitProcessor::test_plot - AttributeError: module 'IPython' has no attribute 'get_ipython'
FAILED qutip-qip-0.2.3/tests/test_device.py::test_pulse_plotting[DispersiveCavityQED] - AttributeError: module 'IPython' has no attribute 'get_ipython'
FAILED qutip-qip-0.2.3/tests/test_device.py::test_pulse_plotting[CircularSpinChain] - AttributeError: module 'IPython' has no attribute 'get_ipython'
FAILED qutip-qip-0.2.3/tests/test_device.py::test_pulse_plotting[LinearSpinChain] - AttributeError: module 'IPython' has no attribute 'get_ipython'
FAILED qutip-qip-0.2.3/tests/test_device.py::test_pulse_plotting[SCQubits] - AttributeError: module 'IPython' has no attribute 'get_ipython'
================================================================== 5 failed, 463 passed, 11 skipped, 970 warnings in 67.24s (0:01:07) ===================================================================

Version: 0.2.3
Python-3.9
FreeBSD 13.1

Chain transpiler calls QubitCircuit with incorrect arguments

The method to_chain_structure calls:

qc_t = QubitCircuit(qc.N, qc.reverse_states)

but the second argument to QubitCircuit is input_states. It should call

qc_t = QubitCircuit(qc.N, reverse_states=qc.reverse_states)

It should probably copy more arguments from the circuit being modified too (e.g. input states, output states, num_cbits).

Adding a link in RTD to fork the repo

Which of the following styles are preferred to link to the main repo : style 1, style 2 ?

I know that in the past @nathanshammah has preferred to have style 2 because style 1 looks a bit old. Personally, I like both.

Right now each page in the documentation has a link to the source file to be able to make changes directly. It would be good to also have a link to the main repo accessible either to the top right or left or some other position.

Measurements with time evolution

When using the pulse models to simulate the time evolution a state, if there are measurements being done in a circuit, the execution fails.

Could time evolution account for measurements?

A possible approach could be to collapse a state, on measurement, into a mixture (with appropraite probabilities) of all the measurement outcomes (to get a density matrix). Then continue evolution from that point on the resulting density matrix.

Understand issue with global phase factor function

The output of Pauli Y is off by a global phase of -1 when decomposed with either of the single qubit decomposition functions. Understand what needs to be fixed for this - missing minus sign ?, not use determinant of input to find the global phase ?

If there's a common multiplication factor for each element of the matrix, the function fails to remove it.

Tutorial notebooks

Hi @BoxiLi @nathanshammah, I'm interested in contributing to this package as I applied for a project for GSoC '22.
I think it could be good to implement some tutorial notebooks to showcase some functionalities of qutip-qip in a user-friendly way. Do you think it could be a good idea?

A clear directory structure to make it simpler to add new processors

Hi @BoxiLi I am starting to build new devices and noticed that we have a slightly split file/directory structure for the pulse-level simulation code that could be more modular/compact. In any new device, I use the following code structure:

  • custom_processor/
    • model.py
    • processor.py
    • compiler.py
    • scheduler.py
    • noise.py
    • transpiler.py

In many cases, these files will just use the qutip-qip code, e.g., the qutip-qip scheduler or the ZZCrossTalk noise. But nevertheless, having this clear folder structure makes things very easy to develop when adding new simulators. Different people working on different aspects of the processor can work independently.

In the code right now, we have files that are spread over different folders, e.g., device/modelprocessor.py that could be processor/modelprocessor.py. Similarly noise.py is just a file and perhaps coule be noise/coherent.py, noise/crosstalk.py. Similarly we have a scheduler in the compiler/scheduler.py as well as a scheduler folder.

I propose to slowly restructure the folder in the main qutip-qip under model, processor, compiler, scheduler, noise and a transpiler folder structure. E.g.:

What do you think?

- src/qutip_qip/
    - model/
        - model.py 
        - spinchainmodel.py
        - cqedmodel.py
    - processor/
        - modelprocessor.py
        - spinchainprocessor.py
        - cqedprocessor.py
    - compiler/
        - simplecompiler.py
        - spinchaincompiler.py
        - cqedcompiler.py
    - scheduler/
        - alapscheduler.py
    - noise/
        - decoherence.py
        - crosstalk.py
    - transpiler/
        - chain.py 

A circuit plotting routine using SymPy

The current circuit plotting in qutip-qip uses LaTeX, which is powerful but requires dependencies that are hard to control as a Python package. The installation for users who do not already have a working tex environment is very clumsy.

As shown in this notebook. Sympy might have a way to do this already (see here). It is much easier to make SymPy an optional package than asking the user to install a full Tex distribution, which could be several Gigabytes large.
As Sympy uses a class for each gate, we could leave a method in our gate class that initialize this SymPy class, e.g:

class XGate(SingleQubitGate):
    ...
    def get_sympy_object(self):
        return sympy.physics.quantum.gate.X(self.targets[0])

And even better, SymPy seems to support QASM 2.0 gates! If that is true, we can first export our circuit to QASM then use SymPy to plot them. I haven't explored this yet.

Circuit.run ignores supplied measure_results.

Constructing a simple circuit like so:

qc = qip_circuit.QubitCircuit(N=1, num_cbits=1)
qc.add_gate("SNOT", targets=0)
qc.add_measurement("M0", targets=0, classical_store=0)

and then performing

classical_store = [None]
final_state = qc.run(q0, cbits=classical_store, measure_results=[0])

Does not always return the 0 state.

The issue is that .run() passes measure_results to the CircuitSimulator constructor, but not to CircuitSimulator.run which then overwrites measure_results with None.

Also, the docstring for .run_statistics documents measure_results but doesn't take it as a parameter. This should just be removed from the docstring.

circuit plot error for gates with classical controls when reverse_states=False

For the default of reverse_states=True, the following circuit with a gate that has a classical control generates the correct LaTeX and plots correctly:

from qutip_qip.circuit import QubitCircuit
qc = QubitCircuit(1, num_cbits=1, reverse_states=True)
qc.add_gate("X", targets=0, classical_controls=[0])
print(qc.latex_code())
qc.png
\documentclass[border=3pt]{standalone}
\usepackage[braket]{qcircuit}
\begin{document}
\Qcircuit @C=1cm @R=1cm {
 &  &  \ctrl{1}  & \qw \\ 
 &  &  \gate{X}  & \qw \\ 
}
\end{document}

image

When reverse_states=False as in the code below:

from qutip_qip.circuit import QubitCircuit
qc = QubitCircuit(1, num_cbits=1, reverse_states=False)
qc.add_gate("X", targets=0, classical_controls=[0])
print(qc.latex_code())
qc.png

I get the following error due to incorrectly generated LaTeX:

\documentclass[border=3pt]{standalone}
\usepackage[braket]{qcircuit}
\begin{document}
\Qcircuit @C=1cm @R=1cm {
 &  &  \gate{X}  & \qw \\ 
 &  &  \ctrl{1}  & \qw \\ 
}
\end{document}

---------------------------------------------------------------------------

RuntimeError                              Traceback (most recent call last)

[/usr/local/lib/python3.10/dist-packages/qutip_qip/circuit/circuit_latex.py](https://localhost:8080/#) in image_from_latex(code, file_type, dpi)
    215                 try:
--> 216                     _run_command(
    217                         (_pdflatex, "-interaction", "batchmode", filename)

4 frames

RuntimeError: 


The above exception was the direct cause of the following exception:

RuntimeError                              Traceback (most recent call last)

[/usr/local/lib/python3.10/dist-packages/qutip_qip/circuit/circuit_latex.py](https://localhost:8080/#) in image_from_latex(code, file_type, dpi)
    228                         + code
    229                     )
--> 230                     raise RuntimeError(message) from e
    231                 _crop_pdf(filename + ".pdf")
    232                 if file_type in _MISSING_CONVERTERS:

RuntimeError: pdflatex failed. Perhaps you do not have it installed, or you are missing the LaTeX package 'qcircuit'.The latex code is printed below. Please try to compile locally using pdflatex:

\documentclass[border=3pt]{standalone}
\usepackage[braket]{qcircuit}
\begin{document}
\Qcircuit @C=1cm @R=1cm {
 &  &  \gate{X}  & \qw \\ 
 &  &  \ctrl{1}  & \qw \\ 
}
\end{document}

I believe there should be a sign change for the argument of \ctrl when reverse_states=False. I'll submit a pull request with this change.

How can i generate the quantum circuit image with qutip?

Dear Respected Members of the Group,
I hope you are doing well.
I am trying to reproduce the results of the following post:

https://nbviewer.ipython.org/github/qutip/qutip-notebooks/blob/master/examples/quantum-gates.ipynb#Gates-in-QuTiP-and-their-representation

in windows 10 environment and with Imagemagick, perl, Ghostscript and Latex are all installed and verified.
However, it seems like it cant generate the qcirc.pdf file. I receive the following error,

Warning (from warnings module):
File "C:\Program Files (x86)\Python36-32\lib\site-packages\qutip_qip\circuit_latex.py", line 128
warnings.warn("Could not locate system 'pdfcrop':"
UserWarning: Could not locate system 'pdfcrop': image output may have additional margins.
Traceback (most recent call last):
File "C:\Users\msi-pc\Desktop\farrrrr.py", line 11, in
q.png
File "C:\Program Files (x86)\Python36-32\lib\site-packages\qutip_qip\circuit.py", line 1821, in png
return DisplayImage(self._raw_png(), embed=True)
File "C:\Program Files (x86)\Python36-32\lib\site-packages\qutip_qip\circuit.py", line 1811, in _raw_png
return _latex.image_from_latex(self.latex_code(), "png")
File "C:\Program Files (x86)\Python36-32\lib\site-packages\qutip_qip\circuit_latex.py", line 245, in image_from_latex
out = CONVERTERSfile_type
File "C:\Program Files (x86)\Python36-32\lib\site-packages\qutip_qip\circuit_latex.py", line 180, in converter
_run_command((which, *configuration.arguments, in_file, out_file))
File "C:\Program Files (x86)\Python36-32\lib\site-packages\qutip_qip\circuit_latex.py", line 65, in _run_command
raise RuntimeError(e.stderr.decode(sys.stderr.encoding)) from None
RuntimeError: magick: unable to open image 'qcirc.pdf': No such file or directory @ error/blob.c/OpenBlob/3537.

I kindly ask for your help with many thanks in advance.
Thank you very much
Yours sincerely,

read_qasm() not recognising some gates

Steps to reproduce
read_qasm('OPENQASM 2.0;\ninclude "qelib1.inc";\n\ncreg c[2];\nqreg q[2];swap q[0],q[1];\n', strmode = True)
Error

Traceback (most recent call last):

  File ~/anaconda3/envs/qsea/lib/python3.10/site-packages/IPython/core/interactiveshell.py:3508 in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)

  Cell In[13], line 1
    read_qasm('OPENQASM 2.0;\ninclude "qelib1.inc";\n\ncreg c[2];\nqreg q[2];swap q[0],q[1];\n', strmode = True)

  File ~/anaconda3/envs/qsea/lib/python3.10/site-packages/qutip_qip/qasm.py:902 in read_qasm
    qasm_obj._final_pass(qc)

  File ~/anaconda3/envs/qsea/lib/python3.10/site-packages/qutip_qip/qasm.py:842 in _final_pass
    raise SyntaxError(err)

  File <string>
SyntaxError: QASM: swap is not a valid QASM command.

Comment

It seems like the qutip parser has predefined qiskit gates in the python code and ignores some gates like swap defined in qelib1.inc. If the mode is “qiskit” only the predefined gates get recognised and many defined ones like swap in qelib1 gets ignored. Hence swap is not recognised

Workaround I'm using: Instead of using the mode qiskit, use a custom mode. Provide the qelib1.inc in the directory. Then the qutip parser will include all the gates in the qelib1.inc. And swap is also parsed.

Question
Am I missing something or is this the intended behaviour? If there is a better way than including the qelib1.inc in the directory, please do let me know. Also,

cannot import name "Gate"

Running the first example from the documentation (https://qutip-qip.readthedocs.io/en/latest/qip-basics.html)

from qutip_qip.circuit import QubitCircuit
from qutip_qip.operations import Gate
from qutip import tensor, basis

qc = QubitCircuit(N=2, num_cbits=1)
swap_gate = Gate(name="SWAP", targets=[0, 1])

qc.add_gate(swap_gate)
qc.add_measurement("M0", targets=[1], classical_store=0) # measurement gate
qc.add_gate("CNOT", controls=0, targets=1)
qc.add_gate("X", targets=0, classical_controls=[0]) # classically controlled gate
qc.add_gate(swap_gate)

print(qc.gates)

gives ImportError: cannot import name 'Gate' from 'qutip_qip.operations'

Generation QIR from QuTiP not working

          To provide an example for generating qir from qutip, I have been referring to the [documentations](https://qutip-qip.readthedocs.io/en/latest/qip-simulator.html?highlight=qir#import-and-export-quantum-circuits); However, I encountered some issues. Specifically, it appears that the current version of qutip-qip (0.3.0) does not provide access to the `.qir` attribute nor the `circuit_to_qir` function. When attempting to import the [qir.py](https://github.com/qutip/qutip-qip/blob/master/src/qutip_qip/qir.py) locally, it is missing dependency on `pyqir.parser` which is gone in the current version of pyqir (0.7.0). This may be due to version incompatibility between the two libraries with the recent updates. 

Next, I adjusted the code to use the pyqir-generator instead of the parser, but I reached the following error about SNOT gate “not implemented in the QIR base profile and may require a custom declaration".

To replicate: Please see here
Current outcome: An error due to missing .qir attribute and circuit_to_qir function in qutip-qip version and the missing dependency pyqir.parser
Expected outcome: To generate qir, as shown in docs
Notes: I would appreciate any suggestions or insights that might help in addressing these issues. Please let me know if there is any additional information or assistance that I can provide to fix this issue.
Environment:
QuTiP-QIP version: 0.0.3
pyqir version: 0.7.0
Python version: 3.9

Originally posted by @PariaNaghavi in #125 (comment)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.