Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The inputs and outputs of the quantum function circuit #11

Closed
josh146 opened this issue Aug 29, 2018 · 29 comments
Closed

The inputs and outputs of the quantum function circuit #11

josh146 opened this issue Aug 29, 2018 · 29 comments
Assignees
Labels
discussion 💬 Requiring extra discussion interface 🔌 Classical machine-learning interfaces
Milestone

Comments

@josh146
Copy link
Member

josh146 commented Aug 29, 2018

This issue is related to how we pass/call the quantum function associated with a QNode. As far as I can see, we want to provide maximum freedom to the user, but are constrained by autograd/design decisions/python decorators.

From @mariaschuld:

  1. The function where the user defines the quantum circuit should be defined in the same way as it is later used. This is at the moment clashing with how autograd wants things, and we don't fully know how autograd wants things because of its poor documentation,
  2. it should have two "slots" for data inputs and arguments, since the user cannot poke around in one
  3. it should be (at least very soon) able to return the expectations of multiple qubits/qmodes, otherwise things like the variational eigensolver or multi-class variational classifier are reduced to a simplicity that takes away the power of what we want to implement

Open questions:

  1. should circuit() take batches of data? For example when the device has some parallelisation functionality for each data point in a batch
  2. how do we envision the user to handle thousands of parameters, layered parameter structures and possibly non-vectorial data in future?
@josh146 josh146 changed the title The arguments of the quantum function circuit() The arguments of the quantum function circuit() Aug 29, 2018
@josh146 josh146 added the discussion 💬 Requiring extra discussion label Aug 29, 2018
@josh146
Copy link
Member Author

josh146 commented Aug 29, 2018

Currently, the user defined quantum function takes an arbitrary number of positional arguments:

def circuit(x,y,z):

Each of these can either be a variational parameter or input data, depending on how the function is defined in the cost function.

Sneaky edit: the following suggestions seem more apt for the cost function, not the quantum node/quantum function. Perhaps the quantum function should remain arbitrary and up to the user, and only the cost function is restricted.

Some suggestions are:

  1. Use two positional arguments, each accepting an array. The first accepts only parameters, the second only input data.

    def circuit(params, input_data):

    While easier to code, perhaps too restrictive for the user, who is used to any combination of arguments in a user defined function?

  2. Use positional arguments for the parameters to be varied, but use a keyword argument accepting an array for input data:

    def circuit(x, y, z, *, input_data):
  3. Support only keyword arguments, each accepting an array:

    def circuit(*, parameters, input_data):

Downsides of 2. and 3. are that the user would have to use the (not commonly known) syntax * for specifying keyword-only arguments.

@josh146
Copy link
Member Author

josh146 commented Aug 29, 2018

Another factor in the design is how to get autograd to play nicely; we want the wrapped quantum function to be called exactly as it is defined, to avoid confusing the user.

At the moment, my qfunc decorator works as follows:

def qfunc(device):
    def qfunc_decorator(func):
        qnode = QNode(func, device)
        @wraps(func)
        def wrapper(*args, **kwargs):
            return qnode(*args, **kwargs)
        return wrapper
    return qfunc_decorator

i.e. if the function is defined with multiple positional arguments, it must be called with a single argument, that is unwrapped behind the scenes. This is confusing, but for some reason, the only way I could get autograd to work with multiple arguments; otherwise I get ArgNum errors.

@smite, do you have any suggestions?

@josh146 josh146 added the interface 🔌 Classical machine-learning interfaces label Aug 29, 2018
@mariaschuld
Copy link
Contributor

mariaschuld commented Aug 29, 2018

I am very much in favour of 1. However, we should keep in mind that a circuit might not take any data, or even any parameters. For example when the quantum node is already optimized and one wants to now optimize a classical node, the parameters of the quantum node are fixed. Or if the quantum node is part of the ML model but not trainable (think of the kernel methods where the quantum node takes data and computes kernel values that are part of a classically trained model).

@josh146
Copy link
Member Author

josh146 commented Aug 29, 2018

Actually, thinking about it some more, do we need to put constraints on the arguments of the quantum function? Perhaps we would need constraints on the arguments to the cost function, since that is being passed on to the optimizer.

@smite
Copy link
Contributor

smite commented Aug 29, 2018

My suggestion is to leave the QNode fairly abstract, a R^n \to R^m mapping, with one input argument and one output argument, which are NumPy 1D arrays. The classical cost function can distinguish between trainable parameters and data. See for example tests/test_optimization.py, especially the map_data() function there.

@co9olguy
Copy link
Member

Programmatically, there is not really any need to distinguish between data and parameters for quantum functions. Within the variational approach, these are all used the same way: as arguments to one-parameter gates. Later on, we can tell the optimizer which we are optimizing (and this retains the flexibility to 'freeze' some parameters while updating others). Let's remember that something being a "parameter" of the quantum circuit does not require that it is trainable/updatable. A parameter is simply something which determines which function from the class of possible functions is the one that is applied to the inputs.

However, there is an argument for making things conceptually clear for the user. Users might not be too familiar with the ideas developing around variational circuits, especially the notion of circuit gradients. Explicitly specifying which parts of the circuit are parameters will make it clearer what is happening with the automatic differentiation.

@co9olguy
Copy link
Member

@smite I am thinking along the same lines, but does the input have to be a 1D array? Doesn't autograd support tuples, lists, nested inputs as well?

@mariaschuld
Copy link
Contributor

I think there could be two options. Make circuit(...) something that has fully user defined inputs, or use keyword arguments "weights" and "data" (which default to None). If it is user defined, do we get into trouble because computing the gradient of a QNode assumes that all arguments are trainable parameters?

@mariaschuld
Copy link
Contributor

By the way, can we call all trainable parameters simpy "weights"? That is clearly separated from circuit parameters, hyperparameters and has an intuitive meaning in ML...

@co9olguy
Copy link
Member

co9olguy commented Aug 29, 2018

@smite: an example based on my earlier comment:
There is an neural network example in autograd where they structure the params into a list of tuples (rather than a 1D array). This does not impact the automatic differentiation in any way, but it makes things much easier to parse for anyone reading the code. Can we do something similar for a Qnode, where the first argument is the params, but it can come in any form that auto grad supports, e.g.:

def my_quantum_function(params):
    # params is a list of (weights, bias) tuples, 
    # i.e., [(W_1, b_1),(W_2, b_2),(W_3, b_3),...]
    for W, b in params:
        Sgate(W)
        Dgate(b)
        ...

This would certainly make things more flexible for the user

@co9olguy co9olguy changed the title The arguments of the quantum function circuit() The inputs and outputs of the quantum function circuit() Aug 29, 2018
@co9olguy
Copy link
Member

co9olguy commented Aug 29, 2018

As @smite mentions above, we likely want to allow for a quantum node to output a vector in R^m. This has multiple use-cases:

  1. a batch of outputs, all measuring the same expectation values, but for different inputs
  2. a set of expectation values, each measured on the same circuit, but potentially for different operators (like how in VQE we measure multiple output operators to build up the Hamiltonian)
  3. a set of expectation values, each measured on the same circuit, but on different qubits/qumodes

A smart way to do this is for us to make measurements/expvals a separate abstraction. All the gates a user declares in my_quantum_function get put in the same same circuit, but the user can declare multiple measurements that the device is responsible for evaluating expectation values of. The device should evaluate each of these separately (on the same input circuit), then the plugin stitches them together and returns the collection back to openqml as a list/array.

Sketch pseudocode:

def my_quantum_function(...):
    # declare the gates of the circuit
    RotX(...)
    RotY(...)
    CNOT(...)
   
    # now declare the measurements that should be performed
    # on the above circuit. The following code should return a
    # 3-dimensional vector of expectation values
    return Expectation([SigmaX, SigmaY, SigmaZ])

@mariaschuld
Copy link
Contributor

Two quick comments, if I may:

  • How would the pseudocode example reflect that the Sigmas act on user-defined qubits?
  • If "Expectation" can also be an array of measurement results (if in the device the shots are >0), isn't the name misleading since the user has to average in order to estimate the expectation?

Maybe Expectation could have attributes
"shots",
"measurement_results" (listing all samples of the measurement)
"expectation" (averaging over samples if shots>0, else the simulated exact value),
"exact" (True if it is the exact value in case of simulations)

Sorry just contributing my two cents...

@cgogolin
Copy link
Contributor

Some thoughts from my side:

  1. As @josh146 said already said above: Can we not leave circuit() up to the user? The optimizer should only care about cost(). A user should in principle even be able to define cost() without referring to a circuit, no?
  2. I think it would be good to avoid the * syntax.
  3. It would be great if the weights/parameters could also be given as dictionaries. This seems to be the most "natural" data type, especially for more complex circuits. This would allow the user to come up with their own naming scheme for the weights. The trained weights should then likewise be returned as an dictionary, so that the user can easily retrieve a specific weight they might be interested in.
  4. If the return value of Expectation() can then be either a description of the statistics (e.g., expectation value and maybe variance), or a specimen from the statistics, i.e., a finite set of samples, then maybe could call it measurement_statistics()?

@mariaschuld
Copy link
Contributor

Quick comment on number 3: The dictionary idea is indeed important for more complex machine learning tasks, but if I am not mistaken this would cause major fixes because we cannot apply autograd directly to cost. I thought about this at length for the QMLT and came to the conclusion that within a realistic 5-year timeframe, having weights as nested arrays or tuples is enough for the type of experiments people do. In 5 years we could always wrap a more abstract argument type around everything. What do you think @cgogolin?

@cgogolin
Copy link
Contributor

If this causes major headaches with autograd, then let's forget about it. I naively thought that it should be easy to serialize a dictionary into an array, then use autograd, and then de-serialize the array back into a dictionary.

@josh146
Copy link
Member Author

josh146 commented Aug 30, 2018

@cgogolin I agree with your points 1, 2. We should be able to support both def circuit(x,y,z), def circuit(params), and any combination of the two (even including keyword arguments - these could be automatically excluded/included from autograd); the only constraint would be ensuring all work with autograd.

@co9olguy, regarding your proposal for expectation values - it makes sense to implement multiple expectation values on different wires, that can be run by a single hardware device:

def circuit():
   ...
   qm.expectation.PauliZ(0)
   qm.expectation.PauliZ(1)
   qm.expectation.PauliZ(3)

which would then automatically return a vector to the user. (Or perhaps we could use a return statement? more of an interface decision).

However, for multiple expectation values on the same wire, I would rather the user define a new device/qnode altogether, like @mariaschuld does in her VQE example:

def ansatz(params):
   qm.Rot(...)
   qm.CNOT(...)

@qfunc
def circuit_meas_x(params):
   ansatz(params)
   qm.expectation.PauliX(1)

@qfunc
def circuit_meas_z(params):
   ansatz(params)
   qm.expectation.PauliZ(1)

This already works with the current refactor, and has the benefits that:

  1. While it is more verbose, it is simple, clear, and very low level - design decisions are left up to the user.
  2. It is a bare-bones solution that doesn't require additional design constraints now - it gives us the flexibility to make these decisions in later versions.
  3. I like that it encapsulates the idea that each QNode is a single quantum device/circuit that can be run agnostically on different frameworks, without any nuance about having to re-run the operations and dynamically change the expectation/measurement.

Thoughts?

@cgogolin
Copy link
Contributor

Concerning multiple measurements on different wires: Keep in mind that some backends/plugins only support one measurement. IBM for example only allows to measure all qubits at once and only exactly one time. For this backend in the ProjectQ plugin is thus only have one observable available wich I would call "AllPauliZ" and I somehow need to "return" (i.e., save to self._out) multiple values from/in execute(). How shall I do that? What are the expected/legal types for self._out after execute()?

@josh146
Copy link
Member Author

josh146 commented Aug 30, 2018

Good point. self._out should ideally be a list, in order to support multiple measurements

@co9olguy
Copy link
Member

I'm hearing arguments in favour of one Qnode <-> one measurement, and there are also suggestions (my own included) to have multiple measurements per Qnode.

As @cgogolin points out, we will be constrained by what the plugins/backends actually allow. Since our main qubit plugin only supports one measurement, I'm thinking it it is the path of least resistance to force Qnodes to only have one measurement for our initial version. Multiple measurements can be stitched together from multiple Qnode outputs.

Any contrary opinions on this particular point?

@cgogolin
Copy link
Contributor

+1 for only one measurement, but keep in mind that even a sigle measurement can return multiple values.

@mariaschuld
Copy link
Contributor

Maybe we can speak about (maximum) one measurement per subsystem per QNode. We do not get into trouble with non-commuting measurements then.

@co9olguy co9olguy changed the title The inputs and outputs of the quantum function circuit() The inputs and outputs of the quantum function circuit Aug 31, 2018
@co9olguy
Copy link
Member

Ok let's go with one Qnode <-> max one expectation value per subsystem as Maria suggested

@co9olguy co9olguy modified the milestones: version 0.1, alpha Aug 31, 2018
@cgogolin
Copy link
Contributor

cgogolin commented Sep 6, 2018

Commit 8a3237b added the return statement return qm.expectation.PauliZ(1) to circuit(), but, as far as I can tell, PauliZ(1) not does not actually return anything. Can you please update me about the outcome of the discussion on return statements in circuit()?

@cgogolin
Copy link
Contributor

cgogolin commented Sep 6, 2018

@josh146 told me that the plan is to "enforce" the return via documentation but leave it purely as sytactic sugar, i.e., it doesn't actually do anything. I think I like this very much! It only gets a bit subtle when when multiple values have to be returned. How is the syntax supposed to look like in this case?

Maybe like this?

def circuit():
   ...
   return [qm.expectation.PauliZ(0), qm.expectation.PauliZ(1), qm.expectation.PauliZ(3)]

This relates to my question on the lifecycle of plugins. Are they expected to be able to cope with multiple calls to expectation()? Could it happen that if the user uses some fancy data type to wrap the multiple return values (instead of an [...]array) that the calls toexpectation()` do not happen in the right order?

@josh146
Copy link
Member Author

josh146 commented Sep 8, 2018

The way it currently works, where return is simply syntactic convention (but not explicitly used in the codebase), this would also work fine with minimal adjustments. Consider the case above:

def circuit():
   ...
   return [qm.expectation.PauliZ(0), qm.expectation.PauliZ(1), qm.expectation.PauliZ(3)]

On the function return, Python will evaluate the three expectation calls from left-to-right. The simplest solution is to modify device._observe to act in a similar manner to device._queue, and 'queue' the observable for each wire when called.

Since Python will always evaluate them from left-to-right, this will preserve the order provided by the user, and the plugin will return the resulting expectation values in the same order when looping through device._observe.

@smite
Copy link
Contributor

smite commented Sep 8, 2018

I would suggest making the return statement functional. This can be done with the syntax

return [qm.expectation.PauliZ(0), qm.expectation.PauliZ(1), qm.expectation.PauliZ(3)]

qm.expectation.PauliZ(3) returns an Expectation instance, so the circuit-defining function returns a list of them. The caller can go through that list, and store in each Expectation instance its index in the list. This enables Device.execute_queued() to construct a corresponding Numpy array of the measured expectation values and return it.

Edit:
The difference to Josh's suggestion above is that only the expectation values that are explicitly returned affect the output of the circuit.

@josh146
Copy link
Member Author

josh146 commented Sep 8, 2018

@smite I am currently implementing exactly that!

@josh146
Copy link
Member Author

josh146 commented Sep 8, 2018

Preliminarily implemented in a42497b. Note that this makes the return statement required, simply by checking that it is provided. This is a temporary solution to #31.

@co9olguy
Copy link
Member

Closing this issue as the API is largely established at this point. If there are any bugs, report them in separate issues

antalszava added a commit that referenced this issue Nov 5, 2022
* Structure

* Struct jax interface

* First draft

* Single measurement is working

* First derivative

* tests

* Add tests

* x64 Jax testing

* Cleanup

* more cleanup

* More tests

* More tests

* QNode testing

* More tests pass

* Typos in tests

* Test JVP structure.

* More tests

* More tests

* More tests

* Typoo

* Coverage

* test

* Jax import test

* Typo

* Trigger CI

* Update param shift

* Docstrings

* Add tests

* JVP

* Docstrings

* Docstrings

* more line

* Pragma

* First draft

* Add tests

* Apply suggestions from code review

Co-authored-by: Edward Jiang <34989448+eddddddy@users.noreply.github.com>
Co-authored-by: antalszava <antalszava@gmail.com>

* Apply suggestions from code review

Co-authored-by: Edward Jiang <34989448+eddddddy@users.noreply.github.com>

* Update from revie

* typo

* Update tests

* missing test

* Apply suggestions from code review

Co-authored-by: antalszava <antalszava@gmail.com>
Co-authored-by: Edward Jiang <34989448+eddddddy@users.noreply.github.com>

* error tracer

* Apply suggestions from code review

Co-authored-by: antalszava <antalszava@gmail.com>

* Review

* Comment from review

* More tests

* Test

* Add device back

* Update with finite diff

* Add ShotTuple

* Shots default is non

* Add more tests

* Black

* Trigger CI

* Review

* Review

* Update tests/returntypes/test_jax_qnode_new.py

Co-authored-by: antalszava <antalszava@gmail.com>

* Update pennylane/interfaces/jax.py

Co-authored-by: antalszava <antalszava@gmail.com>

* black

* Separate tests

* Tests jac

* move finites shots to the same cases as shots=None

* skip adjoint finite shots

* no more warning

Co-authored-by: Edward Jiang <34989448+eddddddy@users.noreply.github.com>
Co-authored-by: antalszava <antalszava@gmail.com>
AlbertMitjans added a commit that referenced this issue Nov 30, 2022
* Fix test

* Fix test

* Small fix

* Add _Expectation class

* Add changelog entry

* Coverage

* Refactor MeasurementProcess

* Fix tests

* Fix tests

* Use child classes

* More refactors

* Address comments

* Add interface tests

* Change state type

* Change state type

* Add test

* Remove error

* Remove comment

* Add tests

* Add tests

* Some changes

* Small fix

* Fix types

* Fix types

* Remove numpy usage

* Fix types

* Fix types

* Add mark

* Revert

* Remove return_type argument

* Small Fix

* Small Fix

* Fix classical shadow

* Small fix

* Small fix

* Use if self.wires

* Small fix

* Fix wire_map

* Fix wire_map

* Fix wire_map

* Revert

* Revert

* Add classical shadow logic to the MP class

* Small fix

* Fix tests

* Fix tests

* Fix tests

* Fix tests

* Fix tests

* Fix tests

* Fix tests

* Add test

* Add wire_order to process_samples

* Docstring

* Add wire_order to process_samples

* Small fix

* Small fix

* Add wire_order to process_samples

* Add wire_order to process_samples

* Change tests

* Add wire_order to process_samples

* Add breaking change message

* Change tests

* Change tests

* Add wire_order to process_samples

* Change tests

* Fix tests

* Fix tests

* Add wire_order to process_samples

* Change tests

* Coverage

* Coverage

* Fix tests

* Add tests

* Revert

* Add tests

* Add tests

* Remove duplicated test

* Add tests

* Small changes

* Fix tests

* black

* Change name

* Add CustomMeasurement

* Small fix

* Small fix

* Small fix

* test (ClassicalShadow): 🧪 Test ClassicalShadow.process method

* Add changelog entry

* test (ClassicalShadow): 🧪 Fix mocker.spy

* revert (ClassicalShadow): ⏪ Remove process_state.

* Revert

* feat: ✨ Add _ShadowExpval class.

* Fix tests

* Fix test

* Fix

* Fix docs

* Fix docs

* Fix types

* Fix types

* Fix docstrings

* fix test

* Add changelog entry

* Remove docs

* Remove docs

* Fix docstrings

* Small change

* Remove return_type

* Fix

* Fix

* Fix

* fix

* fix

* fix

* Fix tests

* Remove mocker

* Add tests

* Fix tests

* Coverage

* Update doc/releases/changelog-dev.md

* Revert merge

* Revert tests

* Revert

* Copy hamiltonian

* Use seed

* Change docstrings

* Change instance check.

* Fix

* Fix

* Remove dead code

* Fix tests

* Add deprecation warning

* Fix docstring

* Add changelog entry

* Fix docstring

* Add type

* Remove type

* Change warning type

* Revert

* Add changelog entry

* Add changelog entry

* Make return_type None by default

* Coverage

* Update tests/measurements/test_counts.py

* Remove unused imports

* Add import

* Move import

* Update tests/measurements/test_measurements.py

* Move ABC inheritannce

* move ABC

* Move import
AlbertMitjans added a commit that referenced this issue Nov 30, 2022
* Add _Expectation class

* Add changelog entry

* Coverage

* Refactor MeasurementProcess

* Fix tests

* Fix tests

* Use child classes

* More refactors

* Address comments

* Add interface tests

* Change state type

* Change state type

* Add test

* Remove error

* Remove comment

* Add tests

* Add tests

* Some changes

* Small fix

* Fix types

* Fix types

* Remove numpy usage

* Fix types

* Fix types

* Add mark

* Revert

* Remove return_type argument

* Small Fix

* Small Fix

* Fix classical shadow

* Small fix

* Small fix

* Use if self.wires

* Small fix

* Fix wire_map

* Fix wire_map

* Fix wire_map

* Revert

* Revert

* Add classical shadow logic to the MP class

* Small fix

* Fix tests

* Fix tests

* Fix tests

* Fix tests

* Fix tests

* Fix tests

* Fix tests

* Add test

* Add wire_order to process_samples

* Docstring

* Add wire_order to process_samples

* Small fix

* Small fix

* Add wire_order to process_samples

* Add wire_order to process_samples

* Change tests

* Add wire_order to process_samples

* Add breaking change message

* Change tests

* Change tests

* Add wire_order to process_samples

* Change tests

* Fix tests

* Fix tests

* Add wire_order to process_samples

* Change tests

* Coverage

* Coverage

* Fix tests

* Add tests

* Revert

* Add tests

* Add tests

* Remove duplicated test

* Add tests

* Small changes

* Fix tests

* black

* Change name

* Add CustomMeasurement

* Small fix

* Small fix

* Small fix

* test (ClassicalShadow): 🧪 Test ClassicalShadow.process method

* Add changelog entry

* test (ClassicalShadow): 🧪 Fix mocker.spy

* revert (ClassicalShadow): ⏪ Remove process_state.

* Revert

* feat: ✨ Add _ShadowExpval class.

* Fix tests

* Fix test

* Fix

* Fix docs

* Fix docs

* Fix types

* Fix types

* Fix docstrings

* fix test

* Add changelog entry

* Remove docs

* Remove docs

* Fix docstrings

* Small change

* Remove return_type

* Fix

* Fix

* Fix

* fix

* fix

* fix

* Fix tests

* Remove mocker

* Add tests

* Fix tests

* Coverage

* Update doc/releases/changelog-dev.md

* Revert merge

* Revert tests

* Revert

* Copy hamiltonian

* Use seed

* Change docstrings

* Change instance check.

* Fix

* Fix

* Remove dead code

* Fix tests

* Add deprecation warning

* Fix docstring

* Add changelog entry

* Fix docstring

* Add type

* Remove type

* Change warning type

* Revert

* Add changelog entry

* Refactor MP

* Add changelog entry

* Make return_type None by default

* Coverage

* Update tests/measurements/test_counts.py

* Remove unused imports

* Add import

* Move import

* Update tests/measurements/test_measurements.py

* Move ABC inheritannce

* move ABC

* Move import
AlbertMitjans added a commit that referenced this issue Dec 1, 2022
* Refactor MeasurementProcess

* Fix tests

* Fix tests

* Use child classes

* More refactors

* Address comments

* Add interface tests

* Change state type

* Change state type

* Add test

* Remove error

* Remove comment

* Add tests

* Add tests

* Some changes

* Small fix

* Fix types

* Fix types

* Remove numpy usage

* Fix types

* Fix types

* Add mark

* Revert

* Remove return_type argument

* Small Fix

* Small Fix

* Fix classical shadow

* Small fix

* Small fix

* Use if self.wires

* Small fix

* Fix wire_map

* Fix wire_map

* Fix wire_map

* Revert

* Revert

* Add classical shadow logic to the MP class

* Small fix

* Fix tests

* Fix tests

* Fix tests

* Fix tests

* Fix tests

* Fix tests

* Fix tests

* Add test

* Add wire_order to process_samples

* Docstring

* Add wire_order to process_samples

* Small fix

* Small fix

* Add wire_order to process_samples

* Add wire_order to process_samples

* Change tests

* Add wire_order to process_samples

* Add breaking change message

* Change tests

* Change tests

* Add wire_order to process_samples

* Change tests

* Fix tests

* Fix tests

* Add wire_order to process_samples

* Change tests

* Coverage

* Coverage

* Fix tests

* Add tests

* Revert

* Add tests

* Add tests

* Remove duplicated test

* Add tests

* Small changes

* Fix tests

* black

* Change name

* Add CustomMeasurement

* Small fix

* Small fix

* Small fix

* test (ClassicalShadow): 🧪 Test ClassicalShadow.process method

* Add changelog entry

* test (ClassicalShadow): 🧪 Fix mocker.spy

* revert (ClassicalShadow): ⏪ Remove process_state.

* Revert

* feat: ✨ Add _ShadowExpval class.

* Fix tests

* Fix test

* Fix

* Fix docs

* Fix docs

* Fix types

* Fix types

* Fix docstrings

* fix test

* Add changelog entry

* Remove docs

* Remove docs

* Fix docstrings

* Small change

* Remove return_type

* Fix

* Fix

* Fix

* fix

* fix

* fix

* Fix tests

* Remove mocker

* Add tests

* Fix tests

* Coverage

* Update doc/releases/changelog-dev.md

* Revert merge

* Revert tests

* Revert

* Copy hamiltonian

* Use seed

* Change docstrings

* Change instance check.

* Fix

* Fix

* Remove dead code

* Fix tests

* Add deprecation warning

* Fix docstring

* Add changelog entry

* Fix docstring

* Add type

* Remove type

* Change warning type

* Revert

* Add changelog entry

* Refactor MP

* Refactor MP

* Coverage

* Add changelog entry

* Make return_type None by default

* Coverage

* Update tests/measurements/test_counts.py

* Remove unused imports

* Add import

* Move import

* Update tests/measurements/test_measurements.py

* Move ABC inheritannce

* Update tests/measurements/test_vn_entropy.py

Co-authored-by: Edward Jiang <34989448+eddddddy@users.noreply.github.com>

* Call _shape_new

* refactor (State): Raise error later in shape method.

* refactor (State): Raise error later in shape method.

* test: 🧪 Change skip message

* move ABC

* Move import

Co-authored-by: Edward Jiang <34989448+eddddddy@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discussion 💬 Requiring extra discussion interface 🔌 Classical machine-learning interfaces
Projects
No open projects
API and user interface
  
Awaiting triage
Development

No branches or pull requests

5 participants