1+ import logging
12import warnings
23from typing import List , Any
34
5+ import numpy as np
6+
47from pycubexr .classes import CNode , Metric
58from pycubexr .classes .metric import MetricType
6- from pycubexr .classes .values import BaseValue
9+ from pycubexr .classes .values import CubeValues
710from pycubexr .utils .exceptions import InvalidConversionInstructionError
811
912
@@ -14,7 +17,7 @@ def __init__(
1417 * ,
1518 metric : Metric ,
1619 cnode_indices : List [int ],
17- values : List [ Any ]
20+ values : np . ndarray
1821 ):
1922 self .metric = metric
2023 self .values = values
@@ -38,11 +41,11 @@ def cnode_values(self, cnode: CNode, convert_to_exclusive: bool = False, convert
3841
3942 cid = self .metric .tree_enumeration [cnode .id ]
4043 if cid not in self .cnode_indices :
41- values = [ 0 ] * self .num_locations ()
44+ values = np . zeros ( self .num_locations (), dtype = self . values . dtype )
4245 else :
4346 start_index = int (self .cnode_indices [cid ] * self .num_locations ())
4447 end_index = start_index + self .num_locations ()
45- values = self .values [start_index : end_index ] # creates copy
48+ values = self .values [start_index : end_index ] # creates no copy
4649
4750 must_convert = ((convert_to_exclusive and self .metric .metric_type == MetricType .INCLUSIVE )
4851 or (convert_to_inclusive and self .metric .metric_type == MetricType .EXCLUSIVE ))
@@ -70,32 +73,65 @@ def _isub(a, b):
7073 for i , y in enumerate (b ):
7174 a [i ] -= y
7275
73- def _convert_values (self , cnode : CNode , values : List [Any ], to_inclusive : bool = True ):
76+ @staticmethod
77+ def _detect_negative_overflow (a , b ):
78+ if np .issubdtype (a .dtype , np .unsignedinteger ):
79+ check = a < b
80+ if np .any (check ):
81+ b = b .copy ()
82+ b [check ] = a [check ]
83+ elif np .issubdtype (a .dtype , np .signedinteger ):
84+ pre_check = a > 0
85+ check = np .full_like (pre_check , False , dtype = bool )
86+ check [~ pre_check ] = - b [~ pre_check ] < np .iinfo (a .dtype ).min - a [~ pre_check ]
87+ check [pre_check ] |= - b [pre_check ] > np .iinfo (a .dtype ).max - a [pre_check ]
88+ if np .any (check ):
89+ if not np .issubdtype (a .dtype , np .int64 ):
90+ bigger_type = np .min_scalar_type (int (np .iinfo (a .dtype ).min ) - 1 )
91+ b = b .astype (bigger_type , copy = False )
92+ else :
93+ b = b .copy ()
94+ b [~ pre_check & check ] = - (np .iinfo (a .dtype ).min - a [~ pre_check & check ])
95+ b [pre_check & check ] = - (np .iinfo (a .dtype ).max - a [pre_check & check ])
96+ return b
97+
98+ def _convert_values (self , cnode : CNode , values : np .ndarray , to_inclusive : bool = True ):
7499 # Go over all cnode children and add the metric values
75- # Does change the values array!
100+ values = values . copy ()
76101 for child_cnode in cnode .get_children ():
77102 child_values = self .cnode_values (child_cnode ,
78103 convert_to_inclusive = True ,
79104 convert_to_exclusive = False )
80105 if to_inclusive :
81- self . _iadd ( values , child_values )
106+ values += child_values
82107 else :
83- self ._isub (values , child_values )
108+ if isinstance (values , np .ndarray ) and np .issubdtype (values .dtype , np .integer ):
109+ child_values = self ._detect_negative_overflow (values , child_values )
110+ values -= child_values
84111 return values
85112
86113 def value (self , cnode : CNode , convert_to_inclusive = False , convert_to_exclusive = False ):
87- res = sum (self .cnode_values (cnode , convert_to_inclusive , convert_to_exclusive ))
88- if isinstance (res , BaseValue ):
89- return res .try_convert ()
114+ res = self .cnode_values (cnode , convert_to_inclusive , convert_to_exclusive )
115+ sum_ = res .sum ()
116+ if isinstance (res , np .ndarray ) and np .issubdtype (res .dtype , np .integer ):
117+ if res .max () > np .iinfo (res .dtype ).max // len (res ):
118+ logging .info ("Used overflow prevention for {0} of {1}" .format (self .metric .name , cnode .region .name ))
119+ # Overflow prevention, really slow
120+ sum_ = 0
121+ for r in res :
122+ sum_ += int (r )
123+
124+ if isinstance (sum_ , CubeValues ):
125+ return sum_ .astype (float )
90126 else :
91- return res
127+ return sum_
92128
93129 def mean (self , cnode : CNode , convert_to_inclusive = False , convert_to_exclusive = False ):
94- values = self .cnode_values (cnode , convert_to_inclusive , convert_to_exclusive )
95- res = sum ( values )
96- if isinstance ( res , BaseValue ):
97- res = res . try_convert ()
98- return res / len ( values )
130+ res = self .cnode_values (cnode , convert_to_inclusive , convert_to_exclusive ). mean ( )
131+ if isinstance ( res , CubeValues ):
132+ return res . astype ( float )
133+ else :
134+ return res
99135
100136 def __repr__ (self ):
101137 return 'MetricValues<{}>' .format (self .__dict__ )
0 commit comments