Ejemplo n.º 1
0
 def _prepare_state(state, qubits):
     c = Circuit()
     for q, s in zip(qubits, state):
         if s == '0':
             pass
         elif s == '1':
             c.append(Gate('X', [q]))
         elif s == '+':
             c.append(Gate('H', [q]))
         elif s == '-':
             c.extend([Gate('X', [q]), Gate('H', [q])])
         else:
             raise ValueError(f"Unexpected token '{s}'")
     return c
Ejemplo n.º 2
0
def remove_swap(circuit: Circuit) -> tuple[Circuit, dict[any, any]]:
    """
    Iteratively remove SWAP's from circuit by actually swapping qubits.
    The output map will have the form new_qubit -> old_qubit.
    """

    # Initialize map
    _qubits_map = {q: q for q in circuit.all_qubits()}

    # Initialize circuit
    _circ = Circuit()

    # Get ideal SWAP
    _SWAP = Gate('SWAP').matrix()

    # For each gate in circuit ..
    for gate in circuit:

        # Check if gate is close to SWAP
        if gate.n_qubits == 2 and gate.qubits and np.allclose(
                gate.matrix(), _SWAP):

            # If true, swap qubits
            _q0 = next(k for k, v in _qubits_map.items() if v == gate.qubits[0])
            _q1 = next(k for k, v in _qubits_map.items() if v == gate.qubits[1])
            _qubits_map[_q0], _qubits_map[_q1] = _qubits_map[_q1], _qubits_map[
                _q0]

        # Otherwise, remap qubits and append
        else:

            # Get the right qubits
            _qubits = [
                next(k
                     for k, v in _qubits_map.items()
                     if v == q)
                for q in gate.qubits
            ]

            # Append to the new circuit
            _circ.append(gate.on(_qubits))

    # Return circuit and map
    return _circ, _qubits_map
Ejemplo n.º 3
0
def expand_iswap(circuit: Circuit) -> Circuit:
    """
    Expand ISWAP's by iteratively replacing with SWAP's, CZ's and Phases.
    """
    from copy import deepcopy

    # Get ideal iSWAP
    _iSWAP = Gate('ISWAP').matrix()

    # Initialize circuit
    _circ = Circuit()

    # For each gate in circuit ..
    for gate in circuit:

        # Check if gate is close to SWAP
        if gate.n_qubits == 2 and gate.qubits and np.allclose(
                gate.matrix(), _iSWAP):

            # Get tags
            _tags = gate.tags if gate.provides('tags') else {}

            # Expand iSWAP
            _ext = [
                Gate('SWAP', qubits=gate.qubits, tags=_tags),
                Gate('CZ', qubits=gate.qubits, tags=_tags),
                Gate('P', qubits=[gate.qubits[0]], tags=_tags),
                Gate('P', qubits=[gate.qubits[1]], tags=_tags),
            ]

            # Append to circuit
            _circ.extend(_ext if gate.power == 1 else (
                g**-1 for g in reversed(_ext)))

        # Otherwise, just append
        else:
            _circ.append(deepcopy(gate))

    # Return circuit
    return _circ
Ejemplo n.º 4
0
Archivo: qasm.py Proyecto: nasa/hybridq
def from_qasm(qasm_string: str) -> Circuit:
    """
    Convert a QASM circuit to `Circuit`.

    Parameters
    ----------
    qasm_string: str
        QASM circuit to convert to `Circuit`.

    Returns
    -------
    Circuit
        QAMS circuit converted to `Circuit`.

    Notes
    -----

    The QASM language used in HybridQ is compatible with the standard QASM.
    However, HybridQ introduces few extensions, which are recognized by the
    parser using ``#@`` at the beginning of the line (``#`` at the beginning of
    the line represent a general comment in QASM). At the moment, the following
    QAMS extensions are supported:

    * **qubits**, used to store `qubits_map`,
    * **power**, used to store the power of the gate,
    * **tags**, used to store the tags associated to the gate,
    * **U**, used to store the matrix reprentation of the gate if gate name is `MATRIX`

    If `Gate.qubits` are not specified, a single `.` is used to represent the
    missing qubits. If `Gate.params` are missing, parameters are just omitted.

    Example
    -------
    >>> from hybridq.extras.qasm import from_qasm
    >>> qasm_str = \"\"\"
    >>> 1
    >>> #@ qubits =
    >>> #@ {
    >>> #@   "0": "42"
    >>> #@ }
    >>> #@ tags =
    >>> #@ {
    >>> #@   "params": false,
    >>> #@   "qubits": false
    >>> #@ }
    >>> rx .
    >>> #@ tags =
    >>> #@ {
    >>> #@   "params": true,
    >>> #@   "qubits": false
    >>> #@ }
    >>> ry . 1.23
    >>> #@ tags =
    >>> #@ {
    >>> #@   "params": false,
    >>> #@   "qubits": true
    >>> #@ }
    >>> #@ power = 1.23
    >>> rz 0
    >>> #@ U =
    >>> #@ [
    >>> #@   [
    >>> #@     "0.7071067811865475",
    >>> #@     "0.7071067811865475"
    >>> #@   ],
    >>> #@   [
    >>> #@     "0.7071067811865475",
    >>> #@     "-0.7071067811865475"
    >>> #@   ]
    >>> #@ ]
    >>> matrix .
    >>> \"\"\"
    >>> from_qasm(qasm_str)
    Circuit([
            Gate(name=RX, tags={'params': False, 'qubits': False})
            Gate(name=RY, params=[1.23], tags={'params': True, 'qubits': False})
            Gate(name=RZ, qubits=[42], tags={'params': False, 'qubits': True})**1.23
            Gate(name=MATRIX, U=np.array(shape=(2, 2), dtype=float64))
    ])
    """

    # Initialize circuit
    circuit = Circuit()

    # Initialize tags
    _extra = None
    _power = None
    _conj = False
    _T = False
    _tags = None
    _qubits_map = None
    _U = None

    for line in (line for line in qasm_string.split('\n')
                 if line and (line[0] != '#' or line[:2] == '#@')):

        if line[:2] == '#@':

            # Strip line
            _line = re.sub(r'\s+', '', line)

            if '#@tags=' in _line:

                if _tags is not None:
                    raise ValueError('Format error.')

                # Initialize tags
                _tags = line.split('=')[-1]
                _extra = 'tags'

            elif '#@U=' in _line:

                if _U is not None:
                    raise ValueError('Format error.')

                # Initialize matrix
                _U = line.split('=')[-1]
                _extra = 'U'

            elif '#@power=' in _line:

                if _power is not None:
                    raise ValueError('Format error.')

                # Initialize power
                _power = line.split('=')[-1]
                _extra = 'power'

            elif '#@conj' in _line:

                if _conj is not False:
                    raise ValueError('Format error.')

                # Initialize conjugation
                _conj = True

            elif '#@T' in _line:

                if _T is not False:
                    raise ValueError('Format error.')

                # Initialize transposition
                _T = True

            elif '#@qubits=' in _line:

                if _qubits_map is not None:
                    raise ValueError('Format error.')

                # Initialize qubits
                _qubits_map = line.split('=')[-1]
                _extra = 'qubits'

            elif _extra:

                # Update tags
                if _extra == 'tags':
                    _tags += line.replace('#@', '')

                # Update matrix
                elif _extra == 'U':
                    _U += line.replace('#@', '')

                # Update power
                elif _extra == 'power':
                    _power += line.replace('#@', '')

                # Update qubits_map
                elif _extra == 'qubits':
                    _qubits_map += line.replace('#@', '')

                # Otherwise, error
                else:
                    raise ValueError('Format error.')

        else:

            # Restart _extra
            _extra = None

            # Strip everything after the first #
            line = line.split('#')[0].split()

            # Make few guesses about format
            if len(line) == 1:
                if _isint(line[0]):
                    warn(
                        f"Skipping '{' '.join(line)}' (most likely the number of qubits in the circuit)."
                    )
                    continue
                else:
                    warn(
                        f"Skipping '{' '.join(line)}' (format is not understood)."
                    )
                    continue

            # Make few guesses about format
            if _isint(line[0]):
                warn(
                    f"Skipping {line[0]} in '{' '.join(line)}' (most likely the circuit layer)."
                )
                del (line[0])

            # Make few guesses about format
            if not _isstring(line[0]):
                warn(
                    f"Skipping '{' '.join(line)}' (format is not understood).")
                continue

            if line[0].upper() == 'MATRIX':

                # Remove name from line
                del (line[0])

                # Add tags
                if not _U:
                    raise ValueError('Format error.')

                # Set gate
                _U = np.real_if_close(
                    np.array([[complex(y) for y in x]
                              for x in json.loads(_U)]))
                gate = Gate('MATRIX', U=_U)

                # Set qubits if present
                if line[0] != '.':

                    # Set qubits
                    gate.on([int(x) for x in line], inplace=True)

                # Reset
                _U = None

            else:

                # Set position
                _p = 0

                # Initialize gate with name
                gate = Gate(line[_p])

                # Check if qubits are provided
                _p += 1
                if line[_p] != '.':

                    # Set qubits
                    gate.on([int(x) for x in line[_p:_p + gate.n_qubits]],
                            inplace=True)
                    _p += gate.n_qubits

                else:

                    # Skip qubits
                    _p += 1

                if _p != len(line):

                    if not gate.provides('params') and (
                            _p + gate.n_params) != len(line):
                        raise ValueError('Format error.')

                    gate.set_params(
                        [float(x) for x in line[_p:_p + gate.n_params]],
                        inplace=True)
                    _p += gate.n_params

                if len(line) != _p:
                    print(line, gate)

            # Add tags
            if _tags:
                gate._set_tags(json.loads(_tags))

            # Apply power
            if _power:
                gate._set_power(float(_power))

            # Add conjugation
            if _conj:
                gate._conj()

            # Add transposition
            if _T:
                gate._T()

            # Append gate to circuit
            circuit.append(gate)

            # Reset
            _tags = None

            # Reset power
            _power = None

            # Reset conjugation
            _conj = False

            # Reset transposition
            _T = False

    # Remap qubits
    if _qubits_map is not None:

        def _int(x):
            try:
                return int(x)
            except:
                return x

        _qubits_map = json.loads(_qubits_map)
        _qubits_map = {int(x): _int(y) for x, y in _qubits_map.items()}
        for gate in circuit:
            if gate.provides('qubits') and gate.qubits is not None:
                gate._on([_qubits_map[x] for x in gate.qubits])

    return circuit