Skip to content

Commit 58c3031

Browse files
committed
Merge branch 'develop' into feature-parity-benchmarking
2 parents e546db3 + 372ac37 commit 58c3031

38 files changed

+799
-534
lines changed

.github/workflows/beta-master.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ jobs:
1313
strategy:
1414
fail-fast: false
1515
matrix:
16-
os: [macos-13, ubuntu-latest, windows-latest] # TODO: Unpin mac version when cvxopt or Python 3.8 dropped
17-
python-version: [3.8, 3.9, '3.10', '3.11']
16+
os: [macos-latest, ubuntu-latest, windows-latest]
17+
python-version: [3.9, '3.10', '3.11', '3.12']
1818
use-cython: ['true', 'false']
1919
uses: ./.github/workflows/reuseable-main.yml
2020
name: Run pyGSTi tests

.github/workflows/develop.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
fail-fast: false
1616
matrix:
1717
os: [ubuntu-latest, windows-latest] # No Mac
18-
python-version: [3.8, 3.9, '3.10', '3.11']
18+
python-version: [3.9, '3.10', '3.11', '3.12']
1919
use-cython: ['true', 'false']
2020
uses: ./.github/workflows/reuseable-main.yml
2121
name: Run pyGSTi tests

.github/workflows/feature-branches.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ jobs:
1818
strategy:
1919
# fail-fast: true is OK here
2020
matrix:
21-
os: [ubuntu-latest, windows-latest] # No Mac
22-
python-version: [3.8, '3.11'] # Only extremal Python versions
21+
os: [ubuntu-latest, windows-latest]
22+
python-version: [3.9, '3.12'] # Only extremal Python versions
2323
uses: ./.github/workflows/reuseable-main.yml
2424
name: Run pyGSTi tests
2525
with:
@@ -28,7 +28,7 @@ jobs:
2828
use-cython: 'true' # Only test environment with Cython
2929
run-unit-tests: 'true'
3030
run-extra-tests: 'false' # No integration tests
31-
run-notebook-tests: 'false' # No notebook tests
31+
run-notebook-tests: 'false' # No notebook tests
3232

3333

3434

.github/workflows/reuseable-main.yml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,19 @@ jobs:
3434
runs-on: ${{ inputs.os }}
3535
steps:
3636
- uses: actions/checkout@v4
37-
- name: Set up installation environment (Linux and Windows)
38-
if: ${{ inputs.os != 'macos-13' }}
37+
- name: Set up installation environment (Linux)
38+
if: ${{ inputs.os == 'ubuntu-latest' }}
3939
run: |
4040
./.github/ci-scripts/before_install.sh
4141
- name: Set up installation environment (MacOS)
42-
if: ${{ inputs.os == 'macos-13' }}
42+
if: ${{ inputs.os == 'macos-latest' }}
4343
run: |
4444
./.github/ci-scripts/before_install_macos.sh
45+
- name: Set up MPI (Windows)
46+
if: ${{ inputs.os == 'windows-latest' }}
47+
uses: mpi4py/setup-mpi@v1
48+
with:
49+
mpi: intelmpi
4550
- name: Set up Python ${{ inputs.python-version }}
4651
uses: actions/setup-python@v5
4752
with:

MANIFEST.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
include README.md CONTRIBUTING.md LICENSE
2-
include pyproject.toml pyproject.toml.no_cython
2+
include pyproject.toml
33
include install_locally.py
44
include requirements.txt
55
include optional-requirements.txt

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ In particular, there are a number of characterization protocols currently implem
3636

3737
PyGSTi is designed with a modular structure so as to be highly customizable
3838
and easily integrated to new or existing python software. It runs using
39-
python 3.8 or higher. To faclilitate integration with software for running
39+
python 3.9 or higher. To faclilitate integration with software for running
4040
cloud-QIP experiments, pyGSTi `Circuit` objects can be converted to IBM's
4141
**OpenQASM** and Rigetti Quantum Computing's **Quil** circuit description languages.
4242

pygsti/algorithms/fiducialpairreduction.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,20 @@
1919
import scipy.linalg as _sla
2020

2121
from math import ceil
22-
import time
2322

2423
from pygsti import baseobjs as _baseobjs
2524
from pygsti import circuits as _circuits
2625

2726
from pygsti.circuits import circuitconstruction as _gsc
2827
from pygsti.modelmembers.operations import EigenvalueParamDenseOp as _EigenvalueParamDenseOp
29-
from pygsti.tools import apply_aliases_to_circuits as _apply_aliases_to_circuits
3028
from pygsti.tools import remove_duplicates as _remove_duplicates
3129
from pygsti.tools import slicetools as _slct
3230
from pygsti.tools.legacytools import deprecate as _deprecated_fn
31+
from pygsti.forwardsims import MatrixForwardSimulator as _MatrixForwardSimulator
3332

34-
from pygsti.algorithms.germselection import construct_update_cache, minamide_style_inverse_trace, compact_EVD, compact_EVD_via_SVD, germ_set_spanning_vectors
33+
from pygsti.algorithms.germselection import construct_update_cache, minamide_style_inverse_trace, compact_EVD, germ_set_spanning_vectors
3534
from pygsti.algorithms import scoring as _scoring
3635

37-
from pygsti.tools.matrixtools import print_mx
3836

3937
import warnings
4038

@@ -1658,6 +1656,10 @@ def find_sufficient_fiducial_pairs_per_germ_global(target_model, prep_fiducials,
16581656
`prep_fiducials` and `meas_fiducials`).
16591657
"""
16601658

1659+
if not isinstance(target_model.sim, _MatrixForwardSimulator):
1660+
target_model = target_model.copy()
1661+
target_model.sim = 'matrix'
1662+
16611663
printer = _baseobjs.VerbosityPrinter.create_printer(verbosity)
16621664

16631665
#if no germ_vector_spanning_set is passed in compute it here.
@@ -1674,7 +1676,7 @@ def find_sufficient_fiducial_pairs_per_germ_global(target_model, prep_fiducials,
16741676
if germ_set_spanning_kwargs is not None:
16751677
used_kwargs.update(germ_set_spanning_kwargs)
16761678

1677-
germ_vector_spanning_set = germ_set_spanning_vectors(target_model, germs,
1679+
germ_vector_spanning_set, _ = germ_set_spanning_vectors(target_model, germs,
16781680
float_type=float_type,
16791681
evd_tol = evd_tol,
16801682
verbosity=verbosity,

pygsti/algorithms/germselection.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,28 @@ def find_germs(target_model, randomize=True, randomization_strength=1e-2,
406406
raise ValueError("'{}' is not a valid algorithm "
407407
"identifier.".format(algorithm))
408408

409-
return germList
409+
#force the line labels on each circuit to match the state space labels for the target model.
410+
#this is suboptimal for many-qubit models, so will probably want to revisit this. #TODO
411+
def fix_line_labels(germListToFix):
412+
fixedGermList = []
413+
if germListToFix is not None:
414+
for ckt_or_list in germListToFix:
415+
if isinstance(ckt_or_list, _circuits.Circuit) and ckt_or_list._static:
416+
new_ckt = ckt_or_list.copy(editable=True)
417+
new_ckt.line_labels = target_model.state_space.state_space_labels
418+
new_ckt.done_editing()
419+
fixedGermList.append(new_ckt)
420+
elif isinstance(ckt_or_list, _circuits.Circuit):
421+
ckt_or_list.line_labels = target_model.state_space.state_space_labels
422+
fixedGermList.append(ckt_or_list)
423+
else:
424+
# This is probably a list of circuits from GRASP w/ return_all = True
425+
# Call this function recursively
426+
fixedGermList.append(fix_line_labels(ckt_or_list))
427+
return fixedGermList
428+
finalGermList = fix_line_labels(germList)
429+
430+
return finalGermList
410431

411432

412433
def compute_germ_set_score(germs, target_model=None, neighborhood=None,
@@ -4519,6 +4540,10 @@ def germ_set_spanning_vectors(target_model, germ_list, assume_real=False, float_
45194540
amplificational properties of the reduced vector set.
45204541
"""
45214542
printer = _baseobjs.VerbosityPrinter.create_printer(verbosity)
4543+
4544+
if not isinstance(target_model.sim, _MatrixForwardSimulator):
4545+
target_model = target_model.copy()
4546+
target_model.sim = 'matrix'
45224547

45234548
#Add some checks related to the option to switch up data types:
45244549
if not assume_real:

pygsti/algorithms/randomcircuit.py

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,7 @@ def create_random_circuit(pspec, length, qubit_labels=None, sampler='Qeliminatio
769769
layer = sample_circuit_layer_of_one_q_gates(pspec, qubit_labels, *lsargs, rand_state=rand_state)
770770
# For even layers, we sample according to the given distribution
771771
else:
772-
layer = sampler(pspec, qubit_labels, *samplerargs)
772+
layer = sampler(pspec, qubit_labels, *samplerargs, rand_state=rand_state)
773773
circuit.insert_layer_inplace(layer, 0)
774774

775775
circuit.done_editing()
@@ -1990,7 +1990,7 @@ def create_random_germpower_mirror_circuits(pspec, absolute_compilation, depths,
19901990
return circlist, outlist, aux
19911991

19921992
###begin BiRB tools###
1993-
def _stabilizer_to_all_zs(stabilizer, qubit_labels, absolute_compilation):
1993+
def _stabilizer_to_all_zs(stabilizer, qubit_labels, absolute_compilation, seed=None):
19941994
#inputs:
19951995
# - stabilizer: An n-qubit Pauli, represented as a string of X, Y, Z, and Is.
19961996
# - qubit_labels: list/tuple of qubit labels
@@ -1999,6 +1999,8 @@ def _stabilizer_to_all_zs(stabilizer, qubit_labels, absolute_compilation):
19991999
# into a Pauli with only Zs and Is.
20002000
# - s, layer, p_layer: Symplectic representation of the layer
20012001
# - stab_circuit: Layer as a compiled circuit
2002+
rng = _np.random.default_rng(seed)
2003+
20022004
n = len(stabilizer)
20032005

20042006
symp_reps = _symp.compute_internal_gate_symplectic_representations()
@@ -2018,7 +2020,7 @@ def _stabilizer_to_all_zs(stabilizer, qubit_labels, absolute_compilation):
20182020
stab_layer.append((s_h, p_h))
20192021
c.append(_lbl.Label('C12','{}'.format(qubit_labels[i])))
20202022
elif stabilizer[i] == 'I':
2021-
rand_clifford = str(_np.random.choice(_np.arange(24)))
2023+
rand_clifford = str(rng.choice(_np.arange(24)))
20222024
s_rand, p_rand = symp_reps['C'+rand_clifford]
20232025
stab_layer.append((s_rand, p_rand))
20242026
c.append(_lbl.Label('C'+rand_clifford,'{}'.format(qubit_labels[i])))
@@ -2064,7 +2066,7 @@ def _symplectic_to_pauli(s,p):
20642066

20652067
return pauli
20662068

2067-
def _sample_random_pauli(n,pspec = None, absolute_compilation = None, qubit_labels = None, circuit = False, include_identity = False):
2069+
def _sample_random_pauli(n,pspec = None, absolute_compilation = None, qubit_labels = None, circuit = False, include_identity = False, seed=None):
20682070
# Samples a random Pauli along with a +-1 phase. Returns the Pauli as a list or as a circuit depending
20692071
# upon the value of "circuit"
20702072
# - n: Number of qubits
@@ -2077,18 +2079,20 @@ def _sample_random_pauli(n,pspec = None, absolute_compilation = None, qubit_labe
20772079
if qubit_labels is not None: qubits = qubit_labels[:] # copy this list
20782080
else: qubits = pspec.qubit_labels[:]
20792081

2082+
rng = _np.random.default_rng(seed)
2083+
20802084
pauli_list = ['I','X','Y','Z']
20812085

20822086
if include_identity is False:
20832087
while True:
2084-
rand_ints = _np.random.randint(0,4,n)
2088+
rand_ints = rng.integers(0, 4, n)
20852089
if sum(rand_ints) != 0: # make sure we don't get all identities
20862090
break
20872091
else:
2088-
rand_ints = _np.random.randint(0, 4, n)
2092+
rand_ints = rng.integers(0, 4, n)
20892093

20902094
pauli = [pauli_list[i] for i in rand_ints]
2091-
if set(pauli) != set('I'): sign = _np.random.choice([-1,1])
2095+
if set(pauli) != set('I'): sign = rng.choice([-1,1])
20922096
else: sign = 1
20932097

20942098
if circuit is False:
@@ -2106,11 +2110,13 @@ def _sample_random_pauli(n,pspec = None, absolute_compilation = None, qubit_labe
21062110
return pauli, sign, pauli_circuit
21072111

21082112

2109-
def _select_neg_evecs(pauli, sign):
2113+
def _select_neg_evecs(pauli, sign, seed=None):
21102114
# Selects the entries in an n-qubit that will be turned be given a -1 1Q eigenstates
21112115
# - pauli: The n-qubit Pauli
21122116
# - sign: Whether you want a -1 or +1 eigenvector
21132117
# Returns: A bitstring whose 0/1 entries specify if you have a +1 or -1 1Q eigenstate
2118+
rng = _np.random.default_rng(seed)
2119+
21142120
n = len(pauli)
21152121
identity_bitstring = [0 if i == 'I' else 1 for i in pauli]
21162122
nonzero_indices = _np.nonzero(identity_bitstring)[0]
@@ -2125,10 +2131,10 @@ def _select_neg_evecs(pauli, sign):
21252131
choices = _np.arange(start = 0, stop = num_nid, step = 2)
21262132
else:
21272133
choices = _np.arange(start = 1, stop = num_nid+1, step = 2)
2128-
num_neg_evecs = _np.random.choice(choices)
2134+
num_neg_evecs = rng.choice(choices)
21292135
assert((-1)**num_neg_evecs == sign)
21302136

2131-
neg_evecs = _np.random.choice(nonzero_indices, num_neg_evecs, replace = False)
2137+
neg_evecs = rng.choice(nonzero_indices, num_neg_evecs, replace = False)
21322138

21332139
bit_evecs = _np.zeros(n)
21342140
bit_evecs[neg_evecs] = 1
@@ -2160,7 +2166,7 @@ def _compose_initial_cliffords(prep_circuit):
21602166
composed_layer.append(new_gate)
21612167
return composed_layer
21622168

2163-
def _sample_stabilizer(pauli, sign, absolute_compilation, qubit_labels):
2169+
def _sample_stabilizer(pauli, sign, absolute_compilation, qubit_labels, seed=None):
21642170
# Samples a random stabilizer of a Pauli, s = s_1 \otimes ... \otimes s_n. For each s_i,
21652171
# we perform the following gates:
21662172
# - s_i = X: H
@@ -2176,10 +2182,10 @@ def _sample_stabilizer(pauli, sign, absolute_compilation, qubit_labels):
21762182
# preparation circuit, and a pygsti circuit representation of the prep circuit
21772183

21782184

2179-
2185+
rng = _np.random.default_rng(seed)
21802186

21812187
n = len(pauli)
2182-
neg_evecs = _select_neg_evecs(pauli, sign)
2188+
neg_evecs = _select_neg_evecs(pauli, sign, seed=seed)
21832189
assert((-1)**sum(neg_evecs) == sign)
21842190
zvals = [0 if neg_evecs[i] == 0 else -1 for i in range(n)]
21852191

@@ -2199,7 +2205,7 @@ def _sample_stabilizer(pauli, sign, absolute_compilation, qubit_labels):
21992205
'Z': 'C0'}
22002206

22012207
x_layer = [symp_reps['I'] if zvals[i] == 0 else symp_reps['X'] for i in range(len(zvals))]
2202-
circ_layer = [circ_dict[i] if i in circ_dict.keys() else 'C'+str(_np.random.randint(24)) for i in pauli]
2208+
circ_layer = [circ_dict[i] if i in circ_dict.keys() else 'C'+str(rng.integers(24)) for i in pauli]
22032209

22042210
init_layer = [symp_reps[circ_layer[i]] for i in range(len(pauli))]
22052211

@@ -2257,8 +2263,7 @@ def _determine_sign(s_state, p_state, measurement):
22572263

22582264

22592265
def create_binary_rb_circuit(pspec, clifford_compilations, length, qubit_labels=None, layer_sampling = 'mixed1q2q', sampler='Qelimination',
2260-
samplerargs=None, addlocal=False, lsargs=None,
2261-
seed=None):
2266+
samplerargs=None, addlocal=False, lsargs=None, seed=None):
22622267
"""
22632268
Generates a "binary randomized benchmarking" (BiRB) circuit.
22642269
@@ -2343,19 +2348,21 @@ def create_binary_rb_circuit(pspec, clifford_compilations, length, qubit_labels=
23432348

23442349

23452350
rand_pauli, rand_sign, pauli_circuit = _sample_random_pauli(n = n, pspec = pspec, qubit_labels=qubit_labels,
2346-
absolute_compilation = clifford_compilations,
2347-
circuit = True, include_identity = False)
2351+
absolute_compilation = clifford_compilations,
2352+
circuit = True, include_identity = False, seed=seed+42)
23482353

2349-
s_inputstate, p_inputstate, s_init_layer, p_init_layer, prep_circuit = _sample_stabilizer(rand_pauli, rand_sign, clifford_compilations, qubit_labels)
2354+
s_inputstate, p_inputstate, s_init_layer, p_init_layer, prep_circuit = _sample_stabilizer(rand_pauli, rand_sign, clifford_compilations,
2355+
qubit_labels, seed=seed+43)
23502356

2351-
s_pc, p_pc = _symp.symplectic_rep_of_clifford_circuit(pauli_circuit, pspec=pspec.subset(gate_names_to_include='all', qubit_labels_to_keep=qubit_labels)) #note: if the pspec contains gates not in pyGSTi, this
2357+
s_pc, p_pc = _symp.symplectic_rep_of_clifford_circuit(pauli_circuit, pspec=pspec.subset(gate_names_to_include='all',
2358+
qubit_labels_to_keep=qubit_labels)) #note: if the pspec contains gates not in pyGSTi, this
23522359

23532360
# build the initial layer of the circuit
23542361
full_circuit = prep_circuit.copy(editable=True)
23552362

23562363
# Sample a random circuit of "native gates".
23572364
if layer_sampling == 'alternating1q2q':
2358-
circuit = random_alternating_clifford_circ(pspec, length, qubit_labels=qubit_labels, two_q_gate_density=samplerargs[0])
2365+
circuit = random_alternating_clifford_circ(pspec, length, qubit_labels=qubit_labels, two_q_gate_density=samplerargs[0], rand_state=rand_state)
23592366
elif layer_sampling == 'mixed1q2q':
23602367
circuit = create_random_circuit(pspec=pspec, length=length, qubit_labels=qubit_labels, sampler=sampler,
23612368
samplerargs=samplerargs, addlocal=addlocal, lsargs=lsargs, rand_state=rand_state)
@@ -2386,7 +2393,7 @@ def create_binary_rb_circuit(pspec, clifford_compilations, length, qubit_labels=
23862393

23872394
# Turn the stabilizer into an all Z and I stabilizer. Append this to the circuit.
23882395

2389-
s_stab, p_stab, stab_circuit = _stabilizer_to_all_zs(pauli, qubit_labels, clifford_compilations)
2396+
s_stab, p_stab, stab_circuit = _stabilizer_to_all_zs(pauli, qubit_labels, clifford_compilations, seed=seed+404)
23902397

23912398
full_circuit.append_circuit_inplace(stab_circuit)
23922399

@@ -2405,7 +2412,7 @@ def create_binary_rb_circuit(pspec, clifford_compilations, length, qubit_labels=
24052412

24062413
return outcircuit, measurement, sign
24072414

2408-
def random_alternating_clifford_circ(pspec, depth, qubit_labels=None, two_q_gate_density=0.25):
2415+
def random_alternating_clifford_circ(pspec, depth, qubit_labels=None, two_q_gate_density=0.25, rand_state=None):
24092416
"""
24102417
Generates a random circuit with composite layers cponsisting of a layer of two-qubit gates followed by
24112418
a layer of of single-qubit gates.
@@ -2443,8 +2450,9 @@ def random_alternating_clifford_circ(pspec, depth, qubit_labels=None, two_q_gate
24432450
#sample # benchmarking layers = depth
24442451
circ = _cir.Circuit(layer_labels=[], line_labels=qubit_labels, editable=True)
24452452
for _ in range(depth):
2446-
oneQ_layer = _cir.Circuit([sample_circuit_layer_of_one_q_gates(pspec, qubit_labels = qubit_labels)]).parallelize()
2447-
twoQ_layer = _cir.Circuit(sample_circuit_layer_by_edgegrab(pspec, qubit_labels=qubit_labels, two_q_gate_density=two_q_gate_density)).parallelize()
2453+
oneQ_layer = _cir.Circuit([sample_circuit_layer_of_one_q_gates(pspec, qubit_labels = qubit_labels, rand_state=rand_state)]).parallelize()
2454+
twoQ_layer = _cir.Circuit(sample_circuit_layer_by_edgegrab(pspec, qubit_labels=qubit_labels, two_q_gate_density=two_q_gate_density,
2455+
rand_state=rand_state)).parallelize()
24482456
circ.append_circuit_inplace(twoQ_layer)
24492457
circ.append_circuit_inplace(oneQ_layer)
24502458
circ.done_editing()

pygsti/baseobjs/label.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,13 @@ def __new__(cls, name, state_space_labels=None, time=None, args=None):
128128

129129
#print(" -> preproc with name=", name, "sslbls=", state_space_labels, "t=", time, "args=", args)
130130
if state_space_labels is None or state_space_labels in ((), (None,)):
131-
if args:
131+
if args is not None:
132132
return LabelTupWithArgs.init(name, (), time, args) # just use empty sslbls
133133
else:
134134
return LabelStr.init(name, time)
135135

136136
else:
137-
if args: return LabelTupWithArgs.init(name, state_space_labels, time, args)
137+
if args is not None: return LabelTupWithArgs.init(name, state_space_labels, time, args)
138138
else:
139139
if time == 0.0:
140140
return LabelTup.init(name, state_space_labels)

0 commit comments

Comments
 (0)