diff --git a/src/main/java/MatchingFileWriter.java b/src/main/java/MatchingFileWriter.java index 00cbc24..b797cfe 100644 --- a/src/main/java/MatchingFileWriter.java +++ b/src/main/java/MatchingFileWriter.java @@ -7,6 +7,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.List; +import java.util.regex.Pattern; public class MatchingFileWriter { @@ -28,6 +29,13 @@ public class MatchingFileWriter { this.allResults = result.getAllResults(); } + public void writeErrorRateToTerminal(){ + for(String s: comments){ + if(s.matches("(Pairing error rate: )(\\d*.\\d+)")){ + System.out.println(s); + } + } + } public void writeResultsToFile(){ String[] headerStrings = new String[headers.size()]; for(int i = 0; i < headers.size(); i++){ diff --git a/src/main/java/Simulator.java b/src/main/java/Simulator.java index 9031c16..877d3b7 100644 --- a/src/main/java/Simulator.java +++ b/src/main/java/Simulator.java @@ -48,43 +48,44 @@ public class Simulator { } public static MatchingResult matchCDR3s(List distinctCells, - Plate samplePlate, Integer lowThreshold, Integer highThreshold){ - System.out.println("Cells: " + distinctCells.size()); + Plate samplePlate, Integer lowThreshold, + Integer highThreshold, boolean verbose){ + if(verbose){System.out.println("Cells: " + distinctCells.size());} Instant start = Instant.now(); int numWells = samplePlate.getSize(); int[] alphaIndex = {cdr3AlphaIndex}; int[] betaIndex = {cdr3BetaIndex}; - System.out.println("Making cell maps"); + if(verbose){System.out.println("Making cell maps");} //HashMap keyed to Alphas, values Betas Map distCellsMapAlphaKey = makePeptideToPeptideMap(distinctCells, 0, 1); - System.out.println("Cell maps made"); + if(verbose){System.out.println("Cell maps made");} - System.out.println("Making well maps"); + if(verbose){System.out.println("Making well maps");} Map allAlphas = samplePlate.assayWellsPeptideP(alphaIndex); Map allBetas = samplePlate.assayWellsPeptideP(betaIndex); int alphaCount = allAlphas.size(); - System.out.println("all alphas count: " + alphaCount); + if(verbose){System.out.println("all alphas count: " + alphaCount);} int betaCount = allBetas.size(); - System.out.println("all betas count: " + betaCount); + if(verbose){System.out.println("all betas count: " + betaCount);} - System.out.println("Well maps made"); + if(verbose){System.out.println("Well maps made");} //Remove saturating-occupancy peptides because they have no signal value. //Remove below-minimum-overlap-threshold peptides because they can't possibly have an overlap with another //peptide that's above the threshold. - System.out.println("Removing peptides present in all wells."); - System.out.println("Removing peptides with occupancy below the minimum overlap threshold"); + if(verbose){System.out.println("Removing peptides present in all wells.");} + if(verbose){System.out.println("Removing peptides with occupancy below the minimum overlap threshold");} filterByOccupancyThreshold(allAlphas, lowThreshold, numWells - 1); filterByOccupancyThreshold(allBetas, lowThreshold, numWells - 1); - System.out.println("Peptides removed"); + if(verbose){System.out.println("Peptides removed");} int pairableAlphaCount = allAlphas.size(); - System.out.println("Remaining alpha count: " + pairableAlphaCount); + if(verbose){System.out.println("Remaining alpha count: " + pairableAlphaCount);} int pairableBetaCount = allBetas.size(); - System.out.println("Remaining beta count: " + pairableBetaCount); + if(verbose){System.out.println("Remaining beta count: " + pairableBetaCount);} - System.out.println("Making vertex maps"); + if(verbose){System.out.println("Making vertex maps");} //For the SimpleWeightedBipartiteGraphMatrixGenerator, all vertices must have // distinct numbers associated with them. Since I'm using a 2D array, that means // distinct indices between the rows and columns. vertexStartValue lets me track where I switch @@ -101,9 +102,9 @@ public class Simulator { Map plateAtoVMap = invertVertexMap(plateVtoAMap); //keys are betas, values are sequential integer vertices from previous map Map plateBtoVMap = invertVertexMap(plateVtoBMap); - System.out.println("Vertex maps made"); + if(verbose){System.out.println("Vertex maps made");} - System.out.println("Creating adjacency matrix"); + if(verbose){System.out.println("Creating adjacency matrix");} //Count how many wells each alpha appears in Map alphaWellCounts = new HashMap<>(); //count how many wells each beta appears in @@ -112,9 +113,9 @@ public class Simulator { countPeptidesAndFillMatrix(samplePlate, allAlphas, allBetas, plateAtoVMap, plateBtoVMap, alphaIndex, betaIndex, alphaWellCounts, betaWellCounts, weights); - System.out.println("matrix created"); + if(verbose){System.out.println("matrix created");} - System.out.println("creating graph"); + if(verbose){System.out.println("creating graph");} SimpleWeightedGraph graph = new SimpleWeightedGraph<>(DefaultWeightedEdge.class); SimpleWeightedBipartiteGraphMatrixGenerator graphGenerator = new SimpleWeightedBipartiteGraphMatrixGenerator(); @@ -126,18 +127,18 @@ public class Simulator { graphGenerator.second(betaVertices); //This will work because LinkedHashMap preserves order of entry graphGenerator.weights(weights); graphGenerator.generateGraph(graph); - System.out.println("Graph created"); + if(verbose){System.out.println("Graph created");} - System.out.println("Eliminating edges with weights outside threshold values"); + if(verbose){System.out.println("Eliminating edges with weights outside threshold values");} filterByOccupancyThreshold(graph, lowThreshold, highThreshold); - System.out.println("Over- and under-weight edges set to 0.0"); + if(verbose){System.out.println("Over- and under-weight edges set to 0.0");} - System.out.println("Finding maximum weighted matching"); + if(verbose){System.out.println("Finding maximum weighted matching");} MaximumWeightBipartiteMatching maxWeightMatching = new MaximumWeightBipartiteMatching(graph, plateVtoAMap.keySet(), plateVtoBMap.keySet()); MatchingAlgorithm.Matching graphMatching = maxWeightMatching.getMatching(); - System.out.println("Matching completed"); + if(verbose){System.out.println("Matching completed");} Instant stop = Instant.now(); //Header for CSV file @@ -209,10 +210,13 @@ public class Simulator { comments.add("Pairing error rate: " + pairingErrorRateTrunc); Duration time = Duration.between(start, stop); comments.add("Simulation time: " + nf.format(time.toSeconds()) + " seconds"); - for(String s: comments){ - System.out.println(s); + if(verbose){ + for(String s: comments){ + System.out.println(s); + } } + return new MatchingResult(samplePlate.getSourceFileName(), comments, header, allResults, matchMap, time); } diff --git a/src/main/java/UserInterface.java b/src/main/java/UserInterface.java index 2d862e5..f9bdab4 100644 --- a/src/main/java/UserInterface.java +++ b/src/main/java/UserInterface.java @@ -1,3 +1,5 @@ +import org.apache.commons.cli.*; + import java.util.List; import java.util.Scanner; import java.util.InputMismatchException; @@ -11,33 +13,85 @@ public class UserInterface { static int input; static boolean quit = false; - public static void main(String args[]) { - while(!quit) { - System.out.println("\nALPHA/BETA T-CELL RECEPTOR MATCHING SIMULATOR"); - System.out.println("Please select an option:"); - System.out.println("1) Generate a population of distinct cells"); - System.out.println("2) Generate a sample plate of T cells"); - System.out.println("3) Simulate CDR3 alpha/beta T cell matching"); - System.out.println("4) Simulate CDR3/CDR1 T cell matching"); - System.out.println("5) Acknowledgements"); - System.out.println("0) Exit"); + public static void main(String[] args) { + + if(args.length != 0){ + Options options = new Options(); + Option matchCDR3 = Option.builder("m") + .longOpt("match") + .desc("Match CDR3s. Requires a cell sample file and any number of plate files.") + .build(); + options.addOption(matchCDR3); + Option inputCells = Option.builder("c") + .longOpt("cellfile") + .hasArg() + .argName("file") + .desc("The cell sample file used for matching") + .required().build(); + options.addOption(inputCells); + Option lowThresh = Option.builder("low") + .hasArg() + .argName("number") + .desc("Sets the minimum occupancy overlap to attempt matching") + .required().build(); + options.addOption(lowThresh); + Option highThresh = Option.builder("high") + .hasArg() + .argName("number") + .desc("Sets the maximum occupancy overlap to attempt matching") + .required().build(); + options.addOption(highThresh); + Option inputPlates = Option.builder("p") + .longOpt("platefiles") + .hasArgs() + .desc("Plate files to match") + .required().build(); + options.addOption(inputPlates); + + CommandLineParser parser = new DefaultParser(); try { - input = sc.nextInt(); - switch(input){ - case 1 -> makeCells(); - case 2 -> makePlate(); - case 3 -> matchCells(); - case 4 -> matchCellsExpanded(); - case 5 -> acknowledge(); - case 0 -> quit = true; - default -> throw new InputMismatchException("Invalid input."); + CommandLine line = parser.parse(options, args); + if(line.hasOption("m")){ + String cellFile = line.getOptionValue("c"); + Integer lowThreshold = Integer.valueOf(line.getOptionValue(lowThresh)); + Integer highThreshold = Integer.valueOf(line.getOptionValue(highThresh)); + for(String plate: line.getOptionValues("p")) { + matchCDR3s(cellFile, plate, lowThreshold, highThreshold); + } } - }catch(InputMismatchException ex){ - System.out.println(ex); - sc.next(); + } + catch (ParseException exp) { + System.err.println("Parsing failed. Reason: " + exp.getMessage()); } } - sc.close(); + else { + while (!quit) { + System.out.println("\nALPHA/BETA T-CELL RECEPTOR MATCHING SIMULATOR"); + System.out.println("Please select an option:"); + System.out.println("1) Generate a population of distinct cells"); + System.out.println("2) Generate a sample plate of T cells"); + System.out.println("3) Simulate CDR3 alpha/beta T cell matching"); + System.out.println("4) Simulate CDR3/CDR1 T cell matching"); + System.out.println("5) Acknowledgements"); + System.out.println("0) Exit"); + try { + input = sc.nextInt(); + switch (input) { + case 1 -> makeCells(); + case 2 -> makePlate(); + case 3 -> matchCells(); + case 4 -> matchCellsCDR1(); + case 5 -> acknowledge(); + case 0 -> quit = true; + default -> throw new InputMismatchException("Invalid input."); + } + } catch (InputMismatchException ex) { + System.out.println(ex); + sc.next(); + } + } + sc.close(); + } } private static void makeCells() { @@ -68,6 +122,22 @@ public class UserInterface { writer.writeCellsToFile(); } + private static void makeCells(String filename, Integer numCells, Integer cdr1Freq){ + CellSample sample = Simulator.generateCellSample(numCells, cdr1Freq); + CellFileWriter writer = new CellFileWriter(filename, sample); + writer.writeCellsToFile(); + } + + private static void makePlate(String cellFile, String filename, Double stdDev, + Integer numWells, Integer numSections, + Integer[] concentrations, Double dropOutRate){ + CellFileReader cellReader = new CellFileReader(cellFile); + Plate samplePlate = new Plate(numWells, dropOutRate, concentrations, stdDev); + samplePlate.fillWells(cellReader.getFilename(), cellReader.getCells()); + PlateFileWriter writer = new PlateFileWriter(filename, samplePlate); + writer.writePlateFile(); + } + //method to output a CSV of private static void makePlate() { String cellFile = null; @@ -149,6 +219,30 @@ public class UserInterface { writer.writePlateFile(); } + private static void matchCDR3s(String cellFile, String plateFile, Integer lowThreshold, Integer highThreshold){ + CellFileReader cellReader = new CellFileReader(cellFile); + PlateFileReader plateReader = new PlateFileReader(plateFile); + Plate plate = new Plate(plateReader.getFilename(), plateReader.getWells()); + if (cellReader.getCells().size() == 0){ + System.exit(0); + } + else if(plate.getWells().size() == 0){ + System.exit(0); + + } + else{ + if(highThreshold >= plate.getSize()){ + highThreshold = plate.getSize() - 1; + } + List cells = cellReader.getCells(); + MatchingResult results = Simulator.matchCDR3s(cells, plate, lowThreshold, highThreshold, false); + //result writer + MatchingFileWriter writer = new MatchingFileWriter(null, results); + writer.writeErrorRateToTerminal(); + } + } + + private static void matchCells() { String filename = null; String cellFile = null; @@ -192,14 +286,14 @@ public class UserInterface { highThreshold = plate.getSize() - 1; } List cells = cellReader.getCells(); - MatchingResult results = Simulator.matchCDR3s(cells, plate, lowThreshold, highThreshold); + MatchingResult results = Simulator.matchCDR3s(cells, plate, lowThreshold, highThreshold, true); //result writer MatchingFileWriter writer = new MatchingFileWriter(filename, results); writer.writeResultsToFile(); } } - public static void matchCellsExpanded(){ + public static void matchCellsCDR1(){ /* The idea here is that we'll get the CDR3 alpha/beta matches first. Then we'll try to match CDR3s to CDR1s by looking at the top two matches for each CDR3. If CDR3s in the same cell simply swap CDR1s, we assume a correct @@ -276,7 +370,7 @@ public class UserInterface { highThresholdCDR1 = plate.getSize() - 1; } List cells = cellReader.getCells(); - MatchingResult preliminaryResults = Simulator.matchCDR3s(cells, plate, lowThresholdCDR3, highThresholdCDR3); + MatchingResult preliminaryResults = Simulator.matchCDR3s(cells, plate, lowThresholdCDR3, highThresholdCDR3, true); MatchingResult[] results = Simulator.matchCDR1s(cells, plate, lowThresholdCDR1, highThresholdCDR1, preliminaryResults); MatchingFileWriter writer = new MatchingFileWriter(filename + "_FirstPass", results[0]);