actions.cpp serif/utils/error/nonfinite.hpp serif/backend/mfem/operators/actions.hpp algorithm utility serif/backend/mfem/shared/finite.hpp serif/utils/error/errors.hpp serif serif::operators serif::operators::backend serif::operators::backend::mfem #include"serif/utils/error/nonfinite.hpp" #include"serif/backend/mfem/operators/actions.hpp" #include<algorithm> #include<utility> #include"serif/backend/mfem/shared/finite.hpp" #include"serif/utils/error/errors.hpp" namespaceserif::operators::backend::mfem{ namespace{ classActionOperatorfinal:public::mfem::Operator{ public: ActionOperator(introws,intcolumns,MatrixActionaction,MatrixActiontranspose,MatrixArrayActionbatch={}):::mfem::Operator(rows,columns),m_action(std::move(action)),m_transpose(std::move(transpose)),m_batch(std::move(batch)){ } voidArrayMult(const::mfem::Array<const::mfem::Vector*>&inputs,::mfem::Array<::mfem::Vector*>&outputs)constoverride{ usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::INVALID_DOF_COUNT>(inputs.Size()==outputs.Size(),"Abatchedactionneedsoneoutputperinput."); if(!m_batch){ for(intk=0;k<inputs.Size();++k){ Mult(*inputs[k],*outputs[k]); } return; } constautocount=static_cast<std::size_t>(inputs.Size()); m_inputs.resize(count); m_outputs.resize(count); std::vector<const::mfem::Vector*>input_views(count); std::vector<::mfem::Vector*>output_views(count); for(std::size_tk=0;k<count;++k){ constauto&input=*inputs[static_cast<int>(k)]; serif_ensure<SERiFErrorCode::INVALID_DOF_COUNT>(input.Size()==width,"Linearizedactionreceived{}coefficients;expected{}.",input.Size(),width); serif_ensure<SERiFErrorCode::NON_FINITE_VALUE_ERROR,utils::errors::NonFiniteValue>(utils::checks::is_finite(input),"Linearizedactiondirectionsmustbefinite."); m_inputs[k]=input; m_outputs[k].SetSize(height); m_outputs[k]=0.0; input_views[k]=&m_inputs[k]; output_views[k]=&m_outputs[k]; } m_batch(input_views,output_views); for(std::size_tk=0;k<count;++k){ serif_ensure<SERiFErrorCode::MATRIX_SHAPE_ERROR>(m_outputs[k].Size()==height,"Alinearizedactionchangeditsdeclaredoutputsizefrom{}to{}.",height,m_outputs[k].Size()); serif_ensure<SERiFErrorCode::NON_FINITE_VALUE_ERROR,utils::errors::NonFiniteValue>(utils::checks::is_finite(m_outputs[k]),"Alinearizedactionproducednonfinitevalues;rejectthislinearizationordirection."); } for(std::size_tk=0;k<count;++k){ *outputs[static_cast<int>(k)]=m_outputs[k]; } //Batchesaretransient(preconditionersetup);donotkeeptheircopiesalivewiththeoperator. m_inputs={}; m_outputs={}; } voidMult(const::mfem::Vector&input,::mfem::Vector&output)constoverride{ apply(input,output,width,height,m_action); } voidMultTranspose(const::mfem::Vector&input,::mfem::Vector&output)constoverride{ apply(input,output,height,width,m_transpose); } private: voidapply(const::mfem::Vector&input,::mfem::Vector&output,intinputs,intoutputs,constMatrixAction&action)const{ usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::INVALID_DOF_COUNT>(input.Size()==inputs,"Linearizedactionreceived{}coefficients;expected{}.",input.Size(),inputs); serif_ensure<SERiFErrorCode::NON_FINITE_VALUE_ERROR,utils::errors::NonFiniteValue>(utils::checks::is_finite(input),"Linearizedactiondirectionsmustbefinite."); m_input=input; m_output.SetSize(outputs); m_output=0.0; action(m_input,m_output); serif_ensure<SERiFErrorCode::MATRIX_SHAPE_ERROR>(m_output.Size()==outputs,"Alinearizedactionchangeditsdeclaredoutputsizefrom{}to{}.",outputs,m_output.Size()); serif_ensure<SERiFErrorCode::NON_FINITE_VALUE_ERROR,utils::errors::NonFiniteValue>(utils::checks::is_finite(m_output),"Alinearizedactionproducednonfinitevalues;rejectthislinearizationordirection."); output=m_output; } MatrixActionm_action,m_transpose; MatrixArrayActionm_batch; mutable::mfem::Vectorm_input,m_output; mutablestd::vector<::mfem::Vector>m_inputs,m_outputs; }; }//namespace LinearizedBlocklinearized_action(introws,intcolumns,MatrixActionaction,MatrixActiontranspose){ usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::INVALID_DOF_COUNT>(rows>=0&&columns>=0,"Linearizedactionsizesmustbenonnegative."); serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(action&&transpose,"Alinearizedactionmustsupplyforwardandtransposecallbacks."); returnLinearizedBlock(std::make_shared<ActionOperator>(rows,columns,std::move(action),std::move(transpose))); } LinearizedBlocklinearized_action(introws,intcolumns,MatrixActionaction,MatrixActiontranspose,MatrixArrayActionbatch){ usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::INVALID_DOF_COUNT>(rows>=0&&columns>=0,"Linearizedactionsizesmustbenonnegative."); serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(action&&transpose&&batch,"Abatchedlinearizedactionmustsupplyforward,transposeandbatchcallbacks."); returnLinearizedBlock(std::make_shared<ActionOperator>(rows,columns,std::move(action),std::move(transpose),std::move(batch))); } voidapply_many(const::mfem::Operator&action,std::span<const::mfem::Vector*const>inputs,std::span<::mfem::Vector*const>outputs){ ::mfem::Array<const::mfem::Vector*>in(static_cast<int>(inputs.size())); ::mfem::Array<::mfem::Vector*>out(static_cast<int>(outputs.size())); for(std::size_tk=0;k<inputs.size();++k){ in[static_cast<int>(k)]=inputs[k]; out[static_cast<int>(k)]=outputs[k]; } action.ArrayMult(in,out); } LinearizedBlocktranspose_block(LinearizedBlockblock){ constautooriginal=block.action(); constautoaction=linearized_action(original->Width(),original->Height(),[original](constauto&input,auto&output){ original->MultTranspose(input,output); },[original](constauto&input,auto&output){ original->Mult(input,output); }); LinearizedBlock::Diagonaldiagonal; LinearizedBlock::Assemblyassembly; if(block.has_diagonal()){ diagonal=[block](){ returnchecked_diagonal(block); }; } if(block.has_assembly()){ assembly=[block](){ returnstd::shared_ptr<const::mfem::SparseMatrix>(::mfem::Transpose(*checked_assembly(block))); }; } returnLinearizedBlock(action.action(),std::move(diagonal),std::move(assembly)); } LinearizedBlockcompose_blocks(LinearizedBlockleft,LinearizedBlockright){ usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::MATRIX_SHAPE_ERROR>(left.action()->Width()==right.action()->Height(),"Composedlinearizedblockshaveincompatibleintermediatedimensions."); constautoaction=linearized_action(left.action()->Height(),right.action()->Width(),[left,right,scratch=::mfem::Vector{}](constauto&input,auto&output)mutable{ scratch.SetSize(right.action()->Height()); scratch=0.0; right.action()->Mult(input,scratch); serif_ensure<SERiFErrorCode::MATRIX_SHAPE_ERROR>(scratch.Size()==left.action()->Width(),"Acomposedlinearizedblockchangeditsintermediatecoordinatecount."); left.action()->Mult(scratch,output); },[left,right,scratch=::mfem::Vector{}](constauto&input,auto&output)mutable{ scratch.SetSize(left.action()->Width()); scratch=0.0; left.action()->MultTranspose(input,scratch); serif_ensure<SERiFErrorCode::MATRIX_SHAPE_ERROR>(scratch.Size()==right.action()->Height(),"Acomposedlinearizedtransposechangeditsintermediatecoordinatecount."); right.action()->MultTranspose(scratch,output); },[left,right,scratch=std::vector<::mfem::Vector>{}](std::span<const::mfem::Vector*const>inputs,std::span<::mfem::Vector*const>outputs)mutable{ scratch.resize(inputs.size()); std::vector<::mfem::Vector*>intermediate(inputs.size()); for(std::size_tk=0;k<inputs.size();++k){ scratch[k].SetSize(right.action()->Height()); scratch[k]=0.0; intermediate[k]=&scratch[k]; } apply_many(*right.action(),inputs,intermediate); std::vector<const::mfem::Vector*>next(inputs.size()); for(std::size_tk=0;k<inputs.size();++k){ serif_ensure<SERiFErrorCode::MATRIX_SHAPE_ERROR>(scratch[k].Size()==left.action()->Width(),"Acomposedlinearizedblockchangeditsintermediatecoordinatecount."); next[k]=&scratch[k]; } apply_many(*left.action(),next,outputs); scratch={}; }); LinearizedBlock::Assemblyassembly; if(left.has_assembly()&&right.has_assembly()){ assembly=[left,right](){ returnstd::shared_ptr<const::mfem::SparseMatrix>(::mfem::Mult(*checked_assembly(left),*checked_assembly(right))); }; } returnLinearizedBlock(action.action(),{},std::move(assembly)); } LinearizedBlocksum_blocks(introws,intcolumns,std::vector<LinearizedBlock>blocks){ usingnamespaceutils::errors; for(constauto&block:blocks){ serif_ensure<SERiFErrorCode::MATRIX_SHAPE_ERROR>(block.action()->Height()==rows&&block.action()->Width()==columns,"Summedlinearizedblocksmusthaveidenticaldimensions."); } constautoaction=linearized_action(rows,columns,[blocks,rows,scratch=::mfem::Vector{}](constauto&input,auto&output)mutable{ scratch.SetSize(rows); for(constauto&block:blocks){ scratch=0.0; block.action()->Mult(input,scratch); serif_ensure<SERiFErrorCode::MATRIX_SHAPE_ERROR>(scratch.Size()==rows,"Asummedlinearizedblockchangeditsresidualcoordinatecount."); output+=scratch; } },[blocks,columns,scratch=::mfem::Vector{}](constauto&input,auto&output)mutable{ scratch.SetSize(columns); for(constauto&block:blocks){ scratch=0.0; block.action()->MultTranspose(input,scratch); serif_ensure<SERiFErrorCode::MATRIX_SHAPE_ERROR>(scratch.Size()==columns,"Asummedlinearizedtransposechangeditsstatecoordinatecount."); output+=scratch; } },[blocks,rows,scratch=std::vector<::mfem::Vector>{}](std::span<const::mfem::Vector*const>inputs,std::span<::mfem::Vector*const>outputs)mutable{ scratch.resize(inputs.size()); std::vector<::mfem::Vector*>results(inputs.size()); for(constauto&block:blocks){ for(std::size_tk=0;k<inputs.size();++k){ scratch[k].SetSize(rows); scratch[k]=0.0; results[k]=&scratch[k]; } apply_many(*block.action(),inputs,results); for(std::size_tk=0;k<inputs.size();++k){ serif_ensure<SERiFErrorCode::MATRIX_SHAPE_ERROR>(scratch[k].Size()==rows,"Asummedlinearizedblockchangeditsresidualcoordinatecount."); *outputs[k]+=scratch[k]; } } scratch={}; }); LinearizedBlock::Diagonaldiagonal; LinearizedBlock::Assemblyassembly; if(rows==columns&&std::ranges::all_of(blocks,[](constauto&block){ returnblock.has_diagonal(); })){ diagonal=[blocks,rows](){ std::vector<double>result(rows,0.0); for(constauto&block:blocks){ constautovalues=checked_diagonal(block); for(intindex=0;index<rows;++index){ result[index]+=values[index]; } } returnresult; }; } if(std::ranges::all_of(blocks,[](constauto&block){ returnblock.has_assembly(); })){ assembly=[blocks,rows,columns](){ autoresult=std::make_shared<::mfem::SparseMatrix>(rows,columns); for(constauto&block:blocks){ constautomatrix=checked_assembly(block); for(introw=0;row<rows;++row){ for(intindex=0;index<matrix->RowSize(row);++index){ result->Add(row,matrix->GetRowColumns(row)[index],matrix->GetRowEntries(row)[index]); } } } result->Finalize(); returnresult; }; } returnLinearizedBlock(action.action(),std::move(diagonal),std::move(assembly)); } }//namespaceserif::operators::backend::mfem