def observable(self, num_qubits: int) -> Union[TensoredOp, List[TensoredOp]]: """The observable operators. Args: num_qubits: The number of qubits on which the observable will be applied. Returns: The observable as a list of sums of Pauli strings. """ zero_op = (I + Z) / 2 one_op = (I - Z) / 2 observables = [] # First we measure the norm of x observables.append(I ^ num_qubits) for i in range(num_qubits): j = num_qubits - i - 1 # TODO this if can be removed once the bug in Opflow is fixed where # TensoredOp([X, TensoredOp([])]).eval() ends up in infinite recursion if i > 0: observables += [ (I ^ j) ^ zero_op ^ TensoredOp(i * [one_op]), (I ^ j) ^ one_op ^ TensoredOp(i * [one_op]), ] else: observables += [(I ^ j) ^ zero_op, (I ^ j) ^ one_op] return observables
def test_permute_on_list_op(self): """Test if ListOp permute method is consistent with PrimitiveOps permute methods.""" op1 = (X ^ Y ^ Z).to_circuit_op() op2 = Z ^ X ^ Y # ComposedOp indices = [1, 2, 0] primitive_op = op1 @ op2 primitive_op_perm = primitive_op.permute(indices) # CircuitOp.permute composed_op = ComposedOp([op1, op2]) composed_op_perm = composed_op.permute(indices) # reduce the ListOp to PrimitiveOp to_primitive = composed_op_perm.oplist[0] @ composed_op_perm.oplist[1] # compare resulting PrimitiveOps equal = np.allclose(primitive_op_perm.to_matrix(), to_primitive.to_matrix()) self.assertTrue(equal) # TensoredOp indices = [3, 5, 4, 0, 2, 1] primitive_op = op1 ^ op2 primitive_op_perm = primitive_op.permute(indices) tensored_op = TensoredOp([op1, op2]) tensored_op_perm = tensored_op.permute(indices) # reduce the ListOp to PrimitiveOp composed_oplist = tensored_op_perm.oplist to_primitive = (composed_oplist[0] @ (composed_oplist[1].oplist[0] ^ composed_oplist[1].oplist[1]) @ composed_oplist[2]) # compare resulting PrimitiveOps equal = np.allclose(primitive_op_perm.to_matrix(), to_primitive.to_matrix()) self.assertTrue(equal) # SummedOp primitive_op = X ^ Y ^ Z summed_op = SummedOp([primitive_op]) indices = [1, 2, 0] primitive_op_perm = primitive_op.permute(indices) # PauliOp.permute summed_op_perm = summed_op.permute(indices) # reduce the ListOp to PrimitiveOp to_primitive = summed_op_perm.oplist[ 0] @ primitive_op @ summed_op_perm.oplist[2] # compare resulting PrimitiveOps equal = np.allclose(primitive_op_perm.to_matrix(), to_primitive.to_matrix()) self.assertTrue(equal)
def test_empty_listops(self): """Test reduce and eval on ListOp with empty oplist.""" with self.subTest("reduce empty ComposedOp "): self.assertEqual(ComposedOp([]).reduce(), ComposedOp([])) with self.subTest("reduce empty TensoredOp "): self.assertEqual(TensoredOp([]).reduce(), TensoredOp([])) with self.subTest("eval empty ComposedOp "): self.assertEqual(ComposedOp([]).eval(), 0.0) with self.subTest("eval empty TensoredOp "): self.assertEqual(TensoredOp([]).eval(), 0.0)
def test_expand_on_list_op(self): """ Test if expanded ListOp has expected num_qubits. """ add_qubits = 3 # ComposedOp composed_op = ComposedOp([(X ^ Y ^ Z), (H ^ T), (Z ^ X ^ Y ^ Z).to_matrix_op()]) expanded = composed_op._expand_dim(add_qubits) self.assertEqual(composed_op.num_qubits + add_qubits, expanded.num_qubits) # TensoredOp tensored_op = TensoredOp([(X ^ Y), (Z ^ I)]) expanded = tensored_op._expand_dim(add_qubits) self.assertEqual(tensored_op.num_qubits + add_qubits, expanded.num_qubits) # SummedOp summed_op = SummedOp([(X ^ Y), (Z ^ I ^ Z)]) expanded = summed_op._expand_dim(add_qubits) self.assertEqual(summed_op.num_qubits + add_qubits, expanded.num_qubits)
def observable(self, num_qubits: int) -> Union[TensoredOp, List[TensoredOp]]: """The observable operator. Args: num_qubits: The number of qubits on which the observable will be applied. Returns: The observable as a sum of Pauli strings. """ zero_op = (I + Z) / 2 return TensoredOp(num_qubits * [zero_op])
def test_coder_operators(self): """Test runtime encoder and decoder for operators.""" x = Parameter("x") y = x + 1 qc = QuantumCircuit(1) qc.h(0) coeffs = np.array([1, 2, 3, 4, 5, 6]) table = PauliTable.from_labels( ["III", "IXI", "IYY", "YIZ", "XYZ", "III"]) op = (2.0 * I ^ I) z2_symmetries = Z2Symmetries( [Pauli("IIZI"), Pauli("ZIII")], [Pauli("IIXI"), Pauli("XIII")], [1, 3], [-1, 1]) isqrt2 = 1 / np.sqrt(2) sparse = scipy.sparse.csr_matrix([[0, isqrt2, 0, isqrt2]]) subtests = ( PauliSumOp(SparsePauliOp(Pauli("XYZX"), coeffs=[2]), coeff=3), PauliSumOp(SparsePauliOp(Pauli("XYZX"), coeffs=[1]), coeff=y), PauliSumOp(SparsePauliOp(Pauli("XYZX"), coeffs=[1 + 2j]), coeff=3 - 2j), PauliSumOp.from_list([("II", -1.052373245772859), ("IZ", 0.39793742484318045)]), PauliSumOp(SparsePauliOp(table, coeffs), coeff=10), MatrixOp(primitive=np.array([[0, -1j], [1j, 0]]), coeff=x), PauliOp(primitive=Pauli("Y"), coeff=x), CircuitOp(qc, coeff=x), EvolvedOp(op, coeff=x), TaperedPauliSumOp(SparsePauliOp(Pauli("XYZX"), coeffs=[2]), z2_symmetries), StateFn(qc, coeff=x), CircuitStateFn(qc, is_measurement=True), DictStateFn("1" * 3, is_measurement=True), VectorStateFn(np.ones(2**3, dtype=complex)), OperatorStateFn(CircuitOp(QuantumCircuit(1))), SparseVectorStateFn(sparse), Statevector([1, 0]), CVaRMeasurement(Z, 0.2), ComposedOp([(X ^ Y ^ Z), (Z ^ X ^ Y ^ Z).to_matrix_op()]), SummedOp([X ^ X * 2, Y ^ Y], 2), TensoredOp([(X ^ Y), (Z ^ I)]), (Z ^ Z) ^ (I ^ 2), ) for op in subtests: with self.subTest(op=op): encoded = json.dumps(op, cls=RuntimeEncoder) self.assertIsInstance(encoded, str) decoded = json.loads(encoded, cls=RuntimeDecoder) self.assertEqual(op, decoded)
def test_gradient_ryy(self, method): """Test the state gradient for YY rotation""" alpha = Parameter("alpha") ham = TensoredOp([Y, alpha * Y]) a = Parameter("a") q = QuantumRegister(2) qc = QuantumCircuit(q) qc.ryy(a, q[0], q[1]) op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) state_grad = Gradient(grad_method=method).convert(operator=op, params=a) values_dict = [{a: np.pi / 8}, {a: np.pi}] correct_values = [[0], [0]] for i, value_dict in enumerate(values_dict): value_dict[alpha] = 1.0 np.testing.assert_array_almost_equal( state_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=1 )
def test_gradient_rxx(self, method): """Test the state gradient for XX rotation""" ham = TensoredOp([Z, X]) a = Parameter("a") q = QuantumRegister(2) qc = QuantumCircuit(q) qc.h(q[0]) qc.rxx(a, q[0], q[1]) op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) params = [a] state_grad = Gradient(grad_method=method).convert(operator=op, params=params) values_dict = [{a: np.pi / 4}, {a: np.pi / 2}] correct_values = [[-0.707], [-1.0]] for i, value_dict in enumerate(values_dict): np.testing.assert_array_almost_equal( state_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=1 )
def _calculate_norm(self, qc: QuantumCircuit) -> float: """Calculates the value of the euclidean norm of the solution. Args: qc: The quantum circuit preparing the solution x to the system. Returns: The value of the euclidean norm of the solution. """ # Calculate the number of qubits nb = qc.qregs[0].size nl = qc.qregs[1].size na = qc.num_ancillas # Create the Operators Zero and One zero_op = (I + Z) / 2 one_op = (I - Z) / 2 # Norm observable observable = one_op ^ TensoredOp((nl + na) * [zero_op]) ^ (I ^ nb) norm_2 = (~StateFn(observable) @ StateFn(qc)).eval() return np.real(np.sqrt(norm_2) / self.scaling)
def test_compose_with_indices(self): """Test compose method using its permutation feature.""" pauli_op = X ^ Y ^ Z circuit_op = T ^ H matrix_op = (X ^ Y ^ H ^ T).to_matrix_op() evolved_op = EvolvedOp(matrix_op) # composition of PrimitiveOps num_qubits = 4 primitive_op = pauli_op @ circuit_op @ matrix_op composed_op = pauli_op @ circuit_op @ evolved_op self.assertEqual(primitive_op.num_qubits, num_qubits) self.assertEqual(composed_op.num_qubits, num_qubits) # with permutation num_qubits = 5 indices = [1, 4] permuted_primitive_op = evolved_op @ circuit_op.permute( indices) @ pauli_op @ matrix_op composed_primitive_op = (evolved_op @ pauli_op.compose( circuit_op, permutation=indices, front=True) @ matrix_op) self.assertTrue( np.allclose(permuted_primitive_op.to_matrix(), composed_primitive_op.to_matrix())) self.assertEqual(num_qubits, permuted_primitive_op.num_qubits) # ListOp num_qubits = 6 tensored_op = TensoredOp([pauli_op, circuit_op]) summed_op = pauli_op + circuit_op.permute([2, 1]) composed_op = circuit_op @ evolved_op @ matrix_op list_op = summed_op @ composed_op.compose( tensored_op, permutation=[1, 2, 3, 5, 4], front=True) self.assertEqual(num_qubits, list_op.num_qubits) num_qubits = 4 circuit_fn = CircuitStateFn(primitive=circuit_op.primitive, is_measurement=True) operator_fn = OperatorStateFn(primitive=circuit_op ^ circuit_op, is_measurement=True) no_perm_op = circuit_fn @ operator_fn self.assertEqual(no_perm_op.num_qubits, num_qubits) indices = [0, 4] perm_op = operator_fn.compose(circuit_fn, permutation=indices, front=True) self.assertEqual(perm_op.num_qubits, max(indices) + 1) # StateFn num_qubits = 3 dim = 2**num_qubits vec = [1.0 / (i + 1) for i in range(dim)] dic = { format(i, "b").zfill(num_qubits): 1.0 / (i + 1) for i in range(dim) } is_measurement = True op_state_fn = OperatorStateFn( matrix_op, is_measurement=is_measurement) # num_qubit = 4 vec_state_fn = VectorStateFn(vec, is_measurement=is_measurement) # 3 dic_state_fn = DictStateFn(dic, is_measurement=is_measurement) # 3 circ_state_fn = CircuitStateFn(circuit_op.to_circuit(), is_measurement=is_measurement) # 2 composed_op = op_state_fn @ vec_state_fn @ dic_state_fn @ circ_state_fn self.assertEqual(composed_op.num_qubits, op_state_fn.num_qubits) # with permutation perm = [2, 4, 6] composed = (op_state_fn @ dic_state_fn.compose( vec_state_fn, permutation=perm, front=True) @ circ_state_fn) self.assertEqual(composed.num_qubits, max(perm) + 1)
def _calculate_observable( self, solution: QuantumCircuit, observable: Optional[Union[LinearSystemObservable, BaseOperator]] = None, observable_circuit: Optional[QuantumCircuit] = None, post_processing: Optional[Callable[[Union[float, List[float]]], Union[float, List[float]]]] = None ) -> Tuple[Union[float, List[float]], Union[float, List[float]]]: """Calculates the value of the observable(s) given. Args: solution: The quantum circuit preparing the solution x to the system. observable: Information to be extracted from the solution. observable_circuit: Circuit to be applied to the solution to extract information. post_processing: Function to compute the value of the observable. Returns: The value of the observable(s) and the circuit results before post-processing as a tuple. """ # Get the number of qubits nb = solution.qregs[0].size nl = solution.qregs[1].size na = solution.num_ancillas # if the observable is given construct post_processing and observable_circuit if observable is not None: observable_circuit = observable.observable_circuit(nb) post_processing = observable.post_processing if isinstance(observable, LinearSystemObservable): observable = observable.observable(nb) # in the other case use the identity as observable else: observable = I ^ nb # Create the Operators Zero and One zero_op = (I + Z) / 2 one_op = (I - Z) / 2 is_list = True if not isinstance(observable_circuit, list): is_list = False observable_circuit = [observable_circuit] observable = [observable] expectations = [] for circ, obs in zip(observable_circuit, observable): circuit = QuantumCircuit(solution.num_qubits) circuit.append(solution, circuit.qubits) circuit.append(circ, range(nb)) ob = one_op ^ TensoredOp((nl + na) * [zero_op]) ^ obs expectations.append(~StateFn(ob) @ StateFn(circuit)) if is_list: # execute all in a list op to send circuits in batches expectations = ListOp(expectations) else: expectations = expectations[0] # check if an expectation converter is given if self._expectation is not None: expectations = self._expectation.convert(expectations) # if otherwise a backend was specified, try to set the best expectation value elif self._sampler is not None: if is_list: op = expectations.oplist[0] else: op = expectations self._expectation = ExpectationFactory.build( op, self._sampler.quantum_instance) if self._sampler is not None: expectations = self._sampler.convert(expectations) # evaluate expectation_results = expectations.eval() # apply post_processing result = post_processing(expectation_results, nb, self.scaling) return result, expectation_results