stdlib: Add Vector2d to PyStats
Change-Id: Icb2f691abf88ef4bac8d277e421329edb000209b
This commit is contained in:
@@ -142,6 +142,64 @@ class Vector(Statistic):
|
||||
return sum(float(self.value[key]) for key in self.values)
|
||||
|
||||
|
||||
class Vector2d(Statistic):
|
||||
"""
|
||||
A 2D vector of scalar values.
|
||||
"""
|
||||
|
||||
value: Dict[Union[str, int, float], Vector]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
value: Dict[Union[str, int, float], Vector],
|
||||
type: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
):
|
||||
assert (
|
||||
len({vector.size() for vector in value.values()}) == 1
|
||||
), "All the Vectors in the 2d Vector are not of equal length."
|
||||
|
||||
super().__init__(
|
||||
value=value,
|
||||
type=type,
|
||||
description=description,
|
||||
)
|
||||
|
||||
def x_size(self) -> int:
|
||||
"""Returns the number of elements in the x dimension."""
|
||||
assert self.value is not None
|
||||
return len(self.value)
|
||||
|
||||
def y_size(self) -> int:
|
||||
"""Returns the number of elements in the y dimension."""
|
||||
assert self.value is not None
|
||||
return len(self.value[0])
|
||||
|
||||
def size(self) -> int:
|
||||
"""Returns the total number of elements."""
|
||||
return self.x_size() * self.y_size()
|
||||
|
||||
def total(self) -> int:
|
||||
"""The total (sum) of all the entries in the 2d vector/"""
|
||||
assert self.value is not None
|
||||
total = 0
|
||||
for vector in self.value.values():
|
||||
for scalar in vector.values():
|
||||
total += scalar.value
|
||||
return total
|
||||
|
||||
def __getitem__(self, index: Union[str, int, float]) -> Vector:
|
||||
assert self.value is not None
|
||||
# In the case of string, we cast strings to integers of floats if they
|
||||
# are numeric. This avoids users having to cast strings to integers.
|
||||
if isinstance(index, str):
|
||||
if index.isindex():
|
||||
index = int(index)
|
||||
elif index.isnumeric():
|
||||
index = float(index)
|
||||
return self.value[index]
|
||||
|
||||
|
||||
class Distribution(Vector):
|
||||
"""
|
||||
A statistic type that stores information relating to distributions. Each
|
||||
|
||||
@@ -138,6 +138,8 @@ def __get_statistic(statistic: _m5.stats.Info) -> Optional[Statistic]:
|
||||
pass
|
||||
elif isinstance(statistic, _m5.stats.VectorInfo):
|
||||
return __get_vector(statistic)
|
||||
elif isinstance(statistic, _m5.stats.Vector2dInfo):
|
||||
return __get_vector2d(statistic)
|
||||
|
||||
return None
|
||||
|
||||
@@ -193,7 +195,6 @@ def __get_distribution(statistic: _m5.stats.DistInfo) -> Distribution:
|
||||
def __get_vector(statistic: _m5.stats.VectorInfo) -> Vector:
|
||||
vec: Dict[Union[str, int, float], Scalar] = {}
|
||||
|
||||
|
||||
for index in range(statistic.size):
|
||||
# All the values in a Vector are Scalar values
|
||||
value = statistic.value[index]
|
||||
@@ -231,6 +232,42 @@ def __get_vector(statistic: _m5.stats.VectorInfo) -> Vector:
|
||||
)
|
||||
|
||||
|
||||
def __get_vector2d(statistic: _m5.stats.Vector2dInfo) -> Vector2d:
|
||||
# All the values in a 2D Vector are Scalar values
|
||||
description = statistic.desc
|
||||
x_size = statistic.x_size
|
||||
y_size = statistic.y_size
|
||||
|
||||
vector_rep: Dict[Union[str, int, float], Vector] = {}
|
||||
for x_index in range(x_size):
|
||||
x_index_string = x_index
|
||||
if x_index in statistic.subnames:
|
||||
x_index_string = str(statistic.subnames[x_index])
|
||||
|
||||
x_desc = description
|
||||
if x_index in statistic.subdescs:
|
||||
x_desc = str(statistic.subdescs[x_index])
|
||||
x_vec: Dict[str, Scalar] = {}
|
||||
for y_index in range(y_size):
|
||||
y_index_val = y_index
|
||||
if y_index in statistic.ysubnames:
|
||||
y_index_val = str(statistic.subnames[y_index])
|
||||
|
||||
x_vec[y_index_val] = Scalar(
|
||||
value=statistic.value[x_index * y_size + y_index],
|
||||
unit=statistic.unit,
|
||||
datatype=StorageType["f64"],
|
||||
)
|
||||
|
||||
vector_rep[x_index_string] = Vector(
|
||||
x_vec,
|
||||
type="Vector",
|
||||
description=x_desc,
|
||||
)
|
||||
|
||||
return Vector2d(value=vector_rep, type="Vector2d", description=description)
|
||||
|
||||
|
||||
def _prepare_stats(group: _m5.stats.Group):
|
||||
"""
|
||||
Prepares the statistics for dumping.
|
||||
|
||||
@@ -76,6 +76,7 @@ cast_stat_info(const statistics::Info *info)
|
||||
*/
|
||||
TRY_CAST(statistics::FormulaInfo);
|
||||
TRY_CAST(statistics::VectorInfo);
|
||||
TRY_CAST(statistics::Vector2dInfo);
|
||||
TRY_CAST(statistics::DistInfo);
|
||||
|
||||
return py::cast(info);
|
||||
@@ -183,6 +184,17 @@ pybind_init_stats(py::module_ &m_native)
|
||||
[](const statistics::VectorInfo &info) { return info.total(); })
|
||||
;
|
||||
|
||||
py::class_<statistics::Vector2dInfo, statistics::Info,
|
||||
std::unique_ptr<statistics::Vector2dInfo, py::nodelete>>(
|
||||
m, "Vector2dInfo")
|
||||
.def_readonly("x_size", &statistics::Vector2dInfo::x)
|
||||
.def_readonly("y_size", &statistics::Vector2dInfo::y)
|
||||
.def_readonly("subnames", &statistics::Vector2dInfo::subnames)
|
||||
.def_readonly("subdescs", &statistics::Vector2dInfo::subdescs)
|
||||
.def_readonly("ysubnames", &statistics::Vector2dInfo::y_subnames)
|
||||
.def_readonly("value", &statistics::Vector2dInfo::cvec)
|
||||
;
|
||||
|
||||
py::class_<statistics::FormulaInfo, statistics::VectorInfo,
|
||||
std::unique_ptr<statistics::FormulaInfo, py::nodelete>>(
|
||||
m, "FormulaInfo")
|
||||
|
||||
@@ -31,5 +31,6 @@ if env['CONF']['USE_TEST_OBJECTS']:
|
||||
'StatTester',
|
||||
'ScalarStatTester',
|
||||
'VectorStatTester',
|
||||
'Vector2dStatTester',
|
||||
])
|
||||
Source('stat_tester.cc')
|
||||
|
||||
@@ -62,3 +62,28 @@ class VectorStatTester(StatTester):
|
||||
"The vector stat's subdescriptions. If empty, the subdescriptions "
|
||||
"are not used.",
|
||||
)
|
||||
|
||||
|
||||
class Vector2dStatTester(StatTester):
|
||||
type = "Vector2dStatTester"
|
||||
cxx_header = "test_objects/stat_tester.hh"
|
||||
cxx_class = "gem5::Vector2dStatTester"
|
||||
|
||||
x_size = Param.Int("The number of elements in the x dimension.")
|
||||
y_size = Param.Int("The number of elements in the y dimension.")
|
||||
|
||||
values = VectorParam.Float("The vector stat's values, flattened.")
|
||||
subnames = VectorParam.String(
|
||||
[],
|
||||
"The vector stat's subnames. If position is empty, index int is "
|
||||
"used instead.",
|
||||
)
|
||||
subdescs = VectorParam.String(
|
||||
[],
|
||||
"The vector stat's subdescriptions. If empty, the subdescriptions "
|
||||
"are not used.",
|
||||
)
|
||||
ysubnames = VectorParam.String(
|
||||
[],
|
||||
"The vector stat's y subdescriptions. If empty, the subdescriptions ",
|
||||
)
|
||||
|
||||
@@ -84,4 +84,53 @@ VectorStatTester::VectorStatTesterStats::VectorStatTesterStats(
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Vector2dStatTester::setStats()
|
||||
{
|
||||
for (int i = 0; i < params.x_size; i++)
|
||||
{
|
||||
for (int j = 0; j < params.y_size; j++)
|
||||
{
|
||||
stats.vector2d[i][j] = (params.values[j + i * params.y_size]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector2dStatTester::Vector2dStatTesterStats::Vector2dStatTesterStats(
|
||||
statistics::Group *parent,
|
||||
const Vector2dStatTesterParams ¶ms
|
||||
) : statistics::Group(parent),
|
||||
vector2d(this,
|
||||
params.name.c_str(),
|
||||
statistics::units::Count::get(),
|
||||
params.description.c_str()
|
||||
)
|
||||
{
|
||||
vector2d.init(params.x_size, params.y_size);
|
||||
|
||||
assert(params.x_size * params.y_size == params.values.size());
|
||||
|
||||
for (int i = 0; i < params.x_size; i++)
|
||||
{
|
||||
if (params.subnames.size() > i) {
|
||||
vector2d.subname(i, params.subnames[i]);
|
||||
} else {
|
||||
vector2d.subname(i, std::to_string(i));
|
||||
}
|
||||
if (params.subdescs.size() > i) {
|
||||
vector2d.subdesc(i, params.subdescs[i]);
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < params.y_size; j++)
|
||||
{
|
||||
if (params.ysubnames.size() > j) {
|
||||
vector2d.ysubname(j, params.ysubnames[j]);
|
||||
} else {
|
||||
vector2d.ysubname(j, std::to_string(j));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace gem5
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "base/statistics.hh"
|
||||
#include "params/ScalarStatTester.hh"
|
||||
#include "params/StatTester.hh"
|
||||
#include "params/Vector2dStatTester.hh"
|
||||
#include "params/VectorStatTester.hh"
|
||||
#include "sim/sim_object.hh"
|
||||
|
||||
@@ -136,6 +137,28 @@ class VectorStatTester : public StatTester
|
||||
} stats;
|
||||
};
|
||||
|
||||
class Vector2dStatTester : public StatTester
|
||||
{
|
||||
private:
|
||||
Vector2dStatTesterParams params;
|
||||
|
||||
public:
|
||||
Vector2dStatTester(const Vector2dStatTesterParams &p) :
|
||||
StatTester(p), params(p), stats(this, p) {}
|
||||
|
||||
protected:
|
||||
void setStats() override;
|
||||
struct Vector2dStatTesterStats : public statistics::Group
|
||||
{
|
||||
Vector2dStatTesterStats(
|
||||
statistics::Group *parent,
|
||||
const Vector2dStatTesterParams ¶ms
|
||||
);
|
||||
statistics::Vector2d vector2d;
|
||||
} stats;
|
||||
};
|
||||
|
||||
|
||||
} // namespace gem5
|
||||
|
||||
#endif // __STAT_TESTER_HH__
|
||||
|
||||
172
tests/gem5/stats/configs/pystat_vector2d_check.py
Normal file
172
tests/gem5/stats/configs/pystat_vector2d_check.py
Normal file
@@ -0,0 +1,172 @@
|
||||
# Copyright (c) 2024 The Regents of the University of California
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met: redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer;
|
||||
# redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution;
|
||||
# neither the name of the copyright holders nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
import m5
|
||||
from m5.objects import (
|
||||
Root,
|
||||
Vector2dStatTester,
|
||||
)
|
||||
from m5.stats.gem5stats import get_simstat
|
||||
|
||||
"""This script is used for checking that Vector2d statistics set in the
|
||||
simulation are correctly parsed through to the python Pystats.
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Tests the output of a Vector2D Pystat."
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"value",
|
||||
help="Comma delimited list representing the 2d vector in a flattened "
|
||||
"state.",
|
||||
type=lambda s: [float(item) for item in s.split(",")],
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"num_vectors",
|
||||
help="The number of vectors in the vector of vectors",
|
||||
type=int,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--name",
|
||||
type=str,
|
||||
default="vector2d",
|
||||
required=False,
|
||||
help="Name of the vector statistic.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--description",
|
||||
type=str,
|
||||
default="",
|
||||
required=False,
|
||||
help="Description of the vector statistic.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--subnames",
|
||||
help="delimited list representing the vector subnames",
|
||||
type=str,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--subdescs",
|
||||
help="delimited list representing the vector subdescs",
|
||||
type=str,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--ysubnames",
|
||||
help="delimited list representing the vector ysubnames",
|
||||
type=str,
|
||||
)
|
||||
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
expected_output = None
|
||||
stat_tester = None
|
||||
|
||||
stat_tester = Vector2dStatTester()
|
||||
stat_tester.name = args.name
|
||||
stat_tester.description = args.description
|
||||
stat_tester.subnames = []
|
||||
if args.subnames:
|
||||
stat_tester.subnames = [str(item) for item in args.subnames.split(",")]
|
||||
|
||||
stat_tester.subdescs = []
|
||||
if args.subdescs:
|
||||
stat_tester.subdescs = [str(item) for item in args.subdescs.split(",")]
|
||||
|
||||
stat_tester.ysubnames = []
|
||||
if args.ysubnames:
|
||||
stat_tester.ysubnames = [str(item) for item in args.ysubnames.split(",")]
|
||||
|
||||
assert (
|
||||
len(args.value) % args.num_vectors == 0
|
||||
), "The number of values is not divisable by the number of vectors."
|
||||
|
||||
stat_tester.x_size = args.num_vectors
|
||||
stat_tester.y_size = len(args.value) / args.num_vectors
|
||||
stat_tester.values = args.value
|
||||
|
||||
vectors = {} # The representation we expect output.
|
||||
for x in range(args.num_vectors):
|
||||
x_index = x if x not in stat_tester.subnames else stat_tester.subnames[x]
|
||||
|
||||
vector = {}
|
||||
for y in range(stat_tester.y_size):
|
||||
to_add = args.value[
|
||||
int(y + (x * (len(args.value) / args.num_vectors)))
|
||||
]
|
||||
vector[y] = {
|
||||
"value": to_add,
|
||||
"type": "Scalar",
|
||||
"unit": "Count",
|
||||
"description": None,
|
||||
"datatype": "f64",
|
||||
}
|
||||
|
||||
vectors[x_index] = {
|
||||
"type": "Vector",
|
||||
"description": stat_tester.subdescs[x]
|
||||
if x in stat_tester.subdescs
|
||||
else stat_tester.description,
|
||||
"value": vector,
|
||||
}
|
||||
|
||||
expected_output = {
|
||||
"type": "Group",
|
||||
"time_conversion": None,
|
||||
args.name: {
|
||||
"type": "Vector2d",
|
||||
"value": vectors,
|
||||
"description": args.description,
|
||||
},
|
||||
}
|
||||
|
||||
root = Root(full_system=False, system=stat_tester)
|
||||
|
||||
m5.instantiate()
|
||||
m5.simulate()
|
||||
|
||||
simstats = get_simstat(stat_tester)
|
||||
output = simstats.to_json()["system"]
|
||||
|
||||
if output != expected_output:
|
||||
print("Output statistics do not match expected:", file=sys.stderr)
|
||||
print("", file=sys.stderr)
|
||||
print("Expected:", file=sys.stderr)
|
||||
print(expected_output, file=sys.stderr)
|
||||
print("", file=sys.stderr)
|
||||
print("Actual:", file=sys.stderr)
|
||||
print(output, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
@@ -182,3 +182,33 @@ gem5_verify_config(
|
||||
valid_isas=(constants.all_compiled_tag,),
|
||||
length=constants.quick_tag,
|
||||
)
|
||||
|
||||
gem5_verify_config(
|
||||
name="pystat_vector2d_test",
|
||||
fixtures=(),
|
||||
verifiers=[],
|
||||
config=joinpath(
|
||||
config.base_dir,
|
||||
"tests",
|
||||
"gem5",
|
||||
"stats",
|
||||
"configs",
|
||||
"pystat_vector2d_check.py",
|
||||
),
|
||||
config_args=[
|
||||
"2.4,4.3,3.7,-1.4,-2,4,0,0",
|
||||
2,
|
||||
"--name",
|
||||
"vector2d_stat",
|
||||
"--description",
|
||||
"A 2d vector statistic with",
|
||||
"--subnames",
|
||||
"decimals,integers",
|
||||
"--subdescs",
|
||||
"A random collection of decimals,A random collection of integers",
|
||||
"--ysubnames",
|
||||
"first,second,third,fourth",
|
||||
],
|
||||
valid_isas=(constants.all_compiled_tag,),
|
||||
length=constants.quick_tag,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user