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')
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)
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
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)
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)
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)
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)
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
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
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
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)
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)
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)
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)
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)
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)
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))
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])
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])
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
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])
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)
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)
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)