




import numpy as np
import qiskit

def  UpperCAmelCase_							(				__UpperCAmelCase						:     int = 8      ,      __UpperCAmelCase						:     int | None = None							) ->			str:
      SCREAMING_SNAKE_CASE_ :    Tuple     						=      np.random.default_rng(seed=UpperCAmelCase__							)

      # Roughly 25% of the qubits will contribute to the key.
      # So we take more than we need.
      SCREAMING_SNAKE_CASE_ :    Dict     						=      6 * key_len
      # Measurement basis for Alice's qubits.
      SCREAMING_SNAKE_CASE_ :    int     						=      rng.integers(2      ,      size=UpperCAmelCase__							)
      # The set of states Alice will prepare.
      SCREAMING_SNAKE_CASE_ :    Dict     						=      rng.integers(2      ,      size=UpperCAmelCase__							)
      # Measurement basis for Bob's qubits.
      SCREAMING_SNAKE_CASE_ :    Tuple     						=      rng.integers(2      ,      size=UpperCAmelCase__							)

      # Quantum Circuit to simulate BB84
      SCREAMING_SNAKE_CASE_ :    int     						=      qiskit.QuantumCircuit(UpperCAmelCase__      ,      name='BB84'							)

      # Alice prepares her qubits according to rules above.
      for index, _ in enumerate(UpperCAmelCase__							):
            if alice_state[index] == 1:
                  bbaa_circ.x(UpperCAmelCase__							)
            if alice_basis[index] == 1:
                  bbaa_circ.h(UpperCAmelCase__							)
      bbaa_circ.barrier()

      # Bob measures the received qubits according to rules above.
      for index, _ in enumerate(UpperCAmelCase__							):
            if bob_basis[index] == 1:
                  bbaa_circ.h(UpperCAmelCase__							)

      bbaa_circ.barrier()
      bbaa_circ.measure_all()

      # Simulate the quantum circuit.
      SCREAMING_SNAKE_CASE_ :    int     						=      qiskit.Aer.get_backend('aer_simulator'							)
      # We only need to run one shot because the key is unique.
      # Multiple shots will produce the same key.
      SCREAMING_SNAKE_CASE_ :    List[str]     						=      qiskit.execute(UpperCAmelCase__      ,      UpperCAmelCase__      ,      shots=1      ,      seed_simulator=UpperCAmelCase__							)
      # Returns the result of measurement.
      SCREAMING_SNAKE_CASE_ :    Any     						=      job.result().get_counts(UpperCAmelCase__							).most_frequent()

      # Extracting the generated key from the simulation results.
      # Only keep measurement results where Alice and Bob chose the same basis.
      SCREAMING_SNAKE_CASE_ :    Union[str, Any]     						=      """""".join(
          [
              result_bit
              for alice_basis_bit, bob_basis_bit, result_bit in zip(
                  UpperCAmelCase__      ,      UpperCAmelCase__      ,      UpperCAmelCase__							)
              if alice_basis_bit == bob_basis_bit
          ]							)

      # Get final key. Pad with 0 if too short, otherwise truncate.
      SCREAMING_SNAKE_CASE_ :    Union[str, Any]     						=      gen_key[:key_len] if len(UpperCAmelCase__							) >= key_len else gen_key.ljust(UpperCAmelCase__      ,      '0'							)
      return key


if __name__ == "__main__":
   print(f'''The generated key is : {bbaa(8, seed=0)}''')
   from doctest import testmod

   testmod()





import gc
import threading
import time

import psutil
import torch





class 	lowerCamelCase_							:


 '''simple docstring'''







 def __init__(							self  :		Optional[Any]						):
       SCREAMING_SNAKE_CASE_     						=      psutil.Process()
       SCREAMING_SNAKE_CASE_     						=      False



 def 				lowerCAmelCase_  (							self  :		Dict						):
       SCREAMING_SNAKE_CASE_     						=      -1

       while True:
             SCREAMING_SNAKE_CASE_     						=      max(self.process.memory_info().rss    ,    self.cpu_memory_peak						)

             # can't sleep or will not catch the peak right (this comment is here on purpose)
             if not self.peak_monitoring:
                   break



 def 				lowerCAmelCase_  (							self  :		List[str]						):
       SCREAMING_SNAKE_CASE_     						=      True
       SCREAMING_SNAKE_CASE_     						=      threading.Thread(target=self.peak_monitor						)
       SCREAMING_SNAKE_CASE_     						=      True
       self.thread.start()




 def 				lowerCAmelCase_  (							self  :		List[str]						):
       SCREAMING_SNAKE_CASE_     						=      False
       self.thread.join()
       return self.cpu_memory_peak


lowerCamelCase__			:       List[str]         =							PeakCPUMemory()

def  UpperCAmelCase_							(				) ->			Tuple:
      # Time
      SCREAMING_SNAKE_CASE_     						=      {'time': time.time()}

      gc.collect()
      torch.cuda.empty_cache()

      # CPU mem
      SCREAMING_SNAKE_CASE_     						=      psutil.Process().memory_info().rss
      cpu_peak_tracker.start()

      # GPU mem
      for i in range(torch.cuda.device_count()							):
            SCREAMING_SNAKE_CASE_     						=      torch.cuda.memory_allocated(__UpperCAmelCase							)
      torch.cuda.reset_peak_memory_stats()

      return measures

def  UpperCAmelCase_							(				__UpperCAmelCase						:     Optional[int]							) ->			Optional[Any]:
      # Time
      SCREAMING_SNAKE_CASE_     						=      {'time': time.time() - start_measures['time']}

      gc.collect()
      torch.cuda.empty_cache()

      # CPU mem
      SCREAMING_SNAKE_CASE_     						=      (psutil.Process().memory_info().rss - start_measures['cpu']) / 2**20
      SCREAMING_SNAKE_CASE_     						=      (cpu_peak_tracker.stop() - start_measures['cpu']) / 2**20

      # GPU mem
      for i in range(torch.cuda.device_count()							):
            SCREAMING_SNAKE_CASE_     						=      (torch.cuda.memory_allocated(__UpperCAmelCase							) - start_measures[str(__UpperCAmelCase							)]) / 2**20
            SCREAMING_SNAKE_CASE_     						=      (torch.cuda.max_memory_allocated(__UpperCAmelCase							) - start_measures[str(__UpperCAmelCase							)]) / 2**20

      return measures





def  UpperCAmelCase_							(				__UpperCAmelCase						:     Optional[int]      ,      __UpperCAmelCase						:     Any							) ->			Optional[Any]:
      print(f"{description}:"							)
      print(f"- Time: {measures['time']:.2f}s"							)
      for i in range(torch.cuda.device_count()							):
            print(f"- GPU {i} allocated: {measures[str(__UpperCAmelCase							)]:.2f}MiB"							)
            SCREAMING_SNAKE_CASE_     						=      measures[f"{i}-peak"]
            print(f"- GPU {i} peak: {peak:.2f}MiB"							)
      print(f"- CPU RAM allocated: {measures['cpu']:.2f}MiB"							)
      print(f"- CPU RAM peak: {measures['cpu-peak']:.2f}MiB"							)