





"""simple docstring"""
import inspect
import os
from argparse import ArgumentParser, Namespace
from importlib import import_module

import huggingface_hub
import numpy as np
from packaging import version

from .. import (
    FEATURE_EXTRACTOR_MAPPING,
    IMAGE_PROCESSOR_MAPPING,
    PROCESSOR_MAPPING,
    TOKENIZER_MAPPING,
    AutoConfig,
    AutoFeatureExtractor,
    AutoImageProcessor,
    AutoProcessor,
    AutoTokenizer,
    is_datasets_available,
    is_tf_available,
    is_torch_available,
)
from ..utils import TF2_WEIGHTS_INDEX_NAME, TF2_WEIGHTS_NAME, logging
from . import BaseTransformersCLICommand


if is_tf_available():
       import tensorflow as tf

       tf.config.experimental.enable_tensor_float_aa_execution(False)

if is_torch_available():
       import torch

if is_datasets_available():
       from datasets import load_dataset


__UpperCamelCase    :	Dict						=     5e-5  # larger error tolerance than in our internal tests, to avoid flaky user-facing errors

def 		_SCREAMING_SNAKE_CASE (_UpperCAmelCase				:						Union[str, Any] ):
 return PTtoTFCommand(
     args.model_name     ,    args.local_dir     ,    args.max_error     ,    args.new_weights     ,    args.no_pr     ,    args.push     ,    args.extra_commit_description     ,    args.override_model_class     ,    )







class 			a       (      _SCREAMING_SNAKE_CASE     ):



 @staticmethod
 def       UpperCamelCase__     (    _snake_case    ):







  """simple docstring"""






  lowerCAmelCase													=   parser.add_parser(
      'pt-to-tf'							,					help=(
          'CLI tool to run convert a transformers model from a PyTorch checkpoint to a TensorFlow checkpoint.'
          ' Can also be used to validate existing weights without opening PRs, with --no-pr.'
      )							,					)
  train_parser.add_argument(
      '--model-name'							,					type=__A							,					required=__A							,					help='The model name, including owner/organization, as seen on the hub.'							,					)
  train_parser.add_argument(
      '--local-dir'							,					type=__A							,					default=''							,					help='Optional local directory of the model repository. Defaults to /tmp/{model_name}'							,					)
  train_parser.add_argument(
      '--max-error'							,					type=__A							,					default=__A							,					help=(
          F'Maximum error tolerance. Defaults to {MAX_ERROR}. This flag should be avoided, use at your own risk.'
      )							,					)
  train_parser.add_argument(
      '--new-weights'							,					action='store_true'							,					help='Optional flag to create new TensorFlow weights, even if they already exist.'							,					)
  train_parser.add_argument(
      '--no-pr'							,					action='store_true'							,					help='Optional flag to NOT open a PR with converted weights.'    )
  train_parser.add_argument(
      '--push'							,					action='store_true'							,					help='Optional flag to push the weights directly to `main` (requires permissions)'							,					)
  train_parser.add_argument(
      '--extra-commit-description'							,					type=__A							,					default=''							,					help='Optional additional commit description to use when opening a PR (e.g. to tag the owner).'							,					)
  train_parser.add_argument(
      '--override-model-class'							,					type=__A							,					default=__A							,					help='If you think you know better than the auto-detector, you can specify the model class here. '
      'Can be either an AutoModel class or a specific model class like BertForSequenceClassification.'							,					)
  train_parser.set_defaults(func=__A    )



 @staticmethod
 def       UpperCamelCase__     (    _snake_case							,					_snake_case    ):







  """simple docstring"""






  lowerCAmelCase													=   set(pt_outputs.keys()    )
  lowerCAmelCase													=   set(tf_outputs.keys()    )
  if pt_out_attrs != tf_out_attrs:
   raise ValueError(
       F'The model outputs have different attributes, aborting. (Pytorch: {pt_out_attrs}, TensorFlow:'
       F' {tf_out_attrs})'    )

  # 2. For each output attribute, computes the difference
  def _find_pt_tf_differences(_snake_case							,					_snake_case							,					_snake_case							,					_snake_case=""    ):
   # If the current attribute is a tensor, it is a leaf and we make the comparison. Otherwise, we will dig in
   # recursivelly, keeping the name of the attribute.
   if isinstance(__A							,					torch.Tensor    ):
    lowerCAmelCase													=   np.max(np.abs(pt_out.numpy() - tf_out.numpy()    )    )
    lowerCAmelCase													=   tensor_difference
   else:
    lowerCAmelCase													=   attr_name
    for i, pt_item in enumerate(__A    ):
     # If it is a named attribute, we keep the name. Otherwise, just its index.
     if isinstance(__A							,					__A    ):
      lowerCAmelCase													=   root_name + pt_item
      lowerCAmelCase													=   tf_out[pt_item]
      lowerCAmelCase													=   pt_out[pt_item]
     else:
      lowerCAmelCase													=   root_name + F'[{i}]'
      lowerCAmelCase													=   tf_out[i]
     lowerCAmelCase													=   _find_pt_tf_differences(__A							,					__A							,					__A							,					__A    )

   return differences

  return _find_pt_tf_differences(__A							,					__A							,					{}    )



 def __init__(    self							,					_snake_case							,					_snake_case							,					_snake_case							,					_snake_case							,					_snake_case							,					_snake_case							,					_snake_case							,					_snake_case							,					*_snake_case							,					):







  """simple docstring"""






  lowerCAmelCase													=   logging.get_logger('transformers-cli/pt_to_tf'    )
  lowerCAmelCase													=   model_name
  lowerCAmelCase													=   local_dir if local_dir else os.path.join('/tmp'							,					__A    )
  lowerCAmelCase													=   max_error
  lowerCAmelCase													=   new_weights
  lowerCAmelCase													=   no_pr
  lowerCAmelCase													=   push
  lowerCAmelCase													=   extra_commit_description
  lowerCAmelCase													=   override_model_class



 def       UpperCamelCase__     (    self							,					_snake_case							,					_snake_case							,					_snake_case    ):







  """simple docstring"""






  def _get_audio_input():
   lowerCAmelCase													=   load_dataset('hf-internal-testing/librispeech_asr_dummy'							,					'clean'							,					split='validation'    )
   lowerCAmelCase													=   ds.sort('id'    ).select(range(2    )    )[:2]['audio']
   lowerCAmelCase													=   [x['array'] for x in speech_samples]
   return raw_samples

  lowerCAmelCase													=   type(pt_model.config    )
  if model_config_class in PROCESSOR_MAPPING:
   lowerCAmelCase													=   AutoProcessor.from_pretrained(self._local_dir    )
   if model_config_class in TOKENIZER_MAPPING and processor.tokenizer.pad_token is None:
    lowerCAmelCase													=   processor.tokenizer.eos_token
  elif model_config_class in IMAGE_PROCESSOR_MAPPING:
   lowerCAmelCase													=   AutoImageProcessor.from_pretrained(self._local_dir    )
  elif model_config_class in FEATURE_EXTRACTOR_MAPPING:
   lowerCAmelCase													=   AutoFeatureExtractor.from_pretrained(self._local_dir    )
  elif model_config_class in TOKENIZER_MAPPING:
   lowerCAmelCase													=   AutoTokenizer.from_pretrained(self._local_dir    )
   if processor.pad_token is None:
    lowerCAmelCase													=   processor.eos_token
  else:
   raise ValueError(F'Unknown data processing type (model config type: {model_config_class})'    )

  lowerCAmelCase													=   set(inspect.signature(pt_model.forward    ).parameters.keys()    )
  lowerCAmelCase													=   {}
  if "input_ids" in model_forward_signature:
   processor_inputs.update(
       {
           'text': ['Hi there!', 'I am a batch with more than one row and different input lengths.'],
           'padding': True,
           'truncation': True,
       }    )
  if "pixel_values" in model_forward_signature:
   lowerCAmelCase													=   load_dataset('cifar10'							,					'plain_text'							,					split='test'    )[:2]['img']
   processor_inputs.update({'images': sample_images}    )
  if "input_features" in model_forward_signature:
   lowerCAmelCase													=   inspect.signature(processor.feature_extractor    ).parameters
   # Pad to the largest input length by default but take feature extractor default
   # padding value if it exists e.g. "max_length" and is not False or None
   if "padding" in feature_extractor_signature:
    lowerCAmelCase													=   feature_extractor_signature['padding'].default
    if default_strategy is not False and default_strategy is not None:
     lowerCAmelCase													=   default_strategy
    else:
     lowerCAmelCase													=   True
   else:
    lowerCAmelCase													=   True
   processor_inputs.update({'audio': _get_audio_input(), 'padding': padding_strategy}    )
  if "input_values" in model_forward_signature:  # Wav2Vec2 audio input
   processor_inputs.update({'audio': _get_audio_input(), 'padding': True}    )
  lowerCAmelCase													=   processor(**__A							,					return_tensors='pt'    )
  lowerCAmelCase													=   processor(**__A							,					return_tensors='tf'    )

  # Extra input requirements, in addition to the input modality
  if (
      config.is_encoder_decoder
      or (hasattr(__A							,					'encoder'    ) and hasattr(__A							,					'decoder'    ))
      or "decoder_input_ids" in tf_dummy_inputs
  ):
   lowerCAmelCase													=   np.asarray([[1], [1]]							,					dtype=__A    ) * (pt_model.config.decoder_start_token_id or 0)
   pt_input.update({'decoder_input_ids': torch.tensor(__A    )}    )
   tf_input.update({'decoder_input_ids': tf.convert_to_tensor(__A    )}    )

  return pt_input, tf_input



 def       UpperCamelCase__     (    self    ):







  """simple docstring"""






  if version.parse(huggingface_hub.__version__    ) < version.parse('0.9.0'    ):
   raise ImportError(
       'The huggingface_hub version must be >= 0.9.0 to use this command. Please update your huggingface_hub'
       ' installation.'    )
  else:
   from huggingface_hub import Repository, create_commit
   from huggingface_hub._commit_api import CommitOperationAdd

  # Fetch remote data
  lowerCAmelCase													=   Repository(local_dir=self._local_dir							,					clone_from=self._model_name    )

  # Load config and get the appropriate architecture -- the latter is needed to convert the head's weights
  lowerCAmelCase													=   AutoConfig.from_pretrained(self._local_dir    )
  lowerCAmelCase													=   config.architectures
  if self._override_model_class is not None:
   if self._override_model_class.startswith('TF'    ):
    lowerCAmelCase													=   [self._override_model_class[2:]]
   else:
    lowerCAmelCase													=   [self._override_model_class]
   try:
    lowerCAmelCase													=   getattr(import_module('transformers'    )							,					architectures[0]    )
   except AttributeError:
    raise ValueError(F'Model class {self._override_model_class} not found in transformers.'    )
   try:
    lowerCAmelCase													=   getattr(import_module('transformers'    )							,					'TF' + architectures[0]    )
   except AttributeError:
    raise ValueError(F'TF model class TF{self._override_model_class} not found in transformers.'    )
  elif architectures is None:  # No architecture defined -- use auto classes
   lowerCAmelCase													=   getattr(import_module('transformers'    )							,					'AutoModel'    )
   lowerCAmelCase													=   getattr(import_module('transformers'    )							,					'TFAutoModel'    )
   self._logger.warning('No detected architecture, using AutoModel/TFAutoModel'    )
  else:  # Architecture defined -- use it
   if len(__A    ) > 1:
    raise ValueError(F'More than one architecture was found, aborting. (architectures = {architectures})'    )
   self._logger.warning(F'Detected architecture: {architectures[0]}'    )
   lowerCAmelCase													=   getattr(import_module('transformers'    )							,					architectures[0]    )
   try:
    lowerCAmelCase													=   getattr(import_module('transformers'    )							,					'TF' + architectures[0]    )
   except AttributeError:
    raise AttributeError(F'The TensorFlow equivalent of {architectures[0]} doesn\'t exist in transformers.'    )

        # Check the TF dummy inputs to see what keys we need in the forward pass
  lowerCAmelCase													=   tf_class.from_config(__A    )
  lowerCAmelCase													=   tf_from_pt_model.dummy_inputs

  del tf_from_pt_model  # Try to keep only one model in memory at a time

  # Load the model and get some basic inputs
  lowerCAmelCase													=   pt_class.from_pretrained(self._local_dir    )
  pt_model.eval()

  lowerCAmelCase							,lowerCAmelCase													=   self.get_inputs(__A							,					__A							,					__A    )

  with torch.no_grad():
   lowerCAmelCase													=   pt_model(**__A							,					output_hidden_states=__A    )
  del pt_model  # will no longer be used, and may have a large memory footprint

  lowerCAmelCase													=   tf_class.from_pretrained(self._local_dir							,					from_pt=__A    )
  lowerCAmelCase													=   tf_from_pt_model(**__A							,					output_hidden_states=__A							,					training=__A    )

  # Confirms that cross loading PT weights into TF worked.
  lowerCAmelCase													=   self.find_pt_tf_differences(__A							,					__A    )
  lowerCAmelCase													=   {k: v for k, v in crossload_differences.items() if 'hidden' not in k}
  lowerCAmelCase													=   {k: v for k, v in crossload_differences.items() if 'hidden' in k}
  if len(__A    ) == 0 and architectures is not None:
   raise ValueError(
       F'Something went wrong -- the config file has architectures ({architectures}), but no model head'
       ' output was found. All outputs start with \'hidden\''    )
  lowerCAmelCase													=   max(output_differences.values()    ) if output_differences else 0.0
  lowerCAmelCase													=   max(hidden_differences.values()    )
  if max_crossload_output_diff > self._max_error or max_crossload_hidden_diff > self._max_error:
   raise ValueError(
       'The cross-loaded TensorFlow model has different outputs, something went wrong!\n'
       + F'\nList of maximum output differences above the threshold ({self._max_error}):\n'
       + '\n'.join([F'{k}: {v:.3e}' for k, v in output_differences.items() if v > self._max_error]    )
       + F'\n\nList of maximum hidden layer differences above the threshold ({self._max_error}):\n'
       + '\n'.join([F'{k}: {v:.3e}' for k, v in hidden_differences.items() if v > self._max_error]    )    )

  # Save the weights in a TF format (if needed) and confirms that the results are still good
  lowerCAmelCase													=   os.path.join(self._local_dir							,					__A    )
  lowerCAmelCase													=   os.path.join(self._local_dir							,					__A    )
  if (not os.path.exists(__A    ) and not os.path.exists(__A    )) or self._new_weights:
   tf_from_pt_model.save_pretrained(self._local_dir    )
  del tf_from_pt_model  # will no longer be used, and may have a large memory footprint

  lowerCAmelCase													=   tf_class.from_pretrained(self._local_dir    )
  lowerCAmelCase													=   tf_model(**__A							,					output_hidden_states=__A    )

  lowerCAmelCase													=   self.find_pt_tf_differences(__A							,					__A    )
  lowerCAmelCase													=   {k: v for k, v in conversion_differences.items() if 'hidden' not in k}
  lowerCAmelCase													=   {k: v for k, v in conversion_differences.items() if 'hidden' in k}
  if len(__A    ) == 0 and architectures is not None:
   raise ValueError(
       F'Something went wrong -- the config file has architectures ({architectures}), but no model head'
       ' output was found. All outputs start with \'hidden\''    )
  lowerCAmelCase													=   max(output_differences.values()    ) if output_differences else 0.0
  lowerCAmelCase													=   max(hidden_differences.values()    )
  if max_conversion_output_diff > self._max_error or max_conversion_hidden_diff > self._max_error:
   raise ValueError(
       'The converted TensorFlow model has different outputs, something went wrong!\n'
       + F'\nList of maximum output differences above the threshold ({self._max_error}):\n'
       + '\n'.join([F'{k}: {v:.3e}' for k, v in output_differences.items() if v > self._max_error]    )
       + F'\n\nList of maximum hidden layer differences above the threshold ({self._max_error}):\n'
       + '\n'.join([F'{k}: {v:.3e}' for k, v in hidden_differences.items() if v > self._max_error]    )    )

  lowerCAmelCase													=   'Update TF weights' if self._new_weights else 'Add TF weights'
  if self._push:
   repo.git_add(auto_lfs_track=__A    )
   repo.git_commit(__A    )
   repo.git_push(blocking=__A    )  # this prints a progress bar with the upload
   self._logger.warning(F'TF weights pushed into {self._model_name}'    )
  elif not self._no_pr:
   self._logger.warning('Uploading the weights into a new PR...'    )
   lowerCAmelCase													=   (
       'Model converted by the [`transformers`\' `pt_to_tf`'
       ' CLI](https://github.com/huggingface/transformers/blob/main/src/transformers/commands/pt_to_tf.py). '
       'All converted model outputs and hidden layers were validated against its PyTorch counterpart.\n\n'
       F'Maximum crossload output difference={max_crossload_output_diff:.3e}; '
       F'Maximum crossload hidden layer difference={max_crossload_hidden_diff:.3e};\n'
       F'Maximum conversion output difference={max_conversion_output_diff:.3e}; '
       F'Maximum conversion hidden layer difference={max_conversion_hidden_diff:.3e};\n'
   )
   if self._max_error > MAX_ERROR:
    commit_descrition += (
        F'\n\nCAUTION: The maximum admissible error was manually increased to {self._max_error}!'
    )
   if self._extra_commit_description:
    commit_descrition += "\n\n" + self._extra_commit_description

   # sharded model -> adds all related files (index and .h5 shards)
   if os.path.exists(__A    ):
    lowerCAmelCase													=   [
        CommitOperationAdd(path_in_repo=__A							,					path_or_fileobj=__A    )
    ]
    for shard_path in tf.io.gfile.glob(self._local_dir + '/tf_model-*.h5'    ):
     operations += [
         CommitOperationAdd(path_in_repo=os.path.basename(__A    )							,					path_or_fileobj=__A    )
     ]
   else:
    lowerCAmelCase													=   [CommitOperationAdd(path_in_repo=__A							,					path_or_fileobj=__A    )]

   lowerCAmelCase													=   create_commit(
       repo_id=self._model_name							,					operations=__A							,					commit_message=__A							,					commit_description=__A							,					repo_type='model'							,					create_pr=__A							,					).pr_url
   self._logger.warning(F'PR open in {hub_pr_url}'    )












"""simple docstring"""
import math
import sys







class 			a       :



 def __init__(    self    ):







  """simple docstring"""






  lowerCAmelCase													=   0
  lowerCAmelCase													=   []
  lowerCAmelCase													=   {}  # To store the pos of node in array



 def       UpperCamelCase__     (    self    ):







  """simple docstring"""






  return self.cur_size == 0



 def       UpperCamelCase__     (    self							,					_snake_case    ):







  """simple docstring"""






  lowerCAmelCase													=   self.left(_snake_case    )
  lowerCAmelCase													=   self.right(_snake_case    )
  if lc < self.cur_size and self.array(_snake_case    )[0] < self.array(_snake_case    )[0]:
   lowerCAmelCase													=   lc
  else:
   lowerCAmelCase													=   idx
  if rc < self.cur_size and self.array(_snake_case    )[0] < self.array(_snake_case    )[0]:
   lowerCAmelCase													=   rc
  if smallest != idx:
   self.swap(_snake_case							,					_snake_case    )
   self.min_heapify(_snake_case    )



 def       UpperCamelCase__     (    self							,					_snake_case    ):







  """simple docstring"""






  lowerCAmelCase													=   self.cur_size
  self.cur_size += 1
  self.array.append((sys.maxsize, tup[1])    )
  self.decrease_key((sys.maxsize, tup[1])							,					tup[0]    )



 def       UpperCamelCase__     (    self    ):







  """simple docstring"""






  lowerCAmelCase													=   self.array[0][1]
  lowerCAmelCase													=   self.array[self.cur_size - 1]
  self.cur_size -= 1
  self.min_heapify(1    )
  del self.pos[min_node]
  return min_node



 def       UpperCamelCase__     (    self							,					_snake_case    ):







  """simple docstring"""






  return 2 * i + 1



 def       UpperCamelCase__     (    self							,					_snake_case    ):







  """simple docstring"""






  return 2 * i + 2



 def       UpperCamelCase__     (    self							,					_snake_case    ):







  """simple docstring"""






  return math.floor(i / 2    )



 def       UpperCamelCase__     (    self							,					_snake_case							,					_snake_case    ):







  """simple docstring"""






  lowerCAmelCase													=   j
  lowerCAmelCase													=   i
  lowerCAmelCase													=   self.array[i]
  lowerCAmelCase													=   self.array[j]
  lowerCAmelCase													=   temp



 def       UpperCamelCase__     (    self							,					_snake_case							,					_snake_case    ):







  """simple docstring"""






  lowerCAmelCase													=   self.pos[tup[1]]
  # assuming the new_d is atmost old_d
  lowerCAmelCase													=   (new_d, tup[1])
  while idx > 0 and self.array[self.par(_snake_case    )][0] > self.array[idx][0]:
   self.swap(_snake_case							,					self.par(_snake_case    )    )
   lowerCAmelCase													=   self.par(_snake_case    )







class 			a       :



 def __init__(    self							,					_snake_case    ):







  """simple docstring"""






  lowerCAmelCase													=   {}  # To store graph: u -> (v,w)
  lowerCAmelCase													=   num  # Number of nodes in graph
  # To store the distance from source vertex
  lowerCAmelCase													=   [0] * self.num_nodes
  lowerCAmelCase													=   [-1] * self.num_nodes  # To store the path



 def       UpperCamelCase__     (    self							,					_snake_case							,					_snake_case							,					_snake_case    ):







  """simple docstring"""






  if u in self.adjList:
   self.adjList[u].append((v, w)    )
  else:
   lowerCAmelCase													=   [(v, w)]

  # Assuming undirected graph
  if v in self.adjList:
   self.adjList[v].append((u, w)    )
  else:
   lowerCAmelCase													=   [(u, w)]



 def       UpperCamelCase__     (    self    ):







  """simple docstring"""






  for u in self.adjList:
   print(_snake_case							,					'->'							,					' -> '.join(str(F'{v}({w})'    ) for v, w in self.adjList[u]    )    )



 def       UpperCamelCase__     (    self							,					_snake_case    ):







  """simple docstring"""






  lowerCAmelCase													=   [-1] * self.num_nodes
  # src is the source node
  lowerCAmelCase													=   0
  lowerCAmelCase													=   PriorityQueue()
  q.insert((0, src)    )  # (dist from src, node)
  for u in self.adjList:
   if u != src:
    lowerCAmelCase													=   sys.maxsize  # Infinity
    lowerCAmelCase													=   -1

  while not q.is_empty():
   lowerCAmelCase													=   q.extract_min()  # Returns node with the min dist from source
   # Update the distance of all the neighbours of u and
   # if their prev dist was INFINITY then push them in Q
   for v, w in self.adjList[u]:
    lowerCAmelCase													=   self.dist[u] + w
    if self.dist[v] > new_dist:
     if self.dist[v] == sys.maxsize:
      q.insert((new_dist, v)    )
     else:
      q.decrease_key((self.dist[v], v)							,					_snake_case    )
     lowerCAmelCase													=   new_dist
     lowerCAmelCase													=   u

        # Show the shortest distances from src
  self.show_distances(_snake_case    )



 def       UpperCamelCase__     (    self							,					_snake_case    ):







  """simple docstring"""






  print(F'Distance from node: {src}'    )
  for u in range(self.num_nodes    ):
   print(F'Node {u} has distance: {self.dist[u]}'    )



 def       UpperCamelCase__     (    self							,					_snake_case							,					_snake_case    ):







  """simple docstring"""






  lowerCAmelCase													=   []
  lowerCAmelCase													=   0
  lowerCAmelCase													=   dest
  # Backtracking from dest to src
  while self.par[temp] != -1:
   path.append(_snake_case    )
   if temp != src:
    for v, w in self.adjList[temp]:
     if v == self.par[temp]:
      cost += w
      break
   lowerCAmelCase													=   self.par[temp]
  path.append(_snake_case    )
  path.reverse()

  print(F'----Path to reach {dest} from {src}----'    )
  for u in path:
   print(F'{u}'							,					end=' '    )
   if u != dest:
    print('-> '							,					end=''    )

  print('\nTotal cost of path: '							,					_snake_case    )


if __name__ == "__main__":
       __UpperCamelCase    :	str						=     Graph(9)
       graph.add_edge(0, 1, 4)
       graph.add_edge(0, 7, 8)
       graph.add_edge(1, 2, 8)
       graph.add_edge(1, 7, 11)
       graph.add_edge(2, 3, 7)
       graph.add_edge(2, 8, 2)
       graph.add_edge(2, 5, 4)
       graph.add_edge(3, 4, 9)
       graph.add_edge(3, 5, 14)
       graph.add_edge(4, 5, 10)
       graph.add_edge(5, 6, 2)
       graph.add_edge(6, 7, 1)
       graph.add_edge(6, 8, 6)
       graph.add_edge(7, 8, 7)
       graph.show_graph()
       graph.dijkstra(0)
       graph.show_path(0, 4)

# OUTPUT
# 0 -> 1(4) -> 7(8)
# 1 -> 0(4) -> 2(8) -> 7(11)
# 7 -> 0(8) -> 1(11) -> 6(1) -> 8(7)
# 2 -> 1(8) -> 3(7) -> 8(2) -> 5(4)
# 3 -> 2(7) -> 4(9) -> 5(14)
# 8 -> 2(2) -> 6(6) -> 7(7)
# 5 -> 2(4) -> 3(14) -> 4(10) -> 6(2)
# 4 -> 3(9) -> 5(10)
# 6 -> 5(2) -> 7(1) -> 8(6)
# Distance from node: 0
# Node 0 has distance: 0
# Node 1 has distance: 4
# Node 2 has distance: 12
# Node 3 has distance: 19
# Node 4 has distance: 21
# Node 5 has distance: 11
# Node 6 has distance: 9
# Node 7 has distance: 8
# Node 8 has distance: 14
# ----Path to reach 4 from 0----
# 0 -> 7 -> 6 -> 5 -> 4
# Total cost of path:  21





