Commit 303a1b53 authored by Paul's avatar Paul
Browse files

Add initial test for shape

parent 16bbb573
...@@ -2,6 +2,14 @@ cmake_minimum_required(VERSION 3.5) ...@@ -2,6 +2,14 @@ cmake_minimum_required(VERSION 3.5)
project(rtglib) project(rtglib)
add_compile_options(-std=c++14)
add_library(rtg
src/shape.cpp
)
target_include_directories(rtg PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
# Move ONNX reader to seperate directory
find_package(Protobuf REQUIRED) find_package(Protobuf REQUIRED)
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS onnx.proto) protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS onnx.proto)
...@@ -10,3 +18,5 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) ...@@ -10,3 +18,5 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
add_executable(read_onnx src/read_onnx.cpp ${PROTO_SRCS}) add_executable(read_onnx src/read_onnx.cpp ${PROTO_SRCS})
target_include_directories(read_onnx PUBLIC ${PROTOBUF_INLCUDE_DIR}) target_include_directories(read_onnx PUBLIC ${PROTOBUF_INLCUDE_DIR})
target_link_libraries(read_onnx ${PROTOBUF_LIBRARY}) target_link_libraries(read_onnx ${PROTOBUF_LIBRARY})
add_subdirectory(test)
#ifndef GUARD_RTGLIB_INSTRUCTION_HPP
#define GUARD_RTGLIB_INSTRUCTION_HPP
#include <rtg/operand.hpp>
#include <rtg/shape.hpp>
namespace rtg {
struct instruction
{
unsigned int id;
std::string name;
shape result;
std::vector<instruction*> arguments;
};
}
#endif
#ifndef GUARD_RTGLIB_LITERAL_HPP
#define GUARD_RTGLIB_LITERAL_HPP
#include <rtg/shape.hpp>
namespace rtg {
struct literal
{
std::vector<char> buffer;
shape shape_;
};
}
#endif
#ifndef GUARD_RTGLIB_OPERAND_HPP
#define GUARD_RTGLIB_OPERAND_HPP
#include <functional>
#include <rtg/shape.hpp>
namespace rtg {
struct argument
{
void* data;
shape s;
};
struct operand
{
std::string name;
std::function<shape(std::vector<shape>)> compute_shape;
std::function<argument(std::vector<argument>)> compute;
};
}
#endif
#ifndef GUARD_RTGLIB_PROGRAM_HPP
#define GUARD_RTGLIB_PROGRAM_HPP
#include <deque>
#include <unordered_map>
#include <rtg/instruction.hpp>
namespace rtg {
struct program
{
// A deque is used to keep references to an instruction stable
std::deque<instruction> instructions;
std::unordered_map<std::string, operand> ops;
};
}
#endif
#ifndef GUARD_RTGLIB_SHAPE_HPP
#define GUARD_RTGLIB_SHAPE_HPP
#include <vector>
#include <cassert>
namespace rtg {
struct shape
{
enum type_t
{
float_type,
int_type
};
shape();
shape(type_t t);
shape(type_t t, std::vector<std::size_t> l);
shape(type_t t, std::vector<std::size_t> l, std::vector<std::size_t> s);
type_t type() const;
const std::vector<std::size_t> lens() const;
const std::vector<std::size_t> strides() const;
std::size_t elements() const;
std::size_t bytes() const;
friend bool operator==(const shape& x, const shape& y);
friend bool operator!=(const shape& x, const shape& y);
template<class T>
struct as
{
using type = T;
template<class U>
T operator()(U u) const
{
return T(u);
}
T operator()() const
{
return {};
}
std::size_t size(std::size_t n=0) const
{
return sizeof(T)*n;
}
template<class U>
T& from(U* buffer, std::size_t n=0) const
{
return *(reinterpret_cast<T*>(buffer)+n);
}
};
template<class Visitor>
void visit_type(Visitor v) const
{
switch(this->type_)
{
case float_type:
v(as<float>());
return;
case int_type:
v(as<int>());
return;
}
assert(true);
}
private:
type_t type_;
std::vector<std::size_t> lens_;
std::vector<std::size_t> strides_;
void calculate_strides();
std::size_t element_space() const;
};
}
#endif
#include <rtg/shape.hpp>
#include <numeric>
#include <algorithm>
#include <functional>
namespace rtg {
shape::shape(type_t t)
: type_(t), lens_({1}), strides_({1})
{}
shape::shape(type_t t, std::vector<std::size_t> l)
: type_(t), lens_(std::move(l))
{
this->calculate_strides();
assert(lens_.size() == strides_.size());
}
shape::shape(type_t t, std::vector<std::size_t> l, std::vector<std::size_t> s)
: type_(t), lens_(std::move(l)), strides_(std::move(s))
{
assert(lens_.size() == strides_.size());
}
void shape::calculate_strides()
{
strides_.clear();
strides_.resize(lens_.size(), 0);
if(strides_.empty())
return;
strides_.back() = 1;
std::partial_sum(
lens_.rbegin(), lens_.rend() - 1, strides_.rbegin() + 1, std::multiplies<std::size_t>());
}
shape::type_t shape::type() const
{
return this->type_;
}
const std::vector<std::size_t> shape::lens() const
{
return this->lens_;
}
const std::vector<std::size_t> shape::strides() const
{
return this->strides_;
}
std::size_t shape::elements() const
{
return std::accumulate(
this->lens().begin(), this->lens().end(), std::size_t{1}, std::multiplies<std::size_t>());
}
std::size_t shape::bytes() const
{
std::size_t n = 0;
this->visit_type([&](auto as) { n = as.size(); });
return n * this->element_space();
}
std::size_t shape::element_space() const
{
// TODO: Get rid of intermediate vector
std::vector<std::size_t> max_indices(this->lens().size());
std::transform(this->lens().begin(),
this->lens().end(),
std::vector<std::size_t>(this->lens().size(), 1).begin(),
max_indices.begin(),
std::minus<std::size_t>());
return std::inner_product(
max_indices.begin(), max_indices.end(), this->strides().begin(), std::size_t{0}) +
1;
}
bool operator==(const shape& x, const shape& y)
{
return x.type() == y.type() && x.lens() == y.lens() && x.strides() == y.strides();
}
bool operator!=(const shape& x, const shape& y)
{
return !(x == y);
}
}
cmake_policy(SET CMP0057 NEW)
include(CTest)
find_package(Threads REQUIRED)
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C ${CMAKE_CFG_INTDIR})
add_custom_target(tests)
set(SKIP_TESTS)
function(add_test_command NAME EXE)
if(NAME IN_LIST SKIP_TESTS)
add_test(NAME ${NAME} COMMAND echo skipped)
set_tests_properties(${NAME} PROPERTIES DISABLED On)
elseif(WIN32)
set(WINPATH)
foreach(PATH ${CMAKE_FIND_ROOT_PATH})
list(APPEND WINPATH ${PATH}/bin)
endforeach()
file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test_${NAME}.cmd"
CONTENT "set PATH=${WINPATH};%PATH%
%1 ${ARGN}")
add_test(NAME ${NAME} COMMAND ${WINE_CMD} cmd /c "${CMAKE_CURRENT_BINARY_DIR}/test_${NAME}.cmd" $<TARGET_FILE:${EXE}>)
else()
if(MIOPEN_TEST_GDB)
file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test_${NAME}.cmake"
CONTENT "
execute_process(COMMAND $<TARGET_FILE:${EXE}> ${ARGN} RESULT_VARIABLE RESULT)
if(NOT RESULT EQUAL 0)
if(EXISTS core)
execute_process(COMMAND gdb $<TARGET_FILE:${EXE}> core -batch -ex bt)
endif()
message(FATAL_ERROR \"Test failed\")
endif()
")
add_test(NAME ${NAME} COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/test_${NAME}.cmake")
else()
add_test(NAME ${NAME} COMMAND ${EXE} ${ARGN})
endif()
endif()
endfunction()
function(add_test_executable TEST_NAME)
add_executable (${TEST_NAME} EXCLUDE_FROM_ALL ${ARGN})
# clang_tidy_check(${TEST_NAME})
target_link_libraries(${TEST_NAME} ${CMAKE_THREAD_LIBS_INIT})
# Cmake does not add flags correctly for gcc
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set_target_properties(${TEST_NAME} PROPERTIES COMPILE_FLAGS -pthread LINK_FLAGS -pthread)
endif()
separate_arguments(MIOPEN_TEST_FLAGS_ARGS UNIX_COMMAND ${MIOPEN_TEST_FLAGS})
if(MIOPEN_TEST_ALL)
set(TEST_COMMAND ${TEST_NAME} ${MIOPEN_TEST_FLOAT_ARG} --all ${MIOPEN_TEST_FLAGS_ARGS})
else()
set(TEST_COMMAND ${TEST_NAME} ${MIOPEN_TEST_FLOAT_ARG} ${MIOPEN_TEST_FLAGS_ARGS})
endif()
add_test_command(${TEST_NAME} ${TEST_COMMAND})
add_dependencies(tests ${TEST_NAME})
add_dependencies(check ${TEST_NAME})
set_tests_properties(${TEST_NAME} PROPERTIES FAIL_REGULAR_EXPRESSION "FAILED")
target_link_libraries(${TEST_NAME} rtg)
endfunction(add_test_executable)
file(GLOB TESTS *.cpp)
foreach(TEST ${TESTS})
get_filename_component(BASE_NAME ${TEST} NAME_WE)
add_test_executable(test_${BASE_NAME} ${TEST})
endforeach()
int main() {
}
#include <rtg/shape.hpp>
#include "test.hpp"
void test_shape_assign()
{
rtg::shape s1{rtg::shape::float_type, {100, 32, 8, 8}};
rtg::shape s2 = s1;
EXPECT(s1 == s2);
EXPECT(!(s1 != s2));
}
void test_shape4()
{
rtg::shape s{rtg::shape::float_type, {100, 32, 8, 8}};
EXPECT(s.type() == rtg::shape::float_type);
EXPECT(s.lens()[0] == 100);
EXPECT(s.lens()[1] == 32);
EXPECT(s.lens()[2] == 8);
EXPECT(s.lens()[3] == 8);
EXPECT(s.strides()[0] == s.lens()[1] * s.strides()[1]);
EXPECT(s.strides()[1] == s.lens()[2] * s.strides()[2]);
EXPECT(s.strides()[2] == s.lens()[3] * s.strides()[3]);
EXPECT(s.strides()[3] == 1);
}
int main() {
test_shape_assign();
test_shape4();
}
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#ifndef GUARD_TEST_TEST_HPP_
#define GUARD_TEST_TEST_HPP_
inline void failed(const char* msg, const char* file, int line)
{
std::cout << "FAILED: " << msg << ": " << file << ": " << line << std::endl;
}
[[gnu::noreturn]] inline void failed_abort(const char* msg, const char* file, int line)
{
failed(msg, file, line);
std::abort();
}
template <class TLeft, class TRight>
inline void expect_equality(const TLeft& left,
const TRight& right,
const char* left_s,
const char* riglt_s,
const char* file,
int line)
{
if(left == right)
return;
std::cout << "FAILED: " << left_s << "(" << left << ") == " << riglt_s << "(" << right
<< "): " << file << ':' << line << std::endl;
std::abort();
}
#define CHECK(...) \
if(!(__VA_ARGS__)) \
failed(#__VA_ARGS__, __FILE__, __LINE__)
#define EXPECT(...) \
if(!(__VA_ARGS__)) \
failed_abort(#__VA_ARGS__, __FILE__, __LINE__)
#define EXPECT_EQUAL(LEFT, RIGHT) expect_equality(LEFT, RIGHT, #LEFT, #RIGHT, __FILE__, __LINE__)
#define STATUS(...) EXPECT((__VA_ARGS__) == 0)
#define FAIL(...) failed(__VA_ARGS__, __FILE__, __LINE__)
template <class F>
bool throws(F f)
{
try
{
f();
return false;
}
catch(...)
{
return true;
}
}
template <class F, class Exception>
bool throws(F f, std::string msg = "")
{
try
{
f();
return false;
}
catch(const Exception& ex)
{
return std::string(ex.what()).find(msg) != std::string::npos;
}
}
template <class T>
void run_test()
{
T t = {};
t.run();
}
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment