Example #1
0
def test_BaseDMRG_raises():
  numpy_backend = backend_factory.get_backend('numpy')
  pytorch_backend = backend_factory.get_backend('pytorch')
  dtype = np.float64
  N = 10
  D = 10
  Jz = np.ones(N - 1)
  Jxy = np.ones(N - 1)
  Bz = np.zeros(N)
  mpo = FiniteXXZ(Jz, Jxy, Bz, dtype=dtype, backend=numpy_backend)
  mps = FiniteMPS.random(
      [2] * (N - 1), [D] * (N - 2), dtype=dtype, backend=numpy_backend)
  with pytest.raises(ValueError):
    BaseDMRG(mps, mpo, numpy_backend.ones((1, 1, 1), dtype=dtype),
             numpy_backend.ones((1, 1, 1), dtype=dtype), 'name')

  mpo = FiniteXXZ(Jz, Jxy, Bz, dtype=np.float64, backend=numpy_backend)
  mps = FiniteMPS.random(
      [2] * N, [D] * (N - 1), dtype=np.float32, backend=numpy_backend)
  with pytest.raises(
      TypeError,
      match="mps.dtype = {} is different from "
      "mpo.dtype = {}".format(mps.dtype, mpo.dtype)):
    BaseDMRG(mps, mpo, numpy_backend.ones((1, 1, 1), dtype=dtype),
             numpy_backend.ones((1, 1, 1), dtype=dtype), 'name')

  mpo = FiniteXXZ(Jz, Jxy, Bz, dtype=np.float64, backend=numpy_backend)
  mps = FiniteMPS.random(
      [2] * N, [D] * (N - 1), dtype=np.float64, backend=pytorch_backend)
  with pytest.raises(TypeError, match="mps and mpo use different backends."):
    BaseDMRG(mps, mpo, numpy_backend.ones((1, 1, 1), dtype=dtype),
             numpy_backend.ones((1, 1, 1), dtype=dtype), 'name')
Example #2
0
def test_BaseDMRG_position(backend_dtype_values):
  backend = backend_factory.get_backend(backend_dtype_values[0])
  dtype = backend_dtype_values[1]

  N = 10
  D = 10
  Jz = np.ones(N - 1)
  Jxy = np.ones(N - 1)
  Bz = np.zeros(N)
  mpo = FiniteXXZ(Jz, Jxy, Bz, dtype=dtype, backend=backend)
  mps = FiniteMPS.random([2] * N, [D] * (N - 1), dtype=dtype, backend=backend)
  dmrg = BaseDMRG(mps, mpo, np.ones((1, 1, 1), dtype=dtype),
                  np.ones((1, 1, 1), dtype=dtype), 'name')
  dmrg.position(N - 1)
  np.testing.assert_allclose(np.arange(N), sorted(list(dmrg.left_envs.keys())))
  np.testing.assert_allclose([N - 1], list(dmrg.right_envs.keys()))
  assert dmrg.mps.center_position == N - 1
  dmrg.position(0)
  np.testing.assert_allclose([0], list(dmrg.left_envs.keys()))
  np.testing.assert_allclose(np.arange(N), sorted(list(dmrg.right_envs.keys())))
  assert dmrg.mps.center_position == 0

  with pytest.raises(IndexError, match="site > length of mps"):
    dmrg.position(N)
  with pytest.raises(IndexError, match="site < 0"):
    dmrg.position(-1)
Example #3
0
 def __init__(self,
              backend: Optional[Text] = None,
              dtype: Optional[Type[np.number]] = None) -> None:
   """
   Args:
     backend (str): name of the backend. Currently supported 
                    backends are 'numpy', 'tensorflow', 'pytorch', 'jax', 'shell'
     dtype: dtype of the backend of network. If `None`, no dtype checks 
            are performed. Default value is `None`. For backend 
            initialization functions like `zeros`, `ones`, `randn` a 
            dtype of `None` defaults to float64
   """
   if backend is None:
     backend = config.default_backend
   if dtype is None:
     dtype = config.default_dtype
   #backend.dtype is initialized from config.py (currently `None`)
   #if `backend.dtype = None`, the backend dtype is set at the first
   #call to `add_node(tensor)` to `backend.dtype = tensor.dtype`
   #if `dtype` is is set at initialization, all tensors added to
   #the network have to have the same `dtype` as the backend
   self.backend = backend_factory.get_backend(backend, dtype)
   self.nodes_set = set()
   # These increments are only used for generating names.
   self.node_increment = 0
   self.edge_increment = 0
Example #4
0
    def random(
        cls,
        d: List[int],
        D: List[int],
        dtype: Type[np.number],
        backend: Optional[Union[AbstractBackend,
                                Text]] = None) -> "InfiniteMPS":
        """Initialize a random `InfiniteMPS`. The resulting state is normalized.
    Its center-position is at 0.

    Args:
      d: A list of physical dimensions.
      D: A list of bond dimensions.
      dtype: A numpy dtype.
      backend: An optional backend.
    Returns:
      `InfiniteMPS`
    """
        #use numpy backend for tensor initialization
        be = backend_factory.get_backend('numpy')
        if len(D) != len(d) + 1:
            raise ValueError(
                'len(D) = {} is different from len(d) + 1= {}'.format(
                    len(D),
                    len(d) + 1))
        if D[-1] != D[0]:
            raise ValueError('D[0]={} != D[-1]={}.'.format(D[0], D[-1]))

        tensors = [
            be.randn((D[n], d[n], D[n + 1]), dtype=dtype)
            for n in range(len(d))
        ]
        return cls(tensors=tensors, center_position=0, backend=backend)
Example #5
0
def test_right_envs_two_non_consecutive_sites(backend_dtype_values):
    dtype = backend_dtype_values[1]
    backend = backend_factory.get_backend(backend_dtype_values[0])

    D, d, N = 3, 2, 5
    tensors = [np.ones((1, d, D), dtype=dtype)
               ] + [np.ones((D, d, D), dtype=dtype)
                    for _ in range(N - 2)] + [np.ones((D, d, 1), dtype=dtype)]
    mps = FiniteMPS(tensors,
                    center_position=None,
                    backend=backend_dtype_values[0],
                    canonicalize=False)
    r = backend.convert_to_tensor(np.ones((1, 1), dtype=dtype))
    exp = {}
    for n in reversed(range(N)):
        t = mps.tensors[n]
        if n in [1, 3]:
            exp[n] = r
        r = ncon([t, r, t], [[-1, 2, 1], [1, 3], [-2, 2, 3]], backend=backend)
    envs = mps.right_envs(sites=[1, 3])
    assert set(envs.keys()) == {3, 1}
    for n in [1, 3]:
        expected = exp[n]
        actual = envs[n]
        np.testing.assert_array_almost_equal(expected, actual)
Example #6
0
    def random(cls,
               d: List[int],
               D: List[int],
               dtype: Type[np.number],
               backend: Optional[Text] = None) -> "BaseMPS":
        """
    Initialize a random BaseMPS of length `N=len(d)`. The resulting state
    is NOT normalized. 

    Args:
      d: A list of `N` physical dimensions.
      D: A list of `N` bond dimensions.
      dtype: A numpy dtype.
      backend: An optional backend.
    Returns:
      `BaseMPS`
    """
        #use numpy backend for tensor initialization.
        #tensors will be converted to backend type during
        #call to __init__
        be = backend_factory.get_backend('numpy')
        tensors = [
            be.randn((D[n], d[n], D[n + 1]), dtype=dtype)
            for n in range(len(d))
        ]
        cls(tensors=tensors, backend=backend)
        return cls
def test_check_normality_raises_value_error(backend):
    backend = backend_factory.get_backend(backend)
    tensor = np.ones((2, 3, 2), dtype=np.float64)
    tensors = [tensor]
    mps = BaseMPS(tensors, backend=backend)
    with pytest.raises(ValueError):
        mps.check_orthonormality(which="keft", site=0)
Example #8
0
    def random(cls,
               d: List[int],
               D: List[int],
               dtype: Type[np.number],
               backend: Optional[Text] = None):
        """
    Initialize a random `FiniteMPS`. The resulting state
    is normalized. Its center-position is at 0.

    Args:
      d: A list of physical dimensions.
      D: A list of bond dimensions.
      dtype: A numpy dtype.
      backend: An optional backend.
    Returns:
      `FiniteMPS`
    """
        #use numpy backend for tensor initialization
        be = backend_factory.get_backend('numpy')
        if len(D) != len(d) - 1:
            raise ValueError(
                'len(D) = {} is different from len(d) - 1 = {}'.format(
                    len(D),
                    len(d) - 1))
        D = [1] + D + [1]
        tensors = [be.randn((D[n], d[n], D[n + 1])) for n in range(len(d))]
        return cls(tensors=tensors, center_position=0, backend=backend)
Example #9
0
 def __init__(self,
              backend: Optional[Text] = None,
              dtype: Optional[Type[np.number]] = None) -> None:
     """
 Args:
   backend (str): name of the backend. Currently supported 
                  backends are 'numpy', 'tensorflow', 'pytorch', 'jax', 'shell'
   dtype: dtype of the backend of network. If `None`, no dtype checks 
          are performed. Default value is `None`. For backend 
          initialization functions like `zeros`, `ones`, `randn` a 
          dtype of `None` defaults to float64
 """
     logging.warn(
         "The TensorNetwork object has been DEPRECATED and will be removed "
         "in future releases.\n"
         "Updating your code is fairly easy. For a detailed tutorial, please "
         "visit "
         "https://medium.com/@keeper6928/upgrading-your-tensornetwork-code-b032f0ab3dd4"
     )
     if backend is None:
         backend = config.default_backend
     if dtype is None:
         dtype = config.default_dtype
     #backend.dtype is initialized from config.py (currently `None`)
     #if `backend.dtype = None`, the backend dtype is set at the first
     #call to `add_node(tensor)` to `backend.dtype = tensor.dtype`
     #if `dtype` is is set at initialization, all tensors added to
     #the network have to have the same `dtype` as the backend
     self.backend = backend_factory.get_backend(backend, dtype)
     self.nodes_set = set()
     # These increments are only used for generating names.
     self.node_increment = 0
     self.edge_increment = 0
Example #10
0
def switch_backend(nodes: Iterable[BaseNode], new_backend: Text) -> None:
  """Change the backend of the nodes.

  This will convert all `node`'s tensors to the `new_backend`'s Tensor type.

  Args:
    nodes: iterable of nodes
    new_backend (str): The new backend.
    dtype (datatype): The dtype of the backend. If `None`,
      a defautl dtype according to config.py will be chosen.

  Returns:
    None
  """
  if new_backend == 'symmetric':
    if np.all([n.backend.name == 'symmetric' for n in nodes]):
      return
    raise ValueError("switching to `symmetric` backend not possible")

  backend = backend_factory.get_backend(new_backend)
  for node in nodes:
    if node.backend.name != "numpy":
      raise NotImplementedError("Can only switch backends when the current "
                                "backend is 'numpy'. Current backend "
                                "is '{}'".format(node.backend))
    node.tensor = backend.convert_to_tensor(node.tensor)
    node.backend = backend
Example #11
0
    def __init__(self,
                 tensors: List[Tensor],
                 backend: Optional[Union[AbstractBackend, Text]] = None,
                 name: Optional[Text] = None) -> None:
        """
    Initialize a BaseMPO.
    Args:
      tensors: A list of `Tensor` objects.
      backend: The name of the backend that should be used to perform 
        contractions. 
      name: A name for the MPO.
    """
        if backend is None:
            backend = get_default_backend()
        if isinstance(backend, AbstractBackend):
            self.backend = backend
        else:
            self.backend = backend_factory.get_backend(backend)
        self.tensors = [self.backend.convert_to_tensor(t) for t in tensors]
        if len(self.tensors) > 0:
            if not all(self.tensors[0].dtype == tensor.dtype
                       for tensor in self.tensors):
                raise TypeError(
                    'not all dtypes in BaseMPO.tensors are the same')

        self.name = name
Example #12
0
 def __init__(self, backend: Optional[Text] = None) -> None:
     if backend is None:
         backend = config.default_backend
     self.backend = backend_factory.get_backend(backend)
     self.nodes_set = set()
     # These increments are only used for generating names.
     self.node_increment = 0
     self.edge_increment = 0
def test_get_tensor(backend):
    backend = backend_factory.get_backend(backend)
    tensor1 = np.ones((2, 3, 2), dtype=np.float64)
    tensor2 = 2 * np.ones((2, 3, 2), dtype=np.float64)
    tensors = [tensor1, tensor2]
    mps = BaseMPS(tensors, backend=backend)
    np.testing.assert_allclose(mps.get_tensor(0), tensor1)
    np.testing.assert_allclose(mps.get_tensor(1), tensor2)
Example #14
0
def test_check_canonical(backend):
  backend = backend_factory.get_backend(backend)
  tensor = np.array([[[1., 2., 1.], [1., -2., 1.]],
                     [[-1., 1., -1.], [-1., 1., -1.]], [[1., 2, 3], [3, 2, 1]]],
                    dtype=np.float64)
  tensors = 6 * [backend.convert_to_tensor(tensor)]
  mps = BaseMPS(tensors, backend=backend, center_position=2)
  np.testing.assert_allclose(mps.check_canonical(), 71.714713)
def test_get_tensor_connector_matrix(backend):
    backend = backend_factory.get_backend(backend)
    tensor1 = np.ones((2, 3, 2), dtype=np.float64)
    tensor2 = 2 * np.ones((2, 3, 2), dtype=np.float64)
    connector = backend.convert_to_tensor(np.ones((2, 2), dtype=np.float64))
    tensors = [tensor1, tensor2]
    mps = BaseMPS(tensors, backend=backend, connector_matrix=connector)
    np.testing.assert_allclose(mps.get_tensor(0), tensor1)
    np.testing.assert_allclose(mps.get_tensor(1), 2 * tensor2)
def test_get_tensor_raises_error(backend):
    backend = backend_factory.get_backend(backend)
    tensor1 = np.ones((2, 3, 2), dtype=np.float64)
    tensor2 = 2 * np.ones((2, 3, 2), dtype=np.float64)
    tensors = [tensor1, tensor2]
    mps = BaseMPS(tensors, backend=backend)
    with pytest.raises(ValueError):
        mps.get_tensor(site=-1)
    with pytest.raises(IndexError):
        mps.get_tensor(site=3)
def test_backend_initialization(backend):
    be = backend_factory.get_backend(backend)
    D, d, N = 10, 2, 10
    tensors = [np.random.randn(1, d, D)
               ] + [np.random.randn(D, d, D)
                    for _ in range(N - 2)] + [np.random.randn(D, d, 1)]
    mps = BaseMPS(tensors, center_position=0, backend=be)
    mps.position(len(mps) - 1)
    Z = mps.position(0, normalize=True)
    np.testing.assert_allclose(Z, 1.0)
Example #18
0
    def __init__(
            self,
            tensors: List[Tensor],
            center_position: Optional[int] = None,
            connector_matrix: Optional[Tensor] = None,
            backend: Optional[Union[Text, AbstractBackend]] = None) -> None:
        """Initialize a BaseMPS.

    Args:
      tensors: A list of `Tensor` objects.
      center_position: The initial position of the center site.
      connector_matrix: A `Tensor` of rank 2 connecting
        different unitcells. A value `None` is equivalent to an identity
        `connector_matrix`.
      backend: The name of the backend that should be used to perform
        contractions. Available backends are currently 'numpy', 'tensorflow',
        'pytorch', 'jax'
    """
        if (center_position is not None) and (center_position < 0 or
                                              center_position >= len(tensors)):
            raise ValueError(
                "`center_position = {}` is different from `None` and "
                "not between 0 <= center_position < {}".format(
                    center_position, len(tensors)))
        if backend is None:
            backend = get_default_backend()
        if isinstance(backend, AbstractBackend):
            self.backend = backend
        else:
            self.backend = backend_factory.get_backend(backend)

        # the dtype is deduced from the tensor object.
        self.tensors = [self.backend.convert_to_tensor(t) for t in tensors]
        if not all(
            [self.tensors[0].dtype == tensor.dtype
             for tensor in self.tensors]):
            raise TypeError('not all dtypes in BaseMPS.tensors are the same')

        self.connector_matrix = connector_matrix
        self.center_position = center_position

        ########################################################################
        ##########       define functions for jitted operations       ##########
        ########################################################################
        def qr_decomposition(tensor):
            return self.backend.qr_decomposition(tensor, 2)

        self.qr_decomposition = self.backend.jit(qr_decomposition)

        def rq_decomposition(tensor):
            return self.backend.rq_decomposition(tensor, 1)

        self.rq_decomposition = self.backend.jit(rq_decomposition)

        self.norm = self.backend.jit(self.backend.norm)
Example #19
0
def test_apply_one_site_gate_2(backend):
  backend = backend_factory.get_backend(backend)
  tensor = np.array([[[1., 2., 1.], [1., -2., 1.]],
                     [[-1., 1., -1.], [-1., 1., -1.]], [[1., 2, 3], [3, 2, 1]]],
                    dtype=np.float64)
  tensors = 6 * [backend.convert_to_tensor(tensor)]
  mps = BaseMPS(tensors, backend=backend, center_position=2)
  gate = backend.convert_to_tensor(np.array([[0, 1], [1, 0]], dtype=np.float64))
  mps.apply_one_site_gate(gate=gate, site=1)
  expected = np.array([[1., -2., 1.], [1., 2., 1.]])
  np.testing.assert_allclose(mps.tensors[1][0], expected)
Example #20
0
def test_apply_one_site_gate_invalid_site_raises_error(backend):
  backend = backend_factory.get_backend(backend)
  tensor = np.array([[[1., 2., 1.], [1., -2., 1.]],
                     [[-1., 1., -1.], [-1., 1., -1.]], [[1., 2, 3], [3, 2, 1]]],
                    dtype=np.float64)
  tensors = 6 * [backend.convert_to_tensor(tensor)]
  mps = BaseMPS(tensors, backend=backend, center_position=2)
  gate = backend.convert_to_tensor(np.ones((2, 2), dtype=np.float64))
  with pytest.raises(ValueError):
    mps.apply_one_site_gate(gate=gate, site=-1)
  with pytest.raises(ValueError):
    mps.apply_one_site_gate(gate=gate, site=6)
Example #21
0
def test_apply_two_site_max_singular_value_not_center_raises_error(backend):
  backend = backend_factory.get_backend(backend)
  tensor = np.array([[[1., 2., 1.], [1., -2., 1.]],
                     [[-1., 1., -1.], [-1., 1., -1.]], [[1., 2, 3], [3, 2, 1]]],
                    dtype=np.float64)
  tensors = 6 * [backend.convert_to_tensor(tensor)]
  mps = BaseMPS(tensors, backend=backend, center_position=2)
  gate = backend.convert_to_tensor(np.ones((2, 2, 2, 2), dtype=np.float64))
  with pytest.raises(ValueError):
    mps.apply_two_site_gate(gate=gate, site1=3, site2=4, max_singular_values=1)
  with pytest.raises(ValueError):
    mps.apply_two_site_gate(gate=gate, site1=3, site2=4, max_truncation_err=.1)
Example #22
0
 def __call__(self, v):
     be = backend_factory.get_backend(self.backend)
     shape = ([self.lenvs[-1].shape[0]] +
              self.psi.physical_dimensions[self.pos:self.pos + 2] +
              [self.renvs[-1].shape[0]])
     vnode = tn.Node(be.reshape(v, shape))
     nodes = ([self.lenvs[-1], vnode] +
              self.H.nodes[self.pos:self.pos + 2] + [self.renvs[-1]])
     vout = tn.ncon(nodes, [(1, 3, -1), (1, 2, 4, 6), (3, -2, 2, 5),
                            (5, -3, 4, 7), (6, 7, -4)],
                    backend=self.backend)
     return be.reshape(vout.tensor, be.shape(v))
Example #23
0
def test_measure_local_operator_value_error(backend):
  backend = backend_factory.get_backend(backend)
  tensor = np.array([[[1., 2., 1.], [1., -2., 1.]],
                     [[-1., 1., -1.], [-1., 1., -1.]], [[1., 2, 3], [3, 2, 1]]],
                    dtype=np.float64)

  tensors = 6 * [backend.convert_to_tensor(tensor)]
  operator = backend.convert_to_tensor(
      np.array([[1, -1], [-1, 1]], dtype=np.float64))
  mps = BaseMPS(tensors, backend=backend)
  with pytest.raises(ValueError):
    mps.measure_local_operator(ops=2 * [operator], sites=[1, 2, 3])
Example #24
0
def test_base_mpo_init(backend_dtype_values):
    backend = backend_factory.get_backend(backend_dtype_values[0])
    dtype = backend_dtype_values[1]
    tensors = [
        backend.randn((1, 5, 2, 2), dtype=dtype),
        backend.randn((5, 5, 2, 2), dtype=dtype),
        backend.randn((5, 1, 2, 2), dtype=dtype)
    ]
    mpo = BaseMPO(tensors=tensors, backend=backend, name='test')
    assert mpo.backend is backend
    assert mpo.dtype == dtype
    np.testing.assert_allclose(mpo.bond_dimensions, [1, 5, 5, 1])
Example #25
0
def test_base_mpo_raises():
    backend = backend_factory.get_backend('numpy')
    tensors = [
        backend.randn((1, 5, 2, 2), dtype=np.float64),
        backend.randn((5, 5, 2, 2), dtype=np.float64),
        backend.randn((5, 1, 2, 2), dtype=np.float32)
    ]
    with pytest.raises(TypeError):
        BaseMPO(tensors=tensors, backend=backend)
    mpo = BaseMPO(tensors=[], backend=backend)
    mpo.tensors = tensors
    with pytest.raises(TypeError):
        mpo.dtype
Example #26
0
def test_measure_two_body_correlator_value_error(backend):
  backend = backend_factory.get_backend(backend)
  tensor = np.array([[[1., 2., 1.], [1., -2., 1.]],
                     [[-1., 1., -1.], [-1., 1., -1.]], [[1., 2, 3], [3, 2, 1]]],
                    dtype=np.float64)

  tensors = 6 * [backend.convert_to_tensor(tensor)]
  operator = backend.convert_to_tensor(
      np.array([[1, -1], [-1, 1]], dtype=np.float64))
  mps = BaseMPS(tensors, backend=backend)
  with pytest.raises(ValueError):
    mps.measure_two_body_correlator(
        op1=operator, op2=operator, site1=-1, sites2=[2])
Example #27
0
  def switch_backend(self, new_backend: Text) -> None:
    """Change this network's backend.

    This will convert all node's tensors to the new backend's Tensor type.
    """
    if self.backend.name != "numpy":
      raise NotImplementedError(
          "Can only switch backends when the current "
          "backend is 'numpy'. Current backend is '{}'".format(
              self.backend.name))
    self.backend = backend_factory.get_backend(new_backend)
    for node in self.nodes_set:
      node.tensor = self.backend.convert_to_tensor(node.tensor)
Example #28
0
def test_apply_transfer_operator_invalid_direction_raises_error(backend):
  backend = backend_factory.get_backend(backend)
  tensor = np.array([[[1., 2., 1.], [1., -2., 1.]],
                     [[-1., 1., -1.], [-1., 1., -1.]], [[1., 2, 3], [3, 2, 1]]],
                    dtype=np.float64)

  tensors = 6 * [backend.convert_to_tensor(tensor)]
  mat = backend.convert_to_tensor(
      np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=np.float64))
  mps = BaseMPS(tensors, backend=backend)
  with pytest.raises(ValueError):
    mps.apply_transfer_operator(site=3, direction=0, matrix=mat)
  with pytest.raises(ValueError):
    mps.apply_transfer_operator(site=3, direction="keft", matrix=mat)
Example #29
0
def test_BaseDMRG_init(backend_dtype_values):
  backend = backend_factory.get_backend(backend_dtype_values[0])
  dtype = backend_dtype_values[1]

  N = 10
  D = 10
  Jz = np.ones(N - 1)
  Jxy = np.ones(N - 1)
  Bz = np.zeros(N)
  mpo = FiniteXXZ(Jz, Jxy, Bz, dtype=dtype, backend=backend)
  mps = FiniteMPS.random([2] * N, [D] * (N - 1), dtype=dtype, backend=backend)
  dmrg = BaseDMRG(mps, mpo, np.ones((1, 1, 1), dtype=dtype),
                  np.ones((1, 1, 1), dtype=dtype), 'name')
  assert dmrg.name == 'name'
  assert dmrg.backend is backend
def test_backend_initialization_raises(backend):
    be = backend_factory.get_backend(backend)
    D, d, N = 10, 2, 10
    tensors = [np.random.randn(1, d, D)
               ] + [np.random.randn(D, d, D)
                    for _ in range(N - 2)] + [np.random.randn(D, d, 1)]
    with pytest.raises(
            ValueError,
            match="`center_position = 10` is different from `None` and "
            "not between 0 <= center_position < 10"):
        BaseMPS(tensors, center_position=N, backend=be)
    with pytest.raises(
            ValueError,
            match="`center_position = -1` is different from `None` and "
            "not between 0 <= center_position < 10"):
        BaseMPS(tensors, center_position=-1, backend=be)