diff --git a/qupulse/pulses/pulse_template.py b/qupulse/pulses/pulse_template.py
index d291dc25..7296da62 100644
--- a/qupulse/pulses/pulse_template.py
+++ b/qupulse/pulses/pulse_template.py
@@ -98,9 +98,9 @@ class PulseTemplate(Serializable, SequencingElement, metaclass=DocStringABCMeta)
         """Returns an expression giving the integral over the pulse."""
 
     def create_program(self, *,
-                       parameters: Optional[Dict[str, Union[Parameter, float, Expression, str, Real]]]=None,
-                       measurement_mapping: Optional[Dict[str, Optional[str]]]=None,
-                       channel_mapping: Optional[Dict[ChannelID, Optional[ChannelID]]]=None,
+                       parameters: Optional[Mapping[str, Union[Parameter, float, Expression, str, Real]]]=None,
+                       measurement_mapping: Optional[Mapping[str, Optional[str]]]=None,
+                       channel_mapping: Optional[Mapping[ChannelID, Optional[ChannelID]]]=None,
                        global_transformation: Optional[Transformation]=None,
                        to_single_waveform: Set[Union[str, 'PulseTemplate']]=None) -> Optional['Loop']:
         """Translates this PulseTemplate into a program Loop.
@@ -128,9 +128,9 @@ class PulseTemplate(Serializable, SequencingElement, metaclass=DocStringABCMeta)
         if to_single_waveform is None:
             to_single_waveform = set()
 
-        for channel in self.defined_channels:
-            if channel not in channel_mapping:
-                channel_mapping[channel] = channel
+        # make sure all channels are mapped
+        complete_channel_mapping = {channel: channel for channel in self.defined_channels}
+        complete_channel_mapping.update(channel_mapping)
 
         non_unique_targets = {channel
                               for channel, count in collections.Counter(channel_mapping.values()).items()
@@ -139,21 +139,20 @@ class PulseTemplate(Serializable, SequencingElement, metaclass=DocStringABCMeta)
             raise ValueError('The following channels are mapped to twice', non_unique_targets)
 
         # make sure all values in the parameters dict are of type Parameter
-        for (key, value) in parameters.items():
-            if not isinstance(value, Parameter):
-                parameters[key] = ConstantParameter(value)
+        parameters = {key: value if isinstance(value, Parameter) else ConstantParameter(value)
+                      for key, value in parameters.items()}
 
         root_loop = Loop()
         # call subclass specific implementation
         self._create_program(parameters=parameters,
                              measurement_mapping=measurement_mapping,
-                             channel_mapping=channel_mapping,
+                             channel_mapping=complete_channel_mapping,
                              global_transformation=global_transformation,
                              to_single_waveform=to_single_waveform,
                              parent_loop=root_loop)
 
         if root_loop.waveform is None and len(root_loop.children) == 0:
-            return None # return None if no program
+            return None  # return None if no program
         return root_loop
 
     @abstractmethod

PulseTemplate.create_program modifies passed parameters object inplace
PulseTemplate.create_program modifies the passed parameters object inplace which will cast present values which among other things will turn floats into `ConstantParameter` objects leading to unwanted side effects on the user side.
diff --git a/tests/pulses/pulse_template_tests.py b/tests/pulses/pulse_template_tests.py
index 73d66223..304f13a7 100644
--- a/tests/pulses/pulse_template_tests.py
+++ b/tests/pulses/pulse_template_tests.py
@@ -165,8 +165,11 @@ class PulseTemplateTest(unittest.TestCase):
     def test_create_program(self) -> None:
         template = PulseTemplateStub(defined_channels={'A'}, parameter_names={'foo'})
         parameters = {'foo': ConstantParameter(2.126), 'bar': -26.2, 'hugo': 'exp(sin(pi/2))', 'append_a_child': '1'}
+        previous_parameters = parameters.copy()
         measurement_mapping = {'M': 'N'}
+        previos_measurement_mapping = measurement_mapping.copy()
         channel_mapping = {'A': 'B'}
+        previous_channel_mapping = channel_mapping.copy()
 
         expected_parameters = {'foo': ConstantParameter(2.126), 'bar': ConstantParameter(-26.2),
                                'hugo': ConstantParameter('exp(sin(pi/2))'), 'append_a_child': ConstantParameter('1')}
@@ -192,6 +195,9 @@ class PulseTemplateTest(unittest.TestCase):
                                               global_transformation=global_transformation)
             _create_program.assert_called_once_with(**expected_internal_kwargs, parent_loop=program)
         self.assertEqual(expected_program, program)
+        self.assertEqual(previos_measurement_mapping, measurement_mapping)
+        self.assertEqual(previous_channel_mapping, channel_mapping)
+        self.assertEqual(previous_parameters, parameters)
 
     def test__create_program(self):
         parameters = {'a': ConstantParameter(.1), 'b': ConstantParameter(.2)}

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.2
      - 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/pulse_template_tests.py::PulseTemplateTest::test_create_program
tests/pulses/pulse_template_tests.py::PulseTemplateTest::test__create_program tests/pulses/pulse_template_tests.py::PulseTemplateTest::test__create_program_single_waveform tests/pulses/pulse_template_tests.py::PulseTemplateTest::test_create_program_channel_mapping tests/pulses/pulse_template_tests.py::PulseTemplateTest::test_create_program_defaults tests/pulses/pulse_template_tests.py::PulseTemplateTest::test_create_program_none tests/pulses/pulse_template_tests.py::PulseTemplateTest::test_matmul tests/pulses/pulse_template_tests.py::PulseTemplateTest::test_rmatmul tests/pulses/pulse_template_tests.py::AtomicPulseTemplateTests::test_build_sequence tests/pulses/pulse_template_tests.py::AtomicPulseTemplateTests::test_build_sequence_no_waveform tests/pulses/pulse_template_tests.py::AtomicPulseTemplateTests::test_internal_create_program tests/pulses/pulse_template_tests.py::AtomicPulseTemplateTests::test_internal_create_program_missing_parameters tests/pulses/pulse_template_tests.py::AtomicPulseTemplateTests::test_internal_create_program_no_waveform tests/pulses/pulse_template_tests.py::AtomicPulseTemplateTests::test_internal_create_program_transformation tests/pulses/pulse_template_tests.py::AtomicPulseTemplateTests::test_is_interruptable
qupulse/pulses/pulse_template.py
qupulse/pulses/pulse_template.py