diff --git a/include/qubit/qmath_fabric.hpp b/include/qubit/qmath_fabric.hpp new file mode 100644 index 00000000..df758f05 --- /dev/null +++ b/include/qubit/qmath_fabric.hpp @@ -0,0 +1,165 @@ +#pragma once + +#include "qubit/qmath_language.hpp" +#include "qubit/qrepresentation_compiler.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace qubit { + +struct QMathFabricChannelBinding { + std::string dependency{}; + QubitId channel{0U}; + + friend bool operator==(const QMathFabricChannelBinding&, const QMathFabricChannelBinding&) = default; +}; + +struct QMathFabricBindingReceipt { + QMathLanguageRoute route{QMathLanguageRoute::Symbolic}; + QMathFidelity fidelity{QMathFidelity::Unverified}; + ExactRepresentationDependencyType dependency_type{0U}; + std::vector bindings{}; + std::size_t component{0U}; + std::size_t generation{0U}; + std::size_t numerical_generation{0U}; + std::size_t declared_dependencies_before{0U}; + std::size_t declared_dependencies_after{0U}; + std::size_t component_merges{0U}; + bool prior_component_known{false}; + bool numerical_generation_preserved{false}; + bool exact{false}; + bool structural_dependency{true}; + std::string source_canonical{}; +}; + +class QMathRepresentationFabricBridge { +public: + [[nodiscard]] static QMathFabricBindingReceipt bind( + ExactRepresentationFabric& fabric, + const QMathLanguageCompilation& source, + std::span bindings) { + require_supported(source); + if (bindings.size() != source.receipt.dependencies.size() || bindings.size() < 2U) { + throw QMathError("QMath fabric binding must cover every source dependency exactly once"); + } + + std::vector support; + support.reserve(bindings.size()); + for (std::size_t index = 0U; index < bindings.size(); ++index) { + if (bindings[index].dependency != source.receipt.dependencies[index]) { + throw QMathError("QMath fabric binding dependency order or identity changed"); + } + support.push_back(bindings[index].channel); + } + std::vector normalized = support; + std::sort(normalized.begin(), normalized.end()); + if (std::adjacent_find(normalized.begin(), normalized.end()) != normalized.end()) { + throw QMathError("QMath fabric binding maps distinct dependencies onto one channel"); + } + + const ExactRepresentationDependencyType type = dependency_type(source.receipt.route); + const std::vector before_dependencies = fabric.dependency_receipts(); + for (const ExactRepresentationDependencyReceipt& receipt : before_dependencies) { + if (receipt.type == type && receipt.support == normalized) { + throw QMathError("QMath fabric dependency is already bound"); + } + } + const std::optional prior = + component_for_support(fabric, before_dependencies, normalized); + const ExactRepresentationFabricStats before = fabric.stats(); + + fabric.declare_dependency(type, support); + + const ExactRepresentationFabricStats after = fabric.stats(); + const std::vector dependency_receipts = fabric.dependency_receipts(); + const ExactRepresentationDependencyReceipt* added = nullptr; + for (const ExactRepresentationDependencyReceipt& receipt : dependency_receipts) { + if (receipt.type == type && receipt.support == normalized) { + if (added != nullptr) { + throw QMathError("QMath fabric dependency identity became ambiguous"); + } + added = &receipt; + } + } + if (added == nullptr || after.declared_dependencies != before.declared_dependencies + 1U) { + throw QMathError("QMath fabric did not retain the declared typed dependency"); + } + + QMathFabricBindingReceipt result; + result.route = source.receipt.route; + result.fidelity = source.receipt.evidence.fidelity; + result.dependency_type = type; + result.bindings.assign(bindings.begin(), bindings.end()); + result.component = added->component; + result.generation = added->generation; + result.numerical_generation = added->numerical_generation; + result.declared_dependencies_before = before.declared_dependencies; + result.declared_dependencies_after = after.declared_dependencies; + result.component_merges = after.component_merges - before.component_merges; + result.prior_component_known = prior.has_value(); + result.numerical_generation_preserved = prior.has_value() && + prior->component == added->component && + prior->numerical_generation == added->numerical_generation; + result.exact = source.receipt.exact; + result.source_canonical = source.receipt.canonical; + return result; + } + + [[nodiscard]] static constexpr ExactRepresentationDependencyType dependency_type( + QMathLanguageRoute route) { + switch (route) { + case QMathLanguageRoute::StrictOrder: + return 0x514D415448000001ULL; + case QMathLanguageRoute::AffineRelation: + return 0x514D415448000002ULL; + case QMathLanguageRoute::HornLogic: + return 0x514D415448000003ULL; + default: + throw QMathError("QMath language route has no persistent fabric dependency contract"); + } + } + +private: + static void require_supported(const QMathLanguageCompilation& source) { + if (!source.receipt.exact || !source.receipt.transform_ready || + !source.receipt.evidence.exact_structure()) { + throw QMathError("QMath fabric requires exact structured mathematical-language evidence"); + } + (void)dependency_type(source.receipt.route); + } + + [[nodiscard]] static std::optional component_for_support( + const ExactRepresentationFabric& fabric, + std::span dependencies, + std::span support) { + std::optional component; + for (const QubitId channel : support) { + std::optional channel_component; + for (const ExactRepresentationDependencyReceipt& dependency : dependencies) { + if (std::binary_search(dependency.support.begin(), dependency.support.end(), channel)) { + if (channel_component.has_value() && *channel_component != dependency.component) { + throw QMathError("QMath fabric dependency receipts disagree on channel component"); + } + channel_component = dependency.component; + } + } + if (!channel_component.has_value()) return std::nullopt; + if (component.has_value() && *component != *channel_component) return std::nullopt; + component = channel_component; + } + if (!component.has_value()) return std::nullopt; + for (const ExactRepresentationComponentReceipt& receipt : fabric.component_receipts()) { + if (receipt.component == *component) return receipt; + } + throw QMathError("QMath fabric component receipt is missing"); + } +}; + +} // namespace qubit diff --git a/tests/test_qlogic.cpp b/tests/test_qlogic.cpp index bc0d1827..43f1750f 100644 --- a/tests/test_qlogic.cpp +++ b/tests/test_qlogic.cpp @@ -1,4 +1,4 @@ -#include "qubit/qmath_language.hpp" +#include "qubit/qmath_fabric.hpp" #include #include @@ -152,5 +152,122 @@ int main() { } require(rejected, "Horn rule cap did not fail closed"); - std::cout << "QHorn logic tests passed\n"; + QStrictOrder order; + const auto order_a = order.add_symbol("A"); + const auto order_b = order.add_symbol("B"); + const auto order_c = order.add_symbol("C"); + require(order.add_before(order_a, order_b) && order.add_before(order_b, order_c), + "fabric strict-order fixture was not admitted"); + const QMathLanguageCompilation order_language = QMathLanguageCompiler::compile(order); + + ExactRepresentationFabric fabric(8U); + const std::vector order_bindings{ + {"A", 0U}, + {"B", 1U}, + {"C", 2U}, + }; + const QMathFabricBindingReceipt order_binding = + QMathRepresentationFabricBridge::bind(fabric, order_language, order_bindings); + require(order_binding.route == QMathLanguageRoute::StrictOrder && + order_binding.dependency_type == QMathRepresentationFabricBridge::dependency_type(QMathLanguageRoute::StrictOrder) && + order_binding.declared_dependencies_before == 0U && + order_binding.declared_dependencies_after == 1U && + !order_binding.prior_component_known && !order_binding.numerical_generation_preserved && + order_binding.exact && order_binding.structural_dependency && + order_binding.source_canonical == order_language.receipt.canonical, + "QMath fabric lost strict-order binding identity or first-bind receipt"); + + QHornLogic shared_logic; + const auto logic_a = shared_logic.add_atom("A"); + const auto logic_b = shared_logic.add_atom("B"); + const auto logic_c = shared_logic.add_atom("C"); + require(shared_logic.add_fact(logic_a, true), "fabric Horn fixture fact was not admitted"); + const std::vector logic_rule_ab{{logic_a, true}}; + const std::vector logic_rule_bc{{logic_b, true}}; + require(shared_logic.add_rule(logic_rule_ab, {logic_b, true}) && + shared_logic.add_rule(logic_rule_bc, {logic_c, true}), + "fabric Horn fixture rules were not admitted"); + const QMathLanguageCompilation shared_logic_language = QMathLanguageCompiler::compile(shared_logic); + const QMathFabricBindingReceipt logic_binding = + QMathRepresentationFabricBridge::bind(fabric, shared_logic_language, order_bindings); + require(logic_binding.route == QMathLanguageRoute::HornLogic && + logic_binding.component == order_binding.component && + logic_binding.prior_component_known && logic_binding.numerical_generation_preserved && + logic_binding.numerical_generation == order_binding.numerical_generation && + logic_binding.generation > order_binding.generation && + logic_binding.component_merges == 0U && + logic_binding.declared_dependencies_after == 2U, + "QMath fabric metadata-only typed dependency changed numerical generation"); + + QAffineRelationSpace separate_relation(2U); + const auto relation_x = separate_relation.add_symbol("X"); + const auto relation_y = separate_relation.add_symbol("Y"); + const QAffineRelationSpace::Displacement relation_delta{QRational(1), QRational(-1)}; + require(separate_relation.add_difference(relation_x, relation_y, relation_delta), + "fabric affine fixture relation was not admitted"); + const QMathLanguageCompilation relation_language = QMathLanguageCompiler::compile(separate_relation); + const std::vector relation_bindings{ + {"X", 3U}, + {"Y", 4U}, + }; + const QMathFabricBindingReceipt relation_binding = + QMathRepresentationFabricBridge::bind(fabric, relation_language, relation_bindings); + require(relation_binding.component != order_binding.component && + fabric.stats().active_components == 2U && relation_binding.component_merges == 0U, + "QMath fabric failed to preserve independent mathematical islands"); + + QHornLogic bridge_logic; + const auto bridge_a = bridge_logic.add_atom("A"); + const auto bridge_x = bridge_logic.add_atom("X"); + require(bridge_logic.add_fact(bridge_a, true), "fabric bridge fact was not admitted"); + const std::vector bridge_rule{{bridge_a, true}}; + require(bridge_logic.add_rule(bridge_rule, {bridge_x, true}), + "fabric bridge rule was not admitted"); + const QMathLanguageCompilation bridge_language = QMathLanguageCompiler::compile(bridge_logic); + const std::vector bridge_bindings{ + {"A", 0U}, + {"X", 3U}, + }; + const QMathFabricBindingReceipt bridge_binding = + QMathRepresentationFabricBridge::bind(fabric, bridge_language, bridge_bindings); + require(bridge_binding.component_merges == 1U && + !bridge_binding.prior_component_known && !bridge_binding.numerical_generation_preserved && + fabric.stats().active_components == 1U, + "QMath fabric did not merge exactly the typed cross-island dependency closure"); + + rejected = false; + try { + (void)QMathRepresentationFabricBridge::bind(fabric, order_language, order_bindings); + } catch (const QMathError&) { + rejected = true; + } + require(rejected, "QMath fabric accepted a duplicate typed dependency binding"); + + const std::vector aliased_bindings{ + {"A", 5U}, + {"B", 5U}, + {"C", 6U}, + }; + rejected = false; + try { + ExactRepresentationFabric alias_fabric(8U); + (void)QMathRepresentationFabricBridge::bind(alias_fabric, order_language, aliased_bindings); + } catch (const QMathError&) { + rejected = true; + } + require(rejected, "QMath fabric accepted an aliased semantic dependency channel mapping"); + + QMathArena symbolic_math; + const auto symbolic = symbolic_math.symbol("z", QMathType::scalar_type()); + const QMathLanguageCompilation symbolic_language = QMathLanguageCompiler::compile(symbolic, symbolic_math); + const std::vector symbolic_bindings{{"z", 7U}}; + rejected = false; + try { + (void)QMathRepresentationFabricBridge::bind(fabric, symbolic_language, symbolic_bindings); + } catch (const QMathError&) { + rejected = true; + } + require(rejected, "QMath fabric accepted an unsupported symbolic route as a dependency fabric program"); + + std::cout << "QHorn logic and mathematical fabric tests passed\n"; }