From 09a9f8f13c291c920804bbcda0aaf6a5c51d21ce Mon Sep 17 00:00:00 2001 From: fjosw Date: Fri, 21 Oct 2022 11:19:58 +0000 Subject: [PATCH] Documentation updated --- docs/pyerrors/obs.html | 1196 ++++++++++++++++++++-------------------- 1 file changed, 597 insertions(+), 599 deletions(-) diff --git a/docs/pyerrors/obs.html b/docs/pyerrors/obs.html index 3fdf2686..f84359f1 100644 --- a/docs/pyerrors/obs.html +++ b/docs/pyerrors/obs.html @@ -898,286 +898,286 @@ 699 return 'Obs[' + str(self) + ']' 700 701 def __str__(self): - 702 if self._dvalue == 0.0: - 703 return str(self.value) - 704 return _format_uncertainty(self.value, self._dvalue) - 705 - 706 def __hash__(self): - 707 hash_tuple = (np.array([self.value]).astype(np.float32).data.tobytes(),) - 708 hash_tuple += tuple([o.astype(np.float32).data.tobytes() for o in self.deltas.values()]) - 709 hash_tuple += tuple([np.array([o.errsq()]).astype(np.float32).data.tobytes() for o in self.covobs.values()]) - 710 hash_tuple += tuple([o.encode() for o in self.names]) - 711 m = hashlib.md5() - 712 [m.update(o) for o in hash_tuple] - 713 return int(m.hexdigest(), 16) & 0xFFFFFFFF - 714 - 715 # Overload comparisons - 716 def __lt__(self, other): - 717 return self.value < other - 718 - 719 def __le__(self, other): - 720 return self.value <= other - 721 - 722 def __gt__(self, other): - 723 return self.value > other - 724 - 725 def __ge__(self, other): - 726 return self.value >= other - 727 - 728 def __eq__(self, other): - 729 return (self - other).is_zero() - 730 - 731 def __ne__(self, other): - 732 return not (self - other).is_zero() - 733 - 734 # Overload math operations - 735 def __add__(self, y): - 736 if isinstance(y, Obs): - 737 return derived_observable(lambda x, **kwargs: x[0] + x[1], [self, y], man_grad=[1, 1]) - 738 else: - 739 if isinstance(y, np.ndarray): - 740 return np.array([self + o for o in y]) - 741 elif y.__class__.__name__ in ['Corr', 'CObs']: - 742 return NotImplemented - 743 else: - 744 return derived_observable(lambda x, **kwargs: x[0] + y, [self], man_grad=[1]) - 745 - 746 def __radd__(self, y): - 747 return self + y - 748 - 749 def __mul__(self, y): - 750 if isinstance(y, Obs): - 751 return derived_observable(lambda x, **kwargs: x[0] * x[1], [self, y], man_grad=[y.value, self.value]) - 752 else: - 753 if isinstance(y, np.ndarray): - 754 return np.array([self * o for o in y]) - 755 elif isinstance(y, complex): - 756 return CObs(self * y.real, self * y.imag) - 757 elif y.__class__.__name__ in ['Corr', 'CObs']: - 758 return NotImplemented - 759 else: - 760 return derived_observable(lambda x, **kwargs: x[0] * y, [self], man_grad=[y]) - 761 - 762 def __rmul__(self, y): - 763 return self * y - 764 - 765 def __sub__(self, y): - 766 if isinstance(y, Obs): - 767 return derived_observable(lambda x, **kwargs: x[0] - x[1], [self, y], man_grad=[1, -1]) - 768 else: - 769 if isinstance(y, np.ndarray): - 770 return np.array([self - o for o in y]) - 771 elif y.__class__.__name__ in ['Corr', 'CObs']: - 772 return NotImplemented - 773 else: - 774 return derived_observable(lambda x, **kwargs: x[0] - y, [self], man_grad=[1]) - 775 - 776 def __rsub__(self, y): - 777 return -1 * (self - y) - 778 - 779 def __pos__(self): - 780 return self - 781 - 782 def __neg__(self): - 783 return -1 * self - 784 - 785 def __truediv__(self, y): - 786 if isinstance(y, Obs): - 787 return derived_observable(lambda x, **kwargs: x[0] / x[1], [self, y], man_grad=[1 / y.value, - self.value / y.value ** 2]) - 788 else: - 789 if isinstance(y, np.ndarray): - 790 return np.array([self / o for o in y]) - 791 elif y.__class__.__name__ in ['Corr', 'CObs']: - 792 return NotImplemented - 793 else: - 794 return derived_observable(lambda x, **kwargs: x[0] / y, [self], man_grad=[1 / y]) - 795 - 796 def __rtruediv__(self, y): - 797 if isinstance(y, Obs): - 798 return derived_observable(lambda x, **kwargs: x[0] / x[1], [y, self], man_grad=[1 / self.value, - y.value / self.value ** 2]) - 799 else: - 800 if isinstance(y, np.ndarray): - 801 return np.array([o / self for o in y]) - 802 elif y.__class__.__name__ in ['Corr', 'CObs']: - 803 return NotImplemented - 804 else: - 805 return derived_observable(lambda x, **kwargs: y / x[0], [self], man_grad=[-y / self.value ** 2]) - 806 - 807 def __pow__(self, y): - 808 if isinstance(y, Obs): - 809 return derived_observable(lambda x: x[0] ** x[1], [self, y]) - 810 else: - 811 return derived_observable(lambda x: x[0] ** y, [self]) - 812 - 813 def __rpow__(self, y): - 814 if isinstance(y, Obs): - 815 return derived_observable(lambda x: x[0] ** x[1], [y, self]) - 816 else: - 817 return derived_observable(lambda x: y ** x[0], [self]) - 818 - 819 def __abs__(self): - 820 return derived_observable(lambda x: anp.abs(x[0]), [self]) - 821 - 822 # Overload numpy functions - 823 def sqrt(self): - 824 return derived_observable(lambda x, **kwargs: np.sqrt(x[0]), [self], man_grad=[1 / 2 / np.sqrt(self.value)]) - 825 - 826 def log(self): - 827 return derived_observable(lambda x, **kwargs: np.log(x[0]), [self], man_grad=[1 / self.value]) - 828 - 829 def exp(self): - 830 return derived_observable(lambda x, **kwargs: np.exp(x[0]), [self], man_grad=[np.exp(self.value)]) - 831 - 832 def sin(self): - 833 return derived_observable(lambda x, **kwargs: np.sin(x[0]), [self], man_grad=[np.cos(self.value)]) - 834 - 835 def cos(self): - 836 return derived_observable(lambda x, **kwargs: np.cos(x[0]), [self], man_grad=[-np.sin(self.value)]) - 837 - 838 def tan(self): - 839 return derived_observable(lambda x, **kwargs: np.tan(x[0]), [self], man_grad=[1 / np.cos(self.value) ** 2]) - 840 - 841 def arcsin(self): - 842 return derived_observable(lambda x: anp.arcsin(x[0]), [self]) - 843 - 844 def arccos(self): - 845 return derived_observable(lambda x: anp.arccos(x[0]), [self]) - 846 - 847 def arctan(self): - 848 return derived_observable(lambda x: anp.arctan(x[0]), [self]) - 849 - 850 def sinh(self): - 851 return derived_observable(lambda x, **kwargs: np.sinh(x[0]), [self], man_grad=[np.cosh(self.value)]) - 852 - 853 def cosh(self): - 854 return derived_observable(lambda x, **kwargs: np.cosh(x[0]), [self], man_grad=[np.sinh(self.value)]) - 855 - 856 def tanh(self): - 857 return derived_observable(lambda x, **kwargs: np.tanh(x[0]), [self], man_grad=[1 / np.cosh(self.value) ** 2]) - 858 - 859 def arcsinh(self): - 860 return derived_observable(lambda x: anp.arcsinh(x[0]), [self]) - 861 - 862 def arccosh(self): - 863 return derived_observable(lambda x: anp.arccosh(x[0]), [self]) - 864 - 865 def arctanh(self): - 866 return derived_observable(lambda x: anp.arctanh(x[0]), [self]) - 867 - 868 - 869class CObs: - 870 """Class for a complex valued observable.""" - 871 __slots__ = ['_real', '_imag', 'tag'] - 872 - 873 def __init__(self, real, imag=0.0): - 874 self._real = real - 875 self._imag = imag - 876 self.tag = None - 877 - 878 @property - 879 def real(self): - 880 return self._real - 881 - 882 @property - 883 def imag(self): - 884 return self._imag - 885 - 886 def gamma_method(self, **kwargs): - 887 """Executes the gamma_method for the real and the imaginary part.""" - 888 if isinstance(self.real, Obs): - 889 self.real.gamma_method(**kwargs) - 890 if isinstance(self.imag, Obs): - 891 self.imag.gamma_method(**kwargs) - 892 - 893 def is_zero(self): - 894 """Checks whether both real and imaginary part are zero within machine precision.""" - 895 return self.real == 0.0 and self.imag == 0.0 - 896 - 897 def conjugate(self): - 898 return CObs(self.real, -self.imag) - 899 - 900 def __add__(self, other): - 901 if isinstance(other, np.ndarray): - 902 return other + self - 903 elif hasattr(other, 'real') and hasattr(other, 'imag'): - 904 return CObs(self.real + other.real, - 905 self.imag + other.imag) - 906 else: - 907 return CObs(self.real + other, self.imag) - 908 - 909 def __radd__(self, y): - 910 return self + y - 911 - 912 def __sub__(self, other): - 913 if isinstance(other, np.ndarray): - 914 return -1 * (other - self) - 915 elif hasattr(other, 'real') and hasattr(other, 'imag'): - 916 return CObs(self.real - other.real, self.imag - other.imag) - 917 else: - 918 return CObs(self.real - other, self.imag) - 919 - 920 def __rsub__(self, other): - 921 return -1 * (self - other) - 922 - 923 def __mul__(self, other): - 924 if isinstance(other, np.ndarray): - 925 return other * self - 926 elif hasattr(other, 'real') and hasattr(other, 'imag'): - 927 if all(isinstance(i, Obs) for i in [self.real, self.imag, other.real, other.imag]): - 928 return CObs(derived_observable(lambda x, **kwargs: x[0] * x[1] - x[2] * x[3], - 929 [self.real, other.real, self.imag, other.imag], - 930 man_grad=[other.real.value, self.real.value, -other.imag.value, -self.imag.value]), - 931 derived_observable(lambda x, **kwargs: x[2] * x[1] + x[0] * x[3], - 932 [self.real, other.real, self.imag, other.imag], - 933 man_grad=[other.imag.value, self.imag.value, other.real.value, self.real.value])) - 934 elif getattr(other, 'imag', 0) != 0: - 935 return CObs(self.real * other.real - self.imag * other.imag, - 936 self.imag * other.real + self.real * other.imag) - 937 else: - 938 return CObs(self.real * other.real, self.imag * other.real) - 939 else: - 940 return CObs(self.real * other, self.imag * other) - 941 - 942 def __rmul__(self, other): - 943 return self * other - 944 - 945 def __truediv__(self, other): - 946 if isinstance(other, np.ndarray): - 947 return 1 / (other / self) - 948 elif hasattr(other, 'real') and hasattr(other, 'imag'): - 949 r = other.real ** 2 + other.imag ** 2 - 950 return CObs((self.real * other.real + self.imag * other.imag) / r, (self.imag * other.real - self.real * other.imag) / r) - 951 else: - 952 return CObs(self.real / other, self.imag / other) - 953 - 954 def __rtruediv__(self, other): - 955 r = self.real ** 2 + self.imag ** 2 - 956 if hasattr(other, 'real') and hasattr(other, 'imag'): - 957 return CObs((self.real * other.real + self.imag * other.imag) / r, (self.real * other.imag - self.imag * other.real) / r) - 958 else: - 959 return CObs(self.real * other / r, -self.imag * other / r) - 960 - 961 def __abs__(self): - 962 return np.sqrt(self.real**2 + self.imag**2) - 963 - 964 def __pos__(self): - 965 return self - 966 - 967 def __neg__(self): - 968 return -1 * self - 969 - 970 def __eq__(self, other): - 971 return self.real == other.real and self.imag == other.imag - 972 - 973 def __str__(self): - 974 return '(' + str(self.real) + int(self.imag >= 0.0) * '+' + str(self.imag) + 'j)' - 975 - 976 def __repr__(self): - 977 return 'CObs[' + str(self) + ']' - 978 - 979 - 980def _format_uncertainty(value, dvalue): - 981 """Creates a string of a value and its error in paranthesis notation, e.g., 13.02(45)""" + 702 return _format_uncertainty(self.value, self._dvalue) + 703 + 704 def __hash__(self): + 705 hash_tuple = (np.array([self.value]).astype(np.float32).data.tobytes(),) + 706 hash_tuple += tuple([o.astype(np.float32).data.tobytes() for o in self.deltas.values()]) + 707 hash_tuple += tuple([np.array([o.errsq()]).astype(np.float32).data.tobytes() for o in self.covobs.values()]) + 708 hash_tuple += tuple([o.encode() for o in self.names]) + 709 m = hashlib.md5() + 710 [m.update(o) for o in hash_tuple] + 711 return int(m.hexdigest(), 16) & 0xFFFFFFFF + 712 + 713 # Overload comparisons + 714 def __lt__(self, other): + 715 return self.value < other + 716 + 717 def __le__(self, other): + 718 return self.value <= other + 719 + 720 def __gt__(self, other): + 721 return self.value > other + 722 + 723 def __ge__(self, other): + 724 return self.value >= other + 725 + 726 def __eq__(self, other): + 727 return (self - other).is_zero() + 728 + 729 def __ne__(self, other): + 730 return not (self - other).is_zero() + 731 + 732 # Overload math operations + 733 def __add__(self, y): + 734 if isinstance(y, Obs): + 735 return derived_observable(lambda x, **kwargs: x[0] + x[1], [self, y], man_grad=[1, 1]) + 736 else: + 737 if isinstance(y, np.ndarray): + 738 return np.array([self + o for o in y]) + 739 elif y.__class__.__name__ in ['Corr', 'CObs']: + 740 return NotImplemented + 741 else: + 742 return derived_observable(lambda x, **kwargs: x[0] + y, [self], man_grad=[1]) + 743 + 744 def __radd__(self, y): + 745 return self + y + 746 + 747 def __mul__(self, y): + 748 if isinstance(y, Obs): + 749 return derived_observable(lambda x, **kwargs: x[0] * x[1], [self, y], man_grad=[y.value, self.value]) + 750 else: + 751 if isinstance(y, np.ndarray): + 752 return np.array([self * o for o in y]) + 753 elif isinstance(y, complex): + 754 return CObs(self * y.real, self * y.imag) + 755 elif y.__class__.__name__ in ['Corr', 'CObs']: + 756 return NotImplemented + 757 else: + 758 return derived_observable(lambda x, **kwargs: x[0] * y, [self], man_grad=[y]) + 759 + 760 def __rmul__(self, y): + 761 return self * y + 762 + 763 def __sub__(self, y): + 764 if isinstance(y, Obs): + 765 return derived_observable(lambda x, **kwargs: x[0] - x[1], [self, y], man_grad=[1, -1]) + 766 else: + 767 if isinstance(y, np.ndarray): + 768 return np.array([self - o for o in y]) + 769 elif y.__class__.__name__ in ['Corr', 'CObs']: + 770 return NotImplemented + 771 else: + 772 return derived_observable(lambda x, **kwargs: x[0] - y, [self], man_grad=[1]) + 773 + 774 def __rsub__(self, y): + 775 return -1 * (self - y) + 776 + 777 def __pos__(self): + 778 return self + 779 + 780 def __neg__(self): + 781 return -1 * self + 782 + 783 def __truediv__(self, y): + 784 if isinstance(y, Obs): + 785 return derived_observable(lambda x, **kwargs: x[0] / x[1], [self, y], man_grad=[1 / y.value, - self.value / y.value ** 2]) + 786 else: + 787 if isinstance(y, np.ndarray): + 788 return np.array([self / o for o in y]) + 789 elif y.__class__.__name__ in ['Corr', 'CObs']: + 790 return NotImplemented + 791 else: + 792 return derived_observable(lambda x, **kwargs: x[0] / y, [self], man_grad=[1 / y]) + 793 + 794 def __rtruediv__(self, y): + 795 if isinstance(y, Obs): + 796 return derived_observable(lambda x, **kwargs: x[0] / x[1], [y, self], man_grad=[1 / self.value, - y.value / self.value ** 2]) + 797 else: + 798 if isinstance(y, np.ndarray): + 799 return np.array([o / self for o in y]) + 800 elif y.__class__.__name__ in ['Corr', 'CObs']: + 801 return NotImplemented + 802 else: + 803 return derived_observable(lambda x, **kwargs: y / x[0], [self], man_grad=[-y / self.value ** 2]) + 804 + 805 def __pow__(self, y): + 806 if isinstance(y, Obs): + 807 return derived_observable(lambda x: x[0] ** x[1], [self, y]) + 808 else: + 809 return derived_observable(lambda x: x[0] ** y, [self]) + 810 + 811 def __rpow__(self, y): + 812 if isinstance(y, Obs): + 813 return derived_observable(lambda x: x[0] ** x[1], [y, self]) + 814 else: + 815 return derived_observable(lambda x: y ** x[0], [self]) + 816 + 817 def __abs__(self): + 818 return derived_observable(lambda x: anp.abs(x[0]), [self]) + 819 + 820 # Overload numpy functions + 821 def sqrt(self): + 822 return derived_observable(lambda x, **kwargs: np.sqrt(x[0]), [self], man_grad=[1 / 2 / np.sqrt(self.value)]) + 823 + 824 def log(self): + 825 return derived_observable(lambda x, **kwargs: np.log(x[0]), [self], man_grad=[1 / self.value]) + 826 + 827 def exp(self): + 828 return derived_observable(lambda x, **kwargs: np.exp(x[0]), [self], man_grad=[np.exp(self.value)]) + 829 + 830 def sin(self): + 831 return derived_observable(lambda x, **kwargs: np.sin(x[0]), [self], man_grad=[np.cos(self.value)]) + 832 + 833 def cos(self): + 834 return derived_observable(lambda x, **kwargs: np.cos(x[0]), [self], man_grad=[-np.sin(self.value)]) + 835 + 836 def tan(self): + 837 return derived_observable(lambda x, **kwargs: np.tan(x[0]), [self], man_grad=[1 / np.cos(self.value) ** 2]) + 838 + 839 def arcsin(self): + 840 return derived_observable(lambda x: anp.arcsin(x[0]), [self]) + 841 + 842 def arccos(self): + 843 return derived_observable(lambda x: anp.arccos(x[0]), [self]) + 844 + 845 def arctan(self): + 846 return derived_observable(lambda x: anp.arctan(x[0]), [self]) + 847 + 848 def sinh(self): + 849 return derived_observable(lambda x, **kwargs: np.sinh(x[0]), [self], man_grad=[np.cosh(self.value)]) + 850 + 851 def cosh(self): + 852 return derived_observable(lambda x, **kwargs: np.cosh(x[0]), [self], man_grad=[np.sinh(self.value)]) + 853 + 854 def tanh(self): + 855 return derived_observable(lambda x, **kwargs: np.tanh(x[0]), [self], man_grad=[1 / np.cosh(self.value) ** 2]) + 856 + 857 def arcsinh(self): + 858 return derived_observable(lambda x: anp.arcsinh(x[0]), [self]) + 859 + 860 def arccosh(self): + 861 return derived_observable(lambda x: anp.arccosh(x[0]), [self]) + 862 + 863 def arctanh(self): + 864 return derived_observable(lambda x: anp.arctanh(x[0]), [self]) + 865 + 866 + 867class CObs: + 868 """Class for a complex valued observable.""" + 869 __slots__ = ['_real', '_imag', 'tag'] + 870 + 871 def __init__(self, real, imag=0.0): + 872 self._real = real + 873 self._imag = imag + 874 self.tag = None + 875 + 876 @property + 877 def real(self): + 878 return self._real + 879 + 880 @property + 881 def imag(self): + 882 return self._imag + 883 + 884 def gamma_method(self, **kwargs): + 885 """Executes the gamma_method for the real and the imaginary part.""" + 886 if isinstance(self.real, Obs): + 887 self.real.gamma_method(**kwargs) + 888 if isinstance(self.imag, Obs): + 889 self.imag.gamma_method(**kwargs) + 890 + 891 def is_zero(self): + 892 """Checks whether both real and imaginary part are zero within machine precision.""" + 893 return self.real == 0.0 and self.imag == 0.0 + 894 + 895 def conjugate(self): + 896 return CObs(self.real, -self.imag) + 897 + 898 def __add__(self, other): + 899 if isinstance(other, np.ndarray): + 900 return other + self + 901 elif hasattr(other, 'real') and hasattr(other, 'imag'): + 902 return CObs(self.real + other.real, + 903 self.imag + other.imag) + 904 else: + 905 return CObs(self.real + other, self.imag) + 906 + 907 def __radd__(self, y): + 908 return self + y + 909 + 910 def __sub__(self, other): + 911 if isinstance(other, np.ndarray): + 912 return -1 * (other - self) + 913 elif hasattr(other, 'real') and hasattr(other, 'imag'): + 914 return CObs(self.real - other.real, self.imag - other.imag) + 915 else: + 916 return CObs(self.real - other, self.imag) + 917 + 918 def __rsub__(self, other): + 919 return -1 * (self - other) + 920 + 921 def __mul__(self, other): + 922 if isinstance(other, np.ndarray): + 923 return other * self + 924 elif hasattr(other, 'real') and hasattr(other, 'imag'): + 925 if all(isinstance(i, Obs) for i in [self.real, self.imag, other.real, other.imag]): + 926 return CObs(derived_observable(lambda x, **kwargs: x[0] * x[1] - x[2] * x[3], + 927 [self.real, other.real, self.imag, other.imag], + 928 man_grad=[other.real.value, self.real.value, -other.imag.value, -self.imag.value]), + 929 derived_observable(lambda x, **kwargs: x[2] * x[1] + x[0] * x[3], + 930 [self.real, other.real, self.imag, other.imag], + 931 man_grad=[other.imag.value, self.imag.value, other.real.value, self.real.value])) + 932 elif getattr(other, 'imag', 0) != 0: + 933 return CObs(self.real * other.real - self.imag * other.imag, + 934 self.imag * other.real + self.real * other.imag) + 935 else: + 936 return CObs(self.real * other.real, self.imag * other.real) + 937 else: + 938 return CObs(self.real * other, self.imag * other) + 939 + 940 def __rmul__(self, other): + 941 return self * other + 942 + 943 def __truediv__(self, other): + 944 if isinstance(other, np.ndarray): + 945 return 1 / (other / self) + 946 elif hasattr(other, 'real') and hasattr(other, 'imag'): + 947 r = other.real ** 2 + other.imag ** 2 + 948 return CObs((self.real * other.real + self.imag * other.imag) / r, (self.imag * other.real - self.real * other.imag) / r) + 949 else: + 950 return CObs(self.real / other, self.imag / other) + 951 + 952 def __rtruediv__(self, other): + 953 r = self.real ** 2 + self.imag ** 2 + 954 if hasattr(other, 'real') and hasattr(other, 'imag'): + 955 return CObs((self.real * other.real + self.imag * other.imag) / r, (self.real * other.imag - self.imag * other.real) / r) + 956 else: + 957 return CObs(self.real * other / r, -self.imag * other / r) + 958 + 959 def __abs__(self): + 960 return np.sqrt(self.real**2 + self.imag**2) + 961 + 962 def __pos__(self): + 963 return self + 964 + 965 def __neg__(self): + 966 return -1 * self + 967 + 968 def __eq__(self, other): + 969 return self.real == other.real and self.imag == other.imag + 970 + 971 def __str__(self): + 972 return '(' + str(self.real) + int(self.imag >= 0.0) * '+' + str(self.imag) + 'j)' + 973 + 974 def __repr__(self): + 975 return 'CObs[' + str(self) + ']' + 976 + 977 + 978def _format_uncertainty(value, dvalue): + 979 """Creates a string of a value and its error in paranthesis notation, e.g., 13.02(45)""" + 980 if dvalue == 0.0: + 981 return str(value) 982 fexp = np.floor(np.log10(dvalue)) 983 if fexp < 0.0: 984 return '{:{form}}({:2.0f})'.format(value, dvalue * 10 ** (-fexp + 1), form='.' + str(-int(fexp) + 1) + 'f') @@ -2563,171 +2563,169 @@ 700 return 'Obs[' + str(self) + ']' 701 702 def __str__(self): -703 if self._dvalue == 0.0: -704 return str(self.value) -705 return _format_uncertainty(self.value, self._dvalue) -706 -707 def __hash__(self): -708 hash_tuple = (np.array([self.value]).astype(np.float32).data.tobytes(),) -709 hash_tuple += tuple([o.astype(np.float32).data.tobytes() for o in self.deltas.values()]) -710 hash_tuple += tuple([np.array([o.errsq()]).astype(np.float32).data.tobytes() for o in self.covobs.values()]) -711 hash_tuple += tuple([o.encode() for o in self.names]) -712 m = hashlib.md5() -713 [m.update(o) for o in hash_tuple] -714 return int(m.hexdigest(), 16) & 0xFFFFFFFF -715 -716 # Overload comparisons -717 def __lt__(self, other): -718 return self.value < other -719 -720 def __le__(self, other): -721 return self.value <= other -722 -723 def __gt__(self, other): -724 return self.value > other -725 -726 def __ge__(self, other): -727 return self.value >= other -728 -729 def __eq__(self, other): -730 return (self - other).is_zero() -731 -732 def __ne__(self, other): -733 return not (self - other).is_zero() -734 -735 # Overload math operations -736 def __add__(self, y): -737 if isinstance(y, Obs): -738 return derived_observable(lambda x, **kwargs: x[0] + x[1], [self, y], man_grad=[1, 1]) -739 else: -740 if isinstance(y, np.ndarray): -741 return np.array([self + o for o in y]) -742 elif y.__class__.__name__ in ['Corr', 'CObs']: -743 return NotImplemented -744 else: -745 return derived_observable(lambda x, **kwargs: x[0] + y, [self], man_grad=[1]) -746 -747 def __radd__(self, y): -748 return self + y -749 -750 def __mul__(self, y): -751 if isinstance(y, Obs): -752 return derived_observable(lambda x, **kwargs: x[0] * x[1], [self, y], man_grad=[y.value, self.value]) -753 else: -754 if isinstance(y, np.ndarray): -755 return np.array([self * o for o in y]) -756 elif isinstance(y, complex): -757 return CObs(self * y.real, self * y.imag) -758 elif y.__class__.__name__ in ['Corr', 'CObs']: -759 return NotImplemented -760 else: -761 return derived_observable(lambda x, **kwargs: x[0] * y, [self], man_grad=[y]) -762 -763 def __rmul__(self, y): -764 return self * y -765 -766 def __sub__(self, y): -767 if isinstance(y, Obs): -768 return derived_observable(lambda x, **kwargs: x[0] - x[1], [self, y], man_grad=[1, -1]) -769 else: -770 if isinstance(y, np.ndarray): -771 return np.array([self - o for o in y]) -772 elif y.__class__.__name__ in ['Corr', 'CObs']: -773 return NotImplemented -774 else: -775 return derived_observable(lambda x, **kwargs: x[0] - y, [self], man_grad=[1]) -776 -777 def __rsub__(self, y): -778 return -1 * (self - y) -779 -780 def __pos__(self): -781 return self -782 -783 def __neg__(self): -784 return -1 * self -785 -786 def __truediv__(self, y): -787 if isinstance(y, Obs): -788 return derived_observable(lambda x, **kwargs: x[0] / x[1], [self, y], man_grad=[1 / y.value, - self.value / y.value ** 2]) -789 else: -790 if isinstance(y, np.ndarray): -791 return np.array([self / o for o in y]) -792 elif y.__class__.__name__ in ['Corr', 'CObs']: -793 return NotImplemented -794 else: -795 return derived_observable(lambda x, **kwargs: x[0] / y, [self], man_grad=[1 / y]) -796 -797 def __rtruediv__(self, y): -798 if isinstance(y, Obs): -799 return derived_observable(lambda x, **kwargs: x[0] / x[1], [y, self], man_grad=[1 / self.value, - y.value / self.value ** 2]) -800 else: -801 if isinstance(y, np.ndarray): -802 return np.array([o / self for o in y]) -803 elif y.__class__.__name__ in ['Corr', 'CObs']: -804 return NotImplemented -805 else: -806 return derived_observable(lambda x, **kwargs: y / x[0], [self], man_grad=[-y / self.value ** 2]) -807 -808 def __pow__(self, y): -809 if isinstance(y, Obs): -810 return derived_observable(lambda x: x[0] ** x[1], [self, y]) -811 else: -812 return derived_observable(lambda x: x[0] ** y, [self]) -813 -814 def __rpow__(self, y): -815 if isinstance(y, Obs): -816 return derived_observable(lambda x: x[0] ** x[1], [y, self]) -817 else: -818 return derived_observable(lambda x: y ** x[0], [self]) -819 -820 def __abs__(self): -821 return derived_observable(lambda x: anp.abs(x[0]), [self]) -822 -823 # Overload numpy functions -824 def sqrt(self): -825 return derived_observable(lambda x, **kwargs: np.sqrt(x[0]), [self], man_grad=[1 / 2 / np.sqrt(self.value)]) -826 -827 def log(self): -828 return derived_observable(lambda x, **kwargs: np.log(x[0]), [self], man_grad=[1 / self.value]) -829 -830 def exp(self): -831 return derived_observable(lambda x, **kwargs: np.exp(x[0]), [self], man_grad=[np.exp(self.value)]) -832 -833 def sin(self): -834 return derived_observable(lambda x, **kwargs: np.sin(x[0]), [self], man_grad=[np.cos(self.value)]) -835 -836 def cos(self): -837 return derived_observable(lambda x, **kwargs: np.cos(x[0]), [self], man_grad=[-np.sin(self.value)]) -838 -839 def tan(self): -840 return derived_observable(lambda x, **kwargs: np.tan(x[0]), [self], man_grad=[1 / np.cos(self.value) ** 2]) -841 -842 def arcsin(self): -843 return derived_observable(lambda x: anp.arcsin(x[0]), [self]) -844 -845 def arccos(self): -846 return derived_observable(lambda x: anp.arccos(x[0]), [self]) -847 -848 def arctan(self): -849 return derived_observable(lambda x: anp.arctan(x[0]), [self]) -850 -851 def sinh(self): -852 return derived_observable(lambda x, **kwargs: np.sinh(x[0]), [self], man_grad=[np.cosh(self.value)]) -853 -854 def cosh(self): -855 return derived_observable(lambda x, **kwargs: np.cosh(x[0]), [self], man_grad=[np.sinh(self.value)]) -856 -857 def tanh(self): -858 return derived_observable(lambda x, **kwargs: np.tanh(x[0]), [self], man_grad=[1 / np.cosh(self.value) ** 2]) -859 -860 def arcsinh(self): -861 return derived_observable(lambda x: anp.arcsinh(x[0]), [self]) -862 -863 def arccosh(self): -864 return derived_observable(lambda x: anp.arccosh(x[0]), [self]) -865 -866 def arctanh(self): -867 return derived_observable(lambda x: anp.arctanh(x[0]), [self]) +703 return _format_uncertainty(self.value, self._dvalue) +704 +705 def __hash__(self): +706 hash_tuple = (np.array([self.value]).astype(np.float32).data.tobytes(),) +707 hash_tuple += tuple([o.astype(np.float32).data.tobytes() for o in self.deltas.values()]) +708 hash_tuple += tuple([np.array([o.errsq()]).astype(np.float32).data.tobytes() for o in self.covobs.values()]) +709 hash_tuple += tuple([o.encode() for o in self.names]) +710 m = hashlib.md5() +711 [m.update(o) for o in hash_tuple] +712 return int(m.hexdigest(), 16) & 0xFFFFFFFF +713 +714 # Overload comparisons +715 def __lt__(self, other): +716 return self.value < other +717 +718 def __le__(self, other): +719 return self.value <= other +720 +721 def __gt__(self, other): +722 return self.value > other +723 +724 def __ge__(self, other): +725 return self.value >= other +726 +727 def __eq__(self, other): +728 return (self - other).is_zero() +729 +730 def __ne__(self, other): +731 return not (self - other).is_zero() +732 +733 # Overload math operations +734 def __add__(self, y): +735 if isinstance(y, Obs): +736 return derived_observable(lambda x, **kwargs: x[0] + x[1], [self, y], man_grad=[1, 1]) +737 else: +738 if isinstance(y, np.ndarray): +739 return np.array([self + o for o in y]) +740 elif y.__class__.__name__ in ['Corr', 'CObs']: +741 return NotImplemented +742 else: +743 return derived_observable(lambda x, **kwargs: x[0] + y, [self], man_grad=[1]) +744 +745 def __radd__(self, y): +746 return self + y +747 +748 def __mul__(self, y): +749 if isinstance(y, Obs): +750 return derived_observable(lambda x, **kwargs: x[0] * x[1], [self, y], man_grad=[y.value, self.value]) +751 else: +752 if isinstance(y, np.ndarray): +753 return np.array([self * o for o in y]) +754 elif isinstance(y, complex): +755 return CObs(self * y.real, self * y.imag) +756 elif y.__class__.__name__ in ['Corr', 'CObs']: +757 return NotImplemented +758 else: +759 return derived_observable(lambda x, **kwargs: x[0] * y, [self], man_grad=[y]) +760 +761 def __rmul__(self, y): +762 return self * y +763 +764 def __sub__(self, y): +765 if isinstance(y, Obs): +766 return derived_observable(lambda x, **kwargs: x[0] - x[1], [self, y], man_grad=[1, -1]) +767 else: +768 if isinstance(y, np.ndarray): +769 return np.array([self - o for o in y]) +770 elif y.__class__.__name__ in ['Corr', 'CObs']: +771 return NotImplemented +772 else: +773 return derived_observable(lambda x, **kwargs: x[0] - y, [self], man_grad=[1]) +774 +775 def __rsub__(self, y): +776 return -1 * (self - y) +777 +778 def __pos__(self): +779 return self +780 +781 def __neg__(self): +782 return -1 * self +783 +784 def __truediv__(self, y): +785 if isinstance(y, Obs): +786 return derived_observable(lambda x, **kwargs: x[0] / x[1], [self, y], man_grad=[1 / y.value, - self.value / y.value ** 2]) +787 else: +788 if isinstance(y, np.ndarray): +789 return np.array([self / o for o in y]) +790 elif y.__class__.__name__ in ['Corr', 'CObs']: +791 return NotImplemented +792 else: +793 return derived_observable(lambda x, **kwargs: x[0] / y, [self], man_grad=[1 / y]) +794 +795 def __rtruediv__(self, y): +796 if isinstance(y, Obs): +797 return derived_observable(lambda x, **kwargs: x[0] / x[1], [y, self], man_grad=[1 / self.value, - y.value / self.value ** 2]) +798 else: +799 if isinstance(y, np.ndarray): +800 return np.array([o / self for o in y]) +801 elif y.__class__.__name__ in ['Corr', 'CObs']: +802 return NotImplemented +803 else: +804 return derived_observable(lambda x, **kwargs: y / x[0], [self], man_grad=[-y / self.value ** 2]) +805 +806 def __pow__(self, y): +807 if isinstance(y, Obs): +808 return derived_observable(lambda x: x[0] ** x[1], [self, y]) +809 else: +810 return derived_observable(lambda x: x[0] ** y, [self]) +811 +812 def __rpow__(self, y): +813 if isinstance(y, Obs): +814 return derived_observable(lambda x: x[0] ** x[1], [y, self]) +815 else: +816 return derived_observable(lambda x: y ** x[0], [self]) +817 +818 def __abs__(self): +819 return derived_observable(lambda x: anp.abs(x[0]), [self]) +820 +821 # Overload numpy functions +822 def sqrt(self): +823 return derived_observable(lambda x, **kwargs: np.sqrt(x[0]), [self], man_grad=[1 / 2 / np.sqrt(self.value)]) +824 +825 def log(self): +826 return derived_observable(lambda x, **kwargs: np.log(x[0]), [self], man_grad=[1 / self.value]) +827 +828 def exp(self): +829 return derived_observable(lambda x, **kwargs: np.exp(x[0]), [self], man_grad=[np.exp(self.value)]) +830 +831 def sin(self): +832 return derived_observable(lambda x, **kwargs: np.sin(x[0]), [self], man_grad=[np.cos(self.value)]) +833 +834 def cos(self): +835 return derived_observable(lambda x, **kwargs: np.cos(x[0]), [self], man_grad=[-np.sin(self.value)]) +836 +837 def tan(self): +838 return derived_observable(lambda x, **kwargs: np.tan(x[0]), [self], man_grad=[1 / np.cos(self.value) ** 2]) +839 +840 def arcsin(self): +841 return derived_observable(lambda x: anp.arcsin(x[0]), [self]) +842 +843 def arccos(self): +844 return derived_observable(lambda x: anp.arccos(x[0]), [self]) +845 +846 def arctan(self): +847 return derived_observable(lambda x: anp.arctan(x[0]), [self]) +848 +849 def sinh(self): +850 return derived_observable(lambda x, **kwargs: np.sinh(x[0]), [self], man_grad=[np.cosh(self.value)]) +851 +852 def cosh(self): +853 return derived_observable(lambda x, **kwargs: np.cosh(x[0]), [self], man_grad=[np.sinh(self.value)]) +854 +855 def tanh(self): +856 return derived_observable(lambda x, **kwargs: np.tanh(x[0]), [self], man_grad=[1 / np.cosh(self.value) ** 2]) +857 +858 def arcsinh(self): +859 return derived_observable(lambda x: anp.arcsinh(x[0]), [self]) +860 +861 def arccosh(self): +862 return derived_observable(lambda x: anp.arccosh(x[0]), [self]) +863 +864 def arctanh(self): +865 return derived_observable(lambda x: anp.arctanh(x[0]), [self]) @@ -3695,8 +3693,8 @@ should agree with samples from a full jackknife analysis up to O(1/N). -
824    def sqrt(self):
-825        return derived_observable(lambda x, **kwargs: np.sqrt(x[0]), [self], man_grad=[1 / 2 / np.sqrt(self.value)])
+            
822    def sqrt(self):
+823        return derived_observable(lambda x, **kwargs: np.sqrt(x[0]), [self], man_grad=[1 / 2 / np.sqrt(self.value)])
 
@@ -3714,8 +3712,8 @@ should agree with samples from a full jackknife analysis up to O(1/N).
-
827    def log(self):
-828        return derived_observable(lambda x, **kwargs: np.log(x[0]), [self], man_grad=[1 / self.value])
+            
825    def log(self):
+826        return derived_observable(lambda x, **kwargs: np.log(x[0]), [self], man_grad=[1 / self.value])
 
@@ -3733,8 +3731,8 @@ should agree with samples from a full jackknife analysis up to O(1/N).
-
830    def exp(self):
-831        return derived_observable(lambda x, **kwargs: np.exp(x[0]), [self], man_grad=[np.exp(self.value)])
+            
828    def exp(self):
+829        return derived_observable(lambda x, **kwargs: np.exp(x[0]), [self], man_grad=[np.exp(self.value)])
 
@@ -3752,8 +3750,8 @@ should agree with samples from a full jackknife analysis up to O(1/N).
-
833    def sin(self):
-834        return derived_observable(lambda x, **kwargs: np.sin(x[0]), [self], man_grad=[np.cos(self.value)])
+            
831    def sin(self):
+832        return derived_observable(lambda x, **kwargs: np.sin(x[0]), [self], man_grad=[np.cos(self.value)])
 
@@ -3771,8 +3769,8 @@ should agree with samples from a full jackknife analysis up to O(1/N).
-
836    def cos(self):
-837        return derived_observable(lambda x, **kwargs: np.cos(x[0]), [self], man_grad=[-np.sin(self.value)])
+            
834    def cos(self):
+835        return derived_observable(lambda x, **kwargs: np.cos(x[0]), [self], man_grad=[-np.sin(self.value)])
 
@@ -3790,8 +3788,8 @@ should agree with samples from a full jackknife analysis up to O(1/N).
-
839    def tan(self):
-840        return derived_observable(lambda x, **kwargs: np.tan(x[0]), [self], man_grad=[1 / np.cos(self.value) ** 2])
+            
837    def tan(self):
+838        return derived_observable(lambda x, **kwargs: np.tan(x[0]), [self], man_grad=[1 / np.cos(self.value) ** 2])
 
@@ -3809,8 +3807,8 @@ should agree with samples from a full jackknife analysis up to O(1/N).
-
842    def arcsin(self):
-843        return derived_observable(lambda x: anp.arcsin(x[0]), [self])
+            
840    def arcsin(self):
+841        return derived_observable(lambda x: anp.arcsin(x[0]), [self])
 
@@ -3828,8 +3826,8 @@ should agree with samples from a full jackknife analysis up to O(1/N).
-
845    def arccos(self):
-846        return derived_observable(lambda x: anp.arccos(x[0]), [self])
+            
843    def arccos(self):
+844        return derived_observable(lambda x: anp.arccos(x[0]), [self])
 
@@ -3847,8 +3845,8 @@ should agree with samples from a full jackknife analysis up to O(1/N).
-
848    def arctan(self):
-849        return derived_observable(lambda x: anp.arctan(x[0]), [self])
+            
846    def arctan(self):
+847        return derived_observable(lambda x: anp.arctan(x[0]), [self])
 
@@ -3866,8 +3864,8 @@ should agree with samples from a full jackknife analysis up to O(1/N).
-
851    def sinh(self):
-852        return derived_observable(lambda x, **kwargs: np.sinh(x[0]), [self], man_grad=[np.cosh(self.value)])
+            
849    def sinh(self):
+850        return derived_observable(lambda x, **kwargs: np.sinh(x[0]), [self], man_grad=[np.cosh(self.value)])
 
@@ -3885,8 +3883,8 @@ should agree with samples from a full jackknife analysis up to O(1/N).
-
854    def cosh(self):
-855        return derived_observable(lambda x, **kwargs: np.cosh(x[0]), [self], man_grad=[np.sinh(self.value)])
+            
852    def cosh(self):
+853        return derived_observable(lambda x, **kwargs: np.cosh(x[0]), [self], man_grad=[np.sinh(self.value)])
 
@@ -3904,8 +3902,8 @@ should agree with samples from a full jackknife analysis up to O(1/N).
-
857    def tanh(self):
-858        return derived_observable(lambda x, **kwargs: np.tanh(x[0]), [self], man_grad=[1 / np.cosh(self.value) ** 2])
+            
855    def tanh(self):
+856        return derived_observable(lambda x, **kwargs: np.tanh(x[0]), [self], man_grad=[1 / np.cosh(self.value) ** 2])
 
@@ -3923,8 +3921,8 @@ should agree with samples from a full jackknife analysis up to O(1/N).
-
860    def arcsinh(self):
-861        return derived_observable(lambda x: anp.arcsinh(x[0]), [self])
+            
858    def arcsinh(self):
+859        return derived_observable(lambda x: anp.arcsinh(x[0]), [self])
 
@@ -3942,8 +3940,8 @@ should agree with samples from a full jackknife analysis up to O(1/N).
-
863    def arccosh(self):
-864        return derived_observable(lambda x: anp.arccosh(x[0]), [self])
+            
861    def arccosh(self):
+862        return derived_observable(lambda x: anp.arccosh(x[0]), [self])
 
@@ -3961,8 +3959,8 @@ should agree with samples from a full jackknife analysis up to O(1/N).
-
866    def arctanh(self):
-867        return derived_observable(lambda x: anp.arctanh(x[0]), [self])
+            
864    def arctanh(self):
+865        return derived_observable(lambda x: anp.arctanh(x[0]), [self])
 
@@ -3981,115 +3979,115 @@ should agree with samples from a full jackknife analysis up to O(1/N).
-
870class CObs:
-871    """Class for a complex valued observable."""
-872    __slots__ = ['_real', '_imag', 'tag']
-873
-874    def __init__(self, real, imag=0.0):
-875        self._real = real
-876        self._imag = imag
-877        self.tag = None
-878
-879    @property
-880    def real(self):
-881        return self._real
-882
-883    @property
-884    def imag(self):
-885        return self._imag
-886
-887    def gamma_method(self, **kwargs):
-888        """Executes the gamma_method for the real and the imaginary part."""
-889        if isinstance(self.real, Obs):
-890            self.real.gamma_method(**kwargs)
-891        if isinstance(self.imag, Obs):
-892            self.imag.gamma_method(**kwargs)
-893
-894    def is_zero(self):
-895        """Checks whether both real and imaginary part are zero within machine precision."""
-896        return self.real == 0.0 and self.imag == 0.0
-897
-898    def conjugate(self):
-899        return CObs(self.real, -self.imag)
-900
-901    def __add__(self, other):
-902        if isinstance(other, np.ndarray):
-903            return other + self
-904        elif hasattr(other, 'real') and hasattr(other, 'imag'):
-905            return CObs(self.real + other.real,
-906                        self.imag + other.imag)
-907        else:
-908            return CObs(self.real + other, self.imag)
-909
-910    def __radd__(self, y):
-911        return self + y
-912
-913    def __sub__(self, other):
-914        if isinstance(other, np.ndarray):
-915            return -1 * (other - self)
-916        elif hasattr(other, 'real') and hasattr(other, 'imag'):
-917            return CObs(self.real - other.real, self.imag - other.imag)
-918        else:
-919            return CObs(self.real - other, self.imag)
-920
-921    def __rsub__(self, other):
-922        return -1 * (self - other)
-923
-924    def __mul__(self, other):
-925        if isinstance(other, np.ndarray):
-926            return other * self
-927        elif hasattr(other, 'real') and hasattr(other, 'imag'):
-928            if all(isinstance(i, Obs) for i in [self.real, self.imag, other.real, other.imag]):
-929                return CObs(derived_observable(lambda x, **kwargs: x[0] * x[1] - x[2] * x[3],
-930                                               [self.real, other.real, self.imag, other.imag],
-931                                               man_grad=[other.real.value, self.real.value, -other.imag.value, -self.imag.value]),
-932                            derived_observable(lambda x, **kwargs: x[2] * x[1] + x[0] * x[3],
-933                                               [self.real, other.real, self.imag, other.imag],
-934                                               man_grad=[other.imag.value, self.imag.value, other.real.value, self.real.value]))
-935            elif getattr(other, 'imag', 0) != 0:
-936                return CObs(self.real * other.real - self.imag * other.imag,
-937                            self.imag * other.real + self.real * other.imag)
-938            else:
-939                return CObs(self.real * other.real, self.imag * other.real)
-940        else:
-941            return CObs(self.real * other, self.imag * other)
-942
-943    def __rmul__(self, other):
-944        return self * other
-945
-946    def __truediv__(self, other):
-947        if isinstance(other, np.ndarray):
-948            return 1 / (other / self)
-949        elif hasattr(other, 'real') and hasattr(other, 'imag'):
-950            r = other.real ** 2 + other.imag ** 2
-951            return CObs((self.real * other.real + self.imag * other.imag) / r, (self.imag * other.real - self.real * other.imag) / r)
-952        else:
-953            return CObs(self.real / other, self.imag / other)
-954
-955    def __rtruediv__(self, other):
-956        r = self.real ** 2 + self.imag ** 2
-957        if hasattr(other, 'real') and hasattr(other, 'imag'):
-958            return CObs((self.real * other.real + self.imag * other.imag) / r, (self.real * other.imag - self.imag * other.real) / r)
-959        else:
-960            return CObs(self.real * other / r, -self.imag * other / r)
-961
-962    def __abs__(self):
-963        return np.sqrt(self.real**2 + self.imag**2)
-964
-965    def __pos__(self):
-966        return self
-967
-968    def __neg__(self):
-969        return -1 * self
-970
-971    def __eq__(self, other):
-972        return self.real == other.real and self.imag == other.imag
-973
-974    def __str__(self):
-975        return '(' + str(self.real) + int(self.imag >= 0.0) * '+' + str(self.imag) + 'j)'
-976
-977    def __repr__(self):
-978        return 'CObs[' + str(self) + ']'
+            
868class CObs:
+869    """Class for a complex valued observable."""
+870    __slots__ = ['_real', '_imag', 'tag']
+871
+872    def __init__(self, real, imag=0.0):
+873        self._real = real
+874        self._imag = imag
+875        self.tag = None
+876
+877    @property
+878    def real(self):
+879        return self._real
+880
+881    @property
+882    def imag(self):
+883        return self._imag
+884
+885    def gamma_method(self, **kwargs):
+886        """Executes the gamma_method for the real and the imaginary part."""
+887        if isinstance(self.real, Obs):
+888            self.real.gamma_method(**kwargs)
+889        if isinstance(self.imag, Obs):
+890            self.imag.gamma_method(**kwargs)
+891
+892    def is_zero(self):
+893        """Checks whether both real and imaginary part are zero within machine precision."""
+894        return self.real == 0.0 and self.imag == 0.0
+895
+896    def conjugate(self):
+897        return CObs(self.real, -self.imag)
+898
+899    def __add__(self, other):
+900        if isinstance(other, np.ndarray):
+901            return other + self
+902        elif hasattr(other, 'real') and hasattr(other, 'imag'):
+903            return CObs(self.real + other.real,
+904                        self.imag + other.imag)
+905        else:
+906            return CObs(self.real + other, self.imag)
+907
+908    def __radd__(self, y):
+909        return self + y
+910
+911    def __sub__(self, other):
+912        if isinstance(other, np.ndarray):
+913            return -1 * (other - self)
+914        elif hasattr(other, 'real') and hasattr(other, 'imag'):
+915            return CObs(self.real - other.real, self.imag - other.imag)
+916        else:
+917            return CObs(self.real - other, self.imag)
+918
+919    def __rsub__(self, other):
+920        return -1 * (self - other)
+921
+922    def __mul__(self, other):
+923        if isinstance(other, np.ndarray):
+924            return other * self
+925        elif hasattr(other, 'real') and hasattr(other, 'imag'):
+926            if all(isinstance(i, Obs) for i in [self.real, self.imag, other.real, other.imag]):
+927                return CObs(derived_observable(lambda x, **kwargs: x[0] * x[1] - x[2] * x[3],
+928                                               [self.real, other.real, self.imag, other.imag],
+929                                               man_grad=[other.real.value, self.real.value, -other.imag.value, -self.imag.value]),
+930                            derived_observable(lambda x, **kwargs: x[2] * x[1] + x[0] * x[3],
+931                                               [self.real, other.real, self.imag, other.imag],
+932                                               man_grad=[other.imag.value, self.imag.value, other.real.value, self.real.value]))
+933            elif getattr(other, 'imag', 0) != 0:
+934                return CObs(self.real * other.real - self.imag * other.imag,
+935                            self.imag * other.real + self.real * other.imag)
+936            else:
+937                return CObs(self.real * other.real, self.imag * other.real)
+938        else:
+939            return CObs(self.real * other, self.imag * other)
+940
+941    def __rmul__(self, other):
+942        return self * other
+943
+944    def __truediv__(self, other):
+945        if isinstance(other, np.ndarray):
+946            return 1 / (other / self)
+947        elif hasattr(other, 'real') and hasattr(other, 'imag'):
+948            r = other.real ** 2 + other.imag ** 2
+949            return CObs((self.real * other.real + self.imag * other.imag) / r, (self.imag * other.real - self.real * other.imag) / r)
+950        else:
+951            return CObs(self.real / other, self.imag / other)
+952
+953    def __rtruediv__(self, other):
+954        r = self.real ** 2 + self.imag ** 2
+955        if hasattr(other, 'real') and hasattr(other, 'imag'):
+956            return CObs((self.real * other.real + self.imag * other.imag) / r, (self.real * other.imag - self.imag * other.real) / r)
+957        else:
+958            return CObs(self.real * other / r, -self.imag * other / r)
+959
+960    def __abs__(self):
+961        return np.sqrt(self.real**2 + self.imag**2)
+962
+963    def __pos__(self):
+964        return self
+965
+966    def __neg__(self):
+967        return -1 * self
+968
+969    def __eq__(self, other):
+970        return self.real == other.real and self.imag == other.imag
+971
+972    def __str__(self):
+973        return '(' + str(self.real) + int(self.imag >= 0.0) * '+' + str(self.imag) + 'j)'
+974
+975    def __repr__(self):
+976        return 'CObs[' + str(self) + ']'
 
@@ -4107,10 +4105,10 @@ should agree with samples from a full jackknife analysis up to O(1/N).
-
874    def __init__(self, real, imag=0.0):
-875        self._real = real
-876        self._imag = imag
-877        self.tag = None
+            
872    def __init__(self, real, imag=0.0):
+873        self._real = real
+874        self._imag = imag
+875        self.tag = None
 
@@ -4128,12 +4126,12 @@ should agree with samples from a full jackknife analysis up to O(1/N).
-
887    def gamma_method(self, **kwargs):
-888        """Executes the gamma_method for the real and the imaginary part."""
-889        if isinstance(self.real, Obs):
-890            self.real.gamma_method(**kwargs)
-891        if isinstance(self.imag, Obs):
-892            self.imag.gamma_method(**kwargs)
+            
885    def gamma_method(self, **kwargs):
+886        """Executes the gamma_method for the real and the imaginary part."""
+887        if isinstance(self.real, Obs):
+888            self.real.gamma_method(**kwargs)
+889        if isinstance(self.imag, Obs):
+890            self.imag.gamma_method(**kwargs)
 
@@ -4153,9 +4151,9 @@ should agree with samples from a full jackknife analysis up to O(1/N).
-
894    def is_zero(self):
-895        """Checks whether both real and imaginary part are zero within machine precision."""
-896        return self.real == 0.0 and self.imag == 0.0
+            
892    def is_zero(self):
+893        """Checks whether both real and imaginary part are zero within machine precision."""
+894        return self.real == 0.0 and self.imag == 0.0
 
@@ -4175,8 +4173,8 @@ should agree with samples from a full jackknife analysis up to O(1/N).
-
898    def conjugate(self):
-899        return CObs(self.real, -self.imag)
+            
896    def conjugate(self):
+897        return CObs(self.real, -self.imag)