Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • da/B-ASIC
  • lukja239/B-ASIC
  • robal695/B-ASIC
3 results
Show changes
Showing
with 1802 additions and 43 deletions
......@@ -3,33 +3,115 @@ B-ASIC Simulation Module.
TODO: More info.
"""
import numpy as np
from collections import defaultdict
from numbers import Number
from typing import List
from typing import List, Dict, DefaultDict, Callable, Sequence, Mapping, Union, Optional, MutableSequence, MutableMapping
from b_asic.operation import ResultKey, ResultMap, MutableResultMap, MutableDelayMap
from b_asic.signal_flow_graph import SFG
ResultArrayMap = Mapping[ResultKey, Sequence[Number]]
MutableResultArrayMap = MutableMapping[ResultKey, MutableSequence[Number]]
InputFunction = Callable[[int], Number]
InputProvider = Union[Number, Sequence[Number], InputFunction]
class OperationState:
"""Simulation state of an operation.
class Simulation:
"""Simulation.
TODO: More info.
"""
output_values: List[Number]
iteration: int
_sfg: SFG
_results: MutableResultArrayMap
_delays: MutableDelayMap
_iteration: int
_input_functions: Sequence[InputFunction]
_input_length: Optional[int]
def __init__(self):
self.output_values = []
self.iteration = 0
def __init__(self, sfg: SFG, input_providers: Optional[Sequence[Optional[InputProvider]]] = None):
self._sfg = sfg
self._results = defaultdict(list)
self._delays = {}
self._iteration = 0
self._input_functions = [lambda _: 0 for _ in range(self._sfg.input_count)]
self._input_length = None
if input_providers is not None:
self.set_inputs(input_providers)
def set_input(self, index: int, input_provider: InputProvider) -> None:
"""Set the input function used to get values for the specific input at the given index to the internal SFG."""
if index < 0 or index >= len(self._input_functions):
raise IndexError(
f"Input index out of range (expected 0-{len(self._input_functions) - 1}, got {index})")
if callable(input_provider):
self._input_functions[index] = input_provider
elif isinstance(input_provider, Number):
self._input_functions[index] = lambda _: input_provider
else:
if self._input_length is None:
self._input_length = len(input_provider)
elif self._input_length != len(input_provider):
raise ValueError(f"Inconsistent input length for simulation (was {self._input_length}, got {len(input_provider)})")
self._input_functions[index] = lambda n: input_provider[n]
class SimulationState:
"""Simulation state.
TODO: More info.
"""
def set_inputs(self, input_providers: Sequence[Optional[InputProvider]]) -> None:
"""Set the input functions used to get values for the inputs to the internal SFG."""
if len(input_providers) != self._sfg.input_count:
raise ValueError(f"Wrong number of inputs supplied to simulation (expected {self._sfg.input_count}, got {len(input_providers)})")
for index, input_provider in enumerate(input_providers):
if input_provider is not None:
self.set_input(index, input_provider)
def step(self, save_results: bool = True, bits_override: Optional[int] = None, truncate: bool = True) -> Sequence[Number]:
"""Run one iteration of the simulation and return the resulting output values."""
return self.run_for(1, save_results, bits_override, truncate)
def run_until(self, iteration: int, save_results: bool = True, bits_override: Optional[int] = None, truncate: bool = True) -> Sequence[Number]:
"""Run the simulation until its iteration is greater than or equal to the given iteration
and return the output values of the last iteration.
"""
result = []
while self._iteration < iteration:
input_values = [self._input_functions[i](self._iteration) for i in range(self._sfg.input_count)]
results = {}
result = self._sfg.evaluate_outputs(input_values, results, self._delays, "", bits_override, truncate)
if save_results:
for key, value in results.items():
self._results[key].append(value)
self._iteration += 1
return result
def run_for(self, iterations: int, save_results: bool = True, bits_override: Optional[int] = None, truncate: bool = True) -> Sequence[Number]:
"""Run a given number of iterations of the simulation and return the output values of the last iteration."""
return self.run_until(self._iteration + iterations, save_results, bits_override, truncate)
def run(self, save_results: bool = True, bits_override: Optional[int] = None, truncate: bool = True) -> Sequence[Number]:
"""Run the simulation until the end of its input arrays and return the output values of the last iteration."""
if self._input_length is None:
raise IndexError("Tried to run unlimited simulation")
return self.run_until(self._input_length, save_results, bits_override, truncate)
@property
def iteration(self) -> int:
"""Get the current iteration number of the simulation."""
return self._iteration
# operation_states: Dict[OperationId, OperationState]
iteration: int
@property
def results(self) -> ResultArrayMap:
"""Get a mapping from result keys to numpy arrays containing all results, including intermediate values,
calculated for each iteration up until now that was run with save_results enabled.
The mapping is indexed using the key() method of Operation with the appropriate output index.
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]}
"""
return {key: np.array(value) for key, value in self._results.items()}
def __init__(self):
self.operation_states = {}
self.iteration = 0
def clear_results(self) -> None:
"""Clear all results that were saved until now."""
self._results.clear()
# TODO: More stuff.
def clear_state(self) -> None:
"""Clear all current state of the simulation, except for the results and iteration."""
self._delays.clear()
"""@package docstring
B-ASIC Special Operations Module.
TODO: More info.
"""
from numbers import Number
from typing import Optional, Sequence
from b_asic.operation import AbstractOperation, ResultKey, DelayMap, MutableResultMap, MutableDelayMap
from b_asic.graph_component import Name, TypeName
from b_asic.port import SignalSourceProvider
class Input(AbstractOperation):
"""Input operation.
TODO: More info.
"""
def __init__(self, name: Name = ""):
super().__init__(input_count=0, output_count=1, name=name)
self.set_param("value", 0)
@classmethod
def type_name(cls) -> TypeName:
return "in"
def evaluate(self):
return self.param("value")
@property
def value(self) -> Number:
"""Get the current value of this input."""
return self.param("value")
@value.setter
def value(self, value: Number) -> None:
"""Set the current value of this input."""
self.set_param("value", value)
class Output(AbstractOperation):
"""Output operation.
TODO: More info.
"""
def __init__(self, src0: Optional[SignalSourceProvider] = None, name: Name = ""):
super().__init__(input_count=1, output_count=0,
name=name, input_sources=[src0])
@classmethod
def type_name(cls) -> TypeName:
return "out"
def evaluate(self, _):
return None
class Delay(AbstractOperation):
"""Unit delay operation.
TODO: More info.
"""
def __init__(self, src0: Optional[SignalSourceProvider] = None, initial_value: Number = 0, name: Name = ""):
super().__init__(input_count=1, output_count=1,
name=name, input_sources=[src0])
self.set_param("initial_value", initial_value)
@classmethod
def type_name(cls) -> TypeName:
return "t"
def evaluate(self, a):
return self.param("initial_value")
def current_output(self, index: int, delays: Optional[DelayMap] = None, prefix: str = "") -> Optional[Number]:
if delays is not None:
return delays.get(self.key(index, prefix), self.param("initial_value"))
return self.param("initial_value")
def evaluate_output(self, index: int, input_values: Sequence[Number], results: Optional[MutableResultMap] = None, delays: Optional[MutableDelayMap] = None, prefix: str = "", bits_override: Optional[int] = None, truncate: bool = True) -> Number:
if index != 0:
raise IndexError(
f"Output index out of range (expected 0-0, got {index})")
if len(input_values) != 1:
raise ValueError(
f"Wrong number of inputs supplied to SFG for evaluation (expected 1, got {len(input_values)})")
key = self.key(index, prefix)
value = self.param("initial_value")
if delays is not None:
value = delays.get(key, value)
delays[key] = self.truncate_inputs(input_values, bits_override)[0] if truncate else input_values[0]
if results is not None:
results[key] = value
return value
@property
def initial_value(self) -> Number:
"""Get the initial value of this delay."""
return self.param("initial_value")
@initial_value.setter
def initial_value(self, value: Number) -> None:
"""Set the initial value of this delay."""
self.set_param("initial_value", value)
# Legacy files
This folder contains currently unused code that is kept for acedemic purposes,
or to be used as a refererence for future development.
## simulation_oop
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
#ifndef ASIC_SIMULATION_CORE_OPERATIONS_H
#define ASIC_SIMULATION_CORE_OPERATIONS_H
#define NOMINMAX
#include "../debug.h"
#include "../number.h"
#include "operation.h"
#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_H
\ No newline at end of file
#include "custom_operation.h"
#include <pybind11/stl.h>
namespace py = pybind11;
namespace asic {
custom_operation::custom_operation(result_key key, pybind11::object evaluate_output, pybind11::object truncate_input,
std::size_t output_count)
: nary_operation(std::move(key))
, m_evaluate_output(std::move(evaluate_output))
, m_truncate_input(std::move(truncate_input))
, m_output_count(output_count) {}
std::size_t custom_operation::output_count() const noexcept {
return m_output_count;
}
number custom_operation::evaluate_output_impl(std::size_t index, evaluation_context const& context) const {
using namespace pybind11::literals;
auto input_values = this->evaluate_inputs(context);
return m_evaluate_output(index, std::move(input_values), "truncate"_a = false).cast<number>();
}
number custom_operation::truncate_input(std::size_t index, number value, std::size_t bits) const {
return m_truncate_input(index, value, bits).cast<number>();
}
} // namespace asic
\ No newline at end of file
#ifndef ASIC_SIMULATION_CUSTOM_OPERATION_H
#define ASIC_SIMULATION_CUSTOM_OPERATION_H
#include "../algorithm.h"
#include "../debug.h"
#include "../number.h"
#include "operation.h"
#include <cstddef>
#include <fmt/format.h>
#include <functional>
#include <pybind11/pybind11.h>
#include <stdexcept>
#include <utility>
namespace asic {
class custom_operation final : public nary_operation {
public:
custom_operation(result_key key, pybind11::object evaluate_output, pybind11::object truncate_input, std::size_t output_count);
[[nodiscard]] std::size_t output_count() const noexcept final;
private:
[[nodiscard]] number evaluate_output_impl(std::size_t index, evaluation_context const& context) const final;
[[nodiscard]] number truncate_input(std::size_t index, number value, std::size_t bits) const final;
pybind11::object m_evaluate_output;
pybind11::object m_truncate_input;
std::size_t m_output_count;
};
} // namespace asic
#endif // ASIC_SIMULATION_CUSTOM_OPERATION_H
\ No newline at end of file
#include "operation.h"
#include "../debug.h"
#include <pybind11/pybind11.h>
namespace py = pybind11;
namespace asic {
signal_source::signal_source(std::shared_ptr<const operation> op, std::size_t index, std::optional<std::size_t> bits)
: m_operation(std::move(op))
, m_index(index)
, m_bits(std::move(bits)) {}
signal_source::operator bool() const noexcept {
return static_cast<bool>(m_operation);
}
std::optional<number> signal_source::current_output(delay_map const& delays) const {
ASIC_ASSERT(m_operation);
return m_operation->current_output(m_index, delays);
}
number signal_source::evaluate_output(evaluation_context const& context) const {
ASIC_ASSERT(m_operation);
return m_operation->evaluate_output(m_index, context);
}
std::optional<std::size_t> signal_source::bits() const noexcept {
return m_bits;
}
abstract_operation::abstract_operation(result_key key)
: m_key(std::move(key)) {}
std::optional<number> abstract_operation::current_output(std::size_t, delay_map const&) const {
return std::nullopt;
}
number abstract_operation::evaluate_output(std::size_t index, evaluation_context const& context) const {
ASIC_ASSERT(index < this->output_count());
ASIC_ASSERT(context.results);
auto const key = this->key_of_output(index);
if (auto const it = context.results->find(key); it != context.results->end()) {
if (it->second) {
return *it->second;
}
throw std::runtime_error{"Direct feedback loop detected when evaluating simulation operation."};
}
auto& result = context.results->try_emplace(key, this->current_output(index, *context.delays))
.first->second; // Use a reference to avoid potential iterator invalidation caused by evaluate_output_impl.
auto const value = this->evaluate_output_impl(index, context);
ASIC_ASSERT(&context.results->at(key) == &result);
result = value;
return value;
}
number abstract_operation::truncate_input(std::size_t index, number value, std::size_t bits) const {
if (value.imag() != 0) {
throw py::type_error{
fmt::format("Complex value cannot be truncated to {} bits as requested by the signal connected to input #{}", bits, index)};
}
if (bits > 64) {
throw py::value_error{
fmt::format("Cannot truncate to {} (more than 64) bits as requested by the singal connected to input #{}", bits, index)};
}
return number{static_cast<number::value_type>(static_cast<std::int64_t>(value.real()) & ((std::int64_t{1} << bits) - 1))};
}
result_key const& abstract_operation::key_base() const {
return m_key;
}
result_key abstract_operation::key_of_output(std::size_t index) const {
if (m_key.empty()) {
return fmt::to_string(index);
}
if (this->output_count() == 1) {
return m_key;
}
return fmt::format("{}.{}", m_key, index);
}
unary_operation::unary_operation(result_key key)
: abstract_operation(std::move(key)) {}
void unary_operation::connect(signal_source in) {
m_in = std::move(in);
}
bool unary_operation::connected() const noexcept {
return static_cast<bool>(m_in);
}
signal_source const& unary_operation::input() const noexcept {
return m_in;
}
number unary_operation::evaluate_input(evaluation_context const& context) const {
auto const value = m_in.evaluate_output(context);
auto const bits = context.bits_override.value_or(m_in.bits().value_or(0));
return (context.truncate && bits) ? this->truncate_input(0, value, bits) : value;
}
binary_operation::binary_operation(result_key key)
: abstract_operation(std::move(key)) {}
void binary_operation::connect(signal_source lhs, signal_source rhs) {
m_lhs = std::move(lhs);
m_rhs = std::move(rhs);
}
signal_source const& binary_operation::lhs() const noexcept {
return m_lhs;
}
signal_source const& binary_operation::rhs() const noexcept {
return m_rhs;
}
number binary_operation::evaluate_lhs(evaluation_context const& context) const {
auto const value = m_lhs.evaluate_output(context);
auto const bits = context.bits_override.value_or(m_lhs.bits().value_or(0));
return (context.truncate && bits) ? this->truncate_input(0, value, bits) : value;
}
number binary_operation::evaluate_rhs(evaluation_context const& context) const {
auto const value = m_rhs.evaluate_output(context);
auto const bits = context.bits_override.value_or(m_rhs.bits().value_or(0));
return (context.truncate && bits) ? this->truncate_input(0, value, bits) : value;
}
nary_operation::nary_operation(result_key key)
: abstract_operation(std::move(key)) {}
void nary_operation::connect(std::vector<signal_source> inputs) {
m_inputs = std::move(inputs);
}
span<signal_source const> nary_operation::inputs() const noexcept {
return m_inputs;
}
std::vector<number> nary_operation::evaluate_inputs(evaluation_context const& context) const {
auto values = std::vector<number>{};
values.reserve(m_inputs.size());
for (auto const& input : m_inputs) {
auto const value = input.evaluate_output(context);
auto const bits = context.bits_override.value_or(input.bits().value_or(0));
values.push_back((context.truncate && bits) ? this->truncate_input(0, value, bits) : value);
}
return values;
}
} // namespace asic
\ No newline at end of file
#ifndef ASIC_SIMULATION_OPERATION_H
#define ASIC_SIMULATION_OPERATION_H
#include "../number.h"
#include "../span.h"
#include <cstddef>
#include <cstdint>
#include <fmt/format.h>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
namespace asic {
class operation;
class signal_source;
using result_key = std::string;
using result_map = std::unordered_map<result_key, std::optional<number>>;
using delay_map = std::unordered_map<result_key, number>;
using delay_queue = std::vector<std::pair<result_key, signal_source const*>>;
struct evaluation_context final {
result_map* results;
delay_map* delays;
delay_queue* deferred_delays;
std::optional<std::size_t> bits_override;
bool truncate;
};
class signal_source final {
public:
signal_source() noexcept = default;
signal_source(std::shared_ptr<const operation> op, std::size_t index, std::optional<std::size_t> bits);
[[nodiscard]] explicit operator bool() const noexcept;
[[nodiscard]] std::optional<number> current_output(delay_map const& delays) const;
[[nodiscard]] number evaluate_output(evaluation_context const& context) const;
[[nodiscard]] std::optional<std::size_t> bits() const noexcept;
private:
std::shared_ptr<const operation> m_operation;
std::size_t m_index = 0;
std::optional<std::size_t> m_bits;
};
class operation {
public:
virtual ~operation() = default;
[[nodiscard]] virtual std::size_t output_count() const noexcept = 0;
[[nodiscard]] virtual std::optional<number> current_output(std::size_t index, delay_map const& delays) const = 0;
[[nodiscard]] virtual number evaluate_output(std::size_t index, evaluation_context const& context) const = 0;
};
class abstract_operation : public operation {
public:
explicit abstract_operation(result_key key);
virtual ~abstract_operation() = default;
[[nodiscard]] std::optional<number> current_output(std::size_t, delay_map const&) const override;
[[nodiscard]] number evaluate_output(std::size_t index, evaluation_context const& context) const override;
protected:
[[nodiscard]] virtual number evaluate_output_impl(std::size_t index, evaluation_context const& context) const = 0;
[[nodiscard]] virtual number truncate_input(std::size_t index, number value, std::size_t bits) const;
[[nodiscard]] result_key const& key_base() const;
[[nodiscard]] result_key key_of_output(std::size_t index) const;
private:
result_key m_key;
};
class unary_operation : public abstract_operation {
public:
explicit unary_operation(result_key key);
virtual ~unary_operation() = default;
void connect(signal_source in);
protected:
[[nodiscard]] bool connected() const noexcept;
[[nodiscard]] signal_source const& input() const noexcept;
[[nodiscard]] number evaluate_input(evaluation_context const& context) const;
private:
signal_source m_in;
};
class binary_operation : public abstract_operation {
public:
explicit binary_operation(result_key key);
virtual ~binary_operation() = default;
void connect(signal_source lhs, signal_source rhs);
protected:
[[nodiscard]] signal_source const& lhs() const noexcept;
[[nodiscard]] signal_source const& rhs() const noexcept;
[[nodiscard]] number evaluate_lhs(evaluation_context const& context) const;
[[nodiscard]] number evaluate_rhs(evaluation_context const& context) const;
private:
signal_source m_lhs;
signal_source m_rhs;
};
class nary_operation : public abstract_operation {
public:
explicit nary_operation(result_key key);
virtual ~nary_operation() = default;
void connect(std::vector<signal_source> inputs);
protected:
[[nodiscard]] span<signal_source const> inputs() const noexcept;
[[nodiscard]] std::vector<number> evaluate_inputs(evaluation_context const& context) const;
private:
std::vector<signal_source> m_inputs;
};
} // namespace asic
#endif // ASIC_SIMULATION_OPERATION_H
\ No newline at end of file
#include "signal_flow_graph.h"
#include "../debug.h"
namespace py = pybind11;
namespace asic {
signal_flow_graph_operation::signal_flow_graph_operation(result_key key)
: abstract_operation(std::move(key)) {}
void signal_flow_graph_operation::create(pybind11::handle sfg, added_operation_cache& added) {
ASIC_DEBUG_MSG("Creating SFG.");
for (auto const& [i, op] : enumerate(sfg.attr("output_operations"))) {
ASIC_DEBUG_MSG("Adding output op.");
m_output_operations.emplace_back(this->key_of_output(i)).connect(make_source(op, 0, added, this->key_base()));
}
for (auto const& op : sfg.attr("input_operations")) {
ASIC_DEBUG_MSG("Adding input op.");
if (!m_input_operations.emplace_back(std::dynamic_pointer_cast<input_operation>(make_operation(op, added, this->key_base())))) {
throw py::value_error{"Invalid input operation in SFG."};
}
}
}
std::vector<std::shared_ptr<input_operation>> const& signal_flow_graph_operation::inputs() noexcept {
return m_input_operations;
}
std::size_t signal_flow_graph_operation::output_count() const noexcept {
return m_output_operations.size();
}
number signal_flow_graph_operation::evaluate_output(std::size_t index, evaluation_context const& context) const {
ASIC_DEBUG_MSG("Evaluating SFG.");
return m_output_operations.at(index).evaluate_output(0, context);
}
number signal_flow_graph_operation::evaluate_output_impl(std::size_t, evaluation_context const&) const {
return number{};
}
signal_source signal_flow_graph_operation::make_source(pybind11::handle op, std::size_t input_index, added_operation_cache& added,
std::string_view prefix) {
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>();
auto bits = std::optional<std::size_t>{};
if (!signal.attr("bits").is_none()) {
bits = signal.attr("bits").cast<std::size_t>();
}
return signal_source{make_operation(operation, added, prefix), index, bits};
}
std::shared_ptr<operation> signal_flow_graph_operation::add_signal_flow_graph_operation(pybind11::handle sfg, added_operation_cache& added,
std::string_view prefix, result_key key) {
auto const new_op = add_operation<signal_flow_graph_operation>(sfg, added, std::move(key));
new_op->create(sfg, added);
for (auto&& [i, input] : enumerate(new_op->inputs())) {
input->connect(make_source(sfg, i, added, prefix));
}
return new_op;
}
std::shared_ptr<custom_operation> signal_flow_graph_operation::add_custom_operation(pybind11::handle op, added_operation_cache& added,
std::string_view prefix, result_key key) {
auto const input_count = op.attr("input_count").cast<std::size_t>();
auto const output_count = op.attr("output_count").cast<std::size_t>();
auto const new_op = add_operation<custom_operation>(
op, added, key, op.attr("evaluate_output"), op.attr("truncate_input"), output_count);
auto inputs = std::vector<signal_source>{};
inputs.reserve(input_count);
for (auto const i : range(input_count)) {
inputs.push_back(make_source(op, i, added, prefix));
}
new_op->connect(std::move(inputs));
return new_op;
}
std::shared_ptr<operation> signal_flow_graph_operation::make_operation(pybind11::handle op, added_operation_cache& added,
std::string_view prefix) {
if (auto const it = added.find(op.ptr()); it != added.end()) {
ASIC_ASSERT(it->second);
return it->second;
}
auto const graph_id = op.attr("graph_id").cast<std::string_view>();
auto const type_name = op.attr("type_name")().cast<std::string_view>();
auto key = (prefix.empty()) ? result_key{graph_id} : fmt::format("{}.{}", prefix, graph_id);
if (type_name == "c") {
auto const value = op.attr("value").cast<number>();
return add_operation<constant_operation>(op, added, std::move(key), value);
}
if (type_name == "add") {
return add_binary_operation<addition_operation>(op, added, prefix, std::move(key));
}
if (type_name == "sub") {
return add_binary_operation<subtraction_operation>(op, added, prefix, std::move(key));
}
if (type_name == "mul") {
return add_binary_operation<multiplication_operation>(op, added, prefix, std::move(key));
}
if (type_name == "div") {
return add_binary_operation<division_operation>(op, added, prefix, std::move(key));
}
if (type_name == "min") {
return add_binary_operation<min_operation>(op, added, prefix, std::move(key));
}
if (type_name == "max") {
return add_binary_operation<max_operation>(op, added, prefix, std::move(key));
}
if (type_name == "sqrt") {
return add_unary_operation<square_root_operation>(op, added, prefix, std::move(key));
}
if (type_name == "conj") {
return add_unary_operation<complex_conjugate_operation>(op, added, prefix, std::move(key));
}
if (type_name == "abs") {
return add_unary_operation<absolute_operation>(op, added, prefix, std::move(key));
}
if (type_name == "cmul") {
auto const value = op.attr("value").cast<number>();
return add_unary_operation<constant_multiplication_operation>(op, added, prefix, std::move(key), value);
}
if (type_name == "bfly") {
return add_binary_operation<butterfly_operation>(op, added, prefix, std::move(key));
}
if (type_name == "in") {
return add_operation<input_operation>(op, added, std::move(key));
}
if (type_name == "out") {
return add_unary_operation<output_operation>(op, added, prefix, std::move(key));
}
if (type_name == "t") {
auto const initial_value = op.attr("initial_value").cast<number>();
return add_unary_operation<delay_operation>(op, added, prefix, std::move(key), initial_value);
}
if (type_name == "sfg") {
return add_signal_flow_graph_operation(op, added, prefix, std::move(key));
}
return add_custom_operation(op, added, prefix, std::move(key));
}
} // namespace asic
\ No newline at end of file
#ifndef ASIC_SIMULATION_SIGNAL_FLOW_GRAPH_H
#define ASIC_SIMULATION_SIGNAL_FLOW_GRAPH_H
#include "../algorithm.h"
#include "../debug.h"
#include "../number.h"
#include "core_operations.h"
#include "custom_operation.h"
#include "operation.h"
#include "special_operations.h"
#include <Python.h>
#include <cstddef>
#include <fmt/format.h>
#include <functional>
#include <memory>
#include <pybind11/pybind11.h>
#include <stdexcept>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <vector>
namespace asic {
class signal_flow_graph_operation final : public abstract_operation {
public:
using added_operation_cache = std::unordered_map<PyObject const*, std::shared_ptr<operation>>;
signal_flow_graph_operation(result_key key);
void create(pybind11::handle sfg, added_operation_cache& added);
[[nodiscard]] std::vector<std::shared_ptr<input_operation>> const& inputs() noexcept;
[[nodiscard]] std::size_t output_count() const noexcept final;
[[nodiscard]] number evaluate_output(std::size_t index, evaluation_context const& context) const final;
private:
[[nodiscard]] number evaluate_output_impl(std::size_t index, evaluation_context const& context) const final;
[[nodiscard]] static signal_source make_source(pybind11::handle op, std::size_t input_index, added_operation_cache& added,
std::string_view prefix);
template <typename Operation, typename... Args>
[[nodiscard]] static std::shared_ptr<Operation> add_operation(pybind11::handle op, added_operation_cache& added, Args&&... args) {
return std::static_pointer_cast<Operation>(
added.try_emplace(op.ptr(), std::make_shared<Operation>(std::forward<Args>(args)...)).first->second);
}
template <typename Operation, typename... Args>
[[nodiscard]] static std::shared_ptr<Operation> add_unary_operation(pybind11::handle op, added_operation_cache& added,
std::string_view prefix, Args&&... args) {
auto const new_op = add_operation<Operation>(op, added, std::forward<Args>(args)...);
new_op->connect(make_source(op, 0, added, prefix));
return new_op;
}
template <typename Operation, typename... Args>
[[nodiscard]] static std::shared_ptr<Operation> add_binary_operation(pybind11::handle op, added_operation_cache& added,
std::string_view prefix, Args&&... args) {
auto const new_op = add_operation<Operation>(op, added, std::forward<Args>(args)...);
new_op->connect(make_source(op, 0, added, prefix), make_source(op, 1, added, prefix));
return new_op;
}
[[nodiscard]] static std::shared_ptr<operation> add_signal_flow_graph_operation(pybind11::handle sfg, added_operation_cache& added,
std::string_view prefix, result_key key);
[[nodiscard]] static std::shared_ptr<custom_operation> add_custom_operation(pybind11::handle op, added_operation_cache& added,
std::string_view prefix, result_key key);
[[nodiscard]] static std::shared_ptr<operation> make_operation(pybind11::handle op, added_operation_cache& added,
std::string_view prefix);
std::vector<output_operation> m_output_operations;
std::vector<std::shared_ptr<input_operation>> m_input_operations;
};
} // namespace asic
#endif // ASIC_SIMULATION_SIGNAL_FLOW_GRAPH_H
\ No newline at end of file
#define NOMINMAX
#include "simulation.h"
#include "../debug.h"
namespace py = pybind11;
namespace asic {
simulation::simulation(pybind11::handle sfg, std::optional<std::vector<std::optional<input_provider_t>>> input_providers)
: m_sfg("")
, m_input_functions(sfg.attr("input_count").cast<std::size_t>(), [](iteration_t) -> number { return number{}; }) {
if (input_providers) {
this->set_inputs(std::move(*input_providers));
}
auto added = signal_flow_graph_operation::added_operation_cache{};
m_sfg.create(sfg, added);
}
void simulation::set_input(std::size_t index, input_provider_t 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_t>(&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_t) -> 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_t>(list->size());
} else if (*m_input_length != static_cast<iteration_t>(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_t n) -> number {
return values.at(n);
};
}
}
void simulation::set_inputs(std::vector<std::optional<input_provider_t>> input_providers) {
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::size_t> bits_override, bool truncate) {
return this->run_for(1, save_results, bits_override, truncate);
}
std::vector<number> simulation::run_until(iteration_t iteration, bool save_results, std::optional<std::size_t> bits_override,
bool truncate) {
auto result = std::vector<number>{};
while (m_iteration < iteration) {
ASIC_DEBUG_MSG("Running simulation iteration.");
for (auto&& [input, function] : zip(m_sfg.inputs(), m_input_functions)) {
input->value(function(m_iteration));
}
result.clear();
result.reserve(m_sfg.output_count());
auto results = result_map{};
auto deferred_delays = delay_queue{};
auto context = evaluation_context{};
context.results = &results;
context.delays = &m_delays;
context.deferred_delays = &deferred_delays;
context.bits_override = bits_override;
context.truncate = truncate;
for (auto const i : range(m_sfg.output_count())) {
result.push_back(m_sfg.evaluate_output(i, context));
}
while (!deferred_delays.empty()) {
auto new_deferred_delays = delay_queue{};
context.deferred_delays = &new_deferred_delays;
for (auto const& [key, src] : deferred_delays) {
ASIC_ASSERT(src);
m_delays[key] = src->evaluate_output(context);
}
deferred_delays = std::move(new_deferred_delays);
}
if (save_results) {
for (auto const& [key, value] : results) {
m_results[key].push_back(value.value());
}
}
++m_iteration;
}
return result;
}
std::vector<number> simulation::run_for(iteration_t iterations, bool save_results, std::optional<std::size_t> bits_override,
bool truncate) {
if (iterations > std::numeric_limits<iteration_t>::max() - m_iteration) {
throw py::value_error("Simulation iteration type overflow!");
}
return this->run_until(m_iteration + iterations, save_results, bits_override, truncate);
}
std::vector<number> simulation::run(bool save_results, std::optional<std::size_t> bits_override, bool truncate) {
if (m_input_length) {
return this->run_until(*m_input_length, save_results, bits_override, truncate);
}
throw py::index_error{"Tried to run unlimited simulation"};
}
iteration_t simulation::iteration() const noexcept {
return m_iteration;
}
pybind11::dict simulation::results() const noexcept {
auto results = py::dict{};
for (auto const& [key, values] : m_results) {
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
#ifndef ASIC_SIMULATION_OOP_H
#define ASIC_SIMULATION_OOP_H
#include "../number.h"
#include "core_operations.h"
#include "custom_operation.h"
#include "operation.h"
#include "signal_flow_graph.h"
#include "special_operations.h"
#include <cstddef>
#include <cstdint>
#include <fmt/format.h>
#include <functional>
#include <limits>
#include <memory>
#include <optional>
#include <pybind11/functional.h>
#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <variant>
#include <vector>
namespace asic {
using iteration_t = std::uint32_t;
using result_array_map = std::unordered_map<std::string, std::vector<number>>;
using input_function_t = std::function<number(iteration_t)>;
using input_provider_t = std::variant<number, std::vector<number>, input_function_t>;
class simulation final {
public:
simulation(pybind11::handle sfg, std::optional<std::vector<std::optional<input_provider_t>>> input_providers = std::nullopt);
void set_input(std::size_t index, input_provider_t input_provider);
void set_inputs(std::vector<std::optional<input_provider_t>> input_providers);
[[nodiscard]] std::vector<number> step(bool save_results, std::optional<std::size_t> bits_override, bool truncate);
[[nodiscard]] std::vector<number> run_until(iteration_t iteration, bool save_results, std::optional<std::size_t> bits_override,
bool truncate);
[[nodiscard]] std::vector<number> run_for(iteration_t iterations, bool save_results, std::optional<std::size_t> bits_override,
bool truncate);
[[nodiscard]] std::vector<number> run(bool save_results, std::optional<std::size_t> bits_override, bool truncate);
[[nodiscard]] iteration_t iteration() const noexcept;
[[nodiscard]] pybind11::dict results() const noexcept;
void clear_results() noexcept;
void clear_state() noexcept;
private:
signal_flow_graph_operation m_sfg;
result_array_map m_results;
delay_map m_delays;
iteration_t m_iteration = 0;
std::vector<input_function_t> m_input_functions;
std::optional<iteration_t> m_input_length;
};
} // namespace asic
#endif // ASIC_SIMULATION_OOP_H
\ No newline at end of file
#include "special_operations.h"
#include "../debug.h"
namespace asic {
input_operation::input_operation(result_key key)
: unary_operation(std::move(key)) {}
std::size_t input_operation::output_count() const noexcept {
return 1;
}
number input_operation::value() const noexcept {
return m_value;
}
void input_operation::value(number value) noexcept {
m_value = value;
}
number input_operation::evaluate_output_impl(std::size_t, evaluation_context const& context) const {
ASIC_DEBUG_MSG("Evaluating input.");
if (this->connected()) {
return this->evaluate_input(context);
}
return m_value;
}
output_operation::output_operation(result_key key)
: unary_operation(std::move(key)) {}
std::size_t output_operation::output_count() const noexcept {
return 1;
}
number output_operation::evaluate_output_impl(std::size_t, evaluation_context const& context) const {
ASIC_DEBUG_MSG("Evaluating output.");
return this->evaluate_input(context);
}
delay_operation::delay_operation(result_key key, number initial_value)
: unary_operation(std::move(key))
, m_initial_value(initial_value) {}
std::size_t delay_operation::output_count() const noexcept {
return 1;
}
std::optional<number> delay_operation::current_output(std::size_t index, delay_map const& delays) const {
auto const key = this->key_of_output(index);
if (auto const it = delays.find(key); it != delays.end()) {
return it->second;
}
return m_initial_value;
}
number delay_operation::evaluate_output(std::size_t index, evaluation_context const& context) const {
ASIC_DEBUG_MSG("Evaluating delay.");
ASIC_ASSERT(index == 0);
ASIC_ASSERT(context.results);
ASIC_ASSERT(context.delays);
ASIC_ASSERT(context.deferred_delays);
auto key = this->key_of_output(index);
auto const value = context.delays->try_emplace(key, m_initial_value).first->second;
auto const& [it, inserted] = context.results->try_emplace(key, value);
if (inserted) {
context.deferred_delays->emplace_back(std::move(key), &this->input());
return value;
}
return it->second.value();
}
[[nodiscard]] number delay_operation::evaluate_output_impl(std::size_t, evaluation_context const&) const {
return number{};
}
} // namespace asic
#ifndef ASIC_SIMULATION_SPECIAL_OPERATIONS_H
#define ASIC_SIMULATION_SPECIAL_OPERATIONS_H
#include "../debug.h"
#include "../number.h"
#include "operation.h"
#include <cassert>
#include <cstddef>
#include <utility>
namespace asic {
class input_operation final : public unary_operation {
public:
explicit input_operation(result_key key);
[[nodiscard]] std::size_t output_count() const noexcept final;
[[nodiscard]] number value() const noexcept;
void value(number value) noexcept;
private:
[[nodiscard]] number evaluate_output_impl(std::size_t index, evaluation_context const& context) const final;
number m_value{};
};
class output_operation final : public unary_operation {
public:
explicit output_operation(result_key key);
[[nodiscard]] std::size_t output_count() const noexcept final;
private:
[[nodiscard]] number evaluate_output_impl(std::size_t index, evaluation_context const& context) const final;
};
class delay_operation final : public unary_operation {
public:
delay_operation(result_key key, number initial_value);
[[nodiscard]] std::size_t output_count() const noexcept final;
[[nodiscard]] std::optional<number> current_output(std::size_t index, delay_map const& delays) const final;
[[nodiscard]] number evaluate_output(std::size_t index, evaluation_context const& context) const final;
private:
[[nodiscard]] number evaluate_output_impl(std::size_t index, evaluation_context const& context) const final;
number m_initial_value;
};
} // namespace asic
#endif // ASIC_SIMULATION_SPECIAL_OPERATIONS_H
\ No newline at end of file
......@@ -6,7 +6,7 @@ import setuptools
from setuptools import Extension
from setuptools.command.build_ext import build_ext
CMAKE_EXE = os.environ.get('CMAKE_EXE', shutil.which('cmake'))
CMAKE_EXE = os.environ.get("CMAKE_EXE", shutil.which("cmake"))
class CMakeExtension(Extension):
def __init__(self, name, sourcedir = ""):
......@@ -25,6 +25,7 @@ class CMakeBuild(build_ext):
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=true",
"-DCMAKE_BUILD_TYPE=" + cmake_build_type,
"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=" + cmake_output_dir,
"-DPYTHON_EXECUTABLE=" + sys.executable,
......@@ -36,9 +37,9 @@ class CMakeBuild(build_ext):
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}")
......@@ -71,10 +72,12 @@ setuptools.setup(
install_requires = [
"pybind11>=2.3.0",
"numpy",
"install_qt_binding"
"pyside2",
"graphviz",
"matplotlib"
],
packages = ["b_asic"],
ext_modules = [CMakeExtension("b_asic")],
cmdclass = {"build_ext": CMakeBuild},
zip_safe = False
)
\ No newline at end of file
)
small_logo.png

39.5 KiB

#ifndef ASIC_ALGORITHM_H
#define ASIC_ALGORITHM_H
#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>
struct range_view final {
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;
}
iterator m_begin;
sentinel m_end;
};
template <typename Range, typename Iterator, typename Sentinel>
struct enumerate_view final {
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;
};
constexpr iterator begin() const {
return iterator{std::begin(m_range), 0};
}
constexpr sentinel end() const {
return std::end(m_range);
}
Range m_range;
};
template <typename Range1, typename Range2, typename Iterator1, typename Iterator2, typename Sentinel1, typename Sentinel2>
struct zip_view final {
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;
};
constexpr iterator begin() const {
return iterator{std::begin(m_range1), std::begin(m_range2)};
}
constexpr sentinel end() const {
return sentinel{std::end(m_range1), std::end(m_range2)};
}
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_H
\ No newline at end of file
#ifndef ASIC_DEBUG_H
#define ASIC_DEBUG_H
#ifndef NDEBUG
#define ASIC_ENABLE_DEBUG_LOGGING 1
#define ASIC_ENABLE_ASSERTS 1
#else
#define ASIC_ENABLE_DEBUG_LOGGING 0
#define ASIC_ENABLE_ASSERTS 0
#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 <filesystem>
#include <cstdlib>
#include <cstdio>
#include <string_view>
#include <fmt/format.h>
#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_H
\ No newline at end of file
#include <pybind11/pybind11.h>
namespace py = pybind11;
namespace asic {
int add(int a, int b) {
return a + b;
}
int sub(int a, int b) {
return a - b;
}
} // namespace asic
PYBIND11_MODULE(_b_asic, m) {
m.doc() = "Better ASIC Toolbox Extension Module.";
m.def("add", &asic::add, "A function which adds two numbers.", py::arg("a"), py::arg("b"));
m.def("sub", &asic::sub, "A function which subtracts two numbers.", py::arg("a"), py::arg("b"));
#include "simulation.h"
#include <pybind11/pybind11.h>
namespace py = pybind11;
PYBIND11_MODULE(_b_asic, module) {
module.doc() = "Better ASIC Toolbox Extension Module.";
asic::define_simulation_class(module);
}
\ No newline at end of file
#ifndef ASIC_NUMBER_H
#define ASIC_NUMBER_H
#include <complex>
#include <pybind11/complex.h>
namespace asic {
using number = std::complex<double>;
} // namespace asic
#endif // ASIC_NUMBER_H
\ No newline at end of file