qiskit-community / qiskit-algorithms Goto Github PK
View Code? Open in Web Editor NEWA library of quantum algorithms for Qiskit.
Home Page: https://qiskit-community.github.io/qiskit-algorithms/
License: Apache License 2.0
A library of quantum algorithms for Qiskit.
Home Page: https://qiskit-community.github.io/qiskit-algorithms/
License: Apache License 2.0
Currently, the NumpyEigensolver implements a _eval_aux_operators
which evaluates the auxiliary operators on the excited states.
Without much effort and using an equality in the form of Eq. 13 in https://doi.org/10.1103%2Fphysrevresearch.3.023244, we can extend it to evaluate transition amplitudes for the same auxiliary operators between two eigenstates of the Hamiltonian.
This would allow, among other, the evaluation of non-adiabatic couplings between the excited states (see in the same article).
The idea would be to have a static method for two fixed eigenstates:
@staticmethod
def _eval_transition_amplitudes(
aux_operators: ListOrDict[OperatorBase], wavefn, wavefm, threshold: float = 1e-12
) -> ListOrDict[Tuple[complex, complex]]:
which would be called by a higher level method for a Dict of pairs (i, j)
def compute_transition_amplitudes(
self,
aux_operators: Optional[ListOrDict[OperatorBase]],
transition_amplitude_pairs: Optional[Dict[Tuple[int, int]]]
) -> Optional[ListOrDict[Tuple[complex, complex]]]:
I would like to discuss improving the way bounds are handled and settle/implement such an improvment:
For variational algorithms, like VQE
, that use an optimizer, while the initial_point
(x0) for the minimize can be passed through the algorithm, if an optimizer supports/requires bounds
the way a user might influence this for the algorithm is not obvious. For instance using BOBYQA
with VQE works with RealAmplitudes
, but fails with UCC since the later ends up with an unbounded setting which fails on the current code. SNOBIT
needs bounds and raises an error to that effect, but how to get it to work is asked. NLOpt global optimizers need a bounds and internally this defaults in the case of any limit being unbounded to something that is not (-3pi or 3pi for lower, upper respectively).
In VQE etc initial point
used to be informed by the ansatz (preferred_init_point) such that if an explicit value was not passed by the user then it would look to the ansatz, and if that did not exist then just picked a random point. This implicit informing by ansatz was rather hidden and it was preferred to remove this mechanism. Now the initial point must be set by a user and if its left at the default it will be random. I start since things are similar and action was taken for initial point.
An anstaz can presently inform about parameter_bounds
. RealAmplitudes does (-pi, pi), Its NLocal parent defaults to None and UCC extends this (via EvolvedOpAnstz) but does not override this. parameter_bounds
has a setter, if you know about it and how this is used by VQE etc., so the value can be set/overridden. For a plain parameterized circuit this property does not exist, but of course, given things are Python. it can set (added) at runtime to inform the bounds for VQE. This allows one to set the bounds such that VQE will run with UCC with BOBYQA etc.
So this is to discuss a possible improvement to variational algos like VQE in regards of bounds:
We could simply just document better how things are currently , a tutorial/howto that covers things.
Another option (which can come with doc improvements related to bounds) would be adding an optional bounds
parameter that defaults to None (unbounded)
We could, like was done with initial_point remove the information from the ansatz and require a user to explicitly pass a bounds if the optimizer requires it (optimizers have flags to query such) - but using minimizer protocol this is unknown to the algo. When using an optimizer the variational algo can simply raise an error if no bounds is given and the optimizer needs it. For minimizer it would be reliant on whatever the underlying behavior of the code is in regards of bounds if it fails to get it.
We could leave parameter_bounds on ansatz and make it clear in some improved docs that this is also used and how. A variational algo could let any user defined bounds override any informed by the ansatz. The goal being to have the way bounds is set be more exposed to the user rather than how things are currently and make it easier for them.
@Cryoris @mrossinek @adekusar-drl I would welcome opinions, other ideas....
While working on #73, @ElePT and I have identified the following inconsistencies and/or problems around the type hints which should be investigated further. Rather than holding up that PR unnecessarily, we decided to gather these here.
That way, they can be investigated one-by-one.
Note: some of these may very likely require code changes to resolve inconsistencies in the interfaces.
np.ndarray
vs Sequence[float]
. It is a known issue that to mypy
the numpy arrays do not satisfy the Sequence
type. This leads to many type ignore statements right now which can hopefully be improved upon.
Sequence[float]
, sometimes its Sequence[Sequence[float]]
, and oftentimes internally it ends up as np.ndarray
causing many type ignore commentsinitial_point
attributes is very inconsistent across its various occurrences.Minimizer
interface does not appear to be supported properly by VQD
and PVQD
(for example) since they do not provide the required jac
argumentListOrDict
type is causing quite some oddities. This has multiple cases:
metadata
(e.g. variance
) types and implementations are inconsistent with the variance sometimes wrapped in dictionaries and sometimes provided as a single numberaux_operators
and/or observables
we had to add many type ignores which would need to be re-evaluated (possibly linked to the previous point)EsimationProblem
have very poor typing around the following points:
post_processing
functioncircuit_results
#108 added support for Python 3.12. This works fine for the algorithms but CI here uses Aer, its listed in requirements.dev, to run the VQE tutorial that shows it with a noise model on Aer. While the published source builds here for CI in Ubuntu and Mac so CI is producing the installs for Aer for 3.12 for those platforms, for Windows it fails to build the wheel. It was decided, see #108, in the interim to limit Win CI here to 3.11 (normally for Mac and Win we do these at min and max Python versions supported - Ubuntu on every version). This issue is a reminder to bump back up the Win CI to 3.12 at when Aer officially supports it and releases pre-built wheels.
Now that the Python 3.6 support is dropped we can use dataclasses
in Qiskit. Since our AlgorithmResult
was essentially a dictionary replacement we implemented ourselves, should we switch to simply using dataclass
as an algorithm result?
That would have advantages like
Do we still want AlgorithmsTestCase
to inherit from QiskitTestCase
? (raised by @woodsp-ibm)
QiskitTestCase
seems to have a lot of terra-specific setup that is not relevant to algorithm tests. Now that qiskit-algorithms
is an independent repository, it might make more sense to follow the test setups of other community repos such as qiskit-optimization, and just add a minimal independent base test class that we can update as required.
This should end up being just the new primitive based algos. The ones that were copied such as VQE, the former ones have been removed, but algos like Grover and AE, which support both, the deprecated opflow/QI path here should go.
SPSA is an efficient optimizer that approximates the gradient each iteration using only two evaluations of the cost-function f(theta). These evaluations occur at f(theta+delta) and f(theta-delta), where delta is a perturbation along a chosen axis in parameter space.
Notably, the cost function at the unperturbed parameters, f(theta), is not evaluated until the very end of the optimization process. This makes sense, as evaluating it each iteration would make the optimization take roughly 50% longer. However, it blinds the user to how the actual value of the cost function is evolving over time.
I think it would be helpful to have an option to additionally evaluate f(theta) periodically throughout the optimization. E.g. the user could specify a period of 10 iterations, which would only increase the runtime ~5%. I think this could provide useful diagnostic information, both for post-mortem analyses (e.g. tuning hyperparameters) or for real-time decision making (e.g. deciding whether to terminate an unpromising optimization run early to save time). It should also help produce plots that are easier to interpret.
Also, this could in some cases allow users to manually terminate (interrupt) a lengthy optimization run and still get a usable result, e.g. if the user notices that the optimizer has already converged but still has many iterations left to run, they could interrupt the execution and still salvage a good estimate of f(theta_optimal) from the saved data.
Let's finally implement Qiskit/qiskit#9866.
py.typed
was removed in the stable branch since otherwise mypy
testing in upstream application repos fails since as typing needs to be addressed - see #74
I would suggest to remove py.typed
from main until such time as #74 is addressed to avoid it being included in any future release that might be made before #74 is done.
The algorithms.optimizers module contains optimizers based on the scikit-quant package - snobfit, imfil and bobyqa. This source code repo for that package seems pretty much unmaintained with last activity 2 years ago and the snobfit issue /scikit-quant/scikit-quant#24, for which unit tests here are skipped, has been open and without any activity since it was created at the end of last year. The Infil optimizer is now emitting numpy deprecation warnings and will break when that function is changed in numpy.
Unlike when these optimizer "wrappers" were added, to conform to the base Optimizer class, there is now also Minimizer protocol so the scikit-quant optimizers could still be used via that e.g. using a partial over their current interface. And as such, given these are breaking down and failing, no longer seem to be supported, and require using older numpy versions to have them work deprecating and removing them from here seems like best course of action but does no longer preclude someone if they really want to use them doing so via the Minimizer protocol that now exists.
Add a release note, with a prelude section that gives an overview of the move of this code out of Qiskit, similar text to what was done there I imagine.
When prototyping or debugging non-quantum parts of the stack I often find myself getting back to the NumPy(Minimum)Eigensolver
classes. However, in less trivial chemistry applications, the naive lowest eigenvalue is oftentimes not a physical one, so I need to rely on adding a filter_criterion
to find a physically meaningful eigenvalue.
Unfortunately, the code currently hard-codes k
when a filter_criterion
is supplied:
https://github.com/Qiskit/qiskit-terra/blob/332bd9fe0bea82c0fdf7329cea3da115d86e3fc2/qiskit/algorithms/eigensolvers/numpy_eigensolver.py#L257-L259
This means that ALL eigenvalues will be computed, which can take a VERY long time.
The reason for doing this is well motivated though, because one cannot know a priori what value to pick for k
.
I would like to propose that we support setting k
and filter_criterion
simultaneously.
Since as an end-user I have written the filter_criterion
myself, I do have more knowledge about the problem that I am trying to solve. Thus, I may also be able to make an educated guess for k
.
And even if that fails, I may be willing to try setting k
to 100, then 200, then 300 and so on , rather than having to wait for say 2**10=1024
eigenvalues to be computed regardless of whether my value of interest might have been in the first 10% or so.
I think the prototyping and debugging speed that could be gained here is quite significant.
k
while also setting a filter_criterion
I would work on this myself, but in the recent months where I thought of doing this I have not found the time to come up with a clean code solution. Thus, I am opening this issue to see if someone else might find this interesting and has the time to do this.
I will gladly review a PR once time has come. Otherwise I might get back to this once I have a bit more time on my hands in the (potentially far) future.
The numpy version is currently pinned to 1.24 because test_imfil
in the test_optimizers_scikitquant.py
module breaks with 1.25 (somewhere internal to the imfil
optimizer when saving its history). There are 2 possible ways we could avoid this pinning
test_imfil
, similar to what is done with the SnobFit tests in that same test module, which are skipped based on a numpy version check.scikit-quant
pkg has not been updated in a couple of years, at some point we might want to think whether to keep it at all. If someone really wants to use it, they could still do it via some partial on their minimize.The algorithms, since Aqua days, all used/relied on a central RNG in algorithm_globals which can be seeded for reproducibility. This file is in Qiskit in qiskit.utils
and the code here still refers to that. While the file there is not deprecated it was really for the algorithms, hence its name, so I could see it being removed from Qiskit. Most likely we need that function moved/copied here but this will be a change for end users on what should be seeded so we need to figure how to move forwards with this.
If we want to evaluate a set of expectation values for the same observables and circuits, but with different circuit parameters, we can cache the whole transpilation and basis transformation processes. Qiskit's opflow easily allows this and to leverage this it would be nice if we extended the eval_observables
to allow returning a callable that takes as input circuit parameters and returns the (efficiently) evaluated expectation values.
Something like this is already used in #8304 and can be extracted and merged with the existing eval_observables
in a follow-up.
With the introduction of primitive-based algorithms we had 2 separate import paths for classes with the same name, so we enforced using the "long" import path to make the import process intentional, instead of allowing importing directly from qiskit.algorithms
. For example:
from qiskit.algorithms import VQE
used to do: from qiskit.algorithms.minimum_eigen_solvers import VQE
, and it was replaced with from qiskit.algorithms.minimum_eigensolvers import VQE
for the new VQE class (while from qiskit.algorithms import VQE
would import the old class).
Now that the old paths have been deprecated and removed, we can bring back the short import path to point to the new classes, should we want to do it for convenience.
Describe the bug : I am working on an Iterative Amplitude Estimation routine that works on sampler. I have a quantum circuit, that I run first using the sampler
from aer
, I import it from there, simply set
sampler = _Sampler(run_options={"shots": num_shots})
and run the algorithm, It gives me a result of 0.64165
which is very near to what I shoud be getting (i verified it using classical caluclations , it should be around 0.64
)
Then I use
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Estimator, Options
where my qiskit_ibm_runtime
version is 0.11.3
Then I run the exact same quantum circuit qc
, using backend = "ibmq_qasm_simulator"
options = Options()
options.execution.shots = 2000
options.resilience_level = 0
and then invoke the session and make use of sampler = Sampler(options=options)
The result I'm getting from this is never the same, and is no where close to my answer that I was getting using aer
Sampler.
Even though the backend I'm using is IBM_Qasm_simulator
, which is error free and ideal simulator, so there should not be any error in the result, but I'm not able to replicate the result that I was getting using the aer
sampler. Why is it so? Since both these make use of shot-type backend, the only difference is that one is running on my local device and the other on IBM cloud. But the answers are so different.
Suggested solutions :
Additional Information : I also had a lengthy conversation on Qiskit's slack as well on this said topic
Slack Chat: https://qiskit.slack.com/archives/C7SJ0PJ5A/p1693194989748429?thread_ts=1693194989.748429&cid=C7SJ0PJ5A
Steps to reproduce :
Here's the code for the quantum circuit
n = 3
obj = 8
mu = 4
c = 0.1
sigma = 1
a = 0
b = 7
reps = 2
p_s = 0.5
p_b = 0.2
demand = NormalDistribution(num_qubits=n,mu=mu,sigma=sigma, bounds=[a,b])
supply = RealAmplitudes(n, reps=reps)
supply = supply.assign_parameters(parameters)
qc = QuantumCircuit(9)
qc.compose(demand,qubits=[3,4,5], inplace=True)
qc.compose(supply, qubits = [0,1,2], inplace=True)
z = 6
z1 = 7
for i in range(n):
qc.x(i)
for i in range(1,n):
qc.cnot(z,i)
for i in range(1,n):
qc.cnot(z,i+n)
qc.ccx(0,n,n+n)
qc.x(0)
for i in range(1,n-1):
qc.cnot(n+n,i)
qc.cnot(n+n,i+n)
qc.ccx(i+n,i,n+n)
qc.ccx(i+n,i,n)
qc.cnot(n+n,n-1)
qc.cnot(n+n,n+n-1)
qc.ccx(n+n-1,n-1,n+n)
for i in range(1,n-1):
qc.cnot(n,n-i)
qc.cnot(n,2*n-i)
qc.ccx(2*n-i-1,n-i-1,n)
for i in range(1,n-1):
qc.ccx(0,n,n-i)
qc.ccx(0,n,2*n-i)
qc.x(0)
qc.ccx(0,n,1)
qc.ccx(0,n,n+1)
for i in range(n):
qc.x(i)
qc.x(2*n)
for i in range(n):
qc.x(i+n)
for i in range(1,n):
qc.cnot(z1,i)
for i in range(1,n):
qc.cnot(z1,i+n)
qc.ccx(0,n,z1)
qc.x(n)
for i in range(1,n-1):
qc.cnot(z1,i+n)
qc.cnot(z1,i)
qc.ccx(i,i+n,z1)
qc.ccx(i,i+n,0)
qc.cnot(z1,z1-2)
qc.cnot(z1,n-1)
qc.ccx(z1-2,n-1,z1)
for i in range(1,n-1):
qc.cnot(0,z1-(i+1))
qc.cnot(0,n-i)
qc.ccx(z1-i-2,n-i-1,0)
for i in range(1,n-1):
qc.ccx(0,n,z1-(i+1))
qc.ccx(0,n,n-i)
qc.x(n)
qc.ccx(0,n,n+1)
qc.ccx(0,n,1)
for i in range(n):
qc.x(n+i)
qc.x(z1)
for i in range(n):
c2ry = RYGate(2*c*p_s * 2**(i)).control(2)
qc.append(c2ry,[i+n,obj-2,obj])
for i in range(n):
c2ry = RYGate(2*c*p_s * 2**(i)).control(2)
qc.append(c2ry,[i,obj-1,obj])
for i in range(n):
c2ry = RYGate(-2*c*p_s * 2**(i)).control(3)
qc.append(c2ry,[i,obj-1,obj-2,obj])
for i in range(n):
qc.cry(-2*c*p_b*(2**i),i,obj)
qc.ry(np.pi/2,obj)
qc.draw('mpl')
Once the quantum circuit qc
is made, first we import sampler
from :
from qiskit_aer.primitives import Sampler
Then call the IAE function:
epsilon = 0.01 # the error
alpha = 0.5 # the alpha value
problem = EstimationProblem(state_preparation=qc,objective_qubits=[obj]) # the quantum circuit
# construct amplitude estimation
iae = IterativeAmplitudeEstimation(
epsilon_target=epsilon,
alpha=alpha,
sampler=Sampler(run_options={"shots": num_shots})
)
and then:
result = iae.estimate(problem)
print(result)
print((result.estimation-0.5)/c)
The result for the final print command is 0.641
But then, when i make use of :
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Estimator, Session, Options
# Save an IBM Quantum account.
QiskitRuntimeService.save_account(channel="ibm_quantum", token="MY API TOKEN",overwrite=True)
and then set the backend
:
backend = "ibmq_qasm_simulator"
and then the options
options = Options()
options.execution.shots = num_shots
options.resilience_level = 0
and finally calling the IAE:
# runt he circuit on sampler
epsilon = 0.01 # the error
alpha = 0.5
with Session(service=service, backend=backend):
sampler = Sampler(options = options)
iae = IterativeAmplitudeEstimation(epsilon_target=epsilon,alpha=alpha, sampler=sampler)
problem = EstimationProblem(state_preparation=qc,objective_qubits=[obj])
result = iae.estimate(problem)
print(result)
print((result.estimation-0.5)/c)
always give me a different result from what is Intended.
Expected behavior : I expect to see the same behaviour as I was seeing from the aer sampler in qiskit ibm runtime sampler.
I already discussed this issue on Slack : https://qiskit.slack.com/archives/C7SJ0PJ5A/p1693194989748429?thread_ts=1693194989.748429&cid=C7SJ0PJ5A
and also opened an Issue in qiskit-ibm-runtime and they suggested to open an issue here,
In the IAE algorithm, on line 478, shouldn't the probability prob be replaced by round_one_counts/round_shots?
In the case where the data from different iterations with the same Grover power are joined together, only the Clopper Pearson confidence intervals seem to consider the joint information, whereas the Chernoff method considers the total shots but the probability of the latest iteration alone.
In Grover here
qiskit-algorithms/qiskit_algorithms/amplitude_amplifiers/grover.py
Lines 212 to 214 in 4464447
reflection_qubits
on what was passed to the AmplificationProblem as a grover_operator
but this is of type QuantumCircuit which does not have that field - only the GroverOperator subclass does. This should be more dynamically taken care of such that if a QuantumCircuit is passed, without that field, that it is handled accordingly.Optimizers are normally expected to use the objective function to evaluate a single point being returned a single value. This is as per scipy optimizers, upon which several are based.
However it was recognized that optimizers computing gradient, say by finite diff, want to evaluate a set of points independently, to compute the gradient, and as such this was parallelizable which could help performance. To inform an optimizer that it was able to do this, ie ask the objective function for the values of a lits of points, where the objective function provided supported that, a max_grouped_evals flag could be set on the optimizer to a number bigger than the default of 1. In this case an optimizer could ask for a list of points and get a list of values back, though. Given the current optimizer spec this should only be done if max_grouped_evals allows it. However it seems a change, not too long ago, altered AQGD to do this by default which violates the current accepted spec we have for optimizers.
So either we change the spec, and require all objective functions to support a list in/out, which I do not think I am in favor of, or we correct AGQD to honor max_grouped_evals. The failure to conform was noted when Qiskit/qiskit#6299 was looked where there is a test with Aer which ends up using the Aer qobj re-parameterization in Circuit Sampler, since it passes in multiple values, and fails in the test when it was attempted to be converted to the newer backend interface.
Please could you add bounds support in qiskit.algorithms.optimizers.SPSA.minimize.
For now bounds are ignored.
Thank you !
https://docs.python.org/3/library/functions.html#id
Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.
Id's get reused - this was an issue with the early reference primitives that used id for the key to store/manage circuits see
which is why the reference primitives use a more complex key, which could simply be used for the state fidelity cache instead of id.
qiskit-algorithms/qiskit_algorithms/state_fidelities/base_state_fidelity.py
Lines 176 to 177 in 745a892
As to changing the circuit, since the key is more done around contents, and you cache a circuit based on how things were I think this change to key will address that TODO
/home/runner/work/qiskit-algorithms/qiskit-algorithms/test/gradients/test_estimator_gradient.py:388: DeprecationWarning: The method qiskit.circuit.quantumcircuit.QuantumCircuit.toffoli()
is deprecated as of qiskit 0.45.0. It will be removed no earlier than 3 months after the release date. Use QuantumCircuit.ccx as direct replacement.
AdaptVQE had some deprecated code when brought over from qiskit.algorithms which was not removed and perhaps should have been. The message refers to Qiskit version which will be confusing now. We had expected users to migrate code before moving from qiskit.algorithms to qiskit_algorithms. .
@deprecate_arg(
"threshold",
since="0.24.0",
pending=True,
new_alias="gradient_threshold",
)
I note this is pending deprecated rather than deprecated so perhaps users are not as aware of it. The alternative to removing it is to leave it but switch version and perhaps go over to deprecated and remove it later.
Qiskit terra interface for CMA-ES which is a stochastic derivative-free numerical optimization algorithm, observed in arXiv:2111.13454 to be on par with and sometimes outperforming SPSA with appropriate hyperparameter tuning 1.
I would be more than happy to work on this. ↩
The QOAO callback attribute docstring says this
callback (Callable[[int, np.ndarray, float, dict[str, Any]], None] | None): A callback
that can access the intermediate data at each optimization step. These data are: the
evaluation count, the optimizer parameters for the ansatz, the evaluated value, the
the metadata dictionary, and the best measurement.
Whereas the callback parameter in the constructor does not have and the best measurement nor does the SamplingVQE callback, in either situation, that this class extends where the callback is done by the parent. I believe and the best measurement should just be simply removed unless there was somehow an intent to have had this - but the callback signature does not accommodate such presently.
As noticed in Qiskit/qiskit#8271 (comment), the value of TimeEvolutionResult.observables
differs for the different available implementations.
Here's a snippet for comparison
import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.circuit.library import RealAmplitudes
from qiskit.algorithms.state_fidelities import ComputeUncompute
from qiskit.algorithms.optimizers import L_BFGS_B
from qiskit.primitives import Sampler, Estimator
from qiskit.quantum_info import Pauli
from qiskit.algorithms.time_evolvers import (
TimeEvolutionProblem,
PVQD,
VarQRTE,
SciPyRealEvolver,
TrotterQRTE,
)
hamiltonian = Pauli("Y")
observables = [Pauli("X"), Pauli("Z")]
ansatz = RealAmplitudes(1, reps=0)
initial_parameters = np.zeros(ansatz.num_parameters)
problem = TimeEvolutionProblem(hamiltonian, time=1, aux_operators=observables)
num_timesteps = 10
fidelity = ComputeUncompute(Sampler())
pvqd = PVQD(
fidelity,
ansatz,
initial_parameters,
estimator=Estimator(),
num_timesteps=num_timesteps,
optimizer=L_BFGS_B(),
)
print("PVQD:")
print(pvqd.evolve(problem).observables)
varqrte = VarQRTE(ansatz, initial_parameters, estimator=Estimator())
print("VarQRTE:")
print(varqrte.evolve(problem).observables)
initial_state = QuantumCircuit(1)
problem.initial_state = ansatz.bind_parameters(initial_parameters)
scipy = SciPyRealEvolver(num_timesteps)
print("SciPy:")
print(scipy.evolve(problem).observables)
trotter = TrotterQRTE(estimator=Estimator())
print("Trotter:")
print(trotter.evolve(problem).observables)
which prints
PVQD:
[array([0., 1.]), array([0.19866933, 0.98006658]), array([0.38941834, 0.92106099]), array([0.56464247, 0.82533562]), array([0.71735609, 0.69670671]), array([0.84147098, 0.54030231]), array([0.93203908, 0.36235776]), array([0.98544973, 0.16996715]), array([ 0.9995736 , -0.02919952]), array([ 0.97384763, -0.22720209]), array([ 0.90929743, -0.41614683])]
VarQRTE:
[[(0.0, {}), (1.0, {})]]
SciPy:
[(array([0. +0.j, 0.19866933+0.j, 0.38941834+0.j, 0.56464247+0.j,
0.71735609+0.j, 0.84147098+0.j, 0.93203909+0.j, 0.98544973+0.j,
0.9995736 +0.j, 0.97384763+0.j, 0.90929743+0.j]), array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])), (array([ 1. +0.j, 0.98006658+0.j, 0.92106099+0.j, 0.82533561+0.j,
0.69670671+0.j, 0.54030231+0.j, 0.36235775+0.j, 0.16996714+0.j,
-0.02919952+0.j, -0.22720209+0.j, -0.41614684+0.j]), array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]))]
Trotter:
[[(0.0, {}), (1.0, {})], [(0.9092974268256818, {}), (-0.4161468365471423, {})]]
The result types should be consistent. I think from a user perspective, the most convenient would be to capture the expectation values of a single observable over all timesteps in an array, which would allow computing averages or plotting very easily. For example, the format of the SciPyEvolver
s seems like a good idea:
observables = [
(values_observable1, stddevs_observable1),
(values_observable2, stddevs_observable2),
...
]
No response
When using the GSLS
optimizer in combination with the QAOA
minimum_eigensolver, the optimization crashes. This seems to stem from the GSLS optimizer not being capable of dealing with parameter bounds of the form [(None, None)]
.
The bug can be reproduced by trying to compute the minimum eigenvalue of a trivial hamiltonian:
from qiskit import Aer
from qiskit.primitives import BackendSampler
from qiskit.quantum_info import SparsePauliOp
from qiskit_algorithms.optimizers import GSLS
from qiskit_algorithms.minimum_eigensolvers import QAOA
backend = Aer.get_backend("aer_simulator")
sampler = BackendSampler(backend)
optimizer = GSLS()
qaoa = QAOA(optimizer=optimizer, sampler=sampler)
hamiltonian = SparsePauliOp(["ZI"])
result = qaoa.compute_minimum_eigenvalue(hamiltonian)
print(result)
This results in the following Traceback:
Traceback (most recent call last):
File "C:\...\minimal_example.py", line 19, in <module>
result = qaoa.compute_minimum_eigenvalue(hamiltonian)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\...\venv\Lib\site-packages\qiskit_algorithms\minimum_eigensolvers\sampling_vqe.py", line 218, in compute_minimum_eigenvalue
optimizer_result = self.optimizer.minimize(
^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\...\venv\Lib\site-packages\qiskit_algorithms\optimizers\gsls.py", line 125, in minimize
x, fun, nfev, _ = self.ls_optimize(x0.size, fun, x0, var_lb, var_ub)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\...\venv\Lib\site-packages\qiskit_algorithms\optimizers\gsls.py", line 185, in ls_optimize
directions, sample_set_x = self.sample_set(n, x, var_lb, var_ub, sample_set_size)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\...\venv\Lib\site-packages\qiskit_algorithms\optimizers\gsls.py", line 298, in sample_set
if (points >= var_lb).all() and (points <= var_ub).all():
^^^^^^^^^^^^^^^^
TypeError: '>=' not supported between instances of 'float' and 'NoneType'
I would expect the optimization process to finish without crashing and to print the final result.
No response
Most collections of optimization models use the old-fashioned MPS
format for model storage, e.g., MIPLIB. Furthermore, due to their verbosity (compare, e.g., square47.mps.gz
wit squaren47.mps
from MIPLIB - 80 MB vs. 1.4GB) people tend to use the gzipped files directly.
If qiskit optimization serves as gateway to classical and quantum optimization, I think it makes perfect sense to include this widely-adopted file format. docplex
supports mps
natively.
Wrong answer when using MaximumLikelihoodAmplitudeEstimationwith
with Sampler
from qiskit.utils import QuantumInstance
from qiskit.algorithms import EstimationProblem, MaximumLikelihoodAmplitudeEstimation
from qiskit import Aer, QuantumCircuit
from qiskit.primitives import Sampler
import numpy as np
number_qubits = 3
state_prep = QuantumCircuit(number_qubits)
for i in range(number_qubits):
state_prep.h(i)
qi = QuantumInstance(Aer.get_backend("aer_simulator"), shots=4000)
sampler = Sampler()
problem = EstimationProblem(
state_preparation=state_prep,
objective_qubits=[number_qubits-1],
)
ae_instance = MaximumLikelihoodAmplitudeEstimation(evaluation_schedule=[0,1,2], quantum_instance=qi)
ae_sampler = MaximumLikelihoodAmplitudeEstimation(evaluation_schedule=[0,1,2], sampler=sampler)
result_instance = ae_instance.estimate(problem)
result_sampler = ae_sampler.estimate(problem)
print("--------- Correct result ---------")
print("Good counts with aer simulator: ",result_instance.good_counts)
print("Circuit results with aer simulator: " ,result_instance.circuit_results)
conf_int = np.array(result_instance.confidence_interval)
print("Estimated value with aer simulator: \t%.4f" % (result_instance.estimation_processed))
print("Confidence interval with aer simulator :\t[%.4f, %.4f]" % tuple(conf_int))
print("\n")
print("--------- Incorrect result ---------")
print("Good counts with Sampler:",result_sampler.good_counts)
print("Circuit results with Sampler:",result_sampler.circuit_results)
conf_int = np.array(result_sampler.confidence_interval)
print("Estimated value with Sampler: \t%.4f" % (result_sampler.estimation_processed))
print("Confidence interval with Sampler:\t[%.4f, %.4f]" % tuple(conf_int))
--------- Correct result ---------
Good counts with aer simulator: [1976, 2000, 2061]
Circuit results with aer simulator:
[{'0': 2024, '1': 1976},
{'0': 2000, '1': 2000},
{'0': 1939, '1': 2061}]
Estimated value with aer simulator: 0.5020
Confidence interval with aer simulator : [0.4994, 0.5046]
--------- Incorrect result ---------
Good counts with Sampler: [0, 0, 0]
Circuit results with Sampler:
[{'000': 0.4999999999999998, '001': 0.4999999999999998},
{'000': 0.49999999999999895, '001': 0.49999999999999895},
{'000': 0.49999999999999817, '001': 0.49999999999999817}]
Estimated value with Sampler: 0.0000
Confidence interval with Sampler:[-0.0000, 0.0000]
Using Sampler should get similar results as with aer simulator
I have identified the problem in MaximumLikelihoodAmplitudeEstimation
and have created a diagram explaining it:
To fix the issue, I suggest changing some parts in the function estimate
in MaximumLikelihoodAmplitudeEstimation
. From this:
if shots is None:
for i, quasi_dist in enumerate(ret.quasi_dists):
circuit_result = {
np.binary_repr(k, circuits[i].num_qubits): v
for k, v in quasi_dist.items()
}
result.circuit_results.append(circuit_result)
shots = 1
else:
# get counts and construct MLE input
for circuit in circuits:
counts = {
np.binary_repr(k, circuit.num_qubits): round(v * shots)
for k, v in ret.quasi_dists[0].items()
}
result.circuit_results.append(counts)
To this:
if shots is None:
for i, quasi_dist in enumerate(ret.quasi_dists):
circuit_result = {
str(k): v
for k, v in quasi_dist.items()
}
result.circuit_results.append(circuit_result)
shots = 1
else:
# get counts and construct MLE input
for index,circuit in enumerate(circuits):
counts = {
str(k): round(v * shots)
for k, v in ret.quasi_dists[index].items()
}
result.circuit_results.append(counts)
If everyone agrees with the solution, I would love to try and push the changes (it will be my first time contributing to Qiskit)
Nightly CI has been failing for the past 3 days (the contents of the repo have not changed at all in that time).
/home/runner/work/qiskit-algorithms/qiskit-algorithms/docs/tutorials/02_vqe_advanced_options.ipynb:161: ERROR: Unexpected indentation.
/home/runner/work/qiskit-algorithms/qiskit-algorithms/docs/tutorials/02_vqe_advanced_options.ipynb:169: ERROR: Unexpected indentation.
/home/runner/work/qiskit-algorithms/qiskit-algorithms/docs/tutorials/02_vqe_advanced_options.ipynb:172: ERROR: Content block expected for the "raw" directive; none found.
/home/runner/work/qiskit-algorithms/qiskit-algorithms/docs/tutorials/02_vqe_advanced_options.ipynb:191: ERROR: Unexpected indentation.
/home/runner/work/qiskit-algorithms/qiskit-algorithms/docs/tutorials/02_vqe_advanced_options.ipynb:199: ERROR: Unexpected indentation.
/home/runner/work/qiskit-algorithms/qiskit-algorithms/docs/tutorials/02_vqe_advanced_options.ipynb:202: ERROR: Content block expected for the "raw" directive; none found.
One change that I do notice is that nbconvert
is now 7.14.0, and has been failing since that version, where the last time it passed was when 7.13.1 was the latest. It appears to be similar to this issue raised on nbconvert jupyter/nbconvert#2092
Tracking TODOs for 0.1.0.
Update header from:
This code is part of Qiskit
to
This code is part of a Qiskit project
.
While part of Qiskit, qiskit.algorithms raised, in a few places, QiskitError directly. Now there is an AlgorithmError (similar to the apps having their own exceptions) which is also used - maybe this should be raised now in those places too now that algorithms are no longer part of Qiskit. I will note AlgorithmError, and the apps' exceptions, derive from QiskitError, but it will list as the former type in a stacketrace which maybe is more desirable than directly raising QiskitError which may cause confusion now that algorithms is no longer part of Qiskit.
That file was used by algorithms to validate parameters and have consistent error messages. Though in Qiskit its only used by algorithms (came from Aqua originally) and so I imagine it likely to be removed from Qiskit.
Related to #21
See also Qiskit/qiskit#10516 where its noted and I suggest that simply making a copy here and switching the algorithms to use that here is likely all that is needed on this side since validation is all internal logic here.
The nightly CI runs have been failing for the last few nights under Python 3.12 - mainly Ubuntu but it did do it with Mac too (Windows has no 3.12 runs yet - see #108)
E.g. https://github.com/qiskit-community/qiskit-algorithms/actions/runs/7256311824/job/19768394836
File "/opt/hostedtoolcache/Python/3.12.1/x64/lib/python3.12/site-packages/stestr/subunit_runner/program.py", line 247, in runTests
self.result = testRunner.run(self.test)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.12.1/x64/lib/python3.12/site-packages/stestr/subunit_runner/run.py", line 51, in run
test(result)
File "/opt/hostedtoolcache/Python/3.12.1/x64/lib/python3.12/unittest/suite.py", line 84, in __call__
return self.run(*args, **kwds)
^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.12.1/x64/lib/python3.12/unittest/suite.py", line 122, in run
test(result)
File "/opt/hostedtoolcache/Python/3.12.1/x64/lib/python3.12/unittest/suite.py", line 84, in __call__
return self.run(*args, **kwds)
^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.12.1/x64/lib/python3.12/unittest/suite.py", line 122, in run
test(result)
File "/opt/hostedtoolcache/Python/3.12.1/x64/lib/python3.12/unittest/suite.py", line 84, in __call__
return self.run(*args, **kwds)
^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.12.1/x64/lib/python3.12/unittest/suite.py", line 122, in run
test(result)
File "/opt/hostedtoolcache/Python/3.12.1/x64/lib/python3.12/unittest/case.py", line 692, in __call__
return self.run(*args, **kwds)
^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.12.1/x64/lib/python3.12/unittest/case.py", line 662, in run
result.stopTest(self)
File "/opt/hostedtoolcache/Python/3.12.1/x64/lib/python3.12/site-packages/subunit/test_results.py", line 127, in stopTest
return self.super.stopTest(test)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.12.1/x64/lib/python3.12/site-packages/subunit/test_results.py", line 56, in stopTest
return self.decorated.stopTest(test)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.12.1/x64/lib/python3.12/site-packages/testtools/testresult/real.py", line 1539, in stopTest
self._tags = self._tags.parent
^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'parent'
And there are numerous warnings about a lack of addDuration
in test result e.g.
/opt/hostedtoolcache/Python/3.12.1/x64/lib/python3.12/unittest/case.py:580: RuntimeWarning: TestResult has no addDuration method
warnings.warn("TestResult has no addDuration method",
A similar issue is noted here too
Hello,
I would like to suggest two small improvements:
'noancillas'
mode (lines 168 and 172), which is highly inefficient in terms of circuit depth (it scales as 2**n
, where n
is the number of qubits, while other implementations scale as n
, at the expense of a higher width - refer to mcx). This behavior is very bad as it may strongly affect the performances in applications. I suggest we expose the mcx_mode
in the EstimationProblem function, as currently done in GroverOperator. We shall also discuss which value should be the default for mcx_mode
: 'noancillas'
for backward compatibility?is_good_state
, but the standard QAE is not able to handle it, for obvious reasons. This fact is very confusing. I think we should have a variable to keep trace whether is_good_state
is not the default one, and AmplitudeEstimation should throw an exception if given an EstimationProblem with a user-defined is_good_state
I can contribute with the code, if you agree.
Given Qiskit/qiskit-tutorials#1473, we should migrate algorithm-related tutorials from qiskit-tutorials to qiskit-algorithms.
To do:
qiskit-tutorials/algorithms
to qiskit_algorithms/tutorials
qiskit.algorithms
to qiskit_algorithms
An Introduction to Algorithms in Qiskit
and perhaps we need something more like An Introduction to Algorithms for Qiskit
instead given the move etc. Text in the main body should be checked over too and reworded appropriately e.g. from same tutorial Qiskit provides a number of Algorithms...
The small code examples in the doc string of qiskit_algorithms/optimizers/spsa.py still show the old signature of optimizers in qiskit instead of the new scipy-style optimizers. This includes a method optimize
that is now called minimize
, returning point, value, niter
instead of OptimizationResult
objects, etc.
--
The code examples shout show the new way of using optimizers in qiskit.
No response
While Implementing an IAE routine, when I reset some qubits to |0> using circuit.reset
, the following error is shown:
CircuitError: 'inverse() not implemented for reset.'
Make a simple circuit just for testing:
from qiskit import QuantumRegister, ClassicalRegister
from qiskit import QuantumCircuit, execute
from qiskit.circuit.library import IntegerComparator
from qiskit import Aer
backend = Aer.get_backend('aer_simulator')
q = QuantumRegister(9,'q')
circuit = QuantumCircuit(q)
a = 3
comparator = IntegerComparator(num_state_qubits=3, value=a, geq=True)
circuit = circuit.compose(comparator,qubits=[0,1,2,3,7,8])
circuit.reset([3, 4, 5])
The Quantum circuit looks like this:
Then simply run the IAE
epsilon = 0.01 # the error
alpha = 0.5 # the alpha value
num_shots = 5000
problem = EstimationProblem(state_preparation=circuit,objective_qubits=[8]) # the quantum circuit
# construct amplitude estimation
iae = IterativeAmplitudeEstimation(
epsilon_target=epsilon,
alpha=alpha,
sampler=Sampler(run_options={"shots": num_shots})
)
and
result = iae.estimate(problem)
print(result)
It should print out IAE result.
It has something to do with the reset
function, and IAE compatibility with it.
It appears that some unit test cases directly extended QiskitTestCase rather than the QiskitAlgorithmsTestCase (which used to extend that class but since moving from Qiskit no longer does so). The test cases that remain using QiskitTestCase as their parent should be changed to QiskitAlgorithmsTestCase
There are a number of files with links to the docs in qiskit.org/ecosystem
that need updating to the new location as follow-up after #115
and also
and I listed this separately since the notebooks have links to Aer and Nature for which maybe its better to wait until they are all migrated off ecosystem and update them just the once. The redirects will suffice for now.
The confidence_interval of the Max Likelihood Amplitude Estimator is already rescaled. The confidence_interval_processed is rescaled twice. This does not happen for example with the Iterative Amplitude Estimator
Run a Max Likelihood Amplitude Estimator with a post processing function
Have the confidence_interval not rescaled, and the confidence_interval_processed only rescaled once
I haven't seen where the bug is
The L-curve solver implemented in qiskit.opflow.NaturalGradient
is a powerful solver for ill-conditioned linear systems. It determines the required regularization by trading off noise in the solution and introduced bias and is an important subroutine in variational time evolution, natural gradient descent or 2nd order gradient descent.
It would be great to have a submodule solvers
or linear_systems
next to qiskit_algorithms.optimizers
where we can add this function.
Printing a SamplingVQE result shows a limited number of fields -it should show the full result as we do for other algorithms.
AlgorithmResult, which SamplingMinimumEigensolverResult inherits, defines a str which prints all the fields in a manner similar to a dict print with field name and value.
SamplingMinimumEigensolverResult however overrides this https://github.com/Qiskit/qiskit-terra/blob/9d336182f41533888a728609ce9e7102bb143180/qiskit/algorithms/minimum_eigensolvers/sampling_mes.py#L128-L138 and prints a limited amount of information. A user then using SamplingVQE or QAOA and printing the result, unlike other algorithm results that print based off AlgorithmResult, sees just this limited data - no info about optimizer, optimal point etc.
I could see this being removed from SamplingMinimumEigensolverResult - but maybe this was done for a nicer format of the fields it does print. The other thought is to have AlgorithmResult prepended to this; or even rework the code to be more like AlgorithResult except these chosen fields to do an improved format. Bottom line is that I think the SamplingVQE result, or other result subclasses should print all their info.
How to proceed about the algorithm-related qiskit-terra releasenotes?
TODO/to be discussed
TODO
Recently this job failed, as below, in similar manner to that raised by
Looking into this further its not the test case as such rather the ComputeUncompute implementation and the job thread logic it uses, more detail below.
test.state_fidelities.test_compute_uncompute.TestComputeUncompute.test_symmetry
-------------------------------------------------------------------------------
Captured traceback:
~~~~~~~~~~~~~~~~~~~
Traceback (most recent call last):
File "/home/runner/work/qiskit-algorithms/qiskit-algorithms/qiskit_algorithms/state_fidelities/compute_uncompute.py", line 161, in _run
result = job.result()
File "/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/qiskit/primitives/primitive_job.py", line 55, in result
return self._future.result()
File "/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/concurrent/futures/_base.py", line 437, in result
return self.__get_result()
File "/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/concurrent/futures/_base.py", line 389, in __get_result
raise self._exception
File "/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/concurrent/futures/thread.py", line 57, in run
result = self.fn(*self.args, **self.kwargs)
File "/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/qiskit/primitives/sampler.py", line 88, in _call
if len(value) != len(self._parameters[i]):
IndexError: list index out of range
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/runner/work/qiskit-algorithms/qiskit-algorithms/test/state_fidelities/test_compute_uncompute.py", line 106, in test_symmetry
results_2 = job_2.result()
File "/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/qiskit/primitives/primitive_job.py", line 55, in result
return self._future.result()
File "/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/concurrent/futures/_base.py", line 437, in result
return self.__get_result()
File "/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/concurrent/futures/_base.py", line 389, in __get_result
raise self._exception
File "/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/concurrent/futures/thread.py", line 57, in run
result = self.fn(*self.args, **self.kwargs)
File "/home/runner/work/qiskit-algorithms/qiskit-algorithms/qiskit_algorithms/state_fidelities/compute_uncompute.py", line 163, in _run
raise AlgorithmError("Sampler job failed!") from exc
qiskit_algorithms.exceptions.AlgorithmError: 'Sampler job failed!'
Running this script, which replicates the logic the failing test, fairly quickly fails the same way as show above.
import numpy as np
from qiskit.circuit import QuantumCircuit, ParameterVector
from qiskit.primitives import Sampler
from qiskit_algorithms.state_fidelities import ComputeUncompute
def main():
i = 1
while True:
do_fidelity()
print(f"{i}\r", end="")
i+=1
def do_fidelity(serial=False):
parameters = ParameterVector("x", 2)
circuit = QuantumCircuit(2)
circuit.rx(parameters[0], 0)
circuit.rx(parameters[1], 1)
sampler = Sampler()
params_l = np.array([[0, 0], [np.pi / 2, 0], [0, np.pi / 2], [np.pi, np.pi]])
params_r = np.array([[0, 0], [0, 0], [np.pi / 2, 0], [0, 0]])
fidelity = ComputeUncompute(sampler)
n = len(params_l)
job_1 = fidelity.run(
[circuit] * n, [circuit] * n, params_l, params_r
)
job_2 = fidelity.run(
[circuit] * n, [circuit] * n, params_r, params_l
)
results_1 = job_1.result()
results_2 = job_2.result()
if not np.allclose(results_1.fidelities, results_2.fidelities, atol=1e-16):
print("Failed")
sys.exit()
if __name__ == "__main__":
main()
From investigation it appears that failure is that the sampler.run()
is part of the logic called by the function that is passed job thread producing the result. That sampler code updates a number of instance variables and is not safe for such usage. I verified things by acquiring and releasing a threading lock in the fidelity primitive around the run call and the above has run over a weekend with no failure. That will not do as a general solution though.
My suggestion would be, if we want the same async behavior on the result/job, to change what is done in the thread to just be the call to the get the sampler job result, from running the sampler (which should happen in the main thread instead) and format up the result. It will be far less in the thread but that should be safe.
As previously already discussed eg. in Qiskit/qiskit#8628 (comment), it would be great it all Qiskit Optimizers have the same signature for the callback function.
The callback function allows to pass back information on the state of the optimization to the user during the optimization. This is useful to track additional information like the optimization history or to check whether the optimization is converging. However, currently each optimizer has it's own signature on the callback, which makes it difficult to write modular code. It would be great it they had consistent signature, such as:
callback(current_parameters, optimizer_state)
The current parameters are known to each optimizer (also the SciPy optimizers) and additional information could be stored in an optimization state, such as introduced in https://github.com/Qiskit/qiskit-terra/blob/5177db6e09917809895fe37878422ba8fcb6321a/qiskit/algorithms/optimizers/gradient_descent.py#L28
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.