// function templates for complex or real state amplitudes
// uses macros QTYPE, qtype, and State, defined in qce.h
//
// this is included twice by qce.c, once for QTYPE Complex and once for QTYPE Real
//
// R. Perry, Aug. 2019
//------------------------------ private ------------------------------
//
// t temporary state copy used in operate()
// tcopy make temporary state copy
//
#define _T_ _name_( t, QTYPE)
//
static State _T_;
//
static void _name_( tcopy, QTYPE)( State q)
{
if( !_T_.a) _T_ = _name_( Q, QTYPE)(q.n);
else if( _T_.n != q.n) { free(_T_.a); _T_ = _name_( Q, QTYPE)(q.n); }
memcpy( _T_.a, q.a, q.N*sizeof(qtype));
}
//------------------------------ public ------------------------------
//
// allocate a new state vector, uninitialized
//
State _name_( Q, QTYPE)(unsigned int n)
{
if( n >= CHAR_BIT*sizeof(unsigned long)) error( "Q: N overflow");
State q; q.n = n; q.N = 1LU << n; unsigned long size = q.N*sizeof(qtype);
if( size/sizeof(qtype) != q.N) error( "Q: size overflow");
q.a = emalloc(size); return q;
}
// set state vector to all zeros
//
void _name_( zero, QTYPE)( State q)
{
for( unsigned long i = 0; i < q.N; ++i) q.a[i] = 0;
}
void _name_( init, QTYPE)( State q, unsigned long k
#ifdef QTYPE_COMPLEX
, int rphase
#endif
)
{
qtype v = 1;
#ifdef QTYPE_COMPLEX
if( rphase) { double a = pi*(2*DRAND-1); v = exp(CMPLX(0,a)); }
#endif
_name_( zero, QTYPE)(q); q.a[k] = v;
}
// swap states i and j
//
void _name_( swap, QTYPE)( State q, unsigned long i, unsigned long j)
{
qtype t = q.a[i]; q.a[i] = q.a[j]; q.a[j] = t;
}
// tensor product
//
State _name_( product, QTYPE)( State r, State s)
{
State q = _name_( Q, QTYPE)(r.n + s.n);
unsigned long i, j, k = 0;
for( i = 0; i < r.N; ++i) for( j = 0; j < s.N; ++j) q.a[k++] = r.a[i]*s.a[j];
return q;
}
// trace distance as a measure of entanglement
// distance is 0 for no entanglement, 1 for max entanglement
//
// trace distance = 1-fidelity; fidelity = (||/(||A||*||B||))**2
// where A (B) is the reduced state with qubit k equal to 0 (1)
//
// If qubit k is not entangled with any of the other qubits,
// then the reduced state A with qubit k equal to 0 should be the same
// as the reduced state B with qubit k equal to 1, i.e. the reduced state
// should be independent of qubit k.
//
// /(||A||*||B||)) is the cosine of the angle between vectors A and B.
// A and B represent the same state if the angle is 0 or 180 degrees,
// which corresponds to the cosine having a magnitude squared value of 1.
//
double _name_( tangle, QTYPE)( State q, unsigned int k)
{
qtype prod = 0; double A = 0, B = 0, r = 0;
unsigned long i = 0, j, k1 = 1LU << k; // k1 = 0...010...0, k'th bit = 1
while( i < q.N) // i will have k'th bit = 0, j will have k'th bit = 1
{
j = i | k1;
#ifdef QTYPE_COMPLEX
prod += conj(q.a[i])*q.a[j];
#else
prod += q.a[i]*q.a[j];
#endif
A += _name_( abs2, QTYPE)(q.a[i]); B += _name_( abs2, QTYPE)(q.a[j]);
++i; i += (i & k1); // skip i values with k'th bit = 1
}
// threshold for zero norm squared: 2 * sum { |e + i*e|**2 } = 2 * (2*e*e*(q.N/2))
//
double t = 2*DBL_EPSILON*DBL_EPSILON*q.N;
if( A > t && B > t) r = 1 - _name_( abs2, QTYPE)(prod)/(A*B);
return r;
}
// Hadamard transformation on qubit k
//
void _name_( H, QTYPE)( State q, unsigned int k)
{
if( k >= q.n) error( "H: bad index");
unsigned long i = 0, j, k1 = 1LU << k; // k1 = 0...010...0, k'th bit = 1
while( i < q.N) // i will have k'th bit = 0, j will have k'th bit = 1
{
j = i | k1; qtype t0 = q.a[i], t1 = q.a[j];
q.a[i] = s2*(t0+t1); q.a[j] = s2*(t0-t1);
++i; i += (i & k1); // skip i values with k'th bit = 1
}
}
#if 0 // algorithm from https://arxiv.org/abs/1801.01037 and https://arxiv.org/abs/1601.07195
void H( State q, unsigned int k)
{
if( k >= q.n) error( "H: bad index");
unsigned long k2 = 1LU << k, k22 = k2 << 1; // 2**k, 2**(k+1)
for( unsigned long i = 0; i < q.N; i += k22) {
unsigned long ik2 = i + k2;
for( unsigned long j = i, jk2 = ik2; j < ik2; ++j, ++jk2) {
qtype t0 = q.a[j], t1 = q.a[jk2];
q.a[j] = s2*(t0+t1); q.a[jk2] = s2*(t0-t1);
}
}
}
#endif
// NOT transformation on qubit k
//
void _name_( X, QTYPE)( State q, unsigned int k)
{
if( k >= q.n) error( "X: bad index");
unsigned long i = 0, j, k1 = 1LU << k; // k1 = 0...010...0, k'th bit = 1
while( i < q.N) // i will have k'th bit = 0, j will have k'th bit = 1
{
j = i | k1; _name_( swap, QTYPE)( q, i, j);
++i; i += (i & k1); // skip i values with k'th bit = 1
}
}
// Z transformation on qubit k
//
void _name_( Z, QTYPE)( State q, unsigned int k)
{
if( k >= q.n) error( "Z: bad index");
unsigned long i = 0, j, k1 = 1LU << k; // k1 = 0...010...0, k'th bit = 1
while( i < q.N) // i will have k'th bit = 0, j will have k'th bit = 1
{
j = i | k1; q.a[j] = -q.a[j];
++i; i += (i & k1); // skip i values with k'th bit = 1
}
}
// NOT transformation on qubit k controlled by qubit c
//
void _name_( CX, QTYPE)( State q, unsigned int c, unsigned int k)
{
if( c >= q.n || k >= q.n || c == k) error( "CX: bad index");
unsigned long c1 = 1LU << c, k1 = 1LU << k, i = c1, j;
// printf( "CX: %u %u\n", c, k); // debug
while( i < q.N) // i will have c'th bit = 1, k'th bit = 0
{
j = i | k1; // k'th bit = 1
_name_( swap, QTYPE)( q, i, j); // printf( " %lu %lu\n", i, j); // debug
++i; i += (i & k1); i |= c1; // skip i values with k'th bit = 1
}
}
// measurement
//
unsigned long _name_( M, QTYPE)( State q)
{
double P = DRAND, R = 0; // R will be cumulative probability
for( unsigned long i = 0; i < q.N; ++i)
{
R += _name_( abs2, QTYPE)(q.a[i]); if( P < R) return i;
}
// R should be 1.0 at this point, and P < 1.0, so should not get here.
// But could happen if R < 1.0 due to accumulation of roundoff errors.
// error( "M: measurement failed");
// fprintf( stderr, "qce: WARNING, measurement failed, P = %g, R = %g\n", P, R);
return q.N-1;
}
// reduce state to the top n qubits based on a measurement m
//
State _name_( reduce, QTYPE)( State q, unsigned int n, unsigned long m)
{
if( n >= q.n) error( "reduce: n >= q.n");
State r = _name_( Q, QTYPE)(n); unsigned long k = q.n-n, mask = (1LU< mask) error( "reduce: measurement out of range");
double s = 0;
for( unsigned long i = 0, j = 0; i < r.N; ++i) // efficient traverse r.N
{
j = (i << k) | m; r.a[i] = q.a[j]; s += _name_( abs2, QTYPE)(q.a[j]);
#if 0 // debug
#ifdef QTYPE_COMPLEX
printf( "reduce: i = %lu, j = %lu, q.a[j] = (%g,%g)\n", i, j, creal(q.a[j]), cimag(q.a[j]) );
#else
printf( "reduce: i = %lu, j = %lu, q.a[j] = (%g)\n", i, j, q.a[j] );
#endif
#endif
}
if( s == 0) error( "reduce: magnitude is 0");
// normalize
//
s = sqrt(s); for( unsigned long i = 0; i < r.N; ++i) r.a[i] /= s;
return r;
}
// collapse state based on a measurement m of qubit k
//
void _name_( collapse, QTYPE)( State q, unsigned int k, unsigned int m)
{
if( k >= q.n) error( "collapse: k >= q.n");
if( m > 1) error( "collapse: m > 1"); // m must be 0 or 1
double s = 0; unsigned long i = 0, j, k1 = 1LU << k;
while( i < q.N) // i will have k'th bit = 0, j will have k'th bit = 1
{
j = i | k1; s += _name_( abs2, QTYPE)(q.a[m?j:i]); q.a[m?i:j] = 0;
++i; i += (i & k1); // skip i values with k'th bit = 1
}
if( s == 0) error( "reduce: magnitude is 0");
// normalize
//
s = sqrt(s); for( i = 0; i < q.N; ++i) q.a[i] /= s;
}
// q = (a,b) -> (a,f(a,b,z))
//
// for an operation which corresponds to a permutation of the state
// where a represents the top n bits and b the bottom m bits with q.n = n + m
//
void _name_( operate, QTYPE)( State q, unsigned int n, Func f, unsigned long z)
{
unsigned int m = q.n - n;
unsigned long M = 1LU << m, mask = M-1, a, b, c, X, Y;
if( n >= q.n) error( "operate: n >= q.n");
_name_( tcopy, QTYPE)(q);
for( X = 0; X < q.N; ++X)
{
a = X >> m; // top n bits
b = X & mask; // bottom m bits, mask = (2**m)-1 = 0111..1 (m 1's)
c = f(a,b,z); // perform the classical operation
if( c > mask) error( "operate: function result overflow");
Y = (a << m) | c; // concatenate the bits
q.a[Y] = _T_.a[X]; // perform the state permutation
}
}
// display message and qubit state or entanglement measure
//
void _name_( print, QTYPE)( const char *msg, State q, int option)
{
printf( "%s:%s", msg, option == PRINT_BITS ? "\n" : "");
if( option == PRINT_TANGLE)
{
for( unsigned int k = 0; k < q.n; ++k) printf( " %g", _name_( tangle, QTYPE)(q,k));
}
else for( unsigned long i = 0; i < q.N; ++i)
{
if( option == PRINT_BITS)
{
printf( " "); printb( i, q.n); // 0+x to make -0 print as just 0
#ifdef QTYPE_COMPLEX
printf( " %-8.4g (%g,%g)\n", abs2_Complex(q.a[i]), 0+creal(q.a[i]), 0+cimag(q.a[i]) );
#else
printf( " %-8.4g (%g)\n", abs2_Real(q.a[i]), 0+q.a[i] );
#endif
}
else
{
if( option == PRINT_LABELS) printf( " %lu:", i); else printf( " ");
#ifdef QTYPE_COMPLEX
printf( "(%g,%g)", 0+creal(q.a[i]), 0+cimag(q.a[i]) );
#else
printf( "(%g)", 0+q.a[i] );
#endif
}
}
if( option != PRINT_BITS) putchar('\n');
}
// magnitude squared
//
// Equivalent to abs(x)**2 but without the square-root and square operations
//
// In C++ this is available as norm(x) for complex
//
double _name_( abs2, QTYPE)( qtype x)
{
#ifdef QTYPE_COMPLEX
double r = creal(x), i = cimag(x); return r*r + i*i;
#else
return x*x;
#endif
}