def test_batch_sample_basic(self, sim): """Test sampling.""" n_samples = 1 n_qubits = 8 qubits = cirq.GridQubit.rect(1, n_qubits) circuit = cirq.Circuit(*cirq.Z.on_each(*qubits[:n_qubits // 2]), *cirq.X.on_each(*qubits[n_qubits // 2:])) test_results = batch_util.batch_sample([circuit], [cirq.ParamResolver({})], n_samples, sim) state = sim.simulate(circuit, cirq.ParamResolver({})) expected_results = _sample_helper(sim, state, len(qubits), n_samples) self.assertAllEqual(expected_results, test_results[0]) self.assertDTypeEqual(test_results, np.int32)
def test_sampling(self, op_and_sim, n_qubits, symbol_names): """Compare sampling with tfq ops and Cirq.""" op = op_and_sim[0] sim = op_and_sim[1] qubits = cirq.GridQubit.rect(1, n_qubits) n_samples = int((2**n_qubits) * 1000) circuit_batch, resolver_batch = \ util.random_symbol_circuit_resolver_batch( qubits, symbol_names, BATCH_SIZE, n_moments=30) for i in range(BATCH_SIZE): circuit_batch[i] += cirq.Circuit( *[cirq.H(qubit) for qubit in qubits]) symbol_values_array = np.array( [[resolver[symbol] for symbol in symbol_names] for resolver in resolver_batch]) op_samples = np.array( op(util.convert_to_tensor(circuit_batch), symbol_names, symbol_values_array, [n_samples]).to_list()) op_histograms = [ np.histogram( sample.dot(1 << np.arange(sample.shape[-1] - 1, -1, -1)), range=(0, 2**len(qubits)), bins=2**len(qubits))[0] for sample in op_samples ] cirq_samples = batch_util.batch_sample(circuit_batch, resolver_batch, n_samples, sim) cirq_histograms = [ np.histogram( sample.dot(1 << np.arange(sample.shape[-1] - 1, -1, -1)), range=(0, 2**len(qubits)), bins=2**len(qubits))[0] for sample in cirq_samples ] for a, b in zip(op_histograms, cirq_histograms): self.assertLess(stats.entropy(a + 1e-8, b + 1e-8), 0.005)
def test_sampling_empty(self, op_and_sim, n_qubits, symbol_names): """Test empty circuits for sampling using cirq and tfq.""" op = op_and_sim[0] sim = op_and_sim[1] qubits = cirq.GridQubit.rect(1, n_qubits) n_samples = int((2**n_qubits) * 1000) circuit_batch = [cirq.Circuit() for _ in range(BATCH_SIZE)] resolver_batch = [cirq.ParamResolver({}) for _ in range(BATCH_SIZE)] symbol_values_array = np.array([[0.0 for _ in symbol_names] for _ in resolver_batch]) op_samples = np.array( op(util.convert_to_tensor(circuit_batch), symbol_names, symbol_values_array, [n_samples]).to_list()) op_histograms = [ np.histogram( sample.dot(1 << np.arange(sample.shape[-1] - 1, -1, -1)), range=(0, 2**len(qubits)), bins=2**len(qubits))[0] for sample in op_samples ] cirq_samples = batch_util.batch_sample(circuit_batch, resolver_batch, n_samples, sim) cirq_histograms = [ np.histogram( sample.dot(1 << np.arange(sample.shape[-1] - 1, -1, -1)), range=(0, 2**len(qubits)), bins=2**len(qubits))[0] for sample in cirq_samples ] for a, b in zip(op_histograms, cirq_histograms): self.assertLess(stats.entropy(a + 1e-8, b + 1e-8), 0.005)
def cirq_sample(programs, symbol_names, symbol_values, num_samples): """Draw samples from circuits. Draw samples from `circuits` where each circuit will have the values in `symbol_values` resolved into the symbols in the circuit (with the ordering defined by `symbol_names`). ```python symbol_names = ['a', 'b', 'c'] programs = tfq.convert_to_tensor( [cirq.Circuit(H(q0) ** sympy.Symbol('a'), X(q1) ** sympy.Symbol('b'), Y(q2) ** sympy.Symbol('c'))] ) symbol_values = [[3,2,1]] n_samples = [100] cirq_sample(programs, symbol_names, sybmol_values, n_samples) ``` Would place the values of 3 into the Symbol labeled 'a', 2 into the symbol labeled 'b' and 1 into the symbol labeled 'c'. Then it would draw 100 samples from the circuit. Note: In the case of circuits with varying size, all nonexistant samples for a particular circuit are padded with -2. Args: programs: `tf.Tensor` of strings with shape [batch_size] containing the string representations of the circuits to be executed. symbol_names: `tf.Tensor` of strings with shape [n_params], which is used to specify the order in which the values in `symbol_values` should be placed inside of the circuits in `programs`. symbol_values: `tf.Tensor` of real numbers with shape [batch_size, n_params] specifying parameter values to resolve into the circuits specified by programs, following the ordering dictated by `symbol_names`. num_samples: `tf.Tensor` with one element indicating the number of samples to draw. Returns: `tf.Tensor` with shape [batch_size, num_samples, <# qubits in largest circuit>] that holds samples (as boolean values) for each circuit. """ def _no_grad(grad): raise RuntimeError( 'Differentiation through a sampling operation is not supported.' ) _input_check_helper(programs, symbol_names, symbol_values) if not (int(tf.size(num_samples)) == 1): raise ValueError("num_samples tensor must have size 1") if not isinstance(num_samples.dtype.as_numpy_dtype(), numbers.Integral): raise TypeError("num_samples tensor must be of integer type") serialized_programs = programs programs, resolvers = _batch_deserialize_helper(programs, symbol_names, symbol_values) num_samples = int(num_samples.numpy()) if isinstance(sampler, (cirq.Simulator, cirq.DensityMatrixSimulator)): # Only local simulators can be handled by batch_sample results = batch_util.batch_sample(programs, resolvers, num_samples, sampler) return np.array(results, dtype=np.int8), _no_grad # All other samplers need terminal measurement gates. programs = [ p + cirq.Circuit(cirq.measure(*sorted(p.all_qubits()), key='tfq')) for p in programs ] max_n_qubits = max(len(p.all_qubits()) for p in programs) if isinstance(sampler, cirq.google.QuantumEngineSampler): # group samples from identical circuits to reduce communication # overhead. Have to keep track of the order in which things came # in to make sure the output is ordered correctly to_be_grouped = [ (ser_prog.numpy(), resolver, index) for index, ( ser_prog, resolver) in enumerate(zip(serialized_programs, resolvers)) ] grouped = _group_tuples(to_be_grouped) # start all the necessary jobs results_mapping = {} for key, value in grouped.items(): program = programs[value[0][1]] resolvers = [x[0] for x in value] orders = [x[1] for x in value] # sampler.run_sweep blocks until results are in, so go around it result = sampler._engine.run_sweep( program=program, params=resolvers, repetitions=num_samples, processor_ids=sampler._processor_ids, gate_set=sampler._gate_set) results_mapping[result] = orders # get all results cirq_results = [None] * len(programs) for key, value in results_mapping.items(): this_results = key.results() for result, index in zip(this_results, value): cirq_results[index] = result else: # All other cirq.Samplers handled here. #TODO(zaqqwerty): replace with run_batch once Cirq #3148 is resolved cirq_results = [] for p, r in zip(programs, resolvers): cirq_results.append(sampler.run(p, r, num_samples)) results = [] for r in cirq_results: results.append( tf.keras.preprocessing.sequence.pad_sequences( r.measurements['tfq'], maxlen=max_n_qubits, dtype=np.int8, value=-2, padding='pre')) return np.array(results, dtype=np.int8), _no_grad