1# Licensed under the Apache License, Version 2.0 (the "License"); 2# you may not use this file except in compliance with the License. 3# You may obtain a copy of the License at 4# 5# http://www.apache.org/licenses/LICENSE-2.0 6# 7# Unless required by applicable law or agreed to in writing, software 8# distributed under the License is distributed on an "AS IS" BASIS, 9# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10# See the License for the specific language governing permissions and 11# limitations under the License. 12 13import itertools 14from typing import cast, Tuple 15 16import numpy as np 17import pytest 18import scipy.linalg as la 19import sympy 20import cirq 21import cirq.contrib.acquaintance as cca 22 23import openfermion 24from openfermion.circuits.gates.fermionic_simulation import ( 25 sum_of_interaction_operator_gate_generators, 26 state_swap_eigen_component, 27) 28 29 30def test_state_swap_eigen_component_args(): 31 with pytest.raises(TypeError): 32 state_swap_eigen_component(0, '12', 1) 33 with pytest.raises(ValueError): 34 state_swap_eigen_component('01', '01', 1) 35 with pytest.raises(ValueError): 36 state_swap_eigen_component('01', '10', 0) 37 with pytest.raises(ValueError): 38 state_swap_eigen_component('01', '100', 1) 39 with pytest.raises(ValueError): 40 state_swap_eigen_component('01', 'ab', 1) 41 42 43@pytest.mark.parametrize('index_pair,n_qubits', [ 44 ((0, 1), 2), 45 ((0, 3), 2), 46]) 47def test_state_swap_eigen_component(index_pair, n_qubits): 48 state_pair = tuple(format(i, '0' + str(n_qubits) + 'b') for i in index_pair) 49 i, j = index_pair 50 dim = 2**n_qubits 51 for sign in (-1, 1): 52 actual_component = state_swap_eigen_component(state_pair[0], 53 state_pair[1], sign) 54 expected_component = np.zeros((dim, dim)) 55 expected_component[i, i] = expected_component[j, j] = 0.5 56 expected_component[i, j] = expected_component[j, i] = sign * 0.5 57 assert np.allclose(actual_component, expected_component) 58 59 60@pytest.mark.parametrize('n_modes, seed', 61 [(7, np.random.randint(1 << 30)) for _ in range(2)]) 62def test_interaction_operator_interconversion(n_modes, seed): 63 operator = openfermion.random_interaction_operator(n_modes, 64 real=False, 65 seed=seed) 66 gates = openfermion.fermionic_simulation_gates_from_interaction_operator( 67 operator) 68 other_operator = sum_of_interaction_operator_gate_generators(n_modes, gates) 69 operator = openfermion.normal_ordered(operator) 70 other_operator = openfermion.normal_ordered(other_operator) 71 assert operator == other_operator 72 73 74def test_interaction_operator_from_bad_gates(): 75 for gates in [{(): 'bad'}, {(0,): cirq.X}]: 76 with pytest.raises(TypeError): 77 sum_of_interaction_operator_gate_generators(5, gates) 78 79 80def random_real(size=None, mag=20): 81 return np.random.uniform(-mag, mag, size) 82 83 84def random_complex(size=None, mag=20): 85 return random_real(size, mag) + 1j * random_real(size, mag) 86 87 88def random_fermionic_simulation_gate(order): 89 exponent = random_real() 90 if order == 2: 91 weights = (random_complex(), random_real()) 92 return openfermion.QuadraticFermionicSimulationGate(weights, 93 exponent=exponent) 94 weights = random_complex(3) 95 if order == 3: 96 return openfermion.CubicFermionicSimulationGate(weights, 97 exponent=exponent) 98 if order == 4: 99 return openfermion.QuarticFermionicSimulationGate(weights, 100 exponent=exponent) 101 102 103def assert_symbolic_decomposition_consistent(gate): 104 expected_unitary = cirq.unitary(gate) 105 106 weights = tuple(sympy.Symbol(f'w{i}') for i in range(gate.num_weights())) 107 exponent = sympy.Symbol('t') 108 symbolic_gate = type(gate)(weights, exponent=exponent) 109 qubits = cirq.LineQubit.range(gate.num_qubits()) 110 circuit = cirq.Circuit(symbolic_gate._decompose_(qubits)) 111 resolver = {'t': gate.exponent} 112 for i, w in enumerate(gate.weights): 113 resolver[f'w{i}'] = w 114 resolved_circuit = cirq.resolve_parameters(circuit, resolver) 115 decomp_unitary = resolved_circuit.unitary(qubit_order=qubits) 116 117 assert np.allclose(expected_unitary, decomp_unitary) 118 119 120def assert_generators_consistent(gate): 121 qubit_generator = gate.qubit_generator_matrix 122 qubit_generator_from_fermion_generator = (super( 123 type(gate), gate).qubit_generator_matrix) 124 assert np.allclose(qubit_generator, qubit_generator_from_fermion_generator) 125 126 127def assert_resolution_consistent(gate): 128 weight_names = [f'w{i}' for i in range(gate.num_weights())] 129 weight_params = [sympy.Symbol(w) for w in weight_names] 130 resolver = dict(zip(weight_names, gate.weights)) 131 resolver['s'] = gate._global_shift 132 resolver['e'] = gate._exponent 133 symbolic_gate = type(gate)(weight_params, 134 exponent=sympy.Symbol('e'), 135 global_shift=sympy.Symbol('s')) 136 resolved_gate = cirq.resolve_parameters(symbolic_gate, resolver) 137 assert resolved_gate == gate 138 139 140def assert_fswap_consistent(gate): 141 gate = gate.__copy__() 142 n_qubits = gate.num_qubits() 143 for i in range(n_qubits - 1): 144 fswap = cirq.kron(np.eye(1 << i), cirq.unitary(openfermion.FSWAP), 145 np.eye(1 << (n_qubits - i - 2))) 146 assert fswap.shape == (1 << n_qubits,) * 2 147 generator = gate.qubit_generator_matrix 148 fswapped_generator = np.linalg.multi_dot([fswap, generator, fswap]) 149 gate.fswap(i) 150 assert np.allclose(gate.qubit_generator_matrix, fswapped_generator) 151 for i in (-1, n_qubits): 152 with pytest.raises(ValueError): 153 gate.fswap(i) 154 155 156def assert_permute_consistent(gate): 157 gate = gate.__copy__() 158 n_qubits = gate.num_qubits() 159 qubits = cirq.LineQubit.range(n_qubits) 160 for pos in itertools.permutations(range(n_qubits)): 161 permuted_gate = gate.__copy__() 162 gate.permute(pos) 163 assert permuted_gate.permuted(pos) == gate 164 actual_unitary = cirq.unitary(permuted_gate) 165 166 ops = [ 167 cca.LinearPermutationGate(n_qubits, dict(zip(range(n_qubits), pos)), 168 openfermion.FSWAP)(*qubits), 169 gate(*qubits), 170 cca.LinearPermutationGate(n_qubits, dict(zip(pos, range(n_qubits))), 171 openfermion.FSWAP)(*qubits) 172 ] 173 circuit = cirq.Circuit(ops) 174 expected_unitary = cirq.unitary(circuit) 175 assert np.allclose(actual_unitary, expected_unitary) 176 177 with pytest.raises(ValueError): 178 gate.permute(range(1, n_qubits)) 179 with pytest.raises(ValueError): 180 gate.permute([1] * n_qubits) 181 182 183def assert_interaction_operator_consistent(gate): 184 interaction_op = gate.interaction_operator_generator() 185 other_gate = gate.from_interaction_operator(operator=interaction_op) 186 if other_gate is None: 187 assert np.allclose(gate.weights, 0) 188 else: 189 assert cirq.approx_eq(gate, other_gate) 190 interaction_op = openfermion.normal_ordered(interaction_op) 191 other_interaction_op = openfermion.InteractionOperator.zero( 192 interaction_op.n_qubits) 193 super(type(gate), 194 gate).interaction_operator_generator(operator=other_interaction_op) 195 other_interaction_op = openfermion.normal_ordered(interaction_op) 196 assert interaction_op == other_interaction_op 197 198 other_interaction_op = super(type(gate), 199 gate).interaction_operator_generator() 200 other_interaction_op = openfermion.normal_ordered(interaction_op) 201 assert interaction_op == other_interaction_op 202 203 204random_quadratic_gates = [random_fermionic_simulation_gate(2) for _ in range(5)] 205manual_quadratic_gates = [ 206 openfermion.QuadraticFermionicSimulationGate(weights) 207 for weights in [cast(Tuple[float, float], (1, 1)), (1, 0), (0, 1), (0, 0)] 208] 209quadratic_gates = random_quadratic_gates + manual_quadratic_gates 210cubic_gates = ([openfermion.CubicFermionicSimulationGate()] + 211 [random_fermionic_simulation_gate(3) for _ in range(5)]) 212quartic_gates = ([openfermion.QuarticFermionicSimulationGate()] + 213 [random_fermionic_simulation_gate(4) for _ in range(5)]) 214gates = quadratic_gates + cubic_gates + quartic_gates 215 216 217@pytest.mark.parametrize('gate', gates) 218def test_fermionic_simulation_gate(gate): 219 openfermion.testing.assert_implements_consistent_protocols(gate) 220 221 generator = gate.qubit_generator_matrix 222 expected_unitary = la.expm(-1j * gate.exponent * generator) 223 actual_unitary = cirq.unitary(gate) 224 assert np.allclose(expected_unitary, actual_unitary) 225 226 assert_fswap_consistent(gate) 227 assert_permute_consistent(gate) 228 assert_generators_consistent(gate) 229 assert_resolution_consistent(gate) 230 assert_interaction_operator_consistent(gate) 231 232 assert gate.num_weights() == super(type(gate), gate).num_weights() 233 234 235@pytest.mark.parametrize('weights', list(np.random.rand(10, 3)) + [(1, 0, 1)]) 236def test_weights_and_exponent(weights): 237 exponents = np.linspace(-1, 1, 8) 238 gates = tuple( 239 openfermion.QuarticFermionicSimulationGate( 240 weights / exponent, exponent=exponent, absorb_exponent=True) 241 for exponent in exponents) 242 243 for g1, g2 in itertools.combinations(gates, 2): 244 assert cirq.approx_eq(g1, g2, atol=1e-100) 245 246 for i, (gate, exponent) in enumerate(zip(gates, exponents)): 247 assert gate.exponent == 1 248 new_exponent = exponents[-i] 249 new_gate = gate._with_exponent(new_exponent) 250 assert new_gate.exponent == new_exponent 251 252 253def test_zero_weights(): 254 for gate_type in [ 255 openfermion.QuadraticFermionicSimulationGate, 256 openfermion.CubicFermionicSimulationGate, 257 openfermion.QuarticFermionicSimulationGate 258 ]: 259 weights = (0,) * gate_type.num_weights() 260 gate = gate_type(weights) 261 n_qubits = gate.num_qubits() 262 263 assert np.allclose(cirq.unitary(gate), np.eye(2**n_qubits)) 264 cirq.testing.assert_decompose_is_consistent_with_unitary(gate) 265 266 operator = openfermion.InteractionOperator.zero(n_qubits) 267 assert gate_type.from_interaction_operator(operator=operator) is None 268 269 270@pytest.mark.parametrize( 271 'weights,exponent', 272 [((np.random.uniform(-5, 5) + 1j * np.random.uniform(-5, 5), 273 np.random.uniform(-5, 5)), np.random.uniform(-5, 5)) for _ in range(5)]) 274def test_quadratic_fermionic_simulation_gate_unitary(weights, exponent): 275 generator = np.zeros((4, 4), dtype=np.complex128) 276 # w0 |10><01| + h.c. 277 generator[2, 1] = weights[0] 278 generator[1, 2] = weights[0].conjugate() 279 # w1 |11><11| 280 generator[3, 3] = weights[1] 281 expected_unitary = la.expm(-1j * exponent * generator) 282 283 gate = openfermion.QuadraticFermionicSimulationGate(weights, 284 exponent=exponent) 285 actual_unitary = cirq.unitary(gate) 286 287 assert np.allclose(expected_unitary, actual_unitary) 288 289 symbolic_gate = (openfermion.QuadraticFermionicSimulationGate( 290 (sympy.Symbol('w0'), sympy.Symbol('w1')), exponent=sympy.Symbol('t'))) 291 qubits = cirq.LineQubit.range(2) 292 circuit = cirq.Circuit(symbolic_gate._decompose_(qubits)) 293 resolver = {'w0': weights[0], 'w1': weights[1], 't': exponent} 294 resolved_circuit = cirq.resolve_parameters(circuit, resolver) 295 decomp_unitary = resolved_circuit.unitary(qubit_order=qubits) 296 297 assert np.allclose(expected_unitary, decomp_unitary) 298 299 300@pytest.mark.parametrize('gate', random_quadratic_gates) 301def test_quadratic_fermionic_simulation_gate_symbolic_decompose(gate): 302 assert_symbolic_decomposition_consistent(gate) 303 304 305def test_cubic_fermionic_simulation_gate_equality(): 306 eq = cirq.testing.EqualsTester() 307 eq.add_equality_group( 308 openfermion.CubicFermionicSimulationGate()**0.5, 309 openfermion.CubicFermionicSimulationGate((1,) * 3, exponent=0.5), 310 openfermion.CubicFermionicSimulationGate((0.5,) * 3), 311 ) 312 eq.add_equality_group(openfermion.CubicFermionicSimulationGate((1j, 0, 0)),) 313 eq.add_equality_group( 314 openfermion.CubicFermionicSimulationGate((sympy.Symbol('s'), 0, 0), 315 exponent=2), 316 openfermion.CubicFermionicSimulationGate((2 * sympy.Symbol('s'), 0, 0), 317 exponent=1), 318 ) 319 eq.add_equality_group( 320 openfermion.CubicFermionicSimulationGate((0, 0.7, 0), global_shift=2), 321 openfermion.CubicFermionicSimulationGate((0, 0.35, 0), 322 global_shift=1, 323 exponent=2), 324 ) 325 eq.add_equality_group( 326 openfermion.CubicFermionicSimulationGate((1, 1, 1)), 327 openfermion.CubicFermionicSimulationGate(((1 + 2 * np.pi), 1, 1)), 328 ) 329 330 331@pytest.mark.parametrize('exponent,control', 332 itertools.product([0, 1, -1, 0.25, -0.5, 0.1], 333 [0, 1, 2])) 334def test_cubic_fermionic_simulation_gate_consistency_special(exponent, control): 335 weights = tuple(np.eye(1, 3, control)[0] * 0.5 * np.pi) 336 general_gate = openfermion.CubicFermionicSimulationGate(weights, 337 exponent=exponent) 338 general_unitary = cirq.unitary(general_gate) 339 340 indices = np.dot(list(itertools.product((0, 1), repeat=3)), 341 (2**np.roll(np.arange(3), -control))[::-1]) 342 special_gate = cirq.ControlledGate(cirq.ISWAP**-exponent) 343 special_unitary = ( 344 cirq.unitary(special_gate)[indices[:, np.newaxis], indices]) 345 346 assert np.allclose(general_unitary, special_unitary) 347 348 349@pytest.mark.parametrize( 350 'weights,exponent', 351 [(np.random.uniform(-5, 5, 3) + 1j * np.random.uniform(-5, 5, 3), 352 np.random.uniform(-5, 5)) for _ in range(5)]) 353def test_cubic_fermionic_simulation_gate_consistency_docstring( 354 weights, exponent): 355 generator = np.zeros((8, 8), dtype=np.complex128) 356 # w0 |110><101| + h.c. 357 generator[6, 5] = weights[0] 358 generator[5, 6] = weights[0].conjugate() 359 # w1 |110><011| + h.c. 360 generator[6, 3] = weights[1] 361 generator[3, 6] = weights[1].conjugate() 362 # w2 |101><011| + h.c. 363 generator[5, 3] = weights[2] 364 generator[3, 5] = weights[2].conjugate() 365 expected_unitary = la.expm(-1j * exponent * generator) 366 367 gate = openfermion.CubicFermionicSimulationGate(weights, exponent=exponent) 368 actual_unitary = cirq.unitary(gate) 369 370 assert np.allclose(expected_unitary, actual_unitary) 371 372 373def test_quartic_fermionic_simulation_consistency(): 374 openfermion.testing.assert_implements_consistent_protocols( 375 openfermion.QuarticFermionicSimulationGate()) 376 377 378quartic_fermionic_simulation_simulator_test_cases = [ 379 (openfermion.QuarticFermionicSimulationGate( 380 (0, 0, 0)), 1., np.ones(16) / 4., np.ones(16) / 4., 5e-6), 381 (openfermion.QuarticFermionicSimulationGate((0.2, -0.1, 0.7)), 0., 382 np.array([1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) / 4., 383 np.array([1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) / 4., 384 5e-6), 385 (openfermion.QuarticFermionicSimulationGate((0.2, -0.1, 0.7)), 0.3, 386 np.array([1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) / 4., 387 np.array([ 388 1, -1, -1, -np.exp(0.21j), -1, -np.exp(-0.03j), 389 np.exp(-0.06j), 1, 1, 390 np.exp(-0.06j), 391 np.exp(-0.03j), 1, 392 np.exp(0.21j), 1, 1, 1 393 ]) / 4., 5e-6), 394 (openfermion.QuarticFermionicSimulationGate((1. / 3, 0, 0)), 1., 395 np.array([0, 0, 0, 0, 0, 0, 1., 0, 0, 1., 0, 0, 0, 0, 0, 0]) / np.sqrt(2), 396 np.array([0, 0, 0, 0, 0, 0, 1., 0, 0, 1., 0, 0, 0, 0, 0, 0]) / np.sqrt(2), 397 5e-6), 398 (openfermion.QuarticFermionicSimulationGate((0, np.pi / 3, 0)), 1., 399 np.array([1., 1., 0, 0, 0, 1., 0, 0, 0, 0., -1., 0, 0, 0, 0, 0]) / 2., 400 np.array([ 401 1., 1., 0, 0, 0, -np.exp(4j * np.pi / 3), 0, 0, 0, 0., 402 -np.exp(1j * np.pi / 3), 0, 0, 0, 0, 0 403 ]) / 2., 5e-6), 404 (openfermion.QuarticFermionicSimulationGate((0, 0, -np.pi / 2)), 1., 405 np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1., 0, 0, 406 0]), np.array([0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 407 0]), 5e-6), 408 (openfermion.QuarticFermionicSimulationGate((0, 0, -0.25 * np.pi)), 1., 409 np.array([0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), 410 np.array([0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1j, 0, 0, 0]) / np.sqrt(2), 411 5e-6), 412 (openfermion.QuarticFermionicSimulationGate( 413 (-np.pi / 4, np.pi / 6, -np.pi / 2)), 1., 414 np.array([0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0]) / np.sqrt(3), 415 np.array([ 416 0, 0, 0, 1j, 0, -1j / 2., 1 / np.sqrt(2), 0, 0, 1j / np.sqrt(2), 417 np.sqrt(3) / 2, 0, 0, 0, 0, 0 418 ]) / np.sqrt(3), 5e-6), 419] 420 421 422@pytest.mark.parametrize('gate, exponent, initial_state, correct_state, atol', 423 quartic_fermionic_simulation_simulator_test_cases) 424def test_quartic_fermionic_simulation_on_simulator(gate, exponent, 425 initial_state, correct_state, 426 atol): 427 428 a, b, c, d = cirq.LineQubit.range(4) 429 circuit = cirq.Circuit(gate(a, b, c, d)**exponent) 430 result = circuit.final_state_vector(initial_state=initial_state) 431 cirq.testing.assert_allclose_up_to_global_phase(result, 432 correct_state, 433 atol=atol) 434 435 436def test_quartic_fermionic_simulation_eq(): 437 eq = cirq.testing.EqualsTester() 438 439 eq.add_equality_group( 440 openfermion.QuarticFermionicSimulationGate((1.2, 0.4, -0.4), 441 exponent=0.5), 442 openfermion.QuarticFermionicSimulationGate((0.3, 0.1, -0.1), 443 exponent=2), 444 openfermion.QuarticFermionicSimulationGate((-0.6, -0.2, 0.2), 445 exponent=-1), 446 openfermion.QuarticFermionicSimulationGate((0.6, 0.2, 2 * np.pi - 0.2)), 447 ) 448 449 eq.add_equality_group( 450 openfermion.QuarticFermionicSimulationGate((-0.6, 0.0, 0.3), 451 exponent=0.5)) 452 453 eq.make_equality_group(lambda: openfermion.QuarticFermionicSimulationGate( 454 (0.1, -0.3, 0.0), exponent=0.0)) 455 eq.make_equality_group(lambda: openfermion.QuarticFermionicSimulationGate( 456 (1., -1., 0.5), exponent=0.75)) 457 458 459def test_quadratic_fermionic_simulation_gate_text_diagram(): 460 gate = openfermion.QuadraticFermionicSimulationGate((1, 1)) 461 a, b, c = cirq.LineQubit.range(3) 462 circuit = cirq.Circuit([gate(a, b), gate(b, c)]) 463 464 assert super(type(gate), gate).wire_symbol(False) == type(gate).__name__ 465 assert (super(type(gate), gate)._diagram_exponent( 466 cirq.CircuitDiagramInfoArgs.UNINFORMED_DEFAULT) == gate._exponent) 467 468 expected_text_diagram = """ 4690: ───↓↑(1, 1)────────────── 470 │ 4711: ───↓↑─────────↓↑(1, 1)─── 472 │ 4732: ──────────────↓↑───────── 474""".strip() 475 cirq.testing.assert_has_diagram(circuit, expected_text_diagram) 476 477 expected_text_diagram = """ 4780: ---a*a(1, 1)--------------- 479 | 4801: ---a*a---------a*a(1, 1)--- 481 | 4822: ---------------a*a--------- 483""".strip() 484 cirq.testing.assert_has_diagram(circuit, 485 expected_text_diagram, 486 use_unicode_characters=False) 487 488 489def test_cubic_fermionic_simulation_gate_text_diagram(): 490 gate = openfermion.CubicFermionicSimulationGate((1, 1, 1)) 491 qubits = cirq.LineQubit.range(5) 492 circuit = cirq.Circuit([gate(*qubits[:3]), gate(*qubits[2:5])]) 493 494 assert super(type(gate), gate).wire_symbol(False) == type(gate).__name__ 495 assert (super(type(gate), gate)._diagram_exponent( 496 cirq.CircuitDiagramInfoArgs.UNINFORMED_DEFAULT) == gate._exponent) 497 498 expected_text_diagram = """ 4990: ───↕↓↑(1, 1, 1)────────────────── 500 │ 5011: ───↕↓↑─────────────────────────── 502 │ 5032: ───↕↓↑────────────↕↓↑(1, 1, 1)─── 504 │ 5053: ──────────────────↕↓↑──────────── 506 │ 5074: ──────────────────↕↓↑──────────── 508""".strip() 509 cirq.testing.assert_has_diagram(circuit, expected_text_diagram) 510 511 expected_text_diagram = """ 5120: ---na*a(1, 1, 1)------------------- 513 | 5141: ---na*a---------------------------- 515 | 5162: ---na*a------------na*a(1, 1, 1)--- 517 | 5183: -------------------na*a------------ 519 | 5204: -------------------na*a------------ 521""".strip() 522 cirq.testing.assert_has_diagram(circuit, 523 expected_text_diagram, 524 use_unicode_characters=False) 525 526 527test_weights = [1.0, 0.5, 0.25, 0.1, 0.0, -0.5] 528 529 530@pytest.mark.parametrize('weights', 531 itertools.chain( 532 itertools.product(test_weights, repeat=3), 533 np.random.rand(10, 3))) 534def test_quartic_fermionic_simulation_decompose(weights): 535 cirq.testing.assert_decompose_is_consistent_with_unitary( 536 openfermion.QuarticFermionicSimulationGate(weights)) 537 538 539@pytest.mark.parametrize( 540 'weights,exponent', 541 [(np.random.uniform(-5, 5, 3) + 1j * np.random.uniform(-5, 5, 3), 542 np.random.uniform(-5, 5)) for _ in range(5)]) 543def test_quartic_fermionic_simulation_unitary(weights, exponent): 544 generator = np.zeros((1 << 4,) * 2, dtype=np.complex128) 545 546 # w0 |1001><0110| + h.c. 547 generator[9, 6] = weights[0] 548 generator[6, 9] = weights[0].conjugate() 549 # w1 |1010><0101| + h.c. 550 generator[10, 5] = weights[1] 551 generator[5, 10] = weights[1].conjugate() 552 # w2 |1100><0011| + h.c. 553 generator[12, 3] = weights[2] 554 generator[3, 12] = weights[2].conjugate() 555 expected_unitary = la.expm(-1j * exponent * generator) 556 557 gate = openfermion.QuarticFermionicSimulationGate(weights, 558 exponent=exponent) 559 actual_unitary = cirq.unitary(gate) 560 561 assert np.allclose(expected_unitary, actual_unitary) 562 563 564def test_quartic_fermionic_simulation_gate_text_diagram(): 565 gate = openfermion.QuarticFermionicSimulationGate((1, 1, 1)) 566 qubits = cirq.LineQubit.range(6) 567 circuit = cirq.Circuit([gate(*qubits[:4]), gate(*qubits[-4:])]) 568 569 assert super(type(gate), gate).wire_symbol(False) == type(gate).__name__ 570 for G in (gate, gate._with_exponent('e')): 571 assert (super(type(G), G)._diagram_exponent( 572 cirq.CircuitDiagramInfoArgs.UNINFORMED_DEFAULT) == G._exponent) 573 574 expected_text_diagram = """ 5750: ───⇊⇈(1, 1, 1)───────────────── 576 │ 5771: ───⇊⇈────────────────────────── 578 │ 5792: ───⇊⇈────────────⇊⇈(1, 1, 1)─── 580 │ │ 5813: ───⇊⇈────────────⇊⇈──────────── 582 │ 5834: ─────────────────⇊⇈──────────── 584 │ 5855: ─────────────────⇊⇈──────────── 586""".strip() 587 cirq.testing.assert_has_diagram(circuit, expected_text_diagram) 588 589 expected_text_diagram = """ 5900: ---a*a*aa(1, 1, 1)--------------------- 591 | 5921: ---a*a*aa------------------------------ 593 | 5942: ---a*a*aa------------a*a*aa(1, 1, 1)--- 595 | | 5963: ---a*a*aa------------a*a*aa------------ 597 | 5984: ---------------------a*a*aa------------ 599 | 6005: ---------------------a*a*aa------------ 601""".strip() 602 cirq.testing.assert_has_diagram(circuit, 603 expected_text_diagram, 604 use_unicode_characters=False) 605 606 607@pytest.mark.parametrize( 608 'weights,exponent', 609 [(np.random.uniform(-5, 5, 3) + 1j * np.random.uniform(-5, 5, 3), 610 np.random.uniform(-5, 5)) for _ in range(5)]) 611def test_quartic_fermionic_simulation_apply_unitary(weights, exponent): 612 gate = openfermion.QuarticFermionicSimulationGate(weights, 613 exponent=exponent) 614 cirq.testing.assert_has_consistent_apply_unitary(gate, atol=5e-6) 615