Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
38aa292
fix compiler redundant-gate deletion indexes
wuzp15 Jul 22, 2026
39cc5b1
fix pure-state swap rewrite ordering
wuzp15 Jul 22, 2026
256a082
fix single-qubit matrix gate compilation
wuzp15 Jul 22, 2026
0be70f6
fix basic simulator controlled-y phase
wuzp15 Jul 22, 2026
91045ec
fix qiskit condition leakage
wuzp15 Jul 22, 2026
a17d4fc
fix complex state-vector norm validation
wuzp15 Jul 22, 2026
247649c
fix qaoa Hamiltonian coefficient scaling
wuzp15 Jul 22, 2026
90cf81d
fix optimizer endpoint propagation
wuzp15 Jul 22, 2026
075b827
fix factorless matrix gate inversion
wuzp15 Jul 22, 2026
b351cef
fix sat string expression parsing
wuzp15 Jul 22, 2026
372f291
fix cloud backend host configuration
wuzp15 Jul 22, 2026
58af751
fix cloud task lifecycle flags
wuzp15 Jul 22, 2026
4eaea23
fix cloud task reconstruction
wuzp15 Jul 22, 2026
345bb89
fix qasm probability measurement config
wuzp15 Jul 22, 2026
1103b14
fix rewritten gate operand ordering
wuzp15 Jul 22, 2026
ee8e5a2
disable unsafe pure-state optimization
wuzp15 Jul 22, 2026
768eab8
fix one-qubit amplitude amplification
wuzp15 Jul 22, 2026
409b1f6
disable unsafe constant-state optimization
wuzp15 Jul 22, 2026
f5471a5
disable failing two-qubit collapse
wuzp15 Jul 22, 2026
47522ba
fix identity gate optimization
wuzp15 Jul 22, 2026
f0fc69f
fix qasm register gate broadcasting
wuzp15 Jul 22, 2026
18082a7
preserve composite gate conditions
wuzp15 Jul 22, 2026
ae57bc3
reject conditional cloud circuits
wuzp15 Jul 22, 2026
7415c25
fix simulator count allocation
wuzp15 Jul 22, 2026
0cceeed
weight simulator random readings
wuzp15 Jul 22, 2026
87d44b6
fix mixed-pauli qaoa evolution
wuzp15 Jul 22, 2026
08ea71f
fix schmidt decomposition spectrum
wuzp15 Jul 22, 2026
12b6915
fix ground state eigenvalue selection
wuzp15 Jul 22, 2026
3855760
decompose multiqubit matrix gates
wuzp15 Jul 22, 2026
94047f0
reject mismatched qasm registers
wuzp15 Jul 22, 2026
033eaf1
support identity-only qaoa problems
wuzp15 Jul 22, 2026
61c0af3
serialize qasm definitions from live dag
wuzp15 Jul 22, 2026
de34595
preserve matrix gate relative phases
wuzp15 Jul 22, 2026
4c4c40a
preserve qasm gate parameter expressions
wuzp15 Jul 22, 2026
e7db1d2
avoid redefining qelib gates
wuzp15 Jul 22, 2026
82a7b07
fix controlled gate matrix blocks
wuzp15 Jul 22, 2026
2d16cb7
serialize optimized qasm gate parameters
wuzp15 Jul 22, 2026
4aeed58
handle numeric optimized callee parameters
wuzp15 Jul 22, 2026
761df29
fix bundled qelib controlled rx
wuzp15 Jul 22, 2026
3110186
handle numeric torch callee parameters
wuzp15 Jul 22, 2026
5f240ef
resolve torch callee parameter arity
wuzp15 Jul 22, 2026
4661a99
fix nested controlled y lowering
wuzp15 Jul 22, 2026
6a825f0
fix nested controlled x construction
wuzp15 Jul 22, 2026
d1bc795
lower controlled matrix gates before execution
wuzp15 Jul 22, 2026
b914d93
defer trainable controlled gate matrices
wuzp15 Jul 22, 2026
91de3de
differentiate composite gate parameters in adjoint
wuzp15 Jul 22, 2026
a7c675d
support gradients for every circuit argument
wuzp15 Jul 22, 2026
f5a2c00
fix torch dense hamiltonian conversion
wuzp15 Jul 22, 2026
89ff854
preserve torch pauli expectation batches
wuzp15 Jul 22, 2026
ab47d54
adapt list hamiltonians for adjoint gradients
wuzp15 Jul 22, 2026
d096572
initialize qaoa qubits for custom ansatz
wuzp15 Jul 22, 2026
ab603de
preserve phase in nested controlled gates
wuzp15 Jul 22, 2026
01eac85
isolate pauli measurements from gate definitions
wuzp15 Jul 22, 2026
27e0ea9
avoid deprecated torch probability shape conversion
wuzp15 Jul 22, 2026
bc038a5
shift decomposed controlled rotations
wuzp15 Jul 22, 2026
afb4350
align pauli terms with circuit qubit order
wuzp15 Jul 22, 2026
58a8447
initialize vqe depth with explicit parameters
wuzp15 Jul 22, 2026
d330083
preserve cloud gate operand order
wuzp15 Jul 22, 2026
28d0289
reject noncommuting default qaoa terms
wuzp15 Jul 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 23 additions & 29 deletions cppsrc/include/model/result.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

#include "result.h"
#include <algorithm>
int Result::repeat = 1024;

Result::Result(/* args */)
Expand All @@ -36,39 +37,32 @@ string Result::to_string(long key, size_t qnum)

map<string, int> Result::get_counts()
{
if (!counts.empty()) return counts;

vector<string> less;
if (!counts.empty() || probabilities.empty() || shots <= 0) return counts;

double probability_total = 0.0;
for (const auto& entry : probabilities) {
probability_total += max(0.0, entry.second);
}
if (probability_total == 0.0) return counts;

vector<pair<double, string>> remainders;
int sum = 0;
for (auto it = probabilities.begin(); it != probabilities.end(); ++it) {
double p = it->second;
double val = p * shots;
int cnt = (int)val;
double rval = round(val);
for (const auto& entry : probabilities) {
double expected = max(0.0, entry.second) * shots / probability_total;
int cnt = (int)floor(expected);
if (cnt > 0) {
counts[it->first] = cnt;
sum += cnt;
}
if(rval > val) less.push_back(it->first);
}
int total = shots - sum;
if (total > 0) {
for (size_t i = 0; i < less.size(); i++)
{
counts[less[i]] += 1;
total--;
if(total==0) break;
counts[entry.first] = cnt;
}
sum += cnt;
remainders.emplace_back(expected - cnt, entry.first);
}

while (total > 0) {
for (auto it = counts.begin(); it != counts.end(); ++it ) {
it->second += 1;
total--;
if(total==0) break;
}

sort(remainders.begin(), remainders.end(),
[](const auto& left, const auto& right) { return left.first > right.first; });
for (int i = 0; i < shots - sum; ++i) {
counts[remainders[i % remainders.size()].second] += 1;
}

return counts;
}

Expand All @@ -93,4 +87,4 @@ string Result::get_random_reading()
}

return probabilities.rbegin()->first;
}
}
10 changes: 9 additions & 1 deletion cppsrc/include/util/graph_attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,14 @@ static int exec_callable_vertex_attr(const igraph_t *graph,
long i,j;
for (i=0,j=0; i<length; i++) {
PyObject *fn = PyList_GetItem(functions, i);
if (!PyCallable_Check(fn)) {
PyObject *number = PyNumber_Float(fn);
if (number == NULL) return IGRAPH_EINVAL;
VECTOR(*value)[i] = PyFloat_AsDouble(number);
Py_DECREF(number);
continue;
}
if (!PyFunction_Check(fn)) return IGRAPH_EINVAL;
PyCodeObject *code = (PyCodeObject*)PyFunction_GET_CODE(fn);
int argcount = code->co_argcount;

Expand Down Expand Up @@ -418,4 +426,4 @@ static int topological_sorting_from_vertex(const igraph_t *graph,

igraph_vector_destroy(&dfs_res);
return 0;
}
}
7 changes: 4 additions & 3 deletions spinqit/algorithm/optimizer/scipy_optim.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ def callback_fn(x):
print('Optimize: step {}, loss: {},'.format(len(loss_list) + 1, loss))
loss_list.append(loss)

minimize(fn, x0=params[0],
method=self.method, options=self.options, callback=callback_fn,
tol=self.tol, **self.kwargs)
result = minimize(fn, x0=params[0],
method=self.method, options=self.options, callback=callback_fn,
tol=self.tol, **self.kwargs)
params[0][...] = result.x
return loss_list
11 changes: 6 additions & 5 deletions spinqit/algorithm/optimizer/spsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ def callback_fn(x):
print('Optimize: step {}, loss: {},'.format(len(loss_list) + 1, loss))
loss_list.append(loss)

minimizeSPSA(qlayer, *params,
niter=self.niter,
paired=False,
callback=callback_fn,
c=self.c, a=self.a, **self.kwargs)
result = minimizeSPSA(qlayer, *params,
niter=self.niter,
paired=False,
callback=callback_fn,
c=self.c, a=self.a, **self.kwargs)
params[0][...] = result.x
return loss_list
75 changes: 51 additions & 24 deletions spinqit/algorithm/qaoa.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
from scipy import sparse

from spinqit import Parameter
from spinqit.model import Circuit, H, Rx, GateBuilder
from spinqit.model.Ising_gate import Z_IsingGateBuilder, X_IsingGateBuilder, Y_IsingGateBuilder
from spinqit.model import Circuit, H, I, Rx, GateBuilder
from spinqit.model.Ising_gate import Z_IsingGateBuilder
from spinqit.primitive.pauli_expectation import pauli_decompose
from spinqit.compiler import compiler
from spinqit.interface.qlayer import QLayer
Expand All @@ -39,17 +39,23 @@ def __init__(
self.qlayer = None
self.optimizer = optimizer

if isinstance(problem, list):
self.__qubit_num = len(problem[0][0])
problem_terms = problem
elif isinstance(problem, sparse.csr_matrix):
self.__qubit_num = int(np.log2(problem.get_shape()[0]))
problem_terms = pauli_decompose(problem.toarray())
else:
raise ValueError(
f'The `problem` should be `list` or `sparse.csr_matrix`, but got {type(problem)} '
)

if problem_ansatz is None:
if isinstance(problem, list):
self.__qubit_num = len(problem[0][0])
problem_ansatz = self._generate_problem_circuit(problem)
elif isinstance(problem, sparse.csr_matrix):
self.__qubit_num = int(np.log2(problem.get_shape()[0]))
problem_ansatz = self._generate_problem_circuit(pauli_decompose(problem.toarray()))
else:
raise ValueError(
f'The `problem` should be `list` or `sparse.csr_matrix`, but got {type(problem)} '
)
problem_ansatz = self._generate_problem_circuit(problem_terms)
elif problem_ansatz.qubit_num != self.__qubit_num:
raise ValueError(
'The custom problem ansatz must act on the same number of qubits as the problem.'
)
self.hamiltonian = problem
self.__depth = depth
self.params = Parameter(np.random.uniform(0, 2 * np.pi, (self.__depth*2,)))
Expand All @@ -65,19 +71,40 @@ def _generate_problem_circuit(self, ham) -> Gate:
raise ValueError(
'The problem hamiltonian should be given in list in __init__().'
)
for i, (left, _) in enumerate(ham):
for right, _ in ham[i + 1:]:
anticommutes = sum(
l != 'I' and r != 'I' and l != r
for l, r in zip(left.upper(), right.upper())
)
if anticommutes % 2:
raise ValueError(
'The default QAOA problem ansatz requires commuting Pauli terms; '
'provide a custom problem_ansatz for noncommuting terms.'
)
for i in range(len(ham)):
if 'Z' in ham[i][0]:
qubits = [idx for idx in range(len(ham[i][0])) if ham[i][0][idx] == 'Z']
rzz = Z_IsingGateBuilder(len(qubits)).to_gate()
builder.append(rzz, qubits, lambda x: x)
elif 'X' in ham[i][0]:
qubits = [idx for idx in range(len(ham[i][0])) if ham[i][0][idx] == 'X']
rxx = X_IsingGateBuilder(len(qubits)).to_gate()
builder.append(rxx, qubits, lambda x: x)
elif 'Y' in ham[i][0]:
qubits = [idx for idx in range(len(ham[i][0])) if ham[i][0][idx] == 'Y']
ryy = Y_IsingGateBuilder(len(qubits)).to_gate()
builder.append(ryy, qubits, lambda x: x)
pauli_string = ham[i][0].upper()
coefficient = ham[i][1]
rotation_params = lambda x, coefficient=coefficient: [
coefficient * value for value in x
]
qubits = [idx for idx, pauli in enumerate(pauli_string) if pauli != 'I']
for idx, pauli in enumerate(pauli_string):
if pauli == 'X':
builder.append(H, [idx])
elif pauli == 'Y':
builder.append(Rx, [idx], np.pi / 2)
elif pauli not in ('I', 'Z'):
raise ValueError(f'Invalid Pauli string `{pauli_string}`.')
if qubits:
builder.append(Z_IsingGateBuilder(len(qubits)).to_gate(), qubits, rotation_params)
for idx, pauli in enumerate(pauli_string):
if pauli == 'X':
builder.append(H, [idx])
elif pauli == 'Y':
builder.append(Rx, [idx], -np.pi / 2)
if builder.size() == 0:
builder.append(I, [0])
return builder.to_gate()

def _generate_mixer_circuit(self) -> Gate:
Expand Down
2 changes: 1 addition & 1 deletion spinqit/algorithm/vqe.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ def __init__(self,
self.optimizer = optimizer
self.fn = None
self.hamiltonian = hamiltonian
self.__depth = depth
if params is not None:
if isinstance(params, tuple):
params = np.random.uniform(0, 2 * np.pi, params)
self.params = Parameter(params, trainable=True)
else:
self.__depth = depth
self.params = Parameter(np.random.uniform(0, 2 * np.pi, (depth, self.__qubit_num, 3)),
trainable=True)
if ansatz is not None:
Expand Down
3 changes: 2 additions & 1 deletion spinqit/backend/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ def check_backend_and_config(backend_mode, **kwargs):
from .spinq_cloud_backend import SpinQCloudConfig
username = kwargs.get('username', None)
keyfile = kwargs.get('keyfile', None)
backend = SpinQCloudBackend(username, keyfile)
host = kwargs.get('host', 'http://cloud.spinq.cn:6060')
backend = SpinQCloudBackend(username, keyfile, host)
config = SpinQCloudConfig()
platform = kwargs.get('platform', 'triangulum_vp')
shots = kwargs.get('shots', 1024)
Expand Down
18 changes: 16 additions & 2 deletions spinqit/backend/backend_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,22 @@ def _add_pauli_gate(gate, qubits, ir):
idx = idx if idx is not False else ir.dag.vcount()
node_idx_list = []
if idx == ir.dag.vcount():
seq = ir.dag.vs.select(lambda x: len(x.out_edges()) != len(x.in_edges()) and x['type'] != 4)
execution_nodes = set()
for register in ir.dag.vs.select(type=NodeType.register.value):
execution_nodes.update(
ir.dag.subcomponent(register.index, mode='out')
)
quantum_node_types = {
NodeType.op.value,
NodeType.caller.value,
NodeType.init_qubit.value,
NodeType.unitary.value,
}
seq = ir.dag.vs.select(
lambda x: x.index in execution_nodes
and x['type'] in quantum_node_types
and len(x.out_edges()) != len(x.in_edges())
)
node_map = {}
for v in seq:
if v['qubits'] is None:
Expand All @@ -90,4 +105,3 @@ def _add_pauli_gate(gate, qubits, ir):
node_idx_list = ir.substitute_nodes([idx], ilist, 0)
ir.remove_nodes([idx])
return node_idx_list

16 changes: 11 additions & 5 deletions spinqit/backend/basic_simulator_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,15 @@ def assemble(self, ir: IntermediateRepresentation):
elif v['name'] == CX.label:
v['name'] = 'CNOT'
elif v['name'] == CY.label:
v['name'] = 'YCON'
qubits, clbits = self.__qubits_and_clbits(v)
subgates = [
Instruction(Sd, [qubits[1]], clbits),
Instruction(CX, qubits, clbits),
Instruction(S, [qubits[1]], clbits),
]
ir.substitute_nodes([v.index], subgates, v['type'])
ir.remove_nodes([v.index], False)
i -= 1
elif v['name'] == CZ.label:
v['name'] = 'ZCON'
elif v['name'] == CCX.label:
Expand All @@ -98,12 +106,10 @@ def assemble(self, ir: IntermediateRepresentation):
def __qubits_and_clbits(v):
edges = v.in_edges()
edges.sort(key=lambda k: k.index)
qubits = []
qubits = list(v['qubits'])
clbits = []
for e in edges:
if 'qubit' in e.attributes() and e['qubit'] is not None:
qubits.append(e['qubit'])
elif 'clbit' in e.attributes() and e['clbit'] is not None:
if 'clbit' in e.attributes() and e['clbit'] is not None:
clbits.append(e['clbit'])
return qubits, clbits

Expand Down
Loading