11#include <Python.h>
2- #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
3- #include <numpy/arrayobject.h>
42
53#include "gtest/gtest.h"
64#include <cmath>
5+ #include <vector>
76
87#include "TSystem.h"
98#include "TMVA/RSofieReader.hxx"
@@ -21,6 +20,20 @@ const char *PyStringAsString(PyObject *string)
2120 return cstring ;
2221}
2322
23+ // Read a NumPy array bound in the Python namespace into a std::vector<float>.
24+ // The array is flattened to a Python list and read through the generic CPython
25+ // API, so this avoids any dependency on the NumPy C API (arrayobject.h).
26+ std ::vector < float > GetTensorValues (PyObject * globalNS , PyObject * localNS , const char * name )
27+ {
28+ std ::string code = std ::string ("outputFlat=" ) + name + ".flatten().tolist()" ;
29+ PyRun_String (code .c_str (), Py_single_input , globalNS , localNS );
30+ PyObject * list = PyDict_GetItemString (localNS , "outputFlat" );
31+ std ::vector < float > values (PyList_Size (list ));
32+ for (std ::size_t i = 0 ; i < values .size (); ++ i )
33+ values [i ] = (float )PyFloat_AsDouble (PyList_GetItem (list , i ));
34+ return values ;
35+ }
36+
2437} // namespace
2538
2639void GenerateModels () {
@@ -78,15 +91,12 @@ TEST(RModelParser_Keras, SEQUENTIAL)
7891 "0.63637339, 0.94483464, 0.11032887, 0.22424818,"
7992 "0.50972592, 0.04671024, 0.39230661, 0.80500943]).reshape(4,8)" ,Py_single_input ,fGlobalNS ,fLocalNS );
8093 PyRun_String ("output=model(input).numpy()" ,Py_single_input ,fGlobalNS ,fLocalNS );
81- PyRun_String ( "outputSize=output.size" , Py_single_input , fGlobalNS ,fLocalNS );
82- std ::size_t pOutputSequentialSize = ( std :: size_t ) PyLong_AsLong ( PyDict_GetItemString ( fLocalNS , "outputSize" ) );
94+ std :: vector < float > pOutputSequential = GetTensorValues ( fGlobalNS ,fLocalNS , "output" );
95+ std ::size_t pOutputSequentialSize = pOutputSequential . size ( );
8396
8497 //Testing the actual and expected output tensor sizes
8598 EXPECT_EQ (outputSequential .size (), pOutputSequentialSize );
8699
87- PyArrayObject * pSequentialValues = (PyArrayObject * )PyDict_GetItemString (fLocalNS ,"output" );
88- float * pOutputSequential = (float * )PyArray_DATA (pSequentialValues );
89-
90100 //Testing the actual and expected output tensor values
91101 for (size_t i = 0 ; i < outputSequential .size (); ++ i ) {
92102 EXPECT_LE (std ::abs (outputSequential [i ] - pOutputSequential [i ]), TOLERANCE );
@@ -124,15 +134,12 @@ TEST(RModelParser_Keras, FUNCTIONAL)
124134 PyRun_String ("input=numpy.array([0.60828574, 0.50069386, 0.75186709, 0.14968806, 0.7692464 ,0.77027585, 0.75095316, 0.96651197,"
125135 "0.38536308, 0.95565917, 0.62796356, 0.13818375, 0.65484891,0.89220363, 0.23879365, 0.00635323]).reshape(2,8)" ,Py_single_input ,fGlobalNS ,fLocalNS );
126136 PyRun_String ("output=model(input).numpy()" ,Py_single_input ,fGlobalNS ,fLocalNS );
127- PyRun_String ( "outputSize=output.size" , Py_single_input , fGlobalNS ,fLocalNS );
128- std ::size_t pOutputFunctionalSize = ( std :: size_t ) PyLong_AsLong ( PyDict_GetItemString ( fLocalNS , "outputSize" ) );
137+ std :: vector < float > pOutputFunctional = GetTensorValues ( fGlobalNS ,fLocalNS , "output" );
138+ std ::size_t pOutputFunctionalSize = pOutputFunctional . size ( );
129139
130140 //Testing the actual and expected output tensor sizes
131141 EXPECT_EQ (outputFunctional .size (), pOutputFunctionalSize );
132142
133- PyArrayObject * pFunctionalValues = (PyArrayObject * )PyDict_GetItemString (fLocalNS ,"output" );
134- float * pOutputFunctional = (float * )PyArray_DATA (pFunctionalValues );
135-
136143 //Testing the actual and expected output tensor values
137144 for (size_t i = 0 ; i < outputFunctional .size (); ++ i ) {
138145 EXPECT_LE (std ::abs (outputFunctional [i ] - pOutputFunctional [i ]), TOLERANCE );
@@ -169,15 +176,12 @@ TEST(RModelParser_Keras, BATCH_NORM)
169176 PyRun_String ("input=numpy.array([0.22308163, 0.95274901, 0.44712538, 0.84640867,"
170177 "0.69947928, 0.29743695, 0.81379782, 0.39650574]).reshape(2,4)" ,Py_single_input ,fGlobalNS ,fLocalNS );
171178 PyRun_String ("output=model(input).numpy()" ,Py_single_input ,fGlobalNS ,fLocalNS );
172- PyRun_String ( "outputSize=output.size" , Py_single_input , fGlobalNS ,fLocalNS );
173- std ::size_t pOutputBatchNormSize = ( std :: size_t ) PyLong_AsLong ( PyDict_GetItemString ( fLocalNS , "outputSize" ) );
179+ std :: vector < float > pOutputBatchNorm = GetTensorValues ( fGlobalNS ,fLocalNS , "output" );
180+ std ::size_t pOutputBatchNormSize = pOutputBatchNorm . size ( );
174181
175182 //Testing the actual and expected output tensor sizes
176183 EXPECT_EQ (outputBatchNorm .size (), pOutputBatchNormSize );
177184
178- PyArrayObject * pBatchNormValues = (PyArrayObject * )PyDict_GetItemString (fLocalNS ,"output" );
179- float * pOutputBatchNorm = (float * )PyArray_DATA (pBatchNormValues );
180-
181185 //Testing the actual and expected output tensor values
182186 for (size_t i = 0 ; i < outputBatchNorm .size (); ++ i ) {
183187 EXPECT_LE (std ::abs (outputBatchNorm [i ] - pOutputBatchNorm [i ]), TOLERANCE );
@@ -218,15 +222,12 @@ TEST(DISABLED_RModelParser_Keras, CONV_VALID)
218222 PyRun_String ("model=load_model('KerasModelConv2D_Valid.keras')" ,Py_single_input ,fGlobalNS ,fLocalNS );
219223 PyRun_String ("input=numpy.ones((1,4,4,1))" ,Py_single_input ,fGlobalNS ,fLocalNS );
220224 PyRun_String ("output=model(input).numpy()" ,Py_single_input ,fGlobalNS ,fLocalNS );
221- PyRun_String ( "outputSize=output.size" , Py_single_input , fGlobalNS ,fLocalNS );
222- std ::size_t pOutputConv2DValidSize = ( std :: size_t ) PyLong_AsLong ( PyDict_GetItemString ( fLocalNS , "outputSize" ) );
225+ std :: vector < float > pOutputConv2DValid = GetTensorValues ( fGlobalNS ,fLocalNS , "output" );
226+ std ::size_t pOutputConv2DValidSize = pOutputConv2DValid . size ( );
223227
224228 //Testing the actual and expected output tensor sizes
225229 EXPECT_EQ (outputConv2D_Valid .size (), pOutputConv2DValidSize );
226230
227- PyArrayObject * pConv2DValidValues = (PyArrayObject * )PyDict_GetItemString (fLocalNS ,"output" );
228- float * pOutputConv2DValid = (float * )PyArray_DATA (pConv2DValidValues );
229-
230231 //Testing the actual and expected output tensor values
231232 for (size_t i = 0 ; i < outputConv2D_Valid .size (); ++ i ) {
232233 EXPECT_LE (std ::abs (outputConv2D_Valid [i ] - pOutputConv2DValid [i ]), TOLERANCE );
@@ -268,15 +269,12 @@ TEST(DISABLED_RModelParser_Keras, CONV_SAME)
268269 PyRun_String ("model=load_model('KerasModelConv2D_Same.keras')" ,Py_single_input ,fGlobalNS ,fLocalNS );
269270 PyRun_String ("input=numpy.ones((1,4,4,1))" ,Py_single_input ,fGlobalNS ,fLocalNS );
270271 PyRun_String ("output=model(input).numpy()" ,Py_single_input ,fGlobalNS ,fLocalNS );
271- PyRun_String ( "outputSize=output.size" , Py_single_input , fGlobalNS ,fLocalNS );
272- std ::size_t pOutputConv2DSameSize = ( std :: size_t ) PyLong_AsLong ( PyDict_GetItemString ( fLocalNS , "outputSize" ) );
272+ std :: vector < float > pOutputConv2DSame = GetTensorValues ( fGlobalNS ,fLocalNS , "output" );
273+ std ::size_t pOutputConv2DSameSize = pOutputConv2DSame . size ( );
273274
274275 //Testing the actual and expected output tensor sizes
275276 EXPECT_EQ (outputConv2D_Same .size (), pOutputConv2DSameSize );
276277
277- PyArrayObject * pConv2DSameValues = (PyArrayObject * )PyDict_GetItemString (fLocalNS ,"output" );
278- float * pOutputConv2DSame = (float * )PyArray_DATA (pConv2DSameValues );
279-
280278 //Testing the actual and expected output tensor values
281279 for (size_t i = 0 ; i < outputConv2D_Same .size (); ++ i ) {
282280 EXPECT_LE (std ::abs (outputConv2D_Same [i ] - pOutputConv2DSame [i ]), TOLERANCE );
@@ -314,15 +312,12 @@ TEST(RModelParser_Keras, RESHAPE)
314312 PyRun_String ("model=load_model('KerasModelReshape.keras')" ,Py_single_input ,fGlobalNS ,fLocalNS );
315313 PyRun_String ("input=numpy.ones((1,4,4,1))" ,Py_single_input ,fGlobalNS ,fLocalNS );
316314 PyRun_String ("output=model(input).numpy()" ,Py_single_input ,fGlobalNS ,fLocalNS );
317- PyRun_String ( "outputSize=output.size" , Py_single_input , fGlobalNS ,fLocalNS );
318- std ::size_t pOutputReshapeSize = ( std :: size_t ) PyLong_AsLong ( PyDict_GetItemString ( fLocalNS , "outputSize" ) );
315+ std :: vector < float > pOutputReshape = GetTensorValues ( fGlobalNS ,fLocalNS , "output" );
316+ std ::size_t pOutputReshapeSize = pOutputReshape . size ( );
319317
320318 //Testing the actual and expected output tensor sizes
321319 EXPECT_EQ (outputReshape .size (), pOutputReshapeSize );
322320
323- PyArrayObject * pReshapeValues = (PyArrayObject * )PyDict_GetItemString (fLocalNS ,"output" );
324- float * pOutputReshape = (float * )PyArray_DATA (pReshapeValues );
325-
326321 //Testing the actual and expected output tensor values
327322 for (size_t i = 0 ; i < outputReshape .size (); ++ i ) {
328323 EXPECT_LE (std ::abs (outputReshape [i ] - pOutputReshape [i ]), TOLERANCE );
@@ -359,14 +354,12 @@ TEST(RModelParser_Keras, CONCATENATE)
359354 PyRun_String ("input_1=numpy.ones((1,2))" ,Py_single_input ,fGlobalNS ,fLocalNS );
360355 PyRun_String ("input_2=numpy.ones((1,2))" ,Py_single_input ,fGlobalNS ,fLocalNS );
361356 PyRun_String ("output=model([input_1,input_2]).numpy()" ,Py_single_input ,fGlobalNS ,fLocalNS );
362- PyRun_String ( "outputSize=output.size" , Py_single_input , fGlobalNS ,fLocalNS );
363- long pOutputConcatenateSize = PyLong_AsLong ( PyDict_GetItemString ( fLocalNS , "outputSize" ) );
357+ std :: vector < float > pOutputConcatenate = GetTensorValues ( fGlobalNS ,fLocalNS , "output" );
358+ long pOutputConcatenateSize = ( long ) pOutputConcatenate . size ( );
364359
365360 //Testing the actual and expected output tensor sizes (can fail if an error eccoured and returns a -1 from Python)
366361 EXPECT_EQ ((long ) outputConcatenate .size (), pOutputConcatenateSize );
367362
368- PyArrayObject * pConcatenateValues = (PyArrayObject * )PyDict_GetItemString (fLocalNS ,"output" );
369- float * pOutputConcatenate = (float * )PyArray_DATA (pConcatenateValues );
370363
371364 //Testing the actual and expected output tensor values
372365 for (size_t i = 0 ; i < outputConcatenate .size (); ++ i ) {
@@ -405,14 +398,12 @@ TEST(RModelParser_Keras, BINARY_OP)
405398 PyRun_String ("input1=numpy.array([[1,2],[3,4]],dtype='float32')" ,Py_single_input ,fGlobalNS ,fLocalNS );
406399 PyRun_String ("input2=numpy.array([[5,6],[7,8]],dtype='float32')" ,Py_single_input ,fGlobalNS ,fLocalNS );
407400 PyRun_String ("output=model([input1,input2]).numpy()" ,Py_single_input ,fGlobalNS ,fLocalNS );
408- PyRun_String ( "outputSize=output.size" , Py_single_input , fGlobalNS ,fLocalNS );
409- long pOutputBinaryOpSize = PyLong_AsLong ( PyDict_GetItemString ( fLocalNS , "outputSize" ) );
401+ std :: vector < float > pOutputBinaryOp = GetTensorValues ( fGlobalNS ,fLocalNS , "output" );
402+ long pOutputBinaryOpSize = ( long ) pOutputBinaryOp . size ( );
410403
411404 //Testing the actual and expected output tensor sizes
412405 EXPECT_EQ ((long ) outputBinaryOp .size (), pOutputBinaryOpSize );
413406
414- PyArrayObject * pBinaryOpValues = (PyArrayObject * )PyDict_GetItemString (fLocalNS ,"output" );
415- float * pOutputBinaryOp = (float * )PyArray_DATA (pBinaryOpValues );
416407
417408 //Testing the actual and expected output tensor values
418409 for (size_t i = 0 ; i < outputBinaryOp .size (); ++ i ) {
@@ -448,15 +439,12 @@ TEST(RModelParser_Keras, ACTIVATIONS)
448439 PyRun_String ("model=load_model('KerasModelActivations.keras')" ,Py_single_input ,fGlobalNS ,fLocalNS );
449440 PyRun_String ("input=numpy.ones((1,8))" ,Py_single_input ,fGlobalNS ,fLocalNS );
450441 PyRun_String ("output=model(input).numpy()" ,Py_single_input ,fGlobalNS ,fLocalNS );
451- PyRun_String ( "outputSize=output.size" , Py_single_input , fGlobalNS ,fLocalNS );
452- std ::size_t pOutputActivationsSize = ( std :: size_t ) PyLong_AsLong ( PyDict_GetItemString ( fLocalNS , "outputSize" ) );
442+ std :: vector < float > pOutputActivations = GetTensorValues ( fGlobalNS ,fLocalNS , "output" );
443+ std ::size_t pOutputActivationsSize = pOutputActivations . size ( );
453444
454445 //Testing the actual and expected output tensor sizes
455446 EXPECT_EQ (outputActivations .size (), pOutputActivationsSize );
456447
457- PyArrayObject * pActivationsValues = (PyArrayObject * )PyDict_GetItemString (fLocalNS ,"output" );
458- float * pOutputActivations = (float * )PyArray_DATA (pActivationsValues );
459-
460448 //Testing the actual and expected output tensor values
461449 for (size_t i = 0 ; i < outputActivations .size (); ++ i ) {
462450 EXPECT_LE (std ::abs (outputActivations [i ] - pOutputActivations [i ]), TOLERANCE );
@@ -491,15 +479,12 @@ TEST(RModelParser_Keras, SWISH)
491479 PyRun_String ("model=load_model('KerasModelSwish.keras')" ,Py_single_input ,fGlobalNS ,fLocalNS );
492480 PyRun_String ("input=numpy.ones((1,8))" ,Py_single_input ,fGlobalNS ,fLocalNS );
493481 PyRun_String ("output=model(input).numpy()" ,Py_single_input ,fGlobalNS ,fLocalNS );
494- PyRun_String ( "outputSize=output.size" , Py_single_input , fGlobalNS ,fLocalNS );
495- std ::size_t pOutputSize = ( std :: size_t ) PyLong_AsLong ( PyDict_GetItemString ( fLocalNS , "outputSize" ) );
482+ std :: vector < float > pOutput = GetTensorValues ( fGlobalNS ,fLocalNS , "output" );
483+ std ::size_t pOutputSize = pOutput . size ( );
496484
497485 //Testing the actual and expected output tensor sizes
498486 EXPECT_EQ (output .size (), pOutputSize );
499487
500- PyArrayObject * pValues = (PyArrayObject * )PyDict_GetItemString (fLocalNS ,"output" );
501- float * pOutput = (float * )PyArray_DATA (pValues );
502-
503488 //Testing the actual and expected output tensor values
504489 for (size_t i = 0 ; i < output .size (); ++ i ) {
505490 EXPECT_LE (std ::abs (output [i ] - pOutput [i ]), TOLERANCE );
@@ -537,8 +522,8 @@ TEST(RModel, CUSTOM_OP)
537522 PyRun_String ("model.add(Lambda(lambda x: x * 2))" ,Py_single_input ,fGlobalNS ,fLocalNS );
538523 PyRun_String ("input=numpy.array([1,1,1,1,1,1,1,1]).reshape(1,8)" ,Py_single_input ,fGlobalNS ,fLocalNS );
539524 PyRun_String ("output=model(input).numpy()" ,Py_single_input ,fGlobalNS ,fLocalNS );
540- PyRun_String ( "outputSize=output.size" , Py_single_input , fGlobalNS ,fLocalNS );
541- std ::size_t pOutputCustomOpSize = ( std :: size_t ) PyLong_AsLong ( PyDict_GetItemString ( fLocalNS , "outputSize" ) );
525+ std :: vector < float > pOutputCustomOp = GetTensorValues ( fGlobalNS ,fLocalNS , "output" );
526+ std ::size_t pOutputCustomOpSize = pOutputCustomOp . size ( );
542527
543528 // get input name for custom (it is output of one before last)
544529 PyRun_String ("outputName = model.get_layer(index=len(model.layers)-2).output.name" ,Py_single_input ,fGlobalNS ,fLocalNS );
@@ -557,9 +542,6 @@ TEST(RModel, CUSTOM_OP)
557542 //Testing the actual and expected output tensor sizes
558543 EXPECT_EQ (outputCustomOp .size (), pOutputCustomOpSize );
559544
560- PyArrayObject * pCustomOpValues = (PyArrayObject * )PyDict_GetItemString (fLocalNS ,"output" );
561- float * pOutputCustomOp = (float * )PyArray_DATA (pCustomOpValues );
562-
563545 //Testing the actual and expected output tensor values
564546 for (size_t i = 0 ; i < outputCustomOp .size (); ++ i ) {
565547 EXPECT_LE (std ::abs (outputCustomOp [i ] - pOutputCustomOp [i ]), TOLERANCE );
0 commit comments