Philote-Cpp
C++ bindings for the Philote MDO standard
Loading...
Searching...
No Matches
explicit.h
Go to the documentation of this file.
1/*
2 Philote C++ Bindings
3
4 Copyright 2022-2025 Christopher A. Lupp
5
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17
18 This work has been cleared for public release, distribution unlimited, case
19 number: AFRL-2023-5716.
20
21 The views expressed are those of the authors and do not reflect the
22 official guidance or position of the United States Government, the
23 Department of Defense or of the United States Air Force.
24
25 Statement from DoD: The Appearance of external hyperlinks does not
26 constitute endorsement by the United States Department of Defense (DoD) of
27 the linked websites, of the information, products, or services contained
28 therein. The DoD does not exercise any editorial, security, or other
29 control over the information you may find at these locations.
30*/
31#pragma once
32
33#include <string>
34#include <map>
35#include <unordered_map>
36#include <utility>
37
38#include <discipline.h>
39#include <variable.h>
40#include "discipline_client.h"
41
42#include <disciplines.grpc.pb.h>
43#include "discipline_server.h"
44
45namespace philote
46{
47 // forward declaration
48 class ExplicitDiscipline;
49
62 class ExplicitServer : public ExplicitService::Service
63 {
64 public:
66 ExplicitServer() = default;
67
69 ~ExplicitServer() noexcept;
70
77 void LinkPointers(std::shared_ptr<philote::ExplicitDiscipline> implementation);
78
84
92 grpc::Status ComputeFunction(grpc::ServerContext *context,
93 grpc::ServerReaderWriter<::philote::Array,
94 ::philote::Array> *stream) override;
95
103 grpc::Status ComputeGradient(grpc::ServerContext *context,
104 grpc::ServerReaderWriter<::philote::Array,
105 ::philote::Array> *stream) override;
106
107 // Test helper methods that accept interface pointers for unit testing with mocks
108 template<typename StreamType>
109 grpc::Status ComputeFunctionImpl(grpc::ServerContext *context, StreamType *stream);
110
111 template<typename StreamType>
112 grpc::Status ComputeGradientImpl(grpc::ServerContext *context, StreamType *stream);
113
114 // Public wrappers for tests
115 grpc::Status ComputeFunctionForTesting(grpc::ServerContext *context,
116 grpc::ServerReaderWriterInterface<::philote::Array,
117 ::philote::Array> *stream) {
118 return ComputeFunctionImpl(context, stream);
119 }
120
121 grpc::Status ComputeGradientForTesting(grpc::ServerContext *context,
122 grpc::ServerReaderWriterInterface<::philote::Array,
123 ::philote::Array> *stream) {
124 return ComputeGradientImpl(context, stream);
125 }
126
127 private:
129 std::shared_ptr<philote::ExplicitDiscipline> implementation_;
130 };
131
263 {
264 public:
269
274
280 void RegisterServices(grpc::ServerBuilder &builder);
281
292 virtual void Compute(const philote::Variables &inputs, philote::Variables &outputs);
293
304 virtual void ComputePartials(const philote::Variables &inputs,
305 Partials &partials);
306
307 private:
309 philote::ExplicitServer explicit_;
311 philote::DisciplineServer discipline_server_;
312 };
313
418 {
419 public:
421 ExplicitClient() = default;
422
424 ~ExplicitClient() noexcept = default;
425
431 void ConnectChannel(std::shared_ptr<grpc::ChannelInterface> channel);
432
442 Variables ComputeFunction(const Variables &inputs);
443
450 Partials ComputeGradient(const Variables &inputs);
451
457 void SetStub(std::unique_ptr<ExplicitService::StubInterface> stub)
458 {
459 stub_ = std::move(stub);
460 }
461
462 protected:
464 std::unique_ptr<ExplicitService::StubInterface> stub_;
465 };
466}
467// Template implementations must be in header
468#include <algorithm>
469
470namespace philote {
471
472template<typename StreamType>
473grpc::Status ExplicitServer::ComputeFunctionImpl(grpc::ServerContext *context, StreamType *stream)
474{
475 if (!implementation_)
476 {
477 return grpc::Status(grpc::StatusCode::FAILED_PRECONDITION, "Discipline implementation not linked");
478 }
479
480 if (!stream)
481 {
482 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, "Stream is null");
483 }
484
485 // Check for cancellation before starting
486 if (context && context->IsCancelled())
487 {
488 return grpc::Status(grpc::StatusCode::CANCELLED, "Request cancelled before start");
489 }
490
491 philote::Array array;
492
493 // preallocate the inputs based on meta data
494 Variables inputs;
495 const auto *discipline = static_cast<philote::Discipline *>(implementation_.get());
496 if (!discipline)
497 {
498 return grpc::Status(grpc::StatusCode::INTERNAL, "Failed to cast implementation to Discipline");
499 }
500
501 for (const auto &var : discipline->var_meta())
502 {
503 std::string name = var.name();
504 if (var.type() == kInput)
505 inputs[name] = Variable(var);
506 }
507
508 // Build O(1) lookup map for variable metadata
509 std::unordered_map<std::string, const VariableMetaData*> var_lookup;
510 for (const auto &var : discipline->var_meta())
511 {
512 var_lookup[var.name()] = &var;
513 }
514
515 while (stream->Read(&array))
516 {
517 // get variables from the stream message
518 const std::string &name = array.name();
519
520 // get the variable corresponding to the current message using O(1) lookup
521 auto var_it = var_lookup.find(name);
522 if (var_it == var_lookup.end())
523 {
524 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, "Variable not found: " + name);
525 }
526 const VariableMetaData* var = var_it->second;
527
528 // obtain the inputs and discrete inputs from the stream
529 if (var->type() == VariableType::kInput)
530 {
531 try
532 {
533 // set the variable slice
534 inputs[name].AssignChunk(array);
535 }
536 catch (const std::exception &e)
537 {
538 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
539 "Failed to assign chunk for variable " + name + ": " + e.what());
540 }
541 }
542 else
543 {
544 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, "Invalid variable type for input: " + name);
545 }
546 }
547
548 // preallocate outputs
549 Variables outputs;
550 for (const VariableMetaData &var : discipline->var_meta())
551 {
552 if (var.type() == kOutput)
553 outputs[var.name()] = Variable(var);
554 }
555
556 // Check for cancellation before expensive computation
557 if (context && context->IsCancelled())
558 {
559 return grpc::Status(grpc::StatusCode::CANCELLED, "Request cancelled before computation");
560 }
561
562 // Set context for discipline to check cancellation during compute
563 discipline->SetContext(context);
564
565 // call the discipline developer-defined Compute function
566 try
567 {
568 implementation_->Compute(inputs, outputs);
569 }
570 catch (const std::exception &e)
571 {
572 discipline->ClearContext();
573 return grpc::Status(grpc::StatusCode::INTERNAL,
574 "Failed to compute outputs: " + std::string(e.what()));
575 }
576
577 // Clear context after computation
578 discipline->ClearContext();
579
580 // Check for cancellation before sending results
581 if (context && context->IsCancelled())
582 {
583 return grpc::Status(grpc::StatusCode::CANCELLED, "Request cancelled before sending results");
584 }
585
586 // iterate through continuous outputs
587 for (const auto &out : outputs)
588 {
589 const std::string &name = out.first;
590 try
591 {
592 out.second.Send(name, "", stream, discipline->stream_opts().num_double(), context);
593 }
594 catch (const std::exception &e)
595 {
596 return grpc::Status(grpc::StatusCode::INTERNAL,
597 "Failed to send output " + name + ": " + e.what());
598 }
599 }
600
601 return grpc::Status::OK;
602}
603
604template<typename StreamType>
605grpc::Status ExplicitServer::ComputeGradientImpl(grpc::ServerContext *context, StreamType *stream)
606{
607 if (!implementation_)
608 {
609 return grpc::Status(grpc::StatusCode::FAILED_PRECONDITION, "Discipline implementation not linked");
610 }
611
612 if (!stream)
613 {
614 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, "Stream is null");
615 }
616
617 // Check for cancellation before starting
618 if (context && context->IsCancelled())
619 {
620 return grpc::Status(grpc::StatusCode::CANCELLED, "Request cancelled before start");
621 }
622
623 philote::Array array;
624
625 // preallocate the inputs based on meta data
626 Variables inputs;
627 const auto *discipline = static_cast<philote::Discipline *>(implementation_.get());
628 if (!discipline)
629 {
630 return grpc::Status(grpc::StatusCode::INTERNAL, "Failed to cast implementation to Discipline");
631 }
632
633 for (const auto &var : discipline->var_meta())
634 {
635 std::string name = var.name();
636 if (var.type() == kInput)
637 inputs[name] = Variable(var);
638 }
639
640 // Build O(1) lookup map for variable metadata
641 std::unordered_map<std::string, const VariableMetaData*> var_lookup;
642 for (const auto &var : discipline->var_meta())
643 {
644 var_lookup[var.name()] = &var;
645 }
646
647 while (stream->Read(&array))
648 {
649 // get variables from the stream message
650 const std::string &name = array.name();
651
652 // get the variable corresponding to the current message using O(1) lookup
653 auto var_it = var_lookup.find(name);
654 if (var_it == var_lookup.end())
655 {
656 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, "Variable not found: " + name);
657 }
658 const VariableMetaData* var = var_it->second;
659
660 // obtain the inputs and discrete inputs from the stream
661 if (var->type() == VariableType::kInput)
662 {
663 try
664 {
665 // set the variable slice
666 inputs[name].AssignChunk(array);
667 }
668 catch (const std::exception &e)
669 {
670 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
671 "Failed to assign chunk for variable " + name + ": " + e.what());
672 }
673 }
674 else
675 {
676 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, "Invalid variable type for input: " + name);
677 }
678 }
679
680 // preallocate outputs
681 Partials partials;
682 for (const PartialsMetaData &par : discipline->partials_meta())
683 {
684 std::vector<size_t> shape;
685 for (const int64_t &dim : par.shape())
686 shape.push_back(dim);
687
688 partials[std::make_pair(par.name(), par.subname())] = Variable(kOutput, shape);
689 }
690
691 // Check for cancellation before expensive computation
692 if (context && context->IsCancelled())
693 {
694 return grpc::Status(grpc::StatusCode::CANCELLED, "Request cancelled before computation");
695 }
696
697 // Set context for discipline to check cancellation during compute
698 discipline->SetContext(context);
699
700 // call the discipline developer-defined Compute function
701 try
702 {
703 implementation_->ComputePartials(inputs, partials);
704 }
705 catch (const std::exception &e)
706 {
707 discipline->ClearContext();
708 return grpc::Status(grpc::StatusCode::INTERNAL,
709 "Failed to compute partials: " + std::string(e.what()));
710 }
711
712 // Clear context after computation
713 discipline->ClearContext();
714
715 // Check for cancellation before sending results
716 if (context && context->IsCancelled())
717 {
718 return grpc::Status(grpc::StatusCode::CANCELLED, "Request cancelled before sending results");
719 }
720
721 // iterate through continuous outputs
722 for (const auto &par : partials)
723 {
724 const std::string &name = par.first.first;
725 const std::string &subname = par.first.second;
726 try
727 {
728 par.second.Send(name, subname, stream, discipline->stream_opts().num_double(), context);
729 }
730 catch (const std::exception &e)
731 {
732 return grpc::Status(grpc::StatusCode::INTERNAL,
733 "Failed to send partial " + name + "/" + subname + ": " + e.what());
734 }
735 }
736
737 return grpc::Status::OK;
738}
739
740} // namespace philote
Client class for interacting with a discipline server.
Definition discipline_client.h:55
Base class for all analysis discipline servers.
Definition discipline_server.h:57
Definition of the discipline base class.
Definition discipline.h:62
Client class for calling a remote explicit discipline.
Definition explicit.h:418
std::unique_ptr< ExplicitService::StubInterface > stub_
explicit service stub
Definition explicit.h:464
ExplicitClient()=default
Constructor.
~ExplicitClient() noexcept=default
Destructor.
Explicit discipline class.
Definition explicit.h:263
virtual void Compute(const philote::Variables &inputs, philote::Variables &outputs)
Function evaluation for the discipline.
virtual void ComputePartials(const philote::Variables &inputs, Partials &partials)
Gradient evaluation for the discipline.
~ExplicitDiscipline() noexcept
Destroy the Explicit Discipline object.
void RegisterServices(grpc::ServerBuilder &builder)
Registers all services with a gRPC channel.
ExplicitDiscipline()
Construct a new Explicit Discipline object.
Server base class for an explicit discipline.
Definition explicit.h:63
grpc::Status ComputeFunctionImpl(grpc::ServerContext *context, StreamType *stream)
Definition explicit.h:473
void LinkPointers(std::shared_ptr< philote::ExplicitDiscipline > implementation)
Links the explicit server to the discipline server and explicit discipline via pointers.
grpc::Status ComputeGradient(grpc::ServerContext *context, grpc::ServerReaderWriter<::philote::Array, ::philote::Array > *stream) override
RPC that computes initiates gradient evaluation.
grpc::Status ComputeFunction(grpc::ServerContext *context, grpc::ServerReaderWriter<::philote::Array, ::philote::Array > *stream) override
RPC that computes initiates function evaluation.
ExplicitServer()=default
Constructor.
~ExplicitServer() noexcept
Destructor.
grpc::Status ComputeGradientImpl(grpc::ServerContext *context, StreamType *stream)
Definition explicit.h:605
void UnlinkPointers()
Dereferences all pointers.
grpc::Status ComputeGradientForTesting(grpc::ServerContext *context, grpc::ServerReaderWriterInterface<::philote::Array, ::philote::Array > *stream)
Definition explicit.h:121
grpc::Status ComputeFunctionForTesting(grpc::ServerContext *context, grpc::ServerReaderWriterInterface<::philote::Array, ::philote::Array > *stream)
Definition explicit.h:115
A class for storing continuous and discrete variables.
Definition variable.h:85
Definition discipline.h:43
std::map< std::string, philote::Variable > Variables
Definition variable.h:404
std::map< std::pair< std::string, std::string >, philote::Variable > Partials
Definition variable.h:405