diff --git a/include/aidge/backend/OperatorImpl.hpp b/include/aidge/backend/OperatorImpl.hpp
index 76dcdc3c4580f0c3bea35cee9ee190d487206ab4..f6f9d1ce88836b81b02525836bc59a43ae0aec9e 100644
--- a/include/aidge/backend/OperatorImpl.hpp
+++ b/include/aidge/backend/OperatorImpl.hpp
@@ -43,22 +43,18 @@ struct ImplSpec {
         std::vector<std::pair<DimSize_t, DimSize_t>> dims;
     };
 
-    ImplSpec() {
-    }
+    ImplSpec(DynamicAttributes attrs_ = DynamicAttributes()):
+        attrs(attrs_) {}
 
-    ImplSpec(IOSpec io) {
-        inputs.push_back(io);
-        outputs.push_back(io);
-    }
+    ImplSpec(IOSpec io, DynamicAttributes attrs_ = DynamicAttributes()):
+        inputs(1, io), outputs(1, io), attrs(attrs_) {}
 
-    ImplSpec(IOSpec i, IOSpec o) {
-        inputs.push_back(i);
-        outputs.push_back(o);
-    }
+    ImplSpec(IOSpec i, IOSpec o, DynamicAttributes attrs_ = DynamicAttributes()):
+        inputs(1, i), outputs(1, o), attrs(attrs_) {}
 
     std::vector<IOSpec> inputs;
     std::vector<IOSpec> outputs;
-    //DynamicAttributes attrs;
+    DynamicAttributes attrs;
 };
 
 inline bool operator==(const ImplSpec::IOSpec& lhs, const ImplSpec::IOSpec& rhs) {
@@ -75,7 +71,8 @@ inline bool operator<(const ImplSpec::IOSpec& lhs, const ImplSpec::IOSpec& rhs)
 
 inline bool operator<(const ImplSpec& lhs, const ImplSpec& rhs) {
     return (lhs.inputs < rhs.inputs)
-        || (lhs.inputs == rhs.inputs && lhs.outputs < rhs.outputs);
+        || (lhs.inputs == rhs.inputs && lhs.outputs < rhs.outputs)
+        || (lhs.inputs == rhs.inputs && lhs.outputs == rhs.outputs && lhs.attrs < rhs.attrs);
 }
 
 /**
@@ -147,7 +144,7 @@ struct fmt::formatter<Aidge::ImplSpec::IOSpec> {
 
     template<typename FormatContext>
     inline auto format(Aidge::ImplSpec::IOSpec const& ioSpec, FormatContext& ctx) const {
-        return fmt::format_to(ctx.out(), "{{{}, {}, {}}}", ioSpec.type, ioSpec.format, ioSpec.dims);
+        return fmt::format_to(ctx.out(), "{}, {}, {}", ioSpec.type, ioSpec.format, ioSpec.dims);
     }
 };
 
@@ -160,7 +157,7 @@ struct fmt::formatter<Aidge::ImplSpec> {
 
     template<typename FormatContext>
     inline auto format(Aidge::ImplSpec const& implSpec, FormatContext& ctx) const {
-        return fmt::format_to(ctx.out(), "{{{}, {}}}", implSpec.inputs, implSpec.outputs);
+        return fmt::format_to(ctx.out(), "{}, {}", implSpec.inputs, implSpec.outputs);
     }
 };
 
diff --git a/include/aidge/utils/DynamicAttributes.hpp b/include/aidge/utils/DynamicAttributes.hpp
index cf7f048dbe5999f433277c46e4e3cb9798c43674..4acc96cf1e291cceb1932ee97af35b8f1b56202b 100644
--- a/include/aidge/utils/DynamicAttributes.hpp
+++ b/include/aidge/utils/DynamicAttributes.hpp
@@ -330,6 +330,8 @@ public:
 
     virtual ~DynamicAttributes() {}
 
+   friend bool operator<(const DynamicAttributes& lhs, const DynamicAttributes& rhs);
+
 private:
 #ifdef PYBIND
     // Stores C++ attributes (copy) and Python-only attributes
@@ -347,6 +349,45 @@ private:
 #endif
 };
 
+inline bool operator<(const DynamicAttributes& lhs, const DynamicAttributes& rhs) {
+    return (lhs.mAttrs < rhs.mAttrs);
+}
+}
+
+namespace future_std {
+inline bool operator<(const future_std::any& lhs, const future_std::any& rhs) {
+    bool result = (lhs.type().before(rhs.type()));
+    if (lhs.type() == rhs.type()) {
+        if (lhs.type() == typeid(std::string))
+            result = (future_std::any_cast<std::string>(lhs) < future_std::any_cast<std::string>(rhs));
+        else if (lhs.type() == typeid(bool))
+            result = (future_std::any_cast<bool>(lhs) < future_std::any_cast<bool>(rhs));
+        else if (lhs.type() == typeid(char))
+            result = (future_std::any_cast<char>(lhs) < future_std::any_cast<char>(rhs));
+        else if (lhs.type() == typeid(unsigned char))
+            result = (future_std::any_cast<unsigned char>(lhs) < future_std::any_cast<unsigned char>(rhs));
+        else if (lhs.type() == typeid(short))
+            result = (future_std::any_cast<short>(lhs) < future_std::any_cast<short>(rhs));
+        else if (lhs.type() == typeid(unsigned short))
+            result = (future_std::any_cast<unsigned short>(lhs) < future_std::any_cast<unsigned short>(rhs));
+        else if (lhs.type() == typeid(int))
+            result = (future_std::any_cast<int>(lhs) < future_std::any_cast<int>(rhs));
+        else if (lhs.type() == typeid(unsigned int))
+            result = (future_std::any_cast<unsigned int>(lhs) < future_std::any_cast<unsigned int>(rhs));
+        else if (lhs.type() == typeid(long long int))
+            result = (future_std::any_cast<long long int>(lhs) < future_std::any_cast<long long int>(rhs));
+        else if (lhs.type() == typeid(unsigned long long int))
+            result = (future_std::any_cast<unsigned long long int>(lhs) < future_std::any_cast<unsigned long long int>(rhs));
+        else if (lhs.type() == typeid(float))
+            result = (future_std::any_cast<float>(lhs) < future_std::any_cast<float>(rhs));
+        else if (lhs.type() == typeid(double))
+            result = (future_std::any_cast<double>(lhs) < future_std::any_cast<double>(rhs));
+        else {
+            AIDGE_THROW_OR_ABORT(std::runtime_error, "Unsupported type {} in std::any operator<", lhs.type().name());
+        }
+    }
+    return result;
+}
 }
 
 #endif /* AIDGE_CORE_UTILS_DYNAMICATTRIBUTES_H_ */
diff --git a/src/backend/OperatorImpl.cpp b/src/backend/OperatorImpl.cpp
index 5e29418d4b8972f43fad172018a0108a01d13250..c13a3b70167d9e33469ebdedfaa2809f51246cf3 100644
--- a/src/backend/OperatorImpl.cpp
+++ b/src/backend/OperatorImpl.cpp
@@ -60,6 +60,10 @@ Aidge::ImplSpec Aidge::OperatorImpl::getRequiredSpec() const {
 
         requiredSpec.outputs.push_back({opTensor.getOutput(i)->dataType(), opTensor.getOutput(i)->dataFormat(), dims});
     }
+    // Attributes
+    if (!mOp.isAtomic()) {
+        requiredSpec.attrs.setAttr("subtype", mOp.type());
+    }
     return requiredSpec;
 }