@Override
  public Solution pack(double upperBound) {

    Set<Cycle> matching = new HashSet<Cycle>();
    double objVal = 0.0;
    Set<Vertex> matchedVerts = new HashSet<Vertex>();

    long start = System.nanoTime();

    // First, use every altruist by packing chains
    if (maxChainSize > 1) {
      for (Vertex alt : pool.getAltruists()) {

        // Can't sample any chains from isolated altruists
        if (pool.outgoingEdgesOf(alt).isEmpty()) {
          continue;
        }

        Cycle chain = sampleAChain(alt, matchedVerts, maxChainSize, usingFailureProbabilities);

        // Couldn't find a legal path from this altruist
        if (null == chain) {
          continue;
        }

        // We check legality of the chain during generation, so add all verts and chain to matching
        Set<Vertex> cVerts = Cycle.getConstituentVertices(chain, pool);
        matchedVerts.addAll(cVerts);
        objVal += chain.getWeight();
        matching.add(chain);

        // If we hit the upper bound, break out
        if (objVal >= upperBound) {
          break;
        }
      }
    }

    // Second, pack remaining vertices in cycles (using a VertexShufflePacker)
    VertexShufflePacker cyclePacker =
        new VertexShufflePacker(
            this.pool, this.cycles, this.membership, this.shuffleType, matchedVerts);
    Solution cyclesOnly = cyclePacker.pack(upperBound - objVal);

    // Add these packed cycles to our full matching
    matching.addAll(cyclesOnly.getMatching());
    objVal += cyclesOnly.getObjectiveValue();

    long end = System.nanoTime();
    long totalTime = end - start;

    // Construct formal matching, return
    Solution sol = new Solution();
    sol.setMatching(matching);
    sol.setObjectiveValue(objVal);
    sol.setSolveTime(totalTime);
    return sol;
  }
  public static void removeKidneyToLiverEdges(
      Pool pool, Random r, double probKidneyToLiver, double pctKidney) {

    IOUtil.dPrintln("Removing edges from some kidney donors to liver pairs.");

    // First, label the vertices as either kidney- or liver-needing (all altruists are assumed
    // kidney)
    Set<Vertex> kidneyPairedDonors = new HashSet<Vertex>();
    Set<Vertex> liverPairedDonors = new HashSet<Vertex>();
    for (VertexPair vp : pool.getPairs()) {
      if (r.nextDouble() < pctKidney) {
        kidneyPairedDonors.add(vp);
      } else {
        liverPairedDonors.add(vp);
      }
    }
    for (VertexAltruist alt : pool.getAltruists()) {
      kidneyPairedDonors.add(alt);
    }

    // Next, for each kidney-paired donor, determine if that donor is willing
    // to give a liver.  If not, remove all outgoing edges to liver-paired donors
    Set<Edge> edgesToRemove = new HashSet<Edge>();
    for (Vertex kidneyV : kidneyPairedDonors) {
      boolean willingToGive = (r.nextDouble() < probKidneyToLiver);
      willingToGive &=
          !(kidneyV.isAltruist()); // disallow any kidney altruists from given to liver pairs
      if (willingToGive) {
        continue;
      }

      for (Edge e : pool.outgoingEdgesOf(kidneyV)) {
        if (liverPairedDonors.contains(pool.getEdgeTarget(e))) {
          edgesToRemove.add(e);
        }
      }
    }
    int removedEdgeCt = 0;
    for (Edge e : edgesToRemove) {
      pool.removeEdge(e);
      removedEdgeCt++;
    }

    IOUtil.dPrintln(
        "Removed "
            + removedEdgeCt
            + " edges from kidney donors to liver pairs ("
            + pool.edgeSet().size()
            + " remain).");
  }