Source code for vulqano.states.mcstates

# This code is part of vulqano.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.


"""
Define a class for Markov chain circuit states.

Note:
While in abstract states the gates are encoded as strings, in Markov chain states
the gates are labeled by integers. This different encoding allows to more quickly
check whether a rule applies to the circuit state.
"""

import numpy as np
from vulqano.states.abstractcircuitstate import AbstractCircuitState
from vulqano.gates.discretegates import ROT_NUMBERS
from vulqano.gates.discretegates import (
    gate_labels_to_ints as discrete_array_strings_to_ints,
)
from vulqano.gates.discretegates import (
    gate_ints_to_labels as discrete_array_ints_to_strings,
)
from vulqano.gates.continuousgates import (
    gate_labels_to_ints as continuous_array_strings_to_ints,
)
from vulqano.gates.continuousgates import (
    gate_ints_to_labels as continuous_array_ints_to_strings,
)


__all__ = [
    "_MCState",
    "DiscreteMCState",
    "ContinuousMCState",
]


class _MCState:
    """
    Base class for Markov chain circuit states.
    """

    def transition(self, position, rule, transition_instructions):
        """
        Locally transforms the circuit by replacing a subcircuit.

        **Arguments**

        position : tuple of ints
            Position in the time-qubit lattice where the new subcircuit is placed.
        rule : DiscereteMCRule
            Transtition rule.
        transition_instructions : object
            Other instructions.

        **Returns**

        None.
        """
        raise NotImplementedError("Not yet implemented")

    def check_rule(self, rule, position):
        """
        Check if a rule can be applied to a region of the state.

        **Arguments**

        rule : DiscreteMCRule
            Discrete MC rule to be applied to a region of the state.
        position : tuple of ints
            Position in the time-qubit lattice where the rule is tested.

        **Returns**

           : (bool, bool)
           The first bool indicates if the rule can be applied. The second bool
           indicates the direction of the rule, True for state_a->state_b, and
           false for state_b->state_a.

        """
        raise NotImplementedError("Not yet implemented")

    def to_abstract(self, name):
        """
        Returns the circuit as an abstract circuit.

        **Arguments**

        name : str
            Name of the circuit.

        **Returns**

        AbstractCircuitState
            Abstract representation of the circuit.
        """
        raise NotImplementedError("Not yet implemented")


[docs] class DiscreteMCState(_MCState): """ Class for discrete Markov chain circuit states. **Arguments** abstractstate : AbstractCircuitState AbstractCircuitState to be converted to a DiscreteMCState. **Attributes** vector : numpy.array Array of gates applied at each time and on each qubit. The first index correspond to the time step, the other indices label the qubit in the lattice. times : int Circuit depth. qubits : touple of ints Number of qubits in each direction of the lattice. dim : int Number of dimension of the circuit state, i.e. 1 + number of dimensions of the qubits lattice. rot_mask : mask True where the qubit is a rotation. """ def __init__(self, abstractstate): if abstractstate.is_continuous: raise ValueError( "Only discrete abstract states can be converted to DiscreteMCState" ) self.vector = discrete_array_strings_to_ints(abstractstate.vector) self.times = abstractstate.times self.qubits = abstractstate.qubits self.dim = abstractstate.dim if self.dim not in [2, 3]: raise ValueError( "Only (1+1)d and (1+2)d MC circuit states are implemented." ) self.rot_mask = np.isin(abstractstate.vector, ROT_NUMBERS)
[docs] def transition(self, position, rule, transition_instructions): """ Locally transforms the circuit by replacing a subcircuit. **Arguments** position : tuple of ints Position in the time-qubit lattice where the new subcircuit is placed. rule : DiscereteMCRule Transtition rule. transition_instructions : (np.array, mask) Matrix representing the new subcircuit. Where the mask is true, the gate is not raplaced. **Returns** None. """ slices = tuple( slice(position[i], position[i] + transition_instructions[0].shape[i]) for i in range(self.dim) ) np.putmask( self.vector[slices], transition_instructions[1], transition_instructions[0], ) self.rot_mask[slices] = np.isin(self.vector[slices], ROT_NUMBERS)
[docs] def check_rule(self, rule, position): """ Check if a rule can be applied to a region of the state. **Arguments** rule : DiscreteMCRule Discrete MC rule to be applied to a region of the state. position : tuple of ints Position in the time-qubit lattice where the rule is tested. **Returns** : (bool, bool) The first bool indicates if the rule can be applied. The second bool indicates the direction of the rule, True for state_a->state_b, and false for state_b->state_a. """ slices = tuple( slice(position[i], position[i] + rule.shape[i]) for i in range(self.dim) ) sub_circ = self.vector[slices] if rule.shape == sub_circ.shape: sub_rot_mask = self.rot_mask[slices] mask = sub_rot_mask & rule.masks[1] if np.all(np.equal(sub_circ, rule.state_a) | rule.masks[0] | mask): return (True, True) if np.all(np.equal(sub_circ, rule.state_b) | rule.masks[0] | mask): return (True, False) return (False, False)
[docs] def to_abstract(self, name): """ Returns the circuit as an abstract circuit. **Arguments** name : str Name of the circuit. **Returns** AbstractCircuitState Abstract representation of the circuit. """ return AbstractCircuitState(discrete_array_ints_to_strings(self.vector), name)
[docs] class ContinuousMCState(_MCState): """ Class for continuous Markov chain circuit states. **Arguments** abstractstate : AbstractCircuitState AbstractCircuitState to be converted to a ContinuousMCState. **Attributes** vector : numpy.array Array of gates applied at each time and on each qubit. The first index correspond to the time step, the other indices label the qubit in the lattice. times : int Circuit depth. qubits : touple of ints dim : int Number of dimension of the circuit state, i.e. 1 + number of dimensions of the qubits lattice. Number of qubits in each direction of the lattice. rot_amplitudes : np array of float. A numpy array of float with the same shape of the circuit, where at each entry a parameter is specified for the corresponding continuous gate. """ def __init__(self, abstractstate): if not abstractstate.is_continuous: raise ValueError( "Only continuous abstract states can be converted to ContinuousMCState" ) self.vector = continuous_array_strings_to_ints(abstractstate.vector) self.times = abstractstate.times self.qubits = abstractstate.qubits self.dim = abstractstate.dim if self.dim not in [2, 3]: raise ValueError( "Only (1+1)d and (1+2)d MC circuit states are implemented." ) self.rot_amplitudes = np.copy(abstractstate.rot_amplitudes)
[docs] def transition(self, position, rule, transition_instructions): """ Locally transforms the circuit by replacing a subcircuit. **Arguments** position : tuple of ints Position in the time-qubit lattice where the new subcircuit is placed. rule : DiscereteMCRule Transtition rule. transition_instructions : (np.array, mask) Matrix representing the new subcircuit. Where the mask is true, the gate is not raplaced. **Returns** None. """ slices = tuple( slice(position[i], position[i] + transition_instructions[0].shape[i]) for i in range(self.dim) ) np.putmask( self.vector[slices], transition_instructions[1], transition_instructions[0], ) np.putmask( self.rot_amplitudes[slices], transition_instructions[1], rule.rot_transformation_func(self.rot_amplitudes[slices]), )
[docs] def check_rule(self, rule, position): """ Check if a rule can be applied to a region of the state. **Arguments** rule : ContinuousMCRule Continuous MC rule to be applied to a region of the state. position : tuple of ints Position in the time-qubit lattice where the rule is tested. **Returns** : (bool, bool) The first bool indicates if the rule can be applied. The second bool indicates the direction of the rule, always True for continuous rules. """ slices = tuple( slice(position[i], position[i] + rule.shape[i]) for i in range(self.dim) ) sub_circ = self.vector[slices] sub_circ_amplitudes = self.rot_amplitudes[slices] if rule.shape == sub_circ.shape: if np.all( np.equal(sub_circ, rule.state_a) | rule.any_mask ) and rule.amplitudes_condition(sub_circ_amplitudes): return (True, True) return (False, False)
[docs] def to_abstract(self, name): """ Returns the circuit as an abstract circuit. **Arguments** name : str Name of the circuit. **Returns** AbstractCircuitState Abstract representation of the circuit. """ return AbstractCircuitState( continuous_array_ints_to_strings(self.vector), name, rot_amplitudes_array=self.rot_amplitudes, )