Skip to content

Commit 09bbc6b

Browse files
authored
[Optimizer] Add an option to store each circuit transformation step (#174)
* [Optimizer] Add an option to store each circuit transformation step * Update test_optimization to test optimization steps * Fix ECC Set name * Rename to optimization_steps and let it able to run from any dir * Fix bugs
1 parent 3014047 commit 09bbc6b

8 files changed

+170
-97
lines changed

src/quartz/circuitseq/circuitseq.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,8 @@ bool CircuitSeq::less_than(const CircuitSeq &other) const {
187187
// so this circuit is greater.
188188
return false;
189189
}
190-
assert(this_ptr->output_gates->size() == 1);
191-
assert(other_ptr->output_gates->size() == 1);
190+
assert(this_ptr->output_gates.size() == 1);
191+
assert(other_ptr->output_gates.size() == 1);
192192
auto this_gate = this_ptr->output_gates[0];
193193
auto other_gate = other_ptr->output_gates[0];
194194
if (!compare_outcome.has_value()) {

src/quartz/tasograph/tasograph.cpp

+108-32
Original file line numberDiff line numberDiff line change
@@ -1557,7 +1557,8 @@ void Graph::draw_circuit(const std::string &src_file_name,
15571557
std::shared_ptr<Graph>
15581558
Graph::greedy_optimize(Context *ctx, const std::string &equiv_file_name,
15591559
bool print_message,
1560-
std::function<float(Graph *)> cost_function) {
1560+
std::function<float(Graph *)> cost_function,
1561+
const std::string &store_all_steps_file_prefix) {
15611562
if (cost_function == nullptr) {
15621563
cost_function = [](Graph *graph) { return graph->total_cost(); };
15631564
}
@@ -1607,6 +1608,11 @@ Graph::greedy_optimize(Context *ctx, const std::string &equiv_file_name,
16071608
bool optimized_in_this_iteration;
16081609
std::vector<Op> all_nodes;
16091610
optimized_graph->topology_order_ops(all_nodes);
1611+
int step_count = 0;
1612+
if (!store_all_steps_file_prefix.empty()) {
1613+
to_qasm(store_all_steps_file_prefix + "0.qasm", /*print_result=*/false,
1614+
/*print_guid=*/false);
1615+
}
16101616
do {
16111617
optimized_in_this_iteration = false;
16121618
for (auto xfer : xfers) {
@@ -1623,6 +1629,13 @@ Graph::greedy_optimize(Context *ctx, const std::string &equiv_file_name,
16231629
optimized_graph->topology_order_ops(all_nodes);
16241630
optimized_this_xfer = true;
16251631
optimized_in_this_iteration = true;
1632+
if (!store_all_steps_file_prefix.empty()) {
1633+
step_count++;
1634+
optimized_graph->to_qasm(store_all_steps_file_prefix +
1635+
std::to_string(step_count) + ".qasm",
1636+
/*print_result=*/false,
1637+
/*print_guid=*/false);
1638+
}
16261639
// Since |all_nodes| has changed, we cannot continue this loop.
16271640
break;
16281641
}
@@ -1638,6 +1651,14 @@ Graph::greedy_optimize(Context *ctx, const std::string &equiv_file_name,
16381651
<< " to " << optimized_cost << std::endl;
16391652
}
16401653

1654+
if (!store_all_steps_file_prefix.empty()) {
1655+
// Store the number of steps.
1656+
std::ofstream fout(store_all_steps_file_prefix + ".txt");
1657+
assert(fout.is_open());
1658+
fout << step_count << std::endl;
1659+
fout.close();
1660+
}
1661+
16411662
return optimized_graph;
16421663
}
16431664

@@ -1901,7 +1922,8 @@ std::shared_ptr<Graph>
19011922
Graph::optimize(Context *ctx, const std::string &equiv_file_name,
19021923
const std::string &circuit_name, bool print_message,
19031924
std::function<float(Graph *)> cost_function,
1904-
double cost_upper_bound, double timeout) {
1925+
double cost_upper_bound, double timeout,
1926+
const std::string &store_all_steps_file_prefix) {
19051927
if (cost_function == nullptr) {
19061928
cost_function = [](Graph *graph) { return graph->total_cost(); };
19071929
}
@@ -1947,24 +1969,22 @@ Graph::optimize(Context *ctx, const std::string &equiv_file_name,
19471969
if (cost_upper_bound == -1) {
19481970
cost_upper_bound = total_cost() * 1.05;
19491971
}
1950-
auto log_file_name =
1951-
equiv_file_name.substr(0, std::max(0, (int)equiv_file_name.size() - 21)) +
1952-
circuit_name + ".log";
19531972
auto preprocessed_graph =
1954-
greedy_optimize(ctx, equiv_file_name, print_message, cost_function);
1955-
// return preprocessed_graph->optimize(xfers, cost_upper_bound,
1956-
// circuit_name,
1957-
// log_file_name, print_message,
1958-
// cost_function, timeout);
1959-
return preprocessed_graph->optimize(xfers, cost_upper_bound, circuit_name, "",
1960-
print_message, cost_function, timeout);
1973+
greedy_optimize(ctx, equiv_file_name, print_message, cost_function,
1974+
store_all_steps_file_prefix);
1975+
return preprocessed_graph->optimize(
1976+
xfers, cost_upper_bound, circuit_name, /*log_file_name=*/"",
1977+
print_message, cost_function, timeout, store_all_steps_file_prefix,
1978+
/*continue_storing_all_steps=*/true);
19611979
}
19621980

19631981
std::shared_ptr<Graph>
19641982
Graph::optimize(const std::vector<GraphXfer *> &xfers, double cost_upper_bound,
19651983
const std::string &circuit_name,
19661984
const std::string &log_file_name, bool print_message,
1967-
std::function<float(Graph *)> cost_function, double timeout) {
1985+
std::function<float(Graph *)> cost_function, double timeout,
1986+
const std::string &store_all_steps_file_prefix,
1987+
bool continue_storing_all_steps) {
19681988
if (cost_function == nullptr) {
19691989
cost_function = [](Graph *graph) { return graph->total_cost(); };
19701990
}
@@ -1985,11 +2005,27 @@ Graph::optimize(const std::vector<GraphXfer *> &xfers, double cost_upper_bound,
19852005
if (print_message) {
19862006
if (!log_file_name.empty()) {
19872007
fout = fopen(log_file_name.c_str(), "w");
2008+
assert(fout);
19882009
} else {
19892010
fout = stdout;
19902011
}
19912012
}
19922013

2014+
// Information necessary to store each step
2015+
std::unordered_map<Graph *, std::shared_ptr<Graph>> previous_graph;
2016+
int step_count = 0;
2017+
if (!store_all_steps_file_prefix.empty()) {
2018+
if (continue_storing_all_steps) {
2019+
std::ifstream fin(store_all_steps_file_prefix + ".txt");
2020+
assert(fin.is_open());
2021+
fin >> step_count;
2022+
fin.close();
2023+
} else {
2024+
to_qasm(store_all_steps_file_prefix + "0.qasm", /*print_result=*/false,
2025+
/*print_guid=*/false);
2026+
}
2027+
}
2028+
19932029
// TODO: make these numbers configurable
19942030
constexpr int kMaxNumCandidates = 2000;
19952031
constexpr int kShrinkToNumCandidates = 1000;
@@ -2009,6 +2045,11 @@ Graph::optimize(const std::vector<GraphXfer *> &xfers, double cost_upper_bound,
20092045
cost_count[cost_function(candidate.get())]++;
20102046
if (new_candidates.size() < kShrinkToNumCandidates) {
20112047
new_candidates.push(candidate);
2048+
} else {
2049+
if (!store_all_steps_file_prefix.empty()) {
2050+
// no need to record history of removed graphs
2051+
previous_graph.erase(candidate.get());
2052+
}
20122053
}
20132054
candidates.pop();
20142055
}
@@ -2030,6 +2071,7 @@ Graph::optimize(const std::vector<GraphXfer *> &xfers, double cost_upper_bound,
20302071
}
20312072
};
20322073

2074+
bool hit_timeout = false;
20332075
while (!candidates.empty()) {
20342076
auto graph = candidates.top();
20352077
candidates.pop();
@@ -2048,7 +2090,8 @@ Graph::optimize(const std::vector<GraphXfer *> &xfers, double cost_upper_bound,
20482090
timeout) {
20492091
std::cout << "Timeout. Program terminated. Best cost is " << best_cost
20502092
<< std::endl;
2051-
return best_graph;
2093+
hit_timeout = true;
2094+
break;
20522095
}
20532096
if (new_graph == nullptr)
20542097
continue;
@@ -2057,33 +2100,66 @@ Graph::optimize(const std::vector<GraphXfer *> &xfers, double cost_upper_bound,
20572100
auto new_cost = cost_function(new_graph.get());
20582101
if (new_cost > cost_upper_bound)
20592102
continue;
2060-
if (hashmap.find(new_hash) == hashmap.end()) {
2061-
hashmap.insert(new_hash);
2062-
candidates.push(new_graph);
2063-
if (candidates.size() > kMaxNumCandidates) {
2064-
shrink_candidates();
2065-
}
2066-
if (new_cost < best_cost) {
2067-
best_cost = new_cost;
2068-
best_graph = new_graph;
2069-
}
2070-
} else
2103+
if (hashmap.find(new_hash) != hashmap.end()) {
20712104
continue;
2105+
}
2106+
hashmap.insert(new_hash);
2107+
candidates.push(new_graph);
2108+
if (!store_all_steps_file_prefix.empty()) {
2109+
// record history
2110+
previous_graph[new_graph.get()] = graph;
2111+
}
2112+
if (candidates.size() > kMaxNumCandidates) {
2113+
shrink_candidates();
2114+
}
2115+
if (new_cost < best_cost) {
2116+
best_cost = new_cost;
2117+
best_graph = new_graph;
2118+
}
20722119
}
2120+
if (hit_timeout) {
2121+
break;
2122+
}
2123+
}
2124+
if (hit_timeout) {
2125+
break;
20732126
}
20742127

20752128
auto end = std::chrono::steady_clock::now();
20762129
if (print_message) {
2077-
fprintf(fout,
2078-
"[%s] Best cost: %f\tcandidate number: %d\tafter %.3f seconds.\n",
2079-
circuit_name.c_str(), best_cost, candidates.size(),
2080-
(double)std::chrono::duration_cast<std::chrono::milliseconds>(
2081-
end - start)
2082-
.count() /
2083-
1000.0);
2130+
fprintf(
2131+
fout,
2132+
"[%s] Best cost: %f\tcandidate number: %zu\tafter %.3f seconds.\n",
2133+
circuit_name.c_str(), best_cost, candidates.size(),
2134+
(double)std::chrono::duration_cast<std::chrono::milliseconds>(end -
2135+
start)
2136+
.count() /
2137+
1000.0);
20842138
fflush(fout);
20852139
}
20862140
}
2141+
2142+
if (!store_all_steps_file_prefix.empty()) {
2143+
std::vector<Graph *> steps(1, best_graph.get());
2144+
while (previous_graph.count(steps.back()) > 0) {
2145+
// there is a previous graph
2146+
steps.push_back(previous_graph[steps.back()].get());
2147+
}
2148+
// no need to save the initial graph again
2149+
for (int i = (int)steps.size() - 2; i >= 0; i--) {
2150+
step_count++;
2151+
steps[i]->to_qasm(store_all_steps_file_prefix +
2152+
std::to_string(step_count) + ".qasm",
2153+
/*print_result=*/false,
2154+
/*print_guid=*/false);
2155+
}
2156+
2157+
// Store the number of steps.
2158+
std::ofstream fout_step(store_all_steps_file_prefix + ".txt");
2159+
fout_step << step_count << std::endl;
2160+
fout_step.close();
2161+
}
2162+
20872163
return best_graph;
20882164
}
20892165

src/quartz/tasograph/tasograph.h

+23-11
Original file line numberDiff line numberDiff line change
@@ -208,12 +208,14 @@ class Graph {
208208
* @param equiv_file_name The ECC set file name.
209209
* @param print_message To output the log to the screen or not.
210210
* @param cost_function The cost function.
211+
* @param store_all_steps_file_prefix If not empty, store each circuit
212+
* transformation step in a file with the corresponding file prefix.
211213
* @return The optimized circuit.
212214
*/
213-
std::shared_ptr<Graph>
214-
greedy_optimize(Context *ctx, const std::string &equiv_file_name,
215-
bool print_message,
216-
std::function<float(Graph *)> cost_function = nullptr);
215+
std::shared_ptr<Graph> greedy_optimize(
216+
Context *ctx, const std::string &equiv_file_name, bool print_message,
217+
std::function<float(Graph *)> cost_function = nullptr,
218+
const std::string &store_all_steps_file_prefix = std::string());
217219
std::shared_ptr<Graph>
218220
optimize_legacy(float alpha, int budget, bool print_subst, Context *ctx,
219221
const std::string &equiv_file_name,
@@ -223,7 +225,7 @@ class Graph {
223225
int timeout = 86400 /*1 day*/);
224226

225227
/**
226-
* Optimize this circuit.
228+
* Optimize this circuit with a greedy phase at the beginning.
227229
* @param ctx The context variable.
228230
* @param equiv_file_name The ECC set file name.
229231
* @param circuit_name The circuit name shown in the log.
@@ -232,17 +234,20 @@ class Graph {
232234
* file name.
233235
* @param cost_function The cost function for the search.
234236
* @param cost_upper_bound The maximum cost of the circuits to be searched.
235-
* @param timeout Timeout in seconds.
237+
* @param timeout Timeout in seconds, for the search phase.
238+
* @param store_all_steps_file_prefix If not empty, store each circuit
239+
* transformation step in a file with the corresponding file prefix.
236240
* @return The optimized circuit.
237241
*/
238242
std::shared_ptr<Graph>
239243
optimize(Context *ctx, const std::string &equiv_file_name,
240244
const std::string &circuit_name, bool print_message,
241245
std::function<float(Graph *)> cost_function = nullptr,
242246
double cost_upper_bound = -1 /*default = current cost * 1.05*/,
243-
double timeout = 3600 /*1 hour*/);
247+
double timeout = 3600 /*1 hour*/,
248+
const std::string &store_all_steps_file_prefix = std::string());
244249
/**
245-
* Optimize this circuit.
250+
* Optimize this circuit without a greedy phase.
246251
* @param xfers The circuit transformations.
247252
* @param cost_upper_bound The maximum cost of the circuits to be searched.
248253
* @param circuit_name The circuit name shown in the log.
@@ -251,19 +256,26 @@ class Graph {
251256
* @param print_message To output the log or not.
252257
* @param cost_function The cost function for the search.
253258
* @param timeout Timeout in seconds.
259+
* @param store_all_steps_file_prefix If not empty, store each circuit
260+
* transformation step in a file with the corresponding file prefix.
261+
* @param continue_storing_all_steps If true, there was a greedy phase
262+
* before calling this function with the same |store_all_steps_file_prefix|.
263+
* We should continue the numbering in this case.
254264
* @return The optimized circuit.
255265
*/
256266
std::shared_ptr<Graph>
257267
optimize(const std::vector<GraphXfer *> &xfers, double cost_upper_bound,
258268
const std::string &circuit_name, const std::string &log_file_name,
259269
bool print_message,
260270
std::function<float(Graph *)> cost_function = nullptr,
261-
double timeout = 3600 /*1 hour*/);
271+
double timeout = 3600 /*1 hour*/,
272+
const std::string &store_all_steps_file_prefix = std::string(),
273+
bool continue_storing_all_steps = false);
262274
void constant_and_rotation_elimination();
263275
void rotation_merging(GateType target_rotation);
264-
std::string to_qasm(bool print_result = false, bool print_id = false) const;
276+
std::string to_qasm(bool print_result = false, bool print_guid = false) const;
265277
void to_qasm(const std::string &save_filename, bool print_result,
266-
bool print_id) const;
278+
bool print_guid) const;
267279
template <class _CharT, class _Traits>
268280
static std::shared_ptr<Graph>
269281
_from_qasm_stream(Context *ctx,

src/test/CMakeLists.txt

+2-4
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ file(GLOB_RECURSE TEST_CONTEXT_SHIFT "test_context_shift.cpp")
1010
file(GLOB_RECURSE TEST_EQUIV_SET "test_equivalence_set.cpp")
1111
file(GLOB_RECURSE TEST_GENERATOR "test_generator.cpp")
1212
file(GLOB_RECURSE TEST_GRAPH_TO_QASM "test_graph_to_qasm.cpp")
13-
file(GLOB_RECURSE TEST_OPTIMIZATION "test_optimization.cpp")
14-
file(GLOB_RECURSE TEST_OPTIMIZATION_SA "test_optimization_sa.cpp")
13+
file(GLOB_RECURSE TEST_OPTIMIZATION_STEPS "test_optimization_steps.cpp")
1514
file(GLOB_RECURSE TEST_PHASE_SHIFT "test_phase_shift.cpp")
1615
file(GLOB_RECURSE TEST_PRUNING "test_pruning.cpp")
1716
file(GLOB_RECURSE TEST_QUARTZ "test_quartz.cpp")
@@ -46,8 +45,7 @@ add_executable(test_context_shift ${TEST_CONTEXT_SHIFT} )
4645
add_executable(test_equiv_set ${TEST_EQUIV_SET} )
4746
add_executable(test_generator ${TEST_GENERATOR} )
4847
add_executable(test_graph_to_qasm ${TEST_GRAPH_TO_QASM} )
49-
add_executable(test_optimization ${TEST_OPTIMIZATION} )
50-
add_executable(test_optimization_sa ${TEST_OPTIMIZATION_SA} )
48+
add_executable(test_optimization_steps ${TEST_OPTIMIZATION_STEPS} )
5149
add_executable(test_phase_shift ${TEST_PHASE_SHIFT} )
5250
add_executable(test_pruning ${TEST_PRUNING} )
5351
add_executable(test_quartz ${TEST_QUARTZ} )

src/test/test_optimization.cpp

-20
This file was deleted.

src/test/test_optimization_sa.cpp

-24
This file was deleted.

0 commit comments

Comments
 (0)