Repository: qutip/qutip

Issue:
More detailed information for GSoC 2020 project
I have some detail information for the GSoC 2020  project "Error mitigation in QuTiP". Unfortunately, I don't have writing access to the QuTiP GitHub Wiki page. I post it here so if anyone finding it nice could copy it to the Wiki page.

I add some details based on the original description:

-----------------------------------
## 1. Error mitigation in QuTiP
From the QuTiP 4.5 release, the qutip.qip module now contains the noisy quantum circuit simulator (which was a GSoC project) providing enhanced features for a pulse-level description of quantum circuits and noise models. A new class `Processor` and several subclasses are added to represent different platforms for quantum computing. They can transfer a quantum circuit into the corresponding control sequence and simulate the dynamics with QuTiP solvers. Different noise models can be added to `qutip.qip.noise` to simulate noise in a quantum device. 

This module is still young and many features can be improved, including new device models, new noise models and integration with the existing general framework for quantum circuits (`qutip.qip.circuit`). There are also possible applications such as error mitigation techniques [1-3].

The tutorial notebooks can be found at http://qutip.org/tutorials.html#nisq. A recent presentation on the FOSDEM conference may help you get an overview (https://fosdem.org/2020/schedule/event/quantum_qutip/). See also the Github Project page for a collection of related issues and ongoing Pull Requests.

### Expected outcomes
 - More devices defined in the `qutip.qip.device` module. At the moment, we have two models: spin chain and cavity QED. We would like to include some other commonly used planform such as Superconducting system, Ion trap system or silicon system. Each model will need a new set of control hamiltonian and a compiler that find the control pulse of a quantum gate. This part is gonna involve some physics and study of commonly used hardware platforms. The related code can be found in `qutip.qip.device` and `qutip.qip.compiler`
- Refactoring the circuit compiler to allow simple pulse scheduling, e.g. Restriction one pulses that can be turned on at the same time.
- Features to perform error mitigation techniques in QuTiP, such as zero-error extrapolation.
- APIs to allow import quantum circuits from other software packages or commonly used language e.g. through quantum assembly language (qasm)
----------------------------------------------------------------

Also, the last outcomes have some overlap with the last project. Although I also find this circuit importation very important, I guess two projects should not have overlap if they are both selected.


Discussion:
Hi Boxi,

Can you please modify the projects of the Wiki page as you deem best?
All: we can also think of changing the order of them. It may be that
project #3 is deemed the most important.

Best wishes,

Nathan

Dr. Nathan Shammah
Postdoctoral Research Scientist
Theoretical Quantum Physics Laboratory
RIKEN, Wako, Saitama, Japan
www.nathanshammah.com



On Sun, Feb 23, 2020 at 5:42 AM Boxi Li <notifications@github.com> wrote:

> I have some detail information for the GSoC 2020 project "Error mitigation
> in QuTiP". Unfortunately, I don't have writing access to the QuTiP GitHub
> Wiki page. I post it here so if anyone finds it nice could copy it to the
> Wiki page.
>
> I add some details based on the original description:
> ------------------------------
> 1. Error mitigation in QuTiP
>
> From the QuTiP 4.5 release, the qutip.qip module now contains the noisy
> quantum circuit simulator (which was a GSoC project) providing enhanced
> features for a pulse-level description of quantum circuits and noise
> models. A new class Processor and several subclasses are added to
> represent different platforms for quantum computing. They can transfer a
> quantum circuit into the corresponding control sequence and simulate the
> dynamics with QuTiP solvers. Different noise models can be added to
> qutip.qip.noise to simulate noise in a quantum device.
>
> This module is still young and many features can be improved, including
> new device models, new noise models and integration with the existing
> general framework for quantum circuits (qutip.qip.circuit). There are
> also possible applications such as error mitigation techniques [1-3].
>
> The tutorial notebooks can be found at
> http://qutip.org/tutorials.html#nisq. A recent presentation on the FOSDEM
> conference may help you get an overview (
> https://fosdem.org/2020/schedule/event/quantum_qutip/). See also the
> Github Project page for a collection of related issues and ongoing Pull
> Requests.
> Expected outcomes
>
>    - More devices defined in the qutip.qip.device module. At the moment,
>    we have two models: spin chain and cavity QED. We would like to include
>    some other commonly used planform such as Superconducting system, Ion trap
>    system or silicon system. Each model will need a new set of control
>    hamiltonian and a compiler that find the control pulse of a quantum gate.
>    This part is gonna involve some physics and study of commonly used hardware
>    platforms. The related code can be found in qutip.qip.device and
>    qutip.qip.compiler
>    - Refactoring the circuit compiler to allow simple pulse scheduling,
>    e.g. Restriction one pulses that can be turned on at the same time.
>    - Features to perform error mitigation techniques in QuTiP, such as
>    zero-error extrapolation.
>    - APIs to allow import quantum circuits from other software packages
>    or commonly used language e.g. through quantum assembly language (qasm)
>
> ------------------------------
>
> Also, the last outcomes have some overlap with the last project. Although
> I also find this circuit importation very important, I guess two projects
> should not have overlap if they are both selected.
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/qutip/qutip/issues/1184?email_source=notifications&email_token=ADPF67A5MSYZVSDKCZD73LTREGE2LA5CNFSM4KZUPCQ2YY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4IPQGYLA>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/ADPF67EWECDUIOQMTKK2N2DREGE2LANCNFSM4KZUPCQQ>
> .
>

Hi Nathan,

I can't modify the Wiki page because I'm not a maintainer of QuTiP. And it seems that there is no way to make a PR to Wiki. I can also send you the markdown source code per email like this, but I’m not sure if it works as I expected.

The following is the markdown source code:

-----------------------------------
## 1. Error mitigation in QuTiP
From the QuTiP 4.5 release, the qutip.qip module now contains the noisy quantum circuit simulator (which was a GSoC project) providing enhanced features for a pulse-level description of quantum circuits and noise models. A new class `Processor` and several subclasses are added to represent different platforms for quantum computing. They can transfer a quantum circuit into the corresponding control sequence and simulate the dynamics with QuTiP solvers. Different noise models can be added to `qutip.qip.noise` to simulate noise in a quantum device.

This module is still young and many features can be improved, including new device models, new noise models and integration with the existing general framework for quantum circuits (`qutip.qip.circuit`). There are also possible applications such as error mitigation techniques [1-3].

The tutorial notebooks can be found at http://qutip.org/tutorials.html#nisq. A recent presentation on the FOSDEM conference may help you get an overview (https://fosdem.org/2020/schedule/event/quantum_qutip/). See also the Github Project page for a collection of related issues and ongoing Pull Requests.

### Expected outcomes
- More devices defined in the `qutip.qip.device` module. At the moment, we have two models: spin chain and cavity QED. We would like to include some other commonly used planform such as Superconducting system, Ion trap system or silicon system. Each model will need a new set of control hamiltonian and a compiler that find the control pulse of a quantum gate. This part is gonna involve some physics and study of commonly used hardware platforms. The related code can be found in `qutip.qip.device` and `qutip.qip.compiler`
- Refactoring the circuit compiler to allow simple pulse scheduling, e.g. Restriction one pulses that can be turned on at the same time.
- Features to perform error mitigation techniques in QuTiP, such as zero-error extrapolation.
- APIs to allow import quantum circuits from other software packages or commonly used language e.g. through quantum assembly language (qasm)
----------------------------------------------------------------

Best
Boxi
Hi,

Yes, I know, that's why I added the md file. I updated it now, thanks,
adding more info on general resources to learn about QuTiP and more
references to the #3 project, i.e. your blog and the talk.
https://github.com/qutip/qutip/wiki/Google-Summer-of-Code-2020

Neill, Alex, if you have a lead, please add a HEOM project.

Bests,

Nathan

Dr. Nathan Shammah
Postdoctoral Research Scientist
Theoretical Quantum Physics Laboratory
RIKEN, Wako, Saitama, Japan
www.nathanshammah.com


On Tue, Feb 25, 2020 at 5:10 PM Boxi Li <notifications@github.com> wrote:

> Hi Nathan,
>
> I can't modify the Wiki page because I'm not a maintainer of QuTiP. And it
> seems that there is no way to make a PR to Wiki. I can also send you the
> markdown source code per email like this, but I’m not sure if it works as I
> expected.
>
> The following is the markdown source code:
>
> -----------------------------------
> ## 1. Error mitigation in QuTiP
> From the QuTiP 4.5 release, the qutip.qip module now contains the noisy
> quantum circuit simulator (which was a GSoC project) providing enhanced
> features for a pulse-level description of quantum circuits and noise
> models. A new class `Processor` and several subclasses are added to
> represent different platforms for quantum computing. They can transfer a
> quantum circuit into the corresponding control sequence and simulate the
> dynamics with QuTiP solvers. Different noise models can be added to
> `qutip.qip.noise` to simulate noise in a quantum device.
>
> This module is still young and many features can be improved, including
> new device models, new noise models and integration with the existing
> general framework for quantum circuits (`qutip.qip.circuit`). There are
> also possible applications such as error mitigation techniques [1-3].
>
> The tutorial notebooks can be found at
> http://qutip.org/tutorials.html#nisq. A recent presentation on the FOSDEM
> conference may help you get an overview (
> https://fosdem.org/2020/schedule/event/quantum_qutip/). See also the
> Github Project page for a collection of related issues and ongoing Pull
> Requests.
>
> ### Expected outcomes
> - More devices defined in the `qutip.qip.device` module. At the moment, we
> have two models: spin chain and cavity QED. We would like to include some
> other commonly used planform such as Superconducting system, Ion trap
> system or silicon system. Each model will need a new set of control
> hamiltonian and a compiler that find the control pulse of a quantum gate.
> This part is gonna involve some physics and study of commonly used hardware
> platforms. The related code can be found in `qutip.qip.device` and
> `qutip.qip.compiler`
> - Refactoring the circuit compiler to allow simple pulse scheduling, e.g.
> Restriction one pulses that can be turned on at the same time.
> - Features to perform error mitigation techniques in QuTiP, such as
> zero-error extrapolation.
> - APIs to allow import quantum circuits from other software packages or
> commonly used language e.g. through quantum assembly language (qasm)
> ----------------------------------------------------------------
>
> Best
> Boxi
>
> From: Nathan Shammah<mailto:notifications@github.com>
> Sent: Tuesday, February 25, 2020 8:36 AM
> To: qutip/qutip<mailto:qutip@noreply.github.com>
> Cc: Boxi Li<mailto:etamin1201@gmail.com>; Author<mailto:
> author@noreply.github.com>
> Subject: Re: [qutip/qutip] More detailed information for GSoC 2020 project
> (#1184)
>
> Hi Boxi,
>
> Can you please modify the projects of the Wiki page as you deem best?
> All: we can also think of changing the order of them. It may be that
> project #3 is deemed the most important.
>
> Best wishes,
>
> Nathan
>
> Dr. Nathan Shammah
> Postdoctoral Research Scientist
> Theoretical Quantum Physics Laboratory
> RIKEN, Wako, Saitama, Japan
> www.nathanshammah.com
>
>
>
> On Sun, Feb 23, 2020 at 5:42 AM Boxi Li <notifications@github.com> wrote:
>
> > I have some detail information for the GSoC 2020 project "Error
> mitigation
> > in QuTiP". Unfortunately, I don't have writing access to the QuTiP GitHub
> > Wiki page. I post it here so if anyone finds it nice could copy it to the
> > Wiki page.
> >
> > I add some details based on the original description:
> > ------------------------------
> > 1. Error mitigation in QuTiP
> >
> > From the QuTiP 4.5 release, the qutip.qip module now contains the noisy
> > quantum circuit simulator (which was a GSoC project) providing enhanced
> > features for a pulse-level description of quantum circuits and noise
> > models. A new class Processor and several subclasses are added to
> > represent different platforms for quantum computing. They can transfer a
> > quantum circuit into the corresponding control sequence and simulate the
> > dynamics with QuTiP solvers. Different noise models can be added to
> > qutip.qip.noise to simulate noise in a quantum device.
> >
> > This module is still young and many features can be improved, including
> > new device models, new noise models and integration with the existing
> > general framework for quantum circuits (qutip.qip.circuit). There are
> > also possible applications such as error mitigation techniques [1-3].
> >
> > The tutorial notebooks can be found at
> > http://qutip.org/tutorials.html#nisq. A recent presentation on the
> FOSDEM
> > conference may help you get an overview (
> > https://fosdem.org/2020/schedule/event/quantum_qutip/). See also the
> > Github Project page for a collection of related issues and ongoing Pull
> > Requests.
> > Expected outcomes
> >
> > - More devices defined in the qutip.qip.device module. At the moment,
> > we have two models: spin chain and cavity QED. We would like to include
> > some other commonly used planform such as Superconducting system, Ion
> trap
> > system or silicon system. Each model will need a new set of control
> > hamiltonian and a compiler that find the control pulse of a quantum gate.
> > This part is gonna involve some physics and study of commonly used
> hardware
> > platforms. The related code can be found in qutip.qip.device and
> > qutip.qip.compiler
> > - Refactoring the circuit compiler to allow simple pulse scheduling,
> > e.g. Restriction one pulses that can be turned on at the same time.
> > - Features to perform error mitigation techniques in QuTiP, such as
> > zero-error extrapolation.
> > - APIs to allow import quantum circuits from other software packages
> > or commonly used language e.g. through quantum assembly language (qasm)
> >
> > ------------------------------
> >
> > Also, the last outcomes have some overlap with the last project. Although
> > I also find this circuit importation very important, I guess two projects
> > should not have overlap if they are both selected.
> >
> > —
> > You are receiving this because you are subscribed to this thread.
> > Reply to this email directly, view it on GitHub
> > <
> https://github.com/qutip/qutip/issues/1184?email_source=notifications&email_token=ADPF67A5MSYZVSDKCZD73LTREGE2LA5CNFSM4KZUPCQ2YY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4IPQGYLA
> >,
> > or unsubscribe
> > <
> https://github.com/notifications/unsubscribe-auth/ADPF67EWECDUIOQMTKK2N2DREGE2LANCNFSM4KZUPCQQ
> >
> > .
> >
>
>
> —
> You are receiving this because you authored the thread.
> Reply to this email directly, view it on GitHub<
> https://github.com/qutip/qutip/issues/1184?email_source=notifications&email_token=AC4QMV2SERZDZGO2EGF677DRETDBTA5CNFSM4KZUPCQ2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEM24JKI#issuecomment-590726313>,
> or unsubscribe<
> https://github.com/notifications/unsubscribe-auth/AC4QMV6SNJVGD5YZKSBN7WDRETDBTANCNFSM4KZUPCQQ
> >.
>
> —
> You are receiving this because you commented.
> Reply to this email directly, view it on GitHub
> <https://github.com/qutip/qutip/issues/1184?email_source=notifications&email_token=ADPF67HHX4IG7LOZAF3XLK3RETG6BA5CNFSM4KZUPCQ2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEM27EFA#issuecomment-590737940>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/ADPF67DIYOYUO35BGQNKCEDRETG6BANCNFSM4KZUPCQQ>
> .
>

updated on the Wiki GSoC 2020 page. https://github.com/qutip/qutip/wiki/Google-Summer-of-Code-2020

Resolving patch:
diff --git a/qutip/core/dimensions.py b/qutip/core/dimensions.py
--- a/qutip/core/dimensions.py
+++ b/qutip/core/dimensions.py
@@ -5,6 +5,7 @@
 # Everything should be explicitly imported, not made available by default.
 
 import numpy as np
+import numbers
 from operator import getitem
 from functools import partial
 from qutip.settings import settings
@@ -16,9 +17,13 @@
 def flatten(l):
     """Flattens a list of lists to the first level.
 
-    Given a list containing a mix of scalars and lists,
-    flattens down to a list of the scalars within the original
-    list.
+    Given a list containing a mix of scalars and lists or a dimension object,
+    flattens it down to a list of the scalars within the original list.
+
+    Parameters
+    ----------
+    l : scalar, list, Space, Dimension
+        Object to flatten.
 
     Examples
     --------
@@ -26,7 +31,14 @@ def flatten(l):
     >>> flatten([[[0], 1], 2]) # doctest: +SKIP
     [0, 1, 2]
 
+    Notes
+    -----
+    Any scalar will be returned wrapped in a list: ``flaten(1) == [1]``.
+    A non-list iterable will not be treated as a list by flatten. For example, flatten would treat a tuple
+    as a scalar.
     """
+    if isinstance(l, (Space, Dimensions)):
+        l = l.as_list()
     if not isinstance(l, list):
         return [l]
     else:
@@ -459,12 +471,20 @@ def dims2idx(self, dims):
         """
         Transform dimensions indices to full array indices.
         """
-        return dims
+        if not isinstance(dims, list) or len(dims) != 1:
+            raise ValueError("Dimensions must be a list of one element")
+        if not (0 <= dims[0] < self.size):
+            raise IndexError("Dimensions out of range")
+        if not isinstance(dims[0], numbers.Integral):
+            raise TypeError("Dimensions must be integers")
+        return dims[0]
 
     def idx2dims(self, idx):
         """
         Transform full array indices to dimensions indices.
         """
+        if not (0 <= idx < self.size):
+            raise IndexError("Index out of range")
         return [idx]
 
     def step(self):
@@ -503,6 +523,9 @@ def replace(self, idx, new):
     def replace_superrep(self, super_rep):
         return self
 
+    def scalar_like(self):
+        return Field()
+
 
 class Field(Space):
     field_instance = None
@@ -595,7 +618,7 @@ def dims2idx(self, dims):
         pos = 0
         step = 1
         for space, dim in zip(self.spaces[::-1], dims[::-1]):
-            pos += space.dims2idx(dim) * step
+            pos += space.dims2idx([dim]) * step
             step *= space.size
         return pos
 
@@ -644,6 +667,9 @@ def replace_superrep(self, super_rep):
             *[space.replace_superrep(super_rep) for space in self.spaces]
         )
 
+    def scalar_like(self):
+        return [space.scalar_like() for space in self.spaces]
+
 
 class SuperSpace(Space):
     _stored_dims = {}
@@ -705,6 +731,9 @@ def replace(self, idx, new):
     def replace_superrep(self, super_rep):
         return SuperSpace(self.oper, rep=super_rep)
 
+    def scalar_like(self):
+        return self.oper.scalar_like()
+
 
 class MetaDims(type):
     def __call__(cls, *args, rep=None):
@@ -920,3 +949,6 @@ def replace_superrep(self, super_rep):
             self.from_.replace_superrep(super_rep),
             self.to_.replace_superrep(super_rep)
         )
+
+    def scalar_like(self):
+        return [self.to_.scalar_like(), self.from_.scalar_like()]
diff --git a/qutip/core/operators.py b/qutip/core/operators.py
--- a/qutip/core/operators.py
+++ b/qutip/core/operators.py
@@ -19,7 +19,7 @@
 
 from . import data as _data
 from .qobj import Qobj
-from .dimensions import flatten
+from .dimensions import flatten, Space
 from .. import settings
 
 
@@ -392,7 +392,7 @@ def destroy(N, offset=0, *, dtype=None):
     Parameters
     ----------
     N : int
-        Dimension of Hilbert space.
+        Number of basis states in the Hilbert space.
 
     offset : int, default: 0
         The lowest number state that is included in the finite number state
@@ -431,7 +431,7 @@ def create(N, offset=0, *, dtype=None):
     Parameters
     ----------
     N : int
-        Dimension of Hilbert space.
+        Number of basis states in the Hilbert space.
 
     offset : int, default: 0
         The lowest number state that is included in the finite number state
@@ -618,46 +618,21 @@ def _f_op(n_sites, site, action, dtype=None):
     return tensor(opers).to(dtype)
 
 
-def _implicit_tensor_dimensions(dimensions):
-    """
-    Total flattened size and operator dimensions for operator creation routines
-    that automatically perform tensor products.
-
-    Parameters
-    ----------
-    dimensions : (int) or (list of int) or (list of list of int)
-        First dimension of an operator which can create an implicit tensor
-        product.  If the type is `int`, it is promoted first to `[dimensions]`.
-        From there, it should be one of the two-elements `dims` parameter of a
-        `qutip.Qobj` representing an `oper` or `super`, with possible tensor
-        products.
-
-    Returns
-    -------
-    size : int
-        Dimension of backing matrix required to represent operator.
-    dimensions : list
-        Dimension list in the form required by ``Qobj`` creation.
-    """
-    if not isinstance(dimensions, list):
-        dimensions = [dimensions]
-    flat = flatten(dimensions)
-    if not all(isinstance(x, numbers.Integral) and x >= 0 for x in flat):
-        raise ValueError("All dimensions must be integers >= 0")
-    return np.prod(flat), [dimensions, dimensions]
-
-
-def qzero(dimensions, *, dtype=None):
+def qzero(dimensions, dims_right=None, *, dtype=None):
     """
     Zero operator.
 
     Parameters
     ----------
-    dimensions : (int) or (list of int) or (list of list of int)
-        Dimension of Hilbert space. If provided as a list of ints, then the
-        dimension is the product over this list, but the ``dims`` property of
-        the new Qobj are set to this list.  This can produce either `oper` or
-        `super` depending on the passed `dimensions`.
+    dimensions : int, list of int, list of list of int, Space
+        Number of basis states in the Hilbert space. If provided as a list of
+        ints, then the dimension is the product over this list, but the
+        ``dims`` property of the new Qobj are set to this list.  This can
+        produce either `oper` or `super` depending on the passed `dimensions`.
+
+    dims_right : int, list of int, list of list of int, Space, optional
+        Number of basis states in the right Hilbert space when the operator is
+        rectangular.
 
     dtype : type or str, optional
         Storage representation. Any data-layer known to ``qutip.data.to`` is
@@ -670,10 +645,17 @@ def qzero(dimensions, *, dtype=None):
 
     """
     dtype = dtype or settings.core["default_dtype"] or _data.CSR
-    size, dimensions = _implicit_tensor_dimensions(dimensions)
+    dims_left = Space(dimensions)
+    size_left = dims_left.size
+    if dims_right is None:
+        dims_right = dims_left
+        size_right = size_left
+    else:
+        dims_right = Space(dims_right)
+        size_right = dims_right.size
+    dims = [dims_left, dims_right]
     # A sparse matrix with no data is equal to a zero matrix.
-    type_ = 'super' if isinstance(dimensions[0][0], list) else 'oper'
-    return Qobj(_data.zeros[dtype](size, size), dims=dimensions,
+    return Qobj(_data.zeros[dtype](size_left, size_right), dims=dims,
                 isherm=True, isunitary=False, copy=False)
 
 
@@ -707,11 +689,11 @@ def qeye(dimensions, *, dtype=None):
 
     Parameters
     ----------
-    dimensions : (int) or (list of int) or (list of list of int)
-        Dimension of Hilbert space. If provided as a list of ints, then the
-        dimension is the product over this list, but the ``dims`` property of
-        the new Qobj are set to this list.  This can produce either `oper` or
-        `super` depending on the passed `dimensions`.
+    dimensions : (int) or (list of int) or (list of list of int), Space
+        Number of basis states in the Hilbert space. If provided as a list of
+        ints, then the dimension is the product over this list, but the
+        ``dims`` property of the new Qobj are set to this list.  This can
+        produce either `oper` or `super` depending on the passed `dimensions`.
 
     dtype : type or str, optional
         Storage representation. Any data-layer known to ``qutip.data.to`` is
@@ -742,9 +724,8 @@ def qeye(dimensions, *, dtype=None):
 
     """
     dtype = dtype or settings.core["default_dtype"] or _data.Dia
-    size, dimensions = _implicit_tensor_dimensions(dimensions)
-    type_ = 'super' if isinstance(dimensions[0][0], list) else 'oper'
-    return Qobj(_data.identity[dtype](size), dims=dimensions,
+    dimensions = Space(dimensions)
+    return Qobj(_data.identity[dtype](dimensions.size), dims=[dimensions]*2,
                 isherm=True, isunitary=True, copy=False)
 
 
@@ -784,7 +765,7 @@ def position(N, offset=0, *, dtype=None):
     Parameters
     ----------
     N : int
-        Number of Fock states in Hilbert space.
+        Number of basis states in the Hilbert space.
 
     offset : int, default: 0
         The lowest number state that is included in the finite number state
@@ -813,7 +794,7 @@ def momentum(N, offset=0, *, dtype=None):
     Parameters
     ----------
     N : int
-        Number of Fock states in Hilbert space.
+        Number of basis states in the Hilbert space.
 
     offset : int, default: 0
         The lowest number state that is included in the finite number state
@@ -842,7 +823,7 @@ def num(N, offset=0, *, dtype=None):
     Parameters
     ----------
     N : int
-        The dimension of the Hilbert space.
+        Number of basis states in the Hilbert space.
 
     offset : int, default: 0
         The lowest number state that is included in the finite number state
@@ -949,7 +930,7 @@ def displace(N, alpha, offset=0, *, dtype=None):
     Parameters
     ----------
     N : int
-        Dimension of Hilbert space.
+        Number of basis states in the Hilbert space.
 
     alpha : float/complex
         Displacement amplitude.
@@ -1044,7 +1025,7 @@ def phase(N, phi0=0, *, dtype=None):
     Parameters
     ----------
     N : int
-        Number of basis states in Hilbert space.
+        Number of basis states in the Hilbert space.
 
     phi0 : float, default: 0
         Reference phase.
@@ -1119,7 +1100,7 @@ def tunneling(N, m=1, *, dtype=None):
     Parameters
     ----------
     N : int
-        Number of basis states in Hilbert space.
+        Number of basis states in the Hilbert space.
 
     m : int, default: 1
         Number of excitations in tunneling event.
@@ -1146,9 +1127,9 @@ def qft(dimensions, *, dtype="dense"):
     Parameters
     ----------
     dimensions : (int) or (list of int) or (list of list of int)
-        Dimension of Hilbert space. If provided as a list of ints, then the
-        dimension is the product over this list, but the ``dims`` property of
-        the new Qobj are set to this list.
+        Number of basis states in the Hilbert space. If provided as a list of
+        ints, then the dimension is the product over this list, but the
+        ``dims`` property of the new Qobj are set to this list.
 
     dtype : str or type, [keyword only] [optional]
         Storage representation. Any data-layer known to ``qutip.data.to`` is
@@ -1160,13 +1141,14 @@ def qft(dimensions, *, dtype="dense"):
         Quantum Fourier transform operator.
 
     """
-    N2, dimensions = _implicit_tensor_dimensions(dimensions)
+    dimensions = Space(dimensions)
+    N2 = dimensions.size
 
     phase = 2.0j * np.pi / N2
     arr = np.arange(N2)
     L, M = np.meshgrid(arr, arr)
     data = np.exp(phase * (L * M)) / np.sqrt(N2)
-    return Qobj(data, dims=dimensions).to(dtype)
+    return Qobj(data, dims=[dimensions]*2).to(dtype)
 
 
 def swap(N, M, *, dtype=None):
diff --git a/qutip/core/states.py b/qutip/core/states.py
--- a/qutip/core/states.py
+++ b/qutip/core/states.py
@@ -18,6 +18,7 @@
 from .qobj import Qobj
 from .operators import jmat, displace, qdiags
 from .tensor import tensor
+from .dimensions import Space
 from .. import settings
 
 
@@ -43,13 +44,29 @@ def _promote_to_zero_list(arg, length):
     raise TypeError("Dimensions must be an integer or list of integers.")
 
 
+def _to_space(dimensions):
+    """
+    Convert `dimensions` to a :class:`.Space`.
+
+    Returns
+    -------
+    space : :class:`.Space`
+    """
+    if isinstance(dimensions, Space):
+        return dimensions
+    elif isinstance(dimensions, list):
+        return Space(dimensions)
+    else:
+        return Space([dimensions])
+
+
 def basis(dimensions, n=None, offset=None, *, dtype=None):
     """Generates the vector representation of a Fock state.
 
     Parameters
     ----------
-    dimensions : int or list of ints
-        Number of Fock states in Hilbert space.  If a list, then the resultant
+    dimensions : int or list of ints, Space
+        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 : int or list of ints, optional (default 0 for all dimensions)
@@ -106,25 +123,40 @@ def basis(dimensions, n=None, offset=None, *, dtype=None):
 
     """
     dtype = dtype or settings.core["default_dtype"] or _data.Dense
-    # Promote all parameters to lists to simplify later logic.
-    if not isinstance(dimensions, list):
-        dimensions = [dimensions]
-    n_dimensions = len(dimensions)
-    ns = [m-off for m, off in zip(_promote_to_zero_list(n, n_dimensions),
-                                  _promote_to_zero_list(offset, n_dimensions))]
-    if any((not isinstance(x, numbers.Integral)) or x < 0 for x in dimensions):
-        raise ValueError("All dimensions must be >= 0.")
-    if not all(0 <= n < dimension for n, dimension in zip(ns, dimensions)):
-        raise ValueError("All basis indices must be "
-                         "`offset <= n < dimension+offset`.")
-    location, size = 0, 1
-    for m, dimension in zip(reversed(ns), reversed(dimensions)):
-        location += m*size
-        size *= dimension
+    # Promote all parameters to Space to simplify later logic.
+    dimensions = _to_space(dimensions)
+
+    size = dimensions.size
+    if n is None:
+        location = 0
+    elif offset:
+        if not isinstance(offset, list):
+            offset = [offset]
+        if not isinstance(n, list):
+            n = [n]
+        if len(n) != len(dimensions.as_list()) or len(offset) != len(n):
+            raise ValueError("All list inputs must be the same length.")
+
+        n_off = [m-off for m, off in zip(n, offset)]
+        try:
+            location = dimensions.dims2idx(n_off)
+        except IndexError:
+            raise ValueError("All basis indices must be integers in the range "
+                             "`offset <= n < dimension+offset`.")
+    else:
+        if not isinstance(n, list):
+            n = [n]
+        if len(n) != len(dimensions.as_list()):
+            raise ValueError("All list inputs must be the same length.")
+        try:
+            location = dimensions.dims2idx(n)
+        except IndexError:
+            raise ValueError("All basis indices must be integers in the range "
+                             "`0 <= n < dimension`.")
 
     data = _data.one_element[dtype]((size, 1), (location, 0), 1)
     return Qobj(data,
-                dims=[dimensions, [1]*n_dimensions],
+                dims=[dimensions, dimensions.scalar_like()],
                 isherm=False,
                 isunitary=False,
                 copy=False)
@@ -249,7 +281,7 @@ def coherent_dm(N, alpha, offset=0, method='operator', *, dtype=None):
     Parameters
     ----------
     N : int
-        Number of Fock states in Hilbert space.
+        Number of basis states in Hilbert space.
 
     alpha : float/complex
         Eigenvalue for coherent state.
@@ -307,8 +339,8 @@ def fock_dm(dimensions, n=None, offset=None, *, dtype=None):
 
     Parameters
     ----------
-    dimensions : int or list of ints
-        Number of Fock states in Hilbert space.  If a list, then the resultant
+    dimensions : int or list of ints, Space
+        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 : int or list of ints, default: 0 for all dimensions
@@ -352,8 +384,8 @@ def fock(dimensions, n=None, offset=None, *, dtype=None):
 
     Parameters
     ----------
-    dimensions : int or list of ints
-        Number of Fock states in Hilbert space.  If a list, then the resultant
+    dimensions : int or list of ints, Space
+        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 : int or list of ints, default: 0 for all dimensions
@@ -467,15 +499,16 @@ def thermal_dm(N, n, method='operator', *, dtype=None):
         return out
 
 
-def maximally_mixed_dm(N, *, dtype=None):
+def maximally_mixed_dm(dimensions, *, dtype=None):
     """
     Returns the maximally mixed density matrix for a Hilbert space of
     dimension N.
 
     Parameters
     ----------
-    N : int
-        Number of basis states in Hilbert space.
+    dimensions : int or list of ints, Space
+        Number of basis states in Hilbert space.  If a list, then the resultant
+        object will be a tensor product over spaces with those dimensions.
 
     dtype : type or str, optional
         Storage representation. Any data-layer known to ``qutip.data.to`` is
@@ -487,9 +520,11 @@ def maximally_mixed_dm(N, *, dtype=None):
         Thermal state density matrix.
     """
     dtype = dtype or settings.core["default_dtype"] or _data.Dia
-    if not isinstance(N, numbers.Integral) or N <= 0:
-        raise ValueError("N must be integer N > 0")
-    return Qobj(_data.identity[dtype](N, scale=1/N), dims=[[N], [N]],
+    dimensions = _to_space(dimensions)
+    N = dimensions.size
+
+    return Qobj(_data.identity[dtype](N, scale=1/N),
+                dims=[dimensions, dimensions],
                 isherm=True, isunitary=(N == 1), copy=False)
 
 
@@ -525,15 +560,16 @@ def ket2dm(Q):
     raise TypeError("Input is not a ket or bra vector.")
 
 
-def projection(N, n, m, offset=None, *, dtype=None):
+def projection(dimensions, n, m, offset=None, *, dtype=None):
     r"""
     The projection operator that projects state :math:`\lvert m\rangle` on
     state :math:`\lvert n\rangle`.
 
     Parameters
     ----------
-    N : int
-        Number of basis states in Hilbert space.
+    dimensions : int or list of ints, Space
+        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
         The number states in the projection.
@@ -553,8 +589,8 @@ def projection(N, n, m, offset=None, *, dtype=None):
     """
     dtype = dtype or settings.core["default_dtype"] or _data.CSR
     return (
-        basis(N, n, offset=offset, dtype=dtype) @ \
-        basis(N, m, offset=offset, dtype=dtype).dag()
+        basis(dimensions, n, offset=offset, dtype=dtype) @
+        basis(dimensions, m, offset=offset, dtype=dtype).dag()
     ).to(dtype)
 
 
@@ -932,7 +968,7 @@ def phase_basis(N, m, phi0=0, *, dtype=None):
     Parameters
     ----------
     N : int
-        Number of basis vectors in Hilbert space.
+        Number of basis states in Hilbert space.
 
     m : int
         Integer corresponding to the mth discrete phase
@@ -963,18 +999,15 @@ def phase_basis(N, m, phi0=0, *, dtype=None):
     return Qobj(data, dims=[[N], [1]], copy=False).to(dtype)
 
 
-def zero_ket(N, dims=None, *, dtype=None):
+def zero_ket(dimensions, *, dtype=None):
     """
     Creates the zero ket vector with shape Nx1 and dimensions `dims`.
 
     Parameters
     ----------
-    N : int
-        Hilbert space dimensionality
-
-    dims : list, optional
-        Optional dimensions if ket corresponds to
-        a composite Hilbert space.
+    dimensions : int or list of ints, Space
+        Number of basis states in Hilbert space.  If a list, then the resultant
+        object will be a tensor product over spaces with those dimensions.
 
     dtype : type or str, optional
         Storage representation. Any data-layer known to ``qutip.data.to`` is
@@ -987,7 +1020,10 @@ def zero_ket(N, dims=None, *, dtype=None):
 
     """
     dtype = dtype or settings.core["default_dtype"] or _data.Dense
-    return Qobj(_data.zeros[dtype](N, 1), dims=dims, copy=False)
+    dimensions = _to_space(dimensions)
+    N = dimensions.size
+    return Qobj(_data.zeros[dtype](N, 1),
+                dims=[dimensions, dimensions.scalar_like()], copy=False)
 
 
 def spin_state(j, m, type='ket', *, dtype=None):
@@ -1171,14 +1207,14 @@ def triplet_states(*, dtype=None):
     ]
 
 
-def w_state(N=3, *, dtype=None):
+def w_state(N_qubit, *, dtype=None):
     """
     Returns the N-qubit W-state:
         ``[ |100..0> + |010..0> + |001..0> + ... |000..1> ] / sqrt(n)``
 
     Parameters
     ----------
-    N : int, default: 3
+    N_qubit : int
         Number of qubits in state
 
     dtype : type or str, optional
@@ -1190,23 +1226,24 @@ def w_state(N=3, *, dtype=None):
     W : :obj:`.Qobj`
         N-qubit W-state
     """
+
     dtype = dtype or settings.core["default_dtype"] or _data.Dense
-    inds = np.zeros(N, dtype=int)
+    inds = np.zeros(N_qubit, dtype=int)
     inds[0] = 1
-    state = basis([2]*N, list(inds), dtype=dtype)
-    for kk in range(1, N):
-        state += basis([2]*N, list(np.roll(inds, kk)), dtype=dtype)
-    return (np.sqrt(1 / N) * state).to(dtype)
+    state = basis([2]*N_qubit, list(inds), dtype=dtype)
+    for kk in range(1, N_qubit):
+        state += basis([2] * N_qubit, list(np.roll(inds, kk)), dtype=dtype)
+    return (np.sqrt(1 / N_qubit) * state).to(dtype)
 
 
-def ghz_state(N=3, *, dtype=None):
+def ghz_state(N_qubit, *, dtype=None):
     """
     Returns the N-qubit GHZ-state:
         ``[ |00...00> + |11...11> ] / sqrt(2)``
 
     Parameters
     ----------
-    N : int, default: 3
+    N_qubit : int
         Number of qubits in state
 
     dtype : type or str, optional
@@ -1218,10 +1255,11 @@ def ghz_state(N=3, *, dtype=None):
     G : qobj
         N-qubit GHZ-state
     """
+
     dtype = dtype or settings.core["default_dtype"] or _data.Dense
     return (
         np.sqrt(0.5) * (
-            basis([2]*N, [0]*N, dtype=dtype) +
-            basis([2]*N, [1]*N, dtype=dtype)
+            basis([2] * N_qubit, [0] * N_qubit, dtype=dtype) +
+            basis([2] * N_qubit, [1] * N_qubit, dtype=dtype)
         )
     ).to(dtype)


Test patch:
diff --git a/qutip/tests/core/test_operators.py b/qutip/tests/core/test_operators.py
--- a/qutip/tests/core/test_operators.py
+++ b/qutip/tests/core/test_operators.py
@@ -107,7 +107,7 @@ def test_diagonal_operators(oper_func, diag, offset, args):
 
 
 @pytest.mark.parametrize(['function', 'message'], [
-    (qutip.qeye, "All dimensions must be integers >= 0"),
+    (qutip.qeye, "Dimensions must be integers > 0"),
     (qutip.destroy, "Hilbert space dimension must be integer value"),
     (qutip.create, "Hilbert space dimension must be integer value"),
 ], ids=["qeye", "destroy", "create"])
@@ -125,14 +125,24 @@ def test_diagonal_raise(function, message):
         1,
         [1],
         [1, 1],
+        qutip.dimensions.Space([2, 3, 4]),
     ])
 def test_implicit_tensor_creation(to_test, dimensions):
     implicit = to_test(dimensions)
     if isinstance(dimensions, numbers.Integral):
         dimensions = [dimensions]
+    if isinstance(dimensions, qutip.dimensions.Space):
+        dimensions = dimensions.as_list()
     assert implicit.dims == [dimensions, dimensions]
 
 
+def test_qzero_rectangular():
+    assert qutip.qzero([2, 3], [3, 4]).dims == [[2, 3], [3, 4]]
+    assert qutip.qzero([2], [3]).dims == [[2], [3]]
+    assert qutip.qzero([2, 3], [3]).dims == [[2, 3], [3]]
+    assert qutip.qzero(qutip.dimensions.Space([2, 3]), qutip.dimensions.Space([3])).dims == [[2, 3], [3]]
+
+
 @pytest.mark.parametrize("to_test", [qutip.qzero, qutip.qeye, qutip.identity])
 def test_super_operator_creation(to_test):
     size = 2
@@ -365,3 +375,15 @@ def test_fcreate_fdestroy(n_sites):
                 assert qutip.commutator(c_0, d_1, 'anti') == zero_tensor
                 assert qutip.commutator(c_1, d_0, 'anti') == zero_tensor
     assert qutip.commutator(identity, c_0) == zero_tensor
+
+@pytest.mark.parametrize(['func', 'args'], [
+    (qutip.qzero, (None,)),
+    (qutip.fock, (None,)),
+    (qutip.fock_dm, (None,)),
+    (qutip.maximally_mixed_dm, ()),
+    (qutip.projection, ([0, 1, 1], [1, 1, 0])),
+    (qutip.zero_ket, ()),
+], ids=_id_func)
+def test_state_space_input(func,  args):
+    dims = qutip.dimensions.Space([2, 2, 2])
+    assert func(dims, *args) == func([2, 2, 2], *args)
diff --git a/qutip/tests/core/test_states.py b/qutip/tests/core/test_states.py
--- a/qutip/tests/core/test_states.py
+++ b/qutip/tests/core/test_states.py
@@ -25,12 +25,12 @@ def test_implicit_tensor_basis_like(to_test, size, n):
 
 @pytest.mark.parametrize("size, n, offset, msg", [
     ([2, 2], [0, 1, 1], [0, 0], "All list inputs must be the same length."),
-    ([2, 2], [0, 1], 0, "All list inputs must be the same length."),
-    (-1, 0, 0, "All dimensions must be >= 0."),
-    (1.5, 0, 0, "All dimensions must be >= 0."),
-    (5, 5, 0, "All basis indices must be `offset <= n < dimension+offset`."),
-    (5, 0, 2, "All basis indices must be `offset <= n < dimension+offset`."),
-], ids=["n too long", "offset too long", "neg dims",
+    ([2, 2], [1, 1], 1, "All list inputs must be the same length."),
+    (-1, 0, 0, "Dimensions must be integers > 0"),
+    (1.5, 0, 0, "Dimensions must be integers > 0"),
+    (5, 5, 0, "All basis indices must be integers in the range `0 <= n < dimension`."),
+    (5, 0, 2, "All basis indices must be integers in the range `offset <= n < dimension+offset`."),
+], ids=["n too long", "offset too short", "neg dims",
         "fraction dims", "n too large", "n too small"]
 )
 def test_basis_error(size, n, offset, msg):
@@ -42,8 +42,7 @@ def test_basis_error(size, n, offset, msg):
 def test_basis_error_type():
     with pytest.raises(TypeError) as e:
         qutip.basis(5, 3.5)
-    assert str(e.value) == ("Dimensions must be an integer "
-                            "or list of integers.")
+    assert str(e.value) == ("Dimensions must be integers")
 
 
 @pytest.mark.parametrize("size, n, m", [
@@ -183,7 +182,7 @@ def test_spin_output(func):
 def test_maximally_mixed_dm_error(N):
     with pytest.raises(ValueError) as e:
         qutip.maximally_mixed_dm(N)
-    assert str(e.value) == "N must be integer N > 0"
+    assert str(e.value) == "Dimensions must be integers > 0"
 
 
 def test_TripletStateNorm():
@@ -316,3 +315,16 @@ def test_state_type(func, args, alias, dtype):
         else:
             for obj in object:
                 assert isinstance(obj.data, dtype)
+
+
+@pytest.mark.parametrize(['func', 'args'], [
+    (qutip.basis, (None,)),
+    (qutip.fock, (None,)),
+    (qutip.fock_dm, (None,)),
+    (qutip.maximally_mixed_dm, ()),
+    (qutip.projection, ([0, 1, 1], [1, 1, 0])),
+    (qutip.zero_ket, ()),
+], ids=_id_func)
+def test_state_space_input(func,  args):
+    dims = qutip.dimensions.Space([2, 2, 2])
+    assert func(dims, *args) == func([2, 2, 2], *args)
\ No newline at end of file