From 79baf2bf29f1fb1ededfcc12cf0bef71701f1063 Mon Sep 17 00:00:00 2001
From: Alex McCaskey <mccaskeyaj@ornl.gov>
Date: Fri, 3 Feb 2017 19:15:47 +0000
Subject: [PATCH] Adding ability for Scaffold compiler and QasmToGraph util to
 handle conditional statements based on measured qubits

---
 .../gate/compilers/scaffold/ScaffCCAPI.hpp    |   2 +-
 .../compilers/scaffold/ScaffoldCompiler.cpp   |  52 +++++
 .../compilers/scaffold/ScaffoldCompiler.hpp   |   7 +
 .../scaffold/tests/ScaffoldCompilerTester.cpp |  23 +--
 quantum/gate/utils/QasmToGraph.hpp            | 195 ++++++++++++------
 5 files changed, 199 insertions(+), 80 deletions(-)

diff --git a/quantum/gate/compilers/scaffold/ScaffCCAPI.hpp b/quantum/gate/compilers/scaffold/ScaffCCAPI.hpp
index 3f46a3b7f..0cbecadec 100644
--- a/quantum/gate/compilers/scaffold/ScaffCCAPI.hpp
+++ b/quantum/gate/compilers/scaffold/ScaffCCAPI.hpp
@@ -71,7 +71,7 @@ public:
 			tempSrcFile.close();
 
 			// Execute the scaffold compiler
-			std::system("scaffcc -fp .tmpSrcFile.scaffold");
+			std::system("scaffcc -fp .tmpSrcFile.scaffold &> /dev/null");
 
 			// Remove the temporary source file, we don't need it anymore
 			std::remove(".tmpSrcFile.scaffold");
diff --git a/quantum/gate/compilers/scaffold/ScaffoldCompiler.cpp b/quantum/gate/compilers/scaffold/ScaffoldCompiler.cpp
index 15b938174..1c96502be 100644
--- a/quantum/gate/compilers/scaffold/ScaffoldCompiler.cpp
+++ b/quantum/gate/compilers/scaffold/ScaffoldCompiler.cpp
@@ -33,6 +33,7 @@
 #include <regex>
 #include "ScaffCCAPI.hpp"
 #include "QasmToGraph.hpp"
+#include <boost/algorithm/string.hpp>
 
 namespace xacc {
 
@@ -48,6 +49,38 @@ void ScaffoldCompiler::modifySource() {
 	kernelSource.erase(kernelSource.find("__qpu__"), 7);
 	kernelSource = std::string("module ") + kernelSource;
 
+	std::string qubitAllocationLine = "   qbit qreg[3];\n";
+
+	// conditional on measurements
+	// FIXME FOR NOW WE ONLY ACCEPT format
+	// 'if (creg[0] == 1) GATEOP'
+	int counter = 1;
+	std::vector<std::string> ifLines;
+	std::regex ifstmts("if\\s?\\(\\w+\\[\\w+\\]\\s?=.*\\s?\\)\\s?");
+	for (auto i = std::sregex_iterator(kernelSource.begin(), kernelSource.end(),
+			ifstmts); i != std::sregex_iterator(); ++i) {
+		std::cout << "HELLO WORLD: " << (*i).str() << "\n";
+		std::vector<std::string> splitVec;
+		std::string ifLine = (*i).str();
+		boost::trim(ifLine);
+		boost::split(splitVec, ifLine, boost::is_any_of(" "));
+		conditionalCodeSegments.push_back("module foo" + std::to_string(counter) + "() {\n"
+				+ qubitAllocationLine + "   " + splitVec[splitVec.size()-1] + ";\n}\nint main() {\n"
+						"   foo" + std::to_string(counter) + "();\n}");
+		counter++;
+
+		ifLines.push_back(ifLine + ";\n");
+	}
+
+	for (auto s : conditionalCodeSegments) {
+		std::cout << s << "\n";
+	}
+
+	for (auto s : ifLines) {
+		auto idx = kernelSource.find(s);
+		kernelSource.erase(idx, s.size());
+	}
+
 	// Get the kernel name
 	std::regex functionName("((\\w+)\\s*\\()\\s*");
 	auto begin = std::sregex_iterator(kernelSource.begin(), kernelSource.end(),
@@ -80,6 +113,25 @@ std::shared_ptr<IR> ScaffoldCompiler::compile() {
 	// Get the Qasm as a Graph...
 	auto circuitGraph = QasmToGraph::getCircuitGraph(qasm);
 
+	// HERE we have main circuit graph, before conditional
+	// if branches... So then for each conditional code statement,
+	// get its circuit graph and add it to the main graph after
+	// the addition of a COND conditional node that will enable the
+	// conditional nodes if the measured cbit is a 1.
+
+	// So a COND node needs to know the gate id of the measurement gate
+	// and the nodes to mark enabled if the measurement is a 1,
+	if (!conditionalCodeSegments.empty()) {
+		std::vector<qci::common::Graph<CircuitNode>> condGraphs;
+		for (auto cond : conditionalCodeSegments) {
+			auto condQasm = scaffcc.getFlatQASMFromSource(cond);
+			auto g = QasmToGraph::getCircuitGraph(condQasm);
+			condGraphs.push_back(g);
+		}
+
+		QasmToGraph::linkConditionalQasm(circuitGraph, condGraphs);
+	}
+
 	// Create a GraphIR instance from that graph
 	auto graphIR = std::make_shared<ScaffoldGraphIR>(circuitGraph);
 
diff --git a/quantum/gate/compilers/scaffold/ScaffoldCompiler.hpp b/quantum/gate/compilers/scaffold/ScaffoldCompiler.hpp
index be662f368..8c641ed51 100644
--- a/quantum/gate/compilers/scaffold/ScaffoldCompiler.hpp
+++ b/quantum/gate/compilers/scaffold/ScaffoldCompiler.hpp
@@ -80,6 +80,13 @@ public:
 	 */
 	virtual ~ScaffoldCompiler() {}
 
+protected:
+
+	/**
+	 * Reference to potential conditional code
+	 */
+	std::vector<std::string> conditionalCodeSegments;
+
 };
 
 }
diff --git a/quantum/gate/compilers/scaffold/tests/ScaffoldCompilerTester.cpp b/quantum/gate/compilers/scaffold/tests/ScaffoldCompilerTester.cpp
index 157973113..2cc2022be 100644
--- a/quantum/gate/compilers/scaffold/tests/ScaffoldCompilerTester.cpp
+++ b/quantum/gate/compilers/scaffold/tests/ScaffoldCompilerTester.cpp
@@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(checkSimpleCompile) {
 
 }
 
-BOOST_AUTO_TEST_CASE(checkAnotherSimpleCompile) {
+BOOST_AUTO_TEST_CASE(checkCodeWithMeasurementIf) {
 	using GraphType = Graph<CircuitNode>;
 
 	auto compiler =
@@ -79,18 +79,15 @@ BOOST_AUTO_TEST_CASE(checkAnotherSimpleCompile) {
 
 	const std::string src("__qpu__ teleport () {\n"
 						"   qbit qreg[3];\n"
+						"   cbit creg[2];\n"
 						"   H(qreg[1]);\n"
 						"   CNOT(qreg[1],qreg[2]);\n"
 						"   CNOT(qreg[0],qreg[1]);\n"
 						"   H(qreg[0]);\n"
-						"   MeasZ(qreg[0]);\n"
-						"   MeasZ(qreg[1]);\n"
-						"   // Cz\n"
-						"   H(qreg[2]);\n"
-						"   CNOT(qreg[2], qreg[1]);\n"
-						"   H(qreg[2]);\n"
-						"   // CX = CNOT\n"
-						"   CNOT(qreg[2], qreg[0]);\n"
+						"   creg[0] = MeasZ(qreg[0]);\n"
+						"   creg[1] = MeasZ(qreg[1]);\n"
+						"   if(creg[0] == 1) Z(qreg[2]);\n"
+						"   if (creg[1] == 1) X(qreg[2]);\n"
 						"}\n");
 
 	auto ir = compiler->compile(src);
@@ -98,10 +95,8 @@ BOOST_AUTO_TEST_CASE(checkAnotherSimpleCompile) {
 	auto graphir = std::dynamic_pointer_cast<xacc::GraphIR<GraphType>>(ir);
 	BOOST_VERIFY(graphir);
 
-	// I drew this out on paper, we should have 12
-	// nodes and 17 edges...
-	BOOST_VERIFY(graphir->order() == 12);
-	BOOST_VERIFY(graphir->size() == 17);
-
 	graphir->persist(std::cout);
+	BOOST_VERIFY(graphir->order() == 16);
+	BOOST_VERIFY(graphir->size() == 23);
+
 }
diff --git a/quantum/gate/utils/QasmToGraph.hpp b/quantum/gate/utils/QasmToGraph.hpp
index d6bbb05e2..f8193e974 100644
--- a/quantum/gate/utils/QasmToGraph.hpp
+++ b/quantum/gate/utils/QasmToGraph.hpp
@@ -33,39 +33,11 @@
 
 #include "Graph.hpp"
 #include <regex>
-#include <boost/unordered_map.hpp>
-#include <boost/assign/list_of.hpp>
 #include <boost/algorithm/string.hpp>
 
 namespace xacc {
 namespace quantum {
 
-using boost::assign::map_list_of;
-
-/**
- * Enumeration of gates we support
- */
-enum SupportedGates {
-	H, CNot, C_Z, C_X, Measure, X, Y, Z,
-	T, S, ZZ, SS, Swap, Toffoli, InitialState,
-	FinalState
-};
-
-/**
- * Create a string to SupportedGates mapping
- */
-const boost::unordered_map<std::string, SupportedGates> strToGate =
-		map_list_of("h", H)("cnot", CNot)("c_z", C_Z)("c_x", C_X)("measure",
-				Measure)("x", X)("y", Y)("z", Z)("t", T)("s", S)("zz", ZZ)("ss",
-				SS)("swap", Swap)("toffoli", Toffoli);
-
-/**
- * Create a SupportedGates to string mapping
- */
-const boost::unordered_map<SupportedGates, std::string> gateToStr =
-		map_list_of(H, "h")(CNot, "cnot")(C_Z, "c_z")(C_X, "c_x")(Measure,
-				"measure")(X, "x")(Y, "y")(Z, "z")(T, "t")(S, "s")(ZZ, "zz")(SS,
-						"ss")(Swap, "swap")(Toffoli, "toffoli")(FinalState, "FinalState")(InitialState, "InitialState");
 /**
  * CircuitNode subclasses QCIVertex to provide the following
  * parameters in the given order:
@@ -74,7 +46,7 @@ const boost::unordered_map<SupportedGates, std::string> gateToStr =
  * Qubit Ids that the gate acts on
  */
 class CircuitNode: public qci::common::QCIVertex<std::string, int, int,
-		std::vector<int>> {
+		std::vector<int>, bool> {
 public:
 	CircuitNode() :
 			QCIVertex() {
@@ -82,6 +54,11 @@ public:
 		propertyNames[1] = "Circuit Layer";
 		propertyNames[2] = "Gate Vertex Id";
 		propertyNames[3] = "Gate Acting Qubits";
+		propertyNames[4] = "Enabled";
+
+		// by default all circuit nodes
+		// are enabled and
+		std::get<4>(properties) = true;
 	}
 };
 
@@ -133,12 +110,10 @@ public:
 		// Set the number of qubits
 		nQubits = qubitVarNameToId.size();
 
-		std::cout << "Number of Qubits is " << nQubits << std::endl;
-
 		// First create a starting node for the initial
 		// wave function - it should have nQubits outgoing
 		// edges
-		graph.addVertex(gateToStr.at(SupportedGates::InitialState), 0, 0, allQbitIds);
+		graph.addVertex("InitialState", 0, 0, allQbitIds, true);
 
 		std::vector<CircuitNode> gateOperations;
 		for (auto line : qasmLines) {
@@ -157,7 +132,6 @@ public:
 				auto g = boost::to_lower_copy(gateCommand[0]);
 				boost::trim(g);
 				if (g == "measz") g = "measure";
-//				auto s = strToGate.at(g);
 				std::get<0>(node.properties) = g;
 
 				// If not a 2 qubit gate, and if the acting
@@ -198,10 +172,18 @@ public:
 		}
 
 		// Add a final layer for the graph sink
-		graph.addVertex(gateToStr.at(SupportedGates::FinalState), layer+1, gateId, allQbitIds);
+		CircuitNode finalNode;
+		std::get<0>(finalNode.properties) = "FinalState";
+		std::get<1>(finalNode.properties) = layer+1;
+		std::get<2>(finalNode.properties) = gateId;
+		std::get<3>(finalNode.properties) = allQbitIds;
+		std::get<4>(finalNode.properties) = true;
+
+		graph.addVertex(finalNode);
+		gateOperations.push_back(finalNode);
 
 		// Set how many layers are in this circuit
-		int maxLayer = layer;
+		int maxLayer = layer+1;
 
 		// Print info...
 		for (auto cn : gateOperations) {
@@ -217,14 +199,123 @@ public:
 			std::cout << "\n\n";
 		}
 
-		// Add an edge between the initial state node
-		// and the layer 1 gates
+		generateEdgesFromLayer(1, graph, gateOperations, 0);
+
+		return graph;
+	}
+
+	/**
+	 * Create connecting conditional nodes that link the main
+	 * circuit graph to subsequent conditional graphs. The conditional
+	 * nodes can be used by Accelerators to figure out if the condition
+	 * code should be executed or not.
+	 * s
+	 * @param mainGraph
+	 * @param conditionalGraphs
+	 */
+	static void linkConditionalQasm(qci::common::Graph<CircuitNode>& mainGraph,
+			std::vector<qci::common::Graph<CircuitNode>> conditionalGraphs) {
+
+		// At this point we have a main circuit graph,
+		// and one or more smaller conditional graphs (each with
+		// initial and final state nodes.
+
+		// We need to loop through the conditional graphs and
+		// pull out the gate vertices (skip initial final state nodes)
+		// and add them to the main graph after some conditional nodes
+
+		// NOTE We assume that the ith conditionalGraph corresponds to the ith
+		// measurement gate...
+		std::vector<int> measurementIds;
+		std::vector<std::vector<int>>measurementQubits;
+		for (int i = 0; i < mainGraph.order(); i++) {
+			if (mainGraph.getVertexProperty<0>(i) == "measure") {
+				measurementIds.push_back(mainGraph.getVertexProperty<2>(i));
+				measurementQubits.push_back(mainGraph.getVertexProperty<3>(i));
+			}
+		}
+
+		assert (measurementIds.size() == conditionalGraphs.size());
+
+		int counter = 0;
+		int finalIdMainGraph = mainGraph.getVertexProperty<2>(mainGraph.order() - 1);
+		int finalLayerMainGraph = mainGraph.getVertexProperty<1>(mainGraph.order() - 1);
+		int layer = finalLayerMainGraph + 1;
+		int id = finalIdMainGraph + 1;
+
+		// By default all conditional graph nodes should be disabled
+		// Conditional nodes should have acting qubits set to the
+		// measured qubit, and gate vertex id set to the measurement
+		// gate vertex id.
+		for (auto g : conditionalGraphs) {
+			CircuitNode node;
+			std::get<0>(node.properties) = "conditional";
+			std::get<2>(node.properties) = measurementIds[counter];
+			std::get<3>(node.properties) = measurementQubits[counter];
+			mainGraph.addVertex(node);
+
+			// Connect the main graph to the cond node
+			mainGraph.addEdge(finalIdMainGraph, id);
+
+			id++;
+
+			std::vector<CircuitNode> newGates;
+			int initialStateId, initialStateLayer;
+			for (int i = 0; i < g.order(); i++) {
+				CircuitNode newVert;
+				newVert.properties = g.getVertexProperties(i);
+				std::get<0>(newVert.properties) = g.getVertexProperty<0>(i);
+				std::get<1>(newVert.properties) = layer+g.getVertexProperty<1>(i);
+				std::get<2>(newVert.properties) = id;
+				std::get<3>(newVert.properties) = g.getVertexProperty<3>(i);
+				std::get<4>(newVert.properties) = false;
+				mainGraph.addVertex(newVert);
+				newGates.push_back(newVert);
+
+				// Connect conditional node with first node here
+				if(i == 0) mainGraph.addEdge(id - 1, id);
+				if(i == 0) initialStateId = id;
+				if (i == 0) initialStateLayer = std::get<1>(newVert.properties);
+
+				id++;
+			}
+
+			generateEdgesFromLayer(initialStateLayer+1, mainGraph, newGates, initialStateId);
+
+			// Set the new layer for the next conditional node
+			layer = mainGraph.getVertexProperty<1>(id - 1) + 1;
+			counter++;
+		}
+
+
+
+
+	}
+
+private:
+
+	/**
+	 * Generate all edges for the circuit graph starting at
+	 * the given layer.
+	 *
+	 * @param layer
+	 * @param graph
+	 * @param gateOperations
+	 * @param initialStateId
+	 */
+	static void generateEdgesFromLayer(const int layer,
+			qci::common::Graph<CircuitNode>& graph,
+			std::vector<CircuitNode>& gateOperations, int initialStateId) {
+
+		int nQubits = std::get<3>(graph.getVertexProperties(0)).size();
+		int maxLayer = std::get<1>(gateOperations[gateOperations.size() - 1].properties);
+
 		std::map<int,int> qubitToCurrentGateId;
 		for (int i = 0 ; i < nQubits; i++) {
-			qubitToCurrentGateId[i] = 0;
+			qubitToCurrentGateId[i] = initialStateId;
 		}
 
-		int currentLayer = 0;
+		int currentLayer = layer;
 		while (currentLayer <= maxLayer) {
 			std::vector<CircuitNode> currentLayerGates;
 			std::copy_if(gateOperations.begin(), gateOperations.end(),
@@ -242,33 +333,7 @@ public:
 
 			currentLayer++;
 		}
-
-		// Add a graph sink - ie a final node representing
-		// the final system state
-		int counter = 0;
-
-		// Walk the list downward, skip first node since is
-		// the graph sink
-		for (int i = gateOperations.size() - 1; i >= 0; i--) {
-			auto gate = gateOperations[i];
-			int currentGateId = std::get<2>(gate.properties);
-			int nQubitsActing = std::get<3>(gate.properties).size();
-			int gateDegree = graph.degree(currentGateId);
-			for (int j = gateDegree; j < 2 * nQubitsActing; j++) {
-				graph.addEdge(currentGateId, gateId);
-				counter++;
-			}
-
-			// Break early if we can...
-			if (counter == nQubits)
-				break;
-		}
-
-		return graph;
 	}
-
-private:
-
 	/**
 	 * This method determines if a new layer should be added to the circuit.
 	 *
-- 
GitLab