QNLP  v1.0
QNLP::NCU< SimulatorType > Class Template Reference

Class definition for applying n-qubit controlled unitary operations. More...

#include <ncu.hpp>

Collaboration diagram for QNLP::NCU< SimulatorType >:
Collaboration graph

Data Structures

struct  OptParamsCX
 For the 5+ controlled NCX decomposition routines defined within https://arxiv.org/pdf/quant-ph/9503016.pdf. More...
 

Public Member Functions

 NCU ()
 Construct a new NCU object. More...
 
 NCU (SimulatorType &qSim)
 Construct a new NCU object. More...
 
 ~NCU ()
 Destroy the NCU object. More...
 
void initialiseMaps (SimulatorType &qSim, std::size_t num_ctrl_lines)
 Add the PauliX and the given unitary U to the maps. More...
 
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. More...
 
GateCache< SimulatorType > & getGateCache ()
 Get the Map of cached gates. Keys are strings, and values are vectors of paired (gate, gate adjoint) types where the index give the value of (gate)^(1/2^i) More...
 
void clearMaps ()
 Clears the maps of stored sqrt matrices. More...
 
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 location. Ensure the gate cache has been populated with the appropriate gate type before running. This avoids O(n) checking of the container at each call for the associated gates. More...
 
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 results. More...
 
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. More...
 
OptParamsCX find_optimal_params (std::size_t num_ctrl_lines)
 Returns the number of 2-qubit operations an optimised decomposition will make for nCX. Caches intermediate results. More...
 

Protected Types

using Mat2x2Type = decltype(std::declval< SimulatorType >().getGateX())
 

Static Protected Attributes

static std::size_t num_gate_ops
 

Private Attributes

std::unordered_set< std::string > default_gates
 
std::unordered_map< std::size_t, std::size_t > op_call_counts_CX
 
std::unordered_map< std::size_t, OptParamsCXopt_op_call_params_CX
 
GateCache< SimulatorType > gate_cache
 

Detailed Description

template<class SimulatorType>
class QNLP::NCU< SimulatorType >

Class definition for applying n-qubit controlled unitary operations.

Template Parameters
SimulatorTypeClass Simulator Type

Definition at line 29 of file ncu.hpp.

Member Typedef Documentation

◆ Mat2x2Type

template<class SimulatorType >
using QNLP::NCU< SimulatorType >::Mat2x2Type = decltype(std::declval<SimulatorType>().getGateX())
protected

Definition at line 55 of file ncu.hpp.

Constructor & Destructor Documentation

◆ NCU() [1/2]

template<class SimulatorType >
QNLP::NCU< SimulatorType >::NCU ( )
inline

Construct a new NCU object.

Definition at line 64 of file ncu.hpp.

64  {
65  //Optimised building block routines for nCX
66  if(op_call_counts_CX.size() == 0){
67  op_call_counts_CX[3] = 13;
68  op_call_counts_CX[5] = 60;
69  }
70  };
std::unordered_map< std::size_t, std::size_t > op_call_counts_CX
Definition: ncu.hpp:47

References QNLP::NCU< SimulatorType >::op_call_counts_CX.

◆ NCU() [2/2]

template<class SimulatorType >
QNLP::NCU< SimulatorType >::NCU ( SimulatorType &  qSim)
inline

Construct a new NCU object.

Parameters
qSimInstance of quantum simulator

Definition at line 77 of file ncu.hpp.

77  : NCU(){
78  gate_cache = GateCache<SimulatorType>(qSim);
79  }
GateCache< SimulatorType > gate_cache
Definition: ncu.hpp:51
NCU()
Construct a new NCU object.
Definition: ncu.hpp:64

References QNLP::NCU< SimulatorType >::gate_cache.

◆ ~NCU()

template<class SimulatorType >
QNLP::NCU< SimulatorType >::~NCU ( )
inline

Destroy the NCU object.

Definition at line 85 of file ncu.hpp.

85  {
86  clearMaps();
87  gate_cache.clearCache();
88  };
GateCache< SimulatorType > gate_cache
Definition: ncu.hpp:51
void clearMaps()
Clears the maps of stored sqrt matrices.
Definition: ncu.hpp:121

References QNLP::NCU< SimulatorType >::clearMaps(), and QNLP::NCU< SimulatorType >::gate_cache.

Here is the call graph for this function:

Member Function Documentation

◆ addToMaps()

template<class SimulatorType >
void QNLP::NCU< SimulatorType >::addToMaps ( SimulatorType &  qSim,
std::string  U_label,
const Mat2x2Type U,
std::size_t  num_ctrl_lines 
)
inline

Add the given unitary matrix to the maps up to the required depth.

Parameters
U

Definition at line 104 of file ncu.hpp.

104  {
105  gate_cache.addToCache(qSim, U_label, U, num_ctrl_lines);
106  }
GateCache< SimulatorType > gate_cache
Definition: ncu.hpp:51

References QNLP::NCU< SimulatorType >::gate_cache.

◆ applyNQubitControl()

template<class SimulatorType >
void QNLP::NCU< SimulatorType >::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 
)
inline

Decompose n-qubit controlled op into 1 and 2 qubit gates. Control indices can be in any specified location. Ensure the gate cache has been populated with the appropriate gate type before running. This avoids O(n) checking of the container at each call for the associated gates.

Template Parameters
TypeComplexDP or ComplexSP
Parameters
qRegQubit register
ctrlIndicesVector of indices for control lines
qTargetTarget qubit for the unitary matrix U
UUnitary matrix, U
depthDepth of recursion.

Definition at line 136 of file ncu.hpp.

143  {
144  //No safety checks; be aware of what is physically possible (qTarget not in control_indices)
145  int local_depth = depth + 1;
146 
147  //Determine the range over which the qubits exist; consider as a count of the control ops, hence +1 since extremeties included
148  std::size_t cOps = ctrlIndices.size();
149 
150  //This can be generalised to find a more optimal set of splitting parameters. Using default example breakdown given in Barenco et al ('95) for now.
151 /* if( (cOps >= 6) && (auxIndices.size() >= 1) && (gateLabel == "X") && (depth == 0) ){
152  auto params = find_optimal_params( ctrlIndices.size() );
153 
154  //Need at least 1 aux qubit to perform decomposition
155  assert( auxIndices.size() > 0);
156 
157  //Gate step 1 setup
158  assert( ctrlIndices.size() >= params.m );
159  std::vector<std::size_t> subMCtrlIndicesNCX(ctrlIndices.begin(), ctrlIndices.begin() + params.m);
160  std::vector<std::size_t> subMAuxIndicesNCX(ctrlIndices.begin() + params.m,ctrlIndices.end());
161  subMAuxIndicesNCX.push_back(qTarget);
162 
163  //Gate step 2 setup
164  std::vector<std::size_t> subLCtrlIndicesNCX(ctrlIndices.begin() + params.m, ctrlIndices.end());
165  subLCtrlIndicesNCX.push_back(auxIndices[0]);
166  std::vector<std::size_t> subLAuxIndicesNCX(ctrlIndices.begin(), ctrlIndices.begin() + params.m);
167 
168  applyNQubitControl(qSim, subMCtrlIndicesNCX, subMAuxIndicesNCX, auxIndices[0], "X", qSim.getGateX(), 0 );
169  applyNQubitControl(qSim, subLCtrlIndicesNCX, subLAuxIndicesNCX, qTarget, "X", qSim.getGateX(), 0 );
170  applyNQubitControl(qSim, subMCtrlIndicesNCX, subMAuxIndicesNCX, auxIndices[0], "X", qSim.getGateX(), 0 );
171  applyNQubitControl(qSim, subLCtrlIndicesNCX, subLAuxIndicesNCX, qTarget, "X", qSim.getGateX(), 0 );
172 
173  }
174 */
175  if( (cOps >= 5) && ( auxIndices.size() >= cOps-2 ) && (gateLabel == "X") && (depth == 0) ){ //161 -> 60 2-qubit gate calls
176  qSim.applyGateCCX( ctrlIndices.back(), *(auxIndices.begin() + ctrlIndices.size() - 3), qTarget);
177 
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)));
180  }
181  qSim.applyGateCCX( *(ctrlIndices.begin()), *(ctrlIndices.begin()+1), *(auxIndices.begin()) );
182 
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)));
185  }
186  qSim.applyGateCCX( ctrlIndices.back(), *(auxIndices.begin() + ctrlIndices.size() - 3), qTarget);
187 
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)));
190  }
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)));
194  }
195  }
196 
197  else if(cOps == 3){ //Optimisation for replacing 17 with 13 2-qubit gate calls
198  //Apply the 13 2-qubit gate calls
199  qSim.applyGateCU( gate_cache.gateCacheMap[gateLabel][local_depth+1].first, ctrlIndices[0], qTarget, gateLabel );
200  qSim.applyGateCX( ctrlIndices[0], ctrlIndices[1]);
201 
202  qSim.applyGateCU( gate_cache.gateCacheMap[gateLabel][local_depth+1].second, ctrlIndices[1], qTarget, gateLabel );
203  qSim.applyGateCX( ctrlIndices[0], ctrlIndices[1]);
204 
205  qSim.applyGateCU( gate_cache.gateCacheMap[gateLabel][local_depth+1].first, ctrlIndices[1], qTarget, gateLabel );
206  qSim.applyGateCX( ctrlIndices[1], ctrlIndices[2]);
207 
208  qSim.applyGateCU( gate_cache.gateCacheMap[gateLabel][local_depth+1].second, ctrlIndices[2], qTarget, gateLabel );
209  qSim.applyGateCX( ctrlIndices[0], ctrlIndices[2]);
210 
211  qSim.applyGateCU( gate_cache.gateCacheMap[gateLabel][local_depth+1].first, ctrlIndices[2], qTarget, gateLabel );
212  qSim.applyGateCX( ctrlIndices[1], ctrlIndices[2]);
213 
214  qSim.applyGateCU( gate_cache.gateCacheMap[gateLabel][local_depth+1].second, ctrlIndices[2], qTarget, gateLabel );
215  qSim.applyGateCX( ctrlIndices[0], ctrlIndices[2]);
216 
217  qSim.applyGateCU( gate_cache.gateCacheMap[gateLabel][local_depth+1].first, ctrlIndices[2], qTarget, gateLabel );
218  }
219 
220  else if (cOps >= 2 && cOps !=3){
221  std::vector<std::size_t> subCtrlIndices(ctrlIndices.begin(), ctrlIndices.end()-1);
222 
223  qSim.applyGateCU( gate_cache.gateCacheMap[gateLabel][local_depth].first, ctrlIndices.back(), qTarget, gateLabel );
224 
225  applyNQubitControl(qSim, subCtrlIndices, auxIndices, ctrlIndices.back(), "X", qSim.getGateX(), 0 );
226 
227  qSim.applyGateCU( gate_cache.gateCacheMap[gateLabel][local_depth].second, ctrlIndices.back(), qTarget, gateLabel );
228 
229  applyNQubitControl(qSim, subCtrlIndices, auxIndices, ctrlIndices.back(), "X", qSim.getGateX(), 0 );
230 
231  applyNQubitControl(qSim, subCtrlIndices, auxIndices, qTarget, gateLabel, gate_cache.gateCacheMap[gateLabel][local_depth+1].first, local_depth );
232  }
233 
234  //If the number of control qubits is less than 2, assume we have decomposed sufficiently
235  else{
236  qSim.applyGateCU(gate_cache.gateCacheMap[gateLabel][depth].first, ctrlIndices[0], qTarget, gateLabel); //The first decomposed matrix value is used here
237  }
238  }
GateCache< SimulatorType > gate_cache
Definition: ncu.hpp:51
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...
Definition: ncu.hpp:136

References QNLP::NCU< SimulatorType >::gate_cache.

◆ clearMaps()

template<class SimulatorType >
void QNLP::NCU< SimulatorType >::clearMaps ( )
inline

Clears the maps of stored sqrt matrices.

Definition at line 121 of file ncu.hpp.

121  {
122  gate_cache.clearCache();
123  }
GateCache< SimulatorType > gate_cache
Definition: ncu.hpp:51

References QNLP::NCU< SimulatorType >::gate_cache.

Referenced by QNLP::NCU< SimulatorType >::~NCU().

Here is the caller graph for this function:

◆ find_optimal_params()

template<class SimulatorType >
OptParamsCX QNLP::NCU< SimulatorType >::find_optimal_params ( std::size_t  num_ctrl_lines)
inline

Returns the number of 2-qubit operations an optimised decomposition will make for nCX. Caches intermediate results.

Parameters
num_ctrl_linesThe number of control lines in the nCX call
Returns
std::size_t The number of 2-qubit gate calls

Definition at line 286 of file ncu.hpp.

286  {
287 
288  //If entry exists, return it
289  auto it = opt_op_call_params_CX.find(num_ctrl_lines);
290  if ( it != opt_op_call_params_CX.end() ){
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;
292 
293  return it->second;
294  }
295 
296  //Determine number of comparisons to make.
297  std::size_t num_comp = static_cast<std::size_t>(std::floor((float)(num_ctrl_lines)/2.0)) +1;
298 
299  OptParamsCX optimal_params;
300  std::size_t tmp_num_ops;
301 
302  for(std::size_t _m = 2; _m <= num_comp; ++_m){
303  std::size_t _l = (num_ctrl_lines - _m + 1);
304  tmp_num_ops = get_ops_for_params(_l, _m);
305  if (_m == 2){
306  optimal_params = OptParamsCX{num_ctrl_lines, _m, _l, tmp_num_ops};
307  }
308  else if ( tmp_num_ops < optimal_params.num_ops){
309  optimal_params = OptParamsCX{num_ctrl_lines, _m, _l, tmp_num_ops};
310  }
311  std::cout << "OPTIMAL PARAMS SEARCH FOR CTRL=" << num_ctrl_lines << " M=" << _m << " L=" << _l << " OPS=" << tmp_num_ops << std::endl;
312  }
313  opt_op_call_params_CX[num_ctrl_lines] = optimal_params;
314  op_call_counts_CX[num_ctrl_lines] = optimal_params.num_ops;
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;
317  }
std::unordered_map< std::size_t, std::size_t > op_call_counts_CX
Definition: ncu.hpp:47
std::unordered_map< std::size_t, OptParamsCX > opt_op_call_params_CX
Definition: ncu.hpp:48
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.
Definition: ncu.hpp:275

References QNLP::NCU< SimulatorType >::get_ops_for_params(), QNLP::NCU< SimulatorType >::OptParamsCX::l, QNLP::NCU< SimulatorType >::OptParamsCX::m, QNLP::NCU< SimulatorType >::OptParamsCX::num_ops, QNLP::NCU< SimulatorType >::op_call_counts_CX, and QNLP::NCU< SimulatorType >::opt_op_call_params_CX.

Here is the call graph for this function:

◆ get_op_calls()

template<class SimulatorType >
std::size_t QNLP::NCU< SimulatorType >::get_op_calls ( std::size_t  num_ctrl_lines)
inline

Returns the number of 2-qubit operations a non optimised decomposition will make. Cache intermediate results.

Parameters
num_ctrl_linesThe number of control lines in the NCU call
Returns
std::size_t The number of 2-qubit gate calls

Definition at line 247 of file ncu.hpp.

247  {
248  std::size_t num_ops = 0;
249  auto it = op_call_counts_CX.find(num_ctrl_lines);
250  if ( it != op_call_counts_CX.end() ){
251  std::cout << "get_op_calls CTRL=" << num_ctrl_lines << " OPS=" << it->second << std::endl;
252 
253  return it->second;
254  }
255  else if (num_ctrl_lines >= 2){
256  num_ops += (2 + 3*get_op_calls(num_ctrl_lines-1));
257  }
258  else{
259  num_ops += 1;
260  }
261  op_call_counts_CX[num_ctrl_lines] = num_ops;
262  std::cout << "get_op_calls CTRL=" << num_ctrl_lines << " OPS=" << num_ops << std::endl;
263 
264  return num_ops;
265  }
std::unordered_map< std::size_t, std::size_t > op_call_counts_CX
Definition: ncu.hpp:47
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 ...
Definition: ncu.hpp:247

References QNLP::NCU< SimulatorType >::op_call_counts_CX.

Referenced by QNLP::NCU< SimulatorType >::get_ops_for_params().

Here is the caller graph for this function:

◆ get_ops_for_params()

template<class SimulatorType >
std::size_t QNLP::NCU< SimulatorType >::get_ops_for_params ( std::size_t  l,
std::size_t  m 
)
inline

Helper method to get optimised number of 2-gate ops for given decomposition params.

Parameters
lmulti-control gate partition 2, l = n-m-1
mmulti-control gate partition 1, m \in {2,...,n-3}
Returns
const std::size_t

Definition at line 275 of file ncu.hpp.

275  {
276  return 2*(get_op_calls(m) + get_op_calls(l));
277  }
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 ...
Definition: ncu.hpp:247

References QNLP::NCU< SimulatorType >::get_op_calls().

Referenced by QNLP::NCU< SimulatorType >::find_optimal_params().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getGateCache()

template<class SimulatorType >
GateCache<SimulatorType>& QNLP::NCU< SimulatorType >::getGateCache ( )
inline

Get the Map of cached gates. Keys are strings, and values are vectors of paired (gate, gate adjoint) types where the index give the value of (gate)^(1/2^i)

Returns
GateCache<SimulatorType> type

Definition at line 113 of file ncu.hpp.

113  {
114  return gate_cache;
115  }
GateCache< SimulatorType > gate_cache
Definition: ncu.hpp:51

References QNLP::NCU< SimulatorType >::gate_cache.

◆ initialiseMaps()

template<class SimulatorType >
void QNLP::NCU< SimulatorType >::initialiseMaps ( SimulatorType &  qSim,
std::size_t  num_ctrl_lines 
)
inline

Add the PauliX and the given unitary U to the maps.

Parameters
U

Definition at line 95 of file ncu.hpp.

95  {
96  gate_cache.initCache(qSim, num_ctrl_lines);
97  }
GateCache< SimulatorType > gate_cache
Definition: ncu.hpp:51

References QNLP::NCU< SimulatorType >::gate_cache.

Field Documentation

◆ default_gates

template<class SimulatorType >
std::unordered_set<std::string> QNLP::NCU< SimulatorType >::default_gates
private

Definition at line 31 of file ncu.hpp.

◆ gate_cache

◆ num_gate_ops

template<class SimulatorType >
std::size_t QNLP::NCU< SimulatorType >::num_gate_ops
staticprotected

Definition at line 57 of file ncu.hpp.

◆ op_call_counts_CX

template<class SimulatorType >
std::unordered_map<std::size_t, std::size_t> QNLP::NCU< SimulatorType >::op_call_counts_CX
private

◆ opt_op_call_params_CX

template<class SimulatorType >
std::unordered_map<std::size_t, OptParamsCX> QNLP::NCU< SimulatorType >::opt_op_call_params_CX
private

Definition at line 48 of file ncu.hpp.

Referenced by QNLP::NCU< SimulatorType >::find_optimal_params().


The documentation for this class was generated from the following file: