Skip to content

Commit

Permalink
Merge branch 'develop' into bugfix/openmp_flags
Browse files Browse the repository at this point in the history
  • Loading branch information
thomashaener committed Jun 21, 2017
2 parents c362951 + fe48ac8 commit 227738a
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 0 deletions.
30 changes: 30 additions & 0 deletions projectq/backends/_sim/_cppkernels/simulator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,36 @@ class Simulator{
vec_[i] = wavefunction[i];
}

void collapse_wavefunction(std::vector<unsigned> const& ids, std::vector<bool> const& values){
run();
assert(ids.size() == values.size());
if (!check_ids(ids))
throw(std::runtime_error("collapse_wavefunction(): Unknown qubit id(s) provided. Try calling eng.flush() before invoking this function."));
std::size_t mask = 0, val = 0;
for (unsigned i = 0; i < ids.size(); ++i){
mask |= (1UL << map_[ids[i]]);
val |= ((values[i]?1UL:0UL) << map_[ids[i]]);
}
// set bad entries to 0 and compute probability of outcome to renormalize
calc_type N = 0.;
#pragma omp parallel for reduction(+:N) schedule(static)
for (std::size_t i = 0; i < vec_.size(); ++i){
if ((i & mask) == val)
N += std::norm(vec_[i]);
}
if (N < 1.e-12)
throw(std::runtime_error("collapse_wavefunction(): Invalid collapse! Probability is ~0."));
// re-normalize (if possible)
N = 1./std::sqrt(N);
#pragma omp parallel for schedule(static)
for (std::size_t i = 0; i < vec_.size(); ++i){
if ((i & mask) != val)
vec_[i] = 0.;
else
vec_[i] *= N;
}
}

void run(){
if (fused_gates_.size() < 1)
return;
Expand Down
1 change: 1 addition & 0 deletions projectq/backends/_sim/_cppsim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ PYBIND11_PLUGIN(_cppsim) {
.def("get_probability", &Simulator::get_probability)
.def("get_amplitude", &Simulator::get_amplitude)
.def("set_wavefunction", &Simulator::set_wavefunction)
.def("collapse_wavefunction", &Simulator::collapse_wavefunction)
.def("run", &Simulator::run)
.def("cheat", &Simulator::cheat)
;
Expand Down
38 changes: 38 additions & 0 deletions projectq/backends/_sim/_pysim.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,44 @@ def set_wavefunction(self, wavefunction, ordering):
self._state = _np.array(wavefunction)
self._map = {ordering[i]: i for i in range(len(ordering))}

def collapse_wavefunction(self, ids, values):
"""
Collapse a quantum register onto a classical basis state.
Args:
ids (list[int]): Qubit IDs to collapse.
values (list[bool]): Measurement outcome for each of the qubit IDs
in `ids`.
Raises:
RuntimeError: If probability of outcome is ~0 or unknown qubits
are provided.
"""
assert len(ids) == len(values)
# all qubits must have been allocated before
if not all([Id in self._map for Id in ids]):
raise RuntimeError("collapse_wavefunction(): Unknown qubit id(s)"
" provided. Try calling eng.flush() before "
"invoking this function.")
mask = 0
val = 0
for i in range(len(ids)):
pos = self._map[ids[i]]
mask |= (1 << pos)
val |= (int(values[i]) << pos)
nrm = 0.
for i in range(len(self._state)):
if (mask & i) == val:
nrm += _np.abs(self._state[i]) ** 2
if nrm < 1.e-12:
raise RuntimeError("collapse_wavefunction(): Invalid collapse! "
"Probability is ~0.")
inv_nrm = 1. / _np.sqrt(nrm)
for i in range(len(self._state)):
if (mask & i) != val:
self._state[i] = 0.
else:
self._state[i] *= inv_nrm

def run(self):
"""
Dummy function to implement the same interface as the c++ simulator.
Expand Down
21 changes: 21 additions & 0 deletions projectq/backends/_sim/_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,27 @@ def set_wavefunction(self, wavefunction, qureg):
self._simulator.set_wavefunction(wavefunction,
[qb.id for qb in qureg])

def collapse_wavefunction(self, qureg, values):
"""
Collapse a quantum register onto a classical basis state.
Args:
qureg (Qureg|list[Qubit]): Qubits to collapse.
values (list[bool]): Measurement outcome for each of the qubits
in `qureg`.
Raises:
RuntimeError: If an outcome has probability (approximately) 0 or
if unknown qubits are provided (see note).
Note:
Make sure all previous commands have passed through the
compilation chain (call main_engine.flush() to make sure).
"""
return self._simulator.collapse_wavefunction([qb.id for qb in qureg],
[bool(v) for v in
values])

def cheat(self):
"""
Access the ordering of the qubits and the state vector directly.
Expand Down
27 changes: 27 additions & 0 deletions projectq/backends/_sim/_simulator_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,33 @@ def test_simulator_set_wavefunction(sim):
Measure | qubits


def test_simulator_collapse_wavefunction(sim):
eng = MainEngine(sim)
qubits = eng.allocate_qureg(4)
# unknown qubits: raises
with pytest.raises(RuntimeError):
eng.backend.collapse_wavefunction(qubits, [0] * 4)
eng.flush()
eng.backend.collapse_wavefunction(qubits, [0] * 4)
assert pytest.approx(eng.backend.get_probability([0] * 4, qubits)) == 1.
All(H) | qubits[1:]
eng.flush()
assert pytest.approx(eng.backend.get_probability([0] * 4, qubits)) == .125
# impossible outcome: raises
with pytest.raises(RuntimeError):
eng.backend.collapse_wavefunction(qubits, [1] + [0] * 3)
eng.backend.collapse_wavefunction(qubits[:-1], [0, 1, 0])
assert (pytest.approx(eng.backend.get_probability([0, 1, 0, 1], qubits))
== .5)
eng.backend.set_wavefunction([1.] + [0.] * 15, qubits)
H | qubits[0]
CNOT | (qubits[0], qubits[1])
eng.flush()
eng.backend.collapse_wavefunction([qubits[0]], [1])
assert (pytest.approx(eng.backend.get_probability([1, 1], qubits[0:2]))
== 1.)


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

0 comments on commit 227738a

Please sign in to comment.