11from functools import reduce
2- from typing import Optional , Union
2+ from typing import Callable , List , Optional , Union
33
44import numpy as np
5+ from numpy .typing import ArrayLike
56
67from qibo import symbols
78from qibo .backends import Backend , _check_backend
89from qibo .config import raise_error
910from qibo .hamiltonians .hamiltonians import Hamiltonian , SymbolicHamiltonian
1011
1112
12- def X (nqubits , dense : bool = True , backend = None ):
13+ def X (
14+ nqubits : int , dense : bool = True , backend : Optional [Backend ] = None
15+ ) -> Hamiltonian | ArrayLike :
1316 """Non-interacting Pauli-:math:`X` Hamiltonian.
1417
1518 .. math::
@@ -28,15 +31,17 @@ def X(nqubits, dense: bool = True, backend=None):
2831 return _OneBodyPauli (nqubits , symbols .X , dense , backend = backend )
2932
3033
31- def Y (nqubits , dense : bool = True , backend = None ):
34+ def Y (
35+ nqubits : int , dense : bool = True , backend : Optional [Backend ] = None
36+ ) -> Hamiltonian | ArrayLike :
3237 """Non-interacting Pauli-:math:`Y` Hamiltonian.
3338
3439 .. math::
3540 H = - \\ sum _{k=0}^{N} \\ , Y_{k} \\ , .
3641
3742 Args:
3843 nqubits (int): number of qubits.
39- dense (bool): If ``True`` it creates the Hamiltonian as a
44+ dense (bool, optional ): If ``True`` it creates the Hamiltonian as a
4045 :class:`qibo.core.hamiltonians.Hamiltonian`, otherwise it creates
4146 a :class:`qibo.core.hamiltonians.SymbolicHamiltonian`.
4247 backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used
@@ -46,29 +51,39 @@ def Y(nqubits, dense: bool = True, backend=None):
4651 return _OneBodyPauli (nqubits , symbols .Y , dense , backend = backend )
4752
4853
49- def Z (nqubits , dense : bool = True , backend = None ):
54+ def Z (
55+ nqubits : int , dense : bool = True , backend : Optional [Backend ] = None
56+ ) -> Hamiltonian | ArrayLike :
5057 """Non-interacting Pauli-:math:`Z` Hamiltonian.
5158
5259 .. math::
5360 H = - \\ sum _{k=0}^{N} \\ , Z_{k} \\ , .
5461
5562 Args:
5663 nqubits (int): number of qubits.
57- dense (bool): If ``True`` it creates the Hamiltonian as a
64+ dense (bool, optional ): If ``True`` it creates the Hamiltonian as a
5865 :class:`qibo.core.hamiltonians.Hamiltonian`, otherwise it creates
5966 a :class:`qibo.core.hamiltonians.SymbolicHamiltonian`.
60- backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used
67+ backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used
6168 in the execution. If ``None``, it uses the current backend.
6269 Defaults to ``None``.
6370 """
6471 return _OneBodyPauli (nqubits , symbols .Z , dense , backend = backend )
6572
6673
67- def TFIM (nqubits , h : float = 0.0 , dense : bool = True , backend = None ):
68- """Transverse field Ising model with periodic boundary conditions.
74+ def TFIM (
75+ nqubits : int ,
76+ h : float = 0.0 ,
77+ dense : bool = True ,
78+ closed_boundary : bool = True ,
79+ backend : Optional [Backend ] = None ,
80+ ):
81+ """:math:`n`-qubit Transverse field Ising model.
6982
7083 .. math::
71- H = - \\ sum _{k=0}^{N} \\ , \\ left(Z_{k} \\ , Z_{k + 1} + h \\ , X_{k}\\ right) \\ , .
84+ H = - \\ sum _{k=0}^{n-1} \\ , \\ left(Z_{k} \\ , Z_{k + 1} + h \\ , X_{k}\\ right) \\ , ,
85+
86+ with :math:`Z_{n-1} Z_{n} \\ equiv Z_{n-1} Z_{0}` if ``closed_boundary=True``.
7287
7388 Args:
7489 nqubits (int): number of qubits.
@@ -77,38 +92,69 @@ def TFIM(nqubits, h: float = 0.0, dense: bool = True, backend=None):
7792 :class:`qibo.core.hamiltonians.Hamiltonian`, otherwise it creates
7893 a :class:`qibo.core.hamiltonians.SymbolicHamiltonian`.
7994 Defaults to ``True``.
95+ closed_boundary (bool, optional): If ``True``, returns TFIM with periodic boundary
96+ condition. If ``False``, returns Hamiltonian with open boundaries.
97+ Defaults to ``True``.
98+ backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used
99+ in the execution. If ``None``, it uses the current backend.
100+ Defaults to ``None``.
80101 """
81102 if nqubits < 2 :
82103 raise_error (ValueError , "Number of qubits must be larger than one." )
83104
84105 backend = _check_backend (backend )
85106
86107 if dense :
87- condition = lambda i , j : i in {j % nqubits , (j + 1 ) % nqubits }
88- ham = - _build_spin_model (nqubits , backend .matrices .Z , condition , backend )
89- if h != 0 :
90- condition = lambda i , j : i == j % nqubits
91- ham -= h * _build_spin_model (
92- nqubits , backend .matrices .X , condition , backend
108+ matrix = backend .zeros ((2 ** nqubits , 2 ** nqubits ), dtype = backend .complex128 )
109+ base_string = [backend .matrices .I ()] * nqubits
110+ for qubit in range (nqubits - 1 ):
111+ base_string [qubit ] = backend .matrices .Z
112+ base_string [qubit + 1 ] = backend .matrices .Z
113+ matrix += reduce (backend .kron , base_string )
114+ base_string [qubit ] = backend .matrices .I ()
115+ base_string [qubit + 1 ] = backend .matrices .I ()
116+
117+ if closed_boundary :
118+ base_string = (
119+ [backend .matrices .Z ]
120+ + [backend .matrices .I ()] * (nqubits - 2 )
121+ + [backend .matrices .Z ]
93122 )
94- return Hamiltonian (nqubits , ham , backend = backend )
123+ matrix += reduce (backend .kron , base_string )
124+
125+ if h != 0 :
126+ base_string = [backend .matrices .I ()] * nqubits
127+ for qubit in range (nqubits ):
128+ base_string [qubit ] = backend .matrices .X
129+ matrix += h * reduce (backend .kron , base_string )
130+ base_string [qubit ] = backend .matrices .I ()
131+
132+ matrix *= - 1
133+
134+ return Hamiltonian (nqubits , matrix , backend = backend )
95135
96136 term = lambda q1 , q2 : symbols .Z (q1 , backend = backend ) * symbols .Z (
97137 q2 , backend = backend
98138 ) + h * symbols .X (q1 , backend = backend )
99- form = - 1 * sum (term (qubit , qubit + 1 ) for qubit in range (nqubits - 1 )) - term (
100- nqubits - 1 , 0
101- )
139+
140+ form = - 1 * sum (term (qubit , qubit + 1 ) for qubit in range (nqubits - 1 ))
141+
142+ if closed_boundary :
143+ form -= term (nqubits - 1 , 0 )
144+ else :
145+ form -= h * symbols .X (nqubits - 1 , backend = backend )
146+
102147 ham = SymbolicHamiltonian (form = form , nqubits = nqubits , backend = backend )
148+
103149 return ham
104150
105151
106152def MaxCut (
107- nqubits ,
153+ nqubits : int ,
108154 dense : bool = True ,
109- adj_matrix : Optional [Union [list [list [float ]], np . ndarray ]] = None ,
155+ adj_matrix : Optional [Union [list [list [float ]], ArrayLike ]] = None ,
110156 backend : Optional [Backend ] = None ,
111- ):
157+ ) -> Hamiltonian | ArrayLike :
112158 """Max Cut Hamiltonian.
113159
114160 .. math::
@@ -152,7 +198,9 @@ def MaxCut(
152198 return ham
153199
154200
155- def LABS (nqubits : int , dense : bool = True , backend : Optional [Backend ] = None ):
201+ def LABS (
202+ nqubits : int , dense : bool = True , backend : Optional [Backend ] = None
203+ ) -> Hamiltonian | ArrayLike :
156204 """Create Hamiltonian of the Low Autocorrelation Binary Sequences (LABS) problem.
157205
158206 Given an integer :math:`n > 2`, the LABS problem consists of finding a binary sequence
@@ -167,7 +215,7 @@ def LABS(nqubits: int, dense: bool = True, backend: Optional[Backend] = None):
167215
168216 Args:
169217 nqubits (int): Total number of qubits.
170- dense (bool): If ``True`` it creates the Hamiltonian as a
218+ dense (bool, optional ): If ``True`` it creates the Hamiltonian as a
171219 :class:`qibo.core.hamiltonians.Hamiltonian`, otherwise it creates
172220 a :class:`qibo.core.hamiltonians.SymbolicHamiltonian`.
173221 backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used
@@ -202,12 +250,12 @@ def LABS(nqubits: int, dense: bool = True, backend: Optional[Backend] = None):
202250
203251
204252def Heisenberg (
205- nqubits ,
253+ nqubits : int ,
206254 coupling_constants : Union [float , int , list , tuple ],
207255 external_field_strengths : Union [float , int ],
208256 dense : bool = True ,
209- backend = None ,
210- ):
257+ backend : Optional [ Backend ] = None ,
258+ ) -> Hamiltonian | ArrayLike :
211259 """Heisenberg model on a :math:`1`-dimensional periodic lattice.
212260
213261 The general :math:`n`-qubit Hamiltonian is given by
@@ -274,8 +322,7 @@ def Heisenberg(
274322
275323 if dense :
276324 condition = lambda i , j : i in {j % nqubits , (j + 1 ) % nqubits }
277- matrix = np .zeros ((2 ** nqubits , 2 ** nqubits ))
278- matrix = backend .cast (matrix , dtype = backend .complex128 )
325+ matrix = backend .zeros ((2 ** nqubits , 2 ** nqubits ), dtype = backend .complex128 )
279326 for ind , pauli in enumerate (paulis ):
280327 double_term = _build_spin_model (
281328 nqubits , pauli (0 , backend = backend ).matrix , condition , backend
@@ -314,11 +361,11 @@ def term(q1, q2):
314361
315362
316363def XXX (
317- nqubits ,
364+ nqubits : int ,
318365 coupling_constant : Union [float , int ] = 1 ,
319366 external_field_strengths : Union [float , int , list , tuple ] = [0.5 , 0 , 0 ],
320367 dense : bool = True ,
321- backend = None ,
368+ backend : Optional [ Backend ] = None ,
322369):
323370 """Heisenberg :math:`\\ mathrm{XXX}` model with periodic boundary conditions.
324371
@@ -371,7 +418,12 @@ def XXX(
371418 )
372419
373420
374- def XXZ (nqubits , delta = 0.5 , dense : bool = True , backend = None ):
421+ def XXZ (
422+ nqubits : int ,
423+ delta : Union [float , int ] = 0.5 ,
424+ dense : bool = True ,
425+ backend : Optional [Backend ] = None ,
426+ ) -> Hamiltonian | ArrayLike :
375427 """Heisenberg :math:`\\ mathrm{XXZ}` model with periodic boundary conditions.
376428
377429 .. math::
@@ -386,7 +438,7 @@ def XXZ(nqubits, delta=0.5, dense: bool = True, backend=None):
386438
387439 Args:
388440 nqubits (int): number of qubits.
389- delta (float, optional): coefficient for the :math:`Z` component.
441+ delta (float or int , optional): coefficient for the :math:`Z` component.
390442 Defaults to :math:`0.5`.
391443 dense (bool, optional): If ``True``, creates the Hamiltonian as a
392444 :class:`qibo.core.hamiltonians.Hamiltonian`, otherwise it creates
@@ -407,11 +459,11 @@ def XXZ(nqubits, delta=0.5, dense: bool = True, backend=None):
407459
408460
409461def GPP (
410- adjacency_matrix ,
462+ adjacency_matrix : ArrayLike ,
411463 penalty_coeff : Union [float , int ] = 0.0 ,
412- node_weights = None ,
464+ node_weights : Optional [ ArrayLike ] = None ,
413465 dense : bool = True ,
414- backend = None ,
466+ backend : Optional [ Backend ] = None ,
415467):
416468 """The Graph Partitioning Problem (GPP) as a quadratic function.
417469
@@ -439,6 +491,11 @@ def GPP(
439491 adjacency_matrix (ndarray): Square symmetric matrix with weigths :math:`A_{jk}`
440492 representing the edges of the graph. For an unweighted graph,
441493 :math:`\\ A_{jk} = 1, \\ ,\\ , \\ forall \\ , j,k`.
494+ penalty_coeff (float or int, optional): hyperparameter :math:`\\ lambda` defining the
495+ strength of the penalty term. Defaults to :math:`0.0`.
496+ node_weights (ArrayLike, optional): Weight of the nodes of the graph.
497+ Used when :math:`\\ lambda \\ neq 0`. If ``None``, all node weights
498+ default to :math:`1`. Defaults to ``None``.
442499 dense (bool, optional): If ``True``, creates the Hamiltonian as a
443500 :class:`qibo.core.hamiltonians.Hamiltonian`, otherwise it creates
444501 a :class:`qibo.core.hamiltonians.SymbolicHamiltonian`.
@@ -491,7 +548,12 @@ def GPP(
491548 return _gpp_dense (adjacency_matrix , penalty_coeff , node_weights , backend )
492549
493550
494- def _gpp_symbolic (adjacency_matrix , penalty_coeff , node_weights , backend ):
551+ def _gpp_symbolic (
552+ adjacency_matrix : ArrayLike ,
553+ penalty_coeff : float | int ,
554+ node_weights : ArrayLike ,
555+ backend : Backend ,
556+ ) -> SymbolicHamiltonian :
495557 def term (index : int ):
496558 return (
497559 symbols .I (index , backend = backend ) - symbols .Z (index , backend = backend )
@@ -520,11 +582,11 @@ def term(index: int):
520582
521583
522584def _gpp_dense (
523- adjacency_matrix ,
524- penalty_coeff ,
525- node_weights ,
526- backend ,
527- ):
585+ adjacency_matrix : ArrayLike ,
586+ penalty_coeff : ArrayLike ,
587+ node_weights : ArrayLike ,
588+ backend : Backend ,
589+ ) -> Hamiltonian :
528590 def term (nqubits , ind_1 , ind_2 = None ):
529591 diag = [id_diag ] * nqubits
530592 diag [ind_1 ] = term_diag
@@ -560,7 +622,7 @@ def term(nqubits, ind_1, ind_2=None):
560622 return Hamiltonian (nqubits , backend .diag (hamiltonian ), backend = backend )
561623
562624
563- def _multikron (matrix_list , backend ) :
625+ def _multikron (matrix_list : List [ ArrayLike ] , backend : Backend ) -> ArrayLike :
564626 """Calculates Kronecker product of a list of matrices.
565627
566628 Args:
@@ -572,7 +634,9 @@ def _multikron(matrix_list, backend):
572634 return reduce (backend .kron , matrix_list )
573635
574636
575- def _build_spin_model (nqubits , matrix , condition , backend ):
637+ def _build_spin_model (
638+ nqubits : int , matrix : ArrayLike , condition : Callable , backend : Backend
639+ ) -> ArrayLike :
576640 """Helper method for building nearest-neighbor spin model Hamiltonians."""
577641 h = sum (
578642 reduce (
@@ -587,7 +651,12 @@ def _build_spin_model(nqubits, matrix, condition, backend):
587651 return h
588652
589653
590- def _OneBodyPauli (nqubits , operator , dense : bool = True , backend = None ):
654+ def _OneBodyPauli (
655+ nqubits : int ,
656+ operator : Callable ,
657+ dense : bool = True ,
658+ backend : Optional [Backend ] = None ,
659+ ) -> Hamiltonian | SymbolicHamiltonian :
591660 """Helper method for constructing non-interacting
592661 :math:`X`, :math:`Y`, and :math:`Z` Hamiltonians."""
593662 backend = _check_backend (backend )
0 commit comments