Repository: IBM/aihwkit (language: python)

Issue:
Add experiments module
## Description and motivation

As a convenient way for running specific use cases (experiments) and encapsulating in an high-level and easy to handle and serialize construct, it would be good to add a new module. This new abstraction would facilitate exploring the toolkit and repeating similar use cases, and also facilitate future integrations.

## Proposed solution

Add an initial module with a specific experiment (training, based on the examples) and an interface for creating the experiment and executing it.

## Alternatives and other information

There are existing packages that provide support for "high level" pytorch interfacing. As the feature evolves, we should evaluate them and look into the possibility of making use of a suitable one - for this initial effort, the feature is envisaged to be small enough that a custom solution would fit the bill.

Add experiments module
## Description and motivation

As a convenient way for running specific use cases (experiments) and encapsulating in an high-level and easy to handle and serialize construct, it would be good to add a new module. This new abstraction would facilitate exploring the toolkit and repeating similar use cases, and also facilitate future integrations.

## Proposed solution

Add an initial module with a specific experiment (training, based on the examples) and an interface for creating the experiment and executing it.

## Alternatives and other information

There are existing packages that provide support for "high level" pytorch interfacing. As the feature evolves, we should evaluate them and look into the possibility of making use of a suitable one - for this initial effort, the feature is envisaged to be small enough that a custom solution would fit the bill.



Discussion / hints:

Fix patch:
diff --git a/.pylintrc b/.pylintrc
index 0464ed54..332b478e 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -10,7 +10,7 @@ fail-under=10
 
 # Add files or directories to the blacklist. They should be base names, not
 # paths.
-ignore=CVS
+ignore=CVS,definitions
 
 # Add files or directories matching the regex patterns to the blacklist. The
 # regex matches against base names, not paths.
@@ -376,7 +376,8 @@ ignored-classes=optparse.Values,thread._local,_thread._local
 # supports qualified module names, as well as Unix pattern matching.
 ignored-modules=torch,torch.optim,aihwkit.simulator.rpu_base,
                 aihwkit.simulator.rpu_base.tiles,
-                aihwkit.simulator.rpu_base.devices
+                aihwkit.simulator.rpu_base.devices,
+                aihwkit.cloud.converter.definitions
 
 # Show a hint with possible names when a member name was not found. The aspect
 # of finding the hint is based on edit distance.
diff --git a/docs/source/api/aihwkit.cloud.converter.definitions.rst b/docs/source/api/aihwkit.cloud.converter.definitions.rst
new file mode 100644
index 00000000..55a673f6
--- /dev/null
+++ b/docs/source/api/aihwkit.cloud.converter.definitions.rst
@@ -0,0 +1,13 @@
+aihwkit.cloud.converter.definitions package
+===========================================
+
+.. automodule:: aihwkit.cloud.converter.definitions
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+Submodules
+----------
+
+.. toctree::
+   :maxdepth: 4
diff --git a/docs/source/api/aihwkit.cloud.converter.exceptions.rst b/docs/source/api/aihwkit.cloud.converter.exceptions.rst
new file mode 100644
index 00000000..22c5aa16
--- /dev/null
+++ b/docs/source/api/aihwkit.cloud.converter.exceptions.rst
@@ -0,0 +1,7 @@
+aihwkit.cloud.converter.exceptions module
+=========================================
+
+.. automodule:: aihwkit.cloud.converter.exceptions
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/docs/source/api/aihwkit.cloud.converter.rst b/docs/source/api/aihwkit.cloud.converter.rst
new file mode 100644
index 00000000..90524e23
--- /dev/null
+++ b/docs/source/api/aihwkit.cloud.converter.rst
@@ -0,0 +1,17 @@
+aihwkit.cloud.converter package
+===============================
+
+.. automodule:: aihwkit.cloud.converter
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+Submodules
+----------
+
+.. toctree::
+   :maxdepth: 4
+
+   aihwkit.cloud.converter.exceptions
+   aihwkit.cloud.converter.definitions
+   aihwkit.cloud.converter.v1
diff --git a/docs/source/api/aihwkit.cloud.converter.v1.rst b/docs/source/api/aihwkit.cloud.converter.v1.rst
new file mode 100644
index 00000000..a9182c91
--- /dev/null
+++ b/docs/source/api/aihwkit.cloud.converter.v1.rst
@@ -0,0 +1,13 @@
+aihwkit.cloud.converter.v1 package
+==================================
+
+.. automodule:: aihwkit.cloud.converter.v1
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+Submodules
+----------
+
+.. toctree::
+   :maxdepth: 4
diff --git a/docs/source/api/aihwkit.cloud.rst b/docs/source/api/aihwkit.cloud.rst
new file mode 100644
index 00000000..5995cba5
--- /dev/null
+++ b/docs/source/api/aihwkit.cloud.rst
@@ -0,0 +1,15 @@
+aihwkit.cloud package
+=====================
+
+.. automodule:: aihwkit.cloud
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+Submodules
+----------
+
+.. toctree::
+   :maxdepth: 4
+
+   aihwkit.cloud.converter
diff --git a/docs/source/api/aihwkit.rst b/docs/source/api/aihwkit.rst
index 8304b68d..02872695 100644
--- a/docs/source/api/aihwkit.rst
+++ b/docs/source/api/aihwkit.rst
@@ -12,6 +12,7 @@ Subpackages
 .. toctree::
    :maxdepth: 4
 
+   aihwkit.cloud
    aihwkit.exceptions
    aihwkit.experiments
    aihwkit.simulator
diff --git a/docs/source/api_reference.rst b/docs/source/api_reference.rst
index 08c82e15..01c08575 100644
--- a/docs/source/api_reference.rst
+++ b/docs/source/api_reference.rst
@@ -5,6 +5,8 @@ API Reference
    :recursive:
 
    aihwkit
+   aihwkit.cloud
+   aihwkit.cloud.converter
    aihwkit.exceptions
    aihwkit.experiments
    aihwkit.experiments.experiments
diff --git a/requirements.txt b/requirements.txt
index 70b99a63..31001e39 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,3 +9,4 @@ scipy
 requests>=2.25,<3
 numpy>=1.18
 dataclasses==0.7; python_version < '3.7'
+protobuf>=3.13.0,<4
diff --git a/setup.cfg b/setup.cfg
index fcbfe50f..bfa8000c 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,6 @@
 [pycodestyle]
 max-line-length = 100
+exclude = *_pb2.py
 
 [mypy]
 python_version = 3.6
@@ -17,3 +18,6 @@ show_none_errors = True
 [mypy-torch.*]
 follow_imports = skip
 follow_imports_for_stubs = True
+
+[mypy-aihwkit.cloud.converter.definitions.*]
+ignore_errors = True
diff --git a/setup.py b/setup.py
index 7e6f514e..d5d92a79 100644
--- a/setup.py
+++ b/setup.py
@@ -21,7 +21,8 @@
     'scipy',
     'requests>=2.25,<3',
     'numpy>=1.18',
-    'dataclasses==0.7; python_version < "3.7"'
+    'dataclasses==0.7; python_version < "3.7"',
+    'protobuf>=3.13.0,<4'
 ]
 
 
diff --git a/src/aihwkit/cloud/__init__.py b/src/aihwkit/cloud/__init__.py
new file mode 100644
index 00000000..3b899826
--- /dev/null
+++ b/src/aihwkit/cloud/__init__.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+
+# (C) Copyright 2020, 2021 IBM. All Rights Reserved.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+"""Functionality related to the cloud client for AIHW Composer API."""
diff --git a/src/aihwkit/cloud/converter/__init__.py b/src/aihwkit/cloud/converter/__init__.py
new file mode 100644
index 00000000..c8c289b2
--- /dev/null
+++ b/src/aihwkit/cloud/converter/__init__.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+
+# (C) Copyright 2020, 2021 IBM. All Rights Reserved.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+"""Conversion utilities for interacting with the AIHW Composer API."""
diff --git a/src/aihwkit/cloud/converter/definitions/__init__.py b/src/aihwkit/cloud/converter/definitions/__init__.py
new file mode 100644
index 00000000..4b8c6b9a
--- /dev/null
+++ b/src/aihwkit/cloud/converter/definitions/__init__.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+
+# (C) Copyright 2020, 2021 IBM. All Rights Reserved.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+"""Protobuf definitions for the AIHW Composer API."""
diff --git a/src/aihwkit/cloud/converter/definitions/common_pb2.py b/src/aihwkit/cloud/converter/definitions/common_pb2.py
new file mode 100644
index 00000000..6dc5576d
--- /dev/null
+++ b/src/aihwkit/cloud/converter/definitions/common_pb2.py
@@ -0,0 +1,512 @@
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: common.proto
+
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from . import onnx_common_pb2 as onnx__common__pb2
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+  name='common.proto',
+  package='aihwx',
+  syntax='proto2',
+  serialized_options=None,
+  serialized_pb=_b('\n\x0c\x63ommon.proto\x12\x05\x61ihwx\x1a\x11onnx_common.proto\"F\n\x0ePrimitiveProto\x12\n\n\x02id\x18\x01 \x02(\t\x12(\n\targuments\x18\x02 \x03(\x0b\x32\x15.aihwx.AttributeProto\"F\n\x0eTransformProto\x12\n\n\x02id\x18\x01 \x02(\t\x12(\n\targuments\x18\x02 \x03(\x0b\x32\x15.aihwx.AttributeProto\"O\n\x17\x41\x63tivationFunctionProto\x12\n\n\x02id\x18\x01 \x02(\t\x12(\n\targuments\x18\x02 \x03(\x0b\x32\x15.aihwx.AttributeProto\"m\n\nLayerProto\x12\n\n\x02id\x18\x01 \x02(\t\x12(\n\targuments\x18\x02 \x03(\x0b\x32\x15.aihwx.AttributeProto\x12)\n\nstate_dict\x18\x03 \x03(\x0b\x32\x15.aihwx.AttributeProto\"\x86\x01\n\x19LayerOrActivationFunction\x12=\n\x13\x61\x63tivation_function\x18\x01 \x01(\x0b\x32\x1e.aihwx.ActivationFunctionProtoH\x00\x12\"\n\x05layer\x18\x02 \x01(\x0b\x32\x11.aihwx.LayerProtoH\x00\x42\x06\n\x04item\"F\n\x0eOptimizerProto\x12\n\n\x02id\x18\x01 \x02(\t\x12(\n\targuments\x18\x02 \x03(\x0b\x32\x15.aihwx.AttributeProto\"F\n\x0eSchedulerProto\x12\n\n\x02id\x18\x01 \x02(\t\x12(\n\targuments\x18\x02 \x03(\x0b\x32\x15.aihwx.AttributeProto\"I\n\x11LossFunctionProto\x12\n\n\x02id\x18\x01 \x02(\t\x12(\n\targuments\x18\x02 \x03(\x0b\x32\x15.aihwx.AttributeProto\";\n\x07Network\x12\x30\n\x06layers\x18\x01 \x03(\x0b\x32 .aihwx.LayerOrActivationFunction\"(\n\x07Version\x12\x0e\n\x06schema\x18\x01 \x02(\x03\x12\r\n\x05opset\x18\x02 \x02(\x03')
+  ,
+  dependencies=[onnx__common__pb2.DESCRIPTOR,])
+
+
+
+
+_PRIMITIVEPROTO = _descriptor.Descriptor(
+  name='PrimitiveProto',
+  full_name='aihwx.PrimitiveProto',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='id', full_name='aihwx.PrimitiveProto.id', index=0,
+      number=1, type=9, cpp_type=9, label=2,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='arguments', full_name='aihwx.PrimitiveProto.arguments', index=1,
+      number=2, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=42,
+  serialized_end=112,
+)
+
+
+_TRANSFORMPROTO = _descriptor.Descriptor(
+  name='TransformProto',
+  full_name='aihwx.TransformProto',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='id', full_name='aihwx.TransformProto.id', index=0,
+      number=1, type=9, cpp_type=9, label=2,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='arguments', full_name='aihwx.TransformProto.arguments', index=1,
+      number=2, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=114,
+  serialized_end=184,
+)
+
+
+_ACTIVATIONFUNCTIONPROTO = _descriptor.Descriptor(
+  name='ActivationFunctionProto',
+  full_name='aihwx.ActivationFunctionProto',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='id', full_name='aihwx.ActivationFunctionProto.id', index=0,
+      number=1, type=9, cpp_type=9, label=2,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='arguments', full_name='aihwx.ActivationFunctionProto.arguments', index=1,
+      number=2, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=186,
+  serialized_end=265,
+)
+
+
+_LAYERPROTO = _descriptor.Descriptor(
+  name='LayerProto',
+  full_name='aihwx.LayerProto',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='id', full_name='aihwx.LayerProto.id', index=0,
+      number=1, type=9, cpp_type=9, label=2,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='arguments', full_name='aihwx.LayerProto.arguments', index=1,
+      number=2, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='state_dict', full_name='aihwx.LayerProto.state_dict', index=2,
+      number=3, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=267,
+  serialized_end=376,
+)
+
+
+_LAYERORACTIVATIONFUNCTION = _descriptor.Descriptor(
+  name='LayerOrActivationFunction',
+  full_name='aihwx.LayerOrActivationFunction',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='activation_function', full_name='aihwx.LayerOrActivationFunction.activation_function', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='layer', full_name='aihwx.LayerOrActivationFunction.layer', index=1,
+      number=2, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+    _descriptor.OneofDescriptor(
+      name='item', full_name='aihwx.LayerOrActivationFunction.item',
+      index=0, containing_type=None, fields=[]),
+  ],
+  serialized_start=379,
+  serialized_end=513,
+)
+
+
+_OPTIMIZERPROTO = _descriptor.Descriptor(
+  name='OptimizerProto',
+  full_name='aihwx.OptimizerProto',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='id', full_name='aihwx.OptimizerProto.id', index=0,
+      number=1, type=9, cpp_type=9, label=2,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='arguments', full_name='aihwx.OptimizerProto.arguments', index=1,
+      number=2, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=515,
+  serialized_end=585,
+)
+
+
+_SCHEDULERPROTO = _descriptor.Descriptor(
+  name='SchedulerProto',
+  full_name='aihwx.SchedulerProto',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='id', full_name='aihwx.SchedulerProto.id', index=0,
+      number=1, type=9, cpp_type=9, label=2,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='arguments', full_name='aihwx.SchedulerProto.arguments', index=1,
+      number=2, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=587,
+  serialized_end=657,
+)
+
+
+_LOSSFUNCTIONPROTO = _descriptor.Descriptor(
+  name='LossFunctionProto',
+  full_name='aihwx.LossFunctionProto',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='id', full_name='aihwx.LossFunctionProto.id', index=0,
+      number=1, type=9, cpp_type=9, label=2,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='arguments', full_name='aihwx.LossFunctionProto.arguments', index=1,
+      number=2, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=659,
+  serialized_end=732,
+)
+
+
+_NETWORK = _descriptor.Descriptor(
+  name='Network',
+  full_name='aihwx.Network',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='layers', full_name='aihwx.Network.layers', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=734,
+  serialized_end=793,
+)
+
+
+_VERSION = _descriptor.Descriptor(
+  name='Version',
+  full_name='aihwx.Version',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='schema', full_name='aihwx.Version.schema', index=0,
+      number=1, type=3, cpp_type=2, label=2,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='opset', full_name='aihwx.Version.opset', index=1,
+      number=2, type=3, cpp_type=2, label=2,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=795,
+  serialized_end=835,
+)
+
+_PRIMITIVEPROTO.fields_by_name['arguments'].message_type = onnx__common__pb2._ATTRIBUTEPROTO
+_TRANSFORMPROTO.fields_by_name['arguments'].message_type = onnx__common__pb2._ATTRIBUTEPROTO
+_ACTIVATIONFUNCTIONPROTO.fields_by_name['arguments'].message_type = onnx__common__pb2._ATTRIBUTEPROTO
+_LAYERPROTO.fields_by_name['arguments'].message_type = onnx__common__pb2._ATTRIBUTEPROTO
+_LAYERPROTO.fields_by_name['state_dict'].message_type = onnx__common__pb2._ATTRIBUTEPROTO
+_LAYERORACTIVATIONFUNCTION.fields_by_name['activation_function'].message_type = _ACTIVATIONFUNCTIONPROTO
+_LAYERORACTIVATIONFUNCTION.fields_by_name['layer'].message_type = _LAYERPROTO
+_LAYERORACTIVATIONFUNCTION.oneofs_by_name['item'].fields.append(
+  _LAYERORACTIVATIONFUNCTION.fields_by_name['activation_function'])
+_LAYERORACTIVATIONFUNCTION.fields_by_name['activation_function'].containing_oneof = _LAYERORACTIVATIONFUNCTION.oneofs_by_name['item']
+_LAYERORACTIVATIONFUNCTION.oneofs_by_name['item'].fields.append(
+  _LAYERORACTIVATIONFUNCTION.fields_by_name['layer'])
+_LAYERORACTIVATIONFUNCTION.fields_by_name['layer'].containing_oneof = _LAYERORACTIVATIONFUNCTION.oneofs_by_name['item']
+_OPTIMIZERPROTO.fields_by_name['arguments'].message_type = onnx__common__pb2._ATTRIBUTEPROTO
+_SCHEDULERPROTO.fields_by_name['arguments'].message_type = onnx__common__pb2._ATTRIBUTEPROTO
+_LOSSFUNCTIONPROTO.fields_by_name['arguments'].message_type = onnx__common__pb2._ATTRIBUTEPROTO
+_NETWORK.fields_by_name['layers'].message_type = _LAYERORACTIVATIONFUNCTION
+DESCRIPTOR.message_types_by_name['PrimitiveProto'] = _PRIMITIVEPROTO
+DESCRIPTOR.message_types_by_name['TransformProto'] = _TRANSFORMPROTO
+DESCRIPTOR.message_types_by_name['ActivationFunctionProto'] = _ACTIVATIONFUNCTIONPROTO
+DESCRIPTOR.message_types_by_name['LayerProto'] = _LAYERPROTO
+DESCRIPTOR.message_types_by_name['LayerOrActivationFunction'] = _LAYERORACTIVATIONFUNCTION
+DESCRIPTOR.message_types_by_name['OptimizerProto'] = _OPTIMIZERPROTO
+DESCRIPTOR.message_types_by_name['SchedulerProto'] = _SCHEDULERPROTO
+DESCRIPTOR.message_types_by_name['LossFunctionProto'] = _LOSSFUNCTIONPROTO
+DESCRIPTOR.message_types_by_name['Network'] = _NETWORK
+DESCRIPTOR.message_types_by_name['Version'] = _VERSION
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+PrimitiveProto = _reflection.GeneratedProtocolMessageType('PrimitiveProto', (_message.Message,), dict(
+  DESCRIPTOR = _PRIMITIVEPROTO,
+  __module__ = 'common_pb2'
+  # @@protoc_insertion_point(class_scope:aihwx.PrimitiveProto)
+  ))
+_sym_db.RegisterMessage(PrimitiveProto)
+
+TransformProto = _reflection.GeneratedProtocolMessageType('TransformProto', (_message.Message,), dict(
+  DESCRIPTOR = _TRANSFORMPROTO,
+  __module__ = 'common_pb2'
+  # @@protoc_insertion_point(class_scope:aihwx.TransformProto)
+  ))
+_sym_db.RegisterMessage(TransformProto)
+
+ActivationFunctionProto = _reflection.GeneratedProtocolMessageType('ActivationFunctionProto', (_message.Message,), dict(
+  DESCRIPTOR = _ACTIVATIONFUNCTIONPROTO,
+  __module__ = 'common_pb2'
+  # @@protoc_insertion_point(class_scope:aihwx.ActivationFunctionProto)
+  ))
+_sym_db.RegisterMessage(ActivationFunctionProto)
+
+LayerProto = _reflection.GeneratedProtocolMessageType('LayerProto', (_message.Message,), dict(
+  DESCRIPTOR = _LAYERPROTO,
+  __module__ = 'common_pb2'
+  # @@protoc_insertion_point(class_scope:aihwx.LayerProto)
+  ))
+_sym_db.RegisterMessage(LayerProto)
+
+LayerOrActivationFunction = _reflection.GeneratedProtocolMessageType('LayerOrActivationFunction', (_message.Message,), dict(
+  DESCRIPTOR = _LAYERORACTIVATIONFUNCTION,
+  __module__ = 'common_pb2'
+  # @@protoc_insertion_point(class_scope:aihwx.LayerOrActivationFunction)
+  ))
+_sym_db.RegisterMessage(LayerOrActivationFunction)
+
+OptimizerProto = _reflection.GeneratedProtocolMessageType('OptimizerProto', (_message.Message,), dict(
+  DESCRIPTOR = _OPTIMIZERPROTO,
+  __module__ = 'common_pb2'
+  # @@protoc_insertion_point(class_scope:aihwx.OptimizerProto)
+  ))
+_sym_db.RegisterMessage(OptimizerProto)
+
+SchedulerProto = _reflection.GeneratedProtocolMessageType('SchedulerProto', (_message.Message,), dict(
+  DESCRIPTOR = _SCHEDULERPROTO,
+  __module__ = 'common_pb2'
+  # @@protoc_insertion_point(class_scope:aihwx.SchedulerProto)
+  ))
+_sym_db.RegisterMessage(SchedulerProto)
+
+LossFunctionProto = _reflection.GeneratedProtocolMessageType('LossFunctionProto', (_message.Message,), dict(
+  DESCRIPTOR = _LOSSFUNCTIONPROTO,
+  __module__ = 'common_pb2'
+  # @@protoc_insertion_point(class_scope:aihwx.LossFunctionProto)
+  ))
+_sym_db.RegisterMessage(LossFunctionProto)
+
+Network = _reflection.GeneratedProtocolMessageType('Network', (_message.Message,), dict(
+  DESCRIPTOR = _NETWORK,
+  __module__ = 'common_pb2'
+  # @@protoc_insertion_point(class_scope:aihwx.Network)
+  ))
+_sym_db.RegisterMessage(Network)
+
+Version = _reflection.GeneratedProtocolMessageType('Version', (_message.Message,), dict(
+  DESCRIPTOR = _VERSION,
+  __module__ = 'common_pb2'
+  # @@protoc_insertion_point(class_scope:aihwx.Version)
+  ))
+_sym_db.RegisterMessage(Version)
+
+
+# @@protoc_insertion_point(module_scope)
diff --git a/src/aihwkit/cloud/converter/definitions/input_file_pb2.py b/src/aihwkit/cloud/converter/definitions/input_file_pb2.py
new file mode 100644
index 00000000..35433a66
--- /dev/null
+++ b/src/aihwkit/cloud/converter/definitions/input_file_pb2.py
@@ -0,0 +1,213 @@
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: input_file.proto
+
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from . import common_pb2 as common__pb2
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+  name='input_file.proto',
+  package='aihwx',
+  syntax='proto2',
+  serialized_options=None,
+  serialized_pb=_b('\n\x10input_file.proto\x12\x05\x61ihwx\x1a\x0c\x63ommon.proto\"\\\n\x07\x44\x61taset\x12\x12\n\ndataset_id\x18\x01 \x02(\t\x12)\n\ntransforms\x18\x02 \x03(\x0b\x32\x15.aihwx.TransformProto\x12\x12\n\nbatch_size\x18\x03 \x01(\x05\"\x9f\x01\n\x08Training\x12(\n\toptimizer\x18\x01 \x02(\x0b\x32\x15.aihwx.OptimizerProto\x12(\n\tscheduler\x18\x02 \x01(\x0b\x32\x15.aihwx.SchedulerProto\x12/\n\rloss_function\x18\x03 \x01(\x0b\x32\x18.aihwx.LossFunctionProto\x12\x0e\n\x06\x65pochs\x18\x04 \x01(\x05\"\x95\x01\n\rTrainingInput\x12\x1f\n\x07version\x18\x01 \x02(\x0b\x32\x0e.aihwx.Version\x12\x1f\n\x07\x64\x61taset\x18\x02 \x02(\x0b\x32\x0e.aihwx.Dataset\x12\x1f\n\x07network\x18\x03 \x02(\x0b\x32\x0e.aihwx.Network\x12!\n\x08training\x18\x04 \x02(\x0b\x32\x0f.aihwx.Training')
+  ,
+  dependencies=[common__pb2.DESCRIPTOR,])
+
+
+
+
+_DATASET = _descriptor.Descriptor(
+  name='Dataset',
+  full_name='aihwx.Dataset',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='dataset_id', full_name='aihwx.Dataset.dataset_id', index=0,
+      number=1, type=9, cpp_type=9, label=2,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='transforms', full_name='aihwx.Dataset.transforms', index=1,
+      number=2, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='batch_size', full_name='aihwx.Dataset.batch_size', index=2,
+      number=3, type=5, cpp_type=1, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=41,
+  serialized_end=133,
+)
+
+
+_TRAINING = _descriptor.Descriptor(
+  name='Training',
+  full_name='aihwx.Training',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='optimizer', full_name='aihwx.Training.optimizer', index=0,
+      number=1, type=11, cpp_type=10, label=2,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='scheduler', full_name='aihwx.Training.scheduler', index=1,
+      number=2, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='loss_function', full_name='aihwx.Training.loss_function', index=2,
+      number=3, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='epochs', full_name='aihwx.Training.epochs', index=3,
+      number=4, type=5, cpp_type=1, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=136,
+  serialized_end=295,
+)
+
+
+_TRAININGINPUT = _descriptor.Descriptor(
+  name='TrainingInput',
+  full_name='aihwx.TrainingInput',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='version', full_name='aihwx.TrainingInput.version', index=0,
+      number=1, type=11, cpp_type=10, label=2,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='dataset', full_name='aihwx.TrainingInput.dataset', index=1,
+      number=2, type=11, cpp_type=10, label=2,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='network', full_name='aihwx.TrainingInput.network', index=2,
+      number=3, type=11, cpp_type=10, label=2,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='training', full_name='aihwx.TrainingInput.training', index=3,
+      number=4, type=11, cpp_type=10, label=2,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=298,
+  serialized_end=447,
+)
+
+_DATASET.fields_by_name['transforms'].message_type = common__pb2._TRANSFORMPROTO
+_TRAINING.fields_by_name['optimizer'].message_type = common__pb2._OPTIMIZERPROTO
+_TRAINING.fields_by_name['scheduler'].message_type = common__pb2._SCHEDULERPROTO
+_TRAINING.fields_by_name['loss_function'].message_type = common__pb2._LOSSFUNCTIONPROTO
+_TRAININGINPUT.fields_by_name['version'].message_type = common__pb2._VERSION
+_TRAININGINPUT.fields_by_name['dataset'].message_type = _DATASET
+_TRAININGINPUT.fields_by_name['network'].message_type = common__pb2._NETWORK
+_TRAININGINPUT.fields_by_name['training'].message_type = _TRAINING
+DESCRIPTOR.message_types_by_name['Dataset'] = _DATASET
+DESCRIPTOR.message_types_by_name['Training'] = _TRAINING
+DESCRIPTOR.message_types_by_name['TrainingInput'] = _TRAININGINPUT
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+Dataset = _reflection.GeneratedProtocolMessageType('Dataset', (_message.Message,), dict(
+  DESCRIPTOR = _DATASET,
+  __module__ = 'input_file_pb2'
+  # @@protoc_insertion_point(class_scope:aihwx.Dataset)
+  ))
+_sym_db.RegisterMessage(Dataset)
+
+Training = _reflection.GeneratedProtocolMessageType('Training', (_message.Message,), dict(
+  DESCRIPTOR = _TRAINING,
+  __module__ = 'input_file_pb2'
+  # @@protoc_insertion_point(class_scope:aihwx.Training)
+  ))
+_sym_db.RegisterMessage(Training)
+
+TrainingInput = _reflection.GeneratedProtocolMessageType('TrainingInput', (_message.Message,), dict(
+  DESCRIPTOR = _TRAININGINPUT,
+  __module__ = 'input_file_pb2'
+  # @@protoc_insertion_point(class_scope:aihwx.TrainingInput)
+  ))
+_sym_db.RegisterMessage(TrainingInput)
+
+
+# @@protoc_insertion_point(module_scope)
diff --git a/src/aihwkit/cloud/converter/definitions/onnx_common_pb2.py b/src/aihwkit/cloud/converter/definitions/onnx_common_pb2.py
new file mode 100644
index 00000000..1279f041
--- /dev/null
+++ b/src/aihwkit/cloud/converter/definitions/onnx_common_pb2.py
@@ -0,0 +1,463 @@
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: onnx_common.proto
+
+# Some of the data structures in this file are derivatives of the ONNX
+# specification (https://github.com/onnx/onnx), licensed under Apache-2.0
+# as per the terms below.
+
+# Copyright (c) ONNX Project Contributors
+#
+# 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 sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+  name='onnx_common.proto',
+  package='aihwx',
+  syntax='proto2',
+  serialized_options=None,
+  serialized_pb=_b('\n\x11onnx_common.proto\x12\x05\x61ihwx\"\x91\x03\n\x0e\x41ttributeProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x31\n\x04type\x18\x14 \x01(\x0e\x32#.aihwx.AttributeProto.AttributeType\x12\t\n\x01\x66\x18\x02 \x01(\x02\x12\t\n\x01i\x18\x03 \x01(\x03\x12\t\n\x01s\x18\x04 \x01(\x0c\x12\x1d\n\x01t\x18\x05 \x01(\x0b\x32\x12.aihwx.TensorProto\x12\t\n\x01\x62\x18\x65 \x01(\x08\x12\x0e\n\x06\x66loats\x18\x07 \x03(\x02\x12\x0c\n\x04ints\x18\x08 \x03(\x03\x12\x0f\n\x07strings\x18\t \x03(\x0c\x12#\n\x07tensors\x18\n \x03(\x0b\x32\x12.aihwx.TensorProto\x12\r\n\x05\x62ools\x18\x66 \x03(\x08\"\x8f\x01\n\rAttributeType\x12\r\n\tUNDEFINED\x10\x00\x12\t\n\x05\x46LOAT\x10\x01\x12\x07\n\x03INT\x10\x02\x12\n\n\x06STRING\x10\x03\x12\n\n\x06TENSOR\x10\x04\x12\x08\n\x04\x42OOL\x10\x65\x12\n\n\x06\x46LOATS\x10\x06\x12\x08\n\x04INTS\x10\x07\x12\x0b\n\x07STRINGS\x10\x08\x12\x0b\n\x07TENSORS\x10\t\x12\t\n\x05\x42OOLS\x10\x66\"\x89\x02\n\x0bTensorProto\x12\x0c\n\x04\x64ims\x18\x01 \x03(\x03\x12\x11\n\tdata_type\x18\x02 \x01(\x05\x12\x16\n\nfloat_data\x18\x04 \x03(\x02\x42\x02\x10\x01\x12\x16\n\nint32_data\x18\x05 \x03(\x05\x42\x02\x10\x01\x12\x13\n\x0bstring_data\x18\x06 \x03(\x0c\x12\x16\n\nint64_data\x18\x07 \x03(\x03\x42\x02\x10\x01\"|\n\x08\x44\x61taType\x12\r\n\tUNDEFINED\x10\x00\x12\t\n\x05\x46LOAT\x10\x01\x12\t\n\x05UINT8\x10\x02\x12\x08\n\x04INT8\x10\x03\x12\n\n\x06UINT16\x10\x04\x12\t\n\x05INT16\x10\x05\x12\t\n\x05INT32\x10\x06\x12\t\n\x05INT64\x10\x07\x12\n\n\x06STRING\x10\x08\x12\x08\n\x04\x42OOL\x10\t\"\x96\x01\n\x10TensorShapeProto\x12.\n\x03\x64im\x18\x01 \x03(\x0b\x32!.aihwx.TensorShapeProto.Dimension\x1aR\n\tDimension\x12\x13\n\tdim_value\x18\x01 \x01(\x03H\x00\x12\x13\n\tdim_param\x18\x02 \x01(\tH\x00\x12\x12\n\ndenotation\x18\x03 \x01(\tB\x07\n\x05value')
+)
+
+
+
+_ATTRIBUTEPROTO_ATTRIBUTETYPE = _descriptor.EnumDescriptor(
+  name='AttributeType',
+  full_name='aihwx.AttributeProto.AttributeType',
+  filename=None,
+  file=DESCRIPTOR,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='UNDEFINED', index=0, number=0,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='FLOAT', index=1, number=1,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='INT', index=2, number=2,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='STRING', index=3, number=3,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='TENSOR', index=4, number=4,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='BOOL', index=5, number=101,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='FLOATS', index=6, number=6,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='INTS', index=7, number=7,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='STRINGS', index=8, number=8,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='TENSORS', index=9, number=9,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='BOOLS', index=10, number=102,
+      serialized_options=None,
+      type=None),
+  ],
+  containing_type=None,
+  serialized_options=None,
+  serialized_start=287,
+  serialized_end=430,
+)
+_sym_db.RegisterEnumDescriptor(_ATTRIBUTEPROTO_ATTRIBUTETYPE)
+
+_TENSORPROTO_DATATYPE = _descriptor.EnumDescriptor(
+  name='DataType',
+  full_name='aihwx.TensorProto.DataType',
+  filename=None,
+  file=DESCRIPTOR,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='UNDEFINED', index=0, number=0,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='FLOAT', index=1, number=1,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='UINT8', index=2, number=2,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='INT8', index=3, number=3,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='UINT16', index=4, number=4,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='INT16', index=5, number=5,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='INT32', index=6, number=6,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='INT64', index=7, number=7,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='STRING', index=8, number=8,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='BOOL', index=9, number=9,
+      serialized_options=None,
+      type=None),
+  ],
+  containing_type=None,
+  serialized_options=None,
+  serialized_start=574,
+  serialized_end=698,
+)
+_sym_db.RegisterEnumDescriptor(_TENSORPROTO_DATATYPE)
+
+
+_ATTRIBUTEPROTO = _descriptor.Descriptor(
+  name='AttributeProto',
+  full_name='aihwx.AttributeProto',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='name', full_name='aihwx.AttributeProto.name', index=0,
+      number=1, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='type', full_name='aihwx.AttributeProto.type', index=1,
+      number=20, type=14, cpp_type=8, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='f', full_name='aihwx.AttributeProto.f', index=2,
+      number=2, type=2, cpp_type=6, label=1,
+      has_default_value=False, default_value=float(0),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='i', full_name='aihwx.AttributeProto.i', index=3,
+      number=3, type=3, cpp_type=2, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='s', full_name='aihwx.AttributeProto.s', index=4,
+      number=4, type=12, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b(""),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='t', full_name='aihwx.AttributeProto.t', index=5,
+      number=5, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='b', full_name='aihwx.AttributeProto.b', index=6,
+      number=101, type=8, cpp_type=7, label=1,
+      has_default_value=False, default_value=False,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='floats', full_name='aihwx.AttributeProto.floats', index=7,
+      number=7, type=2, cpp_type=6, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='ints', full_name='aihwx.AttributeProto.ints', index=8,
+      number=8, type=3, cpp_type=2, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='strings', full_name='aihwx.AttributeProto.strings', index=9,
+      number=9, type=12, cpp_type=9, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='tensors', full_name='aihwx.AttributeProto.tensors', index=10,
+      number=10, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='bools', full_name='aihwx.AttributeProto.bools', index=11,
+      number=102, type=8, cpp_type=7, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+    _ATTRIBUTEPROTO_ATTRIBUTETYPE,
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=29,
+  serialized_end=430,
+)
+
+
+_TENSORPROTO = _descriptor.Descriptor(
+  name='TensorProto',
+  full_name='aihwx.TensorProto',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='dims', full_name='aihwx.TensorProto.dims', index=0,
+      number=1, type=3, cpp_type=2, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='data_type', full_name='aihwx.TensorProto.data_type', index=1,
+      number=2, type=5, cpp_type=1, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='float_data', full_name='aihwx.TensorProto.float_data', index=2,
+      number=4, type=2, cpp_type=6, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=_b('\020\001'), file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='int32_data', full_name='aihwx.TensorProto.int32_data', index=3,
+      number=5, type=5, cpp_type=1, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=_b('\020\001'), file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='string_data', full_name='aihwx.TensorProto.string_data', index=4,
+      number=6, type=12, cpp_type=9, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='int64_data', full_name='aihwx.TensorProto.int64_data', index=5,
+      number=7, type=3, cpp_type=2, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=_b('\020\001'), file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+    _TENSORPROTO_DATATYPE,
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=433,
+  serialized_end=698,
+)
+
+
+_TENSORSHAPEPROTO_DIMENSION = _descriptor.Descriptor(
+  name='Dimension',
+  full_name='aihwx.TensorShapeProto.Dimension',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='dim_value', full_name='aihwx.TensorShapeProto.Dimension.dim_value', index=0,
+      number=1, type=3, cpp_type=2, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='dim_param', full_name='aihwx.TensorShapeProto.Dimension.dim_param', index=1,
+      number=2, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='denotation', full_name='aihwx.TensorShapeProto.Dimension.denotation', index=2,
+      number=3, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+    _descriptor.OneofDescriptor(
+      name='value', full_name='aihwx.TensorShapeProto.Dimension.value',
+      index=0, containing_type=None, fields=[]),
+  ],
+  serialized_start=769,
+  serialized_end=851,
+)
+
+_TENSORSHAPEPROTO = _descriptor.Descriptor(
+  name='TensorShapeProto',
+  full_name='aihwx.TensorShapeProto',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='dim', full_name='aihwx.TensorShapeProto.dim', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[_TENSORSHAPEPROTO_DIMENSION, ],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=701,
+  serialized_end=851,
+)
+
+_ATTRIBUTEPROTO.fields_by_name['type'].enum_type = _ATTRIBUTEPROTO_ATTRIBUTETYPE
+_ATTRIBUTEPROTO.fields_by_name['t'].message_type = _TENSORPROTO
+_ATTRIBUTEPROTO.fields_by_name['tensors'].message_type = _TENSORPROTO
+_ATTRIBUTEPROTO_ATTRIBUTETYPE.containing_type = _ATTRIBUTEPROTO
+_TENSORPROTO_DATATYPE.containing_type = _TENSORPROTO
+_TENSORSHAPEPROTO_DIMENSION.containing_type = _TENSORSHAPEPROTO
+_TENSORSHAPEPROTO_DIMENSION.oneofs_by_name['value'].fields.append(
+  _TENSORSHAPEPROTO_DIMENSION.fields_by_name['dim_value'])
+_TENSORSHAPEPROTO_DIMENSION.fields_by_name['dim_value'].containing_oneof = _TENSORSHAPEPROTO_DIMENSION.oneofs_by_name['value']
+_TENSORSHAPEPROTO_DIMENSION.oneofs_by_name['value'].fields.append(
+  _TENSORSHAPEPROTO_DIMENSION.fields_by_name['dim_param'])
+_TENSORSHAPEPROTO_DIMENSION.fields_by_name['dim_param'].containing_oneof = _TENSORSHAPEPROTO_DIMENSION.oneofs_by_name['value']
+_TENSORSHAPEPROTO.fields_by_name['dim'].message_type = _TENSORSHAPEPROTO_DIMENSION
+DESCRIPTOR.message_types_by_name['AttributeProto'] = _ATTRIBUTEPROTO
+DESCRIPTOR.message_types_by_name['TensorProto'] = _TENSORPROTO
+DESCRIPTOR.message_types_by_name['TensorShapeProto'] = _TENSORSHAPEPROTO
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+AttributeProto = _reflection.GeneratedProtocolMessageType('AttributeProto', (_message.Message,), dict(
+  DESCRIPTOR = _ATTRIBUTEPROTO,
+  __module__ = 'onnx_common_pb2'
+  # @@protoc_insertion_point(class_scope:aihwx.AttributeProto)
+  ))
+_sym_db.RegisterMessage(AttributeProto)
+
+TensorProto = _reflection.GeneratedProtocolMessageType('TensorProto', (_message.Message,), dict(
+  DESCRIPTOR = _TENSORPROTO,
+  __module__ = 'onnx_common_pb2'
+  # @@protoc_insertion_point(class_scope:aihwx.TensorProto)
+  ))
+_sym_db.RegisterMessage(TensorProto)
+
+TensorShapeProto = _reflection.GeneratedProtocolMessageType('TensorShapeProto', (_message.Message,), dict(
+
+  Dimension = _reflection.GeneratedProtocolMessageType('Dimension', (_message.Message,), dict(
+    DESCRIPTOR = _TENSORSHAPEPROTO_DIMENSION,
+    __module__ = 'onnx_common_pb2'
+    # @@protoc_insertion_point(class_scope:aihwx.TensorShapeProto.Dimension)
+    ))
+  ,
+  DESCRIPTOR = _TENSORSHAPEPROTO,
+  __module__ = 'onnx_common_pb2'
+  # @@protoc_insertion_point(class_scope:aihwx.TensorShapeProto)
+  ))
+_sym_db.RegisterMessage(TensorShapeProto)
+_sym_db.RegisterMessage(TensorShapeProto.Dimension)
+
+
+_TENSORPROTO.fields_by_name['float_data']._options = None
+_TENSORPROTO.fields_by_name['int32_data']._options = None
+_TENSORPROTO.fields_by_name['int64_data']._options = None
+# @@protoc_insertion_point(module_scope)
diff --git a/src/aihwkit/cloud/converter/definitions/output_file_pb2.py b/src/aihwkit/cloud/converter/definitions/output_file_pb2.py
new file mode 100644
index 00000000..2876e85e
--- /dev/null
+++ b/src/aihwkit/cloud/converter/definitions/output_file_pb2.py
@@ -0,0 +1,144 @@
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: output_file.proto
+
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from . import onnx_common_pb2 as onnx__common__pb2
+from . import common_pb2 as common__pb2
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+  name='output_file.proto',
+  package='aihwx',
+  syntax='proto2',
+  serialized_options=None,
+  serialized_pb=_b('\n\x11output_file.proto\x12\x05\x61ihwx\x1a\x11onnx_common.proto\x1a\x0c\x63ommon.proto\"I\n\x10\x45pochInformation\x12\r\n\x05\x65poch\x18\x01 \x02(\x05\x12&\n\x07metrics\x18\x02 \x03(\x0b\x32\x15.aihwx.AttributeProto\"\xa3\x01\n\x0eTrainingOutput\x12\x1f\n\x07version\x18\x01 \x02(\x0b\x32\x0e.aihwx.Version\x12\x1f\n\x07network\x18\x02 \x02(\x0b\x32\x0e.aihwx.Network\x12\'\n\x06\x65pochs\x18\x03 \x03(\x0b\x32\x17.aihwx.EpochInformation\x12&\n\x07metrics\x18\x04 \x03(\x0b\x32\x15.aihwx.AttributeProto')
+  ,
+  dependencies=[onnx__common__pb2.DESCRIPTOR,common__pb2.DESCRIPTOR,])
+
+
+
+
+_EPOCHINFORMATION = _descriptor.Descriptor(
+  name='EpochInformation',
+  full_name='aihwx.EpochInformation',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='epoch', full_name='aihwx.EpochInformation.epoch', index=0,
+      number=1, type=5, cpp_type=1, label=2,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='metrics', full_name='aihwx.EpochInformation.metrics', index=1,
+      number=2, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=61,
+  serialized_end=134,
+)
+
+
+_TRAININGOUTPUT = _descriptor.Descriptor(
+  name='TrainingOutput',
+  full_name='aihwx.TrainingOutput',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='version', full_name='aihwx.TrainingOutput.version', index=0,
+      number=1, type=11, cpp_type=10, label=2,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='network', full_name='aihwx.TrainingOutput.network', index=1,
+      number=2, type=11, cpp_type=10, label=2,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='epochs', full_name='aihwx.TrainingOutput.epochs', index=2,
+      number=3, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='metrics', full_name='aihwx.TrainingOutput.metrics', index=3,
+      number=4, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=137,
+  serialized_end=300,
+)
+
+_EPOCHINFORMATION.fields_by_name['metrics'].message_type = onnx__common__pb2._ATTRIBUTEPROTO
+_TRAININGOUTPUT.fields_by_name['version'].message_type = common__pb2._VERSION
+_TRAININGOUTPUT.fields_by_name['network'].message_type = common__pb2._NETWORK
+_TRAININGOUTPUT.fields_by_name['epochs'].message_type = _EPOCHINFORMATION
+_TRAININGOUTPUT.fields_by_name['metrics'].message_type = onnx__common__pb2._ATTRIBUTEPROTO
+DESCRIPTOR.message_types_by_name['EpochInformation'] = _EPOCHINFORMATION
+DESCRIPTOR.message_types_by_name['TrainingOutput'] = _TRAININGOUTPUT
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+EpochInformation = _reflection.GeneratedProtocolMessageType('EpochInformation', (_message.Message,), dict(
+  DESCRIPTOR = _EPOCHINFORMATION,
+  __module__ = 'output_file_pb2'
+  # @@protoc_insertion_point(class_scope:aihwx.EpochInformation)
+  ))
+_sym_db.RegisterMessage(EpochInformation)
+
+TrainingOutput = _reflection.GeneratedProtocolMessageType('TrainingOutput', (_message.Message,), dict(
+  DESCRIPTOR = _TRAININGOUTPUT,
+  __module__ = 'output_file_pb2'
+  # @@protoc_insertion_point(class_scope:aihwx.TrainingOutput)
+  ))
+_sym_db.RegisterMessage(TrainingOutput)
+
+
+# @@protoc_insertion_point(module_scope)
diff --git a/src/aihwkit/cloud/converter/exceptions.py b/src/aihwkit/cloud/converter/exceptions.py
new file mode 100644
index 00000000..900632b4
--- /dev/null
+++ b/src/aihwkit/cloud/converter/exceptions.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+
+# (C) Copyright 2020, 2021 IBM. All Rights Reserved.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+"""Conversion-related Exceptions."""
+
+from aihwkit.exceptions import AihwkitException
+
+
+class ConversionError(AihwkitException):
+    """Errors related to Experiment conversion."""
diff --git a/src/aihwkit/cloud/converter/v1/__init__.py b/src/aihwkit/cloud/converter/v1/__init__.py
new file mode 100644
index 00000000..1b880c20
--- /dev/null
+++ b/src/aihwkit/cloud/converter/v1/__init__.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+
+# (C) Copyright 2020, 2021 IBM. All Rights Reserved.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+"""Helpers for version 1 of the AIHW Composer format."""
diff --git a/src/aihwkit/cloud/converter/v1/mappings.py b/src/aihwkit/cloud/converter/v1/mappings.py
new file mode 100644
index 00000000..fe4809dd
--- /dev/null
+++ b/src/aihwkit/cloud/converter/v1/mappings.py
@@ -0,0 +1,272 @@
+# -*- coding: utf-8 -*-
+
+# (C) Copyright 2020, 2021 IBM. All Rights Reserved.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+"""Mappings for version 1 of the AIHW Composer format."""
+
+from collections import namedtuple
+from typing import Any, Dict
+
+from torch.nn import (
+    BCELoss, BatchNorm2d, Conv2d, ConvTranspose2d, CrossEntropyLoss, Flatten,
+    LeakyReLU, Linear, LogSigmoid, LogSoftmax, MSELoss, MaxPool2d, NLLLoss,
+    ReLU, Sigmoid, Softmax, Tanh
+)
+from torchvision.datasets import FashionMNIST, SVHN
+
+from aihwkit.cloud.converter.definitions.onnx_common_pb2 import AttributeProto
+from aihwkit.cloud.converter.exceptions import ConversionError
+from aihwkit.nn import AnalogConv2d, AnalogLinear
+from aihwkit.optim import AnalogSGD
+from aihwkit.simulator.presets import (
+    CapacitorPreset, EcRamPreset, IdealizedPreset, ReRamESPreset, ReRamSBPreset,
+    TikiTakaCapacitorPreset, TikiTakaEcRamPreset, TikiTakaIdealizedPreset,
+    TikiTakaReRamESPreset, TikiTakaReRamSBPreset
+)
+
+Type = namedtuple('Type', ['attribute_type', 'field', 'fn'])
+
+TYPES = {
+    int: Type(AttributeProto.AttributeType.INT, 'i', lambda x: x),  # type: ignore
+    bool: Type(AttributeProto.AttributeType.BOOL, 'b', lambda x: x),  # type: ignore
+    str: Type(AttributeProto.AttributeType.STRING, 's',  # type: ignore
+              lambda x: x.encode('utf-8')),
+    float: Type(AttributeProto.AttributeType.FLOAT, 'f', lambda x: x)  # type: ignore
+}
+
+TYPES_LISTS = {
+    int: Type(AttributeProto.AttributeType.INTS, 'ints', lambda x: x),  # type: ignore
+    bool: Type(AttributeProto.AttributeType.BOOLS, 'bools', lambda x: x),  # type: ignore
+    str: Type(AttributeProto.AttributeType.STRINGS, 'strings',  # type: ignore
+              lambda x: [y.encode('utf-8') for y in x]),
+    float: Type(AttributeProto.AttributeType.FLOATS, 'floats', lambda x: x)  # type: ignore
+}
+
+
+class Function:
+    """Mapping for a function-like entity."""
+
+    def __init__(
+            self,
+            id_: str,
+            args: Dict
+    ):
+        self.id_ = id_
+        self.args = args
+
+    def to_proto(self, source: object, proto_cls: type) -> object:
+        """Convert a source object into a destination object."""
+        instance = proto_cls(id=self.id_)
+
+        for name, type_ in self.args.items():
+            value = self.get_field_value_to_proto(source, name, None)
+
+            argument = AttributeProto(name=name)
+            if isinstance(type_, list):
+                proto_type = TYPES_LISTS[type_[0]]
+                final_value = proto_type.fn(value)
+
+                # Fix arguments where ``Union[T, Sequence[T]]`` is allowed.
+                # TODO: generalize, as this check assumes int and 2-size.
+                if isinstance(final_value, int):
+                    final_value = [final_value, final_value]
+
+                getattr(argument, proto_type.field).extend(final_value)
+            else:
+                proto_type = TYPES[type_]
+                setattr(argument, proto_type.field, proto_type.fn(value))
+            argument.type = proto_type.attribute_type
+            instance.arguments.append(argument)
+
+        return instance
+
+    def from_proto(self, source: Any, cls: type) -> object:
+        """Convert a proto object into a destination object."""
+        kwargs = {}
+
+        for argument in source.arguments:
+            type_ = self.args[argument.name]
+
+            if isinstance(type_, list):
+                proto_type = TYPES_LISTS[type_[0]]
+            else:
+                proto_type = TYPES[type_]
+
+            new_argument = self.get_argument_from_proto(argument, proto_type.field, None)
+            if isinstance(type_, list):
+                new_argument[argument.name] = list(new_argument[argument.name])
+            kwargs.update(new_argument)
+
+        return cls(**kwargs)
+
+    def get_field_value_to_proto(self, source: Any, field: str, default: Any = None) -> Any:
+        """Get the value of a field."""
+        return getattr(source, field, default)
+
+    def get_argument_from_proto(self, source: Any, field: str, default: Any = None) -> Dict:
+        """Get the value of a field."""
+        return {source.name: getattr(source, field, default)}
+
+
+class LayerFunction(Function):
+    """Mapping for a function-like entity (Layer)."""
+
+    def get_field_value_to_proto(self, source: Any, field: str, default: Any = None) -> Any:
+        if field == 'bias':
+            return getattr(source, 'bias', None) is not None
+        if field == 'rpu_config':
+            preset_cls = type(source.analog_tile.rpu_config)
+            try:
+                return Mappings.presets[preset_cls]
+            except KeyError as ex:
+                raise ConversionError('Invalid rpu_config in layer: {} not '
+                                      'among the presets'.format(preset_cls)) from ex
+
+        return super().get_field_value_to_proto(source, field, default)
+
+    def get_argument_from_proto(self, source: Any, field: str, default: Any = None) -> Dict:
+        """Get the value of a field."""
+        if source.name == 'rpu_config':
+            preset_str = getattr(source, field, default).decode('utf-8')
+            try:
+                preset = InverseMappings.presets[preset_str]
+            except KeyError as ex:
+                raise ConversionError('Invalid rpu_config in layer: {} not '
+                                      'among the presets'.format(preset_str)) from ex
+            return {'rpu_config': preset()}
+
+        return super().get_argument_from_proto(source, field, default)
+
+
+class Mappings:
+    """Mappings between Python entities and AIHW format."""
+    # pylint: disable=too-few-public-methods
+
+    datasets = {
+        FashionMNIST: 'fashion_mnist',
+        SVHN: 'svhn'
+    }
+
+    layers = {
+        AnalogConv2d: LayerFunction('AnalogConv2d', {
+            'in_channels': int,
+            'out_channels': int,
+            'kernel_size': [int],
+            'stride': [int],
+            'padding': [int],
+            'dilation': [int],
+            'bias': bool,
+            'rpu_config': str,
+            'weight_scaling_omega': float
+        }),
+        AnalogLinear: LayerFunction('AnalogLinear', {
+            'in_features': int,
+            'out_features': int,
+            'bias': bool,
+            'rpu_config': str,
+            'weight_scaling_omega': float
+        }),
+        BatchNorm2d: LayerFunction('BatchNorm2d', {
+            'num_features': int
+        }),
+        Conv2d: LayerFunction('Conv2d', {
+            'in_channels': int,
+            'out_channels': int,
+            'kernel_size': [int],
+            'stride': [int],
+            'padding': [int],
+            'dilation': [int],
+            'bias': bool
+        }),
+        ConvTranspose2d: LayerFunction('ConvTranspose2d', {
+            'in_channels': int,
+            'out_channels': int,
+            'kernel_size': [int],
+            'stride': [int],
+            'padding': [int],
+            'output_padding': [int],
+            'dilation': [int],
+            'bias': bool
+        }),
+        Flatten: LayerFunction('Flatten', {}),
+        Linear: LayerFunction('Linear', {
+            'in_features': int,
+            'out_features': int,
+            'bias': bool
+        }),
+        MaxPool2d: LayerFunction('MaxPool2d', {
+            'kernel_size': int,
+            'stride': int,
+            'padding': int,
+            'dilation': int,
+            'ceil_mode': bool
+        })
+    }
+
+    activation_functions = {
+        LeakyReLU: Function('LeakyReLU', {
+            'negative_slope': float
+        }),
+        LogSigmoid: Function('LogSimoid', {}),
+        LogSoftmax: Function('LogSoftmax', {
+            'dim': int
+        }),
+        ReLU: Function('ReLU', {}),
+        Sigmoid: Function('Sigmoid', {}),
+        Softmax: Function('Softmax', {
+            'dim': int
+        }),
+        Tanh: Function('Tanh', {})
+    }
+
+    loss_functions = {
+        BCELoss: Function('BCELoss', {}),
+        CrossEntropyLoss: Function('CrossEntropyLoss', {}),
+        MSELoss: Function('MSELoss', {}),
+        NLLLoss: Function('NLLLoss', {})
+    }
+
+    optimizers = {
+        AnalogSGD: Function('AnalogSGD', {
+            'lr': float
+        })
+    }
+
+    presets = {
+        ReRamESPreset: 'ReRamESPreset',
+        ReRamSBPreset: 'ReRamSBPreset',
+        CapacitorPreset: 'CapacitorPreset',
+        EcRamPreset: 'EcRamPreset',
+        IdealizedPreset: 'IdealizedPreset',
+        TikiTakaReRamESPreset: 'TikiTakaReRamESPreset',
+        TikiTakaReRamSBPreset: 'TikiTakaReRamSBPreset',
+        TikiTakaCapacitorPreset: 'TikiTakaCapacitorPreset',
+        TikiTakaEcRamPreset: 'TikiTakaEcRamPreset',
+        TikiTakaIdealizedPreset: 'TikiTakaIdealizedPreset',
+    }
+
+
+def build_inverse_mapping(mapping: Dict) -> Dict:
+    """Create the inverse mapping between Python entities and AIHW Composer formats."""
+    return {value if not isinstance(value, Function) else value.id_: key
+            for key, value in mapping.items()}
+
+
+class InverseMappings:
+    """Mappings between AIHW Composer format and Python entities."""
+    # pylint: disable=too-few-public-methods
+
+    datasets = build_inverse_mapping(Mappings.datasets)
+    layers = build_inverse_mapping(Mappings.layers)
+    activation_functions = build_inverse_mapping(Mappings.activation_functions)
+    loss_functions = build_inverse_mapping(Mappings.loss_functions)
+    optimizers = build_inverse_mapping(Mappings.optimizers)
+    presets = build_inverse_mapping(Mappings.presets)
diff --git a/src/aihwkit/cloud/converter/v1/training.py b/src/aihwkit/cloud/converter/v1/training.py
new file mode 100644
index 00000000..6d1a440f
--- /dev/null
+++ b/src/aihwkit/cloud/converter/v1/training.py
@@ -0,0 +1,194 @@
+# -*- coding: utf-8 -*-
+
+# (C) Copyright 2020, 2021 IBM. All Rights Reserved.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+"""Converter for `BasicTraining` Experiment."""
+
+from typing import Any, Dict, List
+
+from torch.nn import Module
+from torch.nn.modules.loss import _Loss
+
+from aihwkit.cloud.converter.exceptions import ConversionError
+from aihwkit.cloud.converter.v1.mappings import InverseMappings, Mappings
+from aihwkit.experiments.experiments.training import BasicTraining
+
+from aihwkit.cloud.converter.definitions.input_file_pb2 import (
+    TrainingInput, Dataset, Training
+)
+from aihwkit.cloud.converter.definitions.common_pb2 import (
+    LayerOrActivationFunction, LossFunctionProto, Network, LayerProto,
+    ActivationFunctionProto, OptimizerProto, Version
+)
+from aihwkit.cloud.converter.definitions.onnx_common_pb2 import AttributeProto
+from aihwkit.nn import AnalogSequential
+
+
+class BasicTrainingConverter:
+    """Converter for `BasicTraining` Experiment."""
+
+    def to_proto(self, experiment: BasicTraining) -> Any:
+        """Convert an `Experiment` to its protobuf representation."""
+        version = self._version_to_proto()
+        dataset = self._dataset_to_proto(experiment.dataset,
+                                         experiment.batch_size)
+        network = self._model_to_proto(experiment.model)
+        training = self._training_to_proto(experiment.epochs,
+                                           experiment.learning_rate,
+                                           experiment.loss_function)
+
+        training_input = TrainingInput(
+            version=version,
+            dataset=dataset,
+            network=network,
+            training=training
+        )
+
+        return training_input
+
+    def from_proto(self, training_proto: Any) -> Any:
+        """Convert a protobuf representation to an `Experiment`."""
+        dataset = InverseMappings.datasets[training_proto.dataset.dataset_id]
+        model = self._model_from_proto(training_proto.network)
+        batch_size = training_proto.dataset.batch_size
+        loss_function = InverseMappings.loss_functions[training_proto.training.loss_function.id]
+        epochs = training_proto.training.epochs
+        learning_rate = training_proto.training.optimizer.arguments[0].f
+
+        return BasicTraining(
+            dataset=dataset,
+            model=model,
+            batch_size=batch_size,
+            loss_function=loss_function,
+            epochs=epochs,
+            learning_rate=learning_rate
+        )
+
+    # Methods for converting to proto.
+
+    @staticmethod
+    def _version_to_proto() -> Any:
+        return Version(
+            schema=1,
+            opset=1
+        )
+
+    @staticmethod
+    def _dataset_to_proto(dataset: type, batch_size: int) -> Any:
+        if dataset not in Mappings.datasets.keys():
+            raise ConversionError('Unsupported dataset: {}'.format(dataset))
+
+        return Dataset(
+            dataset_id=Mappings.datasets[dataset],
+            batch_size=batch_size
+        )
+
+    @staticmethod
+    def _model_to_proto(model: Module) -> Any:
+        if not isinstance(model, AnalogSequential):
+            raise ConversionError('Unsupported model: only AnalogSequential is supported')
+
+        children_types = {type(layer) for layer in model.children()}
+        valid_types = set(Mappings.layers.keys()) | set(Mappings.activation_functions.keys())
+        if children_types - valid_types:
+            raise ConversionError('Unsupported layers: {}'.format(children_types - valid_types))
+
+        network = Network()
+        for child in model.children():
+            child_type = type(child)
+            if child_type in Mappings.layers:
+                item = LayerOrActivationFunction(
+                    layer=Mappings.layers[child_type].to_proto(child, LayerProto)
+                )
+            else:
+                item = LayerOrActivationFunction(
+                    activation_function=Mappings.activation_functions[child_type].to_proto(
+                        child, ActivationFunctionProto)
+                )
+            network.layers.extend([item])
+
+        return network
+
+    @staticmethod
+    def _training_to_proto(
+            epochs: int,
+            learning_rate: float,
+            loss_function: _Loss
+    ) -> Any:
+        if loss_function not in Mappings.loss_functions.keys():
+            raise ConversionError('Unsupported loss function: {}'.format(loss_function))
+
+        # Build optimizer manually.
+        optimizer = OptimizerProto(id='AnalogSGD')
+        optimizer.arguments.append(
+            AttributeProto(
+                name='lr',
+                type=AttributeProto.AttributeType.FLOAT,  # type: ignore
+                f=learning_rate))
+
+        training = Training(
+            epochs=epochs,
+            optimizer=optimizer,
+            loss_function=Mappings.loss_functions[loss_function].to_proto(
+                loss_function(), LossFunctionProto)
+        )
+
+        return training
+
+    # Methods for converting from proto.
+
+    @staticmethod
+    def _model_from_proto(model_proto: Any) -> Module:
+        layers = []
+        for layer_proto in model_proto.layers:
+            if layer_proto.WhichOneof('item') == 'layer':
+                layer_cls = InverseMappings.layers[layer_proto.layer.id]
+                layer = Mappings.layers[layer_cls].from_proto(
+                    layer_proto.layer, layer_cls)
+            else:
+                layer_cls = InverseMappings.activation_functions[layer_proto.activation_function.id]
+                layer = Mappings.activation_functions[layer_cls].from_proto(
+                    layer_proto.activation_function, layer_cls)
+
+            layers.append(layer)
+
+        return AnalogSequential(*layers)
+
+
+class BasicTrainingResultConverter:
+    """Converter for `BasicTraining` results."""
+    # pylint: disable=too-few-public-methods
+
+    def from_proto(self, results: Any) -> Any:
+        """Convert a result to its json representation."""
+        return {
+            'version': {
+                'schema': 1,
+                'opset': 1
+            },
+            'epochs': self._epochs_from_proto(results)
+        }
+
+    # Methods for converting from proto.
+    @staticmethod
+    def _epochs_from_proto(epochs_proto: Any) -> List[Dict]:
+        epochs = []
+        for epoch in epochs_proto.epochs:
+            epoch_dict = {
+                'epoch': epoch.epoch,
+                'metrics': {}
+            }
+            for metric in epoch.metrics:
+                epoch_dict['metrics'][metric.name] = metric.f
+
+            epochs.append(epoch_dict)
+
+        return epochs


Test patch:
diff --git a/tests/test_experiments.py b/tests/test_experiments.py
new file mode 100644
index 00000000..8d6d9ce9
--- /dev/null
+++ b/tests/test_experiments.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+
+# (C) Copyright 2020, 2021 IBM. All Rights Reserved.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+"""Tests for Experiments."""
+
+from aihwkit.cloud.converter.v1.training import BasicTrainingConverter
+from aihwkit.nn.modules.base import AnalogModuleBase
+
+from .helpers.decorators import parametrize_over_models
+from .helpers.experiments import (
+    FullyConnectedFashionMNIST, FullyConnectedFashionMNISTTikiTaka,
+    LeNet5FashionMNIST
+)
+from .helpers.testcases import AihwkitTestCase
+
+
+@parametrize_over_models([
+    FullyConnectedFashionMNIST, FullyConnectedFashionMNISTTikiTaka,
+    LeNet5FashionMNIST
+])
+class TestBasicTraining(AihwkitTestCase):
+    """Test BasicTraining Experiment."""
+
+    def test_conversion_roundtrip(self):
+        """Test roundtrip conversion of examples."""
+        experiment_original = self.get_experiment()
+
+        # Convert to proto.
+        converter = BasicTrainingConverter()
+        experiment_proto = converter.to_proto(experiment_original)
+
+        # Convert back to Experiment.
+        experiment_converted = converter.from_proto(experiment_proto)
+
+        # Assert over the Experiments properties.
+        self.assertEqual(experiment_original.dataset, experiment_converted.dataset)
+        self.assertEqual(experiment_original.batch_size, experiment_converted.batch_size)
+        self.assertEqual(experiment_original.loss_function, experiment_converted.loss_function)
+        self.assertEqual(experiment_original.epochs, experiment_converted.epochs)
+
+        # Compare the models by hand, as direct equality comparison is not possible.
+        # self.assertEqual(experiment_original.model, experiment_converted.model)
+        self.assertEqual(len(list(experiment_original.model.children())),
+                         len(list(experiment_converted.model.children())))
+        for layer_a, layer_b in zip(experiment_original.model.children(),
+                                    experiment_converted.model.children()):
+            self.assertEqual(type(layer_a), type(layer_b))
+            if isinstance(layer_a, AnalogModuleBase):
+                self.assertEqual(type(layer_a.analog_tile.rpu_config.device),
+                                 type(layer_b.analog_tile.rpu_config.device))