def __init__( self, hilbert: AbstractHilbert, graph: AbstractGraph, U: float, V: float = 0.0, J: float = 1.0, mu: float = 0.0, dtype: DType = None, ): r""" Constructs a new BoseHubbard operator given a hilbert space, a graph specifying the connectivity and the interaction strength. The chemical potential and the density-density interaction strength can be specified as well. Args: hilbert: Hilbert space the operator acts on. U: The on-site interaction term. V: The strength of density-density interaction term. J: The hopping amplitude. mu: The chemical potential. dtype: The dtype of the matrix elements. Examples: Constructs a BoseHubbard operator for a 2D system. >>> import netket as nk >>> g = nk.graph.Hypercube(length=3, n_dim=2, pbc=True) >>> hi = nk.hilbert.Fock(n_max=3, n_particles=6, N=g.n_nodes) >>> op = nk.operator.BoseHubbard(hi, U=4.0, graph=g) >>> print(op.hilbert.size) 9 """ assert ( graph.n_nodes == hilbert.size ), "The size of the graph must match the hilbert space." assert isinstance(hilbert, Fock) super().__init__(hilbert) if dtype is None: dtype = jnp.promote_types(_dtype(U), _dtype(V)) dtype = jnp.promote_types(dtype, _dtype(J)) dtype = jnp.promote_types(dtype, _dtype(mu)) dtype = np.empty((), dtype=dtype).dtype self._dtype = dtype self._U = np.asarray(U, dtype=dtype) self._V = np.asarray(V, dtype=dtype) self._J = np.asarray(J, dtype=dtype) self._mu = np.asarray(mu, dtype=dtype) self._n_max = hilbert.n_max self._n_sites = hilbert.size self._edges = np.asarray(list(graph.edges())) self._max_conn = 1 + self._edges.shape[0] * 2 self._max_mels = np.empty(self._max_conn, dtype=self.dtype) self._max_xprime = np.empty((self._max_conn, self._n_sites))
def __init__( self, hilbert: AbstractHilbert, graph: AbstractGraph, h: float, J: float = 1.0, dtype: DType = None, ): r""" Constructs the Ising Operator from an hilbert space and a graph specifying the connectivity. Args: hilbert: Hilbert space the operator acts on. h: The strength of the transverse field. J: The strength of the coupling. Default is 1.0. dtype: The dtype of the matrix elements. Examples: Constructs an ``Ising`` operator for a 1D system. >>> import netket as nk >>> g = nk.graph.Hypercube(length=20, n_dim=1, pbc=True) >>> hi = nk.hilbert.Spin(s=0.5, N=g.n_nodes) >>> op = nk.operator.Ising(h=1.321, hilbert=hi, J=0.5, graph=g) >>> print(op) Ising(J=0.5, h=1.321; dim=20) """ assert ( graph.n_nodes == hilbert.size ), "The size of the graph must match the hilbert space" super().__init__(hilbert) if dtype is None: dtype = jnp.promote_types(_dtype(h), _dtype(J)) dtype = np.empty((), dtype=dtype).dtype self._dtype = dtype self._h = np.array(h, dtype=dtype) self._J = np.array(J, dtype=dtype) self._edges = np.asarray( [[u, v] for u, v in graph.edges()], dtype=np.intp, )
def __init__( self, hilbert: AbstractHilbert, graph: AbstractGraph, h: float, J: float = 1.0, dtype: DType = float, ): r""" Constructs the Ising Operator from an hilbert space and a graph specifying the connectivity. Args: hilbert: Hilbert space the operator acts on. h: The strength of the transverse field. J: The strength of the coupling. Default is 1.0. dtype: The dtype of the matrix elements. Examples: Constructs an ``Ising`` operator for a 1D system. >>> import netket as nk >>> g = nk.graph.Hypercube(length=20, n_dim=1, pbc=True) >>> hi = nk.hilbert.Spin(s=0.5, graph=g) >>> op = nk.operator.Ising(h=1.321, hilbert=hi, J=0.5) >>> print(op.hilbert.size) 20 """ assert (graph.n_nodes == hilbert.size ), "The size of the graph must match the hilbert space" super().__init__(hilbert) self._h = dtype(h) self._J = dtype(J) self._edges = np.asarray(list(graph.edges()), dtype=np.intp) self._dtype = dtype
def __init__( self, hilbert: AbstractHilbert, graph: AbstractGraph, J: float = 1, sign_rule=None, ): """ Constructs an Heisenberg operator given a hilbert space and a graph providing the connectivity of the lattice. Args: hilbert: Hilbert space the operator acts on. graph: The graph upon which this hamiltonian is defined. J: The strength of the coupling. Default is 1. sign_rule: If enabled, Marshal's sign rule will be used. On a bipartite lattice, this corresponds to a basis change flipping the Sz direction at every odd site of the lattice. For non-bipartite lattices, the sign rule cannot be applied. Defaults to True if the lattice is bipartite, False otherwise. Examples: Constructs a ``Heisenberg`` operator for a 1D system. >>> import netket as nk >>> g = nk.graph.Hypercube(length=20, n_dim=1, pbc=True) >>> hi = nk.hilbert.Spin(s=0.5, total_sz=0, graph=g) >>> op = nk.operator.Heisenberg(hilbert=hi) >>> print(op.hilbert.size) 20 """ if sign_rule is None: sign_rule = graph.is_bipartite() self._J = J self._sign_rule = sign_rule sz_sz = np.array([ [1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1], ]) exchange = np.array([ [0, 0, 0, 0], [0, 0, 2, 0], [0, 2, 0, 0], [0, 0, 0, 0], ]) if sign_rule: if not graph.is_bipartite(): raise ValueError( "sign_rule=True specified for a non-bipartite lattice") heis_term = sz_sz - exchange else: heis_term = sz_sz + exchange super().__init__( hilbert, graph, bond_ops=[J * heis_term], )
def __init__( self, hilbert: AbstractHilbert, graph: AbstractGraph, J: Union[float, Sequence[float]] = 1.0, sign_rule=None, *, acting_on_subspace: Union[List[int], int] = None, ): """ Constructs an Heisenberg operator given a hilbert space and a graph providing the connectivity of the lattice. Args: hilbert: Hilbert space the operator acts on. graph: The graph upon which this hamiltonian is defined. J: The strength of the coupling. Default is 1. Can pass a sequence of coupling strengths with coloured graphs: edges of colour n will have coupling strength J[n] sign_rule: If True, Marshal's sign rule will be used. On a bipartite lattice, this corresponds to a basis change flipping the Sz direction at every odd site of the lattice. For non-bipartite lattices, the sign rule cannot be applied. Defaults to True if the lattice is bipartite, False otherwise. If a sequence of coupling strengths is passed, defaults to False and a matching sequence of sign_rule must be specified to override it acting_on_subspace: Specifies the mapping between nodes of the graph and Hilbert space sites, so that graph node :code:`i ∈ [0, ..., graph.n_nodes - 1]`, corresponds to :code:`acting_on_subspace[i] ∈ [0, ..., hilbert.n_sites]`. Must be a list of length `graph.n_nodes`. Passing a single integer :code:`start` is equivalent to :code:`[start, ..., start + graph.n_nodes - 1]`. Examples: Constructs a ``Heisenberg`` operator for a 1D system. >>> import netket as nk >>> g = nk.graph.Hypercube(length=20, n_dim=1, pbc=True) >>> hi = nk.hilbert.Spin(s=0.5, total_sz=0, N=g.n_nodes) >>> op = nk.operator.Heisenberg(hilbert=hi, graph=g) >>> print(op) Heisenberg(J=1.0, sign_rule=True; dim=20) """ if isinstance(J, Sequence): # check that the number of Js matches the number of colours assert len(J) == max(graph.edge_colors) + 1 if sign_rule is None: sign_rule = [False] * len(J) else: assert len(sign_rule) == len(J) for i in range(len(J)): subgraph = Graph(edges=graph.edges(filter_color=i)) if sign_rule[i] and not subgraph.is_bipartite(): raise ValueError( "sign_rule=True specified for a non-bipartite lattice" ) else: if sign_rule is None: sign_rule = graph.is_bipartite() elif sign_rule and not graph.is_bipartite(): raise ValueError( "sign_rule=True specified for a non-bipartite lattice") self._J = J self._sign_rule = sign_rule sz_sz = np.array([ [1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1], ]) exchange = np.array([ [0, 0, 0, 0], [0, 0, 2, 0], [0, 2, 0, 0], [0, 0, 0, 0], ]) if isinstance(J, Sequence): bond_ops = [ J[i] * (sz_sz - exchange if sign_rule[i] else sz_sz + exchange) for i in range(len(J)) ] bond_ops_colors = list(range(len(J))) else: bond_ops = [ J * (sz_sz - exchange if sign_rule else sz_sz + exchange) ] bond_ops_colors = [] super().__init__( hilbert, graph, bond_ops=bond_ops, bond_ops_colors=bond_ops_colors, acting_on_subspace=acting_on_subspace, )
def __init__( self, hilbert: AbstractHilbert, graph: AbstractGraph, site_ops=[], bond_ops=[], bond_ops_colors=[], dtype: DType = None, *, acting_on_subspace: Union[List[int], int] = None, ): r""" A graph-based quantum operator. In its simplest terms, this is the sum of local operators living on the edge of an arbitrary graph. A ``GraphOperator`` is constructed giving a hilbert space and either a list of operators acting on sites or a list acting on the bonds. Users can specify the color of the bond that an operator acts on, if desired. If none are specified, the bond operators act on all edges. Args: hilbert: Hilbert space the operator acts on. graph: The graph whose vertices and edges are considered to construct the operator site_ops: A list of operators in matrix form that act on the nodes of the graph. The default is an empty list. Note that if no site_ops are specified, the user must give a list of bond operators. bond_ops: A list of operators that act on the edges of the graph. The default is None. Note that if no bond_ops are specified, the user must give a list of site operators. bond_ops_colors: A list of edge colors, specifying the color each bond operator acts on. The default is an empty list. dtype: Data type type of the matrix elements. acting_on_subspace: Specifies the mapping between nodes of the graph and Hilbert space sites, so that graph node :code:`i ∈ [0, ..., graph.n_nodes]`, corresponds to :code:`acting_on_subspace[i] ∈ [0, ..., hilbert.n_sites]`. Must be a list of length `graph.n_nodes`. Passing a single integer :code:`start` is equivalent to :code:`[start, ..., start + graph.n_nodes - 1]`. Examples: Constructs a ``GraphOperator`` operator for a 2D system. >>> import netket as nk >>> sigmax = [[0, 1], [1, 0]] >>> mszsz = [[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]] >>> edges = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], ... [8, 9], [9, 10], [10, 11], [11, 12], [12, 13], [13, 14], [14, 15], ... [15, 16], [16, 17], [17, 18], [18, 19], [19, 0]] >>> g = nk.graph.Graph(edges=edges) >>> hi = nk.hilbert.CustomHilbert(local_states=[-1, 1], N=g.n_nodes) >>> op = nk.operator.GraphOperator( ... hi, site_ops=[sigmax], bond_ops=[mszsz], graph=g) >>> print(op) GraphOperator(dim=20, #acting_on=40 locations, constant=0.0, dtype=float64, graph=Graph(n_nodes=20, n_edges=20)) """ acting_on_subspace = check_acting_on_subspace(acting_on_subspace, hilbert, graph) self._acting_on_subspace = acting_on_subspace # Ensure that at least one of SiteOps and BondOps was initialized if len(bond_ops) == 0 and len(site_ops) == 0: raise ValueError("Must input at least site_ops or bond_ops.") # Create the local operator as the sum of all site and bond operators operators = [] acting_on = [] # Site operators if len(site_ops) > 0: for i in range(graph.n_nodes): for site_op in site_ops: i_prime = acting_on_subspace[i] operators.append(site_op) acting_on.append([i_prime]) # Bond operators if len(bond_ops_colors) > 0: if len(bond_ops) != len(bond_ops_colors): raise ValueError( """The GraphHamiltonian definition is inconsistent. The sizes of bond_ops and bond_ops_colors do not match.""") if len(bond_ops) > 0: # Use edge_colors to populate operators for (u, v, color) in graph.edges(return_color=True): u, v = acting_on_subspace[u], acting_on_subspace[v] for c, bond_color in enumerate(bond_ops_colors): if bond_color == color: operators.append(bond_ops[c]) acting_on.append([u, v]) else: assert len(bond_ops) == 1 for u, v in graph.edges(): u, v = acting_on_subspace[u], acting_on_subspace[v] operators.append(bond_ops[0]) acting_on.append([u, v]) super().__init__(hilbert, operators, acting_on, dtype=dtype) self._graph = graph
def __init__( self, hilbert: AbstractHilbert, graph: AbstractGraph, site_ops=[], bond_ops=[], bond_ops_colors=[], dtype: DType = None, ): r""" A graph-based quantum operator. In its simplest terms, this is the sum of local operators living on the edge of an arbitrary graph. A ``GraphOperator`` is constructed giving a hilbert space and either a list of operators acting on sites or a list acting on the bonds. Users can specify the color of the bond that an operator acts on, if desired. If none are specified, the bond operators act on all edges. Args: hilbert: Hilbert space the operator acts on. graph: The graph whose vertices and edges are considered to construct the operator site_ops: A list of operators in matrix form that act on the nodes of the graph. The default is an empty list. Note that if no site_ops are specified, the user must give a list of bond operators. bond_ops: A list of operators that act on the edges of the graph. The default is None. Note that if no bond_ops are specified, the user must give a list of site operators. bond_ops_colors: A list of edge colors, specifying the color each bond operator acts on. The default is an empty list. Examples: Constructs a ``GraphOperator`` operator for a 2D system. >>> import netket as nk >>> sigmax = [[0, 1], [1, 0]] >>> mszsz = [[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]] >>> edges = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], ... [8, 9], [9, 10], [10, 11], [11, 12], [12, 13], [13, 14], [14, 15], ... [15, 16], [16, 17], [17, 18], [18, 19], [19, 0]] >>> g = nk.graph.Graph(edges=edges) >>> hi = nk.hilbert.CustomHilbert(local_states=[-1, 1], N=g.n_nodes) >>> op = nk.operator.GraphOperator( ... hi, site_ops=[sigmax], bond_ops=[mszsz], graph=g) >>> print(op) GraphOperator(dim=20, #acting_on=40 locations, constant=0, dtype=float64, graph=Graph(n_nodes=20, n_edges=20)) """ if graph.n_nodes != hilbert.size: raise ValueError( """The number of vertices in the graph ({graph.n_nodes}) must match the hilbert space size ({hilbert.size})""" ) # Ensure that at least one of SiteOps and BondOps was initialized if len(bond_ops) == 0 and len(site_ops) == 0: raise ValueError("Must input at least site_ops or bond_ops.") # Create the local operator as the sum of all site and bond operators operators = [] acting_on = [] # Site operators if len(site_ops) > 0: for i in range(graph.n_nodes): for j, site_op in enumerate(site_ops): operators.append(site_op) acting_on.append([i]) # Bond operators if len(bond_ops_colors) > 0: if len(bond_ops) != len(bond_ops_colors): raise ValueError( """The GraphHamiltonian definition is inconsistent. The sizes of bond_ops and bond_ops_colors do not match.""") if len(bond_ops) > 0: # Use edge_colors to populate operators for (u, v, color) in graph.edges(color=True): edge = u, v for c, bond_color in enumerate(bond_ops_colors): if bond_color == color: operators.append(bond_ops[c]) acting_on.append(edge) else: assert len(bond_ops) == 1 for edge in graph.edges(): operators.append(bond_ops[0]) acting_on.append(edge) super().__init__(hilbert, operators, acting_on, dtype=dtype) self._graph = graph