12 #include <unordered_map> 28 template <
class SimulatorType>
31 std::unordered_set<std::string>
default_gates {
"X",
"Y",
"Z",
"I",
"H"};
55 using Mat2x2Type = decltype(std::declval<SimulatorType>().getGateX());
104 void addToMaps( SimulatorType& qSim, std::string U_label,
const Mat2x2Type& U, std::size_t num_ctrl_lines){
105 gate_cache.addToCache(qSim, U_label, U, num_ctrl_lines);
137 const std::vector<std::size_t> ctrlIndices,
138 const std::vector<std::size_t> auxIndices,
139 const unsigned int qTarget,
140 const std::string gateLabel,
142 const std::size_t depth
145 int local_depth = depth + 1;
148 std::size_t cOps = ctrlIndices.size();
175 if( (cOps >= 5) && ( auxIndices.size() >= cOps-2 ) && (gateLabel ==
"X") && (depth == 0) ){
176 qSim.applyGateCCX( ctrlIndices.back(), *(auxIndices.begin() + ctrlIndices.size() - 3), qTarget);
178 for (std::size_t i = ctrlIndices.size()-2; i >= 2; i--){
179 qSim.applyGateCCX( *(ctrlIndices.begin()+i), *(auxIndices.begin() + (i-2)), *(auxIndices.begin() + (i-1)));
181 qSim.applyGateCCX( *(ctrlIndices.begin()), *(ctrlIndices.begin()+1), *(auxIndices.begin()) );
183 for (std::size_t i = 2; i <= ctrlIndices.size()-2; i++){
184 qSim.applyGateCCX( *(ctrlIndices.begin()+i), *(auxIndices.begin()+(i-2)), *(auxIndices.begin()+(i-1)));
186 qSim.applyGateCCX( ctrlIndices.back(), *(auxIndices.begin() + ctrlIndices.size() - 3), qTarget);
188 for (std::size_t i = ctrlIndices.size()-2; i >= 2; i--){
189 qSim.applyGateCCX( *(ctrlIndices.begin()+i), *(auxIndices.begin() + (i-2)), *(auxIndices.begin() + (i-1)));
191 qSim.applyGateCCX( *(ctrlIndices.begin()), *(ctrlIndices.begin()+1), *(auxIndices.begin()) );
192 for (std::size_t i = 2; i <= ctrlIndices.size()-2; i++){
193 qSim.applyGateCCX( *(ctrlIndices.begin()+i), *(auxIndices.begin()+(i-2)), *(auxIndices.begin()+(i-1)));
199 qSim.applyGateCU(
gate_cache.gateCacheMap[gateLabel][local_depth+1].first, ctrlIndices[0], qTarget, gateLabel );
200 qSim.applyGateCX( ctrlIndices[0], ctrlIndices[1]);
202 qSim.applyGateCU(
gate_cache.gateCacheMap[gateLabel][local_depth+1].second, ctrlIndices[1], qTarget, gateLabel );
203 qSim.applyGateCX( ctrlIndices[0], ctrlIndices[1]);
205 qSim.applyGateCU(
gate_cache.gateCacheMap[gateLabel][local_depth+1].first, ctrlIndices[1], qTarget, gateLabel );
206 qSim.applyGateCX( ctrlIndices[1], ctrlIndices[2]);
208 qSim.applyGateCU(
gate_cache.gateCacheMap[gateLabel][local_depth+1].second, ctrlIndices[2], qTarget, gateLabel );
209 qSim.applyGateCX( ctrlIndices[0], ctrlIndices[2]);
211 qSim.applyGateCU(
gate_cache.gateCacheMap[gateLabel][local_depth+1].first, ctrlIndices[2], qTarget, gateLabel );
212 qSim.applyGateCX( ctrlIndices[1], ctrlIndices[2]);
214 qSim.applyGateCU(
gate_cache.gateCacheMap[gateLabel][local_depth+1].second, ctrlIndices[2], qTarget, gateLabel );
215 qSim.applyGateCX( ctrlIndices[0], ctrlIndices[2]);
217 qSim.applyGateCU(
gate_cache.gateCacheMap[gateLabel][local_depth+1].first, ctrlIndices[2], qTarget, gateLabel );
220 else if (cOps >= 2 && cOps !=3){
221 std::vector<std::size_t> subCtrlIndices(ctrlIndices.begin(), ctrlIndices.end()-1);
223 qSim.applyGateCU(
gate_cache.gateCacheMap[gateLabel][local_depth].first, ctrlIndices.back(), qTarget, gateLabel );
225 applyNQubitControl(qSim, subCtrlIndices, auxIndices, ctrlIndices.back(),
"X", qSim.getGateX(), 0 );
227 qSim.applyGateCU(
gate_cache.gateCacheMap[gateLabel][local_depth].second, ctrlIndices.back(), qTarget, gateLabel );
229 applyNQubitControl(qSim, subCtrlIndices, auxIndices, ctrlIndices.back(),
"X", qSim.getGateX(), 0 );
231 applyNQubitControl(qSim, subCtrlIndices, auxIndices, qTarget, gateLabel,
gate_cache.gateCacheMap[gateLabel][local_depth+1].first, local_depth );
236 qSim.applyGateCU(
gate_cache.gateCacheMap[gateLabel][depth].first, ctrlIndices[0], qTarget, gateLabel);
248 std::size_t num_ops = 0;
251 std::cout <<
"get_op_calls CTRL=" << num_ctrl_lines <<
" OPS=" << it->second << std::endl;
255 else if (num_ctrl_lines >= 2){
262 std::cout <<
"get_op_calls CTRL=" << num_ctrl_lines <<
" OPS=" << num_ops << std::endl;
291 std::cout <<
"FOUND OPTIMAL PARAMS SEARCH FOR CTRL=" << num_ctrl_lines <<
" M=" << it->second.m <<
" L=" << it->second.l <<
" OPS=" << it->second.num_ops << std::endl;
297 std::size_t num_comp = static_cast<std::size_t>(std::floor((
float)(num_ctrl_lines)/2.0)) +1;
300 std::size_t tmp_num_ops;
302 for(std::size_t _m = 2; _m <= num_comp; ++_m){
303 std::size_t _l = (num_ctrl_lines - _m + 1);
306 optimal_params =
OptParamsCX{num_ctrl_lines, _m, _l, tmp_num_ops};
308 else if ( tmp_num_ops < optimal_params.
num_ops){
309 optimal_params =
OptParamsCX{num_ctrl_lines, _m, _l, tmp_num_ops};
311 std::cout <<
"OPTIMAL PARAMS SEARCH FOR CTRL=" << num_ctrl_lines <<
" M=" << _m <<
" L=" << _l <<
" OPS=" << tmp_num_ops << std::endl;
315 std::cout <<
"OPTIMAL PARAMS FOR CTRL=" << num_ctrl_lines <<
" M=" << optimal_params.
m <<
" L=" << optimal_params.
l <<
" OPS=" << optimal_params.
num_ops << std::endl;
316 return optimal_params;
GateCache< SimulatorType > gate_cache
OptParamsCX find_optimal_params(std::size_t num_ctrl_lines)
Returns the number of 2-qubit operations an optimised decomposition will make for nCX....
std::unordered_set< std::string > default_gates
For the 5+ controlled NCX decomposition routines defined within https://arxiv.org/pdf/quant-ph/950301...
static std::size_t num_gate_ops
NCU(SimulatorType &qSim)
Construct a new NCU object.
Class to cache intermediate matrix values used within other parts of the computation....
Templated methods to manipulate small matrices.
std::unordered_map< std::size_t, std::size_t > op_call_counts_CX
Class definition for applying n-qubit controlled unitary operations.
void addToMaps(SimulatorType &qSim, std::string U_label, const Mat2x2Type &U, std::size_t num_ctrl_lines)
Add the given unitary matrix to the maps up to the required depth.
~NCU()
Destroy the NCU object.
void applyNQubitControl(SimulatorType &qSim, const std::vector< std::size_t > ctrlIndices, const std::vector< std::size_t > auxIndices, const unsigned int qTarget, const std::string gateLabel, const Mat2x2Type &U, const std::size_t depth)
Decompose n-qubit controlled op into 1 and 2 qubit gates. Control indices can be in any specified loc...
std::unordered_map< std::size_t, OptParamsCX > opt_op_call_params_CX
GateCache< SimulatorType > & getGateCache()
Get the Map of cached gates. Keys are strings, and values are vectors of paired (gate,...
void clearMaps()
Clears the maps of stored sqrt matrices.
void initialiseMaps(SimulatorType &qSim, std::size_t num_ctrl_lines)
Add the PauliX and the given unitary U to the maps.
std::size_t get_op_calls(std::size_t num_ctrl_lines)
Returns the number of 2-qubit operations a non optimised decomposition will make. Cache intermediate ...
NCU()
Construct a new NCU object.
std::size_t get_ops_for_params(std::size_t l, std::size_t m)
Helper method to get optimised number of 2-gate ops for given decomposition params.
decltype(std::declval< SimulatorType >().getGateX()) Mat2x2Type