1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
|
// The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt
/*
This is an example illustrating the use of the quantum computing
simulation classes from the dlib C++ Library.
This example assumes you are familiar with quantum computing and
Grover's search algorithm and Shor's 9 bit error correcting code
in particular. The example shows how to simulate both of these
algorithms.
The code to simulate Grover's algorithm is primarily here to show
you how to make custom quantum gate objects. The Shor ECC example
is simpler and uses just the default gates that come with the
library.
*/
#include <iostream>
#include <complex>
#include <ctime>
#include <dlib/quantum_computing.h>
#include <dlib/string.h>
using namespace std;
using namespace dlib;
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// This declares a random number generator that we will be using below
dlib::rand rnd;
// ----------------------------------------------------------------------------------------
void shor_encode (
quantum_register& reg
)
/*!
requires
- reg.num_bits() == 1
ensures
- #reg.num_bits() == 9
- #reg == the Shor error coding of the input register
!*/
{
DLIB_CASSERT(reg.num_bits() == 1,"");
quantum_register zeros;
zeros.set_num_bits(8);
reg.append(zeros);
using namespace dlib::quantum_gates;
const gate<1> h = hadamard();
const gate<1> i = noop();
// Note that the expression (h,i) represents the tensor product of the 1 qubit
// h gate with the 1 qubit i gate and larger versions of this expression
// represent even bigger tensor products. So as you see below, we make gates
// big enough to apply to our quantum register by listing out all the gates we
// want to go into the tensor product and then we just apply the resulting gate
// to the quantum register.
// Now apply the gates that constitute Shor's encoding to the input register.
(cnot<3,0>(),i,i,i,i,i).apply_gate_to(reg);
(cnot<6,0>(),i,i).apply_gate_to(reg);
(h,i,i,h,i,i,h,i,i).apply_gate_to(reg);
(cnot<1,0>(),i,cnot<1,0>(),i,cnot<1,0>(),i).apply_gate_to(reg);
(cnot<2,0>(),cnot<2,0>(),cnot<2,0>()).apply_gate_to(reg);
}
// ----------------------------------------------------------------------------------------
void shor_decode (
quantum_register& reg
)
/*!
requires
- reg.num_bits() == 9
ensures
- #reg.num_bits() == 1
- #reg == the decoded qubit that was in the given input register
!*/
{
DLIB_CASSERT(reg.num_bits() == 9,"");
using namespace dlib::quantum_gates;
const gate<1> h = hadamard();
const gate<1> i = noop();
// Now apply the gates that constitute Shor's decoding to the input register
(cnot<2,0>(),cnot<2,0>(),cnot<2,0>()).apply_gate_to(reg);
(cnot<1,0>(),i,cnot<1,0>(),i,cnot<1,0>(),i).apply_gate_to(reg);
(toffoli<0,1,2>(),toffoli<0,1,2>(),toffoli<0,1,2>()).apply_gate_to(reg);
(h,i,i,h,i,i,h,i,i).apply_gate_to(reg);
(cnot<6,0>(),i,i).apply_gate_to(reg);
(cnot<3,0>(),i,i,i,i,i).apply_gate_to(reg);
(toffoli<0,3,6>(),i,i).apply_gate_to(reg);
// Now that we have decoded the value we don't need the extra 8 bits any more so
// remove them from the register.
for (int i = 0; i < 8; ++i)
reg.measure_and_remove_bit(0,rnd);
}
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// This is the function we will use in Grover's search algorithm. In this
// case the value we are searching for is 257.
bool is_key (unsigned long n)
{
return n == 257;
}
// ----------------------------------------------------------------------------------------
template <int bits>
class uf_gate;
namespace dlib {
template <int bits>
struct gate_traits<uf_gate<bits> >
{
static const long num_bits = bits;
static const long dims = dlib::qc_helpers::exp_2_n<num_bits>::value;
};}
template <int bits>
class uf_gate : public gate_exp<uf_gate<bits> >
{
/*!
This gate represents the black box function in Grover's search algorithm.
That is, it is the gate defined as follows:
Uf|x>|y> = |x>|y XOR is_key(x)>
See the documentation for the gate_exp object for the details regarding
the compute_state_element() and operator() functions defined below.
!*/
public:
uf_gate() : gate_exp<uf_gate>(*this) {}
static const long num_bits = gate_traits<uf_gate>::num_bits;
static const long dims = gate_traits<uf_gate>::dims;
const qc_scalar_type operator() (long r, long c) const
{
unsigned long output = c;
// if the input control bit is set
if (is_key(output>>1))
{
output = output^0x1;
}
if ((unsigned long)r == output)
return 1;
else
return 0;
}
template <typename exp>
qc_scalar_type compute_state_element (
const matrix_exp<exp>& reg,
long row_idx
) const
{
unsigned long output = row_idx;
// if the input control bit is set
if (is_key(output>>1))
{
output = output^0x1;
}
return reg(output);
}
};
// ----------------------------------------------------------------------------------------
template <int bits>
class w_gate;
namespace dlib {
template <int bits>
struct gate_traits<w_gate<bits> >
{
static const long num_bits = bits;
static const long dims = dlib::qc_helpers::exp_2_n<num_bits>::value;
}; }
template <int bits>
class w_gate : public gate_exp<w_gate<bits> >
{
/*!
This is the W gate from the Grover algorithm
!*/
public:
w_gate() : gate_exp<w_gate>(*this) {}
static const long num_bits = gate_traits<w_gate>::num_bits;
static const long dims = gate_traits<w_gate>::dims;
const qc_scalar_type operator() (long r, long c) const
{
qc_scalar_type res = 2.0/dims;
if (r != c)
return res;
else
return res - 1.0;
}
template <typename exp>
qc_scalar_type compute_state_element (
const matrix_exp<exp>& reg,
long row_idx
) const
{
qc_scalar_type temp = sum(reg)*2.0/dims;
// compute this value: temp = temp - reg(row_idx)*2.0/dims + reg(row_idx)*(2.0/dims - 1.0)
temp = temp - reg(row_idx);
return temp;
}
};
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
int main()
{
// seed the random number generator
rnd.set_seed(cast_to_string(time(0)));
// Pick out some of the gates we will be using below
using namespace dlib::quantum_gates;
const gate<1> h = quantum_gates::hadamard();
const gate<1> z = quantum_gates::z();
const gate<1> x = quantum_gates::x();
const gate<1> i = quantum_gates::noop();
quantum_register reg;
// We will be doing the 12 qubit version of Grover's search algorithm.
const int bits=12;
reg.set_num_bits(bits);
// set the quantum register to its initial state
(i,i, i,i,i,i,i, i,i,i,i,x).apply_gate_to(reg);
// Print out the starting bits
cout << "starting bits: ";
for (int i = reg.num_bits()-1; i >= 0; --i)
cout << reg.probability_of_bit(i);
cout << endl;
// Now apply the Hadamard gate to all the input bits
(h,h, h,h,h,h,h, h,h,h,h,h).apply_gate_to(reg);
// Here we do the grover iteration
for (int j = 0; j < 35; ++j)
{
(uf_gate<bits>()).apply_gate_to(reg);
(w_gate<bits-1>(),i).apply_gate_to(reg);
cout << j << " probability: bit 1 = " << reg.probability_of_bit(1) << ", bit 9 = " << reg.probability_of_bit(9) << endl;
}
cout << endl;
// Print out the final probability of measuring a 1 for each of the bits
for (int i = reg.num_bits()-1; i >= 1; --i)
cout << "probability for bit " << i << " = " << reg.probability_of_bit(i) << endl;
cout << endl;
cout << "The value we want grover's search to find is 257 which means we should measure a bit pattern of 00100000001" << endl;
cout << "Measured bits: ";
// finally, measure all the bits and print out what they are.
for (int i = reg.num_bits()-1; i >= 1; --i)
cout << reg.measure_bit(i,rnd);
cout << endl;
// Now let's test out the Shor 9 bit encoding
cout << "\n\n\n\nNow let's try playing around with Shor's 9bit error correcting code" << endl;
// Reset the quantum register to contain a single bit
reg.set_num_bits(1);
// Set the state of this single qubit to some random mixture of the two computational bases
reg.state_vector()(0) = qc_scalar_type(rnd.get_random_double(),rnd.get_random_double());
reg.state_vector()(1) = qc_scalar_type(rnd.get_random_double(),rnd.get_random_double());
// Make sure the state of the quantum register is a unit vector
reg.state_vector() /= sqrt(sum(norm(reg.state_vector())));
cout << "state: " << trans(reg.state_vector());
shor_encode(reg);
cout << "x bit corruption on bit 8" << endl;
(x,i,i,i,i,i,i,i,i).apply_gate_to(reg); // mess up the high order bit
shor_decode(reg); // try to decode the register
cout << "state: " << trans(reg.state_vector());
shor_encode(reg);
cout << "x bit corruption on bit 1" << endl;
(i,i,i,i,i,i,i,x,i).apply_gate_to(reg);
shor_decode(reg);
cout << "state: " << trans(reg.state_vector());
shor_encode(reg);
cout << "z bit corruption on bit 8" << endl;
(z,i,i,i,i,i,i,i,i).apply_gate_to(reg);
shor_decode(reg);
cout << "state: " << trans(reg.state_vector());
cout << "\nThe state of the input qubit survived all the corruptions in tact so the code works." << endl;
}
|