26 #ifndef N_Qubit_Decomposition_Tree_Search_H 27 #define N_Qubit_Decomposition_Tree_Search_H 50 std::vector<std::vector<int>>
q;
60 const std::vector<std::vector<int>>& cuts,
62 : num_qubits_(num_qubits), num_edges_(static_cast<
int>(
topology.size())), cuts_(cuts) {
66 int solve_min_cnots(
const std::vector<std::pair<int, double> >& cut_bounds,
int max_total = -1)
const {
71 if (cut_bounds.size() != cuts_.size()) {
72 throw std::invalid_argument(
"cut_bounds size must match cuts size");
75 std::vector<int> edge_counts(num_edges_, 0);
77 for (
int total = 0;; ++total) {
78 if (max_total >= 0 && total > max_total) {
81 if (feasible_for_some_composition(total, edge_counts, cut_bounds, 0, 0)) {
86 int solve_min_cnots(
const std::vector<std::pair<int, double> >& cut_bounds,
double& best_kappa, std::vector<int>& best_edge_counts,
int max_total = -1)
const {
87 if (cut_bounds.size() != cuts_.size()) {
88 throw std::invalid_argument(
"cut_bounds size must match cuts size");
90 best_edge_counts.clear();
91 best_kappa = std::numeric_limits<double>::infinity();
93 std::vector<int> edge_counts(num_edges_, 0);
95 for (
int total = 0;; ++total) {
96 if (max_total >= 0 && total > max_total) {
99 if (best_feasible_for_some_composition(total, edge_counts, cut_bounds, 0, 0, best_kappa, best_edge_counts)) {
107 std::vector<std::vector<int>>
cuts_;
111 cut_to_edges_.clear();
112 cut_to_edges_.reserve(cuts_.size());
114 for (
const std::vector<int>& cut : cuts_) {
115 std::vector<char> in_cut(num_qubits_, 0);
120 std::vector<int> crossing_edges;
121 crossing_edges.reserve(
topology.size());
123 for (
int i = 0; i < static_cast<int>(
topology.size()); ++i) {
125 crossing_edges.push_back(i);
128 cut_to_edges_.push_back(std::move(crossing_edges));
133 const std::vector<std::pair<int, double> >& cut_bounds)
const {
134 for (
int c = 0; c < static_cast<int>(cut_to_edges_.size()); ++c) {
135 const int bound = cut_bounds[c].first;
136 if (bound <= 0)
continue;
139 for (
int edge_idx : cut_to_edges_[c]) {
140 sum += edge_counts[edge_idx];
141 if (sum >= bound)
break;
151 std::vector<int>& edge_counts,
152 const std::vector<std::pair<int, double> >& cut_bounds,
154 int used_sum)
const {
155 const int m =
static_cast<int>(edge_counts.size());
158 edge_counts[pos] = total - used_sum;
159 return composition_satisfies(edge_counts, cut_bounds);
162 const int remaining = total - used_sum;
163 for (
int x = 0;
x <= remaining; ++
x) {
164 edge_counts[pos] =
x;
165 if (feasible_for_some_composition(total, edge_counts, cut_bounds, pos + 1, used_sum +
x)) {
173 std::vector<int>& edge_counts,
174 const std::vector<std::pair<int, double>>& cut_bounds,
178 std::vector<int>& best_edge_counts)
const 180 const bool use_surplus =
true;
181 const int m =
static_cast<int>(edge_counts.size());
184 edge_counts[pos] = total - used_sum;
186 if (!composition_satisfies(edge_counts, cut_bounds)) {
192 double kappa_obj = 0.0;
193 for (
int c = 0; c < static_cast<int>(cut_to_edges_.size()); ++c) {
195 for (
int edge_idx : cut_to_edges_[c]) {
196 coverage += edge_counts[edge_idx];
199 const int surplus = coverage - cut_bounds[c].first;
200 kappa_obj += cut_bounds[c].second *
static_cast<double>(surplus);
202 kappa_obj += cut_bounds[c].second *
static_cast<double>(coverage);
206 if (kappa_obj < best_kappa) {
207 best_kappa = kappa_obj;
208 best_edge_counts = edge_counts;
214 const int remaining = total - used_sum;
215 for (
int x = 0;
x <= remaining; ++
x) {
216 edge_counts[pos] =
x;
217 if (best_feasible_for_some_composition(
218 total, edge_counts, cut_bounds, pos + 1, used_sum +
x,
219 best_kappa, best_edge_counts)) {
228 std::vector<std::tuple<int, double, std::vector<int>, std::vector<std::pair<int, double>>>>
osr_results;
232 return std::get<0>(*std::min_element(osr_results.begin(), osr_results.end(),
233 [](
const std::tuple<int, double, std::vector<int>, std::vector<std::pair<int, double>>>& a,
const std::tuple<int, double, std::vector<int>, std::vector<std::pair<int, double>>>& b) {
234 return std::get<0>(a) < std::get<0>(b);
237 const std::tuple<int, double, std::vector<int>, std::vector<std::pair<int, double>>>&
get_best_osr_result()
const {
238 return *std::min_element(osr_results.begin(), osr_results.end(),
239 [](
const std::tuple<int, double, std::vector<int>, std::vector<std::pair<int, double>>>& a,
const std::tuple<int, double, std::vector<int>, std::vector<std::pair<int, double>>>& b) {
240 if (std::get<0>(a) != std::get<0>(b))
return std::get<0>(a) < std::get<0>(b);
241 if (std::get<1>(a) != std::get<1>(b))
return std::get<1>(a) < std::get<1>(b);
242 double a_sum = std::accumulate(std::get<3>(a).begin(), std::get<3>(a).end(), 0.0, [&a](
double c,
const std::pair<int, double>& d){
return c + d.first * std::get<3>(a).size() + d.second; });
243 double b_sum = std::accumulate(std::get<3>(b).begin(), std::get<3>(b).end(), 0.0, [&b](
double c,
const std::pair<int, double>& d){
return c + d.first * std::get<3>(b).size() + d.second; });
244 return a_sum < b_sum;
257 const std::tuple<int, double, std::vector<int>, std::vector<std::pair<int, double>>>& best_osr = get_best_osr_result();
258 const std::tuple<int, double, std::vector<int>, std::vector<std::pair<int, double>>>& other_best_osr = other.
get_best_osr_result();
259 if (std::get<0>(best_osr) != std::get<0>(other_best_osr))
260 return std::get<0>(best_osr) > std::get<0>(other_best_osr);
261 if (std::get<1>(best_osr) != std::get<1>(other_best_osr))
262 return std::get<1>(best_osr) > std::get<1>(other_best_osr);
263 return std::accumulate(std::get<3>(best_osr).begin(), std::get<3>(best_osr).end(), 0.0, [&best_osr](
double c,
const std::pair<int, double>& d){
return c + d.first * std::get<3>(best_osr).size() + d.second; }) >
264 std::accumulate(std::get<3>(other_best_osr).begin(), std::get<3>(other_best_osr).end(), 0.0, [&other_best_osr](
double c,
const std::pair<int, double>& d){
return c + d.first * std::get<3>(other_best_osr).size() + d.second; });
296 CutInfo(std::vector<std::vector<int>> all_cuts,
MinCnotBoundSolver osr_bound_solver) : all_cuts(
std::move(all_cuts)), osr_bound_solver(
std::move(osr_bound_solver)) {}
348 int accelerator_num = 0);
354 int accelerator_num = 0);
367 std::map<std::string, Config_Element>&
config,
int accelerator_num = 0);
373 std::map<std::string, Config_Element>&
config,
int accelerator_num = 0);
386 virtual void start_decomposition();
420 std::vector<std::vector<int>>& all_cuts,
double Fnorm,
double osr_tol,
421 std::uniform_real_distribution<>& distrib_real, std::mt19937& gen,
452 GrayCodeCNOT tree_search_over_gate_structures_best_first();
472 void set_unitary(
Matrix& Umtx_new);
std::vector< std::tuple< int, double, std::vector< int >, std::vector< std::pair< int, double > > > > osr_results
Copyright 2021 Budapest Quantum Computing Group.
std::map< GrayCodeCNOT, SearchNode > prefixes
Map from Gray code sequences to their OSR result pairs (rank, cost) for different cuts...
MinCnotBoundSolver osr_bound_solver
Map from CNOT pair (target, control) to the indices of cuts that are affected by this pair...
bool operator<(const SearchNode &other) const
bool operator>(const SearchNode &other) const
CutInfo(std::vector< std::vector< int >> all_cuts, MinCnotBoundSolver osr_bound_solver)
std::set< std::vector< int > > visited
Set of visited states (represented as vectors of integers)
int level_limit_min
The minimal number of adaptive layers used in the decomposition.
A base class to determine the decomposition of an N-qubit unitary into a sequence of CNOT and U3 gate...
Header file for Grey code container.
matrix_base< int > possible_target_qbits
List of possible target qubits according to the topology – paired up with possible control qubits...
std::vector< std::vector< int > > q
Queue of states to be processed in the next BFS level.
std::vector< std::vector< int > > cuts_
std::map< GrayCodeCNOT, SearchNode > prefixes
Map of GrayCodeCNOT to OSR (Operator Schmidt Rank) result pairs.
int level_limit
The maximal number of adaptive layers used in the decomposition.
std::vector< std::vector< int > > all_cuts
Vector of all possible qubit cuts, where each cut is represented as a vector of qubit indices...
A base class to determine the decomposition of an N-qubit unitary into a sequence of CNOT and U3 gate...
MinCnotBoundSolver(int num_qubits, const std::vector< std::vector< int >> &cuts, const std::vector< matrix_base< int >> &topology)
SearchNode(GrayCodeCNOT path)
std::vector< matrix_base< int > > topology
A vector of index pairs encoding the connectivity between the qubits.
matrix_base< int > possible_control_qbits
List of possible control qubits according to the topology – paired up with possible target qubits...
virtual void add_finalyzing_layer()
Call to add further layer to the gate structure used in the subdecomposition.
Structure containing the result of tree search over gate structures with Gray code and level informat...
A base class to determine the decomposition of an N-qubit unitary into a sequence of CNOT and U3 gate...
LevelInfo level_info
Updated LevelInfo containing visited states and sequence pairs.
int solve_min_cnots(const std::vector< std::pair< int, double > > &cut_bounds, double &best_kappa, std::vector< int > &best_edge_counts, int max_total=-1) const
Structure containing level information for breadth-first search over gate structures.
std::map< std::vector< int >, GrayCodeCNOT > seq_pairs_of
Map from state vectors to their corresponding Gray code sequences.
int solve_min_cnots(const std::vector< std::pair< int, double > > &cut_bounds, int max_total=-1) const
Double-precision complex matrix (float64).
A class responsible for grouping two-qubit (CNOT,CZ,CH) and one-qubit gates into layers.
Single-precision complex matrix (float32).
dictionary gate_structure
int get_min_cnots() const
std::vector< GrayCodeCNOT > solutions
Vector of successful Gray-code solutions.
const std::tuple< int, double, std::vector< int >, std::vector< std::pair< int, double > > > & get_best_osr_result() const
bool best_feasible_for_some_composition(int total, std::vector< int > &edge_counts, const std::vector< std::pair< int, double >> &cut_bounds, int pos, int used_sum, double &best_kappa, std::vector< int > &best_edge_counts) const
std::vector< std::vector< int > > cut_to_edges_
Structure containing cut information for operator Schmidt rank (OSR) analysis.
bool feasible_for_some_composition(int total, std::vector< int > &edge_counts, const std::vector< std::pair< int, double > > &cut_bounds, int pos, int used_sum) const
void build_cut_to_edges(const std::vector< matrix_base< int >> &topology)
Class to store data of complex arrays and its properties.
bool composition_satisfies(const std::vector< int > &edge_counts, const std::vector< std::pair< int, double > > &cut_bounds) const