@@ -76,6 +76,48 @@ def determinant(a: Union["Quantity[ArrayLike]", ArrayLike]):
7676 return np .linalg .det (a )
7777
7878
79+ def norm_1 (a : Union ["Quantity[ArrayLike]" , ArrayLike ], axes : int | tuple [int ] | None = None ):
80+ """Caculate the 1-norm of an array or an array based quantity."""
81+ if isinstance (a , Quantity ):
82+ if axes is None :
83+ return DerivedQuantity (
84+ value = np .linalg .norm (a .value , ord = 1 , axes = axes ),
85+ units = a .units ,
86+ history = QuantityHistory .apply_operation (Norm_1 , a .history ),
87+ )
88+
89+ else :
90+ return DerivedQuantity (
91+ value = np .linalg .norm (a .value , ord = 1 , axes = axes ),
92+ units = a .units ,
93+ history = QuantityHistory .apply_operation (Norm_1 , a .history , axes = axes ),
94+ )
95+
96+ else :
97+ return np .linalg .norm (a .value , ord = 1 , axes = axes )
98+
99+
100+ def norm_2 (a : Union ["Quantity[ArrayLike]" , ArrayLike ], axes : int | tuple [int ] | None = None ):
101+ """Caculate the 2-norm of an array or an array based quantity."""
102+ if isinstance (a , Quantity ):
103+ if axes is None :
104+ return DerivedQuantity (
105+ value = np .linalg .norm (a .value , axes = axes ),
106+ units = a .units ,
107+ history = QuantityHistory .apply_operation (Norm_2 , a .history ),
108+ )
109+
110+ else :
111+ return DerivedQuantity (
112+ value = np .linalg .norm (a .value , axes = axes ),
113+ units = a .units ,
114+ history = QuantityHistory .apply_operation (Norm_2 , a .history , axes = axes ),
115+ )
116+
117+ else :
118+ return np .linalg .norm (a .value , axes = axes )
119+
120+
79121def dot (a : Union ["Quantity[ArrayLike]" , ArrayLike ], b : Union ["Quantity[ArrayLike]" , ArrayLike ]):
80122 """Dot product of two arrays or two array based quantities"""
81123 a_is_quantity = isinstance (a , Quantity )
@@ -331,6 +373,34 @@ def __eq__(self, other):
331373 return False
332374
333375
376+ class MatrixIdentity (ConstantBase ):
377+ serialisation_name = "identity"
378+
379+ def __init__ (self , size ):
380+ self .size = size
381+
382+ def evaluate (self , variables : dict [int , T ]) -> T :
383+ return np .eye (self .size )
384+
385+ def _derivative (self , hash_value : int ):
386+ return Constant (np .zeros ((self .size , self .size )))
387+
388+ @staticmethod
389+ def _deserialise (parameters : dict ) -> "Operation" :
390+ return MatrixIdentity (self .size )
391+
392+ def _serialise_parameters (self ) -> dict [str , Any ]:
393+ return {"size" : numerical_encode (self .size )}
394+
395+ def summary (self , indent_amount : int = 0 , indent = " " ):
396+ return f"{ indent_amount * indent } { self .size } [Matrix Id.]"
397+
398+ def __eq__ (self , other ):
399+ if isinstance (other , MatrixIdentity ):
400+ return self .size == other .size
401+ return False
402+
403+
334404class Constant (ConstantBase ):
335405 serialisation_name = "constant"
336406
@@ -1071,7 +1141,7 @@ def summary(self, indent_amount: int = 0, indent=" "):
10711141 f"{ indent_amount * indent } Transpose(\n "
10721142 + self .a .summary (indent_amount + 1 , indent )
10731143 + "\n "
1074- + f"{ (indent_amount + 1 ) * indent } { self .axes } \n "
1144+ + f"{ (indent_amount + 1 ) * indent } { list ( self .axes ) } \n "
10751145 + f"{ indent_amount * indent } )"
10761146 )
10771147
@@ -1248,6 +1318,116 @@ def _deserialise(parameters: dict) -> "Operation":
12481318 )
12491319
12501320
1321+ class Norm_1 (Operation ):
1322+ """1-norm of a matrix from numpy"""
1323+
1324+ serialisation_name = "norm_1"
1325+
1326+ def __init__ (self , a : Operation , axes : int | tuple [int ] | None = None ):
1327+ self .a = a
1328+ self .axes = axes
1329+
1330+ def evaluate (self , variables : dict [int , T ]) -> T :
1331+ return np .linalg .norm (self .a .evaluate (variables ), ord = 1 , axis = self .axes )
1332+
1333+ def _derivative (self , hash_value : int ) -> Operation :
1334+ return np .sign (self .a )
1335+
1336+ def _clean (self ):
1337+ clean_a = self .a ._clean ()
1338+ return Norm_1 (clean_a , self .axes )
1339+
1340+ def _serialise_parameters (self ) -> dict [str , Any ]:
1341+ if self .axes is None :
1342+ return {"a" : self .a ._serialise_json ()}
1343+ else :
1344+ return {"a" : self .a ._serialise_json (), "axes" : list (self .axes )}
1345+
1346+ @staticmethod
1347+ def _deserialise (parameters : dict ) -> "Operation" :
1348+ if "axes" in parameters :
1349+ return Norm_1 (a = Operation .deserialise_json (parameters ["a" ]), axes = tuple (parameters ["axes" ]))
1350+ else :
1351+ return Norm_1 (a = Operation .deserialise_json (parameters ["a" ]))
1352+
1353+ def summary (self , indent_amount : int = 0 , indent = " " ):
1354+ if self .axes is None :
1355+ return (
1356+ f"{ indent_amount * indent } Norm_1(\n "
1357+ + self .a .summary (indent_amount + 1 , indent )
1358+ + "\n "
1359+ + f"{ indent_amount * indent } )"
1360+ )
1361+ else :
1362+ return (
1363+ f"{ indent_amount * indent } Norm_1(\n "
1364+ + self .a .summary (indent_amount + 1 , indent )
1365+ + "\n "
1366+ + f"{ (indent_amount + 1 ) * indent } { list (self .axes )} \n "
1367+ + f"{ indent_amount * indent } )"
1368+ )
1369+
1370+ def __eq__ (self , other ):
1371+ if isinstance (other , Norm_1 ):
1372+ return other .a == self .a
1373+ return False
1374+
1375+
1376+ class Norm_2 (Operation ):
1377+ """2-norm of a matrix from numpy"""
1378+
1379+ serialisation_name = "norm_2"
1380+
1381+ def __init__ (self , a : Operation , axes : int | tuple [int ] | None = None ):
1382+ self .a = a
1383+ self .axes = axes
1384+
1385+ def evaluate (self , variables : dict [int , T ]) -> T :
1386+ return np .linalg .norm (self .a .evaluate (variables ), axis = self .axes )
1387+
1388+ def _derivative (self , hash_value : int ) -> Operation :
1389+ return Transpose (Div (self .a , Norm_2 (self .a , self .axes )))
1390+
1391+ def _clean (self ):
1392+ clean_a = self .a ._clean ()
1393+ return Norm_2 (clean_a , self .axes )
1394+
1395+ def _serialise_parameters (self ) -> dict [str , Any ]:
1396+ if self .axes is None :
1397+ return {"a" : self .a ._serialise_json ()}
1398+ else :
1399+ return {"a" : self .a ._serialise_json (), "axes" : list (self .axes )}
1400+
1401+ @staticmethod
1402+ def _deserialise (parameters : dict ) -> "Operation" :
1403+ if "axes" in parameters :
1404+ return Norm_2 (a = Operation .deserialise_json (parameters ["a" ]), axes = tuple (parameters ["axes" ]))
1405+ else :
1406+ return Norm_2 (a = Operation .deserialise_json (parameters ["a" ]))
1407+
1408+ def summary (self , indent_amount : int = 0 , indent = " " ):
1409+ if self .axes is None :
1410+ return (
1411+ f"{ indent_amount * indent } Norm_2(\n "
1412+ + self .a .summary (indent_amount + 1 , indent )
1413+ + "\n "
1414+ + f"{ indent_amount * indent } )"
1415+ )
1416+ else :
1417+ return (
1418+ f"{ indent_amount * indent } Norm_2(\n "
1419+ + self .a .summary (indent_amount + 1 , indent )
1420+ + "\n "
1421+ + f"{ (indent_amount + 1 ) * indent } { list (self .axes )} \n "
1422+ + f"{ indent_amount * indent } )"
1423+ )
1424+
1425+ def __eq__ (self , other ):
1426+ if isinstance (other , Norm_2 ):
1427+ return other .a == self .a
1428+ return False
1429+
1430+
12511431_serialisable_classes = [
12521432 AdditiveIdentity ,
12531433 MultiplicativeIdentity ,
@@ -1276,6 +1456,8 @@ def _deserialise(parameters: dict) -> "Operation":
12761456 MatMul ,
12771457 MatInv ,
12781458 TensorDot ,
1459+ Norm_1 ,
1460+ Norm_2 ,
12791461]
12801462
12811463_serialisation_lookup = {class_ .serialisation_name : class_ for class_ in _serialisable_classes }
0 commit comments