def dense(self): """Creates the equivalent :class:`qibo.abstractions.hamiltonians.MatrixHamiltonian`.""" if self._dense is None: log.warning("Calculating the dense form of a symbolic Hamiltonian. " "This operation is memory inefficient.") self.dense = self.calculate_dense() return self._dense
def state_vector_call(self, state): if self.evolution is None: raise_error( ValueError, "Gap callback can only be used in " "adiabatic evolution models.") hamiltonian = self.evolution.hamiltonian() # Call the eigenvectors so that they are cached for the ``exp`` call hamiltonian.eigenvectors() eigvals = hamiltonian.eigenvalues() if isinstance(self.mode, int): return K.real(eigvals[self.mode]) # case: self.mode == "gap" excited = 1 gap = K.real(eigvals[excited] - eigvals[0]) if not self.check_degenerate: return gap while K.less(gap, EIGVAL_CUTOFF): gap = K.real(eigvals[excited] - eigvals[0]) excited += 1 if excited > 1: log.warning("The Hamiltonian is degenerate. Using eigenvalue {} " "to calculate gap.".format(excited)) return gap
def iterative_grover(self, lamda_value=6/5): """Iterative approach of Grover for when the number of solutions is not known. Args: lamda_value (real): parameter that controls the evolution of the iterative method. Must be between 1 and 4/3. Returns: measured (str): bitstring measured and checked as a valid solution. total_iterations (int): number of times the oracle has been called. """ k = 1 lamda = lamda_value total_iterations = 0 while True: it = np.random.randint(k + 1) if it != 0: total_iterations += it circuit = self.circuit(it) result = circuit(nshots=1) measured = result.frequencies(binary=True).most_common(1)[0][0] if self.check(measured, *self.check_args): return measured, total_iterations k = min(lamda * k, np.sqrt(self.sup_size)) if total_iterations > (9/4) * np.sqrt(self.sup_size): log.warning("Too many total iterations, output might not be solution.") return measured, total_iterations
def set_threads(self, nthreads): log.warning( "`set_threads` is not supported by the tensorflow " "backend. Please use tensorflow's thread setters: " "`tf.config.threading.set_inter_op_parallelism_threads` " "or `tf.config.threading.set_intra_op_parallelism_threads` " "to switch the number of threads.") abstract.AbstractBackend.set_threads(self, nthreads)
def newtonian(loss, initial_parameters, args=(), method='Powell', options=None, processes=None): """Newtonian optimization approaches based on ``scipy.optimize.minimize``. For more details check the `scipy documentation <https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html>`_. .. note:: When using the method ``parallel_L-BFGS-B`` the ``processes`` option controls the number of processes used by the parallel L-BFGS-B algorithm through the ``multiprocessing`` library. By default ``processes=None``, in this case the total number of logical cores are used. Make sure to select the appropriate number of processes for your computer specification, taking in consideration memory and physical cores. In order to obtain optimal results you can control the number of threads used by each process with the ``qibo.set_threads`` method. For example, for small-medium size circuits you may benefit from single thread per process, thus set ``qibo.set_threads(1)`` before running the optimization. Args: loss (callable): Loss as a function of variational parameters to be optimized. initial_parameters (np.ndarray): Initial guess for the variational parameters. args (tuple): optional arguments for the loss function. method (str): Name of method supported by ``scipy.optimize.minimize`` and ``'parallel_L-BFGS-B'`` for a parallel version of L-BFGS-B algorithm. options (dict): Dictionary with options accepted by ``scipy.optimize.minimize``. processes (int): number of processes when using the parallel BFGS method. """ if method == 'parallel_L-BFGS-B': import psutil from qibo.config import raise_error, get_device, get_threads, log if "GPU" in get_device(): # pragma: no cover raise_error(RuntimeError, "Parallel L-BFGS-B cannot be used with GPU.") if ((processes is not None and processes * get_threads() > psutil.cpu_count()) or (processes is None and get_threads() != 1)): # pragma: no cover log.warning( 'Please consider using a lower number of threads per process,' ' or reduce the number of processes for better performance') o = ParallelBFGS(loss, args=args, options=options, processes=processes) m = o.run(initial_parameters) else: from scipy.optimize import minimize m = minimize(loss, initial_parameters, args=args, method=method, options=options) return m.fun, m.x
def set_precision(dtype="double"): """Set precision for states and gates simulation. Args: dtype (str): possible options are 'single' for single precision (complex64) and 'double' for double precision (complex128). """ if not config.ALLOW_SWITCHERS and dtype != K.precision: log.warning("Precision should not be changed after allocating gates.") for bk in K.constructed_backends.values(): bk.set_precision(dtype)
def initialize_tensorflow(self): """Initializes active Tensorflow backend (if available).""" os.environ["TF_CPP_MIN_LOG_LEVEL"] = str(config.LOG_LEVEL) import tensorflow as tf import qibo.tensorflow.custom_operators as op if not op._custom_operators_loaded: # pragma: no cover log.warning("Einsum will be used to apply gates with Tensorflow. " "Removing custom operators from available backends.") self.available_backends.pop("custom") self.available_backends[ "tensorflow"] = TensorflowDefaultEinsumBackend self.active_backend = "tensorflow"
def set_device(name): """Set default execution device. Args: name (str): Device name. Should follow the pattern '/{device type}:{device number}' where device type is one of CPU or GPU. """ if not config.ALLOW_SWITCHERS and name != K.default_device: log.warning("Device should not be changed after allocating gates.") K.set_device(name) for bk in K.constructed_backends.values(): if bk.name != "numpy" and bk != K.active_backend: bk.set_device(name)
def initialize_numpy(self): # pragma: no cover """Initializes active numpy backend (if Tensorflow is not available).""" # case not tested because CI has tf installed log.warning("Tensorflow is not installed. Falling back to numpy. " "Numpy does not support Qibo custom operators and GPU. " "Einsum will be used to apply gates on CPU.") # remove Tensorflow backends self.available_backends.pop("custom") self.available_backends.pop("tensorflow") self.available_backends.pop("tensorflow_defaulteinsum") self.available_backends.pop("tensorflow_matmuleinsum") # use numpy for defaulteinsum and matmuleinsum backends self.available_backends["defaulteinsum"] = NumpyDefaultEinsumBackend self.available_backends["matmuleinsum"] = NumpyMatmulEinsumBackend self.active_backend = "numpy"
def _check_parallel_configuration(processes): """Check if configuration is suitable for efficient parallel execution.""" import psutil from qibo import get_device from qibo.config import raise_error, get_threads, log device = get_device() if device is not None and "GPU" in device: # pragma: no cover raise_error(RuntimeError, "Parallel evaluations cannot be used with GPU.") if ((processes is not None and processes * get_threads() > psutil.cpu_count()) or (processes is None and get_threads() != 1)): # pragma: no cover log.warning( 'Please consider using a lower number of threads per process,' ' or reduce the number of processes for better performance')
def set_backend(backend="qibojit"): """Sets backend used for mathematical operations and applying gates. The following backends are available: 'qibojit': Numba/cupy backend with custom operators for applying gates, 'qibotf': Tensorflow backend with custom operators for applying gates, 'tensorflow': Tensorflow backend that applies gates using ``tf.einsum``, 'numpy': Numpy backend that applies gates using ``np.einsum``. Args: backend (str): A backend from the above options. """ if not config.ALLOW_SWITCHERS and backend != K.name: log.warning("Backend should not be changed after allocating gates.") K.active_backend = backend K.show_config()
def set_backend(backend="qibojit", platform=None): """Sets backend used for mathematical operations and applying gates. The following backends are available: 'qibojit': Numba/cupy backend with custom operators for applying gates, 'tensorflow': Tensorflow backend that applies gates using ``tf.einsum``, 'numpy': Numpy backend that applies gates using ``np.einsum``. Args: backend (str): A backend from the above options. platform (str): Optional platform specification for backends that support this. For example, the 'qibojit' backend supports two platforms ('cupy', 'cuquantum') when used with GPU. """ if not config.ALLOW_SWITCHERS and backend != K.name: log.warning("Backend should not be changed after allocating gates.") K.active_backend = K.construct_backend(backend) if platform is not None: K.set_platform(platform) K.show_config()
def from_symbolic(cls, symbolic_hamiltonian, symbol_map): """Creates a ``Hamiltonian`` from a symbolic Hamiltonian. We refer to the :ref:`How to define custom Hamiltonians using symbols? <symbolicham-example>` example for more details. Args: symbolic_hamiltonian (sympy.Expr): The full Hamiltonian written with symbols. symbol_map (dict): Dictionary that maps each symbol that appears in the Hamiltonian to a pair of (target, matrix). Returns: A :class:`qibo.abstractions.hamiltonians.SymbolicHamiltonian` object that implements the Hamiltonian represented by the given symbolic expression. """ log.warning( "`Hamiltonian.from_symbolic` and the use of symbol maps is " "deprecated. Please use `SymbolicHamiltonian` and Qibo symbols " "to construct Hamiltonians using symbols.") return SymbolicHamiltonian(symbolic_hamiltonian, symbol_map)
def _check_parallel_configuration(processes): # pragma: no cover """Check if configuration is suitable for efficient parallel execution.""" import sys, psutil from qibo import get_device, get_backend, get_threads from qibo.config import raise_error, log device = get_device() if sys.platform == "win32" or sys.platform == 'darwin': # pragma: no cover raise_error(RuntimeError, "Parallel evaluations supported only on linux.") if get_backend() == "tensorflow": # pragma: no cover raise_error( RuntimeError, f"{get_backend()} backend does not support parallel evaluations.") if device is not None and "GPU" in device: # pragma: no cover raise_error(RuntimeError, "Parallel evaluations cannot be used with GPU.") if ((processes is not None and processes * get_threads() > psutil.cpu_count()) or (processes is None and get_threads() != 1)): # pragma: no cover log.warning( 'Please consider using a lower number of threads per process,' ' or reduce the number of processes for better performance')
def __init__(self): self.available_backends = {} self.hardware_backends = {} active_backend = "numpy" # load profile from default file from pathlib import Path profile_path = Path( os.environ.get('QIBO_PROFILE', Path(__file__).parent / "profiles.yml")) try: with open(profile_path) as f: import yaml profile = yaml.safe_load(f) except FileNotFoundError: # pragma: no cover raise_error(FileNotFoundError, f"Profile file {profile_path} not found.") # check if numpy is installed if self.check_availability("numpy"): from qibo.backends.numpy import NumpyBackend self.available_backends["numpy"] = NumpyBackend else: # pragma: no cover raise_error( ModuleNotFoundError, "Numpy is not installed. " "Please install it using " "`pip install numpy`.") for backend in profile.get('backends'): name = backend.get('name') if self.check_availability(name): if name == 'tensorflow' or name == 'qibotf': os.environ["TF_CPP_MIN_LOG_LEVEL"] = str( config.TF_LOG_LEVEL) import tensorflow as tf # pylint: disable=E0401 if tf.__version__ < TF_MIN_VERSION: # pragma: no cover raise_error( RuntimeError, f"TensorFlow version not supported, minimum is {TF_MIN_VERSION}." ) import importlib custom_backend = getattr( importlib.import_module(backend.get('from')), backend.get('class')) self.available_backends[name] = custom_backend if backend.get('is_hardware', False): # pragma: no cover self.hardware_backends[name] = custom_backend if profile.get('default') == name: active_backend = name self.constructed_backends = {} self._active_backend = None self.qnp = self.construct_backend("numpy") # Create the default active backend if "QIBO_BACKEND" in os.environ: # pragma: no cover self.active_backend = os.environ.get("QIBO_BACKEND") else: self.active_backend = active_backend # raise performance warning if qibojit and qibotf are not available self.show_config() if active_backend == "numpy": # pragma: no cover log.warning( "numpy backend uses `np.einsum` and supports CPU only. " "Consider installing the qibojit or qibotf backends for " "increased performance and to enable GPU acceleration.") elif active_backend == "tensorflow": # pragma: no cover # case not tested because CI has tf installed log.warning("qibotf library was not found. `tf.einsum` will be " "used to apply gates. In order to install Qibo's " "high performance custom operators for TensorFlow " "please use `pip install qibotf`. Alternatively, " "consider installing the qibojit backend.")
def __init__(self): # load profile from default file from pathlib import Path profile_path = Path(os.environ.get( 'QIBO_PROFILE', Path(__file__).parent / "profiles.yml")) try: with open(profile_path) as f: import yaml self.profile = yaml.safe_load(f) except FileNotFoundError: # pragma: no cover raise_error(FileNotFoundError, f"Profile file {profile_path} not found.") # dictionary to cache if backends are available # used by ``self.check_availability`` self._availability = {} # create numpy backend (is always available as numpy is a requirement) if self.check_availability("numpy", check_version=False): from qibo.backends.numpy import NumpyBackend self.qnp = NumpyBackend() else: # pragma: no cover raise_error(ModuleNotFoundError, "Numpy is not installed. " "Please install it using " "`pip install numpy`.") # loading backend names and version self._backends_min_version = {backend.get("name"): backend.get("minimum_version") for backend in self.profile.get("backends")} # find the default backend name default_backend = os.environ.get('QIBO_BACKEND', self.profile.get('default')) # check if default backend is described in the profile file if default_backend != "numpy": if default_backend not in (backend.get("name") for backend in self.profile.get("backends")): # pragma: no cover raise_error(ModuleNotFoundError, f"Default backend {default_backend} not set in {profile_path}.") # change the default backend if it is not available if not self.check_availability(default_backend): # pragma: no cover # set the default backend to the first available declared backend in the profile file # if none is available it falls back to numpy for backend in self.profile.get('backends'): name = backend.get('name') if self.check_availability(name) and not backend.get('is_hardware'): # excluding hardware backends from default for development # convenience until proper hardware testing is implemented default_backend = name break # make numpy default if no other backend is available default_backend = "numpy" self.active_backend = None self.constructed_backends = {"numpy": self.qnp} self.hardware_backends = {} # set default backend as active self.active_backend = self.construct_backend(default_backend) # raise performance warning if qibojit is not available self.show_config() if str(self) == "numpy": # pragma: no cover log.warning("numpy backend uses `np.einsum` and supports CPU only. " "Consider installing the qibojit backend for " "increased performance and to enable GPU acceleration.") elif str(self) == "tensorflow": # pragma: no cover # case not tested because CI has tf installed log.warning("tensorflow backend uses `tf.einsum` " "to apply gates. In order to install Qibo's " "high performance custom operators " "please use `pip install qibojit`.")
def eigvalsh(self, x, k=6): if self.issparse(x): log.warning("Calculating sparse matrix eigenvectors because " "sparse modules do not provide ``eigvals`` method.") return self.eigh(x, k=k)[0] return self.backend.linalg.eigvalsh(x)
"""TensorFlow custom operator for Tensor initial state.""" from qibo.config import log _custom_operators_loaded = False try: import tensorflow as tf try: from qibo.tensorflow.custom_operators.python.ops.qibo_tf_custom_operators import initial_state from qibo.tensorflow.custom_operators.python.ops.qibo_tf_custom_operators import transpose_state from qibo.tensorflow.custom_operators.python.ops.qibo_tf_custom_operators import swap_pieces from qibo.tensorflow.custom_operators.python.ops.qibo_tf_custom_operators import apply_gate from qibo.tensorflow.custom_operators.python.ops.qibo_tf_custom_operators import apply_x from qibo.tensorflow.custom_operators.python.ops.qibo_tf_custom_operators import apply_y from qibo.tensorflow.custom_operators.python.ops.qibo_tf_custom_operators import apply_z from qibo.tensorflow.custom_operators.python.ops.qibo_tf_custom_operators import apply_z_pow from qibo.tensorflow.custom_operators.python.ops.qibo_tf_custom_operators import apply_two_qubit_gate from qibo.tensorflow.custom_operators.python.ops.qibo_tf_custom_operators import apply_fsim from qibo.tensorflow.custom_operators.python.ops.qibo_tf_custom_operators import apply_swap from qibo.tensorflow.custom_operators.python.ops.qibo_tf_custom_operators import collapse_state from qibo.tensorflow.custom_operators.python.ops.qibo_tf_custom_operators import measure_frequencies # Import gradients from qibo.tensorflow.custom_operators.python.ops.qibo_tf_custom_operators_grads import _initial_state_grad _custom_operators_loaded = True except tf.errors.NotFoundError: # pragma: no cover log.warning( "Custom operators not found, skipping custom operators load.") except ModuleNotFoundError: # pragma: no cover # case not tested because CI has tf installed log.warning("Tensorflow is not installed. Skipping custom operators load.")
def ground_state(self): if self._ground_state is None: log.warning("Ground state for this Hamiltonian was not given.") return self.eigenvectors()[:, 0] return self._ground_state()
# CI uses tensorflow as default backend K = NumpyBackend() else: # pragma: no cover raise_error( ValueError, "Environment variable `QIBO_BACKEND` has " "unknown value {}. Please select either " "`tensorflow` or `numpy`." "".format(_BACKEND_NAME)) else: try: os.environ["TF_CPP_MIN_LOG_LEVEL"] = str(config.LOG_LEVEL) import tensorflow as tf import qibo.tensorflow.custom_operators as op _CUSTOM_OPERATORS_LOADED = op._custom_operators_loaded if not _CUSTOM_OPERATORS_LOADED: # pragma: no cover log.warning("Removing custom operators from available backends.") AVAILABLE_BACKENDS.remove("custom") K = TensorflowBackend() except ModuleNotFoundError: # pragma: no cover # case not tested because CI has tf installed log.warning("Tensorflow is not installed. Falling back to numpy.") K = NumpyBackend() AVAILABLE_BACKENDS = [ b for b in AVAILABLE_BACKENDS if "tensorflow" not in b ] AVAILABLE_BACKENDS.remove("custom") K.qnp = numpy_backend _BACKEND_NAME = K.name
def set_threads(self, nthreads): log.warning("Numpy backend supports only single-thread execution. " "Cannot change the number of threads.") abstract.AbstractBackend.set_threads(self, nthreads)
def set_device(self, name): log.warning("Numpy does not support device placement. " "Aborting device change.")