1010
1111logger = logging .getLogger (__name__ )
1212
13- from typing import Any , Dict , Tuple , Union , List , Optional
13+ from typing import Any , Dict , Union , List , Optional
1414from azure .quantum .version import __version__
1515from azure .quantum .qiskit .job import (
1616 MICROSOFT_OUTPUT_DATA_FORMAT ,
2727 from qiskit .providers import Provider
2828 from qiskit .providers .models import BackendConfiguration
2929 from qiskit .qobj import QasmQobj , PulseQobj
30- import pyqir as pyqir
3130 from qsharp .interop .qiskit import QSharpBackend
3231 from qsharp import TargetProfile
3332
@@ -101,7 +100,7 @@ def run(
101100
102101 Args:
103102 run_input (QuantumCircuit or List[QuantumCircuit]): An individual or a
104- list of :class:`~qiskit.circuits.QuantumCircuit` to run on the backend.
103+ list of one :class:`~qiskit.circuits.QuantumCircuit` to run on the backend.
105104 shots (int, optional): Number of shots, defaults to None.
106105 options: Any kwarg options to pass to the backend for running the
107106 config. If a key is also present in the options
@@ -334,7 +333,7 @@ def run(
334333
335334 Args:
336335 run_input (QuantumCircuit or List[QuantumCircuit]): An individual or a
337- list of :class:`~qiskit.circuits.QuantumCircuit` to run on the backend.
336+ list of one :class:`~qiskit.circuits.QuantumCircuit` to run on the backend.
338337 shots (int, optional): Number of shots, defaults to None.
339338 options: Any kwarg options to pass to the backend for running the
340339 config. If a key is also present in the options
@@ -349,17 +348,16 @@ def run(
349348 options .pop ("run_input" , None )
350349 options .pop ("circuit" , None )
351350
352- circuits = list ([])
353- if isinstance (run_input , QuantumCircuit ):
354- circuits = [run_input ]
355- else :
356- circuits = run_input
357-
358- max_circuits_per_job = self .configuration ().max_experiments
359- if len (circuits ) > max_circuits_per_job :
360- raise NotImplementedError (
361- f"This backend only supports running a maximum of { max_circuits_per_job } circuits per job."
362- )
351+ circuit = run_input
352+ if isinstance (run_input , list ):
353+ # just in case they passed a list, we only support single-experiment jobs
354+ if len (run_input ) != 1 :
355+ raise NotImplementedError (
356+ f"This backend only supports running a single circuit per job."
357+ )
358+ circuit = run_input [0 ]
359+ if not isinstance (circuit , QuantumCircuit ):
360+ raise ValueError ("Invalid input: expected a QuantumCircuit." )
363361
364362 # config normalization
365363 input_params = self ._get_input_params (options , shots = shots )
@@ -369,18 +367,11 @@ def run(
369367 if self ._can_send_shots_input_param ():
370368 shots_count = input_params .get (self .__class__ ._SHOTS_PARAM_NAME )
371369
372- job_name = ""
373- if len (circuits ) > 1 :
374- job_name = f"batch-{ len (circuits )} "
375- if shots_count is not None :
376- job_name = f"{ job_name } -{ shots_count } "
377- else :
378- job_name = circuits [0 ].name
379- job_name = options .pop ("job_name" , job_name )
370+ job_name = options .pop ("job_name" , circuit .name )
380371
381- metadata = options .pop ("metadata" , self ._prepare_job_metadata (circuits ))
372+ metadata = options .pop ("metadata" , self ._prepare_job_metadata (circuit ))
382373
383- input_data = self ._translate_input (circuits , input_params )
374+ input_data = self ._translate_input (circuit , input_params )
384375
385376 job = super ()._run (job_name , input_data , input_params , metadata , ** options )
386377 logger .info (
@@ -389,28 +380,19 @@ def run(
389380
390381 return job
391382
392- def _prepare_job_metadata (self , circuits : List [ QuantumCircuit ] ) -> Dict [str , str ]:
383+ def _prepare_job_metadata (self , circuit : QuantumCircuit ) -> Dict [str , str ]:
393384 """Returns the metadata relative to the given circuits that will be attached to the Job"""
394- if len (circuits ) == 1 :
395- circuit : QuantumCircuit = circuits [0 ]
396- return {
397- "qiskit" : str (True ),
398- "name" : circuit .name ,
399- "num_qubits" : circuit .num_qubits ,
400- "metadata" : json .dumps (circuit .metadata ),
401- }
402- # for batch jobs, we don't want to store the metadata of each circuit
403- # we fill out the result header in output processing.
404- # These headers don't matter for execution are are only used for
405- # result processing.
406- return {}
407-
408- def _generate_qir (
409- self , circuits : List [QuantumCircuit ], target_profile : TargetProfile , ** kwargs
410- ) -> pyqir .Module :
411-
412- if len (circuits ) == 0 :
413- raise ValueError ("No QuantumCircuits provided" )
385+ return {
386+ "qiskit" : str (True ),
387+ "name" : circuit .name ,
388+ "num_qubits" : circuit .num_qubits ,
389+ "metadata" : json .dumps (circuit .metadata ),
390+ }
391+
392+
393+ def _get_qir_str (
394+ self , circuit : QuantumCircuit , target_profile : TargetProfile , ** kwargs
395+ ) -> str :
414396
415397 config = self .configuration ()
416398 # Barriers aren't removed by transpilation and must be explicitly removed in the Qiskit to QIR translation.
@@ -424,72 +406,36 @@ def _generate_qir(
424406 ** kwargs ,
425407 )
426408
427- name = "batch"
428- if len (circuits ) == 1 :
429- name = circuits [0 ].name
430-
431- if isinstance (circuits , list ):
432- for value in circuits :
433- if not isinstance (value , QuantumCircuit ):
434- raise ValueError ("Input must be List[QuantumCircuit]" )
435- else :
436- raise ValueError ("Input must be List[QuantumCircuit]" )
437-
438- context = pyqir .Context ()
439- llvm_module = pyqir .qir_module (context , name )
440- for circuit in circuits :
441- qir_str = backend .qir (circuit )
442- module = pyqir .Module .from_ir (context , qir_str )
443- entry_point = next (filter (pyqir .is_entry_point , module .functions ))
444- entry_point .name = circuit .name
445- llvm_module .link (module )
446- err = llvm_module .verify ()
447- if err is not None :
448- raise Exception (err )
449-
450- return llvm_module
409+ qir_str = backend .qir (circuit )
410+
411+ return qir_str
451412
452- def _get_qir_str (
453- self ,
454- circuits : List [QuantumCircuit ],
455- target_profile : TargetProfile ,
456- ** to_qir_kwargs ,
457- ) -> str :
458- module = self ._generate_qir (circuits , target_profile , ** to_qir_kwargs )
459- return str (module )
460413
461414 def _translate_input (
462- self , circuits : Union [ QuantumCircuit , List [ QuantumCircuit ]] , input_params : Dict [str , Any ]
415+ self , circuit : QuantumCircuit , input_params : Dict [str , Any ]
463416 ) -> bytes :
464417 """Translates the input values to the QIR expected by the Backend."""
465418 logger .info (f"Using QIR as the job's payload format." )
466- if not (isinstance (circuits , list )):
467- circuits = [circuits ]
468419
469420 target_profile = self ._get_target_profile (input_params )
470421
471422 if logger .isEnabledFor (logging .DEBUG ):
472- qir = self ._get_qir_str (circuits , target_profile , skip_transpilation = True )
423+ qir = self ._get_qir_str (circuit , target_profile , skip_transpilation = True )
473424 logger .debug (f"QIR:\n { qir } " )
474425
475426 # We'll transpile automatically to the supported gates in QIR unless explicitly skipped.
476427 skip_transpilation = input_params .pop ("skipTranspile" , False )
477428
478- module = self ._generate_qir (
479- circuits , target_profile , skip_transpilation = skip_transpilation
429+ qir_str = self ._get_qir_str (
430+ circuit , target_profile , skip_transpilation = skip_transpilation
480431 )
481432
482- def get_func_name (func : pyqir .Function ) -> str :
483- return func .name
484-
485- entry_points = list (
486- map (get_func_name , filter (pyqir .is_entry_point , module .functions ))
487- )
433+ entry_points = ["ENTTRYPOINT_main" ]
488434
489435 if not skip_transpilation :
490436 # We'll only log the QIR again if we performed a transpilation.
491437 if logger .isEnabledFor (logging .DEBUG ):
492- qir = str (module )
438+ qir = str (qir_str )
493439 logger .debug (f"QIR (Post-transpilation):\n { qir } " )
494440
495441 if "items" not in input_params :
@@ -498,7 +444,7 @@ def get_func_name(func: pyqir.Function) -> str:
498444 {"entryPoint" : name , "arguments" : arguments } for name in entry_points
499445 ]
500446
501- return str ( module ) .encode ("utf-8" )
447+ return qir_str .encode ("utf-8" )
502448
503449 def _get_target_profile (self , input_params ) -> TargetProfile :
504450 # Default to Adaptive_RI if not specified on the backend
0 commit comments