diff --git a/aidge_core/unit_tests/test_operator_squeeze.py b/aidge_core/unit_tests/test_operator_squeeze.py
new file mode 100644
index 0000000000000000000000000000000000000000..b43605893f32f17e7b544b2fea09b16bdd982050
--- /dev/null
+++ b/aidge_core/unit_tests/test_operator_squeeze.py
@@ -0,0 +1,194 @@
+"""
+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
+"""
+
+import unittest
+import aidge_core
+from aidge_core import Log
+import numpy as np
+from numpy import testing as npt
+
+
+class TestSqueeze(unittest.TestCase):
+    """
+    Test squeeze operator
+    """
+
+    def setUp(self):
+        ############DEFINING INPUT AND OUTPUTS FOR TESTS
+        axes_to_squeeze_0 = [0]
+        axes_to_squeeze_many = [0, 1, 4]
+        axes_to_squeeze_all = []
+        axes_to_squeeze_error = [1, 2, 4, 5, 10, 3, 42, 127, 12, 3, 4, 1, 4, 50]
+
+        squeeze_dim_0 = aidge_core.Squeeze(axes_to_squeeze_0, name="squeeze_dim_0")
+        squeeze_many = aidge_core.Squeeze(axes_to_squeeze_many, name="squeeze_many")
+        squeeze_all = aidge_core.Squeeze(axes_to_squeeze_all, name="squeeze_all")
+        squeeze_error = aidge_core.Squeeze(axes_to_squeeze_error, name="squeeze_error")
+
+        input_1_data_shape = np.array([1, 2, 3])
+        input_2_data_hape = np.array([1, 1, 3, 3, 1, 9])
+        input_3_data_shape = np.array([1])
+        input_4_data_shape = np.array([1, 1, 4])
+
+        input_axes_0 = axes_to_squeeze_0
+        input_axes_many = axes_to_squeeze_many
+        input_axes_all = axes_to_squeeze_all
+        # input_axes_error = aidge_core.Tensor(axes_to_squeeze_error)
+
+        ####################### DEFINING TEST RUNS
+        self.tests_axes_defined_by_attribute = [
+            (input_1_data_shape, squeeze_dim_0, np.array([2, 3])),
+            (input_1_data_shape, squeeze_all, np.array([2, 3])),
+            (input_2_data_hape, squeeze_dim_0, np.array([1, 3, 3, 1, 9])),
+            (input_2_data_hape, squeeze_many, np.array([3, 3, 9])),
+            (input_2_data_hape, squeeze_all, np.array([3, 3, 9])),
+            (input_3_data_shape, squeeze_dim_0, np.array([])),
+            (input_3_data_shape, squeeze_all, np.array([])),
+            (input_4_data_shape, squeeze_dim_0, np.array([1, 4])),
+            (input_4_data_shape, squeeze_all, np.array([4])),
+        ]
+
+        # operators are puprposefully chosen with different predefined attribute than the input_axes tensor
+        self.tests_axes_defined_by_input = [
+            (input_1_data_shape, input_axes_0, squeeze_error, np.array([2, 3])),
+            (input_1_data_shape, input_axes_all, squeeze_error, np.array([2, 3])),
+            (input_2_data_hape, input_axes_0, squeeze_error, np.array([1, 3, 3, 1, 9])),
+            (input_2_data_hape, input_axes_many, squeeze_error, np.array([3, 3, 9])),
+            (input_2_data_hape, input_axes_all, squeeze_error, np.array([3, 3, 9])),
+            (input_3_data_shape, input_axes_0, squeeze_error, np.array([])),
+            (input_3_data_shape, input_axes_all, squeeze_error, np.array([])),
+            (input_4_data_shape, input_axes_0, squeeze_error, np.array([1, 4])),
+            (input_4_data_shape, input_axes_all, squeeze_error, np.array([4])),
+        ]
+        self.test_error = [
+            (input_1_data_shape, squeeze_error),
+            (input_1_data_shape, squeeze_many),
+            (input_3_data_shape, squeeze_many),
+            (input_4_data_shape, squeeze_many),
+        ]
+        return
+
+    def tearDown(self):
+        pass
+
+    def test_axes_defined_via_tensor_input(self):
+        Log.notice("\ntest_axes_defined_via_tensor_input")
+        for index, (
+            input_shape,
+            input_axes_to_squeeze,
+            squeeze_node_template,
+            output_shape,
+        ) in enumerate(self.tests_axes_defined_by_input):
+            test_squeeze_node = squeeze_node_template
+            test_squeeze_op = test_squeeze_node.get_operator()
+
+            print(f"\nTest {index}")
+            print(f"input shape : {input_shape}")
+            print(f"input axes: {np.array(input_axes_to_squeeze)}")
+            print(f"operator : {test_squeeze_node}")
+            print(f"expected output_shape : {output_shape}")
+
+            test_squeeze_op.set_backend("cpu")
+            test_squeeze_op.set_datatype(aidge_core.dtype.float32)
+
+            input_values = np.ones(shape=input_shape, dtype=np.float32)
+            output_values = np.ones(shape=output_shape, dtype=np.float32)
+
+            input_data = aidge_core.Tensor(input_values)
+            input_data.set_datatype(aidge_core.dtype.float32)
+            input_data.set_backend("cpu")
+
+            input_axes = aidge_core.Tensor(
+                np.array(input_axes_to_squeeze, dtype=np.float32)
+            )
+            input_axes.set_datatype(aidge_core.dtype.int8)
+            input_axes.set_backend("cpu")
+
+            test_squeeze_op.set_input(0, input_data)
+            test_squeeze_op.set_input(1, input_axes)
+
+            self.assertEqual(test_squeeze_op.forward_dims(True), True)
+            test_squeeze_op.forward()
+
+            squeeze_output = test_squeeze_op.get_output(0)
+
+            npt.assert_array_equal(
+                squeeze_output.dims(),
+                output_shape,
+                err_msg=f"SQUEEZE FAILURE : expected result differs from output size\n\toperator : {test_squeeze_node}\n\tinput.shape : {input_shape.shape}",
+            )
+            npt.assert_array_almost_equal(
+                np.array(squeeze_output, dtype=np.float32),
+                output_values,
+                7,
+                err_msg=f"SQUEEZE FAILURE : output tensor values differs from expected values\n\toperator : {test_squeeze_node}\n\tinput.shape : {input_shape.shape}",
+            )
+            # self.assertEqual(test_squeeze_op.dims_forwarded(), True, "SQUEEZE_FAILURE : dims_forwarded failed.")
+        return
+
+    def test_axes_defined_via_attribute(self):
+        Log.notice("\ntest_axes_defined_via_attribute")
+        for index, (input_shape, squeeze_node_template, output_shape) in enumerate(
+            self.tests_axes_defined_by_attribute
+        ):
+            test_squeeze_node = squeeze_node_template
+            test_squeeze_op = test_squeeze_node.get_operator()
+
+            print(f"\nTest {index}")
+            print(f"input size : {input_shape.shape}")
+            print(f"operator : {test_squeeze_node}")
+            print(f"expected output_shape : {output_shape}")
+
+            test_squeeze_node.get_operator().set_backend("cpu")
+
+            input_values = np.ones(shape=input_shape, dtype=np.float32)
+            output_values = np.ones(shape=output_shape, dtype=np.float32)
+            input_data = aidge_core.Tensor(input_values)
+            input_data.set_datatype(aidge_core.dtype.float32)
+            input_data.set_backend("cpu")
+            test_squeeze_op.set_input(0, input_data)
+
+            test_squeeze_op.forward_dims()
+            test_squeeze_op.forward()
+
+            squeeze_output = test_squeeze_op.get_output(0)
+
+            npt.assert_array_equal(
+                squeeze_output.dims(),
+                output_shape,
+                err_msg=f"SQUEEZE FAILURE : expected result differs from output size\n\toperator : {test_squeeze_node}\n\tinput.shape : {input_shape.shape}",
+            )
+            npt.assert_array_almost_equal(
+                np.array(squeeze_output, dtype=np.float32),
+                output_values,
+                7,
+                err_msg=f"SQUEEZE FAILURE : output tensor values differs from expected values\n\toperator : {test_squeeze_node}\n\tinput.shape : {input_shape.shape}",
+            )
+        return
+
+    def test_error(self):
+        for input_shape, squeeze_node_template in self.test_error:
+            test_squeeze_node = squeeze_node_template
+            test_squeeze_op = test_squeeze_node.get_operator()
+
+            input_values = np.ones(shape=input_shape)
+            input_data = aidge_core.Tensor(input_values)
+            input_data.set_datatype(aidge_core.dtype.float32)
+            input_data.set_backend("cpu")
+            test_squeeze_op.set_input(0, input_data)
+
+            with self.assertRaises((RuntimeError, AssertionError)):
+                test_squeeze_op.forward_dims()
+                test_squeeze_op.forward()
+        return
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/include/aidge/operator/Squeeze.hpp b/include/aidge/operator/Squeeze.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..73321b5689c0c10d9d06ea60c551cc6dfaced149
--- /dev/null
+++ b/include/aidge/operator/Squeeze.hpp
@@ -0,0 +1,159 @@
+/********************************************************************************
+ * 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_SQUEEZE_H_
+#define AIDGE_CORE_OPERATOR_SQUEEZE_H_
+
+#include <cstdint>
+#include <cstdlib>
+#include <functional>
+#include <limits>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "aidge/graph/Node.hpp"
+#include "aidge/operator/Operator.hpp"
+#include "aidge/operator/OperatorTensor.hpp"
+#include "aidge/utils/ErrorHandling.hpp"
+#include "aidge/utils/Registrar.hpp"
+#include "aidge/utils/StaticAttributes.hpp"
+#include "aidge/utils/Types.h"
+
+namespace Aidge {
+/**
+ * @brief implementation of the operator squeeze.
+ * @note Since this operator implementation is agnostic to the backend it is
+ * located here instead of in aidge_backend_cpu/cuda.
+ */
+class Squeeze_OpImpl : public OperatorImpl {
+public:
+  Squeeze_OpImpl(const Operator &op, const std::string &backend = "")
+      : OperatorImpl(op, backend) {}
+  void forward() override;
+};
+
+enum class SqueezeAttr {
+  /**
+   * @brief axes to squeeze, if left empty all 1 sized
+   * dimensions will be removed.
+   */
+  Axes
+};
+
+/**
+ * @brief This operator has as purpose to remove dummy dimensions around given
+ * axes.
+ * input#0 : Tensor to squeeze
+ * input#1 Optionnal : 1D tensor that lists the axes to squeeze
+ * @note the axes to squeeze can either be given via attribute or via input #1,
+ * for the sake of simplicity of the example unders, the axes to squeeze are
+ * given via attribute
+ * @example Calling squeeze(1) on a tensor of dimensions (2,1,3,4) will result
+ * in a tensor of dim (2,3,4).
+ * @example Calling squeeze(1) on a tensor of dimensions (1,2,3,4) will result
+ * in a tensor of dim (1,2,3,4).
+ * @example Calling squeeze() with no argument will result in the removal of
+ * every 1-sized dimension in the tensor.
+ */
+class Squeeze_Op
+    : public OperatorTensor,
+      public Registrable<Squeeze_Op, std::string,
+                         std::shared_ptr<OperatorImpl>(const Squeeze_Op &)> {
+
+public:
+  static const std::string
+      Type; // name of the type of the operation (Here "Squeeze")
+
+private:
+  using Attributes_ = StaticAttributes<SqueezeAttr, std::vector<int8_t>>;
+  template <SqueezeAttr e> using attr = typename Attributes_::template attr<e>;
+  const std::shared_ptr<Attributes_> mAttributes;
+
+public:
+  /**
+   * @brief constructor for Squeeze op
+   * @param[in] axes around which perform the operation
+   */
+  Squeeze_Op(const std::vector<int8_t> &axes = {})
+      : OperatorTensor(Type, {InputCategory::Data, InputCategory::OptionalData},
+                       1),
+        mAttributes(
+            std::make_shared<Attributes_>(attr<SqueezeAttr::Axes>(axes))) {
+    mImpl = std::make_shared<Squeeze_OpImpl>(*this);
+  }
+
+  /**
+   * @brief Copy-constructor. Copy the operator attributes and its output
+   * tensor(s), but not its input tensors (the new operator has no input
+   * associated).
+   * @param op Operator to copy.
+   */
+  Squeeze_Op(const Squeeze_Op &op)
+      : OperatorTensor(op), mAttributes(op.mAttributes) {
+    if (!op.backend().empty()) {
+      SET_IMPL_MACRO(Squeeze_Op, *this, op.backend());
+    } else {
+      mImpl = std::make_shared<Squeeze_OpImpl>(*this);
+    }
+  }
+
+  /**
+   * @brief Clone the operator using its copy-constructor.
+   * @see Operator::MatMul_Op
+   */
+  std::shared_ptr<Operator> clone() const override final {
+    return std::make_shared<Squeeze_Op>(*this);
+  }
+
+  /**
+   * @brief Compute dimensions for the output Tensor
+   */
+  bool forwardDims(bool allowDataDependency = false) override final;
+  bool dimsForwarded() const override final;
+
+  void setBackend(const std::string &name,
+                  DeviceIdx_t device = 0) override final;
+
+  inline std::shared_ptr<Attributes> attributes() const override {
+    return mAttributes;
+  }
+
+  /**
+   * @brief axes to squeeze, if left empty all 1 sized
+   * dimensions will be removed.
+   */
+  inline std::vector<int8_t> &axes() const noexcept {
+    return mAttributes->template getAttr<SqueezeAttr::Axes>();
+  }
+
+  static const std::vector<std::string> getInputsName() {
+    return {"data_input", "axes_to_squeeze"};
+  }
+  static const std::vector<std::string> getOutputsName() {
+    return {"squeezed"};
+  }
+};
+
+// helper with C-style array instead of std::array for kernel_dims to allow
+// automatic template DIM deduction
+inline std::shared_ptr<Node> Squeeze(const std::vector<int8_t> axes = {},
+                                     const std::string &name = "") {
+  return std::make_shared<Node>(std::make_shared<Squeeze_Op>(axes), name);
+}
+} // namespace Aidge
+
+namespace {
+template <>
+const char *const EnumStrings<Aidge::SqueezeAttr>::data[] = {"Axes"};
+}
+
+#endif // AIDGE_CORE_OPERATOR_SQUEEZE_H_
diff --git a/python_binding/operator/pybind_Squeeze.cpp b/python_binding/operator/pybind_Squeeze.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ca90fb46af40189dbe66c320ecdd237470ffa112
--- /dev/null
+++ b/python_binding/operator/pybind_Squeeze.cpp
@@ -0,0 +1,52 @@
+/********************************************************************************
+ * 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 <memory>
+#include <pybind11/pybind11.h>
+#include <string>
+#include <vector>
+
+#include "aidge/operator/OperatorTensor.hpp"
+#include "aidge/operator/Squeeze.hpp"
+#include "aidge/utils/Attributes.hpp"
+#include "aidge/utils/Types.h"
+
+namespace py = pybind11;
+namespace Aidge {
+
+void init_Squeeze(py::module &m) {
+  py::class_<Squeeze_Op, std::shared_ptr<Squeeze_Op>, OperatorTensor>(
+      m, "SqueezeOp", py::multiple_inheritance(),
+		R"mydelimiter(
+		Initialize squeeze operator
+		:param axes :   axes to squeeze between [-r;r-1] 
+						with r = input_tensor.nbDims()
+						& r in [-128 , 127]
+		:type axes : :py:class: List[Int]
+		)mydelimiter")
+      .def("get_inputs_name", &Squeeze_Op::getInputsName)
+      .def("get_outputs_name", &Squeeze_Op::getOutputsName)
+      .def("axes", &Squeeze_Op::axes);
+  // Here we bind the constructor of the Squeeze Node. We add an argument
+  // for each attribute of the operator (in here we only have 'axes') and
+  // the last argument is the node's name.
+  m.def("Squeeze", &Squeeze, py::arg("axes") = std::vector<int8_t>({}),
+        py::arg("name") = "",
+        R"mydelimiter(
+    Initialize a node containing a squeeze operator.
+	:param axes :   axes to squeeze between [-r;r-1] 
+					with r = input_tensor.nbDims()
+					& r in [-128 , 127]
+	:type axes : :py:class: List[Int]
+    :param name : name of the node.
+)mydelimiter");
+}
+} // namespace Aidge
diff --git a/python_binding/pybind_core.cpp b/python_binding/pybind_core.cpp
index c72a629b24254a27cf7418af4f22c3df89084ad3..cc23727be624e44fac27d6deb9f658f248cdb97b 100644
--- a/python_binding/pybind_core.cpp
+++ b/python_binding/pybind_core.cpp
@@ -63,6 +63,7 @@ void init_Slice(py::module&);
 void init_Softmax(py::module&);
 void init_Split(py::module&);
 void init_Sqrt(py::module&);
+void init_Squeeze(py::module&);
 void init_Sub(py::module&);
 void init_Tanh(py::module&);
 void init_Transpose(py::module&);
@@ -138,6 +139,7 @@ void init_Aidge(py::module& m) {
     init_Softmax(m);
     init_Split(m);
     init_Sqrt(m);
+    init_Squeeze(m);
     init_Sub(m);
     init_Tanh(m);
     init_Transpose(m);
diff --git a/src/operator/Squeeze.cpp b/src/operator/Squeeze.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..df81ef3ec980b5cf8bd9f8bd39d093cee529cf75
--- /dev/null
+++ b/src/operator/Squeeze.cpp
@@ -0,0 +1,164 @@
+/********************************************************************************
+ * 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/Squeeze.hpp"
+
+#include <algorithm>
+#include <bitset>
+#include <cstdint>
+#include <fmt/core.h>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+#include "aidge/data/Data.hpp"
+#include "aidge/data/Tensor.hpp"
+#include "aidge/utils/ErrorHandling.hpp"
+#include "aidge/utils/Log.hpp"
+#include "aidge/utils/Registrar.hpp"
+#include "aidge/utils/Types.h"
+
+namespace Aidge {
+const std::string Squeeze_Op::Type = "Squeeze";
+
+bool Squeeze_Op::dimsForwarded() const {
+  if ((getInput(1) && !getInput(1)->undefined())) {
+    // output dims are data dependent
+    return false;
+  }
+
+  return OperatorTensor::dimsForwarded();
+}
+
+bool Squeeze_Op::forwardDims(bool allowDataDependency) {
+  // error checking
+  if (!inputsAssociated(false) || getInput(0)->undefined()) {
+    return false;
+  }
+
+  std::shared_ptr<Tensor> fallback;
+  // Input 1 is axes to squeeze (can also be given via attribute)
+  if (getInput(1)) {
+    if (!this->axes().empty()) {
+      Log::notice("{} : ignoring non-empty axes attribute because input#1 "
+                  "takes precedence",
+                  type());
+    }
+
+    if (!allowDataDependency) {
+      Log::warn("{} : unable to forwardDims() because output dims are data "
+                "dependent on input#1",
+                type());
+      return false;
+    }
+
+    this->axes().clear(); // If both are provided input would override attrs
+    this->axes().reserve(getInput(1)->size());
+    const auto &axes =
+        getInput(1)->refCastFrom(fallback, NativeType<int8_t>::type, "cpu");
+    if (axes.nbDims() == 0) {
+      this->axes().clear();
+    } else {
+      AIDGE_ASSERT(
+          axes.nbDims() == 1,
+          "Axes input tensor should be of size 1. Received {} dimensions : {}",
+          axes.nbDims(), axes.dims());
+      std::copy_n(static_cast<int8_t *>(axes.getImpl()->hostPtr()), axes.size(),
+                  std::back_inserter(this->axes()));
+    }
+  }
+
+  std::vector<DimSize_t> input_dims = getInput(0)->dims();
+  std::vector<DimSize_t> output_dims;
+  output_dims.reserve(input_dims.size());
+  std::vector<DimIdx_t> axes_rectified_idx;
+  axes_rectified_idx.reserve(input_dims.size());
+
+  if (this->axes().size() == 0) { // squeeze() => squeeze all 1 sized dimensions
+    Log::debug("this->axes() is empty, all 1 sized dim will be squeezed. If "
+               "this is an error ensure that the values are properly set via "
+               "attribute or data input#1.");
+    std::copy_if(input_dims.begin(), input_dims.end(),
+                 std::back_inserter(output_dims),
+                 [](DimSize_t dim) { return dim != 1; });
+  } else { // squeeze({N,.....}) => squeeze all specified dimensions that are of
+           // size 1.
+    /////// ensure indexes validity and set pythonic negative indexes to their
+    // positive value
+    for (const int8_t &axis : this->axes()) {
+      AIDGE_ASSERT(axis >= static_cast<int8_t>(-input_dims.size()) &&
+                       axis < static_cast<int8_t>(input_dims.size()),
+                   "{} : Axis index OutOfBounds error, expected value "
+                   "within size limits of input tensor : "
+                   "[-{},{}), got {}.",
+                   type(), input_dims.size(), input_dims.size() - 1, axis);
+      auto temp =
+          static_cast<DimIdx_t>(axis >= 0 ? axis : axis + input_dims.size());
+      if (axes_rectified_idx.end() == std::find(axes_rectified_idx.begin(),
+                                                axes_rectified_idx.end(),
+                                                temp)) {
+        axes_rectified_idx.push_back(temp);
+      }
+    }
+
+    // Create output_dims
+    // speeds up binary search
+    std::sort(axes_rectified_idx.begin(), axes_rectified_idx.end());
+    DimSize_t i = 0;
+    std::copy_if(
+        input_dims.begin(), input_dims.end(), std::back_inserter(output_dims),
+        [&axes_rectified_idx, &i, &input_dims](DimSize_t dim) {
+          // if current dim index is found in axes to squeeze
+          // we ensure that this axis is 1 sized, otherwise an error is thrown
+          bool ok = true;
+          if (std::binary_search(axes_rectified_idx.begin(),
+                                 axes_rectified_idx.end(), i)) {
+            AIDGE_ASSERT(dim == 1,
+                         "{} : Tried to squeeze axis nb {} of a tensor of dim "
+                         "{}. Dim to squeeze has to be 1-sized, got size {}."
+                         "Axes to squeeze : {}",
+                         __func__, i, input_dims, input_dims[i],
+                         axes_rectified_idx);
+            ok = false;
+          }
+          i++; // Incrementing counter since there is no enumerate
+               // fctn (until C++23)
+          return ok;
+        });
+  }
+  mOutputs[0]->resize(output_dims);
+  return true;
+}
+
+void Squeeze_Op::setBackend(const std::string &name,
+                            Aidge::DeviceIdx_t device) {
+  if (Registrar<Squeeze_Op>::exists({name})) {
+    SET_IMPL_MACRO(Squeeze_Op, *this, name);
+  } else {
+    mImpl = std::make_shared<Squeeze_OpImpl>(*this);
+  }
+  mOutputs[0]->setBackend(name, device);
+}
+
+void Aidge::Squeeze_OpImpl::forward() {
+  const Squeeze_Op &op_ = static_cast<const Squeeze_Op &>(mOp);
+  // Check if input is provided
+  AIDGE_ASSERT(op_.getInput(0), "Squeeze : missing input 0");
+
+  op_.getOutput(0)->getImpl()->copy(op_.getInput(0)->getImpl()->rawPtr(),
+                                    op_.getInput(0)->size());
+}
+
+} // namespace Aidge
diff --git a/unit_tests/operator/Test_Squeeze_Op.cpp b/unit_tests/operator/Test_Squeeze_Op.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..471a1dcd1e45384b2c65da75ddee9d3ec039dc34
--- /dev/null
+++ b/unit_tests/operator/Test_Squeeze_Op.cpp
@@ -0,0 +1,457 @@
+/********************************************************************************
+ * 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/Squeeze.hpp"
+
+#include <aidge/utils/Types.h>
+#include <algorithm>
+#include <array>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/generators/catch_generators_random.hpp>
+#include <chrono>
+#include <cmath>
+#include <cstddef> // std::size_t
+#include <cstdint> // std::uint16_t
+#include <fmt/core.h>
+#include <iostream>
+#include <iterator>
+#include <memory>
+#include <numeric> // std::accumulate
+#include <ostream>
+#include <random> // std::random_device, std::mt19937, std::uniform_real_distribution
+#include <vector>
+
+#include "aidge/data/Tensor.hpp"
+#include "aidge/utils/TensorUtils.hpp"
+
+namespace Aidge {
+TEST_CASE("[core/operator] Squeeze(forwardDims)", "[Squeeze][forwardDims]") {
+  Log::setConsoleLevel(Log::Notice);
+  constexpr std::uint16_t NB_TRIALS = 10;
+  // Create a random number generator
+  auto random_seed = Catch::Generators::Detail::getSeed;
+  std::mt19937 gen(random_seed());
+
+  // Random float distribution between 0 and 1
+  constexpr int8_t max_nb_dims = 7;
+  std::uniform_real_distribution<float> tensor_value_dist(0.1f, 1.1f);
+  std::uniform_int_distribution<std::size_t> tensor_nb_dims_dist(
+      std::size_t(1), std::size_t(max_nb_dims));
+  std::uniform_int_distribution<std::size_t> tensor_dims_size_dist(
+      std::size_t(1), std::size_t(5));
+  std::uniform_int_distribution<std::size_t> nb_dims_to_squeeze_dist(
+      std::size_t(1), std::size_t(2));
+  std::uniform_int_distribution<short> idx_dims_to_squeeze_dist(-9, 8);
+
+  std::shared_ptr<Tensor> input_T = std::make_shared<Tensor>();
+
+  SECTION("ERROR : Inputs not ready") {
+    SECTION("unconnected input") {
+      std::shared_ptr<Node> squeeze_node = Squeeze();
+      auto op =
+          std::static_pointer_cast<OperatorTensor>(squeeze_node->getOperator());
+      REQUIRE_THROWS(op->forwardDims());
+    }
+
+    SECTION("empty tensor") {
+      // Create the Squeeze Operator
+      std::shared_ptr<Node> squeeze_node = Squeeze(std::vector<int8_t>({0}));
+      auto op =
+          std::static_pointer_cast<OperatorTensor>(squeeze_node->getOperator());
+      op->associateInput(0, input_T);
+
+      CHECK(op->forwardDims() == false);
+    }
+  }
+  SECTION("ERROR : nb_dims_to_squeeze>input.size()") {
+    constexpr size_t nb_dims_to_squeeze = 100;
+
+    std::vector<int8_t> dims_to_squeeze(nb_dims_to_squeeze);
+    std::generate(dims_to_squeeze.begin(), dims_to_squeeze.end(),
+                  [&gen, &idx_dims_to_squeeze_dist]() {
+                    return idx_dims_to_squeeze_dist(gen);
+                  });
+    Log::error("dims_to_sqeeze = {}", dims_to_squeeze);
+
+    std::shared_ptr<Node> squeeze_node = Squeeze(dims_to_squeeze);
+    auto op =
+        std::static_pointer_cast<OperatorTensor>(squeeze_node->getOperator());
+
+    // input tensor
+    const std::size_t nb_dims = tensor_nb_dims_dist(gen);
+    std::vector<std::size_t> dims_in(nb_dims);
+    std::generate(dims_in.begin(), dims_in.end(),
+                  [&tensor_dims_size_dist, &gen]() {
+                    return tensor_dims_size_dist(gen);
+                  });
+
+    // Test
+    input_T->resize(dims_in);
+    op->setInput(0, input_T);
+    REQUIRE_THROWS(op->forwardDims());
+  }
+  SECTION("Compare with reference output") {
+    SECTION("axes is given via attribute") {
+      SECTION("Squeeze a 1-sized-axis") {
+        int8_t nb_dims = 4;
+        std::shared_ptr<Node> squeeze_node = Squeeze(std::vector<int8_t>({0}));
+        auto op = std::static_pointer_cast<OperatorTensor>(
+            squeeze_node->getOperator());
+        op->associateInput(0, input_T);
+
+        std::vector<DimSize_t> dims_in{1, 2, 3, 4};
+        input_T->resize(dims_in);
+
+        CHECK(op->forwardDims());
+        CHECK(op->getOutput(0)->dims() == std::vector<DimSize_t>({2, 3, 4}));
+        CHECK((op->getOutput(0)->dims().size()) == 3);
+      }
+      SECTION("Squeeze multiple 1-sized axes") {
+        // test should be successful
+        std::shared_ptr<Node> squeeze_node =
+            Squeeze(std::vector<int8_t>({1, -4}));
+        auto op = std::static_pointer_cast<OperatorTensor>(
+            squeeze_node->getOperator());
+        op->associateInput(0, input_T);
+
+        std::vector<DimSize_t> dims_in{1, 1, 13, 200};
+        input_T->resize(dims_in);
+
+        CHECK(op->forwardDims());
+        CHECK(op->getOutput(0)->dims() == std::vector<DimSize_t>{13, 200});
+        CHECK((op->getOutput(0)->dims().size()) == 2);
+      }
+      SECTION("Squeeze a non-1-Sized axis") {
+        int8_t nb_dims = 4;
+        std::shared_ptr<Node> squeeze_node = Squeeze(std::vector<int8_t>({3}));
+        auto op = std::static_pointer_cast<OperatorTensor>(
+            squeeze_node->getOperator());
+        op->associateInput(0, input_T);
+
+        std::vector<DimSize_t> dims_in{1, 2, 3, 4};
+        input_T->resize(dims_in);
+
+        REQUIRE_THROWS(op->forwardDims());
+      }
+      SECTION("Squeeze multiple non-sized-axes") {
+        std::shared_ptr<Node> squeeze_node =
+            Squeeze(std::vector<int8_t>({1, -2}));
+        auto op = std::static_pointer_cast<OperatorTensor>(
+            squeeze_node->getOperator());
+        op->associateInput(0, input_T);
+
+        std::array<DimSize_t, 3> dims_in{2, 3, 4};
+        input_T->resize(dims_in);
+
+        REQUIRE_THROWS((op->forwardDims()));
+      }
+    }
+    SECTION("axes is given via tensor") {
+      SECTION("tensor is empty") {
+        // arguments here should be overriden by axes_T values
+        std::shared_ptr<Node> myUnsqueeze =
+            Squeeze(std::vector<std::int8_t>({0, 4}));
+        auto op = std::static_pointer_cast<OperatorTensor>(
+            myUnsqueeze->getOperator());
+        op->associateInput(0, input_T);
+
+        auto axes_T =
+            std::make_shared<Aidge::Tensor>(std::vector<DimSize_t>({}));
+        axes_T->setDataType(Aidge::DataType::Int8);
+        axes_T->setBackend("cpu");
+
+        std::vector<DimSize_t> dims_in{3, 1, 4, 1, 1, 5};
+        input_T->resize(dims_in);
+        op->associateInput(0, input_T);
+        op->associateInput(1, axes_T);
+
+        CHECK(op->forwardDims(true));
+        CHECK(op->getOutput(0)->dims() == std::vector<DimSize_t>({3, 4, 5}));
+      }
+      SECTION("tensor not empty") {
+        // arguments here should be overriden by axes_T values
+        std::shared_ptr<Node> myUnsqueeze =
+            Squeeze(std::vector<std::int8_t>({3, 1}));
+        auto op = std::static_pointer_cast<OperatorTensor>(
+            myUnsqueeze->getOperator());
+        op->associateInput(0, input_T);
+
+        auto axes_T =
+            std::make_shared<Aidge::Tensor>(Aidge::Array1D<int8_t, 2>({0, 3}));
+        axes_T->setDataType(Aidge::DataType::Int8);
+        axes_T->setBackend("cpu");
+
+        std::vector<DimSize_t> dims_in{1, 3, 4, 1, 5};
+        input_T->resize(dims_in);
+        op->associateInput(0, input_T);
+        op->associateInput(1, axes_T);
+
+        CHECK(op->forwardDims(true) == true);
+        CHECK(op->getOutput(0)->dims() == std::vector<DimSize_t>({3, 4, 5}));
+      }
+    }
+  }
+  SECTION("Squeeze()") {
+    // Create the Operator
+    std::shared_ptr<Node> squeeze_node = Squeeze();
+    auto op =
+        std::static_pointer_cast<OperatorTensor>(squeeze_node->getOperator());
+    op->associateInput(0, input_T);
+
+    for (uint16_t trial = 0; trial < NB_TRIALS; ++trial) {
+      // input tensor
+      const std::size_t nb_dims = tensor_nb_dims_dist(gen);
+      std::vector<std::size_t> dims_in(nb_dims);
+
+      std::generate(dims_in.begin(), dims_in.end(),
+                    [&gen, &tensor_dims_size_dist]() {
+                      return tensor_dims_size_dist(gen);
+                    });
+
+      // output tensor
+      std::vector<DimSize_t> dims_out;
+      dims_out.reserve(dims_in.size());
+      std::copy_if(dims_in.begin(), dims_in.end(), std::back_inserter(dims_out),
+                   [](DimSize_t dim) { return dim != 1; });
+      // Test
+      input_T->resize(dims_in);
+      op->setInput(0, input_T);
+      CHECK(op->forwardDims() == true);
+      CHECK(op->getOutput(0)->dims() == dims_out);
+
+      int nb_ones = std::count_if(dims_in.begin(), dims_in.end(),
+                                  [](int8_t dim) { return dim == 1; });
+      CHECK((op->getInput(0)->dims().size() -
+             op->getOutput(0)->dims().size()) == nb_ones);
+    }
+  }
+  SECTION("Squeeze({N,...})") {
+    int number_of_operation{0};
+    for (uint16_t trial = 0; trial < NB_TRIALS; ++trial) {
+      // Create the Operator
+      size_t nb_dims_to_squeeze = nb_dims_to_squeeze_dist(gen);
+      std::vector<int8_t> dims_to_squeeze(nb_dims_to_squeeze);
+      std::generate(dims_to_squeeze.begin(), dims_to_squeeze.end(),
+                    [&gen, &idx_dims_to_squeeze_dist]() {
+                      return idx_dims_to_squeeze_dist(gen);
+                    });
+      std::shared_ptr<Node> squeeze_node = Squeeze({dims_to_squeeze});
+      auto op =
+          std::static_pointer_cast<OperatorTensor>(squeeze_node->getOperator());
+      op->associateInput(0, input_T);
+
+      // input tensor
+      const std::size_t nb_dims_tensor = tensor_nb_dims_dist(gen);
+      std::vector<std::size_t> dims_in(nb_dims_tensor);
+      std::generate(dims_in.begin(), dims_in.end(),
+                    [&gen, &tensor_dims_size_dist]() {
+                      return tensor_dims_size_dist(gen);
+                    });
+      input_T->resize(dims_in);
+      op->setInput(0, input_T);
+
+      // rectifying indexes
+      std::transform(dims_to_squeeze.begin(), dims_to_squeeze.end(),
+                     dims_to_squeeze.begin(),
+                     [&nb_dims_tensor](int8_t dim_to_squeeze) {
+                       return dim_to_squeeze < 0
+                                  ? dim_to_squeeze + nb_dims_tensor
+                                  : dim_to_squeeze;
+                     });
+      std::sort(dims_to_squeeze.begin(), dims_to_squeeze.end());
+      auto it = std::unique(dims_to_squeeze.begin(), dims_to_squeeze.end());
+      dims_to_squeeze.erase(it, dims_to_squeeze.end());
+
+      // ensuring arguments given to Squeeze are good
+      bool not_in_bounds = false;
+      bool dim_to_squeeze_not_1_sized = false;
+      for (const auto dim_to_squeeze : dims_to_squeeze) {
+        not_in_bounds = dim_to_squeeze >= nb_dims_tensor;
+        if (not_in_bounds) {
+          break;
+        }
+        dim_to_squeeze_not_1_sized = dims_in.at(dim_to_squeeze) != 1;
+        if (dim_to_squeeze_not_1_sized) {
+          break;
+        }
+      }
+
+      if (nb_dims_tensor > max_nb_dims || not_in_bounds ||
+          dim_to_squeeze_not_1_sized) {
+        REQUIRE_THROWS(op->forwardDims());
+      } else {
+        // output tensor
+        int i = 0;
+        std::vector<DimSize_t> dims_out;
+        dims_out.reserve(dims_in.size());
+        std::copy_if(dims_in.begin(), dims_in.end(),
+                     std::back_inserter(dims_out),
+                     [&dims_to_squeeze, &i](DimSize_t dim) {
+                       bool ok = dim != 1 ||
+                                 !std::binary_search(dims_to_squeeze.begin(),
+                                                     dims_to_squeeze.end(), i);
+                       i++; // incrementing counter since C++ has not enumerate
+                            // fctn (until C++23)
+                       return ok;
+                     });
+        CHECK(op->forwardDims() == true);
+        CHECK(op->getOutput(0)->dims() == dims_out);
+      }
+    }
+  }
+}
+
+TEST_CASE("[core/operator] Squeeze(forward)", "[Squeeze][forward]") {
+  Log::setConsoleLevel(Log::Notice);
+  constexpr std::uint16_t NB_TRIALS = 10;
+  // Create a random number generator
+  auto random_seed = Catch::Generators::Detail::getSeed;
+  std::mt19937 gen(random_seed());
+
+  constexpr int8_t max_nb_dims = 7;
+  std::uniform_real_distribution<float> tensor_value_dist(0.1f, 1.1f);
+  std::uniform_int_distribution<std::size_t> tensor_nb_dims_dist(
+      std::size_t(1), std::size_t(max_nb_dims));
+  std::uniform_int_distribution<std::size_t> tensor_dims_size_dist(
+      std::size_t(1), std::size_t(5));
+  std::uniform_int_distribution<std::size_t> nb_dims_to_squeeze_dist(
+      std::size_t(1), std::size_t(2));
+  std::uniform_int_distribution<short> idx_dims_to_squeeze_dist(-9, 8);
+
+  std::shared_ptr<Tensor> input_T = std::make_shared<Tensor>();
+
+  // BENCHMARKING
+  std::chrono::time_point<std::chrono::system_clock> start;
+  std::chrono::time_point<std::chrono::system_clock> end;
+  std::chrono::duration<double, std::micro> duration{};
+
+  Log::setConsoleLevel(Log::Notice);
+  int number_of_operation{0};
+  for (uint16_t trial = 0; trial < NB_TRIALS; ++trial) {
+    // Create the Operator
+    size_t nb_dims_to_squeeze = nb_dims_to_squeeze_dist(gen);
+    std::vector<int8_t> dims_to_squeeze(nb_dims_to_squeeze);
+    std::generate(dims_to_squeeze.begin(), dims_to_squeeze.end(),
+                  [&gen, &idx_dims_to_squeeze_dist]() {
+                    return idx_dims_to_squeeze_dist(gen);
+                  });
+    std::shared_ptr<Node> squeeze_node = Squeeze({dims_to_squeeze});
+    auto op =
+        std::static_pointer_cast<OperatorTensor>(squeeze_node->getOperator());
+    op->setDataType(DataType::Float32);
+    op->setBackend("cpu");
+
+    // input tensor
+    const std::size_t nb_dims_tensor = tensor_nb_dims_dist(gen);
+    std::vector<std::size_t> dims_in(nb_dims_tensor);
+    std::generate(dims_in.begin(), dims_in.end(),
+                  [&gen, &tensor_dims_size_dist]() {
+                    return tensor_dims_size_dist(gen);
+                  });
+    input_T->resize(dims_in);
+    op->setInput(0, input_T);
+
+    // rectifying indexes
+    std::transform(dims_to_squeeze.begin(), dims_to_squeeze.end(),
+                   dims_to_squeeze.begin(),
+                   [&nb_dims_tensor](int8_t dim_to_squeeze) {
+                     return dim_to_squeeze < 0 ? dim_to_squeeze + nb_dims_tensor
+                                               : dim_to_squeeze;
+                   });
+
+    // ensuring arguments given to Squeeze are good
+    bool not_in_bounds = false;
+    bool dim_to_squeeze_not_1_sized = false;
+    for (const auto dim_to_squeeze : dims_to_squeeze) {
+      not_in_bounds = dim_to_squeeze >= nb_dims_tensor;
+      if (not_in_bounds) {
+        break;
+      }
+      dim_to_squeeze_not_1_sized = dims_in.at(dim_to_squeeze) != 1;
+      if (dim_to_squeeze_not_1_sized) {
+        break;
+      }
+    }
+    if (nb_dims_tensor > max_nb_dims || not_in_bounds ||
+        dim_to_squeeze_not_1_sized) {
+      REQUIRE_THROWS(op->forwardDims());
+    } else {
+      // output tensor
+      int i = 0;
+      std::vector<DimSize_t> dims_out;
+      dims_out.reserve(dims_in.size());
+      for (DimIdx_t i = 0; i < dims_in.size(); ++i) {
+        if (dims_in[i] == 1 &&
+            std::find(dims_to_squeeze.begin(), dims_to_squeeze.end(), i) !=
+                dims_to_squeeze.end()) {
+          continue;
+        }
+        dims_out.push_back(dims_in[i]);
+      }
+      CHECK(op->forwardDims());
+      CHECK(op->getOutput(0)->dims() == dims_out);
+
+      SECTION("forward") {
+        // Create the input Tensor
+        std::shared_ptr<Tensor> input_T = std::make_shared<Tensor>();
+        input_T->setDataType(DataType::Float32);
+        input_T->setBackend("cpu");
+        op->associateInput(0, input_T);
+
+        // Create results Tensor
+        std::shared_ptr<Tensor> result_T = std::make_shared<Tensor>();
+        result_T->setDataType(DataType::Float32);
+        result_T->setBackend("cpu");
+
+        const std::size_t nb_elems =
+            std::accumulate(dims_in.cbegin(), dims_in.cend(), std::size_t(1),
+                            std::multiplies<std::size_t>());
+        float *array_in = new float[nb_elems];
+        for (std::size_t i = 0; i < nb_elems; ++i) {
+          float val = tensor_value_dist(gen);
+          array_in[i] = val;
+        }
+        number_of_operation += nb_elems; // Copying all values : 1
+                                         // assignation / item in the tensor
+        // input0
+        input_T->resize(dims_in);
+        input_T->getImpl()->setRawPtr(array_in, nb_elems);
+
+        result_T->resize(dims_out);
+        result_T->getImpl()->setRawPtr(array_in, nb_elems);
+
+        CHECK(op->forwardDims() == true);
+        start = std::chrono::system_clock::now();
+        REQUIRE_NOTHROW(squeeze_node->forward());
+        end = std::chrono::system_clock::now();
+        duration +=
+            std::chrono::duration_cast<std::chrono::microseconds>(end - start);
+
+        CHECK(approxEq<float>(*result_T, *(op->getOutput(0))));
+        CHECK(result_T->nbDims() == op->getOutput(0)->nbDims());
+        for (DimSize_t i = 0; i < op->getOutput(0)->nbDims(); ++i) {
+          CHECK(result_T->dims().at(i) == op->getOutput(0)->dims().at(i));
+        }
+        CHECK(approxEq<float>(*result_T, *(op->getOutput(0))));
+
+        delete[] array_in;
+      }
+      std::cout << "Squeeze total execution time : " << duration.count() << "µs"
+                << std::endl;
+      std::cout << "Number of operations : " << number_of_operation
+                << std::endl;
+      std::cout << "Operation / µs = " << number_of_operation / duration.count()
+                << std::endl;
+    }
+  }
+}
+
+} // namespace Aidge