@Override
    public Prediction<MultiLabel> combine(ImmutableOutputInfo<MultiLabel> outputInfo, List<Prediction<MultiLabel>> predictions) {
        int numPredictions = predictions.size();
        double weight = 1.0 / numPredictions;
        int numUsed = 0;
        double[] posScore = new double[outputInfo.size()];
        double[] negScore = new double[outputInfo.size()];
        for (Prediction<MultiLabel> p : predictions) {
            if (numUsed < p.getNumActiveFeatures()) {
                numUsed = p.getNumActiveFeatures();
            }
            DenseVector v = p.getOutput().convertToDenseVector(outputInfo);
            for (int j = 0; j < v.size(); j++) {
                double score = v.get(j);
                if (score > 0.5) {
                    posScore[j] += weight;
                } else {
                    negScore[j] += weight;
                }
            }
        }

        Map<String,MultiLabel> fullLabels = new LinkedHashMap<>();
        Set<Label> predSet = new HashSet<>();
        for (int i = 0; i < posScore.length; i++) {
            String name = outputInfo.getOutput(i).getLabelString();
            double score = posScore[i] / (posScore[i] + negScore[i]);
            Label label = new Label(name, score);
            if (score > 0.5) {
                predSet.add(label);
            }
            fullLabels.put(name,new MultiLabel(label));
        }

        Example<MultiLabel> example = predictions.get(0).getExample();

        return new Prediction<>(new MultiLabel(predSet),fullLabels,numUsed,example,true);
    }
@Test
    public void votingCombinerTest() {
        MultiLabel empty = new MultiLabel(Collections.emptySet());
        MultiLabel purple = new MultiLabel(purpleLbl);
        MultiLabel monkey = new MultiLabel(monkeyLbl);
        MultiLabel dishwasher = new MultiLabel(dishwasherLbl);
        MultiLabel pidgeon = new MultiLabel(pidgeonLbl);
        MultiLabel dryer = new MultiLabel(dryerLbl);
        Set<Label> pmdSet = new HashSet<>();
        pmdSet.add(purpleLbl);
        pmdSet.add(monkeyLbl);
        pmdSet.add(dishwasherLbl);
        MultiLabel pmd = new MultiLabel(pmdSet);

        MutableOutputInfo<MultiLabel> info = factory.generateInfo();
        info.observe(empty);
        info.observe(purple);
        info.observe(monkey);
        info.observe(dishwasher);
        info.observe(pidgeon);
        info.observe(dryer);
        ImmutableOutputInfo<MultiLabel> immutableInfo = info.generateImmutableOutputInfo();

        Example<MultiLabel> testExample = new ArrayExample<>(empty,new String[]{"X_0","X_1","X_2"}, new double[]{1,2,3});
        List<Prediction<MultiLabel>> predictions = new ArrayList<>();

        // Combiner predicts the empty set
        predictions.add(new Prediction<>(empty,3,testExample));
        predictions.add(new Prediction<>(monkey,3,testExample));
        predictions.add(new Prediction<>(purple,3,testExample));
        Prediction<MultiLabel> output = votingCombiner.combine(immutableInfo,predictions);
        assertEquals(empty,output.getOutput());
        assertEquals(5,output.getOutputScores().size());
        assertEquals(1.0/3,output.getOutputScores().get(purpleLbl.getLabel()).getLabelScore(purpleLbl).getAsDouble());
        assertEquals(1.0/3,output.getOutputScores().get(monkeyLbl.getLabel()).getLabelScore(monkeyLbl).getAsDouble());
        assertEquals(0.0,output.getOutputScores().get(dishwasherLbl.getLabel()).getLabelScore(dishwasherLbl).getAsDouble());
        assertEquals(0.0,output.getOutputScores().get(pidgeonLbl.getLabel()).getLabelScore(pidgeonLbl).getAsDouble());
        assertEquals(0.0,output.getOutputScores().get(dryerLbl.getLabel()).getLabelScore(dryerLbl).getAsDouble());
        predictions.clear();

        // Combiner predicts the majority vote for one label
        predictions.add(new Prediction<>(monkey,3,testExample));
        predictions.add(new Prediction<>(monkey,3,testExample));
        predictions.add(new Prediction<>(purple,3,testExample));
        output = votingCombiner.combine(immutableInfo,predictions);
        assertEquals(monkey,output.getOutput());
        assertEquals(5,output.getOutputScores().size());
        assertEquals(1.0/3,output.getOutputScores().get(purpleLbl.getLabel()).getLabelScore(purpleLbl).getAsDouble());
        assertEquals(2.0/3,output.getOutputScores().get(monkeyLbl.getLabel()).getLabelScore(monkeyLbl).getAsDouble());
        assertEquals(0.0,output.getOutputScores().get(dishwasherLbl.getLabel()).getLabelScore(dishwasherLbl).getAsDouble());
        assertEquals(0.0,output.getOutputScores().get(pidgeonLbl.getLabel()).getLabelScore(pidgeonLbl).getAsDouble());
        assertEquals(0.0,output.getOutputScores().get(dryerLbl.getLabel()).getLabelScore(dryerLbl).getAsDouble());
        predictions.clear();

        // Combiner predicts the majority vote for all labels
        predictions.add(new Prediction<>(pmd,3,testExample));
        predictions.add(new Prediction<>(pmd,3,testExample));
        predictions.add(new Prediction<>(purple,3,testExample));
        output = votingCombiner.combine(immutableInfo,predictions);
        assertEquals(pmd,output.getOutput());
        assertEquals(5,output.getOutputScores().size());
        assertEquals(3.0/3,output.getOutputScores().get(purpleLbl.getLabel()).getLabelScore(purpleLbl).getAsDouble());
        assertEquals(2.0/3,output.getOutputScores().get(monkeyLbl.getLabel()).getLabelScore(monkeyLbl).getAsDouble());
        assertEquals(2.0/3,output.getOutputScores().get(dishwasherLbl.getLabel()).getLabelScore(dishwasherLbl).getAsDouble());
        assertEquals(0.0,output.getOutputScores().get(pidgeonLbl.getLabel()).getLabelScore(pidgeonLbl).getAsDouble());
        assertEquals(0.0,output.getOutputScores().get(dryerLbl.getLabel()).getLabelScore(dryerLbl).getAsDouble());
        predictions.clear();

        // Combiner predicts the weighted majority vote
        float[] weights = new float[]{0.1f,0.1f,0.0f,2.0f,3.0f};
        predictions.add(new Prediction<>(pmd,3,testExample));
        predictions.add(new Prediction<>(pmd,3,testExample));
        predictions.add(new Prediction<>(purple,3,testExample));
        predictions.add(new Prediction<>(pidgeon,3,testExample));
        predictions.add(new Prediction<>(dryer,3,testExample));
        output = votingCombiner.combine(immutableInfo,predictions,weights);
        assertEquals(dryer,output.getOutput());
        assertEquals(5,output.getOutputScores().size());
        assertEquals(0.2f/5.2f,output.getOutputScores().get(purpleLbl.getLabel()).getLabelScore(purpleLbl).getAsDouble(),1e-6);
        assertEquals(0.2f/5.2f,output.getOutputScores().get(monkeyLbl.getLabel()).getLabelScore(monkeyLbl).getAsDouble(),1e-6);
        assertEquals(0.2f/5.2f,output.getOutputScores().get(dishwasherLbl.getLabel()).getLabelScore(dishwasherLbl).getAsDouble(),1e-6);
        assertEquals(2.0f/5.2f,output.getOutputScores().get(pidgeonLbl.getLabel()).getLabelScore(pidgeonLbl).getAsDouble(),1e-6);
        assertEquals(3.0f/5.2f,output.getOutputScores().get(dryerLbl.getLabel()).getLabelScore(dryerLbl).getAsDouble(),1e-6);
        predictions.clear();

        // Test invalid weights
        float[] newWeights = new float[2];
        predictions.add(new Prediction<>(pmd,3,testExample));
        predictions.add(new Prediction<>(pmd,3,testExample));
        predictions.add(new Prediction<>(purple,3,testExample));
        assertThrows(IllegalArgumentException.class,() -> votingCombiner.combine(immutableInfo,predictions,newWeights));
        predictions.clear();
        predictions.add(new Prediction<>(pmd,3,testExample));
        assertThrows(IllegalArgumentException.class,() -> votingCombiner.combine(immutableInfo,predictions,newWeights));
    }