in tensorflow_quantum/core/ops/noise/tfq_noisy_expectation.cc [155:250]
void ComputeLarge(const std::vector<int>& num_qubits,
const std::vector<NoisyQsimCircuit>& ncircuits,
const std::vector<std::vector<PauliSum>>& pauli_sums,
const std::vector<std::vector<int>>& num_samples,
tensorflow::OpKernelContext* context,
tensorflow::TTypes<float, 1>::Matrix* output_tensor) {
// Instantiate qsim objects.
const auto tfq_for = tfq::QsimFor(context);
using Simulator = qsim::Simulator<const tfq::QsimFor&>;
using StateSpace = Simulator::StateSpace;
using QTSimulator =
qsim::QuantumTrajectorySimulator<qsim::IO, QsimGate,
qsim::MultiQubitGateFuser, Simulator>;
// Begin simulation.
int largest_nq = 1;
Simulator sim = Simulator(tfq_for);
StateSpace ss = StateSpace(tfq_for);
auto sv = ss.Create(largest_nq);
auto scratch = ss.Create(largest_nq);
tensorflow::GuardedPhiloxRandom random_gen;
int max_n_shots = 1;
for (int i = 0; i < num_samples.size(); i++) {
for (int j = 0; j < num_samples[i].size(); j++) {
max_n_shots = std::max(max_n_shots, num_samples[i][j]);
}
}
random_gen.Init(tensorflow::random::New64(), tensorflow::random::New64());
auto local_gen =
random_gen.ReserveSamples128(ncircuits.size() * max_n_shots + 1);
tensorflow::random::SimplePhilox rand_source(&local_gen);
// Simulate programs one by one. Parallelizing over state vectors
// we no longer parallelize over circuits. Each time we encounter a
// a larger circuit we will grow the Statevector as necessary.
for (int i = 0; i < ncircuits.size(); i++) {
int nq = num_qubits[i];
// (#679) Just ignore empty program
if (ncircuits[i].channels.size() == 0) {
for (int j = 0; j < pauli_sums[i].size(); j++) {
(*output_tensor)(i, j) = -2.0;
}
continue;
}
if (nq > largest_nq) {
largest_nq = nq;
sv = ss.Create(largest_nq);
scratch = ss.Create(largest_nq);
}
QTSimulator::Parameter param;
param.collect_kop_stat = false;
param.collect_mea_stat = false;
param.normalize_before_mea_gates = true;
std::vector<uint64_t> unused_stats;
// Track op-wise stats.
std::vector<int> run_samples(num_samples[i].size(), 0);
std::vector<double> rolling_sums(num_samples[i].size(), 0.0);
while (1) {
ss.SetStateZero(sv);
QTSimulator::RunOnce(param, ncircuits[i], rand_source.Rand64(), ss, sim,
scratch, sv, unused_stats);
// Use this trajectory as a source for all expectation calculations.
for (int j = 0; j < pauli_sums[i].size(); j++) {
if (run_samples[j] >= num_samples[i][j]) {
continue;
}
float exp_v = 0.0;
OP_REQUIRES_OK(context,
ComputeExpectationQsim(pauli_sums[i][j], sim, ss, sv,
scratch, &exp_v));
rolling_sums[j] += static_cast<double>(exp_v);
run_samples[j]++;
}
bool break_loop = true;
for (int j = 0; j < num_samples[i].size(); j++) {
if (run_samples[j] < num_samples[i][j]) {
break_loop = false;
break;
}
}
if (break_loop) {
for (int j = 0; j < num_samples[i].size(); j++) {
rolling_sums[j] /= num_samples[i][j];
(*output_tensor)(i, j) = static_cast<float>(rolling_sums[j]);
}
break;
}
}
}
}