diff --git a/intervals/methods.py b/intervals/methods.py deleted file mode 100644 index 7f49a83..0000000 --- a/intervals/methods.py +++ /dev/null @@ -1,647 +0,0 @@ -""" ------------------------------- -cre: Feb 2022 - -web: github.com/marcodeangelis -org: Univerity of Liverpool - -MIT License ------------------------------- - -These methods are designed to behave neutrally on non-interval inputs. -So, if a non-interval is passed equivalent rules for floats apply. - -Interval to float methods, IR -> R: - -Interval to bool methods, IR -> {0,1}: - -Binary operations, IR2 -> IR -Unary operations, IR -> IR - -Parser, R^(nx2) -> IR^n, R^(mxnx2) -> IR^(mxn), R^(2xmxn) -> IR^(mxn) -This method turns an array of compatible dimension into an interval (array). - -Subintervalisation methods, IR -> IR^n. - -""" -from __future__ import annotations -from typing import (Sequence, Sized, Iterable, Optional, Any, Tuple, Union) - -from itertools import product - -import numpy -from numpy import (ndarray,asarray,transpose,vstack,linspace,zeros,argmax) - -from intervals.number import (Interval, MACHINE_EPS) - -numpy_min = numpy.min -numpy_max = numpy.max -numpy_sqrt= numpy.sqrt -numpy_abs = numpy.abs -numpy_exp = numpy.exp -numpy_sum = numpy.sum -numpy_sin = numpy.sin -numpy_cos = numpy.cos -numpy_tan = numpy.tan -# numpy_cot = numpy.cotang -numpy_pi = numpy.pi -numpy_inf = numpy.Inf - -# Properties or maybe attributes of the interval class. These apply to all interval-like objects. - -##################################################################################### -# methods.py -##################################################################################### -# Interval to float methods, Unary. -def lo(x: Interval) -> Union[float, ndarray]: - """ - Return the left endpoint of an Interval object. - - If x is not of class Interval, input is returned. - - """ - if is_Interval(x): return x.lo - return x - -def hi(x: Interval) -> Union[float, ndarray]: - """ - Return the right endpoint of an Interval object. - - If x is not of class Interval, input is returned. - - """ - if is_Interval(x): return x.hi - return x - -def width(x: Interval) -> Union[float, ndarray]: - """ - Return the width of an Interval object. - - If x is not of class Interval, input is returned. - - """ - if is_Interval(x): return hi(x)-lo(x) - return x - -def rad(x: Interval) -> Union[float, ndarray]: - """ - Return the radius of an Interval object. - - If x is not of class Interval, input is returned. - - """ - if is_Interval(x): return (hi(x)-lo(x))/2 - return x - -def mid(x: Interval) -> Union[float, ndarray]: - """ - Return the midpoint of an Interval. - - If x is not of class Interval, input is returned. - - """ - if is_Interval(x): return (hi(x)+lo(x))/2 - return x - -def mig(x): return numpy_max(numpy_abs(x.lo),numpy_abs(x.hi)) # mignitude -def mag(x): return numpy_min(numpy_abs(x.lo),numpy_abs(x.hi)) # magnitude - -##################################################################################### -# unary.py -##################################################################################### -# Interval to interval methods. Unary. -def abs(x:Interval): - """ - Return the absolute value of an Interval. - - If x is not of class Interval, absolute value is returned assuming input is numerical. - - If x is neither a number (neither Interval not numeric), numpy will throw an exception. - - """ - x_lo_abs = numpy.abs(lo(x)) - if is_Interval(x): - zero_in_x = contain(x,0) - x_hi_abs = numpy.abs(hi(x)) - a = numpy.min((x_lo_abs, x_hi_abs),axis=0) - if x.unsized: - if zero_in_x: a = 0 - else: a[zero_in_x] = 0 - b = numpy.max((x_lo_abs, x_hi_abs),axis=0) - return Interval(a,b) - return x_lo_abs - -def sqrt(x:Interval): - """ - Return the square root of an Interval. - - If x is not of class Interval, the square root is returned assuming input is numerical. - - If x is neither a number (neither Interval not numeric), numpy will throw an exception. - - """ - x_lo_sqrt = numpy.sqrt(lo(x)) - if is_Interval(x): - x_hi_sqrt = numpy.sqrt(hi(x)) - return Interval(x_lo_sqrt,x_hi_sqrt) - return x_lo_sqrt - -def exp(x:Interval): - if is_not_Interval(x): return numpy_exp(x) - return Interval(numpy_exp(lo(x)),numpy_exp(hi(x))) - -##################################################################################### -# binary.py -##################################################################################### -# Binary methods between two intervals -# 2-interval to interval. Bianry. -def max(x:Interval, y:Interval): - if all([is_not_Interval(x),is_not_Interval(y)]): - return numpy.max((x,y), axis=0) - a = numpy.max((lo(x),lo(y)), axis=0) - b = numpy.max((hi(x),hi(y)), axis=0) - return Interval(a,b) -def min(x:Interval, y:Interval): - if all([is_not_Interval(x),is_not_Interval(y)]): - return numpy.min((x,y), axis=0) - a = numpy.min((lo(x),lo(y)), axis=0) - b = numpy.min((hi(x),hi(y)), axis=0) - return Interval(a,b) - -##################################################################################### -# trig.py -##################################################################################### -def sin(x:Interval): - ''' - Implementation of Interval Arithmetic in CORA 2016 - - Matthias Althoff and Dmitry Grebenyuk - - EPiC Series in Computing Volume 43, 2017, Pages 91-105 - - ARCH16. 3rd International Workshop on Applied Verification for Continuous and Hybrid Systems - ''' - if not(is_Interval(x)): return numpy_sin(x) # int, float, ndarray - - if not(x.scalar): return sin_vector(x) - - twopi = 2*numpy_pi - pihalf = numpy_pi/2 - - if width(x)>=twopi: return Interval(-1,1) - - domain1 = Interval(0, pihalf) - domain2 = Interval(pihalf, 3*pihalf) - domain3 = Interval(3*pihalf, twopi) - - yl = x.lo%twopi - yh = x.hi%twopi - y = Interval(lo=yl,hi=yh) - - sin_l = numpy_sin(yl) - sin_h = numpy_sin(yh) - - if contain(domain1,y) & (yl<=yh): return Interval(sin_l,sin_h) - if contain(domain2,y) & (yl<=yh): return Interval(sin_h,sin_l) - if contain(domain3,y) & (yl<=yh): return Interval(sin_l,sin_h) - - case1a = contain(domain1,yl) & contain(domain1,yh) & (yl>yh) - case1b = contain(domain1,yl) & contain(domain3,yh) - case1c = contain(domain2,yl) & contain(domain2,yh) & (yl>yh) - case1d = contain(domain3,yl) & contain(domain3,yh) & (yl>yh) - - case2a = contain(domain1,yl) & contain(domain1,yh) & (yl<=yh) - case2b = contain(domain3,yl) & contain(domain1,yh) - case2c = contain(domain3,yl) & contain(domain3,yh) & (yl<=yh) - - case3a = contain(domain1,yl) & contain(domain2,yh) - case3b = contain(domain3,yl) & contain(domain2,yh) - - case4a = contain(domain2,yl) & contain(domain1,yh) - case4b = contain(domain2,yl) & contain(domain3,yh) - - case5 = contain(domain2,yl) & contain(domain2,yh) & (yl<=yh) - - if case1a | case1b | case1c | case1d : return Interval(-1,1) - if case2a | case2b | case2c : return Interval(sin_l,sin_h) - if case3a | case3b : return Interval(min(sin_l,sin_h),1) - if case4a | case4b : return Interval(-1,max(sin_l,sin_h)) - if case5: return Interval(sin_h,sin_l) - - pass - -def sin_vector(x:Interval): # vectorised version of sin(). - - if x.unsized: return sin(x) - - twopi = 2*numpy_pi - pihalf = numpy_pi/2 - - mask1a = width(x)>=twopi - - domain1 = Interval(0, pihalf) - domain2 = Interval(pihalf, 3*pihalf) - domain3 = Interval(3*pihalf, twopi) - - yl = x.lo%twopi - yh = x.hi%twopi - y = Interval(yl,yh) - - sin_l = numpy_sin(yl) - sin_h = numpy_sin(yh) - - # [l,h] all else - a = sin_l.copy() - b = sin_h.copy() - - # [-1,1] - mask3a = contain(domain1,yl) & contain(domain1,yh) & (yl>yh) - mask3b = contain(domain1,yl) & contain(domain3,yh) - mask3c = contain(domain2,yl) & contain(domain2,yh) & (yl>yh) - mask3d = contain(domain3,yl) & contain(domain3,yh) & (yl>yh) - case1 = mask1a|mask3a|mask3b|mask3c|mask3d - a[case1] = -1 - b[case1] = 1 - if all(case1): return Interval(lo=a,hi=b) - # [h,l] - mask2b = contain(domain2,yl[~case1]) & contain(domain2,yh[~case1]) & (yl[~case1]<=yh[~case1]) #return Interval(sin_h,sin_l) - case2 = mask2b - a[case2] = sin_h[case2] - b[case2] = sin_l[case2] - # [min, 1] - mask5a = contain(domain1,yl[~case1]) & contain(domain2,yh[~case1]) - mask5b = contain(domain3,yl[~case1]) & contain(domain2,yh[~case1]) - case3 = mask5a|mask5b - a[case3] = min(sin_l[case3],sin_h[case3]) - b[case3] = 1 - # [-1, max] - mask6a = contain(domain2,yl[~case1]) & contain(domain1,yh[~case1]) - mask6b = contain(domain2,yl[~case1]) & contain(domain3,yh[~case1]) - case4 = mask6a|mask6b - a[case4] = -1 - b[case4] = max(sin_l[case4],sin_h[case4]) - return Interval(lo=a,hi=b) - -def cos(x:Interval): - ''' - Implementation of Interval Arithmetic in CORA 2016 - - Matthias Althoff and Dmitry Grebenyuk - - EPiC Series in Computing Volume 43, 2017, Pages 91-105 - - ARCH16. 3rd International Workshop on Applied Verification for Continuous and Hybrid Systems - ''' - if not(is_Interval(x)): return numpy_cos(x) # int, float, ndarray - - if not(x.scalar): return cos_vector(x) - - twopi = 2*numpy_pi - - # [-1,1] aka case 0 - if width(x)>=twopi: return Interval(-1,1) - - domain1 = Interval(0, numpy_pi) - domain2 = Interval(numpy_pi, 2*numpy_pi) - - yl = x.lo%twopi - yh = x.hi%twopi - y = Interval(lo=yl,hi=yh) - - cos_l = numpy_cos(yl) - cos_h = numpy_cos(yh) - - # [-1,1] - case1a = (yh=twopi - - domain1 = Interval(0, numpy_pi) - domain2 = Interval(numpy_pi, 2*numpy_pi) - - yl = x.lo%twopi - yh = x.hi%twopi - - cos_l = numpy_cos(yl) - cos_h = numpy_cos(yh) - - a = cos_l.copy() - b = cos_h.copy() - - # [-1,1] - case1a = (yhnumpy_pi - case1b = (zhnumpy_pi - case1b = (zh bool: - if x.unsized: return (lo(x)<=0) & (hi(x)>=0) - else: return any((lo(x).flatten()<=0) & (hi(x).flatten()>=0)) -def intersect(x:Interval, y:Interval): return ~((x=hi(y)) # x contain y -def almost_contain(x:Interval, y:Interval, tol=1e-9): return (lo(y)-lo(x)>-tol) & (hi(x)-hi(y)>-tol) # x contain y -def intersect_vector(x_:Interval,y_:Interval): - ''' - This function checks if the focal elements x, intersect the subpaving y. - - x: A n-list of d-boxes or d-intervals, e.g. a subpaving. x.shape=(r,d) - y: A m-list of d-boxes or d-intervals, e.g. a focal element. y.shape=(p,d) - - out: A (rp)-list of d-arrays of booleans - ''' - x = intervalise(x_) - y = intervalise(y_) - # n,d = x.shape - m,d = y.shape - x_lo = lo(x)# x_lo = numpy.array([xi.lo for xi in x]) - x_hi = hi(x)# x_hi = numpy.array([xi.hi for xi in x]) - where_intersect = numpy.zeros((m,),dtype=bool)# inter = [] - for i, yi in enumerate(tolist(y)): # a focal elem - where_intersect[i] = any(numpy.all(~((x_hi < lo(yi)) | (hi(yi) < x_lo)), axis=1)) - return where_intersect -##################################################################################### -# parser.py -##################################################################################### -# Universal parser -def intervalise(x_: Any, index = -1) -> Union[Interval,Any]: - """ - This function casts an array-like structure into an Interval structure. - All array-like structures will be first coerced into an ndarray of floats. - If the coercion is unsuccessful the following error is thrown: `ValueError: setting an array element with a sequence.` - - For example this is the expected behaviour: - (*) an ndarray of shape (4,2) will be cast as an Interval of shape (4,). - - (*) an ndarray of shape (7,3,2) will be cast as an Interval of shape (7,3). - - (*) an ndarray of shape (3,2,7) will be cast as a degenerate Interval of shape (3,2,7). - - (*) an ndarray of shape (2,3,7) will be cast as an Interval of shape (3,7). - - (*) an ndarray of shape (2,3,7,2) will be cast as an Interval of shape (2,3,7). - - If an ndarray has shape with multiple dimensions having size 2, then the last dimension is intervalised. - So, an ndarray of shape (7,2,2) will be cast as an Interval of shape (7,2) with the last dimension intervalised. - When the ndarray has shape (2,2) again is the last dimension that gets intervalised. - - In case of ambiguity, e.g. (2,5,2), now the first dimension can be forced to be intervalised, selecting index=0, default is -1. - - It returns an interval only if the input is an array-like structure, otherwise it returns the following numpy error: - `ValueError: setting an array element with a sequence.` - - TODO: Parse a list of mixed numbers: interval and ndarrays. - - """ - def treat_list(xx): - xi_lo,xi_hi = [],[] - for xi in xx: # if each element in the list is an interval of homogeneus shape - if xi.__class__.__name__=='Interval': - xi_lo.append(xi.lo) - xi_hi.append(xi.hi) - else: - xi_ = intervalise(xi) # recursion - xi_lo.append(xi_.lo) - xi_hi.append(xi_.hi) - try: return Interval(lo=xi_lo, hi=xi_hi) - except: - print('!! Parsing an interval from list failed.') - return x_ - if x_.__class__.__name__=='Interval': return x_ - try: x = asarray(x_, dtype=float) - except ValueError: # ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (...) + inhomogeneous part. - if x_.__class__.__name__=='list': return treat_list(x_) # attempt to turn a n-list of intervals into a (n,...)-interval - s = x.shape - two=[si==2 for si in s] - if all(two): return Interval(lo=transpose(x)[0],hi=transpose(x)[1]) - elif any(two): - if two[-1]: return Interval(lo=transpose(x)[0],hi=transpose(x)[1]) # the last dimension has size 2 - elif two[0]: return Interval(lo=x[0],hi=x[1]) - elif (two[-1]) & (two[0]): # this is the ambiguous case (2,3,5,2) - if index == 0: return Interval(lo=x[0],hi=x[1]) # first dimension gets intervalised - elif index == -1: return Interval(lo=transpose(x)[0],hi=transpose(x)[1]) - # if (sum(two)==1) & (two[0]): return Interval(lo=x[0],hi=x[1])# there is only one dimension of size 2 and is the first one - print('Array-like structure must have last dimension (or first) of size 2, for it to be coerced to Interval.') - return Interval(lo=x) - else: return Interval(lo=x) - -def sizeit(x:Interval) -> Interval: - ''' - Takes an unsized scalar interval and turns it in to a sized one. - ''' - if is_Interval(x): - if x.scalar & x.unsized: - return Interval(lo=[x.lo], hi=[x.hi]) - return x - -def unsizeit(x:Interval) -> Interval: - ''' - Takes a sized scalar interval and turns it in to a unsized one. - - ''' - if is_Interval(x): - if x.scalar & x.unsized==False: - return Interval(lo=x.lo[0], hi=x.hi[0]) - return x - -def tolist(x:Interval): - if is_not_Interval(x): return x - dim = len(x.shape) - if dim == 1: return [Interval(xi.lo,xi.hi) for xi in x] - if dim == 2: - m,d = x.shape - return [Interval(lo=x.lo[i,:],hi=x.hi[i,:]) for i in range(m)] - if dim > 2: return x # not implemented yet -##################################################################################### -# subint.py -##################################################################################### -def subintervalise(x_:Interval, n:Union[int,tuple]=0): - x = intervalise(x_) - d = len(x.shape) # dimension of the array - if n==0: return x - elif n==1: return x # should return a subtiling (sized interval) - if x.scalar: # or x.scalar == True - xx = linspace(x.lo,x.hi,num=n+1) - return intervalise(vstack([xx[:-1], xx[1:]])) - elif d==1: ## x.shape = (m,) - m = x.shape[0] # size of 1d array - if type(n)==int: n=m*[n] # differential split number - X_sub = [] - for i,xi in enumerate(x): - xxi = subintervalise(xi,n=n[i]) # recursion - X_sub.append(sizeit(xxi).val) - return intervalise(asarray(list(product(*X_sub)),dtype=float)) -# elif len(x.shape)>1: pass # TODO: implement space-product subintervalization with arrays of dimension greater than 2. - else: print('!! Subtiling not yet supported for interval arrays of dimension 2 or larger. Input will be returned.') - return x - -def split_interval(x:Interval,y:float=None): - if y is None: return Interval(lo(x),mid(x)), Interval(mid(x),hi(x)) - - x1, x2 = Interval(lo(x),hi(x)), Interval(lo(x),hi(x)) # TODO: implement copy method - y_in_x = contain(x,y) # x contain y - if x.unsized: - if ~y_in_x: return x1,x2 - return Interval(lo=lo(x),hi=y), Interval(lo=y,hi=hi(x)) - else: pass - # x1[y_in_x] = Interval(lo(x)[y_in_x],hi=y) - # x2[y_in_x] = Interval(y,hi=hi(x)[y_in_x]) - return x1,x2 - -def reconstitute(x_:Interval): - x = intervalise(x_) - d = len(x.shape) # dimension of the subtiling ==1 if scalar, ==2 if 1d array - if d==1: return Interval(lo=numpy.min(x.lo), hi=numpy.max(x.hi)) - elif d==2: return Interval(lo=numpy.min(x.lo,axis=1), hi=numpy.max(x.hi,axis=1)) - else: print('!! Subtiling not yet supported for interval arrays of dimension 2 or larger.') - return x - -def space_product(x_:Union[ndarray,Interval],y_:Union[ndarray,Interval]): return asarray(tuple(product(x_,y_))) - -def bisect(x_:Interval,i:int=None): - """ - :x_: Interval of shape (n,) - - Bisect the largest box if i is None. - """ - x = intervalise(x_) - if x.scalar: - mid_x = mid(x) - return Interval(lo(x),mid_x), Interval(mid_x,hi(x)) - if i is not None: split_index = i - else: - w = width(x) - split_index = argmax(w) - d=x.shape[0] - n=[0]*d - n[split_index]=2 # ex: (0,0,2,0,0,0) if interval of dim 6 has third dimension bisected - x_bisect = subintervalise(x,n=tuple(n)) - return x_bisect[0], x_bisect[1] - -##################################################################################### -# types.py -##################################################################################### -# Interval to bool methods, Unary. -def is_Interval(x:Any) -> bool: return x.__class__.__name__ == 'Interval' -def is_not_Interval(x:Any) -> bool: return x.__class__.__name__ != 'Interval' \ No newline at end of file diff --git a/intervals/plotting.py b/intervals/plotting.py index ca4b39f..f883ab2 100644 --- a/intervals/plotting.py +++ b/intervals/plotting.py @@ -1,15 +1,54 @@ +from __future__ import annotations +import numpy as np +import matplotlib.pyplot as plt + """ -------------------------- -Created Feb 2022 -Marco De Angelis -github.com/marcodeangelis +Editted by Leslie Feb 2024 MIT License -------------------------- """ -from __future__ import annotations -import matplotlib.pyplot as pyplot +def plot_intervals(x, y_i, **kwargs): + """ plot intervals vertically + + args: + x: array-like precise values + x-axis coordinates + y_i: array-like Interval objects + array of intervals + """ + + fig, ax = plt.subplots() + + def basic_plot(x, y_i, **kwargs): + ax.plot([x, x], [y_i.hi, y_i.lo], 'blue', **kwargs) + if np.any(y_i.lo == y_i.hi): + sc_x = x[y_i.lo == y_i.hi] + sc_y = y_i[y_i.lo == y_i.hi].lo + ax.scatter(sc_x, sc_y, c='blue', **kwargs) + + if len(x.shape) > 1: + for xx, interval in zip(x, y_i): + basic_plot([xx, xx], [interval.hi, interval.lo]) + else: + basic_plot(x, y_i) + return ax + + -from intervals.number import Interval +def plot_lower_bound(x, y_i, **kwargs): + """ plot lower bound of intervals + + args: + x: array-like + x-axis coordinates + y_i: array-like + array of intervals + """ + + fig, ax = plt.subplots() + ax.scatter(x, y_i.lo, label='lower bound', **kwargs) + ax.legend() diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index b580b29..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,20 +0,0 @@ -# file: pyproject.toml - -[build-system] -requires = [ - "setuptools >= 68.0.0", - "wheel >= 0.41.0", -] -build-backend = "setuptools.build_meta" - -[project] -name = "intervals" -version = "0.1.2" -authors = [ - {name = "Marco de Angelis", email = "marco.de-angelis@strath.ac.uk"}, -] -requires-python = ">=3.7" -description = "Emulated interval arithmetic in Python." -dependencies = [ - "numpy", -] diff --git a/questions.md b/questions.md new file mode 100644 index 0000000..b5c2b3b --- /dev/null +++ b/questions.md @@ -0,0 +1,3 @@ +Here I note some questions that are to be solved by Marco + +- [ ] how to squeeze e.g. shape (32, 1) into (32,) \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..6a60ec7 --- /dev/null +++ b/setup.py @@ -0,0 +1,11 @@ +from setuptools import find_packages, setup + +setup( + name='intervals', + packages=find_packages(where="src"), + package_dir={"": "src"}, + version='0.2.0', + description='A modified version of the intervals library by Leslie', + author='Marco, Leslie', + license='MIT', +) \ No newline at end of file diff --git a/intervals/__init__.py b/src/intervals/__init__.py similarity index 100% rename from intervals/__init__.py rename to src/intervals/__init__.py diff --git a/src/intervals/activation.py b/src/intervals/activation.py new file mode 100644 index 0000000..f217550 --- /dev/null +++ b/src/intervals/activation.py @@ -0,0 +1,15 @@ +from intervals.number import Interval +from intervals.methods import exp +import numpy as np + + +''' a series of popular activation functions for interval arithmetic ''' + +def sigmoid(x:Interval): return 1/(1 + exp(-x)) +def tanh(x:Interval): return (exp(2*x)-1)/(exp(2*x)+1) + + +def relu(x): + xs=x.shape + x0=np.zeros(xs) + return np.maximum(x0,x) \ No newline at end of file diff --git a/intervals/arithmetic.py b/src/intervals/arithmetic.py similarity index 100% rename from intervals/arithmetic.py rename to src/intervals/arithmetic.py diff --git a/intervals/complex.py b/src/intervals/complex.py similarity index 100% rename from intervals/complex.py rename to src/intervals/complex.py diff --git a/src/intervals/mat_features.py b/src/intervals/mat_features.py new file mode 100644 index 0000000..5279e98 --- /dev/null +++ b/src/intervals/mat_features.py @@ -0,0 +1,131 @@ +import numpy as np +from intervals.methods import (lo,hi,mid,rad,width,intervalise) + +''' this file contains the functions to create interval matrices from numpy arrays + +TODO: func `intervalise()` change the default setting to consume by the last axis to first axis or 2by2 matrix will be wrong; +''' + +def consume_interval(low, high): + """ initialise an interval matrix from `low, high` arrays; + + note: + initialise from the shape (2, m, n) as preferred; + + example: + low = np.arange(9).reshape(3,3) + high = np.arange(10, 19).reshape(3,3) + """ + + a_matrix = np.stack([low, high], axis=0) + return intervalise(a_matrix) + + +def create_interval(matrix, half_width=0.1): + """ mannually create an interval matrix from a numpy array """ + + low = matrix - half_width + high = matrix + half_width + return consume_interval(low, high) + + +def dot(x,y): return sum(x*y) + + +def rowcol(W,x): + """ marco's original implementation of the rowcol function """ + + s = W.shape + y=[] + for i in range(s[0]): + y.append(dot(W[i],x)) + return intervalise(y) + + +def rowcol2(x,W): + """ Leslie changed the argument order for notational convecience original implementation of the rowcol function + + args: + - W: have to be a vector; + """ + + s = x.shape + if len(s) > 1: + y=[] + for i in range(s[0]): + y.append(dot(x[i],W)) + + return intervalise(y) + else: + print('x shall be a 2 dimensional array') + + + +# def rowcol_wrong(x, W): +# """ Leslie's implementation of the rowcol function + +# args: +# - x: a vector, e.g. hidden layer output, as a row vector +# - W: weight matrix of the next layer + +# note: +# - this is not full-fleged interval matrix computation +# - it currently only fits for `x` as a vector +# - it can be used for hidden-layer tensor propagation +# """ + +# s = W.shape +# y=[] +# for j in range(s[1]): +# y.append(dot(x, W[:, j])) +# result = intervalise(y) +# return result[np.newaxis, :] + + +def consume_list(list_intervals): + """ consume a list of interval matrices into a single interval matrix + + ! of no use now + note: + + - being used for interval matrix multiplication + """ + + low, upper = [], [] + for interval in list_intervals: + low.append(interval.lo) + upper.append(interval.hi) + + low_a = np.vstack(low) + upper_a = np.vstack(upper) + + return intervalise(low_a, upper_a) + + +def intvl_matmul(x, W): + """ draft matrix multiplication function for interval matrices + + note: + - can be used for general matrix multiplication + + return: + - an interval matrix + """ + + row_cp_list = [] + sW = W.shape + if (len(sW)) > 1: + if (sW[1]>1): + for j in range(sW[1]): + row_cp_list.append(rowcol2(x, W[:, j])) + return intervalise(row_cp_list) + elif sW[1]==1: + return rowcol2(x, W[:, 0]) + else: + return rowcol2(x, W) + else: + return rowcol2(x, W) + + + + \ No newline at end of file diff --git a/src/intervals/methods.py b/src/intervals/methods.py new file mode 100644 index 0000000..5038230 --- /dev/null +++ b/src/intervals/methods.py @@ -0,0 +1,955 @@ +""" +------------------------------ +cre: Feb 2022 + +web: github.com/marcodeangelis +org: Univerity of Liverpool + +MIT License +------------------------------ + +These methods are designed to behave neutrally on non-interval inputs. +So, if a non-interval is passed equivalent rules for floats apply. + +Interval to float methods, IR -> R: + +Interval to bool methods, IR -> {0,1}: + +Binary operations, IR2 -> IR +Unary operations, IR -> IR + +Parser, R^(nx2) -> IR^n, R^(mxnx2) -> IR^(mxn), R^(2xmxn) -> IR^(mxn) +This method turns an array of compatible dimension into an interval (array). + +Subintervalisation methods, IR -> IR^n. + +""" +from __future__ import annotations +from typing import (Sequence, Sized, Iterable, Optional, Any, Tuple, Union) + +from itertools import product + +import numpy +from numpy import (ndarray, asarray, vstack, linspace, zeros, argmax) + +from intervals.number import (Interval, MACHINE_EPS) + +numpy_min = numpy.min +numpy_max = numpy.max +numpy_sqrt = numpy.sqrt +numpy_abs = numpy.abs +numpy_exp = numpy.exp +numpy_sum = numpy.sum +numpy_sin = numpy.sin +numpy_cos = numpy.cos +numpy_tan = numpy.tan +# numpy_cot = numpy.cotang +numpy_pi = numpy.pi +numpy_inf = numpy.Inf +numpy_transpose = numpy.transpose + +# Properties or maybe attributes of the interval class. These apply to all interval-like objects. + +##################################################################################### +# methods.py +##################################################################################### +# Interval to float methods, Unary. + + +def lo(x: Interval) -> Union[float, ndarray]: + """ + Return the left endpoint of an Interval object. + + If x is not of class Interval, input is returned. + + """ + if is_Interval(x): + return x.lo + return x + + +def hi(x: Interval) -> Union[float, ndarray]: + """ + Return the right endpoint of an Interval object. + + If x is not of class Interval, input is returned. + + """ + if is_Interval(x): + return x.hi + return x + + +def width(x: Interval) -> Union[float, ndarray]: + """ + Return the width of an Interval object. + + If x is not of class Interval, input is returned. + + """ + if is_Interval(x): + return hi(x)-lo(x) + return x + + +def rad(x: Interval) -> Union[float, ndarray]: + """ + Return the radius of an Interval object. + + If x is not of class Interval, input is returned. + + """ + if is_Interval(x): + return (hi(x)-lo(x))/2 + return x + + +def mid(x: Interval) -> Union[float, ndarray]: + """ + Return the midpoint of an Interval. + + If x is not of class Interval, input is returned. + + """ + if is_Interval(x): + return (hi(x)+lo(x))/2 + return x + + +def mig(x): return numpy_max(numpy_abs(x.lo), numpy_abs(x.hi)) # mignitude +def mag(x): return numpy_min(numpy_abs(x.lo), numpy_abs(x.hi)) # magnitude + +##################################################################################### +# unary.py +##################################################################################### +# Interval to interval methods. Unary. + + +def abs(x: Interval): + """ + Return the absolute value of an Interval. + + If x is not of class Interval, absolute value is returned assuming input is numerical. + + If x is neither a number (neither Interval not numeric), numpy will throw an exception. + + """ + x_lo_abs = numpy.abs(lo(x)) + if is_Interval(x): + zero_in_x = contain(x, 0) + x_hi_abs = numpy.abs(hi(x)) + a = numpy.min((x_lo_abs, x_hi_abs), axis=0) + if x.unsized: + if zero_in_x: + a = 0 + else: + a[zero_in_x] = 0 + b = numpy.max((x_lo_abs, x_hi_abs), axis=0) + return Interval(a, b) + return x_lo_abs + + +def sqrt(x: Interval): + """ + Return the square root of an Interval. + + If x is not of class Interval, the square root is returned assuming input is numerical. + + If x is neither a number (neither Interval not numeric), numpy will throw an exception. + + """ + x_lo_sqrt = numpy.sqrt(lo(x)) + if is_Interval(x): + x_hi_sqrt = numpy.sqrt(hi(x)) + return Interval(x_lo_sqrt, x_hi_sqrt) + return x_lo_sqrt + + +def exp(x: Interval): + if is_not_Interval(x): + return numpy_exp(x) + return Interval(numpy_exp(lo(x)), numpy_exp(hi(x))) + +##################################################################################### +# binary.py +##################################################################################### +# Binary methods between two intervals +# 2-interval to interval. Bianry. + + +def max(x: Interval, y: Interval): + if all([is_not_Interval(x), is_not_Interval(y)]): + return numpy.max((x, y), axis=0) + a = numpy.max((lo(x), lo(y)), axis=0) + b = numpy.max((hi(x), hi(y)), axis=0) + return Interval(a, b) + + +def min(x: Interval, y: Interval): + if all([is_not_Interval(x), is_not_Interval(y)]): + return numpy.min((x, y), axis=0) + a = numpy.min((lo(x), lo(y)), axis=0) + b = numpy.min((hi(x), hi(y)), axis=0) + return Interval(a, b) + +##################################################################################### +# trig.py +##################################################################################### + + +def sin(x: Interval): + ''' + Implementation of Interval Arithmetic in CORA 2016 + + Matthias Althoff and Dmitry Grebenyuk + + EPiC Series in Computing Volume 43, 2017, Pages 91-105 + + ARCH16. 3rd International Workshop on Applied Verification for Continuous and Hybrid Systems + ''' + if not (is_Interval(x)): + return numpy_sin(x) # int, float, ndarray + + if not (x.scalar): + return sin_vector(x) + + twopi = 2*numpy_pi + pihalf = numpy_pi/2 + + if width(x) >= twopi: + return Interval(-1, 1) + + domain1 = Interval(0, pihalf) + domain2 = Interval(pihalf, 3*pihalf) + domain3 = Interval(3*pihalf, twopi) + + yl = x.lo % twopi + yh = x.hi % twopi + y = Interval(lo=yl, hi=yh) + + sin_l = numpy_sin(yl) + sin_h = numpy_sin(yh) + + if contain(domain1, y) & (yl <= yh): + return Interval(sin_l, sin_h) + if contain(domain2, y) & (yl <= yh): + return Interval(sin_h, sin_l) + if contain(domain3, y) & (yl <= yh): + return Interval(sin_l, sin_h) + + case1a = contain(domain1, yl) & contain(domain1, yh) & (yl > yh) + case1b = contain(domain1, yl) & contain(domain3, yh) + case1c = contain(domain2, yl) & contain(domain2, yh) & (yl > yh) + case1d = contain(domain3, yl) & contain(domain3, yh) & (yl > yh) + + case2a = contain(domain1, yl) & contain(domain1, yh) & (yl <= yh) + case2b = contain(domain3, yl) & contain(domain1, yh) + case2c = contain(domain3, yl) & contain(domain3, yh) & (yl <= yh) + + case3a = contain(domain1, yl) & contain(domain2, yh) + case3b = contain(domain3, yl) & contain(domain2, yh) + + case4a = contain(domain2, yl) & contain(domain1, yh) + case4b = contain(domain2, yl) & contain(domain3, yh) + + case5 = contain(domain2, yl) & contain(domain2, yh) & (yl <= yh) + + if case1a | case1b | case1c | case1d: + return Interval(-1, 1) + if case2a | case2b | case2c: + return Interval(sin_l, sin_h) + if case3a | case3b: + return Interval(min(sin_l, sin_h), 1) + if case4a | case4b: + return Interval(-1, max(sin_l, sin_h)) + if case5: + return Interval(sin_h, sin_l) + + pass + + +def sin_vector(x: Interval): # vectorised version of sin(). + + if x.unsized: + return sin(x) + + twopi = 2*numpy_pi + pihalf = numpy_pi/2 + + mask1a = width(x) >= twopi + + domain1 = Interval(0, pihalf) + domain2 = Interval(pihalf, 3*pihalf) + domain3 = Interval(3*pihalf, twopi) + + yl = x.lo % twopi + yh = x.hi % twopi + y = Interval(yl, yh) + + sin_l = numpy_sin(yl) + sin_h = numpy_sin(yh) + + # [l,h] all else + a = sin_l.copy() + b = sin_h.copy() + + # [-1,1] + mask3a = contain(domain1, yl) & contain(domain1, yh) & (yl > yh) + mask3b = contain(domain1, yl) & contain(domain3, yh) + mask3c = contain(domain2, yl) & contain(domain2, yh) & (yl > yh) + mask3d = contain(domain3, yl) & contain(domain3, yh) & (yl > yh) + case1 = mask1a | mask3a | mask3b | mask3c | mask3d + a[case1] = -1 + b[case1] = 1 + if all(case1): + return Interval(lo=a, hi=b) + # [h,l] + mask2b = contain(domain2, yl[~case1]) & contain(domain2, yh[~case1]) & ( + yl[~case1] <= yh[~case1]) # return Interval(sin_h,sin_l) + case2 = mask2b + a[case2] = sin_h[case2] + b[case2] = sin_l[case2] + # [min, 1] + mask5a = contain(domain1, yl[~case1]) & contain(domain2, yh[~case1]) + mask5b = contain(domain3, yl[~case1]) & contain(domain2, yh[~case1]) + case3 = mask5a | mask5b + a[case3] = min(sin_l[case3], sin_h[case3]) + b[case3] = 1 + # [-1, max] + mask6a = contain(domain2, yl[~case1]) & contain(domain1, yh[~case1]) + mask6b = contain(domain2, yl[~case1]) & contain(domain3, yh[~case1]) + case4 = mask6a | mask6b + a[case4] = -1 + b[case4] = max(sin_l[case4], sin_h[case4]) + return Interval(lo=a, hi=b) + + +def cos(x: Interval): + ''' + Implementation of Interval Arithmetic in CORA 2016 + + Matthias Althoff and Dmitry Grebenyuk + + EPiC Series in Computing Volume 43, 2017, Pages 91-105 + + ARCH16. 3rd International Workshop on Applied Verification for Continuous and Hybrid Systems + ''' + if not (is_Interval(x)): + return numpy_cos(x) # int, float, ndarray + + if not (x.scalar): + return cos_vector(x) + + twopi = 2*numpy_pi + + # [-1,1] aka case 0 + if width(x) >= twopi: + return Interval(-1, 1) + + domain1 = Interval(0, numpy_pi) + domain2 = Interval(numpy_pi, 2*numpy_pi) + + yl = x.lo % twopi + yh = x.hi % twopi + y = Interval(lo=yl, hi=yh) + + cos_l = numpy_cos(yl) + cos_h = numpy_cos(yh) + + # [-1,1] + case1a = (yh < yl) & contain(domain1, yl) & contain(domain1, yh) + case1b = (yh < yl) & contain(domain2, yl) & contain(domain2, yh) + # [cos_l, cos_h] + case2a = (yl <= yh) & contain(domain2, yl) & contain(domain2, yh) + # [min(cos_l, cos_h), 1] + case3a = contain(domain2, yl) & contain(domain1, yh) + # [-1, max(cos_l, cos_h)] + case4a = contain(domain1, yl) & contain(domain2, yh) + # [cos_h, cos_l] + case5a = (yl <= yh) & contain(domain1, yl) & contain(domain1, yh) + + if case1a | case1b: + return Interval(-1, 1) + if case2a: + return Interval(cos_l, cos_h) + if case3a: + return Interval(min(cos_l, cos_h), 1) + if case4a: + return Interval(-1, max(cos_l, cos_h)) + if case5a: + return Interval(cos_h, cos_l) + + +def cos_vector(x: Interval): # vectorised version of cos() + if x.unsized: + return sin(x) + + twopi = 2*numpy_pi + + case0 = width(x) >= twopi + + domain1 = Interval(0, numpy_pi) + domain2 = Interval(numpy_pi, 2*numpy_pi) + + yl = x.lo % twopi + yh = x.hi % twopi + + cos_l = numpy_cos(yl) + cos_h = numpy_cos(yh) + + a = cos_l.copy() + b = cos_h.copy() + + # [-1,1] + case1a = (yh < yl) & contain(domain1, yl) & contain(domain1, yh) + case1b = (yh < yl) & contain(domain2, yl) & contain(domain2, yh) + case1 = case0 | case1a | case1b + a[case1] = -1 + b[case1] = 1 + # [cos_l, cos_h] + # case2 = (yl<=yh) & contain(domain2,yl) & contain(domain2,yh) + # a[case2] = cos_l[case2] + # b[case2] = cos_h[case2] + # [min(cos_l, cos_h), 1] + case3 = contain(domain2, yl) & contain(domain1, yh) + a[case3] = min(cos_l[case3], cos_h[case3]) + b[case3] = 1 + # [-1, max(cos_l, cos_h)] + case4 = contain(domain1, yl) & contain(domain2, yh) + a[case4] = -1 + b[case4] = max(cos_l[case4], cos_h[case4]) + # [cos_h, cos_l] + case5 = (yl <= yh) & contain(domain1, yl) & contain(domain1, yh) + a[case5] = cos_h[case5] + b[case5] = cos_l[case5] + return Interval(lo=a, hi=b) + + +def tan(x: Interval): + ''' + Implementation of Interval Arithmetic in CORA 2016 + + Matthias Althoff and Dmitry Grebenyuk + + EPiC Series in Computing Volume 43, 2017, Pages 91-105 + + ARCH16. 3rd International Workshop on Applied Verification for Continuous and Hybrid Systems + ''' + + if not (is_Interval(x)): + return numpy_tan(x) # int, float, ndarray + + if not (x.scalar): + return tan_vector(x) + + pihalf = numpy_pi/2 + + domain1 = Interval(0, pihalf) + domain2 = Interval(pihalf, numpy_pi) + + zl = x.lo % numpy_pi + zh = x.hi % numpy_pi + + # [-∞, ∞] + case1a = width(x) > numpy_pi + case1b = (zh < zl) & contain(domain1, zl) & contain(domain1, zh) + case1c = (zh < zl) & contain(domain2, zl) & contain(domain2, zh) + case1d = contain(domain1, zl) & contain(domain2, zh) + + # [tan_l, tan_h] + case2a = (zl <= zh) & contain(domain1, zl) & contain(domain1, zh) + case2b = (zl <= zh) & contain(domain2, zl) & contain(domain2, zh) + + if case1a | case1b | case1c | case1d: + return Interval(-numpy_inf, numpy_inf) + if case2a | case2b: + return Interval(tan(zl), tan(zh)) + else: + return Interval(tan(zl), tan(zh)) + + +def tan_vector(x: Interval): # Vectorised version of tan(). + if x.unsized: + return tan(x) + + pihalf = numpy_pi/2 + + domain1 = Interval(0, pihalf) + domain2 = Interval(pihalf, numpy_pi) + + zl = x.lo % numpy_pi + zh = x.hi % numpy_pi + + tan_l = tan(zl) + tan_h = tan(zh) + + a = tan_l.copy() + b = tan_h.copy() + + # [-∞, ∞] + case1a = width(x) > numpy_pi + case1b = (zh < zl) & contain(domain1, zl) & contain(domain1, zh) + case1c = (zh < zl) & contain(domain2, zl) & contain(domain2, zh) + case1d = contain(domain1, zl) & contain(domain2, zh) + case1 = case1a | case1b | case1c | case1d + a[case1] = -numpy_inf + b[case1] = numpy_inf + + # #[tan_l, tan_h] + # case2a = (zl<=zh) & contain(domain1,zl) & contain(domain1,zh) + # case2b = (zl<=zh) & contain(domain2,zl) & contain(domain2,zh) + return Interval(lo=a, hi=b) + + +# Interval to ndarray[float] +# def linspace(): + + +# def union(): pass # spell the difference between union and subpaving. +# def intersection(): +# def difference(): +# def set_difference(x:Interval,y:Interval): + +##################################################################################### +# set.py +##################################################################################### +# Interval to bool methods, Binary. +# ... +def straddle_zero(x: Interval) -> bool: + if x.unsized: + return (lo(x) <= 0) & (hi(x) >= 0) + else: + return any((lo(x).flatten() <= 0) & (hi(x).flatten() >= 0)) + + +def intersect(x: Interval, y: Interval): return ~( + (x < y) | (y < x)) # commutative + + +def contain(x: Interval, y: Interval): return ( + lo(x) <= lo(y)) & (hi(x) >= hi(y)) # x contain y + + +def almost_contain(x: Interval, y: Interval, tol=1e-9): return (lo(y) - + # x contain y + lo(x) > -tol) & (hi(x)-hi(y) > -tol) + + +def intersect_vector(x_: Interval, y_: Interval): + ''' + This function checks if the focal elements x, intersect the subpaving y. + + x: A n-list of d-boxes or d-intervals, e.g. a subpaving. x.shape=(r,d) + y: A m-list of d-boxes or d-intervals, e.g. a focal element. y.shape=(p,d) + + out: A (rp)-list of d-arrays of booleans + ''' + x = intervalise(x_) + y = intervalise(y_) + # n,d = x.shape + m, d = y.shape + x_lo = lo(x) # x_lo = numpy.array([xi.lo for xi in x]) + x_hi = hi(x) # x_hi = numpy.array([xi.hi for xi in x]) + where_intersect = numpy.zeros((m,), dtype=bool) # inter = [] + for i, yi in enumerate(tolist(y)): #  a focal elem + where_intersect[i] = any( + numpy.all(~((x_hi < lo(yi)) | (hi(yi) < x_lo)), axis=1)) + return where_intersect +##################################################################################### +# parser.py +##################################################################################### +# Universal parser + + +def intervalise(x_: Any, interval_index=-1) -> Union[Interval, Any]: + """ + This function casts an array-like structure into an Interval structure. + All array-like structures will be first coerced into an ndarray of floats. + If the coercion is unsuccessful the following error is thrown: `ValueError: setting an array element with a sequence.` + + For example this is the expected behaviour: + (*) an ndarray of shape (4,2) will be cast as an Interval of shape (4,). + + (*) an ndarray of shape (7,3,2) will be cast as an Interval of shape (7,3). + + (*) an ndarray of shape (3,2,7) will be cast as a degenerate Interval of shape (3,2,7). + + (*) an ndarray of shape (2,3,7) will be cast as an Interval of shape (3,7). + + (*) an ndarray of shape (2,3,7,2) will be cast as an Interval of shape (2,3,7) if interval_index is set to -1. + + If an ndarray has shape with multiple dimensions having size 2, then the last dimension is intervalised. + So, an ndarray of shape (7,2,2) will be cast as an Interval of shape (7,2) with the last dimension intervalised. + When the ndarray has shape (2,2) again is the last dimension that gets intervalised. + + In case of ambiguity, e.g. (2,5,2), now the first dimension can be forced to be intervalised, selecting index=0, default is -1. + + It returns an interval only if the input is an array-like structure, otherwise it returns the following numpy error: + `ValueError: setting an array element with a sequence.` + + TODO: Parse a list of mixed numbers: interval and ndarrays. + + """ + def treat_list(xx): + xi_lo, xi_hi = [], [] + for xi in xx: # if each element in the list is an interval of homogeneus shape + if xi.__class__.__name__ == 'Interval': + xi_lo.append(xi.lo) + xi_hi.append(xi.hi) + else: + xi_ = intervalise(xi) # recursion + xi_lo.append(xi_.lo) + xi_hi.append(xi_.hi) + try: + return Interval(lo=xi_lo, hi=xi_hi) + except: + print('!! Parsing an interval from list failed.') + return x_ + if x_.__class__.__name__ == 'Interval': + return x_ + try: + x = asarray(x_, dtype=float) + # ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (...) + inhomogeneous part. + except ValueError: + if x_.__class__.__name__ == 'list': + # attempt to turn a n-list of intervals into a (n,...)-interval + return treat_list(x_) + s = x.shape + two = [si == 2 for si in s] + # if all(two): return Interval(lo=numpy_transpose(x)[0],hi=numpy_transpose(x)[1]) + if all(two): + return Interval(lo=x[..., 0], hi=x[..., 1]) + elif any(two): + # if two[-1]: return Interval(lo=numpy_transpose(x)[0],hi=numpy_transpose(x)[1]) # the last dimension has size 2 + if two[-1]: + # last dimension has size 2 + return Interval(lo=x[..., 0], hi=x[..., 1]) + elif two[0]: + return Interval(lo=x[0], hi=x[1]) # first dimension has size 2 + elif (two[-1]) & (two[0]): # this is the ambiguous case (2,3,5,2) + if interval_index == 0: + # first dimension gets intervalised + return Interval(lo=x[0], hi=x[1]) + # elif index == -1: return Interval(lo=numpy_transpose(x)[0],hi=numpy_transpose(x)[1]) + elif interval_index == -1: + return Interval(lo=x[..., 0], hi=x[..., 1]) + # if (sum(two)==1) & (two[0]): return Interval(lo=x[0],hi=x[1])# there is only one dimension of size 2 and is the first one + print('Array-like structure must have the last (or first) dimension of size 2, for it to be coerced to Interval.') + return Interval(lo=x) + else: + return Interval(lo=x) + + +def sizeit(x: Interval) -> Interval: + ''' + Takes an unsized scalar interval and turns it in to a sized one. + ''' + if is_Interval(x): + if x.scalar & x.unsized: + return Interval(lo=[x.lo], hi=[x.hi]) + return x + + +def unsizeit(x: Interval) -> Interval: + ''' + Takes a sized scalar interval and turns it in to a unsized one. + + ''' + if is_Interval(x): + if x.scalar & x.unsized == False: + return Interval(lo=x.lo[0], hi=x.hi[0]) + return x + + +def tolist(x: Interval): + if is_not_Interval(x): + return x + dim = len(x.shape) + if dim == 1: + return [Interval(xi.lo, xi.hi) for xi in x] + if dim == 2: + m, d = x.shape + return [Interval(lo=x.lo[i, :], hi=x.hi[i, :]) for i in range(m)] + if dim > 2: + return x # not implemented yet +##################################################################################### +# subint.py +##################################################################################### + + +def subintervalise(x_: Interval, n: Union[int, tuple] = 0): + x = intervalise(x_) + d = len(x.shape) # dimension of the array + if n == 0: + return x + elif n == 1: + return x # should return a subtiling (sized interval) + if x.scalar: # or x.scalar == True + xx = linspace(x.lo, x.hi, num=n+1) + return intervalise(vstack([xx[:-1], xx[1:]])) + elif d == 1: # x.shape = (m,) + m = x.shape[0] # size of 1d array + if type(n) == int: + n = m*[n] # differential split number + X_sub = [] + for i, xi in enumerate(x): + xxi = subintervalise(xi, n=n[i]) # recursion + X_sub.append(sizeit(xxi).val) + return intervalise(asarray(list(product(*X_sub)), dtype=float)) +# elif len(x.shape)>1: pass # TODO: implement space-product subintervalization with arrays of dimension greater than 2. + else: + print('!! Subtiling not yet supported for interval arrays of dimension 2 or larger. Input will be returned.') + return x + + +def split_interval(x: Interval, y: float = None): + if y is None: + return Interval(lo(x), mid(x)), Interval(mid(x), hi(x)) + + x1, x2 = Interval(lo(x), hi(x)), Interval( + lo(x), hi(x)) # TODO: implement copy method + y_in_x = contain(x, y) # x contain y + if x.unsized: + if ~y_in_x: + return x1, x2 + return Interval(lo=lo(x), hi=y), Interval(lo=y, hi=hi(x)) + else: + pass + # x1[y_in_x] = Interval(lo(x)[y_in_x],hi=y) + # x2[y_in_x] = Interval(y,hi=hi(x)[y_in_x]) + return x1, x2 + + +def reconstitute(x_: Interval): + x = intervalise(x_) + d = len(x.shape) #  dimension of the subtiling ==1 if scalar, ==2 if 1d array + if d == 1: + return Interval(lo=numpy.min(x.lo), hi=numpy.max(x.hi)) + elif d == 2: + return Interval(lo=numpy.min(x.lo, axis=1), hi=numpy.max(x.hi, axis=1)) + else: + print( + '!! Subtiling not yet supported for interval arrays of dimension 2 or larger.') + return x + + +def space_product(x_: Union[ndarray, Interval], y_: Union[ndarray, + Interval]): return asarray(tuple(product(x_, y_))) + + +def bisect(x_: Interval, i: int = None): + """ + :x_: Interval of shape (n,) + + Bisect the largest box if i is None. + """ + x = intervalise(x_) + if x.scalar: + mid_x = mid(x) + return Interval(lo(x), mid_x), Interval(mid_x, hi(x)) + if i is not None: + split_index = i + else: + w = width(x) + split_index = argmax(w) + d = x.shape[0] + n = [0]*d + # ex: (0,0,2,0,0,0) if interval of dim 6 has third dimension bisected + n[split_index] = 2 + x_bisect = subintervalise(x, n=tuple(n)) + return x_bisect[0], x_bisect[1] + +##################################################################################### +# types.py +##################################################################################### +# Interval to bool methods, Unary. + + +def is_Interval(x: Any) -> bool: return x.__class__.__name__ == 'Interval' +def is_not_Interval(x: Any) -> bool: return x.__class__.__name__ != 'Interval' + +##################################################################################### +################################# neural_networks.py ################################ +##################################################################################### + + +def dot(x: Interval, y: Interval): return sum(x*y) + + +def rowcol_old(W, x): + ''' + (m,n) x (n,1) -> (m,1) + (m,n) x (n,p) -> (m,p) + (1,n) x (n,1) -> (1,1) + ''' + s = W.shape + x_shape = x.shape + if x_shape[0] == 1: # x is row and must be either squeezed or transposed + x_ = x[0, :] + if x_shape[1] == 1: # this is the correct shape + x_ = x[:, 0] + if len(x_shape) == 1: # x can be row or column (n,) + x_ = x + y = [] + for i in range(s[0]): + y.append(dot(W[i], x_)) + return intervalise(y) + + +def rowcol_W_x(W, x): + ''' + Row by column multiplication between a matrix W and a column vector x. + + (m,n) x (n,1) -> (m,1) + (1,n) x (n,1) -> (1,1) + The following cases are also accepted even though mathematically impossible + (m,n) x (n,) -> (m,1) + (1,n) x (n,1) -> (1,1) + (1,n) x (1,n) -> (1,1) + ''' + m, n = W.shape + x_shape = x.shape + if not ((n == x_shape[0]) | (n == x_shape[1])): + raise ValueError(f'Incompatible shapes ({m},{n}) x ({ + x_shape[0]},1) -> (?,?). Inner sizes must be same, {x_shape[1]} is different from {n}.') + if x_shape[0] == 1: # x is row and must be either squeezed or transposed + x_ = x[0, :] + if x_shape[1] == 1: # this is the correct shape + x_ = x[:, 0] + if len(x_shape) == 1: # x can be row or column (n,) + x_ = x + y = numpy.empty((m, 2)) + for i in range(m): + inner_product = dot(W[i], x_) + y[i, 0] = inner_product.lo + y[i, 1] = inner_product.hi + ylo = numpy.expand_dims(y[..., 0], axis=1) + yhi = numpy.expand_dims(y[..., 1], axis=1) + return Interval(ylo, yhi) + + +def rowcol_xT_WT(x, W): + ''' + Row by column multiplication between the row vector xT and the matrix transpose WT. + (1,n) x (n,m) -> (1,m) + (1,n) x (n,1) -> (1,1) + The following cases are also accepted even though mathematically impossible + (,n) x (n,m) -> (1,m) + (n,1) x (n,m) -> (1,1) + ''' + n, m = W.shape + x_shape = x.shape + if not ((n == x_shape[0]) | (n == x_shape[1])): + raise ValueError(f'Incompatible shapes (1,{x_shape[1]}) x ({n},{ + m}) -> (?,?). Inner sizes must be same, {x_shape[1]} is different from {n}.') + if x_shape[0] == 1: # this is the correct shape + x_ = x[0, :] + if x_shape[1] == 1: # x is row and must be either squeezed or transposed + x_ = x[:, 0] + if len(x_shape) == 1: # x can be row or column (n,) + x_ = x + y = numpy.empty((m, 2)) + for i in range(m): + inner_product = dot(x_, W[..., i]) + y[i, 0] = inner_product.lo + y[i, 1] = inner_product.hi + ylo = numpy.expand_dims(y[..., 0], axis=0) + yhi = numpy.expand_dims(y[..., 1], axis=0) + return Interval(ylo, yhi) + + +def matmul(A, B): + ''' + (m,n) x (n,p) -> (m,p) + (1,n) x (n,1) -> (1,1) + ''' + m, na = A.shape + nb, p = B.shape + if na != nb: + raise ValueError(f'Incompatible shapes ({m},{na}) x ({nb},{ + p}) -> (?,?). Inner sizes must be same, {na} is different from {nb}.') + C = numpy.empty((m, p, 2)) + for i in range(m): + for j in range(p): + inner_product = dot(A[i], B[..., j]) + C[i, j, 0] = inner_product.lo + C[i, j, 1] = inner_product.hi + return intervalise(C) + + +def transpose(x: Interval): # not efficient it creates a new object in memory + ''' + Input an interval of shape (m,n) returns an interval of shape (n,m). + ''' + return Interval(x.val[..., 0], x.val[..., 1]) + + +def squeeze(x: Interval): # not efficient it creates a new object in memory + return Interval(numpy.squeeze(x.lo), numpy.squeeze(x.hi)) + +################################ activation_functions.py ############################# + + +def relu_nointerval(x: ndarray): + positive = x > 0 + output = numpy.zeros(x.shape) + output[positive] = x[positive] + return output + + +def relu_deriv(x: ndarray): + positive = x > 0 + output = numpy.zeros(x.shape) + output[positive] = 1 + return output + + +def relu(x: Interval): + if is_not_Interval(x): + return relu_nointerval(x) + case_1 = x.hi < 0 + x_lo = x.val[..., 0].T + x_hi = x.val[..., 1].T + x_lo[case_1] = 0 + x_hi[case_1] = 0 + case_3 = (x_lo < 0) & (numpy.logical_not(case_1)) + x_lo[case_3] = 0 + relu_x = Interval(lo=x_lo, hi=x_hi) + return relu_x + + +def relu_deriv_interval(x: Interval): + if is_not_Interval(x): + return relu_deriv(x) + x_lo = x.val[..., 0].T + x_hi = x.val[..., 1].T + case_1 = x_hi < 0 + d_lo = numpy.zeros(x_lo.shape) + d_hi = numpy.ones(x_hi.shape) + d_hi[case_1] = 0 + # case_3 = (x_lo<0) & (np.logical_not(case_1)) + case_2 = x_lo > 0 + d_lo[case_2] = 1 + d_relu_x = Interval(lo=d_lo, hi=d_hi) + return d_relu_x + + +def sigmoid(x): return 1/(1+exp(-x)) +def sigmoid_deriv(x): return sigmoid(x)*(1-sigmoid(x)) + +# def tanh_(x): return np.tanh(x) + + +def tanh(x: Interval): return (exp(2*x)-1)/(exp(2*x)+1) + + +def tanh(x: Interval): + r = -1 + s = 1 + u = 1 + t = 1 + return s/u - (s*t/u**2 - r/u)/(t/u + exp(2*x)) +# def cot(x): return 1/np.tan(x) +# def tanh(x): return -(1/(cot(np.arctan(x)/2)**2)) +def cosh(x: Interval): return (1+exp(-2*x))/(2*exp(-x)) +def tanh_deriv(x: Interval): return (1/cosh(x))**2 diff --git a/intervals/number.py b/src/intervals/number.py similarity index 100% rename from intervals/number.py rename to src/intervals/number.py diff --git a/src/intervals/plotting.py b/src/intervals/plotting.py new file mode 100644 index 0000000..507431c --- /dev/null +++ b/src/intervals/plotting.py @@ -0,0 +1,53 @@ +from __future__ import annotations +import numpy as np +import matplotlib.pyplot as plt + +""" +-------------------------- +Editted by Leslie Feb 2024 +MIT License +-------------------------- +""" + +def plot_intervals(x, y_i, **kwargs): + """ plot intervals vertically + + args: + x: array-like precise values + x-axis coordinates + y_i: array-like Interval objects + array of intervals + """ + + fig, ax = plt.subplots() + + def basic_plot(x, y_i, **kwargs): + ax.plot([x, x], [y_i.hi, y_i.lo], 'blue', **kwargs) + if np.any(y_i.lo == y_i.hi): + sc_x = x[y_i.lo == y_i.hi] + sc_y = y_i[y_i.lo == y_i.hi].lo + ax.scatter(sc_x, sc_y, c='blue', **kwargs) + + if len(x.shape) > 1: + for xx, interval in zip(x, y_i): + basic_plot([xx, xx], [interval.hi, interval.lo]) + else: + basic_plot(x, y_i) + return ax + + +def plot_lower_bound(x, y_i, **kwargs): + """ plot lower bound of intervals + + args: + x: array-like + x-axis coordinates + y_i: array-like + array of intervals + """ + + fig, ax = plt.subplots() + ax.scatter(x, y_i.lo, label='lower bound', **kwargs) + ax.legend() + + diff --git a/intervals/random.py b/src/intervals/random.py similarity index 100% rename from intervals/random.py rename to src/intervals/random.py diff --git a/tests/activation_test.ipynb b/tests/activation_test.ipynb new file mode 100644 index 0000000..7d526ca --- /dev/null +++ b/tests/activation_test.ipynb @@ -0,0 +1,382 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 17, + "id": "340b20c6-58ae-4175-a4cf-757905bb8c47", + "metadata": {}, + "outputs": [], + "source": [ + "from intervals.number import Interval as I\n", + "from intervals.methods import (lo,hi,mid,rad,width,intervalise, exp)\n", + "from intervals.random import uniform_endpoints \n", + "from intervals.plotting import plot_intervals, plot_lower_bound\n", + "from intervals.mat_features import create_interval\n", + "from intervals.activation import sigmoid, tanh" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "808b0878-876e-42b3-b603-b41b4b941407", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "38fea7d5-233c-409d-8cf8-a5ff3bf1b95e", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "725c8f94-a582-4a44-8296-ca9de529dad3", + "metadata": {}, + "outputs": [], + "source": [ + "x = np.arange(4)\n", + "y = np.arange(4)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "957cbb39-6781-4bdc-b546-8fddd0ba2a54", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0.0,0.0]\n", + "[0.5,1.5]\n", + "[1.0,3.0]\n", + "[1.5,4.5]" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "y_i = create_interval(matrix=y, half_width=0.5)\n", + "y_i" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "933a61b0-ab3b-4084-9e50-6ff99a41ec66", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "009bae25-a281-47bf-8330-556878ffcdb8", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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e1nfvn0VEBCg3//+95o5c6ZHD49el2N45MgRXX311brpppt01113Vbut2ccQ0qRJk7Rly5Zq+6RIUlJSkpKSktzvBw8erB49euiVV17RE0880dBl1sqIESPcr/v27avExES1b99eH374oSZMmGBiZQ3j9ddf14gRIxQbG3vONk3p+DUlPh9uHnrooWpTtSR16tSpRp8VExNzVo/virtoYmJizrnNzztRlZaWKisr65zbXKi67PObb76pli1batSoUbX+eYmJiZLKzho0xh/GCzmmiYmJKi0t1YEDBxQfH3/W+piYGDkcDmVnZ1c6e5ORkdFgx6sqtd3Ho0eP6oorrtDgwYP16quv1vrnNfYxrEpkZKRsNttZd6ZV928fExNTq/ae4t5773XfXFDb/3v39/dX//79tWfPngaqrv5ERESoW7du56y1qR4/STp48KCWLFlS67OdTen4VRyHjIwMtW7d2r08IyND/fr1q3KbunyP66Teeu/4kPN1KM7IyHAve+WVV4ywsDCjqKioys+q6FC8bt0697LFixd7VIdil8tldOzY0XjooYfqtP2qVasMScZPP/1Uz5XVv3fffdewWq1GVlZWlesrOhR//PHH7mU7duzw6A7Fhw8fNrp27WrcfPPNRmlpaZ0+w1OO4aBBg4x7773X/d7pdBpt2rSptkPxddddV2lZUlKSx3ZIdblcxqRJk4zY2Fhj165ddfqM0tJSIz4+3njwwQfrubr6l5eXZzRv3tz4+9//XuX6pnb8zjR9+nQjJibGKCkpqdV2nnz8dI4Oxc8++6x7WU5OTo06FNfme1ynWuvtk3zAwYMHjR9//NGYMWOGERISYvz444/Gjz/+aOTl5RmGUfYfZe/evY2rrrrK2Lhxo7Fo0SKjVatWxtSpU92fsWbNGiM+Pt44fPiwe9nVV19t9O/f31izZo2xatUqo2vXrsaYMWMaff/OZcmSJYYkY/v27WetO3z4sBEfH2+sWbPGMAzD2LNnj/H4448b69atM/bv3298/vnnRqdOnYwhQ4Y0dtnntXr1auP55583Nm7caOzdu9d49913jVatWhljx451t/n5/hmGYdx9991Gu3btjGXLlhnr1q0zkpKSjKSkJDN24bwOHz5sdOnSxRg2bJhx+PBhIy0tzf04s01TOYYffPCBYbfbjbfeesvYtm2bMXHiRCMiIsJ9h+Jtt91mTJkyxd3+u+++M/z8/Ixnn33W2L59uzF9+nTD39/f2Lx5s1m7UK177rnHCA8PN1asWFHpWBUUFLjb/HwfZ8yYYSxevNjYu3evsX79euPmm282AgMDja1bt5qxC9V66KGHjBUrVhj79+83vvvuOyM5OdmIjIw0jh07ZhhG0z9+FZxOp9GuXTvj4YcfPmtdUzt+eXl57r91kozZs2cbP/74o3Hw4EHDMAxj1qxZRkREhPH5558bmzZtMq6//nqjY8eORmFhofszrrzySuPFF190vz/f97g+EG5qYdy4cYaksx7Lly93tzlw4IAxYsQIo1mzZkZkZKTx0EMPVUruy5cvNyQZ+/fvdy87ceKEMWbMGCMkJMQICwszxo8f7w5MnmDMmDHG4MGDq1y3f//+Sv8Ghw4dMoYMGWK0aNHCsNvtRpcuXYw//elPRk5OTiNWXDPr1683EhMTjfDwcCMwMNDo0aOH8dRTT1U6y/bz/TMMwygsLDT+8Ic/GM2bNzeCgoKMX/3qV5XCgid58803q/xv9syTtk3tGL744otGu3btjICAAGPQoEHG999/7143dOhQY9y4cZXaf/jhh0a3bt2MgIAAo1evXsaCBQsaueKaO9exevPNN91tfr6PDzzwgPvfIzo62rjmmmuMDRs2NH7xNTB69GijdevWRkBAgNGmTRtj9OjRxp49e9zrm/rxq7B48WJDkrFz586z1jW141fxN+vnj4p9cLlcxmOPPWZER0cbdrvdGDZs2Fn73b59e2P69OmVllX3Pa4PFsMwjPq7yAUAAGAuxrkBAABehXADAAC8CuEGAAB4FcINAADwKoQbAADgVQg3AADAqxBuAACAVyHcAAAAr0K4AQAAXoVwAwAAvArhBgAAeBXCDYAm7/jx44qJidFTTz3lXrZ69WoFBARo6dKlJlYGwAxMnAnAKyxcuFA33HCDVq9erfj4ePXr10/XX3+9Zs+ebXZpABoZ4QaA15g0aZKWLFmigQMHavPmzfrhhx9kt9vNLgtAIyPcAPAahYWF6t27t1JTU7V+/Xr16dPH7JIAmIA+NwC8xt69e3X06FG5XC4dOHDA7HIAmIQzNwC8gsPh0KBBg9SvXz/Fx8drzpw52rx5s6KioswuDUAjI9wA8Ap/+tOf9PHHH+unn35SSEiIhg4dqvDwcH355ZdmlwagkXFZCkCTt2LFCs2ZM0f/+te/FBYWJqvVqn/961/69ttv9fLLL5tdHoBGxpkbAADgVThzAwAAvArhBgAAeBXCDQAA8CqEGwAA4FUINwAAwKsQbgAAgFch3AAAAK9CuAEAAF6FcAMAALwK4QYAAHgVwg0AAPAq/x/DBOfe9DPvkQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# draw up a regular sigmoid function\n", + "\n", + "# Import matplotlib, numpy and math \n", + "import matplotlib.pyplot as plt \n", + "import numpy as np \n", + "import math \n", + "\n", + "x = np.linspace(-10, 10, 100) \n", + "\n", + "def regu_logistic(x): \n", + " z = 1/(1 + np.exp(-x)) \n", + " return z\n", + "\n", + "plt.plot(x, z) \n", + "plt.xlabel(\"x\") \n", + "plt.ylabel(\"Sigmoid(X)\") \n", + "\n", + "plt.show() \n" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "f956853f-12a2-4973-b7de-9d54c1972fb2", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "afa9ec48-fc3a-42b1-a4b5-11b90f4d39d1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(-2.5, 0.07585818002124355)\n" + ] + } + ], + "source": [ + "x0 = -2.5\n", + "y0 = regu_logistic(x=x0)\n", + "print(f'({x0}, {y0})')" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "6fe4466a-4b27-4705-b0ff-d435a57ddd11", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(2.5, 0.9241418199787566)\n" + ] + } + ], + "source": [ + "x0 = 2.5\n", + "y0 = regu_logistic(x=x0)\n", + "print(f'({x0}, {y0})')" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "061df87e-d1a0-4f75-889c-4d4a76d333dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[-2.5,2.5]" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test_i = I(-2.5, 2.5)\n", + "test_i" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "d6c33165-f954-4e74-ac20-114594397625", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0.07585818002124355,0.9241418199787566]" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "y_test_i = sigmoid(test_i)\n", + "y_test_i" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0f17d3f8-39e0-408f-9a72-b189e86c22d2", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "b856e442-7ec7-4b6c-81f7-c4437c9e1005", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "base vector with shape (3,)\n" + ] + }, + { + "data": { + "text/plain": [ + "[0.9,1.1]\n", + "[2.7,3.3000000000000003]\n", + "[4.5,5.5]" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "vec = create_interval(matrix=np.array([1, 3, 5]))\n", + "print(\"base vector with shape\", vec.shape)\n", + "vec" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "5c735ddc-5896-4926-9adf-6a1072936e1d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0.7109495026250039,0.7502601055951177]\n", + "[0.9370266439430035,0.9644288107273639]\n", + "[0.9890130573694068,0.995929862284104]\n" + ] + } + ], + "source": [ + "print(sigmoid(vec))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9fcdd40c-e078-4da2-b20f-4dec852ec8aa", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f78d252b-3a50-4ecf-9997-2ae3076bd44b", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7e90f111-71e3-498c-8315-29232eecdcfb", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a83d3079-122f-4025-9fdd-d5a563354c83", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "18307e47-7967-4ad6-9178-d667e3590eb3", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0ZYVFW1tbnHvuuVFfXx8zZ86MZcuWxWuvvTZaswEAVaassNiyZUu0trbG888/H0899VS89957cfnll0d3d/dozQcAVJEp5Sx+8sknB32+bt26mDlzZrS3t8dnP/vZ1MEAgOpTVli8X2dnZ0REzJgxY8g1PT090dPTM/B5V1fX0dwlADCOjfjFm319fbFixYq44IIL4qyzzhpyXVtbWzQ2Ng5cmpubR3qXAMA4N+KwaG1tjZdffjnWr19/xHWrV6+Ozs7OgUtHR8dI7xIAGOdG9FTIzTffHI8//nhs3bo1mpqajri2VCpFqVQa0XAAQHUpKyyKoohbbrklNmzYEJs3b45TTz11tOYCAKpQWWHR2toaDzzwQDz22GNRX18fe/bsiYiIxsbGOP7440dlQACgepT1Gou77747Ojs7Y/HixTF79uyBy4MPPjha8wEAVaTsp0IAAIbib4UAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQZkqlB4DxqigiDh7s/3jq1IiamsrOA1ANnLGAIRw8GDF9ev/lUGAAcGTCAgBI46kQADgGTJ0aceDAfz6uFGEBAMeAmpqIadMqPYWnQgCARMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANGWHxdatW+Oqq66KOXPmRE1NTTz66KOjMBYAUI3KDovu7u5YsGBBrF27djTmAQCq2JRyb3DFFVfEFVdcMRqzAABVruywKFdPT0/09PQMfN7V1TXadwkAVMiov3izra0tGhsbBy7Nzc2jfZcAQIWMelisXr06Ojs7By4dHR2jfZcA49rUqREHDvRfpk6t9DSQa9SfCimVSlEqlUb7bgCqRk1NxLRplZ4CRof3sQAA0pR9xuLAgQPx+uuvD3y+a9eu2LFjR8yYMSNOPvnk1OEAgOpSdli89NJLcfHFFw98vnLlyoiIWL58eaxbty5tMACg+pQdFosXL46iKEZjFgCgynmNBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQZkqlB4DxaurUiAMH/vMxAB9OWMAQamoipk2r9BQA1cVTIQBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAmimVHiBDb2/EH/4QsXt3xOzZERdeGDF5cqWnAoCJZ0RnLNauXRtz586Nurq6WLRoUbzwwgvZcw3br38dMXduxMUXR/zv//b/O3du//UAwNgqOywefPDBWLlyZaxZsya2bdsWCxYsiKVLl8a+fftGY74j+vWvIz7/+Yi33hp8/T/+0X+9uACAsVVTFEVRzg0WLVoU5557btx1110REdHX1xfNzc1xyy23xKpVqz709l1dXdHY2BidnZ3R0NAwsqmj/+mPuXM/GBWH1NRENDVF7NrlaREAOFrD/fld1hmLd999N9rb22PJkiX/+QaTJsWSJUviueeeO+xtenp6oqura9Alwx/+MHRUREQURURHR/86AGBslBUW77zzTvT29sasWbMGXT9r1qzYs2fPYW/T1tYWjY2NA5fm5uaRT/tfdu/OXQcAHL1R/3XT1atXR2dn58Clo6Mj5fvOnp27DgA4emX9uumJJ54YkydPjr179w66fu/evXHSSScd9jalUilKpdLIJxzChRf2v4biH//of9rj/Q69xuLCC9PvGgAYQllnLGpra2PhwoWxadOmgev6+vpi06ZN0dLSkj7ckUyeHHHnnf0f19QM/tqhz//v/7xwEwDGUtlPhaxcuTLuvffe+NnPfhavvvpq3HTTTdHd3R3XX3/9aMx3RNdcE/HwwxH/8z+Dr29q6r/+mmvGfCQAmNDKfufNa6+9Nv75z3/Gd77zndizZ0+cffbZ8eSTT37gBZ1j5ZprIq6+2jtvAsB4UPb7WBytrPexAADGzqi8jwUAwJEICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANKU/ZbeR+vQG312dXWN9V0DACN06Of2h71h95iHxf79+yMiorm5eazvGgA4Svv374/GxsYhvz7mfyukr68v3n777aivr4+a9/+986PQ1dUVzc3N0dHR4W+QfAh7NXz2qjz2a/js1fDZq+Ebzb0qiiL2798fc+bMiUmThn4lxZifsZg0aVI0NTWN2vdvaGhw4A2TvRo+e1Ue+zV89mr47NXwjdZeHelMxSFevAkApBEWAECaYyYsSqVSrFmzJkqlUqVHGffs1fDZq/LYr+GzV8Nnr4ZvPOzVmL94EwA4dh0zZywAgMoTFgBAGmEBAKQRFgBAmqoKi7Vr18bcuXOjrq4uFi1aFC+88MIR1z/00ENx+umnR11dXXz605+O3/72t2M0aeWVs1fr1q2LmpqaQZe6uroxnLZytm7dGldddVXMmTMnampq4tFHH/3Q22zevDnOOeecKJVKcdppp8W6detGfc7xoNy92rx58weOq5qamtizZ8/YDFxBbW1tce6550Z9fX3MnDkzli1bFq+99tqH3m4iPmaNZK8m6mPW3XffHfPnzx9486uWlpZ44oknjnibShxTVRMWDz74YKxcuTLWrFkT27ZtiwULFsTSpUtj3759h13/pz/9Kb70pS/FDTfcENu3b49ly5bFsmXL4uWXXx7jycdeuXsV0f8ubbt37x64vPnmm2M4ceV0d3fHggULYu3atcNav2vXrrjyyivj4osvjh07dsSKFSvia1/7WmzcuHGUJ628cvfqkNdee23QsTVz5sxRmnD82LJlS7S2tsbzzz8fTz31VLz33ntx+eWXR3d395C3maiPWSPZq4iJ+ZjV1NQUt99+e7S3t8dLL70Ul1xySVx99dXxyiuvHHZ9xY6pokqcd955RWtr68Dnvb29xZw5c4q2trbDrv/CF75QXHnllYOuW7RoUfGNb3xjVOccD8rdq/vvv79obGwco+nGr4goNmzYcMQ13/rWt4ozzzxz0HXXXnttsXTp0lGcbPwZzl4988wzRUQU//rXv8ZkpvFs3759RUQUW7ZsGXLNRH7M+m/D2SuPWf9xwgknFPfdd99hv1apY6oqzli8++670d7eHkuWLBm4btKkSbFkyZJ47rnnDnub5557btD6iIilS5cOuf5YMZK9iog4cOBAnHLKKdHc3HzEAp7oJupxdTTOPvvsmD17dlx22WXx7LPPVnqciujs7IyIiBkzZgy5xrHVbzh7FeExq7e3N9avXx/d3d3R0tJy2DWVOqaqIizeeeed6O3tjVmzZg26ftasWUM+X7tnz56y1h8rRrJX8+bNi5/+9Kfx2GOPxS9+8Yvo6+uL888/P956662xGLmqDHVcdXV1xb///e8KTTU+zZ49O3784x/HI488Eo888kg0NzfH4sWLY9u2bZUebUz19fXFihUr4oILLoizzjpryHUT9THrvw13rybyY9bOnTtj+vTpUSqV4sYbb4wNGzbEGWeccdi1lTqmxvyvmzL+tLS0DCre888/Pz71qU/FPffcE9/97ncrOBnVbN68eTFv3ryBz88///x444034o477oif//znFZxsbLW2tsbLL78cf/zjHys9yrg33L2ayI9Z8+bNix07dkRnZ2c8/PDDsXz58tiyZcuQcVEJVXHG4sQTT4zJkyfH3r17B12/d+/eOOmkkw57m5NOOqms9ceKkezV+x133HHxmc98Jl5//fXRGLGqDXVcNTQ0xPHHH1+hqarHeeedN6GOq5tvvjkef/zxeOaZZ6KpqemIayfqY9Yh5ezV+02kx6za2to47bTTYuHChdHW1hYLFiyIO++887BrK3VMVUVY1NbWxsKFC2PTpk0D1/X19cWmTZuGfG6ppaVl0PqIiKeeemrI9ceKkezV+/X29sbOnTtj9uzZozVm1Zqox1WWHTt2TIjjqiiKuPnmm2PDhg3x9NNPx6mnnvqht5mox9ZI9ur9JvJjVl9fX/T09Bz2axU7pkb1paGJ1q9fX5RKpWLdunXFX/7yl+LrX/968ZGPfKTYs2dPURRFcd111xWrVq0aWP/ss88WU6ZMKX7wgx8Ur776arFmzZriuOOOK3bu3Fmp/8KYKXevbrvttmLjxo3FG2+8UbS3txdf/OIXi7q6uuKVV16p1H9hzOzfv7/Yvn17sX379iIiih/+8IfF9u3bizfffLMoiqJYtWpVcd111w2s/9vf/lZMnTq1+OY3v1m8+uqrxdq1a4vJkycXTz75ZKX+C2Om3L264447ikcffbT461//WuzcubO49dZbi0mTJhW///3vK/VfGDM33XRT0djYWGzevLnYvXv3wOXgwYMDazxm9RvJXk3Ux6xVq1YVW7ZsKXbt2lX8+c9/LlatWlXU1NQUv/vd74qiGD/HVNWERVEUxY9+9KPi5JNPLmpra4vzzjuveP755we+dtFFFxXLly8ftP5Xv/pV8clPfrKora0tzjzzzOI3v/nNGE9cOeXs1YoVKwbWzpo1q/jc5z5XbNu2rQJTj71DvxL5/suh/Vm+fHlx0UUXfeA2Z599dlFbW1t8/OMfL+6///4xn7sSyt2r733ve8UnPvGJoq6urpgxY0axePHi4umnn67M8GPscPsUEYOOFY9Z/UayVxP1MeurX/1qccoppxS1tbXFxz72seLSSy8diIqiGD/HlD+bDgCkqYrXWAAA1UFYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABp/h9M/2u+nRF6fgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# plot a vector interval\n", + "_ = plot_intervals(x, y_i)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "7ad57e12-fcd2-4279-bd3e-7864dd355870", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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/k6cOKGHHnpIvXv3VmRkpBITE5WZmalDhw4FbKNr16419uHs2bMbtL/G6lGq23Owpe5DSbW+Bh0Oh5566il/TVPsw/r0t3PnTv3617/2j++5556rtW7u3Lnq2rWrwsPDlZKSUuO94tixY8rKytLFF1+sdu3a6de//rXKy8vr34wJYatXrzb/9m//Zt566y0jySxfvvyc9X/+859NVFSUycvLM/v27TNLly417dq1M//93//tr/nggw9MWFiYmTNnjtm1a5d59NFHTZs2bcwnn3zir5k9e7ZxuVxmxYoV5n//93/NLbfcYpKTk833339vfX///M//bObOnWu2b99uPv30UzNhwgTjcrnMwYMH/TXjx483w4cPN2VlZf7pu+++a9DeGqu/RYsWmejo6ICxu93ugO001f5rrB4PHz4c0N+OHTtMWFiYWbRokb+mqfZhenq6WbRokdmxY4cpKSkxv/zlL03nzp1NZWXlWddpqNfY8OHDTd++fc3mzZvN+++/b7p162bGjRtnfX8VFRUmLS3NvP766+azzz4zRUVFZtCgQWbAgAEB2+nSpYv5wx/+ELAPz/W4NvVoTN2egy11HxpjAvoqKyszCxcuNA6Hw+zbt89f0xT7sD79bd261UyfPt0sXbrUxMfHm2effbZGTV5enmnbtq1ZuHCh2blzp7nrrrtM+/btTXl5ub/m3nvvNUlJSSY/P99s27bNXHPNNWbw4MH17iWkA8sP1eXNIDU11UyfPj1g3rRp08yQIUP892+77TYzcuTIgJqUlBRzzz33GGOMqa6uNvHx8eapp57yL6+oqDBOp9MsXbr0Ars4u4bq78dOnjxpoqKizKuvvuqfN378eDN69OgLGW7QGqq/RYsWGZfLddZtNNf+M6bx9uGzzz5roqKiAv5ANcc+NMaYr7/+2kgyhYWFZ61piNfYrl27jCTz17/+1V/zzjvvGIfDYb766quGbClAQ/RXm61btxpJ5ssvv/TP69KlS61vJI2toXo833Mw1Pbh6NGjzS9+8YuAec2xD+vS3w+dbYyDBg0yWVlZ/vunTp0yiYmJJicnxxhz+jXZpk0bs2zZMn/Np59+aiSZoqKieo09pD8SClZVVVWNjwciIiK0detWnThxQpJUVFSktLS0gJr09HQVFRVJkkpLS+V2uwNqXC6XUlJS/DXNpS79/djRo0d14sQJxcTEBMwvKChQbGysevToocmTJ+vw4cONNu66qmt/lZWV6tKli5KSkjR69Gjt3LnTv8zm/SfVbx8uWLBAY8eOVWRkZMD85tiHHo9Hkmo8n36oIV5jRUVFat++va6++mp/TVpamlq1aqUtW7Y0WD8/1hD9nW27DodD7du3D5g/e/ZsXXzxxerfv7+eeuopnTx5sv6Dr6OG7PFcz8FQ2ofl5eV6++23NXHixBrLmnof1qW/8zl+/LiKi4sD/g1atWqltLQ0/79BcXGxTpw4EVDTs2dPde7cud5/SwksP5Cenq5XXnlFxcXFMsZo27ZteuWVV3TixAl9++23kiS32624uLiA9eLi4uR2u/3Lz8w7W01zqUt/P/bQQw8pMTEx4Ek3fPhwLV68WPn5+XryySdVWFioESNG6NSpU03VSq3q0l+PHj20cOFCrVy5Uq+99pqqq6s1ePBgHTx4UJLd+08Kfh9u3bpVO3bs0KRJkwLmN8c+rK6u1tSpUzVkyBD16tXrrHUN8Rpzu92KjY0NWN66dWvFxMQ02n5sqP5+7NixY3rooYc0bty4gB+d+9d//Vfl5eVpw4YNuueee/TEE0/owQcfbJhmzqIhezzfczCU9uGrr76qqKgo/eM//mPA/Kbeh3Xt73y+/fZbnTp16ryvwbZt29YI2RfytzQkfq25ocyYMUNut1vXXHONjDGKi4vT+PHjNWfOnPP+7HVLEGx/s2fPVl5engoKCgL+Vz927Fj/7d69e6tPnz66/PLLVVBQoJtuuqlJeqlNXfpLTU1Vamqqf53Bgwfryiuv1IsvvqjHHnusuYZeZ8HuwwULFqh3794aNGhQwPzm2IdZWVnasWOHNm3a1Cjbb26N0d+JEyd02223yRijefPmBSybNm2a/3afPn3Utm1b3XPPPcrJyWm0y6c3ZI82/h1prOfowoULdfvtt9c4OtrU+7ClvwZb/rtwA4qIiNDChQt19OhRffHFF9q/f7+6du2qqKgodezYUZIUHx9f4yzn8vJyxcfH+5efmXe2muZSl/7OePrppzV79my9++676tOnzzm3e9lll+mSSy7R3r17G3P45xVMf2e0adNG/fv394/d5v0nBdejz+dTXl5erYehf6yx9+GUKVO0atUqbdiwQZ06dTpnbUO8xuLj4/X1118HLD958qS+++67RtmPDdnfGWfCypdffql169YFHF2pTUpKik6ePKkvvviiXj2cT2P0+EM/fg6Gwj6UpPfff1+7d++ucZSzNo25D4Pp73wuueQShYWFnfc1ePz4cVVUVJy1JlgEllq0adNGnTp1UlhYmPLy8vQP//APAf9Dz8/PD6hft26d/3/tycnJio+PD6jxer3asmVLwP/sm9O5+pOkOXPm6LHHHtOaNWsCPj8+m4MHD+rw4cNKSEhozGHX2fn6+6FTp07pk08+8Y+9Jew/qW49Llu2TFVVVfrNb35z3u011j40xmjKlClavny51q9fr+Tk5POu0xCvsdTUVFVUVKi4uNhfs379elVXVyslJaUhWpPUOP1Jfw8re/bs0XvvvaeLL774vNstKSlRq1atanyMcqEaq8cf+/FzsKXvwzMWLFigAQMGqG/fvufdbmPsw/r0dz5t27bVgAEDAv4NqqurlZ+f7/83GDBggNq0aRNQs3v3bu3fv7/+f0vrdapuC3HkyBGzfft2s337diPJPPPMM2b79u3+M+0ffvhhc8cdd/jrd+/ebf7yl7+Y//u//zNbtmwxY8aMMTExMaa0tNRf88EHH5jWrVubp59+2nz66adm1qxZtX7lsn379mblypXm448/NqNHj26Ur8U2Rn+zZ882bdu2Nf/1X/8V8FW7I0eO+B9z+vTppqioyJSWlpr33nvP/PznPzfdu3c3x44ds76/f//3fzdr1641+/btM8XFxWbs2LEmPDzc7Ny5M+DfoCn2X2P1eMa1115rxowZU+tjNtU+nDx5snG5XKagoCDg+XT06FF/zR133GEefvhh//2Geo0NHz7c9O/f32zZssVs2rTJdO/evcG/EtsY/R0/ftzccsstplOnTqakpCRgu1VVVcYYYz788EPz7LPPmpKSErNv3z7z2muvmY4dO5rMzMwG7a+xeqzrc7Cl7sMzPB6Pueiii8y8efNqPG5T7cP69FdVVeX/u5SQkGCmT59utm/fbvbs2eOvycvLM06n0+Tm5ppdu3aZu+++27Rv3z7gMhH33nuv6dy5s1m/fr3Ztm2bSU1NNampqfXuJaQDy4YNG4ykGtP48eONMae/VnfDDTf463ft2mX69etnIiIiTHR0tBk9erT57LPPamz3jTfeMFdccYVp27atueqqq8zbb78dsLy6utrMmDHDxMXFGafTaW666Saze/fuFtFfly5dat3mrFmzjDHGHD161AwbNsx07NjRtGnTxnTp0sXcddddNa5lYmt/U6dONZ07dzZt27Y1cXFx5pe//KX56KOPAmqaav81Vo/GGPPZZ58ZSebdd9+tsawp92FtvUkKuCbMDTfc4O/3jIZ4jR0+fNiMGzfOtGvXzkRHR5s777zTH7xt7q+0tPSs292wYYMxxpji4mKTkpJiXC6XCQ8PN1deeaV54oknGjxwNlaPdX0OttR9eMaLL75oIiIiTEVFRY1lTbUP69Pf2Z6DP/xbZIwx//mf/+n/ezpo0CCzefPmgOXff/+9+Zd/+RfToUMHc9FFF5lf/epXpqysrN69OP5/QwAAANbiHBYAAGA9AgsAALAegQUAAFiPwAIAAKxHYAEAANYjsAAAAOsRWAAAgPUILAAAwHoEFgAAYD0CCwAAsB6BBQAAWI/AAgAArPf/AHQlBelcujxTAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# plot scaler interval\n", + "plot_intervals(x[2], y_i[2])" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "transformer", + "language": "python", + "name": "transformer" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tests/interval_objects_initialisation.ipynb b/tests/interval_objects_initialisation.ipynb new file mode 100644 index 0000000..f57a421 --- /dev/null +++ b/tests/interval_objects_initialisation.ipynb @@ -0,0 +1,561 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "2f691393-4806-4e64-8c6a-f05e6edf88dc", + "metadata": {}, + "outputs": [], + "source": [ + "from intervals.number import Interval as I\n", + "from intervals.methods import (lo,hi,mid,rad,width,intervalise)\n", + "from intervals.random import uniform_endpoints" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "46392979-d6f8-469d-96ab-3597b32c65d7", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "b800e6a1-73ad-48e6-9b71-0b427d448db5", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "markdown", + "id": "6980eb8f-d0f4-42a4-a5fa-1ccb1f5416c7", + "metadata": { + "jp-MarkdownHeadingCollapsed": true, + "tags": [] + }, + "source": [ + "# scalar interval tests" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f6b628f4-f39d-4d74-84b7-74b7d7bf87fc", + "metadata": {}, + "outputs": [], + "source": [ + "x=I(1,2)\n", + "print(x)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6ac113c-9f19-43b2-b830-bbf9f0f45a53", + "metadata": {}, + "outputs": [], + "source": [ + "y=I(-3,-2)\n", + "print(y)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c98a9989-3d86-4b9b-b2dd-21cb732eb73a", + "metadata": {}, + "outputs": [], + "source": [ + "x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cbab70b8-9665-4893-8144-4a25b1e3c91e", + "metadata": {}, + "outputs": [], + "source": [ + "x+y" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "84a93bee-7aa7-4a03-812e-09e3b60d3dbb", + "metadata": {}, + "outputs": [], + "source": [ + "x*y" + ] + }, + { + "cell_type": "markdown", + "id": "7c9c22db-c883-4750-87bf-054915897b22", + "metadata": { + "jp-MarkdownHeadingCollapsed": true, + "tags": [] + }, + "source": [ + "# vector interval tests" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6d202a1-e089-4625-ba29-5f9f18d67182", + "metadata": {}, + "outputs": [], + "source": [ + "x = I(lo=[1,2,3,4],hi=[2,3,4,5])\n", + "print(x)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "185ada2e-adc2-4f0c-99c9-84afa1fea583", + "metadata": {}, + "outputs": [], + "source": [ + "y = I(lo=[-1,-2,-3,-4], hi=[-1,-1,-1,-1])\n", + "print(y)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "76832dfd-190e-421b-b137-c84fe928635e", + "metadata": {}, + "outputs": [], + "source": [ + "x.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9655b91c-fa6c-4d0c-9a7f-acc2fc8ff9b9", + "metadata": {}, + "outputs": [], + "source": [ + "# dir(x)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "722ee021-7a8e-42bf-b99e-20694036277a", + "metadata": {}, + "outputs": [], + "source": [ + "x * y" + ] + }, + { + "cell_type": "markdown", + "id": "05ee35d3-3415-443b-9751-19a7a49805cb", + "metadata": { + "tags": [] + }, + "source": [ + "# matrix interval initialisation tests" + ] + }, + { + "cell_type": "markdown", + "id": "7f96e186-39dd-4036-971a-91ca6400b10c", + "metadata": { + "tags": [] + }, + "source": [ + "#### Marco's initialisation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "70ecea53-9650-47c6-a156-454823bd3343", + "metadata": {}, + "outputs": [], + "source": [ + "# initialise a regular matrix of certain shape and transform it into an `interval matrix`\n", + "\n", + "a_matrix = np.array([[[1,2], [2,3], [4,5]],\n", + " [[-1,2],[-2,1],[3,5]],\n", + " [[0,2], [3,4], [6,8]]])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "232b9980-09d2-4593-a82f-75dba6c95d74", + "metadata": {}, + "outputs": [], + "source": [ + "a_matrix.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cd04fbb9-ab36-495b-b57d-d2fa39e1d3c2", + "metadata": {}, + "outputs": [], + "source": [ + "a_matrix" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4538f82a-a302-46e1-bb86-da9fac4557a8", + "metadata": {}, + "outputs": [], + "source": [ + "# Transform with Marco's default `intervalise` func\n", + "int_a_matrix = intervalise(a_matrix)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a348ab53-ebad-4716-ae9d-d5989454ed5f", + "metadata": {}, + "outputs": [], + "source": [ + "print(f'data type is: {type(int_a_matrix)}')\n", + "print(f'the value is:\\n{int_a_matrix}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7af1f872-0766-49db-93fe-4ce85569ac84", + "metadata": {}, + "outputs": [], + "source": [ + "# element-wise multiplication\n", + "\n", + "int_a_matrix * int_a_matrix" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1f6f4f5c-31c4-4d22-9254-e535991c0b83", + "metadata": {}, + "outputs": [], + "source": [ + "# test the \n", + "\n", + "a = np.array([[1, 2], [3, 4]])\n", + "a.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52c697e7-b0e0-4ad7-99cb-0fc5bfea60a6", + "metadata": {}, + "outputs": [], + "source": [ + "int_a = intervalise(a)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9b224cce-e633-4e3e-9302-dde146ec3673", + "metadata": {}, + "outputs": [], + "source": [ + "int_a.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a050fa75-1d6e-4196-8a25-ef2d4e6b5630", + "metadata": {}, + "outputs": [], + "source": [ + "# currently, for (4, 2) take the last dimension which shall be 2\n", + "\n", + "a_42 = np.array([[1,2],[2,3],[4,5],[5,6]])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2fa95c2-9e94-4701-91a5-17374700ff6e", + "metadata": {}, + "outputs": [], + "source": [ + "a_42.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "92d6204d-1378-45f0-b071-c6eb4e8de688", + "metadata": {}, + "outputs": [], + "source": [ + "a_42" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5d1c3ee2-0dec-4773-95ff-caef89d2d566", + "metadata": {}, + "outputs": [], + "source": [ + "int_a_42 = intervalise(a_42)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2763802e-5cbd-424a-8ea9-e93a7607c72d", + "metadata": {}, + "outputs": [], + "source": [ + "int_a_42.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c5cb8978-6f97-42f5-ac62-c6b7e2d9c180", + "metadata": {}, + "outputs": [], + "source": [ + "int_a_42" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b80aca89-8348-4fee-b8d8-08ff75e49101", + "metadata": {}, + "outputs": [], + "source": [ + "# somehow, it's weird that it also takes the first dimension" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cabc6404-60e9-436f-968d-3086e7bd35bf", + "metadata": {}, + "outputs": [], + "source": [ + "a_24 = np.array([[1,2,4,5],[2,3,5,6]])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ff3f9852-4bdd-4f41-9dda-fc35cbfc2c2d", + "metadata": {}, + "outputs": [], + "source": [ + "a_24.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fef3f9fd-6675-4065-b458-229aea28ffee", + "metadata": {}, + "outputs": [], + "source": [ + "a_24" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7b530b01-b585-463c-bf9e-3a824cf7cea6", + "metadata": {}, + "outputs": [], + "source": [ + "int_a_24 = intervalise(a_24)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29290e3b-a256-4e94-80be-db71ba4b5da0", + "metadata": {}, + "outputs": [], + "source": [ + "int_a_24.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9b16b7ee-a702-49b8-9776-602e7dac3b74", + "metadata": {}, + "outputs": [], + "source": [ + "int_a_24" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4f879d4e-37da-4e78-8579-ba00f21ca078", + "metadata": {}, + "outputs": [], + "source": [ + "# question, which dimension has the priority" + ] + }, + { + "cell_type": "markdown", + "id": "93c8db4b-11df-47e2-a65c-233c5914b0f1", + "metadata": {}, + "source": [ + "#### Leslie's initialisation\n", + "\n", + "- I'll first test a way to initialise an interval matrix, which is (2, m, n)\n", + "\n", + "- let's create a matrix whose shpae is (2,3,3)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "476b1e18-1a47-4b17-ad62-033202649f84", + "metadata": {}, + "outputs": [], + "source": [ + "from intervals.number import Interval as I\n", + "from intervals.methods import (lo,hi,mid,rad,width,intervalise)\n", + "from intervals.random import uniform_endpoints \n", + "from intervals.mat_features import consume_interval, create_interval, dot, rowcol, rowcol2, intvl_matmul" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "f794fd90-aaaa-4f57-b8e6-027fbeab7953", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[0, 1, 2],\n", + " [3, 4, 5],\n", + " [6, 7, 8]])" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "low = np.arange(9).reshape(3,3)\n", + "low" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "f1592880-326a-4664-8eca-d2c565503b7a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[10, 11, 12],\n", + " [13, 14, 15],\n", + " [16, 17, 18]])" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "high = np.arange(10, 19).reshape(3,3)\n", + "high" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "e7f2addd-33f0-4a92-a9c5-8d87515a0854", + "metadata": {}, + "outputs": [], + "source": [ + "intvl_matrix = consume_interval(low, high)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "b15e3073-8bcc-4ae6-9578-8f0d63f597a0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[ 0. 10.] [ 1. 11.] [ 2. 12.]\n", + "[ 3. 13.] [ 4. 14.] [ 5. 15.]\n", + "[ 6. 16.] [ 7. 17.] [ 8. 18.]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "intvl_matrix" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28a14109-ce40-4315-9738-5cd2635e66cf", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "gpflow", + "language": "python", + "name": "gpflow" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tests/intervals_to_pbox.ipynb b/tests/intervals_to_pbox.ipynb new file mode 100644 index 0000000..ec4d56b --- /dev/null +++ b/tests/intervals_to_pbox.ipynb @@ -0,0 +1,196 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "8f022430-dd8e-4ef8-8a87-d54da67b3367", + "metadata": {}, + "outputs": [], + "source": [ + "from intervals.number import Interval as I\n", + "from intervals.methods import *\n", + "from intervals.random import uniform_endpoints \n", + "from intervals.mat_features import *\n", + "from intervals.activation import sigmoid, tanh\n", + "from intervals.plotting import plot_intervals, plot_lower_bound\n", + "from intervals.methods import matmul\n", + "from intervals.activation import sigmoid, tanh" + ] + }, + { + "cell_type": "markdown", + "id": "ab4f2293-04c1-4171-b1ef-fd07234907bd", + "metadata": {}, + "source": [ + "# collect multiple intervals into a p-box" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "340af6c8-505c-436f-bc9d-e068cda8fe3f", + "metadata": {}, + "outputs": [], + "source": [ + "# p-box initialisation via the mixture method\n", + "n = 7\n", + "\n", + "weights = np.squeeze(np.random.dirichlet(np.ones(7),size=1))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66c76340-db20-4c76-9516-f6b60cfbb254", + "metadata": {}, + "outputs": [], + "source": [ + "weights" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "065128f7-d1c5-47be-9959-22e4351f478c", + "metadata": {}, + "outputs": [], + "source": [ + "sum(weights)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f14e7aab-2922-4bc3-b1e0-9cc5c5b7f6e8", + "metadata": {}, + "outputs": [], + "source": [ + "data = np.random.uniform(low=0.0, high=1.0, size=(7,2))\n", + "data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0547b681-47d3-486c-92ec-1fc8bd9b9236", + "metadata": {}, + "outputs": [], + "source": [ + "data_sorted = np.sort(data, axis=1)\n", + "data_sorted" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a28c275b-c490-4e64-994c-a7839faa0e0f", + "metadata": {}, + "outputs": [], + "source": [ + "data_i = intervalise(data)\n", + "data_i" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c129db6c-3c49-47c9-bd9d-166c89b0fde4", + "metadata": {}, + "outputs": [], + "source": [ + "_ = plot_intervals(x=np.arange(len(data_i)), y_i = data_i)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "94dc3031-9229-4988-9098-d2a84cb4c0fd", + "metadata": {}, + "outputs": [], + "source": [ + "x = np.linspace(-0.5, 1.5, 1000)\n", + "\n", + "def indicator_func(x, data_interval):\n", + " \"\"\" take a loop over all the extreme points \n", + " \n", + " args:\n", + " x: array, support of the distribution\n", + " data_interval: interval array, \n", + " \"\"\"\n", + " \n", + " lb = []\n", + " ub = []\n", + " \n", + " l_vertice = [*data_interval.lo]\n", + " u_vertice = [*data_interval.hi]\n", + " # vertice_sets = [*data_i.lo] + [*data_i.hi]\n", + " \n", + " for i in x:\n", + " lb.append(sum([*data_interval.lo] <= i))\n", + " ub.append(sum([*data_interval.hi] <= i))\n", + " return lb, ub" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "df363a3f-06e3-44f5-81d0-a45473f00d14", + "metadata": {}, + "outputs": [], + "source": [ + "lb, ub = indicator_func(x, data_i)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4775e660-3f86-4a8d-94b7-9c32fd7ca357", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots()\n", + "ax.plot(x, lb, label='lower bound')\n", + "ax.plot(x, ub, label='upper bound')\n", + "ax.set_title('the p-box')\n", + "ax.legend()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1a07b6ab-c26d-4188-8055-5e1a5c503ee7", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "81210e03-61ad-40bb-b023-f2a1533796bf", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tests/matrix_multiplication.ipynb b/tests/matrix_multiplication.ipynb new file mode 100644 index 0000000..e328037 --- /dev/null +++ b/tests/matrix_multiplication.ipynb @@ -0,0 +1,1048 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "bb67a0bf-f22e-4fde-9a2e-4c524333920c", + "metadata": { + "tags": [] + }, + "source": [ + "# interval matrix `calculation` test\n", + "\n", + "- test with simple 3 by 3 case\n", + "- current workflow will work as long as there is no shape as 2" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "f6e8b17c-ae64-4d94-9ed7-0628ebcc22dd", + "metadata": {}, + "outputs": [], + "source": [ + "from intervals.number import Interval as I\n", + "from intervals.methods import (lo,hi,mid,rad,width,intervalise, exp)\n", + "from intervals.random import uniform_endpoints \n", + "from intervals.mat_features import consume_interval, create_interval, dot, rowcol, rowcol2, intvl_matmul, consume_list" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "7778e3c6-c484-49d6-ad56-7d8f4b1fc45a", + "metadata": {}, + "outputs": [], + "source": [ + "from intervals.activation import sigmoid, tanh" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "eb56342b-b2d3-4fd7-85e1-a45f64e12b44", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "95064f3d-8f26-403a-97f2-ffc944bd4e5f", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "markdown", + "id": "bd29348b-91be-43b7-800b-741a918336b3", + "metadata": { + "tags": [] + }, + "source": [ + "#### mat @ mat" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "4d151557-6451-4e1e-8bf9-2d57c4d7950d", + "metadata": {}, + "outputs": [], + "source": [ + "a = np.arange(9).reshape(3,3)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "79da63ed-bd11-4117-8a7d-0826d873a200", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0. 0.] [0.9 1.1] [1.8 2.2]\n", + "[2.7 3.3] [3.6 4.4] [4.5 5.5]\n", + "[5.4 6.6] [6.3 7.7] [7.2 8.8]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v1 = create_interval(matrix=a, half_width=0.1)\n", + "v1" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "aa4e3c4d-7163-4ad3-8f17-2ab8f5b8039e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[12.15 18.15] [34.02 50.82] [55.89 83.49]\n", + "[14.58 21.78] [43.74 65.34] [ 72.9 108.9]\n", + "[17.01 25.41] [53.46 79.86] [ 89.91 134.31]" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\n", + "mat_mul_test = intvl_matmul(v1, v1)\n", + "mat_mul_test" + ] + }, + { + "cell_type": "markdown", + "id": "f5e18d79-62f4-462b-80ca-50b947a7fe84", + "metadata": { + "tags": [] + }, + "source": [ + "#### row @ mat" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "dadc30a0-8f0b-4cce-b3fa-f82cf86612cc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(3,)\n" + ] + }, + { + "data": { + "text/plain": [ + "[0.9,1.1]\n", + "[2.7,3.3000000000000003]\n", + "[4.5,5.5]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "vec = create_interval(matrix=np.array([1, 3, 5]))\n", + "print(vec.shape)\n", + "vec" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "bb37117e-7156-4273-8f68-c373dfb289fe", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(3,)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "vec.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "dda27fcf-29cc-4454-926f-b2bd7bb70715", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0.9,1.1]" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "vec[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "f9ed7783-218a-4757-ad14-e9dd7a04c850", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "x shall be a 2 dimensional array\n", + "x shall be a 2 dimensional array\n", + "x shall be a 2 dimensional array\n" + ] + }, + { + "data": { + "text/plain": [ + "[nan,nan]\n", + "[nan,nan]\n", + "[nan,nan]" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "intvl_matmul(vec, v1)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "68ae81db-9b26-4bbb-bc03-ef0f524cf659", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "x shall be a 2 dimensional array\n" + ] + } + ], + "source": [ + "rowcol2(vec, v1)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "4eff5388-d7b3-4202-a34b-6dcc102a8ce9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(1, 3)\n" + ] + }, + { + "data": { + "text/plain": [ + "[0.9 1.1] [2.7 3.3] [4.5 5.5]" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "row_vec = create_interval(matrix=np.array([[1, 3, 5]]))\n", + "print(row_vec.shape)\n", + "row_vec" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5995b3c-6f73-4f13-b299-160d83ddcf7b", + "metadata": {}, + "outputs": [], + "source": [ + "# In theory, (1 by 3) @ (3 by 3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a2c2999-2a7d-4376-b0fd-70089a4f519c", + "metadata": {}, + "outputs": [], + "source": [ + "# what if we explicitly write down the row vector as (1 by 3)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "f19a824e-afbf-44dc-9f4b-5ebe114a1419", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(3, 1)\n" + ] + }, + { + "data": { + "text/plain": [ + "[31.59 47.19]\n", + "[38.88 58.08]\n", + "[46.17 68.97]" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tt = intvl_matmul(row_vec, v1)\n", + "print(tt.shape)\n", + "tt" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "f6098480-d5af-48c0-acea-0026597818b8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[31.590000000000003,47.190000000000005]\n", + "[38.879999999999995,58.08000000000001]\n", + "[46.17,68.97000000000001]\n" + ] + } + ], + "source": [ + "for i in tt:\n", + " print(i)" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "24aed9d8-2345-4046-928c-dab988247b13", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['_Interval__hi',\n", + " '_Interval__lo',\n", + " '_Interval__shape',\n", + " '__add__',\n", + " '__class__',\n", + " '__delattr__',\n", + " '__dict__',\n", + " '__dir__',\n", + " '__doc__',\n", + " '__eq__',\n", + " '__format__',\n", + " '__ge__',\n", + " '__getattribute__',\n", + " '__getitem__',\n", + " '__getstate__',\n", + " '__gt__',\n", + " '__hash__',\n", + " '__init__',\n", + " '__init_subclass__',\n", + " '__iter__',\n", + " '__le__',\n", + " '__len__',\n", + " '__lt__',\n", + " '__module__',\n", + " '__mul__',\n", + " '__ne__',\n", + " '__neg__',\n", + " '__new__',\n", + " '__next__',\n", + " '__pos__',\n", + " '__pow__',\n", + " '__radd__',\n", + " '__reduce__',\n", + " '__reduce_ex__',\n", + " '__repr__',\n", + " '__rge__',\n", + " '__rgt__',\n", + " '__rle__',\n", + " '__rlt__',\n", + " '__rmul__',\n", + " '__rsub__',\n", + " '__rtruediv__',\n", + " '__setattr__',\n", + " '__sizeof__',\n", + " '__str__',\n", + " '__sub__',\n", + " '__subclasshook__',\n", + " '__truediv__',\n", + " '__weakref__',\n", + " 'hi',\n", + " 'lo',\n", + " 'scalar',\n", + " 'shape',\n", + " 'unsized',\n", + " 'val']" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dir(tt)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1cddc556-f4da-42a4-9ccb-01e7a52098eb", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "3f915c01-50c9-48eb-a996-9f01128d8d4d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[31.59],\n", + " [38.88],\n", + " [46.17]])" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tt.lo" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "af2c2205-0dc2-4077-8873-a8439160d3d3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[47.19],\n", + " [58.08],\n", + " [68.97]])" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tt.hi" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "bf7c771f-e297-46b5-8af8-71ba14ed430b", + "metadata": {}, + "outputs": [], + "source": [ + "def reshape(interval):\n", + " length = len(interval)\n", + " new = I(lo=np.squeeze(interval.lo), hi=np.squeeze(interval.hi))\n", + " return new[np.newaxis, :]" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "5dfbe597-4ba6-4669-a6f1-8b4d3400c0a1", + "metadata": {}, + "outputs": [], + "source": [ + "new = reshape(tt)" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "379ef942-6691-4ce4-89a8-627e35634db4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 3)" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "new.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "fee62ac2-3f42-421b-9b96-5bfec3218d35", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[31.59 47.19] [38.88 58.08] [46.17 68.97]" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "new" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c9c2e95-a4a3-4041-a828-524bfba0305b", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "93d79e7e-57be-454b-97f1-96c708454bd9", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "70d232da-a898-449b-aa13-b6696272d2c2", + "metadata": { + "tags": [] + }, + "source": [ + "#### mat @ col_vec" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6db7d27c-3493-45af-bf03-6fd3ac53bfe8", + "metadata": {}, + "outputs": [], + "source": [ + "# given a matrix\n", + "v1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb667504-afbb-4be1-886e-d757f0535d6c", + "metadata": {}, + "outputs": [], + "source": [ + "intvl_matmul(v1, vec)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77879741-133a-4ef2-94bd-d0d75650aea3", + "metadata": {}, + "outputs": [], + "source": [ + "# therefore, marco's rowvec works when the latter vec is just a vector\n", + "\n", + "test_marco = rowcol2(v1, vec)\n", + "print(test_marco.shape)\n", + "test_marco" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c835a252-6aed-43fb-91a2-3fe62307ed12", + "metadata": {}, + "outputs": [], + "source": [ + "# what if we explicitly express a col vector\n", + "\n", + "col_vec = create_interval(matrix=np.array([1, 3, 5]))\n", + "col_vec.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "55846437-7298-43c7-92f9-a49d85da66a8", + "metadata": {}, + "outputs": [], + "source": [ + "col_vec = col_vec[:, np.newaxis]\n", + "print(col_vec.shape)\n", + "col_vec" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6fe6ff32-de12-45ee-95f4-95a8b4d27385", + "metadata": {}, + "outputs": [], + "source": [ + "intvl_matmul(v1, col_vec)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cff4877d-0a67-4e0a-9804-911231fba552", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a8b747e4-4f00-46a4-a527-ac17234d9b79", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "63eb993c-1d64-4fe0-851f-6fc3cc90d31f", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "ea3e169a-88cf-4645-b613-fc513f686bd1", + "metadata": { + "tags": [] + }, + "source": [ + "#### vector @ matrix" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67ede585-d4b3-4979-b111-8c191117a226", + "metadata": {}, + "outputs": [], + "source": [ + "# create a row vector \n", + "\n", + "row_vec = create_interval(matrix=np.array([1, 3, 5]))\n", + "print(row_vec.shape)\n", + "row_vec" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f75a5aaf-9013-4951-bb84-88a09d50b270", + "metadata": {}, + "outputs": [], + "source": [ + "# sigmoid(row_vec)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0631a1bf-dd74-4b0e-88df-a102c073c9eb", + "metadata": {}, + "outputs": [], + "source": [ + "# sigmoid(v1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9e8d317c-7655-40eb-9f38-407d6310b66f", + "metadata": {}, + "outputs": [], + "source": [ + "# ''' add one more dimension succeeded '''\n", + "\n", + "# added_dim = row_vec[np.newaxis,:]\n", + "# print(added_dim.shape)\n", + "# added_dim" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a58174a-49e1-48f6-af8c-2bc8efb98259", + "metadata": {}, + "outputs": [], + "source": [ + "''' lower dimension failed '''\n", + "\n", + "# np.ravel(added_dim)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8f0f97d-86b1-416c-866c-1c5c2cbeb445", + "metadata": {}, + "outputs": [], + "source": [ + "# matrix which is v1\n", + "'''\n", + "(3, ) @ (3,3)\n", + "\n", + "'''\n", + "\n", + "result = rowcol2(x=row_vec, W=v1)\n", + "result" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5863a47b-c797-473b-8be0-1aa806af23b8", + "metadata": {}, + "outputs": [], + "source": [ + "result.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e8c73ff7-1ade-47d1-b7be-7bcffc17569f", + "metadata": {}, + "outputs": [], + "source": [ + "result_ = intvl_matmul(x=row_vec, W=v1)\n", + "type(result_)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8d928b16-c5c8-4131-9e0d-deedd67484ba", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "5668b7ad-298b-486a-a034-c98515a5c4b9", + "metadata": { + "jp-MarkdownHeadingCollapsed": true, + "tags": [] + }, + "source": [ + "#### scaler @ vector" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c065ed91-870d-4bce-82fa-781036536cc1", + "metadata": {}, + "outputs": [], + "source": [ + "# scalar\n", + "x = 3\n", + "x_intvl = create_interval(matrix=x)\n", + "x_intvl" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57d11069-0ecf-4266-8798-6b5e4e71923b", + "metadata": {}, + "outputs": [], + "source": [ + "# a vector\n", + "W1 = np.array([[1, 3, 5]])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f3643fdb-387d-40dc-a1c6-6e8ee738a3ac", + "metadata": {}, + "outputs": [], + "source": [ + "W1_intvl = create_interval(matrix=W1)\n", + "W1_intvl" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b8ddeda1-0d4c-4994-aef8-89c677a7f009", + "metadata": {}, + "outputs": [], + "source": [ + "W1_intvl.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea3b0076-545a-4014-a01c-a7b00bdfad00", + "metadata": {}, + "outputs": [], + "source": [ + "''' \n", + "(3, ) @ (1, 3)\n", + "'''\n", + "\n", + "h1 = x_intvl * W1_intvl\n", + "h1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c5b06fc9-177b-4c6f-a03f-cf78b506e4a7", + "metadata": {}, + "outputs": [], + "source": [ + "h1.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2c8056b0-c95e-4048-b6b9-b1fc54dd7e83", + "metadata": {}, + "outputs": [], + "source": [ + "'''\n", + "a further test where if (I) a scalar * interval vector matches with (II) scalar interval * interval vector\n", + "'''" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "90868753-32eb-48d9-a2cc-eb5caad50a95", + "metadata": {}, + "outputs": [], + "source": [ + "x = np.array([3.2])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c049bfdf-eb62-4d37-958d-34e8642f639a", + "metadata": {}, + "outputs": [], + "source": [ + "# scaler * interval vector\n", + "x2 = I(3.2, 3.2)\n", + "x2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "90d81cb1-8b19-4ee4-9429-460805f63f1d", + "metadata": {}, + "outputs": [], + "source": [ + "3.2 * h1 == x2 * h1 " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9f8f1d70-c66e-4003-b58c-f253f5b24a08", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb1a50bc-00b0-4a98-bbb9-c5bdb4de1f0a", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "3f04e74c-3d22-4588-90bd-0895933a5b9d", + "metadata": { + "tags": [] + }, + "source": [ + "#### vector @ vector\n", + "\n", + "> to mimic the second layer propagation, from h1 to output\n", + "\n", + "> (1 by 3) * (3 by 1)\n", + "\n", + "> y = hidden_tensor * W2_int" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "55848810-9cfc-4328-b2d2-497f3c24de94", + "metadata": {}, + "outputs": [], + "source": [ + "# consider a column vector\n", + "\n", + "W2 = np.array([1, 3, 5])[:, np.newaxis]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "343ea692-fea7-4650-a45c-42b108dd8aed", + "metadata": {}, + "outputs": [], + "source": [ + "W2_intvl = create_interval(matrix=W2)\n", + "print(W2_intvl.shape)\n", + "W2_intvl" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5f9e63cf-80b8-434d-bcb2-f1aac363a127", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "136c2bd5-81ad-41dc-aa56-aaeef7270828", + "metadata": {}, + "outputs": [], + "source": [ + "y_hat = intvl_matmul(x=h1, W=W2_intvl)\n", + "y_hat" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f0ee30f5-1951-4bb1-a1c6-59adcb9b4219", + "metadata": {}, + "outputs": [], + "source": [ + "y_hat2 = intvl_matmul(x=h1, W=v1)\n", + "y_hat2" + ] + }, + { + "cell_type": "markdown", + "id": "66e3bc0f-eb3c-452a-b7ac-2dba3a898270", + "metadata": {}, + "source": [ + "# activation function" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c7d91b8-31e2-46f3-b4b6-02223ded3d7a", + "metadata": {}, + "outputs": [], + "source": [ + "# sigmoid(x_intvl)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "transformer", + "language": "python", + "name": "transformer" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + }, + "toc-autonumbering": true + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tests/matrix_multiplication_marco.ipynb b/tests/matrix_multiplication_marco.ipynb new file mode 100644 index 0000000..6d0a480 --- /dev/null +++ b/tests/matrix_multiplication_marco.ipynb @@ -0,0 +1,1257 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "bb67a0bf-f22e-4fde-9a2e-4c524333920c", + "metadata": { + "tags": [] + }, + "source": [ + "# interval matrix `calculation` test\n", + "\n", + "- test with simple 3 by 3 case\n", + "- current workflow will work as long as there is no shape as 2" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "f6e8b17c-ae64-4d94-9ed7-0628ebcc22dd", + "metadata": {}, + "outputs": [], + "source": [ + "from intervals.number import Interval as I\n", + "from intervals.methods import (lo,hi,mid,rad,width,intervalise, exp)\n", + "from intervals.random import uniform_endpoints \n", + "from intervals.mat_features import consume_interval, create_interval, dot, rowcol, rowcol2, intvl_matmul, consume_list" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "4f11a1ef-1a8e-4bd8-a9ae-a4e18cd94628", + "metadata": {}, + "outputs": [], + "source": [ + "from intervals.methods import *" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "7778e3c6-c484-49d6-ad56-7d8f4b1fc45a", + "metadata": {}, + "outputs": [], + "source": [ + "from intervals.activation import sigmoid, tanh" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "eb56342b-b2d3-4fd7-85e1-a45f64e12b44", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "95064f3d-8f26-403a-97f2-ffc944bd4e5f", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "markdown", + "id": "647b4ac5-080f-46e0-b60b-bd9fd6ca4b49", + "metadata": {}, + "source": [ + "### prepare the data of various specifications" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "bf02e187-cbb6-4f9f-a9c7-c0be07bb0939", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[2.7,3.3000000000000003]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# scalar\n", + "\n", + "x_scalar = 3\n", + "x_si = create_interval(matrix=x_scalar)\n", + "x_si" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "9bf9840d-3e19-479b-b7e4-2ad3252b006b", + "metadata": {}, + "outputs": [], + "source": [ + "# row_vec = create_interval(matrix=np.array([1, 3, 5]))\n", + "# print(\"row vector\", row_vec.shape)\n", + "# row_vec" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "353d6976-9c74-41aa-92ba-71cdfd0700ec", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "base vector with shape (3,)\n" + ] + }, + { + "data": { + "text/plain": [ + "[0.9,1.1]\n", + "[2.7,3.3000000000000003]\n", + "[4.5,5.5]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# no-shape one-dimensional base vector\n", + "\n", + "vec = create_interval(matrix=np.array([1, 3, 5]))\n", + "print(\"base vector with shape\", vec.shape)\n", + "vec" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "a92cd4bc-51c5-4ecf-962e-a9a73fbd6ee6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "row vector with shape: (1, 3)\n" + ] + }, + { + "data": { + "text/plain": [ + "[0.9 1.1] [2.7 3.3] [4.5 5.5]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# row vector\n", + "\n", + "row_vec = create_interval(matrix=np.array([[1, 3, 5]]))\n", + "print(\"row vector with shape:\", row_vec.shape)\n", + "row_vec" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "22a442bd-185a-49c7-9332-d5e9460e3c89", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "col vector with shape: (3, 1)\n" + ] + }, + { + "data": { + "text/plain": [ + "[0.9 1.1]\n", + "[2.7 3.3]\n", + "[4.5 5.5]" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# col vector\n", + "\n", + "col_vec = vec[:, np.newaxis]\n", + "print(\"col vector with shape:\", col_vec.shape)\n", + "col_vec" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "6a602ea9-5e2f-4ef7-8214-3cd5a3fa70a8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "matrix interval with shape: (3, 3)\n" + ] + }, + { + "data": { + "text/plain": [ + "[0. 0.] [0.9 1.1] [1.8 2.2]\n", + "[2.7 3.3] [3.6 4.4] [4.5 5.5]\n", + "[5.4 6.6] [6.3 7.7] [7.2 8.8]" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# matrix \n", + "\n", + "a = np.arange(9).reshape(3,3)\n", + "mat_i = create_interval(matrix=a, half_width=0.1)\n", + "print(\"matrix interval with shape:\", mat_i.shape)\n", + "mat_i" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9baebab5-4062-4a4d-b30d-7f00428e192a", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "bd29348b-91be-43b7-800b-741a918336b3", + "metadata": { + "tags": [] + }, + "source": [ + "#### mat @ mat" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "aa4e3c4d-7163-4ad3-8f17-2ab8f5b8039e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[12.15 18.15] [34.02 50.82] [55.89 83.49]\n", + "[14.58 21.78] [43.74 65.34] [ 72.9 108.9]\n", + "[17.01 25.41] [53.46 79.86] [ 89.91 134.31]" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mat_mul_test = intvl_matmul(mat_i, mat_i)\n", + "mat_mul_test" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "ec6c2d24-ec3f-460f-9937-fb0f375b22a7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[12.15 18.15] [14.58 21.78] [17.01 25.41]\n", + "[34.02 50.82] [43.74 65.34] [53.46 79.86]\n", + "[55.89 83.49] [ 72.9 108.9] [ 89.91 134.31]" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "''' double check with Marco's version '''\n", + "\n", + "matmul(mat_i, mat_i)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "c8760ce1-ae39-46d7-a44f-9df0cadebd72", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ True, False, False],\n", + " [False, True, False],\n", + " [False, False, True]])" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mat_mul_test == matmul(mat_i, mat_i)" + ] + }, + { + "cell_type": "markdown", + "id": "f5e18d79-62f4-462b-80ca-50b947a7fe84", + "metadata": { + "tags": [] + }, + "source": [ + "#### row @ mat" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "dadc30a0-8f0b-4cce-b3fa-f82cf86612cc", + "metadata": {}, + "outputs": [], + "source": [ + "# vec = create_interval(matrix=np.array([1, 3, 5]))\n", + "# print(vec.shape)\n", + "# vec" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "bb37117e-7156-4273-8f68-c373dfb289fe", + "metadata": {}, + "outputs": [], + "source": [ + "# vec.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "dda27fcf-29cc-4454-926f-b2bd7bb70715", + "metadata": {}, + "outputs": [], + "source": [ + "# scalar\n", + "# vec[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "f9ed7783-218a-4757-ad14-e9dd7a04c850", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "x shall be a 2 dimensional array\n", + "x shall be a 2 dimensional array\n", + "x shall be a 2 dimensional array\n" + ] + }, + { + "data": { + "text/plain": [ + "[nan,nan]\n", + "[nan,nan]\n", + "[nan,nan]" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "intvl_matmul(vec, mat_i)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "68ae81db-9b26-4bbb-bc03-ef0f524cf659", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "x shall be a 2 dimensional array\n" + ] + } + ], + "source": [ + "rowcol2(vec, mat_i)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4eff5388-d7b3-4202-a34b-6dcc102a8ce9", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "f5995b3c-6f73-4f13-b299-160d83ddcf7b", + "metadata": {}, + "outputs": [], + "source": [ + "# In theory, (1 by 3) @ (3 by 3) = (1, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "9a2c2999-2a7d-4376-b0fd-70089a4f519c", + "metadata": {}, + "outputs": [], + "source": [ + "# what if we explicitly write down the row vector as (1 by 3)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "f19a824e-afbf-44dc-9f4b-5ebe114a1419", + "metadata": {}, + "outputs": [], + "source": [ + "# tt = intvl_matmul(row_vec, mat_i)\n", + "# print(tt.shape)\n", + "# tt" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "bf7c771f-e297-46b5-8af8-71ba14ed430b", + "metadata": {}, + "outputs": [], + "source": [ + "# def reshape(interval):\n", + "# length = len(interval)\n", + "# new = I(lo=np.squeeze(interval.lo), hi=np.squeeze(interval.hi))\n", + "# return new[np.newaxis, :]" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "5dfbe597-4ba6-4669-a6f1-8b4d3400c0a1", + "metadata": {}, + "outputs": [], + "source": [ + "# new_rowmat_test = reshape(tt)\n", + "# print(new_rowmat_test.shape)\n", + "# new" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "1c9c2e95-a4a3-4041-a828-524bfba0305b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[31.59 47.19] [38.88 58.08] [46.17 68.97]" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "''' double check with Marco's version '''\n", + "\n", + "rowcol_xT_WT(row_vec, mat_i)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "93d79e7e-57be-454b-97f1-96c708454bd9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[31.59 47.19] [38.88 58.08] [46.17 68.97]" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "matmul(row_vec, mat_i)" + ] + }, + { + "cell_type": "markdown", + "id": "70d232da-a898-449b-aa13-b6696272d2c2", + "metadata": { + "tags": [] + }, + "source": [ + "#### mat @ col_vec" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "eb667504-afbb-4be1-886e-d757f0535d6c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[10.53,15.730000000000002]\n", + "[32.4,48.400000000000006]\n", + "[54.269999999999996,81.07000000000002]" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "intvl_matmul(mat_i, vec)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "9876da08-d5b1-401a-9d58-89d6054d9f44", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[10.53,15.730000000000002]\n", + "[32.4,48.400000000000006]\n", + "[54.269999999999996,81.07000000000002]" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "intvl_matmul(mat_i, col_vec)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "77879741-133a-4ef2-94bd-d0d75650aea3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\" test with marco's old code, which has been updated I guess \"" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "''' test with marco's old code, which has been updated I guess '''\n", + "\n", + "# # therefore, marco's rowvec works when the latter vec is just a vector\n", + "\n", + "# test_marco = rowcol2(mat_i, vec)\n", + "# print(test_marco.shape)\n", + "# test_marco\n", + "\n", + "# # what if we explicitly express a col vector\n", + "\n", + "# col_vec = col_vec[:, np.newaxis]\n", + "# print(col_vec.shape)\n", + "# col_vec\n", + "\n", + "# intvl_matmul(mat_i, col_vec)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "c835a252-6aed-43fb-91a2-3fe62307ed12", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[10.53 15.73]\n", + "[32.4 48.4]\n", + "[54.27 81.07]\n", + "test the universal \"matmul\" function:\n", + "[10.53 15.73]\n", + "[32.4 48.4]\n", + "[54.27 81.07]\n" + ] + } + ], + "source": [ + "''' test with marco's new code '''\n", + "\n", + "print(rowcol_W_x(mat_i, col_vec))\n", + "\n", + "print('test the universal \"matmul\" function:')\n", + "print(matmul(mat_i, col_vec))" + ] + }, + { + "cell_type": "markdown", + "id": "ea3e169a-88cf-4645-b613-fc513f686bd1", + "metadata": { + "tags": [] + }, + "source": [ + "#### vector @ matrix" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "67ede585-d4b3-4979-b111-8c191117a226", + "metadata": {}, + "outputs": [], + "source": [ + "# create a row vector \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "f75a5aaf-9013-4951-bb84-88a09d50b270", + "metadata": {}, + "outputs": [], + "source": [ + "# sigmoid(row_vec)" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "0631a1bf-dd74-4b0e-88df-a102c073c9eb", + "metadata": {}, + "outputs": [], + "source": [ + "# sigmoid(mat_i)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "9e8d317c-7655-40eb-9f38-407d6310b66f", + "metadata": {}, + "outputs": [], + "source": [ + "# ''' add one more dimension succeeded '''\n", + "\n", + "# added_dim = row_vec[np.newaxis,:]\n", + "# print(added_dim.shape)\n", + "# added_dim" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "9a58174a-49e1-48f6-af8c-2bc8efb98259", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "' lower dimension failed '" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "''' lower dimension failed '''\n", + "\n", + "# np.ravel(added_dim)" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "f8f0f97d-86b1-416c-866c-1c5c2cbeb445", + "metadata": {}, + "outputs": [ + { + "ename": "UnboundLocalError", + "evalue": "cannot access local variable 'l' where it is not associated with a value", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mUnboundLocalError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[36], line 7\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# matrix which is mat_i\u001b[39;00m\n\u001b[1;32m 2\u001b[0m \u001b[38;5;124;03m'''\u001b[39;00m\n\u001b[1;32m 3\u001b[0m \u001b[38;5;124;03m(3, ) @ (3,3)\u001b[39;00m\n\u001b[1;32m 4\u001b[0m \n\u001b[1;32m 5\u001b[0m \u001b[38;5;124;03m'''\u001b[39;00m\n\u001b[0;32m----> 7\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mrowcol2\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrow_vec\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mW\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmat_i\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 8\u001b[0m result\n", + "File \u001b[0;32m~/work_leslie/intervals/intervals/mat_features.py:56\u001b[0m, in \u001b[0;36mrowcol2\u001b[0;34m(x, W)\u001b[0m\n\u001b[1;32m 54\u001b[0m y\u001b[38;5;241m=\u001b[39m[]\n\u001b[1;32m 55\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(s[\u001b[38;5;241m0\u001b[39m]): \n\u001b[0;32m---> 56\u001b[0m y\u001b[38;5;241m.\u001b[39mappend(\u001b[43mdot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m[\u001b[49m\u001b[43mi\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43mW\u001b[49m\u001b[43m)\u001b[49m)\n\u001b[1;32m 58\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m intervalise(y)\n\u001b[1;32m 59\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", + "File \u001b[0;32m~/work_leslie/intervals/intervals/mat_features.py:32\u001b[0m, in \u001b[0;36mdot\u001b[0;34m(x, y)\u001b[0m\n\u001b[0;32m---> 32\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mdot\u001b[39m(x,y): \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28msum\u001b[39m(\u001b[43mx\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43my\u001b[49m)\n", + "File \u001b[0;32m~/work_leslie/intervals/intervals/number.py:166\u001b[0m, in \u001b[0;36mInterval.__mul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 164\u001b[0m hi[other_negative]\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlo[other_negative] \u001b[38;5;241m*\u001b[39m other[other_negative]\n\u001b[1;32m 165\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m otherType \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mInterval\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[0;32m--> 166\u001b[0m lo,hi \u001b[38;5;241m=\u001b[39m \u001b[43mmultiply\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43mother\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 167\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m: \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mNotImplemented\u001b[39m\n\u001b[1;32m 168\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m Interval(lo,hi)\n", + "File \u001b[0;32m~/work_leslie/intervals/intervals/arithmetic.py:120\u001b[0m, in \u001b[0;36mmultiply\u001b[0;34m(s, o)\u001b[0m\n\u001b[1;32m 118\u001b[0m l[nn] \u001b[38;5;241m=\u001b[39m s_hi[nn] \u001b[38;5;241m*\u001b[39m o_hi\n\u001b[1;32m 119\u001b[0m h[nn] \u001b[38;5;241m=\u001b[39m s_lo[nn] \u001b[38;5;241m*\u001b[39m o_lo\n\u001b[0;32m--> 120\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43ml\u001b[49m,h\n", + "\u001b[0;31mUnboundLocalError\u001b[0m: cannot access local variable 'l' where it is not associated with a value" + ] + } + ], + "source": [ + "# matrix which is mat_i\n", + "'''\n", + "(3, ) @ (3,3)\n", + "\n", + "'''\n", + "\n", + "result = rowcol2(x=row_vec, W=mat_i)\n", + "result" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5863a47b-c797-473b-8be0-1aa806af23b8", + "metadata": {}, + "outputs": [], + "source": [ + "result.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e8c73ff7-1ade-47d1-b7be-7bcffc17569f", + "metadata": {}, + "outputs": [], + "source": [ + "result_ = intvl_matmul(x=row_vec, W=mat_i)\n", + "type(result_)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8d928b16-c5c8-4131-9e0d-deedd67484ba", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "5668b7ad-298b-486a-a034-c98515a5c4b9", + "metadata": { + "tags": [] + }, + "source": [ + "#### scaler @ vector" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "f3643fdb-387d-40dc-a1c6-6e8ee738a3ac", + "metadata": {}, + "outputs": [], + "source": [ + "W1_intvl = row_vec" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "b8ddeda1-0d4c-4994-aef8-89c677a7f009", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 3)" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "W1_intvl.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "ea3b0076-545a-4014-a01c-a7b00bdfad00", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[2.43 3.63] [ 7.29 10.89] [12.15 18.15]" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "''' \n", + "(3, ) @ (1, 3)\n", + "'''\n", + "\n", + "h1 = x_si * W1_intvl\n", + "h1" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "c5b06fc9-177b-4c6f-a03f-cf78b506e4a7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 3)" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "h1.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "2c8056b0-c95e-4048-b6b9-b1fc54dd7e83", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'\\na further test where if (I) a scalar * interval vector matches with (II) scalar interval * interval vector\\n'" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "'''\n", + "a further test where if (I) a scalar * interval vector matches with (II) scalar interval * interval vector\n", + "'''" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "90868753-32eb-48d9-a2cc-eb5caad50a95", + "metadata": {}, + "outputs": [], + "source": [ + "x_c = np.array([3.2])" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "c049bfdf-eb62-4d37-958d-34e8642f639a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[3.2,3.2]" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# scaler * interval vector\n", + "x_ci = I(3.2, 3.2)\n", + "x_ci" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "90d81cb1-8b19-4ee4-9429-460805f63f1d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ True, True, True]])" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "3.2 * h1 == x_ci * h1 " + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "9f8f1d70-c66e-4003-b58c-f253f5b24a08", + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "not enough values to unpack (expected 2, got 0)", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[45], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mmatmul\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx_ci\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrow_vec\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/work_leslie/intervals/intervals/methods.py:739\u001b[0m, in \u001b[0;36mmatmul\u001b[0;34m(A, B)\u001b[0m\n\u001b[1;32m 734\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mmatmul\u001b[39m(A,B):\n\u001b[1;32m 735\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m'''\u001b[39;00m\n\u001b[1;32m 736\u001b[0m \u001b[38;5;124;03m (m,n) x (n,p) -> (m,p) \u001b[39;00m\n\u001b[1;32m 737\u001b[0m \u001b[38;5;124;03m (1,n) x (n,1) -> (1,1)\u001b[39;00m\n\u001b[1;32m 738\u001b[0m \u001b[38;5;124;03m '''\u001b[39;00m\n\u001b[0;32m--> 739\u001b[0m m,na \u001b[38;5;241m=\u001b[39m A\u001b[38;5;241m.\u001b[39mshape\n\u001b[1;32m 740\u001b[0m nb,p \u001b[38;5;241m=\u001b[39m B\u001b[38;5;241m.\u001b[39mshape\n\u001b[1;32m 741\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m na\u001b[38;5;241m!=\u001b[39mnb: \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mIncompatible shapes (\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mm\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m,\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mna\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) x (\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mnb\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m,\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mp\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) -> (?,?). Inner sizes must be same, \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mna\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m is different from \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mnb\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", + "\u001b[0;31mValueError\u001b[0m: not enough values to unpack (expected 2, got 0)" + ] + } + ], + "source": [ + "matmul(x_ci, row_vec)" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "bb1a50bc-00b0-4a98-bbb9-c5bdb4de1f0a", + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "not enough values to unpack (expected 2, got 0)", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[46], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mmatmul\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx_ci\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcol_vec\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/work_leslie/intervals/intervals/methods.py:739\u001b[0m, in \u001b[0;36mmatmul\u001b[0;34m(A, B)\u001b[0m\n\u001b[1;32m 734\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mmatmul\u001b[39m(A,B):\n\u001b[1;32m 735\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m'''\u001b[39;00m\n\u001b[1;32m 736\u001b[0m \u001b[38;5;124;03m (m,n) x (n,p) -> (m,p) \u001b[39;00m\n\u001b[1;32m 737\u001b[0m \u001b[38;5;124;03m (1,n) x (n,1) -> (1,1)\u001b[39;00m\n\u001b[1;32m 738\u001b[0m \u001b[38;5;124;03m '''\u001b[39;00m\n\u001b[0;32m--> 739\u001b[0m m,na \u001b[38;5;241m=\u001b[39m A\u001b[38;5;241m.\u001b[39mshape\n\u001b[1;32m 740\u001b[0m nb,p \u001b[38;5;241m=\u001b[39m B\u001b[38;5;241m.\u001b[39mshape\n\u001b[1;32m 741\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m na\u001b[38;5;241m!=\u001b[39mnb: \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mIncompatible shapes (\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mm\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m,\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mna\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) x (\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mnb\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m,\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mp\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) -> (?,?). Inner sizes must be same, \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mna\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m is different from \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mnb\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", + "\u001b[0;31mValueError\u001b[0m: not enough values to unpack (expected 2, got 0)" + ] + } + ], + "source": [ + "matmul(x_ci, col_vec)" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "3f353f36-a4e4-4203-a1d7-637be2c46064", + "metadata": {}, + "outputs": [ + { + "ename": "IndexError", + "evalue": "tuple index out of range", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[47], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mrowcol_xT_WT\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx_ci\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmat_i\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/work_leslie/intervals/intervals/methods.py:717\u001b[0m, in \u001b[0;36mrowcol_xT_WT\u001b[0;34m(x, W)\u001b[0m\n\u001b[1;32m 715\u001b[0m n,m \u001b[38;5;241m=\u001b[39m W\u001b[38;5;241m.\u001b[39mshape\n\u001b[1;32m 716\u001b[0m x_shape \u001b[38;5;241m=\u001b[39m x\u001b[38;5;241m.\u001b[39mshape\n\u001b[0;32m--> 717\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m((n\u001b[38;5;241m==\u001b[39m\u001b[43mx_shape\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m]\u001b[49m) \u001b[38;5;241m|\u001b[39m (n\u001b[38;5;241m==\u001b[39mx_shape[\u001b[38;5;241m1\u001b[39m])): \n\u001b[1;32m 718\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mIncompatible shapes (1,\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mx_shape[\u001b[38;5;241m1\u001b[39m]\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) x (\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mn\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m,\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mm\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) -> (?,?). Inner sizes must be same, \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mx_shape[\u001b[38;5;241m1\u001b[39m]\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m is different from \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mn\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 719\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m x_shape[\u001b[38;5;241m0\u001b[39m]\u001b[38;5;241m==\u001b[39m\u001b[38;5;241m1\u001b[39m: \u001b[38;5;66;03m# this is the correct shape\u001b[39;00m\n", + "\u001b[0;31mIndexError\u001b[0m: tuple index out of range" + ] + } + ], + "source": [ + "rowcol_xT_WT(x_ci, mat_i)" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "b4f2d2a6-bdba-4871-8a02-5da9ea8200f2", + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "not enough values to unpack (expected 2, got 0)", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[48], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mrowcol_W_x\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx_ci\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmat_i\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/work_leslie/intervals/intervals/methods.py:687\u001b[0m, in \u001b[0;36mrowcol_W_x\u001b[0;34m(W, x)\u001b[0m\n\u001b[1;32m 676\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mrowcol_W_x\u001b[39m(W,x): \n\u001b[1;32m 677\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m'''\u001b[39;00m\n\u001b[1;32m 678\u001b[0m \u001b[38;5;124;03m Row by column multiplication between a matrix W and a column vector x.\u001b[39;00m\n\u001b[1;32m 679\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 685\u001b[0m \u001b[38;5;124;03m (1,n) x (1,n) -> (1,1) \u001b[39;00m\n\u001b[1;32m 686\u001b[0m \u001b[38;5;124;03m '''\u001b[39;00m\n\u001b[0;32m--> 687\u001b[0m m,n \u001b[38;5;241m=\u001b[39m W\u001b[38;5;241m.\u001b[39mshape\n\u001b[1;32m 688\u001b[0m x_shape \u001b[38;5;241m=\u001b[39m x\u001b[38;5;241m.\u001b[39mshape\n\u001b[1;32m 689\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m((n\u001b[38;5;241m==\u001b[39mx_shape[\u001b[38;5;241m0\u001b[39m]) \u001b[38;5;241m|\u001b[39m (n\u001b[38;5;241m==\u001b[39mx_shape[\u001b[38;5;241m1\u001b[39m])): \n", + "\u001b[0;31mValueError\u001b[0m: not enough values to unpack (expected 2, got 0)" + ] + } + ], + "source": [ + "rowcol_W_x(x_ci, mat_i)" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "a3478b26-17b1-4bc0-959d-15ad49cf16c3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0. 0.] [2.88 3.52] [5.76 7.04]\n", + "[ 8.64 10.56] [11.52 14.08] [14.4 17.6]\n", + "[17.28 21.12] [20.16 24.64] [23.04 28.16]" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x_ci * mat_i" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b5e47923-1120-40a4-b67b-1c30636fb0a2", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "3f04e74c-3d22-4588-90bd-0895933a5b9d", + "metadata": { + "tags": [] + }, + "source": [ + "#### vector @ vector\n", + "\n", + "> to mimic the second layer propagation, from h1 to output\n", + "\n", + "> (1 by 3) * (3 by 1)\n", + "\n", + "> y = hidden_tensor * W2_int" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "55848810-9cfc-4328-b2d2-497f3c24de94", + "metadata": {}, + "outputs": [], + "source": [ + "# consider a column vector\n", + "\n", + "W2_intvl = col_vec" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5f9e63cf-80b8-434d-bcb2-f1aac363a127", + "metadata": {}, + "outputs": [], + "source": [ + "W2_intvl" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "136c2bd5-81ad-41dc-aa56-aaeef7270828", + "metadata": {}, + "outputs": [], + "source": [ + "y_hat = intvl_matmul(x=x_ci, W=W2_intvl)\n", + "y_hat" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f0ee30f5-1951-4bb1-a1c6-59adcb9b4219", + "metadata": {}, + "outputs": [], + "source": [ + "y_hat2 = intvl_matmul(x=h1, W=mat_i)\n", + "y_hat2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "73da3cd2-4bfb-436b-92e8-7b9ba9daaa83", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6a9193cb-dedf-497f-80cb-fef3446b04d1", + "metadata": {}, + "outputs": [], + "source": [ + "# scalar * vector\n", + "\n", + "matmul(x_ci, h1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "abaeead0-1f56-4093-b307-ff336272769c", + "metadata": {}, + "outputs": [], + "source": [ + "matmaul(5, h1)" + ] + }, + { + "cell_type": "markdown", + "id": "66e3bc0f-eb3c-452a-b7ac-2dba3a898270", + "metadata": {}, + "source": [ + "# activation function" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c7d91b8-31e2-46f3-b4b6-02223ded3d7a", + "metadata": {}, + "outputs": [], + "source": [ + "# sigmoid(x_intvl)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57d9c55c-18a7-4ecb-b517-9c1b14417d53", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6c449f9b-2608-4046-b134-6c023f226ea4", + "metadata": {}, + "outputs": [], + "source": [ + "np.dot(3, [1,2,3])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1ad11d51-9e86-4ca0-a34b-2ceac68e007f", + "metadata": {}, + "outputs": [], + "source": [ + "np.dot([3,2,1], [1,2,3])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e6644df3-75c6-45b7-9ae3-21c29d755b7f", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "transformer", + "language": "python", + "name": "transformer" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + }, + "toc-autonumbering": true + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tests/test_plotting.ipynb b/tests/test_plotting.ipynb new file mode 100644 index 0000000..63f679b --- /dev/null +++ b/tests/test_plotting.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 16, + "id": "340b20c6-58ae-4175-a4cf-757905bb8c47", + "metadata": {}, + "outputs": [], + "source": [ + "from intervals.number import Interval as I\n", + "from intervals.methods import (lo,hi,mid,rad,width,intervalise, exp)\n", + "from intervals.random import uniform_endpoints \n", + "from intervals.plotting import plot_intervals, plot_lower_bound\n", + "from intervals.mat_features import create_interval" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "808b0878-876e-42b3-b603-b41b4b941407", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "38fea7d5-233c-409d-8cf8-a5ff3bf1b95e", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "725c8f94-a582-4a44-8296-ca9de529dad3", + "metadata": {}, + "outputs": [], + "source": [ + "x = np.arange(4)\n", + "y = np.arange(4)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "957cbb39-6781-4bdc-b546-8fddd0ba2a54", + "metadata": {}, + "outputs": [], + "source": [ + "y_i = create_interval(matrix=y, half_width=0.5)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "8b862885-dc41-4085-a306-287605210b49", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0.0,0.0]\n", + "[0.5,1.5]\n", + "[1.0,3.0]\n", + "[1.5,4.5]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "y_i" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "70faa6b6-ebe3-4e5a-8847-a3b7395db672", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0])" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x[y_i.lo == y_i.hi]" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "14badbe4-ec58-49f8-9462-944600985e76", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0.])" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "y_i[y_i.lo == y_i.hi].lo" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "86ea9341-371e-40c9-9826-78d9c19ea7d8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots()\n", + "ax.scatter(x[y_i.lo == y_i.hi], y_i[y_i.lo == y_i.hi].lo)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "18307e47-7967-4ad6-9178-d667e3590eb3", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0wgIASCMsAIA0ZYVFW1tbnHvuuVFfXx8zZ86MZcuWxWuvvTZaswEAVaassNiyZUu0trbG888/H0899VS89957cfnll0d3d/dozQcAVJEp5Sx+8sknB32+bt26mDlzZrS3t8dnP/vZ1MEAgOpTVli8X2dnZ0REzJgxY8g1PT090dPTM/B5V1fX0dwlADCOjfjFm319fbFixYq44IIL4qyzzhpyXVtbWzQ2Ng5cmpubR3qXAMA4N+KwaG1tjZdffjnWr19/xHWrV6+Ozs7OgUtHR8dI7xIAGOdG9FTIzTffHI8//nhs3bo1mpqajri2VCpFqVQa0XAAQHUpKyyKoohbbrklNmzYEJs3b45TTz11tOYCAKpQWWHR2toaDzzwQDz22GNRX18fe/bsiYiIxsbGOP7440dlQACgepT1Gou77747Ojs7Y/HixTF79uyBy4MPPjha8wEAVaTsp0IAAIbib4UAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQZkqlB4DxqigiDh7s/3jq1IiamsrOA1ANnLGAIRw8GDF9ev/lUGAAcGTCAgBI46kQADgGTJ0aceDAfz6uFGEBAMeAmpqIadMqPYWnQgCARMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANGWHxdatW+Oqq66KOXPmRE1NTTz66KOjMBYAUI3KDovu7u5YsGBBrF27djTmAQCq2JRyb3DFFVfEFVdcMRqzAABVruywKFdPT0/09PQMfN7V1TXadwkAVMiov3izra0tGhsbBy7Nzc2jfZcAQIWMelisXr06Ojs7By4dHR2jfZcA49rUqREHDvRfpk6t9DSQa9SfCimVSlEqlUb7bgCqRk1NxLRplZ4CRof3sQAA0pR9xuLAgQPx+uuvD3y+a9eu2LFjR8yYMSNOPvnk1OEAgOpSdli89NJLcfHFFw98vnLlyoiIWL58eaxbty5tMACg+pQdFosXL46iKEZjFgCgynmNBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQZkqlB4DxaurUiAMH/vMxAB9OWMAQamoipk2r9BQA1cVTIQBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAmimVHiBDb2/EH/4QsXt3xOzZERdeGDF5cqWnAoCJZ0RnLNauXRtz586Nurq6WLRoUbzwwgvZcw3br38dMXduxMUXR/zv//b/O3du//UAwNgqOywefPDBWLlyZaxZsya2bdsWCxYsiKVLl8a+fftGY74j+vWvIz7/+Yi33hp8/T/+0X+9uACAsVVTFEVRzg0WLVoU5557btx1110REdHX1xfNzc1xyy23xKpVqz709l1dXdHY2BidnZ3R0NAwsqmj/+mPuXM/GBWH1NRENDVF7NrlaREAOFrD/fld1hmLd999N9rb22PJkiX/+QaTJsWSJUviueeeO+xtenp6oqura9Alwx/+MHRUREQURURHR/86AGBslBUW77zzTvT29sasWbMGXT9r1qzYs2fPYW/T1tYWjY2NA5fm5uaRT/tfdu/OXQcAHL1R/3XT1atXR2dn58Clo6Mj5fvOnp27DgA4emX9uumJJ54YkydPjr179w66fu/evXHSSScd9jalUilKpdLIJxzChRf2v4biH//of9rj/Q69xuLCC9PvGgAYQllnLGpra2PhwoWxadOmgev6+vpi06ZN0dLSkj7ckUyeHHHnnf0f19QM/tqhz//v/7xwEwDGUtlPhaxcuTLuvffe+NnPfhavvvpq3HTTTdHd3R3XX3/9aMx3RNdcE/HwwxH/8z+Dr29q6r/+mmvGfCQAmNDKfufNa6+9Nv75z3/Gd77zndizZ0+cffbZ8eSTT37gBZ1j5ZprIq6+2jtvAsB4UPb7WBytrPexAADGzqi8jwUAwJEICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANKU/ZbeR+vQG312dXWN9V0DACN06Of2h71h95iHxf79+yMiorm5eazvGgA4Svv374/GxsYhvz7mfyukr68v3n777aivr4+a9/+986PQ1dUVzc3N0dHR4W+QfAh7NXz2qjz2a/js1fDZq+Ebzb0qiiL2798fc+bMiUmThn4lxZifsZg0aVI0NTWN2vdvaGhw4A2TvRo+e1Ue+zV89mr47NXwjdZeHelMxSFevAkApBEWAECaYyYsSqVSrFmzJkqlUqVHGffs1fDZq/LYr+GzV8Nnr4ZvPOzVmL94EwA4dh0zZywAgMoTFgBAGmEBAKQRFgBAmqoKi7Vr18bcuXOjrq4uFi1aFC+88MIR1z/00ENx+umnR11dXXz605+O3/72t2M0aeWVs1fr1q2LmpqaQZe6uroxnLZytm7dGldddVXMmTMnampq4tFHH/3Q22zevDnOOeecKJVKcdppp8W6detGfc7xoNy92rx58weOq5qamtizZ8/YDFxBbW1tce6550Z9fX3MnDkzli1bFq+99tqH3m4iPmaNZK8m6mPW3XffHfPnzx9486uWlpZ44oknjnibShxTVRMWDz74YKxcuTLWrFkT27ZtiwULFsTSpUtj3759h13/pz/9Kb70pS/FDTfcENu3b49ly5bFsmXL4uWXXx7jycdeuXsV0f8ubbt37x64vPnmm2M4ceV0d3fHggULYu3atcNav2vXrrjyyivj4osvjh07dsSKFSvia1/7WmzcuHGUJ628cvfqkNdee23QsTVz5sxRmnD82LJlS7S2tsbzzz8fTz31VLz33ntx+eWXR3d395C3maiPWSPZq4iJ+ZjV1NQUt99+e7S3t8dLL70Ul1xySVx99dXxyiuvHHZ9xY6pokqcd955RWtr68Dnvb29xZw5c4q2trbDrv/CF75QXHnllYOuW7RoUfGNb3xjVOccD8rdq/vvv79obGwco+nGr4goNmzYcMQ13/rWt4ozzzxz0HXXXnttsXTp0lGcbPwZzl4988wzRUQU//rXv8ZkpvFs3759RUQUW7ZsGXLNRH7M+m/D2SuPWf9xwgknFPfdd99hv1apY6oqzli8++670d7eHkuWLBm4btKkSbFkyZJ47rnnDnub5557btD6iIilS5cOuf5YMZK9iog4cOBAnHLKKdHc3HzEAp7oJupxdTTOPvvsmD17dlx22WXx7LPPVnqciujs7IyIiBkzZgy5xrHVbzh7FeExq7e3N9avXx/d3d3R0tJy2DWVOqaqIizeeeed6O3tjVmzZg26ftasWUM+X7tnz56y1h8rRrJX8+bNi5/+9Kfx2GOPxS9+8Yvo6+uL888/P956662xGLmqDHVcdXV1xb///e8KTTU+zZ49O3784x/HI488Eo888kg0NzfH4sWLY9u2bZUebUz19fXFihUr4oILLoizzjpryHUT9THrvw13rybyY9bOnTtj+vTpUSqV4sYbb4wNGzbEGWeccdi1lTqmxvyvmzL+tLS0DCre888/Pz71qU/FPffcE9/97ncrOBnVbN68eTFv3ryBz88///x444034o477oif//znFZxsbLW2tsbLL78cf/zjHys9yrg33L2ayI9Z8+bNix07dkRnZ2c8/PDDsXz58tiyZcuQcVEJVXHG4sQTT4zJkyfH3r17B12/d+/eOOmkkw57m5NOOqms9ceKkezV+x133HHxmc98Jl5//fXRGLGqDXVcNTQ0xPHHH1+hqarHeeedN6GOq5tvvjkef/zxeOaZZ6KpqemIayfqY9Yh5ezV+02kx6za2to47bTTYuHChdHW1hYLFiyIO++887BrK3VMVUVY1NbWxsKFC2PTpk0D1/X19cWmTZuGfG6ppaVl0PqIiKeeemrI9ceKkezV+/X29sbOnTtj9uzZozVm1Zqox1WWHTt2TIjjqiiKuPnmm2PDhg3x9NNPx6mnnvqht5mox9ZI9ur9JvJjVl9fX/T09Bz2axU7pkb1paGJ1q9fX5RKpWLdunXFX/7yl+LrX/968ZGPfKTYs2dPURRFcd111xWrVq0aWP/ss88WU6ZMKX7wgx8Ur776arFmzZriuOOOK3bu3Fmp/8KYKXevbrvttmLjxo3FG2+8UbS3txdf/OIXi7q6uuKVV16p1H9hzOzfv7/Yvn17sX379iIiih/+8IfF9u3bizfffLMoiqJYtWpVcd111w2s/9vf/lZMnTq1+OY3v1m8+uqrxdq1a4vJkycXTz75ZKX+C2Om3L264447ikcffbT461//WuzcubO49dZbi0mTJhW///3vK/VfGDM33XRT0djYWGzevLnYvXv3wOXgwYMDazxm9RvJXk3Ux6xVq1YVW7ZsKXbt2lX8+c9/LlatWlXU1NQUv/vd74qiGD/HVNWERVEUxY9+9KPi5JNPLmpra4vzzjuveP755we+dtFFFxXLly8ftP5Xv/pV8clPfrKora0tzjzzzOI3v/nNGE9cOeXs1YoVKwbWzpo1q/jc5z5XbNu2rQJTj71DvxL5/suh/Vm+fHlx0UUXfeA2Z599dlFbW1t8/OMfL+6///4xn7sSyt2r733ve8UnPvGJoq6urpgxY0axePHi4umnn67M8GPscPsUEYOOFY9Z/UayVxP1MeurX/1qccoppxS1tbXFxz72seLSSy8diIqiGD/HlD+bDgCkqYrXWAAA1UFYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABp/h9M/2u+nRF6fgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# plot a vector interval\n", + "plot_intervals(x, y_i)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "7ad57e12-fcd2-4279-bd3e-7864dd355870", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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/k6cOKGHHnpIvXv3VmRkpBITE5WZmalDhw4FbKNr16419uHs2bMbtL/G6lGq23Owpe5DSbW+Bh0Oh5566il/TVPsw/r0t3PnTv3617/2j++5556rtW7u3Lnq2rWrwsPDlZKSUuO94tixY8rKytLFF1+sdu3a6de//rXKy8vr34wJYatXrzb/9m//Zt566y0jySxfvvyc9X/+859NVFSUycvLM/v27TNLly417dq1M//93//tr/nggw9MWFiYmTNnjtm1a5d59NFHTZs2bcwnn3zir5k9e7ZxuVxmxYoV5n//93/NLbfcYpKTk833339vfX///M//bObOnWu2b99uPv30UzNhwgTjcrnMwYMH/TXjx483w4cPN2VlZf7pu+++a9DeGqu/RYsWmejo6ICxu93ugO001f5rrB4PHz4c0N+OHTtMWFiYWbRokb+mqfZhenq6WbRokdmxY4cpKSkxv/zlL03nzp1NZWXlWddpqNfY8OHDTd++fc3mzZvN+++/b7p162bGjRtnfX8VFRUmLS3NvP766+azzz4zRUVFZtCgQWbAgAEB2+nSpYv5wx/+ELAPz/W4NvVoTN2egy11HxpjAvoqKyszCxcuNA6Hw+zbt89f0xT7sD79bd261UyfPt0sXbrUxMfHm2effbZGTV5enmnbtq1ZuHCh2blzp7nrrrtM+/btTXl5ub/m3nvvNUlJSSY/P99s27bNXHPNNWbw4MH17iWkA8sP1eXNIDU11UyfPj1g3rRp08yQIUP892+77TYzcuTIgJqUlBRzzz33GGOMqa6uNvHx8eapp57yL6+oqDBOp9MsXbr0Ars4u4bq78dOnjxpoqKizKuvvuqfN378eDN69OgLGW7QGqq/RYsWGZfLddZtNNf+M6bx9uGzzz5roqKiAv5ANcc+NMaYr7/+2kgyhYWFZ61piNfYrl27jCTz17/+1V/zzjvvGIfDYb766quGbClAQ/RXm61btxpJ5ssvv/TP69KlS61vJI2toXo833Mw1Pbh6NGjzS9+8YuAec2xD+vS3w+dbYyDBg0yWVlZ/vunTp0yiYmJJicnxxhz+jXZpk0bs2zZMn/Np59+aiSZoqKieo09pD8SClZVVVWNjwciIiK0detWnThxQpJUVFSktLS0gJr09HQVFRVJkkpLS+V2uwNqXC6XUlJS/DXNpS79/djRo0d14sQJxcTEBMwvKChQbGysevToocmTJ+vw4cONNu66qmt/lZWV6tKli5KSkjR69Gjt3LnTv8zm/SfVbx8uWLBAY8eOVWRkZMD85tiHHo9Hkmo8n36oIV5jRUVFat++va6++mp/TVpamlq1aqUtW7Y0WD8/1hD9nW27DodD7du3D5g/e/ZsXXzxxerfv7+eeuopnTx5sv6Dr6OG7PFcz8FQ2ofl5eV6++23NXHixBrLmnof1qW/8zl+/LiKi4sD/g1atWqltLQ0/79BcXGxTpw4EVDTs2dPde7cud5/SwksP5Cenq5XXnlFxcXFMsZo27ZteuWVV3TixAl9++23kiS32624uLiA9eLi4uR2u/3Lz8w7W01zqUt/P/bQQw8pMTEx4Ek3fPhwLV68WPn5+XryySdVWFioESNG6NSpU03VSq3q0l+PHj20cOFCrVy5Uq+99pqqq6s1ePBgHTx4UJLd+08Kfh9u3bpVO3bs0KRJkwLmN8c+rK6u1tSpUzVkyBD16tXrrHUN8Rpzu92KjY0NWN66dWvFxMQ02n5sqP5+7NixY3rooYc0bty4gB+d+9d//Vfl5eVpw4YNuueee/TEE0/owQcfbJhmzqIhezzfczCU9uGrr76qqKgo/eM//mPA/Kbeh3Xt73y+/fZbnTp16ryvwbZt29YI2RfytzQkfq25ocyYMUNut1vXXHONjDGKi4vT+PHjNWfOnPP+7HVLEGx/s2fPVl5engoKCgL+Vz927Fj/7d69e6tPnz66/PLLVVBQoJtuuqlJeqlNXfpLTU1Vamqqf53Bgwfryiuv1IsvvqjHHnusuYZeZ8HuwwULFqh3794aNGhQwPzm2IdZWVnasWOHNm3a1Cjbb26N0d+JEyd02223yRijefPmBSybNm2a/3afPn3Utm1b3XPPPcrJyWm0y6c3ZI82/h1prOfowoULdfvtt9c4OtrU+7ClvwZb/rtwA4qIiNDChQt19OhRffHFF9q/f7+6du2qqKgodezYUZIUHx9f4yzn8vJyxcfH+5efmXe2muZSl/7OePrppzV79my9++676tOnzzm3e9lll+mSSy7R3r17G3P45xVMf2e0adNG/fv394/d5v0nBdejz+dTXl5erYehf6yx9+GUKVO0atUqbdiwQZ06dTpnbUO8xuLj4/X1118HLD958qS+++67RtmPDdnfGWfCypdffql169YFHF2pTUpKik6ePKkvvviiXj2cT2P0+EM/fg6Gwj6UpPfff1+7d++ucZSzNo25D4Pp73wuueQShYWFnfc1ePz4cVVUVJy1JlgEllq0adNGnTp1UlhYmPLy8vQP//APAf9Dz8/PD6hft26d/3/tycnJio+PD6jxer3asmVLwP/sm9O5+pOkOXPm6LHHHtOaNWsCPj8+m4MHD+rw4cNKSEhozGHX2fn6+6FTp07pk08+8Y+9Jew/qW49Llu2TFVVVfrNb35z3u011j40xmjKlClavny51q9fr+Tk5POu0xCvsdTUVFVUVKi4uNhfs379elVXVyslJaUhWpPUOP1Jfw8re/bs0XvvvaeLL774vNstKSlRq1atanyMcqEaq8cf+/FzsKXvwzMWLFigAQMGqG/fvufdbmPsw/r0dz5t27bVgAEDAv4NqqurlZ+f7/83GDBggNq0aRNQs3v3bu3fv7/+f0vrdapuC3HkyBGzfft2s337diPJPPPMM2b79u3+M+0ffvhhc8cdd/jrd+/ebf7yl7+Y//u//zNbtmwxY8aMMTExMaa0tNRf88EHH5jWrVubp59+2nz66adm1qxZtX7lsn379mblypXm448/NqNHj26Ur8U2Rn+zZ882bdu2Nf/1X/8V8FW7I0eO+B9z+vTppqioyJSWlpr33nvP/PznPzfdu3c3x44ds76/f//3fzdr1641+/btM8XFxWbs2LEmPDzc7Ny5M+DfoCn2X2P1eMa1115rxowZU+tjNtU+nDx5snG5XKagoCDg+XT06FF/zR133GEefvhh//2Geo0NHz7c9O/f32zZssVs2rTJdO/evcG/EtsY/R0/ftzccsstplOnTqakpCRgu1VVVcYYYz788EPz7LPPmpKSErNv3z7z2muvmY4dO5rMzMwG7a+xeqzrc7Cl7sMzPB6Pueiii8y8efNqPG5T7cP69FdVVeX/u5SQkGCmT59utm/fbvbs2eOvycvLM06n0+Tm5ppdu3aZu+++27Rv3z7gMhH33nuv6dy5s1m/fr3Ztm2bSU1NNampqfXuJaQDy4YNG4ykGtP48eONMae/VnfDDTf463ft2mX69etnIiIiTHR0tBk9erT57LPPamz3jTfeMFdccYVp27atueqqq8zbb78dsLy6utrMmDHDxMXFGafTaW666Saze/fuFtFfly5dat3mrFmzjDHGHD161AwbNsx07NjRtGnTxnTp0sXcddddNa5lYmt/U6dONZ07dzZt27Y1cXFx5pe//KX56KOPAmqaav81Vo/GGPPZZ58ZSebdd9+tsawp92FtvUkKuCbMDTfc4O/3jIZ4jR0+fNiMGzfOtGvXzkRHR5s777zTH7xt7q+0tPSs292wYYMxxpji4mKTkpJiXC6XCQ8PN1deeaV54oknGjxwNlaPdX0OttR9eMaLL75oIiIiTEVFRY1lTbUP69Pf2Z6DP/xbZIwx//mf/+n/ezpo0CCzefPmgOXff/+9+Zd/+RfToUMHc9FFF5lf/epXpqysrN69OP5/QwAAANbiHBYAAGA9AgsAALAegQUAAFiPwAIAAKxHYAEAANYjsAAAAOsRWAAAgPUILAAAwHoEFgAAYD0CCwAAsB6BBQAAWI/AAgAArPf/AHQlBelcujxTAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# plot scaler interval\n", + "plot_intervals(x[2], y_i[2])" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "07738b78-0491-48e7-bf29-46599371b926", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x[2]" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "73e9ee8c-4109-445e-999f-1275f155fdc3", + "metadata": {}, + "outputs": [], + "source": [ + "# x_test = np.array([[2]])\n", + "# len(x_test)\n", + "# x_test.shape\n", + "# # *x_test.shape == 1" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "5c55c689-70a7-4151-999f-32cc2079c4a0", + "metadata": {}, + "outputs": [], + "source": [ + "# plot_intervals(x_test, y_i[2])" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "4336c600-e35a-4e5d-b387-2479f8449e95", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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/QoGg/J4PBHbPR5Pt68/+3w+W+sHi54cq/Hjx+vZZ5/VP//5T61YsUKhUEhTpkzRf//731iMPKB097gKBAL65ptvDE3VP6Wmpmrp0qV69dVX9eqrr8rr9erSSy9VfX296dFiKhQK6a677tKFF16o9PT0btcN1eesH4r2WA3l56ytW7fqpJNOktPp1O23367Vq1drwoQJXa419ZgaEP+1F/1Pbm5uRFlPmTJF55xzjv7617/qoYceMjgZBrLx48dr/Pjx4a+nTJmizz77TE888YT+/ve/G5wstubMmaNt27bpvffeMz1KvxftsRrKz1njx49XQ0OD/H6/XnnlFRUXF+vdd9/tNkhMGLRnRkaNGqX4+Hi1tLREbG9paVFKSkqX+6SkpNhaP1j05Fj92PDhwzVx4kTt2rWrL0Yc0Lp7XCUmJuqEE04wNNXAkZ2dPaQeV3fccYfeeOMNvfPOOxo7duwx1w7V56wj7ByrHxtKz1kJCQk688wzlZWVpYqKCmVkZOjJJ5/scq2px9SgjZGEhARlZWWpuro6vC0UCqm6urrb18pyc3Mj1kvSW2+91e36waInx+rHgsGgtm7dqtTU1L4ac8Aaqo+r3tLQ0DAkHleWZemOO+7Q6tWrtWHDBo0bN+64+wzVx1ZPjtWPDeXnrFAopI6Oji6/Z+wx1advjzVs5cqVltPptJYvX259/PHH1m233WadfPLJls/nsyzLsm644QartLQ0vP7999+3hg0bZj322GPW9u3brfLycmv48OHW1q1bTf0KMWP3WC1YsMBav3699dlnn1l1dXXWddddZ7lcLuujjz4y9SvETFtbm7VlyxZry5YtliRr4cKF1pYtW6wvvvjCsizLKi0ttW644Ybw+t27d1snnnii9bvf/c7avn27VVlZacXHx1tVVVWmfoWYsXusnnjiCWvNmjXWp59+am3dutWaO3euFRcXZ7399tumfoWYmT17tuV2u62NGzdazc3N4dvXX38dXsNz1mE9OVZD9TmrtLTUevfdd63GxkbrP//5j1VaWmo5HA7rzTfftCyr/zymBnWMWJZl/eUvf7FOPfVUKyEhwcrOzrY+/PDD8PcuueQSq7i4OGL9Sy+9ZJ111llWQkKCde6551pr166N8cTm2DlWd911V3itx+Oxrr76aqu+vt7A1LF35OOnP74dOT7FxcXWJZdcctQ+mZmZVkJCgnX66adbzz33XMznNsHusXr00UetM844w3K5XFZSUpJ16aWXWhs2bDAzfIx1dZwkRTxWeM46rCfHaqg+Z910003WaaedZiUkJFinnHKKdfnll4dDxLL6z2PKYVmW1bfnXgAAALo3aN8zAgAABgZiBAAAGEWMAAAAo4gRAABgFDECAACMIkYAAIBRxAgAADCKGAEAAEYRIwAAwChiBAAAGEWMAAAAo4gRAABg1P8DuWcTxtU00uYAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_lower_bound(x, y_i)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "147f11e7-a859-449e-ac5d-3bcc2aefdbe8", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "transformer", + "language": "python", + "name": "transformer" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}