void CreateGradientCircuit()

in tensorflow_quantum/core/src/adj_util.cc [37:173]


void CreateGradientCircuit(
    const QsimCircuit& circuit, const std::vector<GateMetaData>& metadata,
    std::vector<std::vector<qsim::GateFused<QsimGate>>>* partial_fuses,
    std::vector<GradientOfGate>* grad_gates) {
  for (int i = 0; i < metadata.size(); i++) {
    if (metadata[i].symbol_values.empty()) {
      continue;
    }
    // found a gate that was constructed with symbols.
    GradientOfGate grad;

    // Single qubit Eigen.
    if (circuit.gates[i].kind == qsim::Cirq::GateKind::kXPowGate ||
        circuit.gates[i].kind == qsim::Cirq::GateKind::kYPowGate ||
        circuit.gates[i].kind == qsim::Cirq::GateKind::kZPowGate ||
        circuit.gates[i].kind == qsim::Cirq::GateKind::kHPowGate) {
      PopulateGradientSingleEigen(
          metadata[i].create_f1, metadata[i].symbol_values[0], i,
          circuit.gates[i].qubits[0], metadata[i].gate_params[0],
          metadata[i].gate_params[1], metadata[i].gate_params[2], &grad);
      grad_gates->push_back(grad);
    }

    // Two qubit Eigen.
    else if (circuit.gates[i].kind == qsim::Cirq::GateKind::kCZPowGate ||
             circuit.gates[i].kind == qsim::Cirq::GateKind::kCXPowGate ||
             circuit.gates[i].kind == qsim::Cirq::GateKind::kXXPowGate ||
             circuit.gates[i].kind == qsim::Cirq::GateKind::kYYPowGate ||
             circuit.gates[i].kind == qsim::Cirq::GateKind::kZZPowGate ||
             circuit.gates[i].kind == qsim::Cirq::GateKind::kISwapPowGate ||
             circuit.gates[i].kind == qsim::Cirq::GateKind::kSwapPowGate) {
      bool swapq = circuit.gates[i].swapped;
      PopulateGradientTwoEigen(
          metadata[i].create_f2, metadata[i].symbol_values[0], i,
          swapq ? circuit.gates[i].qubits[1] : circuit.gates[i].qubits[0],
          swapq ? circuit.gates[i].qubits[0] : circuit.gates[i].qubits[1],
          metadata[i].gate_params[0], metadata[i].gate_params[1],
          metadata[i].gate_params[2], &grad);
      grad_gates->push_back(grad);
    }

    // PhasedX
    else if (circuit.gates[i].kind == qsim::Cirq::GateKind::kPhasedXPowGate) {
      // Process potentially several symbols.
      for (int j = 0; j < metadata[i].symbol_values.size(); j++) {
        if (metadata[i].placeholder_names[j] ==
            GateParamNames::kPhaseExponent) {
          PopulateGradientPhasedXPhasedExponent(
              metadata[i].symbol_values[j], i, circuit.gates[i].qubits[0],
              metadata[i].gate_params[0], metadata[i].gate_params[1],
              metadata[i].gate_params[2], metadata[i].gate_params[3],
              metadata[i].gate_params[4], &grad);
        } else if (metadata[i].placeholder_names[j] ==
                   GateParamNames::kExponent) {
          PopulateGradientPhasedXExponent(
              metadata[i].symbol_values[j], i, circuit.gates[i].qubits[0],
              metadata[i].gate_params[0], metadata[i].gate_params[1],
              metadata[i].gate_params[2], metadata[i].gate_params[3],
              metadata[i].gate_params[4], &grad);
        }
      }
      grad_gates->push_back(grad);
    }

    // Fsim
    else if (circuit.gates[i].kind == qsim::Cirq::GateKind::kFSimGate) {
      // Process potentially several symbols.

      bool swapq = circuit.gates[i].swapped;
      for (int j = 0; j < metadata[i].symbol_values.size(); j++) {
        if (metadata[i].placeholder_names[j] == GateParamNames::kTheta) {
          PopulateGradientFsimTheta(
              metadata[i].symbol_values[j], i,
              swapq ? circuit.gates[i].qubits[1] : circuit.gates[i].qubits[0],
              swapq ? circuit.gates[i].qubits[0] : circuit.gates[i].qubits[1],
              metadata[i].gate_params[0], metadata[i].gate_params[1],
              metadata[i].gate_params[2], metadata[i].gate_params[3], &grad);
        } else if (metadata[i].placeholder_names[j] == GateParamNames::kPhi) {
          PopulateGradientFsimPhi(
              metadata[i].symbol_values[j], i,
              swapq ? circuit.gates[i].qubits[1] : circuit.gates[i].qubits[0],
              swapq ? circuit.gates[i].qubits[0] : circuit.gates[i].qubits[1],
              metadata[i].gate_params[0], metadata[i].gate_params[1],
              metadata[i].gate_params[2], metadata[i].gate_params[3], &grad);
        }
      }
      grad_gates->push_back(grad);
    }

    // PhasedISwap
    else if (circuit.gates[i].kind ==
             qsim::Cirq::GateKind::kPhasedISwapPowGate) {
      // Process potentially several symbols.
      bool swapq = circuit.gates[i].swapped;
      for (int j = 0; j < metadata[i].symbol_values.size(); j++) {
        if (metadata[i].placeholder_names[j] ==
            GateParamNames::kPhaseExponent) {
          PopulateGradientPhasedISwapPhasedExponent(
              metadata[i].symbol_values[j], i,
              swapq ? circuit.gates[i].qubits[1] : circuit.gates[i].qubits[0],
              swapq ? circuit.gates[i].qubits[0] : circuit.gates[i].qubits[1],
              metadata[i].gate_params[0], metadata[i].gate_params[1],
              metadata[i].gate_params[2], metadata[i].gate_params[3], &grad);

        } else if (metadata[i].placeholder_names[j] ==
                   GateParamNames::kExponent) {
          PopulateGradientPhasedISwapExponent(
              metadata[i].symbol_values[j], i,
              swapq ? circuit.gates[i].qubits[1] : circuit.gates[i].qubits[0],
              swapq ? circuit.gates[i].qubits[0] : circuit.gates[i].qubits[1],
              metadata[i].gate_params[0], metadata[i].gate_params[1],
              metadata[i].gate_params[2], metadata[i].gate_params[3], &grad);
        }
      }
      grad_gates->push_back(grad);
    }
  }

  // Produce partial fuses around the gradient gates.
  auto fuser = qsim::BasicGateFuser<qsim::IO, QsimGate>();
  auto left = circuit.gates.begin();
  auto right = left;

  partial_fuses->assign(grad_gates->size() + 1,
                        std::vector<qsim::GateFused<QsimGate>>({}));
  for (int i = 0; i < grad_gates->size(); i++) {
    right = circuit.gates.begin() + (*grad_gates)[i].index;
    (*partial_fuses)[i] =
        fuser.FuseGates(qsim::BasicGateFuser<qsim::IO, QsimGate>::Parameter(),
                        circuit.num_qubits, left, right);
    left = right + 1;
  }
  right = circuit.gates.end();
  (*partial_fuses)[grad_gates->size()] =
      fuser.FuseGates(qsim::BasicGateFuser<qsim::IO, QsimGate>::Parameter(),
                      circuit.num_qubits, left, right);
}