def test_generate_dnf_hamiltonian_content(self): clauses = [ dnf_lib.Clause(2, 4, True, False), dnf_lib.Clause(5, 4, True, True) ] dnf = dnf_lib.DNF(10, clauses) circuit = cirq.Circuit() circuit.append(dnf_circuit_lib.generate_dnf_hamiltonian_exponential( dnf, 0.25), strategy=insert_strategy.InsertStrategy.NEW_THEN_INLINE) operations = list(circuit.all_operations()) self.assertCountEqual(operations, [ cirq.ZPowGate(exponent=-0.5 / math.pi, global_shift=-0.5).on( cirq.LineQubit(4)), cirq.ZPowGate(exponent=0.5 / math.pi, global_shift=-0.5).on( cirq.LineQubit(2)), cirq.ZZPowGate(exponent=0.5 / math.pi, global_shift=-0.5).on( cirq.LineQubit(2), cirq.LineQubit(4)), cirq.ZPowGate(exponent=0.5 / math.pi, global_shift=-0.5).on( cirq.LineQubit(4)), cirq.ZPowGate(exponent=0.5 / math.pi, global_shift=-0.5).on( cirq.LineQubit(5)), cirq.ZZPowGate(exponent=-0.5 / math.pi, global_shift=-0.5).on( cirq.LineQubit(4), cirq.LineQubit(5)), ])
def test_generate_dnf_hamiltonian_order(self): circuit = cirq.Circuit() dnf = dnf_lib.DNF(10, [dnf_lib.Clause(5, 7, False, False)]) circuit.append(dnf_circuit_lib.generate_dnf_hamiltonian_exponential( dnf, 0.5), strategy=insert_strategy.InsertStrategy.NEW_THEN_INLINE) generator = circuit.all_operations() operation = next(generator) self.assertEqual( operation, cirq.ZPowGate(exponent=-1.0 / math.pi, global_shift=-0.5).on(cirq.LineQubit(5))) operation = next(generator) self.assertEqual( operation, cirq.ZPowGate(exponent=-1.0 / math.pi, global_shift=-0.5).on(cirq.LineQubit(7))) operation = next(generator) self.assertEqual( operation, cirq.ZZPowGate(exponent=-1.0 / math.pi, global_shift=-0.5).on(cirq.LineQubit(5), cirq.LineQubit(7))) with self.assertRaises(StopIteration): next(generator)
def test_eval(self, literals, expected_evaluation): # (x_0 OR x_2) AND (x_1 OR not(x_2)) clauses = [ dnf_lib.Clause(0, 2, False, False), dnf_lib.Clause(1, 2, False, True) ] dnf = dnf_lib.DNF(3, clauses) self.assertEqual(dnf.eval(literals), expected_evaluation)
def test_get_probabilities_wrong_shape(self): dnf = dnf_lib.DNF(2, [dnf_lib.Clause(0, 1, True, False)]) # time is chosen so that ZPowGate = sqrt(Z) and Rx = sqrt(X) up to phase. circuit = dnf_circuit_lib.BangBangProtocolCircuit(math.pi / 4, dnf) with self.assertRaisesRegex( ValueError, r'The shape of wavefunction should be \(4\,\) but got \(3\,\)' ): circuit.get_probabilities(wavefunction=np.array([1., 0., 0.]))
def test_get_num_clauses_satisfied(self, literals, expected_clauses_satisfied): # (x_0 OR x_2) AND (x_1 OR not(x_2)) clauses = [ dnf_lib.Clause(0, 2, False, False), dnf_lib.Clause(1, 2, False, True) ] dnf = dnf_lib.DNF(3, clauses) self.assertEqual(dnf.get_num_clauses_satisfied(literals), expected_clauses_satisfied)
def test_constraint_evaluation(self, measurement, expected_value): dnf = dnf_lib.DNF(4, [ dnf_lib.Clause(0, 1, False, False), dnf_lib.Clause(0, 1, True, True), dnf_lib.Clause(0, 1, False, True), dnf_lib.Clause(0, 1, True, False), dnf_lib.Clause(2, 3, False, False) ]) self.assertEqual(dnf.optimal_num_satisfied, 4) circuit = dnf_circuit_lib.BangBangProtocolCircuit(1, dnf) self.assertEqual(circuit.constraint_evaluation(measurement), expected_value)
def test_get_constraint_expectation(self): dnf = dnf_lib.DNF(2, [dnf_lib.Clause(0, 1, True, False)]) # time is chosen so that ZPowGate = sqrt(Z) and Rx = sqrt(X) up to phase. circuit = dnf_circuit_lib.BangBangProtocolCircuit(math.pi / 4, dnf) bangbang_protocol = [ circuit_lib.HamiltonianType.CONSTRAINT, circuit_lib.HamiltonianType.X, ] self.assertAlmostEqual(circuit.get_constraint_expectation( circuit.get_wavefunction(bangbang_protocol)), 0.5, places=5)
def test_str(self): clauses = [ dnf_lib.Clause(0, 2, False, False), dnf_lib.Clause(1, 2, False, True) ] dnf = dnf_lib.DNF(3, clauses) expected_regex = (r'^Number of Literals: 3, ' r'DNF: \(\!?x_\d \|\| \!?x_\d\) && ' r'\(\!?x_\d \|\| \!?x_\d\)$') self.assertRegex(str(dnf), expected_regex) self.assertIn('(x_0 || x_2)', str(dnf)) self.assertIn('(x_1 || !x_2)', str(dnf))
def test_get_probabilities(self): dnf = dnf_lib.DNF(2, [dnf_lib.Clause(0, 1, True, False)]) # time is chosen so that ZPowGate = sqrt(Z) and Rx = sqrt(X) up to phase. circuit = dnf_circuit_lib.BangBangProtocolCircuit(math.pi / 4, dnf) bangbang_protocol = [ circuit_lib.HamiltonianType.CONSTRAINT, circuit_lib.HamiltonianType.X, ] probabilities = circuit.get_probabilities( circuit.get_wavefunction(bangbang_protocol)) np.testing.assert_allclose(probabilities, [0, 0.5, 0.5, 0], atol=0.00001)
def test_get_wavefunction(self): dnf = dnf_lib.DNF(2, [dnf_lib.Clause(0, 1, True, False)]) # time is chosen so that ZPowGate = sqrt(Z) and Rx = sqrt(X) up to phase. circuit = dnf_circuit_lib.BangBangProtocolCircuit(math.pi / 4, dnf) bangbang_protocol = [ circuit_lib.HamiltonianType.CONSTRAINT, circuit_lib.HamiltonianType.X, ] cirq.testing.assert_allclose_up_to_global_phase( circuit.get_wavefunction(bangbang_protocol), np.array([0, (1 + 1j) / 2, (-1 + 1j) / 2, 0]), atol=0.000001)
def test_stochastic_descent_neg_max_num_flips(self): # Every 2-SAT with just one clause will not be satisfied by 1/4 of all # possible literal assignments. # With only one bang, the only way to do better than random guessing is to # apply the DNF hamiltonian. dnf = dnf_lib.DNF(3, [dnf_lib.Clause(0, 1, False, True)]) circuit = dnf_circuit_lib.BangBangProtocolCircuit(1, dnf) with self.assertRaisesRegex( ValueError, 'max_num_flips should be positive, not -10'): stochastic_descent_lib.stochastic_descent( circuit=circuit, max_num_flips=-10, initial_protocol=stochastic_descent_lib.get_random_protocol(5), minimize=False)
def test_stochastic_descent_epoch_neg_max_num_flips(self): dnf = dnf_lib.DNF(3, [dnf_lib.Clause(0, 1, False, True)]) circuit = dnf_circuit_lib.BangBangProtocolCircuit(1, dnf) with self.assertRaisesRegex(ValueError, 'max_num_flips should be positive, not 0'): stochastic_descent_lib._stochastic_descent_epoch( circuit=circuit, bangbang_protocol=[ circuit_lib.HamiltonianType.X, circuit_lib.HamiltonianType.X, circuit_lib.HamiltonianType.CONSTRAINT ], max_num_flips=0, previous_eval=0.75, minimize=False)
def test_stochastic_descent_skip_search(self): dnf = dnf_lib.DNF(2, [dnf_lib.Clause(0, 1, True, True)]) circuit = dnf_circuit_lib.BangBangProtocolCircuit(1, dnf) random_protocol = stochastic_descent_lib.get_random_protocol(2) random_eval = circuit.get_constraint_expectation( circuit.get_wavefunction(random_protocol)) protocol, evaluation, num_epoch = stochastic_descent_lib.stochastic_descent( circuit=circuit, max_num_flips=1, initial_protocol=random_protocol, minimize=False, skip_search=True) self.assertListEqual(protocol, random_protocol) self.assertIsInstance(evaluation, float) self.assertAlmostEqual(evaluation, random_eval) # Zero epoch of stochastic descent. self.assertEqual(num_epoch, 0)
def test_stochastic_descent_epoch_minimize(self): # Every 2-SAT with just one clause will not be satisfied by 1/4 of all # possible literal assignments. dnf = dnf_lib.DNF(3, [dnf_lib.Clause(0, 1, False, True)]) circuit = dnf_circuit_lib.BangBangProtocolCircuit(1, dnf) protocol, evaluation = stochastic_descent_lib._stochastic_descent_epoch( circuit=circuit, bangbang_protocol=[ circuit_lib.HamiltonianType.X, circuit_lib.HamiltonianType.X, circuit_lib.HamiltonianType.CONSTRAINT ], max_num_flips=2, previous_eval=0.75, minimize=True) self.assertLen(protocol, 3) self.assertIsInstance(evaluation, float) self.assertLessEqual(evaluation, 0.75)
def test_generate_qaoa_circuit(self): dnf = dnf_lib.DNF(5, [dnf_lib.Clause(1, 2, False, False)]) circuit = dnf_circuit_lib.BangBangProtocolCircuit(0.2, dnf) qaoa_circuit = circuit.qaoa_circuit([ circuit_lib.HamiltonianType.CONSTRAINT, circuit_lib.HamiltonianType.CONSTRAINT, circuit_lib.HamiltonianType.CONSTRAINT, circuit_lib.HamiltonianType.X, circuit_lib.HamiltonianType.CONSTRAINT, circuit_lib.HamiltonianType.X, circuit_lib.HamiltonianType.X, ]) # Should 21 contain gates # 5 gates from Hadamard Layer # 2 layers of X Hamiltonian, which has 5 gates each # 2 layers of DNF Hamiltonian, which has 3 gates each # 5 + 2*5 + 2*3 = 21 self.assertLen(list(qaoa_circuit.all_operations()), 21)
def test_stochastic_descent_maximize(self): # Every 2-SAT with just one clause will not be satisfied by 1/4 of all # possible literal assignments. # With only one bang, the only way to do better than random guessing is to # apply the DNF hamiltonian. dnf = dnf_lib.DNF(2, [dnf_lib.Clause(0, 1, True, True)]) circuit = dnf_circuit_lib.BangBangProtocolCircuit(1, dnf) random_protocol = stochastic_descent_lib.get_random_protocol(2) random_eval = circuit.get_constraint_expectation( circuit.get_wavefunction(random_protocol)) protocol, evaluation, num_epoch = stochastic_descent_lib.stochastic_descent( circuit=circuit, max_num_flips=2, initial_protocol=stochastic_descent_lib.get_random_protocol(5), minimize=False) self.assertLen(protocol, 5) self.assertIsInstance(evaluation, float) self.assertGreaterEqual(evaluation, random_eval) # Contain at least 1 epoch of stochastic descent. self.assertGreaterEqual(num_epoch, 1)
class DNFTest(parameterized.TestCase): @parameterized.parameters( (dnf_lib.DNF(20, []), 20, set(), 0), (dnf_lib.DNF(5, [ dnf_lib.Clause(0, 1, True, True), dnf_lib.Clause(3, 4, False, False) ]), 5, set([ dnf_lib.Clause(0, 1, True, True), dnf_lib.Clause(3, 4, False, False) ]), 2), (dnf_lib.DNF(4, [ dnf_lib.Clause(0, 1, False, False), dnf_lib.Clause(0, 1, True, True), dnf_lib.Clause(0, 1, False, True), dnf_lib.Clause(0, 1, True, False) ]), 4, set([ dnf_lib.Clause(0, 1, False, False), dnf_lib.Clause(0, 1, True, True), dnf_lib.Clause(0, 1, False, True), dnf_lib.Clause(0, 1, True, False) ]), 3), # Duplicated clauses. (dnf_lib.DNF(5, [ dnf_lib.Clause(0, 1, True, True), dnf_lib.Clause(0, 1, True, True) ]), 5, set([dnf_lib.Clause(0, 1, True, True)]), 1), ) def test_init(self, dnf, expected_literals, expected_clauses, expected_optimal): self.assertEqual(dnf.num_literals, expected_literals) self.assertSetEqual(dnf.clauses, expected_clauses) self.assertEqual(dnf.optimal_num_satisfied, expected_optimal) @parameterized.parameters( (dnf_lib.DNF(5, [ dnf_lib.Clause(0, 1, True, True), dnf_lib.Clause(3, 4, False, False) ]), 2), (dnf_lib.DNF(4, [ dnf_lib.Clause(0, 1, False, False), dnf_lib.Clause(0, 1, True, True), dnf_lib.Clause(0, 1, False, True), dnf_lib.Clause(0, 1, True, False) ]), 3), ) def test_get_optimal_num_satisfied(self, dnf, expected_value): self.assertEqual(dnf._get_optimal_num_satisfied(), expected_value) @parameterized.parameters( (-5, 'num_literals must be at least 2, not -5'), (1, 'num_literals must be at least 2, not 1'), ) def test_init_neg_num(self, num_literals, error_message): with self.assertRaisesRegex(ValueError, error_message): dnf_lib.DNF(num_literals, set()) def test_str(self): clauses = [ dnf_lib.Clause(0, 2, False, False), dnf_lib.Clause(1, 2, False, True) ] dnf = dnf_lib.DNF(3, clauses) expected_regex = (r'^Number of Literals: 3, ' r'DNF: \(\!?x_\d \|\| \!?x_\d\) && ' r'\(\!?x_\d \|\| \!?x_\d\)$') self.assertRegex(str(dnf), expected_regex) self.assertIn('(x_0 || x_2)', str(dnf)) self.assertIn('(x_1 || !x_2)', str(dnf)) @parameterized.parameters( ([True, True, True], True), ([True, True, False], True), ([True, False, True], False), ([True, False, False], True), ([False, True, True], True), ([False, True, False], False), ([False, False, True], False), ([False, False, False], False), ) def test_eval(self, literals, expected_evaluation): # (x_0 OR x_2) AND (x_1 OR not(x_2)) clauses = [ dnf_lib.Clause(0, 2, False, False), dnf_lib.Clause(1, 2, False, True) ] dnf = dnf_lib.DNF(3, clauses) self.assertEqual(dnf.eval(literals), expected_evaluation) @parameterized.parameters( ([True, True, True], 2), ([True, True, False], 2), ([True, False, True], 1), ([True, False, False], 2), ([False, True, True], 2), ([False, True, False], 1), ([False, False, True], 1), ([False, False, False], 1), ) def test_get_num_clauses_satisfied(self, literals, expected_clauses_satisfied): # (x_0 OR x_2) AND (x_1 OR not(x_2)) clauses = [ dnf_lib.Clause(0, 2, False, False), dnf_lib.Clause(1, 2, False, True) ] dnf = dnf_lib.DNF(3, clauses) self.assertEqual(dnf.get_num_clauses_satisfied(literals), expected_clauses_satisfied) @parameterized.parameters((2, 4), (5, 40), (10, 180)) def test_get_number_of_possible_clauses( self, num_literals, expected_number_of_possible_clauses): self.assertEqual(dnf_lib.get_number_of_possible_clauses(num_literals), expected_number_of_possible_clauses) def test_get_random_dnf(self): with mock.patch.object(dnf_lib, 'get_random_clause') as mock_get_random_clause: mock_get_random_clause.side_effect = [ dnf_lib.Clause(0, 1, True, True), dnf_lib.Clause(0, 1, True, True), # Duplicaed clause. dnf_lib.Clause(0, 1, True, False), dnf_lib.Clause(1, 2, True, True), dnf_lib.Clause(3, 4, True, True), dnf_lib.Clause(5, 6, True, True), # Not used since we only need 5 unique clauses. dnf_lib.Clause(7, 8, True, True), dnf_lib.Clause(7, 8, False, True), ] dnf = dnf_lib.get_random_dnf(num_literals=10, num_clauses=5) self.assertEqual(dnf.num_literals, 10) self.assertSetEqual( dnf.clauses, set([ dnf_lib.Clause(0, 1, True, True), dnf_lib.Clause(0, 1, True, False), dnf_lib.Clause(1, 2, True, True), dnf_lib.Clause(3, 4, True, True), dnf_lib.Clause(5, 6, True, True) ])) def test_get_random_dnf_num_clauses_too_large(self): with self.assertRaisesRegex( ValueError, 'num_clauses 10 can not be greater than number of possible clauses 4' ): dnf_lib.get_random_dnf(num_literals=2, num_clauses=10) @parameterized.parameters( ('(x_32 || x_0)', dnf_lib.Clause(0, 32, False, False)), ('(x_2 || !x_39)', dnf_lib.Clause(2, 39, False, True)), ('(!x_11 || x_12)', dnf_lib.Clause(11, 12, True, False)), ('(!x_3 || !x_2)', dnf_lib.Clause(2, 3, True, True))) def test_clause_from_string(self, clause_string, expected_clause): self.assertEqual(dnf_lib.clause_from_string(clause_string), expected_clause) @parameterized.parameters(('as;lkj;lkajsdf'), ('x_2'), ('x_-5'), ('(x_0 | x_1)'), ('x_0 || x_1)'), ('x_0 || x_1'), ('(!!x_3 || x_5)'), ('(!x_3 || x_4 || x_5)')) def test_clause_from_string_invalid(self, clause_string): with self.assertRaisesRegex(ValueError, 'Not the output of a clause string'): dnf_lib.clause_from_string(clause_string) @parameterized.parameters( ('Number of Literals: 12, DNF:', dnf_lib.DNF(12, [])), ('Number of Literals: 4, DNF: ', dnf_lib.DNF(4, [])), ('Number of Literals: 24, DNF: (!x_0 || x_1)', dnf_lib.DNF(24, [dnf_lib.Clause(0, 1, True, False)])), ('Number of Literals: 5, DNF:(x_2 || x_4) && (x_3 || !x_1)', dnf_lib.DNF(5, [ dnf_lib.Clause(2, 4, False, False), dnf_lib.Clause(1, 3, True, False) ]))) def test_dnf_from_string(self, dnf_string, expected_dnf): dnf = dnf_lib.dnf_from_string(dnf_string) self.assertEqual(dnf.num_literals, expected_dnf.num_literals) self.assertEqual(dnf.clauses, expected_dnf.clauses) @parameterized.parameters(('Number of Literals: , DNF:'), ('Number of Literals: 5, DNF: (x_0 || x_1) && ')) def test_dnf_from_string_invalid(self, dnf_string): with self.assertRaisesRegex(ValueError, 'Not the output of a dnf string'): dnf_lib.dnf_from_string(dnf_string)
def test_bangbang_protocol_circuit_init(self): dnf = dnf_lib.DNF(4, [dnf_lib.Clause(0, 3, True, True)]) circuit = dnf_circuit_lib.BangBangProtocolCircuit(5.3, dnf) self.assertEqual(circuit.dnf, dnf) self.assertEqual(circuit.chunk_time, 5.3) self.assertEqual(circuit.num_qubits, 4)
def test_get_hamiltonian_diagonal(self): dnf = dnf_lib.DNF(4, [dnf_lib.Clause(0, 3, True, True)]) circuit = dnf_circuit_lib.BangBangProtocolCircuit(5.3, dnf) np.testing.assert_allclose( circuit.get_hamiltonian_diagonal(), [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0])
def test_bangbang_protocol_circuit_init_neg_chunk_time(self): with self.assertRaisesRegex(ValueError, 'chunk_time must be positive, not -1.2'): dnf_circuit_lib.BangBangProtocolCircuit(-1.2, dnf_lib.DNF(22, []))
def test_init_neg_num(self, num_literals, error_message): with self.assertRaisesRegex(ValueError, error_message): dnf_lib.DNF(num_literals, set())