atcollab / at

Accelerator Toolbox
Apache License 2.0
48 stars 31 forks source link

Variables, parameters and observables #696

Closed lfarv closed 7 months ago

lfarv commented 7 months ago

This replaces #603. We introduce standard ways of parametrising a lattice, varying any quantity and looking at any resulting quantity. There are 3 main classes:

Variables

Variables are references to any scalar quantity. AT includes two predefined variable classes referring to scalar attributes of lattice elements:

Variable referring to other quantities may be created by:

Parameters

Parameters are objects of class Param which can be used as Element attributes instead of numeric values.

Arithmetic combinations of parameters create new read-only parameters of class ParamBase, whose value is permanently kept up-to-date. This is useful to introduce correlation between attributes of different elements.

Presently the use of parameters is limited to Element attributes, but it may me extended in the future (ex: Lattice attributes).

Observables

Observables provide a unified way accessing a large quantity of figures resulting from various computations on lattices. They may be used in parameter scans, matching, response matrices…

AT provides a number of specific observables sharing a common interface, inherited from the Observable base class. They are:

An Observable has optional target, weight and bounds attributes for matching. After evaluation, it has the following main properties:

Custom Observables may be created by providing the adequate evaluation function.

For evaluation, observables must be grouped in an ObservableList which optimises the computation, avoiding redundant function calls. ObservableList provides the evaluate method, and the values, deviations, residuals and sum_residuals properties, among others.

The documentation for the branch is available here. Please read and comment.

lfarv commented 7 months ago

The new classes introduce name conflicts with existing Variable, ElementVariable classes. They also change the interface of matching. Keeping two slightly different matching functions is not desirable, so the idea is to move from the old scheme to the new one and replace the match function. The proposed strategy is:

  1. Starting with this PR, the access to the new classes and functions is:
    from at.future import match, ElementVariable, ...

    The old classes are still available without any change with:

    from at import match, ElementVariable, ...

    or using at.match(...), at.ElementVariable(...).

  2. At some point (version 1.0?), we will switch the default to the new scheme, and the old one will need something like:
    from at.deprecated import match, ElementVariable, ...
simoneliuzzo commented 7 months ago

Dear @lfarv,

this is a very big pull request (26 files!). I have no idea how to "test" it completely.

There are many new classes, many new features. How to test each of them in realistic conditions?

I may test the matching for example.

The known breaking of backward compatibility is also a major issue for me. I stopped using matlab AT for broken backward compatibility. May be some other users will stop using pyAT for this broken backward compatibility. There are big competitors out there, such as XSuite... May be new names could be a better solution?

I am looking at the notebooks in docs/p/notebooks. VERY USEFUL to get trough all this work!

Observable.ipynb

parameters.ipynb

I think that it would be wise to make the above lines work as expected from the user (exactly as in the example!) rather than giving a warning. There are several "work arrounds" explained in the notebook. It remains a bit disappointing. Why going trough all this development if we may not do what we wanted to do when this was started (easy, user friendly, parameterized matching)? I think that the qd1.PolynomB[1] = Param(-0.3) would have to work in the final version.

variables.ipynb

_testvariables.ipynb

These notebook examples are really a good way to understand what the code does. Could we have more examples for the use of other classes mentioned in this pull request? MartrixObservable, GlobalLatticeObservable, Trajectory and Gemoetry observables?

thank you

best regards

Simone

lfarv commented 7 months ago

Hi @simoneliuzzo. Very nice review of the present state of things, thanks !

I'll give today the "easy" answers, I'll go step by step later for the rest.

Variables

variables.ipynb

  • seems incomplete

Work in progess, there is now a better version

_testvariables.ipynb

  • Why is it needed to make VariableLists and ObservableList? Could they simply be python lists?

Just to add vectorised methods corresponding to the individual ones. Examples: VariableList.set(values) to apply a vector of values, ObservableList.values to get a list of all Observable values, ObservableList.sum_residuals, etc. Plus pretty-printing of ObservableLists.

Observables

  • It seams the user has to remember a quite large number of *Observables. I find this not so user friendly. More examples could be of help. Else switching between the various modes with keywords in the same Observable definition (r_in swithes to Trajectory, etc..)?

This is a design decision. When reading the code, it makes clear what each object does, and it makes each class more efficient by avoiding "switch ... case..." tests. What could be done would be to add a kind of factory function on top, which would create the right class based on ... what? A keyword, a flag ? that's not easier to remember. That's why the notebook goes through all classes to give an example of each.

Could we have more examples for the use of other classes mentioned in this pull request? MartrixObservable, GlobalLatticeObservable, Trajectory and Gemoetry observables?

observables.ipynb gives one example of each of all the available Observables. But it can easily be modified, suggestions are welcome!

  • Can statfun argument be used also with LocalOpticsObservable? (I see myself using this LocalOpticsObservable very often)

Of course it can! See in the given examples

LocalOpticsObservable(at.Monitor, 'beta', plane='v', statfun=np.amax)

to get the maximum vertical β on monitors.

  • The call LocalOpticsObservable([33, 101], phase_advance,... Will compute internally at all locations between 0 and 101? if not the phase_advances may be incorrect (may be this has been solved in an other pullrequests?).

This is triggered by the use_integer=True keyword. See the LocalOpticsObservable help for its explanation.

  • can regexp be used to specify LocalOpticsObservable locations?

Not now. You can always do it in two steps

refs = ring.get_bool_index("pattern",  regexp=True)
pbs = LocalOpticsObservable(refs, 'beta', ...)

It can easily be added. I'll do it. But now, regexp is still not accepted in the dozens of optics functions using a refpts argument. So why accepting it here?

swhite2401 commented 7 months ago

Hello @lfarv, thanks for reviving this. I must admit that this is the result of long discussions and many iterations on github and private emails and II have completely lost track and forgot what were the decision taken.

I completely agree with @simoneliuzzo that this is too big to properly review and the github discussion on such large PR will quickly become a total mess. I would really appreciate that this PR is split as follows (I think this was already requested in the previous PR): 1-variables 2-Observables 3-Parameters 4-matching

We can then validate each development gradually and potentially improve the next ones using the review comments. So let's start with variables, I already have several points to discuss on that part. I wait for the new PR to send them/

lfarv commented 7 months ago

@swhite2401: the split is done.

However, variables and parameters are so intricate that any change on parameters in the proposed step 3 would change again the modules approved in step 1. So we go for: 1- variables and parameters 2- observables 3- matching 4- response matrices