Quantum Computing Simulation and Visualization
Richard Perry
Villanova University
Department of Electrical and Computer Engineering
010000101110011010100111101101101110100110111111000010101000100110101001101110101101100010100111100000111111010010100001101000101011111111100010101100111111100000101011011110011010101101111111

(animations: normal, sequential)
Classical Computing
|
Reversible Computing
| |
Ion Trap Quantum Device
| ||
~ x f0 f1 f2 f3 reversible: f1 = x, f2 = NOT x ~ 0 0 0 1 1 ~ 1 0 1 0 1
Two Classical Bits
~ x y f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff ~ 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 ~ 0 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 ~ 1 0 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 ~ 1 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 ~ (x,y) -> (x,f(x,y)) reversible: f5 = y, fa = NOT y, f6 = x XOR y, f9 = NOT (x XOR y) ~ f6: CNOT(x,y) -> (x, x XOR y)
void X( int b[]) { b[0] = !b[0]; } // NOT
int main( void) { int b[1] = { 0 }; // constraint: b[i] = 0 or 1
...
void X( int b[], int k) { b[k] = !b[k]; } // NOT on bit k
void CX( int b[], int c, int k) // controlled-NOT on bit k controlled by bit c, c != k
{
~ b[k] ^= b[c]; // if( b[c]) b[k] = !b[k];
}
int main( void) { int b[2] = { 0, 0 };
...
// q0
// a0 0
// a1 1
//
typedef double complex Complex;
// swap a[i] and a[j]
void swap( Complex a[], int i, int j) { Complex t = a[i]; a[i] = a[j]; a[j] = t; }
void X( Complex a[]) { swap( a, 0, 1); } // NOT
void Z( Complex a[]) { a[1] = -a[1]; } // phase flip
void S( Complex a[]) { a[1] = CMPLX(0,1)*a[1]; } // sqrt(Z)
void H( Complex a[]) // Hadamard
{
~ double s2 = 1/sqrt(2);
~ Complex t0 = a[0], t1 = a[1]; a[0] = s2*(t0+t1); a[1] = s2*(t0-t1);
}
...
int main( void) { Complex a[2] = { 3.0/5.0, CMPLX(0,4.0/5.0) }; // constraint: sum |a[i]|2 = 1
...
// OpenQASM3 U with global phase gamma
//
void U( Complex a[], double theta, double phi, double lambda, double gamma)
{
~ double t = theta/2; Complex t0 = a[0], t1 = a[1], p = exp(CMPLX(0,gamma)),
~ c = p*cos(t), s = p*sin(t), u00 = c, u01 = -exp(CMPLX(0,lambda))*s,
~ u10 = exp(CMPLX(0,phi))*s, u11 = exp(CMPLX(0,phi+lambda))*c;
~ a[0] = u00*t0+u01*t1; a[1] = u10*t0+u11*t1;
}
// q1 q0
// a0 0 0
// a1 0 1
// a2 1 0
// a3 1 1
//
void X( Complex a[], int k) // NOT on qubit k
{
~ if( k == 0) { swap( a, 0, 1); swap( a, 2, 3); }
~ else { swap( a, 0, 2); swap( a, 1, 3); }
}
void CX( Complex a[], int c, int k) // controlled-NOT on qubit k controlled by qubit c
{
~ if( c == 1) swap( a, 2, 3); else swap( a, 1, 3);
}
void Z( Complex a[], int k) // phase-flip qubit k
{
~ if( k == 0) { a[1] = -a[1]; a[3] = -a[3]; }
~ else { a[2] = -a[2]; a[3] = -a[3]; }
}
...
int main( void) { Complex a[4] = { 1, 0, 0, 0 };
...
// q1 q0
// a0 0 0
// a1 0 1
// a2 1 0
// a3 1 1
//
void h( Complex a[], int i, int j) // half Hadamard
{
~ Complex t0 = a[i], t1 = a[j]; a[i] = s2*(t0+t1); a[j] = s2*(t0-t1);
}
void H( Complex a[], int k) // Hadamard on qubit k
{
~ if( k == 0) { h( a, 0, 1); h( a, 2, 3); }
~ else { h( a, 0, 2); h( a, 1, 3); }
}
// Hadamard on qubit k
//
// n qubits, N = 2n amplitudes
//
~ unsigned long i = 0, j, k1 = 1LU << k; // k1 = 0...010...0, k'th bit = 1
~ while( i < N) { // i will have k'th bit = 0, j will have k'th bit = 1
~ j = i | k1; Complex t0 = a[i], t1 = a[j];
~ a[i] = s2*(t0+t1); a[j] = s2*(t0-t1);
~ ++i; i += (i & k1); // skip i values with k'th bit = 1
~ }
// complex magnitude squared
//
double abs2( Complex c) { double x = creal(c), y = cimag(c); return x*x + y*y; }
// 0.0 ... r ... 1.0
// |<------->|<-------->|
// p0 p1
//
void M( Complex a[]) { // measure
~ double r = rand() / (RAND_MAX + 1.0); // 0.0 <= r < 1.0
~ double p = abs2( a[0]);
~ if( r < p) { printf( "M = 0\\\\n"); a[0] = 1; a[1] = 0; }
~ else { printf( "M = 1\\\\n"); a[0] = 0; a[1] = 1; }
}
//
// 0.0 ... r ... 1.0
// |<------->|<-------->|<------->|<-------->|
// p00 p01 p10 p11
//
int Mk( Complex a[]) { // measure overall state
~ double r = rand() / (RAND_MAX + 1.0); // 0.0 <= r < 1.0
~ double p = 0; // cumulative probability
~ int k = 3; for( int i = 0; i < 4; ++i) { p += abs2( a[i]); if( r < p) { k = i; break; } }
~ printf( "k = %i\\\\n", k); return k;
}
void M10( Complex a[]) { // measure qubits 1 and 0
~ int k = Mk( a); a[0]=a[1]=a[2]=a[3]=0; a[k] = 1;
}
void M1( Complex a[]) { // measure qubit 1
~ int k = Mk( a); if( k == 0 || k == 1) { // qubit 1 is 0
~ printf( "M1 = 0\\\\n"); a[2] = a[3] = 0; // normalize remaining states
~ double s = sqrt( abs2(a[0]) + abs2(a[1])); a[0] /= s; a[1] /= s; }
~ else { // qubit 1 is 1
~ printf( "M1 = 1\\\\n"); a[0] = a[1] = 0;
~ double s = sqrt( abs2(a[2]) + abs2(a[3])); a[2] /= s; a[3] /= s; }
}
where A (B) is the reduced state with qubit k equal to 0 (1)
then the reduced state A should be the same as the reduced state B,
i.e. the reduced state should be independent of qubit k.
which corresponds to the cosine having a magnitude squared value of 1.
Real tangle( unsigned int k) const {
~ Complex prod = 0; Real A = 0, B = 0, r = 0;
~ unsigned long i = 0, j, k1 = 1LU << k; // k1 = 0...010...0, k'th bit = 1
~ while( i < N) { // i will have k'th bit = 0, j will have k'th bit = 1
~ j = i | k1; prod += Conj(a[i])*a[j];
~ A += abs2(a[i]); B += abs2(a[j]);
~ ++i; i += (i & k1); // skip i values with k'th bit = 1
~ }
~ Real t = 2*eps*eps*N; // threshold for zero norm squared: 2*sum{|e+i*e|**2} = 2*(2*e*e*(N/2))
~ if( A > t && B > t) r = 1 - abs2(prod)/(A*B);
~ return r;
}
E is the qubit entanglement measure.
~ q: (0.0849528,0.996385) (0,0) (0,0) (0,0) ~ E: 0 0 ~ H1: (0.0600707,0.704551) (0,0) (0.0600707,0.704551) (0,0) ~ E: 0 0 ~ CX10: (0.0600707,0.704551) (0,0) (0,0) (0.0600707,0.704551) ~ E: 1 1
~ X1: (0,0) (0.0600707,0.704551) (0.0600707,0.704551) (0,0) ~ E: 1 1 ~ Z1: (0,0) (0.0600707,0.704551) (-0.0600707,-0.704551) (0,0) ~ E: 1 1
~ CX10: (0,0) (0.0600707,0.704551) (0,0) (-0.0600707,-0.704551) ~ E: 0 0 ~ H1: (0,0) (0,0) (0,0) (0.0849528,0.996385) ~ E: 0 0 ~ P(3) = 1
~ q: (0.285804,0.958288) (0,0) (0,0) (0,0) ~ E: 0 0 ~ H1: (0.202094,0.677612) (0,0) (-0.0171906,0.706898) (0,0) ~ E: 0 0 ~ CX10: (0.202094,0.677612) (0,0) (0,0) (-0.0171906,0.706898) ~ E: 1 1Alice
~ X1: (0,0) (-0.0171906,0.706898) (0.202094,0.677612) (0,0) ~ E: 1 1 ~ Z1: (0,0) (-0.0171906,0.706898) (-0.202094,-0.677612) (0,0) ~ E: 1 1Bob
~ CX10: (0,0) (-0.0171906,0.706898) (0,0) (-0.202094,-0.677612) ~ E: 0 0 ~ H1: (0,0) (-0.296127,0.0883184) (0,0) (-0.0231213,0.950775) ~ E: 0 0 ~ P(3) = 0.904508
(x,y)->(x,(x+y)%M)
I = state index
x = I >> m; // top m bits, M = 2m
y = I & mask; // bottom m bits, mask = M-1 = 0111..1 (m 1's)
j = (x + y) % M; // classical addition
J = (x << m) | j; // concatenate the bits
tj = a[J]; // temporarily save original a[J]
a[J] = a[I]; // update a[J]
|
// flip sign of state m // q.a[m] = -q.a[m]; // inversion about the average // Complex avg = 0; for( unsigned long i = 0; i < q.N; ++i) avg += q.a[i]; avg *= 2.0/q.N; for( unsigned long i = 0; i < q.N; ++i) q.a[i] = avg - q.a[i];
~ // quantum FFT
~ //
~ void qfft( int inv = 0) {
~ fft<Complex,Real>( a, N, inv); // classical FFT, without scaling and bit reversal
~ // scale values and bit-reverse the indices
~ //
~ Real s = 1/std::sqrt((Real)N);
~ for( unsigned long i = 0, j = 0; i < N; ++i) {
~ j = bitrev( i, n);
~ if( i < j) { swap( i, j); a[i] *= s; a[j] *= s; }
~ else if( i == j) a[i] *= s;
~ }
~ }