diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt
index 057122f9..b8b2cc0d 100644
--- a/ReleaseNotes.txt
+++ b/ReleaseNotes.txt
@@ -7,6 +7,7 @@
 - Pulse Templates:
     - `MappingPulseTemplate`:
         - `allow_partial_parameter_mapping` is now True as a default. The default can be changed with the class variable `MappingPulseTemplate.ALLOW_PARTIAL_PARAMETER_MAPPING`.
+        - Add specializations for `map_parameters` because the auto-inference of the return type did not work for empty input.
 
 ## 0.4 ##
 
diff --git a/qupulse/pulses/mapping_pulse_template.py b/qupulse/pulses/mapping_pulse_template.py
index 8d01a432..a21e8738 100644
--- a/qupulse/pulses/mapping_pulse_template.py
+++ b/qupulse/pulses/mapping_pulse_template.py
@@ -238,30 +238,59 @@ class MappingPulseTemplate(PulseTemplate, ParameterConstrainer):
         # return MappingPulseTemplate(template=serializer.deserialize(template),
         #                             **kwargs)
 
+    def _validate_parameters(self, parameters: Dict[str, Union[Parameter, numbers.Real]]):
+        missing = set(self.__external_parameters) - set(parameters.keys())
+        if missing:
+            raise ParameterNotProvidedException(missing.pop())
+        self.validate_parameter_constraints(parameters=parameters)
+
+    def map_parameter_values(self, parameters: Dict[str, numbers.Real]) -> Dict[str, numbers.Real]:
+        """Map parameter values according to the defined mappings.
+
+        Args:
+            parameters: Dictionary with numeric values
+        Returns:
+            A new dictionary with mapped numeric values.
+        """
+        self._validate_parameters(parameters=parameters)
+        return {parameter: mapping_function.evaluate_numeric(**parameters)
+                for parameter, mapping_function in self.__parameter_mapping.items()}
+
+    def map_parameter_objects(self, parameters: Dict[str, Parameter]) ->  Dict[str, Parameter]:
+        """Map parameter objects (instances of Parameter class) according to the defined mappings.
+
+        Args:
+            parameters: Dictionary with parameter objects
+        Returns:
+            A new dictionary with mapped parameter objects
+        """
+        self._validate_parameters(parameters=parameters)
+        return {parameter: MappedParameter(mapping_function,
+                                           {name: parameters[name] for name in mapping_function.variables})
+                for (parameter, mapping_function) in self.__parameter_mapping.items()}
+
     def map_parameters(self,
-                       parameters: Dict[str, Union[Parameter, numbers.Real]]) -> Dict[str, Parameter]:
+                       parameters: Dict[str, Union[Parameter, numbers.Real]]) -> Dict[str,
+                                                                                      Union[Parameter, numbers.Real]]:
         """Map parameter values according to the defined mappings.
 
         Args:
-            parameters (Dict(str -> Parameter)): A mapping of parameter names to Parameter
-                objects/values.
+            parameters: A mapping of parameter names to parameter objects/values.
         Returns:
             A new dictionary which maps parameter names to parameter values which have been
             mapped according to the mappings defined for template.
         """
-        missing = set(self.__external_parameters) - set(parameters.keys())
-        if missing:
-            raise ParameterNotProvidedException(missing.pop())
+        if not parameters and self.__parameter_mapping:
+            raise ValueError('Cannot infer type of return value (numeric or symbolic)')
 
-        self.validate_parameter_constraints(parameters=parameters)
-        if all(isinstance(parameter, Parameter) for parameter in parameters.values()):
-            return {parameter: MappedParameter(mapping_function, {name: parameters[name]
-                                                                  for name in mapping_function.variables})
-                    for (parameter, mapping_function) in self.__parameter_mapping.items()}
-        if all(isinstance(parameter, numbers.Real) for parameter in parameters.values()):
-            return {parameter: mapping_function.evaluate_numeric(**parameters)
-                    for parameter, mapping_function in self.__parameter_mapping.items()}
-        raise TypeError('Values of parameter dict are neither all Parameter nor Real')
+        elif all(isinstance(parameter, numbers.Real) for parameter in parameters.values()):
+            return self.map_parameter_values(parameters=parameters)
+
+        elif all(isinstance(parameter, Parameter) for parameter in parameters.values()):
+            return self.map_parameter_objects(parameters=parameters)
+
+        else:
+            raise TypeError('Values of parameter dict are neither all Parameter nor Real')
 
     def get_updated_measurement_mapping(self, measurement_mapping: Dict[str, str]) -> Dict[str, str]:
         return {k: measurement_mapping[v] for k, v in self.__measurement_mapping.items()}
@@ -277,7 +306,7 @@ class MappingPulseTemplate(PulseTemplate, ParameterConstrainer):
                        channel_mapping: Dict[ChannelID, ChannelID],
                        instruction_block: InstructionBlock) -> None:
         self.template.build_sequence(sequencer,
-                                     parameters=self.map_parameters(parameters),
+                                     parameters=self.map_parameter_objects(parameters),
                                      conditions=conditions,
                                      measurement_mapping=self.get_updated_measurement_mapping(measurement_mapping),
                                      channel_mapping=self.get_updated_channel_mapping(channel_mapping),
@@ -291,7 +320,7 @@ class MappingPulseTemplate(PulseTemplate, ParameterConstrainer):
                                  to_single_waveform: Set[Union[str, 'PulseTemplate']],
                                  parent_loop: Loop) -> None:
         # parameters are validated in map_parameters() call, no need to do it here again explicitly
-        self.template._create_program(parameters=self.map_parameters(parameters),
+        self.template._create_program(parameters=self.map_parameter_objects(parameters),
                                       measurement_mapping=self.get_updated_measurement_mapping(measurement_mapping),
                                       channel_mapping=self.get_updated_channel_mapping(channel_mapping),
                                       global_transformation=global_transformation,
@@ -303,14 +332,14 @@ class MappingPulseTemplate(PulseTemplate, ParameterConstrainer):
                        channel_mapping: Dict[ChannelID, ChannelID]) -> Waveform:
         """This gets called if the parent is atomic"""
         return self.template.build_waveform(
-            parameters=self.map_parameters(parameters),
+            parameters=self.map_parameter_values(parameters),
             channel_mapping=self.get_updated_channel_mapping(channel_mapping))
 
     def get_measurement_windows(self,
                                 parameters: Dict[str, numbers.Real],
                                 measurement_mapping: Dict[str, Optional[str]]) -> List:
         return self.template.get_measurement_windows(
-            parameters=self.map_parameters(parameters=parameters),
+            parameters=self.map_parameter_values(parameters=parameters),
             measurement_mapping=self.get_updated_measurement_mapping(measurement_mapping=measurement_mapping)
         )
 
@@ -318,7 +347,7 @@ class MappingPulseTemplate(PulseTemplate, ParameterConstrainer):
                       parameters: Dict[str, Parameter],
                       conditions: Dict[str, Condition]) -> bool:
         return self.template.requires_stop(
-            self.map_parameters(parameters),
+            self.map_parameter_objects(parameters),
             conditions
         )
 

AtomicMultiChannelPT fails for parameterized template
If I use a parameter in a template then the result of an AtomicMultiChannelPT cannot be rendered or plotted.

A minimal example:

```
import matplotlib.pyplot as plt
from qupulse.pulses.table_pulse_template import concatenate
from qupulse.pulses import TablePT,SequencePT,  AtomicMultiChannelPT 
from qupulse.pulses.plotting import plot, render

gates_template= TablePT({'gate': [(0, 1), (60*1e3, 2, 'hold')]} )    
input_variables = {'period': float(gates_template.duration), 'uptime': 0} 
marker_sequence = (TablePT({'m': [(0, 1), ('uptime', 0), ('period', 0)]}), input_variables)

render(gates_template.create_program()) # works fine!
render(SequencePT(marker_sequence).create_program()) # works fine!

combined_template = AtomicMultiChannelPT(gates_template, marker_sequence )
render(combined_template.create_program()) # fails!

marker_sequence2 = TablePT({'m': [(0, 1), (0, 0), (gates_template.duration, 0)]})
combined_template2 = AtomicMultiChannelPT(gates_template, marker_sequence2 )
render(combined_template2.create_program()) # works!

combined_template3 = AtomicMultiChannelPT(gates_template, SequencePT(marker_sequence ) )
render(combined_template3.create_program()) # fails!
```
diff --git a/tests/pulses/mapping_pulse_template_tests.py b/tests/pulses/mapping_pulse_template_tests.py
index 1917aaa2..a07d6072 100644
--- a/tests/pulses/mapping_pulse_template_tests.py
+++ b/tests/pulses/mapping_pulse_template_tests.py
@@ -175,6 +175,13 @@ class MappingTemplateTests(unittest.TestCase):
         for k, v in st.map_parameters(parameters).items():
             self.assertEqual(v, values[k])
 
+        with self.assertRaisesRegex(ValueError, "type of return value"):
+            st.map_parameters({})
+
+        parameters = dict(t=3, k=2, l=ConstantParameter(7))
+        with self.assertRaisesRegex(TypeError, "neither all Parameter nor Real"):
+            st.map_parameters(parameters)
+
     def test_partial_parameter_mapping(self):
         template = DummyPulseTemplate(parameter_names={'foo', 'bar'})
         st = MappingPulseTemplate(template, parameter_mapping={'foo': 't*k'}, allow_partial_parameter_mapping=True)
@@ -539,4 +546,19 @@ class MappingPulseTemplateOldSerializationTests(unittest.TestCase):
             self.assertEqual(data['channel_mapping'], deserialized.channel_mapping)
             self.assertEqual(data['measurement_mapping'], deserialized.measurement_mapping)
             self.assertEqual(data['parameter_constraints'], [str(pc) for pc in deserialized.parameter_constraints])
-            self.assertIs(deserialized.template, dummy_pt)
\ No newline at end of file
+            self.assertIs(deserialized.template, dummy_pt)
+
+class MappingPulseTemplateRegressionTests(unittest.TestCase):
+    def test_issue_451(self):
+        from qupulse.pulses import TablePT, SequencePT, AtomicMultiChannelPT
+
+        gates_template = TablePT({'gate': [(0, 1), (60 * 1e3, 2, 'hold')]})
+        input_variables = {'period': float(gates_template.duration), 'uptime': 0}
+        marker_sequence = (TablePT({'m': [(0, 1), ('uptime', 0), ('period', 0)]}), input_variables)
+
+        combined_template = AtomicMultiChannelPT(gates_template, marker_sequence )
+        combined_template.create_program()
+
+        marker_sequence2 = TablePT({'m': [(0, 1), (0, 0), (gates_template.duration, 0)]})
+        combined_template2 = AtomicMultiChannelPT(gates_template, marker_sequence2)
+        combined_template2.create_program()

pip3 install .[plotting,zurich-instruments,tektronix,Faster-fractions]
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-xdist pytest-mock pytest-asyncio
apt-get update apt-get install -y libgmp-dev libmpfr-dev libmpc-dev
3.6
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
cached-property==1.5.2
certifi==2021.5.30
coverage==6.2
cycler==0.11.0
execnet==1.9.0
gmpy2==2.1.5
importlib-metadata==4.8.3
iniconfig==1.1.1
kiwisolver==1.3.1
matplotlib==3.3.4
mpmath==1.3.0
numpy==1.19.5
packaging==21.3
Pillow==8.4.0
pluggy==1.0.0
py==1.11.0
pyparsing==3.1.4
pytest==7.0.1
pytest-asyncio==0.16.0
pytest-cov==4.0.0
pytest-mock==3.6.1
pytest-xdist==3.0.2
python-dateutil==2.9.0.post0
qupulse @ file:///qupulse
six==1.17.0
sympy==1.9
tomli==1.2.3
typing_extensions==4.1.1
zipp==3.6.0

name: qupulse
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==22.2.0
      - cached-property==1.5.2
      - coverage==6.2
      - cycler==0.11.0
      - execnet==1.9.0
      - gmpy2==2.1.5
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - kiwisolver==1.3.1
      - matplotlib==3.3.4
      - mpmath==1.3.0
      - numpy==1.19.5
      - packaging==21.3
      - pillow==8.4.0
      - pluggy==1.0.0
      - py==1.11.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pytest-asyncio==0.16.0
      - pytest-cov==4.0.0
      - pytest-mock==3.6.1
      - pytest-xdist==3.0.2
      - python-dateutil==2.9.0.post0
      - qupulse==0.4
      - six==1.17.0
      - sympy==1.9
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - zipp==3.6.0
prefix: /opt/conda/envs/qupulse

tests/pulses/mapping_pulse_template_tests.py::MappingTemplateTests::test_map_parameters
tests/pulses/mapping_pulse_template_tests.py::MappingTemplateTests::test_constrained tests/pulses/mapping_pulse_template_tests.py::MappingTemplateTests::test_parameter_names tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateSequencingTest::test_create_program_parameter_constraint_violation tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateSerializationTests::test_conversion tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateSerializationTests::test_deserialization tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateSerializationTests::test_duplication_error tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateSerializationTests::test_get_type_identifier tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateSerializationTests::test_identifier tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateSerializationTests::test_manual_garbage_collect tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateSerializationTests::test_no_registration_before_correct_serialization tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateSerializationTests::test_renamed tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateSerializationTests::test_renamed_of_anonymous tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateSerializationTests::test_serialization tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateSerializationTests::test_serialization_and_deserialization tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateOldSerializationTests::test_deserialize tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateOldSerializationTests::test_get_serialization_data tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateRegressionTests::test_issue_451
tests/pulses/mapping_pulse_template_tests.py::MappingTemplateTests::test_defined_channels tests/pulses/mapping_pulse_template_tests.py::MappingTemplateTests::test_external_params tests/pulses/mapping_pulse_template_tests.py::MappingTemplateTests::test_from_tuple tests/pulses/mapping_pulse_template_tests.py::MappingTemplateTests::test_from_tuple_exceptions tests/pulses/mapping_pulse_template_tests.py::MappingTemplateTests::test_from_tuple_partial_mappings tests/pulses/mapping_pulse_template_tests.py::MappingTemplateTests::test_get_updated_channel_mapping tests/pulses/mapping_pulse_template_tests.py::MappingTemplateTests::test_get_updated_measurement_mapping tests/pulses/mapping_pulse_template_tests.py::MappingTemplateTests::test_init_exceptions tests/pulses/mapping_pulse_template_tests.py::MappingTemplateTests::test_integral tests/pulses/mapping_pulse_template_tests.py::MappingTemplateTests::test_measurement_names tests/pulses/mapping_pulse_template_tests.py::MappingTemplateTests::test_nested_mapping_avoidance tests/pulses/mapping_pulse_template_tests.py::MappingTemplateTests::test_partial_parameter_mapping tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateSequencingTest::test_create_program tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateSequencingTest::test_create_program_invalid_measurement_mapping tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateSequencingTest::test_create_program_missing_params tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateSequencingTest::test_create_program_subtemplate_none tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateSequencingTest::test_same_channel_error tests/pulses/mapping_pulse_template_tests.py::MappingPulseTemplateOldSequencingTests::test_build_sequence tests/pulses/mapping_pulse_template_tests.py::PulseTemplateParameterMappingExceptionsTests::test_missing_mapping_exception_str tests/pulses/mapping_pulse_template_tests.py::PulseTemplateParameterMappingExceptionsTests::test_unnecessary_mapping_exception_str
qupulse/pulses/mapping_pulse_template.py ReleaseNotes.txt
qupulse/pulses/mapping_pulse_template.py ReleaseNotes.txt