def test_simulate_2q_xeb_circuits(): q0, q1 = cirq.LineQubit.range(2) circuits = [ rqcg.random_rotations_between_two_qubit_circuit( q0, q1, depth=50, two_qubit_op_factory=lambda a, b, _: cirq.SQRT_ISWAP(a, b), ) for _ in range(2) ] cycle_depths = np.arange(3, 50, 9) df = simulate_2q_xeb_circuits( circuits=circuits, cycle_depths=cycle_depths, ) assert len(df) == len(cycle_depths) * len(circuits) for (circuit_i, cycle_depth), row in df.iterrows(): assert 0 <= circuit_i < len(circuits) assert cycle_depth in cycle_depths assert len(row['pure_probs']) == 4 assert np.isclose(np.sum(row['pure_probs']), 1) with multiprocessing.Pool() as pool: df2 = simulate_2q_xeb_circuits(circuits, cycle_depths, pool=pool) pd.testing.assert_frame_equal(df, df2)
def test_incremental_simulate(multiprocess): q0, q1 = cirq.LineQubit.range(2) circuits = [ rqcg.random_rotations_between_two_qubit_circuit( q0, q1, depth=100, two_qubit_op_factory=lambda a, b, _: cirq.SQRT_ISWAP(a, b), ) for _ in range(20) ] cycle_depths = np.arange(3, 100, 9) if multiprocess: pool = multiprocessing.Pool() else: pool = None start = time.perf_counter() df_ref = _ref_simulate_2q_xeb_circuits( circuits=circuits, cycle_depths=cycle_depths, pool=pool, ) end1 = time.perf_counter() df = simulate_2q_xeb_circuits(circuits=circuits, cycle_depths=cycle_depths, pool=pool) end2 = time.perf_counter() if pool is not None: pool.terminate() print("\nnew:", end2 - end1, "old:", end1 - start) pd.testing.assert_frame_equal(df_ref, df)
def test_simulate_circuit_length_validation(): q0, q1 = cirq.LineQubit.range(2) circuits = [ rqcg.random_rotations_between_two_qubit_circuit( q0, q1, depth=10, # not long enough! two_qubit_op_factory=lambda a, b, _: cirq.SQRT_ISWAP(a, b), ) for _ in range(2) ] cycle_depths = np.arange(3, 50, 9) with pytest.raises(ValueError, match='.*not long enough.*'): _ = simulate_2q_xeb_circuits(circuits=circuits, cycle_depths=cycle_depths)
def benchmark_2q_xeb_fidelities( sampled_df: pd.DataFrame, circuits: Sequence['cirq.Circuit'], cycle_depths: Optional[Sequence[int]] = None, param_resolver: 'cirq.ParamResolverOrSimilarType' = None, pool: Optional['multiprocessing.pool.Pool'] = None, ) -> pd.DataFrame: """Simulate and benchmark two-qubit XEB circuits. This uses the estimator from `cirq.experiments.fidelity_estimation.least_squares_xeb_fidelity_from_expectations`, but adapted for use on pandas DataFrames for efficient vectorized operation. Args: sampled_df: The sampled results to benchmark. This is likely produced by a call to `sample_2q_xeb_circuits`. circuits: The library of circuits corresponding to the sampled results in `sampled_df`. cycle_depths: The sequence of cycle depths to benchmark the circuits. If not provided, we use the cycle depths found in `sampled_df`. All requested `cycle_depths` must be present in `sampled_df`. param_resolver: If circuits contain parameters, resolve according to this ParamResolver prior to simulation pool: If provided, execute the simulations in parallel. Returns: A DataFrame with columns 'cycle_depth' and 'fidelity'. Raises: ValueError: If `cycle_depths` is not a non-empty array or if the `cycle_depths` provided includes some values not available in `sampled_df`. """ sampled_cycle_depths = (sampled_df.index.get_level_values( 'cycle_depth').drop_duplicates().sort_values()) if cycle_depths is not None: if len(cycle_depths) == 0: raise ValueError("`cycle_depths` should be a non-empty array_like") not_in_sampled = np.setdiff1d(cycle_depths, sampled_cycle_depths) if len(not_in_sampled) > 0: raise ValueError(f"The `cycle_depths` provided include some not " f"available in `sampled_df`: {not_in_sampled}") sim_cycle_depths = cycle_depths else: sim_cycle_depths = sampled_cycle_depths simulated_df = simulate_2q_xeb_circuits(circuits=circuits, cycle_depths=sim_cycle_depths, param_resolver=param_resolver, pool=pool) # Join the `pure_probs` onto `sampled_df`. By using 'inner', we let # the `cycle_depths` argument to this function control what cycle depths are benchmarked. df = sampled_df.join(simulated_df, how='inner').reset_index() D = 4 # two qubits pure_probs = np.array(df['pure_probs'].to_list()) sampled_probs = np.array(df['sampled_probs'].to_list()) df['e_u'] = np.sum(pure_probs**2, axis=1) df['u_u'] = np.sum(pure_probs, axis=1) / D df['m_u'] = np.sum(pure_probs * sampled_probs, axis=1) df['y'] = df['m_u'] - df['u_u'] df['x'] = df['e_u'] - df['u_u'] df['numerator'] = df['x'] * df['y'] df['denominator'] = df['x']**2 def per_cycle_depth(df): """This function is applied per cycle_depth in the following groupby aggregation.""" fid_lsq = df['numerator'].sum() / df['denominator'].sum() ret = {'fidelity': fid_lsq} def _try_keep(k): """If all the values for a key `k` are the same in this group, we can keep it.""" if k not in df.columns: return # coverage: ignore vals = df[k].unique() if len(vals) == 1: ret[k] = vals[0] else: # coverage: ignore raise AssertionError( f"When computing per-cycle-depth fidelity, multiple " f"values for {k} were grouped together: {vals}") _try_keep('pair') return pd.Series(ret) if 'pair_i' in df.columns: groupby_names = ['layer_i', 'pair_i', 'cycle_depth'] else: groupby_names = ['cycle_depth'] return df.groupby(groupby_names).apply(per_cycle_depth).reset_index()
def benchmark_2q_xeb_fidelities( sampled_df: pd.DataFrame, circuits: Sequence['cirq.Circuit'], cycle_depths: Sequence[int], param_resolver: 'cirq.ParamResolverOrSimilarType' = None, pool: Optional['multiprocessing.pool.Pool'] = None, ) -> pd.DataFrame: """Simulate and benchmark two-qubit XEB circuits. This uses the estimator from `cirq.experiments.fidelity_estimation.least_squares_xeb_fidelity_from_expectations`, but adapted for use on pandas DataFrames for efficient vectorized operation. Args: sampled_df: The sampled results to benchmark. This is likely produced by a call to `sample_2q_xeb_circuits`. circuits: The library of circuits corresponding to the sampled results in `sampled_df`. cycle_depths: The sequence of cycle depths to simulate the circuits. param_resolver: If circuits contain parameters, resolve according to this ParamResolver prior to simulation pool: If provided, execute the simulations in parallel. Returns: A DataFrame with columns 'cycle_depth' and 'fidelity'. """ simulated_df = simulate_2q_xeb_circuits( circuits=circuits, cycle_depths=cycle_depths, param_resolver=param_resolver, pool=pool ) df = sampled_df.join(simulated_df) D = 4 # two qubits pure_probs = np.array(df['pure_probs'].to_list()) sampled_probs = np.array(df['sampled_probs'].to_list()) df['e_u'] = np.sum(pure_probs ** 2, axis=1) df['u_u'] = np.sum(pure_probs, axis=1) / D df['m_u'] = np.sum(pure_probs * sampled_probs, axis=1) df['y'] = df['m_u'] - df['u_u'] df['x'] = df['e_u'] - df['u_u'] df['numerator'] = df['x'] * df['y'] df['denominator'] = df['x'] ** 2 def per_cycle_depth(df): """This function is applied per cycle_depth in the following groupby aggregation.""" fid_lsq = df['numerator'].sum() / df['denominator'].sum() ret = {'fidelity': fid_lsq} def _try_keep(k): """If all the values for a key `k` are the same in this group, we can keep it.""" if k not in df.columns: return # coverage: ignore vals = df[k].unique() if len(vals) == 1: ret[k] = vals[0] else: # coverage: ignore raise AssertionError( f"When computing per-cycle-depth fidelity, multiple " f"values for {k} were grouped together: {vals}" ) _try_keep('pair') return pd.Series(ret) if 'pair_i' in df.columns: groupby_names = ['layer_i', 'pair_i', 'cycle_depth'] else: groupby_names = ['cycle_depth'] return df.reset_index().groupby(groupby_names).apply(per_cycle_depth).reset_index()