def test_unsupported_backend_component(self): """Test that if an unsupported backend component is input then an error is raised.""" backend_component = "SomeNonExistentBackend" # Fill in workflow template with pytest.raises(ValueError, match="The specified backend component is not supported."): gw.gen_expval_workflow( backend_component, backend_specs_default, qasm_circuit_default, operator_string_default, )
def test_submit_raises(self, backend_component, tmpdir): """Test that submitting a workflow to Orquestra with invalid requirements raises an error.""" # Skip if not logged in to Orquestra try_resp = qe_list_workflow() need_login_msg = "token has expired, please log in again\n" if need_login_msg in try_resp: pytest.skip("Has not logged in to the Orquestra platform.") circuits = [qasm_circuit_default] # This will not be a valid operator: will raise error operator = [] # Fill in workflow template workflow = gw.gen_expval_workflow(backend_component, backend_specs_default, circuits, operator) file_name = tmpdir.join("test_workflow.yaml") with open(file_name, "w") as file: d = yaml.dump(workflow, file) # Submit a workflow --- error due to the operator with pytest.raises(ValueError, match="Error"): workflow_id = qe_submit(file_name)
def test_can_submit_and_query_workflow_details(self, resources, tmpdir): """Test that filling in the workflow template for getting expectation values can be submitted to Orquestra and workflow details can be queried.""" # Skip if not logged in to Orquestra try_resp = qe_list_workflow() need_login_msg = "token has expired, please log in again\n" if need_login_msg in try_resp: pytest.skip("Has not logged in to the Orquestra platform.") backend_component = "qe-forest" op = ['["[Z0]"]'] circuits = [qasm_circuit_default] # Fill in workflow template workflow = gw.gen_expval_workflow(backend_component, backend_specs_default, circuits, op, resources=resources) file_name = tmpdir.join("test_workflow.yaml") with open(file_name, "w") as file: d = yaml.dump(workflow, file) # Submit a workflow workflow_id = qe_submit(file_name) workflow_msg = workflow_details(workflow_id) details_string = "".join(workflow_msg) assert workflow_id in details_string
def execute(self, circuit, **kwargs): # Input checks not_all_expval = any(obs.return_type is not Expectation for obs in circuit.observables) if not_all_expval: raise NotImplementedError( f"The {self.short_name} device only supports returning expectation values." ) self.check_validity(circuit.operations, circuit.observables) qasm_circuit = self.serialize_circuit(circuit) # 2. Create the qubit operators ops, identity_indices = self.process_observables(circuit.observables) if not ops: # All the observables were identity, no workflow submission needed return self._asarray([1] * len(identity_indices)) ops_json = json.dumps(ops) # Single step: need to nest the operators into a list ops = [ops_json] qasm_circuit = [qasm_circuit] # 4-5. Create the backend specs & workflow file workflow = gen_expval_workflow( self.qe_component, self.backend_specs, qasm_circuit, ops, resources=self._resources, **kwargs, ) file_id = str(uuid.uuid4()) filename = f"expval-{file_id}.yaml" filepath = write_workflow_file(filename, workflow) # 6. Submit the workflow workflow_id = qe_submit(filepath, keep_file=self._keep_files) if self._keep_files: self._filenames.append(filename) self._latest_id = workflow_id # 7. Loop until finished results = self.single_step_results(workflow_id) # Insert the theoretical value for the expectation value of the # identity operator for idx in identity_indices: results.insert(idx, 1) res = self._asarray(results) return res
def test_can_yaml(self, tmpdir): """Test that filling in the workflow template for getting expectation values produces a valid yaml.""" backend_component = "qe-forest" # Fill in workflow template workflow = gw.gen_expval_workflow( backend_component, backend_specs_default, qasm_circuit_default, operator_string_default ) file_name = tmpdir.join("test_workflow.yaml") with open(file_name, "w") as file: # Testing that no errors arise here yaml.dump(workflow, file)
def test_write_workflow_file(self, tmpdir, monkeypatch): """Test that filling in the workflow template for getting expectation values produces a valid yaml.""" backend_component = "qe-forest" # Fill in workflow template workflow = gw.gen_expval_workflow(backend_component, backend_specs_default, qasm_circuit_default, operator_string_default) file_name = "test_workflow.yaml" with monkeypatch.context() as m: m.setattr(pennylane_orquestra.cli_actions, "user_data_dir", lambda *args: tmpdir) write_workflow_file(file_name, workflow) with open(tmpdir.join(file_name)) as file: loaded_yaml = yaml.load(file, Loader=yaml.FullLoader) assert workflow == loaded_yaml
def test_matches_template(self, resources, test_wf): """Test that generating a two-step workflow matches the pre-defined template.""" backend_component = "qe-forest" # Fill in workflow template circuits = [qasm_circuit_default, qasm_circuit_default] workflow = gw.gen_expval_workflow( backend_component, backend_specs_default, circuits, operator_string_default, resources=resources, ) assert workflow["apiVersion"] == test_wf["apiVersion"] assert workflow["name"] == test_wf["name"] assert workflow["imports"] == test_wf["imports"] compare_two_expval_steps(workflow["steps"][0], test_wf["steps"][0]) compare_two_expval_steps(workflow["steps"][1], test_wf["steps"][1]) assert workflow == test_wf
def multi_step_execute(self, circuits, file_id, **kwargs): """Creates a multi-step workflow for executing a batch of circuits. Args: circuits (list[QuantumTape]): circuits to execute on the device file_id (str): the file id to be used for naming the workflow file Returns: list[array[float]]: list of measured value(s) for the batch """ for circuit in circuits: # Input checks not_all_expval = any(obs.return_type is not Expectation for obs in circuit.observables) if not_all_expval: raise NotImplementedError( f"The {self.short_name} device only supports returning expectation values." ) self.check_validity(circuit.operations, circuit.observables) # 1. Create qasm strings from the circuits qasm_circuits = [ self.serialize_circuit(circuit) for circuit in circuits ] # 2. Create the qubit operators of observables for each circuit ops = [] identity_indices = {} empty_obs_list = [] for idx, circuit in enumerate(circuits): processed_observables, current_id_indices = self.process_observables( circuit.observables) ops.append(processed_observables) if not processed_observables: # Keep track of empty observable lists empty_obs_list.append(idx) identity_indices[idx] = current_id_indices if not all(ops): # There were batches which had only identity observables if not any(ops): # All the batches only had identity observables, no workflow submission needed return [ self._asarray([1] * len(circuit.observables)) for circuit in circuits ] # Remove the empty lists so that those are not submitted ops = [o for o in ops if o] # Multiple steps: need to create json strings as elements of the list ops = [json.dumps(o) for o in ops] # 3-4. Create the backend specs & workflow file workflow = gen_expval_workflow( self.qe_component, self.backend_specs, qasm_circuits, ops, resources=self._resources, **kwargs, ) filename = f"expval-{file_id}.yaml" filepath = write_workflow_file(filename, workflow) # 5. Submit the workflow workflow_id = qe_submit(filepath, keep_file=self._keep_files) self._latest_id = workflow_id if self._keep_files: self._filenames.append(filename) # 6. Loop until finished results = self.multiple_steps_results(workflow_id) results = self.insert_identity_res_batch(results, empty_obs_list, identity_indices) results = [self._asarray(res) for res in results] return results