Skip to content

Commit

Permalink
release/0.10.3
Browse files Browse the repository at this point in the history
  • Loading branch information
ericbrts authored Jan 29, 2024
2 parents b9e7eaa + 3e26d52 commit 18fd12f
Show file tree
Hide file tree
Showing 19 changed files with 130 additions and 35 deletions.
1 change: 1 addition & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ sphinx-autodoc-typehints
nbsphinx
jinja2==3.0.0
ipython
ipykernel
4 changes: 2 additions & 2 deletions docs/source/notebooks/Gedik_qudit.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@
"outputs": [],
"source": [
"# We can use the sampler module in order to get the results of the algorithm.\n",
"sampler = Sampler(remote_pr)\n",
"sampler = Sampler(remote_pr, max_shots_per_call=10000000)\n",
"sampler.default_job_name = \"Gediks_Algo\"\n",
"\n",
"nsample = 100000\n",
Expand Down Expand Up @@ -530,7 +530,7 @@
"\n",
" remote_qpu.with_input(input_state)\n",
" \n",
" sampler_on_qpu = Sampler(remote_qpu)\n",
" sampler_on_qpu = Sampler(remote_qpu, max_shots_per_call=10000000)\n",
"\n",
" nsample = 200000\n",
" remote_job_qpu = sampler_on_qpu.sample_count \n",
Expand Down
4 changes: 2 additions & 2 deletions docs/source/notebooks/QUBO.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
"source": [
"# The shortest path problem using QUBO\n",
"This notebook implements a Perceval code for finding the shortest path on a _directed, weighted graph_ using the Quantum Unconstrained Binary\n",
"Optimization (QUBO) model. It is mainly adapted from https://ieeexplore.ieee.org/document/9186612, and represents the work done during the 2022 LOQCathon. \n",
"Optimization (QUBO) model. It is mainly implementing the algorithm in https://arxiv.org/pdf/2112.09766.pdf and represents the work done during the 2022 LOQCathon. \n",
"\n",
"Authors: Beata Zjawin, Benjamin Pointard, Nathan Claudet, Noé Delorme, Rina Ismailati\n",
"Authors: Beata Zjawin, Benjamin Pointard, Nathan Claudet, Noé Delorme, Rina Ismailati.\n",
"\n",
"The task is to find the shortest path from start to finish (such that the sum of the weights is minimized) on a simple 5-edge graph:\n",
"\n",
Expand Down
21 changes: 13 additions & 8 deletions docs/source/notebooks/Tutorial.ipynb

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/source/notebooks/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ jupyter
tqdm
qiskit~=0.45.1
qutip~=4.7.3
scipy~=1.11.4
seaborn~=0.13
nbconvert
myqlm~=1.9.5
8 changes: 8 additions & 0 deletions perceval/algorithm/sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,14 @@ def add_iteration_list(self, iterations: List[Dict]):
for iter_params in iterations:
self.add_iteration(**iter_params)

def clear_iterations(self):
# In case, the user wants to use the same sampler instance, but with a new iterator
self._iterator = []

@property
def n_iterations(self):
return len(self._iterator)

def _probs_wrapper(self, progress_callback: Callable = None):
# max_shots is used as the invert of the precision set in the probs computation
# Rationale: mimic the fact that the more shots, the more accurate probability distributions are.
Expand Down
19 changes: 15 additions & 4 deletions perceval/rendering/pdisplay.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,20 @@


in_notebook = False
in_ide = "PYCHARM_HOSTED" in os.environ or 'SPY_PYTHONPATH' in os.environ or 'VSCODE' in os.environ


def in_ide():
for key in os.environ:
if 'PYCHARM' in key or 'SPY_PYTHONPATH' in key or 'VSCODE' in key:
return True
return False


try:
from IPython import get_ipython
if 'IPKernelApp' in get_ipython().config:
in_notebook = True
from IPython.display import HTML, display
from IPython.display import display, Math, HTML
except (ImportError, AttributeError):
pass

Expand Down Expand Up @@ -386,8 +393,10 @@ def _default_output_format(o):
Deduces the best output format given the nature of the data to be displayed and the execution context
"""
if in_notebook:
if isinstance(o, Matrix):
return Format.LATEX
return Format.HTML
elif in_ide and (isinstance(o, ACircuit) or isinstance(o, AProcessor)):
elif in_ide() and (isinstance(o, ACircuit) or isinstance(o, AProcessor)):
return Format.MPLOT
return Format.TEXT

Expand Down Expand Up @@ -428,7 +437,9 @@ def pdisplay(o, output_format: Format = None, **opts):

if isinstance(res, drawsvg.Drawing):
return res
elif in_notebook and output_format != Format.TEXT and output_format != Format.LATEX:
elif in_notebook and output_format == Format.LATEX:
display(Math(res))
elif in_notebook and output_format == Format.HTML:
display(HTML(res))
else:
print(res)
Expand Down
4 changes: 0 additions & 4 deletions perceval/runtime/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,6 @@ def _handle_params(self, args, kwargs):
def __call__(self, *args, **kwargs) -> Dict:
return self.execute_sync(*args, **kwargs)

@abstractmethod
def get_results(self) -> Dict:
pass

@property
@abstractmethod
def status(self) -> JobStatus:
Expand Down
4 changes: 3 additions & 1 deletion perceval/runtime/local_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,9 @@ def _get_results(self):
**self._delta_parameters['mapping'])
elif 'results_list' in self._results:
for res in self._results["results_list"]:
res["results"] = self._result_mapping_function(res['results'], **self._delta_parameters['mapping'])
res["results"] = self._result_mapping_function(res['results'],
**self._delta_parameters['mapping'])
else:
raise KeyError("Cannot find either 'result' or 'results_list' in self._results")
self._result_mapping_function = None
return self._results
18 changes: 11 additions & 7 deletions perceval/serialization/_state_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,17 @@ def deserialize_statevector(s) -> StateVector:


def serialize_bssamples(bss: BSSamples) -> str:
bs_set = []
order = []
for s in bss:
if s not in bs_set:
bs_set.append(s)
order.append(bs_set.index(s))
return ';'.join([serialize_state(bs) for bs in bs_set]) + '/' + ';'.join([str(i) for i in order])
order = [0]*len(bss)
mapping = {}
index = 0
for idx, bs in enumerate(bss):
if bs not in mapping:
mapping[bs] = index
order[idx] = index
index += 1
else:
order[idx] = mapping[bs]
return ';'.join([serialize_state(bs) for bs in mapping.keys()]) + '/' + ';'.join([str(i) for i in order])


def deserialize_bssamples(serialized_bss: str) -> BSSamples:
Expand Down
4 changes: 2 additions & 2 deletions perceval/serialization/serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def serialize(dist: SVDistribution, compress=False) -> str:


@dispatch(BSDistribution, compress=(list, bool))
def serialize(dist: BSDistribution, compress=False) -> str:
def serialize(dist: BSDistribution, compress=True) -> str:
tag = "BSDistribution"
compress = _handle_compress_parameter(compress, tag)
serial_bsd = f":PCVL:{tag}:{{" \
Expand All @@ -113,7 +113,7 @@ def serialize(dist: BSDistribution, compress=False) -> str:


@dispatch(BSCount, compress=(list, bool))
def serialize(obj, compress=False) -> str:
def serialize(obj, compress=True) -> str:
tag = "BSCount"
compress = _handle_compress_parameter(compress, tag)
serial_bsc = f":PCVL:{tag}:{{" \
Expand Down
4 changes: 2 additions & 2 deletions perceval/simulators/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def prob_amplitude(self, input_state: BasicState, output_state: BasicState) -> c
@dispatch(StateVector, BasicState)
def prob_amplitude(self, input_state: StateVector, output_state: BasicState) -> complex:
result = complex(0)
for state, pa in input_state.items():
for state, pa in input_state:
result += self.prob_amplitude(state, output_state) * pa
return result

Expand Down Expand Up @@ -162,7 +162,7 @@ def probability(self, input_state: StateVector, output_state: BasicState) -> flo
output_state.clear_annotations()
sv_out = self.evolve(input_state) # This is not as optimized as it could be
result = 0
for state, pa in sv_out.items():
for state, pa in sv_out:
state.clear_annotations()
if state == output_state:
result += abs(pa) ** 2
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
numpy
exqalibur~=0.3.0
exqalibur~=0.3.1
tabulate
pytest
setuptools
Expand Down
6 changes: 4 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@
"Operating System :: OS Independent",
],
packages=package_list,
install_requires=['sympy', 'numpy', 'scipy', 'tabulate', 'matplotlib', 'exqalibur~=0.3.0', 'multipledispatch',
install_requires=['sympy', 'numpy', 'scipy', 'tabulate', 'matplotlib', 'exqalibur~=0.3.1', 'multipledispatch',
'protobuf>=3.20.3', 'drawsvg>=2.0', 'Deprecated', 'requests', 'networkx~=3.1', 'latexcodec',
'platformdirs'],
extras_require={
"qiskit_bridge": ["qiskit~=0.45.1", "seaborn~=0.13"],
"qutip_bridge": ["qutip~=4.7.3"],
"qutip_bridge": ["qutip~=4.7.3",
"scipy~=1.11.4" # Because of an incompatibility between qutip 4.7 and scipy 1.12
],
"myqlm_bridge": ["myqlm~=1.9.5"]
},
setup_requires=["scmver"],
Expand Down
1 change: 1 addition & 0 deletions tests/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ pytest-benchmark
flake8
qiskit~=0.45.1
qutip~=4.7.3
scipy~=1.11.4
myqlm~=1.9.5
22 changes: 22 additions & 0 deletions tests/test_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,28 @@ def test_run_async_cancel():
assert job.status.status == RunningStatus.CANCELED


def test_get_res_run_async():
u = pcvl.Unitary(pcvl.Matrix.random_unitary(6)) # a random unitary matrix
bs = pcvl.BasicState("|1,0,1,0,1,0>") # basic state
proc = pcvl.Processor("SLOS", u) # a processor with a circuit formed of random unitary matrix
proc.with_input(bs) # setting up the input to the processor
job = pcvl.algorithm.Sampler(proc).sample_count # create a sampler job
job.execute_async(10000)
while not job.is_complete:
time.sleep(0.01)

res_1st_call = job.get_results()
res_2nd_call = job.get_results()

assert isinstance(res_1st_call["results"], pcvl.BSCount)
assert isinstance(res_2nd_call["results"], pcvl.BSCount)

assert res_1st_call["results"] == res_2nd_call["results"]
assert res_1st_call["physical_perf"] == res_2nd_call["physical_perf"]
assert res_1st_call["logical_perf"] == res_2nd_call["logical_perf"]



# ============ Remote jobs ============ #
from perceval.runtime import RemoteJob
from perceval.serialization import serialize
Expand Down
24 changes: 24 additions & 0 deletions tests/test_sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,30 @@ def test_sampler_iteration_bad_params():
sampler.add_iteration(input_state=pcvl.BasicState([1, 0, 0])) # input state too large


def test_sampler_clear_iterations():
c = BS() // PS(phi=pcvl.P("phi1")) // BS()
p = pcvl.Processor("SLOS", c)
sampler = Sampler(p)
iteration_list = [
{"circuit_params": {"phi1": 0.5}, "input_state": pcvl.BasicState([1, 1]), "min_detected_photons": 1},
{"circuit_params": {"phi1": 0.9}, "input_state": pcvl.BasicState([1, 1]), "min_detected_photons": 1},
{"circuit_params": {"phi1": 1.57}, "input_state": pcvl.BasicState([1, 0]), "min_detected_photons": 1}
]
assert sampler.n_iterations == 0

sampler.add_iteration_list(iteration_list)
assert sampler.n_iterations == len(iteration_list)

sampler.add_iteration_list(iteration_list)
assert sampler.n_iterations == len(iteration_list)*2

sampler.clear_iterations()
assert sampler.n_iterations == 0

sampler.add_iteration(circuit_params={"phi1": 0.1}, input_state=pcvl.BasicState([0, 1]))
assert sampler.n_iterations == 1


@pytest.mark.parametrize("backend_name", ["SLOS", "CliffordClifford2017"])
def test_sampler_iterator(backend_name):
c = BS() // PS(phi=pcvl.P("phi1")) // BS()
Expand Down
3 changes: 3 additions & 0 deletions tests/test_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,9 @@ def test_compress():
assert not d_not_compressed["circuit"].startswith(zip_prefix)
assert d_not_compressed["integer"] == 43 # JSon-compatible integral types are neither serialized nor compressed

# Compressing a circuit serialization gives a smaller representation
assert len(d_compressed["circuit"]) < len(d_not_compressed['circuit'])

d_only_circuit = serialize(d, compress=["ACircuit"]) # Compress only ACircuit objects
assert not d_only_circuit["input_state"].startswith(zip_prefix)
assert d_only_circuit["circuit"].startswith(zip_prefix)
Expand Down
15 changes: 15 additions & 0 deletions tests/test_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,15 @@ def test_simulator_probampli():
# prob_amplitude call is strict on annotations name
assert simulator.prob_amplitude(input_state, BasicState("|{_:0}{_:2},0>")) == pytest.approx(0)

input_state = StateVector("|{_:0},{_:1}>")
assert simulator.prob_amplitude(input_state, BasicState("|{_:0}{_:1},0>")) == pytest.approx(0.5j)
assert simulator.prob_amplitude(input_state, BasicState("|0,{_:0}{_:1}>")) == pytest.approx(0.5j)
assert simulator.prob_amplitude(input_state, BasicState("|{_:0},{_:1}>")) == pytest.approx(0.5)
assert simulator.prob_amplitude(input_state, BasicState("|{_:1},{_:0}>")) == pytest.approx(-0.5)
assert simulator.prob_amplitude(input_state, BasicState("|2,0>")) == pytest.approx(0)
assert simulator.prob_amplitude(input_state, BasicState("|1,1>")) == pytest.approx(0)
# prob_amplitude call is strict on annotations name
assert simulator.prob_amplitude(input_state, BasicState("|{_:0}{_:2},0>")) == pytest.approx(0)

def test_simulator_probability():
input_state = BasicState("|{_:0},{_:1}>")
Expand All @@ -202,6 +211,12 @@ def test_simulator_probability():
assert simulator.probability(input_state, BasicState("|0,2>")) == pytest.approx(0.5)
assert simulator.probability(input_state, BasicState("|1,1>")) == pytest.approx(0.0)

input_state = StateVector("|{_:0},{_:1}>")
assert simulator.probability(input_state, BasicState("|{_:0}{_:1},0>")) == pytest.approx(0.25)
assert simulator.probability(input_state, BasicState("|2,0>")) == pytest.approx(0.25)
assert simulator.probability(input_state, BasicState("|0,2>")) == pytest.approx(0.25)
assert simulator.probability(input_state, BasicState("|1,1>")) == pytest.approx(0.5)


def test_simulator_probs_sv():
st1 = StateVector("|0,1>")
Expand Down

0 comments on commit 18fd12f

Please sign in to comment.