Skip to content

Commit

Permalink
Added set_wavefunction to simulator (including tests).
Browse files Browse the repository at this point in the history
  • Loading branch information
thomashaener committed Jun 11, 2017
1 parent 144c8d1 commit 2b7026c
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 3 deletions.
29 changes: 26 additions & 3 deletions projectq/backends/_sim/_cppkernels/simulator.hpp
Expand Up @@ -64,6 +64,7 @@ class Simulator{
}

bool get_classical_value(unsigned id, calc_type tol = 1.e-12){
run();
unsigned pos = map_[id];
std::size_t delta = (1UL << pos);

Expand Down Expand Up @@ -267,9 +268,8 @@ class Simulator{
calc_type get_probability(std::vector<bool> const& bit_string,
std::vector<unsigned> const& ids){
run();
for (auto id : ids)
if (map_.count(id) == 0)
throw(std::runtime_error("get_probability(): Unknown qubit id. Please make sure you have called eng.flush()."));
if (!check_ids(ids))
throw(std::runtime_error("get_probability(): Unknown qubit id. Please make sure you have called eng.flush()."));
std::size_t mask = 0, bit_str = 0;
for (unsigned i = 0; i < ids.size(); ++i){
mask |= 1UL << map_[ids[i]];
Expand Down Expand Up @@ -349,6 +349,22 @@ class Simulator{
}
}

void set_wavefunction(StateVector const& wavefunction, std::vector<unsigned> const& ordering){
run();
// make sure there are 2^n amplitudes for n qubits
assert(wavefunction.size() == (1UL << ordering.size()));
// check that all qubits have been allocated previously
if (map_.size() != ordering.size() || !check_ids(ordering))
throw(std::runtime_error("set_wavefunction(): Invalid mapping provided. Please make sure all qubits have been allocated previously (call eng.flush())."));

// set mapping and wavefunction
for (unsigned i = 0; i < ordering.size(); ++i)
map_[ordering[i]] = i;
#pragma omp parallel for schedule(static)
for (std::size_t i = 0; i < wavefunction.size(); ++i)
vec_[i] = wavefunction[i];
}

void run(){
if (fused_gates_.size() < 1)
return;
Expand Down Expand Up @@ -418,6 +434,13 @@ class Simulator{
return ctrlmask;
}

bool check_ids(std::vector<unsigned> const& ids){
for (auto id : ids)
if (!map_.count(id))
return false;
return true;
}

unsigned N_; // #qubits
StateVector vec_;
Map map_;
Expand Down
1 change: 1 addition & 0 deletions projectq/backends/_sim/_cppsim.cpp
Expand Up @@ -54,6 +54,7 @@ PYBIND11_PLUGIN(_cppsim) {
.def("emulate_time_evolution", &Simulator::emulate_time_evolution)
.def("get_probability", &Simulator::get_probability)
.def("get_amplitude", &Simulator::get_amplitude)
.def("set_wavefunction", &Simulator::set_wavefunction)
.def("run", &Simulator::run)
.def("cheat", &Simulator::cheat)
;
Expand Down
22 changes: 22 additions & 0 deletions projectq/backends/_sim/_pysim.py
Expand Up @@ -384,6 +384,28 @@ def kernel(u, d, m):
self._state[id2],
m)

def set_wavefunction(self, wavefunction, ordering):
"""
Set wavefunction and qubit ordering.
Args:
wavefunction (list[complex]): Array of complex amplitudes
describing the wavefunction (must be normalized).
ordering (list): List of ids describing the new ordering of qubits
(i.e., the ordering of the provided wavefunction).
"""
# wavefunction contains 2^n values for n qubits
assert len(wavefunction) == (1 << len(ordering))
# all qubits must have been allocated before
if (not all([Id in self._map for Id in ordering])
or len(self._map) != len(ordering)):
raise RuntimeError("set_wavefunction(): Invalid mapping provided."
" Please make sure all qubits have been "
"allocated previously (call eng.flush()).")

self._state = _np.array(wavefunction)
self._map = {ordering[i]: i for i in range(len(ordering))}

def run(self):
"""
Dummy function to implement the same interface as the c++ simulator.
Expand Down
40 changes: 40 additions & 0 deletions projectq/backends/_sim/_simulator.py
Expand Up @@ -120,6 +120,11 @@ def get_expectation_value(self, qubit_operator, qureg):
Returns:
Expectation value
Note:
Make sure all previous commands (especially allocations) have
passed through the compilation chain (call main_engine.flush() to
make sure).
"""
operator = [(list(term), coeff) for (term, coeff)
in qubit_operator.terms.items()]
Expand All @@ -137,6 +142,11 @@ def get_probability(self, bit_string, qureg):
Returns:
Probability of measuring the provided bit string.
Note:
Make sure all previous commands (especially allocations) have
passed through the compilation chain (call main_engine.flush() to
make sure).
"""
bit_string = [bool(int(b)) for b in bit_string]
return self._simulator.get_probability(bit_string,
Expand All @@ -155,11 +165,37 @@ def get_amplitude(self, bit_string, qureg):
Returns:
Probability amplitude of the provided bit string.
Note:
Make sure all previous commands (especially allocations) have
passed through the compilation chain (call main_engine.flush() to
make sure).
"""
bit_string = [bool(int(b)) for b in bit_string]
return self._simulator.get_amplitude(bit_string,
[qb.id for qb in qureg])

def set_wavefunction(self, wavefunction, qureg):
"""
Set the wavefunction and the qubit ordering of the simulator.
The simulator will adopt the ordering of qureg (instead of reordering
the wavefunction).
Args:
wavefunction (list[complex]): Array of complex amplitudes
describing the wavefunction (must be normalized).
qureg (Qureg|list[Qubit]): Quantum register determining the
ordering. Must contain all allocated qubits.
Note:
Make sure all previous commands (especially allocations) have
passed through the compilation chain (call main_engine.flush() to
make sure).
"""
self._simulator.set_wavefunction(wavefunction,
[qb.id for qb in qureg])

def cheat(self):
"""
Access the ordering of the qubits and the state vector directly.
Expand All @@ -171,6 +207,10 @@ def cheat(self):
A tuple where the first entry is a dictionary mapping qubit
indices to bit-locations and the second entry is the corresponding
state vector.
Note:
Make sure all previous commands have passed through the
compilation chain (call main_engine.flush() to make sure).
"""
return self._simulator.cheat()

Expand Down
14 changes: 14 additions & 0 deletions projectq/backends/_sim/_simulator_test.py
Expand Up @@ -363,6 +363,20 @@ def build_matrix(list_single_matrices):
assert numpy.allclose(res, final_wavefunction)


def test_simulator_set_wavefunction(sim):
eng = MainEngine(sim)
qubits = eng.allocate_qureg(2)
wf = [0., 0., math.sqrt(0.2), math.sqrt(0.8)]
with pytest.raises(RuntimeError):
eng.backend.set_wavefunction(wf, qubits)
eng.flush()
eng.backend.set_wavefunction(wf, qubits)
assert pytest.approx(eng.backend.get_probability('1', [qubits[0]])) == .8
assert pytest.approx(eng.backend.get_probability('01', qubits)) == .2
assert pytest.approx(eng.backend.get_probability('1', [qubits[1]])) == 1.
Measure | qubits


def test_simulator_no_uncompute_exception(sim):
eng = MainEngine(sim, [])
qubit = eng.allocate_qubit()
Expand Down

0 comments on commit 2b7026c

Please sign in to comment.