Commit b706fa0a authored by Ivar Härnqvist's avatar Ivar Härnqvist
Browse files

Add support for FetchContent and clang-tidy, modernize C++ code and fix...

Add support for FetchContent and clang-tidy, modernize C++ code and fix clang-tidy warnings, fix undefined behavior in simulation due to order of evaluation
parent 1b75d8c3
Pipeline #59955 passed with stage
in 1 minute and 30 seconds
Checks: "clang-analyzer-*,
cppcoreguidelines-*,
misc-*,
modernize-*,
performance-*,
portability-*,
readability-*,
-cppcoreguidelines-avoid-c-arrays,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
-cppcoreguidelines-pro-type-union-access,
-misc-non-private-member-variables-in-classes,
-misc-no-recursion,
-modernize-avoid-c-arrays,
-modernize-use-trailing-return-type,
-readability-function-cognitive-complexity,
-readability-magic-numbers,
-readability-named-parameter"
WarningsAsErrors: "*"
HeaderFilterRegex: "src/"
cmake_minimum_required(VERSION 3.8)
project(
"B-ASIC"
project("B-ASIC"
VERSION 1.0.0
DESCRIPTION "Better ASIC Toolbox for Python 3"
LANGUAGES C CXX
)
LANGUAGES C CXX)
# Find dependencies.
find_package(fmt REQUIRED)
find_package(pybind11 CONFIG REQUIRED)
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)
......@@ -32,9 +37,7 @@ 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}"
pybind11_add_module("${TARGET_NAME}"
# Main files.
"${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/simulation.cpp"
......@@ -53,49 +56,44 @@ pybind11_add_module(
)
# Include headers.
target_include_directories(
"${TARGET_NAME}"
PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/src"
)
target_include_directories("${TARGET_NAME}" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src")
# Use C++17.
target_compile_features(
"${TARGET_NAME}"
PRIVATE
cxx_std_17
)
target_compile_features("${TARGET_NAME}" PRIVATE cxx_std_17)
# Set compiler-specific options using generator expressions.
target_compile_options(
"${TARGET_NAME}"
PRIVATE
$<$<OR:$<CXX_COMPILER_ID:GNU>,$<CXX_COMPILER_ID:Clang>>:
-W -Wall -Wextra -Werror -Wno-psabi
$<$<CONFIG:Debug>:-g>
$<$<NOT:$<CONFIG:Debug>>:-O3>
>
$<$<CXX_COMPILER_ID:MSVC>:
/W3 /WX /permissive- /utf-8
$<$<CONFIG:Debug>:/Od>
$<$<NOT:$<CONFIG:Debug>>:/Ot>
>
)
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.
target_link_libraries(
"${TARGET_NAME}"
PRIVATE
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>
)
$<$<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
add_custom_target(copy_binaries ALL
COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_FILE:${TARGET_NAME}>" "${CMAKE_CURRENT_LIST_DIR}"
COMMENT "Copying binaries to ${CMAKE_CURRENT_LIST_DIR}"
DEPENDS "${TARGET_NAME}"
)
DEPENDS "${TARGET_NAME}")
endif()
......@@ -2,4 +2,4 @@ include README.md
include LICENSE
include CMakeLists.txt
include b_asic/GUI/operation_icons/*
recursive-include src *.cpp *.h
recursive-include src *.cpp *.hpp
<img src="logo.png" width="278" height="100">
# B-ASIC - Better ASIC Toolbox
B-ASIC is an ASIC toolbox for Python 3 that simplifies circuit design and optimization.
## Development
How to build and debug the library during development.
### Prerequisites
The following packages are required in order to build the library:
* cmake 3.8+
* gcc 7+/clang 7+/msvc 16+
* fmtlib
* pybind11 2.3.0+
* python 3.6+
* Python:
* graphviz
* matplotlib
* numpy
* pybind11
* pyside2
* qtpy
* scipy
* setuptools
- cmake 3.8+
- gcc 7+/clang 7+/msvc 16+
- fmtlib
- pybind11 2.3.0+
- python 3.6+
- Python:
- graphviz
- matplotlib
- numpy
- pybind11
- pyside2
- qtpy
- scipy
- setuptools
To build a binary distribution, the following additional packages are required:
* Python:
* wheel
- Python:
- wheel
To run the test suite, the following additional packages are required:
* Python:
* pytest
* pytest-cov (for testing with coverage)
- Python:
- pytest
- pytest-cov (for testing with coverage)
To generate the documentation, the following additional packages are required:
* doxygen
- doxygen
### Using CMake directly
How to build using CMake.
#### Configuring
In `B-ASIC`:
```
mkdir build
cd build
......@@ -47,53 +57,73 @@ cmake ..
```
#### Building (Debug)
In `B-ASIC/build`:
```
cmake --build .
```
The output gets written to `B-ASIC/build/lib`.
#### Building (Release)
In `B-ASIC/build`:
```
cmake --build . --config Release
```
The output gets written to `B-ASIC/build/lib`.
### Using setuptools to create a package
How to create a package using setuptools that can be installed using pip.
#### Setup (Binary distribution)
In `B-ASIC`:
```
python3 setup.py bdist_wheel
```
The output gets written to `B-ASIC/dist/b_asic-<version>-<python_tag>-<abi_tag>-<platform_tag>.whl`.
#### Setup (Source distribution)
In `B-ASIC`:
```
python3 setup.py sdist
```
The output gets written to `B-ASIC/dist/b-asic-<version>.tar.gz`.
#### Installation (Binary distribution)
In `B-ASIC/dist`:
```
pip install b_asic-<version>-<python_tag>-<abi_tag>-<platform_tag>.whl
```
#### Installation (Source distribution)
In `B-ASIC/dist`:
```
pip install b-asic-<version>.tar.gz
```
### Running tests
How to run the tests using pytest in a virtual environment.
#### Linux/OS X
In `B-ASIC`:
```
python3 -m venv env
source env/bin/activate
......@@ -102,7 +132,9 @@ pytest
```
#### Windows
In `B-ASIC` (as admin):
```
python3 -m venv env
.\env\Scripts\activate.bat
......@@ -111,26 +143,33 @@ pytest
```
#### Test with coverage
```
pytest --cov=b_asic --cov-report html test
```
### Generating documentation
In `B-ASIC`:
```
doxygen
```
The output gets written to `B-ASIC/doc`.
## Usage
How to build and use the library as a user.
### Installation
```
pip install b_asic
```
### Importing
```
python3
>>> import b_asic as asic
......@@ -138,5 +177,6 @@ python3
```
## License
B-ASIC is distributed under the MIT license.
See the included LICENSE file for more information.
include(FetchContent)
add_subdirectory(fmt)
add_subdirectory(pybind11)
message(STATUS "Fetching fmt...")
FetchContent_Declare(fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt
GIT_TAG d141cdbeb0fb422a3fb7173b285fd38e0d1772dc # 8.0.1
)
FetchContent_MakeAvailable(fmt)
add_library(dependency_fmt INTERFACE)
target_include_directories(dependency_fmt SYSTEM INTERFACE $<TARGET_PROPERTY:fmt-header-only,INTERFACE_INCLUDE_DIRECTORIES>)
target_link_libraries(dependency_fmt INTERFACE fmt-header-only)
message(STATUS "Fetching pybind11...")
FetchContent_Declare(pybind11
GIT_REPOSITORY https://github.com/pybind/pybind11
GIT_TAG f7b499615e14d70ab098a20deb0cdb3889998a1a # 2.8.1
)
FetchContent_MakeAvailable(pybind11)
......@@ -8,4 +8,4 @@ or to be used as a refererence for future development.
This folder contains a C++ implementation of the Simulation class designed
using Object-Oriented Programming, as opposed to the current version that uses
Data-Oriented Design. They are functionally identical, but use different
styles of programming and have different performance characteristics.
\ No newline at end of file
styles of programming and have different performance characteristics.
#ifndef ASIC_SIMULATION_CORE_OPERATIONS_HPP
#define ASIC_SIMULATION_CORE_OPERATIONS_HPP
#include "../debug.hpp"
#include "../number.hpp"
#include "operation.hpp"
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <stdexcept>
#include <utility>
namespace asic {
class constant_operation final : public abstract_operation {
public:
constant_operation(result_key key, number value)
: abstract_operation(std::move(key))
, m_value(value) {}
[[nodiscard]] std::size_t output_count() const noexcept final {
return 1;
}
private:
[[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const&) const final {
ASIC_DEBUG_MSG("Evaluating constant.");
return m_value;
}
number m_value;
};
class addition_operation final : public binary_operation {
public:
explicit addition_operation(result_key key)
: binary_operation(std::move(key)) {}
[[nodiscard]] std::size_t output_count() const noexcept final {
return 1;
}
private:
[[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const& context) const final {
ASIC_DEBUG_MSG("Evaluating addition.");
return this->evaluate_lhs(context) + this->evaluate_rhs(context);
}
};
class subtraction_operation final : public binary_operation {
public:
explicit subtraction_operation(result_key key)
: binary_operation(std::move(key)) {}
[[nodiscard]] std::size_t output_count() const noexcept final {
return 1;
}
private:
[[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const& context) const final {
ASIC_DEBUG_MSG("Evaluating subtraction.");
return this->evaluate_lhs(context) - this->evaluate_rhs(context);
}
};
class multiplication_operation final : public binary_operation {
public:
explicit multiplication_operation(result_key key)
: binary_operation(std::move(key)) {}
[[nodiscard]] std::size_t output_count() const noexcept final {
return 1;
}
private:
[[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const& context) const final {
ASIC_DEBUG_MSG("Evaluating multiplication.");
return this->evaluate_lhs(context) * this->evaluate_rhs(context);
}
};
class division_operation final : public binary_operation {
public:
explicit division_operation(result_key key)
: binary_operation(std::move(key)) {}
[[nodiscard]] std::size_t output_count() const noexcept final {
return 1;
}
private:
[[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const& context) const final {
ASIC_DEBUG_MSG("Evaluating division.");
return this->evaluate_lhs(context) / this->evaluate_rhs(context);
}
};
class min_operation final : public binary_operation {
public:
explicit min_operation(result_key key)
: binary_operation(std::move(key)) {}
[[nodiscard]] std::size_t output_count() const noexcept final {
return 1;
}
private:
[[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const& context) const final {
ASIC_DEBUG_MSG("Evaluating min.");
auto const lhs = this->evaluate_lhs(context);
if (lhs.imag() != 0) {
throw std::runtime_error{"Min does not support complex numbers."};
}
auto const rhs = this->evaluate_rhs(context);
if (rhs.imag() != 0) {
throw std::runtime_error{"Min does not support complex numbers."};
}
return std::min(lhs.real(), rhs.real());
}
};
class max_operation final : public binary_operation {
public:
explicit max_operation(result_key key)
: binary_operation(std::move(key)) {}
[[nodiscard]] std::size_t output_count() const noexcept final {
return 1;
}
private:
[[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const& context) const final {
ASIC_DEBUG_MSG("Evaluating max.");
auto const lhs = this->evaluate_lhs(context);
if (lhs.imag() != 0) {
throw std::runtime_error{"Max does not support complex numbers."};
}
auto const rhs = this->evaluate_rhs(context);
if (rhs.imag() != 0) {
throw std::runtime_error{"Max does not support complex numbers."};
}
return std::max(lhs.real(), rhs.real());
}
};
class square_root_operation final : public unary_operation {
public:
explicit square_root_operation(result_key key)
: unary_operation(std::move(key)) {}
[[nodiscard]] std::size_t output_count() const noexcept final {
return 1;
}
private:
[[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const& context) const final {
ASIC_DEBUG_MSG("Evaluating sqrt.");
return std::sqrt(this->evaluate_input(context));
}
};
class complex_conjugate_operation final : public unary_operation {
public:
explicit complex_conjugate_operation(result_key key)
: unary_operation(std::move(key)) {}
[[nodiscard]] std::size_t output_count() const noexcept final {
return 1;
}
private:
[[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const& context) const final {
ASIC_DEBUG_MSG("Evaluating conj.");
return std::conj(this->evaluate_input(context));
}
};
class absolute_operation final : public unary_operation {
public:
explicit absolute_operation(result_key key)
: unary_operation(std::move(key)) {}
[[nodiscard]] std::size_t output_count() const noexcept final {
return 1;
}
private:
[[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const& context) const final {
ASIC_DEBUG_MSG("Evaluating abs.");
return std::abs(this->evaluate_input(context));
}
};
class constant_multiplication_operation final : public unary_operation {
public:
constant_multiplication_operation(result_key key, number value)
: unary_operation(std::move(key))
, m_value(value) {}
[[nodiscard]] std::size_t output_count() const noexcept final {
return 1;
}
private:
[[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const& context) const final {
ASIC_DEBUG_MSG("Evaluating cmul.");
return this->evaluate_input(context) * m_value;
}
number m_value;
};
class butterfly_operation final : public binary_operation {
public:
explicit butterfly_operation(result_key key)
: binary_operation(std::move(key)) {}
[[nodiscard]] std::size_t output_count() const noexcept final {
return 2;
}
private:
[[nodiscard]] number evaluate_output_impl(std::size_t index, evaluation_context const& context) const final {
ASIC_DEBUG_MSG("Evaluating bfly.");
if (index == 0) {
return this->evaluate_lhs(context) + this->evaluate_rhs(context);
}
return this->evaluate_lhs(context) - this->evaluate_rhs(context);
}
};
} // namespace asic
#endif // ASIC_SIMULATION_CORE_OPERATIONS_HPP
#include "custom_operation.h"
#include "custom_operation.hpp"
#define NOMINMAX
#include <pybind11/stl.h>
namespace py = pybind11;
namespace asic {