switching to high quality piper tts and added label translations
This commit is contained in:
@@ -0,0 +1,488 @@
|
||||
"""Primitive circuit operations on quantum circuits."""
|
||||
|
||||
from functools import reduce
|
||||
|
||||
from sympy.core.sorting import default_sort_key
|
||||
from sympy.core.containers import Tuple
|
||||
from sympy.core.mul import Mul
|
||||
from sympy.core.symbol import Symbol
|
||||
from sympy.core.sympify import sympify
|
||||
from sympy.utilities import numbered_symbols
|
||||
from sympy.physics.quantum.gate import Gate
|
||||
|
||||
__all__ = [
|
||||
'kmp_table',
|
||||
'find_subcircuit',
|
||||
'replace_subcircuit',
|
||||
'convert_to_symbolic_indices',
|
||||
'convert_to_real_indices',
|
||||
'random_reduce',
|
||||
'random_insert'
|
||||
]
|
||||
|
||||
|
||||
def kmp_table(word):
|
||||
"""Build the 'partial match' table of the Knuth-Morris-Pratt algorithm.
|
||||
|
||||
Note: This is applicable to strings or
|
||||
quantum circuits represented as tuples.
|
||||
"""
|
||||
|
||||
# Current position in subcircuit
|
||||
pos = 2
|
||||
# Beginning position of candidate substring that
|
||||
# may reappear later in word
|
||||
cnd = 0
|
||||
# The 'partial match' table that helps one determine
|
||||
# the next location to start substring search
|
||||
table = []
|
||||
table.append(-1)
|
||||
table.append(0)
|
||||
|
||||
while pos < len(word):
|
||||
if word[pos - 1] == word[cnd]:
|
||||
cnd = cnd + 1
|
||||
table.append(cnd)
|
||||
pos = pos + 1
|
||||
elif cnd > 0:
|
||||
cnd = table[cnd]
|
||||
else:
|
||||
table.append(0)
|
||||
pos = pos + 1
|
||||
|
||||
return table
|
||||
|
||||
|
||||
def find_subcircuit(circuit, subcircuit, start=0, end=0):
|
||||
"""Finds the subcircuit in circuit, if it exists.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
If the subcircuit exists, the index of the start of
|
||||
the subcircuit in circuit is returned; otherwise,
|
||||
-1 is returned. The algorithm that is implemented
|
||||
is the Knuth-Morris-Pratt algorithm.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
circuit : tuple, Gate or Mul
|
||||
A tuple of Gates or Mul representing a quantum circuit
|
||||
subcircuit : tuple, Gate or Mul
|
||||
A tuple of Gates or Mul to find in circuit
|
||||
start : int
|
||||
The location to start looking for subcircuit.
|
||||
If start is the same or past end, -1 is returned.
|
||||
end : int
|
||||
The last place to look for a subcircuit. If end
|
||||
is less than 1 (one), then the length of circuit
|
||||
is taken to be end.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
Find the first instance of a subcircuit:
|
||||
|
||||
>>> from sympy.physics.quantum.circuitutils import find_subcircuit
|
||||
>>> from sympy.physics.quantum.gate import X, Y, Z, H
|
||||
>>> circuit = X(0)*Z(0)*Y(0)*H(0)
|
||||
>>> subcircuit = Z(0)*Y(0)
|
||||
>>> find_subcircuit(circuit, subcircuit)
|
||||
1
|
||||
|
||||
Find the first instance starting at a specific position:
|
||||
|
||||
>>> find_subcircuit(circuit, subcircuit, start=1)
|
||||
1
|
||||
|
||||
>>> find_subcircuit(circuit, subcircuit, start=2)
|
||||
-1
|
||||
|
||||
>>> circuit = circuit*subcircuit
|
||||
>>> find_subcircuit(circuit, subcircuit, start=2)
|
||||
4
|
||||
|
||||
Find the subcircuit within some interval:
|
||||
|
||||
>>> find_subcircuit(circuit, subcircuit, start=2, end=2)
|
||||
-1
|
||||
"""
|
||||
|
||||
if isinstance(circuit, Mul):
|
||||
circuit = circuit.args
|
||||
|
||||
if isinstance(subcircuit, Mul):
|
||||
subcircuit = subcircuit.args
|
||||
|
||||
if len(subcircuit) == 0 or len(subcircuit) > len(circuit):
|
||||
return -1
|
||||
|
||||
if end < 1:
|
||||
end = len(circuit)
|
||||
|
||||
# Location in circuit
|
||||
pos = start
|
||||
# Location in the subcircuit
|
||||
index = 0
|
||||
# 'Partial match' table
|
||||
table = kmp_table(subcircuit)
|
||||
|
||||
while (pos + index) < end:
|
||||
if subcircuit[index] == circuit[pos + index]:
|
||||
index = index + 1
|
||||
else:
|
||||
pos = pos + index - table[index]
|
||||
index = table[index] if table[index] > -1 else 0
|
||||
|
||||
if index == len(subcircuit):
|
||||
return pos
|
||||
|
||||
return -1
|
||||
|
||||
|
||||
def replace_subcircuit(circuit, subcircuit, replace=None, pos=0):
|
||||
"""Replaces a subcircuit with another subcircuit in circuit,
|
||||
if it exists.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
If multiple instances of subcircuit exists, the first instance is
|
||||
replaced. The position to being searching from (if different from
|
||||
0) may be optionally given. If subcircuit cannot be found, circuit
|
||||
is returned.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
circuit : tuple, Gate or Mul
|
||||
A quantum circuit.
|
||||
subcircuit : tuple, Gate or Mul
|
||||
The circuit to be replaced.
|
||||
replace : tuple, Gate or Mul
|
||||
The replacement circuit.
|
||||
pos : int
|
||||
The location to start search and replace
|
||||
subcircuit, if it exists. This may be used
|
||||
if it is known beforehand that multiple
|
||||
instances exist, and it is desirable to
|
||||
replace a specific instance. If a negative number
|
||||
is given, pos will be defaulted to 0.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
Find and remove the subcircuit:
|
||||
|
||||
>>> from sympy.physics.quantum.circuitutils import replace_subcircuit
|
||||
>>> from sympy.physics.quantum.gate import X, Y, Z, H
|
||||
>>> circuit = X(0)*Z(0)*Y(0)*H(0)*X(0)*H(0)*Y(0)
|
||||
>>> subcircuit = Z(0)*Y(0)
|
||||
>>> replace_subcircuit(circuit, subcircuit)
|
||||
(X(0), H(0), X(0), H(0), Y(0))
|
||||
|
||||
Remove the subcircuit given a starting search point:
|
||||
|
||||
>>> replace_subcircuit(circuit, subcircuit, pos=1)
|
||||
(X(0), H(0), X(0), H(0), Y(0))
|
||||
|
||||
>>> replace_subcircuit(circuit, subcircuit, pos=2)
|
||||
(X(0), Z(0), Y(0), H(0), X(0), H(0), Y(0))
|
||||
|
||||
Replace the subcircuit:
|
||||
|
||||
>>> replacement = H(0)*Z(0)
|
||||
>>> replace_subcircuit(circuit, subcircuit, replace=replacement)
|
||||
(X(0), H(0), Z(0), H(0), X(0), H(0), Y(0))
|
||||
"""
|
||||
|
||||
if pos < 0:
|
||||
pos = 0
|
||||
|
||||
if isinstance(circuit, Mul):
|
||||
circuit = circuit.args
|
||||
|
||||
if isinstance(subcircuit, Mul):
|
||||
subcircuit = subcircuit.args
|
||||
|
||||
if isinstance(replace, Mul):
|
||||
replace = replace.args
|
||||
elif replace is None:
|
||||
replace = ()
|
||||
|
||||
# Look for the subcircuit starting at pos
|
||||
loc = find_subcircuit(circuit, subcircuit, start=pos)
|
||||
|
||||
# If subcircuit was found
|
||||
if loc > -1:
|
||||
# Get the gates to the left of subcircuit
|
||||
left = circuit[0:loc]
|
||||
# Get the gates to the right of subcircuit
|
||||
right = circuit[loc + len(subcircuit):len(circuit)]
|
||||
# Recombine the left and right side gates into a circuit
|
||||
circuit = left + replace + right
|
||||
|
||||
return circuit
|
||||
|
||||
|
||||
def _sympify_qubit_map(mapping):
|
||||
new_map = {}
|
||||
for key in mapping:
|
||||
new_map[key] = sympify(mapping[key])
|
||||
return new_map
|
||||
|
||||
|
||||
def convert_to_symbolic_indices(seq, start=None, gen=None, qubit_map=None):
|
||||
"""Returns the circuit with symbolic indices and the
|
||||
dictionary mapping symbolic indices to real indices.
|
||||
|
||||
The mapping is 1 to 1 and onto (bijective).
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
seq : tuple, Gate/Integer/tuple or Mul
|
||||
A tuple of Gate, Integer, or tuple objects, or a Mul
|
||||
start : Symbol
|
||||
An optional starting symbolic index
|
||||
gen : object
|
||||
An optional numbered symbol generator
|
||||
qubit_map : dict
|
||||
An existing mapping of symbolic indices to real indices
|
||||
|
||||
All symbolic indices have the format 'i#', where # is
|
||||
some number >= 0.
|
||||
"""
|
||||
|
||||
if isinstance(seq, Mul):
|
||||
seq = seq.args
|
||||
|
||||
# A numbered symbol generator
|
||||
index_gen = numbered_symbols(prefix='i', start=-1)
|
||||
cur_ndx = next(index_gen)
|
||||
|
||||
# keys are symbolic indices; values are real indices
|
||||
ndx_map = {}
|
||||
|
||||
def create_inverse_map(symb_to_real_map):
|
||||
rev_items = lambda item: (item[1], item[0])
|
||||
return dict(map(rev_items, symb_to_real_map.items()))
|
||||
|
||||
if start is not None:
|
||||
if not isinstance(start, Symbol):
|
||||
msg = 'Expected Symbol for starting index, got %r.' % start
|
||||
raise TypeError(msg)
|
||||
cur_ndx = start
|
||||
|
||||
if gen is not None:
|
||||
if not isinstance(gen, numbered_symbols().__class__):
|
||||
msg = 'Expected a generator, got %r.' % gen
|
||||
raise TypeError(msg)
|
||||
index_gen = gen
|
||||
|
||||
if qubit_map is not None:
|
||||
if not isinstance(qubit_map, dict):
|
||||
msg = ('Expected dict for existing map, got ' +
|
||||
'%r.' % qubit_map)
|
||||
raise TypeError(msg)
|
||||
ndx_map = qubit_map
|
||||
|
||||
ndx_map = _sympify_qubit_map(ndx_map)
|
||||
# keys are real indices; keys are symbolic indices
|
||||
inv_map = create_inverse_map(ndx_map)
|
||||
|
||||
sym_seq = ()
|
||||
for item in seq:
|
||||
# Nested items, so recurse
|
||||
if isinstance(item, Gate):
|
||||
result = convert_to_symbolic_indices(item.args,
|
||||
qubit_map=ndx_map,
|
||||
start=cur_ndx,
|
||||
gen=index_gen)
|
||||
sym_item, new_map, cur_ndx, index_gen = result
|
||||
ndx_map.update(new_map)
|
||||
inv_map = create_inverse_map(ndx_map)
|
||||
|
||||
elif isinstance(item, (tuple, Tuple)):
|
||||
result = convert_to_symbolic_indices(item,
|
||||
qubit_map=ndx_map,
|
||||
start=cur_ndx,
|
||||
gen=index_gen)
|
||||
sym_item, new_map, cur_ndx, index_gen = result
|
||||
ndx_map.update(new_map)
|
||||
inv_map = create_inverse_map(ndx_map)
|
||||
|
||||
elif item in inv_map:
|
||||
sym_item = inv_map[item]
|
||||
|
||||
else:
|
||||
cur_ndx = next(gen)
|
||||
ndx_map[cur_ndx] = item
|
||||
inv_map[item] = cur_ndx
|
||||
sym_item = cur_ndx
|
||||
|
||||
if isinstance(item, Gate):
|
||||
sym_item = item.__class__(*sym_item)
|
||||
|
||||
sym_seq = sym_seq + (sym_item,)
|
||||
|
||||
return sym_seq, ndx_map, cur_ndx, index_gen
|
||||
|
||||
|
||||
def convert_to_real_indices(seq, qubit_map):
|
||||
"""Returns the circuit with real indices.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
seq : tuple, Gate/Integer/tuple or Mul
|
||||
A tuple of Gate, Integer, or tuple objects or a Mul
|
||||
qubit_map : dict
|
||||
A dictionary mapping symbolic indices to real indices.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
Change the symbolic indices to real integers:
|
||||
|
||||
>>> from sympy import symbols
|
||||
>>> from sympy.physics.quantum.circuitutils import convert_to_real_indices
|
||||
>>> from sympy.physics.quantum.gate import X, Y, H
|
||||
>>> i0, i1 = symbols('i:2')
|
||||
>>> index_map = {i0 : 0, i1 : 1}
|
||||
>>> convert_to_real_indices(X(i0)*Y(i1)*H(i0)*X(i1), index_map)
|
||||
(X(0), Y(1), H(0), X(1))
|
||||
"""
|
||||
|
||||
if isinstance(seq, Mul):
|
||||
seq = seq.args
|
||||
|
||||
if not isinstance(qubit_map, dict):
|
||||
msg = 'Expected dict for qubit_map, got %r.' % qubit_map
|
||||
raise TypeError(msg)
|
||||
|
||||
qubit_map = _sympify_qubit_map(qubit_map)
|
||||
real_seq = ()
|
||||
for item in seq:
|
||||
# Nested items, so recurse
|
||||
if isinstance(item, Gate):
|
||||
real_item = convert_to_real_indices(item.args, qubit_map)
|
||||
|
||||
elif isinstance(item, (tuple, Tuple)):
|
||||
real_item = convert_to_real_indices(item, qubit_map)
|
||||
|
||||
else:
|
||||
real_item = qubit_map[item]
|
||||
|
||||
if isinstance(item, Gate):
|
||||
real_item = item.__class__(*real_item)
|
||||
|
||||
real_seq = real_seq + (real_item,)
|
||||
|
||||
return real_seq
|
||||
|
||||
|
||||
def random_reduce(circuit, gate_ids, seed=None):
|
||||
"""Shorten the length of a quantum circuit.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
random_reduce looks for circuit identities in circuit, randomly chooses
|
||||
one to remove, and returns a shorter yet equivalent circuit. If no
|
||||
identities are found, the same circuit is returned.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
circuit : Gate tuple of Mul
|
||||
A tuple of Gates representing a quantum circuit
|
||||
gate_ids : list, GateIdentity
|
||||
List of gate identities to find in circuit
|
||||
seed : int or list
|
||||
seed used for _randrange; to override the random selection, provide a
|
||||
list of integers: the elements of gate_ids will be tested in the order
|
||||
given by the list
|
||||
|
||||
"""
|
||||
from sympy.core.random import _randrange
|
||||
|
||||
if not gate_ids:
|
||||
return circuit
|
||||
|
||||
if isinstance(circuit, Mul):
|
||||
circuit = circuit.args
|
||||
|
||||
ids = flatten_ids(gate_ids)
|
||||
|
||||
# Create the random integer generator with the seed
|
||||
randrange = _randrange(seed)
|
||||
|
||||
# Look for an identity in the circuit
|
||||
while ids:
|
||||
i = randrange(len(ids))
|
||||
id = ids.pop(i)
|
||||
if find_subcircuit(circuit, id) != -1:
|
||||
break
|
||||
else:
|
||||
# no identity was found
|
||||
return circuit
|
||||
|
||||
# return circuit with the identity removed
|
||||
return replace_subcircuit(circuit, id)
|
||||
|
||||
|
||||
def random_insert(circuit, choices, seed=None):
|
||||
"""Insert a circuit into another quantum circuit.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
random_insert randomly chooses a location in the circuit to insert
|
||||
a randomly selected circuit from amongst the given choices.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
circuit : Gate tuple or Mul
|
||||
A tuple or Mul of Gates representing a quantum circuit
|
||||
choices : list
|
||||
Set of circuit choices
|
||||
seed : int or list
|
||||
seed used for _randrange; to override the random selections, give
|
||||
a list two integers, [i, j] where i is the circuit location where
|
||||
choice[j] will be inserted.
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
Indices for insertion should be [0, n] if n is the length of the
|
||||
circuit.
|
||||
"""
|
||||
from sympy.core.random import _randrange
|
||||
|
||||
if not choices:
|
||||
return circuit
|
||||
|
||||
if isinstance(circuit, Mul):
|
||||
circuit = circuit.args
|
||||
|
||||
# get the location in the circuit and the element to insert from choices
|
||||
randrange = _randrange(seed)
|
||||
loc = randrange(len(circuit) + 1)
|
||||
choice = choices[randrange(len(choices))]
|
||||
|
||||
circuit = list(circuit)
|
||||
circuit[loc: loc] = choice
|
||||
return tuple(circuit)
|
||||
|
||||
# Flatten the GateIdentity objects (with gate rules) into one single list
|
||||
|
||||
|
||||
def flatten_ids(ids):
|
||||
collapse = lambda acc, an_id: acc + sorted(an_id.equivalent_ids,
|
||||
key=default_sort_key)
|
||||
ids = reduce(collapse, ids, [])
|
||||
ids.sort(key=default_sort_key)
|
||||
return ids
|
||||
Reference in New Issue
Block a user