init commit
This commit is contained in:
+221
@@ -0,0 +1,221 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[codz]
|
||||
*$py.class
|
||||
build*
|
||||
# C extensions
|
||||
*.so
|
||||
*.npy
|
||||
snapshots/
|
||||
test_results*
|
||||
meta.json
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
tests/artifacts/
|
||||
tests/tests/artifacts/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
# Pipfile.lock
|
||||
|
||||
# UV
|
||||
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# uv.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
# poetry.lock
|
||||
# poetry.toml
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
||||
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
||||
# pdm.lock
|
||||
# pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# pixi
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
||||
# pixi.lock
|
||||
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
||||
# in the .venv directory. It is recommended not to include this directory in version control.
|
||||
.pixi
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# Redis
|
||||
*.rdb
|
||||
*.aof
|
||||
*.pid
|
||||
|
||||
# RabbitMQ
|
||||
mnesia/
|
||||
rabbitmq/
|
||||
rabbitmq-data/
|
||||
|
||||
# ActiveMQ
|
||||
activemq-data/
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.envrc
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
# .idea/
|
||||
|
||||
# Abstra
|
||||
# Abstra is an AI-powered process automation framework.
|
||||
# Ignore directories containing user credentials, local state, and settings.
|
||||
# Learn more at https://abstra.io/docs
|
||||
.abstra/
|
||||
|
||||
# Visual Studio Code
|
||||
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
||||
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
||||
# you could uncomment the following to ignore the entire vscode folder
|
||||
# .vscode/
|
||||
|
||||
# Ruff stuff:
|
||||
.ruff_cache/
|
||||
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
|
||||
# Marimo
|
||||
marimo/_static/
|
||||
marimo/_lsp/
|
||||
__marimo__/
|
||||
|
||||
# Streamlit
|
||||
.streamlit/secrets.toml
|
||||
@@ -0,0 +1,83 @@
|
||||
CXX := g++
|
||||
CXXFLAGS := -std=c++20 -O2 -Wall -Wextra -Wpedantic -pthread
|
||||
LDFLAGS := -pthread -lrt
|
||||
ORCH_LDFLAGS := $(LDFLAGS) -lusb-1.0
|
||||
|
||||
BUILD_DIR := build
|
||||
BIN_DIR := $(BUILD_DIR)/bin
|
||||
|
||||
INCLUDES := \
|
||||
-Idata_acq_and_processing/common_cpp/ipc/include \
|
||||
-Idata_acq_and_processing/common_cpp/config/include \
|
||||
-Idata_acq_and_processing/third_party \
|
||||
-Idata_acq_and_processing/third_party/eigen \
|
||||
-Idata_acq_and_processing/sweep_orchestrator/include \
|
||||
-Idata_acq_and_processing/sweep_orchestrator/device_drivers/interfaces \
|
||||
-Idata_acq_and_processing/sweep_orchestrator/device_drivers/radar \
|
||||
-Idata_acq_and_processing/sweep_orchestrator/device_drivers/switches \
|
||||
-Idata_acq_and_processing/preprocessing/calibration_master/include \
|
||||
-Idata_acq_and_processing/preprocessing/reference_master/include \
|
||||
-Idata_acq_and_processing/preprocessing/data_preprocessor/include \
|
||||
-Idata_acq_and_processing/processing/processors/include \
|
||||
-Idata_acq_and_processing/processing/data_processor/include
|
||||
|
||||
COMMON_SOURCES := \
|
||||
data_acq_and_processing/common_cpp/ipc/src/shm_ring.cpp \
|
||||
data_acq_and_processing/common_cpp/ipc/src/shared_types.cpp \
|
||||
data_acq_and_processing/common_cpp/config/src/run_config.cpp
|
||||
|
||||
ORCH_SOURCES := \
|
||||
data_acq_and_processing/sweep_orchestrator/device_drivers/radar/librevna/librevna_minimal_driver_lifecycle.cpp \
|
||||
data_acq_and_processing/sweep_orchestrator/device_drivers/radar/librevna/librevna_minimal_driver_transport.cpp \
|
||||
data_acq_and_processing/sweep_orchestrator/device_drivers/radar/librevna/librevna_minimal_driver_protocol.cpp \
|
||||
data_acq_and_processing/sweep_orchestrator/device_drivers/switches/h7992_minimal_driver.cpp \
|
||||
data_acq_and_processing/sweep_orchestrator/device_drivers/switches/hmc349a_minimal_driver.cpp \
|
||||
data_acq_and_processing/sweep_orchestrator/src/sweep_plan.cpp \
|
||||
data_acq_and_processing/sweep_orchestrator/src/sweep_orchestrator.cpp \
|
||||
data_acq_and_processing/sweep_orchestrator/src/main.cpp
|
||||
|
||||
PREPROC_SOURCES := \
|
||||
data_acq_and_processing/preprocessing/calibration_master/src/calibration_master.cpp \
|
||||
data_acq_and_processing/preprocessing/calibration_master/src/through_calibrator.cpp \
|
||||
data_acq_and_processing/preprocessing/reference_master/src/reference_master.cpp \
|
||||
data_acq_and_processing/preprocessing/data_preprocessor/src/data_preprocessor.cpp \
|
||||
data_acq_and_processing/preprocessing/data_preprocessor/src/main.cpp
|
||||
|
||||
PROCESSOR_SOURCES := \
|
||||
data_acq_and_processing/processing/processors/src/bscan_processor.cpp \
|
||||
data_acq_and_processing/processing/processors/src/passthrough_processor.cpp \
|
||||
data_acq_and_processing/processing/data_processor/src/processing_live_config.cpp \
|
||||
data_acq_and_processing/processing/data_processor/src/data_processor.cpp \
|
||||
data_acq_and_processing/processing/data_processor/src/main.cpp
|
||||
|
||||
SWEEP_ORCH_OBJS := $(addprefix $(BUILD_DIR)/,$(COMMON_SOURCES:.cpp=.o) $(ORCH_SOURCES:.cpp=.o))
|
||||
PREPROCESSOR_OBJS := $(addprefix $(BUILD_DIR)/,$(COMMON_SOURCES:.cpp=.o) $(PREPROC_SOURCES:.cpp=.o))
|
||||
DATA_PROCESSOR_OBJS := $(addprefix $(BUILD_DIR)/,$(COMMON_SOURCES:.cpp=.o) $(PROCESSOR_SOURCES:.cpp=.o))
|
||||
|
||||
TARGETS := \
|
||||
$(BIN_DIR)/sweep_orchestrator \
|
||||
$(BIN_DIR)/data_preprocessor \
|
||||
$(BIN_DIR)/data_processor
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
$(BIN_DIR)/sweep_orchestrator: $(SWEEP_ORCH_OBJS)
|
||||
@mkdir -p $(BIN_DIR)
|
||||
$(CXX) $(SWEEP_ORCH_OBJS) -o $@ $(ORCH_LDFLAGS)
|
||||
|
||||
$(BIN_DIR)/data_preprocessor: $(PREPROCESSOR_OBJS)
|
||||
@mkdir -p $(BIN_DIR)
|
||||
$(CXX) $(PREPROCESSOR_OBJS) -o $@ $(LDFLAGS)
|
||||
|
||||
$(BIN_DIR)/data_processor: $(DATA_PROCESSOR_OBJS)
|
||||
@mkdir -p $(BIN_DIR)
|
||||
$(CXX) $(DATA_PROCESSOR_OBJS) -o $@ $(LDFLAGS)
|
||||
|
||||
$(BUILD_DIR)/%.o: %.cpp
|
||||
@mkdir -p $(dir $@)
|
||||
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)
|
||||
@@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "shared_types.hpp"
|
||||
|
||||
namespace radar::config {
|
||||
|
||||
enum class DriverMode {
|
||||
Mock,
|
||||
Native,
|
||||
};
|
||||
|
||||
enum class SwitchDriverKind {
|
||||
H7992,
|
||||
HMC349A,
|
||||
};
|
||||
|
||||
struct RadarSweepSettings {
|
||||
// Frequency sweep bounds and acquisition settings.
|
||||
float start_hz = 1'000'000.0F;
|
||||
float stop_hz = 6'000'000'000.0F;
|
||||
std::uint32_t points = 501;
|
||||
float if_bandwidth_hz = 1'000.0F;
|
||||
float power_dbm = -10.0F;
|
||||
};
|
||||
|
||||
struct RadarConfig {
|
||||
// Radar device identity and runtime mode.
|
||||
std::string model = "librevna";
|
||||
std::string serial{};
|
||||
DriverMode driver_mode = DriverMode::Mock;
|
||||
float mock_signal_hz = 5'000'000.0F;
|
||||
RadarSweepSettings sweep{};
|
||||
};
|
||||
|
||||
struct SwitchConfig {
|
||||
// One RF switch attached to a specific radar port.
|
||||
std::string name{};
|
||||
DriverMode driver_mode = DriverMode::Mock;
|
||||
SwitchDriverKind driver_kind = SwitchDriverKind::H7992;
|
||||
std::uint32_t radar_port = 0;
|
||||
std::uint32_t positions = 4;
|
||||
std::uint32_t default_position = 0;
|
||||
std::string gpio_chip = "/dev/gpiochip0";
|
||||
std::int32_t pin_a = 17;
|
||||
std::int32_t pin_b = 27;
|
||||
bool invert_logic = false;
|
||||
};
|
||||
|
||||
struct RingEndpointConfig {
|
||||
// Shared-memory endpoint geometry.
|
||||
std::string name{};
|
||||
std::uint32_t capacity = 32;
|
||||
std::uint32_t slot_size_bytes = 2U * 1024U * 1024U;
|
||||
};
|
||||
|
||||
struct RingsConfig {
|
||||
RingEndpointConfig raw{.name = "/radar_raw"};
|
||||
RingEndpointConfig raw_tap{.name = "/radar_raw_tap"};
|
||||
RingEndpointConfig preprocessed{.name = "/radar_preprocessed"};
|
||||
RingEndpointConfig preprocessed_tap{.name = "/radar_preprocessed_tap"};
|
||||
RingEndpointConfig results{.name = "/radar_results"};
|
||||
};
|
||||
|
||||
struct RuntimeConfig {
|
||||
// Runtime pacing and behavior flags for C++ workers.
|
||||
std::uint32_t settling_ms = 0;
|
||||
std::uint32_t idle_sleep_ms = 2;
|
||||
bool continuous = true;
|
||||
std::string processing_live_config_path = "python_app/runtime/processing_live.json";
|
||||
};
|
||||
|
||||
struct PreprocessConfig {
|
||||
// Names and bundle paths selected by Python GUI layer.
|
||||
std::string calibration_set{};
|
||||
std::string reference_set{};
|
||||
std::string calibration_bundle_path{};
|
||||
std::string reference_bundle_path{};
|
||||
};
|
||||
|
||||
struct RunConfig {
|
||||
// Single configuration object shared by all C++ processes.
|
||||
RadarConfig radar{};
|
||||
SwitchConfig input_switch{};
|
||||
SwitchConfig output_switch{};
|
||||
RingsConfig rings{};
|
||||
RuntimeConfig runtime{};
|
||||
PreprocessConfig preprocess{};
|
||||
std::vector<radar::ipc::ComboKey> run_combos{};
|
||||
};
|
||||
|
||||
[[nodiscard]] auto load_run_config(const std::string& path) -> RunConfig;
|
||||
|
||||
} // namespace radar::config
|
||||
@@ -0,0 +1,398 @@
|
||||
#include "run_config.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace radar::config {
|
||||
namespace {
|
||||
|
||||
using Json = nlohmann::json;
|
||||
|
||||
[[nodiscard]] auto required_field(const Json& object, const std::string& key) -> const Json& {
|
||||
const auto found = object.find(key);
|
||||
if (found == object.end()) {
|
||||
throw std::runtime_error("Missing required config field: " + key);
|
||||
}
|
||||
return *found;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto optional_field(const Json& object, const std::string& key) -> const Json* {
|
||||
const auto found = object.find(key);
|
||||
if (found == object.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &(*found);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto as_object(const Json& value, const std::string& context) -> const Json* {
|
||||
if (!value.is_object()) {
|
||||
throw std::runtime_error("Expected JSON object at " + context);
|
||||
}
|
||||
return &value;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto as_array(const Json& value, const std::string& context) -> const Json* {
|
||||
if (!value.is_array()) {
|
||||
throw std::runtime_error("Expected JSON array at " + context);
|
||||
}
|
||||
return &value;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto as_string(const Json& value, const std::string& context) -> const std::string& {
|
||||
if (!value.is_string()) {
|
||||
throw std::runtime_error("Expected JSON string at " + context);
|
||||
}
|
||||
return value.get_ref<const std::string&>();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto as_bool(const Json& value, const std::string& context) -> bool {
|
||||
if (!value.is_boolean()) {
|
||||
throw std::runtime_error("Expected JSON bool at " + context);
|
||||
}
|
||||
return value.get<bool>();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto as_number(const Json& value, const std::string& context) -> double {
|
||||
if (!value.is_number()) {
|
||||
throw std::runtime_error("Expected JSON number at " + context);
|
||||
}
|
||||
return value.get<double>();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto number_to_u32(double value, const std::string& context) -> std::uint32_t {
|
||||
if (value < 0.0 || value > static_cast<double>(std::numeric_limits<std::uint32_t>::max())) {
|
||||
throw std::runtime_error("Value out of uint32 range at " + context);
|
||||
}
|
||||
|
||||
const auto rounded = std::round(value);
|
||||
if (std::fabs(value - rounded) > 0.000001) {
|
||||
throw std::runtime_error("Expected integer value at " + context);
|
||||
}
|
||||
return static_cast<std::uint32_t>(rounded);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto number_to_i32(double value, const std::string& context) -> std::int32_t {
|
||||
const auto rounded = std::round(value);
|
||||
if (std::fabs(value - rounded) > 0.000001) {
|
||||
throw std::runtime_error("Expected integer value at " + context);
|
||||
}
|
||||
|
||||
if (rounded < static_cast<double>(std::numeric_limits<std::int32_t>::min()) ||
|
||||
rounded > static_cast<double>(std::numeric_limits<std::int32_t>::max())) {
|
||||
throw std::runtime_error("Value out of int32 range at " + context);
|
||||
}
|
||||
return static_cast<std::int32_t>(rounded);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto optional_string(
|
||||
const Json& object,
|
||||
const std::string& key,
|
||||
const std::string& fallback
|
||||
) -> std::string {
|
||||
if (const auto* value = optional_field(object, key); value != nullptr) {
|
||||
return as_string(*value, key);
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto optional_bool(const Json& object, const std::string& key, bool fallback) -> bool {
|
||||
if (const auto* value = optional_field(object, key); value != nullptr) {
|
||||
return as_bool(*value, key);
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto optional_u32(
|
||||
const Json& object,
|
||||
const std::string& key,
|
||||
std::uint32_t fallback
|
||||
) -> std::uint32_t {
|
||||
if (const auto* value = optional_field(object, key); value != nullptr) {
|
||||
return number_to_u32(as_number(*value, key), key);
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto optional_i32(
|
||||
const Json& object,
|
||||
const std::string& key,
|
||||
std::int32_t fallback
|
||||
) -> std::int32_t {
|
||||
if (const auto* value = optional_field(object, key); value != nullptr) {
|
||||
return number_to_i32(as_number(*value, key), key);
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto optional_f32(const Json& object, const std::string& key, float fallback) -> float {
|
||||
if (const auto* value = optional_field(object, key); value != nullptr) {
|
||||
return static_cast<float>(as_number(*value, key));
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_driver_mode(const std::string& value) -> DriverMode {
|
||||
if (value == "mock") {
|
||||
return DriverMode::Mock;
|
||||
}
|
||||
if (value == "native") {
|
||||
return DriverMode::Native;
|
||||
}
|
||||
throw std::runtime_error("Unsupported driver_mode value: " + value);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_switch_driver_kind(const std::string& value) -> SwitchDriverKind {
|
||||
if (value == "h7992") {
|
||||
return SwitchDriverKind::H7992;
|
||||
}
|
||||
if (value == "hmc349a") {
|
||||
return SwitchDriverKind::HMC349A;
|
||||
}
|
||||
throw std::runtime_error("Unsupported switch driver value: " + value);
|
||||
}
|
||||
|
||||
void validate_radar_sweep(const RadarSweepSettings& sweep) {
|
||||
if (sweep.points == 0U) {
|
||||
throw std::runtime_error("radar.sweep.points must be > 0");
|
||||
}
|
||||
if (sweep.stop_hz < sweep.start_hz) {
|
||||
throw std::runtime_error("radar.sweep.stop_hz must be >= start_hz");
|
||||
}
|
||||
}
|
||||
|
||||
void validate_switch_config(const SwitchConfig& config) {
|
||||
if (config.radar_port != 1U && config.radar_port != 2U) {
|
||||
throw std::runtime_error("Switch radar_port must be 1 or 2 for " + config.name);
|
||||
}
|
||||
|
||||
if (config.positions == 0U) {
|
||||
throw std::runtime_error("Switch positions must be > 0 for " + config.name);
|
||||
}
|
||||
if (config.default_position >= config.positions) {
|
||||
throw std::runtime_error("Switch default_position is out of range for " + config.name);
|
||||
}
|
||||
|
||||
if (config.driver_mode != DriverMode::Native) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.gpio_chip.empty()) {
|
||||
throw std::runtime_error("gpio_chip is required for native switch mode: " + config.name);
|
||||
}
|
||||
|
||||
if (config.driver_kind == SwitchDriverKind::H7992) {
|
||||
if (config.positions > 4U) {
|
||||
throw std::runtime_error("Native H7992 switch driver supports positions <= 4 for " + config.name);
|
||||
}
|
||||
if (config.pin_a < 0 || config.pin_b < 0 || config.pin_a == config.pin_b) {
|
||||
throw std::runtime_error("pin_a/pin_b are invalid for native H7992 mode: " + config.name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.positions > 2U) {
|
||||
throw std::runtime_error("Native HMC349A switch driver supports positions <= 2 for " + config.name);
|
||||
}
|
||||
if (config.pin_a < 0) {
|
||||
throw std::runtime_error("pin_a (control) is required for native HMC349A mode: " + config.name);
|
||||
}
|
||||
}
|
||||
|
||||
void validate_combo_key(const ipc::ComboKey& key, const RunConfig& config) {
|
||||
if (key.input_pos >= config.input_switch.positions) {
|
||||
throw std::runtime_error("run.combos input index out of range");
|
||||
}
|
||||
if (key.output_pos >= config.output_switch.positions) {
|
||||
throw std::runtime_error("run.combos output index out of range");
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_switch_config(
|
||||
const Json& object,
|
||||
const std::string& default_name,
|
||||
std::uint32_t default_radar_port
|
||||
) -> SwitchConfig {
|
||||
const auto* switch_obj = as_object(object, default_name);
|
||||
|
||||
SwitchConfig config{};
|
||||
config.name = optional_string(*switch_obj, "name", default_name);
|
||||
config.driver_mode = parse_driver_mode(optional_string(*switch_obj, "driver_mode", "mock"));
|
||||
config.driver_kind = parse_switch_driver_kind(optional_string(*switch_obj, "driver", "h7992"));
|
||||
config.radar_port = optional_u32(*switch_obj, "radar_port", default_radar_port);
|
||||
config.positions = optional_u32(*switch_obj, "positions", 4);
|
||||
config.default_position = optional_u32(*switch_obj, "default_position", 0);
|
||||
config.gpio_chip = optional_string(*switch_obj, "gpio_chip", "/dev/gpiochip0");
|
||||
config.pin_a = optional_i32(*switch_obj, "pin_a", 17);
|
||||
config.pin_b = optional_i32(*switch_obj, "pin_b", 27);
|
||||
config.invert_logic = optional_bool(*switch_obj, "invert_logic", false);
|
||||
|
||||
validate_switch_config(config);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_ring_endpoint(
|
||||
const Json& object,
|
||||
const std::string& key,
|
||||
const RingEndpointConfig& fallback
|
||||
) -> RingEndpointConfig {
|
||||
RingEndpointConfig endpoint = fallback;
|
||||
const auto* value = optional_field(object, key);
|
||||
if (value == nullptr) {
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
const auto* endpoint_obj = as_object(*value, key);
|
||||
endpoint.name = optional_string(*endpoint_obj, "name", endpoint.name);
|
||||
endpoint.capacity = optional_u32(*endpoint_obj, "capacity", endpoint.capacity);
|
||||
endpoint.slot_size_bytes = optional_u32(*endpoint_obj, "slot_size_bytes", endpoint.slot_size_bytes);
|
||||
|
||||
if (endpoint.capacity == 0U) {
|
||||
throw std::runtime_error("Ring capacity must be > 0 for " + key);
|
||||
}
|
||||
if (endpoint.slot_size_bytes == 0U) {
|
||||
throw std::runtime_error("Ring slot_size_bytes must be > 0 for " + key);
|
||||
}
|
||||
if (endpoint.name.empty() || endpoint.name.front() != '/') {
|
||||
throw std::runtime_error("Ring name must start with '/' for " + key);
|
||||
}
|
||||
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto load_run_config(const std::string& path) -> RunConfig {
|
||||
std::ifstream stream(path);
|
||||
if (!stream.is_open()) {
|
||||
throw std::runtime_error("Failed to open config file: " + path);
|
||||
}
|
||||
|
||||
const std::string json_text((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());
|
||||
if (json_text.empty()) {
|
||||
throw std::runtime_error("Config file is empty: " + path);
|
||||
}
|
||||
|
||||
Json root{};
|
||||
try {
|
||||
root = Json::parse(json_text);
|
||||
} catch (const Json::parse_error& error) {
|
||||
throw std::runtime_error("Failed to parse config file: " + path + ": " + std::string(error.what()));
|
||||
}
|
||||
|
||||
const auto* root_obj = as_object(root, "root");
|
||||
RunConfig config{};
|
||||
|
||||
{
|
||||
const auto* radar_obj = as_object(required_field(*root_obj, "radar"), "radar");
|
||||
config.radar.model = optional_string(*radar_obj, "model", "librevna");
|
||||
config.radar.serial = optional_string(*radar_obj, "serial", "");
|
||||
config.radar.driver_mode = parse_driver_mode(optional_string(*radar_obj, "driver_mode", "mock"));
|
||||
config.radar.mock_signal_hz = optional_f32(*radar_obj, "mock_signal_hz", 5'000'000.0F);
|
||||
|
||||
const auto* sweep_obj = as_object(required_field(*radar_obj, "sweep"), "radar.sweep");
|
||||
config.radar.sweep.start_hz =
|
||||
static_cast<float>(as_number(required_field(*sweep_obj, "start_hz"), "radar.sweep.start_hz"));
|
||||
config.radar.sweep.stop_hz =
|
||||
static_cast<float>(as_number(required_field(*sweep_obj, "stop_hz"), "radar.sweep.stop_hz"));
|
||||
config.radar.sweep.points =
|
||||
number_to_u32(as_number(required_field(*sweep_obj, "points"), "radar.sweep.points"), "radar.sweep.points");
|
||||
config.radar.sweep.if_bandwidth_hz = static_cast<float>(
|
||||
as_number(required_field(*sweep_obj, "if_bandwidth_hz"), "radar.sweep.if_bandwidth_hz")
|
||||
);
|
||||
config.radar.sweep.power_dbm = optional_f32(*sweep_obj, "stimulus_power_dbm", -10.0F);
|
||||
validate_radar_sweep(config.radar.sweep);
|
||||
}
|
||||
|
||||
{
|
||||
const auto* switches_obj = as_object(required_field(*root_obj, "switches"), "switches");
|
||||
|
||||
if (const auto* port1_value = optional_field(*switches_obj, "port1"); port1_value != nullptr) {
|
||||
const auto* port1_obj = as_object(*port1_value, "switches.port1");
|
||||
const auto* port2_obj = as_object(required_field(*switches_obj, "port2"), "switches.port2");
|
||||
config.output_switch = parse_switch_config(*port1_obj, "port1", 1);
|
||||
config.input_switch = parse_switch_config(*port2_obj, "port2", 2);
|
||||
} else {
|
||||
config.input_switch = parse_switch_config(
|
||||
*as_object(required_field(*switches_obj, "input"), "switches.input"),
|
||||
"input",
|
||||
2
|
||||
);
|
||||
config.output_switch = parse_switch_config(
|
||||
*as_object(required_field(*switches_obj, "output"), "switches.output"),
|
||||
"output",
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
if (config.input_switch.radar_port == config.output_switch.radar_port) {
|
||||
throw std::runtime_error("Switch radar_port mapping must be unique for two switches");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const auto* run_obj = as_object(required_field(*root_obj, "run"), "run");
|
||||
config.runtime.settling_ms = optional_u32(*run_obj, "settling_ms", 0);
|
||||
config.runtime.idle_sleep_ms = optional_u32(*run_obj, "idle_sleep_ms", 2);
|
||||
config.runtime.continuous = optional_bool(*run_obj, "continuous", true);
|
||||
config.runtime.processing_live_config_path = optional_string(
|
||||
*run_obj,
|
||||
"processing_live_config_path",
|
||||
"python_app/runtime/processing_live.json"
|
||||
);
|
||||
|
||||
const auto* combos = as_array(required_field(*run_obj, "combos"), "run.combos");
|
||||
if (combos->empty()) {
|
||||
throw std::runtime_error("run.combos must not be empty");
|
||||
}
|
||||
|
||||
config.run_combos.reserve(combos->size());
|
||||
for (const auto& combo_value : *combos) {
|
||||
const auto* combo_obj = as_object(combo_value, "run.combos[]");
|
||||
|
||||
ipc::ComboKey key{};
|
||||
key.input_pos = number_to_u32(
|
||||
as_number(required_field(*combo_obj, "input"), "run.combos.input"),
|
||||
"run.combos.input"
|
||||
);
|
||||
key.output_pos = number_to_u32(
|
||||
as_number(required_field(*combo_obj, "output"), "run.combos.output"),
|
||||
"run.combos.output"
|
||||
);
|
||||
validate_combo_key(key, config);
|
||||
config.run_combos.push_back(key);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const auto* preprocess_obj = as_object(required_field(*root_obj, "preprocess"), "preprocess");
|
||||
config.preprocess.calibration_set = optional_string(*preprocess_obj, "calibration_set", "");
|
||||
config.preprocess.reference_set = optional_string(*preprocess_obj, "reference_set", "");
|
||||
config.preprocess.calibration_bundle_path = optional_string(*preprocess_obj, "calibration_bundle_path", "");
|
||||
config.preprocess.reference_bundle_path = optional_string(*preprocess_obj, "reference_bundle_path", "");
|
||||
}
|
||||
|
||||
{
|
||||
const auto* rings_obj = as_object(required_field(*root_obj, "rings"), "rings");
|
||||
config.rings.raw = parse_ring_endpoint(*rings_obj, "raw", config.rings.raw);
|
||||
config.rings.raw_tap = parse_ring_endpoint(*rings_obj, "raw_tap", config.rings.raw_tap);
|
||||
config.rings.preprocessed = parse_ring_endpoint(*rings_obj, "preprocessed", config.rings.preprocessed);
|
||||
config.rings.preprocessed_tap = parse_ring_endpoint(
|
||||
*rings_obj,
|
||||
"preprocessed_tap",
|
||||
config.rings.preprocessed_tap
|
||||
);
|
||||
config.rings.results = parse_ring_endpoint(*rings_obj, "results", config.rings.results);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
} // namespace radar::config
|
||||
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace radar::ipc {
|
||||
|
||||
struct Complex32 {
|
||||
float re = 0.0F;
|
||||
float im = 0.0F;
|
||||
};
|
||||
|
||||
struct ComboKey {
|
||||
// Zero-based switch positions.
|
||||
std::uint32_t input_pos = 0;
|
||||
std::uint32_t output_pos = 0;
|
||||
|
||||
[[nodiscard]] auto operator==(const ComboKey& other) const -> bool {
|
||||
return input_pos == other.input_pos && output_pos == other.output_pos;
|
||||
}
|
||||
};
|
||||
|
||||
struct ComboKeyHash {
|
||||
[[nodiscard]] auto operator()(const ComboKey& key) const noexcept -> std::size_t {
|
||||
return (static_cast<std::size_t>(key.input_pos) << 32U) ^ static_cast<std::size_t>(key.output_pos);
|
||||
}
|
||||
};
|
||||
|
||||
struct SweepTraceBlock {
|
||||
ComboKey combo{};
|
||||
// Frequency axis in Hz. Must have the same size as `s21`.
|
||||
std::vector<float> frequency_hz{};
|
||||
// Complex S21 samples for matching frequency points.
|
||||
std::vector<Complex32> s21{};
|
||||
};
|
||||
|
||||
struct RawSweepCollection {
|
||||
std::uint64_t collection_id = 0;
|
||||
std::uint64_t monotonic_ns = 0;
|
||||
std::vector<SweepTraceBlock> traces{};
|
||||
};
|
||||
|
||||
using PreprocessedCollection = RawSweepCollection;
|
||||
|
||||
enum class ResultKind : std::uint8_t {
|
||||
TraceComplex = 1,
|
||||
ScalarF32 = 2,
|
||||
};
|
||||
|
||||
struct ResultPayload {
|
||||
std::string processing_name{};
|
||||
ResultKind kind = ResultKind::TraceComplex;
|
||||
// Valid for TraceComplex payloads.
|
||||
std::vector<float> frequency_hz{};
|
||||
// Valid for TraceComplex payloads.
|
||||
std::vector<Complex32> trace{};
|
||||
// Valid for ScalarF32 payloads.
|
||||
float scalar_value = 0.0F;
|
||||
};
|
||||
|
||||
struct ResultBlock {
|
||||
ComboKey combo{};
|
||||
std::vector<ResultPayload> payloads{};
|
||||
};
|
||||
|
||||
struct ResultCollection {
|
||||
std::uint64_t collection_id = 0;
|
||||
std::uint64_t monotonic_ns = 0;
|
||||
std::vector<ResultBlock> blocks{};
|
||||
};
|
||||
|
||||
// Wire-format serialization helpers for IPC rings.
|
||||
[[nodiscard]] auto serialize_raw_collection(const RawSweepCollection& collection) -> std::vector<std::uint8_t>;
|
||||
[[nodiscard]] auto deserialize_raw_collection(std::span<const std::uint8_t> bytes) -> RawSweepCollection;
|
||||
|
||||
[[nodiscard]] auto serialize_preprocessed_collection(const PreprocessedCollection& collection)
|
||||
-> std::vector<std::uint8_t>;
|
||||
[[nodiscard]] auto deserialize_preprocessed_collection(std::span<const std::uint8_t> bytes)
|
||||
-> PreprocessedCollection;
|
||||
|
||||
[[nodiscard]] auto serialize_result_collection(const ResultCollection& collection) -> std::vector<std::uint8_t>;
|
||||
[[nodiscard]] auto deserialize_result_collection(std::span<const std::uint8_t> bytes) -> ResultCollection;
|
||||
|
||||
// Monotonic timestamp helper used for produced collections.
|
||||
[[nodiscard]] auto current_monotonic_ns() -> std::uint64_t;
|
||||
|
||||
} // namespace radar::ipc
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace radar::ipc {
|
||||
|
||||
class ShmRing {
|
||||
public:
|
||||
ShmRing() = default;
|
||||
~ShmRing();
|
||||
|
||||
ShmRing(const ShmRing&) = delete;
|
||||
auto operator=(const ShmRing&) -> ShmRing& = delete;
|
||||
|
||||
ShmRing(ShmRing&& other) noexcept;
|
||||
auto operator=(ShmRing&& other) noexcept -> ShmRing&;
|
||||
|
||||
[[nodiscard]] static auto open_or_create(
|
||||
const std::string& name,
|
||||
std::uint32_t capacity,
|
||||
std::uint32_t slot_size_bytes
|
||||
) -> ShmRing;
|
||||
|
||||
// Opens ring by name and validates wire-format header fields.
|
||||
[[nodiscard]] static auto open_existing(const std::string& name) -> ShmRing;
|
||||
// Removes shared memory object by name (safe if it does not exist).
|
||||
static void unlink_ring(const std::string& name);
|
||||
|
||||
[[nodiscard]] auto is_open() const -> bool;
|
||||
[[nodiscard]] auto capacity() const -> std::uint32_t;
|
||||
[[nodiscard]] auto slot_size_bytes() const -> std::uint32_t;
|
||||
[[nodiscard]] auto dropped_count() const -> std::uint64_t;
|
||||
|
||||
// Pushes payload with overwrite-oldest policy on overflow.
|
||||
[[nodiscard]] auto push(std::span<const std::uint8_t> payload) -> bool;
|
||||
// Pops the next payload if available.
|
||||
[[nodiscard]] auto pop(std::vector<std::uint8_t>& payload) -> bool;
|
||||
|
||||
private:
|
||||
struct Header;
|
||||
struct SlotHeader;
|
||||
|
||||
int fd_ = -1;
|
||||
std::size_t mapped_size_ = 0;
|
||||
void* mapped_ = nullptr;
|
||||
Header* header_ = nullptr;
|
||||
|
||||
// Slot geometry helpers.
|
||||
[[nodiscard]] auto slot_stride_bytes() const -> std::size_t;
|
||||
[[nodiscard]] auto slot_header(std::uint64_t sequence) const -> SlotHeader*;
|
||||
[[nodiscard]] auto slot_payload(SlotHeader* slot) const -> std::uint8_t*;
|
||||
|
||||
void close();
|
||||
static void validate_name(const std::string& name);
|
||||
};
|
||||
|
||||
} // namespace radar::ipc
|
||||
@@ -0,0 +1,336 @@
|
||||
#include "shared_types.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace radar::ipc {
|
||||
namespace {
|
||||
|
||||
constexpr std::uint32_t kRawCollectionMagic = 0x31574152U; // RAW1
|
||||
constexpr std::uint32_t kPreprocessedCollectionMagic = 0x31525050U; // PRP1
|
||||
constexpr std::uint32_t kResultCollectionMagic = 0x314C5352U; // RSL1
|
||||
|
||||
template <typename T>
|
||||
concept TriviallySerializable = std::is_trivially_copyable_v<T>;
|
||||
|
||||
[[nodiscard]] auto checked_count_to_u32(std::size_t count, const std::string& label) -> std::uint32_t {
|
||||
if (count > std::numeric_limits<std::uint32_t>::max()) {
|
||||
throw std::runtime_error(label + " exceeds uint32 wire-format limit");
|
||||
}
|
||||
return static_cast<std::uint32_t>(count);
|
||||
}
|
||||
|
||||
void ensure_equal_sizes(std::size_t left, std::size_t right, const std::string& label) {
|
||||
if (left != right) {
|
||||
throw std::runtime_error(label + " has inconsistent vector sizes");
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryWriter {
|
||||
public:
|
||||
template <TriviallySerializable T>
|
||||
void write(const T& value) {
|
||||
const auto old_size = bytes_.size();
|
||||
bytes_.resize(old_size + sizeof(T));
|
||||
std::memcpy(bytes_.data() + old_size, &value, sizeof(T));
|
||||
}
|
||||
|
||||
void write_string(const std::string& value) {
|
||||
if (value.size() > std::numeric_limits<std::uint16_t>::max()) {
|
||||
throw std::runtime_error("String is too large for wire format");
|
||||
}
|
||||
|
||||
write(static_cast<std::uint16_t>(value.size()));
|
||||
|
||||
const auto old_size = bytes_.size();
|
||||
bytes_.resize(old_size + value.size());
|
||||
std::memcpy(bytes_.data() + old_size, value.data(), value.size());
|
||||
}
|
||||
|
||||
[[nodiscard]] auto finish() && -> std::vector<std::uint8_t> {
|
||||
return std::move(bytes_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::uint8_t> bytes_{};
|
||||
};
|
||||
|
||||
class BinaryReader {
|
||||
public:
|
||||
explicit BinaryReader(std::span<const std::uint8_t> bytes) : bytes_(bytes) {}
|
||||
|
||||
template <TriviallySerializable T>
|
||||
[[nodiscard]] auto read() -> T {
|
||||
ensure_available(sizeof(T));
|
||||
|
||||
T value{};
|
||||
std::memcpy(&value, bytes_.data() + offset_, sizeof(T));
|
||||
offset_ += sizeof(T);
|
||||
return value;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto read_string() -> std::string {
|
||||
const auto size = read<std::uint16_t>();
|
||||
ensure_available(size);
|
||||
|
||||
const auto* begin = reinterpret_cast<const char*>(bytes_.data() + offset_);
|
||||
std::string value(begin, begin + size);
|
||||
offset_ += size;
|
||||
return value;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_consumed() const -> bool {
|
||||
return offset_ == bytes_.size();
|
||||
}
|
||||
|
||||
private:
|
||||
void ensure_available(std::size_t size) const {
|
||||
if (offset_ + size > bytes_.size()) {
|
||||
throw std::runtime_error("Unexpected end of serialized payload");
|
||||
}
|
||||
}
|
||||
|
||||
std::span<const std::uint8_t> bytes_{};
|
||||
std::size_t offset_ = 0;
|
||||
};
|
||||
|
||||
void write_trace_block(BinaryWriter& writer, const SweepTraceBlock& trace) {
|
||||
ensure_equal_sizes(trace.frequency_hz.size(), trace.s21.size(), "Sweep trace");
|
||||
|
||||
writer.write(trace.combo.input_pos);
|
||||
writer.write(trace.combo.output_pos);
|
||||
writer.write(checked_count_to_u32(trace.frequency_hz.size(), "Trace point count"));
|
||||
|
||||
for (const auto frequency_hz : trace.frequency_hz) {
|
||||
writer.write(frequency_hz);
|
||||
}
|
||||
|
||||
for (const auto& point : trace.s21) {
|
||||
writer.write(point.re);
|
||||
writer.write(point.im);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto read_trace_block(BinaryReader& reader) -> SweepTraceBlock {
|
||||
SweepTraceBlock trace{};
|
||||
trace.combo.input_pos = reader.read<std::uint32_t>();
|
||||
trace.combo.output_pos = reader.read<std::uint32_t>();
|
||||
|
||||
const auto point_count = reader.read<std::uint32_t>();
|
||||
trace.frequency_hz.reserve(point_count);
|
||||
trace.s21.reserve(point_count);
|
||||
|
||||
for (std::uint32_t index = 0; index < point_count; ++index) {
|
||||
trace.frequency_hz.push_back(reader.read<float>());
|
||||
}
|
||||
|
||||
for (std::uint32_t index = 0; index < point_count; ++index) {
|
||||
trace.s21.push_back(Complex32{
|
||||
.re = reader.read<float>(),
|
||||
.im = reader.read<float>(),
|
||||
});
|
||||
}
|
||||
|
||||
return trace;
|
||||
}
|
||||
|
||||
void write_trace_collection(BinaryWriter& writer, std::uint32_t magic, const RawSweepCollection& collection) {
|
||||
writer.write(magic);
|
||||
writer.write(collection.collection_id);
|
||||
writer.write(collection.monotonic_ns);
|
||||
writer.write(checked_count_to_u32(collection.traces.size(), "Trace count"));
|
||||
|
||||
for (const auto& trace : collection.traces) {
|
||||
write_trace_block(writer, trace);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto read_trace_collection(BinaryReader& reader, std::uint32_t expected_magic) -> RawSweepCollection {
|
||||
const auto magic = reader.read<std::uint32_t>();
|
||||
if (magic != expected_magic) {
|
||||
throw std::runtime_error("Unexpected trace collection magic");
|
||||
}
|
||||
|
||||
RawSweepCollection collection{};
|
||||
collection.collection_id = reader.read<std::uint64_t>();
|
||||
collection.monotonic_ns = reader.read<std::uint64_t>();
|
||||
|
||||
const auto trace_count = reader.read<std::uint32_t>();
|
||||
collection.traces.reserve(trace_count);
|
||||
for (std::uint32_t index = 0; index < trace_count; ++index) {
|
||||
collection.traces.push_back(read_trace_block(reader));
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
void write_trace_result_payload(BinaryWriter& writer, const ResultPayload& payload) {
|
||||
ensure_equal_sizes(payload.frequency_hz.size(), payload.trace.size(), "Result trace payload");
|
||||
|
||||
writer.write(checked_count_to_u32(payload.trace.size(), "Result trace point count"));
|
||||
for (const auto frequency_hz : payload.frequency_hz) {
|
||||
writer.write(frequency_hz);
|
||||
}
|
||||
for (const auto& sample : payload.trace) {
|
||||
writer.write(sample.re);
|
||||
writer.write(sample.im);
|
||||
}
|
||||
}
|
||||
|
||||
auto read_trace_result_payload(BinaryReader& reader, ResultPayload* payload) -> void {
|
||||
const auto point_count = reader.read<std::uint32_t>();
|
||||
|
||||
payload->frequency_hz.reserve(point_count);
|
||||
payload->trace.reserve(point_count);
|
||||
|
||||
for (std::uint32_t index = 0; index < point_count; ++index) {
|
||||
payload->frequency_hz.push_back(reader.read<float>());
|
||||
}
|
||||
for (std::uint32_t index = 0; index < point_count; ++index) {
|
||||
payload->trace.push_back(Complex32{
|
||||
.re = reader.read<float>(),
|
||||
.im = reader.read<float>(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void write_result_payload(BinaryWriter& writer, const ResultPayload& payload) {
|
||||
writer.write(static_cast<std::uint8_t>(payload.kind));
|
||||
writer.write_string(payload.processing_name);
|
||||
|
||||
switch (payload.kind) {
|
||||
case ResultKind::TraceComplex:
|
||||
write_trace_result_payload(writer, payload);
|
||||
return;
|
||||
case ResultKind::ScalarF32:
|
||||
writer.write(payload.scalar_value);
|
||||
return;
|
||||
default:
|
||||
throw std::runtime_error("Unsupported result payload kind");
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto read_result_payload(BinaryReader& reader) -> ResultPayload {
|
||||
ResultPayload payload{};
|
||||
payload.kind = static_cast<ResultKind>(reader.read<std::uint8_t>());
|
||||
payload.processing_name = reader.read_string();
|
||||
|
||||
switch (payload.kind) {
|
||||
case ResultKind::TraceComplex:
|
||||
read_trace_result_payload(reader, &payload);
|
||||
return payload;
|
||||
case ResultKind::ScalarF32:
|
||||
payload.scalar_value = reader.read<float>();
|
||||
return payload;
|
||||
default:
|
||||
throw std::runtime_error("Unsupported result payload kind in stream");
|
||||
}
|
||||
}
|
||||
|
||||
void write_result_block(BinaryWriter& writer, const ResultBlock& block) {
|
||||
writer.write(block.combo.input_pos);
|
||||
writer.write(block.combo.output_pos);
|
||||
writer.write(checked_count_to_u32(block.payloads.size(), "Result payload count"));
|
||||
|
||||
for (const auto& payload : block.payloads) {
|
||||
write_result_payload(writer, payload);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto read_result_block(BinaryReader& reader) -> ResultBlock {
|
||||
ResultBlock block{};
|
||||
block.combo.input_pos = reader.read<std::uint32_t>();
|
||||
block.combo.output_pos = reader.read<std::uint32_t>();
|
||||
|
||||
const auto payload_count = reader.read<std::uint32_t>();
|
||||
block.payloads.reserve(payload_count);
|
||||
for (std::uint32_t index = 0; index < payload_count; ++index) {
|
||||
block.payloads.push_back(read_result_payload(reader));
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
void ensure_reader_consumed(const BinaryReader& reader, const std::string& payload_label) {
|
||||
if (!reader.is_consumed()) {
|
||||
throw std::runtime_error("Unexpected trailing bytes in " + payload_label);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto serialize_raw_collection(const RawSweepCollection& collection) -> std::vector<std::uint8_t> {
|
||||
BinaryWriter writer{};
|
||||
write_trace_collection(writer, kRawCollectionMagic, collection);
|
||||
return std::move(writer).finish();
|
||||
}
|
||||
|
||||
auto deserialize_raw_collection(std::span<const std::uint8_t> bytes) -> RawSweepCollection {
|
||||
BinaryReader reader(bytes);
|
||||
auto collection = read_trace_collection(reader, kRawCollectionMagic);
|
||||
ensure_reader_consumed(reader, "raw collection");
|
||||
return collection;
|
||||
}
|
||||
|
||||
auto serialize_preprocessed_collection(const PreprocessedCollection& collection) -> std::vector<std::uint8_t> {
|
||||
BinaryWriter writer{};
|
||||
write_trace_collection(writer, kPreprocessedCollectionMagic, collection);
|
||||
return std::move(writer).finish();
|
||||
}
|
||||
|
||||
auto deserialize_preprocessed_collection(std::span<const std::uint8_t> bytes) -> PreprocessedCollection {
|
||||
BinaryReader reader(bytes);
|
||||
auto collection = read_trace_collection(reader, kPreprocessedCollectionMagic);
|
||||
ensure_reader_consumed(reader, "preprocessed collection");
|
||||
return collection;
|
||||
}
|
||||
|
||||
auto serialize_result_collection(const ResultCollection& collection) -> std::vector<std::uint8_t> {
|
||||
BinaryWriter writer{};
|
||||
writer.write(kResultCollectionMagic);
|
||||
writer.write(collection.collection_id);
|
||||
writer.write(collection.monotonic_ns);
|
||||
writer.write(checked_count_to_u32(collection.blocks.size(), "Result block count"));
|
||||
|
||||
for (const auto& block : collection.blocks) {
|
||||
write_result_block(writer, block);
|
||||
}
|
||||
|
||||
return std::move(writer).finish();
|
||||
}
|
||||
|
||||
auto deserialize_result_collection(std::span<const std::uint8_t> bytes) -> ResultCollection {
|
||||
BinaryReader reader(bytes);
|
||||
|
||||
const auto magic = reader.read<std::uint32_t>();
|
||||
if (magic != kResultCollectionMagic) {
|
||||
throw std::runtime_error("Unexpected result collection magic");
|
||||
}
|
||||
|
||||
ResultCollection collection{};
|
||||
collection.collection_id = reader.read<std::uint64_t>();
|
||||
collection.monotonic_ns = reader.read<std::uint64_t>();
|
||||
|
||||
const auto block_count = reader.read<std::uint32_t>();
|
||||
collection.blocks.reserve(block_count);
|
||||
for (std::uint32_t index = 0; index < block_count; ++index) {
|
||||
collection.blocks.push_back(read_result_block(reader));
|
||||
}
|
||||
|
||||
ensure_reader_consumed(reader, "result collection");
|
||||
return collection;
|
||||
}
|
||||
|
||||
auto current_monotonic_ns() -> std::uint64_t {
|
||||
const auto now = std::chrono::steady_clock::now().time_since_epoch();
|
||||
const auto now_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
|
||||
return static_cast<std::uint64_t>(now_ns);
|
||||
}
|
||||
|
||||
} // namespace radar::ipc
|
||||
@@ -0,0 +1,380 @@
|
||||
#include "shm_ring.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <new>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
|
||||
namespace radar::ipc {
|
||||
|
||||
struct alignas(64) ShmRing::Header {
|
||||
char magic[8]{};
|
||||
std::uint32_t version = 0;
|
||||
std::uint32_t capacity = 0;
|
||||
std::uint32_t slot_size_bytes = 0;
|
||||
std::uint32_t reserved = 0;
|
||||
std::atomic<std::uint64_t> write_seq{};
|
||||
std::atomic<std::uint64_t> read_seq{};
|
||||
std::atomic<std::uint64_t> dropped{};
|
||||
};
|
||||
|
||||
struct alignas(16) ShmRing::SlotHeader {
|
||||
std::uint32_t payload_size = 0;
|
||||
std::uint32_t reserved = 0;
|
||||
std::uint64_t sequence = 0;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::string_view kRingMagic = "RDRRING2";
|
||||
constexpr std::uint32_t kRingVersion = 1;
|
||||
constexpr auto kHeaderInitWaitTimeout = std::chrono::milliseconds(1000);
|
||||
constexpr auto kHeaderInitPollInterval = std::chrono::milliseconds(2);
|
||||
|
||||
[[nodiscard]] auto checked_u64_diff(std::uint64_t left, std::uint64_t right) -> std::uint64_t {
|
||||
return left >= right ? left - right : 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto errno_message(const std::string& action, const std::string& name) -> std::runtime_error {
|
||||
return std::runtime_error(action + " " + name + ": " + std::strerror(errno));
|
||||
}
|
||||
|
||||
class ScopedFd {
|
||||
public:
|
||||
explicit ScopedFd(int fd) : fd_(fd) {}
|
||||
|
||||
~ScopedFd() {
|
||||
if (fd_ >= 0) {
|
||||
::close(fd_);
|
||||
}
|
||||
}
|
||||
|
||||
ScopedFd(const ScopedFd&) = delete;
|
||||
auto operator=(const ScopedFd&) -> ScopedFd& = delete;
|
||||
|
||||
ScopedFd(ScopedFd&& other) noexcept : fd_(std::exchange(other.fd_, -1)) {}
|
||||
|
||||
auto release() -> int {
|
||||
return std::exchange(fd_, -1);
|
||||
}
|
||||
|
||||
private:
|
||||
int fd_ = -1;
|
||||
};
|
||||
|
||||
class ScopedMmap {
|
||||
public:
|
||||
ScopedMmap(void* address, std::size_t size) : address_(address), size_(size) {}
|
||||
|
||||
~ScopedMmap() {
|
||||
if (address_ != nullptr && address_ != MAP_FAILED) {
|
||||
munmap(address_, size_);
|
||||
}
|
||||
}
|
||||
|
||||
ScopedMmap(const ScopedMmap&) = delete;
|
||||
auto operator=(const ScopedMmap&) -> ScopedMmap& = delete;
|
||||
|
||||
ScopedMmap(ScopedMmap&& other) noexcept
|
||||
: address_(std::exchange(other.address_, nullptr)),
|
||||
size_(std::exchange(other.size_, 0U)) {}
|
||||
|
||||
auto release() -> std::pair<void*, std::size_t> {
|
||||
return {std::exchange(address_, nullptr), std::exchange(size_, 0U)};
|
||||
}
|
||||
|
||||
private:
|
||||
void* address_ = nullptr;
|
||||
std::size_t size_ = 0;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
ShmRing::~ShmRing() {
|
||||
close();
|
||||
}
|
||||
|
||||
ShmRing::ShmRing(ShmRing&& other) noexcept {
|
||||
*this = std::move(other);
|
||||
}
|
||||
|
||||
auto ShmRing::operator=(ShmRing&& other) noexcept -> ShmRing& {
|
||||
if (this != &other) {
|
||||
close();
|
||||
|
||||
fd_ = std::exchange(other.fd_, -1);
|
||||
mapped_size_ = std::exchange(other.mapped_size_, 0U);
|
||||
mapped_ = std::exchange(other.mapped_, nullptr);
|
||||
header_ = std::exchange(other.header_, nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto ShmRing::open_or_create(
|
||||
const std::string& name,
|
||||
std::uint32_t capacity,
|
||||
std::uint32_t slot_size_bytes
|
||||
) -> ShmRing {
|
||||
validate_name(name);
|
||||
if (capacity == 0U) {
|
||||
throw std::runtime_error("Ring capacity must be > 0");
|
||||
}
|
||||
if (slot_size_bytes == 0U) {
|
||||
throw std::runtime_error("Ring slot size must be > 0");
|
||||
}
|
||||
|
||||
bool created = false;
|
||||
int fd = shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0660);
|
||||
if (fd >= 0) {
|
||||
created = true;
|
||||
} else if (errno == EEXIST) {
|
||||
fd = shm_open(name.c_str(), O_RDWR, 0660);
|
||||
}
|
||||
|
||||
if (fd < 0) {
|
||||
throw errno_message("Failed to open shared memory ring", name);
|
||||
}
|
||||
|
||||
ScopedFd scoped_fd(fd);
|
||||
|
||||
const auto slot_stride = sizeof(SlotHeader) + static_cast<std::size_t>(slot_size_bytes);
|
||||
const auto mapped_size = sizeof(Header) + slot_stride * capacity;
|
||||
|
||||
if (created) {
|
||||
if (ftruncate(fd, static_cast<off_t>(mapped_size)) != 0) {
|
||||
throw errno_message("Failed to resize shared memory ring", name);
|
||||
}
|
||||
}
|
||||
|
||||
void* mapped = mmap(nullptr, mapped_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (mapped == MAP_FAILED) {
|
||||
throw errno_message("Failed to mmap shared memory ring", name);
|
||||
}
|
||||
|
||||
ScopedMmap scoped_mmap(mapped, mapped_size);
|
||||
auto* header = static_cast<Header*>(mapped);
|
||||
|
||||
if (created) {
|
||||
std::memset(mapped, 0, mapped_size);
|
||||
std::memcpy(header->magic, kRingMagic.data(), kRingMagic.size());
|
||||
header->version = kRingVersion;
|
||||
header->capacity = capacity;
|
||||
header->slot_size_bytes = slot_size_bytes;
|
||||
|
||||
new (&header->write_seq) std::atomic<std::uint64_t>(0);
|
||||
new (&header->read_seq) std::atomic<std::uint64_t>(0);
|
||||
new (&header->dropped) std::atomic<std::uint64_t>(0);
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
} else {
|
||||
const auto deadline = std::chrono::steady_clock::now() + kHeaderInitWaitTimeout;
|
||||
|
||||
while (true) {
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
|
||||
const bool magic_ok = std::memcmp(header->magic, kRingMagic.data(), kRingMagic.size()) == 0;
|
||||
const bool version_ok = header->version == kRingVersion;
|
||||
const bool geometry_ok = header->capacity == capacity && header->slot_size_bytes == slot_size_bytes;
|
||||
|
||||
if (magic_ok && version_ok && geometry_ok) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (std::chrono::steady_clock::now() >= deadline) {
|
||||
if (!magic_ok) {
|
||||
throw std::runtime_error("Shared memory ring magic mismatch for " + name);
|
||||
}
|
||||
if (!version_ok) {
|
||||
throw std::runtime_error("Shared memory ring version mismatch for " + name);
|
||||
}
|
||||
throw std::runtime_error("Shared memory ring geometry mismatch for " + name);
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(kHeaderInitPollInterval);
|
||||
}
|
||||
}
|
||||
|
||||
ShmRing ring{};
|
||||
ring.fd_ = scoped_fd.release();
|
||||
|
||||
const auto [released_mapped, released_size] = scoped_mmap.release();
|
||||
ring.mapped_ = released_mapped;
|
||||
ring.mapped_size_ = released_size;
|
||||
ring.header_ = static_cast<Header*>(released_mapped);
|
||||
return ring;
|
||||
}
|
||||
|
||||
auto ShmRing::open_existing(const std::string& name) -> ShmRing {
|
||||
validate_name(name);
|
||||
|
||||
const int fd = shm_open(name.c_str(), O_RDWR, 0660);
|
||||
if (fd < 0) {
|
||||
throw errno_message("Failed to open shared memory ring", name);
|
||||
}
|
||||
|
||||
ScopedFd scoped_fd(fd);
|
||||
|
||||
struct stat info {};
|
||||
if (fstat(fd, &info) != 0) {
|
||||
throw errno_message("Failed to stat shared memory ring", name);
|
||||
}
|
||||
|
||||
if (info.st_size < static_cast<off_t>(sizeof(Header))) {
|
||||
throw std::runtime_error("Shared memory ring size is too small for " + name);
|
||||
}
|
||||
|
||||
const auto mapped_size = static_cast<std::size_t>(info.st_size);
|
||||
void* mapped = mmap(nullptr, mapped_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (mapped == MAP_FAILED) {
|
||||
throw errno_message("Failed to mmap shared memory ring", name);
|
||||
}
|
||||
|
||||
ScopedMmap scoped_mmap(mapped, mapped_size);
|
||||
auto* header = static_cast<Header*>(mapped);
|
||||
|
||||
if (std::memcmp(header->magic, kRingMagic.data(), kRingMagic.size()) != 0) {
|
||||
throw std::runtime_error("Shared memory ring magic mismatch for " + name);
|
||||
}
|
||||
if (header->version != kRingVersion) {
|
||||
throw std::runtime_error("Shared memory ring version mismatch for " + name);
|
||||
}
|
||||
|
||||
ShmRing ring{};
|
||||
ring.fd_ = scoped_fd.release();
|
||||
|
||||
const auto [released_mapped, released_size] = scoped_mmap.release();
|
||||
ring.mapped_ = released_mapped;
|
||||
ring.mapped_size_ = released_size;
|
||||
ring.header_ = static_cast<Header*>(released_mapped);
|
||||
return ring;
|
||||
}
|
||||
|
||||
void ShmRing::unlink_ring(const std::string& name) {
|
||||
validate_name(name);
|
||||
if (shm_unlink(name.c_str()) != 0 && errno != ENOENT) {
|
||||
throw errno_message("Failed to unlink shared memory ring", name);
|
||||
}
|
||||
}
|
||||
|
||||
auto ShmRing::is_open() const -> bool {
|
||||
return header_ != nullptr;
|
||||
}
|
||||
|
||||
auto ShmRing::capacity() const -> std::uint32_t {
|
||||
return header_ != nullptr ? header_->capacity : 0;
|
||||
}
|
||||
|
||||
auto ShmRing::slot_size_bytes() const -> std::uint32_t {
|
||||
return header_ != nullptr ? header_->slot_size_bytes : 0;
|
||||
}
|
||||
|
||||
auto ShmRing::dropped_count() const -> std::uint64_t {
|
||||
if (header_ == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return header_->dropped.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
auto ShmRing::push(std::span<const std::uint8_t> payload) -> bool {
|
||||
if (header_ == nullptr) {
|
||||
throw std::runtime_error("Ring is not open");
|
||||
}
|
||||
if (payload.size() > header_->slot_size_bytes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto write_seq = header_->write_seq.load(std::memory_order_relaxed);
|
||||
const auto read_seq = header_->read_seq.load(std::memory_order_acquire);
|
||||
|
||||
if (checked_u64_diff(write_seq, read_seq) >= header_->capacity) {
|
||||
header_->read_seq.store(read_seq + 1U, std::memory_order_release);
|
||||
header_->dropped.fetch_add(1U, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
auto* slot = slot_header(write_seq);
|
||||
slot->payload_size = static_cast<std::uint32_t>(payload.size());
|
||||
slot->sequence = write_seq + 1U;
|
||||
std::memcpy(slot_payload(slot), payload.data(), payload.size());
|
||||
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
header_->write_seq.store(write_seq + 1U, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto ShmRing::pop(std::vector<std::uint8_t>& payload) -> bool {
|
||||
if (header_ == nullptr) {
|
||||
throw std::runtime_error("Ring is not open");
|
||||
}
|
||||
|
||||
const auto read_seq = header_->read_seq.load(std::memory_order_relaxed);
|
||||
const auto write_seq = header_->write_seq.load(std::memory_order_acquire);
|
||||
if (read_seq >= write_seq) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* slot = slot_header(read_seq);
|
||||
if (slot->sequence != read_seq + 1U) {
|
||||
// Producer overwrote this slot before consumer read it. Resync to latest.
|
||||
header_->read_seq.store(write_seq, std::memory_order_release);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto payload_size = slot->payload_size;
|
||||
if (payload_size > header_->slot_size_bytes) {
|
||||
header_->read_seq.store(write_seq, std::memory_order_release);
|
||||
throw std::runtime_error("Invalid payload size in shared memory slot");
|
||||
}
|
||||
|
||||
payload.resize(payload_size);
|
||||
std::memcpy(payload.data(), slot_payload(slot), payload_size);
|
||||
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
header_->read_seq.store(read_seq + 1U, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto ShmRing::slot_stride_bytes() const -> std::size_t {
|
||||
return sizeof(SlotHeader) + header_->slot_size_bytes;
|
||||
}
|
||||
|
||||
auto ShmRing::slot_header(std::uint64_t sequence) const -> SlotHeader* {
|
||||
const auto index = sequence % header_->capacity;
|
||||
auto* slots_begin = static_cast<std::uint8_t*>(mapped_) + sizeof(Header);
|
||||
return reinterpret_cast<SlotHeader*>(slots_begin + index * slot_stride_bytes());
|
||||
}
|
||||
|
||||
auto ShmRing::slot_payload(SlotHeader* slot) const -> std::uint8_t* {
|
||||
return reinterpret_cast<std::uint8_t*>(slot) + sizeof(SlotHeader);
|
||||
}
|
||||
|
||||
void ShmRing::close() {
|
||||
if (mapped_ != nullptr) {
|
||||
munmap(mapped_, mapped_size_);
|
||||
mapped_ = nullptr;
|
||||
}
|
||||
|
||||
if (fd_ >= 0) {
|
||||
::close(fd_);
|
||||
fd_ = -1;
|
||||
}
|
||||
|
||||
mapped_size_ = 0;
|
||||
header_ = nullptr;
|
||||
}
|
||||
|
||||
void ShmRing::validate_name(const std::string& name) {
|
||||
if (name.empty() || name.front() != '/') {
|
||||
throw std::runtime_error("POSIX shm name must start with '/'");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace radar::ipc
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "calibrator_interface.hpp"
|
||||
#include "shared_types.hpp"
|
||||
|
||||
namespace radar::preprocessing {
|
||||
|
||||
class CalibrationMaster {
|
||||
public:
|
||||
// `calibrator` encapsulates the actual calibration algorithm (v1: through).
|
||||
explicit CalibrationMaster(std::unique_ptr<CalibratorInterface> calibrator);
|
||||
|
||||
// Loads a serialized raw sweep bundle with one calibration standard per combo.
|
||||
void load_bundle(const std::string& path);
|
||||
// Ensures all runtime combos are present in loaded standards.
|
||||
void validate_combos(const std::vector<ipc::ComboKey>& combos) const;
|
||||
// Applies calibration standard corresponding to measured trace combo.
|
||||
[[nodiscard]] auto apply(const ipc::SweepTraceBlock& measured_trace) const -> ipc::SweepTraceBlock;
|
||||
|
||||
private:
|
||||
std::unique_ptr<CalibratorInterface> calibrator_impl_;
|
||||
std::unordered_map<ipc::ComboKey, ipc::SweepTraceBlock, ipc::ComboKeyHash> standards_by_combo_{};
|
||||
};
|
||||
|
||||
[[nodiscard]] auto make_through_calibrator() -> std::unique_ptr<CalibratorInterface>;
|
||||
|
||||
} // namespace radar::preprocessing
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "shared_types.hpp"
|
||||
|
||||
namespace radar::preprocessing {
|
||||
|
||||
class CalibratorInterface {
|
||||
public:
|
||||
virtual ~CalibratorInterface() = default;
|
||||
|
||||
[[nodiscard]] virtual auto name() const -> std::string = 0;
|
||||
[[nodiscard]] virtual auto apply(
|
||||
const std::vector<ipc::Complex32>& measured,
|
||||
const std::vector<ipc::Complex32>& calibration
|
||||
) const -> std::vector<ipc::Complex32> = 0;
|
||||
};
|
||||
|
||||
} // namespace radar::preprocessing
|
||||
@@ -0,0 +1,101 @@
|
||||
#include "calibration_master.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace radar::preprocessing {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] auto combo_to_string(const ipc::ComboKey& combo) -> std::string {
|
||||
return "input=" + std::to_string(combo.input_pos) + " output=" + std::to_string(combo.output_pos);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto read_binary_file(const std::string& path, const std::string& bundle_label)
|
||||
-> std::vector<std::uint8_t> {
|
||||
std::ifstream stream(path, std::ios::binary);
|
||||
if (!stream.is_open()) {
|
||||
throw std::runtime_error("Failed to open " + bundle_label + " bundle: " + path);
|
||||
}
|
||||
|
||||
return std::vector<std::uint8_t>(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
void validate_trace_layout(const ipc::SweepTraceBlock& trace, const std::string& trace_label) {
|
||||
if (trace.frequency_hz.size() != trace.s21.size()) {
|
||||
throw std::runtime_error(
|
||||
trace_label + " frequency/complex vector size mismatch for combo " + combo_to_string(trace.combo)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CalibrationMaster::CalibrationMaster(std::unique_ptr<CalibratorInterface> calibrator)
|
||||
: calibrator_impl_(std::move(calibrator)) {
|
||||
if (!calibrator_impl_) {
|
||||
throw std::runtime_error("CalibrationMaster requires a non-null calibrator");
|
||||
}
|
||||
}
|
||||
|
||||
void CalibrationMaster::load_bundle(const std::string& path) {
|
||||
if (path.empty()) {
|
||||
throw std::runtime_error("Calibration bundle path must not be empty");
|
||||
}
|
||||
|
||||
const auto bytes = read_binary_file(path, "calibration");
|
||||
if (bytes.empty()) {
|
||||
throw std::runtime_error("Calibration bundle is empty: " + path);
|
||||
}
|
||||
|
||||
const auto collection = ipc::deserialize_raw_collection(bytes);
|
||||
standards_by_combo_.clear();
|
||||
standards_by_combo_.reserve(collection.traces.size());
|
||||
for (const auto& trace : collection.traces) {
|
||||
validate_trace_layout(trace, "Calibration standard");
|
||||
standards_by_combo_.insert_or_assign(trace.combo, trace);
|
||||
}
|
||||
|
||||
if (standards_by_combo_.empty()) {
|
||||
throw std::runtime_error("Calibration bundle does not contain traces: " + path);
|
||||
}
|
||||
}
|
||||
|
||||
void CalibrationMaster::validate_combos(const std::vector<ipc::ComboKey>& combos) const {
|
||||
for (const auto& combo : combos) {
|
||||
if (!standards_by_combo_.contains(combo)) {
|
||||
throw std::runtime_error("Calibration data is missing for combo " + combo_to_string(combo));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto CalibrationMaster::apply(const ipc::SweepTraceBlock& measured_trace) const -> ipc::SweepTraceBlock {
|
||||
validate_trace_layout(measured_trace, "Measured trace");
|
||||
|
||||
const auto found = standards_by_combo_.find(measured_trace.combo);
|
||||
if (found == standards_by_combo_.end()) {
|
||||
throw std::runtime_error(
|
||||
"Calibration standard is missing for measured combo " + combo_to_string(measured_trace.combo)
|
||||
);
|
||||
}
|
||||
|
||||
const auto& standard = found->second;
|
||||
validate_trace_layout(standard, "Calibration standard");
|
||||
if (measured_trace.s21.size() != standard.s21.size()) {
|
||||
throw std::runtime_error(
|
||||
"Calibration standard point count mismatch for combo " + combo_to_string(measured_trace.combo)
|
||||
);
|
||||
}
|
||||
|
||||
ipc::SweepTraceBlock output{};
|
||||
output.combo = measured_trace.combo;
|
||||
output.frequency_hz = measured_trace.frequency_hz;
|
||||
output.s21 = calibrator_impl_->apply(measured_trace.s21, standard.s21);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
} // namespace radar::preprocessing
|
||||
@@ -0,0 +1,76 @@
|
||||
#include "calibration_master.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include <Eigen/Core>
|
||||
|
||||
namespace radar::preprocessing {
|
||||
namespace {
|
||||
|
||||
class ThroughCalibrator final : public CalibratorInterface {
|
||||
public:
|
||||
[[nodiscard]] auto name() const -> std::string override {
|
||||
return "through";
|
||||
}
|
||||
|
||||
[[nodiscard]] auto apply(
|
||||
const std::vector<ipc::Complex32>& measured,
|
||||
const std::vector<ipc::Complex32>& calibration
|
||||
) const -> std::vector<ipc::Complex32> override {
|
||||
if (measured.size() != calibration.size()) {
|
||||
throw std::runtime_error("Through calibration vector size mismatch");
|
||||
}
|
||||
|
||||
static_assert(sizeof(ipc::Complex32) == sizeof(float) * 2U, "Complex32 layout must be two contiguous floats");
|
||||
|
||||
constexpr float epsilon = 1e-12F;
|
||||
using InterleavedComplexView = Eigen::Matrix<float, Eigen::Dynamic, 2, Eigen::RowMajor>;
|
||||
|
||||
const auto point_count = static_cast<Eigen::Index>(measured.size());
|
||||
Eigen::Map<const InterleavedComplexView> measured_view(
|
||||
reinterpret_cast<const float*>(measured.data()),
|
||||
point_count,
|
||||
2
|
||||
);
|
||||
Eigen::Map<const InterleavedComplexView> calibration_view(
|
||||
reinterpret_cast<const float*>(calibration.data()),
|
||||
point_count,
|
||||
2
|
||||
);
|
||||
|
||||
const auto measured_re = measured_view.col(0).array();
|
||||
const auto measured_im = measured_view.col(1).array();
|
||||
const auto calibration_re = calibration_view.col(0).array();
|
||||
const auto calibration_im = calibration_view.col(1).array();
|
||||
|
||||
const Eigen::ArrayXf magnitude_squared = calibration_re.square() + calibration_im.square();
|
||||
const auto stable_division_mask = (magnitude_squared > epsilon);
|
||||
|
||||
const Eigen::ArrayXf corrected_re = stable_division_mask.select(
|
||||
((measured_re * calibration_re) + (measured_im * calibration_im)) / magnitude_squared,
|
||||
measured_re
|
||||
);
|
||||
const Eigen::ArrayXf corrected_im = stable_division_mask.select(
|
||||
((measured_im * calibration_re) - (measured_re * calibration_im)) / magnitude_squared,
|
||||
measured_im
|
||||
);
|
||||
|
||||
std::vector<ipc::Complex32> corrected(measured.size());
|
||||
Eigen::Map<InterleavedComplexView> corrected_view(reinterpret_cast<float*>(corrected.data()), point_count, 2);
|
||||
corrected_view.col(0) = corrected_re.matrix();
|
||||
corrected_view.col(1) = corrected_im.matrix();
|
||||
|
||||
return corrected;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
auto make_through_calibrator() -> std::unique_ptr<CalibratorInterface> {
|
||||
return std::make_unique<ThroughCalibrator>();
|
||||
}
|
||||
|
||||
} // namespace radar::preprocessing
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "calibration_master.hpp"
|
||||
#include "reference_master.hpp"
|
||||
#include "run_config.hpp"
|
||||
#include "shm_ring.hpp"
|
||||
|
||||
namespace radar::preprocessing {
|
||||
|
||||
class DataPreprocessor {
|
||||
public:
|
||||
DataPreprocessor(
|
||||
const config::RunConfig& config,
|
||||
CalibrationMaster& calibration_master,
|
||||
ReferenceMaster& reference_master,
|
||||
ipc::ShmRing& raw_ring,
|
||||
ipc::ShmRing& preprocessed_ring,
|
||||
ipc::ShmRing* preprocessed_tap_ring = nullptr
|
||||
);
|
||||
|
||||
// Main processing loop. Runs until stop flag is set.
|
||||
void run(const std::atomic<bool>& stop_requested);
|
||||
|
||||
private:
|
||||
void validate_startup_prerequisites() const;
|
||||
[[nodiscard]] auto try_pop_raw_collection(
|
||||
std::vector<std::uint8_t>& serialized_raw,
|
||||
ipc::RawSweepCollection* out_collection
|
||||
) -> bool;
|
||||
void publish_preprocessed_collection(const ipc::PreprocessedCollection& collection);
|
||||
|
||||
[[nodiscard]] auto preprocess_collection(const ipc::RawSweepCollection& raw_collection) const
|
||||
-> ipc::PreprocessedCollection;
|
||||
|
||||
const config::RunConfig& config_;
|
||||
CalibrationMaster& calibration_master_;
|
||||
ReferenceMaster& reference_master_;
|
||||
ipc::ShmRing& raw_ring_;
|
||||
ipc::ShmRing& preprocessed_ring_;
|
||||
ipc::ShmRing* preprocessed_tap_ring_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace radar::preprocessing
|
||||
@@ -0,0 +1,85 @@
|
||||
#include "data_preprocessor.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace radar::preprocessing {
|
||||
|
||||
DataPreprocessor::DataPreprocessor(
|
||||
const config::RunConfig& config,
|
||||
CalibrationMaster& calibration_master,
|
||||
ReferenceMaster& reference_master,
|
||||
ipc::ShmRing& raw_ring,
|
||||
ipc::ShmRing& preprocessed_ring,
|
||||
ipc::ShmRing* preprocessed_tap_ring
|
||||
)
|
||||
: config_(config),
|
||||
calibration_master_(calibration_master),
|
||||
reference_master_(reference_master),
|
||||
raw_ring_(raw_ring),
|
||||
preprocessed_ring_(preprocessed_ring),
|
||||
preprocessed_tap_ring_(preprocessed_tap_ring) {}
|
||||
|
||||
void DataPreprocessor::validate_startup_prerequisites() const {
|
||||
calibration_master_.validate_combos(config_.run_combos);
|
||||
reference_master_.validate_combos(config_.run_combos);
|
||||
}
|
||||
|
||||
auto DataPreprocessor::try_pop_raw_collection(
|
||||
std::vector<std::uint8_t>& serialized_raw,
|
||||
ipc::RawSweepCollection* out_collection
|
||||
) -> bool {
|
||||
if (!raw_ring_.pop(serialized_raw)) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(config_.runtime.idle_sleep_ms));
|
||||
return false;
|
||||
}
|
||||
|
||||
*out_collection = ipc::deserialize_raw_collection(serialized_raw);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DataPreprocessor::publish_preprocessed_collection(const ipc::PreprocessedCollection& collection) {
|
||||
const auto serialized_preprocessed = ipc::serialize_preprocessed_collection(collection);
|
||||
if (!preprocessed_ring_.push(serialized_preprocessed)) {
|
||||
throw std::runtime_error("Preprocessed ring slot is too small for serialized collection");
|
||||
}
|
||||
if (preprocessed_tap_ring_ != nullptr && !preprocessed_tap_ring_->push(serialized_preprocessed)) {
|
||||
throw std::runtime_error("Preprocessed tap ring slot is too small for serialized collection");
|
||||
}
|
||||
}
|
||||
|
||||
void DataPreprocessor::run(const std::atomic<bool>& stop_requested) {
|
||||
validate_startup_prerequisites();
|
||||
|
||||
std::vector<std::uint8_t> serialized_raw{};
|
||||
ipc::RawSweepCollection raw_collection{};
|
||||
while (!stop_requested.load(std::memory_order_relaxed)) {
|
||||
if (!try_pop_raw_collection(serialized_raw, &raw_collection)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto preprocessed_collection = preprocess_collection(raw_collection);
|
||||
publish_preprocessed_collection(preprocessed_collection);
|
||||
}
|
||||
}
|
||||
|
||||
auto DataPreprocessor::preprocess_collection(const ipc::RawSweepCollection& raw_collection) const
|
||||
-> ipc::PreprocessedCollection {
|
||||
ipc::PreprocessedCollection preprocessed{};
|
||||
preprocessed.collection_id = raw_collection.collection_id;
|
||||
preprocessed.monotonic_ns = ipc::current_monotonic_ns();
|
||||
preprocessed.traces.reserve(raw_collection.traces.size());
|
||||
|
||||
for (const auto& raw_trace : raw_collection.traces) {
|
||||
// Pipeline order is fixed: calibration first, then reference subtraction.
|
||||
const auto calibrated = calibration_master_.apply(raw_trace);
|
||||
const auto referenced = reference_master_.apply(calibrated);
|
||||
preprocessed.traces.push_back(referenced);
|
||||
}
|
||||
|
||||
return preprocessed;
|
||||
}
|
||||
|
||||
} // namespace radar::preprocessing
|
||||
@@ -0,0 +1,86 @@
|
||||
#include <atomic>
|
||||
#include <csignal>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "calibration_master.hpp"
|
||||
#include "data_preprocessor.hpp"
|
||||
#include "reference_master.hpp"
|
||||
#include "run_config.hpp"
|
||||
#include "shm_ring.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char* kDefaultConfigPath = "run_config.json";
|
||||
|
||||
std::atomic<bool> g_stop_requested{false};
|
||||
|
||||
void signal_handler(int /*signal*/) {
|
||||
g_stop_requested.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void install_signal_handlers() {
|
||||
std::signal(SIGINT, signal_handler);
|
||||
std::signal(SIGTERM, signal_handler);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto read_config_path(int argc, char** argv) -> std::string {
|
||||
std::string config_path = kDefaultConfigPath;
|
||||
for (int index = 1; index < argc; ++index) {
|
||||
const std::string arg = argv[index];
|
||||
if (arg == "--config" && (index + 1) < argc) {
|
||||
config_path = argv[++index];
|
||||
}
|
||||
}
|
||||
return config_path;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
install_signal_handlers();
|
||||
|
||||
try {
|
||||
const auto config_path = read_config_path(argc, argv);
|
||||
const auto config = radar::config::load_run_config(config_path);
|
||||
|
||||
auto raw_ring = radar::ipc::ShmRing::open_or_create(
|
||||
config.rings.raw.name,
|
||||
config.rings.raw.capacity,
|
||||
config.rings.raw.slot_size_bytes
|
||||
);
|
||||
auto preprocessed_ring = radar::ipc::ShmRing::open_or_create(
|
||||
config.rings.preprocessed.name,
|
||||
config.rings.preprocessed.capacity,
|
||||
config.rings.preprocessed.slot_size_bytes
|
||||
);
|
||||
auto preprocessed_tap_ring = radar::ipc::ShmRing::open_or_create(
|
||||
config.rings.preprocessed_tap.name,
|
||||
config.rings.preprocessed_tap.capacity,
|
||||
config.rings.preprocessed_tap.slot_size_bytes
|
||||
);
|
||||
|
||||
// Load preprocessing assets once before entering run loop.
|
||||
radar::preprocessing::CalibrationMaster calibration_master(radar::preprocessing::make_through_calibrator());
|
||||
calibration_master.load_bundle(config.preprocess.calibration_bundle_path);
|
||||
|
||||
radar::preprocessing::ReferenceMaster reference_master;
|
||||
reference_master.load_bundle(config.preprocess.reference_bundle_path);
|
||||
reference_master.prepare_calibrated(calibration_master);
|
||||
|
||||
radar::preprocessing::DataPreprocessor preprocessor(
|
||||
config,
|
||||
calibration_master,
|
||||
reference_master,
|
||||
raw_ring,
|
||||
preprocessed_ring,
|
||||
&preprocessed_tap_ring
|
||||
);
|
||||
preprocessor.run(g_stop_requested);
|
||||
return 0;
|
||||
} catch (const std::exception& exception) {
|
||||
std::cerr << "data_preprocessor error: " << exception.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "shared_types.hpp"
|
||||
|
||||
namespace radar::preprocessing {
|
||||
|
||||
class CalibrationMaster;
|
||||
|
||||
class ReferenceMaster {
|
||||
public:
|
||||
// Loads a serialized raw sweep bundle with one reference trace per combo.
|
||||
void load_bundle(const std::string& path);
|
||||
// Builds calibrated references in memory using currently loaded raw references.
|
||||
// Must be called after load_bundle() and after calibration standards are loaded.
|
||||
void prepare_calibrated(const CalibrationMaster& calibration_master);
|
||||
// Ensures all runtime combos are present in loaded references.
|
||||
void validate_combos(const std::vector<ipc::ComboKey>& combos) const;
|
||||
// Subtracts per-combo reference trace from calibrated trace.
|
||||
[[nodiscard]] auto apply(const ipc::SweepTraceBlock& calibrated_trace) const -> ipc::SweepTraceBlock;
|
||||
|
||||
private:
|
||||
std::unordered_map<ipc::ComboKey, ipc::SweepTraceBlock, ipc::ComboKeyHash> raw_references_by_combo_{};
|
||||
std::unordered_map<ipc::ComboKey, ipc::SweepTraceBlock, ipc::ComboKeyHash> calibrated_references_by_combo_{};
|
||||
};
|
||||
|
||||
} // namespace radar::preprocessing
|
||||
@@ -0,0 +1,141 @@
|
||||
#include "reference_master.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <Eigen/Core>
|
||||
|
||||
#include "calibration_master.hpp"
|
||||
|
||||
namespace radar::preprocessing {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] auto combo_to_string(const ipc::ComboKey& combo) -> std::string {
|
||||
return "input=" + std::to_string(combo.input_pos) + " output=" + std::to_string(combo.output_pos);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto read_binary_file(const std::string& path, const std::string& bundle_label)
|
||||
-> std::vector<std::uint8_t> {
|
||||
std::ifstream stream(path, std::ios::binary);
|
||||
if (!stream.is_open()) {
|
||||
throw std::runtime_error("Failed to open " + bundle_label + " bundle: " + path);
|
||||
}
|
||||
|
||||
return std::vector<std::uint8_t>(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
void validate_trace_layout(const ipc::SweepTraceBlock& trace, const std::string& trace_label) {
|
||||
if (trace.frequency_hz.size() != trace.s21.size()) {
|
||||
throw std::runtime_error(
|
||||
trace_label + " frequency/complex vector size mismatch for combo " + combo_to_string(trace.combo)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ReferenceMaster::load_bundle(const std::string& path) {
|
||||
if (path.empty()) {
|
||||
throw std::runtime_error("Reference bundle path must not be empty");
|
||||
}
|
||||
|
||||
const auto bytes = read_binary_file(path, "reference");
|
||||
if (bytes.empty()) {
|
||||
throw std::runtime_error("Reference bundle is empty: " + path);
|
||||
}
|
||||
|
||||
const auto collection = ipc::deserialize_raw_collection(bytes);
|
||||
raw_references_by_combo_.clear();
|
||||
raw_references_by_combo_.reserve(collection.traces.size());
|
||||
calibrated_references_by_combo_.clear();
|
||||
for (const auto& trace : collection.traces) {
|
||||
validate_trace_layout(trace, "Reference trace");
|
||||
raw_references_by_combo_.insert_or_assign(trace.combo, trace);
|
||||
}
|
||||
|
||||
if (raw_references_by_combo_.empty()) {
|
||||
throw std::runtime_error("Reference bundle does not contain traces: " + path);
|
||||
}
|
||||
}
|
||||
|
||||
void ReferenceMaster::prepare_calibrated(const CalibrationMaster& calibration_master) {
|
||||
if (raw_references_by_combo_.empty()) {
|
||||
throw std::runtime_error("Reference bundle must be loaded before prepare_calibrated()");
|
||||
}
|
||||
|
||||
calibrated_references_by_combo_.clear();
|
||||
calibrated_references_by_combo_.reserve(raw_references_by_combo_.size());
|
||||
for (const auto& [combo, raw_reference] : raw_references_by_combo_) {
|
||||
auto calibrated = calibration_master.apply(raw_reference);
|
||||
calibrated.combo = combo;
|
||||
calibrated_references_by_combo_.insert_or_assign(combo, std::move(calibrated));
|
||||
}
|
||||
}
|
||||
|
||||
void ReferenceMaster::validate_combos(const std::vector<ipc::ComboKey>& combos) const {
|
||||
if (calibrated_references_by_combo_.empty()) {
|
||||
throw std::runtime_error(
|
||||
"Calibrated references are not prepared. Call ReferenceMaster::prepare_calibrated() at startup."
|
||||
);
|
||||
}
|
||||
|
||||
for (const auto& combo : combos) {
|
||||
if (!raw_references_by_combo_.contains(combo)) {
|
||||
throw std::runtime_error("Raw reference data is missing for combo " + combo_to_string(combo));
|
||||
}
|
||||
if (!calibrated_references_by_combo_.contains(combo)) {
|
||||
throw std::runtime_error("Calibrated reference data is missing for combo " + combo_to_string(combo));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto ReferenceMaster::apply(const ipc::SweepTraceBlock& calibrated_trace) const -> ipc::SweepTraceBlock {
|
||||
validate_trace_layout(calibrated_trace, "Calibrated trace");
|
||||
|
||||
const auto found = calibrated_references_by_combo_.find(calibrated_trace.combo);
|
||||
if (found == calibrated_references_by_combo_.end()) {
|
||||
throw std::runtime_error(
|
||||
"Calibrated reference trace is missing for combo " + combo_to_string(calibrated_trace.combo)
|
||||
);
|
||||
}
|
||||
|
||||
const auto& reference = found->second;
|
||||
validate_trace_layout(reference, "Calibrated reference trace");
|
||||
if (calibrated_trace.s21.size() != reference.s21.size()) {
|
||||
throw std::runtime_error(
|
||||
"Reference point count mismatch for combo " + combo_to_string(calibrated_trace.combo)
|
||||
);
|
||||
}
|
||||
|
||||
ipc::SweepTraceBlock output{};
|
||||
output.combo = calibrated_trace.combo;
|
||||
output.frequency_hz = calibrated_trace.frequency_hz;
|
||||
output.s21.resize(calibrated_trace.s21.size());
|
||||
|
||||
static_assert(sizeof(ipc::Complex32) == sizeof(float) * 2U, "Complex32 layout must be two contiguous floats");
|
||||
using InterleavedComplexView = Eigen::Matrix<float, Eigen::Dynamic, 2, Eigen::RowMajor>;
|
||||
const auto point_count = static_cast<Eigen::Index>(calibrated_trace.s21.size());
|
||||
|
||||
Eigen::Map<const InterleavedComplexView> calibrated_view(
|
||||
reinterpret_cast<const float*>(calibrated_trace.s21.data()),
|
||||
point_count,
|
||||
2
|
||||
);
|
||||
Eigen::Map<const InterleavedComplexView> reference_view(
|
||||
reinterpret_cast<const float*>(reference.s21.data()),
|
||||
point_count,
|
||||
2
|
||||
);
|
||||
Eigen::Map<InterleavedComplexView> output_view(reinterpret_cast<float*>(output.s21.data()), point_count, 2);
|
||||
|
||||
output_view = calibrated_view - reference_view;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
} // namespace radar::preprocessing
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "processor_interface.hpp"
|
||||
#include "processing_live_config.hpp"
|
||||
#include "run_config.hpp"
|
||||
#include "shm_ring.hpp"
|
||||
|
||||
namespace radar::processing {
|
||||
|
||||
using ProcessorRegistry = std::unordered_map<std::string, std::unique_ptr<ProcessorInterface>>;
|
||||
|
||||
class DataProcessor {
|
||||
public:
|
||||
DataProcessor(
|
||||
const config::RunConfig& config,
|
||||
ipc::ShmRing& preprocessed_ring,
|
||||
ipc::ShmRing& results_ring,
|
||||
ProcessorRegistry processors
|
||||
);
|
||||
|
||||
void run(const std::atomic<bool>& stop_requested);
|
||||
|
||||
private:
|
||||
[[nodiscard]] auto process_collection(
|
||||
const ipc::PreprocessedCollection& preprocessed,
|
||||
ProcessorInterface& processor,
|
||||
const ProcessingLiveConfig& live_config
|
||||
) -> ipc::ResultCollection;
|
||||
|
||||
[[nodiscard]] auto resolve_processor(const ProcessingLiveConfig& live_config) -> ProcessorInterface&;
|
||||
|
||||
const config::RunConfig& config_;
|
||||
ipc::ShmRing& preprocessed_ring_;
|
||||
ipc::ShmRing& results_ring_;
|
||||
ProcessorRegistry processors_{};
|
||||
std::string default_processor_mode_{};
|
||||
ProcessingLiveConfigLoader live_config_loader_;
|
||||
};
|
||||
|
||||
[[nodiscard]] auto create_default_processors() -> ProcessorRegistry;
|
||||
|
||||
} // namespace radar::processing
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
namespace radar::processing {
|
||||
|
||||
enum class HistoryCommand {
|
||||
None,
|
||||
RemoveLast,
|
||||
ClearAll,
|
||||
};
|
||||
|
||||
struct ProcessingLiveConfig {
|
||||
std::string processor_mode = "pass_through";
|
||||
float gain_db = 0.0F;
|
||||
float phase_deg = 0.0F;
|
||||
std::string bscan_axis = "abs";
|
||||
float bscan_cut_m = 0.824F;
|
||||
float bscan_max_depth_m = 1.0F;
|
||||
float bscan_gain = 1.0F;
|
||||
float bscan_start_freq_mhz = 100.0F;
|
||||
float bscan_stop_freq_mhz = 8800.0F;
|
||||
std::uint64_t history_command_seq = 0;
|
||||
HistoryCommand history_command = HistoryCommand::None;
|
||||
};
|
||||
|
||||
class ProcessingLiveConfigLoader {
|
||||
public:
|
||||
explicit ProcessingLiveConfigLoader(std::string path);
|
||||
|
||||
[[nodiscard]] auto current() const -> ProcessingLiveConfig;
|
||||
[[nodiscard]] auto refresh_if_needed() -> ProcessingLiveConfig;
|
||||
[[nodiscard]] auto revision() const -> std::uint64_t;
|
||||
|
||||
private:
|
||||
[[nodiscard]] auto read_from_file() const -> ProcessingLiveConfig;
|
||||
|
||||
std::string path_{};
|
||||
ProcessingLiveConfig current_{};
|
||||
std::filesystem::file_time_type last_write_time_{};
|
||||
bool has_last_write_time_ = false;
|
||||
std::uint64_t revision_ = 0;
|
||||
std::chrono::steady_clock::time_point next_check_at_{};
|
||||
};
|
||||
|
||||
} // namespace radar::processing
|
||||
@@ -0,0 +1,156 @@
|
||||
#include "data_processor.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#include "bscan_processor.hpp"
|
||||
#include "passthrough_processor.hpp"
|
||||
|
||||
namespace radar::processing {
|
||||
namespace {
|
||||
|
||||
constexpr const char* kDefaultProcessorMode = "pass_through";
|
||||
constexpr std::size_t kBscanReplayWindow = 50U;
|
||||
|
||||
[[nodiscard]] auto replay_history_limit(const config::RunConfig& config) -> std::size_t {
|
||||
const auto preprocessed_capacity = static_cast<std::size_t>(std::max<std::uint32_t>(1U, config.rings.preprocessed.capacity));
|
||||
const auto results_capacity = static_cast<std::size_t>(std::max<std::uint32_t>(1U, config.rings.results.capacity));
|
||||
return std::min({preprocessed_capacity, results_capacity, kBscanReplayWindow});
|
||||
}
|
||||
|
||||
void publish_result_collection(const ipc::ResultCollection& collection, ipc::ShmRing& results_ring) {
|
||||
const auto out_bytes = ipc::serialize_result_collection(collection);
|
||||
if (!results_ring.push(out_bytes)) {
|
||||
throw std::runtime_error("Results ring slot is too small for serialized collection");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DataProcessor::DataProcessor(
|
||||
const config::RunConfig& config,
|
||||
ipc::ShmRing& preprocessed_ring,
|
||||
ipc::ShmRing& results_ring,
|
||||
ProcessorRegistry processors
|
||||
)
|
||||
: config_(config),
|
||||
preprocessed_ring_(preprocessed_ring),
|
||||
results_ring_(results_ring),
|
||||
processors_(std::move(processors)),
|
||||
default_processor_mode_(kDefaultProcessorMode),
|
||||
live_config_loader_(config.runtime.processing_live_config_path) {
|
||||
if (processors_.empty()) {
|
||||
throw std::runtime_error("DataProcessor requires at least one processor");
|
||||
}
|
||||
if (const auto found = processors_.find(default_processor_mode_); found == processors_.end()) {
|
||||
default_processor_mode_ = processors_.begin()->first;
|
||||
}
|
||||
}
|
||||
|
||||
void DataProcessor::run(const std::atomic<bool>& stop_requested) {
|
||||
std::vector<std::uint8_t> bytes{};
|
||||
std::deque<ipc::PreprocessedCollection> preprocessed_history{};
|
||||
const std::size_t history_limit = replay_history_limit(config_);
|
||||
std::uint64_t last_replayed_revision = live_config_loader_.revision();
|
||||
std::uint64_t last_applied_history_command_seq = 0;
|
||||
|
||||
while (!stop_requested.load(std::memory_order_relaxed)) {
|
||||
const auto live_config = live_config_loader_.refresh_if_needed();
|
||||
const auto live_revision = live_config_loader_.revision();
|
||||
auto& processor = resolve_processor(live_config);
|
||||
|
||||
if (live_revision != last_replayed_revision) {
|
||||
if (live_config.history_command_seq > last_applied_history_command_seq) {
|
||||
if (live_config.history_command == HistoryCommand::RemoveLast) {
|
||||
if (!preprocessed_history.empty()) {
|
||||
preprocessed_history.pop_back();
|
||||
}
|
||||
} else if (live_config.history_command == HistoryCommand::ClearAll) {
|
||||
preprocessed_history.clear();
|
||||
}
|
||||
last_applied_history_command_seq = live_config.history_command_seq;
|
||||
}
|
||||
|
||||
if (live_config.processor_mode == "bscan") {
|
||||
for (const auto& cached : preprocessed_history) {
|
||||
const auto replay_result = process_collection(cached, processor, live_config);
|
||||
publish_result_collection(replay_result, results_ring_);
|
||||
}
|
||||
} else if (!preprocessed_history.empty()) {
|
||||
const auto replay_result = process_collection(preprocessed_history.back(), processor, live_config);
|
||||
publish_result_collection(replay_result, results_ring_);
|
||||
}
|
||||
last_replayed_revision = live_revision;
|
||||
}
|
||||
|
||||
if (preprocessed_ring_.pop(bytes)) {
|
||||
auto preprocessed = ipc::deserialize_preprocessed_collection(bytes);
|
||||
preprocessed_history.push_back(std::move(preprocessed));
|
||||
while (preprocessed_history.size() > history_limit) {
|
||||
preprocessed_history.pop_front();
|
||||
}
|
||||
|
||||
const auto result_collection = process_collection(preprocessed_history.back(), processor, live_config);
|
||||
publish_result_collection(result_collection, results_ring_);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(config_.runtime.idle_sleep_ms));
|
||||
}
|
||||
}
|
||||
|
||||
auto DataProcessor::process_collection(
|
||||
const ipc::PreprocessedCollection& preprocessed,
|
||||
ProcessorInterface& processor,
|
||||
const ProcessingLiveConfig& live_config
|
||||
) -> ipc::ResultCollection {
|
||||
ipc::ResultCollection results{};
|
||||
results.collection_id = preprocessed.collection_id;
|
||||
// Keep source monotonic timestamp stable across live-config replays.
|
||||
results.monotonic_ns = preprocessed.monotonic_ns;
|
||||
results.blocks.reserve(preprocessed.traces.size());
|
||||
|
||||
for (const auto& trace : preprocessed.traces) {
|
||||
ipc::ResultBlock block{};
|
||||
block.combo = trace.combo;
|
||||
block.payloads.reserve(1U);
|
||||
block.payloads.push_back(processor.process(trace, live_config));
|
||||
|
||||
results.blocks.push_back(std::move(block));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
auto DataProcessor::resolve_processor(const ProcessingLiveConfig& live_config) -> ProcessorInterface& {
|
||||
const std::string requested_mode =
|
||||
live_config.processor_mode.empty() ? default_processor_mode_ : live_config.processor_mode;
|
||||
|
||||
if (auto found = processors_.find(requested_mode); found != processors_.end()) {
|
||||
return *(found->second);
|
||||
}
|
||||
if (auto fallback = processors_.find(default_processor_mode_); fallback != processors_.end()) {
|
||||
return *(fallback->second);
|
||||
}
|
||||
return *(processors_.begin()->second);
|
||||
}
|
||||
|
||||
auto create_default_processors() -> ProcessorRegistry {
|
||||
ProcessorRegistry processors{};
|
||||
{
|
||||
auto processor = std::make_unique<PassThroughProcessor>();
|
||||
processors.emplace(processor->name(), std::move(processor));
|
||||
}
|
||||
{
|
||||
auto processor = std::make_unique<BScanProcessor>();
|
||||
processors.emplace(processor->name(), std::move(processor));
|
||||
}
|
||||
return processors;
|
||||
}
|
||||
|
||||
} // namespace radar::processing
|
||||
@@ -0,0 +1,69 @@
|
||||
#include <atomic>
|
||||
#include <csignal>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "data_processor.hpp"
|
||||
#include "run_config.hpp"
|
||||
#include "shm_ring.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char* kDefaultConfigPath = "run_config.json";
|
||||
|
||||
std::atomic<bool> g_stop_requested{false};
|
||||
|
||||
void signal_handler(int /*signal*/) {
|
||||
g_stop_requested.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void install_signal_handlers() {
|
||||
std::signal(SIGINT, signal_handler);
|
||||
std::signal(SIGTERM, signal_handler);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto read_config_path(int argc, char** argv) -> std::string {
|
||||
std::string config_path = kDefaultConfigPath;
|
||||
for (int index = 1; index < argc; ++index) {
|
||||
const std::string arg = argv[index];
|
||||
if (arg == "--config" && (index + 1) < argc) {
|
||||
config_path = argv[++index];
|
||||
}
|
||||
}
|
||||
return config_path;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
install_signal_handlers();
|
||||
|
||||
try {
|
||||
const auto config_path = read_config_path(argc, argv);
|
||||
const auto config = radar::config::load_run_config(config_path);
|
||||
|
||||
auto preprocessed_ring = radar::ipc::ShmRing::open_or_create(
|
||||
config.rings.preprocessed.name,
|
||||
config.rings.preprocessed.capacity,
|
||||
config.rings.preprocessed.slot_size_bytes
|
||||
);
|
||||
auto results_ring = radar::ipc::ShmRing::open_or_create(
|
||||
config.rings.results.name,
|
||||
config.rings.results.capacity,
|
||||
config.rings.results.slot_size_bytes
|
||||
);
|
||||
|
||||
radar::processing::DataProcessor processor(
|
||||
config,
|
||||
preprocessed_ring,
|
||||
results_ring,
|
||||
radar::processing::create_default_processors()
|
||||
);
|
||||
processor.run(g_stop_requested);
|
||||
return 0;
|
||||
} catch (const std::exception& exception) {
|
||||
std::cerr << "data_processor error: " << exception.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
#include "processing_live_config.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace radar::processing {
|
||||
namespace {
|
||||
|
||||
using Json = nlohmann::json;
|
||||
|
||||
[[nodiscard]] auto parse_history_command(const std::string& value) -> HistoryCommand {
|
||||
if (value == "none") {
|
||||
return HistoryCommand::None;
|
||||
}
|
||||
if (value == "remove_last") {
|
||||
return HistoryCommand::RemoveLast;
|
||||
}
|
||||
if (value == "clear_all") {
|
||||
return HistoryCommand::ClearAll;
|
||||
}
|
||||
throw std::runtime_error("processing.history_command must be one of: none, remove_last, clear_all");
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_u64_number(const Json& value, const std::string& field_name) -> std::uint64_t {
|
||||
if (!value.is_number()) {
|
||||
throw std::runtime_error(field_name + " must be number");
|
||||
}
|
||||
const double numeric = value.get<double>();
|
||||
if (numeric < 0.0 || numeric > static_cast<double>(std::numeric_limits<std::uint64_t>::max())) {
|
||||
throw std::runtime_error(field_name + " is out of uint64 range");
|
||||
}
|
||||
const double rounded = std::round(numeric);
|
||||
if (std::fabs(numeric - rounded) > 1e-6) {
|
||||
throw std::runtime_error(field_name + " must be integer");
|
||||
}
|
||||
return static_cast<std::uint64_t>(rounded);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_live_config(const std::string& json_text, const std::string& path) -> ProcessingLiveConfig {
|
||||
if (json_text.empty()) {
|
||||
throw std::runtime_error("Processing live config is empty: " + path);
|
||||
}
|
||||
|
||||
Json root{};
|
||||
try {
|
||||
root = Json::parse(json_text);
|
||||
} catch (const Json::parse_error& error) {
|
||||
throw std::runtime_error("Failed to parse processing live config: " + std::string(error.what()));
|
||||
}
|
||||
|
||||
if (!root.is_object()) {
|
||||
throw std::runtime_error("Processing live config root must be JSON object");
|
||||
}
|
||||
|
||||
ProcessingLiveConfig config{};
|
||||
if (const auto found = root.find("processor_mode"); found != root.end()) {
|
||||
if (!found->is_string()) {
|
||||
throw std::runtime_error("processing.processor_mode must be string");
|
||||
}
|
||||
config.processor_mode = found->get<std::string>();
|
||||
}
|
||||
if (const auto found = root.find("gain_db"); found != root.end()) {
|
||||
if (!found->is_number()) {
|
||||
throw std::runtime_error("processing.gain_db must be number");
|
||||
}
|
||||
config.gain_db = static_cast<float>(found->get<double>());
|
||||
}
|
||||
if (const auto found = root.find("phase_deg"); found != root.end()) {
|
||||
if (!found->is_number()) {
|
||||
throw std::runtime_error("processing.phase_deg must be number");
|
||||
}
|
||||
config.phase_deg = static_cast<float>(found->get<double>());
|
||||
}
|
||||
if (const auto found = root.find("bscan_axis"); found != root.end()) {
|
||||
if (!found->is_string()) {
|
||||
throw std::runtime_error("processing.bscan_axis must be string");
|
||||
}
|
||||
config.bscan_axis = found->get<std::string>();
|
||||
}
|
||||
if (const auto found = root.find("bscan_cut_m"); found != root.end()) {
|
||||
if (!found->is_number()) {
|
||||
throw std::runtime_error("processing.bscan_cut_m must be number");
|
||||
}
|
||||
config.bscan_cut_m = static_cast<float>(found->get<double>());
|
||||
}
|
||||
if (const auto found = root.find("bscan_max_depth_m"); found != root.end()) {
|
||||
if (!found->is_number()) {
|
||||
throw std::runtime_error("processing.bscan_max_depth_m must be number");
|
||||
}
|
||||
config.bscan_max_depth_m = static_cast<float>(found->get<double>());
|
||||
}
|
||||
if (const auto found = root.find("bscan_gain"); found != root.end()) {
|
||||
if (!found->is_number()) {
|
||||
throw std::runtime_error("processing.bscan_gain must be number");
|
||||
}
|
||||
config.bscan_gain = static_cast<float>(found->get<double>());
|
||||
}
|
||||
if (const auto found = root.find("bscan_start_freq_mhz"); found != root.end()) {
|
||||
if (!found->is_number()) {
|
||||
throw std::runtime_error("processing.bscan_start_freq_mhz must be number");
|
||||
}
|
||||
config.bscan_start_freq_mhz = static_cast<float>(found->get<double>());
|
||||
}
|
||||
if (const auto found = root.find("bscan_stop_freq_mhz"); found != root.end()) {
|
||||
if (!found->is_number()) {
|
||||
throw std::runtime_error("processing.bscan_stop_freq_mhz must be number");
|
||||
}
|
||||
config.bscan_stop_freq_mhz = static_cast<float>(found->get<double>());
|
||||
}
|
||||
if (const auto found = root.find("history_command_seq"); found != root.end()) {
|
||||
config.history_command_seq = parse_u64_number(*found, "processing.history_command_seq");
|
||||
}
|
||||
if (const auto found = root.find("history_command"); found != root.end()) {
|
||||
if (!found->is_string()) {
|
||||
throw std::runtime_error("processing.history_command must be string");
|
||||
}
|
||||
config.history_command = parse_history_command(found->get<std::string>());
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ProcessingLiveConfigLoader::ProcessingLiveConfigLoader(std::string path)
|
||||
: path_(std::move(path)),
|
||||
next_check_at_(std::chrono::steady_clock::now()) {
|
||||
if (path_.empty()) {
|
||||
throw std::runtime_error("Processing live config path must not be empty");
|
||||
}
|
||||
}
|
||||
|
||||
auto ProcessingLiveConfigLoader::current() const -> ProcessingLiveConfig {
|
||||
return current_;
|
||||
}
|
||||
|
||||
auto ProcessingLiveConfigLoader::revision() const -> std::uint64_t {
|
||||
return revision_;
|
||||
}
|
||||
|
||||
auto ProcessingLiveConfigLoader::refresh_if_needed() -> ProcessingLiveConfig {
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
if (now < next_check_at_) {
|
||||
return current_;
|
||||
}
|
||||
next_check_at_ = now + std::chrono::milliseconds(100);
|
||||
|
||||
std::error_code time_error{};
|
||||
const auto file_time = std::filesystem::last_write_time(path_, time_error);
|
||||
if (time_error) {
|
||||
return current_;
|
||||
}
|
||||
|
||||
if (has_last_write_time_ && file_time == last_write_time_) {
|
||||
return current_;
|
||||
}
|
||||
|
||||
try {
|
||||
current_ = read_from_file();
|
||||
last_write_time_ = file_time;
|
||||
has_last_write_time_ = true;
|
||||
++revision_;
|
||||
} catch (const std::exception& error) {
|
||||
std::cerr << "data_processor warning: failed to refresh live processing config: " << error.what() << '\n';
|
||||
}
|
||||
|
||||
return current_;
|
||||
}
|
||||
|
||||
auto ProcessingLiveConfigLoader::read_from_file() const -> ProcessingLiveConfig {
|
||||
std::ifstream stream(path_);
|
||||
if (!stream.is_open()) {
|
||||
throw std::runtime_error("Failed to open live processing config: " + path_);
|
||||
}
|
||||
|
||||
std::stringstream buffer{};
|
||||
buffer << stream.rdbuf();
|
||||
return parse_live_config(buffer.str(), path_);
|
||||
}
|
||||
|
||||
} // namespace radar::processing
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "processor_interface.hpp"
|
||||
|
||||
namespace radar::processing {
|
||||
|
||||
class BScanProcessor final : public ProcessorInterface {
|
||||
public:
|
||||
[[nodiscard]] auto name() const -> std::string override;
|
||||
[[nodiscard]] auto process(
|
||||
const ipc::SweepTraceBlock& trace,
|
||||
const ProcessingLiveConfig& live_config
|
||||
) -> ipc::ResultPayload override;
|
||||
};
|
||||
|
||||
} // namespace radar::processing
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "processor_interface.hpp"
|
||||
|
||||
namespace radar::processing {
|
||||
|
||||
class PassThroughProcessor final : public ProcessorInterface {
|
||||
public:
|
||||
[[nodiscard]] auto name() const -> std::string override;
|
||||
[[nodiscard]] auto process(
|
||||
const ipc::SweepTraceBlock& trace,
|
||||
const ProcessingLiveConfig& live_config
|
||||
) -> ipc::ResultPayload override;
|
||||
};
|
||||
|
||||
} // namespace radar::processing
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "processing_live_config.hpp"
|
||||
#include "shared_types.hpp"
|
||||
|
||||
namespace radar::processing {
|
||||
|
||||
class ProcessorInterface {
|
||||
public:
|
||||
virtual ~ProcessorInterface() = default;
|
||||
|
||||
[[nodiscard]] virtual auto name() const -> std::string = 0;
|
||||
[[nodiscard]] virtual auto process(
|
||||
const ipc::SweepTraceBlock& trace,
|
||||
const ProcessingLiveConfig& live_config
|
||||
) -> ipc::ResultPayload = 0;
|
||||
};
|
||||
|
||||
} // namespace radar::processing
|
||||
@@ -0,0 +1,237 @@
|
||||
#include "bscan_processor.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <complex>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace radar::processing {
|
||||
namespace {
|
||||
|
||||
constexpr double kPi = 3.14159265358979323846;
|
||||
constexpr double kSpeedOfLightMetersPerSec = 299'792'458.0;
|
||||
|
||||
struct BScanProfile {
|
||||
std::vector<float> depth_m{};
|
||||
std::vector<float> response{};
|
||||
};
|
||||
|
||||
[[nodiscard]] auto next_power_of_two(std::size_t value) -> std::size_t {
|
||||
if (value <= 1U) {
|
||||
return 1U;
|
||||
}
|
||||
|
||||
std::size_t power = 1U;
|
||||
while (power < value) {
|
||||
if (power > (std::numeric_limits<std::size_t>::max() >> 1U)) {
|
||||
return value;
|
||||
}
|
||||
power <<= 1U;
|
||||
}
|
||||
return power;
|
||||
}
|
||||
|
||||
void fft_inplace(std::vector<std::complex<double>>& values, bool inverse) {
|
||||
const std::size_t size = values.size();
|
||||
if (size <= 1U) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::size_t index = 1U, bit_reversed = 0U; index < size; ++index) {
|
||||
std::size_t bit = size >> 1U;
|
||||
while (bit_reversed & bit) {
|
||||
bit_reversed ^= bit;
|
||||
bit >>= 1U;
|
||||
}
|
||||
bit_reversed ^= bit;
|
||||
if (index < bit_reversed) {
|
||||
std::swap(values[index], values[bit_reversed]);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t len = 2U; len <= size; len <<= 1U) {
|
||||
const double angle = 2.0 * kPi * (inverse ? 1.0 : -1.0) / static_cast<double>(len);
|
||||
const std::complex<double> twiddle_step(std::cos(angle), std::sin(angle));
|
||||
const std::size_t half_len = len >> 1U;
|
||||
|
||||
for (std::size_t offset = 0U; offset < size; offset += len) {
|
||||
std::complex<double> twiddle(1.0, 0.0);
|
||||
for (std::size_t i = 0U; i < half_len; ++i) {
|
||||
const auto even = values[offset + i];
|
||||
const auto odd = values[offset + i + half_len] * twiddle;
|
||||
|
||||
values[offset + i] = even + odd;
|
||||
values[offset + i + half_len] = even - odd;
|
||||
twiddle *= twiddle_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!inverse) {
|
||||
return;
|
||||
}
|
||||
|
||||
const double scale = 1.0 / static_cast<double>(size);
|
||||
for (auto& value : values) {
|
||||
value *= scale;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto select_axis(std::complex<double> sample, std::string_view axis) -> double {
|
||||
if (axis == "real") {
|
||||
return std::real(sample);
|
||||
}
|
||||
if (axis == "phase") {
|
||||
return std::arg(sample);
|
||||
}
|
||||
return std::abs(sample);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto fallback_profile(const ipc::SweepTraceBlock& trace) -> BScanProfile {
|
||||
const std::size_t point_count = std::min(trace.frequency_hz.size(), trace.s21.size());
|
||||
BScanProfile fallback{};
|
||||
fallback.depth_m.reserve(point_count);
|
||||
fallback.response.reserve(point_count);
|
||||
|
||||
const float denominator = point_count > 1U ? static_cast<float>(point_count - 1U) : 1.0F;
|
||||
for (std::size_t index = 0U; index < point_count; ++index) {
|
||||
const auto& sample = trace.s21[index];
|
||||
fallback.depth_m.push_back(static_cast<float>(static_cast<float>(index) / denominator));
|
||||
fallback.response.push_back(std::sqrt(sample.re * sample.re + sample.im * sample.im));
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto compute_bscan_profile(
|
||||
const ipc::SweepTraceBlock& trace,
|
||||
const ProcessingLiveConfig& live_config
|
||||
) -> BScanProfile {
|
||||
const std::size_t point_count = std::min(trace.frequency_hz.size(), trace.s21.size());
|
||||
if (point_count < 2U) {
|
||||
return fallback_profile(trace);
|
||||
}
|
||||
|
||||
const double configured_start_hz = static_cast<double>(live_config.bscan_start_freq_mhz) * 1'000'000.0;
|
||||
const double configured_stop_hz = static_cast<double>(live_config.bscan_stop_freq_mhz) * 1'000'000.0;
|
||||
const double start_hz = std::min(configured_start_hz, configured_stop_hz);
|
||||
const double stop_hz = std::max(configured_start_hz, configured_stop_hz);
|
||||
|
||||
std::vector<double> filtered_freq_hz{};
|
||||
std::vector<std::complex<double>> filtered_s21{};
|
||||
filtered_freq_hz.reserve(point_count);
|
||||
filtered_s21.reserve(point_count);
|
||||
|
||||
for (std::size_t index = 0U; index < point_count; ++index) {
|
||||
const double frequency_hz = static_cast<double>(trace.frequency_hz[index]);
|
||||
if (frequency_hz < start_hz || frequency_hz > stop_hz) {
|
||||
continue;
|
||||
}
|
||||
const auto& sample = trace.s21[index];
|
||||
filtered_freq_hz.push_back(frequency_hz);
|
||||
filtered_s21.emplace_back(static_cast<double>(sample.re), static_cast<double>(sample.im));
|
||||
}
|
||||
|
||||
if (filtered_freq_hz.size() < 2U) {
|
||||
return fallback_profile(trace);
|
||||
}
|
||||
|
||||
const std::size_t filtered_count = filtered_freq_hz.size();
|
||||
const double df = (filtered_freq_hz.back() - filtered_freq_hz.front()) / static_cast<double>(filtered_count - 1U);
|
||||
if (df <= 0.0) {
|
||||
return fallback_profile(trace);
|
||||
}
|
||||
|
||||
const auto start_bin = static_cast<std::int64_t>(std::llround(filtered_freq_hz.front() / df));
|
||||
if (start_bin < 0) {
|
||||
return fallback_profile(trace);
|
||||
}
|
||||
|
||||
const auto start_index = static_cast<std::size_t>(start_bin);
|
||||
if (start_index > (std::numeric_limits<std::size_t>::max() / 2U)) {
|
||||
return fallback_profile(trace);
|
||||
}
|
||||
if (start_index > (std::numeric_limits<std::size_t>::max() - filtered_count + 1U)) {
|
||||
return fallback_profile(trace);
|
||||
}
|
||||
|
||||
const std::size_t min_fft_len = 2U * (start_index + filtered_count - 1U);
|
||||
const std::size_t fft_len = next_power_of_two(min_fft_len);
|
||||
if (fft_len < min_fft_len || (fft_len & (fft_len - 1U)) != 0U) {
|
||||
return fallback_profile(trace);
|
||||
}
|
||||
if (start_index > fft_len || filtered_count > (fft_len - start_index)) {
|
||||
return fallback_profile(trace);
|
||||
}
|
||||
|
||||
std::vector<std::complex<double>> spectrum(fft_len, std::complex<double>(0.0, 0.0));
|
||||
for (std::size_t index = 0U; index < filtered_count; ++index) {
|
||||
spectrum[start_index + index] = filtered_s21[index];
|
||||
}
|
||||
|
||||
fft_inplace(spectrum, true);
|
||||
|
||||
const double dt = 1.0 / (static_cast<double>(fft_len) * df);
|
||||
const double cut_m = std::max(0.0, static_cast<double>(live_config.bscan_cut_m));
|
||||
const double max_depth_m = std::max(0.0, static_cast<double>(live_config.bscan_max_depth_m));
|
||||
const double gain = static_cast<double>(live_config.bscan_gain);
|
||||
const double window_start = 2.0 * cut_m;
|
||||
const double window_stop = window_start + (2.0 * max_depth_m);
|
||||
|
||||
BScanProfile profile{};
|
||||
profile.depth_m.reserve(spectrum.size());
|
||||
profile.response.reserve(spectrum.size());
|
||||
|
||||
const std::string_view axis = live_config.bscan_axis;
|
||||
for (std::size_t index = 0U; index < spectrum.size(); ++index) {
|
||||
const double depth_raw = static_cast<double>(index) * dt * kSpeedOfLightMetersPerSec;
|
||||
if (depth_raw < window_start || depth_raw > window_stop) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const double one_way_depth = (depth_raw - window_start) / 2.0;
|
||||
double gain_shape = std::pow(one_way_depth, gain);
|
||||
if (!std::isfinite(gain_shape)) {
|
||||
gain_shape = 0.0;
|
||||
}
|
||||
|
||||
const double axis_value = select_axis(spectrum[index], axis);
|
||||
const double output_value = axis_value * gain_shape;
|
||||
|
||||
profile.depth_m.push_back(static_cast<float>(one_way_depth));
|
||||
profile.response.push_back(static_cast<float>(output_value));
|
||||
}
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto BScanProcessor::name() const -> std::string {
|
||||
return "bscan";
|
||||
}
|
||||
|
||||
auto BScanProcessor::process(const ipc::SweepTraceBlock& trace, const ProcessingLiveConfig& live_config)
|
||||
-> ipc::ResultPayload {
|
||||
ipc::ResultPayload payload{};
|
||||
payload.processing_name = name();
|
||||
payload.kind = ipc::ResultKind::TraceComplex;
|
||||
|
||||
auto profile = compute_bscan_profile(trace, live_config);
|
||||
payload.frequency_hz = std::move(profile.depth_m);
|
||||
payload.trace.reserve(profile.response.size());
|
||||
for (const auto value : profile.response) {
|
||||
payload.trace.push_back(ipc::Complex32{
|
||||
.re = value,
|
||||
.im = 0.0F,
|
||||
});
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
} // namespace radar::processing
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "passthrough_processor.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace radar::processing {
|
||||
namespace {
|
||||
|
||||
constexpr float kPi = 3.14159265358979323846F;
|
||||
|
||||
} // namespace
|
||||
|
||||
auto PassThroughProcessor::name() const -> std::string {
|
||||
return "pass_through";
|
||||
}
|
||||
|
||||
auto PassThroughProcessor::process(const ipc::SweepTraceBlock& trace, const ProcessingLiveConfig& live_config)
|
||||
-> ipc::ResultPayload {
|
||||
ipc::ResultPayload payload{};
|
||||
payload.processing_name = name();
|
||||
payload.kind = ipc::ResultKind::TraceComplex;
|
||||
payload.frequency_hz = trace.frequency_hz;
|
||||
payload.trace = trace.s21;
|
||||
|
||||
const float linear_gain = std::pow(10.0F, live_config.gain_db / 20.0F);
|
||||
const float phase_rad = live_config.phase_deg * (kPi / 180.0F);
|
||||
const float cos_phase = std::cos(phase_rad);
|
||||
const float sin_phase = std::sin(phase_rad);
|
||||
|
||||
for (auto& sample : payload.trace) {
|
||||
const float re = sample.re;
|
||||
const float im = sample.im;
|
||||
sample.re = linear_gain * ((re * cos_phase) - (im * sin_phase));
|
||||
sample.im = linear_gain * ((re * sin_phase) + (im * cos_phase));
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
} // namespace radar::processing
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "shared_types.hpp"
|
||||
|
||||
namespace radar::drivers {
|
||||
|
||||
/**
|
||||
* @brief One S21 sweep acquired from the radar.
|
||||
*
|
||||
* Both vectors must have equal size and aligned indices.
|
||||
*/
|
||||
struct SweepTrace {
|
||||
std::vector<float> frequency_hz{};
|
||||
std::vector<ipc::Complex32> s21{};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Minimal radar interface used by the sweep orchestrator.
|
||||
*
|
||||
* Implementations are expected to be lightweight: configuration is handled by
|
||||
* the Python layer, while this interface only opens/closes and acquires S21.
|
||||
*/
|
||||
class RadarDriver {
|
||||
public:
|
||||
virtual ~RadarDriver() = default;
|
||||
|
||||
/** @brief Open underlying transport and prepare acquisition. */
|
||||
virtual void open() = 0;
|
||||
/** @brief Release all allocated resources. */
|
||||
virtual void close() = 0;
|
||||
/** @brief Acquire one S21 sweep. */
|
||||
[[nodiscard]] virtual auto acquire_s21_sweep() -> SweepTrace = 0;
|
||||
};
|
||||
|
||||
} // namespace radar::drivers
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace radar::drivers {
|
||||
|
||||
/**
|
||||
* @brief Unified RF switch interface consumed by the orchestrator.
|
||||
*/
|
||||
class SwitchDriver {
|
||||
public:
|
||||
virtual ~SwitchDriver() = default;
|
||||
|
||||
/** @brief Open underlying transport and set initial switch state. */
|
||||
virtual void open() = 0;
|
||||
/** @brief Release driver resources. */
|
||||
virtual void close() = 0;
|
||||
/** @brief Number of selectable positions exposed by this switch. */
|
||||
[[nodiscard]] virtual auto position_count() const -> std::uint32_t = 0;
|
||||
/** @brief Switch to a 0-based position. */
|
||||
virtual void switch_to(std::uint32_t position) = 0;
|
||||
};
|
||||
|
||||
} // namespace radar::drivers
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
#include "../librevna_minimal_driver.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "librevna_protocol_common.hpp"
|
||||
|
||||
namespace radar::drivers {
|
||||
namespace detail = radar::drivers::librevna::detail;
|
||||
namespace {
|
||||
|
||||
constexpr std::uint32_t kNativeAcquireMaxAttempts = 3U;
|
||||
|
||||
[[nodiscard]] auto is_retryable_native_acquire_error(std::string_view message) -> bool {
|
||||
constexpr std::array<std::string_view, 5> kRetryableSubstrings = {
|
||||
"Timeout waiting for expected LibreVNA packet type",
|
||||
"Timeout waiting for LibreVNA ACK",
|
||||
"LibreVNA returned NACK",
|
||||
"Failed to read LibreVNA USB bulk packet",
|
||||
"Failed to write LibreVNA USB bulk packet",
|
||||
};
|
||||
|
||||
return std::any_of(
|
||||
kRetryableSubstrings.begin(),
|
||||
kRetryableSubstrings.end(),
|
||||
[message](std::string_view needle) {
|
||||
return message.find(needle) != std::string_view::npos;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
LibreVnaMinimalDriver::LibreVnaMinimalDriver(LibreVnaMinimalDriverSettings settings) : settings_(std::move(settings)) {}
|
||||
|
||||
void LibreVnaMinimalDriver::open() {
|
||||
if (is_open_) {
|
||||
return;
|
||||
}
|
||||
if (settings_.sweep.points == 0U) {
|
||||
throw std::runtime_error("Radar sweep points must be > 0");
|
||||
}
|
||||
|
||||
if (settings_.mode == config::DriverMode::Native) {
|
||||
open_native();
|
||||
}
|
||||
|
||||
is_open_ = true;
|
||||
}
|
||||
|
||||
void LibreVnaMinimalDriver::close() {
|
||||
if (!is_open_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (settings_.mode == config::DriverMode::Native) {
|
||||
close_native();
|
||||
}
|
||||
|
||||
is_open_ = false;
|
||||
}
|
||||
|
||||
auto LibreVnaMinimalDriver::acquire_s21_sweep() -> SweepTrace {
|
||||
if (!is_open_) {
|
||||
throw std::runtime_error("Radar driver is not open");
|
||||
}
|
||||
|
||||
++sweep_index_;
|
||||
switch (settings_.mode) {
|
||||
case config::DriverMode::Mock:
|
||||
return acquire_mock();
|
||||
case config::DriverMode::Native: {
|
||||
std::exception_ptr last_exception{};
|
||||
std::string last_message{};
|
||||
|
||||
for (std::uint32_t attempt = 1; attempt <= kNativeAcquireMaxAttempts; ++attempt) {
|
||||
try {
|
||||
return acquire_native();
|
||||
} catch (const std::exception& exception) {
|
||||
last_exception = std::current_exception();
|
||||
last_message = exception.what();
|
||||
|
||||
if (!is_retryable_native_acquire_error(last_message) || attempt == kNativeAcquireMaxAttempts) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Recover from transient USB/protocol stalls by reconnecting the device.
|
||||
close_native();
|
||||
open_native();
|
||||
}
|
||||
}
|
||||
|
||||
if (last_exception != nullptr) {
|
||||
std::rethrow_exception(last_exception);
|
||||
}
|
||||
throw std::runtime_error("Native acquisition failed without exception detail");
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error("Unsupported radar driver mode");
|
||||
}
|
||||
}
|
||||
|
||||
auto LibreVnaMinimalDriver::acquire_mock() -> SweepTrace {
|
||||
SweepTrace trace{};
|
||||
trace.frequency_hz.reserve(settings_.sweep.points);
|
||||
trace.s21.reserve(settings_.sweep.points);
|
||||
|
||||
const auto span_hz = settings_.sweep.stop_hz - settings_.sweep.start_hz;
|
||||
const auto denominator = settings_.sweep.points > 1U ? static_cast<float>(settings_.sweep.points - 1U) : 1.0F;
|
||||
|
||||
for (std::uint32_t point = 0; point < settings_.sweep.points; ++point) {
|
||||
const auto ratio = static_cast<float>(point) / denominator;
|
||||
const auto frequency_hz = settings_.sweep.start_hz + span_hz * ratio;
|
||||
const auto phase = 2.0F * detail::kPi * (frequency_hz / std::max(settings_.mock_signal_hz, 1.0F)) +
|
||||
static_cast<float>(sweep_index_) * 0.05F;
|
||||
const auto envelope = 0.6F + 0.4F * std::sin(0.5F * phase);
|
||||
|
||||
ipc::Complex32 sample{};
|
||||
sample.re = envelope * std::cos(phase);
|
||||
sample.im = envelope * std::sin(phase);
|
||||
|
||||
trace.frequency_hz.push_back(frequency_hz);
|
||||
trace.s21.push_back(sample);
|
||||
}
|
||||
|
||||
return trace;
|
||||
}
|
||||
|
||||
auto LibreVnaMinimalDriver::acquire_native() -> SweepTrace {
|
||||
if (usb_handle_ == nullptr) {
|
||||
throw std::runtime_error("LibreVNA native handle is not open");
|
||||
}
|
||||
|
||||
// Drop stale datapoints left in queue by previous operations.
|
||||
while (pop_packet(detail::kPacketVnaDatapoint).has_value()) {
|
||||
}
|
||||
|
||||
send_packet_no_payload(detail::kPacketInitiateSweep, true);
|
||||
|
||||
SweepTrace trace{};
|
||||
trace.frequency_hz.assign(settings_.sweep.points, 0.0F);
|
||||
trace.s21.assign(settings_.sweep.points, ipc::Complex32{});
|
||||
|
||||
std::vector<std::uint8_t> received(settings_.sweep.points, 0U);
|
||||
std::uint32_t received_count = 0;
|
||||
|
||||
const auto ifbw_hz = std::max(settings_.sweep.if_bandwidth_hz, 1.0F);
|
||||
const auto estimated_sweep_ms = static_cast<std::uint64_t>(
|
||||
std::ceil((1'000.0 * static_cast<double>(settings_.sweep.points)) / static_cast<double>(ifbw_hz))
|
||||
);
|
||||
// Keep generous timeout margin on slower hosts.
|
||||
const auto timeout_ms = std::max<std::uint64_t>(20'000ULL, estimated_sweep_ms * 8ULL + 1'000ULL);
|
||||
const auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
|
||||
|
||||
while (received_count < settings_.sweep.points) {
|
||||
NativePacket packet{};
|
||||
try {
|
||||
packet = wait_for_packet(detail::kPacketVnaDatapoint, deadline);
|
||||
} catch (const std::exception& exception) {
|
||||
throw std::runtime_error(
|
||||
"Timeout while collecting VNADatapoints (" + std::to_string(received_count) + "/" +
|
||||
std::to_string(settings_.sweep.points) + " points received): " + exception.what()
|
||||
);
|
||||
}
|
||||
|
||||
std::uint32_t point_number = 0;
|
||||
float frequency_hz = 0.0F;
|
||||
ipc::Complex32 s21{};
|
||||
|
||||
if (!decode_vna_datapoint_s21(packet.payload, point_number, frequency_hz, s21)) {
|
||||
throw std::runtime_error("Failed to decode S21 from VNADatapoint packet");
|
||||
}
|
||||
if (point_number >= settings_.sweep.points) {
|
||||
throw std::runtime_error("Received out-of-range VNADatapoint index");
|
||||
}
|
||||
|
||||
if (received[point_number] == 0U) {
|
||||
received[point_number] = 1U;
|
||||
++received_count;
|
||||
}
|
||||
|
||||
trace.frequency_hz[point_number] = frequency_hz;
|
||||
trace.s21[point_number] = s21;
|
||||
}
|
||||
|
||||
return trace;
|
||||
}
|
||||
|
||||
void LibreVnaMinimalDriver::validate_device_info_payload(std::span<const std::uint8_t> payload) const {
|
||||
if (payload.size() != detail::kDeviceInfoPayloadSize) {
|
||||
throw std::runtime_error("Unexpected DeviceInfo payload size");
|
||||
}
|
||||
|
||||
const auto protocol = detail::read_u16_le(payload, detail::kDeviceInfoProtocolOffset);
|
||||
if (protocol != detail::kProtocolV14) {
|
||||
throw std::runtime_error("Unsupported LibreVNA protocol version (expected v14)");
|
||||
}
|
||||
|
||||
const auto ports = payload[detail::kDeviceInfoNumPortsOffset];
|
||||
if (ports < 2U) {
|
||||
throw std::runtime_error("LibreVNA reported invalid number of ports");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace radar::drivers
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
#include "../librevna_minimal_driver.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <complex>
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include "librevna_protocol_common.hpp"
|
||||
|
||||
namespace radar::drivers {
|
||||
namespace detail = radar::drivers::librevna::detail;
|
||||
|
||||
auto LibreVnaMinimalDriver::encode_frame(
|
||||
std::uint8_t packet_type,
|
||||
std::span<const std::uint8_t> payload
|
||||
) -> std::vector<std::uint8_t> {
|
||||
const auto length = payload.size() + detail::kFrameOverheadBytes;
|
||||
if (length > 0xFFFFU) {
|
||||
throw std::runtime_error("Protocol frame is too large");
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> frame(length, 0);
|
||||
frame[0] = detail::kFrameHeader;
|
||||
detail::write_u16_le(frame, 1, static_cast<std::uint16_t>(length));
|
||||
frame[3] = packet_type;
|
||||
std::copy(payload.begin(), payload.end(), frame.begin() + 4);
|
||||
|
||||
std::uint32_t crc = 0;
|
||||
if (packet_type != detail::kPacketVnaDatapoint) {
|
||||
crc = crc32(std::span<const std::uint8_t>(frame.data(), frame.size() - 4U));
|
||||
}
|
||||
detail::write_u32_le(frame, frame.size() - 4U, crc);
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
auto LibreVnaMinimalDriver::decode_vna_datapoint_s21(
|
||||
std::span<const std::uint8_t> payload,
|
||||
std::uint32_t& point_number_out,
|
||||
float& frequency_out,
|
||||
ipc::Complex32& s21_out
|
||||
) -> bool {
|
||||
// VNADatapoint payload layout:
|
||||
// [0..7]=freq_or_time, [8..9]=cdbm, [10..11]=point_number,
|
||||
// followed by N tuples of {real:f32, imag:f32, flags:u8}.
|
||||
if (payload.size() < 12U) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto values_block = payload.size() - 12U;
|
||||
if ((values_block % 9U) != 0U) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto num_values = values_block / 9U;
|
||||
if (num_values == 0U) {
|
||||
return false;
|
||||
}
|
||||
|
||||
point_number_out = detail::read_u16_le(payload, 10);
|
||||
frequency_out = static_cast<float>(detail::read_u64_le(payload, 0));
|
||||
|
||||
const auto real_offset = 12U;
|
||||
const auto imag_offset = real_offset + (4U * num_values);
|
||||
const auto flags_offset = imag_offset + (4U * num_values);
|
||||
|
||||
std::array<std::complex<float>, 8> ref_by_stage{};
|
||||
std::array<std::complex<float>, 8> measured_by_stage{};
|
||||
std::array<bool, 8> has_ref{};
|
||||
std::array<bool, 8> has_measured{};
|
||||
|
||||
for (std::size_t index = 0; index < num_values; ++index) {
|
||||
const auto flags = payload[flags_offset + index];
|
||||
const auto stage = static_cast<std::size_t>(flags >> 5U);
|
||||
if (stage >= ref_by_stage.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto value = std::complex<float>(
|
||||
detail::read_f32_le(payload, real_offset + (4U * index)),
|
||||
detail::read_f32_le(payload, imag_offset + (4U * index))
|
||||
);
|
||||
|
||||
const bool is_reference = (flags & detail::kReferenceFlagMask) != 0U;
|
||||
if ((flags & detail::kPort1Mask) != 0U && is_reference) {
|
||||
ref_by_stage[stage] = value;
|
||||
has_ref[stage] = true;
|
||||
}
|
||||
if ((flags & detail::kPort2Mask) != 0U && !is_reference) {
|
||||
measured_by_stage[stage] = value;
|
||||
has_measured[stage] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// We need one reference sample from port1 and one measured sample from port2.
|
||||
for (std::size_t stage = 0; stage < ref_by_stage.size(); ++stage) {
|
||||
if (!has_ref[stage] || !has_measured[stage]) {
|
||||
continue;
|
||||
}
|
||||
if (std::norm(ref_by_stage[stage]) <= 0.0F) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto ratio = measured_by_stage[stage] / ref_by_stage[stage];
|
||||
s21_out.re = ratio.real();
|
||||
s21_out.im = ratio.imag();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto LibreVnaMinimalDriver::crc32(std::span<const std::uint8_t> data) -> std::uint32_t {
|
||||
std::uint32_t crc = 0xFFFFFFFFU;
|
||||
for (const auto byte : data) {
|
||||
crc ^= static_cast<std::uint32_t>(byte);
|
||||
for (int bit = 0; bit < 8; ++bit) {
|
||||
const auto lsb = crc & 1U;
|
||||
crc >>= 1U;
|
||||
if (lsb != 0U) {
|
||||
crc ^= 0xEDB88320U;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
} // namespace radar::drivers
|
||||
|
||||
+359
@@ -0,0 +1,359 @@
|
||||
#include "../librevna_minimal_driver.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "librevna_protocol_common.hpp"
|
||||
|
||||
namespace radar::drivers {
|
||||
namespace detail = radar::drivers::librevna::detail;
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] auto libusb_error_message(const std::string& prefix, int status_code) -> std::string {
|
||||
return prefix + ": " + libusb_error_name(status_code);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_supported_vid_pid(std::uint16_t vendor_id, std::uint16_t product_id) -> bool {
|
||||
return std::any_of(
|
||||
detail::kSupportedUsbIds.begin(),
|
||||
detail::kSupportedUsbIds.end(),
|
||||
[vendor_id, product_id](const auto& pair) {
|
||||
return pair.first == vendor_id && pair.second == product_id;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_serial_string(libusb_device_handle* handle, std::uint8_t serial_index) -> std::string {
|
||||
if (handle == nullptr || serial_index == 0U) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::array<unsigned char, 256> serial_bytes{};
|
||||
const auto length = libusb_get_string_descriptor_ascii(
|
||||
handle,
|
||||
serial_index,
|
||||
serial_bytes.data(),
|
||||
static_cast<int>(serial_bytes.size())
|
||||
);
|
||||
if (length <= 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return std::string(
|
||||
reinterpret_cast<const char*>(serial_bytes.data()),
|
||||
reinterpret_cast<const char*>(serial_bytes.data() + length)
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto find_matching_device_handle(
|
||||
libusb_context* usb_context,
|
||||
const std::string& expected_serial
|
||||
) -> libusb_device_handle* {
|
||||
libusb_device** devices = nullptr;
|
||||
const auto device_count = libusb_get_device_list(usb_context, &devices);
|
||||
if (device_count < 0) {
|
||||
throw std::runtime_error(
|
||||
libusb_error_message("Failed to enumerate USB devices", static_cast<int>(device_count))
|
||||
);
|
||||
}
|
||||
|
||||
libusb_device_handle* selected_handle = nullptr;
|
||||
for (ssize_t index = 0; index < device_count; ++index) {
|
||||
auto* device = devices[index];
|
||||
if (device == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
libusb_device_descriptor descriptor{};
|
||||
const auto descriptor_status = libusb_get_device_descriptor(device, &descriptor);
|
||||
if (descriptor_status != LIBUSB_SUCCESS) {
|
||||
continue;
|
||||
}
|
||||
if (!is_supported_vid_pid(descriptor.idVendor, descriptor.idProduct)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
libusb_device_handle* candidate_handle = nullptr;
|
||||
if (libusb_open(device, &candidate_handle) != LIBUSB_SUCCESS || candidate_handle == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!expected_serial.empty()) {
|
||||
const auto serial = parse_serial_string(candidate_handle, descriptor.iSerialNumber);
|
||||
if (serial != expected_serial) {
|
||||
libusb_close(candidate_handle);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
selected_handle = candidate_handle;
|
||||
break;
|
||||
}
|
||||
|
||||
libusb_free_device_list(devices, 1);
|
||||
return selected_handle;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void LibreVnaMinimalDriver::open_native() {
|
||||
if (usb_context_ != nullptr || usb_handle_ != nullptr) {
|
||||
throw std::runtime_error("LibreVNA native state is already initialized");
|
||||
}
|
||||
|
||||
const auto init_status = libusb_init(&usb_context_);
|
||||
if (init_status != LIBUSB_SUCCESS) {
|
||||
usb_context_ = nullptr;
|
||||
throw std::runtime_error(libusb_error_message("Failed to initialize libusb", init_status));
|
||||
}
|
||||
|
||||
try {
|
||||
auto* selected_handle = find_matching_device_handle(usb_context_, settings_.serial);
|
||||
|
||||
if (selected_handle == nullptr) {
|
||||
const auto serial_hint = settings_.serial.empty() ? std::string() : " for serial '" + settings_.serial + "'";
|
||||
throw std::runtime_error("No compatible LibreVNA USB device found" + serial_hint);
|
||||
}
|
||||
|
||||
usb_handle_ = selected_handle;
|
||||
|
||||
const auto auto_detach_status = libusb_set_auto_detach_kernel_driver(usb_handle_, 1);
|
||||
if (auto_detach_status != LIBUSB_SUCCESS && auto_detach_status != LIBUSB_ERROR_NOT_SUPPORTED) {
|
||||
throw std::runtime_error(
|
||||
libusb_error_message("Failed to configure USB auto-detach kernel driver", auto_detach_status)
|
||||
);
|
||||
}
|
||||
|
||||
const auto claim_status = libusb_claim_interface(usb_handle_, detail::kUsbInterface);
|
||||
if (claim_status != LIBUSB_SUCCESS) {
|
||||
throw std::runtime_error(libusb_error_message("Failed to claim LibreVNA USB interface", claim_status));
|
||||
}
|
||||
interface_claimed_ = true;
|
||||
|
||||
rx_buffer_.clear();
|
||||
packet_queue_.clear();
|
||||
|
||||
send_packet_no_payload(detail::kPacketRequestDeviceInfo, true);
|
||||
const auto info_packet = wait_for_packet(
|
||||
detail::kPacketDeviceInfo,
|
||||
std::chrono::steady_clock::now() + std::chrono::seconds(detail::kDeviceInfoTimeoutSeconds)
|
||||
);
|
||||
validate_device_info_payload(info_packet.payload);
|
||||
protocol_version_ = detail::read_u16_le(info_packet.payload, detail::kDeviceInfoProtocolOffset);
|
||||
device_num_ports_ = info_packet.payload[detail::kDeviceInfoNumPortsOffset];
|
||||
} catch (...) {
|
||||
close_native();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void LibreVnaMinimalDriver::close_native() {
|
||||
rx_buffer_.clear();
|
||||
packet_queue_.clear();
|
||||
|
||||
if (usb_handle_ != nullptr && interface_claimed_) {
|
||||
libusb_release_interface(usb_handle_, detail::kUsbInterface);
|
||||
interface_claimed_ = false;
|
||||
}
|
||||
|
||||
if (usb_handle_ != nullptr) {
|
||||
libusb_close(usb_handle_);
|
||||
usb_handle_ = nullptr;
|
||||
}
|
||||
|
||||
if (usb_context_ != nullptr) {
|
||||
libusb_exit(usb_context_);
|
||||
usb_context_ = nullptr;
|
||||
}
|
||||
|
||||
protocol_version_ = 0;
|
||||
device_num_ports_ = 0;
|
||||
}
|
||||
|
||||
void LibreVnaMinimalDriver::send_packet(
|
||||
std::uint8_t packet_type,
|
||||
std::span<const std::uint8_t> payload,
|
||||
bool require_ack
|
||||
) {
|
||||
if (usb_handle_ == nullptr) {
|
||||
throw std::runtime_error("Cannot send packet: LibreVNA USB handle is not open");
|
||||
}
|
||||
|
||||
auto frame = encode_frame(packet_type, payload);
|
||||
|
||||
int transferred = 0;
|
||||
const auto transfer_status = libusb_bulk_transfer(
|
||||
usb_handle_,
|
||||
detail::kEndpointOut,
|
||||
reinterpret_cast<unsigned char*>(frame.data()),
|
||||
static_cast<int>(frame.size()),
|
||||
&transferred,
|
||||
detail::kUsbWriteTimeoutMs
|
||||
);
|
||||
if (transfer_status != LIBUSB_SUCCESS) {
|
||||
throw std::runtime_error(libusb_error_message("Failed to write LibreVNA USB bulk packet", transfer_status));
|
||||
}
|
||||
if (transferred != static_cast<int>(frame.size())) {
|
||||
throw std::runtime_error("Incomplete LibreVNA USB bulk write");
|
||||
}
|
||||
|
||||
if (require_ack) {
|
||||
wait_for_ack(std::chrono::steady_clock::now() + std::chrono::milliseconds(detail::kAckTimeoutMs));
|
||||
}
|
||||
}
|
||||
|
||||
void LibreVnaMinimalDriver::send_packet_no_payload(std::uint8_t packet_type, bool require_ack) {
|
||||
send_packet(packet_type, std::span<const std::uint8_t>{}, require_ack);
|
||||
}
|
||||
|
||||
void LibreVnaMinimalDriver::wait_for_ack(std::chrono::steady_clock::time_point deadline) {
|
||||
while (std::chrono::steady_clock::now() < deadline) {
|
||||
std::optional<NativePacket> ack_or_nack{};
|
||||
for (auto iter = packet_queue_.begin(); iter != packet_queue_.end(); ++iter) {
|
||||
if (iter->packet_type != detail::kPacketAck && iter->packet_type != detail::kPacketNack) {
|
||||
continue;
|
||||
}
|
||||
ack_or_nack = std::move(*iter);
|
||||
packet_queue_.erase(iter);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ack_or_nack.has_value()) {
|
||||
if (ack_or_nack->packet_type == detail::kPacketAck) {
|
||||
return;
|
||||
}
|
||||
throw std::runtime_error("LibreVNA returned NACK");
|
||||
}
|
||||
|
||||
pump_usb(deadline);
|
||||
}
|
||||
|
||||
throw std::runtime_error("Timeout waiting for LibreVNA ACK");
|
||||
}
|
||||
|
||||
auto LibreVnaMinimalDriver::wait_for_packet(
|
||||
std::uint8_t expected_type,
|
||||
std::chrono::steady_clock::time_point deadline
|
||||
) -> NativePacket {
|
||||
while (std::chrono::steady_clock::now() < deadline) {
|
||||
if (auto packet = pop_packet(expected_type); packet.has_value()) {
|
||||
return *packet;
|
||||
}
|
||||
pump_usb(deadline);
|
||||
}
|
||||
|
||||
throw std::runtime_error("Timeout waiting for expected LibreVNA packet type");
|
||||
}
|
||||
|
||||
void LibreVnaMinimalDriver::pump_usb(std::chrono::steady_clock::time_point deadline) {
|
||||
if (usb_handle_ == nullptr) {
|
||||
throw std::runtime_error("Cannot read packets: LibreVNA USB handle is not open");
|
||||
}
|
||||
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
if (now >= deadline) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto remaining_ms = std::chrono::duration_cast<std::chrono::milliseconds>(deadline - now).count();
|
||||
const auto timeout_ms = static_cast<unsigned int>(
|
||||
std::clamp<std::int64_t>(remaining_ms, detail::kUsbReadPollMinTimeoutMs, detail::kUsbReadPollMaxTimeoutMs)
|
||||
);
|
||||
|
||||
std::array<unsigned char, detail::kUsbReadChunkBytes> buffer{};
|
||||
int transferred = 0;
|
||||
const auto transfer_status = libusb_bulk_transfer(
|
||||
usb_handle_,
|
||||
detail::kEndpointIn,
|
||||
buffer.data(),
|
||||
static_cast<int>(buffer.size()),
|
||||
&transferred,
|
||||
timeout_ms
|
||||
);
|
||||
|
||||
if (transfer_status == LIBUSB_ERROR_TIMEOUT) {
|
||||
return;
|
||||
}
|
||||
if (transfer_status != LIBUSB_SUCCESS) {
|
||||
throw std::runtime_error(libusb_error_message("Failed to read LibreVNA USB bulk packet", transfer_status));
|
||||
}
|
||||
if (transferred <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
rx_buffer_.insert(rx_buffer_.end(), buffer.begin(), buffer.begin() + transferred);
|
||||
decode_frames_from_buffer();
|
||||
}
|
||||
|
||||
void LibreVnaMinimalDriver::decode_frames_from_buffer() {
|
||||
while (true) {
|
||||
const auto header_iter = std::find(rx_buffer_.begin(), rx_buffer_.end(), detail::kFrameHeader);
|
||||
if (header_iter == rx_buffer_.end()) {
|
||||
rx_buffer_.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (header_iter != rx_buffer_.begin()) {
|
||||
rx_buffer_.erase(rx_buffer_.begin(), header_iter);
|
||||
}
|
||||
|
||||
if (rx_buffer_.size() < 4U) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto declared_length = detail::read_u16_le(rx_buffer_, 1);
|
||||
if (declared_length < detail::kFrameOverheadBytes || declared_length > detail::kMaxFrameLengthBytes) {
|
||||
rx_buffer_.erase(rx_buffer_.begin());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rx_buffer_.size() < declared_length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::vector<std::uint8_t> frame(rx_buffer_.begin(), rx_buffer_.begin() + declared_length);
|
||||
rx_buffer_.erase(rx_buffer_.begin(), rx_buffer_.begin() + declared_length);
|
||||
|
||||
const auto packet_type = frame[3];
|
||||
const auto received_crc = detail::read_u32_le(frame, frame.size() - 4U);
|
||||
|
||||
if (packet_type == detail::kPacketVnaDatapoint) {
|
||||
if (received_crc != 0U) {
|
||||
throw std::runtime_error("Invalid VNADatapoint CRC (expected zero)");
|
||||
}
|
||||
} else {
|
||||
const auto expected_crc = crc32(std::span<const std::uint8_t>(frame.data(), frame.size() - 4U));
|
||||
if (received_crc != expected_crc) {
|
||||
throw std::runtime_error("Invalid LibreVNA packet CRC");
|
||||
}
|
||||
}
|
||||
|
||||
NativePacket packet{};
|
||||
packet.packet_type = packet_type;
|
||||
packet.payload.assign(frame.begin() + 4, frame.end() - 4);
|
||||
packet_queue_.push_back(std::move(packet));
|
||||
}
|
||||
}
|
||||
|
||||
auto LibreVnaMinimalDriver::pop_packet(std::uint8_t packet_type) -> std::optional<NativePacket> {
|
||||
for (auto iter = packet_queue_.begin(); iter != packet_queue_.end(); ++iter) {
|
||||
if (iter->packet_type != packet_type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto packet = std::move(*iter);
|
||||
packet_queue_.erase(iter);
|
||||
return packet;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace radar::drivers
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace radar::drivers::librevna::detail {
|
||||
|
||||
constexpr float kPi = 3.14159265358979323846F;
|
||||
|
||||
// Frame format:
|
||||
// [0]=header, [1..2]=length_le, [3]=packet_type, [...payload...], [crc32_le]
|
||||
constexpr std::uint8_t kFrameHeader = 0x5A;
|
||||
constexpr std::size_t kFrameOverheadBytes = 8;
|
||||
constexpr std::size_t kMaxFrameLengthBytes = 4096;
|
||||
|
||||
constexpr unsigned char kEndpointOut = 0x01;
|
||||
constexpr unsigned char kEndpointIn = 0x81;
|
||||
constexpr int kUsbInterface = 0;
|
||||
|
||||
constexpr std::uint8_t kPacketDeviceInfo = 5;
|
||||
constexpr std::uint8_t kPacketAck = 7;
|
||||
constexpr std::uint8_t kPacketNack = 10;
|
||||
constexpr std::uint8_t kPacketRequestDeviceInfo = 15;
|
||||
constexpr std::uint8_t kPacketVnaDatapoint = 27;
|
||||
constexpr std::uint8_t kPacketInitiateSweep = 32;
|
||||
|
||||
constexpr std::size_t kDeviceInfoPayloadSize = 57;
|
||||
constexpr std::size_t kDeviceInfoProtocolOffset = 0;
|
||||
constexpr std::size_t kDeviceInfoNumPortsOffset = 54;
|
||||
constexpr std::uint16_t kProtocolV14 = 14;
|
||||
|
||||
constexpr std::uint8_t kReferenceFlagMask = 0x10;
|
||||
constexpr std::uint8_t kPort1Mask = 0x01;
|
||||
constexpr std::uint8_t kPort2Mask = 0x02;
|
||||
|
||||
constexpr std::array<std::pair<std::uint16_t, std::uint16_t>, 3> kSupportedUsbIds = {
|
||||
std::pair<std::uint16_t, std::uint16_t>{0x0483, 0x564E},
|
||||
std::pair<std::uint16_t, std::uint16_t>{0x0483, 0x4121},
|
||||
std::pair<std::uint16_t, std::uint16_t>{0x1209, 0x4121},
|
||||
};
|
||||
|
||||
constexpr int kUsbWriteTimeoutMs = 500;
|
||||
constexpr int kAckTimeoutMs = 800;
|
||||
constexpr int kDeviceInfoTimeoutSeconds = 2;
|
||||
constexpr std::size_t kUsbReadChunkBytes = 16 * 1024;
|
||||
constexpr int kUsbReadPollMinTimeoutMs = 1;
|
||||
constexpr int kUsbReadPollMaxTimeoutMs = 50;
|
||||
|
||||
[[nodiscard]] inline auto read_u16_le(std::span<const std::uint8_t> data, std::size_t offset) -> std::uint16_t {
|
||||
if ((offset + sizeof(std::uint16_t)) > data.size()) {
|
||||
throw std::runtime_error("Failed to decode uint16 from payload");
|
||||
}
|
||||
return static_cast<std::uint16_t>(data[offset]) |
|
||||
(static_cast<std::uint16_t>(data[offset + 1]) << 8U);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline auto read_u32_le(std::span<const std::uint8_t> data, std::size_t offset) -> std::uint32_t {
|
||||
if ((offset + sizeof(std::uint32_t)) > data.size()) {
|
||||
throw std::runtime_error("Failed to decode uint32 from payload");
|
||||
}
|
||||
return static_cast<std::uint32_t>(data[offset]) |
|
||||
(static_cast<std::uint32_t>(data[offset + 1]) << 8U) |
|
||||
(static_cast<std::uint32_t>(data[offset + 2]) << 16U) |
|
||||
(static_cast<std::uint32_t>(data[offset + 3]) << 24U);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline auto read_u64_le(std::span<const std::uint8_t> data, std::size_t offset) -> std::uint64_t {
|
||||
if ((offset + sizeof(std::uint64_t)) > data.size()) {
|
||||
throw std::runtime_error("Failed to decode uint64 from payload");
|
||||
}
|
||||
|
||||
std::uint64_t value = 0;
|
||||
for (std::size_t index = 0; index < sizeof(std::uint64_t); ++index) {
|
||||
value |= static_cast<std::uint64_t>(data[offset + index]) << (index * 8U);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline auto read_f32_le(std::span<const std::uint8_t> data, std::size_t offset) -> float {
|
||||
const auto raw = read_u32_le(data, offset);
|
||||
float out = 0.0F;
|
||||
std::memcpy(&out, &raw, sizeof(float));
|
||||
return out;
|
||||
}
|
||||
|
||||
inline void write_u16_le(std::span<std::uint8_t> data, std::size_t offset, std::uint16_t value) {
|
||||
if ((offset + sizeof(std::uint16_t)) > data.size()) {
|
||||
throw std::runtime_error("Failed to encode uint16 into frame");
|
||||
}
|
||||
|
||||
data[offset] = static_cast<std::uint8_t>(value & 0xFFU);
|
||||
data[offset + 1] = static_cast<std::uint8_t>((value >> 8U) & 0xFFU);
|
||||
}
|
||||
|
||||
inline void write_u32_le(std::span<std::uint8_t> data, std::size_t offset, std::uint32_t value) {
|
||||
if ((offset + sizeof(std::uint32_t)) > data.size()) {
|
||||
throw std::runtime_error("Failed to encode uint32 into frame");
|
||||
}
|
||||
|
||||
data[offset] = static_cast<std::uint8_t>(value & 0xFFU);
|
||||
data[offset + 1] = static_cast<std::uint8_t>((value >> 8U) & 0xFFU);
|
||||
data[offset + 2] = static_cast<std::uint8_t>((value >> 16U) & 0xFFU);
|
||||
data[offset + 3] = static_cast<std::uint8_t>((value >> 24U) & 0xFFU);
|
||||
}
|
||||
|
||||
} // namespace radar::drivers::librevna::detail
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if __has_include(<libusb-1.0/libusb.h>)
|
||||
#include <libusb-1.0/libusb.h>
|
||||
#else
|
||||
#include <libusb.h>
|
||||
#endif
|
||||
|
||||
#include "radar_driver.hpp"
|
||||
#include "run_config.hpp"
|
||||
|
||||
namespace radar::drivers {
|
||||
|
||||
/**
|
||||
* @brief Runtime settings for minimal LibreVNA acquisition.
|
||||
*/
|
||||
struct LibreVnaMinimalDriverSettings {
|
||||
config::DriverMode mode = config::DriverMode::Mock;
|
||||
std::string serial{};
|
||||
config::RadarSweepSettings sweep{};
|
||||
float mock_signal_hz = 5'000'000.0F;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Minimal radar driver that acquires S21 sweeps from LibreVNA.
|
||||
*
|
||||
* This class intentionally keeps scope narrow: open/close transport and
|
||||
* acquire one sweep. Full device configuration is expected to be done by the
|
||||
* Python layer before this process starts.
|
||||
*/
|
||||
class LibreVnaMinimalDriver final : public RadarDriver {
|
||||
public:
|
||||
explicit LibreVnaMinimalDriver(LibreVnaMinimalDriverSettings settings);
|
||||
|
||||
void open() override;
|
||||
void close() override;
|
||||
[[nodiscard]] auto acquire_s21_sweep() -> SweepTrace override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Raw protocol packet decoded from framed USB stream.
|
||||
*/
|
||||
struct NativePacket {
|
||||
std::uint8_t packet_type = 0;
|
||||
std::vector<std::uint8_t> payload{};
|
||||
};
|
||||
|
||||
[[nodiscard]] auto acquire_mock() -> SweepTrace;
|
||||
[[nodiscard]] auto acquire_native() -> SweepTrace;
|
||||
|
||||
void open_native();
|
||||
void close_native();
|
||||
|
||||
void send_packet(std::uint8_t packet_type, std::span<const std::uint8_t> payload, bool require_ack);
|
||||
void send_packet_no_payload(std::uint8_t packet_type, bool require_ack);
|
||||
void wait_for_ack(std::chrono::steady_clock::time_point deadline);
|
||||
[[nodiscard]] auto wait_for_packet(std::uint8_t expected_type, std::chrono::steady_clock::time_point deadline)
|
||||
-> NativePacket;
|
||||
void pump_usb(std::chrono::steady_clock::time_point deadline);
|
||||
void decode_frames_from_buffer();
|
||||
|
||||
[[nodiscard]] auto pop_packet(std::uint8_t packet_type) -> std::optional<NativePacket>;
|
||||
[[nodiscard]] static auto encode_frame(std::uint8_t packet_type, std::span<const std::uint8_t> payload)
|
||||
-> std::vector<std::uint8_t>;
|
||||
[[nodiscard]] static auto decode_vna_datapoint_s21(
|
||||
std::span<const std::uint8_t> payload,
|
||||
std::uint32_t& point_number_out,
|
||||
float& frequency_out,
|
||||
ipc::Complex32& s21_out
|
||||
) -> bool;
|
||||
[[nodiscard]] static auto crc32(std::span<const std::uint8_t> data) -> std::uint32_t;
|
||||
void validate_device_info_payload(std::span<const std::uint8_t> payload) const;
|
||||
|
||||
LibreVnaMinimalDriverSettings settings_{};
|
||||
bool is_open_ = false;
|
||||
std::uint64_t sweep_index_ = 0;
|
||||
|
||||
libusb_context* usb_context_ = nullptr;
|
||||
libusb_device_handle* usb_handle_ = nullptr;
|
||||
bool interface_claimed_ = false;
|
||||
|
||||
std::vector<std::uint8_t> rx_buffer_{};
|
||||
std::deque<NativePacket> packet_queue_{};
|
||||
|
||||
std::uint16_t protocol_version_ = 0;
|
||||
std::uint8_t device_num_ports_ = 0;
|
||||
};
|
||||
|
||||
} // namespace radar::drivers
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
#include "h7992_minimal_driver.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <stdexcept>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
|
||||
namespace radar::drivers {
|
||||
namespace {
|
||||
|
||||
// Position mapping for H7992 control pins:
|
||||
// position -> (A, B)
|
||||
constexpr std::array<std::array<std::uint8_t, 2>, 4> kPositionToAB = {
|
||||
std::array<std::uint8_t, 2>{0, 0},
|
||||
std::array<std::uint8_t, 2>{0, 1},
|
||||
std::array<std::uint8_t, 2>{1, 0},
|
||||
std::array<std::uint8_t, 2>{1, 1},
|
||||
};
|
||||
|
||||
void validate_open_settings(const H7992MinimalDriverSettings& settings) {
|
||||
if (settings.positions == 0U || settings.positions > 4U) {
|
||||
throw std::runtime_error("H7992 switch positions must be in range [1, 4] for " + settings.name);
|
||||
}
|
||||
if (settings.default_position >= settings.positions) {
|
||||
throw std::runtime_error("Switch default_position out of range for " + settings.name);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto make_line_values(std::uint8_t pin_a_state, std::uint8_t pin_b_state) -> gpio_v2_line_values {
|
||||
gpio_v2_line_values values{};
|
||||
values.mask = (1ULL << 0U) | (1ULL << 1U);
|
||||
values.bits =
|
||||
(static_cast<std::uint64_t>(pin_a_state) << 0U) | (static_cast<std::uint64_t>(pin_b_state) << 1U);
|
||||
return values;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
H7992MinimalDriver::H7992MinimalDriver(H7992MinimalDriverSettings settings) : settings_(std::move(settings)) {}
|
||||
|
||||
void H7992MinimalDriver::open() {
|
||||
if (is_open_) {
|
||||
return;
|
||||
}
|
||||
validate_open_settings(settings_);
|
||||
|
||||
if (settings_.mode == config::DriverMode::Native) {
|
||||
open_native();
|
||||
}
|
||||
|
||||
is_open_ = true;
|
||||
switch_to(settings_.default_position);
|
||||
}
|
||||
|
||||
void H7992MinimalDriver::close() {
|
||||
if (!is_open_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (settings_.mode == config::DriverMode::Native) {
|
||||
close_native();
|
||||
}
|
||||
|
||||
is_open_ = false;
|
||||
}
|
||||
|
||||
auto H7992MinimalDriver::position_count() const -> std::uint32_t {
|
||||
return settings_.positions;
|
||||
}
|
||||
|
||||
void H7992MinimalDriver::switch_to(std::uint32_t position) {
|
||||
if (!is_open_) {
|
||||
throw std::runtime_error("Switch driver is not open for " + settings_.name);
|
||||
}
|
||||
if (position >= settings_.positions) {
|
||||
throw std::runtime_error("Switch position out of range for " + settings_.name);
|
||||
}
|
||||
|
||||
switch (settings_.mode) {
|
||||
case config::DriverMode::Mock:
|
||||
break;
|
||||
case config::DriverMode::Native:
|
||||
switch_native(position);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unsupported switch driver mode for " + settings_.name);
|
||||
}
|
||||
|
||||
current_position_ = position;
|
||||
}
|
||||
|
||||
void H7992MinimalDriver::open_native() {
|
||||
if (settings_.gpio_chip.empty()) {
|
||||
throw std::runtime_error("gpio_chip is empty for native switch " + settings_.name);
|
||||
}
|
||||
if (settings_.pin_a < 0 || settings_.pin_b < 0 || settings_.pin_a == settings_.pin_b) {
|
||||
throw std::runtime_error("Invalid pin_a/pin_b for native switch " + settings_.name);
|
||||
}
|
||||
|
||||
chip_fd_ = ::open(settings_.gpio_chip.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
if (chip_fd_ < 0) {
|
||||
throw std::runtime_error(
|
||||
"Failed to open gpio chip '" + settings_.gpio_chip + "' for " + settings_.name + ": " + std::strerror(errno)
|
||||
);
|
||||
}
|
||||
|
||||
gpio_v2_line_request request{};
|
||||
// The first requested line maps to logical bit 0, the second to bit 1.
|
||||
request.offsets[0] = static_cast<std::uint32_t>(settings_.pin_a);
|
||||
request.offsets[1] = static_cast<std::uint32_t>(settings_.pin_b);
|
||||
request.num_lines = 2;
|
||||
request.config.flags = GPIO_V2_LINE_FLAG_OUTPUT;
|
||||
std::snprintf(request.consumer, sizeof(request.consumer), "radar_%s", settings_.name.c_str());
|
||||
|
||||
if (::ioctl(chip_fd_, GPIO_V2_GET_LINE_IOCTL, &request) != 0) {
|
||||
const auto error = std::strerror(errno);
|
||||
::close(chip_fd_);
|
||||
chip_fd_ = -1;
|
||||
throw std::runtime_error("Failed to request GPIO lines for " + settings_.name + ": " + error);
|
||||
}
|
||||
|
||||
line_fd_ = request.fd;
|
||||
if (line_fd_ < 0) {
|
||||
::close(chip_fd_);
|
||||
chip_fd_ = -1;
|
||||
throw std::runtime_error("GPIO line request returned invalid fd for " + settings_.name);
|
||||
}
|
||||
}
|
||||
|
||||
void H7992MinimalDriver::close_native() {
|
||||
if (line_fd_ >= 0) {
|
||||
::close(line_fd_);
|
||||
line_fd_ = -1;
|
||||
}
|
||||
if (chip_fd_ >= 0) {
|
||||
::close(chip_fd_);
|
||||
chip_fd_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void H7992MinimalDriver::switch_native(std::uint32_t position) {
|
||||
if (line_fd_ < 0) {
|
||||
throw std::runtime_error("Native GPIO line fd is not open for " + settings_.name);
|
||||
}
|
||||
|
||||
const auto [pin_a_state, pin_b_state] = kPositionToAB[position];
|
||||
auto values = make_line_values(pin_a_state, pin_b_state);
|
||||
|
||||
if (::ioctl(line_fd_, GPIO_V2_LINE_SET_VALUES_IOCTL, &values) != 0) {
|
||||
throw std::runtime_error("Failed to switch GPIO state for " + settings_.name + ": " + std::strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace radar::drivers
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "run_config.hpp"
|
||||
#include "switch_driver.hpp"
|
||||
|
||||
namespace radar::drivers {
|
||||
|
||||
/**
|
||||
* @brief Runtime settings for the H7992 GPIO switch driver.
|
||||
*/
|
||||
struct H7992MinimalDriverSettings {
|
||||
std::string name{};
|
||||
config::DriverMode mode = config::DriverMode::Native;
|
||||
std::uint32_t positions = 4;
|
||||
std::uint32_t default_position = 0;
|
||||
std::string gpio_chip = "/dev/gpiochip0";
|
||||
std::int32_t pin_a = 17;
|
||||
std::int32_t pin_b = 27;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Minimal H7992 implementation for fast position switching.
|
||||
*
|
||||
* Native mode uses Linux GPIO character-device API (uAPI v2).
|
||||
* Mock mode keeps state in memory without touching hardware.
|
||||
*/
|
||||
class H7992MinimalDriver final : public SwitchDriver {
|
||||
public:
|
||||
explicit H7992MinimalDriver(H7992MinimalDriverSettings settings);
|
||||
|
||||
void open() override;
|
||||
void close() override;
|
||||
[[nodiscard]] auto position_count() const -> std::uint32_t override;
|
||||
void switch_to(std::uint32_t position) override;
|
||||
|
||||
private:
|
||||
void open_native();
|
||||
void close_native();
|
||||
void switch_native(std::uint32_t position);
|
||||
|
||||
H7992MinimalDriverSettings settings_{};
|
||||
bool is_open_ = false;
|
||||
std::uint32_t current_position_ = 0;
|
||||
int chip_fd_ = -1;
|
||||
int line_fd_ = -1;
|
||||
};
|
||||
|
||||
} // namespace radar::drivers
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
#include "hmc349a_minimal_driver.hpp"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <stdexcept>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
|
||||
namespace radar::drivers {
|
||||
namespace {
|
||||
|
||||
void validate_open_settings(const HMC349AMinimalDriverSettings& settings) {
|
||||
if (settings.positions == 0U || settings.positions > 2U) {
|
||||
throw std::runtime_error("HMC349A switch positions must be in range [1, 2] for " + settings.name);
|
||||
}
|
||||
if (settings.default_position >= settings.positions) {
|
||||
throw std::runtime_error("Switch default_position out of range for " + settings.name);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
HMC349AMinimalDriver::HMC349AMinimalDriver(HMC349AMinimalDriverSettings settings)
|
||||
: settings_(std::move(settings)) {}
|
||||
|
||||
void HMC349AMinimalDriver::open() {
|
||||
if (is_open_) {
|
||||
return;
|
||||
}
|
||||
|
||||
validate_open_settings(settings_);
|
||||
|
||||
if (settings_.mode == config::DriverMode::Native) {
|
||||
open_native();
|
||||
}
|
||||
|
||||
is_open_ = true;
|
||||
switch_to(settings_.default_position);
|
||||
}
|
||||
|
||||
void HMC349AMinimalDriver::close() {
|
||||
if (!is_open_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (settings_.mode == config::DriverMode::Native) {
|
||||
close_native();
|
||||
}
|
||||
|
||||
is_open_ = false;
|
||||
}
|
||||
|
||||
auto HMC349AMinimalDriver::position_count() const -> std::uint32_t {
|
||||
return settings_.positions;
|
||||
}
|
||||
|
||||
void HMC349AMinimalDriver::switch_to(std::uint32_t position) {
|
||||
if (!is_open_) {
|
||||
throw std::runtime_error("Switch driver is not open for " + settings_.name);
|
||||
}
|
||||
if (position >= settings_.positions) {
|
||||
throw std::runtime_error("Switch position out of range for " + settings_.name);
|
||||
}
|
||||
|
||||
switch (settings_.mode) {
|
||||
case config::DriverMode::Mock:
|
||||
break;
|
||||
case config::DriverMode::Native:
|
||||
switch_native(position);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unsupported switch driver mode for " + settings_.name);
|
||||
}
|
||||
|
||||
current_position_ = position;
|
||||
}
|
||||
|
||||
void HMC349AMinimalDriver::open_native() {
|
||||
if (settings_.gpio_chip.empty()) {
|
||||
throw std::runtime_error("gpio_chip is empty for native switch " + settings_.name);
|
||||
}
|
||||
if (settings_.pin_a < 0) {
|
||||
throw std::runtime_error("pin_a (control) is invalid for native switch " + settings_.name);
|
||||
}
|
||||
|
||||
chip_fd_ = ::open(settings_.gpio_chip.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
if (chip_fd_ < 0) {
|
||||
throw std::runtime_error(
|
||||
"Failed to open gpio chip '" + settings_.gpio_chip + "' for " + settings_.name + ": " + std::strerror(errno)
|
||||
);
|
||||
}
|
||||
|
||||
gpio_v2_line_request request{};
|
||||
// Bit 0 controls pin_a.
|
||||
request.offsets[0] = static_cast<std::uint32_t>(settings_.pin_a);
|
||||
request.num_lines = 1;
|
||||
request.config.flags = GPIO_V2_LINE_FLAG_OUTPUT;
|
||||
std::snprintf(request.consumer, sizeof(request.consumer), "radar_%s", settings_.name.c_str());
|
||||
|
||||
if (::ioctl(chip_fd_, GPIO_V2_GET_LINE_IOCTL, &request) != 0) {
|
||||
const auto error = std::strerror(errno);
|
||||
::close(chip_fd_);
|
||||
chip_fd_ = -1;
|
||||
throw std::runtime_error("Failed to request GPIO lines for " + settings_.name + ": " + error);
|
||||
}
|
||||
|
||||
line_fd_ = request.fd;
|
||||
if (line_fd_ < 0) {
|
||||
::close(chip_fd_);
|
||||
chip_fd_ = -1;
|
||||
throw std::runtime_error("GPIO line request returned invalid fd for " + settings_.name);
|
||||
}
|
||||
}
|
||||
|
||||
void HMC349AMinimalDriver::close_native() {
|
||||
if (line_fd_ >= 0) {
|
||||
::close(line_fd_);
|
||||
line_fd_ = -1;
|
||||
}
|
||||
if (chip_fd_ >= 0) {
|
||||
::close(chip_fd_);
|
||||
chip_fd_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void HMC349AMinimalDriver::switch_native(std::uint32_t position) {
|
||||
if (line_fd_ < 0) {
|
||||
throw std::runtime_error("Native GPIO line fd is not open for " + settings_.name);
|
||||
}
|
||||
|
||||
const std::uint8_t requested_state = static_cast<std::uint8_t>(position & 0x01U);
|
||||
const std::uint8_t control_state = settings_.invert_logic ? (requested_state ^ 0x01U) : requested_state;
|
||||
|
||||
gpio_v2_line_values values{};
|
||||
values.mask = (1ULL << 0U);
|
||||
values.bits = static_cast<std::uint64_t>(control_state);
|
||||
|
||||
if (::ioctl(line_fd_, GPIO_V2_LINE_SET_VALUES_IOCTL, &values) != 0) {
|
||||
throw std::runtime_error("Failed to switch GPIO state for " + settings_.name + ": " + std::strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace radar::drivers
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "run_config.hpp"
|
||||
#include "switch_driver.hpp"
|
||||
|
||||
namespace radar::drivers {
|
||||
|
||||
/**
|
||||
* @brief Runtime settings for the HMC349A GPIO switch driver.
|
||||
*/
|
||||
struct HMC349AMinimalDriverSettings {
|
||||
std::string name{};
|
||||
config::DriverMode mode = config::DriverMode::Native;
|
||||
std::uint32_t positions = 2;
|
||||
std::uint32_t default_position = 0;
|
||||
std::string gpio_chip = "/dev/gpiochip0";
|
||||
std::int32_t pin_a = 17; ///< Control pin.
|
||||
bool invert_logic = false; ///< Invert control logic for position mapping.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Minimal HMC349A implementation for deterministic GPIO control.
|
||||
*
|
||||
* Native mode uses Linux GPIO character-device API (uAPI v2).
|
||||
* Mock mode keeps state in memory without touching hardware.
|
||||
*/
|
||||
class HMC349AMinimalDriver final : public SwitchDriver {
|
||||
public:
|
||||
explicit HMC349AMinimalDriver(HMC349AMinimalDriverSettings settings);
|
||||
|
||||
void open() override;
|
||||
void close() override;
|
||||
[[nodiscard]] auto position_count() const -> std::uint32_t override;
|
||||
void switch_to(std::uint32_t position) override;
|
||||
|
||||
private:
|
||||
void open_native();
|
||||
void close_native();
|
||||
void switch_native(std::uint32_t position);
|
||||
|
||||
HMC349AMinimalDriverSettings settings_{};
|
||||
bool is_open_ = false;
|
||||
std::uint32_t current_position_ = 0;
|
||||
|
||||
int chip_fd_ = -1;
|
||||
int line_fd_ = -1;
|
||||
};
|
||||
|
||||
} // namespace radar::drivers
|
||||
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
#include "radar_driver.hpp"
|
||||
#include "run_config.hpp"
|
||||
#include "shm_ring.hpp"
|
||||
#include "sweep_plan.hpp"
|
||||
#include "switch_driver.hpp"
|
||||
|
||||
namespace radar::acq {
|
||||
|
||||
/**
|
||||
* @brief Produces raw sweep collections for configured switch combinations.
|
||||
*
|
||||
* Lifecycle:
|
||||
* 1. Open radar and switch drivers.
|
||||
* 2. Iterate configured combinations, acquire one sweep per combination.
|
||||
* 3. Publish serialized collections into the raw shared-memory ring.
|
||||
*/
|
||||
class SweepOrchestrator {
|
||||
public:
|
||||
/**
|
||||
* @param config Runtime config loaded from JSON.
|
||||
* @param radar_driver Radar device implementation.
|
||||
* @param input_switch_driver Switch connected to radar input path.
|
||||
* @param output_switch_driver Switch connected to radar output path.
|
||||
* @param raw_ring Shared-memory ring for raw collections.
|
||||
* @param raw_tap_ring Optional tap ring for GUI/debug readers.
|
||||
*/
|
||||
SweepOrchestrator(
|
||||
const config::RunConfig& config,
|
||||
drivers::RadarDriver& radar_driver,
|
||||
drivers::SwitchDriver& input_switch_driver,
|
||||
drivers::SwitchDriver& output_switch_driver,
|
||||
ipc::ShmRing& raw_ring,
|
||||
ipc::ShmRing* raw_tap_ring = nullptr
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Run acquisition loop until stop is requested or single run completes.
|
||||
*/
|
||||
void run(const std::atomic<bool>& stop_requested);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Acquire one collection for all combinations in `plan_`.
|
||||
*/
|
||||
[[nodiscard]] auto acquire_one_collection(std::uint64_t collection_id, const std::atomic<bool>& stop_requested)
|
||||
-> ipc::RawSweepCollection;
|
||||
|
||||
const config::RunConfig& config_;
|
||||
drivers::RadarDriver& radar_driver_;
|
||||
drivers::SwitchDriver& input_switch_driver_;
|
||||
drivers::SwitchDriver& output_switch_driver_;
|
||||
ipc::ShmRing& raw_ring_;
|
||||
ipc::ShmRing* raw_tap_ring_ = nullptr;
|
||||
SweepPlan plan_{};
|
||||
};
|
||||
|
||||
} // namespace radar::acq
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "run_config.hpp"
|
||||
#include "shared_types.hpp"
|
||||
|
||||
namespace radar::acq {
|
||||
|
||||
/**
|
||||
* @brief Pre-validated execution order of switch combinations for one collection.
|
||||
*/
|
||||
struct SweepPlan {
|
||||
std::vector<ipc::ComboKey> ordered_combos{};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Build and validate sweep execution plan from runtime config.
|
||||
*/
|
||||
[[nodiscard]] auto build_sweep_plan(const config::RunConfig& config) -> SweepPlan;
|
||||
|
||||
} // namespace radar::acq
|
||||
@@ -0,0 +1,135 @@
|
||||
#include <atomic>
|
||||
#include <csignal>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "h7992_minimal_driver.hpp"
|
||||
#include "hmc349a_minimal_driver.hpp"
|
||||
#include "librevna_minimal_driver.hpp"
|
||||
#include "run_config.hpp"
|
||||
#include "shm_ring.hpp"
|
||||
#include "sweep_orchestrator.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char* kDefaultConfigPath = "run_config.json";
|
||||
std::atomic<bool> g_stop_requested{false};
|
||||
|
||||
void signal_handler(int /*signal*/) {
|
||||
g_stop_requested.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void install_signal_handlers() {
|
||||
std::signal(SIGINT, signal_handler);
|
||||
std::signal(SIGTERM, signal_handler);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto read_config_path(int argc, char** argv) -> std::string {
|
||||
std::string config_path = kDefaultConfigPath;
|
||||
|
||||
for (int index = 1; index < argc; ++index) {
|
||||
const std::string arg = argv[index];
|
||||
if (arg == "--config" && (index + 1) < argc) {
|
||||
config_path = argv[++index];
|
||||
}
|
||||
}
|
||||
|
||||
return config_path;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto make_radar_driver(const radar::config::RadarConfig& config) -> radar::drivers::LibreVnaMinimalDriver {
|
||||
return radar::drivers::LibreVnaMinimalDriver({
|
||||
.mode = config.driver_mode,
|
||||
.serial = config.serial,
|
||||
.sweep = config.sweep,
|
||||
.mock_signal_hz = config.mock_signal_hz,
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] auto make_h7992_driver(const radar::config::SwitchConfig& config)
|
||||
-> std::unique_ptr<radar::drivers::SwitchDriver> {
|
||||
return std::make_unique<radar::drivers::H7992MinimalDriver>(
|
||||
radar::drivers::H7992MinimalDriverSettings{
|
||||
.name = config.name,
|
||||
.mode = config.driver_mode,
|
||||
.positions = config.positions,
|
||||
.default_position = config.default_position,
|
||||
.gpio_chip = config.gpio_chip,
|
||||
.pin_a = config.pin_a,
|
||||
.pin_b = config.pin_b,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto make_hmc349a_driver(const radar::config::SwitchConfig& config)
|
||||
-> std::unique_ptr<radar::drivers::SwitchDriver> {
|
||||
return std::make_unique<radar::drivers::HMC349AMinimalDriver>(
|
||||
radar::drivers::HMC349AMinimalDriverSettings{
|
||||
.name = config.name,
|
||||
.mode = config.driver_mode,
|
||||
.positions = config.positions,
|
||||
.default_position = config.default_position,
|
||||
.gpio_chip = config.gpio_chip,
|
||||
.pin_a = config.pin_a,
|
||||
.invert_logic = config.invert_logic,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto make_switch_driver(const radar::config::SwitchConfig& config)
|
||||
-> std::unique_ptr<radar::drivers::SwitchDriver> {
|
||||
using radar::config::SwitchDriverKind;
|
||||
|
||||
switch (config.driver_kind) {
|
||||
case SwitchDriverKind::H7992:
|
||||
return make_h7992_driver(config);
|
||||
case SwitchDriverKind::HMC349A:
|
||||
return make_hmc349a_driver(config);
|
||||
default:
|
||||
throw std::runtime_error("Unsupported switch driver kind");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
install_signal_handlers();
|
||||
|
||||
try {
|
||||
const auto config_path = read_config_path(argc, argv);
|
||||
const auto config = radar::config::load_run_config(config_path);
|
||||
|
||||
auto raw_ring = radar::ipc::ShmRing::open_or_create(
|
||||
config.rings.raw.name,
|
||||
config.rings.raw.capacity,
|
||||
config.rings.raw.slot_size_bytes
|
||||
);
|
||||
auto raw_tap_ring = radar::ipc::ShmRing::open_or_create(
|
||||
config.rings.raw_tap.name,
|
||||
config.rings.raw_tap.capacity,
|
||||
config.rings.raw_tap.slot_size_bytes
|
||||
);
|
||||
|
||||
auto radar_driver = make_radar_driver(config.radar);
|
||||
|
||||
auto input_switch = make_switch_driver(config.input_switch);
|
||||
auto output_switch = make_switch_driver(config.output_switch);
|
||||
|
||||
radar::acq::SweepOrchestrator orchestrator(
|
||||
config,
|
||||
radar_driver,
|
||||
*input_switch,
|
||||
*output_switch,
|
||||
raw_ring,
|
||||
&raw_tap_ring
|
||||
);
|
||||
orchestrator.run(g_stop_requested);
|
||||
return 0;
|
||||
} catch (const std::exception& exception) {
|
||||
std::cerr << "sweep_orchestrator error: " << exception.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
#include "sweep_orchestrator.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace radar::acq {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] inline auto should_stop(const std::atomic<bool>& stop_requested) -> bool {
|
||||
return stop_requested.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void sleep_if_needed_ms(std::uint32_t delay_ms) {
|
||||
if (delay_ms == 0U) {
|
||||
return;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
|
||||
}
|
||||
|
||||
void validate_sweep(const drivers::SweepTrace& sweep) {
|
||||
if (sweep.frequency_hz.size() != sweep.s21.size()) {
|
||||
throw std::runtime_error("Radar driver returned inconsistent sweep vectors");
|
||||
}
|
||||
}
|
||||
|
||||
class DriverLifecycleGuard {
|
||||
public:
|
||||
DriverLifecycleGuard(
|
||||
drivers::RadarDriver& radar_driver,
|
||||
drivers::SwitchDriver& input_switch_driver,
|
||||
drivers::SwitchDriver& output_switch_driver
|
||||
)
|
||||
: radar_driver_(radar_driver),
|
||||
input_switch_driver_(input_switch_driver),
|
||||
output_switch_driver_(output_switch_driver) {}
|
||||
|
||||
~DriverLifecycleGuard() noexcept {
|
||||
if (!active_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Shutdown should never hide the primary error path.
|
||||
try {
|
||||
output_switch_driver_.close();
|
||||
} catch (...) {
|
||||
}
|
||||
try {
|
||||
input_switch_driver_.close();
|
||||
} catch (...) {
|
||||
}
|
||||
try {
|
||||
radar_driver_.close();
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
|
||||
void open_all() {
|
||||
radar_driver_.open();
|
||||
input_switch_driver_.open();
|
||||
output_switch_driver_.open();
|
||||
active_ = true;
|
||||
}
|
||||
|
||||
void close_all() {
|
||||
if (!active_) {
|
||||
return;
|
||||
}
|
||||
output_switch_driver_.close();
|
||||
input_switch_driver_.close();
|
||||
radar_driver_.close();
|
||||
active_ = false;
|
||||
}
|
||||
|
||||
private:
|
||||
drivers::RadarDriver& radar_driver_;
|
||||
drivers::SwitchDriver& input_switch_driver_;
|
||||
drivers::SwitchDriver& output_switch_driver_;
|
||||
bool active_ = false;
|
||||
};
|
||||
|
||||
void publish_collection(ipc::ShmRing& raw_ring, ipc::ShmRing* raw_tap_ring, const ipc::RawSweepCollection& collection) {
|
||||
const auto serialized_collection = ipc::serialize_raw_collection(collection);
|
||||
if (!raw_ring.push(serialized_collection)) {
|
||||
throw std::runtime_error("Raw ring slot is too small for serialized collection");
|
||||
}
|
||||
if (raw_tap_ring != nullptr && !raw_tap_ring->push(serialized_collection)) {
|
||||
throw std::runtime_error("Raw tap ring slot is too small for serialized collection");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SweepOrchestrator::SweepOrchestrator(
|
||||
const config::RunConfig& config,
|
||||
drivers::RadarDriver& radar_driver,
|
||||
drivers::SwitchDriver& input_switch_driver,
|
||||
drivers::SwitchDriver& output_switch_driver,
|
||||
ipc::ShmRing& raw_ring,
|
||||
ipc::ShmRing* raw_tap_ring
|
||||
)
|
||||
: config_(config),
|
||||
radar_driver_(radar_driver),
|
||||
input_switch_driver_(input_switch_driver),
|
||||
output_switch_driver_(output_switch_driver),
|
||||
raw_ring_(raw_ring),
|
||||
raw_tap_ring_(raw_tap_ring),
|
||||
plan_(build_sweep_plan(config)) {}
|
||||
|
||||
void SweepOrchestrator::run(const std::atomic<bool>& stop_requested) {
|
||||
DriverLifecycleGuard lifecycle_guard(radar_driver_, input_switch_driver_, output_switch_driver_);
|
||||
// Open devices once and keep them active for the whole acquisition loop.
|
||||
lifecycle_guard.open_all();
|
||||
|
||||
std::uint64_t collection_id = 0;
|
||||
while (!should_stop(stop_requested)) {
|
||||
auto raw_collection = acquire_one_collection(++collection_id, stop_requested);
|
||||
if (raw_collection.traces.empty()) {
|
||||
if (!config_.runtime.continuous) {
|
||||
break;
|
||||
}
|
||||
sleep_if_needed_ms(config_.runtime.idle_sleep_ms);
|
||||
continue;
|
||||
}
|
||||
|
||||
publish_collection(raw_ring_, raw_tap_ring_, raw_collection);
|
||||
|
||||
if (!config_.runtime.continuous) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lifecycle_guard.close_all();
|
||||
}
|
||||
|
||||
auto SweepOrchestrator::acquire_one_collection(
|
||||
std::uint64_t collection_id,
|
||||
const std::atomic<bool>& stop_requested
|
||||
) -> ipc::RawSweepCollection {
|
||||
ipc::RawSweepCollection collection{};
|
||||
collection.collection_id = collection_id;
|
||||
collection.monotonic_ns = ipc::current_monotonic_ns();
|
||||
collection.traces.reserve(plan_.ordered_combos.size());
|
||||
bool interrupted = false;
|
||||
|
||||
for (const auto& combo : plan_.ordered_combos) {
|
||||
if (should_stop(stop_requested)) {
|
||||
interrupted = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Hardware flow for each combo:
|
||||
// 1) switch output path, 2) switch input path, 3) optional settle.
|
||||
output_switch_driver_.switch_to(combo.output_pos);
|
||||
input_switch_driver_.switch_to(combo.input_pos);
|
||||
sleep_if_needed_ms(config_.runtime.settling_ms);
|
||||
|
||||
auto sweep = radar_driver_.acquire_s21_sweep();
|
||||
validate_sweep(sweep);
|
||||
|
||||
ipc::SweepTraceBlock trace{};
|
||||
trace.combo = combo;
|
||||
trace.frequency_hz = std::move(sweep.frequency_hz);
|
||||
trace.s21 = std::move(sweep.s21);
|
||||
collection.traces.push_back(std::move(trace));
|
||||
}
|
||||
|
||||
if (interrupted) {
|
||||
// Do not emit partial collections when stop was requested mid-cycle.
|
||||
collection.traces.clear();
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
} // namespace radar::acq
|
||||
@@ -0,0 +1,19 @@
|
||||
#include "sweep_plan.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace radar::acq {
|
||||
|
||||
auto build_sweep_plan(const config::RunConfig& config) -> SweepPlan {
|
||||
// RunConfig is already validated in common_cpp/config. Keep this function
|
||||
// focused on plan construction only.
|
||||
if (config.run_combos.empty()) {
|
||||
throw std::runtime_error("run.combos must not be empty");
|
||||
}
|
||||
|
||||
SweepPlan plan{};
|
||||
plan.ordered_combos = config.run_combos;
|
||||
return plan;
|
||||
}
|
||||
|
||||
} // namespace radar::acq
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
Copyright (c) 2011, Intel Corporation. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of Intel Corporation nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
@@ -0,0 +1,51 @@
|
||||
Minpack Copyright Notice (1999) University of Chicago. All rights reserved
|
||||
|
||||
Redistribution and use in source and binary forms, with or
|
||||
without modification, are permitted provided that the
|
||||
following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
3. The end-user documentation included with the
|
||||
redistribution, if any, must include the following
|
||||
acknowledgment:
|
||||
|
||||
"This product includes software developed by the
|
||||
University of Chicago, as Operator of Argonne National
|
||||
Laboratory.
|
||||
|
||||
Alternately, this acknowledgment may appear in the software
|
||||
itself, if and wherever such third-party acknowledgments
|
||||
normally appear.
|
||||
|
||||
4. WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS"
|
||||
WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT HOLDER, THE
|
||||
UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND
|
||||
THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE
|
||||
OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL LIABILITY
|
||||
OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR
|
||||
USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF
|
||||
THE SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4)
|
||||
DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION
|
||||
UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL
|
||||
BE CORRECTED.
|
||||
|
||||
5. LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT
|
||||
HOLDER, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF
|
||||
ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT,
|
||||
INCIDENTAL, CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF
|
||||
ANY KIND OR NATURE, INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, WHETHER
|
||||
SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT
|
||||
(INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE,
|
||||
EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE
|
||||
POSSIBILITY OF SUCH LOSS OR DAMAGES.
|
||||
@@ -0,0 +1,373 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
@@ -0,0 +1,11 @@
|
||||
Eigen is primarily MPL2 licensed. See COPYING.MPL2 and these links:
|
||||
http://www.mozilla.org/MPL/2.0/
|
||||
http://www.mozilla.org/MPL/2.0/FAQ.html
|
||||
|
||||
Some files contain third-party code under BSD, LGPL, Apache, or other
|
||||
MPL2-compatible licenses, hence the other COPYING.* files here.
|
||||
|
||||
Note that some optional external dependencies (e.g. FFTW, MPFR C++)
|
||||
and some bundled benchmark code (bench/btl/) are distributed under
|
||||
different licenses, including the GPL. Refer to the individual source
|
||||
files and their respective COPYING files for details.
|
||||
@@ -0,0 +1,52 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_ACCELERATESUPPORT_MODULE_H
|
||||
#define EIGEN_ACCELERATESUPPORT_MODULE_H
|
||||
|
||||
#include "SparseCore"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
/** \ingroup Support_modules
|
||||
* \defgroup AccelerateSupport_Module AccelerateSupport module
|
||||
*
|
||||
* This module provides an interface to the Apple Accelerate library.
|
||||
* It provides the seven following main factorization classes:
|
||||
* - class AccelerateLLT: a Cholesky (LL^T) factorization.
|
||||
* - class AccelerateLDLT: the default LDL^T factorization.
|
||||
* - class AccelerateLDLTUnpivoted: a Cholesky-like LDL^T factorization with only 1x1 pivots and no pivoting
|
||||
* - class AccelerateLDLTSBK: an LDL^T factorization with Supernode Bunch-Kaufman and static pivoting
|
||||
* - class AccelerateLDLTTPP: an LDL^T factorization with full threshold partial pivoting
|
||||
* - class AccelerateQR: a QR factorization
|
||||
* - class AccelerateCholeskyAtA: a QR factorization without storing Q (equivalent to A^TA = R^T R)
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/AccelerateSupport>
|
||||
* \endcode
|
||||
*
|
||||
* In order to use this module, the Accelerate headers must be accessible from
|
||||
* the include paths, and your binary must be linked to the Accelerate framework.
|
||||
* The Accelerate library is only available on Apple hardware.
|
||||
*
|
||||
* Note that many of the algorithms can be influenced by the UpLo template
|
||||
* argument. All matrices are assumed to be symmetric. For example, the following
|
||||
* creates an LDLT factorization where your matrix is symmetric (implicit) and
|
||||
* uses the lower triangle:
|
||||
*
|
||||
* \code
|
||||
* AccelerateLDLT<SparseMatrix<float>, Lower> ldlt;
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/AccelerateSupport/AccelerateSupport.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_ACCELERATESUPPORT_MODULE_H
|
||||
@@ -0,0 +1,43 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_CHOLESKY_MODULE_H
|
||||
#define EIGEN_CHOLESKY_MODULE_H
|
||||
|
||||
#include "Core"
|
||||
#include "Jacobi"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
/** \defgroup Cholesky_Module Cholesky module
|
||||
*
|
||||
*
|
||||
*
|
||||
* This module provides two variants of the Cholesky decomposition for selfadjoint (hermitian) matrices.
|
||||
* Those decompositions are also accessible via the following methods:
|
||||
* - MatrixBase::llt()
|
||||
* - MatrixBase::ldlt()
|
||||
* - SelfAdjointView::llt()
|
||||
* - SelfAdjointView::ldlt()
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/Cholesky>
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/Cholesky/LLT.h"
|
||||
#include "src/Cholesky/LDLT.h"
|
||||
#ifdef EIGEN_USE_LAPACKE
|
||||
#include "src/misc/lapacke_helpers.h"
|
||||
#include "src/Cholesky/LLT_LAPACKE.h"
|
||||
#endif
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_CHOLESKY_MODULE_H
|
||||
@@ -0,0 +1,48 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_CHOLMODSUPPORT_MODULE_H
|
||||
#define EIGEN_CHOLMODSUPPORT_MODULE_H
|
||||
|
||||
#include "SparseCore"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
#include <cholmod.h>
|
||||
|
||||
/** \ingroup Support_modules
|
||||
* \defgroup CholmodSupport_Module CholmodSupport module
|
||||
*
|
||||
* This module provides an interface to the Cholmod library which is part of the <a
|
||||
* href="http://www.suitesparse.com">suitesparse</a> package. It provides the two following main factorization classes:
|
||||
* - class CholmodSupernodalLLT: a supernodal LLT Cholesky factorization.
|
||||
* - class CholmodDecomposition: a general L(D)LT Cholesky factorization with automatic or explicit runtime selection of
|
||||
* the underlying factorization method (supernodal or simplicial).
|
||||
*
|
||||
* For the sake of completeness, this module also propose the two following classes:
|
||||
* - class CholmodSimplicialLLT
|
||||
* - class CholmodSimplicialLDLT
|
||||
* Note that these classes does not bring any particular advantage compared to the built-in
|
||||
* SimplicialLLT and SimplicialLDLT factorization classes.
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/CholmodSupport>
|
||||
* \endcode
|
||||
*
|
||||
* In order to use this module, the cholmod headers must be accessible from the include paths, and your binary must be
|
||||
* linked to the cholmod library and its dependencies. The dependencies depend on how cholmod has been compiled. For a
|
||||
* cmake based project, you can use our FindCholmod.cmake module to help you in this task.
|
||||
*
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/CholmodSupport/CholmodSupport.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_CHOLMODSUPPORT_MODULE_H
|
||||
@@ -0,0 +1,482 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
// Copyright (C) 2007-2011 Benoit Jacob <jacob.benoit.1@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_CORE_MODULE_H
|
||||
#define EIGEN_CORE_MODULE_H
|
||||
|
||||
// Eigen version information.
|
||||
#include "Version"
|
||||
|
||||
// first thing Eigen does: stop the compiler from reporting useless warnings.
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
// then include this file where all our macros are defined. It's really important to do it first because
|
||||
// it's where we do all the compiler/OS/arch detections and define most defaults.
|
||||
#include "src/Core/util/Macros.h"
|
||||
|
||||
// This detects SSE/AVX/NEON/etc. and configure alignment settings
|
||||
#include "src/Core/util/ConfigureVectorization.h"
|
||||
|
||||
// We need cuda_runtime.h/hip_runtime.h to ensure that
|
||||
// the EIGEN_USING_STD macro works properly on the device side
|
||||
#if defined(EIGEN_CUDACC)
|
||||
#include <cuda_runtime.h>
|
||||
#elif defined(EIGEN_HIPCC)
|
||||
#include <hip/hip_runtime.h>
|
||||
#endif
|
||||
|
||||
#ifdef EIGEN_EXCEPTIONS
|
||||
#include <new>
|
||||
#endif
|
||||
|
||||
// Disable the ipa-cp-clone optimization flag with MinGW 6.x or older (enabled by default with -O3)
|
||||
// See http://eigen.tuxfamily.org/bz/show_bug.cgi?id=556 for details.
|
||||
#if EIGEN_COMP_MINGW && EIGEN_GNUC_STRICT_LESS_THAN(6, 0, 0)
|
||||
#pragma GCC optimize("-fno-ipa-cp-clone")
|
||||
#endif
|
||||
|
||||
// Prevent ICC from specializing std::complex operators that silently fail
|
||||
// on device. This allows us to use our own device-compatible specializations
|
||||
// instead.
|
||||
#if EIGEN_COMP_ICC && defined(EIGEN_GPU_COMPILE_PHASE) && !defined(_OVERRIDE_COMPLEX_SPECIALIZATION_)
|
||||
#define _OVERRIDE_COMPLEX_SPECIALIZATION_ 1
|
||||
#endif
|
||||
#include <complex>
|
||||
|
||||
// this include file manages BLAS and MKL related macros
|
||||
// and inclusion of their respective header files
|
||||
#include "src/Core/util/MKL_support.h"
|
||||
#include "src/Core/util/AOCL_Support.h" // ← ADD THIS
|
||||
|
||||
|
||||
#if defined(EIGEN_HAS_CUDA_FP16) || defined(EIGEN_HAS_HIP_FP16)
|
||||
#define EIGEN_HAS_GPU_FP16
|
||||
#endif
|
||||
|
||||
#if defined(EIGEN_HAS_CUDA_BF16) || defined(EIGEN_HAS_HIP_BF16)
|
||||
#define EIGEN_HAS_GPU_BF16
|
||||
#endif
|
||||
|
||||
#if (defined _OPENMP) && (!defined EIGEN_DONT_PARALLELIZE)
|
||||
#define EIGEN_HAS_OPENMP
|
||||
#endif
|
||||
|
||||
#ifdef EIGEN_HAS_OPENMP
|
||||
#include <atomic>
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
// MSVC for windows mobile does not have the errno.h file
|
||||
#if !(EIGEN_COMP_MSVC && EIGEN_OS_WINCE) && !EIGEN_COMP_ARM
|
||||
#define EIGEN_HAS_ERRNO
|
||||
#endif
|
||||
|
||||
#ifdef EIGEN_HAS_ERRNO
|
||||
#include <cerrno>
|
||||
#endif
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#ifndef EIGEN_NO_IO
|
||||
#include <sstream>
|
||||
#include <iosfwd>
|
||||
#endif
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <limits>
|
||||
#include <climits> // for CHAR_BIT
|
||||
// for min/max:
|
||||
#include <algorithm>
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
// for std::is_nothrow_move_assignable
|
||||
#include <type_traits>
|
||||
|
||||
// for std::this_thread::yield().
|
||||
#if !defined(EIGEN_USE_BLAS) && (defined(EIGEN_HAS_OPENMP) || defined(EIGEN_GEMM_THREADPOOL))
|
||||
#include <thread>
|
||||
#endif
|
||||
|
||||
// for __cpp_lib feature test macros
|
||||
#if defined(__has_include) && __has_include(<version>)
|
||||
#include <version>
|
||||
#endif
|
||||
|
||||
// for std::bit_cast()
|
||||
#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L
|
||||
#include <bit>
|
||||
#endif
|
||||
|
||||
// for outputting debug info
|
||||
#ifdef EIGEN_DEBUG_ASSIGN
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
// required for __cpuid, needs to be included after cmath
|
||||
// also required for _BitScanReverse on Windows on ARM
|
||||
#if EIGEN_COMP_MSVC && (EIGEN_ARCH_i386_OR_x86_64 || EIGEN_ARCH_ARM64) && !EIGEN_OS_WINCE
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
#if defined(EIGEN_USE_SYCL)
|
||||
#undef min
|
||||
#undef max
|
||||
#undef isnan
|
||||
#undef isinf
|
||||
#undef isfinite
|
||||
#include <CL/sycl.hpp>
|
||||
#include <map>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#ifndef EIGEN_SYCL_LOCAL_THREAD_DIM0
|
||||
#define EIGEN_SYCL_LOCAL_THREAD_DIM0 16
|
||||
#endif
|
||||
#ifndef EIGEN_SYCL_LOCAL_THREAD_DIM1
|
||||
#define EIGEN_SYCL_LOCAL_THREAD_DIM1 16
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined EIGEN2_SUPPORT_STAGE40_FULL_EIGEN3_STRICTNESS || defined EIGEN2_SUPPORT_STAGE30_FULL_EIGEN3_API || \
|
||||
defined EIGEN2_SUPPORT_STAGE20_RESOLVE_API_CONFLICTS || defined EIGEN2_SUPPORT_STAGE10_FULL_EIGEN2_API || \
|
||||
defined EIGEN2_SUPPORT
|
||||
// This will generate an error message:
|
||||
#error Eigen2-support is only available up to version 3.2. Please go to "http://eigen.tuxfamily.org/index.php?title=Eigen2" for further information
|
||||
#endif
|
||||
|
||||
namespace Eigen {
|
||||
|
||||
// we use size_t frequently and we'll never remember to prepend it with std:: every time just to
|
||||
// ensure QNX/QCC support
|
||||
using std::size_t;
|
||||
// gcc 4.6.0 wants std:: for ptrdiff_t
|
||||
using std::ptrdiff_t;
|
||||
|
||||
} // namespace Eigen
|
||||
|
||||
/** \defgroup Core_Module Core module
|
||||
* This is the main module of Eigen providing dense matrix and vector support
|
||||
* (both fixed and dynamic size) with all the features corresponding to a BLAS library
|
||||
* and much more...
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/Core>
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
#ifdef EIGEN_USE_LAPACKE
|
||||
#ifdef EIGEN_USE_MKL
|
||||
#include "mkl_lapacke.h"
|
||||
#else
|
||||
#include "src/misc/lapacke.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/Core/util/Constants.h"
|
||||
#include "src/Core/util/Meta.h"
|
||||
#include "src/Core/util/Assert.h"
|
||||
#include "src/Core/util/ForwardDeclarations.h"
|
||||
#include "src/Core/util/StaticAssert.h"
|
||||
#include "src/Core/util/XprHelper.h"
|
||||
#include "src/Core/util/Memory.h"
|
||||
#include "src/Core/util/IntegralConstant.h"
|
||||
#include "src/Core/util/Serializer.h"
|
||||
#include "src/Core/util/SymbolicIndex.h"
|
||||
#include "src/Core/util/EmulateArray.h"
|
||||
#include "src/Core/util/MoreMeta.h"
|
||||
|
||||
#include "src/Core/NumTraits.h"
|
||||
#include "src/Core/MathFunctions.h"
|
||||
#include "src/Core/RandomImpl.h"
|
||||
#include "src/Core/GenericPacketMath.h"
|
||||
#include "src/Core/MathFunctionsImpl.h"
|
||||
#include "src/Core/arch/Default/ConjHelper.h"
|
||||
// Generic half float support
|
||||
#include "src/Core/arch/Default/Half.h"
|
||||
#include "src/Core/arch/Default/BFloat16.h"
|
||||
#include "src/Core/arch/Default/GenericPacketMathFunctionsFwd.h"
|
||||
|
||||
#if defined(EIGEN_VECTORIZE_GENERIC) && !defined(EIGEN_DONT_VECTORIZE)
|
||||
#include "src/Core/arch/clang/PacketMath.h"
|
||||
#include "src/Core/arch/clang/TypeCasting.h"
|
||||
#include "src/Core/arch/clang/Complex.h"
|
||||
#include "src/Core/arch/clang/Reductions.h"
|
||||
#include "src/Core/arch/clang/MathFunctions.h"
|
||||
#else
|
||||
#if defined EIGEN_VECTORIZE_AVX512
|
||||
#include "src/Core/arch/SSE/PacketMath.h"
|
||||
#include "src/Core/arch/SSE/Reductions.h"
|
||||
#include "src/Core/arch/AVX/PacketMath.h"
|
||||
#include "src/Core/arch/AVX/Reductions.h"
|
||||
#include "src/Core/arch/AVX512/PacketMath.h"
|
||||
#include "src/Core/arch/AVX512/Reductions.h"
|
||||
#if defined EIGEN_VECTORIZE_AVX512FP16
|
||||
#include "src/Core/arch/AVX512/PacketMathFP16.h"
|
||||
#endif
|
||||
#include "src/Core/arch/SSE/TypeCasting.h"
|
||||
#include "src/Core/arch/AVX/TypeCasting.h"
|
||||
#include "src/Core/arch/AVX512/TypeCasting.h"
|
||||
#if defined EIGEN_VECTORIZE_AVX512FP16
|
||||
#include "src/Core/arch/AVX512/TypeCastingFP16.h"
|
||||
#endif
|
||||
#include "src/Core/arch/SSE/Complex.h"
|
||||
#include "src/Core/arch/AVX/Complex.h"
|
||||
#include "src/Core/arch/AVX512/Complex.h"
|
||||
#include "src/Core/arch/SSE/MathFunctions.h"
|
||||
#include "src/Core/arch/AVX/MathFunctions.h"
|
||||
#include "src/Core/arch/AVX512/MathFunctions.h"
|
||||
#if defined EIGEN_VECTORIZE_AVX512FP16
|
||||
#include "src/Core/arch/AVX512/MathFunctionsFP16.h"
|
||||
#endif
|
||||
#include "src/Core/arch/AVX512/TrsmKernel.h"
|
||||
#elif defined EIGEN_VECTORIZE_AVX
|
||||
// Use AVX for floats and doubles, SSE for integers
|
||||
#include "src/Core/arch/SSE/PacketMath.h"
|
||||
#include "src/Core/arch/SSE/Reductions.h"
|
||||
#include "src/Core/arch/SSE/TypeCasting.h"
|
||||
#include "src/Core/arch/SSE/Complex.h"
|
||||
#include "src/Core/arch/AVX/PacketMath.h"
|
||||
#include "src/Core/arch/AVX/Reductions.h"
|
||||
#include "src/Core/arch/AVX/TypeCasting.h"
|
||||
#include "src/Core/arch/AVX/Complex.h"
|
||||
#include "src/Core/arch/SSE/MathFunctions.h"
|
||||
#include "src/Core/arch/AVX/MathFunctions.h"
|
||||
#elif defined EIGEN_VECTORIZE_SSE
|
||||
#include "src/Core/arch/SSE/PacketMath.h"
|
||||
#include "src/Core/arch/SSE/Reductions.h"
|
||||
#include "src/Core/arch/SSE/TypeCasting.h"
|
||||
#include "src/Core/arch/SSE/MathFunctions.h"
|
||||
#include "src/Core/arch/SSE/Complex.h"
|
||||
#endif
|
||||
|
||||
#if defined(EIGEN_VECTORIZE_ALTIVEC) || defined(EIGEN_VECTORIZE_VSX)
|
||||
#include "src/Core/arch/AltiVec/PacketMath.h"
|
||||
#include "src/Core/arch/AltiVec/TypeCasting.h"
|
||||
#include "src/Core/arch/AltiVec/MathFunctions.h"
|
||||
#include "src/Core/arch/AltiVec/Complex.h"
|
||||
#elif defined EIGEN_VECTORIZE_NEON
|
||||
#include "src/Core/arch/NEON/PacketMath.h"
|
||||
#include "src/Core/arch/NEON/TypeCasting.h"
|
||||
#include "src/Core/arch/NEON/MathFunctions.h"
|
||||
#include "src/Core/arch/NEON/Complex.h"
|
||||
#elif defined EIGEN_VECTORIZE_LSX
|
||||
#include "src/Core/arch/LSX/PacketMath.h"
|
||||
#include "src/Core/arch/LSX/TypeCasting.h"
|
||||
#include "src/Core/arch/LSX/MathFunctions.h"
|
||||
#include "src/Core/arch/LSX/Complex.h"
|
||||
#elif defined EIGEN_VECTORIZE_SVE
|
||||
#include "src/Core/arch/SVE/PacketMath.h"
|
||||
#include "src/Core/arch/SVE/TypeCasting.h"
|
||||
#include "src/Core/arch/SVE/MathFunctions.h"
|
||||
#elif defined EIGEN_VECTORIZE_RVV10
|
||||
#include "src/Core/arch/RVV10/PacketMath.h"
|
||||
#include "src/Core/arch/RVV10/PacketMath4.h"
|
||||
#include "src/Core/arch/RVV10/PacketMath2.h"
|
||||
#include "src/Core/arch/RVV10/TypeCasting.h"
|
||||
#include "src/Core/arch/RVV10/MathFunctions.h"
|
||||
#if defined EIGEN_VECTORIZE_RVV10FP16
|
||||
#include "src/Core/arch/RVV10/PacketMathFP16.h"
|
||||
#endif
|
||||
#if defined EIGEN_VECTORIZE_RVV10BF16
|
||||
#include "src/Core/arch/RVV10/PacketMathBF16.h"
|
||||
#endif
|
||||
#elif defined EIGEN_VECTORIZE_ZVECTOR
|
||||
#include "src/Core/arch/ZVector/PacketMath.h"
|
||||
#include "src/Core/arch/ZVector/MathFunctions.h"
|
||||
#include "src/Core/arch/ZVector/Complex.h"
|
||||
#elif defined EIGEN_VECTORIZE_MSA
|
||||
#include "src/Core/arch/MSA/PacketMath.h"
|
||||
#include "src/Core/arch/MSA/MathFunctions.h"
|
||||
#include "src/Core/arch/MSA/Complex.h"
|
||||
#elif defined EIGEN_VECTORIZE_HVX
|
||||
#include "src/Core/arch/HVX/PacketMath.h"
|
||||
#endif
|
||||
|
||||
#if defined EIGEN_VECTORIZE_GPU
|
||||
#include "src/Core/arch/GPU/PacketMath.h"
|
||||
#include "src/Core/arch/GPU/MathFunctions.h"
|
||||
#include "src/Core/arch/GPU/TypeCasting.h"
|
||||
#endif
|
||||
|
||||
#if defined(EIGEN_USE_SYCL)
|
||||
#include "src/Core/arch/SYCL/InteropHeaders.h"
|
||||
#if !defined(EIGEN_DONT_VECTORIZE_SYCL)
|
||||
#include "src/Core/arch/SYCL/PacketMath.h"
|
||||
#include "src/Core/arch/SYCL/MathFunctions.h"
|
||||
#include "src/Core/arch/SYCL/TypeCasting.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // #ifndef EIGEN_VECTORIZE_GENERIC
|
||||
|
||||
#include "src/Core/arch/Default/Settings.h"
|
||||
// This file provides generic implementations valid for scalar as well
|
||||
#include "src/Core/arch/Default/GenericPacketMathFunctions.h"
|
||||
|
||||
#include "src/Core/functors/TernaryFunctors.h"
|
||||
#include "src/Core/functors/BinaryFunctors.h"
|
||||
#include "src/Core/functors/UnaryFunctors.h"
|
||||
#include "src/Core/functors/NullaryFunctors.h"
|
||||
#include "src/Core/functors/StlFunctors.h"
|
||||
#include "src/Core/functors/AssignmentFunctors.h"
|
||||
|
||||
// Specialized functors for GPU.
|
||||
#ifdef EIGEN_GPUCC
|
||||
#include "src/Core/arch/GPU/Complex.h"
|
||||
#endif
|
||||
|
||||
// Specializations of vectorized activation functions for NEON.
|
||||
#ifdef EIGEN_VECTORIZE_NEON
|
||||
#include "src/Core/arch/NEON/UnaryFunctors.h"
|
||||
#endif
|
||||
|
||||
#include "src/Core/util/IndexedViewHelper.h"
|
||||
#include "src/Core/util/ReshapedHelper.h"
|
||||
#include "src/Core/ArithmeticSequence.h"
|
||||
#ifndef EIGEN_NO_IO
|
||||
#include "src/Core/IO.h"
|
||||
#endif
|
||||
#include "src/Core/DenseCoeffsBase.h"
|
||||
#include "src/Core/DenseBase.h"
|
||||
#include "src/Core/MatrixBase.h"
|
||||
#include "src/Core/EigenBase.h"
|
||||
|
||||
#include "src/Core/Product.h"
|
||||
#include "src/Core/CoreEvaluators.h"
|
||||
#include "src/Core/AssignEvaluator.h"
|
||||
#include "src/Core/RealView.h"
|
||||
#include "src/Core/Assign.h"
|
||||
|
||||
#include "src/Core/ArrayBase.h"
|
||||
#include "src/Core/util/BlasUtil.h"
|
||||
#include "src/Core/DenseStorage.h"
|
||||
#include "src/Core/NestByValue.h"
|
||||
|
||||
// #include "src/Core/ForceAlignedAccess.h"
|
||||
|
||||
#include "src/Core/ReturnByValue.h"
|
||||
#include "src/Core/NoAlias.h"
|
||||
#include "src/Core/PlainObjectBase.h"
|
||||
#include "src/Core/Matrix.h"
|
||||
#include "src/Core/Array.h"
|
||||
#include "src/Core/Fill.h"
|
||||
#include "src/Core/CwiseTernaryOp.h"
|
||||
#include "src/Core/CwiseBinaryOp.h"
|
||||
#include "src/Core/CwiseUnaryOp.h"
|
||||
#include "src/Core/CwiseNullaryOp.h"
|
||||
#include "src/Core/CwiseUnaryView.h"
|
||||
#include "src/Core/SelfCwiseBinaryOp.h"
|
||||
#include "src/Core/InnerProduct.h"
|
||||
#include "src/Core/Dot.h"
|
||||
#include "src/Core/StableNorm.h"
|
||||
#include "src/Core/Stride.h"
|
||||
#include "src/Core/MapBase.h"
|
||||
#include "src/Core/Map.h"
|
||||
#include "src/Core/Ref.h"
|
||||
#include "src/Core/Block.h"
|
||||
#include "src/Core/VectorBlock.h"
|
||||
#include "src/Core/IndexedView.h"
|
||||
#include "src/Core/Reshaped.h"
|
||||
#include "src/Core/Transpose.h"
|
||||
#include "src/Core/DiagonalMatrix.h"
|
||||
#include "src/Core/Diagonal.h"
|
||||
#include "src/Core/DiagonalProduct.h"
|
||||
#include "src/Core/SkewSymmetricMatrix3.h"
|
||||
#include "src/Core/Redux.h"
|
||||
#include "src/Core/Visitor.h"
|
||||
#include "src/Core/FindCoeff.h"
|
||||
#include "src/Core/Fuzzy.h"
|
||||
#include "src/Core/Swap.h"
|
||||
#include "src/Core/CommaInitializer.h"
|
||||
#include "src/Core/GeneralProduct.h"
|
||||
#include "src/Core/Solve.h"
|
||||
#include "src/Core/Inverse.h"
|
||||
#include "src/Core/SolverBase.h"
|
||||
#include "src/Core/PermutationMatrix.h"
|
||||
#include "src/Core/Transpositions.h"
|
||||
#include "src/Core/TriangularMatrix.h"
|
||||
#include "src/Core/SelfAdjointView.h"
|
||||
#include "src/Core/products/GeneralBlockPanelKernel.h"
|
||||
#include "src/Core/DeviceWrapper.h"
|
||||
#ifdef EIGEN_GEMM_THREADPOOL
|
||||
#include "ThreadPool"
|
||||
#endif
|
||||
#include "src/Core/products/Parallelizer.h"
|
||||
#include "src/Core/ProductEvaluators.h"
|
||||
#include "src/Core/products/GeneralMatrixVector.h"
|
||||
#include "src/Core/products/GeneralMatrixMatrix.h"
|
||||
#include "src/Core/SolveTriangular.h"
|
||||
#include "src/Core/products/GeneralMatrixMatrixTriangular.h"
|
||||
#include "src/Core/products/SelfadjointMatrixVector.h"
|
||||
#include "src/Core/products/SelfadjointMatrixMatrix.h"
|
||||
#include "src/Core/products/SelfadjointProduct.h"
|
||||
#include "src/Core/products/SelfadjointRank2Update.h"
|
||||
#include "src/Core/products/TriangularMatrixVector.h"
|
||||
#include "src/Core/products/TriangularMatrixMatrix.h"
|
||||
#include "src/Core/products/TriangularSolverMatrix.h"
|
||||
#include "src/Core/products/TriangularSolverVector.h"
|
||||
#include "src/Core/BandMatrix.h"
|
||||
#include "src/Core/CoreIterators.h"
|
||||
#include "src/Core/ConditionEstimator.h"
|
||||
|
||||
#if !defined(EIGEN_VECTORIZE_GENERIC)
|
||||
#if defined(EIGEN_VECTORIZE_VSX)
|
||||
#include "src/Core/arch/AltiVec/MatrixProduct.h"
|
||||
#elif defined EIGEN_VECTORIZE_NEON
|
||||
#include "src/Core/arch/NEON/GeneralBlockPanelKernel.h"
|
||||
#elif defined EIGEN_VECTORIZE_LSX
|
||||
#include "src/Core/arch/LSX/GeneralBlockPanelKernel.h"
|
||||
#elif defined EIGEN_VECTORIZE_RVV10
|
||||
#include "src/Core/arch/RVV10/GeneralBlockPanelKernel.h"
|
||||
#endif
|
||||
|
||||
#if defined(EIGEN_VECTORIZE_AVX512)
|
||||
#include "src/Core/arch/AVX512/GemmKernel.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "src/Core/Select.h"
|
||||
#include "src/Core/VectorwiseOp.h"
|
||||
#include "src/Core/PartialReduxEvaluator.h"
|
||||
#include "src/Core/Random.h"
|
||||
#include "src/Core/Replicate.h"
|
||||
#include "src/Core/Reverse.h"
|
||||
#include "src/Core/ArrayWrapper.h"
|
||||
#include "src/Core/StlIterators.h"
|
||||
|
||||
#ifdef EIGEN_USE_BLAS
|
||||
#include "src/Core/products/GeneralMatrixMatrix_BLAS.h"
|
||||
#include "src/Core/products/GeneralMatrixVector_BLAS.h"
|
||||
#include "src/Core/products/GeneralMatrixMatrixTriangular_BLAS.h"
|
||||
#include "src/Core/products/SelfadjointMatrixMatrix_BLAS.h"
|
||||
#include "src/Core/products/SelfadjointMatrixVector_BLAS.h"
|
||||
#include "src/Core/products/TriangularMatrixMatrix_BLAS.h"
|
||||
#include "src/Core/products/TriangularMatrixVector_BLAS.h"
|
||||
#include "src/Core/products/TriangularSolverMatrix_BLAS.h"
|
||||
#endif // EIGEN_USE_BLAS
|
||||
|
||||
#ifdef EIGEN_USE_MKL_VML
|
||||
#include "src/Core/Assign_MKL.h"
|
||||
#endif
|
||||
|
||||
#ifdef EIGEN_USE_AOCL_VML
|
||||
#include "src/Core/Assign_AOCL.h"
|
||||
#endif
|
||||
|
||||
#include "src/Core/GlobalFunctions.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_CORE_MODULE_H
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "Core"
|
||||
#include "LU"
|
||||
#include "Cholesky"
|
||||
#include "QR"
|
||||
#include "SVD"
|
||||
#include "Geometry"
|
||||
#include "Eigenvalues"
|
||||
@@ -0,0 +1,2 @@
|
||||
#include "Dense"
|
||||
#include "Sparse"
|
||||
@@ -0,0 +1,63 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_EIGENVALUES_MODULE_H
|
||||
#define EIGEN_EIGENVALUES_MODULE_H
|
||||
|
||||
#include "Core"
|
||||
|
||||
#include "Cholesky"
|
||||
#include "Jacobi"
|
||||
#include "Householder"
|
||||
#include "LU"
|
||||
#include "Geometry"
|
||||
#include "Sparse" // Needed by ComplexQZ.
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
/** \defgroup Eigenvalues_Module Eigenvalues module
|
||||
*
|
||||
*
|
||||
*
|
||||
* This module mainly provides various eigenvalue solvers.
|
||||
* This module also provides some MatrixBase methods, including:
|
||||
* - MatrixBase::eigenvalues(),
|
||||
* - MatrixBase::operatorNorm()
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/Eigenvalues>
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/Eigenvalues/Tridiagonalization.h"
|
||||
#include "src/Eigenvalues/RealSchur.h"
|
||||
#include "src/Eigenvalues/EigenSolver.h"
|
||||
#include "src/Eigenvalues/SelfAdjointEigenSolver.h"
|
||||
#include "src/Eigenvalues/GeneralizedSelfAdjointEigenSolver.h"
|
||||
#include "src/Eigenvalues/HessenbergDecomposition.h"
|
||||
#include "src/Eigenvalues/ComplexSchur.h"
|
||||
#include "src/Eigenvalues/ComplexEigenSolver.h"
|
||||
#include "src/Eigenvalues/RealQZ.h"
|
||||
#include "src/Eigenvalues/ComplexQZ.h"
|
||||
#include "src/Eigenvalues/GeneralizedEigenSolver.h"
|
||||
#include "src/Eigenvalues/MatrixBaseEigenvalues.h"
|
||||
#ifdef EIGEN_USE_LAPACKE
|
||||
#ifdef EIGEN_USE_MKL
|
||||
#include "mkl_lapacke.h"
|
||||
#else
|
||||
#include "src/misc/lapacke.h"
|
||||
#endif
|
||||
#include "src/Eigenvalues/RealSchur_LAPACKE.h"
|
||||
#include "src/Eigenvalues/ComplexSchur_LAPACKE.h"
|
||||
#include "src/Eigenvalues/SelfAdjointEigenSolver_LAPACKE.h"
|
||||
#endif
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_EIGENVALUES_MODULE_H
|
||||
@@ -0,0 +1,62 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_GEOMETRY_MODULE_H
|
||||
#define EIGEN_GEOMETRY_MODULE_H
|
||||
|
||||
#include "Core"
|
||||
|
||||
#include "SVD"
|
||||
#include "LU"
|
||||
#include <limits>
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
/** \defgroup Geometry_Module Geometry module
|
||||
*
|
||||
* This module provides support for:
|
||||
* - fixed-size homogeneous transformations
|
||||
* - translation, scaling, 2D and 3D rotations
|
||||
* - \link Quaternion quaternions \endlink
|
||||
* - cross products (\ref MatrixBase::cross(), \ref MatrixBase::cross3())
|
||||
* - orthogonal vector generation (MatrixBase::unitOrthogonal)
|
||||
* - some linear components: \link ParametrizedLine parametrized-lines \endlink and \link Hyperplane hyperplanes \endlink
|
||||
* - \link AlignedBox axis aligned bounding boxes \endlink
|
||||
* - \link umeyama() least-square transformation fitting \endlink
|
||||
* \code
|
||||
* #include <Eigen/Geometry>
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/Geometry/OrthoMethods.h"
|
||||
#include "src/Geometry/EulerAngles.h"
|
||||
#include "src/Geometry/Homogeneous.h"
|
||||
#include "src/Geometry/RotationBase.h"
|
||||
#include "src/Geometry/Rotation2D.h"
|
||||
#include "src/Geometry/Quaternion.h"
|
||||
#include "src/Geometry/AngleAxis.h"
|
||||
#include "src/Geometry/Transform.h"
|
||||
#include "src/Geometry/Translation.h"
|
||||
#include "src/Geometry/Scaling.h"
|
||||
#include "src/Geometry/Hyperplane.h"
|
||||
#include "src/Geometry/ParametrizedLine.h"
|
||||
#include "src/Geometry/AlignedBox.h"
|
||||
#include "src/Geometry/Umeyama.h"
|
||||
|
||||
#ifndef EIGEN_VECTORIZE_GENERIC
|
||||
// TODO(rmlarsen): Make these work with generic vectorization if possible.
|
||||
// Use the SSE optimized version whenever possible.
|
||||
#if (defined EIGEN_VECTORIZE_SSE) || (defined EIGEN_VECTORIZE_NEON)
|
||||
#include "src/Geometry/arch/Geometry_SIMD.h"
|
||||
#endif
|
||||
#endif
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_GEOMETRY_MODULE_H
|
||||
@@ -0,0 +1,31 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_HOUSEHOLDER_MODULE_H
|
||||
#define EIGEN_HOUSEHOLDER_MODULE_H
|
||||
|
||||
#include "Core"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
/** \defgroup Householder_Module Householder module
|
||||
* This module provides Householder transformations.
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/Householder>
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/Householder/Householder.h"
|
||||
#include "src/Householder/HouseholderSequence.h"
|
||||
#include "src/Householder/BlockHouseholder.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_HOUSEHOLDER_MODULE_H
|
||||
@@ -0,0 +1,52 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_ITERATIVELINEARSOLVERS_MODULE_H
|
||||
#define EIGEN_ITERATIVELINEARSOLVERS_MODULE_H
|
||||
|
||||
#include "SparseCore"
|
||||
#include "OrderingMethods"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
/**
|
||||
* \defgroup IterativeLinearSolvers_Module IterativeLinearSolvers module
|
||||
*
|
||||
* This module currently provides iterative methods to solve problems of the form \c A \c x = \c b, where \c A is a
|
||||
squared matrix, usually very large and sparse.
|
||||
* Those solvers are accessible via the following classes:
|
||||
* - ConjugateGradient for selfadjoint (hermitian) matrices,
|
||||
* - LeastSquaresConjugateGradient for rectangular least-square problems,
|
||||
* - BiCGSTAB for general square matrices.
|
||||
*
|
||||
* These iterative solvers are associated with some preconditioners:
|
||||
* - IdentityPreconditioner - not really useful
|
||||
* - DiagonalPreconditioner - also called Jacobi preconditioner, work very well on diagonal dominant matrices.
|
||||
* - IncompleteLUT - incomplete LU factorization with dual thresholding
|
||||
*
|
||||
* Such problems can also be solved using the direct sparse decomposition modules: SparseCholesky, CholmodSupport,
|
||||
UmfPackSupport, SuperLUSupport, AccelerateSupport.
|
||||
*
|
||||
\code
|
||||
#include <Eigen/IterativeLinearSolvers>
|
||||
\endcode
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/IterativeLinearSolvers/SolveWithGuess.h"
|
||||
#include "src/IterativeLinearSolvers/IterativeSolverBase.h"
|
||||
#include "src/IterativeLinearSolvers/BasicPreconditioners.h"
|
||||
#include "src/IterativeLinearSolvers/ConjugateGradient.h"
|
||||
#include "src/IterativeLinearSolvers/LeastSquareConjugateGradient.h"
|
||||
#include "src/IterativeLinearSolvers/BiCGSTAB.h"
|
||||
#include "src/IterativeLinearSolvers/IncompleteLUT.h"
|
||||
#include "src/IterativeLinearSolvers/IncompleteCholesky.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_ITERATIVELINEARSOLVERS_MODULE_H
|
||||
@@ -0,0 +1,33 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_JACOBI_MODULE_H
|
||||
#define EIGEN_JACOBI_MODULE_H
|
||||
|
||||
#include "Core"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
/** \defgroup Jacobi_Module Jacobi module
|
||||
* This module provides Jacobi and Givens rotations.
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/Jacobi>
|
||||
* \endcode
|
||||
*
|
||||
* In addition to listed classes, it defines the two following MatrixBase methods to apply a Jacobi or Givens rotation:
|
||||
* - MatrixBase::applyOnTheLeft()
|
||||
* - MatrixBase::applyOnTheRight().
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/Jacobi/Jacobi.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_JACOBI_MODULE_H
|
||||
@@ -0,0 +1,43 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_KLUSUPPORT_MODULE_H
|
||||
#define EIGEN_KLUSUPPORT_MODULE_H
|
||||
|
||||
#include "SparseCore"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
extern "C" {
|
||||
#include <btf.h>
|
||||
#include <klu.h>
|
||||
}
|
||||
|
||||
/** \ingroup Support_modules
|
||||
* \defgroup KLUSupport_Module KLUSupport module
|
||||
*
|
||||
* This module provides an interface to the KLU library which is part of the <a
|
||||
* href="http://www.suitesparse.com">suitesparse</a> package. It provides the following factorization class:
|
||||
* - class KLU: a sparse LU factorization, well-suited for circuit simulation.
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/KLUSupport>
|
||||
* \endcode
|
||||
*
|
||||
* In order to use this module, the klu and btf headers must be accessible from the include paths, and your binary must
|
||||
* be linked to the klu library and its dependencies. The dependencies depend on how umfpack has been compiled. For a
|
||||
* cmake based project, you can use our FindKLU.cmake module to help you in this task.
|
||||
*
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/KLUSupport/KLUSupport.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_KLUSUPPORT_MODULE_H
|
||||
@@ -0,0 +1,49 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_LU_MODULE_H
|
||||
#define EIGEN_LU_MODULE_H
|
||||
|
||||
#include "Core"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
/** \defgroup LU_Module LU module
|
||||
* This module includes %LU decomposition and related notions such as matrix inversion and determinant.
|
||||
* This module defines the following MatrixBase methods:
|
||||
* - MatrixBase::inverse()
|
||||
* - MatrixBase::determinant()
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/LU>
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
#include "src/misc/Kernel.h"
|
||||
#include "src/misc/Image.h"
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/LU/FullPivLU.h"
|
||||
#include "src/LU/PartialPivLU.h"
|
||||
#ifdef EIGEN_USE_LAPACKE
|
||||
#include "src/misc/lapacke_helpers.h"
|
||||
#include "src/LU/PartialPivLU_LAPACKE.h"
|
||||
#endif
|
||||
#include "src/LU/Determinant.h"
|
||||
#include "src/LU/InverseImpl.h"
|
||||
|
||||
#ifndef EIGEN_VECTORIZE_GENERIC
|
||||
// TODO(rmlarsen): Make these work with generic vectorization if possible.
|
||||
#if defined EIGEN_VECTORIZE_SSE || defined EIGEN_VECTORIZE_NEON
|
||||
#include "src/LU/arch/InverseSize4.h"
|
||||
#endif
|
||||
#endif
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_LU_MODULE_H
|
||||
@@ -0,0 +1,35 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_METISSUPPORT_MODULE_H
|
||||
#define EIGEN_METISSUPPORT_MODULE_H
|
||||
|
||||
#include "SparseCore"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
extern "C" {
|
||||
#include <metis.h>
|
||||
}
|
||||
|
||||
/** \ingroup Support_modules
|
||||
* \defgroup MetisSupport_Module MetisSupport module
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/MetisSupport>
|
||||
* \endcode
|
||||
* This module defines an interface to the METIS reordering package (http://glaros.dtc.umn.edu/gkhome/views/metis).
|
||||
* It can be used just as any other built-in method as explained in \link OrderingMethods_Module here. \endlink
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/MetisSupport/MetisSupport.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_METISSUPPORT_MODULE_H
|
||||
@@ -0,0 +1,73 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_ORDERINGMETHODS_MODULE_H
|
||||
#define EIGEN_ORDERINGMETHODS_MODULE_H
|
||||
|
||||
#include "SparseCore"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
/**
|
||||
* \defgroup OrderingMethods_Module OrderingMethods module
|
||||
*
|
||||
* This module is currently for internal use only
|
||||
*
|
||||
* It defines various built-in and external ordering methods for sparse matrices.
|
||||
* They are typically used to reduce the number of elements during
|
||||
* the sparse matrix decomposition (LLT, LU, QR).
|
||||
* Precisely, in a preprocessing step, a permutation matrix P is computed using
|
||||
* those ordering methods and applied to the columns of the matrix.
|
||||
* Using for instance the sparse Cholesky decomposition, it is expected that
|
||||
* the nonzeros elements in LLT(A*P) will be much smaller than that in LLT(A).
|
||||
*
|
||||
*
|
||||
* Usage :
|
||||
* \code
|
||||
* #include <Eigen/OrderingMethods>
|
||||
* \endcode
|
||||
*
|
||||
* A simple usage is as a template parameter in the sparse decomposition classes :
|
||||
*
|
||||
* \code
|
||||
* SparseLU<MatrixType, COLAMDOrdering<int> > solver;
|
||||
* \endcode
|
||||
*
|
||||
* \code
|
||||
* SparseQR<MatrixType, COLAMDOrdering<int> > solver;
|
||||
* \endcode
|
||||
*
|
||||
* It is possible as well to call directly a particular ordering method for your own purpose,
|
||||
* \code
|
||||
* AMDOrdering<int> ordering;
|
||||
* PermutationMatrix<Dynamic, Dynamic, int> perm;
|
||||
* SparseMatrix<double> A;
|
||||
* //Fill the matrix ...
|
||||
*
|
||||
* ordering(A, perm); // Call AMD
|
||||
* \endcode
|
||||
*
|
||||
* \note Some of these methods (like AMD or METIS), need the sparsity pattern
|
||||
* of the input matrix to be symmetric. When the matrix is structurally unsymmetric,
|
||||
* Eigen computes internally the pattern of \f$A^T*A\f$ before calling the method.
|
||||
* If your matrix is already symmetric (at least in structure), you can avoid that
|
||||
* by calling the method with a SelfAdjointView type.
|
||||
*
|
||||
* \code
|
||||
* // Call the ordering on the pattern of the lower triangular matrix A
|
||||
* ordering(A.selfadjointView<Lower>(), perm);
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/OrderingMethods/Amd.h"
|
||||
#include "src/OrderingMethods/Ordering.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_ORDERINGMETHODS_MODULE_H
|
||||
@@ -0,0 +1,51 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_PASTIXSUPPORT_MODULE_H
|
||||
#define EIGEN_PASTIXSUPPORT_MODULE_H
|
||||
|
||||
#include "SparseCore"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
extern "C" {
|
||||
#include <pastix_nompi.h>
|
||||
#include <pastix.h>
|
||||
}
|
||||
|
||||
#ifdef complex
|
||||
#undef complex
|
||||
#endif
|
||||
|
||||
/** \ingroup Support_modules
|
||||
* \defgroup PaStiXSupport_Module PaStiXSupport module
|
||||
*
|
||||
* This module provides an interface to the <a href="http://pastix.gforge.inria.fr/">PaSTiX</a> library.
|
||||
* PaSTiX is a general \b supernodal, \b parallel and \b opensource sparse solver.
|
||||
* It provides the two following main factorization classes:
|
||||
* - class PastixLLT : a supernodal, parallel LLt Cholesky factorization.
|
||||
* - class PastixLDLT: a supernodal, parallel LDLt Cholesky factorization.
|
||||
* - class PastixLU : a supernodal, parallel LU factorization (optimized for a symmetric pattern).
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/PaStiXSupport>
|
||||
* \endcode
|
||||
*
|
||||
* In order to use this module, the PaSTiX headers must be accessible from the include paths, and your binary must be
|
||||
* linked to the PaSTiX library and its dependencies. This wrapper resuires PaStiX version 5.x compiled without MPI
|
||||
* support. The dependencies depend on how PaSTiX has been compiled. For a cmake based project, you can use our
|
||||
* FindPaSTiX.cmake module to help you in this task.
|
||||
*
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/PaStiXSupport/PaStiXSupport.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_PASTIXSUPPORT_MODULE_H
|
||||
@@ -0,0 +1,38 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_PARDISOSUPPORT_MODULE_H
|
||||
#define EIGEN_PARDISOSUPPORT_MODULE_H
|
||||
|
||||
#include "SparseCore"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
#include <mkl_pardiso.h>
|
||||
|
||||
/** \ingroup Support_modules
|
||||
* \defgroup PardisoSupport_Module PardisoSupport module
|
||||
*
|
||||
* This module brings support for the Intel(R) MKL PARDISO direct sparse solvers.
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/PardisoSupport>
|
||||
* \endcode
|
||||
*
|
||||
* In order to use this module, the MKL headers must be accessible from the include paths, and your binary must be
|
||||
* linked to the MKL library and its dependencies. See this \ref TopicUsingIntelMKL "page" for more information on
|
||||
* MKL-Eigen integration.
|
||||
*
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/PardisoSupport/PardisoSupport.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_PARDISOSUPPORT_MODULE_H
|
||||
@@ -0,0 +1,48 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_QR_MODULE_H
|
||||
#define EIGEN_QR_MODULE_H
|
||||
|
||||
#include "Core"
|
||||
|
||||
#include "Cholesky"
|
||||
#include "Jacobi"
|
||||
#include "Householder"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
/** \defgroup QR_Module QR module
|
||||
*
|
||||
*
|
||||
*
|
||||
* This module provides various QR decompositions
|
||||
* This module also provides some MatrixBase methods, including:
|
||||
* - MatrixBase::householderQr()
|
||||
* - MatrixBase::colPivHouseholderQr()
|
||||
* - MatrixBase::fullPivHouseholderQr()
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/QR>
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/QR/HouseholderQR.h"
|
||||
#include "src/QR/FullPivHouseholderQR.h"
|
||||
#include "src/QR/ColPivHouseholderQR.h"
|
||||
#include "src/QR/CompleteOrthogonalDecomposition.h"
|
||||
#ifdef EIGEN_USE_LAPACKE
|
||||
#include "src/misc/lapacke_helpers.h"
|
||||
#include "src/QR/HouseholderQR_LAPACKE.h"
|
||||
#include "src/QR/ColPivHouseholderQR_LAPACKE.h"
|
||||
#endif
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_QR_MODULE_H
|
||||
@@ -0,0 +1,32 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_QTMALLOC_MODULE_H
|
||||
#define EIGEN_QTMALLOC_MODULE_H
|
||||
|
||||
#include "Core"
|
||||
|
||||
#if (!EIGEN_MALLOC_ALREADY_ALIGNED)
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
void *qMalloc(std::size_t size) { return Eigen::internal::aligned_malloc(size); }
|
||||
|
||||
void qFree(void *ptr) { Eigen::internal::aligned_free(ptr); }
|
||||
|
||||
void *qRealloc(void *ptr, std::size_t size) {
|
||||
void *newPtr = Eigen::internal::aligned_malloc(size);
|
||||
std::memcpy(newPtr, ptr, size);
|
||||
Eigen::internal::aligned_free(ptr);
|
||||
return newPtr;
|
||||
}
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif
|
||||
|
||||
#endif // EIGEN_QTMALLOC_MODULE_H
|
||||
@@ -0,0 +1,41 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_SPQRSUPPORT_MODULE_H
|
||||
#define EIGEN_SPQRSUPPORT_MODULE_H
|
||||
|
||||
#include "SparseCore"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
#include "SuiteSparseQR.hpp"
|
||||
|
||||
/** \ingroup Support_modules
|
||||
* \defgroup SPQRSupport_Module SuiteSparseQR module
|
||||
*
|
||||
* This module provides an interface to the SPQR library, which is part of the <a
|
||||
* href="http://www.suitesparse.com">suitesparse</a> package.
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/SPQRSupport>
|
||||
* \endcode
|
||||
*
|
||||
* In order to use this module, the SPQR headers must be accessible from the include paths, and your binary must be
|
||||
* linked to the SPQR library and its dependencies (Cholmod, AMD, COLAMD,...). For a cmake based project, you can use
|
||||
* our FindSPQR.cmake and FindCholmod.Cmake modules
|
||||
*
|
||||
*/
|
||||
|
||||
#include "CholmodSupport"
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/SPQRSupport/SuiteSparseQRSupport.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_SVD_MODULE_H
|
||||
#define EIGEN_SVD_MODULE_H
|
||||
|
||||
#include "QR"
|
||||
#include "Householder"
|
||||
#include "Jacobi"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
/** \defgroup SVD_Module SVD module
|
||||
*
|
||||
*
|
||||
*
|
||||
* This module provides SVD decomposition for matrices (both real and complex).
|
||||
* Two decomposition algorithms are provided:
|
||||
* - JacobiSVD implementing two-sided Jacobi iterations is numerically very accurate, fast for small matrices, but very
|
||||
* slow for larger ones.
|
||||
* - BDCSVD implementing a recursive divide & conquer strategy on top of an upper-bidiagonalization which remains fast
|
||||
* for large problems. These decompositions are accessible via the respective classes and following MatrixBase methods:
|
||||
* - MatrixBase::jacobiSvd()
|
||||
* - MatrixBase::bdcSvd()
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/SVD>
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/SVD/UpperBidiagonalization.h"
|
||||
#include "src/SVD/SVDBase.h"
|
||||
#include "src/SVD/JacobiSVD.h"
|
||||
#include "src/SVD/BDCSVD.h"
|
||||
#ifdef EIGEN_USE_LAPACKE
|
||||
#ifdef EIGEN_USE_MKL
|
||||
#include "mkl_lapacke.h"
|
||||
#else
|
||||
#include "src/misc/lapacke.h"
|
||||
#endif
|
||||
#ifndef EIGEN_USE_LAPACKE_STRICT
|
||||
#include "src/SVD/JacobiSVD_LAPACKE.h"
|
||||
#endif
|
||||
#include "src/SVD/BDCSVD_LAPACKE.h"
|
||||
#endif
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_SVD_MODULE_H
|
||||
@@ -0,0 +1,33 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_SPARSE_MODULE_H
|
||||
#define EIGEN_SPARSE_MODULE_H
|
||||
|
||||
/** \defgroup Sparse_Module Sparse meta-module
|
||||
*
|
||||
* Meta-module including all related modules:
|
||||
* - \ref SparseCore_Module
|
||||
* - \ref OrderingMethods_Module
|
||||
* - \ref SparseCholesky_Module
|
||||
* - \ref SparseLU_Module
|
||||
* - \ref SparseQR_Module
|
||||
* - \ref IterativeLinearSolvers_Module
|
||||
*
|
||||
\code
|
||||
#include <Eigen/Sparse>
|
||||
\endcode
|
||||
*/
|
||||
|
||||
#include "SparseCore"
|
||||
#include "OrderingMethods"
|
||||
#include "SparseCholesky"
|
||||
#include "SparseLU"
|
||||
#include "SparseQR"
|
||||
#include "IterativeLinearSolvers"
|
||||
|
||||
#endif // EIGEN_SPARSE_MODULE_H
|
||||
@@ -0,0 +1,40 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2008-2013 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_SPARSECHOLESKY_MODULE_H
|
||||
#define EIGEN_SPARSECHOLESKY_MODULE_H
|
||||
|
||||
#include "SparseCore"
|
||||
#include "OrderingMethods"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
/**
|
||||
* \defgroup SparseCholesky_Module SparseCholesky module
|
||||
*
|
||||
* This module currently provides two variants of the direct sparse Cholesky decomposition for selfadjoint (hermitian)
|
||||
* matrices. Those decompositions are accessible via the following classes:
|
||||
* - SimplicialLLt,
|
||||
* - SimplicialLDLt
|
||||
*
|
||||
* Such problems can also be solved using the ConjugateGradient solver from the IterativeLinearSolvers module.
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/SparseCholesky>
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/SparseCholesky/SimplicialCholesky.h"
|
||||
#include "src/SparseCholesky/SimplicialCholesky_impl.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_SPARSECHOLESKY_MODULE_H
|
||||
@@ -0,0 +1,70 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_SPARSECORE_MODULE_H
|
||||
#define EIGEN_SPARSECORE_MODULE_H
|
||||
|
||||
#include "Core"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
/**
|
||||
* \defgroup SparseCore_Module SparseCore module
|
||||
*
|
||||
* This module provides a sparse matrix representation, and basic associated matrix manipulations
|
||||
* and operations.
|
||||
*
|
||||
* See the \ref TutorialSparse "Sparse tutorial"
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/SparseCore>
|
||||
* \endcode
|
||||
*
|
||||
* This module depends on: Core.
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/SparseCore/SparseUtil.h"
|
||||
#include "src/SparseCore/SparseMatrixBase.h"
|
||||
#include "src/SparseCore/SparseAssign.h"
|
||||
#include "src/SparseCore/CompressedStorage.h"
|
||||
#include "src/SparseCore/AmbiVector.h"
|
||||
#include "src/SparseCore/SparseCompressedBase.h"
|
||||
#include "src/SparseCore/SparseMatrix.h"
|
||||
#include "src/SparseCore/SparseMap.h"
|
||||
#include "src/SparseCore/SparseVector.h"
|
||||
#include "src/SparseCore/SparseRef.h"
|
||||
#include "src/SparseCore/SparseCwiseUnaryOp.h"
|
||||
#include "src/SparseCore/SparseCwiseBinaryOp.h"
|
||||
#include "src/SparseCore/SparseTranspose.h"
|
||||
#include "src/SparseCore/SparseBlock.h"
|
||||
#include "src/SparseCore/SparseDot.h"
|
||||
#include "src/SparseCore/SparseRedux.h"
|
||||
#include "src/SparseCore/SparseView.h"
|
||||
#include "src/SparseCore/SparseDiagonalProduct.h"
|
||||
#include "src/SparseCore/ConservativeSparseSparseProduct.h"
|
||||
#include "src/SparseCore/SparseSparseProductWithPruning.h"
|
||||
#include "src/SparseCore/SparseProduct.h"
|
||||
#include "src/SparseCore/SparseDenseProduct.h"
|
||||
#include "src/SparseCore/SparseSelfAdjointView.h"
|
||||
#include "src/SparseCore/SparseTriangularView.h"
|
||||
#include "src/SparseCore/TriangularSolver.h"
|
||||
#include "src/SparseCore/SparsePermutation.h"
|
||||
#include "src/SparseCore/SparseFuzzy.h"
|
||||
#include "src/SparseCore/SparseSolverBase.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_SPARSECORE_MODULE_H
|
||||
@@ -0,0 +1,50 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2012 Désiré Nuentsa-Wakam <desire.nuentsa_wakam@inria.fr>
|
||||
// Copyright (C) 2012 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_SPARSELU_MODULE_H
|
||||
#define EIGEN_SPARSELU_MODULE_H
|
||||
|
||||
#include "SparseCore"
|
||||
|
||||
/**
|
||||
* \defgroup SparseLU_Module SparseLU module
|
||||
* This module defines a supernodal factorization of general sparse matrices.
|
||||
* The code is fully optimized for supernode-panel updates with specialized kernels.
|
||||
* Please, see the documentation of the SparseLU class for more details.
|
||||
*/
|
||||
|
||||
// Ordering interface
|
||||
#include "OrderingMethods"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/SparseLU/SparseLU_Structs.h"
|
||||
#include "src/SparseLU/SparseLU_SupernodalMatrix.h"
|
||||
#include "src/SparseLU/SparseLUImpl.h"
|
||||
#include "src/SparseCore/SparseColEtree.h"
|
||||
#include "src/SparseLU/SparseLU_Memory.h"
|
||||
#include "src/SparseLU/SparseLU_heap_relax_snode.h"
|
||||
#include "src/SparseLU/SparseLU_relax_snode.h"
|
||||
#include "src/SparseLU/SparseLU_pivotL.h"
|
||||
#include "src/SparseLU/SparseLU_panel_dfs.h"
|
||||
#include "src/SparseLU/SparseLU_kernel_bmod.h"
|
||||
#include "src/SparseLU/SparseLU_panel_bmod.h"
|
||||
#include "src/SparseLU/SparseLU_column_dfs.h"
|
||||
#include "src/SparseLU/SparseLU_column_bmod.h"
|
||||
#include "src/SparseLU/SparseLU_copy_to_ucol.h"
|
||||
#include "src/SparseLU/SparseLU_pruneL.h"
|
||||
#include "src/SparseLU/SparseLU_Utils.h"
|
||||
#include "src/SparseLU/SparseLU.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_SPARSELU_MODULE_H
|
||||
@@ -0,0 +1,38 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_SPARSEQR_MODULE_H
|
||||
#define EIGEN_SPARSEQR_MODULE_H
|
||||
|
||||
#include "SparseCore"
|
||||
#include "OrderingMethods"
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
/** \defgroup SparseQR_Module SparseQR module
|
||||
* \brief Provides QR decomposition for sparse matrices
|
||||
*
|
||||
* This module provides a simplicial version of the left-looking Sparse QR decomposition.
|
||||
* The columns of the input matrix should be reordered to limit the fill-in during the
|
||||
* decomposition. Built-in methods (COLAMD, AMD) or external methods (METIS) can be used to this end.
|
||||
* See the \link OrderingMethods_Module OrderingMethods\endlink module for the list
|
||||
* of built-in and external ordering methods.
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/SparseQR>
|
||||
* \endcode
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/SparseCore/SparseColEtree.h"
|
||||
#include "src/SparseQR/SparseQR.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
// Copyright (C) 2009 Hauke Heibel <hauke.heibel@googlemail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_STDDEQUE_MODULE_H
|
||||
#define EIGEN_STDDEQUE_MODULE_H
|
||||
|
||||
#include "Core"
|
||||
#include <deque>
|
||||
|
||||
#if EIGEN_COMP_MSVC && EIGEN_OS_WIN64 && \
|
||||
(EIGEN_MAX_STATIC_ALIGN_BYTES <= 16) /* MSVC auto aligns up to 16 bytes in 64 bit builds */
|
||||
|
||||
#define EIGEN_DEFINE_STL_DEQUE_SPECIALIZATION(...)
|
||||
|
||||
#else
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/StlSupport/StdDeque.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#endif
|
||||
|
||||
#endif // EIGEN_STDDEQUE_MODULE_H
|
||||
@@ -0,0 +1,29 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2009 Hauke Heibel <hauke.heibel@googlemail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_STDLIST_MODULE_H
|
||||
#define EIGEN_STDLIST_MODULE_H
|
||||
|
||||
#include "Core"
|
||||
#include <list>
|
||||
|
||||
#if EIGEN_COMP_MSVC && EIGEN_OS_WIN64 && \
|
||||
(EIGEN_MAX_STATIC_ALIGN_BYTES <= 16) /* MSVC auto aligns up to 16 bytes in 64 bit builds */
|
||||
|
||||
#define EIGEN_DEFINE_STL_LIST_SPECIALIZATION(...)
|
||||
|
||||
#else
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/StlSupport/StdList.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#endif
|
||||
|
||||
#endif // EIGEN_STDLIST_MODULE_H
|
||||
@@ -0,0 +1,30 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
// Copyright (C) 2009 Hauke Heibel <hauke.heibel@googlemail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_STDVECTOR_MODULE_H
|
||||
#define EIGEN_STDVECTOR_MODULE_H
|
||||
|
||||
#include "Core"
|
||||
#include <vector>
|
||||
|
||||
#if EIGEN_COMP_MSVC && EIGEN_OS_WIN64 && \
|
||||
(EIGEN_MAX_STATIC_ALIGN_BYTES <= 16) /* MSVC auto aligns up to 16 bytes in 64 bit builds */
|
||||
|
||||
#define EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(...)
|
||||
|
||||
#else
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/StlSupport/StdVector.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#endif
|
||||
|
||||
#endif // EIGEN_STDVECTOR_MODULE_H
|
||||
@@ -0,0 +1,70 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_SUPERLUSUPPORT_MODULE_H
|
||||
#define EIGEN_SUPERLUSUPPORT_MODULE_H
|
||||
|
||||
#include "SparseCore"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
#ifdef EMPTY
|
||||
#define EIGEN_EMPTY_WAS_ALREADY_DEFINED
|
||||
#endif
|
||||
|
||||
typedef int int_t;
|
||||
#include <slu_Cnames.h>
|
||||
#include <supermatrix.h>
|
||||
#include <slu_util.h>
|
||||
|
||||
// slu_util.h defines a preprocessor token named EMPTY which is really polluting,
|
||||
// so we remove it in favor of a SUPERLU_EMPTY token.
|
||||
// If EMPTY was already defined then we don't undef it.
|
||||
|
||||
#if defined(EIGEN_EMPTY_WAS_ALREADY_DEFINED)
|
||||
#undef EIGEN_EMPTY_WAS_ALREADY_DEFINED
|
||||
#elif defined(EMPTY)
|
||||
#undef EMPTY
|
||||
#endif
|
||||
|
||||
#define SUPERLU_EMPTY (-1)
|
||||
|
||||
namespace Eigen {
|
||||
struct SluMatrix;
|
||||
}
|
||||
|
||||
/** \ingroup Support_modules
|
||||
* \defgroup SuperLUSupport_Module SuperLUSupport module
|
||||
*
|
||||
* This module provides an interface to the <a href="http://crd-legacy.lbl.gov/~xiaoye/SuperLU/">SuperLU</a> library.
|
||||
* It provides the following factorization class:
|
||||
* - class SuperLU: a supernodal sequential LU factorization.
|
||||
* - class SuperILU: a supernodal sequential incomplete LU factorization (to be used as a preconditioner for iterative
|
||||
* methods).
|
||||
*
|
||||
* \warning This wrapper requires at least versions 4.0 of SuperLU. The 3.x versions are not supported.
|
||||
*
|
||||
* \warning When including this module, you have to use SUPERLU_EMPTY instead of EMPTY which is no longer defined
|
||||
* because it is too polluting.
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/SuperLUSupport>
|
||||
* \endcode
|
||||
*
|
||||
* In order to use this module, the superlu headers must be accessible from the include paths, and your binary must be
|
||||
* linked to the superlu library and its dependencies. The dependencies depend on how superlu has been compiled. For a
|
||||
* cmake based project, you can use our FindSuperLU.cmake module to help you in this task.
|
||||
*
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/SuperLUSupport/SuperLUSupport.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_SUPERLUSUPPORT_MODULE_H
|
||||
@@ -0,0 +1,80 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2016 Benoit Steiner <benoit.steiner.goog@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_THREADPOOL_MODULE_H
|
||||
#define EIGEN_THREADPOOL_MODULE_H
|
||||
|
||||
#include "Core"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
/** \defgroup ThreadPool_Module ThreadPool Module
|
||||
*
|
||||
* This module provides 2 threadpool implementations
|
||||
* - a simple reference implementation
|
||||
* - a faster non blocking implementation
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/ThreadPool>
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <time.h>
|
||||
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
// There are non-parenthesized calls to "max" in the <unordered_map> header,
|
||||
// which trigger a check in test/main.h causing compilation to fail.
|
||||
// We work around the check here by removing the check for max in
|
||||
// the case where we have to emulate thread_local.
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
#include <unordered_map>
|
||||
|
||||
#include "src/Core/util/Meta.h"
|
||||
#include "src/Core/util/MaxSizeVector.h"
|
||||
|
||||
#ifndef EIGEN_MUTEX
|
||||
#define EIGEN_MUTEX std::mutex
|
||||
#endif
|
||||
#ifndef EIGEN_MUTEX_LOCK
|
||||
#define EIGEN_MUTEX_LOCK std::unique_lock<std::mutex>
|
||||
#endif
|
||||
#ifndef EIGEN_CONDVAR
|
||||
#define EIGEN_CONDVAR std::condition_variable
|
||||
#endif
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/ThreadPool/ThreadLocal.h"
|
||||
#include "src/ThreadPool/ThreadYield.h"
|
||||
#include "src/ThreadPool/ThreadCancel.h"
|
||||
#include "src/ThreadPool/EventCount.h"
|
||||
#include "src/ThreadPool/RunQueue.h"
|
||||
#include "src/ThreadPool/ThreadPoolInterface.h"
|
||||
#include "src/ThreadPool/ThreadEnvironment.h"
|
||||
#include "src/ThreadPool/Barrier.h"
|
||||
#include "src/ThreadPool/NonBlockingThreadPool.h"
|
||||
#include "src/ThreadPool/CoreThreadPoolDevice.h"
|
||||
#include "src/ThreadPool/ForkJoin.h"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_CXX11_THREADPOOL_MODULE_H
|
||||
@@ -0,0 +1,42 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_UMFPACKSUPPORT_MODULE_H
|
||||
#define EIGEN_UMFPACKSUPPORT_MODULE_H
|
||||
|
||||
#include "SparseCore"
|
||||
|
||||
#include "src/Core/util/DisableStupidWarnings.h"
|
||||
|
||||
extern "C" {
|
||||
#include <umfpack.h>
|
||||
}
|
||||
|
||||
/** \ingroup Support_modules
|
||||
* \defgroup UmfPackSupport_Module UmfPackSupport module
|
||||
*
|
||||
* This module provides an interface to the UmfPack library which is part of the <a
|
||||
* href="http://www.suitesparse.com">suitesparse</a> package. It provides the following factorization class:
|
||||
* - class UmfPackLU: a multifrontal sequential LU factorization.
|
||||
*
|
||||
* \code
|
||||
* #include <Eigen/UmfPackSupport>
|
||||
* \endcode
|
||||
*
|
||||
* In order to use this module, the umfpack headers must be accessible from the include paths, and your binary must be
|
||||
* linked to the umfpack library and its dependencies. The dependencies depend on how umfpack has been compiled. For a
|
||||
* cmake based project, you can use our FindUmfPack.cmake module to help you in this task.
|
||||
*
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "src/UmfPackSupport/UmfPackSupport.h"
|
||||
// IWYU pragma: endexports
|
||||
|
||||
#include "src/Core/util/ReenableStupidWarnings.h"
|
||||
|
||||
#endif // EIGEN_UMFPACKSUPPORT_MODULE_H
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef EIGEN_VERSION_H
|
||||
#define EIGEN_VERSION_H
|
||||
|
||||
// The "WORLD" version will forever remain "3" for the "Eigen3" library.
|
||||
#define EIGEN_WORLD_VERSION 3
|
||||
// As of Eigen3 5.0.0, we have moved to Semantic Versioning (semver.org).
|
||||
#define EIGEN_MAJOR_VERSION 5
|
||||
#define EIGEN_MINOR_VERSION 0
|
||||
#define EIGEN_PATCH_VERSION 1
|
||||
#define EIGEN_PRERELEASE_VERSION "dev"
|
||||
#define EIGEN_BUILD_VERSION "master"
|
||||
#define EIGEN_VERSION_STRING "5.0.1-dev+master"
|
||||
|
||||
#endif // EIGEN_VERSION_H
|
||||
+423
@@ -0,0 +1,423 @@
|
||||
#ifndef EIGEN_ACCELERATESUPPORT_H
|
||||
#define EIGEN_ACCELERATESUPPORT_H
|
||||
|
||||
#include <Accelerate/Accelerate.h>
|
||||
|
||||
#include <Eigen/Sparse>
|
||||
|
||||
namespace Eigen {
|
||||
|
||||
template <typename MatrixType_, int UpLo_, SparseFactorization_t Solver_, bool EnforceSquare_>
|
||||
class AccelerateImpl;
|
||||
|
||||
/** \ingroup AccelerateSupport_Module
|
||||
* \typedef AccelerateLLT
|
||||
* \brief A direct Cholesky (LLT) factorization and solver based on Accelerate
|
||||
*
|
||||
* \warning Only single and double precision real scalar types are supported by Accelerate
|
||||
*
|
||||
* \tparam MatrixType_ the type of the sparse matrix A, it must be a SparseMatrix<>
|
||||
* \tparam UpLo_ additional information about the matrix structure. Default is Lower.
|
||||
*
|
||||
* \sa \ref TutorialSparseSolverConcept, class AccelerateLLT
|
||||
*/
|
||||
template <typename MatrixType, int UpLo = Lower>
|
||||
using AccelerateLLT = AccelerateImpl<MatrixType, UpLo | Symmetric, SparseFactorizationCholesky, true>;
|
||||
|
||||
/** \ingroup AccelerateSupport_Module
|
||||
* \typedef AccelerateLDLT
|
||||
* \brief The default Cholesky (LDLT) factorization and solver based on Accelerate
|
||||
*
|
||||
* \warning Only single and double precision real scalar types are supported by Accelerate
|
||||
*
|
||||
* \tparam MatrixType_ the type of the sparse matrix A, it must be a SparseMatrix<>
|
||||
* \tparam UpLo_ additional information about the matrix structure. Default is Lower.
|
||||
*
|
||||
* \sa \ref TutorialSparseSolverConcept, class AccelerateLDLT
|
||||
*/
|
||||
template <typename MatrixType, int UpLo = Lower>
|
||||
using AccelerateLDLT = AccelerateImpl<MatrixType, UpLo | Symmetric, SparseFactorizationLDLT, true>;
|
||||
|
||||
/** \ingroup AccelerateSupport_Module
|
||||
* \typedef AccelerateLDLTUnpivoted
|
||||
* \brief A direct Cholesky-like LDL^T factorization and solver based on Accelerate with only 1x1 pivots and no pivoting
|
||||
*
|
||||
* \warning Only single and double precision real scalar types are supported by Accelerate
|
||||
*
|
||||
* \tparam MatrixType_ the type of the sparse matrix A, it must be a SparseMatrix<>
|
||||
* \tparam UpLo_ additional information about the matrix structure. Default is Lower.
|
||||
*
|
||||
* \sa \ref TutorialSparseSolverConcept, class AccelerateLDLTUnpivoted
|
||||
*/
|
||||
template <typename MatrixType, int UpLo = Lower>
|
||||
using AccelerateLDLTUnpivoted = AccelerateImpl<MatrixType, UpLo | Symmetric, SparseFactorizationLDLTUnpivoted, true>;
|
||||
|
||||
/** \ingroup AccelerateSupport_Module
|
||||
* \typedef AccelerateLDLTSBK
|
||||
* \brief A direct Cholesky (LDLT) factorization and solver based on Accelerate with Supernode Bunch-Kaufman and static
|
||||
* pivoting
|
||||
*
|
||||
* \warning Only single and double precision real scalar types are supported by Accelerate
|
||||
*
|
||||
* \tparam MatrixType_ the type of the sparse matrix A, it must be a SparseMatrix<>
|
||||
* \tparam UpLo_ additional information about the matrix structure. Default is Lower.
|
||||
*
|
||||
* \sa \ref TutorialSparseSolverConcept, class AccelerateLDLTSBK
|
||||
*/
|
||||
template <typename MatrixType, int UpLo = Lower>
|
||||
using AccelerateLDLTSBK = AccelerateImpl<MatrixType, UpLo | Symmetric, SparseFactorizationLDLTSBK, true>;
|
||||
|
||||
/** \ingroup AccelerateSupport_Module
|
||||
* \typedef AccelerateLDLTTPP
|
||||
* \brief A direct Cholesky (LDLT) factorization and solver based on Accelerate with full threshold partial pivoting
|
||||
*
|
||||
* \warning Only single and double precision real scalar types are supported by Accelerate
|
||||
*
|
||||
* \tparam MatrixType_ the type of the sparse matrix A, it must be a SparseMatrix<>
|
||||
* \tparam UpLo_ additional information about the matrix structure. Default is Lower.
|
||||
*
|
||||
* \sa \ref TutorialSparseSolverConcept, class AccelerateLDLTTPP
|
||||
*/
|
||||
template <typename MatrixType, int UpLo = Lower>
|
||||
using AccelerateLDLTTPP = AccelerateImpl<MatrixType, UpLo | Symmetric, SparseFactorizationLDLTTPP, true>;
|
||||
|
||||
/** \ingroup AccelerateSupport_Module
|
||||
* \typedef AccelerateQR
|
||||
* \brief A QR factorization and solver based on Accelerate
|
||||
*
|
||||
* \warning Only single and double precision real scalar types are supported by Accelerate
|
||||
*
|
||||
* \tparam MatrixType_ the type of the sparse matrix A, it must be a SparseMatrix<>
|
||||
*
|
||||
* \sa \ref TutorialSparseSolverConcept, class AccelerateQR
|
||||
*/
|
||||
template <typename MatrixType>
|
||||
using AccelerateQR = AccelerateImpl<MatrixType, 0, SparseFactorizationQR, false>;
|
||||
|
||||
/** \ingroup AccelerateSupport_Module
|
||||
* \typedef AccelerateCholeskyAtA
|
||||
* \brief A QR factorization and solver based on Accelerate without storing Q (equivalent to A^TA = R^T R)
|
||||
*
|
||||
* \warning Only single and double precision real scalar types are supported by Accelerate
|
||||
*
|
||||
* \tparam MatrixType_ the type of the sparse matrix A, it must be a SparseMatrix<>
|
||||
*
|
||||
* \sa \ref TutorialSparseSolverConcept, class AccelerateCholeskyAtA
|
||||
*/
|
||||
template <typename MatrixType>
|
||||
using AccelerateCholeskyAtA = AccelerateImpl<MatrixType, 0, SparseFactorizationCholeskyAtA, false>;
|
||||
|
||||
namespace internal {
|
||||
template <typename T>
|
||||
struct AccelFactorizationDeleter {
|
||||
void operator()(T* sym) {
|
||||
if (sym) {
|
||||
SparseCleanup(*sym);
|
||||
delete sym;
|
||||
sym = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename DenseVecT, typename DenseMatT, typename SparseMatT, typename NumFactT>
|
||||
struct SparseTypesTraitBase {
|
||||
typedef DenseVecT AccelDenseVector;
|
||||
typedef DenseMatT AccelDenseMatrix;
|
||||
typedef SparseMatT AccelSparseMatrix;
|
||||
|
||||
typedef SparseOpaqueSymbolicFactorization SymbolicFactorization;
|
||||
typedef NumFactT NumericFactorization;
|
||||
|
||||
typedef AccelFactorizationDeleter<SymbolicFactorization> SymbolicFactorizationDeleter;
|
||||
typedef AccelFactorizationDeleter<NumericFactorization> NumericFactorizationDeleter;
|
||||
};
|
||||
|
||||
template <typename Scalar>
|
||||
struct SparseTypesTrait {};
|
||||
|
||||
template <>
|
||||
struct SparseTypesTrait<double> : SparseTypesTraitBase<DenseVector_Double, DenseMatrix_Double, SparseMatrix_Double,
|
||||
SparseOpaqueFactorization_Double> {};
|
||||
|
||||
template <>
|
||||
struct SparseTypesTrait<float>
|
||||
: SparseTypesTraitBase<DenseVector_Float, DenseMatrix_Float, SparseMatrix_Float, SparseOpaqueFactorization_Float> {
|
||||
};
|
||||
|
||||
} // end namespace internal
|
||||
|
||||
template <typename MatrixType_, int UpLo_, SparseFactorization_t Solver_, bool EnforceSquare_>
|
||||
class AccelerateImpl : public SparseSolverBase<AccelerateImpl<MatrixType_, UpLo_, Solver_, EnforceSquare_> > {
|
||||
protected:
|
||||
using Base = SparseSolverBase<AccelerateImpl>;
|
||||
using Base::derived;
|
||||
using Base::m_isInitialized;
|
||||
|
||||
public:
|
||||
using Base::_solve_impl;
|
||||
|
||||
typedef MatrixType_ MatrixType;
|
||||
typedef typename MatrixType::Scalar Scalar;
|
||||
typedef typename MatrixType::StorageIndex StorageIndex;
|
||||
enum { ColsAtCompileTime = Dynamic, MaxColsAtCompileTime = Dynamic };
|
||||
enum { UpLo = UpLo_ };
|
||||
|
||||
using AccelDenseVector = typename internal::SparseTypesTrait<Scalar>::AccelDenseVector;
|
||||
using AccelDenseMatrix = typename internal::SparseTypesTrait<Scalar>::AccelDenseMatrix;
|
||||
using AccelSparseMatrix = typename internal::SparseTypesTrait<Scalar>::AccelSparseMatrix;
|
||||
using SymbolicFactorization = typename internal::SparseTypesTrait<Scalar>::SymbolicFactorization;
|
||||
using NumericFactorization = typename internal::SparseTypesTrait<Scalar>::NumericFactorization;
|
||||
using SymbolicFactorizationDeleter = typename internal::SparseTypesTrait<Scalar>::SymbolicFactorizationDeleter;
|
||||
using NumericFactorizationDeleter = typename internal::SparseTypesTrait<Scalar>::NumericFactorizationDeleter;
|
||||
|
||||
AccelerateImpl() {
|
||||
m_isInitialized = false;
|
||||
|
||||
auto check_flag_set = [](int value, int flag) { return ((value & flag) == flag); };
|
||||
|
||||
if (check_flag_set(UpLo_, Symmetric)) {
|
||||
m_sparseKind = SparseSymmetric;
|
||||
m_triType = (UpLo_ & Lower) ? SparseLowerTriangle : SparseUpperTriangle;
|
||||
} else if (check_flag_set(UpLo_, UnitLower)) {
|
||||
m_sparseKind = SparseUnitTriangular;
|
||||
m_triType = SparseLowerTriangle;
|
||||
} else if (check_flag_set(UpLo_, UnitUpper)) {
|
||||
m_sparseKind = SparseUnitTriangular;
|
||||
m_triType = SparseUpperTriangle;
|
||||
} else if (check_flag_set(UpLo_, StrictlyLower)) {
|
||||
m_sparseKind = SparseTriangular;
|
||||
m_triType = SparseLowerTriangle;
|
||||
} else if (check_flag_set(UpLo_, StrictlyUpper)) {
|
||||
m_sparseKind = SparseTriangular;
|
||||
m_triType = SparseUpperTriangle;
|
||||
} else if (check_flag_set(UpLo_, Lower)) {
|
||||
m_sparseKind = SparseTriangular;
|
||||
m_triType = SparseLowerTriangle;
|
||||
} else if (check_flag_set(UpLo_, Upper)) {
|
||||
m_sparseKind = SparseTriangular;
|
||||
m_triType = SparseUpperTriangle;
|
||||
} else {
|
||||
m_sparseKind = SparseOrdinary;
|
||||
m_triType = (UpLo_ & Lower) ? SparseLowerTriangle : SparseUpperTriangle;
|
||||
}
|
||||
|
||||
m_order = SparseOrderDefault;
|
||||
}
|
||||
|
||||
explicit AccelerateImpl(const MatrixType& matrix) : AccelerateImpl() { compute(matrix); }
|
||||
|
||||
~AccelerateImpl() {}
|
||||
|
||||
inline Index cols() const { return m_nCols; }
|
||||
inline Index rows() const { return m_nRows; }
|
||||
|
||||
ComputationInfo info() const {
|
||||
eigen_assert(m_isInitialized && "Decomposition is not initialized.");
|
||||
return m_info;
|
||||
}
|
||||
|
||||
void analyzePattern(const MatrixType& matrix);
|
||||
|
||||
void factorize(const MatrixType& matrix);
|
||||
|
||||
void compute(const MatrixType& matrix);
|
||||
|
||||
template <typename Rhs, typename Dest>
|
||||
void _solve_impl(const MatrixBase<Rhs>& b, MatrixBase<Dest>& dest) const;
|
||||
|
||||
/** Sets the ordering algorithm to use. */
|
||||
void setOrder(SparseOrder_t order) { m_order = order; }
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void buildAccelSparseMatrix(const SparseMatrix<T>& a, AccelSparseMatrix& A, std::vector<long>& columnStarts) {
|
||||
const Index nColumnsStarts = a.cols() + 1;
|
||||
|
||||
columnStarts.resize(nColumnsStarts);
|
||||
|
||||
for (Index i = 0; i < nColumnsStarts; i++) columnStarts[i] = a.outerIndexPtr()[i];
|
||||
|
||||
SparseAttributes_t attributes{};
|
||||
attributes.transpose = false;
|
||||
attributes.triangle = m_triType;
|
||||
attributes.kind = m_sparseKind;
|
||||
|
||||
SparseMatrixStructure structure{};
|
||||
structure.attributes = attributes;
|
||||
structure.rowCount = static_cast<int>(a.rows());
|
||||
structure.columnCount = static_cast<int>(a.cols());
|
||||
structure.blockSize = 1;
|
||||
structure.columnStarts = columnStarts.data();
|
||||
structure.rowIndices = const_cast<int*>(a.innerIndexPtr());
|
||||
|
||||
A.structure = structure;
|
||||
A.data = const_cast<T*>(a.valuePtr());
|
||||
}
|
||||
|
||||
void doAnalysis(AccelSparseMatrix& A) {
|
||||
m_numericFactorization.reset(nullptr);
|
||||
|
||||
SparseSymbolicFactorOptions opts{};
|
||||
opts.control = SparseDefaultControl;
|
||||
opts.orderMethod = m_order;
|
||||
opts.order = nullptr;
|
||||
opts.ignoreRowsAndColumns = nullptr;
|
||||
opts.malloc = malloc;
|
||||
opts.free = free;
|
||||
opts.reportError = nullptr;
|
||||
|
||||
m_symbolicFactorization.reset(new SymbolicFactorization(SparseFactor(Solver_, A.structure, opts)));
|
||||
|
||||
SparseStatus_t status = m_symbolicFactorization->status;
|
||||
|
||||
updateInfoStatus(status);
|
||||
|
||||
if (status != SparseStatusOK) m_symbolicFactorization.reset(nullptr);
|
||||
}
|
||||
|
||||
void doFactorization(AccelSparseMatrix& A) {
|
||||
SparseStatus_t status = SparseStatusReleased;
|
||||
|
||||
if (m_symbolicFactorization) {
|
||||
m_numericFactorization.reset(new NumericFactorization(SparseFactor(*m_symbolicFactorization, A)));
|
||||
|
||||
status = m_numericFactorization->status;
|
||||
|
||||
if (status != SparseStatusOK) m_numericFactorization.reset(nullptr);
|
||||
}
|
||||
|
||||
updateInfoStatus(status);
|
||||
}
|
||||
|
||||
protected:
|
||||
void updateInfoStatus(SparseStatus_t status) const {
|
||||
switch (status) {
|
||||
case SparseStatusOK:
|
||||
m_info = Success;
|
||||
break;
|
||||
case SparseFactorizationFailed:
|
||||
case SparseMatrixIsSingular:
|
||||
m_info = NumericalIssue;
|
||||
break;
|
||||
case SparseInternalError:
|
||||
case SparseParameterError:
|
||||
case SparseStatusReleased:
|
||||
default:
|
||||
m_info = InvalidInput;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutable ComputationInfo m_info;
|
||||
Index m_nRows, m_nCols;
|
||||
std::unique_ptr<SymbolicFactorization, SymbolicFactorizationDeleter> m_symbolicFactorization;
|
||||
std::unique_ptr<NumericFactorization, NumericFactorizationDeleter> m_numericFactorization;
|
||||
SparseKind_t m_sparseKind;
|
||||
SparseTriangle_t m_triType;
|
||||
SparseOrder_t m_order;
|
||||
};
|
||||
|
||||
/** Computes the symbolic and numeric decomposition of matrix \a a */
|
||||
template <typename MatrixType_, int UpLo_, SparseFactorization_t Solver_, bool EnforceSquare_>
|
||||
void AccelerateImpl<MatrixType_, UpLo_, Solver_, EnforceSquare_>::compute(const MatrixType& a) {
|
||||
if (EnforceSquare_) eigen_assert(a.rows() == a.cols());
|
||||
|
||||
m_nRows = a.rows();
|
||||
m_nCols = a.cols();
|
||||
|
||||
AccelSparseMatrix A{};
|
||||
std::vector<long> columnStarts;
|
||||
|
||||
buildAccelSparseMatrix(a, A, columnStarts);
|
||||
|
||||
doAnalysis(A);
|
||||
|
||||
if (m_symbolicFactorization) doFactorization(A);
|
||||
|
||||
m_isInitialized = true;
|
||||
}
|
||||
|
||||
/** Performs a symbolic decomposition on the sparsity pattern of matrix \a a.
|
||||
*
|
||||
* This function is particularly useful when solving for several problems having the same structure.
|
||||
*
|
||||
* \sa factorize()
|
||||
*/
|
||||
template <typename MatrixType_, int UpLo_, SparseFactorization_t Solver_, bool EnforceSquare_>
|
||||
void AccelerateImpl<MatrixType_, UpLo_, Solver_, EnforceSquare_>::analyzePattern(const MatrixType& a) {
|
||||
if (EnforceSquare_) eigen_assert(a.rows() == a.cols());
|
||||
|
||||
m_nRows = a.rows();
|
||||
m_nCols = a.cols();
|
||||
|
||||
AccelSparseMatrix A{};
|
||||
std::vector<long> columnStarts;
|
||||
|
||||
buildAccelSparseMatrix(a, A, columnStarts);
|
||||
|
||||
doAnalysis(A);
|
||||
|
||||
m_isInitialized = true;
|
||||
}
|
||||
|
||||
/** Performs a numeric decomposition of matrix \a a.
|
||||
*
|
||||
* The given matrix must have the same sparsity pattern as the matrix on which the symbolic decomposition has been
|
||||
* performed.
|
||||
*
|
||||
* \sa analyzePattern()
|
||||
*/
|
||||
template <typename MatrixType_, int UpLo_, SparseFactorization_t Solver_, bool EnforceSquare_>
|
||||
void AccelerateImpl<MatrixType_, UpLo_, Solver_, EnforceSquare_>::factorize(const MatrixType& a) {
|
||||
eigen_assert(m_symbolicFactorization && "You must first call analyzePattern()");
|
||||
eigen_assert(m_nRows == a.rows() && m_nCols == a.cols());
|
||||
|
||||
if (EnforceSquare_) eigen_assert(a.rows() == a.cols());
|
||||
|
||||
AccelSparseMatrix A{};
|
||||
std::vector<long> columnStarts;
|
||||
|
||||
buildAccelSparseMatrix(a, A, columnStarts);
|
||||
|
||||
doFactorization(A);
|
||||
}
|
||||
|
||||
template <typename MatrixType_, int UpLo_, SparseFactorization_t Solver_, bool EnforceSquare_>
|
||||
template <typename Rhs, typename Dest>
|
||||
void AccelerateImpl<MatrixType_, UpLo_, Solver_, EnforceSquare_>::_solve_impl(const MatrixBase<Rhs>& b,
|
||||
MatrixBase<Dest>& x) const {
|
||||
if (!m_numericFactorization) {
|
||||
m_info = InvalidInput;
|
||||
return;
|
||||
}
|
||||
|
||||
eigen_assert(m_nRows == b.rows());
|
||||
eigen_assert(((b.cols() == 1) || b.outerStride() == b.rows()));
|
||||
|
||||
SparseStatus_t status = SparseStatusOK;
|
||||
|
||||
Scalar* b_ptr = const_cast<Scalar*>(b.derived().data());
|
||||
Scalar* x_ptr = const_cast<Scalar*>(x.derived().data());
|
||||
|
||||
AccelDenseMatrix xmat{};
|
||||
xmat.attributes = SparseAttributes_t();
|
||||
xmat.columnCount = static_cast<int>(x.cols());
|
||||
xmat.rowCount = static_cast<int>(x.rows());
|
||||
xmat.columnStride = xmat.rowCount;
|
||||
xmat.data = x_ptr;
|
||||
|
||||
AccelDenseMatrix bmat{};
|
||||
bmat.attributes = SparseAttributes_t();
|
||||
bmat.columnCount = static_cast<int>(b.cols());
|
||||
bmat.rowCount = static_cast<int>(b.rows());
|
||||
bmat.columnStride = bmat.rowCount;
|
||||
bmat.data = b_ptr;
|
||||
|
||||
SparseSolve(*m_numericFactorization, bmat, xmat);
|
||||
|
||||
updateInfoStatus(status);
|
||||
}
|
||||
|
||||
} // end namespace Eigen
|
||||
|
||||
#endif // EIGEN_ACCELERATESUPPORT_H
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
#ifndef EIGEN_ACCELERATESUPPORT_MODULE_H
|
||||
#error "Please include Eigen/AccelerateSupport instead of including headers inside the src directory directly."
|
||||
#endif
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
#ifndef EIGEN_CHOLESKY_MODULE_H
|
||||
#error "Please include Eigen/Cholesky instead of including headers inside the src directory directly."
|
||||
#endif
|
||||
@@ -0,0 +1,661 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2008-2011 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
// Copyright (C) 2009 Keir Mierle <mierle@gmail.com>
|
||||
// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com>
|
||||
// Copyright (C) 2011 Timothy E. Holy <tim.holy@gmail.com >
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_LDLT_H
|
||||
#define EIGEN_LDLT_H
|
||||
|
||||
// IWYU pragma: private
|
||||
#include "./InternalHeaderCheck.h"
|
||||
|
||||
namespace Eigen {
|
||||
|
||||
namespace internal {
|
||||
template <typename MatrixType_, int UpLo_>
|
||||
struct traits<LDLT<MatrixType_, UpLo_> > : traits<MatrixType_> {
|
||||
typedef MatrixXpr XprKind;
|
||||
typedef SolverStorage StorageKind;
|
||||
typedef int StorageIndex;
|
||||
enum { Flags = 0 };
|
||||
};
|
||||
|
||||
template <typename MatrixType, int UpLo>
|
||||
struct LDLT_Traits;
|
||||
|
||||
// PositiveSemiDef means positive semi-definite and non-zero; same for NegativeSemiDef
|
||||
enum SignMatrix { PositiveSemiDef, NegativeSemiDef, ZeroSign, Indefinite };
|
||||
} // namespace internal
|
||||
|
||||
/** \ingroup Cholesky_Module
|
||||
*
|
||||
* \class LDLT
|
||||
*
|
||||
* \brief Robust Cholesky decomposition of a matrix with pivoting
|
||||
*
|
||||
* \tparam MatrixType_ the type of the matrix of which to compute the LDL^T Cholesky decomposition
|
||||
* \tparam UpLo_ the triangular part that will be used for the decomposition: Lower (default) or Upper.
|
||||
* The other triangular part won't be read.
|
||||
*
|
||||
* Perform a robust Cholesky decomposition of a positive semidefinite or negative semidefinite
|
||||
* matrix \f$ A \f$ such that \f$ A = P^TLDL^*P \f$, where P is a permutation matrix, L
|
||||
* is lower triangular with a unit diagonal and D is a diagonal matrix.
|
||||
*
|
||||
* The decomposition uses pivoting to ensure stability, so that D will have
|
||||
* zeros in the bottom right rank(A) - n submatrix. Avoiding the square root
|
||||
* on D also stabilizes the computation.
|
||||
*
|
||||
* Remember that Cholesky decompositions are not rank-revealing. Also, do not use a Cholesky
|
||||
* decomposition to determine whether a system of equations has a solution.
|
||||
*
|
||||
* This class supports the \link InplaceDecomposition inplace decomposition \endlink mechanism.
|
||||
*
|
||||
* \sa MatrixBase::ldlt(), SelfAdjointView::ldlt(), class LLT
|
||||
*/
|
||||
template <typename MatrixType_, int UpLo_>
|
||||
class LDLT : public SolverBase<LDLT<MatrixType_, UpLo_> > {
|
||||
public:
|
||||
typedef MatrixType_ MatrixType;
|
||||
typedef SolverBase<LDLT> Base;
|
||||
friend class SolverBase<LDLT>;
|
||||
|
||||
EIGEN_GENERIC_PUBLIC_INTERFACE(LDLT)
|
||||
enum {
|
||||
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
|
||||
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime,
|
||||
UpLo = UpLo_
|
||||
};
|
||||
typedef Matrix<Scalar, RowsAtCompileTime, 1, 0, MaxRowsAtCompileTime, 1> TmpMatrixType;
|
||||
|
||||
typedef Transpositions<RowsAtCompileTime, MaxRowsAtCompileTime> TranspositionType;
|
||||
typedef PermutationMatrix<RowsAtCompileTime, MaxRowsAtCompileTime> PermutationType;
|
||||
|
||||
typedef internal::LDLT_Traits<MatrixType, UpLo> Traits;
|
||||
|
||||
/** \brief Default Constructor.
|
||||
*
|
||||
* The default constructor is useful in cases in which the user intends to
|
||||
* perform decompositions via LDLT::compute(const MatrixType&).
|
||||
*/
|
||||
LDLT()
|
||||
: m_matrix(),
|
||||
m_l1_norm(0),
|
||||
m_transpositions(),
|
||||
m_sign(internal::ZeroSign),
|
||||
m_isInitialized(false),
|
||||
m_info(InvalidInput) {}
|
||||
|
||||
/** \brief Default Constructor with memory preallocation
|
||||
*
|
||||
* Like the default constructor but with preallocation of the internal data
|
||||
* according to the specified problem \a size.
|
||||
* \sa LDLT()
|
||||
*/
|
||||
explicit LDLT(Index size)
|
||||
: m_matrix(size, size),
|
||||
m_l1_norm(0),
|
||||
m_transpositions(size),
|
||||
m_temporary(size),
|
||||
m_sign(internal::ZeroSign),
|
||||
m_isInitialized(false),
|
||||
m_info(InvalidInput) {}
|
||||
|
||||
/** \brief Constructor with decomposition
|
||||
*
|
||||
* This calculates the decomposition for the input \a matrix.
|
||||
*
|
||||
* \sa LDLT(Index size)
|
||||
*/
|
||||
template <typename InputType>
|
||||
explicit LDLT(const EigenBase<InputType>& matrix)
|
||||
: m_matrix(matrix.rows(), matrix.cols()),
|
||||
m_l1_norm(0),
|
||||
m_transpositions(matrix.rows()),
|
||||
m_temporary(matrix.rows()),
|
||||
m_sign(internal::ZeroSign),
|
||||
m_isInitialized(false),
|
||||
m_info(InvalidInput) {
|
||||
compute(matrix.derived());
|
||||
}
|
||||
|
||||
/** \brief Constructs a LDLT factorization from a given matrix
|
||||
*
|
||||
* This overloaded constructor is provided for \link InplaceDecomposition inplace decomposition \endlink when \c
|
||||
* MatrixType is a Eigen::Ref.
|
||||
*
|
||||
* \sa LDLT(const EigenBase&)
|
||||
*/
|
||||
template <typename InputType>
|
||||
explicit LDLT(EigenBase<InputType>& matrix)
|
||||
: m_matrix(matrix.derived()),
|
||||
m_l1_norm(0),
|
||||
m_transpositions(matrix.rows()),
|
||||
m_temporary(matrix.rows()),
|
||||
m_sign(internal::ZeroSign),
|
||||
m_isInitialized(false),
|
||||
m_info(InvalidInput) {
|
||||
compute(matrix.derived());
|
||||
}
|
||||
|
||||
/** Clear any existing decomposition
|
||||
* \sa rankUpdate(w,sigma)
|
||||
*/
|
||||
void setZero() { m_isInitialized = false; }
|
||||
|
||||
/** \returns a view of the upper triangular matrix U */
|
||||
inline typename Traits::MatrixU matrixU() const {
|
||||
eigen_assert(m_isInitialized && "LDLT is not initialized.");
|
||||
return Traits::getU(m_matrix);
|
||||
}
|
||||
|
||||
/** \returns a view of the lower triangular matrix L */
|
||||
inline typename Traits::MatrixL matrixL() const {
|
||||
eigen_assert(m_isInitialized && "LDLT is not initialized.");
|
||||
return Traits::getL(m_matrix);
|
||||
}
|
||||
|
||||
/** \returns the permutation matrix P as a transposition sequence.
|
||||
*/
|
||||
inline const TranspositionType& transpositionsP() const {
|
||||
eigen_assert(m_isInitialized && "LDLT is not initialized.");
|
||||
return m_transpositions;
|
||||
}
|
||||
|
||||
/** \returns the coefficients of the diagonal matrix D */
|
||||
inline Diagonal<const MatrixType> vectorD() const {
|
||||
eigen_assert(m_isInitialized && "LDLT is not initialized.");
|
||||
return m_matrix.diagonal();
|
||||
}
|
||||
|
||||
/** \returns true if the matrix is positive (semidefinite) */
|
||||
inline bool isPositive() const {
|
||||
eigen_assert(m_isInitialized && "LDLT is not initialized.");
|
||||
return m_sign == internal::PositiveSemiDef || m_sign == internal::ZeroSign;
|
||||
}
|
||||
|
||||
/** \returns true if the matrix is negative (semidefinite) */
|
||||
inline bool isNegative(void) const {
|
||||
eigen_assert(m_isInitialized && "LDLT is not initialized.");
|
||||
return m_sign == internal::NegativeSemiDef || m_sign == internal::ZeroSign;
|
||||
}
|
||||
|
||||
#ifdef EIGEN_PARSED_BY_DOXYGEN
|
||||
/** \returns a solution x of \f$ A x = b \f$ using the current decomposition of A.
|
||||
*
|
||||
* This function also supports in-place solves using the syntax <tt>x = decompositionObject.solve(x)</tt> .
|
||||
*
|
||||
* \note_about_checking_solutions
|
||||
*
|
||||
* More precisely, this method solves \f$ A x = b \f$ using the decomposition \f$ A = P^T L D L^* P \f$
|
||||
* by solving the systems \f$ P^T y_1 = b \f$, \f$ L y_2 = y_1 \f$, \f$ D y_3 = y_2 \f$,
|
||||
* \f$ L^* y_4 = y_3 \f$ and \f$ P x = y_4 \f$ in succession. If the matrix \f$ A \f$ is singular, then
|
||||
* \f$ D \f$ will also be singular (all the other matrices are invertible). In that case, the
|
||||
* least-square solution of \f$ D y_3 = y_2 \f$ is computed. This does not mean that this function
|
||||
* computes the least-square solution of \f$ A x = b \f$ if \f$ A \f$ is singular.
|
||||
*
|
||||
* \sa MatrixBase::ldlt(), SelfAdjointView::ldlt()
|
||||
*/
|
||||
template <typename Rhs>
|
||||
inline Solve<LDLT, Rhs> solve(const MatrixBase<Rhs>& b) const;
|
||||
#endif
|
||||
|
||||
template <typename Derived>
|
||||
bool solveInPlace(MatrixBase<Derived>& bAndX) const;
|
||||
|
||||
template <typename InputType>
|
||||
LDLT& compute(const EigenBase<InputType>& matrix);
|
||||
|
||||
/** \returns an estimate of the reciprocal condition number of the matrix of
|
||||
* which \c *this is the LDLT decomposition.
|
||||
*/
|
||||
RealScalar rcond() const {
|
||||
eigen_assert(m_isInitialized && "LDLT is not initialized.");
|
||||
return internal::rcond_estimate_helper(m_l1_norm, *this);
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
LDLT& rankUpdate(const MatrixBase<Derived>& w, const RealScalar& alpha = 1);
|
||||
|
||||
/** \returns the internal LDLT decomposition matrix
|
||||
*
|
||||
* TODO: document the storage layout.
|
||||
*/
|
||||
inline const MatrixType& matrixLDLT() const {
|
||||
eigen_assert(m_isInitialized && "LDLT is not initialized.");
|
||||
return m_matrix;
|
||||
}
|
||||
|
||||
MatrixType reconstructedMatrix() const;
|
||||
|
||||
/** \returns the adjoint of \c *this, that is, a const reference to the decomposition itself as the underlying matrix
|
||||
* is self-adjoint.
|
||||
*
|
||||
* This method is provided for compatibility with other matrix decompositions, thus enabling generic code such as:
|
||||
* \code x = decomposition.adjoint().solve(b) \endcode
|
||||
*/
|
||||
const LDLT& adjoint() const { return *this; }
|
||||
|
||||
EIGEN_DEVICE_FUNC constexpr Index rows() const noexcept { return m_matrix.rows(); }
|
||||
EIGEN_DEVICE_FUNC constexpr Index cols() const noexcept { return m_matrix.cols(); }
|
||||
|
||||
/** \brief Reports whether previous computation was successful.
|
||||
*
|
||||
* \returns \c Success if computation was successful,
|
||||
* \c NumericalIssue if the factorization failed because of a zero pivot.
|
||||
*/
|
||||
ComputationInfo info() const {
|
||||
eigen_assert(m_isInitialized && "LDLT is not initialized.");
|
||||
return m_info;
|
||||
}
|
||||
|
||||
#ifndef EIGEN_PARSED_BY_DOXYGEN
|
||||
template <typename RhsType, typename DstType>
|
||||
void _solve_impl(const RhsType& rhs, DstType& dst) const;
|
||||
|
||||
template <bool Conjugate, typename RhsType, typename DstType>
|
||||
void _solve_impl_transposed(const RhsType& rhs, DstType& dst) const;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
EIGEN_STATIC_ASSERT_NON_INTEGER(Scalar)
|
||||
|
||||
/** \internal
|
||||
* Used to compute and store the Cholesky decomposition A = L D L^* = U^* D U.
|
||||
* The strict upper part is used during the decomposition, the strict lower
|
||||
* part correspond to the coefficients of L (its diagonal is equal to 1 and
|
||||
* is not stored), and the diagonal entries correspond to D.
|
||||
*/
|
||||
MatrixType m_matrix;
|
||||
RealScalar m_l1_norm;
|
||||
TranspositionType m_transpositions;
|
||||
TmpMatrixType m_temporary;
|
||||
internal::SignMatrix m_sign;
|
||||
bool m_isInitialized;
|
||||
ComputationInfo m_info;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <int UpLo>
|
||||
struct ldlt_inplace;
|
||||
|
||||
template <>
|
||||
struct ldlt_inplace<Lower> {
|
||||
template <typename MatrixType, typename TranspositionType, typename Workspace>
|
||||
static bool unblocked(MatrixType& mat, TranspositionType& transpositions, Workspace& temp, SignMatrix& sign) {
|
||||
using std::abs;
|
||||
typedef typename MatrixType::Scalar Scalar;
|
||||
typedef typename MatrixType::RealScalar RealScalar;
|
||||
typedef typename TranspositionType::StorageIndex IndexType;
|
||||
eigen_assert(mat.rows() == mat.cols());
|
||||
const Index size = mat.rows();
|
||||
bool found_zero_pivot = false;
|
||||
bool ret = true;
|
||||
|
||||
if (size <= 1) {
|
||||
transpositions.setIdentity();
|
||||
if (size == 0)
|
||||
sign = ZeroSign;
|
||||
else if (numext::real(mat.coeff(0, 0)) > static_cast<RealScalar>(0))
|
||||
sign = PositiveSemiDef;
|
||||
else if (numext::real(mat.coeff(0, 0)) < static_cast<RealScalar>(0))
|
||||
sign = NegativeSemiDef;
|
||||
else
|
||||
sign = ZeroSign;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (Index k = 0; k < size; ++k) {
|
||||
// Find largest diagonal element
|
||||
Index index_of_biggest_in_corner;
|
||||
mat.diagonal().tail(size - k).cwiseAbs().maxCoeff(&index_of_biggest_in_corner);
|
||||
index_of_biggest_in_corner += k;
|
||||
|
||||
transpositions.coeffRef(k) = IndexType(index_of_biggest_in_corner);
|
||||
if (k != index_of_biggest_in_corner) {
|
||||
// apply the transposition while taking care to consider only
|
||||
// the lower triangular part
|
||||
Index s = size - index_of_biggest_in_corner - 1; // trailing size after the biggest element
|
||||
mat.row(k).head(k).swap(mat.row(index_of_biggest_in_corner).head(k));
|
||||
mat.col(k).tail(s).swap(mat.col(index_of_biggest_in_corner).tail(s));
|
||||
std::swap(mat.coeffRef(k, k), mat.coeffRef(index_of_biggest_in_corner, index_of_biggest_in_corner));
|
||||
for (Index i = k + 1; i < index_of_biggest_in_corner; ++i) {
|
||||
Scalar tmp = mat.coeffRef(i, k);
|
||||
mat.coeffRef(i, k) = numext::conj(mat.coeffRef(index_of_biggest_in_corner, i));
|
||||
mat.coeffRef(index_of_biggest_in_corner, i) = numext::conj(tmp);
|
||||
}
|
||||
if (NumTraits<Scalar>::IsComplex)
|
||||
mat.coeffRef(index_of_biggest_in_corner, k) = numext::conj(mat.coeff(index_of_biggest_in_corner, k));
|
||||
}
|
||||
|
||||
// partition the matrix:
|
||||
// A00 | - | -
|
||||
// lu = A10 | A11 | -
|
||||
// A20 | A21 | A22
|
||||
Index rs = size - k - 1;
|
||||
Block<MatrixType, Dynamic, 1> A21(mat, k + 1, k, rs, 1);
|
||||
Block<MatrixType, 1, Dynamic> A10(mat, k, 0, 1, k);
|
||||
Block<MatrixType, Dynamic, Dynamic> A20(mat, k + 1, 0, rs, k);
|
||||
|
||||
if (k > 0) {
|
||||
temp.head(k) = mat.diagonal().real().head(k).asDiagonal() * A10.adjoint();
|
||||
mat.coeffRef(k, k) -= (A10 * temp.head(k)).value();
|
||||
if (rs > 0) A21.noalias() -= A20 * temp.head(k);
|
||||
}
|
||||
|
||||
// In some previous versions of Eigen (e.g., 3.2.1), the scaling was omitted if the pivot
|
||||
// was smaller than the cutoff value. However, since LDLT is not rank-revealing
|
||||
// we should only make sure that we do not introduce INF or NaN values.
|
||||
// Remark that LAPACK also uses 0 as the cutoff value.
|
||||
RealScalar realAkk = numext::real(mat.coeffRef(k, k));
|
||||
bool pivot_is_valid = (abs(realAkk) > RealScalar(0));
|
||||
|
||||
if (k == 0 && !pivot_is_valid) {
|
||||
// The entire diagonal is zero, there is nothing more to do
|
||||
// except filling the transpositions, and checking whether the matrix is zero.
|
||||
sign = ZeroSign;
|
||||
for (Index j = 0; j < size; ++j) {
|
||||
transpositions.coeffRef(j) = IndexType(j);
|
||||
ret = ret && (mat.col(j).tail(size - j - 1).array() == Scalar(0)).all();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((rs > 0) && pivot_is_valid)
|
||||
A21 /= realAkk;
|
||||
else if (rs > 0)
|
||||
ret = ret && (A21.array() == Scalar(0)).all();
|
||||
|
||||
if (found_zero_pivot && pivot_is_valid)
|
||||
ret = false; // factorization failed
|
||||
else if (!pivot_is_valid)
|
||||
found_zero_pivot = true;
|
||||
|
||||
if (sign == PositiveSemiDef) {
|
||||
if (realAkk < static_cast<RealScalar>(0)) sign = Indefinite;
|
||||
} else if (sign == NegativeSemiDef) {
|
||||
if (realAkk > static_cast<RealScalar>(0)) sign = Indefinite;
|
||||
} else if (sign == ZeroSign) {
|
||||
if (realAkk > static_cast<RealScalar>(0))
|
||||
sign = PositiveSemiDef;
|
||||
else if (realAkk < static_cast<RealScalar>(0))
|
||||
sign = NegativeSemiDef;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Reference for the algorithm: Davis and Hager, "Multiple Rank
|
||||
// Modifications of a Sparse Cholesky Factorization" (Algorithm 1)
|
||||
// Trivial rearrangements of their computations (Timothy E. Holy)
|
||||
// allow their algorithm to work for rank-1 updates even if the
|
||||
// original matrix is not of full rank.
|
||||
// Here only rank-1 updates are implemented, to reduce the
|
||||
// requirement for intermediate storage and improve accuracy
|
||||
template <typename MatrixType, typename WDerived>
|
||||
static bool updateInPlace(MatrixType& mat, MatrixBase<WDerived>& w,
|
||||
const typename MatrixType::RealScalar& sigma = 1) {
|
||||
using numext::isfinite;
|
||||
typedef typename MatrixType::Scalar Scalar;
|
||||
typedef typename MatrixType::RealScalar RealScalar;
|
||||
|
||||
const Index size = mat.rows();
|
||||
eigen_assert(mat.cols() == size && w.size() == size);
|
||||
|
||||
RealScalar alpha = 1;
|
||||
|
||||
// Apply the update
|
||||
for (Index j = 0; j < size; j++) {
|
||||
// Check for termination due to an original decomposition of low-rank
|
||||
if (!(isfinite)(alpha)) break;
|
||||
|
||||
// Update the diagonal terms
|
||||
RealScalar dj = numext::real(mat.coeff(j, j));
|
||||
Scalar wj = w.coeff(j);
|
||||
RealScalar swj2 = sigma * numext::abs2(wj);
|
||||
RealScalar gamma = dj * alpha + swj2;
|
||||
|
||||
mat.coeffRef(j, j) += swj2 / alpha;
|
||||
alpha += swj2 / dj;
|
||||
|
||||
// Update the terms of L
|
||||
Index rs = size - j - 1;
|
||||
w.tail(rs) -= wj * mat.col(j).tail(rs);
|
||||
if (!numext::is_exactly_zero(gamma)) mat.col(j).tail(rs) += (sigma * numext::conj(wj) / gamma) * w.tail(rs);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename MatrixType, typename TranspositionType, typename Workspace, typename WType>
|
||||
static bool update(MatrixType& mat, const TranspositionType& transpositions, Workspace& tmp, const WType& w,
|
||||
const typename MatrixType::RealScalar& sigma = 1) {
|
||||
// Apply the permutation to the input w
|
||||
tmp = transpositions * w;
|
||||
|
||||
return ldlt_inplace<Lower>::updateInPlace(mat, tmp, sigma);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ldlt_inplace<Upper> {
|
||||
template <typename MatrixType, typename TranspositionType, typename Workspace>
|
||||
static EIGEN_STRONG_INLINE bool unblocked(MatrixType& mat, TranspositionType& transpositions, Workspace& temp,
|
||||
SignMatrix& sign) {
|
||||
Transpose<MatrixType> matt(mat);
|
||||
return ldlt_inplace<Lower>::unblocked(matt, transpositions, temp, sign);
|
||||
}
|
||||
|
||||
template <typename MatrixType, typename TranspositionType, typename Workspace, typename WType>
|
||||
static EIGEN_STRONG_INLINE bool update(MatrixType& mat, TranspositionType& transpositions, Workspace& tmp, WType& w,
|
||||
const typename MatrixType::RealScalar& sigma = 1) {
|
||||
Transpose<MatrixType> matt(mat);
|
||||
return ldlt_inplace<Lower>::update(matt, transpositions, tmp, w.conjugate(), sigma);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename MatrixType>
|
||||
struct LDLT_Traits<MatrixType, Lower> {
|
||||
typedef const TriangularView<const MatrixType, UnitLower> MatrixL;
|
||||
typedef const TriangularView<const typename MatrixType::AdjointReturnType, UnitUpper> MatrixU;
|
||||
static inline MatrixL getL(const MatrixType& m) { return MatrixL(m); }
|
||||
static inline MatrixU getU(const MatrixType& m) { return MatrixU(m.adjoint()); }
|
||||
};
|
||||
|
||||
template <typename MatrixType>
|
||||
struct LDLT_Traits<MatrixType, Upper> {
|
||||
typedef const TriangularView<const typename MatrixType::AdjointReturnType, UnitLower> MatrixL;
|
||||
typedef const TriangularView<const MatrixType, UnitUpper> MatrixU;
|
||||
static inline MatrixL getL(const MatrixType& m) { return MatrixL(m.adjoint()); }
|
||||
static inline MatrixU getU(const MatrixType& m) { return MatrixU(m); }
|
||||
};
|
||||
|
||||
} // end namespace internal
|
||||
|
||||
/** Compute / recompute the LDLT decomposition A = L D L^* = U^* D U of \a matrix
|
||||
*/
|
||||
template <typename MatrixType, int UpLo_>
|
||||
template <typename InputType>
|
||||
LDLT<MatrixType, UpLo_>& LDLT<MatrixType, UpLo_>::compute(const EigenBase<InputType>& a) {
|
||||
eigen_assert(a.rows() == a.cols());
|
||||
const Index size = a.rows();
|
||||
|
||||
m_matrix = a.derived();
|
||||
|
||||
// Compute matrix L1 norm = max abs column sum.
|
||||
m_l1_norm = RealScalar(0);
|
||||
// TODO: move this code to SelfAdjointView
|
||||
for (Index col = 0; col < size; ++col) {
|
||||
RealScalar abs_col_sum;
|
||||
if (UpLo_ == Lower)
|
||||
abs_col_sum =
|
||||
m_matrix.col(col).tail(size - col).template lpNorm<1>() + m_matrix.row(col).head(col).template lpNorm<1>();
|
||||
else
|
||||
abs_col_sum =
|
||||
m_matrix.col(col).head(col).template lpNorm<1>() + m_matrix.row(col).tail(size - col).template lpNorm<1>();
|
||||
if (abs_col_sum > m_l1_norm) m_l1_norm = abs_col_sum;
|
||||
}
|
||||
|
||||
m_transpositions.resize(size);
|
||||
m_isInitialized = false;
|
||||
m_temporary.resize(size);
|
||||
m_sign = internal::ZeroSign;
|
||||
|
||||
m_info = internal::ldlt_inplace<UpLo>::unblocked(m_matrix, m_transpositions, m_temporary, m_sign) ? Success
|
||||
: NumericalIssue;
|
||||
|
||||
m_isInitialized = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Update the LDLT decomposition: given A = L D L^T, efficiently compute the decomposition of A + sigma w w^T.
|
||||
* \param w a vector to be incorporated into the decomposition.
|
||||
* \param sigma a scalar, +1 for updates and -1 for "downdates," which correspond to removing previously-added column
|
||||
* vectors. Optional; default value is +1. \sa setZero()
|
||||
*/
|
||||
template <typename MatrixType, int UpLo_>
|
||||
template <typename Derived>
|
||||
LDLT<MatrixType, UpLo_>& LDLT<MatrixType, UpLo_>::rankUpdate(
|
||||
const MatrixBase<Derived>& w, const typename LDLT<MatrixType, UpLo_>::RealScalar& sigma) {
|
||||
typedef typename TranspositionType::StorageIndex IndexType;
|
||||
const Index size = w.rows();
|
||||
if (m_isInitialized) {
|
||||
eigen_assert(m_matrix.rows() == size);
|
||||
} else {
|
||||
m_matrix.resize(size, size);
|
||||
m_matrix.setZero();
|
||||
m_transpositions.resize(size);
|
||||
for (Index i = 0; i < size; i++) m_transpositions.coeffRef(i) = IndexType(i);
|
||||
m_temporary.resize(size);
|
||||
m_sign = sigma >= 0 ? internal::PositiveSemiDef : internal::NegativeSemiDef;
|
||||
m_isInitialized = true;
|
||||
}
|
||||
|
||||
internal::ldlt_inplace<UpLo>::update(m_matrix, m_transpositions, m_temporary, w, sigma);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifndef EIGEN_PARSED_BY_DOXYGEN
|
||||
template <typename MatrixType_, int UpLo_>
|
||||
template <typename RhsType, typename DstType>
|
||||
void LDLT<MatrixType_, UpLo_>::_solve_impl(const RhsType& rhs, DstType& dst) const {
|
||||
_solve_impl_transposed<true>(rhs, dst);
|
||||
}
|
||||
|
||||
template <typename MatrixType_, int UpLo_>
|
||||
template <bool Conjugate, typename RhsType, typename DstType>
|
||||
void LDLT<MatrixType_, UpLo_>::_solve_impl_transposed(const RhsType& rhs, DstType& dst) const {
|
||||
// dst = P b
|
||||
dst = m_transpositions * rhs;
|
||||
|
||||
// dst = L^-1 (P b)
|
||||
// dst = L^-*T (P b)
|
||||
matrixL().template conjugateIf<!Conjugate>().solveInPlace(dst);
|
||||
|
||||
// dst = D^-* (L^-1 P b)
|
||||
// dst = D^-1 (L^-*T P b)
|
||||
// more precisely, use pseudo-inverse of D (see bug 241)
|
||||
using std::abs;
|
||||
const typename Diagonal<const MatrixType>::RealReturnType vecD(vectorD());
|
||||
// In some previous versions, tolerance was set to the max of 1/highest (or rather numeric_limits::min())
|
||||
// and the maximal diagonal entry * epsilon as motivated by LAPACK's xGELSS:
|
||||
// RealScalar tolerance = numext::maxi(vecD.array().abs().maxCoeff() * NumTraits<RealScalar>::epsilon(),RealScalar(1)
|
||||
// / NumTraits<RealScalar>::highest()); However, LDLT is not rank revealing, and so adjusting the tolerance wrt to the
|
||||
// highest diagonal element is not well justified and leads to numerical issues in some cases. Moreover, Lapack's
|
||||
// xSYTRS routines use 0 for the tolerance. Using numeric_limits::min() gives us more robustness to denormals.
|
||||
RealScalar tolerance = (std::numeric_limits<RealScalar>::min)();
|
||||
for (Index i = 0; i < vecD.size(); ++i) {
|
||||
if (abs(vecD(i)) > tolerance)
|
||||
dst.row(i) /= vecD(i);
|
||||
else
|
||||
dst.row(i).setZero();
|
||||
}
|
||||
|
||||
// dst = L^-* (D^-* L^-1 P b)
|
||||
// dst = L^-T (D^-1 L^-*T P b)
|
||||
matrixL().transpose().template conjugateIf<Conjugate>().solveInPlace(dst);
|
||||
|
||||
// dst = P^T (L^-* D^-* L^-1 P b) = A^-1 b
|
||||
// dst = P^-T (L^-T D^-1 L^-*T P b) = A^-1 b
|
||||
dst = m_transpositions.transpose() * dst;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \internal use x = ldlt_object.solve(x);
|
||||
*
|
||||
* This is the \em in-place version of solve().
|
||||
*
|
||||
* \param bAndX represents both the right-hand side matrix b and result x.
|
||||
*
|
||||
* \returns true always! If you need to check for existence of solutions, use another decomposition like LU, QR, or SVD.
|
||||
*
|
||||
* This version avoids a copy when the right hand side matrix b is not
|
||||
* needed anymore.
|
||||
*
|
||||
* \sa LDLT::solve(), MatrixBase::ldlt()
|
||||
*/
|
||||
template <typename MatrixType, int UpLo_>
|
||||
template <typename Derived>
|
||||
bool LDLT<MatrixType, UpLo_>::solveInPlace(MatrixBase<Derived>& bAndX) const {
|
||||
eigen_assert(m_isInitialized && "LDLT is not initialized.");
|
||||
eigen_assert(m_matrix.rows() == bAndX.rows());
|
||||
|
||||
bAndX = this->solve(bAndX);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \returns the matrix represented by the decomposition,
|
||||
* i.e., it returns the product: P^T L D L^* P.
|
||||
* This function is provided for debug purpose. */
|
||||
template <typename MatrixType, int UpLo_>
|
||||
MatrixType LDLT<MatrixType, UpLo_>::reconstructedMatrix() const {
|
||||
eigen_assert(m_isInitialized && "LDLT is not initialized.");
|
||||
const Index size = m_matrix.rows();
|
||||
MatrixType res(size, size);
|
||||
|
||||
// P
|
||||
res.setIdentity();
|
||||
res = transpositionsP() * res;
|
||||
// L^* P
|
||||
res = matrixU() * res;
|
||||
// D(L^*P)
|
||||
res = vectorD().real().asDiagonal() * res;
|
||||
// L(DL^*P)
|
||||
res = matrixL() * res;
|
||||
// P^T (LDL^*P)
|
||||
res = transpositionsP().transpose() * res;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/** \cholesky_module
|
||||
* \returns the Cholesky decomposition with full pivoting without square root of \c *this
|
||||
* \sa MatrixBase::ldlt()
|
||||
*/
|
||||
template <typename MatrixType, unsigned int UpLo>
|
||||
inline LDLT<typename SelfAdjointView<MatrixType, UpLo>::PlainObject, UpLo> SelfAdjointView<MatrixType, UpLo>::ldlt()
|
||||
const {
|
||||
return LDLT<PlainObject, UpLo>(m_matrix);
|
||||
}
|
||||
|
||||
/** \cholesky_module
|
||||
* \returns the Cholesky decomposition with full pivoting without square root of \c *this
|
||||
* \sa SelfAdjointView::ldlt()
|
||||
*/
|
||||
template <typename Derived>
|
||||
inline LDLT<typename MatrixBase<Derived>::PlainObject> MatrixBase<Derived>::ldlt() const {
|
||||
return LDLT<PlainObject>(derived());
|
||||
}
|
||||
|
||||
} // end namespace Eigen
|
||||
|
||||
#endif // EIGEN_LDLT_H
|
||||
@@ -0,0 +1,516 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_LLT_H
|
||||
#define EIGEN_LLT_H
|
||||
|
||||
// IWYU pragma: private
|
||||
#include "./InternalHeaderCheck.h"
|
||||
|
||||
namespace Eigen {
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename MatrixType_, int UpLo_>
|
||||
struct traits<LLT<MatrixType_, UpLo_> > : traits<MatrixType_> {
|
||||
typedef MatrixXpr XprKind;
|
||||
typedef SolverStorage StorageKind;
|
||||
typedef int StorageIndex;
|
||||
enum { Flags = 0 };
|
||||
};
|
||||
|
||||
template <typename MatrixType, int UpLo>
|
||||
struct LLT_Traits;
|
||||
} // namespace internal
|
||||
|
||||
/** \ingroup Cholesky_Module
|
||||
*
|
||||
* \class LLT
|
||||
*
|
||||
* \brief Standard Cholesky decomposition (LL^T) of a matrix and associated features
|
||||
*
|
||||
* \tparam MatrixType_ the type of the matrix of which we are computing the LL^T Cholesky decomposition
|
||||
* \tparam UpLo_ the triangular part that will be used for the decomposition: Lower (default) or Upper.
|
||||
* The other triangular part won't be read.
|
||||
*
|
||||
* This class performs a LL^T Cholesky decomposition of a symmetric, positive definite
|
||||
* matrix A such that A = LL^* = U^*U, where L is lower triangular.
|
||||
*
|
||||
* While the Cholesky decomposition is particularly useful to solve selfadjoint problems like D^*D x = b,
|
||||
* for that purpose, we recommend the Cholesky decomposition without square root which is more stable
|
||||
* and even faster. Nevertheless, this standard Cholesky decomposition remains useful in many other
|
||||
* situations like generalised eigen problems with hermitian matrices.
|
||||
*
|
||||
* Remember that Cholesky decompositions are not rank-revealing. This LLT decomposition is only stable on positive
|
||||
* definite matrices, use LDLT instead for the semidefinite case. Also, do not use a Cholesky decomposition to determine
|
||||
* whether a system of equations has a solution.
|
||||
*
|
||||
* Example: \include LLT_example.cpp
|
||||
* Output: \verbinclude LLT_example.out
|
||||
*
|
||||
* \b Performance: for best performance, it is recommended to use a column-major storage format
|
||||
* with the Lower triangular part (the default), or, equivalently, a row-major storage format
|
||||
* with the Upper triangular part. Otherwise, you might get a 20% slowdown for the full factorization
|
||||
* step, and rank-updates can be up to 3 times slower.
|
||||
*
|
||||
* This class supports the \link InplaceDecomposition inplace decomposition \endlink mechanism.
|
||||
*
|
||||
* Note that during the decomposition, only the lower (or upper, as defined by UpLo_) triangular part of A is
|
||||
* considered. Therefore, the strict lower part does not have to store correct values.
|
||||
*
|
||||
* \sa MatrixBase::llt(), SelfAdjointView::llt(), class LDLT
|
||||
*/
|
||||
template <typename MatrixType_, int UpLo_>
|
||||
class LLT : public SolverBase<LLT<MatrixType_, UpLo_> > {
|
||||
public:
|
||||
typedef MatrixType_ MatrixType;
|
||||
typedef SolverBase<LLT> Base;
|
||||
friend class SolverBase<LLT>;
|
||||
|
||||
EIGEN_GENERIC_PUBLIC_INTERFACE(LLT)
|
||||
enum { MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime };
|
||||
|
||||
enum { PacketSize = internal::packet_traits<Scalar>::size, AlignmentMask = int(PacketSize) - 1, UpLo = UpLo_ };
|
||||
|
||||
typedef internal::LLT_Traits<MatrixType, UpLo> Traits;
|
||||
|
||||
/**
|
||||
* \brief Default Constructor.
|
||||
*
|
||||
* The default constructor is useful in cases in which the user intends to
|
||||
* perform decompositions via LLT::compute(const MatrixType&).
|
||||
*/
|
||||
LLT() : m_matrix(), m_l1_norm(0), m_isInitialized(false), m_info(InvalidInput) {}
|
||||
|
||||
/** \brief Default Constructor with memory preallocation
|
||||
*
|
||||
* Like the default constructor but with preallocation of the internal data
|
||||
* according to the specified problem \a size.
|
||||
* \sa LLT()
|
||||
*/
|
||||
explicit LLT(Index size) : m_matrix(size, size), m_l1_norm(0), m_isInitialized(false), m_info(InvalidInput) {}
|
||||
|
||||
template <typename InputType>
|
||||
explicit LLT(const EigenBase<InputType>& matrix)
|
||||
: m_matrix(matrix.rows(), matrix.cols()), m_l1_norm(0), m_isInitialized(false), m_info(InvalidInput) {
|
||||
compute(matrix.derived());
|
||||
}
|
||||
|
||||
/** \brief Constructs a LLT factorization from a given matrix
|
||||
*
|
||||
* This overloaded constructor is provided for \link InplaceDecomposition inplace decomposition \endlink when
|
||||
* \c MatrixType is a Eigen::Ref.
|
||||
*
|
||||
* \sa LLT(const EigenBase&)
|
||||
*/
|
||||
template <typename InputType>
|
||||
explicit LLT(EigenBase<InputType>& matrix)
|
||||
: m_matrix(matrix.derived()), m_l1_norm(0), m_isInitialized(false), m_info(InvalidInput) {
|
||||
compute(matrix.derived());
|
||||
}
|
||||
|
||||
/** \returns a view of the upper triangular matrix U */
|
||||
inline typename Traits::MatrixU matrixU() const {
|
||||
eigen_assert(m_isInitialized && "LLT is not initialized.");
|
||||
return Traits::getU(m_matrix);
|
||||
}
|
||||
|
||||
/** \returns a view of the lower triangular matrix L */
|
||||
inline typename Traits::MatrixL matrixL() const {
|
||||
eigen_assert(m_isInitialized && "LLT is not initialized.");
|
||||
return Traits::getL(m_matrix);
|
||||
}
|
||||
|
||||
#ifdef EIGEN_PARSED_BY_DOXYGEN
|
||||
/** \returns the solution x of \f$ A x = b \f$ using the current decomposition of A.
|
||||
*
|
||||
* Since this LLT class assumes anyway that the matrix A is invertible, the solution
|
||||
* theoretically exists and is unique regardless of b.
|
||||
*
|
||||
* Example: \include LLT_solve.cpp
|
||||
* Output: \verbinclude LLT_solve.out
|
||||
*
|
||||
* \sa solveInPlace(), MatrixBase::llt(), SelfAdjointView::llt()
|
||||
*/
|
||||
template <typename Rhs>
|
||||
inline Solve<LLT, Rhs> solve(const MatrixBase<Rhs>& b) const;
|
||||
#endif
|
||||
|
||||
template <typename Derived>
|
||||
void solveInPlace(const MatrixBase<Derived>& bAndX) const;
|
||||
|
||||
template <typename InputType>
|
||||
LLT& compute(const EigenBase<InputType>& matrix);
|
||||
|
||||
/** \returns an estimate of the reciprocal condition number of the matrix of
|
||||
* which \c *this is the Cholesky decomposition.
|
||||
*/
|
||||
RealScalar rcond() const {
|
||||
eigen_assert(m_isInitialized && "LLT is not initialized.");
|
||||
eigen_assert(m_info == Success && "LLT failed because matrix appears to be negative");
|
||||
return internal::rcond_estimate_helper(m_l1_norm, *this);
|
||||
}
|
||||
|
||||
/** \returns the LLT decomposition matrix
|
||||
*
|
||||
* TODO: document the storage layout
|
||||
*/
|
||||
inline const MatrixType& matrixLLT() const {
|
||||
eigen_assert(m_isInitialized && "LLT is not initialized.");
|
||||
return m_matrix;
|
||||
}
|
||||
|
||||
MatrixType reconstructedMatrix() const;
|
||||
|
||||
/** \brief Reports whether previous computation was successful.
|
||||
*
|
||||
* \returns \c Success if computation was successful,
|
||||
* \c NumericalIssue if the matrix.appears not to be positive definite.
|
||||
*/
|
||||
ComputationInfo info() const {
|
||||
eigen_assert(m_isInitialized && "LLT is not initialized.");
|
||||
return m_info;
|
||||
}
|
||||
|
||||
/** \returns the adjoint of \c *this, that is, a const reference to the decomposition itself as the underlying matrix
|
||||
* is self-adjoint.
|
||||
*
|
||||
* This method is provided for compatibility with other matrix decompositions, thus enabling generic code such as:
|
||||
* \code x = decomposition.adjoint().solve(b) \endcode
|
||||
*/
|
||||
const LLT& adjoint() const noexcept { return *this; }
|
||||
|
||||
constexpr Index rows() const noexcept { return m_matrix.rows(); }
|
||||
constexpr Index cols() const noexcept { return m_matrix.cols(); }
|
||||
|
||||
template <typename VectorType>
|
||||
LLT& rankUpdate(const VectorType& vec, const RealScalar& sigma = 1);
|
||||
|
||||
#ifndef EIGEN_PARSED_BY_DOXYGEN
|
||||
template <typename RhsType, typename DstType>
|
||||
void _solve_impl(const RhsType& rhs, DstType& dst) const;
|
||||
|
||||
template <bool Conjugate, typename RhsType, typename DstType>
|
||||
void _solve_impl_transposed(const RhsType& rhs, DstType& dst) const;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
EIGEN_STATIC_ASSERT_NON_INTEGER(Scalar)
|
||||
|
||||
/** \internal
|
||||
* Used to compute and store L
|
||||
* The strict upper part is not used and even not initialized.
|
||||
*/
|
||||
MatrixType m_matrix;
|
||||
RealScalar m_l1_norm;
|
||||
bool m_isInitialized;
|
||||
ComputationInfo m_info;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename Scalar, int UpLo>
|
||||
struct llt_inplace;
|
||||
|
||||
template <typename MatrixType, typename VectorType>
|
||||
static Index llt_rank_update_lower(MatrixType& mat, const VectorType& vec,
|
||||
const typename MatrixType::RealScalar& sigma) {
|
||||
using std::sqrt;
|
||||
typedef typename MatrixType::Scalar Scalar;
|
||||
typedef typename MatrixType::RealScalar RealScalar;
|
||||
typedef typename MatrixType::ColXpr ColXpr;
|
||||
typedef internal::remove_all_t<ColXpr> ColXprCleaned;
|
||||
typedef typename ColXprCleaned::SegmentReturnType ColXprSegment;
|
||||
typedef Matrix<Scalar, Dynamic, 1> TempVectorType;
|
||||
typedef typename TempVectorType::SegmentReturnType TempVecSegment;
|
||||
|
||||
Index n = mat.cols();
|
||||
eigen_assert(mat.rows() == n && vec.size() == n);
|
||||
|
||||
TempVectorType temp;
|
||||
|
||||
if (sigma > 0) {
|
||||
// This version is based on Givens rotations.
|
||||
// It is faster than the other one below, but only works for updates,
|
||||
// i.e., for sigma > 0
|
||||
temp = sqrt(sigma) * vec;
|
||||
|
||||
for (Index i = 0; i < n; ++i) {
|
||||
JacobiRotation<Scalar> g;
|
||||
g.makeGivens(mat(i, i), -temp(i), &mat(i, i));
|
||||
|
||||
Index rs = n - i - 1;
|
||||
if (rs > 0) {
|
||||
ColXprSegment x(mat.col(i).tail(rs));
|
||||
TempVecSegment y(temp.tail(rs));
|
||||
apply_rotation_in_the_plane(x, y, g);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
temp = vec;
|
||||
RealScalar beta = 1;
|
||||
for (Index j = 0; j < n; ++j) {
|
||||
RealScalar Ljj = numext::real(mat.coeff(j, j));
|
||||
RealScalar dj = numext::abs2(Ljj);
|
||||
Scalar wj = temp.coeff(j);
|
||||
RealScalar swj2 = sigma * numext::abs2(wj);
|
||||
RealScalar gamma = dj * beta + swj2;
|
||||
|
||||
RealScalar x = dj + swj2 / beta;
|
||||
if (x <= RealScalar(0)) return j;
|
||||
RealScalar nLjj = sqrt(x);
|
||||
mat.coeffRef(j, j) = nLjj;
|
||||
beta += swj2 / dj;
|
||||
|
||||
// Update the terms of L
|
||||
Index rs = n - j - 1;
|
||||
if (rs) {
|
||||
temp.tail(rs) -= (wj / Ljj) * mat.col(j).tail(rs);
|
||||
if (!numext::is_exactly_zero(gamma))
|
||||
mat.col(j).tail(rs) =
|
||||
(nLjj / Ljj) * mat.col(j).tail(rs) + (nLjj * sigma * numext::conj(wj) / gamma) * temp.tail(rs);
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
struct llt_inplace<Scalar, Lower> {
|
||||
typedef typename NumTraits<Scalar>::Real RealScalar;
|
||||
template <typename MatrixType>
|
||||
static Index unblocked(MatrixType& mat) {
|
||||
using std::sqrt;
|
||||
|
||||
eigen_assert(mat.rows() == mat.cols());
|
||||
const Index size = mat.rows();
|
||||
for (Index k = 0; k < size; ++k) {
|
||||
Index rs = size - k - 1; // remaining size
|
||||
|
||||
Block<MatrixType, Dynamic, 1> A21(mat, k + 1, k, rs, 1);
|
||||
Block<MatrixType, 1, Dynamic> A10(mat, k, 0, 1, k);
|
||||
Block<MatrixType, Dynamic, Dynamic> A20(mat, k + 1, 0, rs, k);
|
||||
|
||||
RealScalar x = numext::real(mat.coeff(k, k));
|
||||
if (k > 0) x -= A10.squaredNorm();
|
||||
if (x <= RealScalar(0)) return k;
|
||||
mat.coeffRef(k, k) = x = sqrt(x);
|
||||
if (k > 0 && rs > 0) A21.noalias() -= A20 * A10.adjoint();
|
||||
if (rs > 0) A21 /= x;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
template <typename MatrixType>
|
||||
static Index blocked(MatrixType& m) {
|
||||
eigen_assert(m.rows() == m.cols());
|
||||
Index size = m.rows();
|
||||
if (size < 32) return unblocked(m);
|
||||
|
||||
Index blockSize = size / 8;
|
||||
blockSize = (blockSize / 16) * 16;
|
||||
blockSize = (std::min)((std::max)(blockSize, Index(8)), Index(128));
|
||||
|
||||
for (Index k = 0; k < size; k += blockSize) {
|
||||
// partition the matrix:
|
||||
// A00 | - | -
|
||||
// lu = A10 | A11 | -
|
||||
// A20 | A21 | A22
|
||||
Index bs = (std::min)(blockSize, size - k);
|
||||
Index rs = size - k - bs;
|
||||
Block<MatrixType, Dynamic, Dynamic> A11(m, k, k, bs, bs);
|
||||
Block<MatrixType, Dynamic, Dynamic> A21(m, k + bs, k, rs, bs);
|
||||
Block<MatrixType, Dynamic, Dynamic> A22(m, k + bs, k + bs, rs, rs);
|
||||
|
||||
Index ret;
|
||||
if ((ret = unblocked(A11)) >= 0) return k + ret;
|
||||
if (rs > 0) A11.adjoint().template triangularView<Upper>().template solveInPlace<OnTheRight>(A21);
|
||||
if (rs > 0)
|
||||
A22.template selfadjointView<Lower>().rankUpdate(A21,
|
||||
typename NumTraits<RealScalar>::Literal(-1)); // bottleneck
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
template <typename MatrixType, typename VectorType>
|
||||
static Index rankUpdate(MatrixType& mat, const VectorType& vec, const RealScalar& sigma) {
|
||||
return Eigen::internal::llt_rank_update_lower(mat, vec, sigma);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Scalar>
|
||||
struct llt_inplace<Scalar, Upper> {
|
||||
typedef typename NumTraits<Scalar>::Real RealScalar;
|
||||
|
||||
template <typename MatrixType>
|
||||
static EIGEN_STRONG_INLINE Index unblocked(MatrixType& mat) {
|
||||
Transpose<MatrixType> matt(mat);
|
||||
return llt_inplace<Scalar, Lower>::unblocked(matt);
|
||||
}
|
||||
template <typename MatrixType>
|
||||
static EIGEN_STRONG_INLINE Index blocked(MatrixType& mat) {
|
||||
Transpose<MatrixType> matt(mat);
|
||||
return llt_inplace<Scalar, Lower>::blocked(matt);
|
||||
}
|
||||
template <typename MatrixType, typename VectorType>
|
||||
static Index rankUpdate(MatrixType& mat, const VectorType& vec, const RealScalar& sigma) {
|
||||
Transpose<MatrixType> matt(mat);
|
||||
return llt_inplace<Scalar, Lower>::rankUpdate(matt, vec.conjugate(), sigma);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename MatrixType>
|
||||
struct LLT_Traits<MatrixType, Lower> {
|
||||
typedef const TriangularView<const MatrixType, Lower> MatrixL;
|
||||
typedef const TriangularView<const typename MatrixType::AdjointReturnType, Upper> MatrixU;
|
||||
static inline MatrixL getL(const MatrixType& m) { return MatrixL(m); }
|
||||
static inline MatrixU getU(const MatrixType& m) { return MatrixU(m.adjoint()); }
|
||||
static bool inplace_decomposition(MatrixType& m) {
|
||||
return llt_inplace<typename MatrixType::Scalar, Lower>::blocked(m) == -1;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename MatrixType>
|
||||
struct LLT_Traits<MatrixType, Upper> {
|
||||
typedef const TriangularView<const typename MatrixType::AdjointReturnType, Lower> MatrixL;
|
||||
typedef const TriangularView<const MatrixType, Upper> MatrixU;
|
||||
static inline MatrixL getL(const MatrixType& m) { return MatrixL(m.adjoint()); }
|
||||
static inline MatrixU getU(const MatrixType& m) { return MatrixU(m); }
|
||||
static bool inplace_decomposition(MatrixType& m) {
|
||||
return llt_inplace<typename MatrixType::Scalar, Upper>::blocked(m) == -1;
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace internal
|
||||
|
||||
/** Computes / recomputes the Cholesky decomposition A = LL^* = U^*U of \a matrix
|
||||
*
|
||||
* \returns a reference to *this
|
||||
*
|
||||
* Example: \include TutorialLinAlgComputeTwice.cpp
|
||||
* Output: \verbinclude TutorialLinAlgComputeTwice.out
|
||||
*/
|
||||
template <typename MatrixType, int UpLo_>
|
||||
template <typename InputType>
|
||||
LLT<MatrixType, UpLo_>& LLT<MatrixType, UpLo_>::compute(const EigenBase<InputType>& a) {
|
||||
eigen_assert(a.rows() == a.cols());
|
||||
const Index size = a.rows();
|
||||
m_matrix.resize(size, size);
|
||||
if (!internal::is_same_dense(m_matrix, a.derived())) m_matrix = a.derived();
|
||||
|
||||
// Compute matrix L1 norm = max abs column sum.
|
||||
m_l1_norm = RealScalar(0);
|
||||
// TODO: move this code to SelfAdjointView
|
||||
for (Index col = 0; col < size; ++col) {
|
||||
RealScalar abs_col_sum;
|
||||
if (UpLo_ == Lower)
|
||||
abs_col_sum =
|
||||
m_matrix.col(col).tail(size - col).template lpNorm<1>() + m_matrix.row(col).head(col).template lpNorm<1>();
|
||||
else
|
||||
abs_col_sum =
|
||||
m_matrix.col(col).head(col).template lpNorm<1>() + m_matrix.row(col).tail(size - col).template lpNorm<1>();
|
||||
if (abs_col_sum > m_l1_norm) m_l1_norm = abs_col_sum;
|
||||
}
|
||||
|
||||
m_isInitialized = true;
|
||||
bool ok = Traits::inplace_decomposition(m_matrix);
|
||||
m_info = ok ? Success : NumericalIssue;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Performs a rank one update (or dowdate) of the current decomposition.
|
||||
* If A = LL^* before the rank one update,
|
||||
* then after it we have LL^* = A + sigma * v v^* where \a v must be a vector
|
||||
* of same dimension.
|
||||
*/
|
||||
template <typename MatrixType_, int UpLo_>
|
||||
template <typename VectorType>
|
||||
LLT<MatrixType_, UpLo_>& LLT<MatrixType_, UpLo_>::rankUpdate(const VectorType& v, const RealScalar& sigma) {
|
||||
EIGEN_STATIC_ASSERT_VECTOR_ONLY(VectorType);
|
||||
eigen_assert(v.size() == m_matrix.cols());
|
||||
eigen_assert(m_isInitialized);
|
||||
if (internal::llt_inplace<typename MatrixType::Scalar, UpLo>::rankUpdate(m_matrix, v, sigma) >= 0)
|
||||
m_info = NumericalIssue;
|
||||
else
|
||||
m_info = Success;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifndef EIGEN_PARSED_BY_DOXYGEN
|
||||
template <typename MatrixType_, int UpLo_>
|
||||
template <typename RhsType, typename DstType>
|
||||
void LLT<MatrixType_, UpLo_>::_solve_impl(const RhsType& rhs, DstType& dst) const {
|
||||
_solve_impl_transposed<true>(rhs, dst);
|
||||
}
|
||||
|
||||
template <typename MatrixType_, int UpLo_>
|
||||
template <bool Conjugate, typename RhsType, typename DstType>
|
||||
void LLT<MatrixType_, UpLo_>::_solve_impl_transposed(const RhsType& rhs, DstType& dst) const {
|
||||
dst = rhs;
|
||||
|
||||
matrixL().template conjugateIf<!Conjugate>().solveInPlace(dst);
|
||||
matrixU().template conjugateIf<!Conjugate>().solveInPlace(dst);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \internal use x = llt_object.solve(x);
|
||||
*
|
||||
* This is the \em in-place version of solve().
|
||||
*
|
||||
* \param bAndX represents both the right-hand side matrix b and result x.
|
||||
*
|
||||
* This version avoids a copy when the right hand side matrix b is not needed anymore.
|
||||
*
|
||||
* \warning The parameter is only marked 'const' to make the C++ compiler accept a temporary expression here.
|
||||
* This function will const_cast it, so constness isn't honored here.
|
||||
*
|
||||
* \sa LLT::solve(), MatrixBase::llt()
|
||||
*/
|
||||
template <typename MatrixType, int UpLo_>
|
||||
template <typename Derived>
|
||||
void LLT<MatrixType, UpLo_>::solveInPlace(const MatrixBase<Derived>& bAndX) const {
|
||||
eigen_assert(m_isInitialized && "LLT is not initialized.");
|
||||
eigen_assert(m_matrix.rows() == bAndX.rows());
|
||||
matrixL().solveInPlace(bAndX);
|
||||
matrixU().solveInPlace(bAndX);
|
||||
}
|
||||
|
||||
/** \returns the matrix represented by the decomposition,
|
||||
* i.e., it returns the product: L L^*.
|
||||
* This function is provided for debug purpose. */
|
||||
template <typename MatrixType, int UpLo_>
|
||||
MatrixType LLT<MatrixType, UpLo_>::reconstructedMatrix() const {
|
||||
eigen_assert(m_isInitialized && "LLT is not initialized.");
|
||||
return matrixL() * matrixL().adjoint().toDenseMatrix();
|
||||
}
|
||||
|
||||
/** \cholesky_module
|
||||
* \returns the LLT decomposition of \c *this
|
||||
* \sa SelfAdjointView::llt()
|
||||
*/
|
||||
template <typename Derived>
|
||||
inline LLT<typename MatrixBase<Derived>::PlainObject> MatrixBase<Derived>::llt() const {
|
||||
return LLT<PlainObject>(derived());
|
||||
}
|
||||
|
||||
/** \cholesky_module
|
||||
* \returns the LLT decomposition of \c *this
|
||||
* \sa SelfAdjointView::llt()
|
||||
*/
|
||||
template <typename MatrixType, unsigned int UpLo>
|
||||
inline LLT<typename SelfAdjointView<MatrixType, UpLo>::PlainObject, UpLo> SelfAdjointView<MatrixType, UpLo>::llt()
|
||||
const {
|
||||
return LLT<PlainObject, UpLo>(m_matrix);
|
||||
}
|
||||
|
||||
} // end namespace Eigen
|
||||
|
||||
#endif // EIGEN_LLT_H
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
Copyright (c) 2011, Intel Corporation. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of Intel Corporation nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
********************************************************************************
|
||||
* Content : Eigen bindings to LAPACKe
|
||||
* LLt decomposition based on LAPACKE_?potrf function.
|
||||
********************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef EIGEN_LLT_LAPACKE_H
|
||||
#define EIGEN_LLT_LAPACKE_H
|
||||
|
||||
// IWYU pragma: private
|
||||
#include "./InternalHeaderCheck.h"
|
||||
|
||||
namespace Eigen {
|
||||
|
||||
namespace internal {
|
||||
|
||||
namespace lapacke_helpers {
|
||||
// -------------------------------------------------------------------------------------------------------------------
|
||||
// Dispatch for rank update handling upper and lower parts
|
||||
// -------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template <UpLoType Mode>
|
||||
struct rank_update {};
|
||||
|
||||
template <>
|
||||
struct rank_update<Lower> {
|
||||
template <typename MatrixType, typename VectorType>
|
||||
static Index run(MatrixType &mat, const VectorType &vec, const typename MatrixType::RealScalar &sigma) {
|
||||
return Eigen::internal::llt_rank_update_lower(mat, vec, sigma);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct rank_update<Upper> {
|
||||
template <typename MatrixType, typename VectorType>
|
||||
static Index run(MatrixType &mat, const VectorType &vec, const typename MatrixType::RealScalar &sigma) {
|
||||
Transpose<MatrixType> matt(mat);
|
||||
return Eigen::internal::llt_rank_update_lower(matt, vec.conjugate(), sigma);
|
||||
}
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------
|
||||
// Generic lapacke llt implementation that hands of to the dispatches
|
||||
// -------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
template <typename Scalar, UpLoType Mode>
|
||||
struct lapacke_llt {
|
||||
EIGEN_STATIC_ASSERT(((Mode == Lower) || (Mode == Upper)), MODE_MUST_BE_UPPER_OR_LOWER)
|
||||
template <typename MatrixType>
|
||||
static Index blocked(MatrixType &m) {
|
||||
eigen_assert(m.rows() == m.cols());
|
||||
if (m.rows() == 0) {
|
||||
return -1;
|
||||
}
|
||||
/* Set up parameters for ?potrf */
|
||||
lapack_int size = to_lapack(m.rows());
|
||||
lapack_int matrix_order = lapack_storage_of(m);
|
||||
constexpr char uplo = Mode == Upper ? 'U' : 'L';
|
||||
Scalar *a = &(m.coeffRef(0, 0));
|
||||
lapack_int lda = to_lapack(m.outerStride());
|
||||
|
||||
lapack_int info = potrf(matrix_order, uplo, size, to_lapack(a), lda);
|
||||
info = (info == 0) ? -1 : info > 0 ? info - 1 : size;
|
||||
return info;
|
||||
}
|
||||
|
||||
template <typename MatrixType, typename VectorType>
|
||||
static Index rankUpdate(MatrixType &mat, const VectorType &vec, const typename MatrixType::RealScalar &sigma) {
|
||||
return rank_update<Mode>::run(mat, vec, sigma);
|
||||
}
|
||||
};
|
||||
} // namespace lapacke_helpers
|
||||
// end namespace lapacke_helpers
|
||||
|
||||
/*
|
||||
* Here, we just put the generic implementation from lapacke_llt into a full specialization of the llt_inplace
|
||||
* type. By being a full specialization, the versions defined here thus get precedence over the generic implementation
|
||||
* in LLT.h for double, float and complex double, complex float types.
|
||||
*/
|
||||
|
||||
#define EIGEN_LAPACKE_LLT(EIGTYPE) \
|
||||
template <> \
|
||||
struct llt_inplace<EIGTYPE, Lower> : public lapacke_helpers::lapacke_llt<EIGTYPE, Lower> {}; \
|
||||
template <> \
|
||||
struct llt_inplace<EIGTYPE, Upper> : public lapacke_helpers::lapacke_llt<EIGTYPE, Upper> {};
|
||||
|
||||
EIGEN_LAPACKE_LLT(double)
|
||||
EIGEN_LAPACKE_LLT(float)
|
||||
EIGEN_LAPACKE_LLT(std::complex<double>)
|
||||
EIGEN_LAPACKE_LLT(std::complex<float>)
|
||||
|
||||
#undef EIGEN_LAPACKE_LLT
|
||||
|
||||
} // end namespace internal
|
||||
|
||||
} // end namespace Eigen
|
||||
|
||||
#endif // EIGEN_LLT_LAPACKE_H
|
||||
+738
@@ -0,0 +1,738 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2008-2010 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_CHOLMODSUPPORT_H
|
||||
#define EIGEN_CHOLMODSUPPORT_H
|
||||
|
||||
// IWYU pragma: private
|
||||
#include "./InternalHeaderCheck.h"
|
||||
|
||||
namespace Eigen {
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename Scalar>
|
||||
struct cholmod_configure_matrix;
|
||||
|
||||
template <>
|
||||
struct cholmod_configure_matrix<double> {
|
||||
template <typename CholmodType>
|
||||
static void run(CholmodType& mat) {
|
||||
mat.xtype = CHOLMOD_REAL;
|
||||
mat.dtype = CHOLMOD_DOUBLE;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct cholmod_configure_matrix<std::complex<double> > {
|
||||
template <typename CholmodType>
|
||||
static void run(CholmodType& mat) {
|
||||
mat.xtype = CHOLMOD_COMPLEX;
|
||||
mat.dtype = CHOLMOD_DOUBLE;
|
||||
}
|
||||
};
|
||||
|
||||
// Other scalar types are not yet supported by Cholmod
|
||||
// template<> struct cholmod_configure_matrix<float> {
|
||||
// template<typename CholmodType>
|
||||
// static void run(CholmodType& mat) {
|
||||
// mat.xtype = CHOLMOD_REAL;
|
||||
// mat.dtype = CHOLMOD_SINGLE;
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// template<> struct cholmod_configure_matrix<std::complex<float> > {
|
||||
// template<typename CholmodType>
|
||||
// static void run(CholmodType& mat) {
|
||||
// mat.xtype = CHOLMOD_COMPLEX;
|
||||
// mat.dtype = CHOLMOD_SINGLE;
|
||||
// }
|
||||
// };
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/** Wraps the Eigen sparse matrix \a mat into a Cholmod sparse matrix object.
|
||||
* Note that the data are shared.
|
||||
*/
|
||||
template <typename Scalar_, int Options_, typename StorageIndex_>
|
||||
cholmod_sparse viewAsCholmod(Ref<SparseMatrix<Scalar_, Options_, StorageIndex_> > mat) {
|
||||
cholmod_sparse res;
|
||||
res.nzmax = mat.nonZeros();
|
||||
res.nrow = mat.rows();
|
||||
res.ncol = mat.cols();
|
||||
res.p = mat.outerIndexPtr();
|
||||
res.i = mat.innerIndexPtr();
|
||||
res.x = mat.valuePtr();
|
||||
res.z = 0;
|
||||
res.sorted = 1;
|
||||
if (mat.isCompressed()) {
|
||||
res.packed = 1;
|
||||
res.nz = 0;
|
||||
} else {
|
||||
res.packed = 0;
|
||||
res.nz = mat.innerNonZeroPtr();
|
||||
}
|
||||
|
||||
res.dtype = 0;
|
||||
res.stype = -1;
|
||||
|
||||
if (internal::is_same<StorageIndex_, int>::value) {
|
||||
res.itype = CHOLMOD_INT;
|
||||
} else if (internal::is_same<StorageIndex_, SuiteSparse_long>::value) {
|
||||
res.itype = CHOLMOD_LONG;
|
||||
} else {
|
||||
eigen_assert(false && "Index type not supported yet");
|
||||
}
|
||||
|
||||
// setup res.xtype
|
||||
internal::cholmod_configure_matrix<Scalar_>::run(res);
|
||||
|
||||
res.stype = 0;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename Scalar_, int Options_, typename Index_>
|
||||
const cholmod_sparse viewAsCholmod(const SparseMatrix<Scalar_, Options_, Index_>& mat) {
|
||||
cholmod_sparse res = viewAsCholmod(Ref<SparseMatrix<Scalar_, Options_, Index_> >(mat.const_cast_derived()));
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename Scalar_, int Options_, typename Index_>
|
||||
const cholmod_sparse viewAsCholmod(const SparseVector<Scalar_, Options_, Index_>& mat) {
|
||||
cholmod_sparse res = viewAsCholmod(Ref<SparseMatrix<Scalar_, Options_, Index_> >(mat.const_cast_derived()));
|
||||
return res;
|
||||
}
|
||||
|
||||
/** Returns a view of the Eigen sparse matrix \a mat as Cholmod sparse matrix.
|
||||
* The data are not copied but shared. */
|
||||
template <typename Scalar_, int Options_, typename Index_, unsigned int UpLo>
|
||||
cholmod_sparse viewAsCholmod(const SparseSelfAdjointView<const SparseMatrix<Scalar_, Options_, Index_>, UpLo>& mat) {
|
||||
cholmod_sparse res = viewAsCholmod(Ref<SparseMatrix<Scalar_, Options_, Index_> >(mat.matrix().const_cast_derived()));
|
||||
|
||||
if (UpLo == Upper) res.stype = 1;
|
||||
if (UpLo == Lower) res.stype = -1;
|
||||
// swap stype for rowmajor matrices (only works for real matrices)
|
||||
EIGEN_STATIC_ASSERT((Options_ & RowMajorBit) == 0 || NumTraits<Scalar_>::IsComplex == 0,
|
||||
THIS_METHOD_IS_ONLY_FOR_COLUMN_MAJOR_MATRICES);
|
||||
if (Options_ & RowMajorBit) res.stype *= -1;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/** Returns a view of the Eigen \b dense matrix \a mat as Cholmod dense matrix.
|
||||
* The data are not copied but shared. */
|
||||
template <typename Derived>
|
||||
cholmod_dense viewAsCholmod(MatrixBase<Derived>& mat) {
|
||||
EIGEN_STATIC_ASSERT((internal::traits<Derived>::Flags & RowMajorBit) == 0,
|
||||
THIS_METHOD_IS_ONLY_FOR_COLUMN_MAJOR_MATRICES);
|
||||
typedef typename Derived::Scalar Scalar;
|
||||
|
||||
cholmod_dense res;
|
||||
res.nrow = mat.rows();
|
||||
res.ncol = mat.cols();
|
||||
res.nzmax = res.nrow * res.ncol;
|
||||
res.d = Derived::IsVectorAtCompileTime ? mat.derived().size() : mat.derived().outerStride();
|
||||
res.x = (void*)(mat.derived().data());
|
||||
res.z = 0;
|
||||
|
||||
internal::cholmod_configure_matrix<Scalar>::run(res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/** Returns a view of the Cholmod sparse matrix \a cm as an Eigen sparse matrix.
|
||||
* The data are not copied but shared. */
|
||||
template <typename Scalar, typename StorageIndex>
|
||||
Map<const SparseMatrix<Scalar, ColMajor, StorageIndex> > viewAsEigen(cholmod_sparse& cm) {
|
||||
return Map<const SparseMatrix<Scalar, ColMajor, StorageIndex> >(
|
||||
cm.nrow, cm.ncol, static_cast<StorageIndex*>(cm.p)[cm.ncol], static_cast<StorageIndex*>(cm.p),
|
||||
static_cast<StorageIndex*>(cm.i), static_cast<Scalar*>(cm.x));
|
||||
}
|
||||
|
||||
/** Returns a view of the Cholmod sparse matrix factor \a cm as an Eigen sparse matrix.
|
||||
* The data are not copied but shared. */
|
||||
template <typename Scalar, typename StorageIndex>
|
||||
Map<const SparseMatrix<Scalar, ColMajor, StorageIndex> > viewAsEigen(cholmod_factor& cm) {
|
||||
return Map<const SparseMatrix<Scalar, ColMajor, StorageIndex> >(
|
||||
cm.n, cm.n, static_cast<StorageIndex*>(cm.p)[cm.n], static_cast<StorageIndex*>(cm.p),
|
||||
static_cast<StorageIndex*>(cm.i), static_cast<Scalar*>(cm.x));
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
// template specializations for int and long that call the correct cholmod method
|
||||
|
||||
#define EIGEN_CHOLMOD_SPECIALIZE0(ret, name) \
|
||||
template <typename StorageIndex_> \
|
||||
inline ret cm_##name(cholmod_common& Common) { \
|
||||
return cholmod_##name(&Common); \
|
||||
} \
|
||||
template <> \
|
||||
inline ret cm_##name<SuiteSparse_long>(cholmod_common & Common) { \
|
||||
return cholmod_l_##name(&Common); \
|
||||
}
|
||||
|
||||
#define EIGEN_CHOLMOD_SPECIALIZE1(ret, name, t1, a1) \
|
||||
template <typename StorageIndex_> \
|
||||
inline ret cm_##name(t1& a1, cholmod_common& Common) { \
|
||||
return cholmod_##name(&a1, &Common); \
|
||||
} \
|
||||
template <> \
|
||||
inline ret cm_##name<SuiteSparse_long>(t1 & a1, cholmod_common & Common) { \
|
||||
return cholmod_l_##name(&a1, &Common); \
|
||||
}
|
||||
|
||||
EIGEN_CHOLMOD_SPECIALIZE0(int, start)
|
||||
EIGEN_CHOLMOD_SPECIALIZE0(int, finish)
|
||||
|
||||
EIGEN_CHOLMOD_SPECIALIZE1(int, free_factor, cholmod_factor*, L)
|
||||
EIGEN_CHOLMOD_SPECIALIZE1(int, free_dense, cholmod_dense*, X)
|
||||
EIGEN_CHOLMOD_SPECIALIZE1(int, free_sparse, cholmod_sparse*, A)
|
||||
|
||||
EIGEN_CHOLMOD_SPECIALIZE1(cholmod_factor*, analyze, cholmod_sparse, A)
|
||||
EIGEN_CHOLMOD_SPECIALIZE1(cholmod_sparse*, factor_to_sparse, cholmod_factor, L)
|
||||
|
||||
template <typename StorageIndex_>
|
||||
inline cholmod_dense* cm_solve(int sys, cholmod_factor& L, cholmod_dense& B, cholmod_common& Common) {
|
||||
return cholmod_solve(sys, &L, &B, &Common);
|
||||
}
|
||||
template <>
|
||||
inline cholmod_dense* cm_solve<SuiteSparse_long>(int sys, cholmod_factor& L, cholmod_dense& B, cholmod_common& Common) {
|
||||
return cholmod_l_solve(sys, &L, &B, &Common);
|
||||
}
|
||||
|
||||
template <typename StorageIndex_>
|
||||
inline cholmod_sparse* cm_spsolve(int sys, cholmod_factor& L, cholmod_sparse& B, cholmod_common& Common) {
|
||||
return cholmod_spsolve(sys, &L, &B, &Common);
|
||||
}
|
||||
template <>
|
||||
inline cholmod_sparse* cm_spsolve<SuiteSparse_long>(int sys, cholmod_factor& L, cholmod_sparse& B,
|
||||
cholmod_common& Common) {
|
||||
return cholmod_l_spsolve(sys, &L, &B, &Common);
|
||||
}
|
||||
|
||||
template <typename StorageIndex_>
|
||||
inline int cm_factorize_p(cholmod_sparse* A, double beta[2], StorageIndex_* fset, std::size_t fsize, cholmod_factor* L,
|
||||
cholmod_common& Common) {
|
||||
return cholmod_factorize_p(A, beta, fset, fsize, L, &Common);
|
||||
}
|
||||
template <>
|
||||
inline int cm_factorize_p<SuiteSparse_long>(cholmod_sparse* A, double beta[2], SuiteSparse_long* fset,
|
||||
std::size_t fsize, cholmod_factor* L, cholmod_common& Common) {
|
||||
return cholmod_l_factorize_p(A, beta, fset, fsize, L, &Common);
|
||||
}
|
||||
|
||||
#undef EIGEN_CHOLMOD_SPECIALIZE0
|
||||
#undef EIGEN_CHOLMOD_SPECIALIZE1
|
||||
|
||||
} // namespace internal
|
||||
|
||||
enum CholmodMode { CholmodAuto, CholmodSimplicialLLt, CholmodSupernodalLLt, CholmodLDLt };
|
||||
|
||||
/** \ingroup CholmodSupport_Module
|
||||
* \class CholmodBase
|
||||
* \brief The base class for the direct Cholesky factorization of Cholmod
|
||||
* \sa class CholmodSupernodalLLT, class CholmodSimplicialLDLT, class CholmodSimplicialLLT
|
||||
*/
|
||||
template <typename MatrixType_, int UpLo_, typename Derived>
|
||||
class CholmodBase : public SparseSolverBase<Derived> {
|
||||
protected:
|
||||
typedef SparseSolverBase<Derived> Base;
|
||||
using Base::derived;
|
||||
using Base::m_isInitialized;
|
||||
|
||||
public:
|
||||
typedef MatrixType_ MatrixType;
|
||||
enum { UpLo = UpLo_ };
|
||||
typedef typename MatrixType::Scalar Scalar;
|
||||
typedef typename MatrixType::RealScalar RealScalar;
|
||||
typedef MatrixType CholMatrixType;
|
||||
typedef typename MatrixType::StorageIndex StorageIndex;
|
||||
enum { ColsAtCompileTime = MatrixType::ColsAtCompileTime, MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime };
|
||||
|
||||
public:
|
||||
CholmodBase() : m_cholmodFactor(0), m_info(Success), m_factorizationIsOk(false), m_analysisIsOk(false) {
|
||||
EIGEN_STATIC_ASSERT((internal::is_same<double, RealScalar>::value), CHOLMOD_SUPPORTS_DOUBLE_PRECISION_ONLY);
|
||||
m_shiftOffset[0] = m_shiftOffset[1] = 0.0;
|
||||
internal::cm_start<StorageIndex>(m_cholmod);
|
||||
}
|
||||
|
||||
explicit CholmodBase(const MatrixType& matrix)
|
||||
: m_cholmodFactor(0), m_info(Success), m_factorizationIsOk(false), m_analysisIsOk(false) {
|
||||
EIGEN_STATIC_ASSERT((internal::is_same<double, RealScalar>::value), CHOLMOD_SUPPORTS_DOUBLE_PRECISION_ONLY);
|
||||
m_shiftOffset[0] = m_shiftOffset[1] = 0.0;
|
||||
internal::cm_start<StorageIndex>(m_cholmod);
|
||||
compute(matrix);
|
||||
}
|
||||
|
||||
~CholmodBase() {
|
||||
if (m_cholmodFactor) internal::cm_free_factor<StorageIndex>(m_cholmodFactor, m_cholmod);
|
||||
internal::cm_finish<StorageIndex>(m_cholmod);
|
||||
}
|
||||
|
||||
inline StorageIndex cols() const { return internal::convert_index<StorageIndex, Index>(m_cholmodFactor->n); }
|
||||
inline StorageIndex rows() const { return internal::convert_index<StorageIndex, Index>(m_cholmodFactor->n); }
|
||||
|
||||
/** \brief Reports whether previous computation was successful.
|
||||
*
|
||||
* \returns \c Success if computation was successful,
|
||||
* \c NumericalIssue if the matrix.appears to be negative.
|
||||
*/
|
||||
ComputationInfo info() const {
|
||||
eigen_assert(m_isInitialized && "Decomposition is not initialized.");
|
||||
return m_info;
|
||||
}
|
||||
|
||||
/** Computes the sparse Cholesky decomposition of \a matrix */
|
||||
Derived& compute(const MatrixType& matrix) {
|
||||
analyzePattern(matrix);
|
||||
factorize(matrix);
|
||||
return derived();
|
||||
}
|
||||
|
||||
/** Performs a symbolic decomposition on the sparsity pattern of \a matrix.
|
||||
*
|
||||
* This function is particularly useful when solving for several problems having the same structure.
|
||||
*
|
||||
* \sa factorize()
|
||||
*/
|
||||
void analyzePattern(const MatrixType& matrix) {
|
||||
if (m_cholmodFactor) {
|
||||
internal::cm_free_factor<StorageIndex>(m_cholmodFactor, m_cholmod);
|
||||
m_cholmodFactor = 0;
|
||||
}
|
||||
cholmod_sparse A = viewAsCholmod(matrix.template selfadjointView<UpLo>());
|
||||
m_cholmodFactor = internal::cm_analyze<StorageIndex>(A, m_cholmod);
|
||||
|
||||
this->m_isInitialized = true;
|
||||
this->m_info = Success;
|
||||
m_analysisIsOk = true;
|
||||
m_factorizationIsOk = false;
|
||||
}
|
||||
|
||||
/** Performs a numeric decomposition of \a matrix
|
||||
*
|
||||
* The given matrix must have the same sparsity pattern as the matrix on which the symbolic decomposition has been
|
||||
* performed.
|
||||
*
|
||||
* \sa analyzePattern()
|
||||
*/
|
||||
void factorize(const MatrixType& matrix) {
|
||||
eigen_assert(m_analysisIsOk && "You must first call analyzePattern()");
|
||||
cholmod_sparse A = viewAsCholmod(matrix.template selfadjointView<UpLo>());
|
||||
internal::cm_factorize_p<StorageIndex>(&A, m_shiftOffset, 0, 0, m_cholmodFactor, m_cholmod);
|
||||
|
||||
// If the factorization failed, either the input matrix was zero (so m_cholmodFactor == nullptr), or minor is the
|
||||
// column at which it failed. On success minor == n.
|
||||
this->m_info =
|
||||
(m_cholmodFactor != nullptr && m_cholmodFactor->minor == m_cholmodFactor->n ? Success : NumericalIssue);
|
||||
m_factorizationIsOk = true;
|
||||
}
|
||||
|
||||
/** Returns a reference to the Cholmod's configuration structure to get a full control over the performed operations.
|
||||
* See the Cholmod user guide for details. */
|
||||
cholmod_common& cholmod() { return m_cholmod; }
|
||||
|
||||
#ifndef EIGEN_PARSED_BY_DOXYGEN
|
||||
/** \internal */
|
||||
template <typename Rhs, typename Dest>
|
||||
void _solve_impl(const MatrixBase<Rhs>& b, MatrixBase<Dest>& dest) const {
|
||||
eigen_assert(m_factorizationIsOk &&
|
||||
"The decomposition is not in a valid state for solving, you must first call either compute() or "
|
||||
"symbolic()/numeric()");
|
||||
const Index size = m_cholmodFactor->n;
|
||||
EIGEN_UNUSED_VARIABLE(size);
|
||||
eigen_assert(size == b.rows());
|
||||
|
||||
// Cholmod needs column-major storage without inner-stride, which corresponds to the default behavior of Ref.
|
||||
Ref<const Matrix<typename Rhs::Scalar, Dynamic, Dynamic, ColMajor> > b_ref(b.derived());
|
||||
|
||||
cholmod_dense b_cd = viewAsCholmod(b_ref);
|
||||
cholmod_dense* x_cd = internal::cm_solve<StorageIndex>(CHOLMOD_A, *m_cholmodFactor, b_cd, m_cholmod);
|
||||
if (!x_cd) {
|
||||
this->m_info = NumericalIssue;
|
||||
return;
|
||||
}
|
||||
// TODO: optimize this copy by swapping when possible (be careful with alignment, etc.)
|
||||
// NOTE Actually, the copy can be avoided by calling cholmod_solve2 instead of cholmod_solve
|
||||
dest = Matrix<Scalar, Dest::RowsAtCompileTime, Dest::ColsAtCompileTime>::Map(reinterpret_cast<Scalar*>(x_cd->x),
|
||||
b.rows(), b.cols());
|
||||
internal::cm_free_dense<StorageIndex>(x_cd, m_cholmod);
|
||||
}
|
||||
|
||||
/** \internal */
|
||||
template <typename RhsDerived, typename DestDerived>
|
||||
void _solve_impl(const SparseMatrixBase<RhsDerived>& b, SparseMatrixBase<DestDerived>& dest) const {
|
||||
eigen_assert(m_factorizationIsOk &&
|
||||
"The decomposition is not in a valid state for solving, you must first call either compute() or "
|
||||
"symbolic()/numeric()");
|
||||
const Index size = m_cholmodFactor->n;
|
||||
EIGEN_UNUSED_VARIABLE(size);
|
||||
eigen_assert(size == b.rows());
|
||||
|
||||
// note: cs stands for Cholmod Sparse
|
||||
Ref<SparseMatrix<typename RhsDerived::Scalar, ColMajor, typename RhsDerived::StorageIndex> > b_ref(
|
||||
b.const_cast_derived());
|
||||
cholmod_sparse b_cs = viewAsCholmod(b_ref);
|
||||
cholmod_sparse* x_cs = internal::cm_spsolve<StorageIndex>(CHOLMOD_A, *m_cholmodFactor, b_cs, m_cholmod);
|
||||
if (!x_cs) {
|
||||
this->m_info = NumericalIssue;
|
||||
return;
|
||||
}
|
||||
// TODO: optimize this copy by swapping when possible (be careful with alignment, etc.)
|
||||
// NOTE cholmod_spsolve in fact just calls the dense solver for blocks of 4 columns at a time (similar to Eigen's
|
||||
// sparse solver)
|
||||
dest.derived() = viewAsEigen<typename DestDerived::Scalar, typename DestDerived::StorageIndex>(*x_cs);
|
||||
internal::cm_free_sparse<StorageIndex>(x_cs, m_cholmod);
|
||||
}
|
||||
#endif // EIGEN_PARSED_BY_DOXYGEN
|
||||
|
||||
/** Sets the shift parameter that will be used to adjust the diagonal coefficients during the numerical factorization.
|
||||
*
|
||||
* During the numerical factorization, an offset term is added to the diagonal coefficients:\n
|
||||
* \c d_ii = \a offset + \c d_ii
|
||||
*
|
||||
* The default is \a offset=0.
|
||||
*
|
||||
* \returns a reference to \c *this.
|
||||
*/
|
||||
Derived& setShift(const RealScalar& offset) {
|
||||
m_shiftOffset[0] = double(offset);
|
||||
return derived();
|
||||
}
|
||||
|
||||
/** \returns the determinant of the underlying matrix from the current factorization */
|
||||
Scalar determinant() const {
|
||||
using std::exp;
|
||||
return exp(logDeterminant());
|
||||
}
|
||||
|
||||
/** \returns the log determinant of the underlying matrix from the current factorization */
|
||||
Scalar logDeterminant() const {
|
||||
using numext::real;
|
||||
using std::log;
|
||||
eigen_assert(m_factorizationIsOk &&
|
||||
"The decomposition is not in a valid state for solving, you must first call either compute() or "
|
||||
"symbolic()/numeric()");
|
||||
|
||||
RealScalar logDet = 0;
|
||||
Scalar* x = static_cast<Scalar*>(m_cholmodFactor->x);
|
||||
if (m_cholmodFactor->is_super) {
|
||||
// Supernodal factorization stored as a packed list of dense column-major blocks,
|
||||
// as described by the following structure:
|
||||
|
||||
// super[k] == index of the first column of the j-th super node
|
||||
StorageIndex* super = static_cast<StorageIndex*>(m_cholmodFactor->super);
|
||||
// pi[k] == offset to the description of row indices
|
||||
StorageIndex* pi = static_cast<StorageIndex*>(m_cholmodFactor->pi);
|
||||
// px[k] == offset to the respective dense block
|
||||
StorageIndex* px = static_cast<StorageIndex*>(m_cholmodFactor->px);
|
||||
|
||||
Index nb_super_nodes = m_cholmodFactor->nsuper;
|
||||
for (Index k = 0; k < nb_super_nodes; ++k) {
|
||||
StorageIndex ncols = super[k + 1] - super[k];
|
||||
StorageIndex nrows = pi[k + 1] - pi[k];
|
||||
|
||||
Map<const Array<Scalar, 1, Dynamic>, 0, InnerStride<> > sk(x + px[k], ncols, InnerStride<>(nrows + 1));
|
||||
logDet += sk.real().log().sum();
|
||||
}
|
||||
} else {
|
||||
// Simplicial factorization stored as standard CSC matrix.
|
||||
StorageIndex* p = static_cast<StorageIndex*>(m_cholmodFactor->p);
|
||||
Index size = m_cholmodFactor->n;
|
||||
for (Index k = 0; k < size; ++k) logDet += log(real(x[p[k]]));
|
||||
}
|
||||
if (m_cholmodFactor->is_ll) logDet *= 2.0;
|
||||
return logDet;
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
void dumpMemory(Stream& /*s*/) {}
|
||||
|
||||
protected:
|
||||
mutable cholmod_common m_cholmod;
|
||||
cholmod_factor* m_cholmodFactor;
|
||||
double m_shiftOffset[2];
|
||||
mutable ComputationInfo m_info;
|
||||
int m_factorizationIsOk;
|
||||
int m_analysisIsOk;
|
||||
};
|
||||
|
||||
/** \ingroup CholmodSupport_Module
|
||||
* \class CholmodSimplicialLLT
|
||||
* \brief A simplicial direct Cholesky (LLT) factorization and solver based on Cholmod
|
||||
*
|
||||
* This class allows to solve for A.X = B sparse linear problems via a simplicial LL^T Cholesky factorization
|
||||
* using the Cholmod library.
|
||||
* This simplicial variant is equivalent to Eigen's built-in SimplicialLLT class. Therefore, it has little practical
|
||||
* interest. The sparse matrix A must be selfadjoint and positive definite. The vectors or matrices X and B can be
|
||||
* either dense or sparse.
|
||||
*
|
||||
* \tparam MatrixType_ the type of the sparse matrix A, it must be a SparseMatrix<>
|
||||
* \tparam UpLo_ the triangular part that will be used for the computations. It can be Lower
|
||||
* or Upper. Default is Lower.
|
||||
*
|
||||
* \implsparsesolverconcept
|
||||
*
|
||||
* This class supports all kind of SparseMatrix<>: row or column major; upper, lower, or both; compressed or non
|
||||
* compressed.
|
||||
*
|
||||
* \warning Only double precision real and complex scalar types are supported by Cholmod.
|
||||
*
|
||||
* \sa \ref TutorialSparseSolverConcept, class CholmodSupernodalLLT, class SimplicialLLT
|
||||
*/
|
||||
template <typename MatrixType_, int UpLo_ = Lower>
|
||||
class CholmodSimplicialLLT : public CholmodBase<MatrixType_, UpLo_, CholmodSimplicialLLT<MatrixType_, UpLo_> > {
|
||||
typedef CholmodBase<MatrixType_, UpLo_, CholmodSimplicialLLT> Base;
|
||||
using Base::m_cholmod;
|
||||
|
||||
public:
|
||||
typedef MatrixType_ MatrixType;
|
||||
typedef typename MatrixType::Scalar Scalar;
|
||||
typedef typename MatrixType::RealScalar RealScalar;
|
||||
typedef typename MatrixType::StorageIndex StorageIndex;
|
||||
typedef TriangularView<const MatrixType, Eigen::Lower> MatrixL;
|
||||
typedef TriangularView<const typename MatrixType::AdjointReturnType, Eigen::Upper> MatrixU;
|
||||
|
||||
CholmodSimplicialLLT() : Base() { init(); }
|
||||
|
||||
CholmodSimplicialLLT(const MatrixType& matrix) : Base() {
|
||||
init();
|
||||
this->compute(matrix);
|
||||
}
|
||||
|
||||
~CholmodSimplicialLLT() {}
|
||||
|
||||
/** \returns an expression of the factor L */
|
||||
inline MatrixL matrixL() const { return viewAsEigen<Scalar, StorageIndex>(*Base::m_cholmodFactor); }
|
||||
|
||||
/** \returns an expression of the factor U (= L^*) */
|
||||
inline MatrixU matrixU() const { return matrixL().adjoint(); }
|
||||
|
||||
protected:
|
||||
void init() {
|
||||
m_cholmod.final_asis = 0;
|
||||
m_cholmod.supernodal = CHOLMOD_SIMPLICIAL;
|
||||
m_cholmod.final_ll = 1;
|
||||
}
|
||||
};
|
||||
|
||||
/** \ingroup CholmodSupport_Module
|
||||
* \class CholmodSimplicialLDLT
|
||||
* \brief A simplicial direct Cholesky (LDLT) factorization and solver based on Cholmod
|
||||
*
|
||||
* This class allows to solve for A.X = B sparse linear problems via a simplicial LDL^T Cholesky factorization
|
||||
* using the Cholmod library.
|
||||
* This simplicial variant is equivalent to Eigen's built-in SimplicialLDLT class. Therefore, it has little practical
|
||||
* interest. The sparse matrix A must be selfadjoint and positive definite. The vectors or matrices X and B can be
|
||||
* either dense or sparse.
|
||||
*
|
||||
* \tparam MatrixType_ the type of the sparse matrix A, it must be a SparseMatrix<>
|
||||
* \tparam UpLo_ the triangular part that will be used for the computations. It can be Lower
|
||||
* or Upper. Default is Lower.
|
||||
*
|
||||
* \implsparsesolverconcept
|
||||
*
|
||||
* This class supports all kind of SparseMatrix<>: row or column major; upper, lower, or both; compressed or non
|
||||
* compressed.
|
||||
*
|
||||
* \warning Only double precision real and complex scalar types are supported by Cholmod.
|
||||
*
|
||||
* \sa \ref TutorialSparseSolverConcept, class CholmodSupernodalLLT, class SimplicialLDLT
|
||||
*/
|
||||
template <typename MatrixType_, int UpLo_ = Lower>
|
||||
class CholmodSimplicialLDLT : public CholmodBase<MatrixType_, UpLo_, CholmodSimplicialLDLT<MatrixType_, UpLo_> > {
|
||||
typedef CholmodBase<MatrixType_, UpLo_, CholmodSimplicialLDLT> Base;
|
||||
using Base::m_cholmod;
|
||||
|
||||
public:
|
||||
typedef MatrixType_ MatrixType;
|
||||
typedef typename MatrixType::Scalar Scalar;
|
||||
typedef typename MatrixType::RealScalar RealScalar;
|
||||
typedef typename MatrixType::StorageIndex StorageIndex;
|
||||
typedef Matrix<Scalar, Dynamic, 1> VectorType;
|
||||
typedef TriangularView<const MatrixType, Eigen::UnitLower> MatrixL;
|
||||
typedef TriangularView<const typename MatrixType::AdjointReturnType, Eigen::UnitUpper> MatrixU;
|
||||
|
||||
CholmodSimplicialLDLT() : Base() { init(); }
|
||||
|
||||
CholmodSimplicialLDLT(const MatrixType& matrix) : Base() {
|
||||
init();
|
||||
this->compute(matrix);
|
||||
}
|
||||
|
||||
~CholmodSimplicialLDLT() {}
|
||||
|
||||
/** \returns a vector expression of the diagonal D */
|
||||
inline VectorType vectorD() const {
|
||||
auto cholmodL = viewAsEigen<Scalar, StorageIndex>(*Base::m_cholmodFactor);
|
||||
|
||||
VectorType D{cholmodL.rows()};
|
||||
|
||||
for (Index k = 0; k < cholmodL.outerSize(); ++k) {
|
||||
typename decltype(cholmodL)::InnerIterator it{cholmodL, k};
|
||||
D(k) = it.value();
|
||||
}
|
||||
|
||||
return D;
|
||||
}
|
||||
|
||||
/** \returns an expression of the factor L */
|
||||
inline MatrixL matrixL() const { return viewAsEigen<Scalar, StorageIndex>(*Base::m_cholmodFactor); }
|
||||
|
||||
/** \returns an expression of the factor U (= L^*) */
|
||||
inline MatrixU matrixU() const { return matrixL().adjoint(); }
|
||||
|
||||
protected:
|
||||
void init() {
|
||||
m_cholmod.final_asis = 1;
|
||||
m_cholmod.supernodal = CHOLMOD_SIMPLICIAL;
|
||||
}
|
||||
};
|
||||
|
||||
/** \ingroup CholmodSupport_Module
|
||||
* \class CholmodSupernodalLLT
|
||||
* \brief A supernodal Cholesky (LLT) factorization and solver based on Cholmod
|
||||
*
|
||||
* This class allows to solve for A.X = B sparse linear problems via a supernodal LL^T Cholesky factorization
|
||||
* using the Cholmod library.
|
||||
* This supernodal variant performs best on dense enough problems, e.g., 3D FEM, or very high order 2D FEM.
|
||||
* The sparse matrix A must be selfadjoint and positive definite. The vectors or matrices
|
||||
* X and B can be either dense or sparse.
|
||||
*
|
||||
* \tparam MatrixType_ the type of the sparse matrix A, it must be a SparseMatrix<>
|
||||
* \tparam UpLo_ the triangular part that will be used for the computations. It can be Lower
|
||||
* or Upper. Default is Lower.
|
||||
*
|
||||
* \implsparsesolverconcept
|
||||
*
|
||||
* This class supports all kind of SparseMatrix<>: row or column major; upper, lower, or both; compressed or non
|
||||
* compressed.
|
||||
*
|
||||
* \warning Only double precision real and complex scalar types are supported by Cholmod.
|
||||
*
|
||||
* \sa \ref TutorialSparseSolverConcept
|
||||
*/
|
||||
template <typename MatrixType_, int UpLo_ = Lower>
|
||||
class CholmodSupernodalLLT : public CholmodBase<MatrixType_, UpLo_, CholmodSupernodalLLT<MatrixType_, UpLo_> > {
|
||||
typedef CholmodBase<MatrixType_, UpLo_, CholmodSupernodalLLT> Base;
|
||||
using Base::m_cholmod;
|
||||
|
||||
public:
|
||||
typedef MatrixType_ MatrixType;
|
||||
typedef typename MatrixType::Scalar Scalar;
|
||||
typedef typename MatrixType::RealScalar RealScalar;
|
||||
typedef typename MatrixType::StorageIndex StorageIndex;
|
||||
|
||||
CholmodSupernodalLLT() : Base() { init(); }
|
||||
|
||||
CholmodSupernodalLLT(const MatrixType& matrix) : Base() {
|
||||
init();
|
||||
this->compute(matrix);
|
||||
}
|
||||
|
||||
~CholmodSupernodalLLT() {}
|
||||
|
||||
/** \returns an expression of the factor L */
|
||||
inline MatrixType matrixL() const {
|
||||
// Convert Cholmod factor's supernodal storage format to Eigen's CSC storage format
|
||||
cholmod_sparse* cholmodL = internal::cm_factor_to_sparse(*Base::m_cholmodFactor, m_cholmod);
|
||||
MatrixType L = viewAsEigen<Scalar, StorageIndex>(*cholmodL);
|
||||
internal::cm_free_sparse<StorageIndex>(cholmodL, m_cholmod);
|
||||
|
||||
return L;
|
||||
}
|
||||
|
||||
/** \returns an expression of the factor U (= L^*) */
|
||||
inline MatrixType matrixU() const { return matrixL().adjoint(); }
|
||||
|
||||
protected:
|
||||
void init() {
|
||||
m_cholmod.final_asis = 1;
|
||||
m_cholmod.supernodal = CHOLMOD_SUPERNODAL;
|
||||
}
|
||||
};
|
||||
|
||||
/** \ingroup CholmodSupport_Module
|
||||
* \class CholmodDecomposition
|
||||
* \brief A general Cholesky factorization and solver based on Cholmod
|
||||
*
|
||||
* This class allows to solve for A.X = B sparse linear problems via a LL^T or LDL^T Cholesky factorization
|
||||
* using the Cholmod library. The sparse matrix A must be selfadjoint and positive definite. The vectors or matrices
|
||||
* X and B can be either dense or sparse.
|
||||
*
|
||||
* This variant permits to change the underlying Cholesky method at runtime.
|
||||
* On the other hand, it does not provide access to the result of the factorization.
|
||||
* The default is to let Cholmod automatically choose between a simplicial and supernodal factorization.
|
||||
*
|
||||
* \tparam MatrixType_ the type of the sparse matrix A, it must be a SparseMatrix<>
|
||||
* \tparam UpLo_ the triangular part that will be used for the computations. It can be Lower
|
||||
* or Upper. Default is Lower.
|
||||
*
|
||||
* \implsparsesolverconcept
|
||||
*
|
||||
* This class supports all kind of SparseMatrix<>: row or column major; upper, lower, or both; compressed or non
|
||||
* compressed.
|
||||
*
|
||||
* \warning Only double precision real and complex scalar types are supported by Cholmod.
|
||||
*
|
||||
* \sa \ref TutorialSparseSolverConcept
|
||||
*/
|
||||
template <typename MatrixType_, int UpLo_ = Lower>
|
||||
class CholmodDecomposition : public CholmodBase<MatrixType_, UpLo_, CholmodDecomposition<MatrixType_, UpLo_> > {
|
||||
typedef CholmodBase<MatrixType_, UpLo_, CholmodDecomposition> Base;
|
||||
using Base::m_cholmod;
|
||||
|
||||
public:
|
||||
typedef MatrixType_ MatrixType;
|
||||
|
||||
CholmodDecomposition() : Base() { init(); }
|
||||
|
||||
CholmodDecomposition(const MatrixType& matrix) : Base() {
|
||||
init();
|
||||
this->compute(matrix);
|
||||
}
|
||||
|
||||
~CholmodDecomposition() {}
|
||||
|
||||
void setMode(CholmodMode mode) {
|
||||
switch (mode) {
|
||||
case CholmodAuto:
|
||||
m_cholmod.final_asis = 1;
|
||||
m_cholmod.supernodal = CHOLMOD_AUTO;
|
||||
break;
|
||||
case CholmodSimplicialLLt:
|
||||
m_cholmod.final_asis = 0;
|
||||
m_cholmod.supernodal = CHOLMOD_SIMPLICIAL;
|
||||
m_cholmod.final_ll = 1;
|
||||
break;
|
||||
case CholmodSupernodalLLt:
|
||||
m_cholmod.final_asis = 1;
|
||||
m_cholmod.supernodal = CHOLMOD_SUPERNODAL;
|
||||
break;
|
||||
case CholmodLDLt:
|
||||
m_cholmod.final_asis = 1;
|
||||
m_cholmod.supernodal = CHOLMOD_SIMPLICIAL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void init() {
|
||||
m_cholmod.final_asis = 1;
|
||||
m_cholmod.supernodal = CHOLMOD_AUTO;
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace Eigen
|
||||
|
||||
#endif // EIGEN_CHOLMODSUPPORT_H
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
#ifndef EIGEN_CHOLMODSUPPORT_MODULE_H
|
||||
#error "Please include Eigen/CholmodSupport instead of including headers inside the src directory directly."
|
||||
#endif
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2017 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_ARITHMETIC_SEQUENCE_H
|
||||
#define EIGEN_ARITHMETIC_SEQUENCE_H
|
||||
|
||||
// IWYU pragma: private
|
||||
#include "./InternalHeaderCheck.h"
|
||||
|
||||
namespace Eigen {
|
||||
|
||||
namespace internal {
|
||||
|
||||
// Helper to cleanup the type of the increment:
|
||||
template <typename T>
|
||||
struct cleanup_seq_incr {
|
||||
typedef typename cleanup_index_type<T, DynamicIndex>::type type;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// seq(first,last,incr) and seqN(first,size,incr)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
template <typename FirstType = Index, typename SizeType = Index, typename IncrType = internal::FixedInt<1> >
|
||||
class ArithmeticSequence;
|
||||
|
||||
template <typename FirstType, typename SizeType, typename IncrType>
|
||||
ArithmeticSequence<typename internal::cleanup_index_type<FirstType>::type,
|
||||
typename internal::cleanup_index_type<SizeType>::type,
|
||||
typename internal::cleanup_seq_incr<IncrType>::type>
|
||||
seqN(FirstType first, SizeType size, IncrType incr);
|
||||
|
||||
/** \class ArithmeticSequence
|
||||
* \ingroup Core_Module
|
||||
*
|
||||
* This class represents an arithmetic progression \f$ a_0, a_1, a_2, ..., a_{n-1}\f$ defined by
|
||||
* its \em first value \f$ a_0 \f$, its \em size (aka length) \em n, and the \em increment (aka stride)
|
||||
* that is equal to \f$ a_{i+1}-a_{i}\f$ for any \em i.
|
||||
*
|
||||
* It is internally used as the return type of the Eigen::seq and Eigen::seqN functions, and as the input arguments
|
||||
* of DenseBase::operator()(const RowIndices&, const ColIndices&), and most of the time this is the
|
||||
* only way it is used.
|
||||
*
|
||||
* \tparam FirstType type of the first element, usually an Index,
|
||||
* but internally it can be a symbolic expression
|
||||
* \tparam SizeType type representing the size of the sequence, usually an Index
|
||||
* or a compile time integral constant. Internally, it can also be a symbolic expression
|
||||
* \tparam IncrType type of the increment, can be a runtime Index, or a compile time integral constant (default is
|
||||
* compile-time 1)
|
||||
*
|
||||
* \sa Eigen::seq, Eigen::seqN, DenseBase::operator()(const RowIndices&, const ColIndices&), class IndexedView
|
||||
*/
|
||||
template <typename FirstType, typename SizeType, typename IncrType>
|
||||
class ArithmeticSequence {
|
||||
public:
|
||||
constexpr ArithmeticSequence() = default;
|
||||
constexpr ArithmeticSequence(FirstType first, SizeType size) : m_first(first), m_size(size) {}
|
||||
constexpr ArithmeticSequence(FirstType first, SizeType size, IncrType incr)
|
||||
: m_first(first), m_size(size), m_incr(incr) {}
|
||||
|
||||
enum {
|
||||
// SizeAtCompileTime = internal::get_fixed_value<SizeType>::value,
|
||||
IncrAtCompileTime = internal::get_fixed_value<IncrType, DynamicIndex>::value
|
||||
};
|
||||
|
||||
/** \returns the size, i.e., number of elements, of the sequence */
|
||||
constexpr Index size() const { return m_size; }
|
||||
|
||||
/** \returns the first element \f$ a_0 \f$ in the sequence */
|
||||
constexpr Index first() const { return m_first; }
|
||||
|
||||
/** \returns the value \f$ a_i \f$ at index \a i in the sequence. */
|
||||
constexpr Index operator[](Index i) const { return m_first + i * m_incr; }
|
||||
|
||||
constexpr const FirstType& firstObject() const { return m_first; }
|
||||
constexpr const SizeType& sizeObject() const { return m_size; }
|
||||
constexpr const IncrType& incrObject() const { return m_incr; }
|
||||
|
||||
protected:
|
||||
FirstType m_first;
|
||||
SizeType m_size;
|
||||
IncrType m_incr;
|
||||
|
||||
public:
|
||||
constexpr auto reverse() const -> decltype(Eigen::seqN(m_first + (m_size + fix<-1>()) * m_incr, m_size, -m_incr)) {
|
||||
return seqN(m_first + (m_size + fix<-1>()) * m_incr, m_size, -m_incr);
|
||||
}
|
||||
};
|
||||
|
||||
/** \returns an ArithmeticSequence starting at \a first, of length \a size, and increment \a incr
|
||||
*
|
||||
* \sa seqN(FirstType,SizeType), seq(FirstType,LastType,IncrType) */
|
||||
template <typename FirstType, typename SizeType, typename IncrType>
|
||||
ArithmeticSequence<typename internal::cleanup_index_type<FirstType>::type,
|
||||
typename internal::cleanup_index_type<SizeType>::type,
|
||||
typename internal::cleanup_seq_incr<IncrType>::type>
|
||||
seqN(FirstType first, SizeType size, IncrType incr) {
|
||||
return ArithmeticSequence<typename internal::cleanup_index_type<FirstType>::type,
|
||||
typename internal::cleanup_index_type<SizeType>::type,
|
||||
typename internal::cleanup_seq_incr<IncrType>::type>(first, size, incr);
|
||||
}
|
||||
|
||||
/** \returns an ArithmeticSequence starting at \a first, of length \a size, and unit increment
|
||||
*
|
||||
* \sa seqN(FirstType,SizeType,IncrType), seq(FirstType,LastType) */
|
||||
template <typename FirstType, typename SizeType>
|
||||
ArithmeticSequence<typename internal::cleanup_index_type<FirstType>::type,
|
||||
typename internal::cleanup_index_type<SizeType>::type>
|
||||
seqN(FirstType first, SizeType size) {
|
||||
return ArithmeticSequence<typename internal::cleanup_index_type<FirstType>::type,
|
||||
typename internal::cleanup_index_type<SizeType>::type>(first, size);
|
||||
}
|
||||
|
||||
#ifdef EIGEN_PARSED_BY_DOXYGEN
|
||||
|
||||
/** \returns an ArithmeticSequence starting at \a f, up (or down) to \a l, and with positive (or negative) increment \a
|
||||
* incr
|
||||
*
|
||||
* It is essentially an alias to:
|
||||
* \code
|
||||
* seqN(f, (l-f+incr)/incr, incr);
|
||||
* \endcode
|
||||
*
|
||||
* \sa seqN(FirstType,SizeType,IncrType), seq(FirstType,LastType)
|
||||
*/
|
||||
template <typename FirstType, typename LastType, typename IncrType>
|
||||
auto seq(FirstType f, LastType l, IncrType incr);
|
||||
|
||||
/** \returns an ArithmeticSequence starting at \a f, up (or down) to \a l, and unit increment
|
||||
*
|
||||
* It is essentially an alias to:
|
||||
* \code
|
||||
* seqN(f,l-f+1);
|
||||
* \endcode
|
||||
*
|
||||
* \sa seqN(FirstType,SizeType), seq(FirstType,LastType,IncrType)
|
||||
*/
|
||||
template <typename FirstType, typename LastType>
|
||||
auto seq(FirstType f, LastType l);
|
||||
|
||||
#else // EIGEN_PARSED_BY_DOXYGEN
|
||||
|
||||
template <typename FirstType, typename LastType>
|
||||
auto seq(FirstType f, LastType l)
|
||||
-> decltype(seqN(typename internal::cleanup_index_type<FirstType>::type(f),
|
||||
(typename internal::cleanup_index_type<LastType>::type(l) -
|
||||
typename internal::cleanup_index_type<FirstType>::type(f) + fix<1>()))) {
|
||||
return seqN(typename internal::cleanup_index_type<FirstType>::type(f),
|
||||
(typename internal::cleanup_index_type<LastType>::type(l) -
|
||||
typename internal::cleanup_index_type<FirstType>::type(f) + fix<1>()));
|
||||
}
|
||||
|
||||
template <typename FirstType, typename LastType, typename IncrType>
|
||||
auto seq(FirstType f, LastType l, IncrType incr)
|
||||
-> decltype(seqN(typename internal::cleanup_index_type<FirstType>::type(f),
|
||||
(typename internal::cleanup_index_type<LastType>::type(l) -
|
||||
typename internal::cleanup_index_type<FirstType>::type(f) +
|
||||
typename internal::cleanup_seq_incr<IncrType>::type(incr)) /
|
||||
typename internal::cleanup_seq_incr<IncrType>::type(incr),
|
||||
typename internal::cleanup_seq_incr<IncrType>::type(incr))) {
|
||||
typedef typename internal::cleanup_seq_incr<IncrType>::type CleanedIncrType;
|
||||
return seqN(typename internal::cleanup_index_type<FirstType>::type(f),
|
||||
(typename internal::cleanup_index_type<LastType>::type(l) -
|
||||
typename internal::cleanup_index_type<FirstType>::type(f) + CleanedIncrType(incr)) /
|
||||
CleanedIncrType(incr),
|
||||
CleanedIncrType(incr));
|
||||
}
|
||||
|
||||
#endif // EIGEN_PARSED_BY_DOXYGEN
|
||||
|
||||
namespace placeholders {
|
||||
|
||||
/** \cpp11
|
||||
* \returns a symbolic ArithmeticSequence representing the last \a size elements with increment \a incr.
|
||||
*
|
||||
* It is a shortcut for: \code seqN(last-(size-fix<1>)*incr, size, incr) \endcode
|
||||
* \anchor Eigen_placeholders_lastN
|
||||
* \sa lastN(SizeType), seqN(FirstType,SizeType), seq(FirstType,LastType,IncrType) */
|
||||
template <typename SizeType, typename IncrType>
|
||||
auto lastN(SizeType size, IncrType incr)
|
||||
-> decltype(seqN(Eigen::placeholders::last - (size - fix<1>()) * incr, size, incr)) {
|
||||
return seqN(Eigen::placeholders::last - (size - fix<1>()) * incr, size, incr);
|
||||
}
|
||||
|
||||
/** \cpp11
|
||||
* \returns a symbolic ArithmeticSequence representing the last \a size elements with a unit increment.
|
||||
*
|
||||
* It is a shortcut for: \code seq(last+fix<1>-size, last) \endcode
|
||||
*
|
||||
* \sa lastN(SizeType,IncrType, seqN(FirstType,SizeType), seq(FirstType,LastType) */
|
||||
template <typename SizeType>
|
||||
auto lastN(SizeType size) -> decltype(seqN(Eigen::placeholders::last + fix<1>() - size, size)) {
|
||||
return seqN(Eigen::placeholders::last + fix<1>() - size, size);
|
||||
}
|
||||
|
||||
} // namespace placeholders
|
||||
|
||||
/** \namespace Eigen::indexing
|
||||
* \ingroup Core_Module
|
||||
*
|
||||
* The sole purpose of this namespace is to be able to import all functions
|
||||
* and symbols that are expected to be used within operator() for indexing
|
||||
* and slicing. If you already imported the whole Eigen namespace:
|
||||
* \code using namespace Eigen; \endcode
|
||||
* then you are already all set. Otherwise, if you don't want/cannot import
|
||||
* the whole Eigen namespace, the following line:
|
||||
* \code using namespace Eigen::indexing; \endcode
|
||||
* is equivalent to:
|
||||
* \code
|
||||
using Eigen::fix;
|
||||
using Eigen::seq;
|
||||
using Eigen::seqN;
|
||||
using Eigen::placeholders::all;
|
||||
using Eigen::placeholders::last;
|
||||
using Eigen::placeholders::lastN; // c++11 only
|
||||
using Eigen::placeholders::lastp1;
|
||||
\endcode
|
||||
*/
|
||||
namespace indexing {
|
||||
using Eigen::fix;
|
||||
using Eigen::seq;
|
||||
using Eigen::seqN;
|
||||
using Eigen::placeholders::all;
|
||||
using Eigen::placeholders::last;
|
||||
using Eigen::placeholders::lastN;
|
||||
using Eigen::placeholders::lastp1;
|
||||
} // namespace indexing
|
||||
|
||||
} // end namespace Eigen
|
||||
|
||||
#endif // EIGEN_ARITHMETIC_SEQUENCE_H
|
||||
@@ -0,0 +1,374 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_ARRAY_H
|
||||
#define EIGEN_ARRAY_H
|
||||
|
||||
// IWYU pragma: private
|
||||
#include "./InternalHeaderCheck.h"
|
||||
|
||||
namespace Eigen {
|
||||
|
||||
namespace internal {
|
||||
template <typename Scalar_, int Rows_, int Cols_, int Options_, int MaxRows_, int MaxCols_>
|
||||
struct traits<Array<Scalar_, Rows_, Cols_, Options_, MaxRows_, MaxCols_>>
|
||||
: traits<Matrix<Scalar_, Rows_, Cols_, Options_, MaxRows_, MaxCols_>> {
|
||||
typedef ArrayXpr XprKind;
|
||||
typedef ArrayBase<Array<Scalar_, Rows_, Cols_, Options_, MaxRows_, MaxCols_>> XprBase;
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
/** \class Array
|
||||
* \ingroup Core_Module
|
||||
*
|
||||
* \brief General-purpose arrays with easy API for coefficient-wise operations
|
||||
*
|
||||
* The %Array class is very similar to the Matrix class. It provides
|
||||
* general-purpose one- and two-dimensional arrays. The difference between the
|
||||
* %Array and the %Matrix class is primarily in the API: the API for the
|
||||
* %Array class provides easy access to coefficient-wise operations, while the
|
||||
* API for the %Matrix class provides easy access to linear-algebra
|
||||
* operations.
|
||||
*
|
||||
* See documentation of class Matrix for detailed information on the template parameters
|
||||
* storage layout.
|
||||
*
|
||||
* This class can be extended with the help of the plugin mechanism described on the page
|
||||
* \ref TopicCustomizing_Plugins by defining the preprocessor symbol \c EIGEN_ARRAY_PLUGIN.
|
||||
*
|
||||
* \sa \blank \ref TutorialArrayClass, \ref TopicClassHierarchy
|
||||
*/
|
||||
template <typename Scalar_, int Rows_, int Cols_, int Options_, int MaxRows_, int MaxCols_>
|
||||
class Array : public PlainObjectBase<Array<Scalar_, Rows_, Cols_, Options_, MaxRows_, MaxCols_>> {
|
||||
public:
|
||||
typedef PlainObjectBase<Array> Base;
|
||||
EIGEN_DENSE_PUBLIC_INTERFACE(Array)
|
||||
|
||||
enum { Options = Options_ };
|
||||
typedef typename Base::PlainObject PlainObject;
|
||||
|
||||
protected:
|
||||
template <typename Derived, typename OtherDerived, bool IsVector>
|
||||
friend struct internal::conservative_resize_like_impl;
|
||||
|
||||
using Base::m_storage;
|
||||
|
||||
public:
|
||||
using Base::base;
|
||||
using Base::coeff;
|
||||
using Base::coeffRef;
|
||||
|
||||
/**
|
||||
* The usage of
|
||||
* using Base::operator=;
|
||||
* fails on MSVC. Since the code below is working with GCC and MSVC, we skipped
|
||||
* the usage of 'using'. This should be done only for operator=.
|
||||
*/
|
||||
template <typename OtherDerived>
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Array& operator=(const EigenBase<OtherDerived>& other) {
|
||||
return Base::operator=(other);
|
||||
}
|
||||
|
||||
/** Set all the entries to \a value.
|
||||
* \sa DenseBase::setConstant(), DenseBase::fill()
|
||||
*/
|
||||
/* This overload is needed because the usage of
|
||||
* using Base::operator=;
|
||||
* fails on MSVC. Since the code below is working with GCC and MSVC, we skipped
|
||||
* the usage of 'using'. This should be done only for operator=.
|
||||
*/
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Array& operator=(const Scalar& value) {
|
||||
Base::setConstant(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Copies the value of the expression \a other into \c *this with automatic resizing.
|
||||
*
|
||||
* *this might be resized to match the dimensions of \a other. If *this was a null matrix (not already initialized),
|
||||
* it will be initialized.
|
||||
*
|
||||
* Note that copying a row-vector into a vector (and conversely) is allowed.
|
||||
* The resizing, if any, is then done in the appropriate way so that row-vectors
|
||||
* remain row-vectors and vectors remain vectors.
|
||||
*/
|
||||
template <typename OtherDerived>
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Array& operator=(const DenseBase<OtherDerived>& other) {
|
||||
return Base::_set(other);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Assigns arrays to each other.
|
||||
*
|
||||
* \note This is a special case of the templated operator=. Its purpose is
|
||||
* to prevent a default operator= from hiding the templated operator=.
|
||||
*
|
||||
* \callgraph
|
||||
*/
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Array& operator=(const Array& other) { return Base::_set(other); }
|
||||
|
||||
/** Default constructor.
|
||||
*
|
||||
* For fixed-size matrices, does nothing.
|
||||
*
|
||||
* For dynamic-size matrices, creates an empty matrix of size 0. Does not allocate any array. Such a matrix
|
||||
* is called a null matrix. This constructor is the unique way to create null matrices: resizing
|
||||
* a matrix to 0 is not supported.
|
||||
*
|
||||
* \sa resize(Index,Index)
|
||||
*/
|
||||
#ifdef EIGEN_INITIALIZE_COEFFS
|
||||
EIGEN_DEVICE_FUNC constexpr Array() : Base() { EIGEN_INITIALIZE_COEFFS_IF_THAT_OPTION_IS_ENABLED }
|
||||
#else
|
||||
EIGEN_DEVICE_FUNC constexpr Array() = default;
|
||||
#endif
|
||||
/** \brief Move constructor */
|
||||
EIGEN_DEVICE_FUNC constexpr Array(Array&&) = default;
|
||||
EIGEN_DEVICE_FUNC Array& operator=(Array&& other) noexcept(std::is_nothrow_move_assignable<Scalar>::value) {
|
||||
Base::operator=(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** \brief Construct a row of column vector with fixed size from an arbitrary number of coefficients.
|
||||
*
|
||||
* \only_for_vectors
|
||||
*
|
||||
* This constructor is for 1D array or vectors with more than 4 coefficients.
|
||||
*
|
||||
* \warning To construct a column (resp. row) vector of fixed length, the number of values passed to this
|
||||
* constructor must match the fixed number of rows (resp. columns) of \c *this.
|
||||
*
|
||||
*
|
||||
* Example: \include Array_variadic_ctor_cxx11.cpp
|
||||
* Output: \verbinclude Array_variadic_ctor_cxx11.out
|
||||
*
|
||||
* \sa Array(const std::initializer_list<std::initializer_list<Scalar>>&)
|
||||
* \sa Array(const Scalar&), Array(const Scalar&,const Scalar&)
|
||||
*/
|
||||
template <typename... ArgTypes>
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Array(const Scalar& a0, const Scalar& a1, const Scalar& a2, const Scalar& a3,
|
||||
const ArgTypes&... args)
|
||||
: Base(a0, a1, a2, a3, args...) {}
|
||||
|
||||
/** \brief Constructs an array and initializes it from the coefficients given as initializer-lists grouped by row.
|
||||
* \cpp11
|
||||
*
|
||||
* In the general case, the constructor takes a list of rows, each row being represented as a list of coefficients:
|
||||
*
|
||||
* Example: \include Array_initializer_list_23_cxx11.cpp
|
||||
* Output: \verbinclude Array_initializer_list_23_cxx11.out
|
||||
*
|
||||
* Each of the inner initializer lists must contain the exact same number of elements, otherwise an assertion is
|
||||
* triggered.
|
||||
*
|
||||
* In the case of a compile-time column 1D array, implicit transposition from a single row is allowed.
|
||||
* Therefore <code> Array<int,Dynamic,1>{{1,2,3,4,5}}</code> is legal and the more verbose syntax
|
||||
* <code>Array<int,Dynamic,1>{{1},{2},{3},{4},{5}}</code> can be avoided:
|
||||
*
|
||||
* Example: \include Array_initializer_list_vector_cxx11.cpp
|
||||
* Output: \verbinclude Array_initializer_list_vector_cxx11.out
|
||||
*
|
||||
* In the case of fixed-sized arrays, the initializer list sizes must exactly match the array sizes,
|
||||
* and implicit transposition is allowed for compile-time 1D arrays only.
|
||||
*
|
||||
* \sa Array(const Scalar& a0, const Scalar& a1, const Scalar& a2, const Scalar& a3, const ArgTypes&... args)
|
||||
*/
|
||||
EIGEN_DEVICE_FUNC constexpr Array(const std::initializer_list<std::initializer_list<Scalar>>& list) : Base(list) {}
|
||||
|
||||
#ifndef EIGEN_PARSED_BY_DOXYGEN
|
||||
template <typename T>
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit Array(const T& x) {
|
||||
Base::template _init1<T>(x);
|
||||
}
|
||||
|
||||
template <typename T0, typename T1>
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Array(const T0& val0, const T1& val1) {
|
||||
this->template _init2<T0, T1>(val0, val1);
|
||||
}
|
||||
|
||||
#else
|
||||
/** \brief Constructs a fixed-sized array initialized with coefficients starting at \a data */
|
||||
EIGEN_DEVICE_FUNC explicit Array(const Scalar* data);
|
||||
/** Constructs a vector or row-vector with given dimension. \only_for_vectors
|
||||
*
|
||||
* Note that this is only useful for dynamic-size vectors. For fixed-size vectors,
|
||||
* it is redundant to pass the dimension here, so it makes more sense to use the default
|
||||
* constructor Array() instead.
|
||||
*/
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit Array(Index dim);
|
||||
/** constructs an initialized 1x1 Array with the given coefficient
|
||||
* \sa const Scalar& a0, const Scalar& a1, const Scalar& a2, const Scalar& a3, const ArgTypes&... args */
|
||||
Array(const Scalar& value);
|
||||
/** constructs an uninitialized array with \a rows rows and \a cols columns.
|
||||
*
|
||||
* This is useful for dynamic-size arrays. For fixed-size arrays,
|
||||
* it is redundant to pass these parameters, so one should use the default constructor
|
||||
* Array() instead. */
|
||||
Array(Index rows, Index cols);
|
||||
/** constructs an initialized 2D vector with given coefficients
|
||||
* \sa Array(const Scalar& a0, const Scalar& a1, const Scalar& a2, const Scalar& a3, const ArgTypes&... args) */
|
||||
Array(const Scalar& val0, const Scalar& val1);
|
||||
#endif // end EIGEN_PARSED_BY_DOXYGEN
|
||||
|
||||
/** constructs an initialized 3D vector with given coefficients
|
||||
* \sa Array(const Scalar& a0, const Scalar& a1, const Scalar& a2, const Scalar& a3, const ArgTypes&... args)
|
||||
*/
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Array(const Scalar& val0, const Scalar& val1, const Scalar& val2) {
|
||||
EIGEN_STATIC_ASSERT_VECTOR_SPECIFIC_SIZE(Array, 3)
|
||||
m_storage.data()[0] = val0;
|
||||
m_storage.data()[1] = val1;
|
||||
m_storage.data()[2] = val2;
|
||||
}
|
||||
/** constructs an initialized 4D vector with given coefficients
|
||||
* \sa Array(const Scalar& a0, const Scalar& a1, const Scalar& a2, const Scalar& a3, const ArgTypes&... args)
|
||||
*/
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Array(const Scalar& val0, const Scalar& val1, const Scalar& val2,
|
||||
const Scalar& val3) {
|
||||
EIGEN_STATIC_ASSERT_VECTOR_SPECIFIC_SIZE(Array, 4)
|
||||
m_storage.data()[0] = val0;
|
||||
m_storage.data()[1] = val1;
|
||||
m_storage.data()[2] = val2;
|
||||
m_storage.data()[3] = val3;
|
||||
}
|
||||
|
||||
/** Copy constructor */
|
||||
EIGEN_DEVICE_FUNC constexpr Array(const Array&) = default;
|
||||
|
||||
private:
|
||||
struct PrivateType {};
|
||||
|
||||
public:
|
||||
/** \sa MatrixBase::operator=(const EigenBase<OtherDerived>&) */
|
||||
template <typename OtherDerived>
|
||||
EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Array(
|
||||
const EigenBase<OtherDerived>& other,
|
||||
std::enable_if_t<internal::is_convertible<typename OtherDerived::Scalar, Scalar>::value, PrivateType> =
|
||||
PrivateType())
|
||||
: Base(other.derived()) {}
|
||||
|
||||
EIGEN_DEVICE_FUNC constexpr Index innerStride() const noexcept { return 1; }
|
||||
EIGEN_DEVICE_FUNC constexpr Index outerStride() const noexcept { return this->innerSize(); }
|
||||
|
||||
#ifdef EIGEN_ARRAY_PLUGIN
|
||||
#include EIGEN_ARRAY_PLUGIN
|
||||
#endif
|
||||
|
||||
private:
|
||||
template <typename MatrixType, typename OtherDerived, bool SwapPointers>
|
||||
friend struct internal::matrix_swap_impl;
|
||||
};
|
||||
|
||||
/** \defgroup arraytypedefs Global array typedefs
|
||||
* \ingroup Core_Module
|
||||
*
|
||||
* %Eigen defines several typedef shortcuts for most common 1D and 2D array types.
|
||||
*
|
||||
* The general patterns are the following:
|
||||
*
|
||||
* \c ArrayRowsColsType where \c Rows and \c Cols can be \c 2,\c 3,\c 4 for fixed size square matrices or \c X for
|
||||
* dynamic size, and where \c Type can be \c i for integer, \c f for float, \c d for double, \c cf for complex float, \c
|
||||
* cd for complex double.
|
||||
*
|
||||
* For example, \c Array33d is a fixed-size 3x3 array type of doubles, and \c ArrayXXf is a dynamic-size matrix of
|
||||
* floats.
|
||||
*
|
||||
* There are also \c ArraySizeType which are self-explanatory. For example, \c Array4cf is
|
||||
* a fixed-size 1D array of 4 complex floats.
|
||||
*
|
||||
* With \cpp11, template alias are also defined for common sizes.
|
||||
* They follow the same pattern as above except that the scalar type suffix is replaced by a
|
||||
* template parameter, i.e.:
|
||||
* - `ArrayRowsCols<Type>` where `Rows` and `Cols` can be \c 2,\c 3,\c 4, or \c X for fixed or dynamic size.
|
||||
* - `ArraySize<Type>` where `Size` can be \c 2,\c 3,\c 4 or \c X for fixed or dynamic size 1D arrays.
|
||||
*
|
||||
* \sa class Array
|
||||
*/
|
||||
|
||||
#define EIGEN_MAKE_ARRAY_TYPEDEFS(Type, TypeSuffix, Size, SizeSuffix) \
|
||||
/** \ingroup arraytypedefs */ \
|
||||
typedef Array<Type, Size, Size> Array##SizeSuffix##SizeSuffix##TypeSuffix; \
|
||||
/** \ingroup arraytypedefs */ \
|
||||
typedef Array<Type, Size, 1> Array##SizeSuffix##TypeSuffix;
|
||||
|
||||
#define EIGEN_MAKE_ARRAY_FIXED_TYPEDEFS(Type, TypeSuffix, Size) \
|
||||
/** \ingroup arraytypedefs */ \
|
||||
typedef Array<Type, Size, Dynamic> Array##Size##X##TypeSuffix; \
|
||||
/** \ingroup arraytypedefs */ \
|
||||
typedef Array<Type, Dynamic, Size> Array##X##Size##TypeSuffix;
|
||||
|
||||
#define EIGEN_MAKE_ARRAY_TYPEDEFS_ALL_SIZES(Type, TypeSuffix) \
|
||||
EIGEN_MAKE_ARRAY_TYPEDEFS(Type, TypeSuffix, 2, 2) \
|
||||
EIGEN_MAKE_ARRAY_TYPEDEFS(Type, TypeSuffix, 3, 3) \
|
||||
EIGEN_MAKE_ARRAY_TYPEDEFS(Type, TypeSuffix, 4, 4) \
|
||||
EIGEN_MAKE_ARRAY_TYPEDEFS(Type, TypeSuffix, Dynamic, X) \
|
||||
EIGEN_MAKE_ARRAY_FIXED_TYPEDEFS(Type, TypeSuffix, 2) \
|
||||
EIGEN_MAKE_ARRAY_FIXED_TYPEDEFS(Type, TypeSuffix, 3) \
|
||||
EIGEN_MAKE_ARRAY_FIXED_TYPEDEFS(Type, TypeSuffix, 4)
|
||||
|
||||
EIGEN_MAKE_ARRAY_TYPEDEFS_ALL_SIZES(int, i)
|
||||
EIGEN_MAKE_ARRAY_TYPEDEFS_ALL_SIZES(float, f)
|
||||
EIGEN_MAKE_ARRAY_TYPEDEFS_ALL_SIZES(double, d)
|
||||
EIGEN_MAKE_ARRAY_TYPEDEFS_ALL_SIZES(std::complex<float>, cf)
|
||||
EIGEN_MAKE_ARRAY_TYPEDEFS_ALL_SIZES(std::complex<double>, cd)
|
||||
|
||||
#undef EIGEN_MAKE_ARRAY_TYPEDEFS_ALL_SIZES
|
||||
#undef EIGEN_MAKE_ARRAY_TYPEDEFS
|
||||
#undef EIGEN_MAKE_ARRAY_FIXED_TYPEDEFS
|
||||
|
||||
#define EIGEN_MAKE_ARRAY_TYPEDEFS(Size, SizeSuffix) \
|
||||
/** \ingroup arraytypedefs */ \
|
||||
/** \brief \cpp11 */ \
|
||||
template <typename Type> \
|
||||
using Array##SizeSuffix##SizeSuffix = Array<Type, Size, Size>; \
|
||||
/** \ingroup arraytypedefs */ \
|
||||
/** \brief \cpp11 */ \
|
||||
template <typename Type> \
|
||||
using Array##SizeSuffix = Array<Type, Size, 1>;
|
||||
|
||||
#define EIGEN_MAKE_ARRAY_FIXED_TYPEDEFS(Size) \
|
||||
/** \ingroup arraytypedefs */ \
|
||||
/** \brief \cpp11 */ \
|
||||
template <typename Type> \
|
||||
using Array##Size##X = Array<Type, Size, Dynamic>; \
|
||||
/** \ingroup arraytypedefs */ \
|
||||
/** \brief \cpp11 */ \
|
||||
template <typename Type> \
|
||||
using Array##X##Size = Array<Type, Dynamic, Size>;
|
||||
|
||||
EIGEN_MAKE_ARRAY_TYPEDEFS(2, 2)
|
||||
EIGEN_MAKE_ARRAY_TYPEDEFS(3, 3)
|
||||
EIGEN_MAKE_ARRAY_TYPEDEFS(4, 4)
|
||||
EIGEN_MAKE_ARRAY_TYPEDEFS(Dynamic, X)
|
||||
EIGEN_MAKE_ARRAY_FIXED_TYPEDEFS(2)
|
||||
EIGEN_MAKE_ARRAY_FIXED_TYPEDEFS(3)
|
||||
EIGEN_MAKE_ARRAY_FIXED_TYPEDEFS(4)
|
||||
|
||||
#undef EIGEN_MAKE_ARRAY_TYPEDEFS
|
||||
#undef EIGEN_MAKE_ARRAY_FIXED_TYPEDEFS
|
||||
|
||||
#define EIGEN_USING_ARRAY_TYPEDEFS_FOR_TYPE_AND_SIZE(TypeSuffix, SizeSuffix) \
|
||||
using Eigen::Matrix##SizeSuffix##TypeSuffix; \
|
||||
using Eigen::Vector##SizeSuffix##TypeSuffix; \
|
||||
using Eigen::RowVector##SizeSuffix##TypeSuffix;
|
||||
|
||||
#define EIGEN_USING_ARRAY_TYPEDEFS_FOR_TYPE(TypeSuffix) \
|
||||
EIGEN_USING_ARRAY_TYPEDEFS_FOR_TYPE_AND_SIZE(TypeSuffix, 2) \
|
||||
EIGEN_USING_ARRAY_TYPEDEFS_FOR_TYPE_AND_SIZE(TypeSuffix, 3) \
|
||||
EIGEN_USING_ARRAY_TYPEDEFS_FOR_TYPE_AND_SIZE(TypeSuffix, 4) \
|
||||
EIGEN_USING_ARRAY_TYPEDEFS_FOR_TYPE_AND_SIZE(TypeSuffix, X)
|
||||
|
||||
#define EIGEN_USING_ARRAY_TYPEDEFS \
|
||||
EIGEN_USING_ARRAY_TYPEDEFS_FOR_TYPE(i) \
|
||||
EIGEN_USING_ARRAY_TYPEDEFS_FOR_TYPE(f) \
|
||||
EIGEN_USING_ARRAY_TYPEDEFS_FOR_TYPE(d) \
|
||||
EIGEN_USING_ARRAY_TYPEDEFS_FOR_TYPE(cf) \
|
||||
EIGEN_USING_ARRAY_TYPEDEFS_FOR_TYPE(cd)
|
||||
|
||||
} // end namespace Eigen
|
||||
|
||||
#endif // EIGEN_ARRAY_H
|
||||
@@ -0,0 +1,210 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_ARRAYBASE_H
|
||||
#define EIGEN_ARRAYBASE_H
|
||||
|
||||
// IWYU pragma: private
|
||||
#include "./InternalHeaderCheck.h"
|
||||
|
||||
namespace Eigen {
|
||||
|
||||
template <typename ExpressionType>
|
||||
class MatrixWrapper;
|
||||
|
||||
/** \class ArrayBase
|
||||
* \ingroup Core_Module
|
||||
*
|
||||
* \brief Base class for all 1D and 2D array, and related expressions
|
||||
*
|
||||
* An array is similar to a dense vector or matrix. While matrices are mathematical
|
||||
* objects with well defined linear algebra operators, an array is just a collection
|
||||
* of scalar values arranged in a one or two dimensional fashion. As the main consequence,
|
||||
* all operations applied to an array are performed coefficient wise. Furthermore,
|
||||
* arrays support scalar math functions of the c++ standard library (e.g., std::sin(x)), and convenient
|
||||
* constructors allowing to easily write generic code working for both scalar values
|
||||
* and arrays.
|
||||
*
|
||||
* This class is the base that is inherited by all array expression types.
|
||||
*
|
||||
* \tparam Derived is the derived type, e.g., an array or an expression type.
|
||||
*
|
||||
* This class can be extended with the help of the plugin mechanism described on the page
|
||||
* \ref TopicCustomizing_Plugins by defining the preprocessor symbol \c EIGEN_ARRAYBASE_PLUGIN.
|
||||
*
|
||||
* \sa class MatrixBase, \ref TopicClassHierarchy
|
||||
*/
|
||||
template <typename Derived>
|
||||
class ArrayBase : public DenseBase<Derived> {
|
||||
public:
|
||||
#ifndef EIGEN_PARSED_BY_DOXYGEN
|
||||
/** The base class for a given storage type. */
|
||||
typedef ArrayBase StorageBaseType;
|
||||
|
||||
typedef ArrayBase Eigen_BaseClassForSpecializationOfGlobalMathFuncImpl;
|
||||
|
||||
typedef typename internal::traits<Derived>::StorageKind StorageKind;
|
||||
typedef typename internal::traits<Derived>::Scalar Scalar;
|
||||
typedef typename internal::packet_traits<Scalar>::type PacketScalar;
|
||||
typedef typename NumTraits<Scalar>::Real RealScalar;
|
||||
|
||||
typedef DenseBase<Derived> Base;
|
||||
using Base::ColsAtCompileTime;
|
||||
using Base::Flags;
|
||||
using Base::IsVectorAtCompileTime;
|
||||
using Base::MaxColsAtCompileTime;
|
||||
using Base::MaxRowsAtCompileTime;
|
||||
using Base::MaxSizeAtCompileTime;
|
||||
using Base::RowsAtCompileTime;
|
||||
using Base::SizeAtCompileTime;
|
||||
|
||||
using Base::coeff;
|
||||
using Base::coeffRef;
|
||||
using Base::cols;
|
||||
using Base::const_cast_derived;
|
||||
using Base::derived;
|
||||
using Base::lazyAssign;
|
||||
using Base::rows;
|
||||
using Base::size;
|
||||
using Base::operator-;
|
||||
using Base::operator=;
|
||||
using Base::operator+=;
|
||||
using Base::operator-=;
|
||||
using Base::operator*=;
|
||||
using Base::operator/=;
|
||||
|
||||
typedef typename Base::CoeffReturnType CoeffReturnType;
|
||||
|
||||
typedef typename Base::PlainObject PlainObject;
|
||||
|
||||
/** \internal Represents a matrix with all coefficients equal to one another*/
|
||||
typedef CwiseNullaryOp<internal::scalar_constant_op<Scalar>, PlainObject> ConstantReturnType;
|
||||
#endif // not EIGEN_PARSED_BY_DOXYGEN
|
||||
|
||||
#define EIGEN_CURRENT_STORAGE_BASE_CLASS Eigen::ArrayBase
|
||||
#define EIGEN_DOC_UNARY_ADDONS(X, Y)
|
||||
#include "../plugins/MatrixCwiseUnaryOps.inc"
|
||||
#include "../plugins/ArrayCwiseUnaryOps.inc"
|
||||
#include "../plugins/CommonCwiseBinaryOps.inc"
|
||||
#include "../plugins/MatrixCwiseBinaryOps.inc"
|
||||
#include "../plugins/ArrayCwiseBinaryOps.inc"
|
||||
#ifdef EIGEN_ARRAYBASE_PLUGIN
|
||||
#include EIGEN_ARRAYBASE_PLUGIN
|
||||
#endif
|
||||
#undef EIGEN_CURRENT_STORAGE_BASE_CLASS
|
||||
#undef EIGEN_DOC_UNARY_ADDONS
|
||||
|
||||
/** Special case of the template operator=, in order to prevent the compiler
|
||||
* from generating a default operator= (issue hit with g++ 4.1)
|
||||
*/
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& operator=(const ArrayBase& other) {
|
||||
internal::call_assignment(derived(), other.derived());
|
||||
return derived();
|
||||
}
|
||||
|
||||
/** Set all the entries to \a value.
|
||||
* \sa DenseBase::setConstant(), DenseBase::fill() */
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& operator=(const Scalar& value) {
|
||||
Base::setConstant(value);
|
||||
return derived();
|
||||
}
|
||||
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& operator+=(const Scalar& other) {
|
||||
internal::call_assignment(this->derived(), PlainObject::Constant(rows(), cols(), other),
|
||||
internal::add_assign_op<Scalar, Scalar>());
|
||||
return derived();
|
||||
}
|
||||
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& operator-=(const Scalar& other) {
|
||||
internal::call_assignment(this->derived(), PlainObject::Constant(rows(), cols(), other),
|
||||
internal::sub_assign_op<Scalar, Scalar>());
|
||||
return derived();
|
||||
}
|
||||
|
||||
/** replaces \c *this by \c *this + \a other.
|
||||
*
|
||||
* \returns a reference to \c *this
|
||||
*/
|
||||
template <typename OtherDerived>
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& operator+=(const ArrayBase<OtherDerived>& other) {
|
||||
call_assignment(derived(), other.derived(), internal::add_assign_op<Scalar, typename OtherDerived::Scalar>());
|
||||
return derived();
|
||||
}
|
||||
|
||||
/** replaces \c *this by \c *this - \a other.
|
||||
*
|
||||
* \returns a reference to \c *this
|
||||
*/
|
||||
template <typename OtherDerived>
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& operator-=(const ArrayBase<OtherDerived>& other) {
|
||||
call_assignment(derived(), other.derived(), internal::sub_assign_op<Scalar, typename OtherDerived::Scalar>());
|
||||
return derived();
|
||||
}
|
||||
|
||||
/** replaces \c *this by \c *this * \a other coefficient wise.
|
||||
*
|
||||
* \returns a reference to \c *this
|
||||
*/
|
||||
template <typename OtherDerived>
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& operator*=(const ArrayBase<OtherDerived>& other) {
|
||||
call_assignment(derived(), other.derived(), internal::mul_assign_op<Scalar, typename OtherDerived::Scalar>());
|
||||
return derived();
|
||||
}
|
||||
|
||||
/** replaces \c *this by \c *this / \a other coefficient wise.
|
||||
*
|
||||
* \returns a reference to \c *this
|
||||
*/
|
||||
template <typename OtherDerived>
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& operator/=(const ArrayBase<OtherDerived>& other) {
|
||||
call_assignment(derived(), other.derived(), internal::div_assign_op<Scalar, typename OtherDerived::Scalar>());
|
||||
return derived();
|
||||
}
|
||||
|
||||
public:
|
||||
EIGEN_DEVICE_FUNC constexpr ArrayBase<Derived>& array() { return *this; }
|
||||
EIGEN_DEVICE_FUNC constexpr const ArrayBase<Derived>& array() const { return *this; }
|
||||
|
||||
/** \returns an \link Eigen::MatrixBase Matrix \endlink expression of this array
|
||||
* \sa MatrixBase::array() */
|
||||
EIGEN_DEVICE_FUNC constexpr MatrixWrapper<Derived> matrix() { return MatrixWrapper<Derived>(derived()); }
|
||||
EIGEN_DEVICE_FUNC constexpr const MatrixWrapper<const Derived> matrix() const {
|
||||
return MatrixWrapper<const Derived>(derived());
|
||||
}
|
||||
|
||||
protected:
|
||||
EIGEN_DEFAULT_COPY_CONSTRUCTOR(ArrayBase)
|
||||
EIGEN_DEFAULT_EMPTY_CONSTRUCTOR_AND_DESTRUCTOR(ArrayBase)
|
||||
|
||||
private:
|
||||
explicit ArrayBase(Index);
|
||||
ArrayBase(Index, Index);
|
||||
template <typename OtherDerived>
|
||||
explicit ArrayBase(const ArrayBase<OtherDerived>&);
|
||||
|
||||
protected:
|
||||
// mixing arrays and matrices is not legal
|
||||
template <typename OtherDerived>
|
||||
Derived& operator+=(const MatrixBase<OtherDerived>&) {
|
||||
EIGEN_STATIC_ASSERT(std::ptrdiff_t(sizeof(typename OtherDerived::Scalar)) == -1,
|
||||
YOU_CANNOT_MIX_ARRAYS_AND_MATRICES);
|
||||
return *this;
|
||||
}
|
||||
// mixing arrays and matrices is not legal
|
||||
template <typename OtherDerived>
|
||||
Derived& operator-=(const MatrixBase<OtherDerived>&) {
|
||||
EIGEN_STATIC_ASSERT(std::ptrdiff_t(sizeof(typename OtherDerived::Scalar)) == -1,
|
||||
YOU_CANNOT_MIX_ARRAYS_AND_MATRICES);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace Eigen
|
||||
|
||||
#endif // EIGEN_ARRAYBASE_H
|
||||
@@ -0,0 +1,166 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2009-2010 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_ARRAYWRAPPER_H
|
||||
#define EIGEN_ARRAYWRAPPER_H
|
||||
|
||||
// IWYU pragma: private
|
||||
#include "./InternalHeaderCheck.h"
|
||||
|
||||
namespace Eigen {
|
||||
|
||||
/** \class ArrayWrapper
|
||||
* \ingroup Core_Module
|
||||
*
|
||||
* \brief Expression of a mathematical vector or matrix as an array object
|
||||
*
|
||||
* This class is the return type of MatrixBase::array(), and most of the time
|
||||
* this is the only way it is used.
|
||||
*
|
||||
* \sa MatrixBase::array(), class MatrixWrapper
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
template <typename ExpressionType>
|
||||
struct traits<ArrayWrapper<ExpressionType> > : public traits<remove_all_t<typename ExpressionType::Nested> > {
|
||||
typedef ArrayXpr XprKind;
|
||||
// Let's remove NestByRefBit
|
||||
enum {
|
||||
Flags0 = traits<remove_all_t<typename ExpressionType::Nested> >::Flags,
|
||||
LvalueBitFlag = is_lvalue<ExpressionType>::value ? LvalueBit : 0,
|
||||
Flags = (Flags0 & ~(NestByRefBit | LvalueBit)) | LvalueBitFlag
|
||||
};
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
template <typename ExpressionType>
|
||||
class ArrayWrapper : public ArrayBase<ArrayWrapper<ExpressionType> > {
|
||||
public:
|
||||
typedef ArrayBase<ArrayWrapper> Base;
|
||||
EIGEN_DENSE_PUBLIC_INTERFACE(ArrayWrapper)
|
||||
EIGEN_INHERIT_ASSIGNMENT_OPERATORS(ArrayWrapper)
|
||||
typedef internal::remove_all_t<ExpressionType> NestedExpression;
|
||||
|
||||
typedef std::conditional_t<internal::is_lvalue<ExpressionType>::value, Scalar, const Scalar>
|
||||
ScalarWithConstIfNotLvalue;
|
||||
|
||||
typedef typename internal::ref_selector<ExpressionType>::non_const_type NestedExpressionType;
|
||||
|
||||
using Base::coeffRef;
|
||||
|
||||
EIGEN_DEVICE_FUNC constexpr explicit EIGEN_STRONG_INLINE ArrayWrapper(ExpressionType& matrix)
|
||||
: m_expression(matrix) {}
|
||||
|
||||
EIGEN_DEVICE_FUNC constexpr Index rows() const noexcept { return m_expression.rows(); }
|
||||
EIGEN_DEVICE_FUNC constexpr Index cols() const noexcept { return m_expression.cols(); }
|
||||
EIGEN_DEVICE_FUNC constexpr Index outerStride() const noexcept { return m_expression.outerStride(); }
|
||||
EIGEN_DEVICE_FUNC constexpr Index innerStride() const noexcept { return m_expression.innerStride(); }
|
||||
|
||||
EIGEN_DEVICE_FUNC constexpr ScalarWithConstIfNotLvalue* data() { return m_expression.data(); }
|
||||
EIGEN_DEVICE_FUNC constexpr const Scalar* data() const { return m_expression.data(); }
|
||||
|
||||
EIGEN_DEVICE_FUNC inline const Scalar& coeffRef(Index rowId, Index colId) const {
|
||||
return m_expression.coeffRef(rowId, colId);
|
||||
}
|
||||
|
||||
EIGEN_DEVICE_FUNC inline const Scalar& coeffRef(Index index) const { return m_expression.coeffRef(index); }
|
||||
|
||||
template <typename Dest>
|
||||
EIGEN_DEVICE_FUNC inline void evalTo(Dest& dst) const {
|
||||
dst = m_expression;
|
||||
}
|
||||
|
||||
EIGEN_DEVICE_FUNC constexpr const internal::remove_all_t<NestedExpressionType>& nestedExpression() const {
|
||||
return m_expression;
|
||||
}
|
||||
|
||||
/** Forwards the resizing request to the nested expression
|
||||
* \sa DenseBase::resize(Index) */
|
||||
EIGEN_DEVICE_FUNC void resize(Index newSize) { m_expression.resize(newSize); }
|
||||
/** Forwards the resizing request to the nested expression
|
||||
* \sa DenseBase::resize(Index,Index)*/
|
||||
EIGEN_DEVICE_FUNC void resize(Index rows, Index cols) { m_expression.resize(rows, cols); }
|
||||
|
||||
protected:
|
||||
NestedExpressionType m_expression;
|
||||
};
|
||||
|
||||
/** \class MatrixWrapper
|
||||
* \ingroup Core_Module
|
||||
*
|
||||
* \brief Expression of an array as a mathematical vector or matrix
|
||||
*
|
||||
* This class is the return type of ArrayBase::matrix(), and most of the time
|
||||
* this is the only way it is used.
|
||||
*
|
||||
* \sa MatrixBase::matrix(), class ArrayWrapper
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
template <typename ExpressionType>
|
||||
struct traits<MatrixWrapper<ExpressionType> > : public traits<remove_all_t<typename ExpressionType::Nested> > {
|
||||
typedef MatrixXpr XprKind;
|
||||
// Let's remove NestByRefBit
|
||||
enum {
|
||||
Flags0 = traits<remove_all_t<typename ExpressionType::Nested> >::Flags,
|
||||
LvalueBitFlag = is_lvalue<ExpressionType>::value ? LvalueBit : 0,
|
||||
Flags = (Flags0 & ~(NestByRefBit | LvalueBit)) | LvalueBitFlag
|
||||
};
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
template <typename ExpressionType>
|
||||
class MatrixWrapper : public MatrixBase<MatrixWrapper<ExpressionType> > {
|
||||
public:
|
||||
typedef MatrixBase<MatrixWrapper<ExpressionType> > Base;
|
||||
EIGEN_DENSE_PUBLIC_INTERFACE(MatrixWrapper)
|
||||
EIGEN_INHERIT_ASSIGNMENT_OPERATORS(MatrixWrapper)
|
||||
typedef internal::remove_all_t<ExpressionType> NestedExpression;
|
||||
|
||||
typedef std::conditional_t<internal::is_lvalue<ExpressionType>::value, Scalar, const Scalar>
|
||||
ScalarWithConstIfNotLvalue;
|
||||
|
||||
typedef typename internal::ref_selector<ExpressionType>::non_const_type NestedExpressionType;
|
||||
|
||||
using Base::coeffRef;
|
||||
|
||||
EIGEN_DEVICE_FUNC constexpr explicit inline MatrixWrapper(ExpressionType& matrix) : m_expression(matrix) {}
|
||||
|
||||
EIGEN_DEVICE_FUNC constexpr Index rows() const noexcept { return m_expression.rows(); }
|
||||
EIGEN_DEVICE_FUNC constexpr Index cols() const noexcept { return m_expression.cols(); }
|
||||
EIGEN_DEVICE_FUNC constexpr Index outerStride() const noexcept { return m_expression.outerStride(); }
|
||||
EIGEN_DEVICE_FUNC constexpr Index innerStride() const noexcept { return m_expression.innerStride(); }
|
||||
|
||||
EIGEN_DEVICE_FUNC constexpr ScalarWithConstIfNotLvalue* data() { return m_expression.data(); }
|
||||
EIGEN_DEVICE_FUNC constexpr const Scalar* data() const { return m_expression.data(); }
|
||||
|
||||
EIGEN_DEVICE_FUNC inline const Scalar& coeffRef(Index rowId, Index colId) const {
|
||||
return m_expression.derived().coeffRef(rowId, colId);
|
||||
}
|
||||
|
||||
EIGEN_DEVICE_FUNC inline const Scalar& coeffRef(Index index) const { return m_expression.coeffRef(index); }
|
||||
|
||||
EIGEN_DEVICE_FUNC constexpr const internal::remove_all_t<NestedExpressionType>& nestedExpression() const {
|
||||
return m_expression;
|
||||
}
|
||||
|
||||
/** Forwards the resizing request to the nested expression
|
||||
* \sa DenseBase::resize(Index) */
|
||||
EIGEN_DEVICE_FUNC void resize(Index newSize) { m_expression.resize(newSize); }
|
||||
/** Forwards the resizing request to the nested expression
|
||||
* \sa DenseBase::resize(Index,Index)*/
|
||||
EIGEN_DEVICE_FUNC void resize(Index rows, Index cols) { m_expression.resize(rows, cols); }
|
||||
|
||||
protected:
|
||||
NestedExpressionType m_expression;
|
||||
};
|
||||
|
||||
} // end namespace Eigen
|
||||
|
||||
#endif // EIGEN_ARRAYWRAPPER_H
|
||||
@@ -0,0 +1,84 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2007 Michael Olbrich <michael.olbrich@gmx.net>
|
||||
// Copyright (C) 2006-2010 Benoit Jacob <jacob.benoit.1@gmail.com>
|
||||
// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_ASSIGN_H
|
||||
#define EIGEN_ASSIGN_H
|
||||
|
||||
// IWYU pragma: private
|
||||
#include "./InternalHeaderCheck.h"
|
||||
|
||||
namespace Eigen {
|
||||
|
||||
template <typename Derived>
|
||||
template <typename OtherDerived>
|
||||
EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Derived& DenseBase<Derived>::lazyAssign(
|
||||
const DenseBase<OtherDerived>& other) {
|
||||
enum { SameType = internal::is_same<typename Derived::Scalar, typename OtherDerived::Scalar>::value };
|
||||
|
||||
EIGEN_STATIC_ASSERT_LVALUE(Derived)
|
||||
EIGEN_STATIC_ASSERT_SAME_MATRIX_SIZE(Derived, OtherDerived)
|
||||
EIGEN_STATIC_ASSERT(
|
||||
SameType,
|
||||
YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
|
||||
|
||||
eigen_assert(rows() == other.rows() && cols() == other.cols());
|
||||
internal::call_assignment_no_alias(derived(), other.derived());
|
||||
|
||||
return derived();
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
template <typename OtherDerived>
|
||||
EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Derived& DenseBase<Derived>::operator=(
|
||||
const DenseBase<OtherDerived>& other) {
|
||||
internal::call_assignment(derived(), other.derived());
|
||||
return derived();
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Derived& DenseBase<Derived>::operator=(const DenseBase& other) {
|
||||
internal::call_assignment(derived(), other.derived());
|
||||
return derived();
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Derived& MatrixBase<Derived>::operator=(const MatrixBase& other) {
|
||||
internal::call_assignment(derived(), other.derived());
|
||||
return derived();
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
template <typename OtherDerived>
|
||||
EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Derived& MatrixBase<Derived>::operator=(
|
||||
const DenseBase<OtherDerived>& other) {
|
||||
internal::call_assignment(derived(), other.derived());
|
||||
return derived();
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
template <typename OtherDerived>
|
||||
EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Derived& MatrixBase<Derived>::operator=(
|
||||
const EigenBase<OtherDerived>& other) {
|
||||
internal::call_assignment(derived(), other.derived());
|
||||
return derived();
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
template <typename OtherDerived>
|
||||
EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Derived& MatrixBase<Derived>::operator=(
|
||||
const ReturnByValue<OtherDerived>& other) {
|
||||
other.derived().evalTo(derived());
|
||||
return derived();
|
||||
}
|
||||
|
||||
} // end namespace Eigen
|
||||
|
||||
#endif // EIGEN_ASSIGN_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Assign_AOCL.h - AOCL Vectorized Math Dispatch Layer for Eigen
|
||||
*
|
||||
* Copyright (c) 2025, Advanced Micro Devices, Inc. All rights reserved.
|
||||
*
|
||||
* Description:
|
||||
* ------------
|
||||
* This file implements a high-performance dispatch layer that automatically
|
||||
* routes Eigen's element-wise mathematical operations to AMD Optimizing CPU
|
||||
* Libraries (AOCL) Vector Math Library (VML) functions when beneficial for
|
||||
* performance.
|
||||
*
|
||||
* The dispatch system uses C++ template specialization to intercept Eigen's
|
||||
* assignment operations and redirect them to AOCL's VRDA functions, which
|
||||
* provide optimized implementations for AMD Zen architectures.
|
||||
*
|
||||
* Key Features:
|
||||
* -------------
|
||||
* 1. Automatic Dispatch: Seamlessly routes supported operations to AOCL without
|
||||
* requiring code changes in user applications
|
||||
*
|
||||
* 2. Performance Optimization: Uses AOCL VRDA functions optimized for Zen
|
||||
* family processors with automatic SIMD instruction selection (AVX2, AVX-512)
|
||||
*
|
||||
* 3. Threshold-Based Activation: Only activates for vectors larger than
|
||||
* EIGEN_AOCL_VML_THRESHOLD (default: 128 elements) to avoid overhead on
|
||||
* small vectors
|
||||
*
|
||||
* 4. Precision-Specific Handling:
|
||||
* - Double precision: AOCL VRDA vectorized functions
|
||||
* - Single precision: Scalar fallback (preserves correctness)
|
||||
*
|
||||
* 5. Memory Layout Compatibility: Ensures direct memory access and compatible
|
||||
* storage orders between source and destination for optimal performance
|
||||
*
|
||||
* Supported Operations:
|
||||
* ---------------------
|
||||
* UNARY OPERATIONS (vector → vector):
|
||||
* - Transcendental: exp(), sin(), cos(), sqrt(), log(), log10(), log2()
|
||||
*
|
||||
* BINARY OPERATIONS (vector op vector → vector):
|
||||
* - Arithmetic: +, *, pow()
|
||||
*
|
||||
* Template Specialization Mechanism:
|
||||
* -----------------------------------
|
||||
* The system works by specializing Eigen's Assignment template for:
|
||||
* 1. CwiseUnaryOp with scalar_*_op functors (unary operations)
|
||||
* 2. CwiseBinaryOp with scalar_*_op functors (binary operations)
|
||||
* 3. Dense2Dense assignment context with AOCL-compatible traits
|
||||
*
|
||||
* Dispatch conditions (all must be true):
|
||||
* - Source and destination have DirectAccessBit (contiguous memory)
|
||||
* - Compatible storage orders (both row-major or both column-major)
|
||||
* - Vector size ≥ EIGEN_AOCL_VML_THRESHOLD or Dynamic size
|
||||
* - Supported data type (currently double precision for VRDA)
|
||||
*
|
||||
* Integration Example:
|
||||
* --------------------
|
||||
* // Standard Eigen code - no changes required
|
||||
* VectorXd x = VectorXd::Random(10000);
|
||||
* VectorXd y = VectorXd::Random(10000);
|
||||
* VectorXd result;
|
||||
*
|
||||
* // These operations are automatically dispatched to AOCL:
|
||||
* result = x.array().exp(); // → amd_vrda_exp()
|
||||
* result = x.array().sin(); // → amd_vrda_sin()
|
||||
* result = x.array() + y.array(); // → amd_vrda_add()
|
||||
* result = x.array().pow(y.array()); // → amd_vrda_pow()
|
||||
*
|
||||
* Configuration:
|
||||
* --------------
|
||||
* Required preprocessor definitions:
|
||||
* - EIGEN_USE_AOCL_ALL or EIGEN_USE_AOCL_MT: Enable AOCL integration
|
||||
* - EIGEN_USE_AOCL_VML: Enable Vector Math Library dispatch
|
||||
*
|
||||
* Compilation Requirements:
|
||||
* -------------------------
|
||||
* Include paths:
|
||||
* - AOCL headers: -I${AOCL_ROOT}/include
|
||||
* - Eigen headers: -I/path/to/eigen
|
||||
*
|
||||
* Link libraries:
|
||||
* - AOCL MathLib: -lamdlibm
|
||||
* - Standard math: -lm
|
||||
*
|
||||
* Compiler flags:
|
||||
* - Optimization: -O3 (required for inlining)
|
||||
* - Architecture: -march=znver5 or -march=native
|
||||
* - Vectorization: -mfma -mavx512f (if supported)
|
||||
*
|
||||
* Platform Support:
|
||||
* ------------------
|
||||
* - Primary: Linux x86_64 with AMD Zen family processors
|
||||
* - Compilers: GCC 8+, Clang 10+, AOCC (recommended)
|
||||
* - AOCL Version: 4.0+ (with VRDA support)
|
||||
*
|
||||
* Error Handling:
|
||||
* ---------------
|
||||
* - Graceful fallback to scalar operations for unsupported configurations
|
||||
* - Compile-time detection of AOCL availability
|
||||
* - Runtime size and alignment validation with eigen_assert()
|
||||
*
|
||||
* Developer:
|
||||
* ----------
|
||||
* Name: Sharad Saurabh Bhaskar
|
||||
* Email: shbhaska@amd.com
|
||||
* Organization: Advanced Micro Devices, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef EIGEN_ASSIGN_AOCL_H
|
||||
#define EIGEN_ASSIGN_AOCL_H
|
||||
|
||||
namespace Eigen {
|
||||
namespace internal {
|
||||
|
||||
// Traits for unary operations.
|
||||
template <typename Dst, typename Src> class aocl_assign_traits {
|
||||
private:
|
||||
enum {
|
||||
DstHasDirectAccess = !!(Dst::Flags & DirectAccessBit),
|
||||
SrcHasDirectAccess = !!(Src::Flags & DirectAccessBit),
|
||||
StorageOrdersAgree = (int(Dst::IsRowMajor) == int(Src::IsRowMajor)),
|
||||
InnerSize = Dst::IsVectorAtCompileTime ? int(Dst::SizeAtCompileTime)
|
||||
: (Dst::Flags & RowMajorBit) ? int(Dst::ColsAtCompileTime)
|
||||
: int(Dst::RowsAtCompileTime),
|
||||
LargeEnough =
|
||||
(InnerSize == Dynamic) || (InnerSize >= EIGEN_AOCL_VML_THRESHOLD)
|
||||
};
|
||||
|
||||
public:
|
||||
enum {
|
||||
EnableAoclVML = DstHasDirectAccess && SrcHasDirectAccess &&
|
||||
StorageOrdersAgree && LargeEnough,
|
||||
Traversal = LinearTraversal
|
||||
};
|
||||
};
|
||||
|
||||
// Traits for binary operations (e.g., add, pow).
|
||||
template <typename Dst, typename Lhs, typename Rhs>
|
||||
class aocl_assign_binary_traits {
|
||||
private:
|
||||
enum {
|
||||
DstHasDirectAccess = !!(Dst::Flags & DirectAccessBit),
|
||||
LhsHasDirectAccess = !!(Lhs::Flags & DirectAccessBit),
|
||||
RhsHasDirectAccess = !!(Rhs::Flags & DirectAccessBit),
|
||||
StorageOrdersAgree = (int(Dst::IsRowMajor) == int(Lhs::IsRowMajor)) &&
|
||||
(int(Dst::IsRowMajor) == int(Rhs::IsRowMajor)),
|
||||
InnerSize = Dst::IsVectorAtCompileTime ? int(Dst::SizeAtCompileTime)
|
||||
: (Dst::Flags & RowMajorBit) ? int(Dst::ColsAtCompileTime)
|
||||
: int(Dst::RowsAtCompileTime),
|
||||
LargeEnough =
|
||||
(InnerSize == Dynamic) || (InnerSize >= EIGEN_AOCL_VML_THRESHOLD)
|
||||
};
|
||||
|
||||
public:
|
||||
enum {
|
||||
EnableAoclVML = DstHasDirectAccess && LhsHasDirectAccess &&
|
||||
RhsHasDirectAccess && StorageOrdersAgree && LargeEnough
|
||||
};
|
||||
};
|
||||
|
||||
// Unary operation dispatch for float (scalar fallback).
|
||||
#define EIGEN_AOCL_VML_UNARY_CALL_FLOAT(EIGENOP) \
|
||||
template <typename DstXprType, typename SrcXprNested> \
|
||||
struct Assignment< \
|
||||
DstXprType, CwiseUnaryOp<scalar_##EIGENOP##_op<float>, SrcXprNested>, \
|
||||
assign_op<float, float>, Dense2Dense, \
|
||||
std::enable_if_t< \
|
||||
aocl_assign_traits<DstXprType, SrcXprNested>::EnableAoclVML>> { \
|
||||
typedef CwiseUnaryOp<scalar_##EIGENOP##_op<float>, SrcXprNested> \
|
||||
SrcXprType; \
|
||||
static void run(DstXprType &dst, const SrcXprType &src, \
|
||||
const assign_op<float, float> &) { \
|
||||
eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols()); \
|
||||
Eigen::Index n = dst.size(); \
|
||||
if (n <= 0) \
|
||||
return; \
|
||||
const float *input = \
|
||||
reinterpret_cast<const float *>(src.nestedExpression().data()); \
|
||||
float *output = reinterpret_cast<float *>(dst.data()); \
|
||||
for (Eigen::Index i = 0; i < n; ++i) { \
|
||||
output[i] = std::EIGENOP(input[i]); \
|
||||
} \
|
||||
} \
|
||||
};
|
||||
|
||||
// Unary operation dispatch for double (AOCL vectorized).
|
||||
#define EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(EIGENOP, AOCLOP) \
|
||||
template <typename DstXprType, typename SrcXprNested> \
|
||||
struct Assignment< \
|
||||
DstXprType, CwiseUnaryOp<scalar_##EIGENOP##_op<double>, SrcXprNested>, \
|
||||
assign_op<double, double>, Dense2Dense, \
|
||||
std::enable_if_t< \
|
||||
aocl_assign_traits<DstXprType, SrcXprNested>::EnableAoclVML>> { \
|
||||
typedef CwiseUnaryOp<scalar_##EIGENOP##_op<double>, SrcXprNested> \
|
||||
SrcXprType; \
|
||||
static void run(DstXprType &dst, const SrcXprType &src, \
|
||||
const assign_op<double, double> &) { \
|
||||
eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols()); \
|
||||
Eigen::Index n = dst.size(); \
|
||||
eigen_assert(n <= INT_MAX && "AOCL does not support arrays larger than INT_MAX"); \
|
||||
if (n <= 0) \
|
||||
return; \
|
||||
const double *input = \
|
||||
reinterpret_cast<const double *>(src.nestedExpression().data()); \
|
||||
double *output = reinterpret_cast<double *>(dst.data()); \
|
||||
int aocl_n = internal::convert_index<int>(n); \
|
||||
AOCLOP(aocl_n, const_cast<double *>(input), output); \
|
||||
} \
|
||||
};
|
||||
|
||||
// Instantiate unary calls for float (scalar).
|
||||
// EIGEN_AOCL_VML_UNARY_CALL_FLOAT(exp)
|
||||
|
||||
// Instantiate unary calls for double (AOCL vectorized).
|
||||
EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(exp2, amd_vrda_exp2)
|
||||
EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(exp, amd_vrda_exp)
|
||||
EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(sin, amd_vrda_sin)
|
||||
EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(cos, amd_vrda_cos)
|
||||
EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(sqrt, amd_vrda_sqrt)
|
||||
EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(cbrt, amd_vrda_cbrt)
|
||||
EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(abs, amd_vrda_fabs)
|
||||
EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(log, amd_vrda_log)
|
||||
EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(log10, amd_vrda_log10)
|
||||
EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(log2, amd_vrda_log2)
|
||||
|
||||
// Binary operation dispatch for float (scalar fallback).
|
||||
#define EIGEN_AOCL_VML_BINARY_CALL_FLOAT(EIGENOP, STDFUNC) \
|
||||
template <typename DstXprType, typename LhsXprNested, typename RhsXprNested> \
|
||||
struct Assignment< \
|
||||
DstXprType, \
|
||||
CwiseBinaryOp<scalar_##EIGENOP##_op<float, float>, LhsXprNested, \
|
||||
RhsXprNested>, \
|
||||
assign_op<float, float>, Dense2Dense, \
|
||||
std::enable_if_t<aocl_assign_binary_traits< \
|
||||
DstXprType, LhsXprNested, RhsXprNested>::EnableAoclVML>> { \
|
||||
typedef CwiseBinaryOp<scalar_##EIGENOP##_op<float, float>, LhsXprNested, \
|
||||
RhsXprNested> \
|
||||
SrcXprType; \
|
||||
static void run(DstXprType &dst, const SrcXprType &src, \
|
||||
const assign_op<float, float> &) { \
|
||||
eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols()); \
|
||||
Eigen::Index n = dst.size(); \
|
||||
if (n <= 0) \
|
||||
return; \
|
||||
const float *lhs = reinterpret_cast<const float *>(src.lhs().data()); \
|
||||
const float *rhs = reinterpret_cast<const float *>(src.rhs().data()); \
|
||||
float *output = reinterpret_cast<float *>(dst.data()); \
|
||||
for (Eigen::Index i = 0; i < n; ++i) { \
|
||||
output[i] = STDFUNC(lhs[i], rhs[i]); \
|
||||
} \
|
||||
} \
|
||||
};
|
||||
|
||||
// Binary operation dispatch for double (AOCL vectorized).
|
||||
#define EIGEN_AOCL_VML_BINARY_CALL_DOUBLE(EIGENOP, AOCLOP) \
|
||||
template <typename DstXprType, typename LhsXprNested, typename RhsXprNested> \
|
||||
struct Assignment< \
|
||||
DstXprType, \
|
||||
CwiseBinaryOp<scalar_##EIGENOP##_op<double, double>, LhsXprNested, \
|
||||
RhsXprNested>, \
|
||||
assign_op<double, double>, Dense2Dense, \
|
||||
std::enable_if_t<aocl_assign_binary_traits< \
|
||||
DstXprType, LhsXprNested, RhsXprNested>::EnableAoclVML>> { \
|
||||
typedef CwiseBinaryOp<scalar_##EIGENOP##_op<double, double>, LhsXprNested, \
|
||||
RhsXprNested> \
|
||||
SrcXprType; \
|
||||
static void run(DstXprType &dst, const SrcXprType &src, \
|
||||
const assign_op<double, double> &) { \
|
||||
eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols()); \
|
||||
Eigen::Index n = dst.size(); \
|
||||
eigen_assert(n <= INT_MAX && "AOCL does not support arrays larger than INT_MAX"); \
|
||||
if (n <= 0) \
|
||||
return; \
|
||||
const double *lhs = reinterpret_cast<const double *>(src.lhs().data()); \
|
||||
const double *rhs = reinterpret_cast<const double *>(src.rhs().data()); \
|
||||
double *output = reinterpret_cast<double *>(dst.data()); \
|
||||
int aocl_n = internal::convert_index<int>(n); \
|
||||
AOCLOP(aocl_n, const_cast<double *>(lhs), const_cast<double *>(rhs), output); \
|
||||
} \
|
||||
};
|
||||
|
||||
// Instantiate binary calls for float (scalar).
|
||||
// EIGEN_AOCL_VML_BINARY_CALL_FLOAT(sum, std::plus<float>) // Using
|
||||
// scalar_sum_op for addition EIGEN_AOCL_VML_BINARY_CALL_FLOAT(pow, std::pow)
|
||||
|
||||
// Instantiate binary calls for double (AOCL vectorized).
|
||||
EIGEN_AOCL_VML_BINARY_CALL_DOUBLE(sum, amd_vrda_add) // Using scalar_sum_op for addition
|
||||
EIGEN_AOCL_VML_BINARY_CALL_DOUBLE(pow, amd_vrda_pow)
|
||||
EIGEN_AOCL_VML_BINARY_CALL_DOUBLE(max, amd_vrda_fmax)
|
||||
EIGEN_AOCL_VML_BINARY_CALL_DOUBLE(min, amd_vrda_fmin)
|
||||
|
||||
} // namespace internal
|
||||
} // namespace Eigen
|
||||
|
||||
#endif // EIGEN_ASSIGN_AOCL_H
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
Copyright (c) 2011, Intel Corporation. All rights reserved.
|
||||
Copyright (C) 2015 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of Intel Corporation nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
********************************************************************************
|
||||
* Content : Eigen bindings to Intel(R) MKL
|
||||
* MKL VML support for coefficient-wise unary Eigen expressions like a=b.sin()
|
||||
********************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef EIGEN_ASSIGN_VML_H
|
||||
#define EIGEN_ASSIGN_VML_H
|
||||
|
||||
// IWYU pragma: private
|
||||
#include "./InternalHeaderCheck.h"
|
||||
|
||||
namespace Eigen {
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
class vml_assign_traits {
|
||||
private:
|
||||
enum {
|
||||
DstHasDirectAccess = Dst::Flags & DirectAccessBit,
|
||||
SrcHasDirectAccess = Src::Flags & DirectAccessBit,
|
||||
StorageOrdersAgree = (int(Dst::IsRowMajor) == int(Src::IsRowMajor)),
|
||||
InnerSize = int(Dst::IsVectorAtCompileTime) ? int(Dst::SizeAtCompileTime)
|
||||
: int(Dst::Flags) & RowMajorBit ? int(Dst::ColsAtCompileTime)
|
||||
: int(Dst::RowsAtCompileTime),
|
||||
InnerMaxSize = int(Dst::IsVectorAtCompileTime) ? int(Dst::MaxSizeAtCompileTime)
|
||||
: int(Dst::Flags) & RowMajorBit ? int(Dst::MaxColsAtCompileTime)
|
||||
: int(Dst::MaxRowsAtCompileTime),
|
||||
MaxSizeAtCompileTime = Dst::SizeAtCompileTime,
|
||||
|
||||
MightEnableVml = bool(StorageOrdersAgree) && bool(DstHasDirectAccess) && bool(SrcHasDirectAccess) &&
|
||||
Src::InnerStrideAtCompileTime == 1 && Dst::InnerStrideAtCompileTime == 1,
|
||||
MightLinearize = bool(MightEnableVml) && (int(Dst::Flags) & int(Src::Flags) & LinearAccessBit),
|
||||
VmlSize = bool(MightLinearize) ? MaxSizeAtCompileTime : InnerMaxSize,
|
||||
LargeEnough = (VmlSize == Dynamic) || VmlSize >= EIGEN_MKL_VML_THRESHOLD
|
||||
};
|
||||
|
||||
public:
|
||||
enum { EnableVml = MightEnableVml && LargeEnough, Traversal = MightLinearize ? LinearTraversal : DefaultTraversal };
|
||||
};
|
||||
|
||||
#define EIGEN_PP_EXPAND(ARG) ARG
|
||||
#if !defined(EIGEN_FAST_MATH) || (EIGEN_FAST_MATH != 1)
|
||||
#define EIGEN_VMLMODE_EXPAND_xLA , VML_HA
|
||||
#else
|
||||
#define EIGEN_VMLMODE_EXPAND_xLA , VML_LA
|
||||
#endif
|
||||
|
||||
#define EIGEN_VMLMODE_EXPAND_x_
|
||||
|
||||
#define EIGEN_VMLMODE_PREFIX_xLA vm
|
||||
#define EIGEN_VMLMODE_PREFIX_x_ v
|
||||
#define EIGEN_VMLMODE_PREFIX(VMLMODE) EIGEN_CAT(EIGEN_VMLMODE_PREFIX_x, VMLMODE)
|
||||
|
||||
#define EIGEN_MKL_VML_DECLARE_UNARY_CALL(EIGENOP, VMLOP, EIGENTYPE, VMLTYPE, VMLMODE) \
|
||||
template <typename DstXprType, typename SrcXprNested> \
|
||||
struct Assignment<DstXprType, CwiseUnaryOp<scalar_##EIGENOP##_op<EIGENTYPE>, SrcXprNested>, \
|
||||
assign_op<EIGENTYPE, EIGENTYPE>, Dense2Dense, \
|
||||
std::enable_if_t<vml_assign_traits<DstXprType, SrcXprNested>::EnableVml>> { \
|
||||
typedef CwiseUnaryOp<scalar_##EIGENOP##_op<EIGENTYPE>, SrcXprNested> SrcXprType; \
|
||||
static void run(DstXprType &dst, const SrcXprType &src, const assign_op<EIGENTYPE, EIGENTYPE> &func) { \
|
||||
resize_if_allowed(dst, src, func); \
|
||||
eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols()); \
|
||||
if (vml_assign_traits<DstXprType, SrcXprNested>::Traversal == (int)LinearTraversal) { \
|
||||
VMLOP(dst.size(), (const VMLTYPE *)src.nestedExpression().data(), \
|
||||
(VMLTYPE *)dst.data() EIGEN_PP_EXPAND(EIGEN_VMLMODE_EXPAND_x##VMLMODE)); \
|
||||
} else { \
|
||||
const Index outerSize = dst.outerSize(); \
|
||||
for (Index outer = 0; outer < outerSize; ++outer) { \
|
||||
const EIGENTYPE *src_ptr = src.IsRowMajor ? &(src.nestedExpression().coeffRef(outer, 0)) \
|
||||
: &(src.nestedExpression().coeffRef(0, outer)); \
|
||||
EIGENTYPE *dst_ptr = dst.IsRowMajor ? &(dst.coeffRef(outer, 0)) : &(dst.coeffRef(0, outer)); \
|
||||
VMLOP(dst.innerSize(), (const VMLTYPE *)src_ptr, \
|
||||
(VMLTYPE *)dst_ptr EIGEN_PP_EXPAND(EIGEN_VMLMODE_EXPAND_x##VMLMODE)); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
};
|
||||
|
||||
#define EIGEN_MKL_VML_DECLARE_UNARY_CALLS_REAL(EIGENOP, VMLOP, VMLMODE) \
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALL(EIGENOP, EIGEN_CAT(EIGEN_VMLMODE_PREFIX(VMLMODE), s##VMLOP), float, float, VMLMODE) \
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALL(EIGENOP, EIGEN_CAT(EIGEN_VMLMODE_PREFIX(VMLMODE), d##VMLOP), double, double, VMLMODE)
|
||||
|
||||
#define EIGEN_MKL_VML_DECLARE_UNARY_CALLS_CPLX(EIGENOP, VMLOP, VMLMODE) \
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALL(EIGENOP, EIGEN_CAT(EIGEN_VMLMODE_PREFIX(VMLMODE), c##VMLOP), scomplex, \
|
||||
MKL_Complex8, VMLMODE) \
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALL(EIGENOP, EIGEN_CAT(EIGEN_VMLMODE_PREFIX(VMLMODE), z##VMLOP), dcomplex, \
|
||||
MKL_Complex16, VMLMODE)
|
||||
|
||||
#define EIGEN_MKL_VML_DECLARE_UNARY_CALLS(EIGENOP, VMLOP, VMLMODE) \
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS_REAL(EIGENOP, VMLOP, VMLMODE) \
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS_CPLX(EIGENOP, VMLOP, VMLMODE)
|
||||
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS(sin, Sin, LA)
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS(asin, Asin, LA)
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS(sinh, Sinh, LA)
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS(cos, Cos, LA)
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS(acos, Acos, LA)
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS(cosh, Cosh, LA)
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS(tan, Tan, LA)
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS(atan, Atan, LA)
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS(tanh, Tanh, LA)
|
||||
// EIGEN_MKL_VML_DECLARE_UNARY_CALLS(abs, Abs, _)
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS(exp, Exp, LA)
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS(log, Ln, LA)
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS(log10, Log10, LA)
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS(sqrt, Sqrt, _)
|
||||
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS_REAL(square, Sqr, _)
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS_CPLX(arg, Arg, _)
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS_REAL(round, Round, _)
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS_REAL(floor, Floor, _)
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS_REAL(ceil, Ceil, _)
|
||||
EIGEN_MKL_VML_DECLARE_UNARY_CALLS_REAL(cbrt, Cbrt, _)
|
||||
|
||||
#define EIGEN_MKL_VML_DECLARE_POW_CALL(EIGENOP, VMLOP, EIGENTYPE, VMLTYPE, VMLMODE) \
|
||||
template <typename DstXprType, typename SrcXprNested, typename Plain> \
|
||||
struct Assignment<DstXprType, \
|
||||
CwiseBinaryOp<scalar_##EIGENOP##_op<EIGENTYPE, EIGENTYPE>, SrcXprNested, \
|
||||
const CwiseNullaryOp<internal::scalar_constant_op<EIGENTYPE>, Plain>>, \
|
||||
assign_op<EIGENTYPE, EIGENTYPE>, Dense2Dense, \
|
||||
std::enable_if_t<vml_assign_traits<DstXprType, SrcXprNested>::EnableVml>> { \
|
||||
typedef CwiseBinaryOp<scalar_##EIGENOP##_op<EIGENTYPE, EIGENTYPE>, SrcXprNested, \
|
||||
const CwiseNullaryOp<internal::scalar_constant_op<EIGENTYPE>, Plain>> \
|
||||
SrcXprType; \
|
||||
static void run(DstXprType &dst, const SrcXprType &src, const assign_op<EIGENTYPE, EIGENTYPE> &func) { \
|
||||
resize_if_allowed(dst, src, func); \
|
||||
eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols()); \
|
||||
VMLTYPE exponent = reinterpret_cast<const VMLTYPE &>(src.rhs().functor().m_other); \
|
||||
if (vml_assign_traits<DstXprType, SrcXprNested>::Traversal == LinearTraversal) { \
|
||||
VMLOP(dst.size(), (const VMLTYPE *)src.lhs().data(), exponent, \
|
||||
(VMLTYPE *)dst.data() EIGEN_PP_EXPAND(EIGEN_VMLMODE_EXPAND_x##VMLMODE)); \
|
||||
} else { \
|
||||
const Index outerSize = dst.outerSize(); \
|
||||
for (Index outer = 0; outer < outerSize; ++outer) { \
|
||||
const EIGENTYPE *src_ptr = \
|
||||
src.IsRowMajor ? &(src.lhs().coeffRef(outer, 0)) : &(src.lhs().coeffRef(0, outer)); \
|
||||
EIGENTYPE *dst_ptr = dst.IsRowMajor ? &(dst.coeffRef(outer, 0)) : &(dst.coeffRef(0, outer)); \
|
||||
VMLOP(dst.innerSize(), (const VMLTYPE *)src_ptr, exponent, \
|
||||
(VMLTYPE *)dst_ptr EIGEN_PP_EXPAND(EIGEN_VMLMODE_EXPAND_x##VMLMODE)); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
};
|
||||
|
||||
EIGEN_MKL_VML_DECLARE_POW_CALL(pow, vmsPowx, float, float, LA)
|
||||
EIGEN_MKL_VML_DECLARE_POW_CALL(pow, vmdPowx, double, double, LA)
|
||||
EIGEN_MKL_VML_DECLARE_POW_CALL(pow, vmcPowx, scomplex, MKL_Complex8, LA)
|
||||
EIGEN_MKL_VML_DECLARE_POW_CALL(pow, vmzPowx, dcomplex, MKL_Complex16, LA)
|
||||
|
||||
} // end namespace internal
|
||||
|
||||
} // end namespace Eigen
|
||||
|
||||
#endif // EIGEN_ASSIGN_VML_H
|
||||
@@ -0,0 +1,338 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_BANDMATRIX_H
|
||||
#define EIGEN_BANDMATRIX_H
|
||||
|
||||
// IWYU pragma: private
|
||||
#include "./InternalHeaderCheck.h"
|
||||
|
||||
namespace Eigen {
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename Derived>
|
||||
class BandMatrixBase : public EigenBase<Derived> {
|
||||
public:
|
||||
enum {
|
||||
Flags = internal::traits<Derived>::Flags,
|
||||
CoeffReadCost = internal::traits<Derived>::CoeffReadCost,
|
||||
RowsAtCompileTime = internal::traits<Derived>::RowsAtCompileTime,
|
||||
ColsAtCompileTime = internal::traits<Derived>::ColsAtCompileTime,
|
||||
MaxRowsAtCompileTime = internal::traits<Derived>::MaxRowsAtCompileTime,
|
||||
MaxColsAtCompileTime = internal::traits<Derived>::MaxColsAtCompileTime,
|
||||
Supers = internal::traits<Derived>::Supers,
|
||||
Subs = internal::traits<Derived>::Subs,
|
||||
Options = internal::traits<Derived>::Options
|
||||
};
|
||||
typedef typename internal::traits<Derived>::Scalar Scalar;
|
||||
typedef Matrix<Scalar, RowsAtCompileTime, ColsAtCompileTime> DenseMatrixType;
|
||||
typedef typename DenseMatrixType::StorageIndex StorageIndex;
|
||||
typedef typename internal::traits<Derived>::CoefficientsType CoefficientsType;
|
||||
typedef EigenBase<Derived> Base;
|
||||
|
||||
protected:
|
||||
enum {
|
||||
DataRowsAtCompileTime = ((Supers != Dynamic) && (Subs != Dynamic)) ? 1 + Supers + Subs : Dynamic,
|
||||
SizeAtCompileTime = min_size_prefer_dynamic(RowsAtCompileTime, ColsAtCompileTime)
|
||||
};
|
||||
|
||||
public:
|
||||
using Base::cols;
|
||||
using Base::derived;
|
||||
using Base::rows;
|
||||
|
||||
/** \returns the number of super diagonals */
|
||||
inline Index supers() const { return derived().supers(); }
|
||||
|
||||
/** \returns the number of sub diagonals */
|
||||
inline Index subs() const { return derived().subs(); }
|
||||
|
||||
/** \returns an expression of the underlying coefficient matrix */
|
||||
inline const CoefficientsType& coeffs() const { return derived().coeffs(); }
|
||||
|
||||
/** \returns an expression of the underlying coefficient matrix */
|
||||
inline CoefficientsType& coeffs() { return derived().coeffs(); }
|
||||
|
||||
/** \returns a vector expression of the \a i -th column,
|
||||
* only the meaningful part is returned.
|
||||
* \warning the internal storage must be column major. */
|
||||
inline Block<CoefficientsType, Dynamic, 1> col(Index i) {
|
||||
EIGEN_STATIC_ASSERT((int(Options) & int(RowMajor)) == 0, THIS_METHOD_IS_ONLY_FOR_COLUMN_MAJOR_MATRICES);
|
||||
Index start = 0;
|
||||
Index len = coeffs().rows();
|
||||
if (i <= supers()) {
|
||||
start = supers() - i;
|
||||
len = (std::min)(rows(), std::max<Index>(0, coeffs().rows() - (supers() - i)));
|
||||
} else if (i >= rows() - subs())
|
||||
len = std::max<Index>(0, coeffs().rows() - (i + 1 - rows() + subs()));
|
||||
return Block<CoefficientsType, Dynamic, 1>(coeffs(), start, i, len, 1);
|
||||
}
|
||||
|
||||
/** \returns a vector expression of the main diagonal */
|
||||
inline Block<CoefficientsType, 1, SizeAtCompileTime> diagonal() {
|
||||
return Block<CoefficientsType, 1, SizeAtCompileTime>(coeffs(), supers(), 0, 1, (std::min)(rows(), cols()));
|
||||
}
|
||||
|
||||
/** \returns a vector expression of the main diagonal (const version) */
|
||||
inline const Block<const CoefficientsType, 1, SizeAtCompileTime> diagonal() const {
|
||||
return Block<const CoefficientsType, 1, SizeAtCompileTime>(coeffs(), supers(), 0, 1, (std::min)(rows(), cols()));
|
||||
}
|
||||
|
||||
template <int Index>
|
||||
struct DiagonalIntReturnType {
|
||||
enum {
|
||||
ReturnOpposite =
|
||||
(int(Options) & int(SelfAdjoint)) && (((Index) > 0 && Supers == 0) || ((Index) < 0 && Subs == 0)),
|
||||
Conjugate = ReturnOpposite && NumTraits<Scalar>::IsComplex,
|
||||
ActualIndex = ReturnOpposite ? -Index : Index,
|
||||
DiagonalSize =
|
||||
(RowsAtCompileTime == Dynamic || ColsAtCompileTime == Dynamic)
|
||||
? Dynamic
|
||||
: (ActualIndex < 0 ? min_size_prefer_dynamic(ColsAtCompileTime, RowsAtCompileTime + ActualIndex)
|
||||
: min_size_prefer_dynamic(RowsAtCompileTime, ColsAtCompileTime - ActualIndex))
|
||||
};
|
||||
typedef Block<CoefficientsType, 1, DiagonalSize> BuildType;
|
||||
typedef std::conditional_t<Conjugate, CwiseUnaryOp<internal::scalar_conjugate_op<Scalar>, BuildType>, BuildType>
|
||||
Type;
|
||||
};
|
||||
|
||||
/** \returns a vector expression of the \a N -th sub or super diagonal */
|
||||
template <int N>
|
||||
inline typename DiagonalIntReturnType<N>::Type diagonal() {
|
||||
return typename DiagonalIntReturnType<N>::BuildType(coeffs(), supers() - N, (std::max)(0, N), 1, diagonalLength(N));
|
||||
}
|
||||
|
||||
/** \returns a vector expression of the \a N -th sub or super diagonal */
|
||||
template <int N>
|
||||
inline const typename DiagonalIntReturnType<N>::Type diagonal() const {
|
||||
return typename DiagonalIntReturnType<N>::BuildType(coeffs(), supers() - N, (std::max)(0, N), 1, diagonalLength(N));
|
||||
}
|
||||
|
||||
/** \returns a vector expression of the \a i -th sub or super diagonal */
|
||||
inline Block<CoefficientsType, 1, Dynamic> diagonal(Index i) {
|
||||
eigen_assert((i < 0 && -i <= subs()) || (i >= 0 && i <= supers()));
|
||||
return Block<CoefficientsType, 1, Dynamic>(coeffs(), supers() - i, std::max<Index>(0, i), 1, diagonalLength(i));
|
||||
}
|
||||
|
||||
/** \returns a vector expression of the \a i -th sub or super diagonal */
|
||||
inline const Block<const CoefficientsType, 1, Dynamic> diagonal(Index i) const {
|
||||
eigen_assert((i < 0 && -i <= subs()) || (i >= 0 && i <= supers()));
|
||||
return Block<const CoefficientsType, 1, Dynamic>(coeffs(), supers() - i, std::max<Index>(0, i), 1,
|
||||
diagonalLength(i));
|
||||
}
|
||||
|
||||
template <typename Dest>
|
||||
inline void evalTo(Dest& dst) const {
|
||||
dst.resize(rows(), cols());
|
||||
dst.setZero();
|
||||
dst.diagonal() = diagonal();
|
||||
for (Index i = 1; i <= supers(); ++i) dst.diagonal(i) = diagonal(i);
|
||||
for (Index i = 1; i <= subs(); ++i) dst.diagonal(-i) = diagonal(-i);
|
||||
}
|
||||
|
||||
DenseMatrixType toDenseMatrix() const {
|
||||
DenseMatrixType res(rows(), cols());
|
||||
evalTo(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
protected:
|
||||
inline Index diagonalLength(Index i) const {
|
||||
return i < 0 ? (std::min)(cols(), rows() + i) : (std::min)(rows(), cols() - i);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \class BandMatrix
|
||||
* \ingroup Core_Module
|
||||
*
|
||||
* \brief Represents a rectangular matrix with a banded storage
|
||||
*
|
||||
* \tparam Scalar_ Numeric type, i.e. float, double, int
|
||||
* \tparam Rows_ Number of rows, or \b Dynamic
|
||||
* \tparam Cols_ Number of columns, or \b Dynamic
|
||||
* \tparam Supers_ Number of super diagonal
|
||||
* \tparam Subs_ Number of sub diagonal
|
||||
* \tparam Options_ A combination of either \b #RowMajor or \b #ColMajor, and of \b #SelfAdjoint
|
||||
* The former controls \ref TopicStorageOrders "storage order", and defaults to
|
||||
* column-major. The latter controls whether the matrix represents a selfadjoint
|
||||
* matrix in which case either Supers of Subs have to be null.
|
||||
*
|
||||
* \sa class TridiagonalMatrix
|
||||
*/
|
||||
|
||||
template <typename Scalar_, int Rows_, int Cols_, int Supers_, int Subs_, int Options_>
|
||||
struct traits<BandMatrix<Scalar_, Rows_, Cols_, Supers_, Subs_, Options_> > {
|
||||
typedef Scalar_ Scalar;
|
||||
typedef Dense StorageKind;
|
||||
typedef Eigen::Index StorageIndex;
|
||||
enum {
|
||||
CoeffReadCost = NumTraits<Scalar>::ReadCost,
|
||||
RowsAtCompileTime = Rows_,
|
||||
ColsAtCompileTime = Cols_,
|
||||
MaxRowsAtCompileTime = Rows_,
|
||||
MaxColsAtCompileTime = Cols_,
|
||||
Flags = LvalueBit,
|
||||
Supers = Supers_,
|
||||
Subs = Subs_,
|
||||
Options = Options_,
|
||||
DataRowsAtCompileTime = ((Supers != Dynamic) && (Subs != Dynamic)) ? 1 + Supers + Subs : Dynamic
|
||||
};
|
||||
typedef Matrix<Scalar, DataRowsAtCompileTime, ColsAtCompileTime, int(Options) & int(RowMajor) ? RowMajor : ColMajor>
|
||||
CoefficientsType;
|
||||
};
|
||||
|
||||
template <typename Scalar_, int Rows, int Cols, int Supers, int Subs, int Options>
|
||||
class BandMatrix : public BandMatrixBase<BandMatrix<Scalar_, Rows, Cols, Supers, Subs, Options> > {
|
||||
public:
|
||||
typedef typename internal::traits<BandMatrix>::Scalar Scalar;
|
||||
typedef typename internal::traits<BandMatrix>::StorageIndex StorageIndex;
|
||||
typedef typename internal::traits<BandMatrix>::CoefficientsType CoefficientsType;
|
||||
|
||||
explicit inline BandMatrix(Index rows = Rows, Index cols = Cols, Index supers = Supers, Index subs = Subs)
|
||||
: m_coeffs(1 + supers + subs, cols), m_rows(rows), m_supers(supers), m_subs(subs) {}
|
||||
|
||||
/** \returns the number of columns */
|
||||
constexpr Index rows() const { return m_rows.value(); }
|
||||
|
||||
/** \returns the number of rows */
|
||||
constexpr Index cols() const { return m_coeffs.cols(); }
|
||||
|
||||
/** \returns the number of super diagonals */
|
||||
constexpr Index supers() const { return m_supers.value(); }
|
||||
|
||||
/** \returns the number of sub diagonals */
|
||||
constexpr Index subs() const { return m_subs.value(); }
|
||||
|
||||
inline const CoefficientsType& coeffs() const { return m_coeffs; }
|
||||
inline CoefficientsType& coeffs() { return m_coeffs; }
|
||||
|
||||
protected:
|
||||
CoefficientsType m_coeffs;
|
||||
internal::variable_if_dynamic<Index, Rows> m_rows;
|
||||
internal::variable_if_dynamic<Index, Supers> m_supers;
|
||||
internal::variable_if_dynamic<Index, Subs> m_subs;
|
||||
};
|
||||
|
||||
template <typename CoefficientsType_, int Rows_, int Cols_, int Supers_, int Subs_, int Options_>
|
||||
class BandMatrixWrapper;
|
||||
|
||||
template <typename CoefficientsType_, int Rows_, int Cols_, int Supers_, int Subs_, int Options_>
|
||||
struct traits<BandMatrixWrapper<CoefficientsType_, Rows_, Cols_, Supers_, Subs_, Options_> > {
|
||||
typedef typename CoefficientsType_::Scalar Scalar;
|
||||
typedef typename CoefficientsType_::StorageKind StorageKind;
|
||||
typedef typename CoefficientsType_::StorageIndex StorageIndex;
|
||||
enum {
|
||||
CoeffReadCost = internal::traits<CoefficientsType_>::CoeffReadCost,
|
||||
RowsAtCompileTime = Rows_,
|
||||
ColsAtCompileTime = Cols_,
|
||||
MaxRowsAtCompileTime = Rows_,
|
||||
MaxColsAtCompileTime = Cols_,
|
||||
Flags = LvalueBit,
|
||||
Supers = Supers_,
|
||||
Subs = Subs_,
|
||||
Options = Options_,
|
||||
DataRowsAtCompileTime = ((Supers != Dynamic) && (Subs != Dynamic)) ? 1 + Supers + Subs : Dynamic
|
||||
};
|
||||
typedef CoefficientsType_ CoefficientsType;
|
||||
};
|
||||
|
||||
template <typename CoefficientsType_, int Rows_, int Cols_, int Supers_, int Subs_, int Options_>
|
||||
class BandMatrixWrapper
|
||||
: public BandMatrixBase<BandMatrixWrapper<CoefficientsType_, Rows_, Cols_, Supers_, Subs_, Options_> > {
|
||||
public:
|
||||
typedef typename internal::traits<BandMatrixWrapper>::Scalar Scalar;
|
||||
typedef typename internal::traits<BandMatrixWrapper>::CoefficientsType CoefficientsType;
|
||||
typedef typename internal::traits<BandMatrixWrapper>::StorageIndex StorageIndex;
|
||||
|
||||
explicit inline BandMatrixWrapper(const CoefficientsType& coeffs, Index rows = Rows_, Index cols = Cols_,
|
||||
Index supers = Supers_, Index subs = Subs_)
|
||||
: m_coeffs(coeffs), m_rows(rows), m_supers(supers), m_subs(subs) {
|
||||
EIGEN_UNUSED_VARIABLE(cols);
|
||||
// eigen_assert(coeffs.cols()==cols() && (supers()+subs()+1)==coeffs.rows());
|
||||
}
|
||||
|
||||
/** \returns the number of columns */
|
||||
constexpr Index rows() const { return m_rows.value(); }
|
||||
|
||||
/** \returns the number of rows */
|
||||
constexpr Index cols() const { return m_coeffs.cols(); }
|
||||
|
||||
/** \returns the number of super diagonals */
|
||||
constexpr Index supers() const { return m_supers.value(); }
|
||||
|
||||
/** \returns the number of sub diagonals */
|
||||
constexpr Index subs() const { return m_subs.value(); }
|
||||
|
||||
inline const CoefficientsType& coeffs() const { return m_coeffs; }
|
||||
|
||||
protected:
|
||||
const CoefficientsType& m_coeffs;
|
||||
internal::variable_if_dynamic<Index, Rows_> m_rows;
|
||||
internal::variable_if_dynamic<Index, Supers_> m_supers;
|
||||
internal::variable_if_dynamic<Index, Subs_> m_subs;
|
||||
};
|
||||
|
||||
/**
|
||||
* \class TridiagonalMatrix
|
||||
* \ingroup Core_Module
|
||||
*
|
||||
* \brief Represents a tridiagonal matrix with a compact banded storage
|
||||
*
|
||||
* \tparam Scalar Numeric type, i.e. float, double, int
|
||||
* \tparam Size Number of rows and cols, or \b Dynamic
|
||||
* \tparam Options Can be 0 or \b SelfAdjoint
|
||||
*
|
||||
* \sa class BandMatrix
|
||||
*/
|
||||
template <typename Scalar, int Size, int Options>
|
||||
class TridiagonalMatrix : public BandMatrix<Scalar, Size, Size, Options & SelfAdjoint ? 0 : 1, 1, Options | RowMajor> {
|
||||
typedef BandMatrix<Scalar, Size, Size, Options & SelfAdjoint ? 0 : 1, 1, Options | RowMajor> Base;
|
||||
typedef typename Base::StorageIndex StorageIndex;
|
||||
|
||||
public:
|
||||
explicit TridiagonalMatrix(Index size = Size) : Base(size, size, Options & SelfAdjoint ? 0 : 1, 1) {}
|
||||
|
||||
inline typename Base::template DiagonalIntReturnType<1>::Type super() { return Base::template diagonal<1>(); }
|
||||
inline const typename Base::template DiagonalIntReturnType<1>::Type super() const {
|
||||
return Base::template diagonal<1>();
|
||||
}
|
||||
inline typename Base::template DiagonalIntReturnType<-1>::Type sub() { return Base::template diagonal<-1>(); }
|
||||
inline const typename Base::template DiagonalIntReturnType<-1>::Type sub() const {
|
||||
return Base::template diagonal<-1>();
|
||||
}
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
struct BandShape {};
|
||||
|
||||
template <typename Scalar_, int Rows_, int Cols_, int Supers_, int Subs_, int Options_>
|
||||
struct evaluator_traits<BandMatrix<Scalar_, Rows_, Cols_, Supers_, Subs_, Options_> >
|
||||
: public evaluator_traits_base<BandMatrix<Scalar_, Rows_, Cols_, Supers_, Subs_, Options_> > {
|
||||
typedef BandShape Shape;
|
||||
};
|
||||
|
||||
template <typename CoefficientsType_, int Rows_, int Cols_, int Supers_, int Subs_, int Options_>
|
||||
struct evaluator_traits<BandMatrixWrapper<CoefficientsType_, Rows_, Cols_, Supers_, Subs_, Options_> >
|
||||
: public evaluator_traits_base<BandMatrixWrapper<CoefficientsType_, Rows_, Cols_, Supers_, Subs_, Options_> > {
|
||||
typedef BandShape Shape;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AssignmentKind<DenseShape, BandShape> {
|
||||
typedef EigenBase2EigenBase Kind;
|
||||
};
|
||||
|
||||
} // end namespace internal
|
||||
|
||||
} // end namespace Eigen
|
||||
|
||||
#endif // EIGEN_BANDMATRIX_H
|
||||
@@ -0,0 +1,427 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
// Copyright (C) 2006-2010 Benoit Jacob <jacob.benoit.1@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_BLOCK_H
|
||||
#define EIGEN_BLOCK_H
|
||||
|
||||
// IWYU pragma: private
|
||||
#include "./InternalHeaderCheck.h"
|
||||
|
||||
namespace Eigen {
|
||||
|
||||
namespace internal {
|
||||
template <typename XprType_, int BlockRows, int BlockCols, bool InnerPanel_>
|
||||
struct traits<Block<XprType_, BlockRows, BlockCols, InnerPanel_>> : traits<XprType_> {
|
||||
typedef typename traits<XprType_>::Scalar Scalar;
|
||||
typedef typename traits<XprType_>::StorageKind StorageKind;
|
||||
typedef typename traits<XprType_>::XprKind XprKind;
|
||||
typedef typename ref_selector<XprType_>::type XprTypeNested;
|
||||
typedef std::remove_reference_t<XprTypeNested> XprTypeNested_;
|
||||
enum {
|
||||
MatrixRows = traits<XprType_>::RowsAtCompileTime,
|
||||
MatrixCols = traits<XprType_>::ColsAtCompileTime,
|
||||
RowsAtCompileTime = MatrixRows == 0 ? 0 : BlockRows,
|
||||
ColsAtCompileTime = MatrixCols == 0 ? 0 : BlockCols,
|
||||
MaxRowsAtCompileTime = BlockRows == 0 ? 0
|
||||
: RowsAtCompileTime != Dynamic ? int(RowsAtCompileTime)
|
||||
: int(traits<XprType_>::MaxRowsAtCompileTime),
|
||||
MaxColsAtCompileTime = BlockCols == 0 ? 0
|
||||
: ColsAtCompileTime != Dynamic ? int(ColsAtCompileTime)
|
||||
: int(traits<XprType_>::MaxColsAtCompileTime),
|
||||
|
||||
XprTypeIsRowMajor = (int(traits<XprType_>::Flags) & RowMajorBit) != 0,
|
||||
IsRowMajor = (MaxRowsAtCompileTime == 1 && MaxColsAtCompileTime != 1) ? 1
|
||||
: (MaxColsAtCompileTime == 1 && MaxRowsAtCompileTime != 1) ? 0
|
||||
: XprTypeIsRowMajor,
|
||||
HasSameStorageOrderAsXprType = (IsRowMajor == XprTypeIsRowMajor),
|
||||
InnerSize = IsRowMajor ? int(ColsAtCompileTime) : int(RowsAtCompileTime),
|
||||
InnerStrideAtCompileTime = HasSameStorageOrderAsXprType ? int(inner_stride_at_compile_time<XprType_>::ret)
|
||||
: int(outer_stride_at_compile_time<XprType_>::ret),
|
||||
OuterStrideAtCompileTime = HasSameStorageOrderAsXprType ? int(outer_stride_at_compile_time<XprType_>::ret)
|
||||
: int(inner_stride_at_compile_time<XprType_>::ret),
|
||||
|
||||
// FIXME, this traits is rather specialized for dense object and it needs to be cleaned further
|
||||
FlagsLvalueBit = is_lvalue<XprType_>::value ? LvalueBit : 0,
|
||||
FlagsRowMajorBit = IsRowMajor ? RowMajorBit : 0,
|
||||
Flags = (traits<XprType_>::Flags & (DirectAccessBit | (InnerPanel_ ? CompressedAccessBit : 0))) | FlagsLvalueBit |
|
||||
FlagsRowMajorBit,
|
||||
// FIXME DirectAccessBit should not be handled by expressions
|
||||
//
|
||||
// Alignment is needed by MapBase's assertions
|
||||
// We can sefely set it to false here. Internal alignment errors will be detected by an eigen_internal_assert in the
|
||||
// respective evaluator
|
||||
Alignment = 0,
|
||||
InnerPanel = InnerPanel_ ? 1 : 0
|
||||
};
|
||||
};
|
||||
|
||||
template <typename XprType, int BlockRows = Dynamic, int BlockCols = Dynamic, bool InnerPanel = false,
|
||||
bool HasDirectAccess = internal::has_direct_access<XprType>::ret>
|
||||
class BlockImpl_dense;
|
||||
|
||||
} // end namespace internal
|
||||
|
||||
template <typename XprType, int BlockRows, int BlockCols, bool InnerPanel, typename StorageKind>
|
||||
class BlockImpl;
|
||||
|
||||
/** \class Block
|
||||
* \ingroup Core_Module
|
||||
*
|
||||
* \brief Expression of a fixed-size or dynamic-size block
|
||||
*
|
||||
* \tparam XprType the type of the expression in which we are taking a block
|
||||
* \tparam BlockRows the number of rows of the block we are taking at compile time (optional)
|
||||
* \tparam BlockCols the number of columns of the block we are taking at compile time (optional)
|
||||
* \tparam InnerPanel is true, if the block maps to a set of rows of a row major matrix or
|
||||
* to set of columns of a column major matrix (optional). The parameter allows to determine
|
||||
* at compile time whether aligned access is possible on the block expression.
|
||||
*
|
||||
* This class represents an expression of either a fixed-size or dynamic-size block. It is the return
|
||||
* type of DenseBase::block(Index,Index,Index,Index) and DenseBase::block<int,int>(Index,Index) and
|
||||
* most of the time this is the only way it is used.
|
||||
*
|
||||
* However, if you want to directly manipulate block expressions,
|
||||
* for instance if you want to write a function returning such an expression, you
|
||||
* will need to use this class.
|
||||
*
|
||||
* Here is an example illustrating the dynamic case:
|
||||
* \include class_Block.cpp
|
||||
* Output: \verbinclude class_Block.out
|
||||
*
|
||||
* \note Even though this expression has dynamic size, in the case where \a XprType
|
||||
* has fixed size, this expression inherits a fixed maximal size which means that evaluating
|
||||
* it does not cause a dynamic memory allocation.
|
||||
*
|
||||
* Here is an example illustrating the fixed-size case:
|
||||
* \include class_FixedBlock.cpp
|
||||
* Output: \verbinclude class_FixedBlock.out
|
||||
*
|
||||
* \sa DenseBase::block(Index,Index,Index,Index), DenseBase::block(Index,Index), class VectorBlock
|
||||
*/
|
||||
template <typename XprType, int BlockRows, int BlockCols, bool InnerPanel>
|
||||
class Block
|
||||
: public BlockImpl<XprType, BlockRows, BlockCols, InnerPanel, typename internal::traits<XprType>::StorageKind> {
|
||||
typedef BlockImpl<XprType, BlockRows, BlockCols, InnerPanel, typename internal::traits<XprType>::StorageKind> Impl;
|
||||
using BlockHelper = internal::block_xpr_helper<Block>;
|
||||
|
||||
public:
|
||||
// typedef typename Impl::Base Base;
|
||||
typedef Impl Base;
|
||||
EIGEN_GENERIC_PUBLIC_INTERFACE(Block)
|
||||
EIGEN_INHERIT_ASSIGNMENT_OPERATORS(Block)
|
||||
|
||||
typedef internal::remove_all_t<XprType> NestedExpression;
|
||||
|
||||
/** Column or Row constructor
|
||||
*/
|
||||
EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Block(XprType& xpr, Index i) : Impl(xpr, i) {
|
||||
eigen_assert((i >= 0) && (((BlockRows == 1) && (BlockCols == XprType::ColsAtCompileTime) && i < xpr.rows()) ||
|
||||
((BlockRows == XprType::RowsAtCompileTime) && (BlockCols == 1) && i < xpr.cols())));
|
||||
}
|
||||
|
||||
/** Fixed-size constructor
|
||||
*/
|
||||
EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Block(XprType& xpr, Index startRow, Index startCol)
|
||||
: Impl(xpr, startRow, startCol) {
|
||||
EIGEN_STATIC_ASSERT(RowsAtCompileTime != Dynamic && ColsAtCompileTime != Dynamic,
|
||||
THIS_METHOD_IS_ONLY_FOR_FIXED_SIZE)
|
||||
eigen_assert(startRow >= 0 && BlockRows >= 0 && startRow + BlockRows <= xpr.rows() && startCol >= 0 &&
|
||||
BlockCols >= 0 && startCol + BlockCols <= xpr.cols());
|
||||
}
|
||||
|
||||
/** Dynamic-size constructor
|
||||
*/
|
||||
EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Block(XprType& xpr, Index startRow, Index startCol, Index blockRows,
|
||||
Index blockCols)
|
||||
: Impl(xpr, startRow, startCol, blockRows, blockCols) {
|
||||
eigen_assert((RowsAtCompileTime == Dynamic || RowsAtCompileTime == blockRows) &&
|
||||
(ColsAtCompileTime == Dynamic || ColsAtCompileTime == blockCols));
|
||||
eigen_assert(startRow >= 0 && blockRows >= 0 && startRow <= xpr.rows() - blockRows && startCol >= 0 &&
|
||||
blockCols >= 0 && startCol <= xpr.cols() - blockCols);
|
||||
}
|
||||
|
||||
// convert nested blocks (e.g. Block<Block<MatrixType>>) to a simple block expression (Block<MatrixType>)
|
||||
|
||||
using ConstUnwindReturnType = Block<const typename BlockHelper::BaseType, BlockRows, BlockCols, InnerPanel>;
|
||||
using UnwindReturnType = Block<typename BlockHelper::BaseType, BlockRows, BlockCols, InnerPanel>;
|
||||
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE ConstUnwindReturnType unwind() const {
|
||||
return ConstUnwindReturnType(BlockHelper::base(*this), BlockHelper::row(*this, 0), BlockHelper::col(*this, 0),
|
||||
this->rows(), this->cols());
|
||||
}
|
||||
|
||||
template <typename T = Block, typename EnableIf = std::enable_if_t<!std::is_const<T>::value>>
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE UnwindReturnType unwind() {
|
||||
return UnwindReturnType(BlockHelper::base(*this), BlockHelper::row(*this, 0), BlockHelper::col(*this, 0),
|
||||
this->rows(), this->cols());
|
||||
}
|
||||
};
|
||||
|
||||
// The generic default implementation for dense block simply forward to the internal::BlockImpl_dense
|
||||
// that must be specialized for direct and non-direct access...
|
||||
template <typename XprType, int BlockRows, int BlockCols, bool InnerPanel>
|
||||
class BlockImpl<XprType, BlockRows, BlockCols, InnerPanel, Dense>
|
||||
: public internal::BlockImpl_dense<XprType, BlockRows, BlockCols, InnerPanel> {
|
||||
typedef internal::BlockImpl_dense<XprType, BlockRows, BlockCols, InnerPanel> Impl;
|
||||
typedef typename XprType::StorageIndex StorageIndex;
|
||||
|
||||
public:
|
||||
typedef Impl Base;
|
||||
EIGEN_INHERIT_ASSIGNMENT_OPERATORS(BlockImpl)
|
||||
EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE BlockImpl(XprType& xpr, Index i) : Impl(xpr, i) {}
|
||||
EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE BlockImpl(XprType& xpr, Index startRow, Index startCol)
|
||||
: Impl(xpr, startRow, startCol) {}
|
||||
EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE BlockImpl(XprType& xpr, Index startRow, Index startCol,
|
||||
Index blockRows, Index blockCols)
|
||||
: Impl(xpr, startRow, startCol, blockRows, blockCols) {}
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
/** \internal Internal implementation of dense Blocks in the general case. */
|
||||
template <typename XprType, int BlockRows, int BlockCols, bool InnerPanel, bool HasDirectAccess>
|
||||
class BlockImpl_dense : public internal::dense_xpr_base<Block<XprType, BlockRows, BlockCols, InnerPanel>>::type {
|
||||
typedef Block<XprType, BlockRows, BlockCols, InnerPanel> BlockType;
|
||||
typedef typename internal::ref_selector<XprType>::non_const_type XprTypeNested;
|
||||
|
||||
public:
|
||||
typedef typename internal::dense_xpr_base<BlockType>::type Base;
|
||||
EIGEN_DENSE_PUBLIC_INTERFACE(BlockType)
|
||||
EIGEN_INHERIT_ASSIGNMENT_OPERATORS(BlockImpl_dense)
|
||||
|
||||
/** Column or Row constructor
|
||||
*/
|
||||
EIGEN_DEVICE_FUNC constexpr BlockImpl_dense(XprType& xpr, Index i)
|
||||
: m_xpr(xpr),
|
||||
// It is a row if and only if BlockRows==1 and BlockCols==XprType::ColsAtCompileTime,
|
||||
// and it is a column if and only if BlockRows==XprType::RowsAtCompileTime and BlockCols==1,
|
||||
// all other cases are invalid.
|
||||
// The case a 1x1 matrix seems ambiguous, but the result is the same anyway.
|
||||
m_startRow((BlockRows == 1) && (BlockCols == XprType::ColsAtCompileTime) ? i : 0),
|
||||
m_startCol((BlockRows == XprType::RowsAtCompileTime) && (BlockCols == 1) ? i : 0),
|
||||
m_blockRows(BlockRows == 1 ? 1 : xpr.rows()),
|
||||
m_blockCols(BlockCols == 1 ? 1 : xpr.cols()) {}
|
||||
|
||||
/** Fixed-size constructor
|
||||
*/
|
||||
EIGEN_DEVICE_FUNC constexpr BlockImpl_dense(XprType& xpr, Index startRow, Index startCol)
|
||||
: m_xpr(xpr), m_startRow(startRow), m_startCol(startCol), m_blockRows(BlockRows), m_blockCols(BlockCols) {}
|
||||
|
||||
/** Dynamic-size constructor
|
||||
*/
|
||||
EIGEN_DEVICE_FUNC constexpr BlockImpl_dense(XprType& xpr, Index startRow, Index startCol, Index blockRows,
|
||||
Index blockCols)
|
||||
: m_xpr(xpr), m_startRow(startRow), m_startCol(startCol), m_blockRows(blockRows), m_blockCols(blockCols) {}
|
||||
|
||||
EIGEN_DEVICE_FUNC constexpr Index rows() const { return m_blockRows.value(); }
|
||||
EIGEN_DEVICE_FUNC constexpr Index cols() const { return m_blockCols.value(); }
|
||||
|
||||
EIGEN_DEVICE_FUNC inline Scalar& coeffRef(Index rowId, Index colId) {
|
||||
EIGEN_STATIC_ASSERT_LVALUE(XprType)
|
||||
return m_xpr.coeffRef(rowId + m_startRow.value(), colId + m_startCol.value());
|
||||
}
|
||||
|
||||
EIGEN_DEVICE_FUNC inline const Scalar& coeffRef(Index rowId, Index colId) const {
|
||||
return m_xpr.derived().coeffRef(rowId + m_startRow.value(), colId + m_startCol.value());
|
||||
}
|
||||
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const CoeffReturnType coeff(Index rowId, Index colId) const {
|
||||
return m_xpr.coeff(rowId + m_startRow.value(), colId + m_startCol.value());
|
||||
}
|
||||
|
||||
EIGEN_DEVICE_FUNC inline Scalar& coeffRef(Index index) {
|
||||
EIGEN_STATIC_ASSERT_LVALUE(XprType)
|
||||
return m_xpr.coeffRef(m_startRow.value() + (RowsAtCompileTime == 1 ? 0 : index),
|
||||
m_startCol.value() + (RowsAtCompileTime == 1 ? index : 0));
|
||||
}
|
||||
|
||||
EIGEN_DEVICE_FUNC inline const Scalar& coeffRef(Index index) const {
|
||||
return m_xpr.coeffRef(m_startRow.value() + (RowsAtCompileTime == 1 ? 0 : index),
|
||||
m_startCol.value() + (RowsAtCompileTime == 1 ? index : 0));
|
||||
}
|
||||
|
||||
EIGEN_DEVICE_FUNC inline const CoeffReturnType coeff(Index index) const {
|
||||
return m_xpr.coeff(m_startRow.value() + (RowsAtCompileTime == 1 ? 0 : index),
|
||||
m_startCol.value() + (RowsAtCompileTime == 1 ? index : 0));
|
||||
}
|
||||
|
||||
template <int LoadMode>
|
||||
EIGEN_DEVICE_FUNC inline PacketScalar packet(Index rowId, Index colId) const {
|
||||
return m_xpr.template packet<Unaligned>(rowId + m_startRow.value(), colId + m_startCol.value());
|
||||
}
|
||||
|
||||
template <int LoadMode>
|
||||
EIGEN_DEVICE_FUNC inline void writePacket(Index rowId, Index colId, const PacketScalar& val) {
|
||||
m_xpr.template writePacket<Unaligned>(rowId + m_startRow.value(), colId + m_startCol.value(), val);
|
||||
}
|
||||
|
||||
template <int LoadMode>
|
||||
EIGEN_DEVICE_FUNC inline PacketScalar packet(Index index) const {
|
||||
return m_xpr.template packet<Unaligned>(m_startRow.value() + (RowsAtCompileTime == 1 ? 0 : index),
|
||||
m_startCol.value() + (RowsAtCompileTime == 1 ? index : 0));
|
||||
}
|
||||
|
||||
template <int LoadMode>
|
||||
EIGEN_DEVICE_FUNC inline void writePacket(Index index, const PacketScalar& val) {
|
||||
m_xpr.template writePacket<Unaligned>(m_startRow.value() + (RowsAtCompileTime == 1 ? 0 : index),
|
||||
m_startCol.value() + (RowsAtCompileTime == 1 ? index : 0), val);
|
||||
}
|
||||
|
||||
#ifdef EIGEN_PARSED_BY_DOXYGEN
|
||||
/** \sa MapBase::data() */
|
||||
EIGEN_DEVICE_FUNC constexpr const Scalar* data() const;
|
||||
EIGEN_DEVICE_FUNC inline Index innerStride() const;
|
||||
EIGEN_DEVICE_FUNC inline Index outerStride() const;
|
||||
#endif
|
||||
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const internal::remove_all_t<XprTypeNested>& nestedExpression() const {
|
||||
return m_xpr;
|
||||
}
|
||||
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE XprType& nestedExpression() { return m_xpr; }
|
||||
|
||||
EIGEN_DEVICE_FUNC constexpr StorageIndex startRow() const noexcept { return m_startRow.value(); }
|
||||
|
||||
EIGEN_DEVICE_FUNC constexpr StorageIndex startCol() const noexcept { return m_startCol.value(); }
|
||||
|
||||
protected:
|
||||
XprTypeNested m_xpr;
|
||||
const internal::variable_if_dynamic<StorageIndex, (XprType::RowsAtCompileTime == 1 && BlockRows == 1) ? 0 : Dynamic>
|
||||
m_startRow;
|
||||
const internal::variable_if_dynamic<StorageIndex, (XprType::ColsAtCompileTime == 1 && BlockCols == 1) ? 0 : Dynamic>
|
||||
m_startCol;
|
||||
const internal::variable_if_dynamic<StorageIndex, RowsAtCompileTime> m_blockRows;
|
||||
const internal::variable_if_dynamic<StorageIndex, ColsAtCompileTime> m_blockCols;
|
||||
};
|
||||
|
||||
/** \internal Internal implementation of dense Blocks in the direct access case.*/
|
||||
template <typename XprType, int BlockRows, int BlockCols, bool InnerPanel>
|
||||
class BlockImpl_dense<XprType, BlockRows, BlockCols, InnerPanel, true>
|
||||
: public MapBase<Block<XprType, BlockRows, BlockCols, InnerPanel>> {
|
||||
typedef Block<XprType, BlockRows, BlockCols, InnerPanel> BlockType;
|
||||
typedef typename internal::ref_selector<XprType>::non_const_type XprTypeNested;
|
||||
enum { XprTypeIsRowMajor = (int(traits<XprType>::Flags) & RowMajorBit) != 0 };
|
||||
|
||||
/** \internal Returns base+offset (unless base is null, in which case returns null).
|
||||
* Adding an offset to nullptr is undefined behavior, so we must avoid it.
|
||||
*/
|
||||
template <typename Scalar>
|
||||
EIGEN_DEVICE_FUNC constexpr EIGEN_ALWAYS_INLINE static Scalar* add_to_nullable_pointer(Scalar* base, Index offset) {
|
||||
return base != nullptr ? base + offset : nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
typedef MapBase<BlockType> Base;
|
||||
EIGEN_DENSE_PUBLIC_INTERFACE(BlockType)
|
||||
EIGEN_INHERIT_ASSIGNMENT_OPERATORS(BlockImpl_dense)
|
||||
|
||||
/** Column or Row constructor
|
||||
*/
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE BlockImpl_dense(XprType& xpr, Index i)
|
||||
: Base((BlockRows == 0 || BlockCols == 0)
|
||||
? nullptr
|
||||
: add_to_nullable_pointer(
|
||||
xpr.data(),
|
||||
i * (((BlockRows == 1) && (BlockCols == XprType::ColsAtCompileTime) && (!XprTypeIsRowMajor)) ||
|
||||
((BlockRows == XprType::RowsAtCompileTime) && (BlockCols == 1) &&
|
||||
(XprTypeIsRowMajor))
|
||||
? xpr.innerStride()
|
||||
: xpr.outerStride())),
|
||||
BlockRows == 1 ? 1 : xpr.rows(), BlockCols == 1 ? 1 : xpr.cols()),
|
||||
m_xpr(xpr),
|
||||
m_startRow((BlockRows == 1) && (BlockCols == XprType::ColsAtCompileTime) ? i : 0),
|
||||
m_startCol((BlockRows == XprType::RowsAtCompileTime) && (BlockCols == 1) ? i : 0) {
|
||||
init();
|
||||
}
|
||||
|
||||
/** Fixed-size constructor
|
||||
*/
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE BlockImpl_dense(XprType& xpr, Index startRow, Index startCol)
|
||||
: Base((BlockRows == 0 || BlockCols == 0)
|
||||
? nullptr
|
||||
: add_to_nullable_pointer(xpr.data(),
|
||||
xpr.innerStride() * (XprTypeIsRowMajor ? startCol : startRow) +
|
||||
xpr.outerStride() * (XprTypeIsRowMajor ? startRow : startCol))),
|
||||
m_xpr(xpr),
|
||||
m_startRow(startRow),
|
||||
m_startCol(startCol) {
|
||||
init();
|
||||
}
|
||||
|
||||
/** Dynamic-size constructor
|
||||
*/
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE BlockImpl_dense(XprType& xpr, Index startRow, Index startCol, Index blockRows,
|
||||
Index blockCols)
|
||||
: Base((blockRows == 0 || blockCols == 0)
|
||||
? nullptr
|
||||
: add_to_nullable_pointer(xpr.data(),
|
||||
xpr.innerStride() * (XprTypeIsRowMajor ? startCol : startRow) +
|
||||
xpr.outerStride() * (XprTypeIsRowMajor ? startRow : startCol)),
|
||||
blockRows, blockCols),
|
||||
m_xpr(xpr),
|
||||
m_startRow(startRow),
|
||||
m_startCol(startCol) {
|
||||
init();
|
||||
}
|
||||
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const internal::remove_all_t<XprTypeNested>& nestedExpression() const noexcept {
|
||||
return m_xpr;
|
||||
}
|
||||
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE XprType& nestedExpression() { return m_xpr; }
|
||||
|
||||
/** \sa MapBase::innerStride() */
|
||||
EIGEN_DEVICE_FUNC constexpr Index innerStride() const noexcept {
|
||||
return internal::traits<BlockType>::HasSameStorageOrderAsXprType ? m_xpr.innerStride() : m_xpr.outerStride();
|
||||
}
|
||||
|
||||
/** \sa MapBase::outerStride() */
|
||||
EIGEN_DEVICE_FUNC constexpr Index outerStride() const noexcept {
|
||||
return internal::traits<BlockType>::HasSameStorageOrderAsXprType ? m_xpr.outerStride() : m_xpr.innerStride();
|
||||
}
|
||||
|
||||
EIGEN_DEVICE_FUNC constexpr StorageIndex startRow() const noexcept { return m_startRow.value(); }
|
||||
|
||||
EIGEN_DEVICE_FUNC constexpr StorageIndex startCol() const noexcept { return m_startCol.value(); }
|
||||
|
||||
#ifndef __SUNPRO_CC
|
||||
// FIXME sunstudio is not friendly with the above friend...
|
||||
// META-FIXME there is no 'friend' keyword around here. Is this obsolete?
|
||||
protected:
|
||||
#endif
|
||||
|
||||
#ifndef EIGEN_PARSED_BY_DOXYGEN
|
||||
/** \internal used by allowAligned() */
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE BlockImpl_dense(XprType& xpr, const Scalar* data, Index blockRows,
|
||||
Index blockCols)
|
||||
: Base(data, blockRows, blockCols), m_xpr(xpr) {
|
||||
init();
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void init() {
|
||||
m_outerStride =
|
||||
internal::traits<BlockType>::HasSameStorageOrderAsXprType ? m_xpr.outerStride() : m_xpr.innerStride();
|
||||
}
|
||||
|
||||
XprTypeNested m_xpr;
|
||||
const internal::variable_if_dynamic<StorageIndex, (XprType::RowsAtCompileTime == 1 && BlockRows == 1) ? 0 : Dynamic>
|
||||
m_startRow;
|
||||
const internal::variable_if_dynamic<StorageIndex, (XprType::ColsAtCompileTime == 1 && BlockCols == 1) ? 0 : Dynamic>
|
||||
m_startCol;
|
||||
Index m_outerStride;
|
||||
};
|
||||
|
||||
} // end namespace internal
|
||||
|
||||
} // end namespace Eigen
|
||||
|
||||
#endif // EIGEN_BLOCK_H
|
||||
@@ -0,0 +1,148 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef EIGEN_COMMAINITIALIZER_H
|
||||
#define EIGEN_COMMAINITIALIZER_H
|
||||
|
||||
// IWYU pragma: private
|
||||
#include "./InternalHeaderCheck.h"
|
||||
|
||||
namespace Eigen {
|
||||
|
||||
/** \class CommaInitializer
|
||||
* \ingroup Core_Module
|
||||
*
|
||||
* \brief Helper class used by the comma initializer operator
|
||||
*
|
||||
* This class is internally used to implement the comma initializer feature. It is
|
||||
* the return type of MatrixBase::operator<<, and most of the time this is the only
|
||||
* way it is used.
|
||||
*
|
||||
* \sa \blank \ref MatrixBaseCommaInitRef "MatrixBase::operator<<", CommaInitializer::finished()
|
||||
*/
|
||||
template <typename XprType>
|
||||
struct CommaInitializer {
|
||||
typedef typename XprType::Scalar Scalar;
|
||||
|
||||
EIGEN_DEVICE_FUNC constexpr CommaInitializer(XprType& xpr, const Scalar& s)
|
||||
: m_xpr(xpr), m_row(0), m_col(1), m_currentBlockRows(1) {
|
||||
eigen_assert(m_xpr.rows() > 0 && m_xpr.cols() > 0 && "Cannot comma-initialize a 0x0 matrix (operator<<)");
|
||||
m_xpr.coeffRef(0, 0) = s;
|
||||
}
|
||||
|
||||
template <typename OtherDerived>
|
||||
EIGEN_DEVICE_FUNC inline CommaInitializer(XprType& xpr, const DenseBase<OtherDerived>& other)
|
||||
: m_xpr(xpr), m_row(0), m_col(other.cols()), m_currentBlockRows(other.rows()) {
|
||||
eigen_assert(m_xpr.rows() >= other.rows() && m_xpr.cols() >= other.cols() &&
|
||||
"Cannot comma-initialize a 0x0 matrix (operator<<)");
|
||||
m_xpr.template block<OtherDerived::RowsAtCompileTime, OtherDerived::ColsAtCompileTime>(0, 0, other.rows(),
|
||||
other.cols()) = other;
|
||||
}
|
||||
|
||||
/* Copy/Move constructor which transfers ownership. This is crucial in
|
||||
* absence of return value optimization to avoid assertions during destruction. */
|
||||
EIGEN_DEVICE_FUNC inline CommaInitializer(const CommaInitializer& o)
|
||||
: m_xpr(o.m_xpr), m_row(o.m_row), m_col(o.m_col), m_currentBlockRows(o.m_currentBlockRows) {
|
||||
// Mark original object as finished. In absence of R-value references we need to const_cast:
|
||||
const_cast<CommaInitializer&>(o).m_row = m_xpr.rows();
|
||||
const_cast<CommaInitializer&>(o).m_col = m_xpr.cols();
|
||||
const_cast<CommaInitializer&>(o).m_currentBlockRows = 0;
|
||||
}
|
||||
|
||||
/* inserts a scalar value in the target matrix */
|
||||
EIGEN_DEVICE_FUNC CommaInitializer &operator,(const Scalar& s) {
|
||||
if (m_col == m_xpr.cols()) {
|
||||
m_row += m_currentBlockRows;
|
||||
m_col = 0;
|
||||
m_currentBlockRows = 1;
|
||||
eigen_assert(m_row < m_xpr.rows() && "Too many rows passed to comma initializer (operator<<)");
|
||||
}
|
||||
eigen_assert(m_col < m_xpr.cols() && "Too many coefficients passed to comma initializer (operator<<)");
|
||||
eigen_assert(m_currentBlockRows == 1);
|
||||
m_xpr.coeffRef(m_row, m_col++) = s;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* inserts a matrix expression in the target matrix */
|
||||
template <typename OtherDerived>
|
||||
EIGEN_DEVICE_FUNC CommaInitializer &operator,(const DenseBase<OtherDerived>& other) {
|
||||
if (m_col == m_xpr.cols() && (other.cols() != 0 || other.rows() != m_currentBlockRows)) {
|
||||
m_row += m_currentBlockRows;
|
||||
m_col = 0;
|
||||
m_currentBlockRows = other.rows();
|
||||
eigen_assert(m_row + m_currentBlockRows <= m_xpr.rows() &&
|
||||
"Too many rows passed to comma initializer (operator<<)");
|
||||
}
|
||||
eigen_assert((m_col + other.cols() <= m_xpr.cols()) &&
|
||||
"Too many coefficients passed to comma initializer (operator<<)");
|
||||
eigen_assert(m_currentBlockRows == other.rows());
|
||||
m_xpr.template block<OtherDerived::RowsAtCompileTime, OtherDerived::ColsAtCompileTime>(m_row, m_col, other.rows(),
|
||||
other.cols()) = other;
|
||||
m_col += other.cols();
|
||||
return *this;
|
||||
}
|
||||
|
||||
EIGEN_DEVICE_FUNC inline ~CommaInitializer()
|
||||
#if defined VERIFY_RAISES_ASSERT && (!defined EIGEN_NO_ASSERTION_CHECKING) && defined EIGEN_EXCEPTIONS
|
||||
noexcept(false) // Eigen::eigen_assert_exception
|
||||
#endif
|
||||
{
|
||||
finished();
|
||||
}
|
||||
|
||||
/** \returns the built matrix once all its coefficients have been set.
|
||||
* Calling finished is 100% optional. Its purpose is to write expressions
|
||||
* like this:
|
||||
* \code
|
||||
* quaternion.fromRotationMatrix((Matrix3f() << axis0, axis1, axis2).finished());
|
||||
* \endcode
|
||||
*/
|
||||
EIGEN_DEVICE_FUNC inline XprType& finished() {
|
||||
eigen_assert(((m_row + m_currentBlockRows) == m_xpr.rows() || m_xpr.cols() == 0) && m_col == m_xpr.cols() &&
|
||||
"Too few coefficients passed to comma initializer (operator<<)");
|
||||
return m_xpr;
|
||||
}
|
||||
|
||||
XprType& m_xpr; // target expression
|
||||
Index m_row; // current row id
|
||||
Index m_col; // current col id
|
||||
Index m_currentBlockRows; // current block height
|
||||
};
|
||||
|
||||
/** \anchor MatrixBaseCommaInitRef
|
||||
* Convenient operator to set the coefficients of a matrix.
|
||||
*
|
||||
* The coefficients must be provided in a row major order and exactly match
|
||||
* the size of the matrix. Otherwise an assertion is raised.
|
||||
*
|
||||
* Example: \include MatrixBase_set.cpp
|
||||
* Output: \verbinclude MatrixBase_set.out
|
||||
*
|
||||
* \note According the c++ standard, the argument expressions of this comma initializer are evaluated in arbitrary
|
||||
* order.
|
||||
*
|
||||
* \sa CommaInitializer::finished(), class CommaInitializer
|
||||
*/
|
||||
template <typename Derived>
|
||||
EIGEN_DEVICE_FUNC inline CommaInitializer<Derived> DenseBase<Derived>::operator<<(const Scalar& s) {
|
||||
return CommaInitializer<Derived>(*static_cast<Derived*>(this), s);
|
||||
}
|
||||
|
||||
/** \sa operator<<(const Scalar&) */
|
||||
template <typename Derived>
|
||||
template <typename OtherDerived>
|
||||
EIGEN_DEVICE_FUNC inline CommaInitializer<Derived> DenseBase<Derived>::operator<<(
|
||||
const DenseBase<OtherDerived>& other) {
|
||||
return CommaInitializer<Derived>(*static_cast<Derived*>(this), other);
|
||||
}
|
||||
|
||||
} // end namespace Eigen
|
||||
|
||||
#endif // EIGEN_COMMAINITIALIZER_H
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user