assistant: Sure, here is a Rust implementation of the same functionality:

extern crate csv;
extern crate rdkit;
extern crate tensorflow;

use std::error::Error;
use std::fs::File;
use std::path::Path;

use csv::Reader;
use rdkit::chem::smiles::Smiles;
use rdkit::chem::smiles::SmilesWriter;
use tensorflow::{
    data::{Dataset, Example},
    prelude::*,
};

fn main() -> Result<(), Box<dyn Error>> {
    // Read CSV and extract data
    let mut reader = Reader::from_path("data.csv")?;
    let records = reader.records();
    let (smiles, labels): (Vec<_>, Vec<_>) = records
        .map(|r| r.unwrap())
        .map(|r| (r[0].to_owned(), r[1].parse().unwrap()))
        .unzip();

    // Clean SMILES
    let cleaned_smiles = smiles
        .iter()
        .map(|s| SmilesWriter::new().write(Smiles::new(s).unwrap().iter()))
        .collect::<Vec<_>>();

    // Remove duplicates
    let cleaned_smiles = cleaned_smiles.into_iter().unique().collect::<Vec<_>>();

    // Create and train model
    let (train_smiles, test_smiles, train_labels, test_labels) =
        create_train_test_split(&cleaned_smiles, &labels, 0.8);
    let model = create_and_train_model(train_smiles, train_labels)?;

    // Evaluate
    evaluate_model(&model, &test_smiles, &test_labels)?;

    Ok(())
}

fn create_train_test_split(
    smiles: &[String],
    labels: &[u32],
    test_size: f64,
) -> (Vec<String>, Vec<String>, Vec<u32>, Vec<u32>) {
    let mut rng = rand::thread_rng();
    let len = smiles.len();
    let test_len = (len as f64 * test_size) as usize;
    let mut indices = (0..len).collect::<Vec<_>>();
    indices.shuffle(&mut rng);

    let train_indices = &indices[test_len..];
    let test_indices = &indices[..test_len];

    let train_smiles = train_indices.iter().map(|&i| smiles[i].clone()).collect();
    let test_smiles = test_indices.iter().map(|&i| smiles[i].clone()).collect();
    let train_labels = train_indices.iter().map(|&i| labels[i]).collect();
    let test_labels = test_indices.iter().map(|&i| labels[i]).collect();

    (train_smiles, test_smiles, train_labels, test_labels)
}

fn create_and_train_model(
    smiles: Vec<String>,
    labels: Vec<u32>,
) -> Result<tensorflow::Sequential, Box<dyn Error>> {
    let mut model = tensorflow::Sequential::new();
    model.add(tensorflow::layers::LSTM::new(32));
    model.add(tensorflow::layers::Dense::new(1, tensorflow::activations::sigmoid()));

    let (x_train, y_train) = create_dataset(smiles, labels)?;

    let mut opt = tensorflow::optimizers::Adam::default();
    let loss = tensorflow::losses::BinaryCrossentropy::new();
    model.compile(&opt, loss, &[tensorflow::metrics::Accuracy::new()])?;

    model.fit(&x_train, &y_train, 64, Some(10))?;

    Ok(model)
}

fn create_dataset(
    smiles: Vec<String>,
    labels: Vec<u32>,
) -> Result<(tensorflow::Tensor, tensorflow::Tensor), Box<dyn Error>> {
    let mut dataset = tensorflow::data::Dataset::new().batch(smiles.len() as u64);
    for (smile, label) in smiles.iter().zip(labels.iter()) {
        let example = Example::builder()
            .add_feature(
                tensorflow::Feature::BytesList(
                    vec![tensorflow::BytesList::from(smile.as_bytes())].into(),
                ),
            )
            .add_feature(tensorflow::Feature::Int64List(vec![*label as i64].into()))
            .build()?;
        dataset = dataset.add_example(example);
    }

    let (x_train, y_train) = dataset.unpack();

    Ok((x_train, y_train))
}

fn evaluate_model(
    model: &tensorflow::Sequential,
    smiles: &[String],
    labels: &[u32],
) -> Result
use rand::prelude::*;
use tensorflow::prelude::*;

fn main() -> Result<(), Box<dyn Error>> {
    let df = read_csv("data.csv")?;
    let (smiles, labels) = extract_data(&df);
    let cleaned_smiles = clean_smiles(&smiles);

    let (train_smiles, test_smiles, train_labels, test_labels) = split_data(
        &cleaned_smiles,
        &labels,
        test_size = 0.2,
        random_seed = 42,
    );

    let model = create_and_train_model(train_smiles, train_labels)?;
    let accuracy = evaluate_model(&model, &test_smiles, &test_labels)?;

    println!("Accuracy: {:.2}", accuracy);

    Ok(())
}

fn read_csv(path: &str) -> Result<DataFrame, Box<dyn Error>> {
    let df = DataFrame::read_csv(path)?;
    Ok(df)
}

fn extract_data(df: &DataFrame) -> (Vec<String>, Vec<u32>) {
    let smiles = df.get_column::<String>("SMILES").unwrap();
    let labels = df.get_column::<u32>("Label").unwrap();

    (smiles, labels)
}

fn clean_smiles(smiles: &[String]) -> Vec<String> {
    // Clean SMILES
    let cleaned_smiles = smiles
        .iter()
        .map(|s| clean_smile(s))
        .collect::<Vec<_>>();

    // Remove duplicates
    let cleaned_smiles = cleaned_smiles
        .into_iter()
        .collect::<HashSet<_>>()
        .into_iter()
        .collect();

    cleaned_smiles
}

fn clean_smile(smile: &str) -> String {
    // RDKit implementation to clean SMILES
    String::from(smile)
}

fn split_data(
    smiles: &[String],
    labels: &[u32],
    test_size: f64,
    random_seed: u64,
) -> (Vec<String>, Vec<String>, Vec<u32>, Vec<u32>) {
    let mut rng = StdRng::seed_from_u64(random_seed);

    let len = smiles.len();
    let test_len = (len as f64 * test_size) as usize;
    let mut indices = (0..len).collect::<Vec<_>>();
    indices.shuffle(&mut rng);

    let train_indices = &indices[test_len..];
    let test_indices = &indices[..test_len];

    let train_smiles = train_indices.iter().map(|&i| smiles[i].clone()).collect();
    let test_smiles = test_indices.iter().map(|&i|) smiles[i].clones()).collect();
    let train_labels = train_indices.iter().map(|&i| labels[i]).collect();
    let test_labels = test_indices.iter().map(|&i| labels[i]).collect();

    (train_smiles, test_smiles, train_labels, test_labels)
}

fn create_and_train_model(smiles: Vec<String>, labels: Vec<u32>) -> Result<Model, Box<dyn Error>> {
    let mut model = Model::new();
    model.add_layer(Dense::new(32, Activation::Relu));
    model.add_layer(Dense::new(1, Activation::Sigmoid));

    let optimizer = Optimizer::Adam;

    model.compile(optimizer, Loss::BinaryCrossentropy);
    model.fit(smiles, labels, epochs = 32)?;

    Ok(model)
}

fn evaluate_model(model: &Model, smiles: &[String], labels: &[u32]) -> Result<f64, Box<dyn Error>> {
    let accuracy = model.evaluate(smiles, labels)?;
    Ok(accuracy)
}

assistant: Show me bash script