/*************************************************************************
* *
* This file is part of the 20n/act project. *
* 20n/act enables DNA prediction for synthetic biology/bioengineering. *
* Copyright (C) 2017 20n Labs, Inc. *
* *
* Please direct all queries to act@20n.com. *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
*************************************************************************/
package com.act.biointerpretation.sars;
import act.shared.Reaction;
import chemaxon.reaction.ReactionException;
import chemaxon.reaction.Reactor;
import chemaxon.struc.RxnMolecule;
import com.act.biointerpretation.mechanisminspection.ErosCorpus;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONObject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
public class OneSubstrateOneRoCharacterizer implements ReactionGroupCharacterizer {
private static final Logger LOGGER = LogManager.getFormatterLogger(OneSubstrateOneRoCharacterizer.class);
private final DbAPI dbApi;
private final FullReactionBuilder reactionBuilder;
private final ErosCorpus roCorpus;
private final List<SarFactory> sarFactories;
public OneSubstrateOneRoCharacterizer(DbAPI dbApi,
List<SarFactory> sarFactories,
FullReactionBuilder reactionBuilder,
ErosCorpus roCorpus) {
this.dbApi = dbApi;
this.sarFactories = sarFactories;
this.reactionBuilder = reactionBuilder;
this.roCorpus = roCorpus;
}
/**
* Characterizes the ReactionGroup by splitting it into sets of reactions that have the same RO, and characterizing
* each by its own generalized Reactor and SAR.
*
* @param group The ReactionGroup to characterize.
* @return A list of the resulting CharacterizedGroups.
* @throws IOException
*/
@Override
public List<CharacterizedGroup> characterizeGroup(ReactionGroup group) {
List<CharacterizedGroup> resultGroups = new ArrayList<>();
List<Reaction> workingReactionList = dbApi.getReactions(group);
workingReactionList.removeIf(r -> r.getSubstrates().length != 1 || r.getProducts().length != 1);
Integer initialReactionCount = workingReactionList.size();
while (workingReactionList.size() > 1) {
Integer roId = getMostCommonRo(workingReactionList);
// If no RO explains any of the reactions, reject this set.
if (roId == null) {
break;
}
List<Reaction> matchingReactions = getReactionsMatching(workingReactionList, roId);
LOGGER.info("%d of %d reactions matching RO %d", matchingReactions.size(), initialReactionCount, roId);
workingReactionList.removeAll(matchingReactions);
if (matchingReactions.size() == 1) {
LOGGER.warn("Group %s has only 1 substrate for RO %d", group.getName(), roId);
continue;
}
List<RxnMolecule> rxnMolecules = dbApi.getRxnMolecules(matchingReactions);
Optional<CharacterizedGroup> characterization = characterizeUniformGroup(rxnMolecules, roId, group.getName());
if (characterization.isPresent()) {
resultGroups.add(characterization.get());
}
}
return resultGroups;
}
/**
* Characterizes a group of reactions that all share the same RO by applying the SAR builders and the
* FullReactionBuilder to the reactions. If any step fails, an empty Optional is returned.
*
* @param reactions The reactions the characterize.
* @param roId The RO that matches all of the reactions.
* @param groupName The name of the ReactionGroup.
* @return
*/
private Optional<CharacterizedGroup> characterizeUniformGroup(List<RxnMolecule> reactions,
Integer roId,
String groupName) {
List<Sar> sars = buildSars(reactions);
Reactor fullReactor;
try {
fullReactor = buildFullReactor(reactions, roId);
} catch (ReactionException e) {
return Optional.empty();
}
return Optional.of(new CharacterizedGroup(groupName, sars, new SerializableReactor(fullReactor, roId)));
}
private Reactor buildFullReactor(List<RxnMolecule> reactions, Integer roId) throws ReactionException {
Reactor seedReactor = roCorpus.getEro(roId).getReactor();
return reactionBuilder.buildReaction(reactions, seedReactor);
}
private List<Sar> buildSars(List<RxnMolecule> reactions) {
List<Sar> sars = new ArrayList<>();
for (SarFactory builder : sarFactories) {
sars.add(builder.buildSar(reactions));
}
return sars;
}
/**
* Gets the mechanistic validator result associated with the most of the reactions.
*
* @param reactions The reactions associated with the group.
* @return The most common RO.
*/
private Integer getMostCommonRo(List<Reaction> reactions) {
Map<Integer, List<Reaction>> roIdToReactions = getRoIdToReactionsMap(reactions);
Map<Integer, Integer> roIdToCount = roIdToReactions.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().size()));
return Collections.max(roIdToCount.entrySet(), Map.Entry.comparingByValue()).getKey();
}
/**
* Gets all the reactions in the list which have the given RO as a mechanistic validator result.
*
* @param reactions The reactions associated with the group.
* @return The set of ROs associated with all of these reactions.
*/
private List<Reaction> getReactionsMatching(List<Reaction> reactions, Integer roId) {
return getRoIdToReactionsMap(reactions).get(roId);
}
/**
* Process the mechanistic validator results of the given reactions to build a map from RO ID to all reactions with
* that ID as a validator result.
*
* @param reactions The reactions to process.
* @return The map from ro id -> associated reactions.
*/
private Map<Integer, List<Reaction>> getRoIdToReactionsMap(List<Reaction> reactions) {
Map<Integer, List<Reaction>> roIdToReactionsMap = new HashMap<>();
for (Reaction reaction : reactions) {
JSONObject validatorResults = reaction.getMechanisticValidatorResult();
if (validatorResults != null) {
for (Object roId : reaction.getMechanisticValidatorResult().keySet()) {
Integer id = Integer.parseInt(roId.toString());
List<Reaction> reactionList = roIdToReactionsMap.get(id);
if (reactionList == null) {
reactionList = new ArrayList<>();
roIdToReactionsMap.put(id, reactionList);
}
reactionList.add(reaction);
}
}
}
return roIdToReactionsMap;
}
}