# Path: guacamol/utils/chemistry.py
# def smiles_to_rdkit_mol(smiles: str) -> Optional[Chem.Mol]:
#     """
#     Converts a SMILES string to a RDKit molecule.
# 
#     Args:
#         smiles: SMILES string of the molecule
# 
#     Returns:
#         RDKit Mol, None if the SMILES string is invalid
#     """
#     mol = Chem.MolFromSmiles(smiles)
# 
#     #  Sanitization check (detects invalid valence)
#     if mol is not None:
#         try:
#             Chem.SanitizeMol(mol)
#         except ValueError:
#             return None
# 
#     return mol
# 
# Path: guacamol/score_modifier.py
# class ScoreModifier:
#     """
#     Interface for score modifiers.
#     """
# 
#     @abstractmethod
#     def __call__(self, x):
#         """
#         Apply the modifier on x.
# 
#         Args:
#             x: float or np.array to modify
# 
#         Returns:
#             float or np.array (depending on the type of x) after application of the distance function.
#         """
# 
# class LinearModifier(ScoreModifier):
#     """
#     Score modifier that multiplies the score by a scalar (default: 1, i.e. do nothing).
#     """
# 
#     def __init__(self, slope=1.0):
#         self.slope = slope
# 
#     def __call__(self, x):
#         return self.slope * x
# 
# Path: guacamol/utils/math.py
# def geometric_mean(values: List[float]) -> float:
#     """
#     Computes the geometric mean of a list of values.
#     """
#     a = np.array(values)
#     return a.prod() ** (1.0 / len(a))


from abc import abstractmethod
from typing import List, Optional
from rdkit import Chem
from guacamol.utils.chemistry import smiles_to_rdkit_mol
from guacamol.score_modifier import ScoreModifier, LinearModifier
from guacamol.utils.math import geometric_mean
import logging
import numpy as np

        scores = [self.corrupt_score if raw_score is None
                  else self.modify_score(raw_score)
                  for raw_score in raw_scores]

        return scores

    @abstractmethod
    def raw_score_list(self, smiles_list: List[str]) -> List[float]:
        """
        Calculate the objective score before application of the modifier for a batch of molecules.

        Args:
            smiles_list: list of SMILES strings to process

        Returns:
            A list of scores. For unsuccessful calculations or invalid molecules, `None` should be given as a value for
            the corresponding molecule.
        """
        raise NotImplementedError


class ScoringFunctionBasedOnRdkitMol(MoleculewiseScoringFunction):
    """
    Base class for scoring functions that calculate scores based on rdkit.Chem.Mol instances.

    Derived classes must implement the `score_mol` function.
    """

    def raw_score(self, smiles: str) -> float:

# Path: guacamol/utils/chemistry.py
# def smiles_to_rdkit_mol(smiles: str) -> Optional[Chem.Mol]:
#     """
#     Converts a SMILES string to a RDKit molecule.
# 
#     Args:
#         smiles: SMILES string of the molecule
# 
#     Returns:
#         RDKit Mol, None if the SMILES string is invalid
#     """
#     mol = Chem.MolFromSmiles(smiles)
# 
#     #  Sanitization check (detects invalid valence)
#     if mol is not None:
#         try:
#             Chem.SanitizeMol(mol)
#         except ValueError:
#             return None
# 
#     return mol
# 
# Path: guacamol/score_modifier.py
# class ScoreModifier:
#     """
#     Interface for score modifiers.
#     """
# 
#     @abstractmethod
#     def __call__(self, x):
#         """
#         Apply the modifier on x.
# 
#         Args:
#             x: float or np.array to modify
# 
#         Returns:
#             float or np.array (depending on the type of x) after application of the distance function.
#         """
# 
# class LinearModifier(ScoreModifier):
#     """
#     Score modifier that multiplies the score by a scalar (default: 1, i.e. do nothing).
#     """
# 
#     def __init__(self, slope=1.0):
#         self.slope = slope
# 
#     def __call__(self, x):
#         return self.slope * x
# 
# Path: guacamol/utils/math.py
# def geometric_mean(values: List[float]) -> float:
#     """
#     Computes the geometric mean of a list of values.
#     """
#     a = np.array(values)
#     return a.prod() ** (1.0 / len(a))


# Path: guacamol/scoring_function.py
from abc import abstractmethod
from typing import List, Optional
from rdkit import Chem
from guacamol.utils.chemistry import smiles_to_rdkit_mol
from guacamol.score_modifier import ScoreModifier, LinearModifier
from guacamol.utils.math import geometric_mean
import logging
import numpy as np


        scores = [self.corrupt_score if raw_score is None
                  else self.modify_score(raw_score)
                  for raw_score in raw_scores]

        return scores

    @abstractmethod
    def raw_score_list(self, smiles_list: List[str]) -> List[float]:
        """
        Calculate the objective score before application of the modifier for a batch of molecules.

        Args:
            smiles_list: list of SMILES strings to process

        Returns:
            A list of scores. For unsuccessful calculations or invalid molecules, `None` should be given as a value for
            the corresponding molecule.
        """
        raise NotImplementedError


class ScoringFunctionBasedOnRdkitMol(MoleculewiseScoringFunction):
    """
    Base class for scoring functions that calculate scores based on rdkit.Chem.Mol instances.

    Derived classes must implement the `score_mol` function.
    """

    def raw_score(self, smiles: str) -> float:

        mol = smiles_to_rdkit_mol(smiles)