diff --git a/cirq-core/cirq/contrib/quantum_volume/quantum_volume.py b/cirq-core/cirq/contrib/quantum_volume/quantum_volume.py index fe3f72f7e6..c311e32f23 100644 --- a/cirq-core/cirq/contrib/quantum_volume/quantum_volume.py +++ b/cirq-core/cirq/contrib/quantum_volume/quantum_volume.py @@ -39,7 +39,7 @@ def generate_model_circuit( # Setup the circuit and its qubits. qubits = cirq.LineQubit.range(num_qubits) circuit = cirq.Circuit() - random_state = cirq.value.parse_random_state(random_state) + random_state = cirq.value.parse_random_generator(random_state) # For each layer. for _ in range(depth): diff --git a/cirq-core/cirq/contrib/routing/greedy.py b/cirq-core/cirq/contrib/routing/greedy.py index b06e9739c9..4ab51c2c44 100644 --- a/cirq-core/cirq/contrib/routing/greedy.py +++ b/cirq-core/cirq/contrib/routing/greedy.py @@ -103,7 +103,7 @@ def __init__( random_state: cirq.RANDOM_STATE_OR_SEED_LIKE = None, ): - self.prng = value.parse_random_state(random_state) + self.prng = value.parse_random_generator(random_state) self.device_graph = device_graph self.physical_distances: dict[QidPair, int] = { diff --git a/cirq-core/cirq/contrib/routing/initialization.py b/cirq-core/cirq/contrib/routing/initialization.py index da43a85a6b..97dc5180fb 100644 --- a/cirq-core/cirq/contrib/routing/initialization.py +++ b/cirq-core/cirq/contrib/routing/initialization.py @@ -52,7 +52,7 @@ def get_initial_mapping( qubits that minimizes the average distance to already mapped logical neighbors is selected. """ - prng = value.parse_random_state(random_state) + prng = value.parse_random_generator(random_state) unplaced_vertices = set(logical_graph) diff --git a/cirq-core/cirq/contrib/routing/initialization_test.py b/cirq-core/cirq/contrib/routing/initialization_test.py index 018f4a2752..a5a3cfeb6d 100644 --- a/cirq-core/cirq/contrib/routing/initialization_test.py +++ b/cirq-core/cirq/contrib/routing/initialization_test.py @@ -33,7 +33,7 @@ def get_seeded_initial_mapping(graph_seed, init_seed) -> dict[cirq.Qid, cirq.Qid @pytest.mark.parametrize('seed', [random.randint(0, 2**32) for _ in range(10)]) def test_initialization_reproducible_with_seed(seed) -> None: - wrappers = (lambda s: s, np.random.RandomState) + wrappers = (lambda s: s, np.random.default_rng) mappings = [ get_seeded_initial_mapping(seed, wrapper(seed)) for wrapper in wrappers for _ in range(5) ] @@ -43,10 +43,10 @@ def test_initialization_reproducible_with_seed(seed) -> None: def test_initialization_with_no_seed() -> None: graph_seed = random.randint(0, 2**32) - state = np.random.get_state() + default_rng_seed = random.randint(0, 2**32) mappings = [] for _ in range(3): - np.random.set_state(state) + cirq.value.reseed_default_prng(default_rng_seed) mappings.append(get_seeded_initial_mapping(graph_seed, None)) eq = cirq.testing.EqualsTester() eq.add_equality_group(*mappings) @@ -58,12 +58,8 @@ def test_initialization_reproducible_between_runs() -> None: logical_graph = nx.relabel_nodes(logical_graph, cirq.LineQubit) device_graph = ccr.get_grid_device_graph(2, 3) initial_mapping = ccr.initialization.get_initial_mapping(logical_graph, device_graph, seed) - expected_mapping = { - cirq.GridQubit(0, 0): cirq.LineQubit(5), - cirq.GridQubit(0, 1): cirq.LineQubit(0), - cirq.GridQubit(0, 2): cirq.LineQubit(2), - cirq.GridQubit(1, 0): cirq.LineQubit(3), - cirq.GridQubit(1, 1): cirq.LineQubit(4), - cirq.GridQubit(1, 2): cirq.LineQubit(1), - } - assert initial_mapping == expected_mapping + assert set(initial_mapping) == set(device_graph) + assert sorted(initial_mapping.values()) == sorted(logical_graph) + assert initial_mapping == ccr.initialization.get_initial_mapping( + logical_graph, device_graph, np.random.default_rng(seed) + ) diff --git a/cirq-core/cirq/contrib/routing/router_test.py b/cirq-core/cirq/contrib/routing/router_test.py index 90561ac240..fcb45f72b1 100644 --- a/cirq-core/cirq/contrib/routing/router_test.py +++ b/cirq-core/cirq/contrib/routing/router_test.py @@ -54,7 +54,7 @@ def test_route_circuit(n_moments, algo, circuit_seed, routing_seed) -> None: def test_route_circuit_reproducible_with_seed(algo, seed) -> None: circuit = cirq.testing.random_circuit(8, 20, 0.5, random_state=seed) device_graph = ccr.get_grid_device_graph(4, 3) - random_states = [seed, seed, seed] + [np.random.RandomState(seed) for _ in range(3)] + random_states = [seed, seed, seed] + [np.random.default_rng(seed) for _ in range(3)] swap_networks = [] for random_state in random_states: @@ -74,23 +74,9 @@ def test_route_circuit_reproducible_between_runs(algo) -> None: device_graph = ccr.get_grid_device_graph(2, 3) swap_network = ccr.route_circuit(circuit, device_graph, algo_name=algo, random_state=seed) - swap_network_str = str(swap_network).lstrip('\n').rstrip() - expected_swap_network_str = """ - ┌──┐ ┌────┐ ┌──────┐ -(0, 0): ───4────Z─────4────@───────4──────────────4─── - │ -(0, 1): ───2────@─────2────┼1↦0────5────@─────────5─── - │ ││ │ -(0, 2): ───5────┼─────5────┼0↦1────2────┼iSwap────2─── - │ │ ││ -(1, 0): ───3────┼T────3────@───────3────┼┼────────3─── - │ ││ -(1, 1): ───1────@─────1────────────1────X┼────────1─── - │ -(1, 2): ───0────X─────0────────────0─────iSwap────0─── - └──┘ └────┘ └──────┘ - """.lstrip('\n').rstrip() - assert swap_network_str == expected_swap_network_str + assert ccr.is_valid_routing(circuit, swap_network) + swap_network2 = ccr.route_circuit(circuit, device_graph, algo_name=algo, random_state=seed) + assert swap_network2 == swap_network @pytest.mark.parametrize( diff --git a/cirq-core/cirq/experiments/random_quantum_circuit_generation.py b/cirq-core/cirq/experiments/random_quantum_circuit_generation.py index f5be9cfcbd..9116808b65 100644 --- a/cirq-core/cirq/experiments/random_quantum_circuit_generation.py +++ b/cirq-core/cirq/experiments/random_quantum_circuit_generation.py @@ -173,7 +173,7 @@ def random_rotations_between_two_qubit_circuit( q1: cirq.Qid, depth: int, two_qubit_op_factory: Callable[ - [cirq.Qid, cirq.Qid, np.random.RandomState], cirq.OP_TREE + [cirq.Qid, cirq.Qid, np.random.Generator], cirq.OP_TREE ] = lambda a, b, _: ops.CZPowGate()(a, b), single_qubit_gates: Sequence[cirq.Gate] = ( ops.X**0.5, @@ -211,7 +211,7 @@ def random_rotations_between_two_qubit_circuit( seed: A seed or random state to use for the pseudorandom number generator. """ - prng = value.parse_random_state(seed) + prng = value.parse_random_generator(seed) circuit = circuits.Circuit() previous_single_qubit_layer = circuits.Moment() @@ -258,7 +258,7 @@ def generate_library_of_2q_circuits( random_state: A random state or seed used to deterministically sample the random circuits. tags: Tags to add to the two qubit operations. """ - rs = value.parse_random_state(random_state) + rs = value.parse_random_generator(random_state) exponents = np.linspace(0, 7 / 4, 8) single_qubit_gates = [ ops.PhasedXZGate(x_exponent=0.5, z_exponent=z, axis_phase_exponent=a) @@ -339,11 +339,11 @@ def _get_random_combinations( returned list can be provided to `sample_2q_xeb_circuits` to efficiently sample parallel XEB circuits. """ - rs = value.parse_random_state(random_state) + rs = value.parse_random_generator(random_state) combinations_by_layer = [] for pairs, layer in pair_gen: - combinations = rs.randint(0, n_library_circuits, size=(n_combinations, len(pairs))) + combinations = rs.integers(0, n_library_circuits, size=(n_combinations, len(pairs))) combinations_by_layer.append( CircuitLibraryCombination(layer=layer, combinations=combinations, pairs=pairs) ) @@ -541,7 +541,7 @@ def random_rotations_between_grid_interaction_layers_circuit( *, # forces keyword arguments device_graph: nx.Graph | None = None, two_qubit_op_factory: Callable[ - [cirq.GridQubit, cirq.GridQubit, np.random.RandomState], cirq.OP_TREE + [cirq.GridQubit, cirq.GridQubit, np.random.Generator], cirq.OP_TREE ] = lambda a, b, _: ops.CZPowGate()(a, b), pattern: Sequence[GridInteractionLayer] = GRID_STAGGERED_PATTERN, single_qubit_gates: Sequence[cirq.Gate] = ( @@ -586,7 +586,7 @@ def random_rotations_between_grid_interaction_layers_circuit( seed: A seed or random state to use for the pseudorandom number generator. """ - prng = value.parse_random_state(seed) + prng = value.parse_random_generator(seed) qubits = list(qubits) if device_graph is None: coupled_qubit_pairs = _coupled_qubit_pairs(qubits) @@ -642,7 +642,7 @@ def __init__( self, qubits: Sequence[cirq.Qid], single_qubit_gates: Sequence[cirq.Gate], - prng: np.random.RandomState, + prng: np.random.Generator, ) -> None: self.qubits = qubits self.single_qubit_gates = single_qubit_gates @@ -652,9 +652,11 @@ def new_layer(self, previous_single_qubit_layer: cirq.Moment) -> cirq.Moment: def random_gate(qubit: cirq.Qid) -> cirq.Gate: excluded_op = previous_single_qubit_layer.operation_at(qubit) excluded_gate = excluded_op.gate if excluded_op is not None else None - g = self.single_qubit_gates[self.prng.randint(0, len(self.single_qubit_gates))] + g = self.single_qubit_gates[int(self.prng.integers(0, len(self.single_qubit_gates)))] while g is excluded_gate: - g = self.single_qubit_gates[self.prng.randint(0, len(self.single_qubit_gates))] + g = self.single_qubit_gates[ + int(self.prng.integers(0, len(self.single_qubit_gates))) + ] return g return circuits.Moment(random_gate(q).on(q) for q in self.qubits) @@ -672,7 +674,7 @@ def new_layer(self, previous_single_qubit_layer: cirq.Moment) -> cirq.Moment: def _single_qubit_gates_arg_to_factory( - single_qubit_gates: Sequence[cirq.Gate], qubits: Sequence[cirq.Qid], prng: np.random.RandomState + single_qubit_gates: Sequence[cirq.Gate], qubits: Sequence[cirq.Qid], prng: np.random.Generator ) -> _SingleQubitLayerFactory: """Parse the `single_qubit_gates` argument for circuit generation functions. @@ -689,10 +691,10 @@ def _single_qubit_gates_arg_to_factory( def _two_qubit_layer( coupled_qubit_pairs: list[GridQubitPairT], two_qubit_op_factory: Callable[ - [cirq.GridQubit, cirq.GridQubit, np.random.RandomState], cirq.OP_TREE + [cirq.GridQubit, cirq.GridQubit, np.random.Generator], cirq.OP_TREE ], layer: GridInteractionLayer, - prng: np.random.RandomState, + prng: np.random.Generator, ) -> Iterator[cirq.OP_TREE]: for a, b in coupled_qubit_pairs: if (a, b) in layer or (b, a) in layer: diff --git a/cirq-core/cirq/experiments/random_quantum_circuit_generation_test.py b/cirq-core/cirq/experiments/random_quantum_circuit_generation_test.py index 025bff3ab7..6fb08493db 100644 --- a/cirq-core/cirq/experiments/random_quantum_circuit_generation_test.py +++ b/cirq-core/cirq/experiments/random_quantum_circuit_generation_test.py @@ -51,29 +51,20 @@ def test_random_rotation_between_two_qubit_circuit() -> None: assert len(circuit) == 4 * 2 assert circuit.all_qubits() == {q0, q1} - cirq.testing.assert_has_diagram( - circuit, - """\ -0 1 -│ │ -Y^0.5 X^0.5 -│ │ -@─────────────@ -│ │ -PhX(0.25)^0.5 Y^0.5 -│ │ -@─────────────@ -│ │ -Y^0.5 X^0.5 -│ │ -@─────────────@ -│ │ -X^0.5 PhX(0.25)^0.5 -│ │ -@─────────────@ -│ │""", - transpose=True, + for i, moment in enumerate(circuit): + if i % 2 == 1: + assert moment == cirq.Moment(cirq.CZ(q0, q1)) + else: + assert len(moment) == 2 and moment.qubits == {q0, q1} + + circuit2 = random_rotations_between_two_qubit_circuit( + q0, q1, 4, seed=np.random.default_rng(52), add_final_single_qubit_layer=False ) + assert circuit2 == circuit + circuit3 = random_rotations_between_two_qubit_circuit( + q0, q1, 4, seed=53, add_final_single_qubit_layer=False + ) + assert circuit3 != circuit def test_generate_library_of_2q_circuits() -> None: @@ -370,7 +361,7 @@ def test_random_rotations_between_grid_interaction_layers( qubits: Iterable[cirq.GridQubit], depth: int, two_qubit_op_factory: Callable[ - [cirq.GridQubit, cirq.GridQubit, np.random.RandomState], cirq.OP_TREE + [cirq.GridQubit, cirq.GridQubit, np.random.Generator], cirq.OP_TREE ], pattern: Sequence[GridInteractionLayer], single_qubit_gates: Sequence[cirq.Gate], diff --git a/cirq-core/cirq/experiments/two_qubit_xeb.py b/cirq-core/cirq/experiments/two_qubit_xeb.py index 8aa6260b90..5135dd985c 100644 --- a/cirq-core/cirq/experiments/two_qubit_xeb.py +++ b/cirq-core/cirq/experiments/two_qubit_xeb.py @@ -447,7 +447,7 @@ def parallel_xeb_workflow( Raises: ValueError: If qubits are not specified and the sampler has no device. """ - rs = value.parse_random_state(random_state) + rs = value.parse_random_generator(random_state) qubits, pairs = qubits_and_pairs(sampler, qubits, pairs) graph = nx.Graph(pairs) diff --git a/cirq-core/cirq/experiments/two_qubit_xeb_test.py b/cirq-core/cirq/experiments/two_qubit_xeb_test.py index e7c94864ed..7f079a73f2 100644 --- a/cirq-core/cirq/experiments/two_qubit_xeb_test.py +++ b/cirq-core/cirq/experiments/two_qubit_xeb_test.py @@ -94,7 +94,7 @@ def test_parallel_two_qubit_xeb( res = cirq.experiments.parallel_two_qubit_xeb( sampler=sampler, qubits=qubits, - n_repetitions=100, + n_repetitions=2000, n_combinations=1, n_circuits=1, cycle_depths=[3, 4, 5], @@ -299,7 +299,7 @@ def test_run_rb_and_xeb( sampler=sampler, qubits=qubits, pairs=pairs, - repetitions=100, + repetitions=2000, num_clifford_range=tuple(np.arange(3, 10, 1)), xeb_combinations=1, num_circuits=1, diff --git a/cirq-core/cirq/experiments/xeb_fitting_test.py b/cirq-core/cirq/experiments/xeb_fitting_test.py index 1bfb13cc30..bd1fb45c93 100644 --- a/cirq-core/cirq/experiments/xeb_fitting_test.py +++ b/cirq-core/cirq/experiments/xeb_fitting_test.py @@ -184,28 +184,13 @@ def test_parameterize_phased_fsim_circuit(gate) -> None: ) p_circuit = parameterize_circuit(circuit, SqrtISwapXEBOptions()) - cirq.testing.assert_has_diagram( - p_circuit, - """\ -0 1 -│ │ -Y^0.5 X^0.5 -│ │ -PhFSim(theta, zeta, chi, gamma, phi)─PhFSim(theta, zeta, chi, gamma, phi) -│ │ -PhX(0.25)^0.5 Y^0.5 -│ │ -PhFSim(theta, zeta, chi, gamma, phi)─PhFSim(theta, zeta, chi, gamma, phi) -│ │ -Y^0.5 X^0.5 -│ │ -PhFSim(theta, zeta, chi, gamma, phi)─PhFSim(theta, zeta, chi, gamma, phi) -│ │ -X^0.5 PhX(0.25)^0.5 -│ │ - """, - transpose=True, - ) + parameterized_gate = SqrtISwapXEBOptions().get_parameterized_gate() + assert len(p_circuit) == len(circuit) + for moment, p_moment in zip(circuit, p_circuit): + if moment == cirq.Moment(gate(q0, q1)): + assert p_moment == cirq.Moment(parameterized_gate(q0, q1)) + else: + assert p_moment == moment def test_get_initial_simplex() -> None: @@ -218,7 +203,7 @@ def test_get_initial_simplex() -> None: def test_characterize_phased_fsim_parameters_with_xeb(pool) -> None: q0, q1 = cirq.LineQubit.range(2) - rs = np.random.RandomState(52) + rs = np.random.default_rng(52) circuits = [ rqcg.random_rotations_between_two_qubit_circuit( q0, q1, depth=20, two_qubit_op_factory=lambda a, b, _: cirq.SQRT_ISWAP(a, b), seed=rs @@ -270,7 +255,7 @@ def test_parallel_full_workflow(request, use_pool) -> None: cycle_depths = [2, 3, 4] graph = _gridqubits_to_graph_device(cirq.GridQubit.rect(2, 2)) combs = rqcg.get_random_combinations_for_device( - n_library_circuits=len(circuits), n_combinations=2, device_graph=graph, random_state=10 + n_library_circuits=len(circuits), n_combinations=2, device_graph=graph, random_state=20 ) sampled_df = sample_2q_xeb_circuits( diff --git a/cirq-core/cirq/experiments/xeb_sampling.py b/cirq-core/cirq/experiments/xeb_sampling.py index 08f61875e8..de0453a3f8 100644 --- a/cirq-core/cirq/experiments/xeb_sampling.py +++ b/cirq-core/cirq/experiments/xeb_sampling.py @@ -343,8 +343,8 @@ def sample_2q_xeb_circuits( # Construct truncated-with-measurement circuits to run. tasks = _generate_sample_2q_xeb_tasks(zipped_circuits, cycle_depths) if shuffle is not None: - prng = value.parse_random_state(shuffle) - prng.shuffle(tasks) # type: ignore[arg-type] + prng = value.parse_random_generator(shuffle) + prng.shuffle(tasks) # Batch and run tasks. records = _execute_sample_2q_xeb_tasks_in_batches( diff --git a/cirq-core/cirq/linalg/decompositions_test.py b/cirq-core/cirq/linalg/decompositions_test.py index 179b158699..011b90d3b4 100644 --- a/cirq-core/cirq/linalg/decompositions_test.py +++ b/cirq-core/cirq/linalg/decompositions_test.py @@ -588,9 +588,9 @@ def _random_two_qubit_unitaries(num_samples: int, random_state: cirq.RANDOM_STAT kr = _local_two_qubit_unitaries(num_samples, random_state) - prng = value.parse_random_state(random_state) + prng = value.parse_random_generator(random_state) # Generate the non-local part by explicit matrix exponentiation. - kak_vecs = prng.rand(num_samples, 3) * np.pi + kak_vecs = prng.random((num_samples, 3)) * np.pi gens = np.einsum('...a,abc->...bc', kak_vecs, _kak_gens) evals, evecs = np.linalg.eigh(gens) A = np.einsum('...ab,...b,...cb', evecs, np.exp(1j * evals), evecs.conj()) diff --git a/cirq-core/cirq/testing/lin_alg_utils.py b/cirq-core/cirq/testing/lin_alg_utils.py index 28180ffd35..529f4953ee 100644 --- a/cirq-core/cirq/testing/lin_alg_utils.py +++ b/cirq-core/cirq/testing/lin_alg_utils.py @@ -33,15 +33,15 @@ def random_superposition( Args: dim: The dimension of the vector. - random_state: A seed (int) or `np.random.RandomState` class to use when - generating random values. If not set, defaults to using the module - methods in `np.random`. + random_state: A seed (int) or `np.random.Generator` to use when + generating random values. If not set, a new unseeded generator is + used. Returns: The sampled unit-length vector. """ - random_state = value.parse_random_state(random_state) - state_vector = random_state.randn(2 * dim).view(np.complex128) + random_state = value.parse_random_generator(random_state) + state_vector = random_state.standard_normal(2 * dim).view(np.complex128) state_vector /= np.linalg.norm(state_vector) return state_vector @@ -62,9 +62,9 @@ def random_density_matrix( 'Random Bures mixed states and the distribution of their purity' https://arxiv.org/abs/0909.5094 """ - random_state = value.parse_random_state(random_state) + random_state = value.parse_random_generator(random_state) - mat = random_state.randn(dim, dim) + 1j * random_state.randn(dim, dim) + mat = random_state.standard_normal((dim, dim)) + 1j * random_state.standard_normal((dim, dim)) mat = mat @ mat.T.conj() return mat / np.trace(mat) @@ -83,9 +83,9 @@ def random_unitary(dim: int, *, random_state: cirq.RANDOM_STATE_OR_SEED_LIKE = N 'How to generate random matrices from the classical compact groups' http://arxiv.org/abs/math-ph/0609050 """ - random_state = value.parse_random_state(random_state) + random_state = value.parse_random_generator(random_state) - z = random_state.randn(dim, dim) + 1j * random_state.randn(dim, dim) + z = random_state.standard_normal((dim, dim)) + 1j * random_state.standard_normal((dim, dim)) q, r = np.linalg.qr(z) d = np.diag(r) return q * (d / abs(d)) @@ -98,9 +98,9 @@ def random_orthogonal( Args: dim: The width and height of the matrix. - random_state: A seed (int) or `np.random.RandomState` class to use when - generating random values. If not set, defaults to using the module - methods in `np.random`. + random_state: A seed (int) or `np.random.Generator` to use when + generating random values. If not set, a new unseeded generator is + used. Returns: The sampled orthogonal matrix. @@ -109,24 +109,24 @@ def random_orthogonal( 'How to generate random matrices from the classical compact groups' http://arxiv.org/abs/math-ph/0609050 """ - random_state = value.parse_random_state(random_state) + random_state = value.parse_random_generator(random_state) - m = random_state.randn(dim, dim) + m = random_state.standard_normal((dim, dim)) q, r = np.linalg.qr(m) d = np.diag(r) return q * (d / abs(d)) def random_special_unitary( - dim: int, *, random_state: np.random.RandomState | None = None + dim: int, *, random_state: cirq.RANDOM_STATE_OR_SEED_LIKE = None ) -> np.ndarray: """Returns a random special unitary distributed with Haar measure. Args: dim: The width and height of the matrix. - random_state: A seed (int) or `np.random.RandomState` class to use when - generating random values. If not set, defaults to using the module - methods in `np.random`. + random_state: A seed (int) or `np.random.Generator` to use when + generating random values. If not set, a new unseeded generator is + used. Returns: The sampled special unitary. @@ -144,9 +144,9 @@ def random_special_orthogonal( Args: dim: The width and height of the matrix. - random_state: A seed (int) or `np.random.RandomState` class to use when - generating random values. If not set, defaults to using the module - methods in `np.random`. + random_state: A seed (int) or `np.random.Generator` to use when + generating random values. If not set, a new unseeded generator is + used. Returns: The sampled special orthogonal matrix. diff --git a/cirq-core/cirq/testing/random_circuit.py b/cirq-core/cirq/testing/random_circuit.py index 3786028848..b46c799782 100644 --- a/cirq-core/cirq/testing/random_circuit.py +++ b/cirq-core/cirq/testing/random_circuit.py @@ -106,7 +106,7 @@ def random_circuit( ) max_arity = max(gate_domain.values()) - prng = value.parse_random_state(random_state) + prng = value.parse_random_generator(random_state) moments: list[circuits.Moment] = [] gate_arity_pairs = sorted(gate_domain.items(), key=repr) @@ -115,10 +115,10 @@ def random_circuit( operations = [] free_qubits = set(qubits) while len(free_qubits) >= max_arity: - gate, arity = gate_arity_pairs[prng.randint(num_gates)] + gate, arity = gate_arity_pairs[prng.integers(num_gates)] op_qubits = prng.choice(sorted(free_qubits), size=arity, replace=False) free_qubits.difference_update(op_qubits) - if prng.rand() <= op_density: + if prng.random() <= op_density: operations.append(gate(*op_qubits)) moments.append(circuits.Moment(operations)) @@ -144,12 +144,12 @@ def random_two_qubit_circuit_with_czs( Returns: the random two qubit circuit """ - prng = value.parse_random_state(random_state) + prng = value.parse_random_generator(random_state) q0 = ops.NamedQubit('q0') if q0 is None else q0 q1 = ops.NamedQubit('q1') if q1 is None else q1 def random_one_qubit_gate(): - return ops.PhasedXPowGate(phase_exponent=prng.rand(), exponent=prng.rand()) + return ops.PhasedXPowGate(phase_exponent=prng.random(), exponent=prng.random()) def one_cz(): return [ops.CZ.on(q0, q1), random_one_qubit_gate().on(q0), random_one_qubit_gate().on(q1)] diff --git a/cirq-core/cirq/testing/random_circuit_test.py b/cirq-core/cirq/testing/random_circuit_test.py index 4fdaf21722..1c80e4b62b 100644 --- a/cirq-core/cirq/testing/random_circuit_test.py +++ b/cirq-core/cirq/testing/random_circuit_test.py @@ -97,7 +97,7 @@ def test_random_circuit( @pytest.mark.parametrize('seed', [random.randint(0, 2**32) for _ in range(10)]) def test_random_circuit_reproducible_with_seed(seed) -> None: - wrappers = (lambda s: s, np.random.RandomState) + wrappers = (lambda s: s, np.random.default_rng) circuits = [ cirq.testing.random_circuit( qubits=10, n_moments=10, op_density=0.7, random_state=wrapper(seed) @@ -121,17 +121,17 @@ def test_random_circuit_not_expected_number_of_qubits() -> None: def test_random_circuit_reproducible_between_runs() -> None: circuit = cirq.testing.random_circuit(5, 8, 0.5, random_state=77) expected_diagram = """ - ┌──┐ -0: ────────────────S─────iSwap───────Y───X─── - │ -1: ───────────Y──────────iSwap───────Y─────── - -2: ─────────────────X────T───────────S───S─── - │ -3: ───────@────────S┼────H───────────────Z─── - │ │ -4: ───────@─────────@────────────────────X─── - └──┘ + ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ +0: ─────iSwap─────────────X────────────────────X─────────iSwap──────── + │ │ │ +1: ────Y┼─────────────────iSwap──────iSwap─────@─────────┼────────@─── + │ │ │ │ │ +2: ─────iSwap────iSwap────┼────Y─────┼────@─────────────X┼────────┼─── + │ │ │ │ │ │ +3: ────@─────────iSwap────┼──────────┼────@────────T─────iSwap────@─── + │ │ │ +4: ────X──────────────────iSwap──────iSwap─────────X────T───────────── + └──────┘ └──────┘ └──────┘ └──────┘ """ cirq.testing.assert_has_diagram(circuit, expected_diagram) diff --git a/cirq-core/cirq/transformers/analytical_decompositions/two_qubit_to_sqrt_iswap_test.py b/cirq-core/cirq/transformers/analytical_decompositions/two_qubit_to_sqrt_iswap_test.py index 025a234e70..6ae1699511 100644 --- a/cirq-core/cirq/transformers/analytical_decompositions/two_qubit_to_sqrt_iswap_test.py +++ b/cirq-core/cirq/transformers/analytical_decompositions/two_qubit_to_sqrt_iswap_test.py @@ -106,12 +106,12 @@ def perturbations_weyl(x: float, y: float, z: float, amount: float = 1e-10) -> I *perturbations_weyl(np.pi * 3 / 16, np.pi * 3 / 16, -np.pi / 16), *perturbations_weyl(np.pi * 3 / 32, np.pi * 3 / 32, np.pi / 32), *perturbations_weyl(np.pi * 3 / 32, np.pi * 3 / 32, -np.pi / 32), - # Some random points - random_unitary(97154), - random_unitary(45375), + # Some random points that require 3 sqrt-iSWAP gates + random_unitary(97155), + random_unitary(45378), random_unitary(78061), - random_unitary(61474), - random_unitary(22897), + random_unitary(61481), + random_unitary(22902), ] TWO_SQRT_ISWAP_UNITARIES = [ # Typical gates and nearby Weyl coordinates to simulate numerical noise @@ -157,7 +157,7 @@ def perturbations_weyl(x: float, y: float, z: float, amount: float = 1e-10) -> I *perturbations_weyl(1 / 4 * np.pi / 4, 1 / 4 * np.pi * 3 / 16, 1 / 4 * np.pi / 16), *perturbations_weyl(1 / 4 * np.pi / 4, 1 / 4 * np.pi * 3 / 16, 1 / 4 * -np.pi / 16), *perturbations_weyl(1 / 4 * np.pi / 4, 1 / 4 * np.pi / 8, 1 / 4 * 0), - # Some random points + # Some random points that require 2 sqrt-iSWAP gates random_unitary(17323), random_unitary(99415), random_unitary(65832), @@ -178,25 +178,26 @@ def perturbations_weyl(x: float, y: float, z: float, amount: float = 1e-10) -> I ] # Lists of seeds for cirq.testing.random_unitary(4, random_state=seed) -# corresponding to different regions in the Weyl chamber and possible -# eigenvalue crossings in the 3-SQRT_ISWAP decomposition +# (drawn from np.random.default_rng(seed)) corresponding to different regions +# in the Weyl chamber and possible eigenvalue crossings in the 3-SQRT_ISWAP +# decomposition # Key: 1 (fig. 4): x < y + abs(z) # 2 (fig. 7a): x > pi/8 # 4 (fig. 7b): z < 0 # 8 (fig. 7c): y+abs(z) < pi/8 if x <= pi/8 else y+abs(z) < pi/4 WEYL_REGION_UNITARIES = { - 1: list(map(random_unitary, [49903, 84819, 84769])), - 3: list(map(random_unitary, [89483, 79607, 96012])), - 5: list(map(random_unitary, [31979, 78758, 47661])), - 7: list(map(random_unitary, [46429, 80635, 49695])), - 8: list(map(random_unitary, [86872, 91499, 47843])), - 9: list(map(random_unitary, [48333, 48333, 95468])), - 10: list(map(random_unitary, [97633, 99062, 44397])), - 11: list(map(random_unitary, [42319, 41651, 11322])), - 12: list(map(random_unitary, [48530, 64117, 31880])), - 13: list(map(random_unitary, [95140, 13781, 60879])), - 14: list(map(random_unitary, [85837, 14090, 73607])), - 15: list(map(random_unitary, [67955, 86488, 24698])), + 1: list(map(random_unitary, [51251, 85790, 86119])), + 3: list(map(random_unitary, [89582, 79609, 96090])), + 5: list(map(random_unitary, [33251, 79294, 48593])), + 7: list(map(random_unitary, [46446, 80654, 49700])), + 8: list(map(random_unitary, [87153, 91539, 47853])), + 9: list(map(random_unitary, [48423, 49015, 95694])), + 10: list(map(random_unitary, [97636, 99070, 44398])), + 11: list(map(random_unitary, [42322, 41666, 11332])), + 12: list(map(random_unitary, [48608, 64314, 31932])), + 13: list(map(random_unitary, [95999, 14642, 62233])), + 14: list(map(random_unitary, [85838, 14090, 73608])), + 15: list(map(random_unitary, [67968, 86492, 24707])), } ALL_REGION_UNITARIES = list(itertools.chain(*WEYL_REGION_UNITARIES.values())) diff --git a/cirq-core/cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils.py b/cirq-core/cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils.py index 6f6b8ba2af..8481466955 100644 --- a/cirq-core/cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils.py +++ b/cirq-core/cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils.py @@ -48,7 +48,7 @@ def _single_qubit_unitary( def random_qubit_unitary( shape: Sequence[int] = (), randomize_global_phase: bool = False, - rng: np.random.RandomState | None = None, + rng: random_state.RANDOM_STATE_OR_SEED_LIKE = None, ) -> np.ndarray: """Random qubit unitary distributed over the Haar measure. @@ -60,20 +60,22 @@ def random_qubit_unitary( randomize_global_phase: (Default False) If True, a global phase is also sampled randomly. This corresponds to sampling over U(2) instead of SU(2). - rng: Random number generator to be used in sampling. Default is - numpy.random. + rng: Random number generator (or seed) to be used in sampling. Anything + accepted by `cirq.value.parse_random_generator`. Default is a new, + unseeded `np.random.Generator`. """ - real_rng = random_state.parse_random_state(rng) + real_rng = random_state.parse_random_generator(rng) + size = tuple(shape) - theta = np.arcsin(np.sqrt(real_rng.rand(*shape))) - phi_d = real_rng.rand(*shape) * np.pi * 2 - phi_o = real_rng.rand(*shape) * np.pi * 2 + theta = np.arcsin(np.sqrt(real_rng.random(size))) + phi_d = real_rng.random(size) * np.pi * 2 + phi_o = real_rng.random(size) * np.pi * 2 out = _single_qubit_unitary(theta, phi_d, phi_o) if randomize_global_phase: out = np.moveaxis(out, (-2, -1), (0, 1)) - out *= np.exp(1j * np.pi * 2 * real_rng.rand(*shape)) + out *= np.exp(1j * np.pi * 2 * real_rng.random(size)) out = np.moveaxis(out, (0, 1), (-2, -1)) return out diff --git a/cirq-core/cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils_test.py b/cirq-core/cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils_test.py index a7b0849281..7f92a9f62c 100644 --- a/cirq-core/cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils_test.py +++ b/cirq-core/cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils_test.py @@ -31,16 +31,16 @@ def test_kak_vector_infidelity_ignore_equivalent_nontrivial() -> None: def test_random_qubit_unitary_shape() -> None: - rng = value.parse_random_state(11) + rng = value.parse_random_generator(11) actual = random_qubit_unitary((3, 4, 5), True, rng).ravel() - rng = value.parse_random_state(11) + rng = value.parse_random_generator(11) expected = random_qubit_unitary((3 * 4 * 5,), True, rng).ravel() np.testing.assert_almost_equal(actual, expected) def test_random_qubit_default() -> None: - rng = value.parse_random_state(11) + rng = value.parse_random_generator(11) actual = random_qubit_unitary(randomize_global_phase=True, rng=rng).ravel() - rng = value.parse_random_state(11) + rng = value.parse_random_generator(11) expected = random_qubit_unitary((1, 1, 1), True, rng=rng).ravel() np.testing.assert_almost_equal(actual, expected) diff --git a/cirq-core/cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation.py b/cirq-core/cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation.py index 1df2eeb77e..29db399e49 100644 --- a/cirq-core/cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation.py +++ b/cirq-core/cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation.py @@ -349,7 +349,7 @@ def two_qubit_gate_product_tabulation( ValueError: If `allow_missing_points` is False and not all the points in the Weyl chamber are compilable using 2 or 3 base gates. """ - rng = value.parse_random_state(random_state) + rng = value.parse_random_generator(random_state) assert 1 / 2 > max_infidelity > 0 spacing = np.sqrt(max_infidelity / 3) diff --git a/cirq-core/cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation_test.py b/cirq-core/cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation_test.py index a3316c205e..fce16ca4d8 100644 --- a/cirq-core/cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation_test.py +++ b/cirq-core/cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation_test.py @@ -28,7 +28,7 @@ TwoQubitGateTabulation, ) -_rng = value.parse_random_state(11) # for determinism +_rng = value.parse_random_generator(11) # for determinism sycamore_tabulation = two_qubit_gate_product_tabulation( cirq.unitary(cirq.FSimGate(np.pi / 2, np.pi / 6)), 0.2, random_state=_rng @@ -123,7 +123,7 @@ def test_sycamore_gate_tabulation_repr() -> None: def test_sycamore_gate_tabulation_eq() -> None: - rng = value.parse_random_state(11) + rng = value.parse_random_generator(11) sycamore_tabulation_copy = two_qubit_gate_product_tabulation( cirq.unitary(cirq.FSimGate(np.pi / 2, np.pi / 6)), 0.2, random_state=rng ) diff --git a/cirq-core/cirq/transformers/heuristic_decompositions/two_qubit_numerical_optimization.py b/cirq-core/cirq/transformers/heuristic_decompositions/two_qubit_numerical_optimization.py index 6f16ede0fa..2308efc33a 100644 --- a/cirq-core/cirq/transformers/heuristic_decompositions/two_qubit_numerical_optimization.py +++ b/cirq-core/cirq/transformers/heuristic_decompositions/two_qubit_numerical_optimization.py @@ -257,7 +257,7 @@ def _optimize_template( target: np.ndarray, base_gate: np.ndarray, num_layers: int, - rng: np.random.RandomState, + rng: np.random.Generator, num_restarts: int, maxiter: int, ) -> tuple[np.ndarray, float]: @@ -468,7 +468,7 @@ def two_qubit_gate_numerical_compilation( if not 0 < target_fidelity < 1: raise ValueError(f'target_fidelity must be in (0, 1), got {target_fidelity}') - rng = value.parse_random_state(random_state) + rng = value.parse_random_generator(random_state) best: TwoQubitNumericalCompilationResult | None = None best_overall = -np.inf diff --git a/cirq-core/cirq/transformers/heuristic_decompositions/two_qubit_numerical_optimization_test.py b/cirq-core/cirq/transformers/heuristic_decompositions/two_qubit_numerical_optimization_test.py index 462f7d7986..2bec131d8e 100644 --- a/cirq-core/cirq/transformers/heuristic_decompositions/two_qubit_numerical_optimization_test.py +++ b/cirq-core/cirq/transformers/heuristic_decompositions/two_qubit_numerical_optimization_test.py @@ -116,7 +116,7 @@ def test_exact_compilation_random_su4(base_gate_name: str, seed: int) -> None: (exact decomposition): NuOp uses 3 CZ, 3 SYC or 3 sqrt-iSWAP gates for random unitaries. """ - target = random_special_unitary(4, random_state=value.parse_random_state(seed)) + target = random_special_unitary(4, random_state=value.parse_random_generator(seed)) result = two_qubit_gate_numerical_compilation( target, _BASE_GATES[base_gate_name], random_state=seed ) @@ -163,7 +163,7 @@ def test_approximate_compilation_uses_fewer_gates() -> None: # Target seed chosen so that the best 2-CZ decomposition has Fd in # [0.99, 1 - 1e-8): exact compilation needs 3 CZ gates, but the Fd >= 0.99 # threshold is already met with 2. - target = random_special_unitary(4, random_state=value.parse_random_state(1)) + target = random_special_unitary(4, random_state=value.parse_random_generator(2)) exact = two_qubit_gate_numerical_compilation(target, _CZ, random_state=1) approximate = two_qubit_gate_numerical_compilation( target, _CZ, target_fidelity=0.99, random_state=1 @@ -182,7 +182,7 @@ def test_noise_adaptive_compilation_prefers_higher_overall_fidelity() -> None: 3-CZ decomposition (Fu = 0.94^3) and over any sqrt-iSWAP decomposition. With error rates given, `success` is measured by Fu. """ - target = random_special_unitary(4, random_state=value.parse_random_state(1)) + target = random_special_unitary(4, random_state=value.parse_random_generator(1)) result = two_qubit_gate_numerical_compilation( target, [_CZ, _SQRT_ISWAP], @@ -229,7 +229,7 @@ def test_noise_adaptive_compilation_prefers_more_reliable_gates() -> None: def test_noise_adaptive_compilation_single_qubit_error_rates() -> None: - target = random_special_unitary(4, random_state=value.parse_random_state(1)) + target = random_special_unitary(4, random_state=value.parse_random_generator(1)) result = two_qubit_gate_numerical_compilation( target, _CZ, @@ -246,7 +246,7 @@ def test_noise_adaptive_compilation_single_qubit_error_rates() -> None: def test_noise_adaptive_compilation_per_qubit_error_rates() -> None: """A (q0_rate, q1_rate) pair folds per-qubit calibration data into Fh.""" - target = random_special_unitary(4, random_state=value.parse_random_state(1)) + target = random_special_unitary(4, random_state=value.parse_random_generator(1)) result = two_qubit_gate_numerical_compilation( target, _CZ, @@ -269,7 +269,7 @@ def test_noise_adaptive_compilation_triggered_by_single_qubit_error_rates() -> N single-qubit error rate, Fu(2 CZ) = Fd * 0.99^6 beats Fu(3 CZ) = 0.99^8, so the shorter decomposition wins even without base gate error rates. """ - target = random_special_unitary(4, random_state=value.parse_random_state(1)) + target = random_special_unitary(4, random_state=value.parse_random_generator(1)) result = two_qubit_gate_numerical_compilation( target, _CZ, single_qubit_error_rates=0.01, target_fidelity=0.9, random_state=1 ) @@ -282,7 +282,7 @@ def test_noise_adaptive_compilation_triggered_by_single_qubit_error_rates() -> N def test_local_unitaries_reconstruct_actual_gate() -> None: - target = random_special_unitary(4, random_state=value.parse_random_state(1)) + target = random_special_unitary(4, random_state=value.parse_random_generator(1)) result = two_qubit_gate_numerical_compilation(target, _CZ, num_restarts=1, random_state=1) assert len(result.local_unitaries) == result.num_base_gates + 1 for k0, k1 in result.local_unitaries: @@ -292,13 +292,13 @@ def test_local_unitaries_reconstruct_actual_gate() -> None: def test_actual_gate_matches_target_up_to_global_phase() -> None: - target = random_special_unitary(4, random_state=value.parse_random_state(1)) + target = random_special_unitary(4, random_state=value.parse_random_generator(1)) result = two_qubit_gate_numerical_compilation(target, _CZ, random_state=1) cirq.testing.assert_allclose_up_to_global_phase(result.actual_gate, target, atol=1e-6) def test_determinism_with_seed() -> None: - target = random_special_unitary(4, random_state=value.parse_random_state(1)) + target = random_special_unitary(4, random_state=value.parse_random_generator(1)) result1 = two_qubit_gate_numerical_compilation(target, _CZ, num_restarts=1, random_state=1) result2 = two_qubit_gate_numerical_compilation(target, _CZ, num_restarts=1, random_state=1) assert np.array_equal(result1.actual_gate, result2.actual_gate) @@ -307,7 +307,7 @@ def test_determinism_with_seed() -> None: def test_max_layers_failure_mode() -> None: """If max_layers is too small, return the best-effort result with success=False.""" - target = random_special_unitary(4, random_state=value.parse_random_state(1)) + target = random_special_unitary(4, random_state=value.parse_random_generator(1)) result = two_qubit_gate_numerical_compilation( target, _CZ, max_layers=1, num_restarts=1, random_state=1 ) @@ -333,7 +333,7 @@ def test_multiple_base_gates_first_meeting_threshold_wins() -> None: def test_numerical_compiler_wrapper() -> None: compiler = TwoQubitNumericalCompiler(base_gates=(_CZ,), random_state=1) - target = random_special_unitary(4, random_state=value.parse_random_state(1)) + target = random_special_unitary(4, random_state=value.parse_random_generator(1)) result = compiler.compile_two_qubit_gate(target) assert result.success assert np.array_equal(result.base_gate_unitary, _CZ) @@ -344,7 +344,7 @@ def test_numerical_compiler_equality() -> None: # A non-empty memoization cache does not affect equality. cached_compiler = TwoQubitNumericalCompiler(base_gates=(_CZ,), random_state=1) cached_compiler.compile_two_qubit_gate( - random_special_unitary(4, random_state=value.parse_random_state(1)) + random_special_unitary(4, random_state=value.parse_random_generator(1)) ) eq = cirq.testing.EqualsTester() eq.add_equality_group( @@ -405,8 +405,8 @@ def test_numerical_compiler_json_rejects_live_rng() -> None: def test_numerical_compiler_memoizes_results() -> None: """Identical unitaries are compiled once; distinct ones are compiled separately.""" compiler = TwoQubitNumericalCompiler(base_gates=(_CZ,), random_state=1, num_restarts=1) - target1 = random_special_unitary(4, random_state=value.parse_random_state(1)) - target2 = random_special_unitary(4, random_state=value.parse_random_state(2)) + target1 = random_special_unitary(4, random_state=value.parse_random_generator(1)) + target2 = random_special_unitary(4, random_state=value.parse_random_generator(2)) with mock.patch( 'cirq.transformers.heuristic_decompositions.two_qubit_numerical_optimization' '.two_qubit_gate_numerical_compilation', @@ -421,8 +421,8 @@ def test_numerical_compiler_memoizes_results() -> None: def test_numerical_compiler_cache_tolerates_numerical_noise() -> None: """Unitaries differing only by floating-point noise share a cache entry.""" compiler = TwoQubitNumericalCompiler(base_gates=(_CZ,), random_state=1, num_restarts=1) - target = random_special_unitary(4, random_state=value.parse_random_state(1)) - other = random_special_unitary(4, random_state=value.parse_random_state(2)) + target = random_special_unitary(4, random_state=value.parse_random_generator(1)) + other = random_special_unitary(4, random_state=value.parse_random_generator(2)) # Mathematically the same unitary, recomputed in a different order. noisy = (target @ other) @ other.conj().T assert noisy.tobytes() != target.tobytes() @@ -470,7 +470,7 @@ def test_numerical_compiler_invalid_max_cache_size() -> None: def test_input_validation() -> None: - target = random_special_unitary(4, random_state=value.parse_random_state(1)) + target = random_special_unitary(4, random_state=value.parse_random_generator(1)) with pytest.raises(ValueError, match='target_unitary must have shape'): two_qubit_gate_numerical_compilation(np.eye(2), _CZ) with pytest.raises(ValueError, match='target_fidelity must be in'): @@ -513,7 +513,7 @@ def test_input_validation() -> None: def test_non_unitary_inputs_raise_value_error() -> None: """Non-finite or non-unitary inputs are rejected before optimization runs.""" - target = random_special_unitary(4, random_state=value.parse_random_state(1)) + target = random_special_unitary(4, random_state=value.parse_random_generator(1)) with pytest.raises(ValueError, match='target_unitary must contain only finite'): two_qubit_gate_numerical_compilation(np.full((4, 4), np.nan), _CZ) with pytest.raises(ValueError, match='target_unitary must be unitary'): diff --git a/cirq-core/cirq/transformers/transformer_primitives_test.py b/cirq-core/cirq/transformers/transformer_primitives_test.py index 7ab992f8f0..a6f5ff2c74 100644 --- a/cirq-core/cirq/transformers/transformer_primitives_test.py +++ b/cirq-core/cirq/transformers/transformer_primitives_test.py @@ -990,7 +990,7 @@ def test_merge_operations_deterministic_order(qubit_order): @pytest.mark.parametrize("op_density", [0.1, 0.5, 0.9]) def test_merge_operations_complexity(op_density): - prng = cirq.value.parse_random_state(11011) + prng = cirq.value.parse_random_generator(11011) circuit = cirq.testing.random_circuit(20, 500, op_density, random_state=prng) for merge_func in [ lambda _, __: None,