<commit_before>from __future__ import print_function, division

from .hamiltonian_core import ishamiltonian
from .hamiltonian_core import _check_static
from .hamiltonian_core import supported_dtypes
from .hamiltonian_core import hamiltonian

from ._make_hamiltonian import _consolidate_static

from ..basis import spin_basis_1d as _default_basis
from ..basis import isbasis as _isbasis

# need linear algebra packages
import scipy.sparse.linalg as _sla
import scipy.sparse as _sp
import numpy as _np

from scipy.sparse.linalg import LinearOperator

from six import iteritems
from six.moves import zip

__all__=["quantum_LinearOperator","isquantum_LinearOperator"]

class quantum_LinearOperator(LinearOperator):
	"""Applies a quantum operator directly onto a state, without constructing the operator matrix.

	The `quantum_LinearOperator` class uses the `basis.Op()` function to calculate the matrix vector product on the 
	fly, greatly reducing the amount of memory needed for a calculation at the cost of speed.

	This object is useful for doing large scale Lanczos calculations using the `eigsh` method.

	Notes
	-----
	The class does NOT yet support time-dependent operators. 

	Examples
	---------

	The following example shows how to construct and use `quantum_LinearOperator` objects.

	.. literalinclude:: ../../doc_examples/quantum_linearOperator-example.py
		:linenos:
		:language: python
		:lines: 7-

	"""
	def __init__(self,static_list,N=None,basis=None,diagonal=None,check_symm=True,check_herm=True,check_pcon=True,dtype=_np.complex128,**basis_args):
		"""Intializes the `quantum_LinearOperator` object.
		
		Parameters
		-----------

		static_list : list
			Contains list of objects to calculate the static part of a `quantum_LinearOperator` operator. Same as
			the `static` argument of the `quantum_operator` class. The format goes like:

			>>> static_list=[[opstr_1,[indx_11,...,indx_1m]],matrix_2,...]

		N : int, optional 
			number of sites to create the default spin basis with.
		basis : :obj:`basis`, optional
			basis object to construct quantum operator with.
		diagonal : array_like
			array containing diagonal matrix elements precalculated by other means. 
		dtype : 'type'
			Data type (e.g. numpy.float64) to construct the operator with.
		check_symm : bool, optional 
			Enable/Disable symmetry check on `static_list` and `dynamic_list`.
		check_herm : bool, optional
			Enable/Disable hermiticity check on `static_list` and `dynamic_list`.
		check_pcon : bool, optional
			Enable/Disable particle conservation check on `static_list` and `dynamic_list`.
		basis_args : dict
			Optional additional arguments to pass to the `basis` class, if not already using a `basis` object
			to create the operator.

		"""

		if type(static_list) in [list,tuple]:
			for ele in static_list:
				if not _check_static(ele):
					raise ValueError("quantum_LinearOperator only supports operator string representations.")
		else: 
			raise TypeError('expecting list/tuple of lists/tuples containing opstr and list of indx')

		if dtype not in supported_dtypes:
			raise TypeError('hamiltonian does not support type: '+str(dtype))
		else:
			self._dtype=dtype


		if N==[]:
			raise ValueError("second argument of `quantum_LinearOperator()` canNOT be an empty list.")
		elif type(N) is int and basis is None:
			self._basis = _default_basis(N,**basis_args)
		elif N is None and _isbasis(basis):
			self._basis = basis
		else:
			raise ValueError("expecting integer for N or basis object for basis.")

		self._unique_me = self.basis._unique_me
		self._transposed = False
		self._conjugated = False
		self._scale = _np.array(1.0,dtype=dtype)
		self._dtype = dtype
		self._ndim = 2
		self._shape = (self._basis.Ns,self._basis.Ns)


		if check_herm:
			self.basis.check_hermitian(static_list, [])

		if check_symm:
			self.basis.check_symm(static_list,[])

		if check_pcon:
			self.basis.check_pcon(static_list,[])

		if diagonal is not None:
			self.set_diagonal(diagonal)
		else:
			self._diagonal = None



		static_list = _consolidate_static(static_list)
		self._static_list = []
		for opstr,indx,J in static_list:
			ME,row,col = self.basis.Op(opstr,indx,J,self._dtype)
			if (row==col).all():
				if self._diagonal is None:
					self._diagonal = _np.zeros((self.Ns,),dtype=ME.real.dtype)

				if self._unique_me:
					if row.shape[0] == self.Ns:
						self._diagonal += ME.real
					else:
						self._diagonal[row] += ME[row].real
				else:
					while len(row) > 0:
						# if there are multiply matrix elements per row as there are for some
						# symmetries availible then do the indexing for unique elements then
						# delete them from the list and then repeat until all elements have been 
						# taken care of. This is less memory efficient but works well for when
						# there are a few number of matrix elements per row. 
						row_unique,args = _np.unique(row,return_index=True)

						self._diagonal[row_unique] += ME[args].real
						row = _np.delete(row,args)
						ME = _np.delete(ME,args)					
			else:
				self._static_list.append((opstr,indx,J))
				





	@property
	def shape(self):
		"""tuple: shape of linear operator."""
		return self._shape

	@property
	def basis(self):
		""":obj:`basis`: basis used to build the `hamiltonian` object. 

		Defaults to `None` if operator has  no basis (i.e. was created externally and passed as a precalculated array).
		"""
		return self._basis

	@property
	def ndim(self):
		"""int: number of dimensions, always equal to 2."""<commit_msg>fixing typo to enable example code to appear online<commit_after>from __future__ import print_function, division

from .hamiltonian_core import ishamiltonian
from .hamiltonian_core import _check_static
from .hamiltonian_core import supported_dtypes
from .hamiltonian_core import hamiltonian

from ._make_hamiltonian import _consolidate_static

from ..basis import spin_basis_1d as _default_basis
from ..basis import isbasis as _isbasis

# need linear algebra packages
import scipy.sparse.linalg as _sla
import scipy.sparse as _sp
import numpy as _np

from scipy.sparse.linalg import LinearOperator

from six import iteritems
from six.moves import zip

__all__=["quantum_LinearOperator","isquantum_LinearOperator"]

class quantum_LinearOperator(LinearOperator):
	"""Applies a quantum operator directly onto a state, without constructing the operator matrix.

	The `quantum_LinearOperator` class uses the `basis.Op()` function to calculate the matrix vector product on the 
	fly, greatly reducing the amount of memory needed for a calculation at the cost of speed.

	This object is useful for doing large scale Lanczos calculations using the `eigsh` method.

	Notes
	-----
	The class does NOT yet support time-dependent operators. 

	Examples
	---------

	The following example shows how to construct and use `quantum_LinearOperator` objects.

	.. literalinclude:: ../../doc_examples/quantum_LinearOperator-example.py
		:linenos:
		:language: python
		:lines: 7-

	"""
	def __init__(self,static_list,N=None,basis=None,diagonal=None,check_symm=True,check_herm=True,check_pcon=True,dtype=_np.complex128,**basis_args):
		"""Intializes the `quantum_LinearOperator` object.
		
		Parameters
		-----------

		static_list : list
			Contains list of objects to calculate the static part of a `quantum_LinearOperator` operator. Same as
			the `static` argument of the `quantum_operator` class. The format goes like:

			>>> static_list=[[opstr_1,[indx_11,...,indx_1m]],matrix_2,...]

		N : int, optional 
			number of sites to create the default spin basis with.
		basis : :obj:`basis`, optional
			basis object to construct quantum operator with.
		diagonal : array_like
			array containing diagonal matrix elements precalculated by other means. 
		dtype : 'type'
			Data type (e.g. numpy.float64) to construct the operator with.
		check_symm : bool, optional 
			Enable/Disable symmetry check on `static_list` and `dynamic_list`.
		check_herm : bool, optional
			Enable/Disable hermiticity check on `static_list` and `dynamic_list`.
		check_pcon : bool, optional
			Enable/Disable particle conservation check on `static_list` and `dynamic_list`.
		basis_args : dict
			Optional additional arguments to pass to the `basis` class, if not already using a `basis` object
			to create the operator.

		"""

		if type(static_list) in [list,tuple]:
			for ele in static_list:
				if not _check_static(ele):
					raise ValueError("quantum_LinearOperator only supports operator string representations.")
		else: 
			raise TypeError('expecting list/tuple of lists/tuples containing opstr and list of indx')

		if dtype not in supported_dtypes:
			raise TypeError('hamiltonian does not support type: '+str(dtype))
		else:
			self._dtype=dtype


		if N==[]:
			raise ValueError("second argument of `quantum_LinearOperator()` canNOT be an empty list.")
		elif type(N) is int and basis is None:
			self._basis = _default_basis(N,**basis_args)
		elif N is None and _isbasis(basis):
			self._basis = basis
		else:
			raise ValueError("expecting integer for N or basis object for basis.")

		self._unique_me = self.basis._unique_me
		self._transposed = False
		self._conjugated = False
		self._scale = _np.array(1.0,dtype=dtype)
		self._dtype = dtype
		self._ndim = 2
		self._shape = (self._basis.Ns,self._basis.Ns)


		if check_herm:
			self.basis.check_hermitian(static_list, [])

		if check_symm:
			self.basis.check_symm(static_list,[])

		if check_pcon:
			self.basis.check_pcon(static_list,[])

		if diagonal is not None:
			self.set_diagonal(diagonal)
		else:
			self._diagonal = None



		static_list = _consolidate_static(static_list)
		self._static_list = []
		for opstr,indx,J in static_list:
			ME,row,col = self.basis.Op(opstr,indx,J,self._dtype)
			if (row==col).all():
				if self._diagonal is None:
					self._diagonal = _np.zeros((self.Ns,),dtype=ME.real.dtype)

				if self._unique_me:
					if row.shape[0] == self.Ns:
						self._diagonal += ME.real
					else:
						self._diagonal[row] += ME[row].real
				else:
					while len(row) > 0:
						# if there are multiple matrix elements per row as there are for some
						# symmetries availible then do the indexing for unique elements then
						# delete them from the list and then repeat until all elements have been 
						# taken care of. This is less memory efficient but works well for when
						# there are a few number of matrix elements per row. 
						row_unique,args = _np.unique(row,return_index=True)

						self._diagonal[row_unique] += ME[args].real
						row = _np.delete(row,args)
						ME = _np.delete(ME,args)					
			else:
				self._static_list.append((opstr,indx,J))
		





	@property
	def shape(self):
		"""tuple: shape of linear operator."""
		return self._shape

	@property
	def basis(self):
		""":obj:`basis`: basis used to build the `hamiltonian` object. 

		Defaults to `None` if operator has  no basis (i.e. was created externally and passed as a precalculated array).
		"""
		return self._basis

	@property
	def ndim(self):
		"""int: number of dimensions, always equal to 2."""
{'fraction_non_alphanumeric': 0.09184595913939612, 'fraction_numerical': 0.003254384378954981, 'mean_word_length': 3.9103417665335107, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4256285', 'n_tokens_mistral': 3932, 'n_tokens_neox': 3320, 'n_words': 1123}