-
Notifications
You must be signed in to change notification settings - Fork 15
Support phlex::sources and implicit providers
#637
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -107,6 +107,17 @@ namespace phlex::experimental { | |
| std::move(name), std::move(pred), std::move(unf), c, std::move(destination_data_layer)); | ||
| } | ||
|
|
||
| /// @brief Registers a source (used by the framework to create provider nodes) | ||
| template <std::derived_from<source> Source, typename... Args> | ||
| void source(std::string name, Args&&... args) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same comment as put in |
||
| requires(not is_bound_object<T>) | ||
| { | ||
| // The bound object is created when invoking source<Source>(...), so we explicitly indicate that | ||
| // no bound object should be used in the create_glue(...) call. | ||
| return create_glue(false).template source<Source>(std::move(name), | ||
| std::forward<Args>(args)...); | ||
| } | ||
|
|
||
| /// @brief Registers an output node. | ||
| auto output(std::string name, is_output_like auto f, concurrency c = concurrency::serial) | ||
| { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,14 @@ | ||
| #include "phlex/core/make_computational_edges.hpp" | ||
|
|
||
| #include "fmt/format.h" | ||
| #include "oneapi/tbb/flow_graph.h" | ||
| #include "spdlog/spdlog.h" | ||
|
|
||
| #include <algorithm> | ||
| #include <cassert> | ||
| #include <ranges> | ||
| #include <span> | ||
| #include <stdexcept> | ||
|
|
||
| using namespace std::string_literals; | ||
|
|
||
|
|
@@ -26,14 +28,24 @@ namespace phlex::experimental { | |
| return nullptr; | ||
| } | ||
|
|
||
| index_router::provider_input_ports_t edges_from_explicit_providers( | ||
| index_router::head_ports_t head_ports, provider_nodes& explicit_providers) | ||
| provider_bundles find_matching_implicit_providers(source_map const& sources, | ||
| product_selector const& input_product) | ||
| { | ||
| assert(!head_ports.empty()); | ||
| provider_bundles result; | ||
| for (auto const& src : sources | std::views::values) { | ||
| result.append_range(src->create_providers(input_product)); | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| // FIXME: Should return a list of head ports that cannot be matched to an explicit provider. | ||
| std::pair<index_router::provider_input_ports_t, index_router::head_ports_t> | ||
| edges_from_explicit_providers(index_router::head_ports_t head_ports, | ||
| provider_nodes& explicit_providers) | ||
| { | ||
| assert(!head_ports.empty()); | ||
|
|
||
| index_router::provider_input_ports_t result; | ||
| index_router::head_ports_t unconsumed_head_ports; | ||
| for (auto const& [node_name, ports] : head_ports) { | ||
| for (auto const& [input_product, port] : ports) { | ||
| // Find the provider that has the right product name (hidden in the | ||
|
|
@@ -47,12 +59,55 @@ namespace phlex::experimental { | |
| input_product.to_string()); | ||
| make_edge(matched_provider->output_port(), *port); | ||
| } else { | ||
| throw std::runtime_error("No provider found for product: "s + | ||
| input_product.to_string()); | ||
| unconsumed_head_ports[node_name].push_back({input_product, port}); | ||
| } | ||
| } | ||
| } | ||
| return result; | ||
| return {std::move(result), std::move(unconsumed_head_ports)}; | ||
| } | ||
|
|
||
| std::pair<index_router::provider_input_ports_t, index_router::head_ports_t> | ||
| edges_from_implicit_providers(index_router::head_ports_t head_ports, | ||
| provider_nodes& providers, | ||
| source_map const& sources, | ||
| tbb::flow::graph& g) | ||
| { | ||
| index_router::provider_input_ports_t result; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider renaming |
||
| index_router::head_ports_t unconsumed_head_ports; | ||
| for (auto const& [node_name, ports] : head_ports) { | ||
| for (auto const& [input_product, port] : ports) { | ||
| // If we have a source node that can produce this product, use it. | ||
| auto bundles = find_matching_implicit_providers(sources, input_product); | ||
| if (bundles.empty()) { | ||
| unconsumed_head_ports[node_name].push_back({input_product, port}); | ||
| continue; | ||
| } | ||
|
|
||
| // For now we require only one implicit provider. This will change in the future. | ||
| if (bundles.size() > 1ull) { | ||
| auto error_msg = fmt::format( | ||
| "Multiple implicit providers found for product '{}', required by node '{}':\n", | ||
| input_product.to_string(), | ||
| node_name); | ||
| throw std::runtime_error(error_msg); | ||
| } | ||
|
|
||
| auto& bundle = bundles[0]; | ||
| auto const& spec = bundle.specification(); | ||
| auto node = std::make_unique<provider_node>(spec.creator(), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider filing an issue that makes the argument list of the constructor of |
||
| bundle.get_concurrency().value, | ||
| g, | ||
| bundle.release_provider_function(), | ||
| spec, | ||
| bundle.layer(), | ||
| bundle.stage()); | ||
| auto const provider_name = node->name().to_string(); | ||
| result.try_emplace(provider_name, input_product, node->input_port()); | ||
| make_edge(node->output_port(), *port); | ||
| providers.try_emplace(provider_name, std::move(node)); | ||
| } | ||
| } | ||
| return {std::move(result), std::move(unconsumed_head_ports)}; | ||
| } | ||
|
|
||
| index_router::head_ports_t edges_within_computational_graph( | ||
|
|
@@ -111,7 +166,9 @@ namespace phlex::experimental { | |
| } | ||
|
|
||
| std::tuple<index_router::provider_input_ports_t, std::map<std::string, named_index_ports>> | ||
| make_computational_edges(node_catalog& nodes, std::map<std::string, filter>& filters) | ||
| make_computational_edges(node_catalog& nodes, | ||
| std::map<std::string, filter>& filters, | ||
| tbb::flow::graph& g) | ||
| { | ||
| auto const producers = nodes.producers(); | ||
| auto const consumers = nodes.consumers(); | ||
|
|
@@ -124,8 +181,27 @@ namespace phlex::experimental { | |
|
|
||
| edges_to_outputs(nodes.providers, producers, nodes.outputs); | ||
|
|
||
| auto provider_input_ports = | ||
| auto [explicit_provider_input_ports, unconsumed_head_ports] = | ||
| edges_from_explicit_providers(std::move(head_ports), nodes.providers); | ||
|
|
||
| auto [implicit_provider_input_ports, unmatched_head_ports] = edges_from_implicit_providers( | ||
| std::move(unconsumed_head_ports), nodes.providers, nodes.sources, g); | ||
|
|
||
| if (not unmatched_head_ports.empty()) { | ||
| std::string error_msg{"No provider found for the following required products:\n"}; | ||
| for (auto const& [node_name, ports] : unmatched_head_ports) { | ||
| for (auto const& [input_product, _] : ports) { | ||
| error_msg += fmt::format( | ||
| " - Node '{}' requires product '{}'\n", node_name, input_product.to_string()); | ||
| } | ||
| } | ||
| throw std::runtime_error(error_msg); | ||
| } | ||
|
|
||
| // Combine implicit and explicit provider input ports. | ||
| auto provider_input_ports = std::move(explicit_provider_input_ports); | ||
| provider_input_ports.merge(std::move(implicit_provider_input_ports)); | ||
|
|
||
| auto multilayer_join_index_ports = multilayer_ports(consumers); | ||
|
|
||
| return std::make_tuple(std::move(provider_input_ports), std::move(multilayer_join_index_ports)); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| #include "phlex/core/source.hpp" | ||
|
|
||
| #include <utility> | ||
|
|
||
| namespace phlex::experimental { | ||
| provider_bundle::provider_bundle(provider_function_t f, | ||
| concurrency c, | ||
| product_specification spec, | ||
| std::string layer, | ||
| std::string stage) : | ||
| provider_function_{std::move(f)}, | ||
| concurrency_{c}, | ||
| spec_{std::move(spec)}, | ||
| layer_{std::move(layer)}, | ||
| stage_{std::move(stage)} | ||
| { | ||
| } | ||
|
|
||
| std::function<provider_function_t> provider_bundle::release_provider_function() | ||
| { | ||
| return std::move(provider_function_); | ||
| } | ||
|
|
||
| product_specification const& provider_bundle::specification() const noexcept { return spec_; } | ||
| identifier const& provider_bundle::layer() const noexcept { return layer_; } | ||
| identifier const& provider_bundle::stage() const noexcept { return stage_; } | ||
| concurrency provider_bundle::get_concurrency() const noexcept { return concurrency_; } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because this creates a new thing in the graph on which it is called, consider renaming to
add_source.This probably should be done for the other node types as well, so an issue that addresses this should be created.