Skip to content

Commit fe48ac8

Browse files
thomashaenerdamiansteiger
authored andcommitted
Added collapse_wavefunction to simulator. (ProjectQ-Framework#122)
1 parent 22259a1 commit fe48ac8

5 files changed

Lines changed: 117 additions & 0 deletions

File tree

projectq/backends/_sim/_cppkernels/simulator.hpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,36 @@ class Simulator{
365365
vec_[i] = wavefunction[i];
366366
}
367367

368+
void collapse_wavefunction(std::vector<unsigned> const& ids, std::vector<bool> const& values){
369+
run();
370+
assert(ids.size() == values.size());
371+
if (!check_ids(ids))
372+
throw(std::runtime_error("collapse_wavefunction(): Unknown qubit id(s) provided. Try calling eng.flush() before invoking this function."));
373+
std::size_t mask = 0, val = 0;
374+
for (unsigned i = 0; i < ids.size(); ++i){
375+
mask |= (1UL << map_[ids[i]]);
376+
val |= ((values[i]?1UL:0UL) << map_[ids[i]]);
377+
}
378+
// set bad entries to 0 and compute probability of outcome to renormalize
379+
calc_type N = 0.;
380+
#pragma omp parallel for reduction(+:N) schedule(static)
381+
for (std::size_t i = 0; i < vec_.size(); ++i){
382+
if ((i & mask) == val)
383+
N += std::norm(vec_[i]);
384+
}
385+
if (N < 1.e-12)
386+
throw(std::runtime_error("collapse_wavefunction(): Invalid collapse! Probability is ~0."));
387+
// re-normalize (if possible)
388+
N = 1./std::sqrt(N);
389+
#pragma omp parallel for schedule(static)
390+
for (std::size_t i = 0; i < vec_.size(); ++i){
391+
if ((i & mask) != val)
392+
vec_[i] = 0.;
393+
else
394+
vec_[i] *= N;
395+
}
396+
}
397+
368398
void run(){
369399
if (fused_gates_.size() < 1)
370400
return;

projectq/backends/_sim/_cppsim.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ PYBIND11_PLUGIN(_cppsim) {
5555
.def("get_probability", &Simulator::get_probability)
5656
.def("get_amplitude", &Simulator::get_amplitude)
5757
.def("set_wavefunction", &Simulator::set_wavefunction)
58+
.def("collapse_wavefunction", &Simulator::collapse_wavefunction)
5859
.def("run", &Simulator::run)
5960
.def("cheat", &Simulator::cheat)
6061
;

projectq/backends/_sim/_pysim.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,44 @@ def set_wavefunction(self, wavefunction, ordering):
406406
self._state = _np.array(wavefunction)
407407
self._map = {ordering[i]: i for i in range(len(ordering))}
408408

409+
def collapse_wavefunction(self, ids, values):
410+
"""
411+
Collapse a quantum register onto a classical basis state.
412+
413+
Args:
414+
ids (list[int]): Qubit IDs to collapse.
415+
values (list[bool]): Measurement outcome for each of the qubit IDs
416+
in `ids`.
417+
Raises:
418+
RuntimeError: If probability of outcome is ~0 or unknown qubits
419+
are provided.
420+
"""
421+
assert len(ids) == len(values)
422+
# all qubits must have been allocated before
423+
if not all([Id in self._map for Id in ids]):
424+
raise RuntimeError("collapse_wavefunction(): Unknown qubit id(s)"
425+
" provided. Try calling eng.flush() before "
426+
"invoking this function.")
427+
mask = 0
428+
val = 0
429+
for i in range(len(ids)):
430+
pos = self._map[ids[i]]
431+
mask |= (1 << pos)
432+
val |= (int(values[i]) << pos)
433+
nrm = 0.
434+
for i in range(len(self._state)):
435+
if (mask & i) == val:
436+
nrm += _np.abs(self._state[i]) ** 2
437+
if nrm < 1.e-12:
438+
raise RuntimeError("collapse_wavefunction(): Invalid collapse! "
439+
"Probability is ~0.")
440+
inv_nrm = 1. / _np.sqrt(nrm)
441+
for i in range(len(self._state)):
442+
if (mask & i) != val:
443+
self._state[i] = 0.
444+
else:
445+
self._state[i] *= inv_nrm
446+
409447
def run(self):
410448
"""
411449
Dummy function to implement the same interface as the c++ simulator.

projectq/backends/_sim/_simulator.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,27 @@ def set_wavefunction(self, wavefunction, qureg):
196196
self._simulator.set_wavefunction(wavefunction,
197197
[qb.id for qb in qureg])
198198

199+
def collapse_wavefunction(self, qureg, values):
200+
"""
201+
Collapse a quantum register onto a classical basis state.
202+
203+
Args:
204+
qureg (Qureg|list[Qubit]): Qubits to collapse.
205+
values (list[bool]): Measurement outcome for each of the qubits
206+
in `qureg`.
207+
208+
Raises:
209+
RuntimeError: If an outcome has probability (approximately) 0 or
210+
if unknown qubits are provided (see note).
211+
212+
Note:
213+
Make sure all previous commands have passed through the
214+
compilation chain (call main_engine.flush() to make sure).
215+
"""
216+
return self._simulator.collapse_wavefunction([qb.id for qb in qureg],
217+
[bool(v) for v in
218+
values])
219+
199220
def cheat(self):
200221
"""
201222
Access the ordering of the qubits and the state vector directly.

projectq/backends/_sim/_simulator_test.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,33 @@ def test_simulator_set_wavefunction(sim):
377377
Measure | qubits
378378

379379

380+
def test_simulator_collapse_wavefunction(sim):
381+
eng = MainEngine(sim)
382+
qubits = eng.allocate_qureg(4)
383+
# unknown qubits: raises
384+
with pytest.raises(RuntimeError):
385+
eng.backend.collapse_wavefunction(qubits, [0] * 4)
386+
eng.flush()
387+
eng.backend.collapse_wavefunction(qubits, [0] * 4)
388+
assert pytest.approx(eng.backend.get_probability([0] * 4, qubits)) == 1.
389+
All(H) | qubits[1:]
390+
eng.flush()
391+
assert pytest.approx(eng.backend.get_probability([0] * 4, qubits)) == .125
392+
# impossible outcome: raises
393+
with pytest.raises(RuntimeError):
394+
eng.backend.collapse_wavefunction(qubits, [1] + [0] * 3)
395+
eng.backend.collapse_wavefunction(qubits[:-1], [0, 1, 0])
396+
assert (pytest.approx(eng.backend.get_probability([0, 1, 0, 1], qubits))
397+
== .5)
398+
eng.backend.set_wavefunction([1.] + [0.] * 15, qubits)
399+
H | qubits[0]
400+
CNOT | (qubits[0], qubits[1])
401+
eng.flush()
402+
eng.backend.collapse_wavefunction([qubits[0]], [1])
403+
assert (pytest.approx(eng.backend.get_probability([1, 1], qubits[0:2]))
404+
== 1.)
405+
406+
380407
def test_simulator_no_uncompute_exception(sim):
381408
eng = MainEngine(sim, [])
382409
qubit = eng.allocate_qubit()

0 commit comments

Comments
 (0)