code: def get_dopants_from_substitution_probabilities(structure, num_dopants=5,
                                                  threshold=0.001,
                                                  match_oxi_sign=False):
      
      els_have_oxi_states = [hasattr(s, "oxi_state") for s in structure.species]
  
      if not all(els_have_oxi_states):
          raise ValueError("All sites in structure must have oxidation states to "
                           "predict dopants.")
  
      sp = SubstitutionPredictor(threshold=threshold)
  
      subs = [sp.list_prediction([s]) for s in set(structure.species)]
      subs = [{'probability': pred['probability'],
               'dopant_species': list(pred['substitutions'].keys())[0],
               'original_species': list(pred['substitutions'].values())[0]}
              for species_preds in subs for pred in species_preds]
      subs.sort(key=lambda x: x['probability'], reverse=True)
  
      return _get_dopants(subs, num_dopants, match_oxi_sign)
docs: Get dopant suggestions based on substitution probabilities.
  
      Args:
          structure (Structure): A pymatgen structure decorated with
              oxidation states.
          num_dopants (int): The number of suggestions to return for
              n- and p-type dopants.
          threshold (float): Probability threshold for substitutions.
          match_oxi_sign (bool): Whether to force the dopant and original species
              to have the same sign of oxidation state. E.g. If the original site
              is in a negative charge state, then only negative dopants will be
              returned.
  
      Returns:
          (dict): Dopant suggestions, given as a dictionary with keys "n_type" and
          "p_type". The suggestions for each doping type are given as a list of
          dictionaries, each with they keys:
  
          - "probability": The probability of substitution.
          - "dopant_species": The dopant species.
          - "original_species": The substituted species.