From fbd90a6e00137de9028972623db56596cc874c23 Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson <oscar.gustafsson@gmail.com> Date: Mon, 3 Apr 2023 17:52:59 +0200 Subject: [PATCH] Remove C-based simulator for now --- .gitlab-ci.yml | 2 - CMakeLists.txt | 99 --------- b_asic/__init__.py | 6 - setup.py | 70 ------- src/algorithm.hpp | 350 ------------------------------- src/debug.hpp | 94 --------- src/main.cpp | 9 - src/number.hpp | 14 -- src/simulation.cpp | 62 ------ src/simulation.hpp | 13 -- src/simulation/compile.cpp | 314 ---------------------------- src/simulation/compile.hpp | 62 ------ src/simulation/format_code.hpp | 129 ------------ src/simulation/instruction.hpp | 55 ----- src/simulation/run.cpp | 188 ----------------- src/simulation/run.hpp | 23 --- src/simulation/simulation.cpp | 130 ------------ src/simulation/simulation.hpp | 55 ----- src/span.hpp | 362 --------------------------------- test/test_fast_simulation.py | 277 ------------------------- test/test_sfg.py | 108 +--------- test/test_simulation.py | 1 - 22 files changed, 1 insertion(+), 2422 deletions(-) delete mode 100644 CMakeLists.txt delete mode 100644 src/algorithm.hpp delete mode 100644 src/debug.hpp delete mode 100644 src/main.cpp delete mode 100644 src/number.hpp delete mode 100644 src/simulation.cpp delete mode 100644 src/simulation.hpp delete mode 100644 src/simulation/compile.cpp delete mode 100644 src/simulation/compile.hpp delete mode 100644 src/simulation/format_code.hpp delete mode 100644 src/simulation/instruction.hpp delete mode 100644 src/simulation/run.cpp delete mode 100644 src/simulation/run.hpp delete mode 100644 src/simulation/simulation.cpp delete mode 100644 src/simulation/simulation.hpp delete mode 100644 src/span.hpp delete mode 100644 test/test_fast_simulation.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dec82b56..1d2bb62c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,8 +13,6 @@ before_script: # - export CXXFLAGS='--coverage' # Install without dependencies to make sure that requirements.txt is up-to-date - pip install --no-deps -ve . - # Move file, but should be handled by installation - - mv _b_asic* b_asic - pip show b_asic - export QT_API=$QT_API # Install test dependencies diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index c1009f8e..00000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,99 +0,0 @@ -cmake_minimum_required(VERSION 3.8) -project("B-ASIC" - VERSION 1.0.0 - DESCRIPTION "Better ASIC Toolbox for Python 3" - LANGUAGES C CXX) - -option(ASIC_USE_FETCHCONTENT "Automatically download dependencies" ON) -option(ASIC_USE_CLANG_TIDY "Use clang-tidy for static analysis" OFF) -option(ASIC_BUILDING_PYTHON_DISTRIBUTION "Don't copy compiled binaries to project directory" OFF) - -set(LIBRARY_NAME "b_asic") # Name of the python library directory. -set(TARGET_NAME "_${LIBRARY_NAME}") # Name of this extension module. - -# Find dependencies. -if(ASIC_USE_FETCHCONTENT) - add_subdirectory(dependencies) -else() - find_package(fmt REQUIRED) - find_package(pybind11 CONFIG REQUIRED) -endif() - -# Set output directory for compiled binaries. -if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) - include(GNUInstallDirs) - set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_INSTALL_LIBDIR}") -endif() -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") -set(CMAKE_PDB_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") -set(CMAKE_PDB_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") -set(CMAKE_PDB_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") - -# Add files to be compiled into Python module. -pybind11_add_module("${TARGET_NAME}" - # Main files. - "${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/src/simulation.cpp" - - # For DOD simulation. - "${CMAKE_CURRENT_SOURCE_DIR}/src/simulation/compile.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/src/simulation/run.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/src/simulation/simulation.cpp" - - # For OOP simulation (see legacy folder). - #"${CMAKE_CURRENT_SOURCE_DIR}/src/simulation/custom_operation.cpp" - #"${CMAKE_CURRENT_SOURCE_DIR}/src/simulation/operation.cpp" - #"${CMAKE_CURRENT_SOURCE_DIR}/src/simulation/signal_flow_graph.cpp" - #"${CMAKE_CURRENT_SOURCE_DIR}/src/simulation/simulation.cpp" - #"${CMAKE_CURRENT_SOURCE_DIR}/src/simulation/special_operations.cpp" -) - -# Include headers. -target_include_directories("${TARGET_NAME}" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src") - -# Use C++17. -target_compile_features("${TARGET_NAME}" PRIVATE cxx_std_17) - -# Set compiler-specific options using generator expressions. -target_compile_options("${TARGET_NAME}" PRIVATE - $<$<CXX_COMPILER_ID:GNU>: -std=c++17 -Wall -Wextra -Wpedantic -Werror -Wno-psabi $<$<CONFIG:Debug>:-Og -g> $<$<CONFIG:Release>:-O3> $<$<CONFIG:MinSizeRel>:-Os> $<$<CONFIG:RelWithDebInfo>:-O3 -g>> - $<$<CXX_COMPILER_ID:Clang>: -std=c++17 -Wall -Wextra -Wpedantic -Werror $<$<CONFIG:Debug>:-Og -g> $<$<CONFIG:Release>:-O3> $<$<CONFIG:MinSizeRel>:-Os> $<$<CONFIG:RelWithDebInfo>:-O3 -g>> - $<$<CXX_COMPILER_ID:MSVC>: /std:c++17 /W3 /permissive- /WX /wd4996 /utf-8 $<$<CONFIG:Debug>:/Od> $<$<CONFIG:Release>:/Ot> $<$<CONFIG:MinSizeRel>:/Os> $<$<CONFIG:RelWithDebInfo>:/Ot /Od>>) - -# Add libraries. Note: pybind11 is already added in pybind11_add_module. -if(ASIC_USE_FETCHCONTENT) - target_link_libraries("${TARGET_NAME}" PRIVATE - dependency_fmt) -else() - target_link_libraries("${TARGET_NAME}" PRIVATE - $<TARGET_NAME_IF_EXISTS:fmt::fmt-header-only> - $<$<NOT:$<TARGET_EXISTS:fmt::fmt-header-only>>:fmt::fmt>) -endif() - -# Set up clang-tidy. -if(ASIC_USE_CLANG_TIDY) - find_program(CLANG_TIDY NAMES clang-tidy REQUIRED) - set_property(TARGET "${TARGET_NAME}" PROPERTY CXX_CLANG_TIDY ${CLANG_TIDY}) - if(MSVC) - set_target_properties("${TARGET_NAME}" PROPERTIES - VS_GLOBAL_RunCodeAnalysis true - VS_GLOBAL_EnableMicrosoftCodeAnalysis false - VS_GLOBAL_EnableClangTidyCodeAnalysis true - VS_GLOBAL_ClangTidyToolExe "${CLANG_TIDY}") - endif() -endif() - -# Copy binaries to project folder for debugging during development. -if(NOT ASIC_BUILDING_PYTHON_DISTRIBUTION) - add_custom_target(copy_binaries ALL - COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_FILE:${TARGET_NAME}>" "${CMAKE_CURRENT_LIST_DIR}/${LIBRARY_NAME}" - COMMENT "Copying binaries to ${CMAKE_CURRENT_LIST_DIR}/${LIBRARY_NAME}" - DEPENDS "${TARGET_NAME}") -endif() diff --git a/b_asic/__init__.py b/b_asic/__init__.py index 9db092b3..f1fded40 100644 --- a/b_asic/__init__.py +++ b/b_asic/__init__.py @@ -1,12 +1,6 @@ """B-ASIC - Better ASIC Toolbox. ASIC toolbox that simplifies circuit design and optimization. """ -# Extension module (C++). -# NOTE: If this import gives an error, -# make sure the C++ module has been compiled and installed properly. -# See the included README.md for more information on how to build/install. -from b_asic._b_asic import * - # Python modules. from b_asic.core_operations import * from b_asic.graph_component import * diff --git a/setup.py b/setup.py index 637b4da4..db7a75bb 100644 --- a/setup.py +++ b/setup.py @@ -1,72 +1,4 @@ -import os -import shutil -import subprocess -import sys - import setuptools -from setuptools import Extension -from setuptools.command.build_ext import build_ext - - -class CMakeExtension(Extension): - def __init__(self, name, sourcedir=""): - super().__init__(name, sources=[]) - self.sourcedir = os.path.abspath(sourcedir) - - -class CMakeBuild(build_ext): - def build_extension(self, ext): - CMAKE_EXE = os.environ.get( - "CMAKE_EXE", shutil.which("cmake3") or shutil.which("cmake") - ) - if not isinstance(ext, CMakeExtension): - return super().build_extension(ext) - - if not CMAKE_EXE: - raise RuntimeError( - f"Cannot build extension {ext.name}: CMake executable not " - "found! Set the CMAKE_EXE environment variable or update your " - "path." - ) - - cmake_build_type = "Debug" if self.debug else "Release" - cmake_output_dir = os.path.abspath( - os.path.dirname(self.get_ext_fullpath(ext.name)) - ) - cmake_configure_argv = [ - CMAKE_EXE, - ext.sourcedir, - "-DASIC_BUILDING_PYTHON_DISTRIBUTION=ON", - "-DCMAKE_BUILD_TYPE=" + cmake_build_type, - "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=" + cmake_output_dir, - "-DPYTHON_EXECUTABLE=" + sys.executable, - ] - cmake_build_argv = [ - CMAKE_EXE, - "--build", - ".", - "--config", - cmake_build_type, - ] - - if not os.path.exists(self.build_temp): - os.makedirs(self.build_temp) - - env = os.environ.copy() - - print(f"=== Configuring {ext.name} ===") - print(f"Temp dir: {self.build_temp}") - print(f"Output dir: {cmake_output_dir}") - subprocess.check_call(cmake_configure_argv, cwd=self.build_temp, env=env) - - print(f"=== Building {ext.name} ===") - print(f"Temp dir: {self.build_temp}") - print(f"Output dir: {cmake_output_dir}") - print(f"Build type: {cmake_build_type}") - subprocess.check_call(cmake_build_argv, cwd=self.build_temp, env=env) - - print() - setuptools.setup( author=( @@ -81,6 +13,4 @@ setuptools.setup( " andbo467@student.liu.se, mikael.henriksson@liu.se, frans.skarman@liu.se," " petter.kallstrom@liu.se, olle.hansson@liu.se" ), - ext_modules=[CMakeExtension("_b_asic")], - cmdclass={"build_ext": CMakeBuild}, ) diff --git a/src/algorithm.hpp b/src/algorithm.hpp deleted file mode 100644 index 4a609160..00000000 --- a/src/algorithm.hpp +++ /dev/null @@ -1,350 +0,0 @@ -#ifndef ASIC_ALGORITHM_HPP -#define ASIC_ALGORITHM_HPP - -#include <cstddef> -#include <iterator> -#include <memory> -#include <type_traits> -#include <utility> - -namespace asic { -namespace detail { - -template <typename Reference> -class arrow_proxy final { -public: - template <typename Ref> - constexpr explicit arrow_proxy(Ref&& r) - : m_r(std::forward<Ref>(r)) {} - - Reference* operator->() { - return std::addressof(m_r); - } - -private: - Reference m_r; -}; - -template <typename T> -class range_view final { -public: - class iterator final { - public: - using difference_type = std::ptrdiff_t; - using value_type = T const; - using reference = value_type&; - using pointer = value_type*; - using iterator_category = std::random_access_iterator_tag; - - constexpr iterator() noexcept = default; - constexpr explicit iterator(T value) noexcept - : m_value(value) {} - - [[nodiscard]] constexpr bool operator==(iterator const& other) const noexcept { - return m_value == other.m_value; - } - - [[nodiscard]] constexpr bool operator!=(iterator const& other) const noexcept { - return m_value != other.m_value; - } - - [[nodiscard]] constexpr bool operator<(iterator const& other) const noexcept { - return m_value < other.m_value; - } - - [[nodiscard]] constexpr bool operator>(iterator const& other) const noexcept { - return m_value > other.m_value; - } - - [[nodiscard]] constexpr bool operator<=(iterator const& other) const noexcept { - return m_value <= other.m_value; - } - - [[nodiscard]] constexpr bool operator>=(iterator const& other) const noexcept { - return m_value >= other.m_value; - } - - [[nodiscard]] constexpr reference operator*() const noexcept { - return m_value; - } - - [[nodiscard]] constexpr pointer operator->() const noexcept { - return std::addressof(**this); - } - - constexpr iterator& operator++() noexcept { - ++m_value; - return *this; - } - - constexpr iterator operator++(int) noexcept { - return iterator{m_value++}; - } - - constexpr iterator& operator--() noexcept { - --m_value; - return *this; - } - - constexpr iterator operator--(int) noexcept { - return iterator{m_value--}; - } - - constexpr iterator& operator+=(difference_type n) noexcept { - m_value += n; - return *this; - } - - constexpr iterator& operator-=(difference_type n) noexcept { - m_value -= n; - return *this; - } - - [[nodiscard]] constexpr T operator[](difference_type n) noexcept { - return m_value + static_cast<T>(n); - } - - [[nodiscard]] constexpr friend iterator operator+(iterator const& lhs, difference_type rhs) noexcept { - return iterator{lhs.m_value + rhs}; - } - - [[nodiscard]] constexpr friend iterator operator+(difference_type lhs, iterator const& rhs) noexcept { - return iterator{lhs + rhs.m_value}; - } - - [[nodiscard]] constexpr friend iterator operator-(iterator const& lhs, difference_type rhs) noexcept { - return iterator{lhs.m_value - rhs}; - } - - [[nodiscard]] constexpr friend difference_type operator-(iterator const& lhs, iterator const& rhs) noexcept { - return static_cast<difference_type>(lhs.m_value - rhs.m_value); - } - - private: - T m_value{}; - }; - - using sentinel = iterator; - - template <typename First, typename Last> - constexpr range_view(First&& first, Last&& last) noexcept - : m_begin(std::forward<First>(first)) - , m_end(std::forward<Last>(last)) {} - - [[nodiscard]] constexpr iterator begin() const noexcept { - return m_begin; - } - - [[nodiscard]] constexpr sentinel end() const noexcept { - return m_end; - } - -private: - iterator m_begin; - sentinel m_end; -}; - -template <typename Range, typename Iterator, typename Sentinel> -class enumerate_view final { -public: - using sentinel = Sentinel; - - class iterator final { - public: - using difference_type = typename std::iterator_traits<Iterator>::difference_type; - using value_type = typename std::iterator_traits<Iterator>::value_type; - using reference = std::pair<std::size_t const&, decltype(*std::declval<Iterator const>())>; - using pointer = arrow_proxy<reference>; - using iterator_category = - std::common_type_t<typename std::iterator_traits<Iterator>::iterator_category, std::bidirectional_iterator_tag>; - - constexpr iterator() = default; - - constexpr iterator(Iterator it, std::size_t index) - : m_it(std::move(it)) - , m_index(index) {} - - [[nodiscard]] constexpr bool operator==(iterator const& other) const { - return m_it == other.m_it; - } - - [[nodiscard]] constexpr bool operator!=(iterator const& other) const { - return m_it != other.m_it; - } - - [[nodiscard]] constexpr bool operator==(sentinel const& other) const { - return m_it == other; - } - - [[nodiscard]] constexpr bool operator!=(sentinel const& other) const { - return m_it != other; - } - - [[nodiscard]] constexpr reference operator*() const { - return reference{m_index, *m_it}; - } - - [[nodiscard]] constexpr pointer operator->() const { - return pointer{**this}; - } - - constexpr iterator& operator++() { - ++m_it; - ++m_index; - return *this; - } - - constexpr iterator operator++(int) { - return iterator{m_it++, m_index++}; - } - - constexpr iterator& operator--() { - --m_it; - --m_index; - return *this; - } - - constexpr iterator operator--(int) { - return iterator{m_it--, m_index--}; - } - - private: - Iterator m_it; - std::size_t m_index = 0; - }; - - template <typename R> - constexpr explicit enumerate_view(R&& range) - : m_range(std::forward<R>(range)) {} - - [[nodiscard]] constexpr iterator begin() const { - return iterator{std::begin(m_range), 0}; - } - - [[nodiscard]] constexpr sentinel end() const { - return std::end(m_range); - } - -private: - Range m_range; -}; - -template <typename Range1, typename Range2, typename Iterator1, typename Iterator2, typename Sentinel1, typename Sentinel2> -class zip_view final { -public: - using sentinel = std::pair<Sentinel1, Sentinel2>; - - class iterator final { - public: - using difference_type = std::common_type_t<typename std::iterator_traits<Iterator1>::difference_type, - typename std::iterator_traits<Iterator2>::difference_type>; - using value_type = - std::pair<typename std::iterator_traits<Iterator1>::value_type, typename std::iterator_traits<Iterator2>::value_type>; - using reference = std::pair<decltype(*std::declval<Iterator1 const>()), decltype(*std::declval<Iterator2 const>())>; - using pointer = arrow_proxy<reference>; - using iterator_category = - std::common_type_t<typename std::iterator_traits<Iterator1>::iterator_category, - typename std::iterator_traits<Iterator2>::iterator_category, std::bidirectional_iterator_tag>; - - constexpr iterator() = default; - - constexpr iterator(Iterator1 it1, Iterator2 it2) - : m_it1(std::move(it1)) - , m_it2(std::move(it2)) {} - - [[nodiscard]] constexpr bool operator==(iterator const& other) const { - return m_it1 == other.m_it1 && m_it2 == other.m_it2; - } - - [[nodiscard]] constexpr bool operator!=(iterator const& other) const { - return !(*this == other); - } - - [[nodiscard]] constexpr bool operator==(sentinel const& other) const { - return m_it1 == other.first || m_it2 == other.second; - } - - [[nodiscard]] constexpr bool operator!=(sentinel const& other) const { - return !(*this == other); - } - - [[nodiscard]] constexpr reference operator*() const { - return reference{*m_it1, *m_it2}; - } - - [[nodiscard]] constexpr pointer operator->() const { - return pointer{**this}; - } - - constexpr iterator& operator++() { - ++m_it1; - ++m_it2; - return *this; - } - - constexpr iterator operator++(int) { - return iterator{m_it1++, m_it2++}; - } - - constexpr iterator& operator--() { - --m_it1; - --m_it2; - return *this; - } - - constexpr iterator operator--(int) { - return iterator{m_it1--, m_it2--}; - } - - private: - Iterator1 m_it1; - Iterator2 m_it2; - }; - - template <typename R1, typename R2> - constexpr zip_view(R1&& range1, R2&& range2) - : m_range1(std::forward<R1>(range1)) - , m_range2(std::forward<R2>(range2)) {} - - [[nodiscard]] constexpr iterator begin() const { - return iterator{std::begin(m_range1), std::begin(m_range2)}; - } - - [[nodiscard]] constexpr sentinel end() const { - return sentinel{std::end(m_range1), std::end(m_range2)}; - } - -private: - Range1 m_range1; - Range2 m_range2; -}; - -} // namespace detail - -template <typename First, typename Last, typename T = std::remove_cv_t<std::remove_reference_t<First>>> -[[nodiscard]] constexpr auto range(First&& first, Last&& last) { - return detail::range_view<T>{std::forward<First>(first), std::forward<Last>(last)}; -} - -template <typename Last, typename T = std::remove_cv_t<std::remove_reference_t<Last>>> -[[nodiscard]] constexpr auto range(Last&& last) { - return detail::range_view<T>{T{}, std::forward<Last>(last)}; -} - -template <typename Range, typename Iterator = decltype(std::begin(std::declval<Range>())), - typename Sentinel = decltype(std::end(std::declval<Range>()))> -[[nodiscard]] constexpr auto enumerate(Range&& range) { - return detail::enumerate_view<Range, Iterator, Sentinel>{std::forward<Range>(range)}; -} - -template <typename Range1, typename Range2, typename Iterator1 = decltype(std::begin(std::declval<Range1>())), - typename Iterator2 = decltype(std::begin(std::declval<Range2>())), - typename Sentinel1 = decltype(std::end(std::declval<Range1>())), typename Sentinel2 = decltype(std::end(std::declval<Range2>()))> -[[nodiscard]] constexpr auto zip(Range1&& range1, Range2&& range2) { - return detail::zip_view<Range1, Range2, Iterator1, Iterator2, Sentinel1, Sentinel2>{std::forward<Range1>(range1), - std::forward<Range2>(range2)}; -} - -} // namespace asic - -#endif // ASIC_ALGORITHM_HPP diff --git a/src/debug.hpp b/src/debug.hpp deleted file mode 100644 index e1253790..00000000 --- a/src/debug.hpp +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef ASIC_DEBUG_HPP -#define ASIC_DEBUG_HPP - -#ifndef NDEBUG - -#ifndef ASIC_ENABLE_DEBUG_LOGGING -#define ASIC_ENABLE_DEBUG_LOGGING 1 -#endif - -#ifndef ASIC_ENABLE_ASSERTS -#define ASIC_ENABLE_ASSERTS 1 -#endif - -#else - -#ifndef ASIC_ENABLE_DEBUG_LOGGING -#define ASIC_ENABLE_DEBUG_LOGGING 0 -#endif - -#ifndef ASIC_ENABLE_ASSERTS -#define ASIC_ENABLE_ASSERTS 0 -#endif - -#endif // NDEBUG - -#if ASIC_ENABLE_DEBUG_LOGGING -#include <filesystem> -#include <fmt/format.h> -#include <fstream> -#include <ostream> -#include <string_view> -#include <utility> -#endif // ASIC_ENABLE_DEBUG_LOGGING - -#if ASIC_ENABLE_ASSERTS -#include <cstdio> -#include <cstdlib> -#include <filesystem> -#include <fmt/format.h> -#include <string_view> -#endif // ASIC_ENABLE_ASSERTS - -namespace asic { - -constexpr auto debug_log_filename = "_b_asic_debug_log.txt"; - -namespace detail { - -#if ASIC_ENABLE_DEBUG_LOGGING -inline void log_debug_msg_string(std::string_view file, int line, std::string_view string) { - static auto log_file = std::ofstream{debug_log_filename, std::ios::trunc}; - log_file << fmt::format("{:<40}: {}", fmt::format("{}:{}", std::filesystem::path{file}.filename().generic_string(), line), string) - << std::endl; -} - -template <typename Format, typename... Args> -inline void log_debug_msg(std::string_view file, int line, Format&& format, Args&&... args) { - log_debug_msg_string(file, line, fmt::format(std::forward<Format>(format), std::forward<Args>(args)...)); -} -#endif // ASIC_ENABLE_DEBUG_LOGGING - -#if ASIC_ENABLE_ASSERTS -inline void fail_assert(std::string_view file, int line, std::string_view condition_string) { -#if ASIC_ENABLE_DEBUG_LOGGING - log_debug_msg(file, line, "Assertion failed: {}", condition_string); -#endif // ASIC_ENABLE_DEBUG_LOGGING - fmt::print(stderr, "{}:{}: Assertion failed: {}\n", std::filesystem::path{file}.filename().generic_string(), line, condition_string); - std::abort(); -} - -template <typename BoolConvertible> -inline void check_assert(std::string_view file, int line, std::string_view condition_string, BoolConvertible&& condition) { - if (!static_cast<bool>(condition)) { - fail_assert(file, line, condition_string); - } -} -#endif // ASIC_ENABLE_ASSERTS - -} // namespace detail -} // namespace asic - -#if ASIC_ENABLE_DEBUG_LOGGING -#define ASIC_DEBUG_MSG(...) (asic::detail::log_debug_msg(__FILE__, __LINE__, __VA_ARGS__)) -#else -#define ASIC_DEBUG_MSG(...) ((void)0) -#endif // ASIC_ENABLE_DEBUG_LOGGING - -#if ASIC_ENABLE_ASSERTS -#define ASIC_ASSERT(condition) (asic::detail::check_assert(__FILE__, __LINE__, #condition, (condition))) -#else -#define ASIC_ASSERT(condition) ((void)0) -#endif - -#endif // ASIC_DEBUG_HPP diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index 8ac36c73..00000000 --- a/src/main.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "simulation.hpp" - -#define NOMINMAX -#include <pybind11/pybind11.h> - -PYBIND11_MODULE(_b_asic, module) { // NOLINT - module.doc() = "Better ASIC Toolbox Extension Module."; - asic::define_simulation_class(module); -} diff --git a/src/number.hpp b/src/number.hpp deleted file mode 100644 index 869a93f1..00000000 --- a/src/number.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef ASIC_NUMBER_HPP -#define ASIC_NUMBER_HPP - -#define NOMINMAX -#include <complex> -#include <pybind11/complex.h> - -namespace asic { - -using number = std::complex<double>; - -} // namespace asic - -#endif // ASIC_NUMBER_HPP diff --git a/src/simulation.cpp b/src/simulation.cpp deleted file mode 100644 index 9381925b..00000000 --- a/src/simulation.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "simulation.hpp" - -#include "simulation/simulation.hpp" - -namespace py = pybind11; - -namespace asic { - -void define_simulation_class(pybind11::module& module) { - // clang-format off - py::class_<simulation>(module, "FastSimulation") - .def(py::init<py::handle>(), - py::arg("sfg"), - "SFG Constructor.") - - .def(py::init<py::handle, std::optional<std::vector<std::optional<input_provider_type>>>>(), - py::arg("sfg"), py::arg("input_providers"), - "SFG Constructor.") - - .def("set_input", &simulation::set_input, - py::arg("index"), py::arg("input_provider"), - "Set the input function used to get values for the specific input at the given index to the internal SFG.") - - .def("set_inputs", &simulation::set_inputs, - py::arg("input_providers"), - "Set the input functions used to get values for the inputs to the internal SFG.") - - .def("step", &simulation::step, - py::arg("save_results") = true, py::arg("bits_override") = py::none{}, py::arg("quantize") = true, - "Run one iteration of the simulation and return the resulting output values.") - - .def("run_until", &simulation::run_until, - py::arg("iteration"), py::arg("save_results") = true, py::arg("bits_override") = py::none{}, py::arg("quantize") = true, - "Run the simulation until its iteration is greater than or equal to the given iteration\n" - "and return the output values of the last iteration.") - - .def("run_for", &simulation::run_for, - py::arg("iterations"), py::arg("save_results") = true, py::arg("bits_override") = py::none{}, py::arg("quantize") = true, - "Run a given number of iterations of the simulation and return the output values of the last iteration.") - - .def("run", &simulation::run, - py::arg("save_results") = true, py::arg("bits_override") = py::none{}, py::arg("quantize") = true, - "Run the simulation until the end of its input arrays and return the output values of the last iteration.") - - .def_property_readonly("iteration", &simulation::iteration, - "Get the current iteration number of the simulation.") - - .def_property_readonly("results", &simulation::results, - "Get a mapping from result keys to numpy arrays containing all results, including intermediate values,\n" - "calculated for each iteration up until now that was run with save_results enabled.\n" - "The mapping is indexed using the key() method of Operation with the appropriate output index.\n" - "Example result after 3 iterations: {\"c1\": [3, 6, 7], \"c2\": [4, 5, 5], \"bfly1.0\": [7, 0, 0], \"bfly1.1\": [-1, 0, 2], \"0\": [7, -2, -1]}") - - .def("clear_results", &simulation::clear_results, - "Clear all results that were saved until now.") - - .def("clear_state", &simulation::clear_state, - "Clear all current state of the simulation, except for the results and iteration."); - // clang-format on -} - -} // namespace asic diff --git a/src/simulation.hpp b/src/simulation.hpp deleted file mode 100644 index 5e7ab381..00000000 --- a/src/simulation.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef ASIC_SIMULATION_HPP -#define ASIC_SIMULATION_HPP - -#define NOMINMAX -#include <pybind11/pybind11.h> - -namespace asic { - -void define_simulation_class(pybind11::module& module); - -} // namespace asic - -#endif // ASIC_SIMULATION_HPP diff --git a/src/simulation/compile.cpp b/src/simulation/compile.cpp deleted file mode 100644 index fd8c204e..00000000 --- a/src/simulation/compile.cpp +++ /dev/null @@ -1,314 +0,0 @@ -#include "compile.hpp" - -#include "../algorithm.hpp" -#include "../debug.hpp" -#include "../span.hpp" -#include "format_code.hpp" - -#define NOMINMAX -#include <Python.h> -#include <fmt/format.h> -#include <limits> -#include <optional> -#include <string_view> -#include <tuple> -#include <unordered_map> -#include <utility> - -namespace py = pybind11; - -namespace asic { - -[[nodiscard]] static result_key key_base(py::handle op, std::string_view prefix) { - auto const graph_id = op.attr("graph_id").cast<std::string_view>(); - return (prefix.empty()) ? result_key{graph_id} : fmt::format("{}.{}", prefix, graph_id); -} - -[[nodiscard]] static result_key key_of_output(py::handle op, std::size_t output_index, std::string_view prefix) { - auto base = key_base(op, prefix); - if (base.empty()) { - return fmt::to_string(output_index); - } - if (op.attr("output_count").cast<std::size_t>() == 1) { - return base; - } - return fmt::format("{}.{}", base, output_index); -} - -class compiler final { -public: - simulation_code compile(py::handle sfg) { - ASIC_DEBUG_MSG("Compiling code..."); - this->initialize_code(sfg.attr("input_count").cast<std::size_t>(), sfg.attr("output_count").cast<std::size_t>()); - auto deferred_delays = delay_queue{}; - this->add_outputs(sfg, deferred_delays); - this->add_deferred_delays(std::move(deferred_delays)); - this->resolve_invalid_result_indices(); - ASIC_DEBUG_MSG("Compiled code:\n{}\n", format_compiled_simulation_code(m_code)); - return std::move(m_code); - } - -private: - struct sfg_info final { - py::handle sfg; - std::size_t prefix_length; - - sfg_info(py::handle sfg, std::size_t prefix_length) - : sfg(sfg) - , prefix_length(prefix_length) {} - - [[nodiscard]] std::size_t find_input_operation_index(py::handle op) const { - for (auto const& [i, in] : enumerate(sfg.attr("input_operations"))) { - if (in.is(op)) { - return i; - } - } - throw py::value_error{"Stray Input operation in simulation SFG"}; - } - }; - - using sfg_info_stack = std::vector<sfg_info>; - using delay_queue = std::vector<std::tuple<std::size_t, py::handle, std::string, sfg_info_stack>>; - using added_output_cache = std::unordered_set<PyObject const*>; - using added_result_cache = std::unordered_map<PyObject const*, result_index_type>; - using added_custom_operation_cache = std::unordered_map<PyObject const*, std::size_t>; - - static constexpr auto no_result_index = std::numeric_limits<result_index_type>::max(); - - void initialize_code(std::size_t input_count, std::size_t output_count) { - m_code.required_stack_size = 0; - m_code.input_count = input_count; - m_code.output_count = output_count; - } - - void add_outputs(py::handle sfg, delay_queue& deferred_delays) { - for (auto const i : range(m_code.output_count)) { - this->add_operation_output(sfg, i, std::string_view{}, sfg_info_stack{}, deferred_delays); - } - } - - void add_deferred_delays(delay_queue&& deferred_delays) { - while (!deferred_delays.empty()) { - auto new_deferred_delays = delay_queue{}; - for (auto const& [delay_index, op, prefix, sfg_stack] : deferred_delays) { - this->add_source(op, 0, prefix, sfg_stack, deferred_delays); - this->add_instruction(instruction_type::update_delay, no_result_index, -1).index = delay_index; - } - deferred_delays = new_deferred_delays; - } - } - - void resolve_invalid_result_indices() { - for (auto& instruction : m_code.instructions) { - if (instruction.result_index == no_result_index) { - instruction.result_index = static_cast<result_index_type>(m_code.result_keys.size()); - } - } - } - - [[nodiscard]] static sfg_info_stack push_sfg(sfg_info_stack const& sfg_stack, py::handle sfg, std::size_t prefix_length) { - auto const new_size = static_cast<std::size_t>(sfg_stack.size() + 1); - auto new_sfg_stack = sfg_info_stack{}; - new_sfg_stack.reserve(new_size); - for (auto const& info : sfg_stack) { - new_sfg_stack.push_back(info); - } - new_sfg_stack.emplace_back(sfg, prefix_length); - return new_sfg_stack; - } - - [[nodiscard]] static sfg_info_stack pop_sfg(sfg_info_stack const& sfg_stack) { - ASIC_ASSERT(!sfg_stack.empty()); - auto const new_size = static_cast<std::size_t>(sfg_stack.size() - 1); - auto new_sfg_stack = sfg_info_stack{}; - new_sfg_stack.reserve(new_size); - for (auto const& info : span{sfg_stack}.first(new_size)) { - new_sfg_stack.push_back(info); - } - return new_sfg_stack; - } - - instruction& add_instruction(instruction_type type, result_index_type result_index, std::ptrdiff_t stack_diff) { - m_stack_depth += stack_diff; - if (m_stack_depth < 0) { - throw py::value_error{"Detected input/output count mismatch in simulation SFG"}; - } - if (auto const stack_size = static_cast<std::size_t>(m_stack_depth); stack_size > m_code.required_stack_size) { - m_code.required_stack_size = stack_size; - } - auto& instruction = m_code.instructions.emplace_back(); - instruction.type = type; - instruction.result_index = result_index; - return instruction; - } - - [[nodiscard]] std::optional<result_index_type> begin_operation_output(py::handle op, std::size_t output_index, - std::string_view prefix) { - auto* const pointer = op.attr("outputs")[py::int_{output_index}].ptr(); - if (m_incomplete_outputs.count(pointer) != 0) { - // Make sure the output doesn't depend on its own value, unless it's a delay operation. - if (op.attr("type_name")().cast<std::string_view>() != "t") { - throw py::value_error{"Direct feedback loop detected in simulation SFG"}; - } - } - // Try to add a new result. - auto const [it, inserted] = m_added_results.try_emplace(pointer, static_cast<result_index_type>(m_code.result_keys.size())); - if (inserted) { - if (m_code.result_keys.size() >= static_cast<std::size_t>(std::numeric_limits<result_index_type>::max())) { - throw py::value_error{fmt::format("Simulation SFG requires too many outputs to be stored (limit: {})", - std::numeric_limits<result_index_type>::max())}; - } - m_code.result_keys.push_back(key_of_output(op, output_index, prefix)); - m_incomplete_outputs.insert(pointer); - return it->second; - } - // If the result has already been added, we re-use the old result and - // return std::nullopt to indicate that we don't need to add all the required instructions again. - this->add_instruction(instruction_type::push_result, it->second, 1).index = static_cast<std::size_t>(it->second); - return std::nullopt; - } - - void end_operation_output(py::handle op, std::size_t output_index) { - auto* const pointer = op.attr("outputs")[py::int_{output_index}].ptr(); - [[maybe_unused]] auto const erased = m_incomplete_outputs.erase(pointer); - ASIC_ASSERT(erased == 1); - } - - [[nodiscard]] std::size_t try_add_custom_operation(py::handle op) { - auto const [it, inserted] = m_added_custom_operations.try_emplace(op.ptr(), m_added_custom_operations.size()); - if (inserted) { - auto& custom_operation = m_code.custom_operations.emplace_back(); - custom_operation.evaluate_output = op.attr("evaluate_output"); - custom_operation.input_count = op.attr("input_count").cast<std::size_t>(); - custom_operation.output_count = op.attr("output_count").cast<std::size_t>(); - } - return it->second; - } - - [[nodiscard]] std::size_t add_delay_info(number initial_value, result_index_type result_index) { - auto const delay_index = m_code.delays.size(); - auto& delay = m_code.delays.emplace_back(); - delay.initial_value = initial_value; - delay.result_index = result_index; - return delay_index; - } - - void add_source(py::handle op, std::size_t input_index, std::string_view prefix, sfg_info_stack const& sfg_stack, - delay_queue& deferred_delays) { - auto const signal = py::object{op.attr("inputs")[py::int_{input_index}].attr("signals")[py::int_{0}]}; - auto const src = py::handle{signal.attr("source")}; - auto const operation = py::handle{src.attr("operation")}; - auto const index = src.attr("index").cast<std::size_t>(); - this->add_operation_output(operation, index, prefix, sfg_stack, deferred_delays); - if (!signal.attr("bits").is_none()) { - auto const bits = signal.attr("bits").cast<std::size_t>(); - if (bits > 64) { - throw py::value_error{"Cannot quantize to more than 64 bits"}; - } - this->add_instruction(instruction_type::quantize, no_result_index, 0).bit_mask = static_cast<std::int64_t>( - (std::int64_t{1} << bits) - 1); - } - } - - void add_unary_operation_output(py::handle op, result_index_type result_index, std::string_view prefix, sfg_info_stack const& sfg_stack, - delay_queue& deferred_delays, instruction_type type) { - this->add_source(op, 0, prefix, sfg_stack, deferred_delays); - this->add_instruction(type, result_index, 0); - } - - void add_binary_operation_output(py::handle op, result_index_type result_index, std::string_view prefix, - sfg_info_stack const& sfg_stack, delay_queue& deferred_delays, instruction_type type) { - this->add_source(op, 0, prefix, sfg_stack, deferred_delays); - this->add_source(op, 1, prefix, sfg_stack, deferred_delays); - this->add_instruction(type, result_index, -1); - } - - void add_operation_output(py::handle op, std::size_t output_index, std::string_view prefix, sfg_info_stack const& sfg_stack, - delay_queue& deferred_delays) { - auto const type_name = op.attr("type_name")().cast<std::string_view>(); - if (type_name == "out") { - this->add_source(op, 0, prefix, sfg_stack, deferred_delays); - } else if (auto const result_index = this->begin_operation_output(op, output_index, prefix)) { - if (type_name == "c") { - this->add_instruction(instruction_type::push_constant, *result_index, 1).value = op.attr("value").cast<number>(); - } else if (type_name == "add") { - this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::addition); - } else if (type_name == "sub") { - this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::subtraction); - } else if (type_name == "mul") { - this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::multiplication); - } else if (type_name == "div") { - this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::division); - } else if (type_name == "min") { - this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::min); - } else if (type_name == "max") { - this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::max); - } else if (type_name == "sqrt") { - this->add_unary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::square_root); - } else if (type_name == "conj") { - this->add_unary_operation_output( - op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::complex_conjugate); - } else if (type_name == "abs") { - this->add_unary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::absolute); - } else if (type_name == "cmul") { - this->add_source(op, 0, prefix, sfg_stack, deferred_delays); - this->add_instruction(instruction_type::constant_multiplication, *result_index, 0).value = op.attr("value").cast<number>(); - } else if (type_name == "bfly") { - if (output_index == 0) { - this->add_source(op, 0, prefix, sfg_stack, deferred_delays); - this->add_source(op, 1, prefix, sfg_stack, deferred_delays); - this->add_instruction(instruction_type::addition, *result_index, -1); - } else { - this->add_source(op, 0, prefix, sfg_stack, deferred_delays); - this->add_source(op, 1, prefix, sfg_stack, deferred_delays); - this->add_instruction(instruction_type::subtraction, *result_index, -1); - } - } else if (type_name == "in") { - if (sfg_stack.empty()) { - throw py::value_error{"Encountered Input operation outside SFG in simulation"}; - } - auto const& info = sfg_stack.back(); - auto const input_index = info.find_input_operation_index(op); - if (sfg_stack.size() == 1) { - this->add_instruction(instruction_type::push_input, *result_index, 1).index = input_index; - } else { - this->add_source(info.sfg, input_index, prefix.substr(0, info.prefix_length), pop_sfg(sfg_stack), deferred_delays); - this->add_instruction(instruction_type::forward_value, *result_index, 0); - } - } else if (type_name == "t") { - auto const delay_index = this->add_delay_info(op.attr("initial_value").cast<number>(), *result_index); - deferred_delays.emplace_back(delay_index, op, std::string{prefix}, sfg_stack); - this->add_instruction(instruction_type::push_delay, *result_index, 1).index = delay_index; - } else if (type_name == "sfg") { - auto const output_op = py::handle{op.attr("output_operations")[py::int_{output_index}]}; - this->add_source(output_op, 0, key_base(op, prefix), push_sfg(sfg_stack, op, prefix.size()), deferred_delays); - this->add_instruction(instruction_type::forward_value, *result_index, 0); - } else { - auto const custom_operation_index = this->try_add_custom_operation(op); - auto const& custom_operation = m_code.custom_operations[custom_operation_index]; - for (auto const i : range(custom_operation.input_count)) { - this->add_source(op, i, prefix, sfg_stack, deferred_delays); - } - auto const custom_source_index = m_code.custom_sources.size(); - auto& custom_source = m_code.custom_sources.emplace_back(); - custom_source.custom_operation_index = custom_operation_index; - custom_source.output_index = output_index; - auto const stack_diff = std::ptrdiff_t{1} - static_cast<std::ptrdiff_t>(custom_operation.input_count); - this->add_instruction(instruction_type::custom, *result_index, stack_diff).index = custom_source_index; - } - this->end_operation_output(op, output_index); - } - } - - simulation_code m_code{}; - added_output_cache m_incomplete_outputs{}; - added_result_cache m_added_results{}; - added_custom_operation_cache m_added_custom_operations{}; - std::ptrdiff_t m_stack_depth = 0; -}; - -simulation_code compile_simulation(pybind11::handle sfg) { - return compiler{}.compile(sfg); -} - -} // namespace asic diff --git a/src/simulation/compile.hpp b/src/simulation/compile.hpp deleted file mode 100644 index ee7fa3f1..00000000 --- a/src/simulation/compile.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef ASIC_SIMULATION_COMPILE_HPP -#define ASIC_SIMULATION_COMPILE_HPP - -#include "instruction.hpp" - -#define NOMINMAX -#include <cstddef> -#include <pybind11/pybind11.h> -#include <string> -#include <vector> - -namespace asic { - -using result_key = std::string; - -struct simulation_code final { - struct custom_operation final { - // Python function used to evaluate the custom operation. - pybind11::object evaluate_output{}; - // Number of inputs that the custom operation takes. - std::size_t input_count = 0; - // Number of outputs that the custom operation gives. - std::size_t output_count = 0; - }; - - struct custom_source final { - // Index into custom_operations where the custom_operation corresponding to this custom_source is located. - std::size_t custom_operation_index = 0; - // Output index of the custom_operation that this source gets it value from. - std::size_t output_index = 0; - }; - - struct delay_info final { - // Initial value to set at the start of the simulation. - number initial_value{}; - // The result index where the current value should be stored at the start of each iteration. - result_index_type result_index = 0; - }; - - // Instructions to execute for one full iteration of the simulation. - std::vector<instruction> instructions{}; - // Custom operations used by the simulation. - std::vector<custom_operation> custom_operations{}; - // Signal sources that use custom operations. - std::vector<custom_source> custom_sources{}; - // Info about the delay operations used in the simulation. - std::vector<delay_info> delays{}; - // Keys for each result produced by the simulation. The index of the key matches the index of the result in the simulation state. - std::vector<result_key> result_keys{}; - // Number of values expected as input to the simulation. - std::size_t input_count = 0; - // Number of values given as output from the simulation. This will be the number of values left on the stack after a full iteration of the simulation has been run. - std::size_t output_count = 0; - // Maximum number of values that need to be able to fit on the stack in order to run a full iteration of the simulation. - std::size_t required_stack_size = 0; -}; - -[[nodiscard]] simulation_code compile_simulation(pybind11::handle sfg); - -} // namespace asic - -#endif // ASIC_SIMULATION_COMPILE_HPP diff --git a/src/simulation/format_code.hpp b/src/simulation/format_code.hpp deleted file mode 100644 index b338c325..00000000 --- a/src/simulation/format_code.hpp +++ /dev/null @@ -1,129 +0,0 @@ -#ifndef ASIC_SIMULATION_FORMAT_CODE_HPP -#define ASIC_SIMULATION_FORMAT_CODE_HPP - -#include "../algorithm.hpp" -#include "../debug.hpp" -#include "../number.hpp" -#include "compile.hpp" -#include "instruction.hpp" - -#include <fmt/format.h> -#include <string> - -namespace asic { - -[[nodiscard]] inline std::string format_number(number const& value) { - if (value.imag() == 0) { - return fmt::to_string(value.real()); - } - if (value.real() == 0) { - return fmt::format("{}j", value.imag()); - } - if (value.imag() < 0) { - return fmt::format("{}-{}j", value.real(), -value.imag()); - } - return fmt::format("{}+{}j", value.real(), value.imag()); -} - -[[nodiscard]] inline std::string format_compiled_simulation_code_result_keys(simulation_code const& code) { - auto result = std::string{}; - for (auto const& [i, result_key] : enumerate(code.result_keys)) { - result += fmt::format("{:>2}: \"{}\"\n", i, result_key); - } - return result; -} - -[[nodiscard]] inline std::string format_compiled_simulation_code_delays(simulation_code const& code) { - auto result = std::string{}; - for (auto const& [i, delay] : enumerate(code.delays)) { - ASIC_ASSERT(delay.result_index < code.result_keys.size()); - result += fmt::format("{:>2}: Initial value: {}, Result: {}: \"{}\"\n", - i, - format_number(delay.initial_value), - delay.result_index, - code.result_keys[delay.result_index]); - } - return result; -} - -[[nodiscard]] inline std::string format_compiled_simulation_code_instruction(instruction const& instruction) { - // clang-format off - switch (instruction.type) { - case instruction_type::push_input: return fmt::format("push_input inputs[{}]", instruction.index); - case instruction_type::push_result: return fmt::format("push_result results[{}]", instruction.index); - case instruction_type::push_delay: return fmt::format("push_delay delays[{}]", instruction.index); - case instruction_type::push_constant: return fmt::format("push_constant {}", format_number(instruction.value)); - case instruction_type::quantize: return fmt::format("quantize {:#018x}", instruction.bit_mask); - case instruction_type::addition: return "addition"; - case instruction_type::subtraction: return "subtraction"; - case instruction_type::multiplication: return "multiplication"; - case instruction_type::division: return "division"; - case instruction_type::min: return "min"; - case instruction_type::max: return "max"; - case instruction_type::square_root: return "square_root"; - case instruction_type::complex_conjugate: return "complex_conjugate"; - case instruction_type::absolute: return "absolute"; - case instruction_type::constant_multiplication: return fmt::format("constant_multiplication {}", format_number(instruction.value)); - case instruction_type::update_delay: return fmt::format("update_delay delays[{}]", instruction.index); - case instruction_type::custom: return fmt::format("custom custom_sources[{}]", instruction.index); - case instruction_type::forward_value: return "forward_value"; - } - // clang-format on - return std::string{}; -} - -[[nodiscard]] inline std::string format_compiled_simulation_code_instructions(simulation_code const& code) { - auto result = std::string{}; - for (auto const& [i, instruction] : enumerate(code.instructions)) { - auto instruction_string = format_compiled_simulation_code_instruction(instruction); - if (instruction.result_index < code.result_keys.size()) { - instruction_string = fmt::format( - "{:<26} -> {}: \"{}\"", instruction_string, instruction.result_index, code.result_keys[instruction.result_index]); - } - result += fmt::format("{:>2}: {}\n", i, instruction_string); - } - return result; -} - -[[nodiscard]] inline std::string format_compiled_simulation_code(simulation_code const& code) { - return fmt::format( - "==============================================\n" - "> Code stats\n" - "==============================================\n" - "Input count: {}\n" - "Output count: {}\n" - "Instruction count: {}\n" - "Required stack size: {}\n" - "Delay count: {}\n" - "Result count: {}\n" - "Custom operation count: {}\n" - "Custom source count: {}\n" - "==============================================\n" - "> Delays\n" - "==============================================\n" - "{}" - "==============================================\n" - "> Result keys\n" - "==============================================\n" - "{}" - "==============================================\n" - "> Instructions\n" - "==============================================\n" - "{}" - "==============================================", - code.input_count, - code.output_count, - code.instructions.size(), - code.required_stack_size, - code.delays.size(), - code.result_keys.size(), - code.custom_operations.size(), - code.custom_sources.size(), - format_compiled_simulation_code_delays(code), - format_compiled_simulation_code_result_keys(code), - format_compiled_simulation_code_instructions(code)); -} - -} // namespace asic - -#endif // ASIC_SIMULATION_FORMAT_CODE_HPP diff --git a/src/simulation/instruction.hpp b/src/simulation/instruction.hpp deleted file mode 100644 index 9b03d9e0..00000000 --- a/src/simulation/instruction.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef ASIC_SIMULATION_INSTRUCTION_HPP -#define ASIC_SIMULATION_INSTRUCTION_HPP - -#include "../number.hpp" - -#include <cstddef> -#include <cstdint> -#include <optional> - -namespace asic { - -enum class instruction_type : std::uint8_t { - push_input, // push(inputs[index]) - push_result, // push(results[index]) - push_delay, // push(delays[index]) - push_constant, // push(value) - quantize, // push(trunc(pop(), bit_mask)) - addition, // rhs=pop(), lhs=pop(), push(lhs + rhs) - subtraction, // rhs=pop(), lhs=pop(), push(lhs - rhs) - multiplication, // rhs=pop(), lhs=pop(), push(lhs * rhs) - division, // rhs=pop(), lhs=pop(), push(lhs / rhs) - min, // rhs=pop(), lhs=pop(), push(min(lhs, rhs)) - max, // rhs=pop(), lhs=pop(), push(max(lhs, rhs)) - square_root, // push(sqrt(pop())) - complex_conjugate, // push(conj(pop())) - absolute, // push(abs(pop())) - constant_multiplication, // push(pop() * value) - update_delay, // delays[index] = pop() - custom, // Custom operation. Uses custom_source[index]. - forward_value // Forward the current value on the stack (push(pop()), i.e. do nothing). -}; - -using result_index_type = std::uint16_t; - -struct instruction final { - constexpr instruction() noexcept // NOLINT(cppcoreguidelines-pro-type-member-init) - : index(0) {} - - union { - // Index used by push_input, push_result, delay and custom. - std::size_t index; - // Bit mask used by quantize. - std::int64_t bit_mask; - // Constant value used by push_constant and constant_multiplication. - number value; - }; - // Index into where the result of the instruction will be stored. If the result should be ignored, this index will be one past the last valid result index. - result_index_type result_index = 0; - // Specifies what kind of operation the instruction should execute. - instruction_type type = instruction_type::forward_value; -}; - -} // namespace asic - -#endif // ASIC_SIMULATION_INSTRUCTION_HPP diff --git a/src/simulation/run.cpp b/src/simulation/run.cpp deleted file mode 100644 index 1998b82e..00000000 --- a/src/simulation/run.cpp +++ /dev/null @@ -1,188 +0,0 @@ -#include "run.hpp" - -#include "../algorithm.hpp" -#include "../debug.hpp" -#include "format_code.hpp" - -#define NOMINMAX -#include <algorithm> -#include <complex> -#include <cstddef> -#include <fmt/format.h> -#include <iterator> -#include <pybind11/pybind11.h> -#include <pybind11/stl.h> -#include <stdexcept> - -namespace py = pybind11; - -namespace asic { - -[[nodiscard]] static number quantize_value(number value, std::int64_t bit_mask) { - if (value.imag() != 0) { - throw py::type_error{"Complex value cannot be quantized"}; - } - return number{static_cast<number::value_type>(static_cast<std::int64_t>(value.real()) & bit_mask)}; -} - -[[nodiscard]] static std::int64_t setup_truncation_parameters(bool& quantize, std::optional<std::uint8_t>& bits_override) { - if (quantize && bits_override) { - quantize = false; // Ignore quantize instructions, they will be quantized using bits_override instead. - if (*bits_override > 64) { - throw py::value_error{"Cannot quantize to more than 64 bits"}; - } - return static_cast<std::int64_t>((std::int64_t{1} << *bits_override) - 1); // Return the bit mask override to use. - } - bits_override.reset(); // Don't use bits_override if quantize is false. - return std::int64_t{}; -} - -simulation_state run_simulation(simulation_code const& code, span<number const> inputs, span<number> delays, - std::optional<std::uint8_t> bits_override, bool quantize) { - ASIC_ASSERT(inputs.size() == code.input_count); - ASIC_ASSERT(delays.size() == code.delays.size()); - ASIC_ASSERT(code.output_count <= code.required_stack_size); - - auto state = simulation_state{}; - - // Setup results. - state.results.resize(code.result_keys.size() + 1); // Add one space to store ignored results. - // Initialize delay results to their current values. - for (auto const& [i, delay] : enumerate(code.delays)) { - state.results[delay.result_index] = delays[i]; - } - - // Setup stack. - state.stack.resize(code.required_stack_size); - auto* stack_pointer = state.stack.data(); - - // Utility functions to make the stack manipulation code below more readable. - // Should hopefully be inlined by the compiler. - auto const push = [&](number value) -> void { - ASIC_ASSERT(std::distance(state.stack.data(), stack_pointer) < static_cast<std::ptrdiff_t>(state.stack.size())); - *stack_pointer++ = value; - }; - auto const pop = [&]() -> number { - ASIC_ASSERT(std::distance(state.stack.data(), stack_pointer) > std::ptrdiff_t{0}); - return *--stack_pointer; - }; - auto const peek = [&]() -> number { - ASIC_ASSERT(std::distance(state.stack.data(), stack_pointer) > std::ptrdiff_t{0}); - ASIC_ASSERT(std::distance(state.stack.data(), stack_pointer) <= static_cast<std::ptrdiff_t>(state.stack.size())); - return *(stack_pointer - 1); - }; - - // Check if results should be quantized. - auto const bit_mask_override = setup_truncation_parameters(quantize, bits_override); - - // Hot instruction evaluation loop. - for (auto const& instruction : code.instructions) { - ASIC_DEBUG_MSG("Evaluating {}.", format_compiled_simulation_code_instruction(instruction)); - // Execute the instruction. - switch (instruction.type) { - case instruction_type::push_input: - push(inputs[instruction.index]); - break; - case instruction_type::push_result: - push(state.results[instruction.index]); - break; - case instruction_type::push_delay: - push(delays[instruction.index]); - break; - case instruction_type::push_constant: - push(instruction.value); - break; - case instruction_type::quantize: - if (quantize) { - push(quantize_value(pop(), instruction.bit_mask)); - } - break; - case instruction_type::addition: { - auto const rhs = pop(); - auto const lhs = pop(); - push(lhs + rhs); - break; - } - case instruction_type::subtraction: { - auto const rhs = pop(); - auto const lhs = pop(); - push(lhs - rhs); - break; - } - case instruction_type::multiplication: { - auto const rhs = pop(); - auto const lhs = pop(); - push(lhs * rhs); - break; - } - case instruction_type::division: { - auto const rhs = pop(); - auto const lhs = pop(); - push(lhs / rhs); - break; - } - case instruction_type::min: { - auto const rhs = pop(); - auto const lhs = pop(); - if (lhs.imag() != 0 || rhs.imag() != 0) { - throw std::runtime_error{"Min does not support complex numbers."}; - } - push(std::min(lhs.real(), rhs.real())); - break; - } - case instruction_type::max: { - auto const rhs = pop(); - auto const lhs = pop(); - if (lhs.imag() != 0 || rhs.imag() != 0) { - throw std::runtime_error{"Max does not support complex numbers."}; - } - push(std::max(lhs.real(), rhs.real())); - break; - } - case instruction_type::square_root: - push(std::sqrt(pop())); - break; - case instruction_type::complex_conjugate: - push(std::conj(pop())); - break; - case instruction_type::absolute: - push(number{std::abs(pop())}); - break; - case instruction_type::constant_multiplication: - push(pop() * instruction.value); - break; - case instruction_type::update_delay: - delays[instruction.index] = pop(); - break; - case instruction_type::custom: { - using namespace pybind11::literals; - auto const& src = code.custom_sources[instruction.index]; - auto const& op = code.custom_operations[src.custom_operation_index]; - auto input_values = std::vector<number>{}; - input_values.reserve(op.input_count); - for (auto i = std::size_t{0}; i < op.input_count; ++i) { - input_values.push_back(pop()); - } - push(op.evaluate_output(src.output_index, std::move(input_values), "quantize"_a = quantize).cast<number>()); - break; - } - case instruction_type::forward_value: - // Do nothing, since doing push(pop()) would be pointless. - break; - } - // If we've been given a global override for how many bits to use, always quantize the result. - if (bits_override) { - push(quantize_value(pop(), bit_mask_override)); - } - // Store the result. - state.results[instruction.result_index] = peek(); - } - - // Remove the space that we used for ignored results. - state.results.pop_back(); - // Erase the portion of the stack that does not contain the output values. - state.stack.erase(state.stack.begin() + static_cast<std::ptrdiff_t>(code.output_count), state.stack.end()); - return state; -} - -} // namespace asic diff --git a/src/simulation/run.hpp b/src/simulation/run.hpp deleted file mode 100644 index 7cc05e5f..00000000 --- a/src/simulation/run.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef ASIC_SIMULATION_RUN_HPP -#define ASIC_SIMULATION_RUN_HPP - -#include "../number.hpp" -#include "../span.hpp" -#include "compile.hpp" - -#include <cstdint> -#include <vector> - -namespace asic { - -struct simulation_state final { - std::vector<number> stack{}; - std::vector<number> results{}; -}; - -simulation_state run_simulation(simulation_code const& code, span<number const> inputs, span<number> delays, - std::optional<std::uint8_t> bits_override, bool quantize); - -} // namespace asic - -#endif // ASIC_SIMULATION_RUN_HPP diff --git a/src/simulation/simulation.cpp b/src/simulation/simulation.cpp deleted file mode 100644 index 34ac320f..00000000 --- a/src/simulation/simulation.cpp +++ /dev/null @@ -1,130 +0,0 @@ -#include "simulation.hpp" - -#include "../algorithm.hpp" -#include "../debug.hpp" -#include "compile.hpp" -#include "run.hpp" - -#define NOMINMAX -#include <fmt/format.h> -#include <limits> -#include <pybind11/numpy.h> -#include <utility> - -namespace py = pybind11; - -namespace asic { - -simulation::simulation(pybind11::handle sfg, std::optional<std::vector<std::optional<input_provider_type>>> input_providers) - : m_code(compile_simulation(sfg)) - , m_input_functions(sfg.attr("input_count").cast<std::size_t>(), [](iteration_type) -> number { return number{}; }) { - m_delays.reserve(m_code.delays.size()); - for (auto const& delay : m_code.delays) { - m_delays.push_back(delay.initial_value); - } - if (input_providers) { - this->set_inputs(std::move(*input_providers)); - } -} - -void simulation::set_input(std::size_t index, input_provider_type input_provider) { - if (index >= m_input_functions.size()) { - throw py::index_error{fmt::format("Input index out of range (expected 0-{}, got {})", m_input_functions.size() - 1, index)}; - } - if (auto* const callable = std::get_if<input_function_type>(&input_provider)) { - m_input_functions[index] = std::move(*callable); - } else if (auto* const numeric = std::get_if<number>(&input_provider)) { - m_input_functions[index] = [value = *numeric](iteration_type) -> number { - return value; - }; - } else if (auto* const list = std::get_if<std::vector<number>>(&input_provider)) { - if (!m_input_length) { - m_input_length = static_cast<iteration_type>(list->size()); - } else if (*m_input_length != static_cast<iteration_type>(list->size())) { - throw py::value_error{fmt::format("Inconsistent input length for simulation (was {}, got {})", *m_input_length, list->size())}; - } - m_input_functions[index] = [values = std::move(*list)](iteration_type n) -> number { - return values.at(n); - }; - } -} - -void simulation::set_inputs( - std::vector<std::optional<input_provider_type>> input_providers) { // NOLINT(performance-unnecessary-value-param) - if (input_providers.size() != m_input_functions.size()) { - throw py::value_error{fmt::format( - "Wrong number of inputs supplied to simulation (expected {}, got {})", m_input_functions.size(), input_providers.size())}; - } - for (auto&& [i, input_provider] : enumerate(input_providers)) { - if (input_provider) { - this->set_input(i, std::move(*input_provider)); - } - } -} - -std::vector<number> simulation::step(bool save_results, std::optional<std::uint8_t> bits_override, bool quantize) { - return this->run_for(1, save_results, bits_override, quantize); -} - -std::vector<number> simulation::run_until(iteration_type iteration, bool save_results, std::optional<std::uint8_t> bits_override, - bool quantize) { - auto result = std::vector<number>{}; - while (m_iteration < iteration) { - ASIC_DEBUG_MSG("Running simulation iteration."); - auto inputs = std::vector<number>(m_code.input_count); - for (auto&& [input, function] : zip(inputs, m_input_functions)) { - input = function(m_iteration); - } - auto state = run_simulation(m_code, inputs, m_delays, bits_override, quantize); - result = std::move(state.stack); - if (save_results) { - m_results.push_back(std::move(state.results)); - } - ++m_iteration; - } - return result; -} - -std::vector<number> simulation::run_for(iteration_type iterations, bool save_results, std::optional<std::uint8_t> bits_override, - bool quantize) { - if (iterations > std::numeric_limits<iteration_type>::max() - m_iteration) { - throw py::value_error("Simulation iteration type overflow!"); - } - return this->run_until(m_iteration + iterations, save_results, bits_override, quantize); -} - -std::vector<number> simulation::run(bool save_results, std::optional<std::uint8_t> bits_override, bool quantize) { - if (m_input_length) { - return this->run_until(*m_input_length, save_results, bits_override, quantize); - } - throw py::index_error{"Tried to run unlimited simulation"}; -} - -iteration_type simulation::iteration() const noexcept { - return m_iteration; -} - -pybind11::dict simulation::results() const noexcept { - auto results = py::dict{}; - if (!m_results.empty()) { - for (auto const& [i, key] : enumerate(m_code.result_keys)) { - auto values = std::vector<number>{}; - values.reserve(m_results.size()); - for (auto const& result : m_results) { - values.push_back(result[i]); - } - results[py::str{key}] = py::array{static_cast<py::ssize_t>(values.size()), values.data()}; - } - } - return results; -} - -void simulation::clear_results() noexcept { - m_results.clear(); -} - -void simulation::clear_state() noexcept { - m_delays.clear(); -} - -} // namespace asic diff --git a/src/simulation/simulation.hpp b/src/simulation/simulation.hpp deleted file mode 100644 index ec930e83..00000000 --- a/src/simulation/simulation.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef ASIC_SIMULATION_DOD_HPP -#define ASIC_SIMULATION_DOD_HPP - -#include "../number.hpp" -#include "compile.hpp" - -#define NOMINMAX -#include <cstddef> -#include <cstdint> -#include <functional> -#include <optional> -#include <pybind11/functional.h> -#include <pybind11/pybind11.h> -#include <pybind11/stl.h> -#include <variant> -#include <vector> - -namespace asic { - -using iteration_type = std::uint32_t; -using input_function_type = std::function<number(iteration_type)>; -using input_provider_type = std::variant<number, std::vector<number>, input_function_type>; - -class simulation final { -public: - simulation(pybind11::handle sfg, std::optional<std::vector<std::optional<input_provider_type>>> input_providers = std::nullopt); - - void set_input(std::size_t index, input_provider_type input_provider); - void set_inputs(std::vector<std::optional<input_provider_type>> input_providers); - - [[nodiscard]] std::vector<number> step(bool save_results, std::optional<std::uint8_t> bits_override, bool quantize); - [[nodiscard]] std::vector<number> run_until(iteration_type iteration, bool save_results, std::optional<std::uint8_t> bits_override, - bool quantize); - [[nodiscard]] std::vector<number> run_for(iteration_type iterations, bool save_results, std::optional<std::uint8_t> bits_override, - bool quantize); - [[nodiscard]] std::vector<number> run(bool save_results, std::optional<std::uint8_t> bits_override, bool quantize); - - [[nodiscard]] iteration_type iteration() const noexcept; - [[nodiscard]] pybind11::dict results() const noexcept; - - void clear_results() noexcept; - void clear_state() noexcept; - -private: - simulation_code m_code; - std::vector<input_function_type> m_input_functions; - std::vector<number> m_delays{}; - std::optional<iteration_type> m_input_length{}; - iteration_type m_iteration = 0; - std::vector<std::vector<number>> m_results{}; -}; - -} // namespace asic - -#endif // ASIC_SIMULATION_DOD_HPP diff --git a/src/span.hpp b/src/span.hpp deleted file mode 100644 index 3ccc34ea..00000000 --- a/src/span.hpp +++ /dev/null @@ -1,362 +0,0 @@ -#ifndef ASIC_SPAN_HPP -#define ASIC_SPAN_HPP - -#include <algorithm> -#include <array> -#include <cassert> -#include <cstddef> -#include <iterator> -#include <limits> -#include <type_traits> -#include <utility> - -namespace asic { - -constexpr auto dynamic_size = static_cast<std::size_t>(-1); - -// C++17-compatible std::span substitute. -template <typename T, std::size_t Size = dynamic_size> -class span; - -namespace detail { - -template <typename T> -struct is_span_impl : std::false_type {}; - -template <typename T, std::size_t Size> -struct is_span_impl<span<T, Size>> : std::true_type {}; - -template <typename T> -struct is_span : is_span_impl<std::remove_cv_t<T>> {}; - -template <typename T> -constexpr auto is_span_v = is_span<T>::value; - -template <typename T> -struct is_std_array_impl : std::false_type {}; - -template <typename T, std::size_t Size> -struct is_std_array_impl<std::array<T, Size>> : std::true_type {}; - -template <typename T> -struct is_std_array : is_std_array_impl<std::remove_cv_t<T>> {}; - -template <typename T> -constexpr auto is_std_array_v = is_std_array<T>::value; - -template <std::size_t From, std::size_t To> -struct is_size_convertible : std::bool_constant<From == To || From == dynamic_size || To == dynamic_size> {}; - -template <std::size_t From, std::size_t To> -constexpr auto is_size_convertible_v = is_size_convertible<From, To>::value; - -template <typename From, typename To> -struct is_element_type_convertible : std::bool_constant<std::is_convertible_v<From (*)[], To (*)[]>> {}; - -template <typename From, typename To> -constexpr auto is_element_type_convertible_v = is_element_type_convertible<From, To>::value; - -template <typename T, std::size_t Size> -struct span_base { - using element_type = T; - using pointer = element_type*; - using size_type = std::size_t; - - constexpr span_base() noexcept = default; - - constexpr span_base(pointer data, [[maybe_unused]] size_type size) - : m_data(data) { - assert(size == Size); - } - - template <size_type N> - constexpr span_base(span_base<T, N> other) - : m_data(other.data()) { - static_assert(N == Size || N == dynamic_size); - assert(other.size() == Size); - } - - [[nodiscard]] constexpr pointer data() const noexcept { - return m_data; - } - - [[nodiscard]] constexpr size_type size() const noexcept { - return Size; - } - -private: - pointer m_data = nullptr; -}; - -template <typename T> -struct span_base<T, dynamic_size> { - using element_type = T; - using pointer = element_type*; - using size_type = std::size_t; - - constexpr span_base() noexcept = default; - - constexpr span_base(pointer data, size_type size) - : m_data(data) - , m_size(size) {} - - template <size_type N> - explicit constexpr span_base(span_base<T, N> other) - : m_data(other.data()) - , m_size(other.size()) {} - - [[nodiscard]] constexpr pointer data() const noexcept { - return m_data; - } - - [[nodiscard]] constexpr size_type size() const noexcept { - return m_size; - } - -private: - pointer m_data = nullptr; - size_type m_size = 0; -}; - -template <typename T, std::size_t Size, std::size_t Offset, std::size_t N> -struct subspan_type { - using type = span<T, (N != dynamic_size) ? N : (Size != dynamic_size) ? Size - Offset : Size>; -}; - -template <typename T, std::size_t Size, std::size_t Offset, std::size_t Count> -using subspan_type_t = typename subspan_type<T, Size, Offset, Count>::type; - -} // namespace detail - -template <typename T, std::size_t Size> -class span final : public detail::span_base<T, Size> { // NOLINT(cppcoreguidelines-special-member-functions) -public: - using element_type = typename detail::span_base<T, Size>::element_type; - using pointer = typename detail::span_base<T, Size>::pointer; - using size_type = typename detail::span_base<T, Size>::size_type; - using value_type = std::remove_cv_t<element_type>; - using reference = element_type&; - using iterator = element_type*; - using const_iterator = const element_type*; - using reverse_iterator = std::reverse_iterator<iterator>; - using const_reverse_iterator = std::reverse_iterator<const_iterator>; - - // Default constructor. - constexpr span() noexcept = default; - - // Construct from pointer, size. - constexpr span(pointer data, size_type size) - : detail::span_base<T, Size>(data, size) {} - - // Copy constructor. - template <typename U, std::size_t N, typename = std::enable_if_t<detail::is_size_convertible_v<N, Size>>, - typename = std::enable_if_t<detail::is_element_type_convertible_v<U, T>>> - constexpr span(span<U, N> const& other) - : span(other.data(), other.size()) {} - - // Copy assignment. - constexpr span& operator=(span const&) noexcept = default; - - // Destructor. - ~span() = default; - - // Construct from begin, end. - constexpr span(pointer begin, pointer end) - : span(begin, end - begin) {} - - // Construct from C array. - template <std::size_t N> - constexpr span(element_type (&arr)[N]) noexcept - : span(std::data(arr), N) {} - - // Construct from std::array. - template <std::size_t N, typename = std::enable_if_t<N != 0>> - constexpr span(std::array<value_type, N>& arr) noexcept - : span(std::data(arr), N) {} - - // Construct from empty std::array. - constexpr span(std::array<value_type, 0>&) noexcept - : span() {} - - // Construct from const std::array. - template <std::size_t N, typename = std::enable_if_t<N != 0>> - constexpr span(std::array<value_type, N> const& arr) noexcept - : span(std::data(arr), N) {} - - // Construct from empty const std::array. - constexpr span(std::array<value_type, 0> const&) noexcept - : span() {} - - // Construct from other container. - template < - typename Container, typename = std::enable_if_t<!detail::is_span_v<Container>>, - typename = std::enable_if_t<!detail::is_std_array_v<Container>>, typename = decltype(std::data(std::declval<Container>())), - typename = decltype(std::size(std::declval<Container>())), - typename = std::enable_if_t<std::is_convertible_v<typename Container::pointer, pointer>>, - typename = std::enable_if_t<std::is_convertible_v<typename Container::pointer, decltype(std::data(std::declval<Container>()))>>> - constexpr span(Container& container) - : span(std::data(container), std::size(container)) {} - - // Construct from other const container. - template < - typename Container, typename Element = element_type, typename = std::enable_if_t<std::is_const_v<Element>>, - typename = std::enable_if_t<!detail::is_span_v<Container>>, typename = std::enable_if_t<!detail::is_std_array_v<Container>>, - typename = decltype(std::data(std::declval<Container>())), typename = decltype(std::size(std::declval<Container>())), - typename = std::enable_if_t<std::is_convertible_v<typename Container::pointer, pointer>>, - typename = std::enable_if_t<std::is_convertible_v<typename Container::pointer, decltype(std::data(std::declval<Container>()))>>> - constexpr span(Container const& container) - : span(std::data(container), std::size(container)) {} - - [[nodiscard]] constexpr iterator begin() const noexcept { - return this->data(); - } - - [[nodiscard]] constexpr const_iterator cbegin() const noexcept { - return this->data(); - } - - [[nodiscard]] constexpr iterator end() const noexcept { - return this->data() + this->size(); - } - - [[nodiscard]] constexpr const_iterator cend() const noexcept { - return this->data() + this->size(); - } - - [[nodiscard]] constexpr reverse_iterator rbegin() const noexcept { - return std::make_reverse_iterator(this->end()); - } - - [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { - return std::make_reverse_iterator(this->cend()); - } - - [[nodiscard]] constexpr reverse_iterator rend() const noexcept { - return std::make_reverse_iterator(this->begin()); - } - - [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { - return std::make_reverse_iterator(this->cbegin()); - } - - [[nodiscard]] constexpr reference operator[](size_type i) const noexcept { - assert(i < this->size()); - return this->data()[i]; - } - - [[nodiscard]] constexpr reference operator()(size_type i) const noexcept { - assert(i < this->size()); - return this->data()[i]; - } - - [[nodiscard]] constexpr size_type size_bytes() const noexcept { - return this->size() * sizeof(element_type); - } - - [[nodiscard]] constexpr bool empty() const noexcept { - return this->size() == 0; - } - - [[nodiscard]] constexpr reference front() const noexcept { - assert(!this->empty()); - return this->data()[0]; - } - - [[nodiscard]] constexpr reference back() const noexcept { - assert(!this->empty()); - return this->data()[this->size() - 1]; - } - - template <std::size_t N> - [[nodiscard]] constexpr span<T, N> first() const { - static_assert(N != dynamic_size && N <= Size); - return {this->data(), N}; - } - - template <std::size_t N> - [[nodiscard]] constexpr span<T, N> last() const { - static_assert(N != dynamic_size && N <= Size); - return {this->data() + (Size - N), N}; - } - - template <std::size_t Offset, std::size_t N = dynamic_size> - [[nodiscard]] constexpr auto subspan() const -> detail::subspan_type_t<T, Size, Offset, N> { - static_assert(Offset <= Size); - return {this->data() + Offset, (N == dynamic_size) ? this->size() - Offset : N}; - } - - [[nodiscard]] constexpr span<T, dynamic_size> first(size_type n) const { - assert(n <= this->size()); - return {this->data(), n}; - } - - [[nodiscard]] constexpr span<T, dynamic_size> last(size_type n) const { - return this->subspan(this->size() - n); - } - - [[nodiscard]] constexpr span<T, dynamic_size> subspan(size_type offset, size_type n = dynamic_size) const { - if constexpr (Size == dynamic_size) { - assert(offset <= this->size()); - if (n == dynamic_size) { - return {this->data() + offset, this->size() - offset}; - } - assert(n <= this->size()); - assert(offset + n <= this->size()); - return {this->data() + offset, n}; - } else { - return span<T, dynamic_size>{*this}.subspan(offset, n); - } - } -}; - -template <typename T, std::size_t LhsSize, std::size_t RhsSize> -[[nodiscard]] constexpr bool operator==(span<T, LhsSize> lhs, span<T, RhsSize> rhs) { - return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); -} - -template <typename T, std::size_t LhsSize, std::size_t RhsSize> -[[nodiscard]] constexpr bool operator!=(span<T, LhsSize> lhs, span<T, RhsSize> rhs) { - return !(lhs == rhs); -} - -template <typename T, std::size_t LhsSize, std::size_t RhsSize> -[[nodiscard]] constexpr bool operator<(span<T, LhsSize> lhs, span<T, RhsSize> rhs) { - return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); -} - -template <typename T, std::size_t LhsSize, std::size_t RhsSize> -[[nodiscard]] constexpr bool operator<=(span<T, LhsSize> lhs, span<T, RhsSize> rhs) { - return !(lhs > rhs); -} - -template <typename T, std::size_t LhsSize, std::size_t RhsSize> -[[nodiscard]] constexpr bool operator>(span<T, LhsSize> lhs, span<T, RhsSize> rhs) { - return rhs < lhs; -} - -template <typename T, std::size_t LhsSize, std::size_t RhsSize> -[[nodiscard]] constexpr bool operator>=(span<T, LhsSize> lhs, span<T, RhsSize> rhs) { - return !(lhs < rhs); -} - -template <typename Container> -span(Container&) -> span<typename Container::value_type>; - -template <typename Container> -span(Container const&) -> span<typename Container::value_type const>; - -template <typename T, std::size_t N> -span(T (&)[N]) -> span<T, N>; - -template <typename T, std::size_t N> -span(std::array<T, N>&) -> span<T, N>; - -template <typename T, std::size_t N> -span(std::array<T, N> const&) -> span<T const, N>; - -template <typename T, typename Dummy> -span(T, Dummy &&) -> span<std::remove_reference_t<decltype(std::declval<T>()[0])>>; - -} // namespace asic - -#endif // ASIC_SPAN_HPP diff --git a/test/test_fast_simulation.py b/test/test_fast_simulation.py deleted file mode 100644 index d2773847..00000000 --- a/test/test_fast_simulation.py +++ /dev/null @@ -1,277 +0,0 @@ -import numpy as np -import pytest - -from b_asic import ( - SFG, - Addition, - Butterfly, - Constant, - FastSimulation, - Output, - Subtraction, -) - - -class TestRunFor: - def test_with_lambdas_as_input(self, sfg_two_inputs_two_outputs): - simulation = FastSimulation( - sfg_two_inputs_two_outputs, [lambda n: n + 3, lambda n: 1 + n * 2] - ) - - output = simulation.run_for(101, save_results=True) - - assert output[0] == 304 - assert output[1] == 505 - - assert simulation.results["0"][100] == 304 - assert simulation.results["1"][100] == 505 - - assert simulation.results["in1"][0] == 3 - assert simulation.results["in2"][0] == 1 - assert simulation.results["add1"][0] == 4 - assert simulation.results["add2"][0] == 5 - assert simulation.results["0"][0] == 4 - assert simulation.results["1"][0] == 5 - - assert simulation.results["in1"][1] == 4 - assert simulation.results["in2"][1] == 3 - assert simulation.results["add1"][1] == 7 - assert simulation.results["add2"][1] == 10 - assert simulation.results["0"][1] == 7 - assert simulation.results["1"][1] == 10 - - assert simulation.results["in1"][2] == 5 - assert simulation.results["in2"][2] == 5 - assert simulation.results["add1"][2] == 10 - assert simulation.results["add2"][2] == 15 - assert simulation.results["0"][2] == 10 - assert simulation.results["1"][2] == 15 - - assert simulation.results["in1"][3] == 6 - assert simulation.results["in2"][3] == 7 - assert simulation.results["add1"][3] == 13 - assert simulation.results["add2"][3] == 20 - assert simulation.results["0"][3] == 13 - assert simulation.results["1"][3] == 20 - - def test_with_numpy_arrays_as_input(self, sfg_two_inputs_two_outputs): - input0 = np.array([5, 9, 25, -5, 7]) - input1 = np.array([7, 3, 3, 54, 2]) - simulation = FastSimulation( - sfg_two_inputs_two_outputs, [input0, input1] - ) - - output = simulation.run_for(5, save_results=True) - - assert output[0] == 9 - assert output[1] == 11 - - assert isinstance(simulation.results["in1"], np.ndarray) - assert isinstance(simulation.results["in2"], np.ndarray) - assert isinstance(simulation.results["add1"], np.ndarray) - assert isinstance(simulation.results["add2"], np.ndarray) - assert isinstance(simulation.results["0"], np.ndarray) - assert isinstance(simulation.results["1"], np.ndarray) - - assert simulation.results["in1"][0] == 5 - assert simulation.results["in2"][0] == 7 - assert simulation.results["add1"][0] == 12 - assert simulation.results["add2"][0] == 19 - assert simulation.results["0"][0] == 12 - assert simulation.results["1"][0] == 19 - - assert simulation.results["in1"][1] == 9 - assert simulation.results["in2"][1] == 3 - assert simulation.results["add1"][1] == 12 - assert simulation.results["add2"][1] == 15 - assert simulation.results["0"][1] == 12 - assert simulation.results["1"][1] == 15 - - assert simulation.results["in1"][2] == 25 - assert simulation.results["in2"][2] == 3 - assert simulation.results["add1"][2] == 28 - assert simulation.results["add2"][2] == 31 - assert simulation.results["0"][2] == 28 - assert simulation.results["1"][2] == 31 - - assert simulation.results["in1"][3] == -5 - assert simulation.results["in2"][3] == 54 - assert simulation.results["add1"][3] == 49 - assert simulation.results["add2"][3] == 103 - assert simulation.results["0"][3] == 49 - assert simulation.results["1"][3] == 103 - - assert simulation.results["0"][4] == 9 - assert simulation.results["1"][4] == 11 - - def test_with_numpy_array_overflow(self, sfg_two_inputs_two_outputs): - input0 = np.array([5, 9, 25, -5, 7]) - input1 = np.array([7, 3, 3, 54, 2]) - simulation = FastSimulation( - sfg_two_inputs_two_outputs, [input0, input1] - ) - simulation.run_for(5) - with pytest.raises(IndexError): - simulation.step() - - def test_run_whole_numpy_array(self, sfg_two_inputs_two_outputs): - input0 = np.array([5, 9, 25, -5, 7]) - input1 = np.array([7, 3, 3, 54, 2]) - simulation = FastSimulation( - sfg_two_inputs_two_outputs, [input0, input1] - ) - simulation.run() - assert len(simulation.results["0"]) == 5 - assert len(simulation.results["1"]) == 5 - with pytest.raises(IndexError): - simulation.step() - - def test_delay(self, sfg_delay): - simulation = FastSimulation(sfg_delay) - simulation.set_input(0, [5, -2, 25, -6, 7, 0]) - simulation.run_for(6, save_results=True) - - assert simulation.results["0"][0] == 0 - assert simulation.results["0"][1] == 5 - assert simulation.results["0"][2] == -2 - assert simulation.results["0"][3] == 25 - assert simulation.results["0"][4] == -6 - assert simulation.results["0"][5] == 7 - - def test_find_result_key(self, precedence_sfg_delays): - sim = FastSimulation( - precedence_sfg_delays, - [[0, 4, 542, 42, 31.314, 534.123, -453415, 5431]], - ) - sim.run() - assert ( - sim.results[ - precedence_sfg_delays.find_result_keys_by_name("ADD2")[0] - ][4] - == 31220 - ) - assert ( - sim.results[ - precedence_sfg_delays.find_result_keys_by_name("A1")[0] - ][2] - == 80 - ) - - -class TestRun: - def test_save_results(self, sfg_two_inputs_two_outputs): - simulation = FastSimulation(sfg_two_inputs_two_outputs, [2, 3]) - assert not simulation.results - simulation.run_for(10, save_results=False) - assert not simulation.results - simulation.run_for(10) - assert len(simulation.results["0"]) == 10 - assert len(simulation.results["1"]) == 10 - simulation.run_for(10, save_results=True) - assert len(simulation.results["0"]) == 20 - assert len(simulation.results["1"]) == 20 - simulation.run_for(10, save_results=False) - assert len(simulation.results["0"]) == 20 - assert len(simulation.results["1"]) == 20 - simulation.run_for(13, save_results=True) - assert len(simulation.results["0"]) == 33 - assert len(simulation.results["1"]) == 33 - simulation.step(save_results=False) - assert len(simulation.results["0"]) == 33 - assert len(simulation.results["1"]) == 33 - simulation.step() - assert len(simulation.results["0"]) == 34 - assert len(simulation.results["1"]) == 34 - simulation.clear_results() - assert not simulation.results - - def test_nested(self, sfg_nested): - input0 = np.array([5, 9]) - input1 = np.array([7, 3]) - simulation = FastSimulation(sfg_nested, [input0, input1]) - - output0 = simulation.step() - output1 = simulation.step() - - assert output0[0] == 11405 - assert output1[0] == 4221 - - def test_accumulator(self, sfg_accumulator): - data_in = np.array([5, -2, 25, -6, 7, 0]) - reset = np.array([0, 0, 0, 1, 0, 0]) - simulation = FastSimulation(sfg_accumulator, [data_in, reset]) - output0 = simulation.step() - output1 = simulation.step() - output2 = simulation.step() - output3 = simulation.step() - output4 = simulation.step() - output5 = simulation.step() - assert output0[0] == 0 - assert output1[0] == 5 - assert output2[0] == 3 - assert output3[0] == 28 - assert output4[0] == 0 - assert output5[0] == 7 - - def test_simple_accumulator(self, sfg_simple_accumulator): - data_in = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - simulation = FastSimulation(sfg_simple_accumulator, [data_in]) - simulation.run() - assert list(simulation.results["0"]) == [ - 0, - 1, - 3, - 6, - 10, - 15, - 21, - 28, - 36, - 45, - ] - - def test_simple_filter(self, sfg_simple_filter): - input0 = np.array([1, 2, 3, 4, 5]) - simulation = FastSimulation(sfg_simple_filter, [input0]) - simulation.run_for(len(input0), save_results=True) - assert all( - simulation.results["0"] == np.array([0, 1.0, 2.5, 4.25, 6.125]) - ) - - def test_custom_operation(self, sfg_custom_operation): - simulation = FastSimulation(sfg_custom_operation, [lambda n: n + 1]) - simulation.run_for(5) - assert all(simulation.results["0"] == np.array([2, 4, 6, 8, 10])) - assert all(simulation.results["1"] == np.array([2, 4, 8, 16, 32])) - - -class TestLarge: - def test_1k_additions(self): - prev_op = Addition(Constant(1), Constant(1)) - for _ in range(999): - prev_op = Addition(prev_op, Constant(2)) - sfg = SFG(outputs=[Output(prev_op)]) - simulation = FastSimulation(sfg, []) - assert simulation.step()[0] == 2000 - - def test_1k_subtractions(self): - prev_op = Subtraction(Constant(0), Constant(2)) - for _ in range(999): - prev_op = Subtraction(prev_op, Constant(2)) - sfg = SFG(outputs=[Output(prev_op)]) - simulation = FastSimulation(sfg, []) - assert simulation.step()[0] == -2000 - - def test_1k_butterfly(self): - prev_op_add = Addition(Constant(1), Constant(1)) - prev_op_sub = Subtraction(Constant(-1), Constant(1)) - for _ in range(499): - prev_op_add = Addition(prev_op_add, Constant(2)) - for _ in range(499): - prev_op_sub = Subtraction(prev_op_sub, Constant(2)) - butterfly = Butterfly(prev_op_add, prev_op_sub) - sfg = SFG( - outputs=[Output(butterfly.output(0)), Output(butterfly.output(1))] - ) - simulation = FastSimulation(sfg, []) - assert list(simulation.step()) == [0, 2000] diff --git a/test/test_sfg.py b/test/test_sfg.py index ce95b106..516f4377 100644 --- a/test/test_sfg.py +++ b/test/test_sfg.py @@ -9,7 +9,7 @@ from typing import Counter, Dict, Type import pytest -from b_asic import SFG, FastSimulation, Input, Output, Signal +from b_asic import SFG, Input, Output, Signal from b_asic.core_operations import ( Absolute, Addition, @@ -345,112 +345,6 @@ class TestReplaceComponents: ) -class TestConstructSFG: - def test_1k_additions(self): - prev_op = Addition(Constant(1), Constant(1)) - for _ in range(999): - prev_op = Addition(prev_op, Constant(2)) - sfg = SFG(outputs=[Output(prev_op)]) - sim = FastSimulation(sfg) - sim.step() - assert sim.results["0"][0].real == 2000 - - def test_1k_subtractions(self): - prev_op = Subtraction(Constant(0), Constant(2)) - for _ in range(999): - prev_op = Subtraction(prev_op, Constant(2)) - sfg = SFG(outputs=[Output(prev_op)]) - sim = FastSimulation(sfg) - sim.step() - assert sim.results["0"][0].real == -2000 - - def test_1k_butterfly(self): - prev_op_add = Addition(Constant(1), Constant(1)) - prev_op_sub = Subtraction(Constant(-1), Constant(1)) - for _ in range(499): - prev_op_add = Addition(prev_op_add, Constant(2)) - for _ in range(499): - prev_op_sub = Subtraction(prev_op_sub, Constant(2)) - butterfly = Butterfly(prev_op_add, prev_op_sub) - sfg = SFG(outputs=[Output(butterfly.output(0)), Output(butterfly.output(1))]) - sim = FastSimulation(sfg) - sim.step() - assert sim.results["0"][0].real == 0 - assert sim.results["1"][0].real == 2000 - - def test_1k_multiplications(self): - prev_op = Multiplication(Constant(3), Constant(0.5)) - for _ in range(999): - prev_op = Multiplication(prev_op, Constant(1.01)) - sfg = SFG(outputs=[Output(prev_op)]) - sim = FastSimulation(sfg) - sim.step() - assert sim.results["0"][0].real == 31127.458868040336 - - def test_1k_divisions(self): - prev_op = Division(Constant(3), Constant(0.5)) - for _ in range(999): - prev_op = Division(prev_op, Constant(1.01)) - sfg = SFG(outputs=[Output(prev_op)]) - sim = FastSimulation(sfg) - sim.step() - assert sim.results["0"][0].real == 0.00028913378500165966 - - def test_1k_mins(self): - prev_op = Min(Constant(3.14159), Constant(43.14123843)) - for _ in range(999): - prev_op = Min(prev_op, Constant(43.14123843)) - sfg = SFG(outputs=[Output(prev_op)]) - sim = FastSimulation(sfg) - sim.step() - assert sim.results["0"][0].real == 3.14159 - - def test_1k_maxs(self): - prev_op = Max(Constant(3.14159), Constant(43.14123843)) - for _ in range(999): - prev_op = Max(prev_op, Constant(3.14159)) - sfg = SFG(outputs=[Output(prev_op)]) - sim = FastSimulation(sfg) - sim.step() - assert sim.results["0"][0].real == 43.14123843 - - def test_1k_square_roots(self): - prev_op = SquareRoot(Constant(1000000)) - for _ in range(4): - prev_op = SquareRoot(prev_op) - sfg = SFG(outputs=[Output(prev_op)]) - sim = FastSimulation(sfg) - sim.step() - assert sim.results["0"][0].real == 1.539926526059492 - - def test_1k_complex_conjugates(self): - prev_op = ComplexConjugate(Constant(10 + 5j)) - for _ in range(999): - prev_op = ComplexConjugate(prev_op) - sfg = SFG(outputs=[Output(prev_op)]) - sim = FastSimulation(sfg) - sim.step() - assert sim.results["0"] == [10 + 5j] - - def test_1k_absolutes(self): - prev_op = Absolute(Constant(-3.14159)) - for _ in range(999): - prev_op = Absolute(prev_op) - sfg = SFG(outputs=[Output(prev_op)]) - sim = FastSimulation(sfg) - sim.step() - assert sim.results["0"][0].real == 3.14159 - - def test_1k_constant_multiplications(self): - prev_op = ConstantMultiplication(1.02, Constant(3.14159)) - for _ in range(999): - prev_op = ConstantMultiplication(1.02, prev_op) - sfg = SFG(outputs=[Output(prev_op)]) - sim = FastSimulation(sfg) - sim.step() - assert sim.results["0"][0].real == 1251184247.0026844 - - class TestInsertComponent: def test_insert_component_in_sfg(self, large_operation_tree_names): sfg = SFG(outputs=[Output(large_operation_tree_names)]) diff --git a/test/test_simulation.py b/test/test_simulation.py index d2d59512..cdcb86c0 100644 --- a/test/test_simulation.py +++ b/test/test_simulation.py @@ -2,7 +2,6 @@ import numpy as np import pytest from b_asic import Simulation -from b_asic._b_asic import FastSimulation as Simulation class TestRunFor: -- GitLab