# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
from collections import deque
from functools import lru_cache
from sisl._internal import set_module
__all__ = ['Mixer', 'History', 'Metric']
@set_module("sisl.mixing")
class Mixer:
r""" Base class mixer """
def clear(self):
r""" Dummy for history mixers such that all mixers can call `clear` """
pass
@set_module("sisl.mixing")
class Metric:
r""" Perform inner products using a metric
An inner product can be defined as:
.. math::
s = \langle \mathbf a | \mathbf M | \mathbf b \rangle
where generally the metric :math:`\mathbf M = 1`.
"""
[docs] def __init__(self, metric=None):
if metric is None:
class _dummy_dot:
def __call__(self, a):
return a
self._metric = _dummy_dot()
else:
self._metric = metric
[docs] def inner(self, a, b):
r""" Perform the inner product between `a` and `b`
Parameters
----------
a, b: object
"""
try:
return a.dot(self._metric(b))
except:
return (a * self._metric(b)).sum()
__call__ = inner
@set_module("sisl.mixing")
class History:
r""" A history class for retaining a set of history elements
A history class may contain several different variables in a `collections.deque`
list allowing easy managing of the length of the history.
Attributes
----------
variables : int
number of different variables stored as a history
history_max : int or tuple of int
maximum number of history elements
Parameters
----------
history : int, optional
number of maximum history elements stored
variables : int, optional
number of different variables stored as a history.
"""
[docs] def __init__(self, history=2, variables=2):
# Create a list of queues
self._hist = [deque(maxlen=history) for i in range(variables)]
def __str__(self):
""" str of the object """
return self.__class__.__name__ + f"{{history: {self.history}/{self.history_max}, variables={self.variables}}}"
@property
@lru_cache(maxsize=1)
def variables(self):
r""" Number of different variables that can be contained """
return len(self._hist)
@property
@lru_cache(maxsize=1)
def history_max(self):
r""" Maximum number of elements stored in the history for each variable """
return self._hist[0].maxlen
@property
def history(self):
r""" Number of elements in the history """
return len(self._hist[0])
__len__ = history
[docs] def append(self, *args, variable=None):
r""" Add variables to the history
Parameters
----------
*args : tuple of object
each variable will be added to the history of the mixer
variable : int or listlike of int
specify which variables the history should be added to, note:
``len(args) == len(variable)``
"""
if variable is None:
variable = range(self.variables)
# Clarify a few things
variable = list(variable)
if len(args) != len(variable):
raise ValueError(f"{self.__class__.__name__}.append requires same length input")
for i, arg in zip(variable, args):
self._hist[i].append(arg)
[docs] def clear(self, index=None, variables=None):
r""" Clear variables to the history
Parameters
----------
index : int or array_like of int
which indices of the history we should clear
variables : int or array_like of int
specify which variables should be cleared
"""
if variables is None:
variables = range(self.variables)
variables = list(variables)
if index is None:
for v in variables:
self._hist[v].clear()
else:
index = list(index)
# We need to ensure we delete in the correct order
index.sort(reverse=True)
for v in variables:
for i in index:
del self._hist[v][i]