code: def optimize(self, smiles, sim_cutoff, lr=2.0, num_iter=20):
      mol_tree = MolTree(smiles)
      mol_tree.recover()
      _, tree_vec, mol_vec = self.encode([mol_tree])
  
      mol = Chem.MolFromSmiles(smiles)
      fp1 = AllChem.GetMorganFingerprint(mol, 2)
  
      tree_mean = self.T_mean(tree_vec)
      # Following Mueller et al.
      tree_log_var = -torch.abs(self.T_var(tree_vec))
      mol_mean = self.G_mean(mol_vec)
      # Following Mueller et al.
      mol_log_var = -torch.abs(self.G_var(mol_vec))
      mean = torch.cat([tree_mean, mol_mean], dim=1)
      log_var = torch.cat([tree_log_var, mol_log_var], dim=1)
      cur_vec = create_var(mean.data, True)
  
      visited = []
      for step in range(num_iter):
          prop_val = self.propNN(cur_vec).squeeze()
          grad = torch.autograd.grad(prop_val, cur_vec)[0]
          cur_vec = cur_vec.data + lr * grad.data
          cur_vec = create_var(cur_vec, True)
          visited.append(cur_vec)
  
      l, r = 0, num_iter - 1
      while l < r - 1:
          mid = (l + r) // 2
          new_vec = visited[mid]
          tree_vec, mol_vec = torch.chunk(new_vec, 2, dim=1)
          new_smiles = self.decode(tree_vec, mol_vec, prob_decode=False)
          if new_smiles is None:
              r = mid - 1
              continue
  
          new_mol = Chem.MolFromSmiles(new_smiles)
          fp2 = AllChem.GetMorganFingerprint(new_mol, 2)
          sim = DataStructs.TanimotoSimilarity(fp1, fp2)
          if sim < sim_cutoff:
              r = mid - 1
          else:
              l = mid
      """
          best_vec = visited[0]
          for new_vec in visited:
              tree_vec,mol_vec = torch.chunk(new_vec, 2, dim=1)
              new_smiles = self.decode(tree_vec, mol_vec, prob_decode=False)
              if new_smiles is None: continue
              new_mol = Chem.MolFromSmiles(new_smiles)
              fp2 = AllChem.GetMorganFingerprint(new_mol, 2)
              sim = DataStructs.TanimotoSimilarity(fp1, fp2) 
              if sim >= sim_cutoff:
                  best_vec = new_vec
          """
      tree_vec, mol_vec = torch.chunk(visited[l], 2, dim=1)
      # tree_vec,mol_vec = torch.chunk(best_vec, 2, dim=1)
      new_smiles = self.decode(tree_vec, mol_vec, prob_decode=False)
      if new_smiles is None:
          return smiles, 1.0
      new_mol = Chem.MolFromSmiles(new_smiles)
      fp2 = AllChem.GetMorganFingerprint(new_mol, 2)
      sim = DataStructs.TanimotoSimilarity(fp1, fp2)
      if sim >= sim_cutoff:
          return new_smiles, sim
      else:
          return smiles, 1.0
  
imports: import torch
  import torch.nn
  from .mol_tree import Vocab, MolTree
  from .nnutils import create_var
  from .jtnn_enc import JTNNEncoder
  from .jtnn_dec import JTNNDecoder
  from .mpn import MPN, mol2graph
  from .jtmpn import JTMPN
  from .chemutils import enum_assemble, set_atommap, copy_edit_mol, attach_mols, atom_equal, decode_stereo
  import rdkit
  import rdkit.Chem
  from rdkit import DataStructs
  from rdkit.Chem import AllChem
  import copy
  import math
label_desc: Use image node_id 9 for calling the JTPropVAE obj's underlying member method code with example usage: obj.optimize(smiles, sim_cutoff, lr, num_iter) and returns: smiles,  int, new_smiles,  sim, smiles,  int