in source/neuropod/backends/neuropod_backend.cc [79:214]
void validate_tensors_against_specs(const NeuropodValueMap & tensors,
const std::vector<TensorSpec> &specs,
const std::string & debug_spec_name)
{
// A vector of all the tensor names in the spec.
// This is used to check for extra tensors that were provided,
// but not in the spec
std::unordered_set<std::string> spec_tensor_names;
// All instances of a symbol in a specification must
// resolve to the same value at runtime. See below for more detail
std::unordered_map<std::string, int64_t> symbol_actual_map;
for (const auto &spec : specs)
{
// Add the item's name to a set used to check for extra tensors
spec_tensor_names.emplace(spec.name);
// Try to find a tensor with the same name as this item
auto tensor_it = tensors.find(spec.name);
if (tensor_it == tensors.end())
{
// For now, all tensors are optional
// TODO(vip): Fix this once we have a better way of marking items as optional
continue;
}
// Get the tensor
const auto &tensor = tensor_it->second->as_tensor();
// Validate data type
if (tensor->get_tensor_type() != spec.type)
{
// Throw an error
NEUROPOD_ERROR("Tensor '{}' in the {} is expected to be of type {}, but was of type {}",
spec.name,
debug_spec_name,
spec.type,
tensor->get_tensor_type());
}
// Validate the number of dimensions
if (tensor->get_dims().size() != spec.dims.size())
{
// Throw an error
NEUROPOD_ERROR("Tensor '{}' in the {} is expected to have {} dimensions, but had {}",
spec.name,
debug_spec_name,
spec.dims.size(),
tensor->get_dims().size());
}
// Validate the shape
for (size_t i = 0; i < spec.dims.size(); i++)
{
auto dim = tensor->get_dims()[i];
auto expected = spec.dims[i];
if (expected.value == -1)
{
// Any value of dim is okay
continue;
}
else if (expected.value > 0) // NOLINT(readability-else-after-return)
{
// Check that we have the expected number of items
if (dim != expected.value)
{
// Throw an error
NEUROPOD_ERROR("Dim {} of tensor '{}' in the {} is expected to be of size {}, but was of size {}",
i,
spec.name,
debug_spec_name,
expected.value,
dim);
}
}
else if (expected.value < -1)
{
// `expected` is a symbol.
// Every instance of `expected` should have the same value
// For example, if a symbol of "num_classes" is used multiple times in the spec,
// all instances must have the same value
auto actual_it = symbol_actual_map.find(expected.symbol);
if (actual_it != symbol_actual_map.end())
{
// We've seen this symbol before
auto actual_value = actual_it->second;
// Make sure this usage matches the previous value
if (dim != actual_value)
{
// Throw an error
NEUROPOD_ERROR(
"All dims with symbol '{}' should be the same size. "
"Dim {} of tensor '{}' in the {} was expected to be of size {}, but was of size {}",
expected.symbol,
i,
spec.name,
debug_spec_name,
actual_value,
dim);
}
}
else
{
// This is the first time we're seeing this symbol
// Add it to the map so we can check future occurrances of this symbol
symbol_actual_map[expected.symbol] = dim;
}
}
else
{
// Throw an error
NEUROPOD_ERROR(
"Invalid value of expected shape for item in the {}: {}", debug_spec_name, expected.value);
}
}
}
// Check for extra tensors that are not included in the spec
std::vector<std::string> unexpected_tensors;
for (const auto &item : tensors)
{
if (spec_tensor_names.find(item.first) == spec_tensor_names.end())
{
unexpected_tensors.emplace_back(item.first);
}
}
if (!unexpected_tensors.empty())
{
// Throw an error
NEUROPOD_ERROR("Tensor name(s) '{}' are not found in the {}", unexpected_tensors, debug_spec_name);
}
}