computation_cache.hpp serif/utils/error/errors.hpp array concepts cstddef cstdint expected memory stdexcept string tuple type_traits utility serif/utils/dependencies/preparation.hpp serif/utils/dependencies/observers.hpp serif/utils/dependencies/detail/cache_storage.hpp src/compile_time_checks/serif/geometry/surface.cpp src/compile_time_checks/serif/geometry/surface_boundary.cpp src/compile_time_checks/serif/solver/normalization/plan.cpp src/compile_time_checks/serif/utils/dependencies/computation_graph.cpp src/compile_time_checks/serif/utils/dependencies/mixed_errors.cpp src/include/serif/backend/mfem/model/equilibrium_evaluator.hpp src/include/serif/backend/mfem/serialize/top.hpp src/include/serif/backend/mfem/solver/seed/fitted.hpp src/include/serif/utils/dependencies/dependencies.hpp serif::utils::dependencies::ComputationCache serif serif::utils serif::utils::dependencies #pragmaonce #include"serif/utils/error/errors.hpp" #include<array> #include<concepts> #include<cstddef> #include<cstdint> #include<expected> #include<memory> #include<stdexcept> #include<string> #include<tuple> #include<type_traits> #include<utility> #include"serif/utils/dependencies/preparation.hpp" #include"serif/utils/dependencies/observers.hpp" #include"serif/utils/dependencies/detail/cache_storage.hpp" namespaceserif::utils::dependencies{ template<typenameGraphT,typenameContextT,typenameErrorT=std::string,typenameObserversT=Observers<>> requiresCanPrepare<GraphT, ContextT, ErrorT>&&ObserversFor<ObserversT, GraphT, ContextT> classComputationCache{ public: usingTraits=detail::ComputationCacheTraits<GraphT, ContextT, ErrorT>; usingBindings=typenameTraits::Bindings; usingSources=typenameTraits::Sources; usingReport=PreparationReport<typename GraphT::Results, Sources>; usingFailure=PreparationFailure<ErrorT>; explicitComputationCache(GraphTgraph,ObserversTobservers=ObserversT{}): m_graph(std::move(graph)),m_observers(std::move(observers)){ } ComputationCache(constComputationCache&)=delete; ComputationCache&operator=(constComputationCache&)=delete; ComputationCache(ComputationCache&&other): m_graph(std::move(other.m_graph)), m_observers(std::move(other.m_observers)), m_storage(std::move(other.m_storage)), m_report(std::exchange(other.m_report,{})), m_ready(std::exchange(other.m_ready,false)), m_preparation_count(std::exchange(other.m_preparation_count,0)){ } ComputationCache&operator=(ComputationCache&&)=delete; template<IsKeyListRequestedT=GraphT::Results> requiresdetail::key_list_subset_v<std::remove_cvref_t<RequestedT>,typenameGraphT::Results> [[nodiscard]]std::expected<Report,Failure>try_prepare( constContextT&context, RequestedT={}, constPreparationModemode=PreparationMode::reuse_unchanged ){ clear_preparation(); Storagecandidate=m_storage; Reportreport; std::array<bool,source_count>inspected{}; constexprautorequired=GraphT::Dependencies::required(std::remove_cvref_t<RequestedT>{}); autooutcome=prepare_in_order<0>(context,candidate,report,inspected,required,mode); if(!outcome){ returnstd::unexpected(std::move(outcome.error())); } m_storage=std::move(candidate); m_report=report; m_ready=true; ++m_preparation_count; returnreport; } template<typenameKeyT> requiresdetail::contains_key_v<KeyT, typename GraphT::Results> [[nodiscard]]boolcontains()const{ returnm_ready&&m_report.templatestatus<KeyT>()!=PreparationStatus::not_required; } template<typenameKeyT> requiresdetail::contains_key_v<KeyT, typename GraphT::Results> [[nodiscard]]constauto&get()const{ if(!contains<KeyT>()){ serif::utils::errors::serif_error<std::logic_error, serif::utils::errors::SERiFErrorCode::INVALID_ARGUMENT>( "dependencies::ComputationCache::get:thisresultisunavailable.Successfullypreparerequestedresults" "containingitskeyoraresultthatdependsonitbeforereadingit." ); } return*std::get<GraphT::templateindex<KeyT>()>(m_storage.results).value; } template<typenameKeyT> requiresdetail::contains_key_v<KeyT, typename GraphT::Results> [[nodiscard]]autoshare()const{ (void)get<KeyT>(); returnstd::get<GraphT::templateindex<KeyT>()>(m_storage.results).value; } [[nodiscard]]constReport&report()constnoexcept{ returnm_report; } [[nodiscard]]boolready()constnoexcept{ returnm_ready; } [[nodiscard]]std::size_tpreparation_count()constnoexcept{ returnm_preparation_count; } voidclear_preparation()noexcept{ m_report={}; m_ready=false; } voidinvalidate()noexcept{ m_storage={}; clear_preparation(); } private: staticconstexprstd::size_tsource_count=detail::KeyListSize<Sources>::value; usingStorage=detail::ComputationCacheStorage<GraphT, Bindings, typename Traits::Resources, std::make_index_sequence<GraphT::size>>; usingResultMask=typenameGraphT::Dependencies::Mask; usingSourceMask=std::array<bool,source_count>; template<typenameKeyT> [[nodiscard]]staticconstevalstd::size_tversion_index(){ ifconstexpr(detail::contains_key_v<KeyT, Sources>){ returndetail::type_index<KeyT>(typenameSources::Keys{}); }else{ returnsource_count+GraphT::templateindex<KeyT>(); } } template<typenameKeyT> voidinspect_source( constContextT&context, Storage&candidate, Report&report, SourceMask&inspected, constPreparationModemode )const{ ifconstexpr(detail::contains_key_v<KeyT, Sources>){ constexprstd::size_tindex=version_index<KeyT>(); if(inspected[index]){ return; } inspected[index]=true; constauto&resource=context.templateresource<KeyT>(); auto&snapshot=std::get<index>(candidate.sources); constvoid*identity=source_identity(resource.policy(),resource.get()); constboolchanged=mode==PreparationMode::rebuild||!snapshot.captured||snapshot.identity!=identity||!resource.policy().matches(*snapshot.captured,resource.get()); if(changed){ usingCaptured=typenamestd::remove_reference_t<decltype(snapshot)>::Captured; snapshot.captured=std::make_shared<constCaptured>(resource.policy().capture(resource.get())); snapshot.identity=identity; ++candidate.versions[index]; report.changed_sources[index]=true; } } } template<typenameKeyT> [[nodiscard]]constauto&input_value(constContextT&context,constStorage&candidate)const{ ifconstexpr(detail::contains_key_v<KeyT, Sources>){ returncontext.templateget<KeyT>(); }else{ return*std::get<GraphT::templateindex<KeyT>()>(candidate.results).value; } } template<typenameComputationT,typenameKeyT> [[nodiscard]]constauto&input(constContextT&context,constStorage&candidate)const{ ifconstexpr(ComputationT::owns_inputs){ returnstd::get<GraphT::templateindex<KeyT>()>(candidate.results).value; }else{ returninput_value<KeyT>(context,candidate); } } template<std::size_tIndex,typename...Keys> [[nodiscard]]autoevaluate_node( constContextT&context, constStorage&candidate, KeyList<Keys...> ){ usingComputation=GraphT::templateComputationAt<Index>; constauto&computation=m_graph.templatecomputation<Index>(); returnm_observers.templateevaluate<typenameComputation::Key>([&](){ //AcommafoldmakesedgecallbacksfollowInputsorder,unlikefunctionarguments. (m_observers.templateedge<Keys,typenameComputation::Key>(input_value<Keys>(context,candidate)),...); returncomputation.evaluate(input<Computation, Keys>(context,candidate)...); }); } template<std::size_tIndex,typename...Keys> [[nodiscard]]std::expected<void,Failure>prepare_one( constContextT&context, Storage&candidate, Report&report, SourceMask&inspected, constPreparationModemode, KeyList<Keys...> ){ usingComputation=GraphT::templateComputationAt<Index>; usingResult=detail::ResolveComputation<Computation, Bindings>; (inspect_source<Keys>(context,candidate,report,inspected,mode),...); conststd::array<std::uint64_t,sizeof...(Keys)>versions{candidate.versions[version_index<Keys>()]...}; auto&cached=std::get<Index>(candidate.results); if(mode==PreparationMode::reuse_unchanged&&cached.value&&cached.input_versions==versions){ report.statuses[Index]=PreparationStatus::reused; return{}; } autoresult=evaluate_node<Index>(context,candidate,KeyList<Keys...>{}); ifconstexpr(Result::fallible){ if(!result){ returnstd::unexpected(Failure{ .computation_index=Index, .error=std::move(result.error()) }); } cached.value=std::make_shared<consttypenameResult::Value>(std::move(*result)); }else{ cached.value=std::make_shared<consttypenameResult::Value>(std::move(result)); } cached.input_versions=versions; ++candidate.versions[source_count+Index]; report.statuses[Index]=PreparationStatus::computed; return{}; } template<std::size_tPosition> [[nodiscard]]std::expected<void,Failure>prepare_in_order( constContextT&context, Storage&candidate, Report&report, SourceMask&inspected, constResultMask&required, PreparationModemode ){ ifconstexpr(Position<GraphT::size){ constexprstd::size_tindex=GraphT::Dependencies::order[Position]; if(required[index]){ autooutcome=prepare_one<index>( context,candidate,report,inspected,mode,typenameGraphT::templateComputationAt<index>::Inputs{} ); if(!outcome){ returnoutcome; } } returnprepare_in_order<Position + 1>(context,candidate,report,inspected,required,mode); }else{ return{}; } } GraphTm_graph; [[no_unique_address]]ObserversTm_observers; Storagem_storage; Reportm_report; boolm_ready{false}; std::size_tm_preparation_count{0}; }; template<typenameErrorT=std::string,IsComputationGraphGraphT,IsDependencyContextContextT,typenameObserversT=Observers<>> requiresCanPrepare<GraphT, ContextT, ErrorT>&&ObserversFor<ObserversT, GraphT, ContextT>&& std::constructible_from<std::remove_cvref_t<GraphT>,GraphT>&&std::constructible_from<std::remove_cvref_t<ObserversT>,ObserversT> [[nodiscard]]automake_computation_cache(GraphT&&graph,constContextT&,ObserversT&&observers=ObserversT{}){ returnComputationCache<std::remove_cvref_t<GraphT>,std::remove_cvref_t<ContextT>,ErrorT,std::remove_cvref_t<ObserversT>>( std::forward<GraphT>(graph),std::forward<ObserversT>(observers)); } template<typenameErrorT=std::string,IsDependencyContextContextT,typename...Computations> requiresCompatibleComputations<Computations...>&& (std::constructible_from<std::remove_cvref_t<Computations>,Computations>&&...)&& CanPrepare<ComputationGraph<std::remove_cvref_t<Computations>...>,ContextT,ErrorT> [[nodiscard]]automake_computation_cache(constContextT&context,Computations&&...computations){ returnmake_computation_cache<ErrorT>(make_computation_graph(std::forward<Computations>(computations)...),context); } }