def create_noiseless_virtual_engine_from_templates(
    processor_ids: str | list[str], template_names: str | list[str]
) -> SimulatedLocalEngine:
    """Creates a noiseless virtual engine object from a device specification template.

    Args:
        processor_ids: names of the processors to simulate.  These are arbitrary
            string identifiers and do not have to match the processors' names
            in QCS.  There can be a single string or a list of strings for multiple
            processors.  Use `list_virtual_processors` to obtain recognized names.
        template_names: File names of the device specification templates, see
            cirq_google/devices/specifications for valid templates.  There can
            be a single str for a template name or a list of strings.  Each
            template name should be matched to a single processor id.
        gate_sets: Iterable of serializers to use in the processor.

    Raises:
        ValueError: if processor_ids and template_names are not the same length.
    """
    if isinstance(processor_ids, str):
        processor_ids = [processor_ids]
    if isinstance(template_names, str):
        template_names = [template_names]
    if len(processor_ids) != len(template_names):
        raise ValueError('Must provide equal numbers of processor ids and template names.')

    specifications = [
        _create_device_spec_from_template(template_name) for template_name in template_names
    ]
    return create_noiseless_virtual_engine_from_proto(processor_ids, specifications)
Creates a noiseless virtual engine object from a device specification template.

    Args:
        processor_ids: names of the processors to simulate.  These are arbitrary
            string identifiers and do not have to match the processors' names
            in QCS.  There can be a single string or a list of strings for multiple
            processors.  Use `list_virtual_processors` to obtain recognized names.
        template_names: File names of the device specification templates, see
            cirq_google/devices/specifications for valid templates.  There can
            be a single str for a template name or a list of strings.  Each
            template name should be matched to a single processor id.
        gate_sets: Iterable of serializers to use in the processor.

    Raises:
        ValueError: if processor_ids and template_names are not the same length.
    
cirq-google/cirq_google/engine/virtual_engine_factory.py