From aa5663ddc04edee4660753e03d30130407ea18fb Mon Sep 17 00:00:00 2001 From: Dmitry Bolotin Date: Wed, 22 Aug 2018 13:36:35 +0300 Subject: [PATCH 1/4] This fixes inconsistent D gene chain selection algorithms. --- .../mixcr/assembler/CloneFactory.java | 3 +- .../PartialAlignmentsAssemblerAligner.java | 2 +- .../mixcr/vdjaligners/VDJCAligner.java | 84 +++++++++++++++++-- .../vdjaligners/VDJCAlignerAbstract.java | 16 ++-- .../mixcr/vdjaligners/VDJCAlignerPVFirst.java | 61 +------------- .../mixcr/vdjaligners/VDJCAlignerS.java | 35 ++++---- .../milaboratory/mixcr/util/RunMiXCRTest.java | 15 ++-- 7 files changed, 114 insertions(+), 102 deletions(-) diff --git a/src/main/java/com/milaboratory/mixcr/assembler/CloneFactory.java b/src/main/java/com/milaboratory/mixcr/assembler/CloneFactory.java index de68d2c3f..f15b0dd1b 100644 --- a/src/main/java/com/milaboratory/mixcr/assembler/CloneFactory.java +++ b/src/main/java/com/milaboratory/mixcr/assembler/CloneFactory.java @@ -229,7 +229,8 @@ Clone create(int id, CloneAccumulator accumulator) { if (from < to) hits.put(GeneType.Diversity, dAligner.align(sequenceToAlign, - VDJCAligner.getPossibleDLoci(hits.get(GeneType.Variable), hits.get(GeneType.Joining)), + VDJCAligner.getPossibleDLoci(hits.get(GeneType.Variable), hits.get(GeneType.Joining), + null), from, to, indexOfAssemblingFeatureWithD, assemblingFeatures.length)); else diff --git a/src/main/java/com/milaboratory/mixcr/partialassembler/PartialAlignmentsAssemblerAligner.java b/src/main/java/com/milaboratory/mixcr/partialassembler/PartialAlignmentsAssemblerAligner.java index 34e75f044..8c02475e3 100644 --- a/src/main/java/com/milaboratory/mixcr/partialassembler/PartialAlignmentsAssemblerAligner.java +++ b/src/main/java/com/milaboratory/mixcr/partialassembler/PartialAlignmentsAssemblerAligner.java @@ -297,7 +297,7 @@ protected VDJCAlignmentResult process0(VDJCMultiRead input) { if (vAl == null || jAl == null || singleDAligner == null) dResult = new VDJCHit[0]; else - dResult = singleDAligner.align(targets[dGeneTarget].getSequence(), getPossibleDLoci(vResult, jResult), + dResult = singleDAligner.align(targets[dGeneTarget].getSequence(), getPossibleDLoci(vResult, jResult, null), vAl.getSequence2Range().getTo(), jAl.getSequence2Range().getFrom(), dGeneTarget, nReads); diff --git a/src/main/java/com/milaboratory/mixcr/vdjaligners/VDJCAligner.java b/src/main/java/com/milaboratory/mixcr/vdjaligners/VDJCAligner.java index 0dc754856..cbf96e5f4 100644 --- a/src/main/java/com/milaboratory/mixcr/vdjaligners/VDJCAligner.java +++ b/src/main/java/com/milaboratory/mixcr/vdjaligners/VDJCAligner.java @@ -30,10 +30,11 @@ package com.milaboratory.mixcr.vdjaligners; import cc.redberry.pipe.Processor; +import com.milaboratory.core.alignment.batch.AlignmentHit; import com.milaboratory.core.io.sequence.SequenceRead; import com.milaboratory.core.io.sequence.SingleRead; +import com.milaboratory.mixcr.basictypes.HasGene; import com.milaboratory.mixcr.basictypes.VDJCAlignments; -import com.milaboratory.mixcr.basictypes.VDJCHit; import com.milaboratory.util.HashFunctions; import com.milaboratory.util.RandomUtil; import io.repseq.core.Chains; @@ -172,12 +173,81 @@ public static VDJCAligner createAligner(VDJCAlignerParameters alignerParameters, : new VDJCAlignerS(alignerParameters); } - public static Chains getPossibleDLoci(VDJCHit[] vHits, VDJCHit[] jHits) { - Chains chains = new Chains(); - for (VDJCHit h : vHits) - chains = chains.merge(h.getGene().getChains()); - for (VDJCHit h : jHits) - chains = chains.merge(h.getGene().getChains()); + // public static Chains getPossibleDLoci(VDJCHit[] vHits, VDJCHit[] jHits) { + // Chains chains = new Chains(); + // for (VDJCHit h : vHits) + // chains = chains.merge(h.getGene().getChains()); + // for (VDJCHit h : jHits) + // chains = chains.merge(h.getGene().getChains()); + // return chains; + // } + + /* + * Common utility methods + */ + + static Chains getVJCommonChains(final HasGene[] vHits, final HasGene[] jHits) { + return getChains(vHits).intersection(getChains(jHits)); + } + + /** + * Merge chains of all V/D/J/C hits. + * + * Allows null as input. + */ + public static Chains getChains(final HasGene[] hits) { + if (hits == null || hits.length == 0) + return Chains.ALL; + Chains chains = hits[0].getGene().getChains(); + for (int i = 1; i < hits.length; i++) + chains = chains.merge(hits[i].getGene().getChains()); return chains; } + + /** + * Calculates possible chains for D gene alignment, in the presence of following V, J and C genes. + * + * Allows nulls as input. + */ + public static Chains getPossibleDLoci(HasGene[] vHits, HasGene[] jHits, HasGene[] cHits) { + Chains intersection = getChains(vHits) + .intersection(getChains(jHits)) + .intersection(getChains(cHits)); + + if (!intersection.isEmpty()) + return intersection; + + // If intersection is empty, we are working with chimera + // lets calculate all possible D loci by merging + Chains chains = Chains.EMPTY; + if (vHits != null) + for (HasGene h : vHits) + chains = chains.merge(h.getGene().getChains()); + if (jHits != null) + for (HasGene h : jHits) + chains = chains.merge(h.getGene().getChains()); + if (cHits != null) + for (HasGene h : cHits) + chains = chains.merge(h.getGene().getChains()); + + return chains; + } + + static HasGene[] wrapAlignmentHits(final AlignmentHit[] hits) { + if (hits == null) + return null; + HasGene[] res = new HasGene[hits.length]; + for (int i = 0; i < hits.length; i++) + res[i] = wrapAlignmentHit(hits[i]); + return res; + } + + static HasGene wrapAlignmentHit(final AlignmentHit hit) { + return new HasGene() { + @Override + public VDJCGene getGene() { + return hit.getRecordPayload(); + } + }; + } } diff --git a/src/main/java/com/milaboratory/mixcr/vdjaligners/VDJCAlignerAbstract.java b/src/main/java/com/milaboratory/mixcr/vdjaligners/VDJCAlignerAbstract.java index effa002da..17da0f5e7 100644 --- a/src/main/java/com/milaboratory/mixcr/vdjaligners/VDJCAlignerAbstract.java +++ b/src/main/java/com/milaboratory/mixcr/vdjaligners/VDJCAlignerAbstract.java @@ -60,14 +60,14 @@ public VDJCAlignerAbstract(VDJCAlignerParameters parameters) { } VDJCAlignerAbstract(boolean initialized, - VDJCAlignerParameters parameters, - EnumMap> genesToAlign, - List usedGenes, - SingleDAligner singleDAligner, - EnumMap> filters, - BatchAlignerWithBaseWithFilter> vAligner, - BatchAlignerWithBaseWithFilter> jAligner, - BatchAlignerWithBaseWithFilter> cAligner) { + VDJCAlignerParameters parameters, + EnumMap> genesToAlign, + List usedGenes, + SingleDAligner singleDAligner, + EnumMap> filters, + BatchAlignerWithBaseWithFilter> vAligner, + BatchAlignerWithBaseWithFilter> jAligner, + BatchAlignerWithBaseWithFilter> cAligner) { super(initialized, parameters, genesToAlign, usedGenes); this.singleDAligner = singleDAligner; this.filters = filters; diff --git a/src/main/java/com/milaboratory/mixcr/vdjaligners/VDJCAlignerPVFirst.java b/src/main/java/com/milaboratory/mixcr/vdjaligners/VDJCAlignerPVFirst.java index ee9675afb..808acb752 100644 --- a/src/main/java/com/milaboratory/mixcr/vdjaligners/VDJCAlignerPVFirst.java +++ b/src/main/java/com/milaboratory/mixcr/vdjaligners/VDJCAlignerPVFirst.java @@ -366,7 +366,7 @@ PAlignmentHelper alignVThenJ(final Target target) { boolean vChimera = checkAndEliminateChimera(vAl1, vAl2, GeneType.Variable); - PairedHit[] vHits = extractDoubleHits(vAl1, vAl2); + PairedHit[] vHits = createPairedHits(vAl1, vAl2); /* * Step 2: of round of V gene alignment with more precise algorithm @@ -433,7 +433,7 @@ PAlignmentHelper alignVThenJ(final Target target) { boolean jChimera = checkAndEliminateChimera(jAl1, jAl2, GeneType.Joining); - PairedHit[] jHits = extractDoubleHits(jAl1, jAl2); + PairedHit[] jHits = createPairedHits(jAl1, jAl2); for (PairedHit jHit : jHits) { if (jHit.hit0 == null) { @@ -584,7 +584,7 @@ List> performJAlignment( /** * Converts two AlignmentResults to an array of paired hits (each paired hit for a particular V of J gene) */ - static PairedHit[] extractDoubleHits(List>... results) { + static PairedHit[] createPairedHits(List>... results) { Map hits = new HashMap<>(); addHits(hits, results[0], 0); addHits(hits, results[1], 1); @@ -612,28 +612,6 @@ static void addHits(Map hits, } } - static Chains getVJCommonChains(final PairedHit[] vHits, final PairedHit[] jHits) { - return getChains(vHits).intersection(getChains(jHits)); - } - - static Chains getChains(final PairedHit[] hits) { - if (hits.length == 0) - return Chains.ALL; - Chains chains = hits[0].getGene().getChains(); - for (int i = 1; i < hits.length; i++) - chains = chains.merge(hits[i].getGene().getChains()); - return chains; - } - - static Chains getChains(final VDJCHit[] hits) { - if (hits.length == 0) - return Chains.ALL; - Chains chains = hits[0].getGene().getChains(); - for (int i = 1; i < hits.length; i++) - chains = chains.merge(hits[i].getGene().getChains()); - return chains; - } - static final PreVDJCHit[] zeroArray = new PreVDJCHit[0]; @SuppressWarnings("unchecked") static final AlignmentHit[] zeroKArray = new AlignmentHit[0]; @@ -819,39 +797,6 @@ public void filterHits(float minTotalScore, int maxHits) { } } - public static Chains getPossibleDLoci(PairedHit[] vHits, PairedHit[] jHits, VDJCHit[] cHits) { - Chains intersection = getChains(vHits) - .intersection(getChains(jHits)) - .intersection(getChains(cHits)); - - if (!intersection.isEmpty()) - return intersection; - - // If intersection is empty, we are working with chimer - // lets calculate all possible D loci - Chains chains = new Chains(); - for (PairedHit h : vHits) - chains = chains.merge(h.getGene().getChains()); - for (PairedHit h : jHits) - chains = chains.merge(h.getGene().getChains()); - for (VDJCHit h : cHits) - chains = chains.merge(h.getGene().getChains()); - - return chains; - } - - ///** - // * Converts array of "internal" PairedHits to a double array of KAlignmentHits to pass this value to a VDJAlignment - // * constructor (VDJAlignmentImmediate). - // */ - //@SuppressWarnings("unchecked") - //static AlignmentHit[][] toArray(PairedHit[] hits) { - // AlignmentHit[][] hitsArray = new AlignmentHit[hits.length][]; - // for (int i = 0; i < hits.length; ++i) - // hitsArray[i] = new AlignmentHit[]{hits[i].hit0, hits[i].hit1}; - // return hitsArray; - //} - /** * Calculates normal "sum" score for each hit and sort hits according to this score. */ diff --git a/src/main/java/com/milaboratory/mixcr/vdjaligners/VDJCAlignerS.java b/src/main/java/com/milaboratory/mixcr/vdjaligners/VDJCAlignerS.java index d1d44dd1d..8daab46cc 100644 --- a/src/main/java/com/milaboratory/mixcr/vdjaligners/VDJCAlignerS.java +++ b/src/main/java/com/milaboratory/mixcr/vdjaligners/VDJCAlignerS.java @@ -338,17 +338,6 @@ public void calculateHits(float minTotalScore, int maxHits) { public void alignDC() { NucleotideSequence sequence = target.targets[0].getSequence(); - if (singleDAligner != null && hasKVAndJHits()) { - //Alignment of D gene - int from = vResult.get(0).getAlignment().getSequence2Range().getTo(), - to = jResult.get(0).getAlignment().getSequence2Range().getFrom(); - List dResult = from > to ? - Collections.emptyList() : - singleDAligner.align0(sequence, getPossibleDLoci(), from, to); - dHits = PreVDJCHit.convert(getDGenesToAlign(), - parameters.getFeatureToAlign(GeneType.Diversity), dResult); - } - boolean doCAlignment = cAligner != null; if (parameters.getAllowNoCDR3PartAlignments()) @@ -367,6 +356,21 @@ public void alignDC() { cHits = createHits(res.getHits(), parameters.getFeatureToAlign(GeneType.Constant)); } else cHits = new VDJCHit[0]; + + if (singleDAligner != null && hasKVAndJHits()) { + //Alignment of D gene + int from = vResult.get(0).getAlignment().getSequence2Range().getTo(), + to = jResult.get(0).getAlignment().getSequence2Range().getFrom(); + List dResult = from > to ? + Collections.emptyList() : + singleDAligner.align0(sequence, getPossibleDLoci( + wrapAlignmentHits(vHits), + wrapAlignmentHits(jHits), cHits), + from, to); + dHits = PreVDJCHit.convert(getDGenesToAlign(), + parameters.getFeatureToAlign(GeneType.Diversity), dResult); + } + } public boolean hasNoKVNorKJHits() { @@ -411,15 +415,6 @@ public float sumScore() { return score; } - public Chains getPossibleDLoci() { - Chains chains = new Chains(); - for (AlignmentHit vHit : vResult) - chains = chains.merge(vHit.getRecordPayload().getChains()); - for (AlignmentHit jHit : jResult) - chains = chains.merge(jHit.getRecordPayload().getChains()); - return chains; - } - public VDJCAlignments toVDJCAlignments(long inputId) { EnumMap hits = new EnumMap<>(GeneType.class); diff --git a/src/test/java/com/milaboratory/mixcr/util/RunMiXCRTest.java b/src/test/java/com/milaboratory/mixcr/util/RunMiXCRTest.java index d7ea5c4a2..f40b42a10 100644 --- a/src/test/java/com/milaboratory/mixcr/util/RunMiXCRTest.java +++ b/src/test/java/com/milaboratory/mixcr/util/RunMiXCRTest.java @@ -60,7 +60,8 @@ public void test1() throws Exception { RunMiXCR.AssembleResult assemble = RunMiXCR.assemble(align); for (Clone clone : assemble.cloneSet.getClones()) { - Chains vjLoci = VDJCAligner.getPossibleDLoci(clone.getHits(GeneType.Variable), clone.getHits(GeneType.Joining)); + Chains vjLoci = VDJCAligner.getPossibleDLoci(clone.getHits(GeneType.Variable), clone.getHits(GeneType.Joining), + null); for (VDJCHit dHit : clone.getHits(GeneType.Diversity)) Assert.assertTrue(vjLoci.intersects(dHit.getGene().getChains())); } @@ -117,7 +118,7 @@ public void test2() throws Exception { RunMiXCR.AlignResult align = RunMiXCR.align(params); List reads = new ArrayList<>(); - try(PairedFastqReader fReader = new PairedFastqReader( + try (PairedFastqReader fReader = new PairedFastqReader( RunMiXCR.class.getResource("/sequences/test_R1.fastq").getFile(), RunMiXCR.class.getResource("/sequences/test_R2.fastq").getFile())) { for (PairedRead s : CUtils.it(fReader)) @@ -125,13 +126,13 @@ public void test2() throws Exception { } File tempFile = TempFileManager.getTempFile(); - try(VDJCAlignmentsWriter writer = new VDJCAlignmentsWriter(tempFile)) { + try (VDJCAlignmentsWriter writer = new VDJCAlignmentsWriter(tempFile)) { writer.header(align.aligner); for (VDJCAlignments alignment : align.alignments) writer.write(alignment); } - try(VDJCAlignmentsReader reader = new VDJCAlignmentsReader(tempFile)) { + try (VDJCAlignmentsReader reader = new VDJCAlignmentsReader(tempFile)) { int tr = 0; for (VDJCAlignments alignment : CUtils.it(reader)) { PairedRead actual = reads.get((int) alignment.getReadId()); @@ -154,7 +155,7 @@ public void test3() throws Exception { RunMiXCR.AlignResult align = RunMiXCR.align(params); List reads = new ArrayList<>(); - try(PairedFastqReader fReader = new PairedFastqReader( + try (PairedFastqReader fReader = new PairedFastqReader( "/Users/poslavsky/Projects/milab/temp/R1_part.fastq.gz", "/Users/poslavsky/Projects/milab/temp/R2_part.fastq.gz", true)) { for (PairedRead s : CUtils.it(fReader)) @@ -162,13 +163,13 @@ public void test3() throws Exception { } File tempFile = TempFileManager.getTempFile(); - try(VDJCAlignmentsWriter writer = new VDJCAlignmentsWriter(tempFile)) { + try (VDJCAlignmentsWriter writer = new VDJCAlignmentsWriter(tempFile)) { writer.header(align.aligner); for (VDJCAlignments alignment : align.alignments) writer.write(alignment); } - try(VDJCAlignmentsReader reader = new VDJCAlignmentsReader(tempFile)) { + try (VDJCAlignmentsReader reader = new VDJCAlignmentsReader(tempFile)) { int tr = 0; for (VDJCAlignments alignment : CUtils.it(reader)) { PairedRead actual = reads.get((int) alignment.getReadId()); From a1a718ad531e05dbc091ca0136ed022d4ad54c8c Mon Sep 17 00:00:00 2001 From: Dmitry Bolotin Date: Wed, 22 Aug 2018 14:23:52 +0300 Subject: [PATCH 2/4] Migration to new Jackson annotations for non-null inclusion. This fixes #412 --- .../mixcr/assembler/CloneFactoryParameters.java | 9 +++++---- .../mixcr/vdjaligners/VDJCAlignerParameters.java | 8 ++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/milaboratory/mixcr/assembler/CloneFactoryParameters.java b/src/main/java/com/milaboratory/mixcr/assembler/CloneFactoryParameters.java index eab2a6390..42081d1a6 100644 --- a/src/main/java/com/milaboratory/mixcr/assembler/CloneFactoryParameters.java +++ b/src/main/java/com/milaboratory/mixcr/assembler/CloneFactoryParameters.java @@ -32,6 +32,7 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.milaboratory.mixcr.basictypes.ClonalUpdatableParameters; @@ -93,25 +94,25 @@ public VJCClonalAlignerParameters getVJCParameters(GeneType geneType) { } @JsonProperty("vParameters") - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_NULL) public VJCClonalAlignerParameters getVParameters() { return vdcParameters.get(GeneType.Variable); } @JsonProperty("jParameters") - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_NULL) public VJCClonalAlignerParameters getJParameters() { return vdcParameters.get(GeneType.Joining); } @JsonProperty("cParameters") - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_NULL) public VJCClonalAlignerParameters getCParameters() { return vdcParameters.get(GeneType.Constant); } @JsonProperty("dParameters") - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_NULL) public DClonalAlignerParameters getDParameters() { return dParameters; } diff --git a/src/main/java/com/milaboratory/mixcr/vdjaligners/VDJCAlignerParameters.java b/src/main/java/com/milaboratory/mixcr/vdjaligners/VDJCAlignerParameters.java index 4eee8b448..a24eed910 100644 --- a/src/main/java/com/milaboratory/mixcr/vdjaligners/VDJCAlignerParameters.java +++ b/src/main/java/com/milaboratory/mixcr/vdjaligners/VDJCAlignerParameters.java @@ -190,25 +190,25 @@ public KGeneAlignmentParameters getVJCGeneAlignerParameters(GeneType geneType) { } @JsonProperty("vParameters") - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_NULL) public KGeneAlignmentParameters getVAlignerParameters() { return getVJCGeneAlignerParameters(GeneType.Variable); } @JsonProperty("dParameters") - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_NULL) public DAlignerParameters getDAlignerParameters() { return (DAlignerParameters) getGeneAlignerParameters(GeneType.Diversity); } @JsonProperty("jParameters") - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_NULL) public KGeneAlignmentParameters getJAlignerParameters() { return getVJCGeneAlignerParameters(GeneType.Joining); } @JsonProperty("cParameters") - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_NULL) public KGeneAlignmentParameters getCAlignerParameters() { return getVJCGeneAlignerParameters(GeneType.Constant); } From 99f9cc04e4c0778a7c9a6bb96dbe929887b7a3d3 Mon Sep 17 00:00:00 2001 From: Dmitry Bolotin Date: Wed, 22 Aug 2018 15:21:32 +0300 Subject: [PATCH 3/4] Changelog. --- CHANGELOG_CURRENT | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG_CURRENT b/CHANGELOG_CURRENT index e69de29bb..596b02b3b 100644 --- a/CHANGELOG_CURRENT +++ b/CHANGELOG_CURRENT @@ -0,0 +1 @@ +Fixes rare inconsistency in behaviour between paired-end and single-end aligners, clone assembler and partial sequence assembler in respect to target D gene set selection based on the chains on V, J and C genes (#408) \ No newline at end of file From 6689e2ad76108dcae12b9327ea57cb9882562080 Mon Sep 17 00:00:00 2001 From: Dmitry Bolotin Date: Wed, 22 Aug 2018 15:48:08 +0300 Subject: [PATCH 4/4] Release v2.1.12 --- CHANGELOG | 8 ++++++++ CHANGELOG_CURRENT | 1 - pom.xml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 7454339db..53da0b54b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,12 @@ +MiXCR 2.1.12 (22 Aug 2018) +======================== + +-- Fixes rare inconsistency in behaviour between paired-end and single-end aligners, clone + assembler and partial sequence assembler in respect to target D gene set selection based on the + chains on V, J and C genes (#408) + + MiXCR 2.1.11 (15 Jun 2018) ======================== diff --git a/CHANGELOG_CURRENT b/CHANGELOG_CURRENT index 596b02b3b..e69de29bb 100644 --- a/CHANGELOG_CURRENT +++ b/CHANGELOG_CURRENT @@ -1 +0,0 @@ -Fixes rare inconsistency in behaviour between paired-end and single-end aligners, clone assembler and partial sequence assembler in respect to target D gene set selection based on the chains on V, J and C genes (#408) \ No newline at end of file diff --git a/pom.xml b/pom.xml index 813e328d2..1ffb149c0 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ com.milaboratory mixcr - 2.1.11 + 2.1.12 jar MiXCR