|
| 1 | +# Special thanks to Marius Minea for teaching a great class on higher-order functions! - xoth42 |
| 2 | + |
| 3 | +""" |
| 4 | + cleanup_two_measurements!(ops,num_pairs) |
| 5 | +
|
| 6 | +Remove unnecessary measurements (back to back on the same qubit) |
| 7 | +""" |
| 8 | +function cleanup_two_measurements!(ops,num_pairs) |
| 9 | + # This starts as [false,false...] for each pair/qubit. |
| 10 | + # for each op, mark this accordingly -> non-measure would set the qubits to false, and yes measure would set qubits to true. If we encounter a measure on something that already has true, it is a redundant back-to-back measure, so we delete that op. |
| 11 | + last_op_on_pair_was_measure = falses(num_pairs) |
| 12 | + |
| 13 | + ops_to_delete = [] # this will collect the indexes of redundant measures to be removed |
| 14 | + |
| 15 | + # go through the each operation |
| 16 | + for (op_index,op) in enumerate(ops) |
| 17 | + measure = typeof(op) <: AbstractMeasurement # is the op a measurement? |
| 18 | + for qubit in affectedqubits(op) |
| 19 | + if measure |
| 20 | + # measure found, is it redundant? |
| 21 | + if last_op_on_pair_was_measure[qubit] |
| 22 | + # redundant measure found. add index to delete list |
| 23 | + push!(ops_to_delete,op_index) |
| 24 | + else |
| 25 | + # non redundant, but mark measured array incase there is another |
| 26 | + last_op_on_pair_was_measure[qubit] = true |
| 27 | + end |
| 28 | + else |
| 29 | + # this op is not a measurement, mark measured array as false |
| 30 | + last_op_on_pair_was_measure[qubit] = false |
| 31 | + end |
| 32 | + end |
| 33 | + end |
| 34 | + |
| 35 | + # Now act on the delete list |
| 36 | + return deleteat!(ops,ops_to_delete) |
| 37 | +end |
| 38 | + |
| 39 | +""" |
| 40 | + get_used_qubits(ops) |
| 41 | +
|
| 42 | +Get a Set of all qubits that are affected by the ops. |
| 43 | +""" |
| 44 | +function get_used_qubits(ops) |
| 45 | + # for each op, get its affectedqubits. (map ops to qubits) |
| 46 | + # affected qubits can return Int, or Vector{Int} |
| 47 | + # expand these results (...), making a set of all ints |
| 48 | + # this effectively is the set of all 'affected/used' qubits/pairs |
| 49 | + return reduce((set,qubits) -> push!(set, affectedqubits(qubits)...), ops; init=Set()) |
| 50 | +end |
| 51 | + |
| 52 | +""" |
| 53 | + unsafe_cleanup_untargeted_pairs!(ops,num_pairs,num_purified) |
| 54 | +
|
| 55 | +UNSAFE |
| 56 | +Check if the circuit has an unused qubit (technically a pair), if so, add a random two qubit Bell preserving gate `CNOTPerm` and coincidence measure in a random basis, ie, use it in a "probably" good way. |
| 57 | +""" |
| 58 | +function unsafe_cleanup_untargeted_pairs!(ops,num_pairs,num_purified) |
| 59 | + if num_pairs <= num_purified |
| 60 | + # assumption for adding CNOTPerms. |
| 61 | + # if the user wants it anyway, just warn and return |
| 62 | + @warn "Not enough pairs - skipping canonicalization step cleanup_untargeted_pairs!" |
| 63 | + return ops |
| 64 | + end |
| 65 | + |
| 66 | + # The pairs that should be used (all of them) |
| 67 | + pairs_to_use = Set([i for i in 1:num_pairs]) |
| 68 | + |
| 69 | + # pairs that are actually used |
| 70 | + pairs_used = get_used_qubits(ops) |
| 71 | + |
| 72 | + # pairs that are unused, and others |
| 73 | + pairs_unused = symdiff(pairs_to_use,pairs_used) |
| 74 | + |
| 75 | + # use any unused pairs |
| 76 | + for pair in pairs_unused |
| 77 | + # all pairs should be in the usable pairs |
| 78 | + @assert pair in pairs_to_use |
| 79 | + |
| 80 | + # if it is a purified pair, use this pair as a control for a new CNOT |
| 81 | + if pair <= num_purified |
| 82 | + pushfirst!(ops,CNOTPerm(rand(1:6),rand(1:6),pair,rand(num_purified+1:num_pairs))) |
| 83 | + else |
| 84 | + # add a CNOT, and measure to the front. New ops becomes -> [CNOT, MEAS, ...] |
| 85 | + # using pushfirst! so this is done in opposite order. |
| 86 | + # control will be a purified pair, and target is this pair |
| 87 | + pushfirst!(ops,rand(BellMeasure,pair)) |
| 88 | + pushfirst!(ops,rand(CNOTPerm,rand(1:num_purified),pair)) |
| 89 | + end |
| 90 | + end |
| 91 | + |
| 92 | + return ops |
| 93 | +end |
| 94 | + |
| 95 | +""" |
| 96 | + cleanup_measurements_on_top_qubits!(ops,num_purified) |
| 97 | +
|
| 98 | +Checks for and removes any measurements on purified pairs |
| 99 | +""" |
| 100 | +function cleanup_measurements_on_top_qubits!(ops,num_purified) |
| 101 | + return filter!(op-> |
| 102 | + !( # remove it if, |
| 103 | + typeof(op) <: AbstractMeasurement # it is a measurement and, |
| 104 | + && op.sidx <= num_purified # it measures a purified pair |
| 105 | + ), ops) |
| 106 | +end |
| 107 | + |
| 108 | +""" |
| 109 | + unsafe_cleanup_nonmeasurement_last_steps!(ops,num_pairs,num_purified) |
| 110 | +
|
| 111 | +UNSAFE |
| 112 | +If there is a non-measurement in the last step of a non-purified pair, add random coincidence measurements. |
| 113 | +""" |
| 114 | +function unsafe_cleanup_nonmeasurement_last_steps!(ops,num_pairs,num_purified) |
| 115 | + if num_pairs <= num_purified |
| 116 | + @warn "Not enough pairs - skipping canonicalization step cleanup_nonmeasurement_last_steps!" |
| 117 | + return ops |
| 118 | + end |
| 119 | + |
| 120 | + non_pure_last_steps = zeros(Int64,num_pairs - num_purified) # array id, plus the num_purified, is the non_pure pair number. The contents of the array |
| 121 | + notDone = true |
| 122 | + while notDone |
| 123 | + # get all of the last steps |
| 124 | + for (op_index,op) in enumerate(ops) |
| 125 | + qubits = affectedqubits(op) |
| 126 | + for qubit in qubits |
| 127 | + if qubit > num_purified |
| 128 | + # this is a non purified pair, mark a new last step |
| 129 | + non_pure_last_steps[qubit - num_purified] = op_index |
| 130 | + end |
| 131 | + end |
| 132 | + end |
| 133 | + |
| 134 | + # check if any of the last ops are not measurements, and ignore zeros (no ops on that qubit) |
| 135 | + qubits_who_need_measurements = reduce((arr, (qubit, op_index)) -> |
| 136 | + (op_index == 0 # filter out zeros |
| 137 | + || typeof(ops[op_index]) <: AbstractMeasurement) ? arr : # and filter out measurments |
| 138 | + push!(arr,qubit+num_purified), # otherwise, keep it and add 'num_purified' so the qubit index is correct |
| 139 | + enumerate(non_pure_last_steps);init=[]) |
| 140 | + |
| 141 | + # all of the marked ops are non_last measurements, so add measures to them |
| 142 | + for qubit in qubits_who_need_measurements |
| 143 | + push!(ops,rand(BellMeasure,qubit)) |
| 144 | + end |
| 145 | + |
| 146 | + # check if done |
| 147 | + if length(qubits_who_need_measurements) == 0 |
| 148 | + notDone = false |
| 149 | + end |
| 150 | + end |
| 151 | + return ops |
| 152 | +end |
| 153 | + |
| 154 | +""" |
| 155 | + canonicalize_cleanup!(pop::Population,num_pairs,num_purified; |
| 156 | + safe=true # true to only improve circuits, false to include canonicalizations that may make some circuits worse/make experimental changes to try to improve them |
| 157 | + ) |
| 158 | +
|
| 159 | +Apply all of the canonicalization cleanup methods to a population with tmap. Defaults to safe canonicalizations. |
| 160 | +""" |
| 161 | +function canonicalize_cleanup!(pop::Population,num_pairs,num_purified; |
| 162 | + safe=true # :all (include canonicalizations that may make some circuits worse/make experimental changes to try to improve) or :safe (only improve circuits) |
| 163 | + ) |
| 164 | + # Function to change the ops based on the canonicalizations requested |
| 165 | + cleanup! = safe ? |
| 166 | + (indiv) -> begin # safe is true |
| 167 | + # Safe canonicalization |
| 168 | + cleanup_measurements_on_top_qubits!(indiv.ops,num_purified) |
| 169 | + cleanup_two_measurements!(indiv.ops,num_pairs) |
| 170 | + end : (indiv) -> begin # safe is false |
| 171 | + # Unsafe canonicalization (plus safe checks) |
| 172 | + cleanup_measurements_on_top_qubits!(indiv.ops,num_purified) |
| 173 | + cleanup_two_measurements!(indiv.ops,num_pairs) |
| 174 | + unsafe_cleanup_nonmeasurement_last_steps!(indiv.ops,num_pairs,num_purified) |
| 175 | + unsafe_cleanup_untargeted_pairs!(indiv.ops,num_pairs,num_purified) |
| 176 | + end |
| 177 | + |
| 178 | + tmap(cleanup!, pop.individuals) |
| 179 | +end |
0 commit comments