diff options
Diffstat (limited to 'ml/dlib/dlib/quantum_computing')
-rw-r--r-- | ml/dlib/dlib/quantum_computing/quantum_computing.h | 863 | ||||
-rw-r--r-- | ml/dlib/dlib/quantum_computing/quantum_computing_abstract.h | 590 |
2 files changed, 0 insertions, 1453 deletions
diff --git a/ml/dlib/dlib/quantum_computing/quantum_computing.h b/ml/dlib/dlib/quantum_computing/quantum_computing.h deleted file mode 100644 index afa2e40e7..000000000 --- a/ml/dlib/dlib/quantum_computing/quantum_computing.h +++ /dev/null @@ -1,863 +0,0 @@ -// Copyright (C) 2008 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_QUANTUM_COMPUTINg_1_ -#define DLIB_QUANTUM_COMPUTINg_1_ - -#include <complex> -#include <cmath> -#include "../matrix.h" -#include "../rand.h" -#include "../enable_if.h" -#include "../algs.h" -#include "quantum_computing_abstract.h" - -namespace dlib -{ - - template <typename T> - struct gate_traits {}; - - namespace qc_helpers - { - - // ------------------------------------------------------------------------------------ - - // This is a template to compute the value of 2^n at compile time - template <long n> - struct exp_2_n - { - COMPILE_TIME_ASSERT(0 <= n && n <= 30); - static const long value = exp_2_n<n-1>::value*2; - }; - - template <> - struct exp_2_n<0> - { - static const long value = 1; - }; - - // ------------------------------------------------------------------------------------ - - } - - typedef std::complex<double> qc_scalar_type; - -// ---------------------------------------------------------------------------------------- - - class quantum_register - { - public: - - quantum_register() - { - set_num_bits(1); - } - - int num_bits ( - ) const - { - return num_bits_in_register; - } - - void set_num_bits ( - int num_bits - ) - { - // make sure requires clause is not broken - DLIB_CASSERT(1 <= num_bits && num_bits <= 30, - "\tvoid quantum_register::set_num_bits()" - << "\n\tinvalid arguments to this function" - << "\n\tnum_bits: " << num_bits - << "\n\tthis: " << this - ); - - num_bits_in_register = num_bits; - - unsigned long size = 1; - for (int i = 0; i < num_bits; ++i) - size *= 2; - - state.set_size(size); - - zero_all_bits(); - } - - void zero_all_bits() - { - set_all_elements(state,0); - state(0) = 1; - } - - void append ( - const quantum_register& reg - ) - { - num_bits_in_register += reg.num_bits_in_register; - state = tensor_product(state, reg.state); - } - - template <typename rand_type> - bool measure_bit ( - int bit, - rand_type& rnd - ) - { - // make sure requires clause is not broken - DLIB_CASSERT(0 <= bit && bit < num_bits(), - "\tbool quantum_register::measure_bit()" - << "\n\tinvalid arguments to this function" - << "\n\tbit: " << bit - << "\n\tnum_bits(): " << num_bits() - << "\n\tthis: " << this - ); - - const bool value = (rnd.get_random_double() < probability_of_bit(bit)); - - // Next we set all the states where this bit doesn't have the given value to 0 - - // But first make a mask that selects our bit - unsigned long mask = 1; - for (int i = 0; i < bit; ++i) - mask <<= 1; - - // loop over all the elements in the state vector and zero out those that - // conflict with the measurement we just made. - for (long r = 0; r < state.nr(); ++r) - { - const unsigned long field = r; - // if this state indicates that the bit should be set and it isn't - if ((field & mask) && !value) - { - state(r) = 0; - } - // else if this state indicates that the bit should not be set and it is - else if (!(field & mask) && value) - { - state(r) = 0; - } - } - - // normalize the state - state = state/(std::sqrt(sum(norm(state)))); - - return value; - } - - template <typename rand_type> - bool measure_and_remove_bit ( - int bit, - rand_type& rnd - ) - { - // make sure requires clause is not broken - DLIB_CASSERT(0 <= bit && bit < num_bits() && num_bits() > 0, - "\tbool quantum_register::measure_and_remove_bit()" - << "\n\tinvalid arguments to this function" - << "\n\tbit: " << bit - << "\n\tnum_bits(): " << num_bits() - << "\n\tthis: " << this - ); - - - const bool value = (rnd.get_random_double() < probability_of_bit(bit)); - quantum_register temp; - temp.set_num_bits(num_bits()-1); - - - // Next we set all the states where this bit doesn't have the given value to 0 - - // But first make a mask that selects our bit - unsigned long mask = 1; - for (int i = 0; i < bit; ++i) - mask <<= 1; - - long count = 0; - for (long r = 0; r < state.nr(); ++r) - { - const unsigned long field = r; - // if this basis vector is one that matches the measured state then keep it - if (((field & mask) != 0) == value) - { - temp.state(count) = state(r); - ++count; - } - } - - // normalize the state - temp.state = temp.state/std::sqrt(sum(norm(temp.state))); - - temp.swap(*this); - - return value; - } - - double probability_of_bit ( - int bit - ) const - { - // make sure requires clause is not broken - DLIB_CASSERT(0 <= bit && bit < num_bits(), - "\tdouble quantum_register::probability_of_bit()" - << "\n\tinvalid arguments to this function" - << "\n\tbit: " << bit - << "\n\tnum_bits(): " << num_bits() - << "\n\tthis: " << this - ); - - - // make a mask that selects our bit - unsigned long mask = 1; - for (int i = 0; i < bit; ++i) - mask <<= 1; - - // now find the total probability of all the states that have the given bit set - double prob = 0; - for (long r = 0; r < state.nr(); ++r) - { - const unsigned long field = r; - if (field & mask) - { - prob += std::norm(state(r)); - } - } - - - return prob; - } - - const matrix<qc_scalar_type,0,1>& state_vector() const { return state; } - matrix<qc_scalar_type,0,1>& state_vector() { return state; } - - void swap ( - quantum_register& item - ) - { - exchange(num_bits_in_register, item.num_bits_in_register); - state.swap(item.state); - } - - private: - - int num_bits_in_register; - matrix<qc_scalar_type,0,1> state; - }; - - inline void swap ( - quantum_register& a, - quantum_register& b - ) { a.swap(b); } - -// ---------------------------------------------------------------------------------------- - - template <typename T> - class gate_exp - { - public: - static const long num_bits = gate_traits<T>::num_bits; - static const long dims = gate_traits<T>::dims; - - gate_exp(T& exp_) : exp(exp_) {} - - const qc_scalar_type operator() (long r, long c) const { return exp(r,c); } - - const matrix<qc_scalar_type> mat ( - ) const - { - matrix<qc_scalar_type,dims,dims> m; - for (long r = 0; r < m.nr(); ++r) - { - for (long c = 0; c < m.nc(); ++c) - { - m(r,c) = exp(r,c); - } - } - return m; - } - - void apply_gate_to (quantum_register& reg) const - { - // make sure requires clause is not broken - DLIB_CASSERT(reg.num_bits() == num_bits, - "\tvoid gate_exp::apply_gate_to()" - << "\n\tinvalid arguments to this function" - << "\n\treg.num_bits(): " << reg.num_bits() - << "\n\tnum_bits: " << num_bits - << "\n\tthis: " << this - ); - - - quantum_register temp(reg); - - - // check if any of the elements of the register are 1 and if so then - // we don't have to do the full matrix multiply. Or check if only a small number are non-zero. - long non_zero_elements = 0; - for (long r = 0; r < dims; ++r) - { - if (reg.state_vector()(r) != qc_scalar_type(0)) - ++non_zero_elements; - - reg.state_vector()(r) = 0; - } - - - if (non_zero_elements > 3) - { - // do a full matrix multiply to compute the output state - for (long r = 0; r < dims; ++r) - { - reg.state_vector()(r) = compute_state_element(temp.state_vector(),r); - } - } - else - { - // do a matrix multiply but only use the columns in the gate matrix - // that correspond to the non-zero register elements - for (long r = 0; r < dims; ++r) - { - if (temp.state_vector()(r) != qc_scalar_type(0)) - { - for (long i = 0; i < dims; ++i) - { - reg.state_vector()(i) += temp.state_vector()(r)*exp(i,r); - } - } - } - } - } - - template <typename exp> - qc_scalar_type compute_state_element ( - const matrix_exp<exp>& reg, - long row_idx - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(reg.nr() == dims && reg.nc() == 1 && - 0 <= row_idx && row_idx < dims, - "\tqc_scalar_type gate_exp::compute_state_element(reg,row_idx)" - << "\n\tinvalid arguments to this function" - << "\n\treg.nr(): " << reg.nr() - << "\n\treg.nc(): " << reg.nc() - << "\n\tdims: " << dims - << "\n\trow_idx: " << row_idx - << "\n\tthis: " << this - ); - - - return this->exp.compute_state_element(reg,row_idx); - } - - const T& ref() const { return exp; } - - private: - T& exp; - }; - -// ---------------------------------------------------------------------------------------- - - - template <typename T, typename U> - class composite_gate; - - template <typename T, typename U> - struct gate_traits<composite_gate<T,U> > - { - static const long num_bits = T::num_bits + U::num_bits; - static const long dims = qc_helpers::exp_2_n<num_bits>::value; - }; - - template <typename T, typename U> - class composite_gate : public gate_exp<composite_gate<T,U> > - { - public: - - typedef T lhs_type; - typedef U rhs_type; - - composite_gate(const composite_gate& g) : gate_exp<composite_gate>(*this), lhs(g.lhs), rhs(g.rhs) {} - - composite_gate( - const gate_exp<T>& lhs_, - const gate_exp<U>& rhs_ - ) : gate_exp<composite_gate>(*this), lhs(lhs_.ref()), rhs(rhs_.ref()) {} - - - - static const long num_bits = gate_traits<composite_gate>::num_bits; - static const long dims = gate_traits<composite_gate>::dims; - - const qc_scalar_type operator() (long r, long c) const { return lhs(r/U::dims,c/U::dims)*rhs(r%U::dims, c%U::dims); } - - template <typename exp> - qc_scalar_type compute_state_element ( - const matrix_exp<exp>& reg, - long row_idx - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(reg.nr() == dims && reg.nc() == 1 && - 0 <= row_idx && row_idx < dims, - "\tqc_scalar_type composite_gate::compute_state_element(reg,row_idx)" - << "\n\tinvalid arguments to this function" - << "\n\treg.nr(): " << reg.nr() - << "\n\treg.nc(): " << reg.nc() - << "\n\tdims: " << dims - << "\n\trow_idx: " << row_idx - << "\n\tthis: " << this - ); - - - qc_scalar_type result = 0; - for (long c = 0; c < T::dims; ++c) - { - if (lhs(row_idx/U::dims,c) != qc_scalar_type(0)) - { - result += lhs(row_idx/U::dims,c) * rhs.compute_state_element(subm(reg,c*U::dims,0,U::dims,1), row_idx%U::dims); - } - } - - return result; - } - - - const T lhs; - const U rhs; - }; - -// ---------------------------------------------------------------------------------------- - - template <long bits> - class gate; - template <long bits> - struct gate_traits<gate<bits> > - { - static const long num_bits = bits; - static const long dims = qc_helpers::exp_2_n<num_bits>::value; - }; - -// ---------------------------------------------------------------------------------------- - - template <long bits> - class gate : public gate_exp<gate<bits> > - { - public: - gate() : gate_exp<gate>(*this) { set_all_elements(data,0); } - gate(const gate& g) :gate_exp<gate>(*this), data(g.data) {} - - template <typename T> - explicit gate(const gate_exp<T>& g) : gate_exp<gate>(*this) - { - COMPILE_TIME_ASSERT(T::num_bits == num_bits); - for (long r = 0; r < dims; ++r) - { - for (long c = 0; c < dims; ++c) - { - data(r,c) = g(r,c); - } - } - } - - static const long num_bits = gate_traits<gate>::num_bits; - static const long dims = gate_traits<gate>::dims; - - const qc_scalar_type& operator() (long r, long c) const { return data(r,c); } - qc_scalar_type& operator() (long r, long c) { return data(r,c); } - - template <typename exp> - qc_scalar_type compute_state_element ( - const matrix_exp<exp>& reg, - long row_idx - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(reg.nr() == dims && reg.nc() == 1 && - 0 <= row_idx && row_idx < dims, - "\tqc_scalar_type gate::compute_state_element(reg,row_idx)" - << "\n\tinvalid arguments to this function" - << "\n\treg.nr(): " << reg.nr() - << "\n\treg.nc(): " << reg.nc() - << "\n\tdims: " << dims - << "\n\trow_idx: " << row_idx - << "\n\tthis: " << this - ); - - - return (data*reg)(row_idx); - } - - private: - - matrix<qc_scalar_type,dims,dims> data; - }; - -// ---------------------------------------------------------------------------------------- - - namespace qc_helpers - { - // This is the maximum number of bits used for cached sub-matrices in composite_gate expressions - const int qc_block_chunking_size = 8; - - template <typename T> - struct is_composite_gate { const static bool value = false; }; - template <typename T, typename U> - struct is_composite_gate<composite_gate<T,U> > { const static bool value = true; }; - - - // These overloads all deal with intelligently composing chains of composite_gate expressions - // such that the resulting expression has the form: - // (gate_exp,(gate_exp,(gate_exp,(gate_exp())))) - // and each gate_exp contains a cached gate matrix for a gate of at most qc_block_chunking_size bits. - // This facilitates the optimizations in the compute_state_element() function. - template <typename T, typename U, typename V, typename enabled = void> - struct combine_gates; - - // This is a base case of this recursive template. It takes care of converting small composite_gates into - // cached gate objects. - template <typename T, typename U, typename V> - struct combine_gates<T,U,V,typename enable_if_c<(T::num_bits + U::num_bits <= qc_block_chunking_size)>::type > - { - typedef composite_gate<gate<T::num_bits + U::num_bits>,V> result_type; - - static const result_type eval ( - const composite_gate<T,U>& lhs, - const gate_exp<V>& rhs - ) - { - typedef gate<T::num_bits + U::num_bits> gate_type; - return composite_gate<gate_type,V>(gate_type(lhs), rhs); - } - }; - - // this is the recursive step of this template - template <typename T, typename U, typename V> - struct combine_gates<T,U,V,typename enable_if_c<(is_composite_gate<U>::value == true)>::type > - { - typedef typename combine_gates<typename U::lhs_type, typename U::rhs_type, V>::result_type inner_type; - typedef composite_gate<T,inner_type> result_type; - - static const result_type eval ( - const composite_gate<T,U>& lhs, - const gate_exp<V>& rhs - ) - { - return composite_gate<T,inner_type>(lhs.lhs, combine_gates<typename U::lhs_type, typename U::rhs_type, V>::eval(lhs.rhs,rhs)); - } - - }; - - // This is a base case of this recursive template. It takes care of adding new gates when the left - // hand side is too big to just turn it into a cached gate object. - template <typename T, typename U, typename V> - struct combine_gates<T,U,V,typename enable_if_c<(T::num_bits + U::num_bits > qc_block_chunking_size && - is_composite_gate<U>::value == false)>::type > - { - typedef composite_gate<T,composite_gate<U, V> > result_type; - - static const result_type eval ( - const composite_gate<T,U>& lhs, - const gate_exp<V>& rhs - ) - { - return result_type(lhs.lhs, composite_gate<U,V>(lhs.rhs, rhs)); - } - - }; - - } - - template <typename T, typename U> - const composite_gate<T,U> operator, ( - const gate_exp<T>& lhs, - const gate_exp<U>& rhs - ) - { - return composite_gate<T,U>(lhs,rhs); - } - - template <typename T, typename U, typename V> - const typename qc_helpers::combine_gates<T,U,V>::result_type operator, ( - const composite_gate<T,U>& lhs, - const gate_exp<V>& rhs - ) - { - return qc_helpers::combine_gates<T,U,V>::eval(lhs,rhs); - } - - // If you are getting an error here then it means that you are trying to combine a gate expression - // with an integer somewhere (and that is an error). - template <typename T> void operator, ( const gate_exp<T>&, int) { COMPILE_TIME_ASSERT(sizeof(T) > 100000000); } - template <typename T> void operator, ( int, const gate_exp<T>&) { COMPILE_TIME_ASSERT(sizeof(T) > 100000000); } - -// ---------------------------------------------------------------------------------------- - - namespace quantum_gates - { - template <int control_bit, int target_bit> - class cnot; - - template <int control_bit1, int control_bit2, int target_bit> - class toffoli; - } - - template <int control_bit, int target_bit> - struct gate_traits<quantum_gates::cnot<control_bit, target_bit> > - { - static const long num_bits = tabs<control_bit-target_bit>::value+1; - static const long dims = qc_helpers::exp_2_n<num_bits>::value; - }; - - template <int control_bit1, int control_bit2, int target_bit> - struct gate_traits<quantum_gates::toffoli<control_bit1, control_bit2, target_bit> > - { - static const long num_bits = tmax<tabs<control_bit1-target_bit>::value, - tabs<control_bit2-target_bit>::value>::value+1; - static const long dims = qc_helpers::exp_2_n<num_bits>::value; - }; - - -// ---------------------------------------------------------------------------------------- - - namespace quantum_gates - { - - inline const gate<1> hadamard( - ) - { - gate<1> h; - h(0,0) = std::sqrt(1/2.0); - h(0,1) = std::sqrt(1/2.0); - h(1,0) = std::sqrt(1/2.0); - h(1,1) = -std::sqrt(1/2.0); - return h; - } - - // ------------------------------------------------------------------------------------ - - inline const gate<1> x( - ) - { - gate<1> x; - x(0,1) = 1; - x(1,0) = 1; - return x; - } - - // ------------------------------------------------------------------------------------ - - inline const gate<1> y( - ) - { - gate<1> x; - qc_scalar_type i(0,1); - x(0,1) = -i; - x(1,0) = i; - return x; - } - - // ------------------------------------------------------------------------------------ - - inline const gate<1> z( - ) - { - gate<1> z; - z(0,0) = 1; - z(1,1) = -1; - return z; - } - - // ------------------------------------------------------------------------------------ - - inline const gate<1> noop( - ) - { - gate<1> i; - i(0,0) = 1; - i(1,1) = 1; - return i; - } - - // ------------------------------------------------------------------------------------ - - template <int control_bit, int target_bit> - class cnot : public gate_exp<cnot<control_bit, target_bit> > - { - public: - COMPILE_TIME_ASSERT(control_bit != target_bit); - - cnot() : gate_exp<cnot>(*this) - { - const int min_bit = std::min(control_bit, target_bit); - - control_mask = 1; - target_mask = 1; - - // make the masks so that their only on bit corresponds to the given control_bit and target_bit bits - for (int i = 0; i < control_bit-min_bit; ++i) - control_mask <<= 1; - for (int i = 0; i < target_bit-min_bit; ++i) - target_mask <<= 1; - } - - static const long num_bits = gate_traits<cnot>::num_bits; - static const long dims = gate_traits<cnot>::dims; - - const qc_scalar_type operator() (long r, long c) const - { - unsigned long output; - // if the input control bit is set - if (control_mask&c) - { - output = c^target_mask; - } - else - { - output = c; - } - - 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 - { - // make sure requires clause is not broken - DLIB_ASSERT(reg.nr() == dims && reg.nc() == 1 && - 0 <= row_idx && row_idx < dims, - "\tqc_scalar_type cnot::compute_state_element(reg,row_idx)" - << "\n\tinvalid arguments to this function" - << "\n\treg.nr(): " << reg.nr() - << "\n\treg.nc(): " << reg.nc() - << "\n\tdims: " << dims - << "\n\trow_idx: " << row_idx - << "\n\tthis: " << this - ); - - - unsigned long output = row_idx; - // if the input control bit is set - if (control_mask&output) - { - output = output^target_mask; - } - - return reg(output); - } - - private: - - unsigned long control_mask; - unsigned long target_mask; - - - }; - - // ------------------------------------------------------------------------------------ - - template <int control_bit1, int control_bit2, int target_bit> - class toffoli : public gate_exp<toffoli<control_bit1, control_bit2, target_bit> > - { - public: - COMPILE_TIME_ASSERT(control_bit1 != target_bit && control_bit2 != target_bit && control_bit1 != control_bit2); - COMPILE_TIME_ASSERT((control_bit1 < target_bit && control_bit2 < target_bit) ||(control_bit1 > target_bit && control_bit2 > target_bit) ); - - toffoli() : gate_exp<toffoli>(*this) - { - const int min_bit = std::min(std::min(control_bit1, control_bit2), target_bit); - - control1_mask = 1; - control2_mask = 1; - target_mask = 1; - - // make the masks so that their only on bit corresponds to the given control_bit1 and target_bit bits - for (int i = 0; i < control_bit1-min_bit; ++i) - control1_mask <<= 1; - for (int i = 0; i < control_bit2-min_bit; ++i) - control2_mask <<= 1; - for (int i = 0; i < target_bit-min_bit; ++i) - target_mask <<= 1; - } - - static const long num_bits = gate_traits<toffoli>::num_bits; - static const long dims = gate_traits<toffoli>::dims; - - const qc_scalar_type operator() (long r, long c) const - { - unsigned long output; - // if the input control bits are set - if ((control1_mask&c) && (control2_mask&c)) - { - output = c^target_mask; - } - else - { - output = c; - } - - 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 - { - // make sure requires clause is not broken - DLIB_ASSERT(reg.nr() == dims && reg.nc() == 1 && - 0 <= row_idx && row_idx < dims, - "\tqc_scalar_type toffoli::compute_state_element(reg,row_idx)" - << "\n\tinvalid arguments to this function" - << "\n\treg.nr(): " << reg.nr() - << "\n\treg.nc(): " << reg.nc() - << "\n\tdims: " << dims - << "\n\trow_idx: " << row_idx - << "\n\tthis: " << this - ); - - - unsigned long output; - // if the input control bits are set - if ((control1_mask&row_idx) && (control2_mask&row_idx)) - { - output = row_idx^target_mask; - } - else - { - output = row_idx; - } - - return reg(output); - - } - - private: - - unsigned long control1_mask; - unsigned long control2_mask; - unsigned long target_mask; - - - }; - - - // ------------------------------------------------------------------------------------ - - } - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_QUANTUM_COMPUTINg_1_ - - diff --git a/ml/dlib/dlib/quantum_computing/quantum_computing_abstract.h b/ml/dlib/dlib/quantum_computing/quantum_computing_abstract.h deleted file mode 100644 index bcc65af23..000000000 --- a/ml/dlib/dlib/quantum_computing/quantum_computing_abstract.h +++ /dev/null @@ -1,590 +0,0 @@ -// Copyright (C) 2008 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#undef DLIB_QUANTUM_COMPUTINg_ABSTRACT_ -#ifdef DLIB_QUANTUM_COMPUTINg_ABSTRACT_ - -#include <complex> -#include "../matrix.h" -#include "../rand.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - typedef std::complex<double> qc_scalar_type; - -// ---------------------------------------------------------------------------------------- - - class quantum_register - { - /*! - INITIAL VALUE - - num_bits() == 1 - - state_vector().nr() == 2 - - state_vector().nc() == 1 - - state_vector()(0) == 1 - - state_vector()(1) == 0 - - probability_of_bit(0) == 0 - - - i.e. This register represents a single quantum bit and it is - completely in the 0 state. - - WHAT THIS OBJECT REPRESENTS - This object represents a set of quantum bits. - !*/ - - public: - - quantum_register( - ); - /*! - ensures - - this object is properly initialized - !*/ - - int num_bits ( - ) const; - /*! - ensures - - returns the number of quantum bits in this register - !*/ - - void set_num_bits ( - int new_num_bits - ); - /*! - requires - - 1 <= new_num_bits <= 30 - ensures - - #num_bits() == new_num_bits - - #state_vector().nr() == 2^new_num_bits - (i.e. the size of the state_vector is exponential in the number of bits in a register) - - for all valid i: - - probability_of_bit(i) == 0 - !*/ - - void zero_all_bits( - ); - /*! - ensures - - for all valid i: - - probability_of_bit(i) == 0 - !*/ - - void append ( - const quantum_register& reg - ); - /*! - ensures - - #num_bits() == num_bits() + reg.num_bits() - - #this->state_vector() == tensor_product(this->state_vector(), reg.state_vector()) - - The original bits in *this become the high order bits of the resulting - register and all the bits in reg end up as the low order bits in the - resulting register. - !*/ - - double probability_of_bit ( - int bit - ) const; - /*! - requires - - 0 <= bit < num_bits() - ensures - - returns the probability of measuring the given bit and it being in the 1 state. - - The returned value is also equal to the sum of norm(state_vector()(i)) for all - i where the bit'th bit in i is set to 1. (note that the lowest order bit is bit 0) - !*/ - - template <typename rand_type> - bool measure_bit ( - int bit, - rand_type& rnd - ); - /*! - requires - - 0 <= bit < num_bits() - - rand_type == an implementation of dlib/rand/rand_float_abstract.h - ensures - - measures the given bit in this register. Let R denote the boolean - result of the measurement, where true means the bit was measured to - have value 1 and false means it had a value of 0. - - if (R == true) then - - returns true - - #probability_of_bit(bit) == 1 - - else - - returns false - - #probability_of_bit(bit) == 0 - !*/ - - template <typename rand_type> - bool measure_and_remove_bit ( - int bit, - rand_type& rnd - ); - /*! - requires - - num_bits() > 1 - - 0 <= bit < num_bits() - - rand_type == an implementation of dlib/rand/rand_float_abstract.h - ensures - - measures the given bit in this register. Let R denote the boolean - result of the measurement, where true means the bit was measured to - have value 1 and false means it had a value of 0. - - #num_bits() == num_bits() - 1 - - removes the bit that was measured from this register. - - if (R == true) then - - returns true - - else - - returns false - !*/ - - const matrix<qc_scalar_type,0,1>& state_vector( - ) const; - /*! - ensures - - returns a const reference to the state vector that describes the state of - the quantum bits in this register. - !*/ - - matrix<qc_scalar_type,0,1>& state_vector( - ); - /*! - ensures - - returns a non-const reference to the state vector that describes the state of - the quantum bits in this register. - !*/ - - void swap ( - quantum_register& item - ); - /*! - ensures - - swaps *this and item - !*/ - - }; - - inline void swap ( - quantum_register& a, - quantum_register& b - ) { a.swap(b); } - /*! - provides a global swap function - !*/ - -// ---------------------------------------------------------------------------------------- - - template <typename T> - class gate_exp - { - /*! - REQUIREMENTS ON T - T must be some object that inherits from gate_exp and implements its own - version of operator() and compute_state_element(). - - WHAT THIS OBJECT REPRESENTS - This object represents an expression that evaluates to a quantum gate - that operates on T::num_bits qubits. - - This object makes it easy to create new types of gate objects. All - you need to do is inherit from gate_exp in the proper way and - then you can use your new gate objects in conjunction with all the - others. - !*/ - - public: - - static const long num_bits = T::num_bits; - static const long dims = T::dims; - - gate_exp( - T& exp - ); - /*! - ensures - - #&ref() == &exp - !*/ - - const qc_scalar_type operator() ( - long r, - long c - ) const; - /*! - requires - - 0 <= r < dims - - 0 <= c < dims - ensures - - returns ref()(r,c) - !*/ - - void apply_gate_to ( - quantum_register& reg - ) const; - /*! - requires - - reg.num_bits() == num_bits - ensures - - applies this quantum gate to the given quantum register - - Let M represent the matrix for this quantum gate, then - #reg().state_vector() = M*reg().state_vector() - !*/ - - template <typename exp> - qc_scalar_type compute_state_element ( - const matrix_exp<exp>& reg, - long row_idx - ) const; - /*! - requires - - reg.nr() == dims - - reg.nc() == 1 - - 0 <= row_idx < dims - ensures - - Let M represent the matrix for this gate, then - this function returns rowm(M*reg, row_idx) - (i.e. returns the row_idx row of what you get when you apply this - gate to the given column vector in reg) - - This function works by calling ref().compute_state_element(reg,row_idx) - !*/ - - const T& ref( - ); - /*! - ensures - - returns a reference to the subexpression contained in this object - !*/ - - const matrix<qc_scalar_type> mat ( - ) const; - /*! - ensures - - returns a dense matrix object that contains the matrix for this gate - !*/ - }; - -// ---------------------------------------------------------------------------------------- - - template <typename T, typename U> - class composite_gate : public gate_exp<composite_gate<T,U> > - { - /*! - REQUIREMENTS ON T AND U - Both must be gate expressions that inherit from gate_exp - - WHAT THIS OBJECT REPRESENTS - This object represents a quantum gate that is the tensor product of - two other quantum gates. - - - As an example, suppose you have 3 registers, reg_high, reg_low, and reg_all. Also - suppose that reg_all is what you get when you append reg_high and reg_low, - so reg_all.state_vector() == tensor_product(reg_high.state_vector(),reg_low.state_vector()). - - Then applying a composite gate to reg_all would give you the same thing as - applying the lhs gate to reg_high and the rhs gate to reg_low and then appending - the two resulting registers. So the lhs gate of a composite_gate applies to - the high order bits of a regitser and the rhs gate applies to the lower order bits. - !*/ - public: - - composite_gate ( - const composite_gate& g - ); - /*! - ensures - - *this is a copy of g - !*/ - - composite_gate( - const gate_exp<T>& lhs_, - const gate_exp<U>& rhs_ - ): - /*! - ensures - - #lhs == lhs_.ref() - - #rhs == rhs_.ref() - - #num_bits == T::num_bits + U::num_bits - - #dims == 2^num_bits - - #&ref() == this - !*/ - - const qc_scalar_type operator() ( - long r, - long c - ) const; - /*! - requires - - 0 <= r < dims - - 0 <= c < dims - ensures - - Let M denote the tensor product of lhs with rhs, then this function - returns M(r,c) - (i.e. returns lhs(r/U::dims,c/U::dims)*rhs(r%U::dims, c%U::dims)) - !*/ - - template <typename exp> - qc_scalar_type compute_state_element ( - const matrix_exp<exp>& reg, - long row_idx - ) const; - /*! - requires - - reg.nr() == dims - - reg.nc() == 1 - - 0 <= row_idx < dims - ensures - - Let M represent the matrix for this gate, then this function - returns rowm(M*reg, row_idx) - (i.e. returns the row_idx row of what you get when you apply this - gate to the given column vector in reg) - - This function works by calling rhs.compute_state_element() and using elements - of the matrix in lhs. - !*/ - - static const long num_bits; - static const long dims; - - const T lhs; - const U rhs; - }; - -// ---------------------------------------------------------------------------------------- - - template <long bits> - class gate : public gate_exp<gate<bits> > - { - /*! - REQUIREMENTS ON bits - 0 < bits <= 30 - - WHAT THIS OBJECT REPRESENTS - This object represents a quantum gate that operates on bits qubits. - It stores its gate matrix explicitly in a dense in-memory matrix. - !*/ - - public: - gate( - ); - /*! - ensures - - num_bits == bits - - dims == 2^bits - - #&ref() == this - - for all valid r and c: - #(*this)(r,c) == 0 - !*/ - - gate ( - const gate& g - ); - /*! - ensures - - *this is a copy of g - !*/ - - template <typename T> - explicit gate( - const gate_exp<T>& g - ); - /*! - requires - - T::num_bits == num_bits - ensures - - num_bits == bits - - dims == 2^bits - - #&ref() == this - - for all valid r and c: - #(*this)(r,c) == g(r,c) - !*/ - - const qc_scalar_type& operator() ( - long r, - long c - ) const; - /*! - requires - - 0 <= r < dims - - 0 <= c < dims - ensures - - Let M denote the matrix for this gate, then this function - returns a const reference to M(r,c) - !*/ - - qc_scalar_type& operator() ( - long r, - long c - ); - /*! - requires - - 0 <= r < dims - - 0 <= c < dims - ensures - - Let M denote the matrix for this gate, then this function - returns a non-const reference to M(r,c) - !*/ - - template <typename exp> - qc_scalar_type compute_state_element ( - const matrix_exp<exp>& reg, - long row_idx - ) const; - /*! - requires - - reg.nr() == dims - - reg.nc() == 1 - - 0 <= row_idx < dims - ensures - - Let M represent the matrix for this gate, then this function - returns rowm(M*reg, row_idx) - (i.e. returns the row_idx row of what you get when you apply this - gate to the given column vector in reg) - !*/ - - static const long num_bits; - static const long dims; - - }; - -// ---------------------------------------------------------------------------------------- - - template <typename T, typename U> - const composite_gate<T,U> operator, ( - const gate_exp<T>& lhs, - const gate_exp<U>& rhs - ) { return composite_gate<T,U>(lhs,rhs); } - /*! - ensures - - returns a composite_gate that represents the tensor product of the lhs - gate with the rhs gate. - !*/ - -// ---------------------------------------------------------------------------------------- - - namespace quantum_gates - { - - inline const gate<1> hadamard( - ); - /*! - ensures - - returns the Hadamard gate. - (i.e. A gate with a matrix of - |1, 1| - 1/sqrt(2) * |1,-1| ) - !*/ - - inline const gate<1> x( - ); - /*! - ensures - - returns the not gate. - (i.e. A gate with a matrix of - |0, 1| - |1, 0| ) - !*/ - - inline const gate<1> y( - ); - /*! - ensures - - returns the y gate. - (i.e. A gate with a matrix of - |0,-i| - |i, 0| ) - !*/ - - inline const gate<1> z( - ); - /*! - ensures - - returns the z gate. - (i.e. A gate with a matrix of - |1, 0| - |0,-1| ) - !*/ - - inline const gate<1> noop( - ); - /*! - ensures - - returns the no-op or identity gate. - (i.e. A gate with a matrix of - |1, 0| - |0, 1| ) - !*/ - - template < - int control_bit, - int target_bit - > - class cnot : public gate_exp<cnot<control_bit, target_bit> > - { - /*! - REQUIREMENTS ON control_bit AND target_bit - - control_bit != target_bit - - WHAT THIS OBJECT REPRESENTS - This object represents the controlled-not quantum gate. It is a gate that - operates on abs(control_bit-target_bit)+1 qubits. - - In terms of the computational basis vectors, this gate maps input - vectors to output vectors in the following way: - - if (the input vector corresponds to a state where the control_bit - qubit is 1) then - - this gate outputs the computational basis vector that - corresponds to the state where the target_bit has been flipped - with respect to the input vector - - else - - this gate outputs the input vector unmodified - - !*/ - }; - - template < - int control_bit1, - int control_bit2, - int target_bit - > - class toffoli : public gate_exp<toffoli<control_bit1, control_bit2, target_bit> > - { - /*! - REQUIREMENTS ON control_bit1, control_bit2, AND target_bit - - all the arguments denote different bits, i.e.: - - control_bit1 != target_bit - - control_bit2 != target_bit - - control_bit1 != control_bit2 - - The target bit can't be in-between the control bits, i.e.: - - (control_bit1 < target_bit && control_bit2 < target_bit) || - (control_bit1 > target_bit && control_bit2 > target_bit) - - WHAT THIS OBJECT REPRESENTS - This object represents the toffoli variant of a controlled-not quantum gate. - It is a gate that operates on max(abs(control_bit2-target_bit),abs(control_bit1-target_bit))+1 - qubits. - - In terms of the computational basis vectors, this gate maps input - vectors to output vectors in the following way: - - if (the input vector corresponds to a state where the control_bit1 and - control_bit2 qubits are 1) then - - this gate outputs the computational basis vector that - corresponds to the state where the target_bit has been flipped - with respect to the input vector - - else - - this gate outputs the input vector unmodified - - !*/ - }; - - // ------------------------------------------------------------------------------------ - - } - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_QUANTUM_COMPUTINg_ABSTRACT_ - - - |