chiral-computing-unit 0.1.2

Computing Units of Chiral: An All-in-One Data Processing Tool
Documentation
use chiral_common::{OperatorOutput, Operator, OperatorReport};
use chiral_operator::fingerprint::ob_similarity::{Input, Output, Report};

/// Data
pub struct Data {
    dsk: chiral_common::kinds::Dataset,
    ids: Vec<chiral_common::DatasetID>,
    fps: Vec<chiral_common::FPData>
}

impl From<(&chiral_common::kinds::Fingerprint, &chiral_common::kinds::Dataset, &chiral_data::DocSMILES)> for Data {
    fn from((fpk, dsk_in, doc): (&chiral_common::kinds::Fingerprint, &chiral_common::kinds::Dataset, &chiral_data::DocSMILES)) -> Self {
        let fpk_ob = super::to_ob_fp_kind(fpk);
        let fpg = openbabel::fingerprint::FingerprintGenerator::new(fpk_ob);
        let fps = fpg.get_fingerprint_for_smiles_vec(doc.get_smiles_vec());
        let ids = doc.get_ids().to_vec();
        Self { dsk: dsk_in.to_owned(), ids, fps }
    }
}

impl chiral_common::OperatorData for Data {
    fn len(&self) -> usize {
        self.ids.len()
    }
}

/// Operator
pub struct OpenBabelSimilaritySearching {
    fpk: chiral_common::kinds::Fingerprint,
    fpg: openbabel::fingerprint::FingerprintGenerator,
}

impl From<chiral_common::kinds::Fingerprint> for OpenBabelSimilaritySearching {
    fn from(fpk: chiral_common::kinds::Fingerprint) -> Self {
        let fpk_ob = super::to_ob_fp_kind(&fpk);
        let fpg = openbabel::fingerprint::FingerprintGenerator::new(fpk_ob);
        Self { fpk, fpg }
    }
}

impl chiral_common::Operator for OpenBabelSimilaritySearching {
    type InputType = Input;
    type DataType = Data;
    type OutputType = Output; 
    type ReportType = Report;

    fn compute(&self, input: &Self::InputType, data: &Self::DataType) -> Self::OutputType {
        let mol = openbabel::molecule::Molecule::new_from_smiles(&input.smiles);
        let fp_target = self.fpg.get_fingerprint(&mol);
        let results = data.fps.iter()
            .map(|fp| super::similarity_tanimoto(fp, &fp_target))
            .zip(data.ids.iter())
            .filter(|(coeff, _)| *coeff > input.threshold)
            .map(|(mr, id)| (mr, id.to_string()))
            .collect();

        Output { results }
    }

    fn report(&self, input: Self::InputType, data: &Self::DataType, output: Self::OutputType) -> Self::ReportType {
        Report {
            input, 
            fpk: self.fpk.to_owned(),
            dsk: data.dsk.to_owned(),
            output
        }
    }
}

/// Computing Unit
pub struct OpenBabelSimilaritySearchingUnit {
    op: OpenBabelSimilaritySearching,
    data: Data,
    output: Output
}

impl From<(&chiral_common::kinds::Fingerprint, &chiral_common::kinds::Dataset, &chiral_data::DocSMILES)> for OpenBabelSimilaritySearchingUnit {
    fn from((fpk, dsk, doc): (&chiral_common::kinds::Fingerprint, &chiral_common::kinds::Dataset, &chiral_data::DocSMILES)) -> Self {
        let op = OpenBabelSimilaritySearching::from(fpk.to_owned());
        let data = Data::from((fpk, dsk, doc));
        Self { op, data, output: Output::blank() }
    }
}

impl chiral_common::ComputingUnit for OpenBabelSimilaritySearchingUnit {
    fn compute(&mut self, serialized_input: &chiral_common::SerializedFormat) {
        self.output.clear();
        let input = Input::deserialize(serialized_input);
        self.output = self.op.compute(&input, &self.data);
    }

    fn report(&self, serialized_input: &chiral_common::SerializedFormat) -> chiral_common::SerializedFormat {
        let input = Input::deserialize(serialized_input);
        self.op.report(input, &self.data, self.output.to_owned()).serialize()
    }

    fn output_len(&self) -> usize {
        self.output.len()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use chiral_common::{Operator, OperatorData, OperatorOutput, OperatorReport, ComputingUnit};
    use chiral_data::Dummy;

    #[test]
    fn test_op() {
        let dsk = chiral_common::kinds::Dataset::Dummy;
        let doc_smiles = chiral_data::DocSMILES::dummy();
        let com_fpk = chiral_common::kinds::Fingerprint::kind_openbabel_ecfp4(2048);
        let op = OpenBabelSimilaritySearching::from(com_fpk.to_owned());
        let data = Data::from((&com_fpk, &dsk, &doc_smiles));
        assert_eq!(data.len(), 4);
        let input = Input { smiles: String::from("c1ccccc1"), threshold: 0.045 };
        let output = op.compute(&input, &data);
        assert_eq!(output.len(), 2); 
        let report = op.report(input, &data, output);
        let serialized_report = OperatorReport::serialize(&report);
        let report_deserialized: Report = OperatorReport::deserialize(&serialized_report);
        assert_eq!(report_deserialized.fpk, com_fpk);
        assert_eq!(report_deserialized.dsk, dsk);
        assert_eq!(report_deserialized.output.len(), 2);
    }

    #[test]
    fn test_cu() {
        let dsk = chiral_common::kinds::Dataset::Dummy;
        let doc_smiles = chiral_data::DocSMILES::dummy();
        let fpk = chiral_common::kinds::Fingerprint::kind_openbabel_ecfp4(2048);
        let mut cu = OpenBabelSimilaritySearchingUnit::from((&fpk, &dsk, &doc_smiles));
        let input = Input { smiles: String::from("c1ccccc1"), threshold: 0.045 };
        cu.compute(&input.serialize());
        assert_eq!(cu.output_len(), 2); 
    }
}