From 5f982ca6ac106cb6fbaa19c11cc81a36324557ca Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Thu, 28 Jan 2021 20:04:52 +0300 Subject: [PATCH 01/99] Removed obsolete ie_profiling.hpp (#4043) --- .../src/template_async_infer_request.cpp | 9 +- .../src/template_executable_network.cpp | 5 +- docs/template_plugin/src/template_plugin.cpp | 17 +- .../base/ie_infer_async_request_base.hpp | 1 - .../src/plugin_api/ie_profiling.hpp | 242 ------------------ .../src/preprocessing/ie_preprocess_data.hpp | 1 - .../src/preprocessing/ie_preprocess_gapi.hpp | 1 - 7 files changed, 19 insertions(+), 257 deletions(-) delete mode 100644 inference-engine/src/plugin_api/ie_profiling.hpp diff --git a/docs/template_plugin/src/template_async_infer_request.cpp b/docs/template_plugin/src/template_async_infer_request.cpp index 3facaf7327d5f6..41c1f62724f6b5 100644 --- a/docs/template_plugin/src/template_async_infer_request.cpp +++ b/docs/template_plugin/src/template_async_infer_request.cpp @@ -25,16 +25,19 @@ TemplateAsyncInferRequest::TemplateAsyncInferRequest( if (remoteDevice) { _pipeline = { {cpuTaskExecutor, [this] { - IE_PROFILING_AUTO_SCOPE(PreprocessingAndStartPipeline) + OV_ITT_SCOPED_TASK(itt::domains::TemplatePlugin, + "TemplateAsyncInferRequest::PreprocessingAndStartPipeline"); _inferRequest->inferPreprocess(); _inferRequest->startPipeline(); }}, {_waitExecutor, [this] { - IE_PROFILING_AUTO_SCOPE(WaitPipeline) + OV_ITT_SCOPED_TASK(itt::domains::TemplatePlugin, + "TemplateAsyncInferRequest::WaitPipeline"); _inferRequest->waitPipeline(); }}, {cpuTaskExecutor, [this] { - IE_PROFILING_AUTO_SCOPE(Postprocessing) + OV_ITT_SCOPED_TASK(itt::domains::TemplatePlugin, + "TemplateAsyncInferRequest::Postprocessing"); _inferRequest->inferPostprocess(); }} }; diff --git a/docs/template_plugin/src/template_executable_network.cpp b/docs/template_plugin/src/template_executable_network.cpp index 7eff8da41484be..2914289cf4c4a3 100644 --- a/docs/template_plugin/src/template_executable_network.cpp +++ b/docs/template_plugin/src/template_executable_network.cpp @@ -10,6 +10,7 @@ #include "template/template_config.hpp" #include "template_plugin.hpp" #include "template_executable_network.hpp" +#include "template_itt.hpp" using namespace TemplatePlugin; @@ -61,7 +62,7 @@ TemplatePlugin::ExecutableNetwork::ExecutableNetwork(std::istream & model, model.read(dataBlob->buffer(), dataSize); } - // TODO: implement Import / Export of configuration options + // TODO: implement Import / Export of configuration options and merge with `cfg` // TODO: implement Import / Export of network precisions, layouts, preprocessing info auto cnnnetwork = _plugin->GetCore()->ReadNetwork(xmlString, std::move(dataBlob)); @@ -188,6 +189,8 @@ InferenceEngine::Parameter TemplatePlugin::ExecutableNetwork::GetMetric(const st // ! [executable_network:export_impl] void TemplatePlugin::ExecutableNetwork::ExportImpl(std::ostream& modelStream) { + OV_ITT_SCOPED_TASK(itt::domains::TemplatePlugin, "ExecutableNetwork::ExportImpl"); + // Note: custom ngraph extensions are not supported std::map custom_opsets; std::stringstream xmlFile, binFile; diff --git a/docs/template_plugin/src/template_plugin.cpp b/docs/template_plugin/src/template_plugin.cpp index 6b9610f722f00e..ff339499645cb0 100644 --- a/docs/template_plugin/src/template_plugin.cpp +++ b/docs/template_plugin/src/template_plugin.cpp @@ -16,6 +16,7 @@ #include #include "template/template_config.hpp" +#include "template_itt.hpp" #include "template_plugin.hpp" #include "template_executable_network.hpp" #include "template_infer_request.hpp" @@ -74,6 +75,8 @@ std::shared_ptr TransformNetwork(const std::shared_ptr& config) { - // TODO: Import network from stream is not mandatory functionality; - // Can just throw an exception and remove the code below - Configuration exportedCfg; - - // some code below which reads exportedCfg from `model` stream - // .. + OV_ITT_SCOPED_TASK(itt::domains::TemplatePlugin, "Plugin::ImportNetworkImpl"); - auto cfg = Configuration(config, exportedCfg); - auto exec_network_impl = std::make_shared(model, cfg, std::static_pointer_cast(shared_from_this())); + Configuration cfg(config); + auto exec_network_impl = std::make_shared(model, cfg, + std::static_pointer_cast(shared_from_this())); return make_executable_network(exec_network_impl); } @@ -129,6 +128,8 @@ InferenceEngine::ExecutableNetwork Plugin::ImportNetworkImpl(std::istream& model // ! [plugin:query_network] InferenceEngine::QueryNetworkResult Plugin::QueryNetwork(const InferenceEngine::CNNNetwork &network, const ConfigMap& config) const { + OV_ITT_SCOPED_TASK(itt::domains::TemplatePlugin, "Plugin::QueryNetwork"); + InferenceEngine::QueryNetworkResult res; Configuration cfg{config, _cfg, false}; diff --git a/inference-engine/src/plugin_api/cpp_interfaces/base/ie_infer_async_request_base.hpp b/inference-engine/src/plugin_api/cpp_interfaces/base/ie_infer_async_request_base.hpp index 75f2cc32184f37..886de20784007c 100644 --- a/inference-engine/src/plugin_api/cpp_interfaces/base/ie_infer_async_request_base.hpp +++ b/inference-engine/src/plugin_api/cpp_interfaces/base/ie_infer_async_request_base.hpp @@ -13,7 +13,6 @@ #include #include "ie_iinfer_request.hpp" #include "ie_preprocess.hpp" -#include "ie_profiling.hpp" namespace InferenceEngine { diff --git a/inference-engine/src/plugin_api/ie_profiling.hpp b/inference-engine/src/plugin_api/ie_profiling.hpp deleted file mode 100644 index 1fa34cef89ee2a..00000000000000 --- a/inference-engine/src/plugin_api/ie_profiling.hpp +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright (C) 2018-2020 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -/** - * @brief [DEPRECATED] Defines API to profile your plugin using Intel VTune. - * @details This API is still available but deprecated. Use plugin_itt.hpp instead. - * @file ie_profiling.hpp - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * @cond - */ - -#ifdef ENABLE_PROFILING_ITT -#include -#endif - -namespace InferenceEngine { - -template -void annotateBegin(Static&& static_, Block&& block_); - -template -void annotateEnd(Static&& static_, Block&& block_); - -template -struct Annotate { - struct Static_ { - template - struct idx {}; - - template - struct idx : idx {}; - - template - struct idx<0, S...> { - using type = idx; - }; - - template - Static_(ArgTuple&& arg_tuple, idx): static_ {std::get(std::forward(arg_tuple))...} {} - - template - explicit Static_(ArgTuple&& arg_tuple) - : Static_ {std::forward(arg_tuple), typename idx::value>::type {}} {} - - Static static_; - }; - - static Static_ static_; - - Block block_; - - Annotate(const Annotate&) = delete; - Annotate& operator=(const Annotate&) = delete; - Annotate(Annotate&&) = default; - Annotate& operator=(Annotate&&) = default; - - template - inline explicit Annotate(Ts&&... xs): block_ {std::forward(xs)...} { - annotateBegin(static_.static_, block_); - } - - inline ~Annotate() { - annotateEnd(static_.static_, block_); - } -}; - -template -typename Annotate::Static_ Annotate::static_(Local::staticArgs()); - -#define IE_ANNOTATE_CONCAT(x, y) IE_ANNOTATE_CONCAT_EVAL(x, y) -#define IE_ANNOTATE_CONCAT_EVAL(x, y) x##y - -#define IE_ANNOTATE_UNPACK(tuple) IE_ANNOTATE_UNPACK_EVAL tuple -#define IE_ANNOTATE_UNPACK_EVAL(...) __VA_ARGS__ - -#define IE_ANNOTATE_MAKE_NAME(lib_name, postfix) \ - IE_ANNOTATE_CONCAT(IE_ANNOTATE_CONCAT(IE_ANNOTATE_CONCAT(__intel_util_annotate_, lib_name), postfix), __LINE__) - -#define IE_ANNOTATE_LOCAL_TYPE_NAME(lib_name) IE_ANNOTATE_MAKE_NAME(lib_name, _ctx) -#define IE_ANNOTATE_VARIABLE_NAME(lib_name) IE_ANNOTATE_MAKE_NAME(lib_name, _variable) -#define IE_ANNOTATE_FUNC_NAME(lib_name) IE_ANNOTATE_MAKE_NAME(lib_name, _func) - -#define IE_ANNOTATE_MAKE_SCOPE_TYPE(lib_name, static_type, block_type, make_static_args_tuple) \ - struct IE_ANNOTATE_LOCAL_TYPE_NAME(lib_name) \ - : ::InferenceEngine::Annotate { \ - using ::InferenceEngine::Annotate::Annotate; \ - static auto staticArgs() -> decltype(std::make_tuple(IE_ANNOTATE_UNPACK(make_static_args_tuple))) { \ - return std::make_tuple(IE_ANNOTATE_UNPACK(make_static_args_tuple)); \ - } \ - } - -#define IE_ANNOTATE_MAKE_SCOPE(lib_name, static_type, block_type, make_static_args_tuple, make_block_args_tuple) \ - IE_ANNOTATE_MAKE_SCOPE_TYPE(lib_name, static_type, block_type, make_static_args_tuple) \ - IE_ANNOTATE_VARIABLE_NAME(lib_name) {IE_ANNOTATE_UNPACK(make_block_args_tuple)}; - -#ifdef ENABLE_PROFILING_ITT -struct IttTaskHandles { - __itt_domain* const domain; - __itt_string_handle* const handle; - - explicit IttTaskHandles(const char* taskName) - : domain {__itt_domain_create("InferenceEngine")}, handle {__itt_string_handle_create(taskName)} {} -}; - -struct IttBlock {}; - -inline static void annotateBegin(IttTaskHandles& h, IttBlock&) { - __itt_task_begin(h.domain, __itt_null, __itt_null, h.handle); -} - -inline static void annotateEnd(IttTaskHandles& h, IttBlock&) { - __itt_task_end(h.domain); -} - -#define IE_ITT_SCOPE(task_name) \ - IE_ANNOTATE_MAKE_SCOPE(InferenceEngineItt, ::InferenceEngine::IttTaskHandles, ::InferenceEngine::IttBlock, \ - (task_name), ()) -#else -#define IE_ITT_SCOPE(task_name) -#endif - -#define IE_STR(x) IE_STR_(x) -#define IE_STR_(x) #x - -struct ProfilingTask; - -struct IttStatic {}; - -struct IttProfilingTask { - ProfilingTask* t; -}; - -static void annotateBegin(IttStatic&, IttProfilingTask& t); -static void annotateEnd(IttStatic&, IttProfilingTask& t); - -/** - * @class ProfilingTask - * @ingroup ie_dev_profiling - * @brief Used to annotate section of code which would be named at runtime - */ -struct ProfilingTask { - ProfilingTask() = default; - //! @private - ProfilingTask(const ProfilingTask&) = default; - - ProfilingTask& operator =(const ProfilingTask&) = default; - - /** - * @brief Construct ProfilingTask with runtime defined name - */ - inline explicit ProfilingTask(const std::string& taskName) - : name(taskName) -#ifdef ENABLE_PROFILING_ITT - , - domain(__itt_domain_create("InferenceEngine")), - handle(__itt_string_handle_create(taskName.c_str())) -#endif - { - } - -private: - friend void annotateBegin(IttStatic&, IttProfilingTask& t); - friend void annotateEnd(IttStatic&, IttProfilingTask& t); - - std::string name; -#ifdef ENABLE_PROFILING_ITT - __itt_domain* domain; - __itt_string_handle* handle; -#endif -}; - -inline static void annotateBegin(IttStatic&, IttProfilingTask& t) { -#ifdef ENABLE_PROFILING_ITT - __itt_task_begin(t.t->domain, __itt_null, __itt_null, t.t->handle); -#else - (void)t; -#endif -} - -inline static void annotateEnd(IttStatic&, IttProfilingTask& t) { -#ifdef ENABLE_PROFILING_ITT - __itt_task_end(t.t->domain); -#else - (void)t; -#endif -} - -#ifdef ENABLE_PROFILING_ITT -#define IE_ITT_TASK_SCOPE(profilingTask) \ - IE_ANNOTATE_MAKE_SCOPE(InferenceEngineIttScopeTask, ::InferenceEngine::IttStatic, \ - ::InferenceEngine::IttProfilingTask, (), (&(profilingTask))) -#else -#define IE_ITT_TASK_SCOPE(profiling_task) -#endif - -inline static void annotateSetThreadName(const char* name) { -#ifdef ENABLE_PROFILING_ITT - __itt_thread_set_name(name); -#else - (void)(name); -#endif -} - -/** - * @def IE_PROFILING_AUTO_SCOPE(NAME) - * @ingroup ie_dev_profiling - * @brief Annotate section of code till scope exit to be profiled using known at compile time @p NAME as section id - * @param NAME Known at compile time name of section of code that is passed to profiling back end - */ -#define IE_PROFILING_AUTO_SCOPE(NAME) IE_ITT_SCOPE(IE_STR(NAME)); - - -/** - * @def IE_PROFILING_AUTO_SCOPE_TASK(PROFILING_TASK) - * @ingroup ie_dev_profiling - * @brief Annotate section of code till scope exit to be profiled runtime configured variable of ProfilingTask type. - * ProfilingTask::name will be used as section id. - * @param PROFILING_TASK variable of ProfilingTask type - */ -#define IE_PROFILING_AUTO_SCOPE_TASK(PROFILING_TASK) IE_ITT_TASK_SCOPE(PROFILING_TASK); -} // namespace InferenceEngine - -/** - * @endcond - */ diff --git a/inference-engine/src/preprocessing/ie_preprocess_data.hpp b/inference-engine/src/preprocessing/ie_preprocess_data.hpp index 969141a9d3e9ac..f0e351f09bff59 100644 --- a/inference-engine/src/preprocessing/ie_preprocess_data.hpp +++ b/inference-engine/src/preprocessing/ie_preprocess_data.hpp @@ -9,7 +9,6 @@ #include #include -#include #include #include diff --git a/inference-engine/src/preprocessing/ie_preprocess_gapi.hpp b/inference-engine/src/preprocessing/ie_preprocess_gapi.hpp index de953f46c4ff51..d2aaf707bfaf7a 100644 --- a/inference-engine/src/preprocessing/ie_preprocess_gapi.hpp +++ b/inference-engine/src/preprocessing/ie_preprocess_gapi.hpp @@ -13,7 +13,6 @@ #include #include #include -#include "ie_profiling.hpp" #include // FIXME: Move this definition back to ie_preprocess_data, From 08c4ac5372bfb6062a096d81b3fd9aa2f7c9a28a Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Thu, 28 Jan 2021 20:15:06 +0300 Subject: [PATCH 02/99] Refactor install_openvino_dependencies script: extra options and cleanup (#3868) * Refactor install_openvino_dependencies script: extra options and cleanup * install_dependencies: added more python tools * install_openvino_dependencies: extra OS checks Verify consistency for future edits * install_openvino_dependencies: clarify messages --- .../install_openvino_dependencies.sh | 527 ++++++++++-------- 1 file changed, 307 insertions(+), 220 deletions(-) diff --git a/scripts/install_dependencies/install_openvino_dependencies.sh b/scripts/install_dependencies/install_openvino_dependencies.sh index eb0bfb8fd19fbf..e474bd68f57778 100755 --- a/scripts/install_dependencies/install_openvino_dependencies.sh +++ b/scripts/install_dependencies/install_openvino_dependencies.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2018 - 2020 Intel Corporation +# Copyright (c) 2018 - 2021 Intel Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,260 +16,347 @@ set -e -if [ $EUID -ne 0 ]; then - echo "ERROR: this script must be run as root to install 3rd party packages." >&2 - echo "Please try again with \"sudo -E $0\", or as root." >&2 - exit 1 -fi +#=================================================================================================== +# Option parsing -params=$@ +all_comp=(opencv_req opencv_opt python dev myriad dlstreamer installer pot) +os=${os:-auto} -yes_or_no_ffmpeg() { - if [ "$params" == "-y" ]; then - return 0 - fi +# public options +interactive=yes +dry= +extra= +print= +comp=() - while true; do - read -p "Add third-party RPM Fusion repository and install FFmpeg package (y/n): " yn - case $yn in - [Yy]*) return 0 ;; - [Nn]*) return 1 ;; - esac - done -} +# private options +keepcache= +selftest= -yes_or_no_gst_bad_ugly() { - if [ "$params" == "-y" ]; then - return 0 - fi +while :; do + case $1 in + -h|-\?|--help) + echo "Options:" + echo " -y non-interactive run (off)" + echo " -n dry-run, assume no (off)" + echo " -c= install component , can be repeated (${all_comp[*]})" + echo " -e add extra repositories (CentOS 7) (off)" + echo " -p print package list and exit (off)" + exit + ;; + -y) interactive= ;; + -n) dry=yes ;; + -c=?*) comp+=("${1#*=}") ;; + -e) extra=yes ;; + -p) print=yes ;; + --selftest) selftest=yes ;; + --keepcache) keepcache=yes ;; + *) break ;; + esac + shift +done - while true; do - read -p "Add third-party RPM Epel, Nux, Fusion, Forensics repositories and install dependencies for GStreamer Bad & Ugly Plugins (y/n): " yn - case $yn in - [Yy]*) return 0 ;; - [Nn]*) return 1 ;; - esac +# No components selected - install all +if [ ${#comp[@]} -eq 0 ]; then + comp=(${all_comp[@]}) +fi + +#=================================================================================================== +# Selftest + +if [ -n "$selftest" ] ; then + for image in centos:7 ubuntu:18.04 ubuntu:20.04 ; do + for opt in "-h" "-p" "-e -p" "-n" "-n -e" "-y" "-y -e" ; do + echo "||" + echo "|| Test $image / '$opt'" + echo "||" + SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + docker run -it --rm \ + --volume ${SCRIPT_DIR}:/scripts:ro,Z \ + --volume yum-cache:/var/cache/yum \ + --volume apt-cache:/var/cache/apt/archives \ + -e DEBIAN_FRONTEND=noninteractive \ + $image \ + bash /scripts/${0##*/} $opt --keepcache + echo "||" + echo "|| Completed: $image / '$opt'" + echo "||" + done done -} - -if [ -f /etc/lsb-release ]; then - # Ubuntu - echo - echo "This script installs the following OpenVINO 3rd-party dependencies:" - echo " 1. GTK+, FFmpeg and GStreamer libraries used by OpenCV" - echo " 2. libusb library required for Myriad plugin for Inference Engine" - echo " 3. build dependencies for OpenVINO samples" - echo " 4. build dependencies for GStreamer Plugins" - echo - PKGS=( - cpio - build-essential - cmake - libusb-1.0-0-dev - libdrm-dev - libgstreamer1.0-0 + echo "Self test finished, to remove temporary docker volumes run: + 'docker volume rm yum-cache apt-cache'" + exit 0 +fi + +#=================================================================================================== +# OS detection + +if [ "$os" == "auto" ] ; then + os=$( . /etc/os-release ; echo "${ID}${VERSION_ID}" ) + case $os in + centos7|ubuntu18.04|ubuntu20.04) [ -z "$print" ] && echo "Detected OS: ${os}" ;; + *) echo "Unsupported OS: ${os:-detection failed}" >&2 ; exit 1 ;; + esac +fi + +#=================================================================================================== +# Collect packages + +extra_repos=() + +if [ "$os" == "ubuntu18.04" ] ; then + + pkgs_opencv_req=(libgtk-3-0) + pkgs_python=(python3 python3-dev python3-venv python3-setuptools python3-pip) + pkgs_dev=(cmake g++ gcc libc6-dev make curl) + pkgs_myriad=(libusb-1.0-0) + pkgs_installer=(cpio) + pkgs_pot=() + pkgs_opencv_opt=( + gstreamer1.0-plugins-bad gstreamer1.0-plugins-base gstreamer1.0-plugins-good + gstreamer1.0-plugins-ugly + libavcodec57 + libavformat57 + libavresample3 + libavutil55 + libgstreamer1.0-0 + libswscale4 + ) + pkgs_dlstreamer=( + ffmpeg + flex + gstreamer1.0-alsa gstreamer1.0-plugins-bad + gstreamer1.0-plugins-base + gstreamer1.0-plugins-good + gstreamer1.0-plugins-ugly gstreamer1.0-vaapi + libfaac0 + libfluidsynth1 + libgl-dev + libglib2.0 + libgstreamer1.0-0 + libnettle6 + libtag-extras1 + python3-gi + ) + +elif [ "$os" == "ubuntu20.04" ] ; then + + pkgs_opencv_req=(libgtk-3-0) + pkgs_python=(python3 python3-dev python3-venv python3-setuptools python3-pip) + pkgs_dev=(cmake g++ gcc libc6-dev make curl) + pkgs_myriad=(libusb-1.0-0) + pkgs_installer=(cpio) + pkgs_pot=(libblas-dev liblapack-dev gfortran) + pkgs_opencv_opt=( + gstreamer1.0-plugins-bad + gstreamer1.0-plugins-base + gstreamer1.0-plugins-good + gstreamer1.0-plugins-ugly + libavcodec58 + libavformat58 + libavresample4 + libavutil56 + libgstreamer1.0-0 + libswscale5 + ) + pkgs_dlstreamer=( ffmpeg + flex + gstreamer1.0-alsa + gstreamer1.0-libav + gstreamer1.0-plugins-bad + gstreamer1.0-plugins-base + gstreamer1.0-plugins-good + gstreamer1.0-plugins-ugly + gstreamer1.0-vaapi + libfaac0 + libfluidsynth2 + libgl-dev + libglib2.0-0 + libgstreamer-plugins-base1.0-dev + libgstreamer1.0-0 + libgstrtspserver-1.0-dev + libnettle7 + libopenexr24 + libtag-extras1 + python3-gi + python3-gst-1.0 ) - system_ver=$(cat /etc/lsb-release | grep -i "DISTRIB_RELEASE" | cut -d "=" -f2) - if [ "$system_ver" = "16.04" ]; then - PKGS+=( libgtk2.0-0 ) - else - if [ "$system_ver" = "20.04" ]; then - PKGS+=( gstreamer1.0-plugins-ugly - gstreamer1.0-libav - libgstreamer-plugins-base1.0-dev - gstreamer1.0-alsa - libgstrtspserver-1.0-dev - python3-gst-1.0 - libfluidsynth2 - libnettle7 - libopenexr24 - python3.8 - libpython3.8 - libglib2.0-0 - ) - elif [ "$system_ver" = "18.04" ]; then - PKGS+=( libfluidsynth1 - libnettle6 - libopenexr22 - gstreamer1.0-plugins-ugly - gstreamer1.0-alsa - libglib2.0 - ) - fi - PKGS+=( flex - libgl-dev - libtag-extras1 - libusb-1.0-0-dev - libfaac0 - python3-gi - libgtk-3-0 - ) - fi - apt update - # shellcheck disable=SC2068 - apt install -y ${PKGS[@]} -else - # CentOS - echo - echo "This script installs the following OpenVINO 3rd-party dependencies:" - echo " 1. GTK+ and GStreamer libraries used by OpenCV" - echo " 2. libusb library required for Myriad plugin for Inference Engine" - echo " 3. Python 3.6 for Model Optimizer" - echo " 4. gcc 4.8.5 and other build dependencies for OpenVINO samples" - echo " 5. build dependencies for GStreamer Plugins" - echo - PKGS=( - libusbx-devel - gtk2 + +elif [ "$os" == "centos7" ] ; then + + # find -name *.so -exec objdump -p {} \; | grep NEEDED | sort -u | cut -c 23- | xargs -t -n1 yum -q whatprovides + + pkgs_opencv_req=(gtk2) + pkgs_python=(python3 python3-devel python3-setuptools python3-pip) + pkgs_dev=(gcc gcc-c++ make glibc libstdc++ libgcc cmake curl) + pkgs_myriad=(libusbx) + pkgs_installer=() + pkgs_pot=() + pkgs_opencv_opt=( gstreamer1 - gstreamer1-plugins-good gstreamer1-plugins-bad-free - gcc - gcc-c++ - make - glibc-static + gstreamer1-plugins-good + gstreamer1-plugins-ugly-free + ) + pkgs_dlstreamer=( + OpenEXR-libs + alsa-lib + boost-regex + bzip2-libs + cairo + cdparanoia-libs + flac-libs + flite + gdk-pixbuf2 + glib2 glibc - libstdc++-static - libstdc++ - libstdc++ - libgcc - cmake - python36 - python36-pip - glib2-devel - flex gmp - gsl - libcap - libcap - gettext - libXrandr + gsm + gstreamer1 + gstreamer1-plugins-bad-free + gstreamer1-plugins-base + ilmbase libX11 - iso-codes - mesa-libEGL - mesa-libGLES - mesa-libGL - libgudev1 - libtheora - cdparanoia - pango - mesa-libgbm - alsa-lib + libXdamage + libXext + libXfixes + libXrandr + libXrender + libXv + libdrm + libdv + libgcc + libglvnd-glx libjpeg-turbo + libogg + libpng + librdkafka + librsvg2 + libsndfile + libsoup + libstdc++ + libtheora + libuuid + libv4l libvisual - libXv - opus libvorbis - patch - bzip2 - libv4l - flac - gdk-pixbuf2 - libdv - mpg123 - libraw1394 - libavc1394 - libiec61883 + libxml2 + mpg123-libs + neon + nettle + openjpeg2 + openssl-libs + opus + orc + pango pulseaudio-libs - libsoup + sbc + soundtouch speex wavpack - boost-regex-1.53.0 + xz-libs + zlib ) - yum install -y ${PKGS[@]} - # Thirdparty repositories for installing GStreamer Bad & Ugly Plugins dependencies. - if yes_or_no_gst_bad_ugly; then - GST_BAD_UGLY_PKGS=( - bluez-libs - libusb + if [ -n "$extra" ] ; then + # 1 RPMFusion + extra_repos+=(https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm) + pkgs_opencv_opt+=(ffmpeg-libs) + pkgs_dlstreamer+=( + libde265 + libmms + librtmp + opencore-amr + vo-amrwbenc + ) + # 2 EPEL + extra_repos+=(https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm) + pkgs_dlstreamer+=( + fluidsynth-libs + game-music-emu libass libbs2b libchromaprint - lcms2 - libssh2 - libdc1394 - libXext - libssh - libdca - faac - fdk-aac - flite - fluidsynth - game-music-emu - gsm - nettle - kate - liblrdf - libde265 - mjpegtools - libmms libmodplug - libmpcdec - neon openal-soft - OpenEXR - openjpeg2 - openni - libdvdnav - librtmp - librsvg2 - sbc - libsndfile - soundtouch + paho-c spandsp - libsrtp - zvbi - vo-amrwbenc - webrtc-audio-processing - wildmidi zbar - libnice - libxkbcommon - opencore-amr - libva - python36-gobject - python3-devel + zvbi + ) + # 3 ForensicsTools + extra_repos+=(https://forensics.cert.org/cert-forensics-tools-release-el7.rpm) + pkgs_dlstreamer+=( + faac + fdk-aac ) - yum install -y epel-release - rpm -Uvh https://download1.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm - RPMFUSION_IS_INSTALLED=1 - yum install -y https://forensics.cert.org/cert-forensics-tools-release-el7.rpm - yum install -y ${GST_BAD_UGLY_PKGS[@]} - else - echo "Dependencies for GStreamer Ugly & Bad plugins installation skipped." - echo fi - echo - echo "Intel(R) Distribution of OpenVINO(TM) toolkit can use FFmpeg for processing video streams with OpenCV. Please select your preferred method for installing FFmpeg:" - echo - echo "Option 1: Allow installer script to add a third party repository, RPM Fusion (https://rpmfusion.org/), which contains FFmpeg. FFmpeg rpm package will be installed from this repository. " - echo "WARNING: This repository is NOT PROVIDED OR SUPPORTED by Intel or CentOS. Neither Intel nor CentOS has control over this repository. Terms governing your use of FFmpeg can be found here: https://www.ffmpeg.org/legal.html " - echo "Once added, this repository will be enabled on your operating system and can thus receive updates to all packages installed from it. " - echo - echo "Consider the following ways to prevent unintended 'updates' from this third party repository from over-writing some core part of CentOS:" - echo "a) Only enable these archives from time to time, and generally leave them disabled. See: man yum" - echo "b) Use the exclude= and includepkgs= options on a per sub-archive basis, in the matching .conf file found in /etc/yum.repos.d/ See: man yum.conf" - echo "c) The yum Priorities plug-in can prevent a 3rd party repository from replacing base packages, or prevent base/updates from replacing a 3rd party package." - echo - echo "Option 2: Skip FFmpeg installation." - echo - - if yes_or_no_ffmpeg; then - if [[ -z $RPMFUSION_IS_INSTALLED ]]; then - yum install -y epel-release - rpm -Uvh https://download1.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm - fi - yum install -y ffmpeg +else + echo "Internal script error: invalid OS after check (package selection)" >&2 + exit 3 +fi + +#=================================================================================================== +# Gather packages and print list + +pkgs=() +for comp in ${comp[@]} ; do + var=pkgs_${comp}[@] + pkgs+=(${!var}) +done + +if [ ${#pkgs[@]} -eq 0 ]; then + if [ -n "$print" ] ; then + echo "No packages to install" >&2 + exit 1 else - echo "FFmpeg installation skipped. You may build FFmpeg from sources as described here: https://trac.ffmpeg.org/wiki/CompilationGuide/Centos" - echo + echo "No packages to install" + exit 0 fi - exit fi + +if [ -n "$print" ] ; then + echo "${pkgs[*]}" + exit 0 +fi + +#=================================================================================================== +# Actual installation + +if [ $EUID -ne 0 ]; then + echo "ERROR: this script must be run as root to install 3rd party packages." >&2 + echo "Please try again with \"sudo -E $0\", or as root." >&2 + exit 1 +fi + +iopt= + +if [ "$os" == "ubuntu18.04" ] || [ "$os" == "ubuntu20.04" ] ; then + + [ -z "$interactive" ] && iopt="-y" + [ -n "$dry" ] && iopt="--dry-run" + [ -n "$keepcache" ] && rm -f /etc/apt/apt.conf.d/docker-clean + + apt-get update && apt-get install --no-install-recommends $iopt ${pkgs[@]} + +elif [ "$os" == "centos7" ] ; then + + [ -z "$interactive" ] && iopt="--assumeyes" + [ -n "$dry" ] && iopt="--downloadonly" + [ -n "$keepcache" ] && iopt="$iopt --setopt=keepcache=1" + [ ${#extra_repos[@]} -ne 0 ] && yum localinstall $iopt --nogpgcheck ${extra_repos[@]} + + yum install $iopt ${pkgs[@]} + +else + echo "Internal script error: invalid OS after check (package installation)" >&2 + exit 3 +fi + +exit 0 From b0f5a339e1ed04b8a1d9416ca115fa86b0f5df90 Mon Sep 17 00:00:00 2001 From: Yury Gaydaychuk Date: Thu, 28 Jan 2021 21:16:13 +0300 Subject: [PATCH 03/99] Proposal test uses special run() method to check exception throwing (#4062) * proposal test uses special run() method to check exception throwing * validate() removed from run() --- .../shared/include/behavior/invalid_cases/proposal.hpp | 1 + .../plugin/shared/src/behavior/invalid_cases/proposal.cpp | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/inference-engine/tests/functional/plugin/shared/include/behavior/invalid_cases/proposal.hpp b/inference-engine/tests/functional/plugin/shared/include/behavior/invalid_cases/proposal.hpp index 880f9d1fda665e..5ca19336b489bf 100644 --- a/inference-engine/tests/functional/plugin/shared/include/behavior/invalid_cases/proposal.hpp +++ b/inference-engine/tests/functional/plugin/shared/include/behavior/invalid_cases/proposal.hpp @@ -23,6 +23,7 @@ class ProposalBehTest protected: void SetUp() override; void Validate() override {}; + void Run() override; const LayerTestsDefinitions::normalize_type normalize = true; const LayerTestsDefinitions::feat_stride_type feat_stride = 1; diff --git a/inference-engine/tests/functional/plugin/shared/src/behavior/invalid_cases/proposal.cpp b/inference-engine/tests/functional/plugin/shared/src/behavior/invalid_cases/proposal.cpp index 3ba5ed9a5316c9..2292a8b6a293a9 100644 --- a/inference-engine/tests/functional/plugin/shared/src/behavior/invalid_cases/proposal.cpp +++ b/inference-engine/tests/functional/plugin/shared/src/behavior/invalid_cases/proposal.cpp @@ -93,6 +93,11 @@ void ProposalBehTest::SetUp() { function = std::make_shared(results, params, "proposal"); } +void ProposalBehTest::Run() { + LoadNetwork(); + Infer(); +} + TEST_P(ProposalBehTest, CompareWithRefs) { ASSERT_THROW(Run(), InferenceEngine::details::InferenceEngineException); } From 2ebae7cf304d9def9fc8dbca7cb8120d100de774 Mon Sep 17 00:00:00 2001 From: Ilya Churaev Date: Fri, 29 Jan 2021 06:28:31 +0300 Subject: [PATCH 04/99] Introduce the Broker API to map original framework names to OV (#3800) * Added tests * Fixed tests * Added tests to check addOutput method * Added support of port names in the IR * Update copyrights * Deprecate tensor name * Fixed comments * Enabled functional tests for GPU, GNA and Myriad * Fixed get_tensor().get_names() * Added unit test to check tensor names * Fixed code style * Skip add output test for GNA * Added serialization support * Added PythonAPI * Fixed tests * Fixed tests * Fixed typo * Try to disable GNA test * Fixed tests * Removed unused variables * Fixed tests * Update documentation * Fixed comment --- .../src/openvino/inference_engine/ie_api.pyx | 8 + .../openvino/inference_engine/ie_api_impl.cpp | 8 + .../openvino/inference_engine/ie_api_impl.hpp | 3 + .../inference_engine/ie_api_impl_defs.pxd | 2 + .../ie_bridges/python/tests/test_IENetwork.py | 58 ++++++ inference-engine/include/cpp/ie_cnn_network.h | 28 ++- inference-engine/include/ie_icnn_network.hpp | 43 ++++- .../src/cldnn_engine/ops/result.cpp | 2 + .../src/cldnn_engine/ops/split.cpp | 2 + .../cnn_network_ngraph_impl.cpp | 57 +++++- .../cnn_network_ngraph_impl.hpp | 9 +- .../src/convert_function_to_cnn_network.cpp | 6 +- .../src/readers/ir_reader/ie_ir_parser.cpp | 12 +- .../src/readers/ir_reader/ie_ir_parser.hpp | 3 +- .../control_flow/unroll_tensor_iterator.cpp | 8 +- .../convert_ti_to_sequences.cpp | 8 +- .../src/transformations/serialize.cpp | 9 + .../ir_serialization/tensor_names.cpp | 58 ++++++ .../ngraph_reader/tensor_names.cpp | 89 ++++++++++ .../subgraph_tests/tensor_names.cpp | 16 ++ .../skip_tests_config.cpp | 1 + .../subgraph_tests/tensor_names.cpp | 17 ++ .../subgraph_tests/tensor_names.cpp | 18 ++ .../subgraph_tests/tensor_names.cpp | 19 ++ .../include/subgraph_tests/tensor_names.hpp | 166 ++++++++++++++++++ .../subgraph/tensor_names.hpp | 28 +++ .../src/subgraph/tensor_names.cpp | 35 ++++ .../common_test_utils/ngraph_test_utils.cpp | 7 + .../core/include/ngraph/descriptor/output.hpp | 2 + .../core/include/ngraph/descriptor/tensor.hpp | 7 + ngraph/core/include/ngraph/node.hpp | 4 + ngraph/core/include/ngraph/node_output.hpp | 1 + ngraph/core/include/ngraph/runtime/tensor.hpp | 1 + ngraph/core/src/descriptor/tensor.cpp | 35 +++- ngraph/core/src/graph_util.cpp | 2 + ngraph/core/src/node.cpp | 20 ++- ngraph/core/src/runtime/host_tensor.cpp | 4 + ngraph/core/src/runtime/tensor.cpp | 2 + ngraph/test/tensor.cpp | 21 +++ 39 files changed, 788 insertions(+), 31 deletions(-) create mode 100644 inference-engine/tests/functional/inference_engine/ir_serialization/tensor_names.cpp create mode 100644 inference-engine/tests/functional/inference_engine/ngraph_reader/tensor_names.cpp create mode 100644 inference-engine/tests/functional/plugin/cpu/shared_tests_instances/subgraph_tests/tensor_names.cpp create mode 100644 inference-engine/tests/functional/plugin/gna/shared_tests_instances/subgraph_tests/tensor_names.cpp create mode 100644 inference-engine/tests/functional/plugin/gpu/shared_tests_instances/subgraph_tests/tensor_names.cpp create mode 100644 inference-engine/tests/functional/plugin/myriad/shared_tests_instances/subgraph_tests/tensor_names.cpp create mode 100644 inference-engine/tests/functional/plugin/shared/include/subgraph_tests/tensor_names.hpp create mode 100644 inference-engine/tests/functional/shared_test_classes/include/shared_test_classes/subgraph/tensor_names.hpp create mode 100644 inference-engine/tests/functional/shared_test_classes/src/subgraph/tensor_names.cpp diff --git a/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api.pyx b/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api.pyx index e92dcaaa58e8ba..3a84f61cfa9dcc 100644 --- a/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api.pyx +++ b/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api.pyx @@ -1439,6 +1439,14 @@ cdef class IENetwork: def _get_function_capsule(self): return self.impl.getFunction() + def get_ov_name_for_tensor(self, orig_name: str): + name = bytes(orig_name, 'utf-8') + return self.impl.getOVNameForTensor(name).decode('utf-8') + + def get_ov_name_for_operation(self, orig_name: str): + name = bytes(orig_name, 'utf-8') + return self.impl.getOVNameForOperation(name).decode('utf-8') + cdef class BlobBuffer: """Copy-less accessor for Inference Engine Blob""" diff --git a/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl.cpp b/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl.cpp index 226cc73bc2ee42..7a2bd205a0837d 100644 --- a/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl.cpp +++ b/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl.cpp @@ -260,6 +260,14 @@ const std::map InferenceEnginePython::IE return outputs; } +std::string InferenceEnginePython::IENetwork::getOVNameForTensor(const std::string& orig_name) { + return actual->getOVNameForTensor(orig_name); +} + +std::string InferenceEnginePython::IENetwork::getOVNameForOperation(const std::string& orig_name) { + return actual->getOVNameForOperation(orig_name); +} + void InferenceEnginePython::IENetwork::addOutput(const std::string &out_layer, size_t port_id) { actual->addOutput(out_layer, port_id); diff --git a/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl.hpp b/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl.hpp index 5534d1ddb53215..eff8c8cec3f504 100644 --- a/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl.hpp +++ b/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl.hpp @@ -71,6 +71,9 @@ struct IENetwork { IENetwork() = default; void convertToOldRepresentation(); + + std::string getOVNameForTensor(const std::string& orig_name); + std::string getOVNameForOperation(const std::string& orig_name); }; diff --git a/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl_defs.pxd b/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl_defs.pxd index d11d8b526a8743..91b3e9af849e90 100644 --- a/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl_defs.pxd +++ b/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl_defs.pxd @@ -175,6 +175,8 @@ cdef extern from "ie_api_impl.hpp" namespace "InferenceEnginePython": void load_from_buffer(const char*xml, size_t xml_size, uint8_t*bin, size_t bin_size) except + object getFunction() except + void convertToOldRepresentation() except + + string getOVNameForTensor(const string &) except + + string getOVNameForOperation(const string &) except + cdef cppclass InferRequestWrap: double exec_time; diff --git a/inference-engine/ie_bridges/python/tests/test_IENetwork.py b/inference-engine/ie_bridges/python/tests/test_IENetwork.py index e3c52497814e1f..a1192fe64e9ccf 100644 --- a/inference-engine/ie_bridges/python/tests/test_IENetwork.py +++ b/inference-engine/ie_bridges/python/tests/test_IENetwork.py @@ -247,3 +247,61 @@ def test_multi_out_data(): assert net.outputs["28/Reshape"].name == "28/Reshape" and net.outputs["28/Reshape"].shape == [1, 5184] assert net.outputs["fc_out"].name == "fc_out" and net.outputs["fc_out"].shape == [1, 10] pass + +def test_tensor_names(): + model = """ + + + + + + + 1 + 3 + 22 + 22 + + + + + + + 1 + 3 + 22 + 22 + + + + + 1 + 3 + 22 + 22 + + + + + + + 1 + 3 + 22 + 22 + + + + + + + + + + """ + ie = IECore() + weights = b'' + net = ie.read_network(model=model.encode('utf-8'), weights=weights, init_from_buffer=True) + assert net.get_ov_name_for_tensor("relu_t") == "activation" + assert net.get_ov_name_for_tensor("identity_t") == "activation" + assert net.get_ov_name_for_tensor("input") == "in1" + assert net.get_ov_name_for_operation("output") == "activation" diff --git a/inference-engine/include/cpp/ie_cnn_network.h b/inference-engine/include/cpp/ie_cnn_network.h index 9544646a6d089d..8fc28ec41351d0 100644 --- a/inference-engine/include/cpp/ie_cnn_network.h +++ b/inference-engine/include/cpp/ie_cnn_network.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2020 Intel Corporation +// Copyright (C) 2018-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // @@ -189,6 +189,32 @@ class INFERENCE_ENGINE_API_CLASS(CNNNetwork) { */ void serialize(const std::string& xmlPath, const std::string& binPath = {}) const; + /** + * @brief Method maps framework tensor name to OpenVINO name + * + * @param orig_name Framework tensor name + * + * @return OpenVINO name + */ + std::string getOVNameForTensor(const std::string& orig_name) const { + std::string ov_name; + CALL_STATUS_FNC(getOVNameForTensor, ov_name, orig_name); + return ov_name; + } + + /** + * @brief Method maps framework operator name to OpenVINO name + * + * @param orig_name Framework operation name + * + * @return OpenVINO name + */ + std::string getOVNameForOperation(const std::string& orig_name) const { + std::string ov_name; + CALL_STATUS_FNC(getOVNameForOperation, ov_name, orig_name); + return ov_name; + } + protected: IE_SUPPRESS_DEPRECATED_START /** diff --git a/inference-engine/include/ie_icnn_network.hpp b/inference-engine/include/ie_icnn_network.hpp index 946e3044a30daf..2c6b5bea3ff2f0 100644 --- a/inference-engine/include/ie_icnn_network.hpp +++ b/inference-engine/include/ie_icnn_network.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2020 Intel Corporation +// Copyright (C) 2018-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // @@ -69,9 +69,11 @@ class INFERENCE_ENGINE_ICNNNETWORK_CLASS(ICNNNetwork) : public details::IRelease * * For single and multiple outputs networks. * - * This method need to be called to find output names for using them later + * This method need to be called to find out OpenVINO output names for using them later * when calling InferenceEngine::InferRequest::GetBlob or InferenceEngine::InferRequest::SetBlob * + * If you want to use framework names, you can use InferenceEngine::ICNNNetwork::getOVNameForTensor or + * InferenceEngine::ICNNNetwork::getOVNameForOperation methods to map framework names to OpenVINO names * * @param out Reference to the OutputsDataMap object */ @@ -82,9 +84,12 @@ class INFERENCE_ENGINE_ICNNNETWORK_CLASS(ICNNNetwork) : public details::IRelease * object. * * For single and multiple inputs networks. - * This method need to be called to find out input names for using them later + * This method need to be called to find out OpenVINO input names for using them later * when calling InferenceEngine::InferRequest::SetBlob * + * If you want to use framework names, you can use InferenceEngine::ICNNNetwork::getOVNameForTensor or + * InferenceEngine::ICNNNetwork::getOVNameForOperation methods to map framework names to OpenVINO names + * * @param inputs Reference to InputsDataMap object. */ virtual void getInputsInfo(InputsDataMap& inputs) const noexcept = 0; @@ -179,6 +184,38 @@ class INFERENCE_ENGINE_ICNNNETWORK_CLASS(ICNNNetwork) : public details::IRelease virtual StatusCode serialize(const std::string& xmlPath, const std::string& binPath, ResponseDesc* resp) const noexcept = 0; + /** + * @brief Methods maps framework tensor name to OpenVINO name + * + * @param ov_name OpenVINO name + * @param orig_name Framework tensor name + * @param resp Pointer to the response message that holds a description of an error if any occurred + * + * @return Status code of the operation + */ + virtual StatusCode getOVNameForTensor(std::string& ov_name, const std::string& orig_name, ResponseDesc* resp) const noexcept { + (void) ov_name; + (void) orig_name; + (void) resp; + return NOT_IMPLEMENTED; + } + + /** + * @brief Methods maps framework operation name to OpenVINO name + * + * @param ov_name OpenVINO name + * @param orig_name Framework operation name + * @param resp Pointer to the response message that holds a description of an error if any occurred + * + * @return Status code of the operation + */ + virtual StatusCode getOVNameForOperation(std::string& ov_name, const std::string& orig_name, ResponseDesc* resp) const noexcept { + (void) ov_name; + (void) orig_name; + (void) resp; + return NOT_IMPLEMENTED; + } + /** * @brief A virtual destructor. */ diff --git a/inference-engine/src/cldnn_engine/ops/result.cpp b/inference-engine/src/cldnn_engine/ops/result.cpp index 56ad5e9f5c017a..536caf22eb7555 100644 --- a/inference-engine/src/cldnn_engine/ops/result.cpp +++ b/inference-engine/src/cldnn_engine/ops/result.cpp @@ -18,7 +18,9 @@ void CreateResultOp(Program& p, const std::shared_ptr& o p.ValidateInputs(op, {1}); auto prev = op->get_input_node_shared_ptr(0); + NGRAPH_SUPPRESS_DEPRECATED_START auto inputID = op->get_input_source_output(0).get_tensor().get_name(); + NGRAPH_SUPPRESS_DEPRECATED_END if (inputID.empty()) { inputID = prev->get_friendly_name(); if (prev->get_output_size() > 1) { diff --git a/inference-engine/src/cldnn_engine/ops/split.cpp b/inference-engine/src/cldnn_engine/ops/split.cpp index 65cbf59873b831..3639a3c583a2e5 100644 --- a/inference-engine/src/cldnn_engine/ops/split.cpp +++ b/inference-engine/src/cldnn_engine/ops/split.cpp @@ -24,6 +24,7 @@ void CreateCommonSplitOp(Program& p, const std::shared_ptr& op) { for (size_t i = 0; i < op->get_output_size(); i++) { std::string outLayerName = layerName + (is_single_out_split ? "" : "." + std::to_string(i)); const auto outLayerDims = op->get_output_shape(i); + NGRAPH_SUPPRESS_DEPRECATED_START if (outLayerDims.size() != startOffset.size()) { THROW_IE_EXCEPTION << "Invalid dimesions in split layer: " << op->get_friendly_name() << " output: " << op->get_output_tensor_name(i); @@ -34,6 +35,7 @@ void CreateCommonSplitOp(Program& p, const std::shared_ptr& op) { << " output: " << op->get_output_tensor_name(i); } } + NGRAPH_SUPPRESS_DEPRECATED_END auto outTensor = CldnnTensorFromIEDims(outLayerDims, 1); auto offsetTensor = CldnnTensorFromIEDims(startOffset, 0); diff --git a/inference-engine/src/inference_engine/cnn_network_ngraph_impl.cpp b/inference-engine/src/inference_engine/cnn_network_ngraph_impl.cpp index 2355a5674ab20c..715e362ea47b92 100644 --- a/inference-engine/src/inference_engine/cnn_network_ngraph_impl.cpp +++ b/inference-engine/src/inference_engine/cnn_network_ngraph_impl.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2020 Intel Corporation +// Copyright (C) 2018-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // @@ -122,6 +122,12 @@ CNNNetworkNGraphImpl::CNNNetworkNGraphImpl( std::string outName = layer->get_friendly_name(); IE_ASSERT(layer->get_output_size() == 1); // Parameter as only singly output port + // map original names to OpenVINO name + _opNames[outName] = outName; + for (const auto& name : layer->get_output_tensor(0).get_names()) { + _tensorNames[name] = outName; + } + DataPtr& ptr = _data[outName]; IE_ASSERT(ptr); // Data must be allocated after the reshape method @@ -139,7 +145,10 @@ CNNNetworkNGraphImpl::CNNNetworkNGraphImpl( } CNNNetworkNGraphImpl::CNNNetworkNGraphImpl(const CNNNetwork& network) { - if (network.getFunction() == nullptr) { + IE_SUPPRESS_DEPRECATED_START + const ICNNNetwork& iNetwork = network; + const auto net = dynamic_cast(&iNetwork); + if (network.getFunction() == nullptr || !net) { THROW_IE_EXCEPTION << "Cannot create CNNNetwork with nGraph from legacy network format!"; } @@ -147,6 +156,9 @@ CNNNetworkNGraphImpl::CNNNetworkNGraphImpl(const CNNNetwork& network) { InputsDataMap inputs = network.getInputsInfo(); OutputsDataMap outputs = network.getOutputsInfo(); + _opNames = net->_opNames; + _tensorNames = net->_tensorNames; + for (const auto& outputInfo : outputs) { const auto& name = outputInfo.second->getName(); DataPtr output = std::make_shared(name, outputInfo.second->getTensorDesc()); @@ -164,6 +176,7 @@ CNNNetworkNGraphImpl::CNNNetworkNGraphImpl(const CNNNetwork& network) { info->setLayout(inputInfo.second->getLayout()); _inputData[name] = info; } + IE_SUPPRESS_DEPRECATED_END } void CNNNetworkNGraphImpl::setInputInfo(InputInfo::Ptr data) { @@ -204,19 +217,22 @@ StatusCode CNNNetworkNGraphImpl::addOutput(const std::string& layerName, size_t try { for (const auto & layer : _ngraph_function->get_ops()) { - if (layer->get_friendly_name() == layerName) { + // Result can have the same name as previous operation + if (layer->get_friendly_name() == layerName && !std::dynamic_pointer_cast(layer)) { + std::string outputName = layerName; + if (layer->outputs().size() != 1) { + outputName += "." + std::to_string(outputIndex); + } + // Check that we don't have a result for the output port for (const auto& port : layer->output(outputIndex).get_target_inputs()) { if (dynamic_cast(port.get_node())) return OK; } auto result = make_shared<::ngraph::op::Result>(layer->output(outputIndex)); + result->set_friendly_name(outputName); _ngraph_function->add_results({result}); - std::string outputName = layerName; - if (layer->outputs().size() != 1) { - outputName += "." + std::to_string(outputIndex); - } if (_outputData.count(outputName) == 0) { reshape(); } @@ -237,6 +253,17 @@ void CNNNetworkNGraphImpl::addOutput(const ::ngraph::Output<::ngraph::Node> & ou createDataForResult(output, dataName, data); _data[dataName] = data; _outputData[dataName] = data; + + // Save original framework names + for (const auto& name : output.get_tensor().get_names()) { + _tensorNames[name] = dataName; + } + for (const auto consumerInput : output.get_target_inputs()) { + const auto &consumerLayer = consumerInput.get_node()->shared_from_this(); + if (std::dynamic_pointer_cast(consumerLayer)) { + _opNames[consumerLayer->get_friendly_name()] = dataName; + } + } } size_t CNNNetworkNGraphImpl::getBatchSize() const noexcept { @@ -391,7 +418,7 @@ StatusCode CNNNetworkNGraphImpl::serialize(const std::string& xmlPath, ResponseDesc* resp) const noexcept { try { std::map custom_opsets; - for (auto extension : _ie_extensions) { + for (const auto& extension : _ie_extensions) { auto opset = extension->getOpSets(); custom_opsets.insert(begin(opset), end(opset)); } @@ -410,6 +437,20 @@ StatusCode CNNNetworkNGraphImpl::serialize(const std::string& xmlPath, return OK; } +StatusCode CNNNetworkNGraphImpl::getOVNameForTensor(std::string& ov_name, const std::string& orig_name, ResponseDesc* resp) const noexcept { + if (_tensorNames.find(orig_name) == _tensorNames.end()) + return DescriptionBuffer(NOT_FOUND, resp) << "Framework tensor with name \"" << orig_name << "\" was not mapped to OpenVINO data!"; + ov_name = _tensorNames.at(orig_name); + return OK; +} + +StatusCode CNNNetworkNGraphImpl::getOVNameForOperation(std::string& ov_name, const std::string& orig_name, ResponseDesc* resp) const noexcept { + if (_opNames.find(orig_name) == _opNames.end()) + return DescriptionBuffer(NOT_FOUND, resp) << "Framework operation with name \"" << orig_name << "\" was not mapped to OpenVINO data!"; + ov_name = _opNames.at(orig_name); + return OK; +} + StatusCode CNNNetworkNGraphImpl::setBatchSize(size_t size, ResponseDesc* responseDesc) noexcept { try { if (getBatchSize() == size) return OK; diff --git a/inference-engine/src/inference_engine/cnn_network_ngraph_impl.hpp b/inference-engine/src/inference_engine/cnn_network_ngraph_impl.hpp index 7d3070afaec472..7778ec8ae82424 100644 --- a/inference-engine/src/inference_engine/cnn_network_ngraph_impl.hpp +++ b/inference-engine/src/inference_engine/cnn_network_ngraph_impl.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2020 Intel Corporation +// Copyright (C) 2018-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -81,6 +82,10 @@ class INFERENCE_ENGINE_API_CLASS(CNNNetworkNGraphImpl): public ICNNNetwork { StatusCode serialize(const std::string& xmlPath, const std::string& binPath, ResponseDesc* resp) const noexcept override; + StatusCode getOVNameForTensor(std::string& ov_name, const std::string& orig_name, ResponseDesc* resp) const noexcept override; + + StatusCode getOVNameForOperation(std::string& ov_name, const std::string& orig_name, ResponseDesc* resp) const noexcept override; + // used by convertFunctionToICNNNetwork from legacy library std::map _data; protected: @@ -91,6 +96,8 @@ class INFERENCE_ENGINE_API_CLASS(CNNNetworkNGraphImpl): public ICNNNetwork { InferenceEngine::InputsDataMap _inputData; std::map _outputData; const std::vector _ie_extensions; + std::unordered_map _opNames; + std::unordered_map _tensorNames; /** * @brief Create DataPtr for nGraph operation diff --git a/inference-engine/src/legacy_api/src/convert_function_to_cnn_network.cpp b/inference-engine/src/legacy_api/src/convert_function_to_cnn_network.cpp index df634bf08e1491..423da8dde53d28 100644 --- a/inference-engine/src/legacy_api/src/convert_function_to_cnn_network.cpp +++ b/inference-engine/src/legacy_api/src/convert_function_to_cnn_network.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2020 Intel Corporation +// Copyright (C) 2018-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // @@ -1876,7 +1876,9 @@ void convertFunctionToICNNNetwork(const std::shared_ptroutData.clear(); continue; } + NGRAPH_SUPPRESS_DEPRECATED_START auto outName = layer->output(i).get_tensor().get_name(); + NGRAPH_SUPPRESS_DEPRECATED_END if (outName.empty()) { outName = ngraph::op::util::create_ie_output_name(layer->output(i)); } @@ -1930,7 +1932,9 @@ void convertFunctionToICNNNetwork(const std::shared_ptr(layer)) { IE_ASSERT(layer->get_input_size() == 1); const auto &input = layer->input_value(0); + NGRAPH_SUPPRESS_DEPRECATED_START auto name = input.get_tensor().get_name(); + NGRAPH_SUPPRESS_DEPRECATED_END if (!name.empty()) cnnNetworkImpl->addOutput(name); else diff --git a/inference-engine/src/readers/ir_reader/ie_ir_parser.cpp b/inference-engine/src/readers/ir_reader/ie_ir_parser.cpp index 05a48664f523fc..cb40347a4135af 100644 --- a/inference-engine/src/readers/ir_reader/ie_ir_parser.cpp +++ b/inference-engine/src/readers/ir_reader/ie_ir_parser.cpp @@ -601,7 +601,7 @@ void V10Parser::parsePreProcess(CNNNetwork& network, const pugi::xml_node& root, } V10Parser::GenericLayerParams V10Parser::XmlDeserializer::parseGenericParams(const pugi::xml_node& node) { - const auto parsePort = [](const pugi::xml_node& parentNode, + const auto parsePort = [this](const pugi::xml_node& parentNode, const GenericLayerParams& params, bool input) -> GenericLayerParams::LayerPortData { GenericLayerParams::LayerPortData port; @@ -626,6 +626,12 @@ V10Parser::GenericLayerParams V10Parser::XmlDeserializer::parseGenericParams(con type = InferenceEngine::details::convertPrecision(preStr); } port.precision = type; + std::vector names; + if (getParameters(parentNode, "names", names)) { + for (const auto& name : names) { + port.names.emplace(name); + } + } return port; }; GenericLayerParams params; @@ -823,6 +829,10 @@ std::shared_ptr V10Parser::XmlDeserializer::createNode( } ngraphNode->set_friendly_name(params.name); + for (size_t i = 0; i < params.outputPorts.size() && i < ngraphNode->get_output_size(); ++i) { + if (!params.outputPorts[i].names.empty()) + ngraphNode->get_output_tensor(i).set_names(params.outputPorts[i].names); + } return ngraphNode; } diff --git a/inference-engine/src/readers/ir_reader/ie_ir_parser.hpp b/inference-engine/src/readers/ir_reader/ie_ir_parser.hpp index a97872faabeee0..43d1c4000038a4 100644 --- a/inference-engine/src/readers/ir_reader/ie_ir_parser.hpp +++ b/inference-engine/src/readers/ir_reader/ie_ir_parser.hpp @@ -69,6 +69,7 @@ class V10Parser : public IParser { // Precision and dimensions are needed only for GenericIE op ngraph::element::Type_t precision; SizeVector dims; + std::unordered_set names; }; size_t layerId; std::string version; @@ -355,4 +356,4 @@ class V10Parser : public IParser { #endif // IR_READER_V10 -} // namespace InferenceEngine \ No newline at end of file +} // namespace InferenceEngine diff --git a/inference-engine/src/transformations/src/transformations/control_flow/unroll_tensor_iterator.cpp b/inference-engine/src/transformations/src/transformations/control_flow/unroll_tensor_iterator.cpp index f7a54157e5f047..c2db73ea2ceef6 100644 --- a/inference-engine/src/transformations/src/transformations/control_flow/unroll_tensor_iterator.cpp +++ b/inference-engine/src/transformations/src/transformations/control_flow/unroll_tensor_iterator.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020 Intel Corporation +// Copyright (C) 2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // @@ -129,8 +129,10 @@ bool ngraph::pass::UnrollTensorIterator::run_on_function(std::shared_ptroutput(0).get_tensor().set_name( op::util::create_ie_output_name(ti->output(concat_desc->m_output_index))); + NGRAPH_SUPPRESS_DEPRECATED_END // connect the Concat layer to the corresponding TI outputs for (auto &input : ti->output(concat_desc->m_output_index).get_target_inputs()) { input.replace_source_output(concat); @@ -140,7 +142,9 @@ bool ngraph::pass::UnrollTensorIterator::run_on_function(std::shared_ptr result = body_functions[0]->get_results().at(concat_desc->m_body_value_index); const auto& input_to_res = result->get_input_source_output(0); // set output name to Tensor to store it for ngraph to cnn conversion + NGRAPH_SUPPRESS_DEPRECATED_START input_to_res.get_tensor().set_name(op::util::create_ie_output_name(ti->output(concat_desc->m_output_index))); + NGRAPH_SUPPRESS_DEPRECATED_END for (auto &input : ti->output(concat_desc->m_output_index).get_target_inputs()) { input.replace_source_output(input_to_res); } @@ -153,7 +157,9 @@ bool ngraph::pass::UnrollTensorIterator::run_on_function(std::shared_ptrinput_value(0); // set output name to Tensor to store it for ngraph to cnn conversion + NGRAPH_SUPPRESS_DEPRECATED_START in_value.get_tensor().set_name(op::util::create_ie_output_name(ti->output(output_desc->m_output_index))); + NGRAPH_SUPPRESS_DEPRECATED_END for (const auto &input : ti->output(output_desc->m_output_index).get_target_inputs()) { input.replace_source_output(result->get_input_source_output(0)); } diff --git a/inference-engine/src/transformations/src/transformations/op_conversions/convert_ti_to_sequences.cpp b/inference-engine/src/transformations/src/transformations/op_conversions/convert_ti_to_sequences.cpp index c30123803b8197..4a1d8b8b28e4bb 100644 --- a/inference-engine/src/transformations/src/transformations/op_conversions/convert_ti_to_sequences.cpp +++ b/inference-engine/src/transformations/src/transformations/op_conversions/convert_ti_to_sequences.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020 Intel Corporation +// Copyright (C) 2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // @@ -180,7 +180,9 @@ ngraph::pass::ConvertTensorIteratorToLSTMSequence::ConvertTensorIteratorToLSTMSe for (const auto &input : ti->output(ordered_out_descs[i]->m_output_index).get_target_inputs()) { input.replace_source_output(outputs[i]->output(0)); } + NGRAPH_SUPPRESS_DEPRECATED_START outputs[i]->get_output_tensor(0).set_name(op::util::create_ie_output_name(ti->output(ordered_out_descs[i]->m_output_index))); + NGRAPH_SUPPRESS_DEPRECATED_END } } @@ -334,7 +336,9 @@ ngraph::pass::ConvertTensorIteratorToRNNSequence::ConvertTensorIteratorToRNNSequ for (const auto &input : ti->output(ordered_out_descs[i]->m_output_index).get_target_inputs()) { input.replace_source_output(outputs[i]->output(0)); } + NGRAPH_SUPPRESS_DEPRECATED_START outputs[i]->get_output_tensor(0).set_name(op::util::create_ie_output_name(ti->output(ordered_out_descs[i]->m_output_index))); + NGRAPH_SUPPRESS_DEPRECATED_END } } @@ -489,7 +493,9 @@ ngraph::pass::ConvertTensorIteratorToGRUSequence::ConvertTensorIteratorToGRUSequ for (const auto &input : ti->output(ordered_out_descs[i]->m_output_index).get_target_inputs()) { input.replace_source_output(outputs[i]->output(0)); } + NGRAPH_SUPPRESS_DEPRECATED_START outputs[i]->get_output_tensor(0).set_name(op::util::create_ie_output_name(ti->output(ordered_out_descs[i]->m_output_index))); + NGRAPH_SUPPRESS_DEPRECATED_END } } diff --git a/inference-engine/src/transformations/src/transformations/serialize.cpp b/inference-engine/src/transformations/src/transformations/serialize.cpp index 06a994469fc7bb..9662722340db58 100644 --- a/inference-engine/src/transformations/src/transformations/serialize.cpp +++ b/inference-engine/src/transformations/src/transformations/serialize.cpp @@ -662,6 +662,15 @@ void ngfunction_2_irv10(pugi::xml_node& netXml, port.append_attribute("id").set_value(port_id++); port.append_attribute("precision") .set_value(get_output_precision_name(o).c_str()); + std::string names; + for (const auto& name : o.get_tensor().get_names()) { + if (!names.empty()) + names += ", "; + names += name; + } + if (!names.empty()) { + port.append_attribute("names").set_value(names.c_str()); + } for (auto d : o.get_shape()) { pugi::xml_node dim = port.append_child("dim"); dim.append_child(pugi::xml_node_type::node_pcdata) diff --git a/inference-engine/tests/functional/inference_engine/ir_serialization/tensor_names.cpp b/inference-engine/tests/functional/inference_engine/ir_serialization/tensor_names.cpp new file mode 100644 index 00000000000000..9cf118e2f98f80 --- /dev/null +++ b/inference-engine/tests/functional/inference_engine/ir_serialization/tensor_names.cpp @@ -0,0 +1,58 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include +#include +#include +#include "common_test_utils/ngraph_test_utils.hpp" +#include "ie_core.hpp" +#include "ngraph/ngraph.hpp" +#include "transformations/serialize.hpp" +#include + +class TensorNameSerializationTest : public CommonTestUtils::TestsCommon { +protected: + std::string test_name = GetTestName() + "_" + GetTimestamp(); + std::string m_out_xml_path = test_name + ".xml"; + std::string m_out_bin_path = test_name + ".bin"; + + void TearDown() override { + std::remove(m_out_xml_path.c_str()); + std::remove(m_out_bin_path.c_str()); + } +}; + +TEST_F(TensorNameSerializationTest, SerializeFunctionWithTensorNames) { + InferenceEngine::Core ie; + + std::shared_ptr function; + { + auto parameter = std::make_shared(ngraph::element::Type_t::f32, ngraph::Shape{1, 3, 10, 10}); + parameter->set_friendly_name("parameter"); + parameter->get_output_tensor(0).set_names({"input"}); + auto relu_prev = std::make_shared(parameter); + relu_prev->set_friendly_name("relu_prev"); + relu_prev->get_output_tensor(0).set_names({"relu_prev_t", "identity_prev_t"}); + auto relu = std::make_shared(relu_prev); + relu->set_friendly_name("relu"); + relu->get_output_tensor(0).set_names({"relu_t", "identity"}); + const ngraph::ResultVector results{std::make_shared(relu)}; + results[0]->set_friendly_name("out"); + ngraph::ParameterVector params{parameter}; + function = std::make_shared(results, params, "TensorNames"); + } + + InferenceEngine::CNNNetwork expected(function); + expected.serialize(m_out_xml_path, m_out_bin_path); + auto result = ie.ReadNetwork(m_out_xml_path, m_out_bin_path); + + bool success; + std::string message; + std::tie(success, message) = + compare_functions(result.getFunction(), expected.getFunction(), true, true, true, true); + + ASSERT_TRUE(success) << message; +} diff --git a/inference-engine/tests/functional/inference_engine/ngraph_reader/tensor_names.cpp b/inference-engine/tests/functional/inference_engine/ngraph_reader/tensor_names.cpp new file mode 100644 index 00000000000000..2e3af86518d07b --- /dev/null +++ b/inference-engine/tests/functional/inference_engine/ngraph_reader/tensor_names.cpp @@ -0,0 +1,89 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include "ngraph_reader_tests.hpp" + +TEST_F(NGraphReaderTests, ReadNetworkWithTensorNames) { + std::string model = R"V0G0N( + + + + + + + 1 + 3 + 22 + 22 + + + + + + + 1 + 3 + 22 + 22 + + + + + 1 + 3 + 22 + 22 + + + + + + + 1 + 3 + 22 + 22 + + + + + + + + + +)V0G0N"; + Core ie; + Blob::Ptr weights; + + auto network = ie.ReadNetwork(model, weights); + auto function = network.getFunction(); + auto inputs = network.getInputsInfo(); + auto outputs = network.getOutputsInfo(); + std::unordered_set inNames; + for (const auto& in : inputs) + inNames.emplace(in.first); + std::unordered_set outNames; + for (const auto& out : outputs) + outNames.emplace(out.first); + + ASSERT_EQ(1, inputs.size()); + ASSERT_EQ(1, outputs.size()); + ASSERT_EQ(1, function->get_results().size()); + + for (const auto& param : function->get_parameters()) { + ASSERT_TRUE(inNames.count(network.getOVNameForOperation(param->get_friendly_name()))); + ASSERT_TRUE(!param->get_output_tensor(0).get_names().empty()); + for (const auto& name : param->get_output_tensor(0).get_names()) + ASSERT_TRUE(inNames.count(network.getOVNameForTensor(name))); + } + + for (const auto& result : function->get_results()) { + ASSERT_TRUE(outNames.count(network.getOVNameForOperation(result->get_friendly_name()))); + ASSERT_TRUE(!result->get_input_tensor(0).get_names().empty()); + for (const auto& name : result->get_input_tensor(0).get_names()) + ASSERT_TRUE(outNames.count(network.getOVNameForTensor(name))); + } +} diff --git a/inference-engine/tests/functional/plugin/cpu/shared_tests_instances/subgraph_tests/tensor_names.cpp b/inference-engine/tests/functional/plugin/cpu/shared_tests_instances/subgraph_tests/tensor_names.cpp new file mode 100644 index 00000000000000..99ceae1156ac85 --- /dev/null +++ b/inference-engine/tests/functional/plugin/cpu/shared_tests_instances/subgraph_tests/tensor_names.cpp @@ -0,0 +1,16 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include "subgraph_tests/tensor_names.hpp" +#include "common_test_utils/test_constants.hpp" + +using namespace SubgraphTestsDefinitions; + +namespace { + INSTANTIATE_TEST_CASE_P(smoke_Check, TensorNamesTest, + ::testing::Values(CommonTestUtils::DEVICE_CPU), + TensorNamesTest::getTestCaseName); +} // namespace diff --git a/inference-engine/tests/functional/plugin/gna/shared_tests_instances/skip_tests_config.cpp b/inference-engine/tests/functional/plugin/gna/shared_tests_instances/skip_tests_config.cpp index 99ea78b165f7ad..ec2853f0eb298e 100644 --- a/inference-engine/tests/functional/plugin/gna/shared_tests_instances/skip_tests_config.cpp +++ b/inference-engine/tests/functional/plugin/gna/shared_tests_instances/skip_tests_config.cpp @@ -9,6 +9,7 @@ std::vector disabledTestPatterns() { return { + ".*TensorNamesTest\\.CheckAddOutput.*", // TODO: FIX BUG 31661 // TODO: support InferRequest in GNAPlugin ".*InferRequestTests\\.canRun3AsyncRequestsConsistentlyFromThreadsWithoutWait.*", diff --git a/inference-engine/tests/functional/plugin/gna/shared_tests_instances/subgraph_tests/tensor_names.cpp b/inference-engine/tests/functional/plugin/gna/shared_tests_instances/subgraph_tests/tensor_names.cpp new file mode 100644 index 00000000000000..0729a36e9a4dc6 --- /dev/null +++ b/inference-engine/tests/functional/plugin/gna/shared_tests_instances/subgraph_tests/tensor_names.cpp @@ -0,0 +1,17 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include "subgraph_tests/tensor_names.hpp" +#include "common_test_utils/test_constants.hpp" + +using namespace SubgraphTestsDefinitions; + +namespace { + INSTANTIATE_TEST_CASE_P(smoke_Check, TensorNamesTest, + ::testing::Values(CommonTestUtils::DEVICE_GNA), + TensorNamesTest::getTestCaseName); +} // namespace + diff --git a/inference-engine/tests/functional/plugin/gpu/shared_tests_instances/subgraph_tests/tensor_names.cpp b/inference-engine/tests/functional/plugin/gpu/shared_tests_instances/subgraph_tests/tensor_names.cpp new file mode 100644 index 00000000000000..b5258c33fd5e89 --- /dev/null +++ b/inference-engine/tests/functional/plugin/gpu/shared_tests_instances/subgraph_tests/tensor_names.cpp @@ -0,0 +1,18 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include "subgraph_tests/tensor_names.hpp" +#include "common_test_utils/test_constants.hpp" + +using namespace SubgraphTestsDefinitions; + +namespace { + INSTANTIATE_TEST_CASE_P(smoke_Check, TensorNamesTest, + ::testing::Values(CommonTestUtils::DEVICE_GPU), + TensorNamesTest::getTestCaseName); +} // namespace + + diff --git a/inference-engine/tests/functional/plugin/myriad/shared_tests_instances/subgraph_tests/tensor_names.cpp b/inference-engine/tests/functional/plugin/myriad/shared_tests_instances/subgraph_tests/tensor_names.cpp new file mode 100644 index 00000000000000..93e978ab427b07 --- /dev/null +++ b/inference-engine/tests/functional/plugin/myriad/shared_tests_instances/subgraph_tests/tensor_names.cpp @@ -0,0 +1,19 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include "subgraph_tests/tensor_names.hpp" +#include "common_test_utils/test_constants.hpp" + +using namespace SubgraphTestsDefinitions; + +namespace { + INSTANTIATE_TEST_CASE_P(smoke_Check, TensorNamesTest, + ::testing::Values(CommonTestUtils::DEVICE_MYRIAD), + TensorNamesTest::getTestCaseName); +} // namespace + + + diff --git a/inference-engine/tests/functional/plugin/shared/include/subgraph_tests/tensor_names.hpp b/inference-engine/tests/functional/plugin/shared/include/subgraph_tests/tensor_names.hpp new file mode 100644 index 00000000000000..69b109d670cd1e --- /dev/null +++ b/inference-engine/tests/functional/plugin/shared/include/subgraph_tests/tensor_names.hpp @@ -0,0 +1,166 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "shared_test_classes/subgraph/tensor_names.hpp" +#include + +namespace SubgraphTestsDefinitions { + +TEST_P(TensorNamesTest, CheckTensorNames) { + cnnNetwork = InferenceEngine::CNNNetwork{function}; + ConfigureNetwork(); + + auto inputs = cnnNetwork.getInputsInfo(); + auto outputs = cnnNetwork.getOutputsInfo(); + std::unordered_set inNames; + for (const auto& in : inputs) + inNames.emplace(in.first); + std::unordered_set outNames; + for (const auto& out : outputs) + outNames.emplace(out.first); + + for (const auto& param : function->get_parameters()) { + ASSERT_TRUE(inNames.count(cnnNetwork.getOVNameForOperation(param->get_friendly_name()))); + for (const auto& name : param->get_output_tensor(0).get_names()) + ASSERT_TRUE(inNames.count(cnnNetwork.getOVNameForTensor(name))); + } + + for (const auto& result : function->get_results()) { + ASSERT_TRUE(outNames.count(cnnNetwork.getOVNameForOperation(result->get_friendly_name()))); + for (const auto& name : result->input_value(0).get_tensor().get_names()) + ASSERT_TRUE(outNames.count(cnnNetwork.getOVNameForTensor(name))); + } + + executableNetwork = core->LoadNetwork(cnnNetwork, targetDevice, configuration); + inferRequest = executableNetwork.CreateInferRequest(); + + for (const auto& param : function->get_parameters()) { + ASSERT_NO_THROW(inferRequest.GetBlob(cnnNetwork.getOVNameForOperation(param->get_friendly_name()))); + for (const auto& name : param->get_output_tensor(0).get_names()) + ASSERT_NO_THROW(inferRequest.GetBlob(cnnNetwork.getOVNameForTensor(name))); + } + + for (const auto& result : function->get_results()) { + ASSERT_NO_THROW(inferRequest.GetBlob(cnnNetwork.getOVNameForOperation(result->get_friendly_name()))); + for (const auto& name : result->get_input_tensor(0).get_names()) { + ASSERT_NO_THROW(inferRequest.GetBlob(cnnNetwork.getOVNameForTensor(name))); + } + } +} + +TEST_P(TensorNamesTest, CheckTensorNamesAfterClone) { + cnnNetwork = InferenceEngine::CNNNetwork{function}; + InferenceEngine::CNNNetwork clonedNet(static_cast(cnnNetwork)); + ConfigureNetwork(); + + auto inputs = clonedNet.getInputsInfo(); + auto outputs = clonedNet.getOutputsInfo(); + std::unordered_set inNames; + for (const auto& in : inputs) + inNames.emplace(in.first); + std::unordered_set outNames; + for (const auto& out : outputs) + outNames.emplace(out.first); + + for (const auto& param : function->get_parameters()) { + ASSERT_TRUE(inNames.count(clonedNet.getOVNameForOperation(param->get_friendly_name()))); + for (const auto& name : param->get_output_tensor(0).get_names()) + ASSERT_TRUE(inNames.count(clonedNet.getOVNameForTensor(name))); + } + + for (const auto& result : function->get_results()) { + ASSERT_TRUE(outNames.count(clonedNet.getOVNameForOperation(result->get_friendly_name()))); + + for (const auto& name : result->get_input_tensor(0).get_names()) { + ASSERT_TRUE(outNames.count(clonedNet.getOVNameForTensor(name))); + } + } + + executableNetwork = core->LoadNetwork(clonedNet, targetDevice, configuration); + inferRequest = executableNetwork.CreateInferRequest(); + + for (const auto& param : function->get_parameters()) { + ASSERT_NO_THROW(inferRequest.GetBlob(clonedNet.getOVNameForOperation(param->get_friendly_name()))); + for (const auto& name : param->get_output_tensor(0).get_names()) + ASSERT_NO_THROW(inferRequest.GetBlob(clonedNet.getOVNameForTensor(name))); + } + + for (const auto& result : function->get_results()) { + ASSERT_NO_THROW(inferRequest.GetBlob(clonedNet.getOVNameForOperation(result->get_friendly_name()))); + for (const auto& name : result->input_value(0).get_tensor().get_names()) + ASSERT_NO_THROW(inferRequest.GetBlob(clonedNet.getOVNameForTensor(name))); + } +} + +TEST_P(TensorNamesTest, CheckAddOutput) { + SKIP_IF_CURRENT_TEST_IS_DISABLED(); + cnnNetwork = InferenceEngine::CNNNetwork{function}; + ConfigureNetwork(); + + auto inputs = cnnNetwork.getInputsInfo(); + auto outputs = cnnNetwork.getOutputsInfo(); + std::unordered_set inNames; + for (const auto& in : inputs) + inNames.emplace(in.first); + std::unordered_set outNames; + for (const auto& out : outputs) + outNames.emplace(out.first); + + ASSERT_EQ(1, inputs.size()); + ASSERT_EQ(1, outputs.size()); + ASSERT_EQ(1, function->get_results().size()); + + // Check that relu_prev doesn't exist in output and input maps + ASSERT_THROW(cnnNetwork.getOVNameForOperation("relu_prev"), InferenceEngine::NotFound); + for (const std::string& tensor_name : {"relu_prev_t", "identity_prev_t"}) { + ASSERT_THROW(cnnNetwork.getOVNameForOperation(tensor_name), InferenceEngine::NotFound); + } + + // Add relu_prev as output + cnnNetwork.addOutput("relu_prev"); + + inputs = cnnNetwork.getInputsInfo(); + outputs = cnnNetwork.getOutputsInfo(); + inNames.clear(); + for (const auto& in : inputs) + inNames.emplace(in.first); + outNames.clear(); + for (const auto& out : outputs) + outNames.emplace(out.first); + + ASSERT_EQ(1, inputs.size()); + ASSERT_EQ(2, outputs.size()); + ASSERT_EQ(2, function->get_results().size()); + + // Check that relu_prev exists in output map + ASSERT_FALSE(inNames.count(cnnNetwork.getOVNameForOperation("relu_prev"))); + for (const std::string& tensor_name : {"relu_prev_t", "identity_prev_t"}) { + ASSERT_FALSE(inNames.count(cnnNetwork.getOVNameForTensor(tensor_name))); + } + ASSERT_TRUE(outNames.count(cnnNetwork.getOVNameForOperation("relu_prev"))); + for (const std::string& tensor_name : {"relu_prev_t", "identity_prev_t"}) { + ASSERT_TRUE(outNames.count(cnnNetwork.getOVNameForTensor(tensor_name))); + } + + executableNetwork = core->LoadNetwork(cnnNetwork, targetDevice, configuration); + inferRequest = executableNetwork.CreateInferRequest(); + + for (const auto& param : cnnNetwork.getFunction()->get_parameters()) { + ASSERT_NO_THROW(inferRequest.GetBlob(cnnNetwork.getOVNameForOperation(param->get_friendly_name()))); + for (const auto& name : param->get_output_tensor(0).get_names()) + ASSERT_NO_THROW(inferRequest.GetBlob(cnnNetwork.getOVNameForTensor(name))); + } + + for (const auto& result : cnnNetwork.getFunction()->get_results()) { + ASSERT_NO_THROW(inferRequest.GetBlob(cnnNetwork.getOVNameForOperation(result->get_friendly_name()))); + for (const auto& name : result->get_input_tensor(0).get_names()) { + ASSERT_NO_THROW(inferRequest.GetBlob(cnnNetwork.getOVNameForTensor(name))); + } + } +} + +} // namespace SubgraphTestsDefinitions + diff --git a/inference-engine/tests/functional/shared_test_classes/include/shared_test_classes/subgraph/tensor_names.hpp b/inference-engine/tests/functional/shared_test_classes/include/shared_test_classes/subgraph/tensor_names.hpp new file mode 100644 index 00000000000000..dfa2cbeaa259d7 --- /dev/null +++ b/inference-engine/tests/functional/shared_test_classes/include/shared_test_classes/subgraph/tensor_names.hpp @@ -0,0 +1,28 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include +#include + +#include "shared_test_classes/base/layer_test_utils.hpp" +#include "ngraph_functions/builders.hpp" + +namespace SubgraphTestsDefinitions { + +typedef std::tuple< + std::string // Device name +> constResultParams; + +class TensorNamesTest : public testing::WithParamInterface, + virtual public LayerTestsUtils::LayerTestsCommon { +public: + static std::string getTestCaseName(testing::TestParamInfo obj); +protected: + void SetUp() override; +}; +} // namespace SubgraphTestsDefinitions diff --git a/inference-engine/tests/functional/shared_test_classes/src/subgraph/tensor_names.cpp b/inference-engine/tests/functional/shared_test_classes/src/subgraph/tensor_names.cpp new file mode 100644 index 00000000000000..f31eec544a0daf --- /dev/null +++ b/inference-engine/tests/functional/shared_test_classes/src/subgraph/tensor_names.cpp @@ -0,0 +1,35 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "shared_test_classes/subgraph/tensor_names.hpp" + +namespace SubgraphTestsDefinitions { + +std::string TensorNamesTest::getTestCaseName(testing::TestParamInfo obj) { + std::string targetDevice; + std::tie(targetDevice) = obj.param; + std::ostringstream result; + result << "TargetDevice=" << targetDevice; + return result.str(); +} + +void TensorNamesTest::SetUp() { + std::tie(targetDevice) = this->GetParam(); + + auto parameter = std::make_shared(ngraph::element::Type_t::f32, ngraph::Shape{1, 3, 10, 10}); + parameter->set_friendly_name("parameter"); + parameter->get_output_tensor(0).set_names({"input"}); + auto relu_prev = std::make_shared(parameter); + relu_prev->set_friendly_name("relu_prev"); + relu_prev->get_output_tensor(0).set_names({"relu_prev_t", "identity_prev_t"}); + auto relu = std::make_shared(relu_prev); + relu->set_friendly_name("relu"); + relu->get_output_tensor(0).set_names({"relu_t", "identity"}); + const ngraph::ResultVector results{std::make_shared(relu)}; + results[0]->set_friendly_name("out"); + ngraph::ParameterVector params{parameter}; + function = std::make_shared(results, params, "TensorNames"); +} + +} // namespace SubgraphTestsDefinitions diff --git a/inference-engine/tests/ie_test_utils/common_test_utils/ngraph_test_utils.cpp b/inference-engine/tests/ie_test_utils/common_test_utils/ngraph_test_utils.cpp index fa05b365e7add5..b3f8957f1b636c 100644 --- a/inference-engine/tests/ie_test_utils/common_test_utils/ngraph_test_utils.cpp +++ b/inference-engine/tests/ie_test_utils/common_test_utils/ngraph_test_utils.cpp @@ -248,6 +248,13 @@ std::pair compare_functions( } for (int i = 0; i < node1->outputs().size(); ++i) { + const auto& tensor1 = node1->output(i).get_tensor(); + const auto& tensor2 = node2->output(i).get_tensor(); + + if (tensor1.get_names() != tensor2.get_names()) { + err_log << "Output tensors names are different for nodes: " + << node1->get_friendly_name() << " and " << node2->get_friendly_name() << std::endl; + } if (!node1->output(i).get_partial_shape().same_scheme( node2->output(i).get_partial_shape())) { err_log << "Different shape detected\n" diff --git a/ngraph/core/include/ngraph/descriptor/output.hpp b/ngraph/core/include/ngraph/descriptor/output.hpp index c7c2fc875a6ba3..611961c5e3e3bc 100644 --- a/ngraph/core/include/ngraph/descriptor/output.hpp +++ b/ngraph/core/include/ngraph/descriptor/output.hpp @@ -17,6 +17,8 @@ #pragma once #include +#include +#include #include #include "ngraph/descriptor/input.hpp" diff --git a/ngraph/core/include/ngraph/descriptor/tensor.hpp b/ngraph/core/include/ngraph/descriptor/tensor.hpp index 123b2ec507b66f..fcb527a4381edf 100644 --- a/ngraph/core/include/ngraph/descriptor/tensor.hpp +++ b/ngraph/core/include/ngraph/descriptor/tensor.hpp @@ -18,6 +18,7 @@ #include #include +#include #include "ngraph/partial_shape.hpp" #include "ngraph/shape.hpp" @@ -44,8 +45,13 @@ namespace ngraph Node* node, size_t node_output_number); + NGRAPH_DEPRECATED("get_name() is deprecated! Please use get_names() instead.") const std::string& get_name() const; + NGRAPH_DEPRECATED("set_name() is deprecated! Please use set_names() instead.") void set_name(const std::string& name); + + const std::unordered_set& get_names() const; + void set_names(const std::unordered_set& names); void set_tensor_type(const element::Type& element_type, const PartialShape& pshape); void set_element_type(const element::Type& elemenet_type); void set_partial_shape(const PartialShape& partial_shape); @@ -68,6 +74,7 @@ namespace ngraph size_t m_node_output_number{0}; std::string m_name; + std::unordered_set m_names; }; NGRAPH_API diff --git a/ngraph/core/include/ngraph/node.hpp b/ngraph/core/include/ngraph/node.hpp index 628a9c26866bea..f195dfbdbacaa7 100644 --- a/ngraph/core/include/ngraph/node.hpp +++ b/ngraph/core/include/ngraph/node.hpp @@ -327,6 +327,8 @@ namespace ngraph descriptor::Tensor& get_input_tensor(size_t i) const; /// Returns the tensor name for output i + NGRAPH_DEPRECATED( + "The tensor name was deprecated. Use get_output_tensor(i).get_names() instead.") const std::string& get_output_tensor_name(size_t i) const; std::set> get_output_target_inputs(size_t i) const; @@ -347,6 +349,8 @@ namespace ngraph const PartialShape& get_input_partial_shape(size_t i) const; /// Returns the tensor name for input i + NGRAPH_DEPRECATED( + "The tensor name was deprecated. Use get_input_tensor(i).get_names() instead.") const std::string& get_input_tensor_name(size_t i) const; std::unordered_set liveness_new_list; diff --git a/ngraph/core/include/ngraph/node_output.hpp b/ngraph/core/include/ngraph/node_output.hpp index 359a1441cfc4b2..bcaed7812d3b2e 100644 --- a/ngraph/core/include/ngraph/node_output.hpp +++ b/ngraph/core/include/ngraph/node_output.hpp @@ -17,6 +17,7 @@ #pragma once #include +#include #include "ngraph/descriptor/tensor.hpp" #include "ngraph/partial_shape.hpp" diff --git a/ngraph/core/include/ngraph/runtime/tensor.hpp b/ngraph/core/include/ngraph/runtime/tensor.hpp index 8985957faab24d..9e83c3a3f61072 100644 --- a/ngraph/core/include/ngraph/runtime/tensor.hpp +++ b/ngraph/core/include/ngraph/runtime/tensor.hpp @@ -63,6 +63,7 @@ namespace ngraph /// \brief Get tensor's unique name /// \return tensor's name + NGRAPH_DEPRECATED("Only output ports have names") const std::string& get_name() const; /// \brief Get the stale value of the tensor. A tensor is stale if its data is diff --git a/ngraph/core/src/descriptor/tensor.cpp b/ngraph/core/src/descriptor/tensor.cpp index 9669e9e3b8d6f9..dc9cc59b4b2f57 100644 --- a/ngraph/core/src/descriptor/tensor.cpp +++ b/ngraph/core/src/descriptor/tensor.cpp @@ -42,11 +42,6 @@ descriptor::Tensor::Tensor(const element::Type& element_type, { } -void descriptor::Tensor::set_name(const string& name) -{ - m_name = name; -} - void descriptor::Tensor::set_tensor_type(const element::Type& element_type, const PartialShape& pshape) { @@ -90,13 +85,41 @@ size_t descriptor::Tensor::size() const return shape_size(get_shape()) * m_element_type.size(); } +NGRAPH_SUPPRESS_DEPRECATED_START +void descriptor::Tensor::set_name(const string& name) +{ + m_name = name; +} + const std::string& descriptor::Tensor::get_name() const { return m_name; } +NGRAPH_SUPPRESS_DEPRECATED_END + +const std::unordered_set& descriptor::Tensor::get_names() const +{ + return m_names; +} + +void descriptor::Tensor::set_names(const std::unordered_set& names) +{ + m_names = names; +} ostream& operator<<(ostream& out, const descriptor::Tensor& tensor) { - out << "Tensor(" << tensor.get_name() << ")"; + std::string names; + for (const auto& name : tensor.get_names()) + { + if (!names.empty()) + names += ", "; + names += name; + } + NGRAPH_SUPPRESS_DEPRECATED_START + if (names.empty()) + names = tensor.get_name(); + NGRAPH_SUPPRESS_DEPRECATED_END + out << "Tensor(" << names << ")"; return out; } diff --git a/ngraph/core/src/graph_util.cpp b/ngraph/core/src/graph_util.cpp index bee2c68c0e3b48..fc011de40a54ac 100644 --- a/ngraph/core/src/graph_util.cpp +++ b/ngraph/core/src/graph_util.cpp @@ -924,7 +924,9 @@ bool ngraph::replace_output_update_name(Output output, const Output& { replacement.get_node()->set_friendly_name(output.get_node()->get_friendly_name()); // Update output tensor name + NGRAPH_SUPPRESS_DEPRECATED_START replacement.get_tensor().set_name(output.get_node()->get_friendly_name()); + NGRAPH_SUPPRESS_DEPRECATED_END } output.replace(replacement); copy_runtime_info({replacement.get_node_shared_ptr(), output.get_node_shared_ptr()}, diff --git a/ngraph/core/src/node.cpp b/ngraph/core/src/node.cpp index b74ee51111e254..6aad6d0d501a7a 100644 --- a/ngraph/core/src/node.cpp +++ b/ngraph/core/src/node.cpp @@ -143,6 +143,10 @@ std::shared_ptr { clone->add_control_dependency(cdep); } + for (size_t i = 0; i < get_output_size(); i++) + { + clone->get_output_tensor(i).set_names(get_output_tensor(i).get_names()); + } return clone; } @@ -658,13 +662,6 @@ descriptor::Tensor& Node::get_input_tensor(size_t i) const return input.get_tensor(); } -const string& Node::get_output_tensor_name(size_t i) const -{ - NGRAPH_CHECK( - i < m_outputs.size(), "index '", i, "' out of range in get_output_tensor_name(size_t i)"); - return m_outputs[i].get_tensor().get_name(); -} - size_t Node::get_input_size() const { return m_inputs.size(); @@ -690,6 +687,7 @@ const PartialShape& Node::get_input_partial_shape(size_t i) const return m_inputs[i].get_partial_shape(); } +NGRAPH_SUPPRESS_DEPRECATED_START const string& Node::get_input_tensor_name(size_t i) const { NGRAPH_CHECK( @@ -697,6 +695,14 @@ const string& Node::get_input_tensor_name(size_t i) const return m_inputs[i].get_tensor().get_name(); } +const string& Node::get_output_tensor_name(size_t i) const +{ + NGRAPH_CHECK( + i < m_outputs.size(), "index '", i, "' out of range in get_output_tensor_name(size_t i)"); + return m_outputs[i].get_tensor().get_name(); +} +NGRAPH_SUPPRESS_DEPRECATED_END + bool Node::has_same_type(std::shared_ptr node) const { if (get_output_size() != node->get_output_size()) diff --git a/ngraph/core/src/runtime/host_tensor.cpp b/ngraph/core/src/runtime/host_tensor.cpp index 2c5de03136e8ed..da996869442eb9 100644 --- a/ngraph/core/src/runtime/host_tensor.cpp +++ b/ngraph/core/src/runtime/host_tensor.cpp @@ -65,10 +65,12 @@ runtime::HostTensor::HostTensor(const std::string& name) { } +NGRAPH_SUPPRESS_DEPRECATED_START runtime::HostTensor::HostTensor(const Output& value) : HostTensor(value.get_element_type(), value.get_partial_shape(), value.get_tensor().get_name()) { } +NGRAPH_SUPPRESS_DEPRECATED_END void runtime::HostTensor::allocate_buffer() { @@ -101,11 +103,13 @@ void runtime::HostTensor::allocate_buffer() } } +NGRAPH_SUPPRESS_DEPRECATED_START runtime::HostTensor::HostTensor(const std::shared_ptr& constant) : HostTensor(constant->output(0).get_tensor().get_name()) { initialize(constant); } +NGRAPH_SUPPRESS_DEPRECATED_END void runtime::HostTensor::initialize(const std::shared_ptr& constant) { diff --git a/ngraph/core/src/runtime/tensor.cpp b/ngraph/core/src/runtime/tensor.cpp index e5da131c7f3781..21e9c328a24d16 100644 --- a/ngraph/core/src/runtime/tensor.cpp +++ b/ngraph/core/src/runtime/tensor.cpp @@ -49,7 +49,9 @@ size_t runtime::Tensor::get_size_in_bytes() const const std::string& runtime::Tensor::get_name() const { + NGRAPH_SUPPRESS_DEPRECATED_START return m_descriptor->get_name(); + NGRAPH_SUPPRESS_DEPRECATED_END } bool runtime::Tensor::get_stale() const diff --git a/ngraph/test/tensor.cpp b/ngraph/test/tensor.cpp index 216831dc0de8ce..be9cc26ab1f180 100644 --- a/ngraph/test/tensor.cpp +++ b/ngraph/test/tensor.cpp @@ -23,6 +23,7 @@ #include "gtest/gtest.h" #include "ngraph/function.hpp" #include "ngraph/ngraph.hpp" +#include "ngraph/opsets/opset6.hpp" #include "ngraph/pass/manager.hpp" #include "pass/liveness.hpp" #include "util/test_tools.hpp" @@ -91,3 +92,23 @@ TEST(tensor, output_flag) EXPECT_TRUE(op::is_output(f0->get_output_op(i))); } } + +TEST(tensor, tensor_names) +{ + auto arg0 = make_shared(element::f32, Shape{1}); + arg0->set_friendly_name("data"); + arg0->get_output_tensor(0).set_names({"input"}); + + auto relu = make_shared(arg0); + relu->set_friendly_name("relu"); + relu->get_output_tensor(0).set_names({"relu_t", "identity"}); + auto f0 = make_shared(relu, ParameterVector{arg0}); + + ASSERT_EQ(arg0->get_output_tensor(0).get_names(), relu->get_input_tensor(0).get_names()); + ASSERT_EQ(arg0->get_output_tensor(0).get_names(), + relu->input_value(0).get_tensor().get_names()); + ASSERT_EQ(f0->get_result()->get_input_tensor(0).get_names(), + relu->get_output_tensor(0).get_names()); + ASSERT_EQ(f0->get_result()->input_value(0).get_tensor().get_names(), + relu->get_output_tensor(0).get_names()); +} From a67a72084f5ea6c5941c990d10b3487328003d2c Mon Sep 17 00:00:00 2001 From: Vladislav Volkov Date: Fri, 29 Jan 2021 06:53:42 +0300 Subject: [PATCH 05/99] -no-unused-XXX options added for selective build mode (#3702) --- openvino/conditional_compilation/CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/openvino/conditional_compilation/CMakeLists.txt b/openvino/conditional_compilation/CMakeLists.txt index 6fd8b9539ffe0e..0d625ac1eb2508 100644 --- a/openvino/conditional_compilation/CMakeLists.txt +++ b/openvino/conditional_compilation/CMakeLists.txt @@ -51,6 +51,15 @@ elseif(SELECTIVE_BUILD STREQUAL "ON") target_compile_definitions(${TARGET_NAME} INTERFACE SELECTIVE_BUILD) + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" + OR CMAKE_CXX_COMPILER_ID MATCHES "^(Apple)?Clang$") + # After disabling a block of code, some variables might be unused. + target_compile_options(${TARGET_NAME} INTERFACE + -Wno-unused-function + -Wno-unused-parameter + -Wunused-local-typedefs) + endif() + set(GENERATED_HEADER ${CMAKE_CURRENT_BINARY_DIR}/conditional_compilation_gen.h) set(GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/scripts/ccheader.py) From a8b921791e4434cb2d092312c2d68a65004db85e Mon Sep 17 00:00:00 2001 From: Gorokhov Dmitriy Date: Fri, 29 Jan 2021 08:38:58 +0300 Subject: [PATCH 06/99] [CPU] Disabled input zero point fusing into fp32 Convolution (#4056) --- .../src/mkldnn_plugin/mkldnn_graph_optimizer.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/inference-engine/src/mkldnn_plugin/mkldnn_graph_optimizer.cpp b/inference-engine/src/mkldnn_plugin/mkldnn_graph_optimizer.cpp index bc0ade25d957da..46d32b9981d8fe 100644 --- a/inference-engine/src/mkldnn_plugin/mkldnn_graph_optimizer.cpp +++ b/inference-engine/src/mkldnn_plugin/mkldnn_graph_optimizer.cpp @@ -216,7 +216,7 @@ void MKLDNNGraphOptimizer::FuseConvolutionAndZeroPoints(MKLDNNGraph &graph) { return true; }; - auto initializeInputZeroPoints = [](MKLDNNNodePtr node, MKLDNNNodePtr parent0) { + auto initializeInputZeroPoints = [](MKLDNNNodePtr node, MKLDNNNodePtr parent0, MKLDNNNodePtr parent1) { auto* convNode = dynamic_cast(node.get()); if (convNode == nullptr) THROW_IE_EXCEPTION << "Cannot get convolution node " << node->getName(); @@ -225,6 +225,14 @@ void MKLDNNGraphOptimizer::FuseConvolutionAndZeroPoints(MKLDNNGraph &graph) { int OC = node->getChildEdgesAtPort(0)[0]->getDims()[1]; if (parent0->getType() == Eltwise) { + // The plug-in doesn't support FP32 convolution with input/weights zero points. + // In case weights are in FP32 (or we have zero points on weights which are not supported by INT8 convolution) we cannot use + // INT8 implementation so we have to disable input zero points fusing as well. + auto weightsLayer = parent1->getCnnLayer(); + if (!weightsLayer || weightsLayer->type != "Const" || weightsLayer->outData[0]->getPrecision() != Precision::I8) { + return false; + } + auto* eltwiseNode = dynamic_cast(parent0.get()); if (eltwiseNode->getOpType() != Subtract) return false; @@ -395,7 +403,8 @@ void MKLDNNGraphOptimizer::FuseConvolutionAndZeroPoints(MKLDNNGraph &graph) { if (!isSutableConvNode(conv)) continue; auto dataEltwise = conv->getParentEdgesAtPort(0)[0]->getParent(); - if (initializeInputZeroPoints(conv, dataEltwise)) { + auto weightsEltwise = conv->getParentEdgesAtPort(1)[0]->getParent(); + if (initializeInputZeroPoints(conv, dataEltwise, weightsEltwise)) { auto p_edge = dataEltwise->getParentEdgesAtPort(1)[0]; removeEdge(graph, p_edge); From 6b54e738d72acf8569de7ed3ba20b34ddf40d7c4 Mon Sep 17 00:00:00 2001 From: Anton Chetverikov Date: Fri, 29 Jan 2021 10:08:06 +0300 Subject: [PATCH 07/99] Update operation attributes (#3814) * Allign attribute values in spec * Fix wrong attribute name in spec * Add `get_boolean_attr` function * Add get_type function * Update conv attrs * Update copyright year * Add missed attrs, update copyright year * Fix year in copyright * Update ir parser for RegionYolo layer * Remove wrong changes for BinaryConvolution * Remove get_type function as it no more needed * Update check for reduce ops * Fix error in reduce attrs * Update ir_engine to work with bool attrs * Update DetectionOutput operation * Update PSROIPooling * remove redundant attrs from spec * Update get_boolean_attr function * Update Reduce operations * Update DetectionOutput specification * Update specification for missed attrs * Apply comments * Fixconst renumbering logic * Fix typo * Change default value to fix broken shape inference * Add additional asserts * Add comment * model-optimizer/mo/utils/ir_reader/layer_to_class.py * Sort imports * Sort imports * Update year in copyright * Update const * Remove changes from const restoring * Rename function * remove unnecessary changes * model-optimizer/mo/front/extractor_test.py * Fix year in copyright * Add soft_get * Fix exclude-pad attribute name for AvgPool operation * Update exclude_pad attribute values * Remove useless comment * Update examples in specification * Remove file added by mistake * Resolve comments * Resolve comments * Add return value * Allign global_pool attribute --- .../ops/detection/DeformablePSROIPooling_1.md | 2 +- docs/ops/detection/DetectionOutput_1.md | 2 +- docs/ops/detection/PriorBoxClustered_1.md | 2 +- docs/ops/detection/PriorBox_1.md | 2 +- docs/ops/image/Interpolate_4.md | 2 +- docs/ops/pooling/AvgPool_1.md | 14 +++--- docs/ops/sequence/CTCGreedyDecoder_1.md | 5 ++- .../extensions/front/caffe/pooling_ext.py | 8 ++-- .../front/caffe/pooling_ext_test.py | 16 +++---- .../extensions/front/mxnet/pooling_ext.py | 4 +- .../front/mxnet/pooling_ext_test.py | 4 +- .../extensions/front/onnx/pooling_ext.py | 6 +-- .../extensions/front/tf/pooling_ext.py | 4 +- .../extensions/front/tf/pooling_ext_test.py | 4 +- .../extensions/ops/DetectionOutput.py | 43 ++++++------------- model-optimizer/extensions/ops/GRUCell.py | 9 ++-- model-optimizer/extensions/ops/MatMul.py | 7 +-- model-optimizer/extensions/ops/ReduceOps.py | 5 ++- .../extensions/ops/adaptive_avg_pooling.py | 4 +- model-optimizer/extensions/ops/bucketize.py | 7 +-- .../extensions/ops/ctc_greedy_decoder.py | 9 ++-- model-optimizer/extensions/ops/ctc_loss.py | 11 ++++- model-optimizer/extensions/ops/cumsum.py | 6 ++- model-optimizer/extensions/ops/interpolate.py | 11 +++-- model-optimizer/extensions/ops/mvn.py | 9 ++-- .../extensions/ops/non_max_suppression.py | 9 ++-- model-optimizer/extensions/ops/priorbox.py | 14 +++--- .../extensions/ops/priorbox_clustered.py | 9 ++-- model-optimizer/extensions/ops/proposal.py | 14 +++--- .../extensions/ops/psroipooling.py | 4 +- model-optimizer/extensions/ops/regionyolo.py | 6 +-- model-optimizer/mo/front/extractor.py | 16 ++++++- model-optimizer/mo/front/extractor_test.py | 19 +++++++- .../mo/ops/deformable_convolution.py | 4 +- model-optimizer/mo/ops/pooling.py | 6 +-- model-optimizer/mo/ops/pooling_test.py | 12 +++--- model-optimizer/mo/ops/reshape.py | 5 ++- model-optimizer/mo/ops/roipooling.py | 3 +- 38 files changed, 182 insertions(+), 135 deletions(-) diff --git a/docs/ops/detection/DeformablePSROIPooling_1.md b/docs/ops/detection/DeformablePSROIPooling_1.md index 2adcfb82e2e5c2..43abd2d65f6596 100644 --- a/docs/ops/detection/DeformablePSROIPooling_1.md +++ b/docs/ops/detection/DeformablePSROIPooling_1.md @@ -90,7 +90,7 @@ The box coordinates are specified as five element tuples: *[batch_id, x_1, y_1, ```xml - + 1 diff --git a/docs/ops/detection/DetectionOutput_1.md b/docs/ops/detection/DetectionOutput_1.md index 6175a9668982bc..363ef6ae4ea57c 100644 --- a/docs/ops/detection/DetectionOutput_1.md +++ b/docs/ops/detection/DetectionOutput_1.md @@ -156,7 +156,7 @@ At each feature map cell, *DetectionOutput* predicts the offsets relative to the ```xml - + 1 diff --git a/docs/ops/detection/PriorBoxClustered_1.md b/docs/ops/detection/PriorBoxClustered_1.md index 5d798442a92b6b..6762fe5fe97e68 100644 --- a/docs/ops/detection/PriorBoxClustered_1.md +++ b/docs/ops/detection/PriorBoxClustered_1.md @@ -110,7 +110,7 @@ If *clip* is defined, the coordinates of prior boxes are recalculated with the f ```xml - + 2 diff --git a/docs/ops/detection/PriorBox_1.md b/docs/ops/detection/PriorBox_1.md index 99842bc05caac1..4a4aa35186bda3 100644 --- a/docs/ops/detection/PriorBox_1.md +++ b/docs/ops/detection/PriorBox_1.md @@ -160,7 +160,7 @@ ```xml - + 2 diff --git a/docs/ops/image/Interpolate_4.md b/docs/ops/image/Interpolate_4.md index 72bb435227381f..a2f78226307524 100644 --- a/docs/ops/image/Interpolate_4.md +++ b/docs/ops/image/Interpolate_4.md @@ -80,7 +80,7 @@ * *cube_coeff* -* **Description**: *cube_coeff* specifies the parameter *a* for cubic interpolation (see, e.g. [article](https://ieeexplore.ieee.org/document/1163711/)). *cube_coeff* is used only when `mode == cubic`. + * **Description**: *cube_coeff* specifies the parameter *a* for cubic interpolation (see, e.g. [article](https://ieeexplore.ieee.org/document/1163711/)). *cube_coeff* is used only when `mode == cubic`. * **Range of values**: floating point number * **Type**: any of supported floating point type * **Default value**: `-0.75` diff --git a/docs/ops/pooling/AvgPool_1.md b/docs/ops/pooling/AvgPool_1.md index b19cbca9ab7fdb..78792d77d11836 100644 --- a/docs/ops/pooling/AvgPool_1.md +++ b/docs/ops/pooling/AvgPool_1.md @@ -48,9 +48,9 @@ * **Default value**: None * **Required**: *yes* -* *exclude_pad* +* *exclude-pad* - * **Description**: *exclude_pad* is a type of pooling strategy for values in the padding area. For example, if *exclude_pad* is "true", then zero-values that came from padding are not included in averaging calculation. + * **Description**: *exclude-pad* is a type of pooling strategy for values in the padding area. For example, if *exclude-pad* is "true", then zero-values that came from padding are not included in averaging calculation. * **Range of values**: true or false * **Type**: boolean * **Default value**: None @@ -94,7 +94,7 @@ output_{j} = \frac{\sum_{i = 0}^{n}x_{i}}{n} ```xml - + 1 @@ -114,7 +114,7 @@ output_{j} = \frac{\sum_{i = 0}^{n}x_{i}}{n} - + 1 @@ -134,7 +134,7 @@ output_{j} = \frac{\sum_{i = 0}^{n}x_{i}}{n} - + 1 @@ -154,7 +154,7 @@ output_{j} = \frac{\sum_{i = 0}^{n}x_{i}}{n} - + 1 @@ -174,7 +174,7 @@ output_{j} = \frac{\sum_{i = 0}^{n}x_{i}}{n} - + 1 diff --git a/docs/ops/sequence/CTCGreedyDecoder_1.md b/docs/ops/sequence/CTCGreedyDecoder_1.md index 59c6bf267eea7b..7a56c7e57ad683 100644 --- a/docs/ops/sequence/CTCGreedyDecoder_1.md +++ b/docs/ops/sequence/CTCGreedyDecoder_1.md @@ -19,9 +19,9 @@ Sequences in the batch can have different length. The lengths of sequences are c **Attributes** -* *merge_repeated* +* *ctc_merge_repeated* - * **Description**: *merge_repeated* is a flag for merging repeated labels during the CTC calculation. + * **Description**: *ctc_merge_repeated* is a flag for merging repeated labels during the CTC calculation. * **Range of values**: true or false * **Type**: `boolean` * **Default value**: true @@ -41,6 +41,7 @@ Sequences in the batch can have different length. The lengths of sequences are c ```xml + 20 diff --git a/model-optimizer/extensions/front/caffe/pooling_ext.py b/model-optimizer/extensions/front/caffe/pooling_ext.py index 102ed82339bcb0..873e0d38be43df 100644 --- a/model-optimizer/extensions/front/caffe/pooling_ext.py +++ b/model-optimizer/extensions/front/caffe/pooling_ext.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ def extract(cls, node): param = proto_layer.pooling_param method = 'max' - exclude_pad = 'true' + exclude_pad = True kernel = [0, 0] stride = [1, 1] padding = [0, 0] @@ -46,10 +46,10 @@ def extract(cls, node): if param.pool == 0: method = 'max' - exclude_pad = 'true' + exclude_pad = True elif param.pool == 1: method = 'avg' - exclude_pad = 'false' + exclude_pad = False else: raise ValueError('Unknown Pooling Method!') diff --git a/model-optimizer/extensions/front/caffe/pooling_ext_test.py b/model-optimizer/extensions/front/caffe/pooling_ext_test.py index 576896c2342859..c5ada7f7787052 100644 --- a/model-optimizer/extensions/front/caffe/pooling_ext_test.py +++ b/model-optimizer/extensions/front/caffe/pooling_ext_test.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ def test_pooling_ext_global(self): 'stride': 2, 'pad': 3, 'pool': 0, - 'global_pooling': 1, + 'global_pooling': True, 'ceil_mode': 1 } node = PB({'pb': FakeProtoLayer(FakeMultiParam(params))}) @@ -48,9 +48,9 @@ def test_pooling_ext_global(self): 'pad': np.array([[0, 0], [0, 0], [0, 0], [0, 0]], dtype=np.int64), 'pad_spatial_shape': np.array([[0, 0], [0, 0]], dtype=np.int64), 'pool_method': 'max', - 'exclude_pad': 'true', + 'exclude_pad': True, 'infer': Pooling.infer, - 'global_pool': 1, + 'global_pool': True, 'output_spatial_shape': None, 'pooling_convention': 'full', 'rounding_type': 'ceil' @@ -72,7 +72,7 @@ def test_pooling_ext(self): 'stride': 2, 'pad': 3, 'pool': 1, - 'global_pooling': 0, + 'global_pooling': False, 'ceil_mode': 0 } node = PB({'pb': FakeProtoLayer(FakeMultiParam(params))}) @@ -84,9 +84,9 @@ def test_pooling_ext(self): 'pad': np.array([[0, 0], [0, 0], [3, 3], [3, 3]], dtype=np.int64), 'pad_spatial_shape': np.array([[3, 3], [3, 3]], dtype=np.int64), 'pool_method': 'avg', - 'exclude_pad': 'false', + 'exclude_pad': False, 'infer': Pooling.infer, - 'global_pool': 0, + 'global_pool': False, 'output_spatial_shape': None, 'pooling_convention': 'valid' } @@ -106,7 +106,7 @@ def test_pooling_ext_exception(self): 'stride': 2, 'pad': 3, 'pool': 3, - 'global_pooling': 1 + 'global_pooling': True } node = PB({'pb': FakeProtoLayer(FakeMultiParam(params))}) self.assertRaises(ValueError, PoolingFrontExtractor.extract, node) diff --git a/model-optimizer/extensions/front/mxnet/pooling_ext.py b/model-optimizer/extensions/front/mxnet/pooling_ext.py index feae85da36004c..54b9a7be00a5b2 100644 --- a/model-optimizer/extensions/front/mxnet/pooling_ext.py +++ b/model-optimizer/extensions/front/mxnet/pooling_ext.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -41,7 +41,7 @@ def extract(cls, node): 'pad': np.array([[0, 0], [0, 0], *[[pad, pad] for pad in padding]], dtype=np.int64), 'pad_spatial_shape': np.array([[pad, pad] for pad in padding], dtype=np.int64), 'pool_method': method, - 'exclude_pad': 'false', + 'exclude_pad': False, 'output_spatial_shape': None, 'spatial_dims': None, 'channel_dims': np.array([1], dtype=np.int64), diff --git a/model-optimizer/extensions/front/mxnet/pooling_ext_test.py b/model-optimizer/extensions/front/mxnet/pooling_ext_test.py index 17258cc0b18b2a..7212a27f232ae3 100644 --- a/model-optimizer/extensions/front/mxnet/pooling_ext_test.py +++ b/model-optimizer/extensions/front/mxnet/pooling_ext_test.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -40,7 +40,7 @@ def test_conv_ext_ideal_numbers(self): 'stride': np.array([1, 1, 3, 2]), 'window': np.array([1, 1, 3, 4]), 'pool_method': 'max', - 'exclude_pad': 'false', + 'exclude_pad': False, } for key in exp_res.keys(): diff --git a/model-optimizer/extensions/front/onnx/pooling_ext.py b/model-optimizer/extensions/front/onnx/pooling_ext.py index 3e41e664983520..40abbfeeda70b6 100644 --- a/model-optimizer/extensions/front/onnx/pooling_ext.py +++ b/model-optimizer/extensions/front/onnx/pooling_ext.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -112,7 +112,7 @@ def common_onnx_pool_extractor(node): # exclude_pad = True only when count_include_pad == 0 exclude_pad = onnx_attr(node, 'count_include_pad', 'i', default=0) == 0 - global_pooling = 0 + global_pooling = False if node.op in ['MaxPool', 'GlobalMaxPool']: method = 'max' elif node.op in ['AveragePool', 'GlobalAveragePool']: @@ -136,7 +136,7 @@ def common_onnx_pool_extractor(node): 'pad': final_pads, 'pad_spatial_shape': np.array(pads, dtype=np.int64) if pads is not None else None, 'pool_method': method, - 'exclude_pad': 'true' if exclude_pad else 'false', + 'exclude_pad': True if exclude_pad else False, 'global_pool': global_pooling, 'output_spatial_shape': None, 'rounding_type': rt, diff --git a/model-optimizer/extensions/front/tf/pooling_ext.py b/model-optimizer/extensions/front/tf/pooling_ext.py index 028ed2fd5699ea..9606990d49d005 100644 --- a/model-optimizer/extensions/front/tf/pooling_ext.py +++ b/model-optimizer/extensions/front/tf/pooling_ext.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -86,6 +86,6 @@ def create_pooling_attrs(node, pool_method): 'pool_method': pool_method, 'type': 'Pooling', 'layout': data_format.s.decode(), - 'exclude_pad': 'true', + 'exclude_pad': True, } return attrs \ No newline at end of file diff --git a/model-optimizer/extensions/front/tf/pooling_ext_test.py b/model-optimizer/extensions/front/tf/pooling_ext_test.py index bcab0ed87f18f7..9f74a74fa7e8bf 100644 --- a/model-optimizer/extensions/front/tf/pooling_ext_test.py +++ b/model-optimizer/extensions/front/tf/pooling_ext_test.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -48,7 +48,7 @@ def test_pool_defaults(self): 'pad': None, # will be inferred when input shape is known 'pad_spatial_shape': None, 'type': 'Pooling', - 'exclude_pad': 'true', + 'exclude_pad': True, } node = PB({'pb': pb}) AvgPoolFrontExtractor.extract(node) diff --git a/model-optimizer/extensions/ops/DetectionOutput.py b/model-optimizer/extensions/ops/DetectionOutput.py index 9a7f52c72076a2..787a4b2d207949 100644 --- a/model-optimizer/extensions/ops/DetectionOutput.py +++ b/model-optimizer/extensions/ops/DetectionOutput.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ import numpy as np from mo.front.common.partial_infer.multi_box_detection import multi_box_detection_infer +from mo.front.extractor import bool_to_str from mo.graph.graph import Graph, Node from mo.ops.op import Op @@ -34,48 +35,32 @@ def __init__(self, graph: Graph, attrs: dict): 'infer': multi_box_detection_infer, 'input_width': 1, 'input_height': 1, - 'normalized': 1, - 'share_location': 1, - 'variance_encoded_in_target': 0, + 'normalized': True, + 'share_location': True, + 'clip_after_nms': False, + 'clip_before_nms': False, + 'decrease_label_id': False, + 'variance_encoded_in_target': False, 'type_infer': self.type_infer, }, attrs) def supported_attrs(self): return [ 'background_label_id', - 'clip_after_nms', - 'clip_before_nms', + ('clip_after_nms', lambda node: bool_to_str(node, 'clip_after_nms')), + ('clip_before_nms', lambda node: bool_to_str(node, 'clip_before_nms')), 'code_type', 'confidence_threshold', - 'decrease_label_id', - 'eta', - 'height', - 'height_scale', + ('decrease_label_id', lambda node: bool_to_str(node, 'decrease_label_id')), 'input_height', 'input_width', - 'interp_mode', 'keep_top_k', - 'label_map_file', - 'name_size_file', 'nms_threshold', - 'normalized', + ('normalized', lambda node: bool_to_str(node, 'normalized')), 'num_classes', - 'num_test_image', - 'output_directory', - 'output_format', - 'output_name_prefix', - 'pad_mode', - 'pad_value', - 'prob', - 'resize_mode', - 'save_file', - 'share_location', + ('share_location', lambda node: bool_to_str(node, 'share_location')), 'top_k', - 'variance_encoded_in_target', - 'visualize', - 'visualize_threshold', - 'width', - 'width_scale', + ('variance_encoded_in_target', lambda node: bool_to_str(node, 'variance_encoded_in_target')), 'objectness_score', ] diff --git a/model-optimizer/extensions/ops/GRUCell.py b/model-optimizer/extensions/ops/GRUCell.py index 67a48c4639d634..5ae69b339fdae3 100644 --- a/model-optimizer/extensions/ops/GRUCell.py +++ b/model-optimizer/extensions/ops/GRUCell.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,6 +14,7 @@ limitations under the License. """ from mo.front.common.partial_infer.utils import mark_input_bins +from mo.front.extractor import bool_to_str from mo.graph.graph import Node, Graph from mo.ops.op import Op from mo.utils.error import Error @@ -39,13 +40,13 @@ def __init__(self, graph: Graph, attrs: dict): mandatory_props = { 'type': __class__.op, 'op': __class__.op, - 'version': 'experimental', 'infer': __class__.infer, 'in_ports_count': 4, 'out_ports_count': 1, 'version': 'opset3', 'wr_input_id': 2, - 'gates_count': 3 + 'gates_count': 3, + 'linear_before_reset': False, } super().__init__(graph, mandatory_props, attrs) @@ -66,7 +67,7 @@ def backend_attrs(self): 'activation_alpha', 'activation_beta', 'clip', - 'linear_before_reset', + ('linear_before_reset', lambda node: bool_to_str(node, 'linear_before_reset')), ] @staticmethod diff --git a/model-optimizer/extensions/ops/MatMul.py b/model-optimizer/extensions/ops/MatMul.py index 4b0f1b4d1cde7e..7181ddc33167ee 100644 --- a/model-optimizer/extensions/ops/MatMul.py +++ b/model-optimizer/extensions/ops/MatMul.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ import numpy as np from mo.front.common.partial_infer.utils import assign_dims_to_weights, int64_array +from mo.front.extractor import bool_to_str from mo.graph.graph import Node, Graph from mo.ops.op import Op @@ -44,8 +45,8 @@ def __init__(self, graph: Graph, attrs: dict): def supported_attrs(self): return [ - 'transpose_a', - 'transpose_b', + ('transpose_a', lambda node: bool_to_str(node, 'transpose_a')), + ('transpose_b', lambda node: bool_to_str(node, 'transpose_b')), ] @staticmethod diff --git a/model-optimizer/extensions/ops/ReduceOps.py b/model-optimizer/extensions/ops/ReduceOps.py index 622b5eec3a3b13..3c8cc0714eca12 100644 --- a/model-optimizer/extensions/ops/ReduceOps.py +++ b/model-optimizer/extensions/ops/ReduceOps.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ import numpy as np from mo.front.common.partial_infer.utils import int64_array +from mo.front.extractor import bool_to_str from mo.graph.graph import Node, Graph from mo.graph.perm_inputs import PermuteInputs from mo.ops.op import Op @@ -106,7 +107,7 @@ def __init__(self, graph: Graph, attrs: dict): def supported_attrs(self): return [ - ('keep_dims', lambda node: str(node.keep_dims)), + ('keep_dims', lambda node: bool_to_str(node, 'keep_dims')), ] diff --git a/model-optimizer/extensions/ops/adaptive_avg_pooling.py b/model-optimizer/extensions/ops/adaptive_avg_pooling.py index 1d22a6fbc2c636..2e2460518b7ac7 100644 --- a/model-optimizer/extensions/ops/adaptive_avg_pooling.py +++ b/model-optimizer/extensions/ops/adaptive_avg_pooling.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -55,7 +55,7 @@ def infer(cls, node: Node): 'pad': int64_array([[0, 0], [0, 0], [0, 0], [0, 0]]), 'pad_spatial_shape': int64_array([[0, 0], [0, 0]]), 'pool_method': 'avg', - 'exclude_pad': 'false', + 'exclude_pad': False, 'output_spatial_shape': None, 'spatial_dims': None, 'channel_dims': int64_array([1]), diff --git a/model-optimizer/extensions/ops/bucketize.py b/model-optimizer/extensions/ops/bucketize.py index 850c1b9526f7e3..777a28b9c400d5 100644 --- a/model-optimizer/extensions/ops/bucketize.py +++ b/model-optimizer/extensions/ops/bucketize.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ import numpy as np +from mo.front.extractor import bool_to_str from mo.graph.graph import Node, Graph from mo.middle.passes.convert_data_type import np_data_type_to_destination_type from mo.ops.op import Op @@ -42,10 +43,10 @@ def __init__(self, graph: Graph, attrs: dict): def backend_attrs(self): version = self.get_opset() if version == "extension": - return ['with_right_bound'] + return [('with_right_bound', lambda node: bool_to_str(node, 'with_right_bound'))] else: return [ - 'with_right_bound', + ('with_right_bound', lambda node: bool_to_str(node, 'with_right_bound')), ('output_type', lambda node: np_data_type_to_destination_type(node.output_type)), ] diff --git a/model-optimizer/extensions/ops/ctc_greedy_decoder.py b/model-optimizer/extensions/ops/ctc_greedy_decoder.py index 74f523cc16e6d7..9d0fda38ae666d 100644 --- a/model-optimizer/extensions/ops/ctc_greedy_decoder.py +++ b/model-optimizer/extensions/ops/ctc_greedy_decoder.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ """ from mo.front.common.partial_infer.utils import int64_array +from mo.front.extractor import bool_to_str from mo.graph.graph import Node, Graph from mo.ops.op import Op @@ -32,13 +33,15 @@ def __init__(self, graph: Graph, attrs: dict): 'reinterp_shape': True, 'in_ports_count': 2, - 'out_ports_count': 1 + 'out_ports_count': 1, + + 'ctc_merge_repeated': True } super().__init__(graph, mandatory_props, attrs) def supported_attrs(self): return [ - 'ctc_merge_repeated' + ('ctc_merge_repeated', lambda node: bool_to_str(node, 'ctc_merge_repeated')) ] @staticmethod diff --git a/model-optimizer/extensions/ops/ctc_loss.py b/model-optimizer/extensions/ops/ctc_loss.py index 6004c634fe052d..1a5bdce41aab75 100644 --- a/model-optimizer/extensions/ops/ctc_loss.py +++ b/model-optimizer/extensions/ops/ctc_loss.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2020 Intel Corporation + Copyright (C) 2020-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ import numpy as np from mo.front.common.partial_infer.utils import int64_array +from mo.front.extractor import bool_to_str from mo.graph.graph import Node, Graph from mo.ops.op import Op @@ -35,11 +36,17 @@ def __init__(self, graph: Graph, attrs: dict): 'in_ports_count': 5, 'out_ports_count': 1, + + 'preprocess_collapse_repeated': False, + 'ctc_merge_repeated': True, + 'unique': False } super().__init__(graph, mandatory_props, attrs) def backend_attrs(self): - return ['preprocess_collapse_repeated', 'ctc_merge_repeated', 'unique'] + return [('preprocess_collapse_repeated', lambda node: bool_to_str(node, 'preprocess_collapse_repeated')), + ('ctc_merge_repeated', lambda node: bool_to_str(node, 'ctc_merge_repeated')), + ('unique', lambda node: bool_to_str(node, 'unique'))] @staticmethod def type_infer(node): diff --git a/model-optimizer/extensions/ops/cumsum.py b/model-optimizer/extensions/ops/cumsum.py index 6cc192ac90b016..f7a6dc130b5e6c 100644 --- a/model-optimizer/extensions/ops/cumsum.py +++ b/model-optimizer/extensions/ops/cumsum.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ """ import numpy as np +from mo.front.extractor import bool_to_str from mo.graph.graph import Node, Graph from mo.ops.op import Op @@ -48,7 +49,8 @@ def __init__(self, graph: Graph, attrs: dict): }, attrs) def supported_attrs(self): - return ["exclusive", "reverse"] + return [('exclusive', lambda node: bool_to_str(node, 'exclusive')), + ('reverse', lambda node: bool_to_str(node, 'reverse'))] @staticmethod def infer(node: Node): diff --git a/model-optimizer/extensions/ops/interpolate.py b/model-optimizer/extensions/ops/interpolate.py index 95e62d20d5cb50..c0d807eb39fb5b 100644 --- a/model-optimizer/extensions/ops/interpolate.py +++ b/model-optimizer/extensions/ops/interpolate.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,9 +16,11 @@ import math + import numpy as np from mo.front.common.partial_infer.utils import int64_array +from mo.front.extractor import bool_to_str from mo.graph.graph import Node, Graph from mo.ops.op import Op, PermuteAttrs @@ -116,11 +118,14 @@ def __init__(self, graph: Graph, attrs: dict): self.attributes_for_opsets = { 'opset1': [ ('axes', lambda node: ','.join(map(str, node.axes))), - 'mode', 'align_corners', 'antialias', 'pads_begin', 'pads_end', + ('antialias', lambda node: bool_to_str(node, 'antialias')), + ('align_corners', lambda node: bool_to_str(node, 'align_corners')), + 'mode', 'pads_begin', 'pads_end', ], 'opset4': [ - 'mode', 'antialias', 'nearest_mode', 'cube_coeff', 'coordinate_transformation_mode', + 'mode', 'nearest_mode', 'cube_coeff', 'coordinate_transformation_mode', 'shape_calculation_mode', + ('antialias', lambda node: bool_to_str(node, 'antialias')), ('pads_begin', lambda node: pad_attribute_to_str(node, 'pads_begin')), ('pads_end', lambda node: pad_attribute_to_str(node, 'pads_end')), ] diff --git a/model-optimizer/extensions/ops/mvn.py b/model-optimizer/extensions/ops/mvn.py index a2ad9ff49ee1c1..d085cc4472ad18 100644 --- a/model-optimizer/extensions/ops/mvn.py +++ b/model-optimizer/extensions/ops/mvn.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,9 +13,10 @@ See the License for the specific language governing permissions and limitations under the License. """ +from mo.front.caffe.extractors.utils import get_canonical_axis_index from mo.front.common.layout import get_features_dim from mo.front.common.partial_infer.elemental import copy_shape_infer -from mo.front.caffe.extractors.utils import get_canonical_axis_index +from mo.front.extractor import bool_to_str from mo.graph.graph import Graph from mo.ops.op import Op from mo.utils.error import Error @@ -44,7 +45,9 @@ def supported_attrs(self): return ['eps', 'across_channels', 'normalize_variance', 'axes'] def backend_attrs(self): - return ['eps', 'across_channels', 'normalize_variance'] + return ['eps', + ('across_channels', lambda node: bool_to_str(node, 'across_channels')), + ('normalize_variance', lambda node: bool_to_str(node, 'normalize_variance'))] @staticmethod def infer(node: None): diff --git a/model-optimizer/extensions/ops/non_max_suppression.py b/model-optimizer/extensions/ops/non_max_suppression.py index 6777bcfd3e5242..3273fea409f80b 100644 --- a/model-optimizer/extensions/ops/non_max_suppression.py +++ b/model-optimizer/extensions/ops/non_max_suppression.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ import numpy as np from mo.front.common.partial_infer.utils import int64_array +from mo.front.extractor import bool_to_str from mo.graph.graph import Node, Graph from mo.middle.passes.convert_data_type import np_data_type_to_destination_type from mo.ops.op import Op @@ -53,10 +54,12 @@ def __init__(self, graph: Graph, attrs: dict): def backend_attrs(self): version = self.get_opset() if version in ['opset3', 'opset4', 'opset5']: - return ['sort_result_descending', 'box_encoding', + return [('sort_result_descending', lambda node: bool_to_str(node, 'sort_result_descending')), + 'box_encoding', ('output_type', lambda node: np_data_type_to_destination_type(node.output_type))] elif version == 'opset1': - return ['sort_result_descending', 'box_encoding'] + return [('sort_result_descending', lambda node: bool_to_str(node, 'sort_result_descending')), + 'box_encoding'] else: raise Error('Unsupported operation opset version "{}"'.format(version)) diff --git a/model-optimizer/extensions/ops/priorbox.py b/model-optimizer/extensions/ops/priorbox.py index e21d0f66435749..50b4e54f4ec5a6 100644 --- a/model-optimizer/extensions/ops/priorbox.py +++ b/model-optimizer/extensions/ops/priorbox.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ import numpy as np from mo.front.common.layout import get_width_dim, get_height_dim -from mo.front.extractor import attr_getter +from mo.front.extractor import attr_getter, bool_to_str from mo.graph.graph import Node, Graph from mo.ops.op import Op @@ -30,7 +30,9 @@ def __init__(self, graph: Graph, attrs: dict): 'type': self.op, 'op': self.op, 'version': 'opset1', - 'flip': 1, + 'flip': True, + 'clip': True, + 'scale_all_sizes': True, 'max_size': np.array([]), 'min_size': np.array([]), 'aspect_ratio': np.array([]), @@ -66,11 +68,11 @@ def supported_attrs(self): def backend_attrs(self): return [ - 'flip', - 'clip', + ('flip', lambda node: bool_to_str(node, 'flip')), + ('clip', lambda node: bool_to_str(node, 'clip')), 'step', 'offset', - 'scale_all_sizes', + ('scale_all_sizes', lambda node: bool_to_str(node, 'scale_all_sizes')), ('min_size', lambda node: attr_getter(node, 'min_size')), ('max_size', lambda node: attr_getter(node, 'max_size')), ('aspect_ratio', lambda node: attr_getter(node, 'aspect_ratio')), diff --git a/model-optimizer/extensions/ops/priorbox_clustered.py b/model-optimizer/extensions/ops/priorbox_clustered.py index 3f9847eb5b0c95..dd3ee398ace389 100644 --- a/model-optimizer/extensions/ops/priorbox_clustered.py +++ b/model-optimizer/extensions/ops/priorbox_clustered.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ import numpy as np from mo.front.common.layout import get_width_dim, get_height_dim -from mo.front.extractor import attr_getter +from mo.front.extractor import attr_getter, bool_to_str from mo.graph.graph import Node, Graph from mo.ops.op import Op @@ -34,6 +34,7 @@ def __init__(self, graph: Graph, attrs: dict): 'out_ports_count': 1, 'infer': self.priorbox_clustered_infer, 'type_infer': self.type_infer, + 'clip': True, } super().__init__(graph, mandatory_props, attrs) @@ -55,9 +56,7 @@ def supported_attrs(self): def backend_attrs(self): return [ - 'flip', - 'clip', - 'img_size', + ('clip', lambda node: bool_to_str(node, 'clip')), 'img_h', 'img_w', 'step', diff --git a/model-optimizer/extensions/ops/proposal.py b/model-optimizer/extensions/ops/proposal.py index 928b32d8dc3ba8..b452324169a2ae 100644 --- a/model-optimizer/extensions/ops/proposal.py +++ b/model-optimizer/extensions/ops/proposal.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ """ from mo.front.common.partial_infer.utils import int64_array -from mo.front.extractor import attr_getter +from mo.front.extractor import attr_getter, bool_to_str from mo.graph.graph import Node, Graph from mo.ops.op import Op @@ -32,7 +32,9 @@ def __init__(self, graph: Graph, attrs: dict): 'infer': ProposalOp.proposal_infer, 'in_ports_count': 3, 'out_ports_count': 2, - 'normalize': 0, + 'normalize': False, + 'clip_before_nms': True, + 'clip_after_nms': False, } super().__init__(graph, mandatory_props, attrs) @@ -61,9 +63,9 @@ def backend_attrs(self): 'framework', 'box_coordinate_scale', 'box_size_scale', - 'normalize', - 'clip_after_nms', - 'clip_before_nms', + ('normalize', lambda node: bool_to_str(node, 'normalize')), + ('clip_after_nms', lambda node: bool_to_str(node, 'clip_after_nms')), + ('clip_before_nms', lambda node: bool_to_str(node, 'clip_before_nms')), ] @staticmethod diff --git a/model-optimizer/extensions/ops/psroipooling.py b/model-optimizer/extensions/ops/psroipooling.py index 249dcac01c4ef1..ef569bdb23b68b 100644 --- a/model-optimizer/extensions/ops/psroipooling.py +++ b/model-optimizer/extensions/ops/psroipooling.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -45,8 +45,6 @@ def supported_attrs(self): 'mode', 'spatial_bins_x', 'spatial_bins_y', - 'pooled_width', - 'pooled_height', ] @staticmethod diff --git a/model-optimizer/extensions/ops/regionyolo.py b/model-optimizer/extensions/ops/regionyolo.py index bab2835d7e32fd..30944715495677 100644 --- a/model-optimizer/extensions/ops/regionyolo.py +++ b/model-optimizer/extensions/ops/regionyolo.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ from mo.front.caffe.extractors.utils import get_canonical_axis_index from mo.front.common.layout import get_batch_dim, get_height_dim, get_width_dim, shape_for_layout -from mo.front.extractor import attr_getter +from mo.front.extractor import attr_getter, bool_to_str from mo.graph.graph import Node, Graph from mo.ops.op import Op @@ -56,7 +56,7 @@ def backend_attrs(self): 'num', 'axis', 'end_axis', - 'do_softmax', + ('do_softmax', lambda node: bool_to_str(node, 'do_softmax')), ('anchors', lambda node: attr_getter(node, 'anchors')), ('mask', lambda node: attr_getter(node, 'mask')) ] diff --git a/model-optimizer/mo/front/extractor.py b/model-optimizer/mo/front/extractor.py index b94badaa45ef3c..19679614d0f80a 100644 --- a/model-optimizer/mo/front/extractor.py +++ b/model-optimizer/mo/front/extractor.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -131,6 +131,20 @@ def attr_getter(node: Node, name: str): return None +def bool_to_str(node: Node, attr: str): + # Function converts 0/1 or bool False/True values to str 'false'/'true' which need to appear in IR + attribute_name = node.soft_get(attr, None) + if attribute_name is None: + return None + if isinstance(attribute_name, bool): + return str(attribute_name).lower() + elif attribute_name in [0, 1]: + return str(bool(attribute_name)).lower() + else: + raise Error('Wrong value {} for boolean attribute {} in node {}'.format( + attribute_name, attr, node.soft_get('name'))) + + def kernel_getter(node: Node, dim: int): if node.kind == 'op' and node.op in ['Conv2D', 'DepthwiseConv2dNative', 'Deconv2D']: if node.has('kernel_spatial'): diff --git a/model-optimizer/mo/front/extractor_test.py b/model-optimizer/mo/front/extractor_test.py index 44f4b3b25058d9..0950ddc9e963df 100644 --- a/model-optimizer/mo/front/extractor_test.py +++ b/model-optimizer/mo/front/extractor_test.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ from mo.front.extractor import input_user_data_repack, output_user_data_repack, update_ie_fields, add_input_op, \ get_node_id_with_ports from mo.front.extractor import spatial_attr_getter, add_input_ops, attr_getter, CaffePythonFrontExtractorOp, \ - add_output_ops + add_output_ops, bool_to_str from mo.graph.graph import Node from mo.utils.error import Error from mo.utils.ir_engine.compare_graphs import compare_graphs @@ -672,3 +672,18 @@ def test_get_attrs(self): param_str = "'test_attr_1': 12, 'test_attr_2': 'sdf sdf'" attrs = CaffePythonFrontExtractorOp.get_attrs(FakePythonParam(FakeMultiParam({'param_str': param_str}))) self.assertEqual(exp_attrs, attrs) + +class TestBoolToSrtFunction(unittest.TestCase): + def test_bool_to_str(self): + graph = build_graph(nodes_attributes, + [('input', 'pool_1'), + ('pool_1', 'output'), + ('output', 'op_output') + ], + {'pool_1': {'bool_attr': None} + }) + pool_1_node = Node(graph, 'pool_1') + attrs = [(True, 'true'), (False, 'false'), (1, 'true'), (0, 'false')] + for attr in attrs: + pool_1_node.bool_attr = attr[0] + self.assertEqual(attr[1], bool_to_str(pool_1_node, 'bool_attr')) diff --git a/model-optimizer/mo/ops/deformable_convolution.py b/model-optimizer/mo/ops/deformable_convolution.py index e2e3f436a350b7..3c4860d3218cbe 100644 --- a/model-optimizer/mo/ops/deformable_convolution.py +++ b/model-optimizer/mo/ops/deformable_convolution.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -28,6 +28,8 @@ def __init__(self, graph: Graph, attrs: dict): 'op': __class__.op, 'version': 'opset1', 'infer': Convolution.infer, + 'group': 1, + 'deformable_group': 1, 'multiplication_transparent': True, 'multiplication_transparent_ports': [(0, 0), (2, 0)], 'in_ports_count': 3, diff --git a/model-optimizer/mo/ops/pooling.py b/model-optimizer/mo/ops/pooling.py index f8bc2c7f922b38..01c2f789fe6749 100644 --- a/model-optimizer/mo/ops/pooling.py +++ b/model-optimizer/mo/ops/pooling.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ from mo.graph.graph import Node, Graph from mo.ops.op import Op, PermuteAttrs from mo.utils.error import Error +from mo.front.extractor import bool_to_str class Pooling(Op): @@ -44,8 +45,7 @@ def backend_attrs(self): ('pads_begin', lambda node: ','.join(map(str, get_backend_pad(node.pad, node.spatial_dims, 0)))), ('pads_end', lambda node: ','.join(map(str, get_backend_pad(node.pad, node.spatial_dims, 1)))), - ('pool-method', 'pool_method'), - ('exclude-pad', 'exclude_pad'), + ('exclude-pad', lambda node: bool_to_str(node, 'exclude_pad')), 'rounding_type', ('auto_pad', lambda node: node.auto_pad if node.has_valid('auto_pad') else 'explicit'), diff --git a/model-optimizer/mo/ops/pooling_test.py b/model-optimizer/mo/ops/pooling_test.py index 456e31b92669b8..ab365ec06fe00d 100644 --- a/model-optimizer/mo/ops/pooling_test.py +++ b/model-optimizer/mo/ops/pooling_test.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -42,7 +42,7 @@ def test_pooling_infer(self): 'pool': {'window': np.array([1, 1, 1, 1]), 'stride': np.array([1, 1, 2, 2]), 'pad': np.array([[0, 0], [0, 0], [3, 3], [3, 3]]), 'pad_spatial_shape': np.array([[3, 3], [3, 3]]), - 'pool_method': 'avg', 'exclude_pad': 'false', 'global_pool': 0, + 'pool_method': 'avg', 'exclude_pad': False, 'global_pool': False, 'output_spatial_shape': None, 'output_shape': None, 'kernel_spatial': np.array([3, 3]), 'spatial_dims': np.array([2, 3]), 'channel_dims': np.array([1]), 'batch_dims': np.array([0]), @@ -68,7 +68,7 @@ def test_pooling_infer_decrement_input_spatial(self): 'pool': {'window': np.array([1, 1, 1, 1]), 'stride': np.array([1, 1, 3, 3]), 'pad': np.array([[0, 0], [0, 0], [3, 3], [3, 3]]), 'pad_spatial_shape': np.array([[1, 1], [1, 1]]), - 'pool_method': 'avg', 'exclude_pad': 'false', 'global_pool': 0, + 'pool_method': 'avg', 'exclude_pad': False, 'global_pool': False, 'output_spatial_shape': None, 'output_shape': None, 'kernel_spatial': np.array([3, 3]), 'spatial_dims': np.array([2, 3]), 'channel_dims': np.array([1]), 'batch_dims': np.array([0]), @@ -94,7 +94,7 @@ def test_pooling_infer_no_convention(self): 'pool': {'window': np.array([1, 1, 1, 1]), 'stride': np.array([1, 1, 2, 2]), 'pad': np.array([[0, 0], [0, 0], [3, 3], [3, 3]]), 'pad_spatial_shape': np.array([[3, 3], [3, 3]]), - 'pool_method': 'avg', 'exclude_pad': 'false', 'global_pool': 0, + 'pool_method': 'avg', 'exclude_pad': False, 'global_pool': False, 'output_spatial_shape': None, 'output_shape': None, 'kernel_spatial': np.array([3, 3]), 'spatial_dims': np.array([2, 3]), 'channel_dims': np.array([1]), 'batch_dims': np.array([0])} @@ -119,7 +119,7 @@ def test_pooling_infer_no_shape(self): 'pool': {'window': np.array([1, 1, 1, 1]), 'stride': np.array([1, 1, 2, 2]), 'pad': np.array([[0, 0], [0, 0], [3, 3], [3, 3]]), 'pad_spatial_shape': np.array([[3, 3], [3, 3]]), - 'pool_method': 'avg', 'exclude_pad': 'false', + 'pool_method': 'avg', 'exclude_pad': False, 'output_spatial_shape': None, 'output_shape': None, 'kernel_spatial': np.array([3, 3]), 'spatial_dims': np.array([2, 3]), 'channel_dims': np.array([1]), 'batch_dims': np.array([0]), @@ -142,7 +142,7 @@ def test_pooling_infer_wrong_input_shape(self): 'pool': {'window': np.array([1, 1, 5, 5]), 'stride': np.array([1, 1, 2, 2]), 'pad': np.array([[0, 0], [0, 0], [1, 1], [1, 1]]), 'pad_spatial_shape': np.array([[1, 1], [1, 1]]), - 'pool_method': 'avg', 'exclude_pad': 'false', 'global_pool': 0, + 'pool_method': 'avg', 'exclude_pad': False, 'global_pool': False, 'output_spatial_shape': None, 'output_shape': None, 'kernel_spatial': np.array([3, 3]), 'spatial_dims': np.array([2, 3]), 'channel_dims': np.array([1]), 'batch_dims': np.array([0]), diff --git a/model-optimizer/mo/ops/reshape.py b/model-optimizer/mo/ops/reshape.py index 55329533cd0fa7..018b1d68935513 100644 --- a/model-optimizer/mo/ops/reshape.py +++ b/model-optimizer/mo/ops/reshape.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ """ import numpy as np +from mo.front.extractor import bool_to_str from mo.graph.graph import Node, Graph from mo.graph.perm_inputs import PermuteInputs from mo.ops.op import Op @@ -40,7 +41,7 @@ def __init__(self, graph: Graph, attrs: dict): }, attrs) def supported_attrs(self): - return ['special_zero'] + return [('special_zero', lambda node: bool_to_str(node, 'special_zero'))] @staticmethod def infer(node: Node): diff --git a/model-optimizer/mo/ops/roipooling.py b/model-optimizer/mo/ops/roipooling.py index 713953990426a8..6c48639769a973 100644 --- a/model-optimizer/mo/ops/roipooling.py +++ b/model-optimizer/mo/ops/roipooling.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ def __init__(self, graph, attrs: dict): 'pooled_h': None, 'pooled_w': None, 'spatial_scale': 0.0625, + 'method': 'max', 'infer': roipooling_infer, 'in_ports_count': 2, 'out_ports_count': 1, From 450f01280ac1df1a6e268e1b83986a9be497542f Mon Sep 17 00:00:00 2001 From: Patryk Elszkowski Date: Fri, 29 Jan 2021 10:30:57 +0100 Subject: [PATCH 08/99] [compare_function] compare ops attributes (#3966) * [compare_function] compare ops attributes value by value * Storage cleanup * Add comparison for: - SubGraphOpInputDescription - SubGraphOpOutputDescription - SpecialBodyPorts * cleanup * Report error on unhandled types * Change comparison of floating-point to general approach Co-authored-by: Patryk Elszkowski --- .../ir_serialization/serialize.cpp | 2 +- .../ir_serialization/tensor_iterator.cpp | 2 +- .../common_test_utils/ngraph_test_utils.cpp | 567 +++++++++++++++++- .../common_test_utils/ngraph_test_utils.hpp | 3 +- 4 files changed, 552 insertions(+), 22 deletions(-) diff --git a/inference-engine/tests/functional/inference_engine/ir_serialization/serialize.cpp b/inference-engine/tests/functional/inference_engine/ir_serialization/serialize.cpp index 5480ecfd378e22..98a6c33bcbe20b 100644 --- a/inference-engine/tests/functional/inference_engine/ir_serialization/serialize.cpp +++ b/inference-engine/tests/functional/inference_engine/ir_serialization/serialize.cpp @@ -53,7 +53,7 @@ TEST_P(SerializationTest, CompareFunctions) { bool success; std::string message; - std::tie(success, message) = compare_functions(result.getFunction(), expected.getFunction(), true, false, true); + std::tie(success, message) = compare_functions(result.getFunction(), expected.getFunction(), true, false, true, true, true); ASSERT_TRUE(success) << message; } diff --git a/inference-engine/tests/functional/inference_engine/ir_serialization/tensor_iterator.cpp b/inference-engine/tests/functional/inference_engine/ir_serialization/tensor_iterator.cpp index 79a7a3d5dc5247..cd6ac9fd8a466f 100644 --- a/inference-engine/tests/functional/inference_engine/ir_serialization/tensor_iterator.cpp +++ b/inference-engine/tests/functional/inference_engine/ir_serialization/tensor_iterator.cpp @@ -40,7 +40,7 @@ class SerializationTensorIteratorTest : public ::testing::Test { bool success; std::string message; - std::tie(success, message) = compare_functions(result.getFunction(), expected.getFunction(), true); + std::tie(success, message) = compare_functions(result.getFunction(), expected.getFunction(), true, false, false, true, true); ASSERT_TRUE(success) << message; } }; diff --git a/inference-engine/tests/ie_test_utils/common_test_utils/ngraph_test_utils.cpp b/inference-engine/tests/ie_test_utils/common_test_utils/ngraph_test_utils.cpp index b3f8957f1b636c..7b36c9e708c74a 100644 --- a/inference-engine/tests/ie_test_utils/common_test_utils/ngraph_test_utils.cpp +++ b/inference-engine/tests/ie_test_utils/common_test_utils/ngraph_test_utils.cpp @@ -5,9 +5,11 @@ #include "ngraph_test_utils.hpp" #include +#include #include #include #include +#include #include #include @@ -83,6 +85,520 @@ std::string name(const Node& n) { return n->get_friendly_name(); } +namespace attr_comparison { + +using AttrName = std::string; + +class Result { +public: + explicit Result(std::string m = {}) : m_message(std::move(m)) {} + + const std::string& message() const { + return m_message; + } + + bool has_error() const { + return !m_message.empty(); + } + + Result& operator+=(const std::string& msg) { + m_message.append(m_break_line_no, '\n').append(msg); + m_break_line_no = 1; + return *this; + } + +private: + std::string m_message; + int m_break_line_no{0}; +}; + +using SubGraphOpInputDescription = + std::vector>; + +using SubGraphOpOutputDescription = + std::vector>; + +using SpecialBodyPorts = ngraph::op::v5::Loop::SpecialBodyPorts; + +namespace storage { + +class MemoryChunk { +public: + using Data = std::vector; + MemoryChunk(Data data) : m_data{std::move(data)} {} + + Data::const_pointer data() const { + return m_data.data(); + } + + size_t size() const { + return m_data.size(); + } + +private: + Data m_data; +}; + +template +class AttributeStorage { +public: + bool insert_value(AttrName name, AttrValue value) { + return m_attributes.insert({std::move(name), std::move(value)}).second; + } + + const AttrValue* get_value(const AttrName& name) const { + const auto found = m_attributes.find(name); + if (found != end(m_attributes)) { + return std::addressof(found->second); + } + return {}; + } + + std::size_t get_attributes_number() const { + return m_attributes.size(); + } + +private: + std::map m_attributes; +}; + +class Storage : private AttributeStorage, + private AttributeStorage, + private AttributeStorage, + private AttributeStorage, + private AttributeStorage, + private AttributeStorage, + private AttributeStorage, + private AttributeStorage, + private AttributeStorage, + private AttributeStorage, + private AttributeStorage, + private AttributeStorage, + private AttributeStorage, + private AttributeStorage>, + private AttributeStorage>, + private AttributeStorage>, + private AttributeStorage>, + private AttributeStorage>, + private AttributeStorage>, + private AttributeStorage>, + private AttributeStorage>, + private AttributeStorage>, + private AttributeStorage>, + private AttributeStorage>, + private AttributeStorage, + private AttributeStorage, + private AttributeStorage { +public: + template + const AttributeStorage& storage() const { + return *static_cast*>(this); + } + template + AttributeStorage& storage() { + return *static_cast*>(this); + } + + size_t stored_attributes_number() const { + return storage().get_attributes_number() + + storage().get_attributes_number() + + storage().get_attributes_number() + + storage().get_attributes_number() + + storage().get_attributes_number() + + storage().get_attributes_number() + + storage().get_attributes_number() + + storage().get_attributes_number() + + storage().get_attributes_number() + + storage().get_attributes_number() + + storage().get_attributes_number() + + storage().get_attributes_number() + + storage().get_attributes_number() + + storage>().get_attributes_number() + + storage>().get_attributes_number() + + storage>().get_attributes_number() + + storage>().get_attributes_number() + + storage>().get_attributes_number() + + storage>().get_attributes_number() + + storage>().get_attributes_number() + + storage>().get_attributes_number() + + storage>().get_attributes_number() + + storage>().get_attributes_number() + + storage>().get_attributes_number() + + storage().get_attributes_number() + + storage().get_attributes_number() + + storage().get_attributes_number(); + } +}; + +} // namespace storage + +class ReadAndStoreAttributes : public ngraph::AttributeVisitor, protected storage::Storage { +public: + void on_adapter(const std::string& name, ngraph::ValueAccessor& adapter) override { + if (auto inputs = + ngraph::as_type>(&adapter)) { + insert(name, inputs->get()); + } else if ( + auto outputs = + ngraph::as_type>(&adapter)) { + insert(name, outputs->get()); + } else if ( + auto ports = ngraph::as_type>(&adapter)) { + insert(name, ports->get()); + } else { + m_read_result += "store attr [ ERR ]: " + name + + " [drop `void` comparison which is '" + adapter.get_type_info().name + + "']"; + } + } + + void on_adapter(const std::string& name, ngraph::ValueAccessor& adapter) override { + const auto beg = static_cast(adapter.get_ptr()); + const auto end = beg + adapter.size(); + insert(name, storage::MemoryChunk{storage::MemoryChunk::Data(beg, end)}); + } + +#define ON_ADAPTER(TYPE) \ + void on_adapter(const std::string& name, ngraph::ValueAccessor& adapter) override { \ + insert(name, adapter.get()); \ + } + + ON_ADAPTER(bool) + ON_ADAPTER(std::string) + ON_ADAPTER(int8_t) + ON_ADAPTER(int16_t) + ON_ADAPTER(int32_t) + ON_ADAPTER(int64_t) + ON_ADAPTER(uint8_t) + ON_ADAPTER(uint16_t) + ON_ADAPTER(uint32_t) + ON_ADAPTER(uint64_t) + ON_ADAPTER(float) + ON_ADAPTER(double) + ON_ADAPTER(std::vector) + ON_ADAPTER(std::vector) + ON_ADAPTER(std::vector) + ON_ADAPTER(std::vector) + ON_ADAPTER(std::vector) + ON_ADAPTER(std::vector) + ON_ADAPTER(std::vector) + ON_ADAPTER(std::vector) + ON_ADAPTER(std::vector) + ON_ADAPTER(std::vector) + ON_ADAPTER(std::vector) + +#undef ON_ADAPTER + + void on_adapter( + const std::string&, ngraph::ValueAccessor>&) override { + // handled by `compare_functions` drop it here + } + + template + const AttrValue* get(const AttrName& name) const { + return storage().get_value(name); + } + + template + bool insert(AttrName name, AttrValue value) { + return storage().insert_value(std::move(name), std::move(value)); + } + + size_t attributes_number() const { + return stored_attributes_number(); + } + + const Result read_result() const { + return m_read_result; + } + +private: + Result m_read_result; +}; + +namespace equal { + +template +struct Equal { + static bool equal_value(const Value& lhs, const Value& rhs) { + return lhs == rhs; + } +}; + +template <> +struct Equal { + static bool equal_value(float lhs, float rhs) { + return std::abs(lhs - rhs) < 1e-5; + } +}; + +template <> +struct Equal { + static bool equal_value(double lhs, double rhs) { + return std::abs(lhs - rhs) < 1e-5; + } +}; + +template <> +struct Equal> { + static bool equal_value(const std::vector& lhs, const std::vector& rhs) { + return lhs.size() == rhs.size() && + std::equal(begin(lhs), end(lhs), begin(rhs), Equal::equal_value); + } +}; + +template <> +struct Equal> { + static bool equal_value(const std::vector& lhs, const std::vector& rhs) { + return lhs.size() == rhs.size() && + std::equal(begin(lhs), end(lhs), begin(rhs), Equal::equal_value); + } +}; + +template <> +struct Equal { + static bool equal_value( + SubGraphOpInputDescription::const_reference lhs, + SubGraphOpInputDescription::const_reference rhs) { + const auto& lhs_type_info = lhs->get_type_info(); + const auto& rhs_type_info = rhs->get_type_info(); + if (lhs_type_info != rhs_type_info) { + return false; + } + using SubGraphOp = ngraph::op::util::SubGraphOp; + if (lhs_type_info == SubGraphOp::SliceInputDescription::type_info) { + const auto& l_input = static_cast(*lhs); + const auto& r_input = static_cast(*rhs); + return l_input.m_start == r_input.m_start && l_input.m_stride == r_input.m_stride && + l_input.m_part_size == r_input.m_part_size && l_input.m_end == r_input.m_end && + l_input.m_axis == r_input.m_axis; + } else if (lhs_type_info == SubGraphOp::MergedInputDescription::type_info) { + return true; + } else if (lhs_type_info == SubGraphOp::InvariantInputDescription::type_info) { + return true; + } + return false; + } +}; + +template <> +struct Equal { + static bool equal_value( + const SubGraphOpInputDescription& lhs, const SubGraphOpInputDescription& rhs) { + if (lhs.size() != rhs.size()) { + return false; + } + return std::is_permutation( + begin(lhs), end(lhs), begin(rhs), + Equal::equal_value); + } +}; + +template <> +struct Equal { + static bool equal_value( + SubGraphOpOutputDescription::const_reference lhs, + SubGraphOpOutputDescription::const_reference rhs) { + const auto& lhs_type_info = lhs->get_type_info(); + const auto& rhs_type_info = rhs->get_type_info(); + if (lhs_type_info != rhs_type_info) { + return false; + } + using SubGraphOp = ngraph::op::util::SubGraphOp; + if (lhs_type_info == SubGraphOp::ConcatOutputDescription::type_info) { + const auto& l_output = static_cast(*lhs); + const auto& r_output = static_cast(*rhs); + return l_output.m_start == r_output.m_start && l_output.m_stride == r_output.m_stride && + l_output.m_part_size == r_output.m_part_size && + l_output.m_end == r_output.m_end && l_output.m_axis == r_output.m_axis; + } else if (lhs_type_info == SubGraphOp::BodyOutputDescription::type_info) { + const auto& l_output = static_cast(*lhs); + const auto& r_output = static_cast(*rhs); + return l_output.m_iteration == r_output.m_iteration; + } + return false; + } +}; + +template <> +struct Equal { + static bool equal_value( + const SubGraphOpOutputDescription& lhs, const SubGraphOpOutputDescription& rhs) { + if (lhs.size() != rhs.size()) { + return false; + } + return std::is_permutation( + begin(lhs), end(lhs), begin(rhs), + Equal::equal_value); + } +}; + +template <> +struct Equal { + static bool equal_value(const SpecialBodyPorts& lhs, const SpecialBodyPorts& rhs) { + return lhs.current_iteration_input_idx == rhs.current_iteration_input_idx; + } +}; + +} // namespace equal + +class ReadAndCompareAttributes : public ngraph::AttributeVisitor { +public: + ReadAndCompareAttributes(const ReadAndStoreAttributes& ref) + : m_attr_ref(ref), m_cmp_result{ref.read_result()} {} + + void on_adapter(const std::string& name, ngraph::ValueAccessor& adapter) override { + if (should_return()) { + return; + } + m_visited_attributes.insert(name); + if (auto inputs = + ngraph::as_type>(&adapter)) { + verify(name, inputs->get()); + } else if ( + auto outputs = + ngraph::as_type>(&adapter)) { + verify(name, outputs->get()); + } else if ( + auto ports = ngraph::as_type>(&adapter)) { + verify(name, ports->get()); + } else { + m_cmp_result += "compare attr [ ERR ]: " + name + + " [drop `void` comparison which is '" + adapter.get_type_info().name + + "']"; + } + } + + void on_adapter(const std::string& name, ngraph::ValueAccessor& adapter) override { + if (should_return()) { + return; + } + m_visited_attributes.insert(name); + const auto ref_value = m_attr_ref.get(name); + if (!ref_value) { + m_cmp_result += "missing attribute name: " + name; + return; + } + + if (adapter.size() != ref_value->size() || + std::memcmp(ref_value->data(), adapter.get_ptr(), ref_value->size()) != 0) { + m_cmp_result += "mismatch in value: " + name; + return; + } + } + +#define ON_ADAPTER(TYPE) \ + void on_adapter(const std::string& name, ngraph::ValueAccessor& adapter) override { \ + verify(name, adapter.get()); \ + } + + ON_ADAPTER(bool) + ON_ADAPTER(std::string) + ON_ADAPTER(int8_t) + ON_ADAPTER(int16_t) + ON_ADAPTER(int32_t) + ON_ADAPTER(int64_t) + ON_ADAPTER(uint8_t) + ON_ADAPTER(uint16_t) + ON_ADAPTER(uint32_t) + ON_ADAPTER(uint64_t) + ON_ADAPTER(float) + ON_ADAPTER(double) + ON_ADAPTER(std::vector) + ON_ADAPTER(std::vector) + ON_ADAPTER(std::vector) + ON_ADAPTER(std::vector) + ON_ADAPTER(std::vector) + ON_ADAPTER(std::vector) + ON_ADAPTER(std::vector) + ON_ADAPTER(std::vector) + ON_ADAPTER(std::vector) + ON_ADAPTER(std::vector) + ON_ADAPTER(std::vector) + +#undef ON_ADAPTER + + void on_adapter( + const std::string&, ngraph::ValueAccessor>&) override { + // handled by `compare_functions` drop it here + } + + bool all_attr_was_compared() const { + return m_visited_attributes.size() == m_attr_ref.attributes_number(); + } + + size_t compared_attr_number() const { + return m_visited_attributes.size(); + } + + const Result& cmp_result() const { + return m_cmp_result; + } + +private: + bool should_return() const { + return m_fast_exit && m_cmp_result.has_error(); + } + template + void verify(const std::string& name, const AttrValue& attr_value) { + if (should_return()) { + return; + } + m_visited_attributes.insert(name); + const auto ref_value = m_attr_ref.get(name); + if (!ref_value) { + m_cmp_result += "missing attribute name: " + name; + return; + } + + if (!equal::Equal::equal_value(*ref_value, attr_value)) { + m_cmp_result += "mismatch in value: " + name; + return; + } + } + + const ReadAndStoreAttributes& m_attr_ref; + Result m_cmp_result; + std::set m_visited_attributes; + bool m_fast_exit{true}; +}; + +} // namespace attr_comparison + +class CompareNodesAttributes { +public: + CompareNodesAttributes() : m_compare_attr(m_store_attr) {} + + attr_comparison::ReadAndStoreAttributes& get_ref_reder() { + return m_store_attr; + } + + attr_comparison::ReadAndCompareAttributes& get_cmp_reader() { + return m_compare_attr; + } + + bool equal() const { + return m_compare_attr.all_attr_was_compared() && !m_compare_attr.cmp_result().has_error(); + } + + friend std::string to_string(const CompareNodesAttributes& c) { + const auto& result = c.m_compare_attr.cmp_result(); + if (result.has_error()) { + return result.message(); + } + if (!c.m_compare_attr.all_attr_was_compared()) { + return "not all of attr was compared: " + + std::to_string(c.m_compare_attr.compared_attr_number()) + " vs " + + std::to_string(c.m_store_attr.attributes_number()); + } + return "looks good [compared " + std::to_string(c.m_compare_attr.compared_attr_number()) + + " attributes]"; + } + +private: + attr_comparison::ReadAndStoreAttributes m_store_attr; + attr_comparison::ReadAndCompareAttributes m_compare_attr; +}; } // namespace std::pair compare_functions( @@ -91,7 +607,8 @@ std::pair compare_functions( const bool compareConstValues, const bool compareNames, const bool compareRuntimeKeys, - const bool comparePrecisions) { + const bool comparePrecisions, + const bool compareAttributes) { /* * This function compares two nGraph functions and requires them to have exactly one output * + Check nodes types @@ -109,21 +626,23 @@ std::pair compare_functions( if (f1_results.size() != f2_results.size()) { return error( - "Number of results is different: " + to_str(f1_results.size()) + " and " + to_str(f2_results.size())); + "Number of results is different: " + to_str(f1_results.size()) + " and " + + to_str(f2_results.size())); } const auto& f1_sinks = f1->get_sinks(); const auto& f2_sinks = f2->get_sinks(); if (f1_sinks.size() != f2_sinks.size()) { return error( - "Number of sinks is different: " + to_str(f1_sinks.size()) + " and " + to_str(f2_sinks.size())); + "Number of sinks is different: " + to_str(f1_sinks.size()) + " and " + + to_str(f2_sinks.size())); } std::ostringstream err_log; using ComparedNodes = std::pair; std::queue q; - std::unordered_set used; + std::unordered_set used; for (size_t i = 0; i < f1_results.size(); ++i) { if (compareNames) { @@ -134,7 +653,7 @@ std::pair compare_functions( " and " + name(f2_results[i]->get_input_node_shared_ptr(0))); } } - q.push({ f1_results[i].get(), f2_results[i].get() }); + q.push({f1_results[i].get(), f2_results[i].get()}); used.insert(f1_results[i].get()); } @@ -150,12 +669,13 @@ std::pair compare_functions( return error(typeInfoToStr(type_info1) + " != " + typeInfoToStr(type_info2)); } - auto subgraph1 = dynamic_cast(node1); - auto subgraph2 = dynamic_cast(node2); + auto subgraph1 = dynamic_cast(node1); + auto subgraph2 = dynamic_cast(node2); if (subgraph1 && subgraph2) { - auto res = compare_functions(subgraph1->get_function(), subgraph2->get_function(), - compareConstValues, compareNames, compareRuntimeKeys, comparePrecisions); + auto res = compare_functions( + subgraph1->get_function(), subgraph2->get_function(), compareConstValues, + compareNames, compareRuntimeKeys, comparePrecisions, compareAttributes); if (!res.first) { return res; } @@ -189,14 +709,14 @@ std::pair compare_functions( auto const2 = ngraph::as_type_ptr(node2->get_input_node_shared_ptr(i)); const auto equal = [](std::shared_ptr c1, std::shared_ptr c2) { - const auto &c1v = c1->cast_vector(); - const auto &c2v = c2->cast_vector(); - - return c1v.size() == c2v.size() && - std::equal(begin(c1v), end(c1v), begin(c2v), - [](const double &s1, const double & s2) { - return std::abs(s1 - s2) < 0.001; - }); + const auto& c1v = c1->cast_vector(); + const auto& c2v = c2->cast_vector(); + + return c1v.size() == c2v.size() && std::equal( + begin(c1v), end(c1v), begin(c2v), + [](const double& s1, const double& s2) { + return std::abs(s1 - s2) < 0.001; + }); }; if (const1 && const2 && !equal(const1, const2)) { @@ -264,11 +784,20 @@ std::pair compare_functions( << std::endl; } } - } + if (compareAttributes) { + CompareNodesAttributes compare_nodes; + node1->visit_attributes(compare_nodes.get_ref_reder()); + node2->visit_attributes(compare_nodes.get_cmp_reader()); + if (!compare_nodes.equal()) { + return error( + "Comparison of attributes failed for nodes " + name(node1) + ", " + + name(node2) + " [cmp status: " + to_string(compare_nodes) + "]"); + } + } + } return {err_log.str().empty(), err_log.str()}; } - void check_rt_info(const std::shared_ptr& f) { static const std::vector attrs_to_check{"Variant::RuntimeAttribute::FusedNames"}; diff --git a/inference-engine/tests/ie_test_utils/common_test_utils/ngraph_test_utils.hpp b/inference-engine/tests/ie_test_utils/common_test_utils/ngraph_test_utils.hpp index 0ccfd3e35a34e4..0047702bcc7227 100644 --- a/inference-engine/tests/ie_test_utils/common_test_utils/ngraph_test_utils.hpp +++ b/inference-engine/tests/ie_test_utils/common_test_utils/ngraph_test_utils.hpp @@ -24,7 +24,8 @@ std::pair compare_functions( const bool compareConstValues = false, const bool compareNames = false, const bool compareRuntimeKeys = false, - const bool comparePrecisions = true); + const bool comparePrecisions = true, + const bool compareAttributes = false); void check_rt_info(const std::shared_ptr& f); From 4b64c64c625d5d278b0e438cef4698776dae5b7c Mon Sep 17 00:00:00 2001 From: Vladislav Volkov Date: Fri, 29 Jan 2021 13:11:50 +0300 Subject: [PATCH 09/99] Suppressing warning about unused variables for selective build of MKLDNN plugin. (#4039) --- inference-engine/src/mkldnn_plugin/CMakeLists.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/inference-engine/src/mkldnn_plugin/CMakeLists.txt b/inference-engine/src/mkldnn_plugin/CMakeLists.txt index fc4c03aeb0be3b..5b3f34db420a30 100644 --- a/inference-engine/src/mkldnn_plugin/CMakeLists.txt +++ b/inference-engine/src/mkldnn_plugin/CMakeLists.txt @@ -161,6 +161,14 @@ ie_add_plugin(NAME ${TARGET_NAME} set_ie_threading_interface_for(${TARGET_NAME}) +if(SELECTIVE_BUILD STREQUAL "ON") + # After disabling a block of code, some variables might be unused. + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" + OR CMAKE_CXX_COMPILER_ID MATCHES "^(Apple)?Clang$") + target_compile_options(${TARGET_NAME} PRIVATE -Wno-unused-variable) + endif() +endif() + target_link_libraries(${TARGET_NAME} PRIVATE mkldnn inference_engine inference_engine_legacy inference_engine_transformations inference_engine_lp_transformations openvino::conditional_compilation) From 7500bbd3b15dc6aa229ea56c013446160e05e2b4 Mon Sep 17 00:00:00 2001 From: Yury Gaydaychuk Date: Fri, 29 Jan 2021 15:09:39 +0300 Subject: [PATCH 10/99] [CPU] ROIPooling with 1x1 pooled shape in bilinear mode fixed (#4020) --- .../mkldnn_plugin/nodes/mkldnn_roi_pooling_node.cpp | 10 ++++++---- .../single_layer_tests/roi_pooling.cpp | 1 + ngraph/test/runtime/ie/unit_test.manifest | 3 --- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_roi_pooling_node.cpp b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_roi_pooling_node.cpp index 00809fc42d792b..91e99f6d3fd97d 100644 --- a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_roi_pooling_node.cpp +++ b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_roi_pooling_node.cpp @@ -491,11 +491,13 @@ void MKLDNNROIPoolingNode::execute(mkldnn::stream strm) { float roi_end_w_ = src_roi_ptr[3]; float roi_end_h_ = src_roi_ptr[4]; - float height_scale = ((roi_end_h_ - roi_start_h_) * (jpp.ih - 1)) / (jpp.pooled_h - 1); - float width_scale = ((roi_end_w_ - roi_start_w_) * (jpp.iw - 1)) / (jpp.pooled_w - 1); + float height_scale = (jpp.pooled_h > 1 ? ((roi_end_h_ - roi_start_h_) * (jpp.ih - 1)) / (jpp.pooled_h - 1) : 0); + float width_scale = (jpp.pooled_w > 1 ? ((roi_end_w_ - roi_start_w_) * (jpp.iw - 1)) / (jpp.pooled_w - 1) : 0); - float in_y = (oh * height_scale + roi_start_h_ * (jpp.ih - 1)); - float in_x = (ow * width_scale + roi_start_w_ * (jpp.iw - 1)); + float in_y = (jpp.pooled_h > 1 ? (oh * height_scale + roi_start_h_ * (jpp.ih - 1)) : + 0.5 * (roi_start_h_ + roi_end_h_) * (jpp.ih - 1)); + float in_x = (jpp.pooled_w > 1 ? (ow * width_scale + roi_start_w_ * (jpp.iw - 1)) : + 0.5 * (roi_start_w_ + roi_end_w_) * (jpp.iw - 1)); if (in_y < 0 || in_y > jpp.ih - 1 || in_x < 0 || in_x > jpp.iw - 1) { if (roi_pooling_kernel) { diff --git a/inference-engine/tests/functional/plugin/cpu/shared_tests_instances/single_layer_tests/roi_pooling.cpp b/inference-engine/tests/functional/plugin/cpu/shared_tests_instances/single_layer_tests/roi_pooling.cpp index bc244cf5b571ca..b67e0bd252bf83 100644 --- a/inference-engine/tests/functional/plugin/cpu/shared_tests_instances/single_layer_tests/roi_pooling.cpp +++ b/inference-engine/tests/functional/plugin/cpu/shared_tests_instances/single_layer_tests/roi_pooling.cpp @@ -22,6 +22,7 @@ const std::vector> pooledShapes_max = { }; const std::vector> pooledShapes_bilinear = { + {1, 1}, {2, 2}, {3, 3}, {6, 6} diff --git a/ngraph/test/runtime/ie/unit_test.manifest b/ngraph/test/runtime/ie/unit_test.manifest index adffca1acf21d6..8353a2e1f95f0d 100644 --- a/ngraph/test/runtime/ie/unit_test.manifest +++ b/ngraph/test/runtime/ie/unit_test.manifest @@ -1176,9 +1176,6 @@ IE_CPU.onnx_model_nonmaxsuppression_center_point_box_format IE_CPU.onnx_model_nonmaxsuppression_single_box IE_CPU.nonmaxsuppression_suppress_by_IOU_and_scores_without_constants -# Bug in CPU plugin for ROIPooling when pooled size is 1x1 and method is bilinear -IE_CPU.roi_pooling_1x1_bilinear - # Unsupported dynamic op IE_CPU.range_v4_trunc_inputs IE_CPU.onnx_model_reduce_sum_13_axes_as_input From 3669205a444eb69712ae418f6d1ef8927b5b0e91 Mon Sep 17 00:00:00 2001 From: Evgeny Lazarev Date: Fri, 29 Jan 2021 15:48:33 +0300 Subject: [PATCH 11/99] Added support for the MxNet op take (#4071) --- .../Supported_Frameworks_Layers.md | 1 + model-optimizer/automation/package_BOM.txt | 1 + .../extensions/front/mxnet/take_ext.py | 33 +++++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 model-optimizer/extensions/front/mxnet/take_ext.py diff --git a/docs/MO_DG/prepare_model/Supported_Frameworks_Layers.md b/docs/MO_DG/prepare_model/Supported_Frameworks_Layers.md index 869cfa49d5e942..e938848a679444 100644 --- a/docs/MO_DG/prepare_model/Supported_Frameworks_Layers.md +++ b/docs/MO_DG/prepare_model/Supported_Frameworks_Layers.md @@ -108,6 +108,7 @@ Standard MXNet\* symbols: | SoftmaxActivation | No | | SoftmaxOutput | No | | SoftSign | No | +| Take | The attribute 'mode' is not supported | | Tile | No | | UpSampling | No | | Where | No | diff --git a/model-optimizer/automation/package_BOM.txt b/model-optimizer/automation/package_BOM.txt index e4080d168e1274..b488271b996c19 100644 --- a/model-optimizer/automation/package_BOM.txt +++ b/model-optimizer/automation/package_BOM.txt @@ -224,6 +224,7 @@ extensions/front/mxnet/ssd_pattern_remove_transpose.py extensions/front/mxnet/ssd_reorder_detection_out_inputs.py extensions/front/mxnet/stack_ext.py extensions/front/mxnet/swapaxis_ext.py +extensions/front/mxnet/take_ext.py extensions/front/mxnet/tile_ext.py extensions/front/mxnet/tile_replacer.py extensions/front/mxnet/transpose_ext.py diff --git a/model-optimizer/extensions/front/mxnet/take_ext.py b/model-optimizer/extensions/front/mxnet/take_ext.py new file mode 100644 index 00000000000000..590f0a3d68633d --- /dev/null +++ b/model-optimizer/extensions/front/mxnet/take_ext.py @@ -0,0 +1,33 @@ +""" + Copyright (C) 2017-2021 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +from extensions.ops.gather import AttributedGather +from mo.front.extractor import FrontExtractorOp +from mo.front.mxnet.extractors.utils import get_mxnet_layer_attrs +from mo.graph.graph import Node + + +class TakeExtractor(FrontExtractorOp): + op = 'take' + enabled = True + + @classmethod + def extract(cls, node: Node): + attrs = get_mxnet_layer_attrs(node.symbol_dict) + AttributedGather.update_node_stat(node, { + 'axis': attrs.int('axis', 0), + }) + return cls.enabled From 8abbfbc85581f7ece6c28cee9aead61d2c74ca00 Mon Sep 17 00:00:00 2001 From: Vitaliy Urusovskij Date: Fri, 29 Jan 2021 16:54:19 +0300 Subject: [PATCH 12/99] Design test config and integrate in CC tests (#4051) --- tests/conditional_compilation/conftest.py | 41 ++++++++----------- tests/conditional_compilation/test_collect.py | 4 +- tests/conditional_compilation/test_config.yml | 15 +++++++ tests/conditional_compilation/test_infer.py | 4 +- tests/lib/path_utils.py | 22 ++++++++++ 5 files changed, 59 insertions(+), 27 deletions(-) create mode 100644 tests/conditional_compilation/test_config.yml create mode 100644 tests/lib/path_utils.py diff --git a/tests/conditional_compilation/conftest.py b/tests/conditional_compilation/conftest.py index 3a0ae137029f95..881e5d273d699e 100644 --- a/tests/conditional_compilation/conftest.py +++ b/tests/conditional_compilation/conftest.py @@ -7,7 +7,7 @@ """ Pytest configuration for compilation tests. Sample usage: -python3 -m pytest --artifacts ./compiled --models_root= \ +python3 -m pytest --artifacts ./compiled --test_conf= \ --sea_runtool=./IntelSEAPI/runtool/sea_runtool.py \ --benchmark_app=./bin/benchmark_app test_collect.py """ @@ -17,32 +17,30 @@ from pathlib import Path import pytest +import yaml # add ../lib to imports sys.path.insert( 0, str((Path(getsourcefile(lambda: 0)) / ".." / ".." / "lib").resolve(strict=True)) ) -# Using models from https://github.com/openvinotoolkit/testdata -# $find models -wholename "*.xml" -TESTS = [ - {"path": "models/mobilenet_v2_1.4_224/mobilenet_v2_1.4_224_i8.xml"}, - {"path": "models/mobilenet_v2_1.0_224/mobilenet_v2_1.0_224_i8.xml"}, - {"path": "models/inception_v3/inception_v3_i8.xml"}, - {"path": "models/resnet_v1_50/resnet_v1_50_i8.xml"}, - {"path": "models/test_model/test_model_fp16.xml"}, - {"path": "models/test_model/test_model_fp32.xml"}, -] +from path_utils import expand_env_vars # pylint: disable=import-error def pytest_addoption(parser): """ Define extra options for pytest options """ parser.addoption( - "--models_root", required=True, type=Path, help="Path to models root directory" + "--test_conf", + type=Path, + default=Path(__file__).parent / "test_config.yml", + help="Path to models root directory" ) parser.addoption( - "--sea_runtool", required=True, type=Path, help="Path to sea_runtool.py" + "--sea_runtool", + required=True, + type=Path, + help="Path to sea_runtool.py" ) parser.addoption( "--benchmark_app", @@ -65,14 +63,17 @@ def pytest_generate_tests(metafunc): params = [] ids = [] - for test in TESTS: + with open(metafunc.config.getoption('test_conf'), "r") as file: + test_cases = yaml.safe_load(file) + + for test in test_cases: extra_args = {} - path = test["path"] + model_path = test["model"]["path"] if "marks" in test: extra_args["marks"] = test["marks"] - params.append(pytest.param(Path(path), **extra_args)) - ids = ids + [path] + params.append(pytest.param(Path(expand_env_vars(model_path)), **extra_args)) + ids = ids + [model_path] metafunc.parametrize("model", params, ids=ids) @@ -88,12 +89,6 @@ def benchmark_app(request): return request.config.getoption("benchmark_app") -@pytest.fixture(scope="session") -def models_root(request): - """Fixture function for command-line option.""" - return request.config.getoption("models_root") - - @pytest.fixture(scope="session") def artifacts(request): """Fixture function for command-line option.""" diff --git a/tests/conditional_compilation/test_collect.py b/tests/conditional_compilation/test_collect.py index 4088c2f7d220a8..4f85b8efcf4e6e 100644 --- a/tests/conditional_compilation/test_collect.py +++ b/tests/conditional_compilation/test_collect.py @@ -11,7 +11,7 @@ from proc_utils import cmd_exec # pylint: disable=import-error -def test_cc_collect(model, sea_runtool, benchmark_app, models_root, artifacts): +def test_cc_collect(model, sea_runtool, benchmark_app, artifacts): """ Test conditional compilation statistics collection """ out = artifacts / model.parent / model.stem @@ -29,7 +29,7 @@ def test_cc_collect(model, sea_runtool, benchmark_app, models_root, artifacts): "!", str(benchmark_app), "-d=CPU", - f"-m={models_root / model}", + f"-m={model}", "-niter=1", "-nireq=1", ] diff --git a/tests/conditional_compilation/test_config.yml b/tests/conditional_compilation/test_config.yml new file mode 100644 index 00000000000000..ac03e092fe1c14 --- /dev/null +++ b/tests/conditional_compilation/test_config.yml @@ -0,0 +1,15 @@ +# Using models from https://github.com/openvinotoolkit/testdata +# $find models -wholename "*.xml" + +- model: + path: ${TESTDATA}/models/mobilenet_v2_1.4_224/mobilenet_v2_1.4_224_i8.xml +- model: + path: ${TESTDATA}/models/mobilenet_v2_1.0_224/mobilenet_v2_1.0_224_i8.xml +- model: + path: ${TESTDATA}/models/inception_v3/inception_v3_i8.xml +- model: + path: ${TESTDATA}/models/resnet_v1_50/resnet_v1_50_i8.xml +- model: + path: ${TESTDATA}/models/test_model/test_model_fp16.xml +- model: + path: ${TESTDATA}/models/test_model/test_model_fp32.xml diff --git a/tests/conditional_compilation/test_infer.py b/tests/conditional_compilation/test_infer.py index b7d66f72015233..85df816b94fc86 100644 --- a/tests/conditional_compilation/test_infer.py +++ b/tests/conditional_compilation/test_infer.py @@ -8,10 +8,10 @@ from proc_utils import cmd_exec # pylint: disable=import-error -def test_infer(model, models_root, benchmark_app): +def test_infer(model, benchmark_app): """ Test inference with conditional compiled binaries """ returncode, _ = cmd_exec( - [str(benchmark_app), "-d=CPU", f"-m={models_root / model}", "-niter=1", "-nireq=1"] + [str(benchmark_app), "-d=CPU", f"-m={model}", "-niter=1", "-nireq=1"] ) assert returncode == 0, f"Command exited with non-zero status {returncode}" diff --git a/tests/lib/path_utils.py b/tests/lib/path_utils.py new file mode 100644 index 00000000000000..f65182761e20d0 --- /dev/null +++ b/tests/lib/path_utils.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +# Copyright (C) 2021 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +""" Common utilities for working with processes. +""" + +import os + + +def expand_env_vars(obj): + """Expand environment variables in provided object.""" + + if isinstance(obj, list): + for i, value in enumerate(obj): + obj[i] = expand_env_vars(value) + elif isinstance(obj, dict): + for name, value in obj.items(): + obj[name] = expand_env_vars(value) + else: + obj = os.path.expandvars(obj) + return obj From abaf155b4220ad6d0567f76504d22b9fbd5e3d34 Mon Sep 17 00:00:00 2001 From: Sergey Lyubimtsev Date: Sun, 31 Jan 2021 12:54:50 +0300 Subject: [PATCH 13/99] Prevent targets installation for 3rd party libs (mkl-dnn) (#4096) --- inference-engine/thirdparty/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inference-engine/thirdparty/CMakeLists.txt b/inference-engine/thirdparty/CMakeLists.txt index 041724c3cc3017..5586d900cd84ec 100644 --- a/inference-engine/thirdparty/CMakeLists.txt +++ b/inference-engine/thirdparty/CMakeLists.txt @@ -94,6 +94,6 @@ if(ENABLE_MKL_DNN) set(OpenMP_cmake_included ON) ## to skip "omp simd" inside a code. Lead to some crashes inside NDK LLVM.. endif() - add_subdirectory(mkl-dnn) + add_subdirectory(mkl-dnn EXCLUDE_FROM_ALL) add_library(mkldnn ALIAS dnnl) endif() From 8cb25fdb989ef94eb8a0f8c905cddee310d0a166 Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Mon, 1 Feb 2021 09:12:32 +0300 Subject: [PATCH 14/99] [Python API] Support of FP16 blobs (#3893) * [Python API] Support of FP16 blobs * test_Blob refactoring * support fp16 for exec_net.infer method * add precisions Co-authored-by: anastasia.kuporosova --- .../openvino/inference_engine/constants.pyx | 5 +- .../src/openvino/inference_engine/ie_api.pyx | 8 ++- .../ie_bridges/python/tests/test_Blob.py | 69 ++++--------------- 3 files changed, 24 insertions(+), 58 deletions(-) diff --git a/inference-engine/ie_bridges/python/src/openvino/inference_engine/constants.pyx b/inference-engine/ie_bridges/python/src/openvino/inference_engine/constants.pyx index 188d38940bd422..ce5ca4d6dede81 100644 --- a/inference-engine/ie_bridges/python/src/openvino/inference_engine/constants.pyx +++ b/inference-engine/ie_bridges/python/src/openvino/inference_engine/constants.pyx @@ -18,7 +18,7 @@ from .cimport ie_api_impl_defs as C import numpy as np from enum import Enum -supported_precisions = ["FP32", "FP64", "FP16", "I64", "U64", "I32", "U32", "I16", "I8", "U16", "U8"] +supported_precisions = ["FP32", "FP64", "FP16", "I64", "U64", "I32", "U32", "I16", "I8", "U16", "U8", "BOOL"] known_plugins = ['CPU', 'GPU', 'FPGA', 'MYRIAD', 'HETERO', 'HDDL', 'MULTI'] @@ -34,7 +34,8 @@ format_map = { 'U16' : np.uint16, 'I8' : np.int8, 'U8' : np.uint8, - 'I64' : np.int64 + 'I64' : np.int64, + 'BOOL' : np.uint8 } layout_str_to_enum = {'ANY': C.Layout.ANY, diff --git a/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api.pyx b/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api.pyx index 3a84f61cfa9dcc..c92040d40cf76a 100644 --- a/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api.pyx +++ b/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api.pyx @@ -173,7 +173,8 @@ cdef class Blob: fp64_array_memview = self._array_data self._ptr = C.make_shared_blob[double](c_tensor_desc, &fp64_array_memview[0], fp64_array_memview.shape[0]) elif precision == "FP16": - raise RuntimeError("Currently, it's impossible to set_blob with FP16 precision") + I16_array_memview = self._array_data.view(dtype=np.int16) + self._ptr = C.make_shared_blob[int16_t](c_tensor_desc, &I16_array_memview[0], I16_array_memview.shape[0]) elif precision == "I16": I16_array_memview = self._array_data self._ptr = C.make_shared_blob[int16_t](c_tensor_desc, &I16_array_memview[0], I16_array_memview.shape[0]) @@ -1222,7 +1223,10 @@ cdef class InferRequest: def _fill_inputs(self, inputs): for k, v in inputs.items(): assert k in self._inputs_list, f"No input with name {k} found in network" - self.input_blobs[k].buffer[:] = v + if self.input_blobs[k].tensor_desc.precision == "FP16": + self.input_blobs[k].buffer[:] = v.view(dtype=np.int16) + else: + self.input_blobs[k].buffer[:] = v ## This class contains the information about the network model read from IR and allows you to manipulate with diff --git a/inference-engine/ie_bridges/python/tests/test_Blob.py b/inference-engine/ie_bridges/python/tests/test_Blob.py index 7220f87cbd8adf..2353e60c61c293 100644 --- a/inference-engine/ie_bridges/python/tests/test_Blob.py +++ b/inference-engine/ie_bridges/python/tests/test_Blob.py @@ -36,87 +36,48 @@ def test_get_buffer(): blob = Blob(tensor_desc, array) assert np.array_equal(blob.buffer, array) - -def test_write_to_buffer_fp32(): - tensor_desc = TensorDesc("FP32", [1, 3, 127, 127], "NCHW") - array = np.zeros(shape=(1, 3, 127, 127), dtype=np.float32) +def write_to_buffer(precision, numpy_precision): + tensor_desc = TensorDesc(precision, [1, 3, 127, 127], "NCHW") + array = np.zeros(shape=(1, 3, 127, 127), dtype=numpy_precision) blob = Blob(tensor_desc, array) - ones_arr = np.ones(shape=(1, 3, 127, 127), dtype=np.float32) + ones_arr = np.ones(shape=(1, 3, 127, 127), dtype=numpy_precision) blob.buffer[:] = ones_arr assert np.array_equal(blob.buffer, ones_arr) +def test_write_to_buffer_fp32(): + write_to_buffer("FP32", np.float32) + def test_write_to_buffer_fp64(): - tensor_desc = TensorDesc("FP64", [1, 3, 127, 127], "NCHW") - array = np.zeros(shape=(1, 3, 127, 127), dtype=np.float64) - blob = Blob(tensor_desc, array) - ones_arr = np.ones(shape=(1, 3, 127, 127), dtype=np.float64) - blob.buffer[:] = ones_arr - assert np.array_equal(blob.buffer, ones_arr) + write_to_buffer("FP64", np.float64) -@pytest.mark.skip(reason="Need to figure out how to implement right conversion") def test_write_to_buffer_fp16(): - tensor_desc = TensorDesc("FP16", [1, 3, 127, 127], "NCHW") - array = np.zeros(shape=(1, 3, 127, 127), dtype=np.float16) - blob = Blob(tensor_desc, array) - ones_arr = np.ones(shape=(1, 3, 127, 127), dtype=np.float16) - blob.buffer[:] = ones_arr - assert np.array_equal(blob.buffer, ones_arr) + write_to_buffer("FP16", np.float16) def test_write_to_buffer_int8(): - tensor_desc = TensorDesc("I8", [1, 3, 127, 127], "NCHW") - array = np.zeros(shape=(1, 3, 127, 127), dtype=np.int8) - blob = Blob(tensor_desc, array) - ones_arr = np.ones(shape=(1, 3, 127, 127), dtype=np.int8) - blob.buffer[:] = ones_arr - assert np.array_equal(blob.buffer, ones_arr) + write_to_buffer("I8", np.int8) def test_write_to_buffer_uint8(): - tensor_desc = TensorDesc("U8", [1, 3, 127, 127], "NCHW") - array = np.zeros(shape=(1, 3, 127, 127), dtype=np.uint8) - blob = Blob(tensor_desc, array) - ones_arr = np.ones(shape=(1, 3, 127, 127), dtype=np.uint8) - blob.buffer[:] = ones_arr - assert np.array_equal(blob.buffer, ones_arr) + write_to_buffer("U8", np.uint8) def test_write_to_buffer_int32(): - tensor_desc = TensorDesc("I32", [1, 3, 127, 127], "NCHW") - array = np.zeros(shape=(1, 3, 127, 127), dtype=np.int32) - blob = Blob(tensor_desc, array) - ones_arr = np.ones(shape=(1, 3, 127, 127), dtype=np.int32) - blob.buffer[:] = ones_arr - assert np.array_equal(blob.buffer, ones_arr) + write_to_buffer("I32", np.int32) def test_write_to_buffer_int16(): - tensor_desc = TensorDesc("I16", [1, 3, 127, 127], "NCHW") - array = np.zeros(shape=(1, 3, 127, 127), dtype=np.int16) - blob = Blob(tensor_desc, array) - ones_arr = np.ones(shape=(1, 3, 127, 127), dtype=np.int16) - blob.buffer[:] = ones_arr - assert np.array_equal(blob.buffer, ones_arr) + write_to_buffer("I16", np.int16) def test_write_to_buffer_uint16(): - tensor_desc = TensorDesc("U16", [1, 3, 127, 127], "NCHW") - array = np.zeros(shape=(1, 3, 127, 127), dtype=np.uint16) - blob = Blob(tensor_desc, array) - ones_arr = np.ones(shape=(1, 3, 127, 127), dtype=np.uint16) - blob.buffer[:] = ones_arr - assert np.array_equal(blob.buffer, ones_arr) + write_to_buffer("U16", np.uint16) def test_write_to_buffer_int64(): - tensor_desc = TensorDesc("I64", [1, 3, 127, 127], "NCHW") - array = np.zeros(shape=(1, 3, 127, 127), dtype=np.int64) - blob = Blob(tensor_desc, array) - ones_arr = np.ones(shape=(1, 3, 127, 127), dtype=np.int64) - blob.buffer[:] = ones_arr - assert np.array_equal(blob.buffer, ones_arr) + write_to_buffer("I64", np.int64) def test_write_numpy_scalar_int64(): From a6a5635a59b6a5c5fa393c46c7a02ca7d40ddb03 Mon Sep 17 00:00:00 2001 From: Anna Likholat Date: Mon, 1 Feb 2021 11:26:03 +0300 Subject: [PATCH 15/99] added log extractor for tf (#4090) --- .../extensions/front/tf/activation_ext.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/model-optimizer/extensions/front/tf/activation_ext.py b/model-optimizer/extensions/front/tf/activation_ext.py index 83492f8754edad..d8a856260b1f7b 100644 --- a/model-optimizer/extensions/front/tf/activation_ext.py +++ b/model-optimizer/extensions/front/tf/activation_ext.py @@ -14,7 +14,7 @@ limitations under the License. """ from extensions.ops.activation_ops import Abs, Elu, Erf, Exp, ReLU, LeakyReLU, LogicalNot, ReLU6, Sigmoid, \ - Sin, Sinh, Cos, Cosh, Tan, Tanh, Ceiling, Atanh, Acosh, Asinh, Mish + Sin, Sinh, Cos, Cosh, Tan, Tanh, Ceiling, Atanh, Acosh, Asinh, Mish, Log from mo.front.extractor import FrontExtractorOp @@ -220,3 +220,13 @@ class MishExtractor(FrontExtractorOp): def extract(cls, node): Mish.update_node_stat(node) return cls.enabled + + +class LogExtractor(FrontExtractorOp): + op = 'Log' + enabled = True + + @classmethod + def extract(cls, node): + Log.update_node_stat(node) + return cls.enabled From 1a787cb3ba9b2babfe31e18e93eee343836a49a8 Mon Sep 17 00:00:00 2001 From: Eugeny Volosenkov Date: Mon, 1 Feb 2021 13:17:17 +0300 Subject: [PATCH 16/99] Re-implement caffe old-style extractors with extractor extensions (#3675) * move crop extractor * Add concat_ext.py * Add roipooling_ext.py * Add roipooling_ext * Add scale extractor * Add scale extractor * Add bn_ext.py and dropout_ext.py * Add bn_ext.py and dropout_ext.py * Add bn_ext.py and dropout_ext.py * Fix bn.ext.py * Sort fix * Fix bn_test.py * rename to batchnorm_ext * Add bn_ext * Fix batchnorm_ext.py * small fix * Small fix --- model-optimizer/automation/package_BOM.txt | 13 +- .../extensions/front/caffe/batchnorm_ext.py | 53 +++++++ model-optimizer/extensions/front/caffe/bn.py | 5 +- .../front/caffe/bn_ext.py} | 19 ++- .../extensions/front/caffe/bn_test.py | 7 +- .../extensions/front/caffe/concat_ext.py | 32 ++++ .../front/caffe/crop_ext.py} | 2 +- .../front/caffe/crop_ext_test.py} | 4 +- .../front/caffe/dropout_ext.py} | 23 +-- .../extensions/front/caffe/roipooling_ext.py | 35 +++++ .../extensions/front/caffe/scale_ext.py | 55 +++++++ model-optimizer/extensions/ops/BN.py | 35 +++++ model-optimizer/mo/front/caffe/extractor.py | 23 +-- .../mo/front/caffe/extractors/batchnorm.py | 63 -------- .../front/caffe/extractors/batchnorm_test.py | 147 ------------------ .../mo/front/caffe/extractors/concat_test.py | 37 ----- .../mo/front/caffe/extractors/scale.py | 47 ------ .../mo/front/caffe/extractors/scale_test.py | 144 ----------------- 18 files changed, 253 insertions(+), 491 deletions(-) create mode 100644 model-optimizer/extensions/front/caffe/batchnorm_ext.py rename model-optimizer/{mo/front/caffe/extractors/concat.py => extensions/front/caffe/bn_ext.py} (64%) create mode 100644 model-optimizer/extensions/front/caffe/concat_ext.py rename model-optimizer/{mo/front/caffe/extractors/crop.py => extensions/front/caffe/crop_ext.py} (96%) rename model-optimizer/{mo/front/caffe/extractors/crop_test.py => extensions/front/caffe/crop_ext_test.py} (94%) rename model-optimizer/{mo/front/caffe/extractors/roipooling.py => extensions/front/caffe/dropout_ext.py} (57%) create mode 100644 model-optimizer/extensions/front/caffe/roipooling_ext.py create mode 100644 model-optimizer/extensions/front/caffe/scale_ext.py create mode 100644 model-optimizer/extensions/ops/BN.py delete mode 100644 model-optimizer/mo/front/caffe/extractors/batchnorm.py delete mode 100644 model-optimizer/mo/front/caffe/extractors/batchnorm_test.py delete mode 100644 model-optimizer/mo/front/caffe/extractors/concat_test.py delete mode 100644 model-optimizer/mo/front/caffe/extractors/scale.py delete mode 100644 model-optimizer/mo/front/caffe/extractors/scale_test.py diff --git a/model-optimizer/automation/package_BOM.txt b/model-optimizer/automation/package_BOM.txt index b488271b996c19..d781682f3a9ae5 100644 --- a/model-optimizer/automation/package_BOM.txt +++ b/model-optimizer/automation/package_BOM.txt @@ -71,15 +71,20 @@ extensions/front/caffe/accum_ext.py extensions/front/caffe/argmax_ext.py extensions/front/caffe/ArgMaxFlatten.py extensions/front/caffe/axpy.py +extensions/front/caffe/batchnorm_ext.py extensions/front/caffe/binarization.py extensions/front/caffe/binary_conv_ext.py extensions/front/caffe/bn.py +extensions/front/caffe/bn_ext.py +extensions/front/caffe/concat_ext.py extensions/front/caffe/conv_ext.py extensions/front/caffe/correlation_ext.py +extensions/front/caffe/crop_ext.py extensions/front/caffe/ctcgreedydecoder_ext.py extensions/front/caffe/CustomLayersMapping.xml.example extensions/front/caffe/data_augmentation_ext.py extensions/front/caffe/detection_output.py +extensions/front/caffe/dropout_ext.py extensions/front/caffe/elementwise_ext.py extensions/front/caffe/eltwise_add_normalize.py extensions/front/caffe/elu.py @@ -106,6 +111,8 @@ extensions/front/caffe/relu_ext.py extensions/front/caffe/reorgyolo_ext.py extensions/front/caffe/resample_ext.py extensions/front/caffe/reshape.py +extensions/front/caffe/roipooling_ext.py +extensions/front/caffe/scale_ext.py extensions/front/caffe/shufflechannel_ext.py extensions/front/caffe/sigmoid.py extensions/front/caffe/simplernms_ext.py @@ -618,6 +625,7 @@ extensions/ops/axpy.py extensions/ops/BatchNormInference.py extensions/ops/binarization.py extensions/ops/BlockLSTM.py +extensions/ops/BN.py extensions/ops/box_nms.py extensions/ops/bucketize.py extensions/ops/Cast.py @@ -758,12 +766,7 @@ mo/front/caffe/collect_attributes.py mo/front/caffe/custom_layers_mapping.py mo/front/caffe/extractor.py mo/front/caffe/extractors/__init__.py -mo/front/caffe/extractors/batchnorm.py -mo/front/caffe/extractors/concat.py -mo/front/caffe/extractors/crop.py mo/front/caffe/extractors/native_caffe.py -mo/front/caffe/extractors/roipooling.py -mo/front/caffe/extractors/scale.py mo/front/caffe/extractors/tile.py mo/front/caffe/extractors/utils.py mo/front/caffe/loader.py diff --git a/model-optimizer/extensions/front/caffe/batchnorm_ext.py b/model-optimizer/extensions/front/caffe/batchnorm_ext.py new file mode 100644 index 00000000000000..419fb738f1ae32 --- /dev/null +++ b/model-optimizer/extensions/front/caffe/batchnorm_ext.py @@ -0,0 +1,53 @@ +""" + Copyright (C) 2018-2021 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +import numpy as np + +from extensions.ops.BatchNormInference import BatchNormInference +from mo.front.caffe.extractors.utils import embed_input +from mo.front.extractor import FrontExtractorOp + + +class BatchNormalizationExtractor(FrontExtractorOp): + op = 'batchnorm' + enabled = True + + @classmethod + def extract(cls, node): + eps = node.pb.batch_norm_param.eps + attrs = { + 'eps': eps + } + pb_model = None if not node.soft_get('model_pb', None) else node.model_pb + if pb_model: + blobs = pb_model.blobs + assert len(blobs) >= 2, 'BatchNorm accepts not less then two input blobs' + mean = np.array(blobs[0].data) + variance = np.array(blobs[1].data) + + if len(blobs) == 3: + scale = blobs[2].data[0] + if scale != 0: + scale = 1.0 / scale + mean *= scale + variance *= scale + + embed_input(attrs, 1, 'gamma', np.ones(mean.shape), 'gamma') + embed_input(attrs, 2, 'beta', np.zeros(variance.shape), 'beta') + embed_input(attrs, 3, 'mean', mean, 'biases') + embed_input(attrs, 4, 'variance', variance, 'weights') + + BatchNormInference.update_node_stat(node, attrs) + return cls.enabled diff --git a/model-optimizer/extensions/front/caffe/bn.py b/model-optimizer/extensions/front/caffe/bn.py index 3ad77c441c512e..4aa3cdc34323fa 100644 --- a/model-optimizer/extensions/front/caffe/bn.py +++ b/model-optimizer/extensions/front/caffe/bn.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ class BNToScaleShift(FrontReplacementOp): """ Replaces BN layer with ScaleShift. """ - op = "batchNormInference" + op = "BN" enabled = True def replace_op(self, graph: Graph, node: Node): @@ -35,6 +35,7 @@ def replace_op(self, graph: Graph, node: Node): param = graph.node[node.id]['pb'].bn_param pb_model = graph.node[node.id]['model_pb'] + blobs = pb_model.blobs if len(blobs) != 4: diff --git a/model-optimizer/mo/front/caffe/extractors/concat.py b/model-optimizer/extensions/front/caffe/bn_ext.py similarity index 64% rename from model-optimizer/mo/front/caffe/extractors/concat.py rename to model-optimizer/extensions/front/caffe/bn_ext.py index 1c7afba15e77e0..39f83be46065f7 100644 --- a/model-optimizer/mo/front/caffe/extractors/concat.py +++ b/model-optimizer/extensions/front/caffe/bn_ext.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,12 +14,15 @@ limitations under the License. """ -from mo.front.common.partial_infer.concat import concat_infer +from extensions.ops.BN import BN +from mo.front.extractor import FrontExtractorOp -def concat_ext(pb_layer, pb_model): - return { - 'type': "Concat", - 'axis': pb_layer.concat_param.axis, - 'infer': concat_infer - } +class BNExtractor(FrontExtractorOp): + op = 'BN' + enabled = True + + @classmethod + def extract(cls, node): + BN.update_node_stat(node, {}) + return cls.enabled diff --git a/model-optimizer/extensions/front/caffe/bn_test.py b/model-optimizer/extensions/front/caffe/bn_test.py index 37c0c3fa5b5cd4..c6be7929e05463 100644 --- a/model-optimizer/extensions/front/caffe/bn_test.py +++ b/model-optimizer/extensions/front/caffe/bn_test.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,9 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. """ -import unittest - import numpy as np +import unittest from extensions.front.caffe.bn import BNToScaleShift from mo.graph.graph import Node @@ -47,7 +46,7 @@ def test_bn(self): FakeParam('data', shift)]) nodes = [ ('input', {'kind': 'op', 'type': 'Identity', 'op': 'Identity'}), - ('bn', {'type': None, 'kind': 'op', 'op': 'batchNormInference', 'pb': bn_pb, 'model_pb': bn_bin}), + ('bn', {'type': None, 'kind': 'op', 'op': 'BN', 'pb': bn_pb, 'model_pb': bn_bin}), ('output', {'kind': 'op', 'type': 'Identity', 'op': 'Identity'}), ] edges = [ diff --git a/model-optimizer/extensions/front/caffe/concat_ext.py b/model-optimizer/extensions/front/caffe/concat_ext.py new file mode 100644 index 00000000000000..abbc85cd390f99 --- /dev/null +++ b/model-optimizer/extensions/front/caffe/concat_ext.py @@ -0,0 +1,32 @@ +""" + Copyright (C) 2018-2021 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +from mo.front.extractor import FrontExtractorOp +from mo.ops.concat import Concat + + +class ConcatFrontExtractor(FrontExtractorOp): + op = 'concat' + enabled = True + + @classmethod + def extract(cls, node): + pb = node.pb + mapping_rule = { + 'axis': pb.concat_param.axis, + } + Concat.update_node_stat(node, mapping_rule) + return cls.enabled diff --git a/model-optimizer/mo/front/caffe/extractors/crop.py b/model-optimizer/extensions/front/caffe/crop_ext.py similarity index 96% rename from model-optimizer/mo/front/caffe/extractors/crop.py rename to model-optimizer/extensions/front/caffe/crop_ext.py index 55957e56869525..073e286b4330f0 100644 --- a/model-optimizer/mo/front/caffe/extractors/crop.py +++ b/model-optimizer/extensions/front/caffe/crop_ext.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/model-optimizer/mo/front/caffe/extractors/crop_test.py b/model-optimizer/extensions/front/caffe/crop_ext_test.py similarity index 94% rename from model-optimizer/mo/front/caffe/extractors/crop_test.py rename to model-optimizer/extensions/front/caffe/crop_ext_test.py index 6476aabac3cb28..755c0c066d1ae2 100644 --- a/model-optimizer/mo/front/caffe/extractors/crop_test.py +++ b/model-optimizer/extensions/front/caffe/crop_ext_test.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ import unittest from unittest.mock import patch -from mo.front.caffe.extractors.crop import CropFrontExtractor +from extensions.front.caffe.crop_ext import CropFrontExtractor from mo.front.common.partial_infer.crop import crop_infer from mo.ops.crop import Crop from mo.ops.op import Op diff --git a/model-optimizer/mo/front/caffe/extractors/roipooling.py b/model-optimizer/extensions/front/caffe/dropout_ext.py similarity index 57% rename from model-optimizer/mo/front/caffe/extractors/roipooling.py rename to model-optimizer/extensions/front/caffe/dropout_ext.py index fd7fc4edfedd18..2737986e83155b 100644 --- a/model-optimizer/mo/front/caffe/extractors/roipooling.py +++ b/model-optimizer/extensions/front/caffe/dropout_ext.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,15 +14,16 @@ limitations under the License. """ -from mo.front.common.partial_infer.roipooling import roipooling_infer +from extensions.ops.identity import Identity +from mo.front.extractor import FrontExtractorOp +from mo.graph.graph import Node -def roipooling_ext(proto_layer, model_layer): - param = proto_layer.roi_pooling_param - return { - 'type': 'ROIPooling', - 'pooled_h': param.pooled_h, - 'pooled_w': param.pooled_w, - 'spatial_scale': param.spatial_scale, - 'infer': roipooling_infer - } +class DropoutFrontExtractor(FrontExtractorOp): + op = 'dropout' + enabled = True + + @classmethod + def extract(cls, node: Node): + Identity.update_node_stat(node, {}) + return cls.enabled diff --git a/model-optimizer/extensions/front/caffe/roipooling_ext.py b/model-optimizer/extensions/front/caffe/roipooling_ext.py new file mode 100644 index 00000000000000..76cbd4fd3925b0 --- /dev/null +++ b/model-optimizer/extensions/front/caffe/roipooling_ext.py @@ -0,0 +1,35 @@ +""" + Copyright (C) 2018-2021 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +from mo.front.extractor import FrontExtractorOp +from mo.ops.roipooling import ROIPooling + + +class ROIPoolingFrontExtractor(FrontExtractorOp): + op = 'roipooling' + enabled = True + + @classmethod + def extract(cls, node): + param = node.pb.roi_pooling_param + attrs = { + 'pooled_h': param.pooled_h, + 'pooled_w': param.pooled_w, + 'spatial_scale': param.spatial_scale, + } + + ROIPooling.update_node_stat(node, attrs) + return cls.enabled diff --git a/model-optimizer/extensions/front/caffe/scale_ext.py b/model-optimizer/extensions/front/caffe/scale_ext.py new file mode 100644 index 00000000000000..53c5a1a323b396 --- /dev/null +++ b/model-optimizer/extensions/front/caffe/scale_ext.py @@ -0,0 +1,55 @@ +""" + Copyright (C) 2018-2021 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +import numpy as np + +from mo.front.caffe.extractors.utils import embed_input, weights_biases +from mo.front.common.partial_infer.elemental import copy_shape_infer +from mo.front.extractor import FrontExtractorOp +from mo.ops.scale_shift import ScaleShiftOp +from mo.utils.utils import NamedAttrsClass + + +class ScaleFrontExtractor(FrontExtractorOp): + op = 'scale' + enabled = True + + @classmethod + def extract(cls, node): + pb = node.pb + model = node.model_pb + param = pb.scale_param + attrs = { + 'axis': param.axis, + } + + if model is None and len(pb.bottom) == 1: + # default weights and biases for scale layer if the caffemodel file doesn't contain them + model = NamedAttrsClass({'blobs': np.array([NamedAttrsClass({'data': np.array([1])}), + NamedAttrsClass({'data': np.array([0])})])}) + # scale with 1 input and 1 or 2 blobs + if model and len(model.blobs) != 0 and len(pb.bottom) == 1: + attrs.update(weights_biases(param.bias_term, model)) + # 2 inputs + bias + elif len(pb.bottom) == 2 and param.bias_term: + if model is None or len(model.blobs) == 0: + # default bias for scale layer with 2 inputs if the caffemodel file doesn't contain them + model = NamedAttrsClass({'blobs': np.array([NamedAttrsClass({'data': np.array([0])})])}) + + embed_input(attrs, 1, 'biases', model.blobs[0].data) + ScaleShiftOp.update_node_stat(node, attrs) + return cls.enabled + diff --git a/model-optimizer/extensions/ops/BN.py b/model-optimizer/extensions/ops/BN.py new file mode 100644 index 00000000000000..2e3114d4fcb408 --- /dev/null +++ b/model-optimizer/extensions/ops/BN.py @@ -0,0 +1,35 @@ +""" + Copyright (C) 2018-2021 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +from mo.graph.graph import Graph +from mo.ops.op import Op + + +class BN(Op): + """ + BN operation comes from caffe and will be replaced by BNToScaleShift FrontReplacer. + """ + op = 'BN' + enabled = False + + def __init__(self, graph: Graph, attrs: dict): + super().__init__(graph, { + 'type': None, + 'op': self.op, + 'in_ports_count': 5, + 'out_ports_count': 1, + 'infer': None + }, attrs) diff --git a/model-optimizer/mo/front/caffe/extractor.py b/model-optimizer/mo/front/caffe/extractor.py index d66dac26388de6..5570efd443206c 100644 --- a/model-optimizer/mo/front/caffe/extractor.py +++ b/model-optimizer/mo/front/caffe/extractor.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2020 Intel Corporation + Copyright (C) 2018-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,11 +14,7 @@ limitations under the License. """ -from mo.front.caffe.extractors.batchnorm import batch_norm_ext -from mo.front.caffe.extractors.concat import concat_ext from mo.front.caffe.extractors.native_caffe import native_caffe_node_extractor -from mo.front.caffe.extractors.roipooling import roipooling_ext -from mo.front.caffe.extractors.scale import scale_ext from mo.front.common.partial_infer.elemental import copy_shape_infer from mo.front.common.register_custom_ops import extension_op_extractor from mo.front.extractor import CaffePythonFrontExtractorOp @@ -36,22 +32,8 @@ def node_pb_arg(pb_extractor): Keys are names that appear as layer names in .prototxt. Full list is available here: http://caffe.berkeleyvision.org/tutorial/layers.html """ -caffe_type_extractors = { - # Common Layers - 'dropout': node_pb_arg(lambda _, __: dict(op='Dropout', infer=copy_shape_infer)), - # Normalization Layers - 'batchnorm': node_pb_arg(batch_norm_ext), - - # Activation Layers - 'scale': node_pb_arg(scale_ext), - - # Utility Layers - 'concat': node_pb_arg(concat_ext), - - # Custom, implemented in IE, Fast-RCNN-specific - 'roipooling': node_pb_arg(roipooling_ext), -} +caffe_type_extractors = {} def common_caffe_fields(node: Node) -> dict: @@ -62,6 +44,7 @@ def common_caffe_fields(node: Node) -> dict: if isinstance(layer_type, int): layer_type = pb.LayerType.DESCRIPTOR.values_by_number[layer_type].name layer_type = str(layer_type) + return { 'kind': 'op', 'name': pb.name, diff --git a/model-optimizer/mo/front/caffe/extractors/batchnorm.py b/model-optimizer/mo/front/caffe/extractors/batchnorm.py deleted file mode 100644 index 02bb833aa0d6e9..00000000000000 --- a/model-optimizer/mo/front/caffe/extractors/batchnorm.py +++ /dev/null @@ -1,63 +0,0 @@ -""" - Copyright (C) 2018-2020 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -""" - -import numpy as np - -from mo.front.caffe.extractors.utils import embed_input -from mo.front.common.partial_infer.elemental import copy_shape_infer - - -def batch_norm_ext(pb_layer, pb_model): - """ - Extracts properties of the BatchNorm layer. - In case of scale, scale is merged into mean and variance - Args: - pb_layer: proto layer, contains own properties of the layer, i.e epsilon - pb_model: caffemodel layer, contains blobs with 0: mean, 1: variance, (opt)2: scale - - Returns: - attrs object with type, partial inference function and mean/variance properties. - """ - assert pb_layer, 'Protobuf layer can not be empty' - param = pb_layer.batch_norm_param - attrs = { - 'op': 'BatchNormalization', - 'type': 'BatchNormalization', - 'eps': param.eps, - 'infer': copy_shape_infer - } - - if not pb_model: - return attrs - - blobs = pb_model.blobs - assert len(blobs) >= 2, 'BatchNorm accepts not less then two input blobs' - mean = np.array(blobs[0].data) - variance = np.array(blobs[1].data) - - if len(blobs) == 3: - scale = blobs[2].data[0] - if scale != 0: - scale = 1.0 / scale - mean *= scale - variance *= scale - - embed_input(attrs, 1, 'gamma', np.ones(mean.shape), 'gamma') - embed_input(attrs, 2, 'beta', np.zeros(variance.shape), 'beta') - embed_input(attrs, 3, 'mean', mean, 'biases') - embed_input(attrs, 4, 'variance', variance, 'weights') - - return attrs diff --git a/model-optimizer/mo/front/caffe/extractors/batchnorm_test.py b/model-optimizer/mo/front/caffe/extractors/batchnorm_test.py deleted file mode 100644 index b852cdeff9c6b9..00000000000000 --- a/model-optimizer/mo/front/caffe/extractors/batchnorm_test.py +++ /dev/null @@ -1,147 +0,0 @@ -""" - Copyright (C) 2018-2020 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -""" - -import unittest - -import numpy as np - -from mo.front.caffe.extractors.batchnorm import batch_norm_ext -from mo.front.common.partial_infer.elemental import copy_shape_infer -from mo.utils.unittest.extractors import FakeParam, FakeModelLayer - - -class FakeBNProtoLayer: - def __init__(self, eps): - self.batch_norm_param = FakeParam('eps', eps) - - -class TestShapesParsing(unittest.TestCase): - def test_bn_ext_no_ml_no_pb(self): - self.assertRaises(AssertionError, batch_norm_ext, None, None) - - def test_bn_ext_no_ml(self): - res = batch_norm_ext(FakeBNProtoLayer(10), None) - exp_res = { - 'op': 'BatchNormalization', - 'type': 'BatchNormalization', - 'eps': 10, - 'infer': copy_shape_infer - } - self.assertEqual(res, exp_res) - - def test_bn_ext_ml_one_blob(self): - self.assertRaises(AssertionError, batch_norm_ext, FakeBNProtoLayer(10), FakeModelLayer([np.array([1, 2])])) - - def test_bn_ext_ml_two_blobs(self): - mean_blob = np.array([1., 2.]) - variance_blob = np.array([3., 4.]) - blobs = [mean_blob, variance_blob] - res = batch_norm_ext(FakeBNProtoLayer(10), - FakeModelLayer(blobs)) - exp_res = { - 'type': 'BatchNormalization', - 'eps': 10, - 'infer': copy_shape_infer, - 'mean': mean_blob, - 'variance': variance_blob, - 'embedded_inputs': [ - (1, 'gamma', { - 'bin': 'gamma' - }), - (2, 'beta', { - 'bin': 'beta' - }), - (3, 'mean', { - 'bin': 'biases' - }), - (4, 'variance', { - 'bin': 'weights' - }) - ] - } - for i in exp_res: - if i in ('mean', 'variance'): - np.testing.assert_array_equal(res[i], exp_res[i]) - else: - self.assertEqual(res[i], exp_res[i]) - - def test_bn_ext_ml_three_blobs(self): - mean_blob = np.array([1., 2.]) - variance_blob = np.array([3., 4.]) - scale_blob = np.array([5., ]) - blobs = [mean_blob, variance_blob, scale_blob] - res = batch_norm_ext(FakeBNProtoLayer(10), - FakeModelLayer(blobs)) - exp_res = { - 'type': 'BatchNormalization', - 'eps': 10, - 'infer': copy_shape_infer, - 'mean': mean_blob * 0.2, - 'variance': variance_blob * 0.2, - 'embedded_inputs': [ - (1, 'gamma', { - 'bin': 'gamma' - }), - (2, 'beta', { - 'bin': 'beta' - }), - (3, 'mean', { - 'bin': 'biases' - }), - (4, 'variance', { - 'bin': 'weights' - }) - ] - } - for i in exp_res: - if i in ('mean', 'variance'): - np.testing.assert_array_equal(res[i], exp_res[i]) - else: - self.assertEqual(res[i], exp_res[i]) - - def test_bn_ext_ml_three_blobs_zero_scale(self): - mean_blob = np.array([1., 2.]) - variance_blob = np.array([3., 4.]) - scale_blob = np.array([0., ]) - blobs = [mean_blob, variance_blob, scale_blob] - res = batch_norm_ext(FakeBNProtoLayer(10), - FakeModelLayer(blobs)) - exp_res = { - 'type': 'BatchNormalization', - 'eps': 10, - 'infer': copy_shape_infer, - 'mean': mean_blob * 0., - 'variance': variance_blob * 0., - 'embedded_inputs': [ - (1, 'gamma', { - 'bin': 'gamma' - }), - (2, 'beta', { - 'bin': 'beta' - }), - (3, 'mean', { - 'bin': 'biases' - }), - (4, 'variance', { - 'bin': 'weights' - }) - ] - } - for i in exp_res: - if i in ('mean', 'variance'): - np.testing.assert_array_equal(res[i], exp_res[i]) - else: - self.assertEqual(res[i], exp_res[i]) \ No newline at end of file diff --git a/model-optimizer/mo/front/caffe/extractors/concat_test.py b/model-optimizer/mo/front/caffe/extractors/concat_test.py deleted file mode 100644 index 1840c029740455..00000000000000 --- a/model-optimizer/mo/front/caffe/extractors/concat_test.py +++ /dev/null @@ -1,37 +0,0 @@ -""" - Copyright (C) 2018-2020 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -""" - -import unittest - -from mo.front.caffe.extractors.concat import concat_ext -from mo.front.common.partial_infer.concat import concat_infer -from mo.utils.unittest.extractors import FakeParam - - -class FakeProtoLayer: - def __init__(self, axis): - self.concat_param = FakeParam('axis', axis) - - -class TestConcat(unittest.TestCase): - def test_concat(self): - res = concat_ext(FakeProtoLayer(10), None) - exp_res = { - 'axis': 10, - 'infer': concat_infer, - 'type': 'Concat' - } - self.assertEqual(res, exp_res) diff --git a/model-optimizer/mo/front/caffe/extractors/scale.py b/model-optimizer/mo/front/caffe/extractors/scale.py deleted file mode 100644 index 59a2efd3a4b8b7..00000000000000 --- a/model-optimizer/mo/front/caffe/extractors/scale.py +++ /dev/null @@ -1,47 +0,0 @@ -""" - Copyright (C) 2018-2020 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -""" - -import numpy as np - -from mo.front.caffe.extractors.utils import embed_input, weights_biases -from mo.front.common.partial_infer.elemental import copy_shape_infer -from mo.utils.utils import NamedAttrsClass - - -def scale_ext(pl, ml): - param = pl.scale_param - attrs = { - 'op': 'ScaleShift', - 'type': 'ScaleShift', - 'axis': param.axis, - 'infer': copy_shape_infer - } - if ml is None and len(pl.bottom) == 1: - # default weights and biases for scale layer if the caffemodel file doesn't contain them - ml = NamedAttrsClass({'blobs': np.array([NamedAttrsClass({'data': np.array([1])}), - NamedAttrsClass({'data': np.array([0])})])}) - # scale with 1 input and 1 or 2 blobs - if ml and len(ml.blobs) != 0 and len(pl.bottom) == 1: - attrs.update(weights_biases(param.bias_term, ml)) - # 2 inputs + bias - elif len(pl.bottom) == 2 and param.bias_term: - if ml is None or len(ml.blobs) == 0: - # default bias for scale layer with 2 inputs if the caffemodel file doesn't contain them - ml = NamedAttrsClass({'blobs': np.array([NamedAttrsClass({'data': np.array([0])})])}) - - embed_input(attrs, 1, 'biases', ml.blobs[0].data) - - return attrs diff --git a/model-optimizer/mo/front/caffe/extractors/scale_test.py b/model-optimizer/mo/front/caffe/extractors/scale_test.py deleted file mode 100644 index f1c1baf84c69c9..00000000000000 --- a/model-optimizer/mo/front/caffe/extractors/scale_test.py +++ /dev/null @@ -1,144 +0,0 @@ -""" - Copyright (C) 2018-2020 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -""" - -import unittest - -import numpy as np - -from mo.front.caffe.extractors.scale import scale_ext -from mo.front.common.partial_infer.elemental import copy_shape_infer -from mo.utils.unittest.extractors import FakeMultiParam, FakeModelLayer - - -class FakeProtoLayer: - def __init__(self, val, bottom2=False): - self.scale_param = val - if bottom2: - self.bottom = {"bottom1", "bottom2"} - else: - self.bottom = {"bottom1"} - - -class TestScale(unittest.TestCase): - def test_scale_ext(self): - mean_blob = np.array([1., 2.]) - variance_blob = np.array([3., 4.]) - blobs = [mean_blob, variance_blob] - params = { - 'type': 'Scale', - 'axis': 0, - 'bias_term': True - } - - res = scale_ext(FakeProtoLayer(FakeMultiParam(params)), FakeModelLayer(blobs)) - exp_res = { - 'op': 'ScaleShift', - 'type': 'ScaleShift', - 'axis': 0, - 'infer': copy_shape_infer, - 'weights': mean_blob, - 'biases': variance_blob, - 'embedded_inputs': [ - (1, 'weights', { - 'bin': 'weights' - }), - (2, 'biases', { - 'bin': 'biases' - }) - ] - } - for i in exp_res: - if i in ('weights', 'biases'): - np.testing.assert_array_equal(res[i], exp_res[i]) - else: - self.assertEqual(res[i], exp_res[i]) - - def test_scale_2inputs_ext(self): - params = { - 'type': 'Scale', - 'axis': 0, - 'bias_term': False - } - - res = scale_ext(FakeProtoLayer(FakeMultiParam(params), True), None) - exp_res = { - 'op': 'ScaleShift', - 'type': 'ScaleShift', - 'axis': 0, - 'infer': copy_shape_infer, - } - for i in exp_res: - self.assertEqual(res[i], exp_res[i]) - - def test_scale_2inputs_bias_ext(self): - variance_blob = np.array([3., 4.]) - blobs = [variance_blob] - - params = { - 'type': 'Scale', - 'axis': 0, - 'bias_term': True - } - - res = scale_ext(FakeProtoLayer(FakeMultiParam(params), True), FakeModelLayer(blobs)) - exp_res = { - 'op': 'ScaleShift', - 'type': 'ScaleShift', - 'axis': 0, - 'infer': copy_shape_infer, - 'biases': variance_blob, - 'embedded_inputs': [ - (1, 'biases', { - 'bin': 'biases' - })] - } - for i in exp_res: - if i in ('biases'): - np.testing.assert_array_equal(res[i], exp_res[i]) - else: - self.assertEqual(res[i], exp_res[i]) - - def test_create_default_weights(self): - """ - There are situations when scale layer doesn't have weights and biases. This test checks that if they are not - available in the caffemodel file then default values [1] and [0] are generated. - """ - scale_blob = np.array([1]) - bias_blob = np.array([0]) - params = { - 'type': 'Scale', - 'axis': 0, - 'bias_term': True - } - - res = scale_ext(FakeProtoLayer(FakeMultiParam(params)), None) - exp_res = { - 'op': 'ScaleShift', - 'type': 'ScaleShift', - 'axis': 0, - 'infer': copy_shape_infer, - 'weights': scale_blob, - 'biases': bias_blob, - 'embedded_inputs': [ - (1, 'weights', { - 'bin': 'weights' - }), - (2, 'biases', { - 'bin': 'biases' - }) - ] - } - self.assertDictEqual(exp_res, res) From 80781ff359f0b7bfd657cd3a78911c4ec108931b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ewa=20Tusie=C5=84?= Date: Mon, 1 Feb 2021 12:39:39 +0100 Subject: [PATCH 17/99] Add MVN-6 op to ONNX importer (#4012) --- .../src/op/mean_variance_normalization.cpp | 9 ++- ngraph/test/models/onnx/mvn_v6.prototxt | 57 +++++++++++++++++++ ngraph/test/onnx/onnx_import.in.cpp | 20 +++++++ ngraph/test/runtime/ie/unit_test.manifest | 1 + 4 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 ngraph/test/models/onnx/mvn_v6.prototxt diff --git a/ngraph/frontend/onnx_import/src/op/mean_variance_normalization.cpp b/ngraph/frontend/onnx_import/src/op/mean_variance_normalization.cpp index 55abf537ba9852..4e2e7ea7cce7fa 100644 --- a/ngraph/frontend/onnx_import/src/op/mean_variance_normalization.cpp +++ b/ngraph/frontend/onnx_import/src/op/mean_variance_normalization.cpp @@ -49,11 +49,14 @@ namespace ngraph OutputVector mean_variance_normalization(const Node& node) { auto data = node.get_ng_inputs().at(0); - auto axes = node.get_attribute_value>("axes", {0, 2, 3}); + auto axes = + node.get_attribute_value>("axes", {0, 2, 3}); const std::vector normalized_axes = ngraph::normalize_axes( node.get_description(), axes, data.get_partial_shape().rank()); - - return {std::make_shared(data, AxisSet(normalized_axes))}; + auto const_axes = default_opset::Constant::create( + element::i64, Shape{normalized_axes.size()}, normalized_axes); + return {std::make_shared( + data, const_axes, true, 1e-09, ngraph::op::MVNEpsMode::OUTSIDE_SQRT)}; } } // namespace set_9 diff --git a/ngraph/test/models/onnx/mvn_v6.prototxt b/ngraph/test/models/onnx/mvn_v6.prototxt new file mode 100644 index 00000000000000..8dc05acc8d4910 --- /dev/null +++ b/ngraph/test/models/onnx/mvn_v6.prototxt @@ -0,0 +1,57 @@ +ir_version: 4 +producer_name: "backend-test" +graph { + node { + input: "X" + output: "Y" + op_type: "MeanVarianceNormalization" + } + name: "test_mvn" + input { + name: "X" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 3 + } + dim { + dim_value: 3 + } + dim { + dim_value: 3 + } + dim { + dim_value: 1 + } + } + } + } + } + output { + name: "Y" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 3 + } + dim { + dim_value: 3 + } + dim { + dim_value: 3 + } + dim { + dim_value: 1 + } + } + } + } + } +} +opset_import { + version: 10 +} diff --git a/ngraph/test/onnx/onnx_import.in.cpp b/ngraph/test/onnx/onnx_import.in.cpp index d832a127b9bd3b..ec0a046fdc1dc3 100644 --- a/ngraph/test/onnx/onnx_import.in.cpp +++ b/ngraph/test/onnx/onnx_import.in.cpp @@ -3946,3 +3946,23 @@ NGRAPH_TEST(${BACKEND_NAME}, onnx_clip_inbounds) test_case.add_expected_output(Shape{data.size()}, data); test_case.run(); } + +NGRAPH_TEST(${BACKEND_NAME}, onnx_mvn_v6) +{ + auto function = onnx_import::import_onnx_model( + file_util::path_join(SERIALIZED_ZOO, "onnx/mvn_v6.prototxt")); + + auto test_case = test::TestCase(function); + test_case.add_input( + {0.8439683, 0.5665144, 0.05836735, 0.02916367, 0.12964272, 0.5060197, 0.79538304, + 0.9411346, 0.9546573, 0.17730942, 0.46192095, 0.26480448, 0.6746842, 0.01665257, + 0.62473077, 0.9240844, 0.9722341, 0.11965699, 0.41356155, 0.9129373, 0.59330076, + 0.81929934, 0.7862604, 0.11799799, 0.69248444, 0.54119414, 0.07513223}); + test_case.add_expected_output( + Shape{3, 3, 3, 1}, + {1.3546423, 0.33053496, -1.5450814, -1.2106764, -0.8925952, 0.29888135, 0.38083088, + 0.81808794, 0.85865635, -1.1060555, -0.05552877, -0.78310335, 0.83281356, -1.250282, + 0.67467856, 0.7669372, 0.9113869, -1.6463585, -0.23402764, 1.6092131, 0.42940593, + 1.2906139, 1.1860244, -0.92945826, 0.0721334, -0.38174, -1.7799333}); + test_case.run(); +} diff --git a/ngraph/test/runtime/ie/unit_test.manifest b/ngraph/test/runtime/ie/unit_test.manifest index 8353a2e1f95f0d..24f55e606c4c8f 100644 --- a/ngraph/test/runtime/ie/unit_test.manifest +++ b/ngraph/test/runtime/ie/unit_test.manifest @@ -1618,3 +1618,4 @@ evaluate_mvn_6 evaluate_mvn_6_inside_sqrt evaluate_mvn_6_across_chanells evaluate_mvn_6_across_batch +IE_CPU.onnx_mvn_v6 From 4aa6f6a1686d7acebc7f3caf35cd55d6687b0563 Mon Sep 17 00:00:00 2001 From: Nikolay Tyukaev Date: Mon, 1 Feb 2021 14:57:55 +0300 Subject: [PATCH 18/99] docs copy code button (#4017) * copy code button * copy code button updates --- docs/doxygen/assets/bootstrap.bundle.min.js | 8 ++++ docs/doxygen/assets/bootstrap.min.css | 7 ++++ docs/doxygen/assets/customdoxygen.css | 34 ++++++++++++--- docs/doxygen/assets/openvino-layout.js | 46 +++++++++++++++++++++ docs/doxygen/header.html.in | 5 ++- 5 files changed, 93 insertions(+), 7 deletions(-) create mode 100644 docs/doxygen/assets/bootstrap.bundle.min.js create mode 100644 docs/doxygen/assets/bootstrap.min.css diff --git a/docs/doxygen/assets/bootstrap.bundle.min.js b/docs/doxygen/assets/bootstrap.bundle.min.js new file mode 100644 index 00000000000000..6952361b1b2a0b --- /dev/null +++ b/docs/doxygen/assets/bootstrap.bundle.min.js @@ -0,0 +1,8 @@ +/*! + * Bootstrap v4.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("jquery")):"function"==typeof define&&define.amd?define(["exports","jquery"],t):t((e=e||self).bootstrap={},e.jQuery)}(this,function(e,p){"use strict";function i(e,t){for(var n=0;nthis._items.length-1||e<0))if(this._isSliding)p(this._element).one(V.SLID,function(){return t.to(e)});else{if(n===e)return this.pause(),void this.cycle();var i=n=i.clientWidth&&n>=i.clientHeight}),u=0l[e]&&!i.escapeWithReference&&(n=Math.min(h[t],l[e]-("right"===e?h.width:h.height))),Ye({},t,n)}};return c.forEach(function(e){var t=-1!==["left","top"].indexOf(e)?"primary":"secondary";h=ze({},h,u[t](e))}),e.offsets.popper=h,e},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,n=t.popper,i=t.reference,o=e.placement.split("-")[0],r=Math.floor,s=-1!==["top","bottom"].indexOf(o),a=s?"right":"bottom",l=s?"left":"top",c=s?"width":"height";return n[a]r(i[a])&&(e.offsets.popper[l]=r(i[a])),e}},arrow:{order:500,enabled:!0,fn:function(e,t){var n;if(!gt(e.instance.modifiers,"arrow","keepTogether"))return e;var i=t.element;if("string"==typeof i){if(!(i=e.instance.popper.querySelector(i)))return e}else if(!e.instance.popper.contains(i))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),e;var o=e.placement.split("-")[0],r=e.offsets,s=r.popper,a=r.reference,l=-1!==["left","right"].indexOf(o),c=l?"height":"width",h=l?"Top":"Left",u=h.toLowerCase(),f=l?"left":"top",d=l?"bottom":"right",p=nt(i)[c];a[d]-ps[d]&&(e.offsets.popper[u]+=a[u]+p-s[d]),e.offsets.popper=Xe(e.offsets.popper);var m=a[u]+a[c]/2-p/2,g=ke(e.instance.popper),_=parseFloat(g["margin"+h],10),v=parseFloat(g["border"+h+"Width"],10),y=m-e.offsets.popper[u]-_-v;return y=Math.max(Math.min(s[c]-p,y),0),e.arrowElement=i,e.offsets.arrow=(Ye(n={},u,Math.round(y)),Ye(n,f,""),n),e},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(m,g){if(at(m.instance.modifiers,"inner"))return m;if(m.flipped&&m.placement===m.originalPlacement)return m;var _=Ze(m.instance.popper,m.instance.reference,g.padding,g.boundariesElement,m.positionFixed),v=m.placement.split("-")[0],y=it(v),E=m.placement.split("-")[1]||"",b=[];switch(g.behavior){case Et:b=[v,y];break;case bt:b=yt(v);break;case wt:b=yt(v,!0);break;default:b=g.behavior}return b.forEach(function(e,t){if(v!==e||b.length===t+1)return m;v=m.placement.split("-")[0],y=it(v);var n=m.offsets.popper,i=m.offsets.reference,o=Math.floor,r="left"===v&&o(n.right)>o(i.left)||"right"===v&&o(n.left)o(i.top)||"bottom"===v&&o(n.top)o(_.right),l=o(n.top)o(_.bottom),h="left"===v&&s||"right"===v&&a||"top"===v&&l||"bottom"===v&&c,u=-1!==["top","bottom"].indexOf(v),f=!!g.flipVariations&&(u&&"start"===E&&s||u&&"end"===E&&a||!u&&"start"===E&&l||!u&&"end"===E&&c),d=!!g.flipVariationsByContent&&(u&&"start"===E&&a||u&&"end"===E&&s||!u&&"start"===E&&c||!u&&"end"===E&&l),p=f||d;(r||h||p)&&(m.flipped=!0,(r||h)&&(v=b[t+1]),p&&(E=function(e){return"end"===e?"start":"start"===e?"end":e}(E)),m.placement=v+(E?"-"+E:""),m.offsets.popper=ze({},m.offsets.popper,ot(m.instance.popper,m.offsets.reference,m.placement)),m=st(m.instance.modifiers,m,"flip"))}),m},behavior:"flip",padding:5,boundariesElement:"viewport",flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,n=t.split("-")[0],i=e.offsets,o=i.popper,r=i.reference,s=-1!==["left","right"].indexOf(n),a=-1===["top","left"].indexOf(n);return o[s?"left":"top"]=r[n]-(a?o[s?"width":"height"]:0),e.placement=it(t),e.offsets.popper=Xe(o),e}},hide:{order:800,enabled:!0,fn:function(e){if(!gt(e.instance.modifiers,"hide","preventOverflow"))return e;var t=e.offsets.reference,n=rt(e.instance.modifiers,function(e){return"preventOverflow"===e.name}).boundaries;if(t.bottomn.right||t.top>n.bottom||t.rightdocument.documentElement.clientHeight;!this._isBodyOverflowing&&e&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!e&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var e=document.body.getBoundingClientRect();this._isBodyOverflowing=e.left+e.right
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:Cn,popperConfig:null},Fn="show",Mn="out",Wn={HIDE:"hide"+Nn,HIDDEN:"hidden"+Nn,SHOW:"show"+Nn,SHOWN:"shown"+Nn,INSERTED:"inserted"+Nn,CLICK:"click"+Nn,FOCUSIN:"focusin"+Nn,FOCUSOUT:"focusout"+Nn,MOUSEENTER:"mouseenter"+Nn,MOUSELEAVE:"mouseleave"+Nn},Un="fade",Bn="show",qn=".tooltip-inner",Kn=".arrow",Qn="hover",Vn="focus",Yn="click",zn="manual",Xn=function(){function i(e,t){if("undefined"==typeof St)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=e,this.config=this._getConfig(t),this.tip=null,this._setListeners()}var e=i.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(e){if(this._isEnabled)if(e){var t=this.constructor.DATA_KEY,n=p(e.currentTarget).data(t);n||(n=new this.constructor(e.currentTarget,this._getDelegateConfig()),p(e.currentTarget).data(t,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(p(this.getTipElement()).hasClass(Bn))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),p.removeData(this.element,this.constructor.DATA_KEY),p(this.element).off(this.constructor.EVENT_KEY),p(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&p(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===p(this.element).css("display"))throw new Error("Please use show on visible elements");var e=p.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){p(this.element).trigger(e);var n=m.findShadowRoot(this.element),i=p.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!i)return;var o=this.getTipElement(),r=m.getUID(this.constructor.NAME);o.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&p(o).addClass(Un);var s="function"==typeof this.config.placement?this.config.placement.call(this,o,this.element):this.config.placement,a=this._getAttachment(s);this.addAttachmentClass(a);var l=this._getContainer();p(o).data(this.constructor.DATA_KEY,this),p.contains(this.element.ownerDocument.documentElement,this.tip)||p(o).appendTo(l),p(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new St(this.element,o,this._getPopperConfig(a)),p(o).addClass(Bn),"ontouchstart"in document.documentElement&&p(document.body).children().on("mouseover",null,p.noop);var c=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,p(t.element).trigger(t.constructor.Event.SHOWN),e===Mn&&t._leave(null,t)};if(p(this.tip).hasClass(Un)){var h=m.getTransitionDurationFromElement(this.tip);p(this.tip).one(m.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},e.hide=function(e){function t(){n._hoverState!==Fn&&i.parentNode&&i.parentNode.removeChild(i),n._cleanTipClass(),n.element.removeAttribute("aria-describedby"),p(n.element).trigger(n.constructor.Event.HIDDEN),null!==n._popper&&n._popper.destroy(),e&&e()}var n=this,i=this.getTipElement(),o=p.Event(this.constructor.Event.HIDE);if(p(this.element).trigger(o),!o.isDefaultPrevented()){if(p(i).removeClass(Bn),"ontouchstart"in document.documentElement&&p(document.body).children().off("mouseover",null,p.noop),this._activeTrigger[Yn]=!1,this._activeTrigger[Vn]=!1,this._activeTrigger[Qn]=!1,p(this.tip).hasClass(Un)){var r=m.getTransitionDurationFromElement(i);p(i).one(m.TRANSITION_END,t).emulateTransitionEnd(r)}else t();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(e){p(this.getTipElement()).addClass(Ln+"-"+e)},e.getTipElement=function(){return this.tip=this.tip||p(this.config.template)[0],this.tip},e.setContent=function(){var e=this.getTipElement();this.setElementContent(p(e.querySelectorAll(qn)),this.getTitle()),p(e).removeClass(Un+" "+Bn)},e.setElementContent=function(e,t){"object"!=typeof t||!t.nodeType&&!t.jquery?this.config.html?(this.config.sanitize&&(t=In(t,this.config.whiteList,this.config.sanitizeFn)),e.html(t)):e.text(t):this.config.html?p(t).parent().is(e)||e.empty().append(t):e.text(p(t).text())},e.getTitle=function(){var e=this.element.getAttribute("data-original-title");return e=e||("function"==typeof this.config.title?this.config.title.call(this.element):this.config.title)},e._getPopperConfig=function(e){var t=this;return l({},{placement:e,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:Kn},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(e){e.originalPlacement!==e.placement&&t._handlePopperPlacementChange(e)},onUpdate:function(e){return t._handlePopperPlacementChange(e)}},{},this.config.popperConfig)},e._getOffset=function(){var t=this,e={};return"function"==typeof this.config.offset?e.fn=function(e){return e.offsets=l({},e.offsets,{},t.config.offset(e.offsets,t.element)||{}),e}:e.offset=this.config.offset,e},e._getContainer=function(){return!1===this.config.container?document.body:m.isElement(this.config.container)?p(this.config.container):p(document).find(this.config.container)},e._getAttachment=function(e){return Hn[e.toUpperCase()]},e._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(e){if("click"===e)p(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(e){return i.toggle(e)});else if(e!==zn){var t=e===Qn?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=e===Qn?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;p(i.element).on(t,i.config.selector,function(e){return i._enter(e)}).on(n,i.config.selector,function(e){return i._leave(e)})}}),this._hideModalHandler=function(){i.element&&i.hide()},p(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var e=typeof this.element.getAttribute("data-original-title");!this.element.getAttribute("title")&&"string"==e||(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(e,t){var n=this.constructor.DATA_KEY;(t=t||p(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),p(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusin"===e.type?Vn:Qn]=!0),p(t.getTipElement()).hasClass(Bn)||t._hoverState===Fn?t._hoverState=Fn:(clearTimeout(t._timeout),t._hoverState=Fn,t.config.delay&&t.config.delay.show?t._timeout=setTimeout(function(){t._hoverState===Fn&&t.show()},t.config.delay.show):t.show())},e._leave=function(e,t){var n=this.constructor.DATA_KEY;(t=t||p(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),p(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusout"===e.type?Vn:Qn]=!1),t._isWithActiveTrigger()||(clearTimeout(t._timeout),t._hoverState=Mn,t.config.delay&&t.config.delay.hide?t._timeout=setTimeout(function(){t._hoverState===Mn&&t.hide()},t.config.delay.hide):t.hide())},e._isWithActiveTrigger=function(){for(var e in this._activeTrigger)if(this._activeTrigger[e])return!0;return!1},e._getConfig=function(e){var t=p(this.element).data();return Object.keys(t).forEach(function(e){-1!==xn.indexOf(e)&&delete t[e]}),"number"==typeof(e=l({},this.constructor.Default,{},t,{},"object"==typeof e&&e?e:{})).delay&&(e.delay={show:e.delay,hide:e.delay}),"number"==typeof e.title&&(e.title=e.title.toString()),"number"==typeof e.content&&(e.content=e.content.toString()),m.typeCheckConfig(An,e,this.constructor.DefaultType),e.sanitize&&(e.template=In(e.template,e.whiteList,e.sanitizeFn)),e},e._getDelegateConfig=function(){var e={};if(this.config)for(var t in this.config)this.constructor.Default[t]!==this.config[t]&&(e[t]=this.config[t]);return e},e._cleanTipClass=function(){var e=p(this.getTipElement()),t=e.attr("class").match(Pn);null!==t&&t.length&&e.removeClass(t.join(""))},e._handlePopperPlacementChange=function(e){var t=e.instance;this.tip=t.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(e.placement))},e._fixTransition=function(){var e=this.getTipElement(),t=this.config.animation;null===e.getAttribute("x-placement")&&(p(e).removeClass(Un),this.config.animation=!1,this.hide(),this.show(),this.config.animation=t)},i._jQueryInterface=function(n){return this.each(function(){var e=p(this).data(On),t="object"==typeof n&&n;if((e||!/dispose|hide/.test(n))&&(e||(e=new i(this,t),p(this).data(On,e)),"string"==typeof n)){if("undefined"==typeof e[n])throw new TypeError('No method named "'+n+'"');e[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.4.1"}},{key:"Default",get:function(){return Rn}},{key:"NAME",get:function(){return An}},{key:"DATA_KEY",get:function(){return On}},{key:"Event",get:function(){return Wn}},{key:"EVENT_KEY",get:function(){return Nn}},{key:"DefaultType",get:function(){return jn}}]),i}();p.fn[An]=Xn._jQueryInterface,p.fn[An].Constructor=Xn,p.fn[An].noConflict=function(){return p.fn[An]=kn,Xn._jQueryInterface};var Gn="popover",$n="bs.popover",Jn="."+$n,Zn=p.fn[Gn],ei="bs-popover",ti=new RegExp("(^|\\s)"+ei+"\\S+","g"),ni=l({},Xn.Default,{placement:"right",trigger:"click",content:"",template:''}),ii=l({},Xn.DefaultType,{content:"(string|element|function)"}),oi="fade",ri="show",si=".popover-header",ai=".popover-body",li={HIDE:"hide"+Jn,HIDDEN:"hidden"+Jn,SHOW:"show"+Jn,SHOWN:"shown"+Jn,INSERTED:"inserted"+Jn,CLICK:"click"+Jn,FOCUSIN:"focusin"+Jn,FOCUSOUT:"focusout"+Jn,MOUSEENTER:"mouseenter"+Jn,MOUSELEAVE:"mouseleave"+Jn},ci=function(e){function i(){return e.apply(this,arguments)||this}!function(e,t){e.prototype=Object.create(t.prototype),(e.prototype.constructor=e).__proto__=t}(i,e);var t=i.prototype;return t.isWithContent=function(){return this.getTitle()||this._getContent()},t.addAttachmentClass=function(e){p(this.getTipElement()).addClass(ei+"-"+e)},t.getTipElement=function(){return this.tip=this.tip||p(this.config.template)[0],this.tip},t.setContent=function(){var e=p(this.getTipElement());this.setElementContent(e.find(si),this.getTitle());var t=this._getContent();"function"==typeof t&&(t=t.call(this.element)),this.setElementContent(e.find(ai),t),e.removeClass(oi+" "+ri)},t._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},t._cleanTipClass=function(){var e=p(this.getTipElement()),t=e.attr("class").match(ti);null!==t&&0=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||ecode{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#0069d9;border-color:#0062cc;box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{color:#fff;background-color:#5a6268;border-color:#545b62;box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#218838;border-color:#1e7e34;box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#138496;border-color:#117a8b;box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{color:#212529;background-color:#e0a800;border-color:#d39e00;box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c82333;border-color:#bd2130;box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{color:#212529;background-color:#e2e6ea;border-color:#dae0e5;box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{color:#fff;background-color:#23272b;border-color:#1d2124;box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 0%;flex:1 1 0%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before,.custom-control-input[disabled]~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img,.card-img-bottom,.card-img-top{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal .list-group-item.active{margin-top:0}.list-group-horizontal .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm .list-group-item.active{margin-top:0}.list-group-horizontal-sm .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md .list-group-item.active{margin-top:0}.list-group-horizontal-md .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg .list-group-item.active{margin-top:0}.list-group-horizontal-lg .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl .list-group-item.active{margin-top:0}.list-group-horizontal-xl .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush .list-group-item{border-right-width:0;border-left-width:0;border-radius:0}.list-group-flush .list-group-item:first-child{border-top-width:0}.list-group-flush:last-child .list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;overflow-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} +/*# sourceMappingURL=bootstrap.min.css.map */ diff --git a/docs/doxygen/assets/customdoxygen.css b/docs/doxygen/assets/customdoxygen.css index 438b9cc762b406..452a80948e1dcf 100644 --- a/docs/doxygen/assets/customdoxygen.css +++ b/docs/doxygen/assets/customdoxygen.css @@ -379,7 +379,7 @@ h3 { } h4 { - font: normal 400 1.25/1.25 "Lato", "Helvetica", sans-serif; + font: normal 400 1.25rem/1.25 "Lato", "Helvetica", sans-serif; } /* "H1" headings */ @@ -791,7 +791,11 @@ a.see-all { } #download-link { - margin-right:3.28rem; + margin-right: 2.25rem; + } + + #install-link { + margin-right: 3.28rem; } } @@ -816,7 +820,11 @@ div.old-version > p { } #download-link { - margin-right: 3.75rem; + margin-right: 2.25rem; +} + +#install-link { + margin-right: 3.75rem; } .nav-placeholder { @@ -1298,11 +1306,26 @@ pre.fragment { background: #f9f9f9; border: none; counter-reset: codegroup; - margin: 1.1rem 0; - padding: 0.75rem 1rem; + margin-bottom: 1.1rem; + padding: 0 1rem 0.75rem 0; overflow: auto; } +.code-container { + background: #f9f9f9; + margin-top: 0.75rem; +} + +.code-header { + display: flex; + justify-content: flex-end; + padding: 0.3rem; +} + +.copy-button { + cursor: pointer; +} + div.line { box-sizing: content-box; font-size: 12px; @@ -1425,6 +1448,7 @@ iframe#MSearchResults { transform: translateY(-50%); right: 9rem; width: 60vw; + padding-left:5px; } #search-slider { diff --git a/docs/doxygen/assets/openvino-layout.js b/docs/doxygen/assets/openvino-layout.js index 69ed6d2aeb948d..db73c971843a57 100644 --- a/docs/doxygen/assets/openvino-layout.js +++ b/docs/doxygen/assets/openvino-layout.js @@ -505,6 +505,7 @@ function openVinoContent() { searchSlider.on('click', function() { $(this).toggleClass('closed open'); $("#MSearchField").animate({width:'toggle'},200); + $('#MSearchField').focus(); }); if (['http:', 'https:'].indexOf(window.location.protocol) !== -1) { $('#MSearchField').replaceWith(''); @@ -540,6 +541,51 @@ function openVinoContent() { $(".contents").prepend($(".header")); } + // assign clipboard button for each .fragment element + $('.fragment').wrap('
'); + $('.code-container').prepend($('
')); + var $copyButton = $('content_copy'); + $copyButton.click(function() { + var self = this; + $(self).text('check_circle_outline') + .css('color', '#003C71') + .css("pointer-events", 'none');; + $(self).next('.copy-tooltip') + .attr('data-original-title', 'Copied!') + .tooltip('show') + .addClass('active'); + var fragment = $(self.parentElement.parentElement).children('div.fragment')[0]; + var text = []; + $(fragment).children('div.line').each(function(key, val) { + text.push(val.innerText); + }); + text = text.join('\n'); + var $placeholder = $('