Skip to content

Commit a5b9ee6

Browse files
authored
Merge pull request #800 from UWB-Biocomputing/ShaikhDevelopment
Adding VertexType to Recorders
2 parents 53a67de + e9ade75 commit a5b9ee6

File tree

9 files changed

+294
-8
lines changed

9 files changed

+294
-8
lines changed

Simulator/Recorders/Hdf5Recorder.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,22 @@ void Hdf5Recorder::compileHistories()
189189
} else if (variableInfo.hdf5Datatype_ == PredType::NATIVE_INT) {
190190
vector<int> dataBuffer(variableInfo.variableLocation_.getNumElements());
191191
for (size_t i = 0; i < variableInfo.variableLocation_.getNumElements(); ++i) {
192-
dataBuffer[i] = get<int>(variableInfo.variableLocation_.getElement(i));
192+
// For type int, a distinction needs to be made between vertexType and regular integers
193+
// Since vertexType is an enum class, it needs to first be converted to an integer before storing
194+
195+
// 'decltype' determines the type at compile time
196+
// 'decay_t' removes any references/const from the type
197+
// This simplifies comparisons through 'is_same_v' to ensure type matches vertexType
198+
if (std::is_same_v<vertexType,
199+
std::decay_t<decltype(get<vertexType>(
200+
variableInfo.variableLocation_.getElement(i)))>>) {
201+
// If type matches vertexType, convert to int before storing
202+
dataBuffer[i] = static_cast<int>(
203+
get<vertexType>(variableInfo.variableLocation_.getElement(i)));
204+
} else {
205+
// Otherwise, store as a regular integer
206+
dataBuffer[i] = get<int>(variableInfo.variableLocation_.getElement(i));
207+
}
193208
}
194209
variableInfo.hdf5DataSet_.write(dataBuffer.data(), variableInfo.hdf5Datatype_,
195210
memSpace, fileSpace);

Simulator/Recorders/Hdf5Recorder.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ class Hdf5Recorder : public Recorder {
114114
hdf5Datatype_ = PredType::NATIVE_FLOAT;
115115
} else if (dataType_ == typeid(double).name()) {
116116
hdf5Datatype_ = PredType::NATIVE_DOUBLE;
117+
} else if (dataType_ == typeid(vertexType).name()) {
118+
hdf5Datatype_ = PredType::NATIVE_INT;
117119
} else {
118120
throw runtime_error("Unsupported data type");
119121
}
@@ -136,7 +138,21 @@ class Hdf5Recorder : public Recorder {
136138
} else if (hdf5Datatype_ == PredType::NATIVE_INT) {
137139
vector<int> dataBuffer(variableLocation_.getNumElements());
138140
for (int i = 0; i < variableLocation_.getNumElements(); ++i) {
139-
dataBuffer[i] = get<int>(variableLocation_.getElement(i));
141+
// For type int, a distinction needs to be made between vertexType and regular integers
142+
// Since vertexType is an enum class, it needs to first be converted to an integer before storing
143+
144+
// 'decltype' determines the type at compile time
145+
// 'decay_t' removes any references/const from the type
146+
// This simplifies comparisons through 'is_same_v' to ensure type matches vertexType
147+
if (std::is_same_v<vertexType, std::decay_t<decltype(get<vertexType>(
148+
variableLocation_.getElement(i)))>>) {
149+
// If type matches vertexType, convert to int before storing
150+
dataBuffer[i]
151+
= static_cast<int>(get<vertexType>(variableLocation_.getElement(i)));
152+
} else {
153+
// Otherwise, store as a regular integer
154+
dataBuffer[i] = get<int>(variableLocation_.getElement(i));
155+
}
140156
}
141157
hdf5DataSet_.write(dataBuffer.data(), hdf5Datatype_);
142158

Simulator/Recorders/RecordableBase.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#pragma once
1515

1616
using namespace std;
17+
#include "VertexType.h"
1718
#include <string>
1819
#include <typeinfo>
1920
#include <variant>
@@ -24,7 +25,7 @@ using namespace std;
2425

2526
/// A list of pre-defined basic data types for variablse in all the simulations
2627
/// These pre-defined types should match with the types of variant in Recorder
27-
using variantTypes = variant<uint64_t, bool, int, BGFLOAT>;
28+
using variantTypes = variant<uint64_t, bool, int, BGFLOAT, vertexType>;
2829

2930
class RecordableBase {
3031
public:

Simulator/Recorders/Recorder.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ using namespace std;
1919

2020

2121
/// a list of pre-defined basic data types in recorded variables
22-
using multipleTypes = variant<uint64_t, bool, int, BGFLOAT>;
22+
using multipleTypes = variant<uint64_t, bool, int, BGFLOAT, vertexType>;
2323

2424
//TODO: remove it after implemtating the Hdf5Recorder
2525
class AllVertices;
2626
class Recorder {
2727
public:
2828
/// The recorded variable Type/Updated frequency
29-
enum class UpdatedType {
29+
enum UpdatedType {
3030
CONSTANT, // value doesn't change in each epoch
3131
DYNAMIC // value is updated in each peoch
3232
// Add more variable types as needed

Simulator/Recorders/XmlRecorder.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ string XmlRecorder::toXML(const string &name, vector<multipleTypes> &singleBuffe
115115
os << get<int>(element) << " ";
116116
} else if (basicType == typeid(BGFLOAT).name()) {
117117
os << get<BGFLOAT>(element) << " ";
118+
} else if (basicType == typeid(vertexType).name()) {
119+
os << static_cast<int>(get<vertexType>(element)) << " ";
118120
} else {
119121
perror("Error recording Recordable object");
120122
exit(EXIT_FAILURE);

Simulator/Utils/Global.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// Globally available functions and default parameter values.
99

1010
#pragma once
11+
#include "MTRand.h"
1112

1213
// Debug output is included in both debug/release builds now.
1314
// The Default for debug is "LOW" and "OFF" for Release.
@@ -64,6 +65,7 @@ using uint64_t = unsigned long long int; //included in inttypes.h, which is no
6465
//#include "Norm.h"
6566
#include "Coordinate.h"
6667
#include "VectorMatrix.h"
68+
#include "VertexType.h"
6769

6870
using namespace std;
6971

@@ -98,6 +100,8 @@ const int g_nMaxChunkSize = 100;
98100
// CALR: Caller radii
99101
// PSAP: PSAP nodes
100102
// EMS, FIRE, LAW: Responder nodes
103+
/*
104+
// Moved to Utils/VertexType.h
101105
enum class vertexType {
102106
// Neuro
103107
INH = 1,
@@ -117,6 +121,7 @@ inline std::ostream &operator<<(std::ostream &os, vertexType vT)
117121
os << static_cast<int>(vT);
118122
return os;
119123
}
124+
*/
120125

121126
// Edge types.
122127
// NEURO:

Simulator/Utils/VertexType.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* @file VertexType.h
3+
*
4+
* @ingroup Simulator/Utils
5+
*
6+
* @brief Enum class of vertex types
7+
*/
8+
9+
// NETWORK MODEL VARIABLES NMV-BEGIN {
10+
// Vertex types.
11+
// NEURO:
12+
// INH - Inhibitory neuron
13+
// EXC - Excitory neuron
14+
// NG911:
15+
// CALR: Caller radii
16+
// PSAP: PSAP nodes
17+
// EMS, FIRE, LAW: Responder nodes
18+
19+
#ifndef VERTEX_TYPE_H
20+
#define VERTEX_TYPE_H
21+
22+
#include <ostream>
23+
24+
enum class vertexType {
25+
// Neuro
26+
INH = 1,
27+
EXC = 2,
28+
// NG911
29+
CALR = 3,
30+
PSAP = 4,
31+
EMS = 5,
32+
FIRE = 6,
33+
LAW = 7,
34+
// UNDEF
35+
VTYPE_UNDEF = 0
36+
};
37+
38+
// Custom streaming operator<< for the enum class vertexType
39+
inline std::ostream &operator<<(std::ostream &os, vertexType vT)
40+
{
41+
os << static_cast<int>(vT);
42+
return os;
43+
}
44+
45+
#endif // VERTEX_TYPE_H

Testing/UnitTesting/Hdf5RecorderTests.cpp

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,36 @@ TEST(Hdf5RecorderTest, RegisterVectorVariableTest)
9595
ASSERT_EQ(Recorder::UpdatedType::DYNAMIC, variableTable[1].variableType_);
9696
}
9797

98+
// Unit test for registerVariable method with a vector of NeuronType enums
99+
TEST(Hdf5RecorderTest, RegisterVertexTypeTest)
100+
{
101+
// Create an instance of Hdf5Recorder
102+
std::string outputFile = "../Testing/UnitTesting/TestOutput/Hdf5test_output_register.h5";
103+
Hdf5Recorder recorder(outputFile);
104+
recorder.init();
105+
106+
// Create a vector of NeuronType enums
107+
RecordableVector<vertexType> neuronTypes;
108+
neuronTypes.resize(2);
109+
neuronTypes[0] = vertexType::EXC;
110+
neuronTypes[1] = vertexType::INH;
111+
112+
// register the vector of NeuronTypes
113+
recorder.registerVariable("neuron_types", neuronTypes, Recorder::UpdatedType::DYNAMIC);
114+
115+
// Verify that the registered variables are stored correctly
116+
const auto &variableTable = recorder.getVariableTable();
117+
ASSERT_EQ(1, variableTable.size()); // Only one variable, "neuron_types"
118+
119+
// Verify that the registered variable name matches
120+
ASSERT_EQ("neuron_types", variableTable[0].variableName_);
121+
ASSERT_EQ(&neuronTypes, &variableTable[0].variableLocation_);
122+
123+
// Verify the type of update for this variable
124+
ASSERT_EQ(Recorder::UpdatedType::DYNAMIC, variableTable[0].variableType_);
125+
}
126+
127+
98128
// Define the test case for saving simulation data
99129
TEST(Hdf5RecorderTest, SaveSimDataTest)
100130
{
@@ -139,6 +169,51 @@ TEST(Hdf5RecorderTest, SaveSimDataTest)
139169
}
140170
}
141171

172+
// Unit test for saving simulation data with a vector of NeuronType enums
173+
TEST(Hdf5RecorderTest, SaveSimDataVertexTypeTest)
174+
{
175+
// Define a temporary file path for testing
176+
std::string outputFile = "../Testing/UnitTesting/TestOutput/Hdf5test_output_save.h5";
177+
178+
// Create an instance of Hdf5Recorder
179+
Hdf5Recorder recorder(outputFile);
180+
recorder.init();
181+
182+
// Create and configure RecordableVector<vertexType> for testing
183+
RecordableVector<vertexType> neuronTypes;
184+
neuronTypes.resize(3);
185+
neuronTypes[0] = vertexType::EXC;
186+
neuronTypes[1] = vertexType::INH;
187+
neuronTypes[2] = vertexType::EXC;
188+
189+
// Register the variable with Hdf5Recorder
190+
recorder.registerVariable("neuron_types", neuronTypes, Recorder::UpdatedType::CONSTANT);
191+
192+
// Call saveSimData() to write the data to the file
193+
recorder.saveSimData();
194+
195+
// Open the HDF5 file and read back the data
196+
H5File file(outputFile, H5F_ACC_RDONLY);
197+
DataSet dataset = file.openDataSet("neuron_types");
198+
DataSpace dataspace = dataset.getSpace();
199+
200+
hsize_t num_elements;
201+
dataspace.getSimpleExtentDims(&num_elements, nullptr);
202+
203+
// Read the data into a buffer
204+
vector<int> dataBuffer(num_elements);
205+
dataset.read(dataBuffer.data(), PredType::NATIVE_INT);
206+
207+
// Verify the data matches the expected NeuronType values (converted to int)
208+
vector<int> expectedData = {static_cast<int>(vertexType::EXC), static_cast<int>(vertexType::INH),
209+
static_cast<int>(vertexType::EXC)};
210+
211+
ASSERT_EQ(expectedData.size(), dataBuffer.size());
212+
for (size_t i = 0; i < expectedData.size(); ++i) {
213+
EXPECT_EQ(expectedData[i], dataBuffer[i]);
214+
}
215+
}
216+
142217
// Define the test case for compiling histories
143218
TEST(Hdf5RecorderTest, CompileHistoriesTest)
144219
{
@@ -190,4 +265,64 @@ TEST(Hdf5RecorderTest, CompileHistoriesTest)
190265
}
191266
}
192267

268+
// Define the test case for compiling histories with vertexType enum
269+
TEST(Hdf5RecorderTest, CompileHistoriesVertexTypeTest)
270+
{
271+
// Define temporary file path for testing
272+
std::string outputFile
273+
= "../Testing/UnitTesting/TestOutput/Hdf5test_output_compile_histories_neuron_type.h5";
274+
275+
// Create an instance of Hdf5Recorder
276+
Hdf5Recorder recorder(outputFile);
277+
recorder.init();
278+
279+
// Create and configure EventBuffer for testing (stored as int)
280+
EventBuffer eventBufferNeuron(5);
281+
282+
// Register the variable with Hdf5Recorder as DYNAMIC
283+
recorder.registerVariable("neuron_types", eventBufferNeuron, Recorder::UpdatedType::DYNAMIC);
284+
285+
// Expected values for checking correctness
286+
std::vector<int> expectedData;
287+
288+
// Call compileHistories() multiple times to simulate multiple epochs
289+
for (int epoch = 0; epoch < 3; ++epoch) {
290+
// Clear and insert new NeuronType values
291+
eventBufferNeuron.clear();
292+
eventBufferNeuron.insertEvent(static_cast<int>(vertexType::EXC));
293+
eventBufferNeuron.insertEvent(static_cast<int>(vertexType::INH));
294+
eventBufferNeuron.insertEvent(static_cast<int>(vertexType::EXC));
295+
eventBufferNeuron.insertEvent(static_cast<int>(vertexType::EXC));
296+
eventBufferNeuron.insertEvent(static_cast<int>(vertexType::INH));
297+
298+
// Append expected values for this epoch
299+
expectedData.push_back(static_cast<int>(vertexType::EXC));
300+
expectedData.push_back(static_cast<int>(vertexType::INH));
301+
expectedData.push_back(static_cast<int>(vertexType::EXC));
302+
expectedData.push_back(static_cast<int>(vertexType::EXC));
303+
expectedData.push_back(static_cast<int>(vertexType::INH));
304+
305+
// Call compile history
306+
recorder.compileHistories();
307+
}
308+
309+
// Open the HDF5 file and read back the data
310+
H5File file(outputFile, H5F_ACC_RDONLY);
311+
DataSet dataset = file.openDataSet("neuron_types");
312+
DataSpace dataspace = dataset.getSpace();
313+
314+
hsize_t num_elements;
315+
dataspace.getSimpleExtentDims(&num_elements, nullptr);
316+
317+
std::vector<int> dataBuffer(num_elements);
318+
dataset.read(dataBuffer.data(), PredType::NATIVE_INT);
319+
320+
// Ensure data size matches expectation
321+
ASSERT_EQ(expectedData.size(), dataBuffer.size());
322+
323+
// Verify that stored values match expected values
324+
for (size_t i = 0; i < expectedData.size(); ++i) {
325+
EXPECT_EQ(expectedData[i], dataBuffer[i]);
326+
}
327+
}
193328
#endif // HDF5

0 commit comments

Comments
 (0)