|
|
@@ -0,0 +1,528 @@ |
|
|
|
// graphsim.cpp |
|
|
|
|
|
|
|
#include "graphsim.h" |
|
|
|
|
|
|
|
#include <utility> |
|
|
|
#include <ctime> |
|
|
|
|
|
|
|
//!Random coin toss. Change here to insert your favorite RNG. |
|
|
|
int bool_rand (void) { |
|
|
|
return random () > RAND_MAX/2; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//! Instantiate a quantum register with 'numQubits' qubits, initally all in state |0>. |
|
|
|
/*! If randomize > -1 the RNG will be seeded with the current time plus the value |
|
|
|
of randomize. (Otherwise, it is not seeded.) That the value of randomize is |
|
|
|
added to the seed is useful in parallel processing settings where you want to |
|
|
|
ensure different seeds. (If you call this from Python, remember, that Python's |
|
|
|
RNG is not seeded.) */ |
|
|
|
GraphRegister::GraphRegister (VertexIndex numQubits, int randomize) |
|
|
|
: vertices (numQubits) |
|
|
|
{ |
|
|
|
if (randomize > -1) { |
|
|
|
srandom (time(NULL) + randomize); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//! Copy constructor |
|
|
|
/*! Clones a register */ |
|
|
|
GraphRegister::GraphRegister (GraphRegister& gr) |
|
|
|
: vertices (gr.vertices) |
|
|
|
{ |
|
|
|
} |
|
|
|
|
|
|
|
//! Add an edge to the graph underlying the state. |
|
|
|
void GraphRegister::add_edge (VertexIndex v1, VertexIndex v2) { |
|
|
|
assert (v1 != v2); |
|
|
|
vertices[v1].neighbors.insert (v2); |
|
|
|
vertices[v2].neighbors.insert (v1); |
|
|
|
//D cerr << "adding edge " << v1 << " - " << v2 << endl; |
|
|
|
} |
|
|
|
|
|
|
|
//! Delete an edge to the graph underlying the state. |
|
|
|
void GraphRegister::del_edge (VertexIndex v1, VertexIndex v2) { |
|
|
|
DBGOUT ("deling edge " << v1 << " - " << v2 << endl); |
|
|
|
vertices[v1].neighbors.erase (v2); |
|
|
|
vertices[v2].neighbors.erase (v1); |
|
|
|
} |
|
|
|
|
|
|
|
//! Toggle an edge to the graph underlying the state. |
|
|
|
/*! (i.e. add it if not present, and delete it if present.) */ |
|
|
|
void GraphRegister::toggle_edge (VertexIndex v1, VertexIndex v2) |
|
|
|
{ |
|
|
|
int n1 = vertices[v1].neighbors.erase (v2); |
|
|
|
if (n1 == 1) { |
|
|
|
vertices[v2].neighbors.erase (v1); |
|
|
|
} else { |
|
|
|
assert (n1 == 0); |
|
|
|
add_edge (v1, v2); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//! Create the Stabilizer of the state. |
|
|
|
/*! This is useful to print out the stabilizer (or to compare with CHP). |
|
|
|
You can also use print_stabilizer.*/ |
|
|
|
Stabilizer& GraphRegister::get_full_stabilizer (void) const |
|
|
|
{ |
|
|
|
hash_set<VertexIndex> all_qubits; |
|
|
|
for (VertexIterConst i = vertices.begin(); i != vertices.end(); i++) { |
|
|
|
all_qubits.insert (i-vertices.begin()); |
|
|
|
} |
|
|
|
Stabilizer *s = new Stabilizer (*this, all_qubits); |
|
|
|
return *s; |
|
|
|
} |
|
|
|
|
|
|
|
//! Prints out the description of the current state |
|
|
|
/*! in terms of adjacency lists of the graph and the VOps.*/ |
|
|
|
void GraphRegister::print_adj_list (ostream& os) const |
|
|
|
{ |
|
|
|
for (VertexIndex i = 0; i < vertices.size(); i++) { |
|
|
|
print_adj_list_line (os, i); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//! Prints the line for Vertex i in the adjacency list representation of the state. |
|
|
|
void GraphRegister::print_adj_list_line (ostream& os, VertexIndex i) const |
|
|
|
{ |
|
|
|
os << "Vertex " << i << ": VOp " |
|
|
|
<< vertices[i].byprod.get_name() << ", neighbors "; |
|
|
|
for (VtxIdxIterConst j = vertices[i].neighbors.begin(); |
|
|
|
j != vertices[i].neighbors.end(); j++) { |
|
|
|
os << *j << " "; |
|
|
|
} |
|
|
|
os << endl; |
|
|
|
} |
|
|
|
|
|
|
|
//! Print the current state in stabilizer representation. |
|
|
|
void GraphRegister::print_stabilizer (ostream &os) const |
|
|
|
{ |
|
|
|
get_full_stabilizer().print (os); |
|
|
|
} |
|
|
|
|
|
|
|
/*! Use the cphase look-up table. This is called by cphase after VOps that do not |
|
|
|
commute with the cphase gate have been removed as far as possible. */ |
|
|
|
void GraphRegister::cphase_with_table (VertexIndex v1, VertexIndex v2) |
|
|
|
{ |
|
|
|
// Structure of the table: |
|
|
|
// first index: whether there was an edge between the operands before |
|
|
|
// (0=no, 1= yes) |
|
|
|
// second and third index: byprod op of v1 and v2 |
|
|
|
// third index: information to obtain: |
|
|
|
// 0= whether after the cphase there is an edges |
|
|
|
// 1,2= new values of the byprod ops of v1 and v2Id |
|
|
|
static const unsigned short cphase_tbl[2][24][24][3] = |
|
|
|
#include "cphase.tbl" |
|
|
|
; |
|
|
|
#ifdef DEBUGOUTPUT |
|
|
|
DBGOUT ("cphase_with_table called on:\n"); |
|
|
|
print_adj_list_line (cout, v1); |
|
|
|
print_adj_list_line (cout, v2); |
|
|
|
#endif |
|
|
|
ConnectionInfo ci = getConnectionInfo (v1, v2); |
|
|
|
unsigned op1 = vertices[v1].byprod.op; |
|
|
|
unsigned op2 = vertices[v2].byprod.op; |
|
|
|
// The table must only be used if a vertex has either no |
|
|
|
// non-operand neighbors, or a diagonal byprod op |
|
|
|
assert ((!ci.non1) || vertices[v1].byprod.is_diagonal()); |
|
|
|
assert ((!ci.non2) || vertices[v2].byprod.is_diagonal()); |
|
|
|
if (cphase_tbl[ci.wasEdge][op1][op2][0]) { |
|
|
|
add_edge (v1, v2); |
|
|
|
DBGOUT ("adding edge" << endl); |
|
|
|
} else { |
|
|
|
del_edge (v1, v2); |
|
|
|
DBGOUT ("deling edge" << endl); |
|
|
|
} |
|
|
|
vertices[v1].byprod.op = cphase_tbl[ci.wasEdge][op1][op2][1]; |
|
|
|
vertices[v2].byprod.op = cphase_tbl[ci.wasEdge][op1][op2][2]; |
|
|
|
#ifdef DEBUGOUTPUT |
|
|
|
DBGOUT ("cphase_with_table: after:\n"); |
|
|
|
print_adj_list_line (cout, v1); |
|
|
|
print_adj_list_line (cout, v2); |
|
|
|
#endif |
|
|
|
// The condition above must also hold afterwards: |
|
|
|
ci = getConnectionInfo (v1, v2); |
|
|
|
assert ((!ci.non1) || vertices[v1].byprod.is_diagonal()); |
|
|
|
assert ((!ci.non2) || vertices[v2].byprod.is_diagonal()); |
|
|
|
} |
|
|
|
|
|
|
|
/*! Check whether the qubits are connected to each other and to non-operand vertices.*/ |
|
|
|
ConnectionInfo GraphRegister::getConnectionInfo |
|
|
|
(VertexIndex v1, VertexIndex v2) { |
|
|
|
ConnectionInfo ci; |
|
|
|
ci.wasEdge = |
|
|
|
vertices[v1].neighbors.find(v2) != vertices[v1].neighbors.end(); |
|
|
|
if (! ci.wasEdge) { |
|
|
|
ci.non1 = vertices[v1].neighbors.size() >= 1; |
|
|
|
ci.non2 = vertices[v2].neighbors.size() >= 1; |
|
|
|
} else { |
|
|
|
ci.non1 = vertices[v1].neighbors.size() >= 2; |
|
|
|
ci.non2 = vertices[v2].neighbors.size() >= 2; |
|
|
|
} |
|
|
|
return ci; |
|
|
|
} |
|
|
|
|
|
|
|
//! Do a conditional phase gate between the two qubits. |
|
|
|
void GraphRegister::cphase (VertexIndex v1, VertexIndex v2) |
|
|
|
{ |
|
|
|
// If there are non-operand neighbors, we can use neighborhood inversion |
|
|
|
// to remove the byprod operators. |
|
|
|
// These will store whether the operand vertices have nonoperand neighbors. |
|
|
|
#ifdef DEBUGOUTPUT |
|
|
|
DBGOUT ("before cphase between " << v1 << " and " << v2 << endl); |
|
|
|
print_adj_list_line (cout, v1); |
|
|
|
print_adj_list_line (cout, v2); |
|
|
|
#endif |
|
|
|
ConnectionInfo ci = getConnectionInfo (v1, v2); |
|
|
|
|
|
|
|
if (ci.non1) { |
|
|
|
DBGOUT ("cphase: left vertex has NONs -> putting it to Id\n"); |
|
|
|
remove_byprod_op (v1, v2); |
|
|
|
} |
|
|
|
ci = getConnectionInfo (v1, v2); |
|
|
|
if (ci.non2) { |
|
|
|
DBGOUT ("cphase: right vertex has NONs -> putting it to Id\n"); |
|
|
|
remove_byprod_op (v2, v1); |
|
|
|
} |
|
|
|
ci = getConnectionInfo (v1, v2); |
|
|
|
if (ci.non1 && !vertices[v1].byprod.is_diagonal()) { |
|
|
|
// this can happen if v1 was first skipped |
|
|
|
DBGOUT ("cphase: left one needs treatment again -> putting it to Id\n"); |
|
|
|
remove_byprod_op (v1, v2); |
|
|
|
} |
|
|
|
cphase_with_table (v1, v2); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//! Do a controlled not gate between the vertices vc (control) and vt (target). |
|
|
|
void GraphRegister::cnot (VertexIndex vc, VertexIndex vt) { |
|
|
|
hadamard (vt); |
|
|
|
cphase (vc, vt); |
|
|
|
hadamard (vt); |
|
|
|
} |
|
|
|
|
|
|
|
//! This structure is needed only by toggle_edges |
|
|
|
struct Edge: public pair<VertexIndex, VertexIndex> { |
|
|
|
Edge (const VertexIndex a, const VertexIndex b) { |
|
|
|
if (a < b) { |
|
|
|
first = a; second = b; |
|
|
|
} else { |
|
|
|
first = b; second = a; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
//! This structure is needed only by toggle_edges |
|
|
|
struct edge_hash { |
|
|
|
size_t operator() (const Edge& e) const |
|
|
|
{ |
|
|
|
return e.first << 16 ^ e.second; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
//! Toggles the edges between the vertex sets vs1 and vs2. |
|
|
|
/*! The function takes extra care not to invert an edge twice. If vs1 and |
|
|
|
vs2 are disjunct, this cannot happen and we do not need the function. |
|
|
|
If vs1 == v2s, we can do without, too. */ |
|
|
|
void GraphRegister::toggle_edges (const hash_set<VertexIndex> vs1, |
|
|
|
const hash_set<VertexIndex> vs2) |
|
|
|
{ |
|
|
|
hash_set<Edge, edge_hash> procd_edges; |
|
|
|
for (VtxIdxIterConst i = vs1.begin(); i != vs1.end(); i++) { |
|
|
|
for (VtxIdxIterConst j = vs2.begin(); j != vs2.end(); j++) { |
|
|
|
if ((*i != *j) && |
|
|
|
(procd_edges.find (Edge (*i, *j)) == procd_edges.end())) { |
|
|
|
procd_edges.insert (Edge (*i, *j)); |
|
|
|
toggle_edge (*i, *j); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//! Get the adjacency matrix as an edgelist |
|
|
|
std::vector<double> GraphRegister::adj_list () |
|
|
|
{ |
|
|
|
std::vector<double> out; |
|
|
|
for (int i = 0; i < 100; ++i) |
|
|
|
{ |
|
|
|
out.push_back(i); |
|
|
|
} |
|
|
|
return out; |
|
|
|
} |
|
|
|
|
|
|
|
//! Measure the bare graph state in the Z basis. |
|
|
|
int GraphRegister::graph_Z_measure (VertexIndex v, int force) |
|
|
|
{ |
|
|
|
int res; |
|
|
|
if (force == -1) { |
|
|
|
res = bool_rand (); |
|
|
|
} else { |
|
|
|
res = force; |
|
|
|
} |
|
|
|
DBGOUT ("gZm" << v << "," << res << " "); |
|
|
|
#ifdef DEBUGOUTPUT |
|
|
|
print_adj_list_line (cout, v); |
|
|
|
#endif |
|
|
|
hash_set<VertexIndex> nbg = vertices[v].neighbors; |
|
|
|
for (VtxIdxIter i = nbg.begin(); i != nbg.end(); i++) { |
|
|
|
del_edge (v, *i); |
|
|
|
if (res) { |
|
|
|
vertices[*i].byprod = vertices[*i].byprod * lco_Z; |
|
|
|
} |
|
|
|
} |
|
|
|
if (! res) { |
|
|
|
vertices[v].byprod = vertices[v].byprod * lco_H; |
|
|
|
} else { |
|
|
|
vertices[v].byprod = vertices[v].byprod * lco_X * lco_H; |
|
|
|
} |
|
|
|
return res; |
|
|
|
} |
|
|
|
|
|
|
|
//! Measure the bare graph state in the Y basis. |
|
|
|
int GraphRegister::graph_Y_measure (VertexIndex v, int force) |
|
|
|
{ |
|
|
|
int res; |
|
|
|
if (force == -1) { |
|
|
|
res = bool_rand (); |
|
|
|
} else { |
|
|
|
res = force; |
|
|
|
} |
|
|
|
DBGOUT ("gYm" << v << "," << res << " "); |
|
|
|
hash_set<VertexIndex> vnbg = vertices[v].neighbors; |
|
|
|
for (VtxIdxIter i = vnbg.begin(); i != vnbg.end(); i++) { |
|
|
|
if (res) { |
|
|
|
vertices[*i].byprod = vertices[*i].byprod * lco_spiZ; |
|
|
|
} else { |
|
|
|
vertices[*i].byprod = vertices[*i].byprod * lco_smiZ; |
|
|
|
} |
|
|
|
} |
|
|
|
vnbg.insert (v); // Now, vnbg is the set of v and its neighbours. |
|
|
|
for (VtxIdxIter i = vnbg.begin(); i != vnbg.end(); i++) { |
|
|
|
for (VtxIdxIter j = i; j != vnbg.end(); j++) { |
|
|
|
if (i != j) { |
|
|
|
toggle_edge (*i, *j); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (! res) { |
|
|
|
// Messergebnis: +|0y> |
|
|
|
vertices[v].byprod = vertices[v].byprod * lco_S; |
|
|
|
} else { |
|
|
|
// Messergebnis: -|0y> |
|
|
|
vertices[v].byprod = vertices[v].byprod * lco_S.herm_adjoint(); |
|
|
|
} |
|
|
|
return res; |
|
|
|
} |
|
|
|
|
|
|
|
//! Measure the bare graph state in the X basis. |
|
|
|
int GraphRegister::graph_X_measure (VertexIndex v, bool* determined, |
|
|
|
int force) |
|
|
|
{ |
|
|
|
if (vertices[v].neighbors.size () == 0) { |
|
|
|
//not entangled qubit => result always 0 |
|
|
|
DBGOUT ("gXm" << v << ",D "); |
|
|
|
if (determined != NULL) |
|
|
|
*determined = true; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
if (determined != NULL) |
|
|
|
*determined = false; |
|
|
|
// entangled qubit => let's get on with the complicated procedure |
|
|
|
// throw a die: |
|
|
|
int res; |
|
|
|
if (force == -1) { |
|
|
|
res = bool_rand (); |
|
|
|
} else { |
|
|
|
res = force; |
|
|
|
} |
|
|
|
DBGOUT ("gXm" << v << "," << res << " "); |
|
|
|
DBGOUT ("forced: " << force << endl); |
|
|
|
VertexIndex vb = *vertices[v].neighbors.begin(); // the choosen vertex |
|
|
|
//D cerr << "vb = " << vb << endl; |
|
|
|
// preparation step: store the neighborhood of v and vb |
|
|
|
hash_set<VertexIndex> vn = vertices[v].neighbors; |
|
|
|
hash_set<VertexIndex> vbn = vertices[vb].neighbors; |
|
|
|
// First, put the byproduct ops: |
|
|
|
if (! res) { |
|
|
|
// measured a |+>: |
|
|
|
// lco_spiY on vb |
|
|
|
vertices[vb].byprod = vertices[vb].byprod * lco_spiY; |
|
|
|
// Z on all in nbg(v) \ nbg(vb) \ {vb} |
|
|
|
for (VtxIdxIter i = vertices[v].neighbors.begin(); |
|
|
|
i != vertices[v].neighbors.end(); i++) { |
|
|
|
if (*i != vb && |
|
|
|
vertices[vb].neighbors.find(*i) == vertices[vb].neighbors.end()) { |
|
|
|
vertices[*i].byprod = vertices[*i].byprod * lco_Z; |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
// measured a |->: |
|
|
|
// lco_smiY on vb, and lco_Z on v: |
|
|
|
vertices[vb].byprod = vertices[vb].byprod * lco_smiY; |
|
|
|
vertices[v].byprod = vertices[v].byprod * lco_Z; |
|
|
|
// Z on all in nbg(vb) \ nbg(v) \ {v} |
|
|
|
for (VtxIdxIter i = vertices[vb].neighbors.begin(); |
|
|
|
i != vertices[vb].neighbors.end(); i++) { |
|
|
|
if (*i != v && |
|
|
|
vertices[v].neighbors.find(*i) == vertices[v].neighbors.end()) { |
|
|
|
vertices[*i].byprod = vertices[*i].byprod * lco_Z; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
// Toggling the edges in three steps |
|
|
|
// STEP 1: complement with Edges (nbg(v), nbg(vb)): |
|
|
|
toggle_edges (vn, vbn); |
|
|
|
// STEP 2: complement with the complete subgraph induced by the |
|
|
|
// intersection of nbg(v) and nbg(vb): |
|
|
|
// First, make the intersection |
|
|
|
hash_set<VertexIndex> isc; |
|
|
|
for (VtxIdxIter i = vn.begin(); i != vn.end(); i++) { |
|
|
|
if (vbn.find(*i) != vbn.end()) { |
|
|
|
isc.insert (*i); |
|
|
|
} |
|
|
|
} |
|
|
|
// Now, toggle the edges |
|
|
|
for (VtxIdxIter i = isc.begin(); i != isc.end(); i++) { |
|
|
|
for (VtxIdxIter j = i; j != isc.end(); j++) { |
|
|
|
if (*i != *j) { |
|
|
|
toggle_edge (*i, *j); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
// STEP 3: Toggle all edges from vb to nbg(v) \ {vb} |
|
|
|
for (VtxIdxIter i = vn.begin(); i != vn.end(); i++) { |
|
|
|
if (*i != vb) { |
|
|
|
toggle_edge (vb, *i); |
|
|
|
} |
|
|
|
} |
|
|
|
return res; |
|
|
|
} |
|
|
|
|
|
|
|
//! Measure qubit v in basis 'basis'. |
|
|
|
/* For basis, pass a LocCliffOp object which has to be equal to one of lco_X, lco_Y |
|
|
|
or lco_Z. If you want to now, whether the result was choosen at random or determined |
|
|
|
by the state, pass a bool pointer in which this information will be written. |
|
|
|
If you want to force the result to be a certain value, pass 0 or 1 to 'force'. This only |
|
|
|
works, if the result is not determined. If it is, 'force' is ignored.*/ |
|
|
|
int GraphRegister::measure (VertexIndex v, LocCliffOp basis, bool* determined, |
|
|
|
int force) |
|
|
|
{ |
|
|
|
assert (basis.op >= lco_X.op && basis.op <= lco_Z.op); |
|
|
|
if (determined != NULL) { |
|
|
|
*determined = false; |
|
|
|
} |
|
|
|
assert (force >= -1 && force <= 1); |
|
|
|
LocCliffOp basis_orig = basis; |
|
|
|
RightPhase rp = basis.conjugate (vertices[v].byprod.herm_adjoint()); |
|
|
|
assert (rp == rp_p1 || rp == rp_m1); |
|
|
|
if (force != -1 && rp == rp_m1) { |
|
|
|
force = force ^ 0x01; |
|
|
|
} |
|
|
|
int res; |
|
|
|
switch (basis.op) { |
|
|
|
case 1 /* lco_X */: res = graph_X_measure (v, determined, force); break; |
|
|
|
case 2 /* lco_Y */: res = graph_Y_measure (v, force); break; |
|
|
|
case 3 /* lco_Z */: res = graph_Z_measure (v, force); break; |
|
|
|
default: exit (1); |
|
|
|
} |
|
|
|
if (rp == rp_m1) { |
|
|
|
res = ! res; |
|
|
|
} else { |
|
|
|
assert (rp == rp_p1); |
|
|
|
} |
|
|
|
// check: the measured vertex should be singled out: |
|
|
|
assert (vertices[v].neighbors.size() == 0); |
|
|
|
// Check that the vertex is now in the correct eigenstate: |
|
|
|
LocCliffOp assert_op = lco_X; |
|
|
|
assert (assert_op.conjugate (vertices[v].byprod) == (res ? rp_m1 : rp_p1)); |
|
|
|
assert (assert_op == basis_orig); |
|
|
|
return res; |
|
|
|
} |
|
|
|
|
|
|
|
//! Do a neighborhood inversion (i.e. local complementation) about vertex v. |
|
|
|
/* This changes the state's graph representation but not the state itself, as the |
|
|
|
necessary correction to the VOps are applied. */ |
|
|
|
void GraphRegister::invert_neighborhood (VertexIndex v) |
|
|
|
{ |
|
|
|
// Invert the neighborhood: |
|
|
|
#ifdef DEBUGOUTPUT |
|
|
|
DBGOUT ("Inverting about "); |
|
|
|
print_adj_list_line (cout, v); |
|
|
|
#endif |
|
|
|
hash_set<VertexIndex> vn = vertices[v].neighbors; |
|
|
|
for (VtxIdxIter i = vn.begin(); i != vn.end(); i++) { |
|
|
|
for (VtxIdxIter j = i; j != vn.end(); j++) { |
|
|
|
if (*i != *j) { |
|
|
|
//cerr << "toggling " << *i << "," << *j << endl; |
|
|
|
toggle_edge (*i, *j); |
|
|
|
} |
|
|
|
} |
|
|
|
// and adjust the local Cliffords: |
|
|
|
vertices[*i].byprod = vertices[*i].byprod * lco_spiZ.herm_adjoint(); |
|
|
|
} |
|
|
|
// finally, adjust the local Clifford of v: |
|
|
|
vertices[v].byprod = vertices[v].byprod * lco_smiX.herm_adjoint(); |
|
|
|
} |
|
|
|
|
|
|
|
//! Do neighborhood inversions to reduce the VOp of vertex v to the identity. |
|
|
|
/* 'avoid' is avoided as swapping partner, i.e. the swapping partner will not |
|
|
|
be 'avoid' unless this is the only neighbor of v. If no neighbor is available, |
|
|
|
the program exits with error message.*/ |
|
|
|
bool GraphRegister::remove_byprod_op (VertexIndex v, VertexIndex avoid) |
|
|
|
{ |
|
|
|
// A lookup table on how any LC operator can be composed from them |
|
|
|
// generators lco_spiZ (denoted 'V') and lco_smiX (denoted 'U'): |
|
|
|
// (The lookup table was generated from sqrt-gen.nb and copied here manually.) |
|
|
|
const char * comp_tbl [24] = |
|
|
|
{"UUUU", "UU", "VVUU", "VV", |
|
|
|
"VUU", "V", "VVV", "UUV", |
|
|
|
"UVU", "UVUUU", "UVVVU", "UUUVU", |
|
|
|
"UVV", "VVU", "UUU", "U", |
|
|
|
"VVVU", "UUVU", "VU", "VUUU", |
|
|
|
"UUUV", "UVVV", "UV", "UVUU"}; |
|
|
|
// Of course, we need a neighborhood |
|
|
|
if (vertices[v].neighbors.size() == 0) { |
|
|
|
cerr << "remove_byprod_op: called with isolated vertex. Aborting.\n"; |
|
|
|
} |
|
|
|
// This will be the swapping partner: |
|
|
|
VertexIndex vb = * vertices[v].neighbors.begin (); |
|
|
|
if (vb == avoid) { |
|
|
|
// Is there an alternative to 'avoid'? If so, use it. |
|
|
|
VtxIdxIter vbi = vertices[v].neighbors.begin (); |
|
|
|
vbi++; |
|
|
|
if (vbi != vertices[v].neighbors.end()) { |
|
|
|
vb = *vbi; |
|
|
|
} |
|
|
|
} |
|
|
|
#ifdef DEBUGOUTPUT |
|
|
|
DBGOUT ("remove_byprod_op called: (v, avoid, vb):\n"); |
|
|
|
print_adj_list_line (cout, v); |
|
|
|
print_adj_list_line (cout, avoid); |
|
|
|
print_adj_list_line (cout, vb); |
|
|
|
#endif |
|
|
|
const char * comp = comp_tbl[vertices[v].byprod.op]; |
|
|
|
DBGOUT ("using " << comp << endl); |
|
|
|
for (int pos = strlen(comp)-1; pos >= 0; pos--) { |
|
|
|
if (comp[pos] == 'U') { |
|
|
|
// A U will vanish if we do an inversion on v |
|
|
|
DBGOUT ("U ->"); |
|
|
|
invert_neighborhood (v); |
|
|
|
} else { |
|
|
|
assert (comp[pos] == 'V'); |
|
|
|
DBGOUT ("V ->"); |
|
|
|
// For this we need to invert on a neighbor of v |
|
|
|
invert_neighborhood (vb); |
|
|
|
} |
|
|
|
} |
|
|
|
#ifdef DEBUGOUTPUT |
|
|
|
DBGOUT ("remove_byprod_op, after: (v, avoid, vb):\n"); |
|
|
|
print_adj_list_line (cout, v); |
|
|
|
print_adj_list_line (cout, avoid); |
|
|
|
print_adj_list_line (cout, vb); |
|
|
|
#endif |
|
|
|
// Now, we should have lco_Id left |
|
|
|
assert (vertices[v].byprod == lco_Id); |
|
|
|
return true; |
|
|
|
} |
|
|
|
|