// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "MapFeatureDescriptor.h" #include "SequenceFeatureDescriptor.h" #include "TensorFeatureDescriptor.h" #include "WinMLAdapter.h" namespace Windows::AI::MachineLearning { // SequenceBase // // This is the base class for all data based Sequence types. // // Supported derived classes: // Map, Map // template struct SequenceBase : public winrt::implements< SequenceBase, winml::ILearningModelFeatureValue, WinML::ISequenceFeatureValue, WinML::ILotusValueProviderPrivate> { using AbiMapStringToFloat = wfc::IMap; using AbiMapInt64BitToFloat = wfc::IMap; template struct ValidLotusType { using Type = T; }; template <> struct ValidLotusType { using Type = std::map; using TKey = std::string; using TValue = float; }; template <> struct ValidLotusType { using Type = std::map; using TKey = int64_t; using TValue = float; }; template void GetElementDescriptor(winml::ILearningModelFeatureDescriptor* result) { *result = TensorFeatureDescriptorFrom::CreateAnonymous( std::vector{1, 1, 1, 1}); } template <> void GetElementDescriptor>( winml::ILearningModelFeatureDescriptor* result) { // zero dimensional tensor has empty shape auto value_descriptor = WinML::TensorFeatureDescriptorFrom::CreateAnonymous( std::vector{}); *result = winrt::make( nullptr /* set to null as values are name-less */, nullptr /* set to null as values are description-less */, false /* set to false as values dont have required annotations */, winml::TensorKind::String /* key kind */, value_descriptor /* value kind */); } template <> void GetElementDescriptor>( winml::ILearningModelFeatureDescriptor* result) { // zero dimensional tensor has empty shape auto value_descriptor = WinML::TensorFeatureDescriptorFrom::CreateAnonymous( std::vector{}); *result = winrt::make( nullptr /* set to null as values are name-less */, nullptr /* set to null as values are description-less */, false /* set to false as values dont have required annotations */, winml::TensorKind::Int64 /* key kind */, value_descriptor /* value kind */); } using LotusSequence = std::vector::Type>; using ABISequence = wfc::IIterable; SequenceBase(const ABISequence& data) : data_(data) {} static winml::ILearningModelFeatureValue Create() { auto sequence = winrt::single_threaded_vector(); return winrt::make(sequence); } static winml::ILearningModelFeatureValue Create( const ABISequence& data) { return winrt::make(data); } // ILearningModelFeatureValue implementation winml::LearningModelFeatureKind Kind() { return winml::LearningModelFeatureKind::Sequence; } STDMETHOD(get_ElementDescriptor) ( winml::ILearningModelFeatureDescriptor* result) { FAIL_FAST_IF_NULL(result); GetElementDescriptor(result); return S_OK; } template static typename ValidLotusType::Type ConvertToValidLotusType( TRawType raw) { return raw; } template <> static typename ValidLotusType::Type ConvertToValidLotusType( winrt::hstring raw) { return WinML::Strings::UTF8FromHString(raw); } template <> static typename ValidLotusType::Type ConvertToValidLotusType( AbiMapStringToFloat raw) { std::map lotus_map; for (auto pair : raw) { auto key = WinML::Strings::UTF8FromHString(pair.Key()); lotus_map[key] = pair.Value(); } return lotus_map; } template <> static typename ValidLotusType::Type ConvertToValidLotusType( AbiMapInt64BitToFloat raw) { std::map lotus_map; for (const auto& pair : raw) { lotus_map[pair.Key()] = pair.Value(); } return lotus_map; } static LotusSequence ConvertToLotusSequence( const ABISequence& sequence) { LotusSequence lotus_sequence; std::transform( begin(sequence), end(sequence), std::back_inserter(lotus_sequence), [](const auto& value) { return ConvertToValidLotusType(value); }); return lotus_sequence; } STDMETHOD(GetOrtValue)( WinML::BindingContext& context, _winmla::IOrtValue** ml_value) { // TODO: Tensorized data should be cached so multiple bindings work more efficiently // Create a copy of the sequence auto sequence = context.type == WinML::BindingType::kInput ? std::make_unique(ConvertToLotusSequence(data_)) : std::make_unique(); winrt::com_ptr<_winmla::IWinMLAdapter> adapter; RETURN_IF_FAILED(OrtGetWinMLAdapter(adapter.put())); auto lotus_type = adapter->GetVectorMapType( TensorKindFrom::TKey>::Type, TensorKindFrom::TValue>::Type); winrt::com_ptr<_winmla::IOrtValue> ml_value_out; adapter->CreateOrtValue(sequence.release(), lotus_type, ml_value_out.put()); *ml_value = ml_value_out.detach(); return S_OK; } STDMETHOD(IsPlaceholder) ( bool* p_is_placeholder) { FAIL_FAST_IF_NULL(p_is_placeholder); *p_is_placeholder = false; return S_OK; } template static TRawType ConvertToABIType( typename ValidLotusType::Type lotus_value) { return lotus_value; } template <> static winrt::hstring ConvertToABIType( typename ValidLotusType::Type lotus_value) { return WinML::Strings::HStringFromUTF8(lotus_value); } template <> static AbiMapStringToFloat ConvertToABIType( typename ValidLotusType::Type lotus_value) { std::map copy; for (const auto& pair : lotus_value) { auto key = WinML::Strings::HStringFromUTF8(pair.first); copy[key] = pair.second; } return winrt::single_threaded_map( std::move(copy)); } template <> static AbiMapInt64BitToFloat ConvertToABIType( typename ValidLotusType::Type lotus_value) { return winrt::single_threaded_map( std::move(lotus_value)); } STDMETHOD(UpdateSourceResourceData)( BindingContext& context, _winmla::IOrtValue* ml_value) { auto writable_vector = data_.as>(); writable_vector.Clear(); winrt::com_ptr<_winmla::IWinMLAdapter> adapter; RETURN_IF_FAILED(OrtGetWinMLAdapter(adapter.put())); const LotusSequence& sequence = *static_cast(adapter->GetVectorData( ml_value, TensorKindFrom::TKey>::Type, TensorKindFrom::TValue>::Type)); for (const auto& element : sequence) { writable_vector.Append(ConvertToABIType(element)); } return S_OK; } STDMETHOD(AbiRepresentation) ( wf::IInspectable& abi_representation) { data_.as(abi_representation); return S_OK; } private: ABISequence data_; }; } // namespace Windows::AI::MachineLearning