diff --git a/include/aidge/aidge.hpp b/include/aidge/aidge.hpp
index 651a5de69596ee867a97b06ba683f49b05a41303..6ef7441f8d62471fcc7b2425a8b465184778b752 100644
--- a/include/aidge/aidge.hpp
+++ b/include/aidge/aidge.hpp
@@ -47,6 +47,7 @@
 #include "aidge/operator/Gather.hpp"
 #include "aidge/operator/GenericOperator.hpp"
 #include "aidge/operator/GlobalAveragePooling.hpp"
+#include "aidge/operator/GridSample.hpp"
 #include "aidge/operator/MatMul.hpp"
 #include "aidge/operator/MaxPooling.hpp"
 #include "aidge/operator/MetaOperator.hpp"
diff --git a/include/aidge/operator/GridSample.hpp b/include/aidge/operator/GridSample.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..af44a5df5de6908d58951b93921d49ec8e7df708
--- /dev/null
+++ b/include/aidge/operator/GridSample.hpp
@@ -0,0 +1,98 @@
+/********************************************************************************
+ * Copyright (c) 2023 CEA-List
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
+
+#ifndef AIDGE_CORE_OPERATOR_GRIDSAMPLE_H_
+#define AIDGE_CORE_OPERATOR_GRIDSAMPLE_H_
+
+#include <cstddef>  // std::size_t
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "aidge/graph/Node.hpp"
+#include "aidge/operator/OperatorTensor.hpp"
+#include "aidge/utils/Attributes.hpp"
+#include "aidge/utils/Registrar.hpp"
+#include "aidge/utils/StaticAttributes.hpp"
+
+namespace Aidge {
+
+enum class GridSampleAttr { Mode, PaddingMode, AlignCorners };
+
+template <DimIdx_t DIM>
+class GridSample_Op : public OperatorTensor,
+	public Registrable<GridSample_Op<DIM>, std::string, std::shared_ptr<OperatorImpl>(const GridSample_Op<DIM>&)> {
+
+public:
+	static const std::string Type;
+
+	enum class Mode { Linear, Nearest, Cubic };
+	enum class PaddingMode { Zeros, Border, Reflexion };
+
+private:
+	using Attributes_ = StaticAttributes<GridSampleAttr, Mode, PaddingMode, bool>;
+	template <GridSampleAttr e>
+	using attr = typename Attributes_::template attr<e>;
+	const std::shared_ptr<Attributes_> mAttributes;
+
+public:
+
+	GridSample_Op(Mode mode = Mode::Linear,
+			PaddingMode paddingMode = PaddingMode::Zeros,
+			bool alignCorners = false);
+
+	GridSample_Op(const GridSample_Op<DIM>& other);
+	~GridSample_Op() noexcept;
+
+public:
+
+	std::shared_ptr<Operator> clone() const override;
+
+	bool forwardDims(bool /*allowDataDependencies*/ = false) override final;
+
+	void setBackend(const std::string& name, DeviceIdx_t device = 0) override final;
+
+	inline std::shared_ptr<Attributes> attributes() const override { return mAttributes; }
+	inline Mode mode() const { return mAttributes->template getAttr<GridSampleAttr::Mode>(); }
+	inline PaddingMode paddingMode() const { return mAttributes->template getAttr<GridSampleAttr::PaddingMode>(); }
+	inline bool alignBorders() const { return mAttributes->template getAttr<GridSampleAttr::AlignCorners>(); }
+
+	static const std::vector<std::string> getInputsName() {
+		return {"data_input", "grid_field"};
+	}
+	static const std::vector<std::string> getOutputsName() {
+		return {"data_output"};
+	}
+};
+
+extern template class GridSample_Op<1>;
+extern template class GridSample_Op<2>;
+
+template <DimIdx_t DIM>
+std::shared_ptr<Node> GridSample(
+                        typename GridSample_Op<DIM>::Mode mode = GridSample_Op<DIM>::Mode::Linear,
+                        typename GridSample_Op<DIM>::PaddingMode paddingMode = GridSample_Op<DIM>::PaddingMode::Zeros,
+                        bool alignCorners = false,
+                        const std::string& name = "");
+
+} // namespace Aidge
+
+
+namespace {
+template <>
+const char* const EnumStrings<Aidge::GridSampleAttr>::data[] = {
+    "mode",
+    "padding_mode",
+    "align_corners"
+};
+}
+
+#endif /* AIDGE_CORE_OPERATOR_GRIDSAMPLE_H_ */
diff --git a/python_binding/operator/pybind_GridSample.cpp b/python_binding/operator/pybind_GridSample.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9c96856fc0c7c1d324b91df435e8d66e98a0240f
--- /dev/null
+++ b/python_binding/operator/pybind_GridSample.cpp
@@ -0,0 +1,82 @@
+/********************************************************************************
+ * Copyright (c) 2023 CEA-List
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
+
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+#include <string>
+#include <vector>
+#include <array>
+
+#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/data/Tensor.hpp"
+#include "aidge/operator/GridSample.hpp"
+#include "aidge/operator/OperatorTensor.hpp"
+#include "aidge/utils/Types.h"
+#include "aidge/utils/Registrar.hpp" // declare_registrable
+
+static Aidge::GridSample_Op::Mode stringToInterpolationMode(const std::string& mode) {
+    static std::unordered_map<std::string, Aidge::GridSample_Op::Mode> map = {
+        {"linear", Aidge::GridSample_Op::Mode::Linear},
+        {"nearest", Aidge::GridSample_Op::Mode::Nearest},
+        {"cubic", Aidge::GridSample_Op::Mode::Cubic}
+    }
+    return map[mode];
+}
+
+static Aidge::GridSample_Op::PaddingMode stringToPaddingMode(const std::string& mode) {
+    static std::unordered_map<std::string, Aidge::GridSample_Op::PaddingMode> map = {
+        {"zeros", Aidge::GridSample_Op::PaddingMode::Zeros},
+        {"border", Aidge::GridSample_Op::PaddingMode::Border},
+        {"reflexion", Aidge::GridSample_Op::PaddingMode::Reflexion}
+    }
+    return map[mode];
+}
+
+namespace py = pybind11;
+namespace Aidge {
+
+template <DimIdx_t DIM> void declare_GridSampleOp(py::module &m) {
+  const std::string pyClassName("GridSampleOp" + std::to_string(DIM) + "D");
+  py::class_<GridSample_Op<DIM>, std::shared_ptr<GridSample_Op<DIM>>, OperatorTensor>(
+    m, pyClassName.c_str(),
+    py::multiple_inheritance())
+        .def(py::init([](const std::string& mode,
+                         const std::string& padding_mode,
+                         bool align_corners) {
+            return new GridSample_Op<DIM>(stringToInterpolationMode(mode), stringToPaddingMode(padding_mode), align_corners);
+        }), py::arg("mode") = "linear",
+            py::arg("padding_mode") = "zeros",
+            py::arg("alogn_corners") = false)
+        .def_static("get_inputs_name", &GridSample_Op<DIM>::getInputsName)
+        .def_static("get_outputs_name", &GridSample_Op<DIM>::getOutputsName)
+        ;
+
+  declare_registrable<GridSample_Op<DIM>>(m, pyClassName);
+
+  m.def(("GridSample" + std::to_string(DIM) + "D").c_str(), [](const std::string& mode,
+                                                        const std::string& padding_mode,
+                                                        bool align_corners
+                                                        const std::string& name) {
+        return GridSample<DIM>(stringToInterpolationMode(mode), stringToPaddingMode(padding_mode), align_corners, name);
+    }, py::arg("mode"),
+       py::arg("padding_mode"),
+       py::arg("align_corners"),
+       py::arg("name") = "");
+}
+
+
+void init_GridSample(py::module &m) {
+  declare_GridSampleOp<1>(m);
+  declare_GridSampleOp<2>(m);
+//   declare_GridSampleOp<3>(m);
+}
+
+} // namespace Aidge
diff --git a/python_binding/pybind_core.cpp b/python_binding/pybind_core.cpp
index 9443ed55eaaf6dc04ad9ee4612ed9d491aed54ae..918143213f3dd490ef0e448f086c09135b05f6af 100644
--- a/python_binding/pybind_core.cpp
+++ b/python_binding/pybind_core.cpp
@@ -39,6 +39,7 @@ void init_FC(py::module&);
 void init_Gather(py::module&);
 void init_GenericOperator(py::module&);
 void init_GlobalAveragePooling(py::module&);
+void init_GridSample(py::module&);
 void init_LeakyReLU(py::module&);
 void init_MatMul(py::module&);
 void init_MaxPooling(py::module&);
@@ -110,6 +111,7 @@ void init_Aidge(py::module& m) {
     init_Gather(m);
     init_GenericOperator(m);
     init_GlobalAveragePooling(m);
+    init_GridSample(m);
     init_LeakyReLU(m);
     init_MatMul(m);
     init_MaxPooling(m);
diff --git a/src/operator/GridSample.cpp b/src/operator/GridSample.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1b6d4a237623e43e06e3cc945184c467a0070200
--- /dev/null
+++ b/src/operator/GridSample.cpp
@@ -0,0 +1,112 @@
+/********************************************************************************
+ * Copyright (c) 2023 CEA-List
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
+
+#include "aidge/operator/GridSample.hpp"
+
+#include <cstddef>    // std::size_t
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "aidge/data/Tensor.hpp"
+#include "aidge/utils/ErrorHandling.hpp"
+#include "aidge/utils/Registrar.hpp"
+#include "aidge/utils/Types.h"
+
+template <Aidge::DimIdx_t DIM>
+const std::string Aidge::GridSample_Op<DIM>::Type = "GridSample";
+
+template <Aidge::DimIdx_t DIM>
+Aidge::GridSample_Op<DIM>::GridSample_Op(
+    typename Aidge::GridSample_Op<DIM>::Mode mode,
+    typename Aidge::GridSample_Op<DIM>::PaddingMode paddingMode,
+    bool alignCorners)
+    : OperatorTensor(Type, {InputCategory::Data, InputCategory::Param}, 1),
+      mAttributes(std::make_shared<Attributes_>(
+        attr<GridSampleAttr::Mode>(mode),
+        attr<GridSampleAttr::PaddingMode>(paddingMode),
+        attr<GridSampleAttr::AlignCorners>(alignCorners)))
+{
+    // ctor
+}
+
+template <Aidge::DimIdx_t DIM>
+Aidge::GridSample_Op<DIM>::GridSample_Op(const Aidge::GridSample_Op<DIM>& other)
+    : OperatorTensor(other),
+      mAttributes(other.mAttributes)
+{
+    if (other.mImpl) {
+        SET_IMPL_MACRO(GridSample_Op<DIM>, *this, other.backend());
+    } else {
+        mImpl = nullptr;
+    }
+}
+
+template <Aidge::DimIdx_t DIM>
+Aidge::GridSample_Op<DIM>::~GridSample_Op() noexcept = default;
+
+template <Aidge::DimIdx_t DIM>
+std::shared_ptr<Aidge::Operator> Aidge::GridSample_Op<DIM>::clone() const {
+    return std::make_shared<GridSample_Op<DIM>>(*this);
+}
+
+template <Aidge::DimIdx_t DIM>
+bool Aidge::GridSample_Op<DIM>::forwardDims(bool /*allowDataDependency*/) {
+    // TODO: adapt for other formats than NCHW
+    if (inputsAssociated()) {
+        // check data has batch and channel dimensions: (N, C, D0, D1, ..., DN)
+        AIDGE_ASSERT((getInput(0)->nbDims() == (DIM+2)),
+                    "Wrong input size for {} operator.", type());
+        // check grid field
+        // should be (N, D0_out, D1_out, ..., DN_out, N+1)
+        AIDGE_ASSERT(((getInput(1)->nbDims() == (DIM+2)) &&
+            (getInput(1)->dims<DIM+2>()[DIM+1] == DIM)),
+            "Wrong grid size {} for {} operator.", getInput(1)->dims(), type());
+
+        std::array<DimSize_t, DIM + 2> outputDims{};
+        const std::vector<DimSize_t>& inputDims(getInput(1)->dims());
+        outputDims[1] = getInput(0)->dims<DIM+2>()[1];
+        outputDims[0] = inputDims[0];
+        for (std::size_t i = 2; i < DIM+2; ++i) {
+            outputDims[i] = inputDims[i-1];
+        }
+
+        mOutputs[0]->resize(outputDims);
+        return true;
+    }
+
+    return false;
+}
+
+
+template <Aidge::DimIdx_t DIM>
+void Aidge::GridSample_Op<DIM>::setBackend(const std::string &name, Aidge::DeviceIdx_t device) {
+    SET_IMPL_MACRO(GridSample_Op<DIM>, *this, name);
+    mOutputs[0]->setBackend(name, device);
+}
+
+template class Aidge::GridSample_Op<1>;
+template class Aidge::GridSample_Op<2>;
+
+template <Aidge::DimIdx_t DIM>
+std::shared_ptr<Aidge::Node> Aidge::GridSample(
+                        typename Aidge::GridSample_Op<DIM>::Mode mode,
+                        typename Aidge::GridSample_Op<DIM>::PaddingMode paddingMode,
+                        bool alignCorners,
+                        const std::string& name)
+{
+    return std::make_shared<Node>(
+        GridSample_Op<DIM>(
+            mode,
+            paddingMode,
+            alignCorners),
+        name);
+}
\ No newline at end of file
diff --git a/unit_tests/operator/Test_GridSample_Op.cpp b/unit_tests/operator/Test_GridSample_Op.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..06555b27a42ade1798e5f602ea9d48aeb2e0f8bb
--- /dev/null
+++ b/unit_tests/operator/Test_GridSample_Op.cpp
@@ -0,0 +1,89 @@
+/********************************************************************************
+ * Copyright (c) 2023 CEA-List
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
+
+#include <cstddef>  // std::size_t
+#include <memory>
+#include <random>   // std::mt19937, std::uniform_int_distribution
+#include <vector>
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/generators/catch_generators_random.hpp>
+
+#include "aidge/data/Tensor.hpp"
+#include "aidge/operator/GridSample.hpp"
+#include "aidge/operator/OperatorTensor.hpp"
+
+namespace Aidge {
+
+TEST_CASE("[core/operator] GridSample_Op(forwardDims)", "[GridSample][forwardDims]") {
+    constexpr std::uint16_t NBTRIALS = 10;
+
+    // Create a random number generator
+    auto rd = Catch::Generators::Detail::getSeed;
+    std::mt19937 gen(rd());
+    std::uniform_int_distribution<std::size_t> dimsDist(1, 10);
+    std::uniform_int_distribution<std::size_t> nbDimsDist(1, 5);
+
+    // Create GridSample Operator
+    std::shared_ptr<Node> myGridSample = GridSample<2>(GridSample_Op<2>::Mode::Cubic, GridSample_Op<2>::PaddingMode::Border, false);
+    auto op = std::static_pointer_cast<OperatorTensor>(myGridSample -> getOperator());
+
+    // input_0
+    std::shared_ptr<Tensor> data_in0 = std::make_shared<Tensor>();
+    op -> associateInput(0,data_in0);
+    // input_1
+    std::shared_ptr<Tensor> grid_in1 = std::make_shared<Tensor>();
+    op -> associateInput(1,grid_in1);
+
+    SECTION("Valid shape provided") {
+        for (std::uint16_t trial = 0; trial < NBTRIALS; ++trial) {
+
+            std::size_t N = dimsDist(gen);
+            std::size_t C = dimsDist(gen);
+            std::size_t H_data_in0 = dimsDist(gen);
+            std::size_t W_data_in0 = dimsDist(gen);
+            std::size_t H_grid_in1 = dimsDist(gen);
+            std::size_t W_grid_in1 = dimsDist(gen);
+
+            data_in0->resize({N, C, H_data_in0, W_data_in0});
+            grid_in1->resize({N, H_grid_in1, W_grid_in1, 2});
+
+            REQUIRE_NOTHROW(op->forwardDims());
+            REQUIRE((op->getOutput(0)->dims()) == std::vector<std::size_t>({N, C, H_grid_in1, W_grid_in1}));
+        }
+    }
+    SECTION("Invalid shape provided") {
+        std::size_t N_in = dimsDist(gen);
+        std::size_t C = dimsDist(gen);
+        std::size_t H_data_in0 = dimsDist(gen);
+        std::size_t W_data_in0 = dimsDist(gen);
+        std::size_t H_grid_in1 = dimsDist(gen);
+        std::size_t W_grid_in1 = dimsDist(gen);
+
+        // different batch number
+        std::size_t N_out = N_in+1;
+        data_in0->resize({N_in, C, H_data_in0, W_data_in0});
+        grid_in1->resize({N_out, H_grid_in1, W_grid_in1, 2});
+        REQUIRE_THROWS(op->forwardDims());
+
+        // different number of dimensions
+        data_in0->resize({N_in, C, H_data_in0, W_data_in0});
+        grid_in1->resize({N_in, H_grid_in1, W_grid_in1, 2, 2});
+        REQUIRE_THROWS(op->forwardDims());
+
+        // wrong pixel coordinates number
+        data_in0->resize({N_in, C, H_data_in0, W_data_in0});
+        grid_in1->resize({N_in, H_grid_in1, W_grid_in1, 2 + dimsDist(gen)});
+        REQUIRE_THROWS(op->forwardDims());
+    }
+}
+
+} // namespace Aidge