zxcalc/pyzx

Clarification on applying individual transformations

Opened this issue · 5 comments

My goal is to apply a single transformation to a graph that satisfies the 'graph-like' precondition.

First approach

I tried to apply the following transformations mentioned in #199 (to_gh and spider_simp):

fname = os.path.join('..','circuits','Fast', 'mod5_4_before')
circ = zx.Circuit.load(fname).to_basic_gates()
x = circ.to_graph() # We first have to convert the circuit into a ZX-graph
initial_graph = copy.deepcopy(x)

# turn all red spiders into green spiders
zx.to_gh(initial_graph)

# simplify: remove excess HAD's, fuse along non-HAD edges, remove parallel edges and self-loops
zx.simplify.spider_simp(initial_graph, quiet=True)
# checking transformed graph
print(f'zx.simplify.is_graph_like:{zx.simplify.is_graph_like(initial_graph)}')  

Output:

zx.simplify.is_graph_like:False

But the graph was not 'graph-like'.

Second approach

My second approach was using specific functions to convert to 'graph-like'.

I converted the 'mod5_4_before' circuit to a graph, and later to a 'graph-like' graph using the 'to_graph_like' function.
I have to say that to_graph_like function appears in a code block with a note:
"#The functions below haven't been updated in a while. Use at your own risk.", although the function appears to implement the requirements necessary for a graph to satisfy the 'graph-like' condition according to 'Hybrid quantum-classical circuit simplification
with the ZX-calculus'
. (page 6).
After this, I applied the transformation 'zx.rules.remove_ids' to the graph of type 'graph-like', and checked the type of graph using the 'is_graph_like' function, this function returned False -> so, it was not a graph of type 'graph-like'.

fname = os.path.join('.','circuits','Fast', 'mod5_4_before')
circ = zx.Circuit.load(fname).to_basic_gates()
x = circ.to_graph() # We first have to convert the circuit into a ZX-graph
graph = copy.deepcopy(x)
print('--------------------------------------')
graph_aux = copy.deepcopy(graph)
zx.simplify.to_graph_like(graph_aux)
print(f'zx.simplify.is_graph_like(graph_aux):{zx.simplify.is_graph_like(graph_aux)}')
print('--------------------------------------')
zx.draw(graph_aux, labels=True)
print('--------------------------------------')
matcher_params = zx.rules.match_ids_parallel(graph_aux)
print(f'matcher_params[0]:{matcher_params[0]}')
print('--------------------------------------')
zx.rules.apply_rule(graph_aux, zx.rules.remove_ids,[matcher_params[0]])  
zx.draw(graph_aux, labels=True)
print(f'zx.simplify.is_graph_like:{zx.simplify.is_graph_like(graph_aux)}')

Output:

image

As I said at the beginning of this comment, my goal is to apply a transform to a circuit.
In this case, I tested the 'zx.rules.remove_ids' transformation, and the result does not seem to be satisfactory.

I am very confused, please:

  • Could someone explain to me this result?
    And
  • Could someone tell me how can I apply any transformation individually without getting errors?

The first thing seems to be a bug, potentially in the is_graph_like function.
The second one is because removing an identity breaks the graph being graph like. You can see this in your last picture: there is a regular edge between vertices 38 and 48. If you want to make it graph like then you should fuse the spiders again.

I've now changed the default behaviour to is_graph_like so that it makes sense here. The old behaviour you can get by setting the new parameter strict=True.

Thanks @jvdwetering.

  1. In the first case, the result is now a "graph-like" (strict=False) graph according to the default behavior of the modified function.

  2. In the second case, yes, if I only fuse the spiders again, the resulting graph is 'graph-like', as indicated by the is_graph_like function.

Taking these results into account:

  • Should I generalize and believe that every time I apply a transformation (any) to a 'graph-like' graph, it is enough to fuse the spiders immediately afterwards to still get a 'graph-like' graph?
  • Or should I also take into account the other conditions present in the to_graph_like function relating to:
      - #ensure all I/O are connected to a Z-spider
    # ensure all I/O are connected to a Z-spider

      - #each Z-spider can only be connected to at most 1 I/O
    # each Z-spider can only be connected to at most 1 I/O
    ?

It depends on what you want to do with your graph-like diagram. But yes, fusing all spiders is definitely a necessity.

What I would like to do with my graph-type diagram is to be able to apply a set of transformations to a circuit, making sure that I do not modify the functionality/semantics of the graph corresponding to the circuit.

So this is my question, taking into account the new parameter in the function I can apply to a circuit:

  • the least restrictive call -> is_graph_like(g, strict=False)
    or
  • the most strict call -> is_graph_like(g, strict=True)

Which one should I apply?