def test_evals(self): """ evals test """ # pylint: disable=no-member # TODO: Think about eval names self.assertEqual(Z.eval('0').eval('0'), 1) self.assertEqual(Z.eval('1').eval('0'), 0) self.assertEqual(Z.eval('0').eval('1'), 0) self.assertEqual(Z.eval('1').eval('1'), -1) self.assertEqual(X.eval('0').eval('0'), 0) self.assertEqual(X.eval('1').eval('0'), 1) self.assertEqual(X.eval('0').eval('1'), 1) self.assertEqual(X.eval('1').eval('1'), 0) self.assertEqual(Y.eval('0').eval('0'), 0) self.assertEqual(Y.eval('1').eval('0'), -1j) self.assertEqual(Y.eval('0').eval('1'), 1j) self.assertEqual(Y.eval('1').eval('1'), 0) with self.assertRaises(ValueError): Y.eval('11') with self.assertRaises(ValueError): (X ^ Y).eval('1111') with self.assertRaises(ValueError): Y.eval((X ^ X).to_matrix_op()) # Check that Pauli logic eval returns same as matrix logic self.assertEqual(PrimitiveOp(Z.to_matrix()).eval('0').eval('0'), 1) self.assertEqual(PrimitiveOp(Z.to_matrix()).eval('1').eval('0'), 0) self.assertEqual(PrimitiveOp(Z.to_matrix()).eval('0').eval('1'), 0) self.assertEqual(PrimitiveOp(Z.to_matrix()).eval('1').eval('1'), -1) self.assertEqual(PrimitiveOp(X.to_matrix()).eval('0').eval('0'), 0) self.assertEqual(PrimitiveOp(X.to_matrix()).eval('1').eval('0'), 1) self.assertEqual(PrimitiveOp(X.to_matrix()).eval('0').eval('1'), 1) self.assertEqual(PrimitiveOp(X.to_matrix()).eval('1').eval('1'), 0) self.assertEqual(PrimitiveOp(Y.to_matrix()).eval('0').eval('0'), 0) self.assertEqual(PrimitiveOp(Y.to_matrix()).eval('1').eval('0'), -1j) self.assertEqual(PrimitiveOp(Y.to_matrix()).eval('0').eval('1'), 1j) self.assertEqual(PrimitiveOp(Y.to_matrix()).eval('1').eval('1'), 0) pauli_op = Z ^ I ^ X ^ Y mat_op = PrimitiveOp(pauli_op.to_matrix()) full_basis = list(map(''.join, itertools.product('01', repeat=pauli_op.num_qubits))) for bstr1, bstr2 in itertools.product(full_basis, full_basis): # print('{} {} {} {}'.format(bstr1, bstr2, pauli_op.eval(bstr1, bstr2), # mat_op.eval(bstr1, bstr2))) np.testing.assert_array_almost_equal(pauli_op.eval(bstr1).eval(bstr2), mat_op.eval(bstr1).eval(bstr2)) gnarly_op = SummedOp([(H ^ I ^ Y).compose(X ^ X ^ Z).tensor(Z), PrimitiveOp(Operator.from_label('+r0I')), 3 * (X ^ CX ^ T)], coeff=3 + .2j) gnarly_mat_op = PrimitiveOp(gnarly_op.to_matrix()) full_basis = list(map(''.join, itertools.product('01', repeat=gnarly_op.num_qubits))) for bstr1, bstr2 in itertools.product(full_basis, full_basis): np.testing.assert_array_almost_equal(gnarly_op.eval(bstr1).eval(bstr2), gnarly_mat_op.eval(bstr1).eval(bstr2))
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 add(self, other: OperatorBase) -> OperatorBase: if not self.num_qubits == other.num_qubits: raise ValueError( 'Sum over statefns with different numbers of qubits, {} and {}, is not well ' 'defined'.format(self.num_qubits, other.num_qubits)) # Right now doesn't make sense to add a StateFn to a Measurement if isinstance( other, DictStateFn) and self.is_measurement == other.is_measurement: # TODO add compatibility with vector and Operator? if self.primitive == other.primitive: return DictStateFn(self.primitive, coeff=self.coeff + other.coeff, is_measurement=self.is_measurement) else: new_dict = { b: (v * self.coeff) + (other.primitive.get(b, 0) * other.coeff) for (b, v) in self.primitive.items() } new_dict.update({ b: v * other.coeff for (b, v) in other.primitive.items() if b not in self.primitive }) return StateFn(new_dict, is_measurement=self._is_measurement) # pylint: disable=cyclic-import,import-outside-toplevel from qiskit.aqua.operators import SummedOp return SummedOp([self, other])
def test_list_op_parameters(self): """Test that Parameters are stored correctly in a List Operator""" lam = Parameter('λ') phi = Parameter('φ') omega = Parameter('ω') mat_op = PrimitiveOp([[0, 1], [1, 0]], coeff=omega) qc = QuantumCircuit(1) qc.rx(phi, 0) qc_op = PrimitiveOp(qc) op1 = SummedOp([mat_op, qc_op]) params = [phi, omega] self.assertEqual(op1.parameters, set(params)) # check list nesting case op2 = PrimitiveOp([[1, 0], [0, -1]], coeff=lam) list_op = ListOp([op1, op2]) params.append(lam) self.assertEqual(list_op.parameters, set(params))
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 test_add(self): """ add test """ pauli_sum = 3 * X + Y self.assertIsInstance(pauli_sum, PauliSumOp) expected = PauliSumOp(3.0 * SparsePauliOp(Pauli(label="X")) + SparsePauliOp(Pauli(label="Y"))) self.assertEqual(pauli_sum, expected) pauli_sum = X + Y summed_op = SummedOp([X, Y]) self.assertEqual(pauli_sum, summed_op) self.assertEqual(summed_op, pauli_sum)
assert indented_str_content == initial_str.split("\n") assert "\t\tString\n\t\tto indent\n" == ListOp._indent( "String\nto indent\n", indentation="\t\t") print( ComposedOp([ SummedOp([ ComposedOp([ OperatorStateFn(SummedOp( [0.18093119978423136 * Z, -1.052373245772859 * I], abelian=True), is_measurement=True), PrimitiveOp(HGate()) ]), ComposedOp([ OperatorStateFn(SummedOp([ 0.18093119978423136 * Z, -1.052373245772859 * I - 1.052373245772859 * I ], abelian=True), is_measurement=True), I ]) ]), PrimitiveOp(HGate()) ])) print(ComposedOp([SummedOp([X, Z])])) # https://github.com/Qiskit/qiskit-aqua/pull/1111 -> Matrix multiplicative factor (z + z == 2 * z) z = MatrixOp([[1, 0], [0, -1]])
def test_summed_op_reduce(self): """Test SummedOp""" sum_op = (X ^ X * 2) + (Y ^ Y) # type: SummedOp with self.subTest('SummedOp test 1'): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ['XX', 'YY']) self.assertListEqual([op.coeff for op in sum_op], [2, 1]) sum_op = (X ^ X * 2) + (Y ^ Y) sum_op += Y ^ Y with self.subTest('SummedOp test 2-a'): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ['XX', 'YY', 'YY']) self.assertListEqual([op.coeff for op in sum_op], [2, 1, 1]) sum_op = sum_op.collapse_summands() with self.subTest('SummedOp test 2-b'): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ['XX', 'YY']) self.assertListEqual([op.coeff for op in sum_op], [2, 2]) sum_op = (X ^ X * 2) + (Y ^ Y) sum_op += (Y ^ Y) + (X ^ X * 2) with self.subTest('SummedOp test 3-a'): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ['XX', 'YY', 'YY', 'XX']) self.assertListEqual([op.coeff for op in sum_op], [2, 1, 1, 2]) sum_op = sum_op.reduce() with self.subTest('SummedOp test 3-b'): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ['XX', 'YY']) self.assertListEqual([op.coeff for op in sum_op], [4, 2]) sum_op = SummedOp([X ^ X * 2, Y ^ Y], 2) with self.subTest('SummedOp test 4-a'): self.assertEqual(sum_op.coeff, 2) self.assertListEqual([str(op.primitive) for op in sum_op], ['XX', 'YY']) self.assertListEqual([op.coeff for op in sum_op], [2, 1]) sum_op = sum_op.collapse_summands() with self.subTest('SummedOp test 4-b'): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ['XX', 'YY']) self.assertListEqual([op.coeff for op in sum_op], [4, 2]) sum_op = SummedOp([X ^ X * 2, Y ^ Y], 2) sum_op += Y ^ Y with self.subTest('SummedOp test 5-a'): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ['XX', 'YY', 'YY']) self.assertListEqual([op.coeff for op in sum_op], [4, 2, 1]) sum_op = sum_op.collapse_summands() with self.subTest('SummedOp test 5-b'): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ['XX', 'YY']) self.assertListEqual([op.coeff for op in sum_op], [4, 3]) sum_op = SummedOp([X ^ X * 2, Y ^ Y], 2) sum_op += (X ^ X) * 2 + (Y ^ Y) with self.subTest('SummedOp test 6-a'): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ['XX', 'YY', 'XX', 'YY']) self.assertListEqual([op.coeff for op in sum_op], [4, 2, 2, 1]) sum_op = sum_op.collapse_summands() with self.subTest('SummedOp test 6-b'): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ['XX', 'YY']) self.assertListEqual([op.coeff for op in sum_op], [6, 3]) sum_op = SummedOp([X ^ X * 2, Y ^ Y], 2) sum_op += sum_op with self.subTest('SummedOp test 7-a'): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ['XX', 'YY', 'XX', 'YY']) self.assertListEqual([op.coeff for op in sum_op], [4, 2, 4, 2]) sum_op = sum_op.collapse_summands() with self.subTest('SummedOp test 7-b'): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ['XX', 'YY']) self.assertListEqual([op.coeff for op in sum_op], [8, 4]) sum_op = SummedOp([X ^ X * 2, Y ^ Y], 2) + SummedOp([X ^ X * 2, Z ^ Z], 3) with self.subTest('SummedOp test 8-a'): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ['XX', 'YY', 'XX', 'ZZ']) self.assertListEqual([op.coeff for op in sum_op], [4, 2, 6, 3]) sum_op = sum_op.collapse_summands() with self.subTest('SummedOp test 8-b'): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ['XX', 'YY', 'ZZ']) self.assertListEqual([op.coeff for op in sum_op], [10, 2, 3])
def _parameter_shift( self, operator: OperatorBase, params: Union[ParameterExpression, ParameterVector, List]) -> OperatorBase: r""" Args: operator: The operator containing circuits we are taking the derivative of. params: The parameters (ω) we are taking the derivative with respect to. If a ParameterVector is provided, each parameter will be shifted. Returns: param_shifted_op: An operator object which evaluates to the respective gradients. Raises: ValueError: If the given parameters do not occur in the provided operator TypeError: If the operator has more than one circuit representing the quantum state """ if isinstance(params, (ParameterVector, list)): param_grads = [ self._parameter_shift(operator, param) for param in params ] absent_params = [ params[i] for i, grad_ops in enumerate(param_grads) if grad_ops is None ] if len(absent_params) > 0: raise ValueError( "The following parameters do not appear in the provided operator: ", absent_params) return ListOp(absent_params) # By this point, it's only one parameter param = params if not isinstance(param, ParameterExpression): raise ValueError if isinstance(operator, ListOp) and not isinstance(operator, ComposedOp): return_op = operator.traverse( partial(self._parameter_shift, params=param)) # Remove any branch of the tree where the relevant parameter does not occur trimmed_oplist = [op for op in return_op.oplist if op is not None] # If all branches are None, remove the parent too if len(trimmed_oplist) == 0: return None # Rebuild the operator with the trimmed down oplist properties = { 'coeff': return_op._coeff, 'abelian': return_op._abelian } if return_op.__class__ == ListOp: properties['combo_fn'] = return_op.combo_fn return return_op.__class__(oplist=trimmed_oplist, **properties) else: circs = self.get_unique_circuits(operator) if len(circs) > 1: raise TypeError( 'Please define an operator with a single circuit representing ' 'the quantum state.') if len(circs) == 0: return operator circ = circs[0] if self.analytic: # Unroll the circuit into a gate set for which the gradient may be computed # using pi/2 shifts. circ = ParamShift._unroll_to_supported_operations(circ) operator = ParamShift._replace_operator_circuit(operator, circ) if param not in circ._parameter_table: return ~Zero @ One shifted_ops = [] summed_shifted_op = None for m, param_occurence in enumerate(circ._parameter_table[param]): param_index = param_occurence[1] pshift_op = deepcopy(operator) mshift_op = deepcopy(operator) # We need the circuit objects of the newly instantiated operators pshift_circ = self.get_unique_circuits(pshift_op)[0] mshift_circ = self.get_unique_circuits(mshift_op)[0] pshift_gate = pshift_circ._parameter_table[param][m][0] mshift_gate = mshift_circ._parameter_table[param][m][0] p_param = pshift_gate.params[param_index] m_param = mshift_gate.params[param_index] # For analytic gradients the circuit parameters are shifted once by +pi/2 and # once by -pi/2. if self.analytic: shift_constant = 0.5 pshift_gate.params[param_index] = (p_param + (np.pi / (4 * shift_constant))) mshift_gate.params[param_index] = (m_param - (np.pi / (4 * shift_constant))) # For finite difference gradients the circuit parameters are shifted once by # +epsilon and once by -epsilon. else: shift_constant = 1. / (2 * self._epsilon) pshift_gate.params[param_index] = (p_param + self._epsilon) mshift_gate.params[param_index] = (m_param - self._epsilon) # The results of the shifted operators are now evaluated according the parameter # shift / finite difference formula. if isinstance(operator, ComposedOp): shifted_op = shift_constant * (pshift_op - mshift_op) # If the operator represents a quantum state then we apply a special combo # function to evaluate probability gradients. elif isinstance(operator, StateFn): shifted_op = ListOp([pshift_op, mshift_op], combo_fn=partial( self._prob_combo_fn, shift_constant=shift_constant)) else: raise TypeError( 'Probability gradients are not supported for the given ' 'operator type') if isinstance(p_param, ParameterExpression) and not isinstance( p_param, Parameter): expr_grad = DerivativeBase.parameter_expression_grad( p_param, param) shifted_op *= expr_grad if not summed_shifted_op: summed_shifted_op = shifted_op else: summed_shifted_op += shifted_op shifted_ops.append(summed_shifted_op) if not SummedOp(shifted_ops).reduce(): return ~StateFn(Zero) @ One else: return SummedOp(shifted_ops).reduce()
def test_to_pauli_op(self): """ test to_pauli_op method """ target = X + Y self.assertIsInstance(target, PauliSumOp) expected = SummedOp([X, Y]) self.assertEqual(target.to_pauli_op(), expected)