Skip to content

Commit d957828

Browse files
committed
Merge remote-tracking branch 'origin/devel' into devel
2 parents 49a3dea + 383098b commit d957828

File tree

9 files changed

+58
-157
lines changed

9 files changed

+58
-157
lines changed

.github/workflows/python-unittest-ci.yml

Lines changed: 11 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -26,46 +26,16 @@ jobs:
2626
run: |
2727
python -m pip install --upgrade pip
2828
pip install pytest
29-
pip install mosek numba numpy scipy sympy tqdm
30-
# - name: Set up MOSEK license
31-
# env:
32-
# MOSEK_LICENSE_KEY: ${{ secrets.MOSEK_LICENSE_KEY }}
33-
# run: |
34-
# mkdir -p $HOME/mosek
35-
# echo "$MOSEK_LICENSE_KEY" > $HOME/mosek/mosek.lic
29+
pip install mosek numba networkx numpy scipy sympy tqdm
30+
- name: Set up MOSEK license
31+
env:
32+
MOSEK_LICENSE_KEY: ${{ secrets.MOSEK_LICENSE_KEY }}
33+
run: |
34+
mkdir -p $HOME/mosek
35+
echo "$MOSEK_LICENSE_KEY" > $HOME/mosek/mosek.lic
3636
- name: Test with pytest
3737
run: |
38-
pytest -v \
39-
--ignore=test/test_writers.py \
40-
--ignore=test/test_optimize.py \
41-
--deselect=test/test_solvers.py::TestSDP::test_LP_with_SDP \
42-
--deselect=test/test_solvers.py::TestSDP::test_SDP \
43-
--deselect=test/test_solvers.py::TestSDP::test_SDP_equalities \
44-
--deselect=test/test_solvers.py::TestSDP::test_SDP_inequalities \
45-
--deselect=test/test_solvers.py::TestLP::test_LP_free_bounds \
46-
--deselect=test/test_solvers.py::TestLP::test_LP_non_negative_bounds \
47-
--deselect=test/test_solvers.py::TestLP::test_LP_lower_bounds_of_zero \
48-
--deselect=test/test_solvers.py::TestLP::test_LP_free_bounds_of_zero_and_non_negative \
49-
--deselect=test/test_solvers.py::TestLP::test_LP_negative_lower_bounds \
50-
--deselect=test/test_solvers.py::TestLP::test_LP_negative_lower_bounds_and_non_negative \
51-
--deselect=test/test_solvers.py::TestSolverProcesses::test_semiknown_constraints \
52-
--deselect=test/test_solvers.py::TestSolverProcesses::test_partially_known_objective \
53-
--deselect=test/test_pipeline.py::TestSDPOutput::test_bounds \
54-
--deselect=test/test_pipeline.py::TestSDPOutput::test_CHSH \
55-
--deselect=test/test_pipeline.py::TestSDPOutput::test_GHZ_commuting \
56-
--deselect=test/test_pipeline.py::TestSDPOutput::test_GHZ_NC \
57-
--deselect=test/test_pipeline.py::TestSDPOutput::test_instrumental \
58-
--deselect=test/test_pipeline.py::TestSDPOutput::test_lpi \
59-
--deselect=test/test_pipeline.py::TestSDPOutput::test_supports \
60-
--deselect=test/test_pipeline.py::TestLPOutput::test_bounds \
61-
--deselect=test/test_pipeline.py::TestLPOutput::test_instrumental \
62-
--deselect=test/test_pipeline.py::TestLPOutput::test_supports \
63-
--deselect=test/test_pipeline.py::TestInstrumental::test_instrumental_fanout \
64-
--deselect=test/test_pipeline.py::TestInstrumental::test_instrumental_nonfanout \
65-
--deselect=test/test_pipeline.py::TestBell::test_bell_fanout \
66-
--deselect=test/test_pipeline.py::TestBell::test_bell_nonfanout \
67-
--deselect=test/test_pipeline.py::TestTriangle::test_triangle_fanout \
68-
--deselect=test/test_pipeline.py::TestTriangle::test_triangle_nonfanout \
69-
--deselect=test/test_pipeline.py::TestEvans::test_evans_fanout \
70-
--deselect=test/test_pipeline.py::TestEvans::test_evans_nonfanout \
71-
--deselect=test/test_pipeline.py::TestFullNN::test_fullnetworknonlocality_3partite_line
38+
pytest -v
39+
- name: Clean up MOSEK license file
40+
run: |
41+
rm $HOME/mosek/mosek.lic

docs/build.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ Functions to build problem elements
22
===================================
33
.. autofunction:: inflation.sdp.quantum_tools.calculate_momentmatrix_1d_internal
44
.. autofunction:: inflation.sdp.quantum_tools.construct_normalization_eqs
5-
.. autofunction:: inflation.sdp.quantum_tools.generate_operators
65

76
Auxiliary functions
87
-------------------

docs/examples.ipynb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@
546546
"sdp = InflationSDP(triangle)\n",
547547
"sdp.generate_relaxation(\"npa2\")\n",
548548
"\n",
549-
"mmnts = sdp.measurements\n",
549+
"mmnts = triangle.measurements_symbolic\n",
550550
"A0, B0, C0, A1, B1, C1 = (1 - 2*mmnts[party][0][setting][0]\n",
551551
" for setting in range(2)\n",
552552
" for party in range(3))\n",
@@ -612,7 +612,7 @@
612612
"\n",
613613
"sdp.generate_relaxation(list(npa2_union_local1))\n",
614614
"\n",
615-
"mmnts = sdp.measurements\n",
615+
"mmnts = triangle.measurements_symbolic\n",
616616
"A0, B0, C0, A1, B1, C1 = (1 - 2*mmnts[party][0][setting][0]\n",
617617
" for setting in range(2)\n",
618618
" for party in range(3))\n",
@@ -813,7 +813,7 @@
813813
"InfSDP.generate_relaxation(\n",
814814
" InfSDP.build_columns(\"local1\", max_monomial_length=3)\n",
815815
" )\n",
816-
"meas = InfSDP.measurements\n",
816+
"meas = InfProb.measurements_symbolic\n",
817817
"A0 = meas[0][0][0][0]\n",
818818
"C0 = meas[2][0][0][0]\n",
819819
"E0 = meas[3][0][0][0]\n",
@@ -938,7 +938,7 @@
938938
" classical_sources=\"all\")\n",
939939
"sdp = InflationSDP(bell)\n",
940940
"sdp.generate_relaxation(\"npa2\")\n",
941-
"meas = sdp.measurements\n",
941+
"meas = bell.measurements_symbolic\n",
942942
"\n",
943943
"def get_W_reduced(N):\n",
944944
" \"\"\"Generates the reduced four-body state for the N-partite\n",

inflation/InflationProblem.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,11 @@ def __init__(self,
382382
for o in O_vals.flat:
383383
measurements_per_party[i, s, o, -1] = o
384384
self.measurements.append(measurements_per_party)
385-
385+
386+
self.measurements_symbolic = \
387+
[np.apply_along_axis(self._1d_to_symbol, -1, measurements_per_party)
388+
for measurements_per_party in self.measurements]
389+
386390
# Useful for LP
387391
self._ortho_groups_per_party = []
388392
for p, measurements_per_party in enumerate(self.measurements):
@@ -606,15 +610,16 @@ def _lexrepr_to_all_names(self) -> np.ndarray:
606610
@cached_property
607611
def _lexrepr_to_symbols(self) -> np.ndarray:
608612
"""For each operator in the lexorder, create a sympy symbol with the
609-
same name as returned by InflationPRoblem._lexrepr_to_names()
613+
same name as returned by InflationProblem._lexrepr_to_names()
610614
611615
Returns
612616
-------
613617
list
614618
List of the same length as lexorder, where the i-th element is the
615619
string representation of the i-th operator in the lexorder.
616620
"""
617-
return np.array([Symbol(name, commutative=False) for name in self._lexrepr_to_names],
621+
return np.array([Symbol(name, commutative=False)
622+
for name in self._lexrepr_to_names],
618623
dtype=object)
619624

620625
@cached_property
@@ -631,6 +636,25 @@ def names_to_ints(self) -> dict:
631636
"""
632637
return {name: i + 1 for i, name in enumerate(self.names)}
633638

639+
def _1d_to_symbol(self, repr_1d: np.ndarray) -> Symbol:
640+
"""Convert a 1D representation of an operator to a sympy symbol.
641+
642+
Parameters
643+
----------
644+
repr_1d : np.ndarray
645+
1D representation of an operator.
646+
647+
Returns
648+
-------
649+
sympy.Symbol
650+
Sympy symbol representing the operator.
651+
"""
652+
name = self._interpretation_to_name(self._interpret_operator(repr_1d))
653+
if not self._any_inflation:
654+
# If there is no inflation, we can remove the copy indices
655+
name = name.split('^{')[0] + name.split('}')[1]
656+
return Symbol(name, commutative=False)
657+
634658
###########################################################################
635659
# FUNCTIONS PERTAINING TO KNOWABILITY #
636660
###########################################################################
@@ -705,8 +729,6 @@ def _interpretation_to_name_old(op: dict, replace_trivial_setting=True) -> str:
705729
op_as_str += "_" + str(op['Outcome'])
706730
return op_as_str
707731

708-
709-
710732
def _is_knowable_q_non_networks(self, monomial: np.ndarray) -> bool:
711733
"""Checks if a monomial (written as a sequence of operators in 2d array
712734
form) corresponds to a knowable probability. The function assumes that

inflation/sdp/InflationSDP.py

Lines changed: 3 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@
2727
from .quantum_tools import (apply_inflation_symmetries,
2828
calculate_momentmatrix_1d_internal,
2929
construct_normalization_eqs,
30-
flatten_symbolic_powers,
31-
generate_operators
30+
flatten_symbolic_powers
3231
)
3332
from .sdp_utils import solveSDP_MosekFUSION
3433
from .writer_utils import (write_to_csv,
@@ -103,11 +102,10 @@ def __init__(self,
103102
self.setting_cardinalities = inflationproblem.settings_per_party
104103
self._quantum_sources = inflationproblem._nonclassical_sources
105104

106-
self.measurements = self._generate_parties()
107105
if self.verbose > 1:
108106
eprint("Number of single operator measurements per party:", end="")
109107
prefix = " "
110-
for i, measures in enumerate(self.measurements):
108+
for i, measures in enumerate(self.InflationProblem.measurements_symbolic):
111109
counter = count()
112110
deque(zip(chain.from_iterable(
113111
chain.from_iterable(measures)),
@@ -2016,66 +2014,10 @@ def _discover_columns_symmetries(self) -> np.ndarray:
20162014
+ f" {permutations_failed} symmetries were not be implemented.")
20172015
return np.unique(discovered_symmetries, axis=0)[1:]
20182016

2019-
def _generate_parties(self) -> List[List[List[List[sp.Symbol]]]]:
2020-
"""Generates all the party operators in the quantum inflation.
2021-
2022-
Returns
2023-
-------
2024-
List[List[List[List[sympy.Symbol]]]]
2025-
The measurement operators as symbols. The array is indexed as
2026-
measurements[p][c][i][o] for party p, inflation copies c, input i,
2027-
and output o.
2028-
"""
2029-
settings = self.setting_cardinalities
2030-
outcomes = self.outcome_cardinalities
2031-
2032-
assert len(settings) == len(outcomes), \
2033-
"There\'s a different number of settings and outcomes"
2034-
assert len(settings) == self.hypergraph.shape[1], \
2035-
"The hypergraph does not have as many columns as parties"
2036-
measurements = []
2037-
parties = self.names
2038-
n_states = self.hypergraph.shape[0]
2039-
for pos, [party, ins, outs] in enumerate(zip(parties,
2040-
settings,
2041-
outcomes)):
2042-
party_meas = []
2043-
# Generate all possible copy indices for a party
2044-
all_inflation_indices = product(
2045-
*[list(range(self.inflation_levels[p_idx]))
2046-
for p_idx in np.flatnonzero(self.hypergraph[:, pos])])
2047-
# Include zeros in the positions of states not feeding the party
2048-
all_indices = []
2049-
for inflation_indices in all_inflation_indices:
2050-
indices = []
2051-
i = 0
2052-
for idx in range(n_states):
2053-
if self.hypergraph[idx, pos] == 0:
2054-
indices.append("0")
2055-
elif self.hypergraph[idx, pos] == 1:
2056-
# The +1 is just to begin at 1
2057-
indices.append(str(inflation_indices[i] + 1))
2058-
i += 1
2059-
else:
2060-
raise Exception("You don\'t have a proper hypergraph")
2061-
all_indices.append(indices)
2062-
# Generate measurements for every combination of indices.
2063-
# The -1 in outs - 1 is because the use of Collins-Gisin notation
2064-
# (see [arXiv:quant-ph/0306129]), whereby the last operator is
2065-
# understood to be written as the identity minus the rest.
2066-
for indices in all_indices:
2067-
meas = generate_operators(
2068-
[outs - 1 for _ in range(ins)],
2069-
party + "_" + "_".join(indices)
2070-
)
2071-
party_meas.append(meas)
2072-
measurements.append(party_meas)
2073-
return measurements
2074-
20752017
###########################################################################
20762018
# HELPER FUNCTIONS FOR ENSURING CONSISTENCY #
20772019
###########################################################################
2078-
2020+
20792021
def _cleanup_after_set_values(self) -> None:
20802022
"""Helper function to reset or make consistent class attributes after
20812023
setting values."""

inflation/sdp/monomial_classes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def __init__(self, inflation_sdp_instance, lexmon: np.ndarray):
4646
[3, 2, 0, 1, 4, 5]])
4747
4848
Given that this moment is knowable and can be associated with a
49-
probability, it is given the name ``"pAC(35|24)"``.
49+
probability, it is given the name ``"P[A_2=3 C_4=5]"``.
5050
5151
Parameters
5252
----------

inflation/sdp/quantum_tools.py

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -367,38 +367,6 @@ def expand_moment_normalisation(moment: np.ndarray,
367367
return eqs
368368

369369

370-
def generate_operators(outs_per_input: List[int],
371-
name: str
372-
) -> List[List[List[sympy.core.symbol.Symbol]]]:
373-
"""Generates the list of ``sympy.core.symbol.Symbol`` variables
374-
representing the measurements for a given party. The variables are treated
375-
as non-commuting. This code is adapted from `ncpol2sdpa
376-
<https://github.com/peterwittek/ncpol2sdpa/>`_.
377-
378-
Parameters
379-
----------
380-
outs_per_input : List[int]
381-
The number of outcomes of each measurement for a given party
382-
name : str
383-
The name to be associated to the party
384-
385-
Returns
386-
-------
387-
list
388-
The list of Sympy operators
389-
"""
390-
ops_per_input = []
391-
for x, outs in enumerate(outs_per_input):
392-
ops_per_output_per_input = []
393-
for o in range(outs):
394-
ops_per_output_per_input.append(
395-
sympy.Symbol(name + "_" + str(x) + "_" + str(o),
396-
commutative=False)
397-
)
398-
ops_per_input.append(ops_per_output_per_input)
399-
return ops_per_input
400-
401-
402370
def lexicographic_order(infSDP) -> Dict[str, int]:
403371
"""Return a user-friendly representation of the lexicographic order.
404372

test/test_functions.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ def test_sanitize(self):
2222
self.assertEqual(monom, sdp._sanitise_moment(monom),
2323
f"Sanitization of {monom} as a CompoundMonomial is " +
2424
f"giving {sdp._sanitise_moment(monom)}.")
25-
mon1 = sdp.measurements[0][0][0][0]
26-
mon2 = sdp.measurements[0][0][0][1]
27-
mon3 = sdp.measurements[0][0][1][0]
25+
mon1 = bellScenario.measurements_symbolic[0][0][0][0]
26+
mon2 = bellScenario.measurements_symbolic[0][0][0][1]
27+
mon3 = bellScenario.measurements_symbolic[0][0][1][0]
2828
# Tests for symbols and combinations
2929
mon = mon1
3030
truth = sdp.moments[2]
@@ -111,7 +111,7 @@ def _extra_equalities(self,
111111
extra_constraints: list,
112112
truth: int):
113113
self.assertEqual(len(problem.moment_equalities), truth,
114-
f"The number of implicit equalities is incorrect.")
114+
"The number of implicit equalities is incorrect.")
115115
with self.subTest("Test extra equalities"):
116116
problem.set_extra_equalities(extra_constraints)
117117
self.assertEqual(len(problem.moment_equalities), truth + 2,
@@ -127,7 +127,7 @@ def _extra_inequalities(self,
127127
extra_constraints: list,
128128
truth: int):
129129
self.assertEqual(len(problem.moment_inequalities), truth,
130-
f"The number of implicit inequalities is incorrect.")
130+
"The number of implicit inequalities is incorrect.")
131131
with self.subTest("Test extra inequalities"):
132132
problem.set_extra_inequalities(extra_constraints)
133133
self.assertEqual(len(problem.moment_inequalities), truth + 2,
@@ -167,7 +167,7 @@ def test_sdp(self):
167167
sdp = InflationSDP(self.bellScenario)
168168
sdp.generate_relaxation("npa1")
169169
compound_mon = sdp.moments[-1]
170-
sym_mon = sdp.measurements[0][0][0][0]
170+
sym_mon = self.bellScenario.measurements_symbolic[0][0][0][0]
171171
str_mon = "pA(0|0)"
172172
int_mon = 1
173173
sym_eq = Symbol("pA(0|0)") + 2 * Symbol("<A_1_0_0 A_1_1_0>")

test/test_pipeline.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class TestMonomialGeneration(unittest.TestCase):
4646
[0], [1], [2],
4747
[0, 0], [0, 1], [0, 2], [1, 1], [1, 2], [2, 2]]
4848
# Monomials for the NPA level 2 in the bilocality scenario
49-
meas = bilocalSDP.measurements
49+
meas = bilocality_c.measurements_symbolic
5050
A_1_0_0_0 = meas[0][0][0][0]
5151
A_2_0_0_0 = meas[0][1][0][0]
5252
B_1_1_0_0 = meas[1][0][0][0]
@@ -181,8 +181,8 @@ class TestReset(unittest.TestCase):
181181
del physical_bounds[sdp.One]
182182

183183
def prepare_objects(self, infSDP):
184-
var1 = infSDP.measurements[0][0][0][0]
185-
var2 = infSDP.measurements[0][0][1][0]
184+
var1 = trivial.measurements_symbolic[0][0][0][0]
185+
var2 = trivial.measurements_symbolic[0][0][1][0]
186186
infSDP.set_objective(var1, "max")
187187
infSDP.set_bounds({var1*var2: 0.9}, "up")
188188
infSDP.set_bounds({var1*var2: 0.1}, "lo")
@@ -349,7 +349,7 @@ def test_bounds(self):
349349
very_trivial = InflationProblem({"a": ["b"]}, outcomes_per_party=[2])
350350
sdp = InflationSDP(very_trivial)
351351
sdp.generate_relaxation("npa1")
352-
operator = np.asarray(sdp.measurements).flatten()[0]
352+
operator = np.asarray(very_trivial.measurements_symbolic).flatten()[0]
353353
sdp.set_objective(operator, "max")
354354
sdp.set_bounds({operator: ub}, "up")
355355
sdp.solve()
@@ -374,7 +374,7 @@ def test_CHSH(self):
374374
"The count of knowable moments is wrong.")
375375
self.assertEqual(sdp.n_unknowable, 2,
376376
"The count of unknowable moments is wrong.")
377-
meas = sdp.measurements
377+
meas = bellScenario.measurements_symbolic
378378
A0 = 2*meas[0][0][0][0] - 1
379379
A1 = 2*meas[0][0][1][0] - 1
380380
B0 = 2*meas[1][0][0][0] - 1
@@ -551,7 +551,7 @@ def test_instrumental(self):
551551

552552
def test_lpi(self):
553553
sdp = InflationSDP(trivial)
554-
[[[[A10], [A11]], [[A20], [A21]]]] = sdp.measurements
554+
[[[[A10,_],[A11,_]],[[A20,_],[A21,_]]]] = trivial.measurements_symbolic
555555
sdp.generate_relaxation([1,
556556
A10, A11, A20, A21,
557557
A10*A11, A10*A21, A11*A20, A20*A21])

0 commit comments

Comments
 (0)