001/* 
002 * Copyright 2015 Alexander Nozik.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package hep.dataforge.datafitter;
017
018import hep.dataforge.meta.Meta;
019import hep.dataforge.context.GlobalContext;
020import hep.dataforge.description.Element;
021import hep.dataforge.exceptions.NameNotFoundException;
022import hep.dataforge.io.log.Logable;
023import hep.dataforge.io.XMLAnnotationParser;
024import hep.dataforge.maths.NamedDoubleArray;
025import hep.dataforge.maths.NamedDoubleSet;
026import hep.dataforge.names.Names;
027import java.text.ParseException;
028import java.util.Collection;
029import java.util.HashMap;
030import java.util.LinkedHashMap;
031import java.util.List;
032import java.util.Scanner;
033
034/**
035 * Реализация набора параметров, которая будет потом использоваться в Result,
036 * Fitter и Spectrum
037 *
038 * Подразумевается, что ParamSet обязательно содержит помимо значения хотя бы
039 * ошибку.
040 *
041 * @author Alexander Nozik
042 * @version $Id: $Id
043 */
044public class ParamSet implements NamedDoubleSet {
045
046    /**
047     * <p>
048     * fromAnnotation.</p>
049     *
050     * @param cfg a {@link hep.dataforge.meta.Meta} object.
051     * @return a {@link hep.dataforge.datafitter.ParamSet} object.
052     */
053    @Element(name = "param", multiple = true, description = "The fit prameter", target = "method::hep.dataforge.datafitter.Param.fromAnnotation")
054    @Element(name = "params", description = "Could be used as a wrapper for 'param' elements. Used solely on purpose of xml readability.")
055    public static ParamSet fromAnnotation(Meta cfg) {
056        if (cfg.hasNode("params")) {
057            cfg = cfg.getMeta("params");
058        }
059
060        if (cfg.hasNode("param")) {
061            List<Meta> params;
062            params = cfg.getMetaList("param");
063            ParamSet set = new ParamSet();
064            for (Meta param : params) {
065                set.setPar(Param.fromAnnotation(param));
066            }
067            return set;
068        } else {
069            //Возрвщвем пустой лист. Нужно для совместимости со значениями параметров по-умолчанию
070            return new ParamSet();
071        }
072    }
073
074    /**
075     * <p>
076     * fromXML.</p>
077     *
078     * @param xml a {@link java.lang.String} object.
079     * @return a {@link hep.dataforge.datafitter.ParamSet} object.
080     * @throws java.text.ParseException if any.
081     */
082    public static ParamSet fromXML(String xml) throws ParseException {
083        XMLAnnotationParser parser = new XMLAnnotationParser();
084        return fromAnnotation(parser.fromString(xml));
085    }
086
087    /**
088     * Read parameter set from lines using 'name'       = value ± error (lower,upper)
089     * syntax
090     *
091     * @param str a {@link java.lang.String} object.
092     * @return a {@link hep.dataforge.datafitter.ParamSet} object.
093     */
094    public static ParamSet fromString(String str) {
095        Scanner scan = new Scanner(str);
096        ParamSet set = new ParamSet();
097        while (scan.hasNextLine()) {
098            set.setPar(Param.fromString(scan.nextLine()));
099        }
100        return set;
101    }
102
103    private final HashMap<String, Param> params;
104
105    /**
106     * Generates set of parameters with predefined names.
107     *
108     * @param names an array of {@link java.lang.String} objects.
109     * @throws hep.dataforge.exceptions.NameNotFoundException if any.
110     */
111    public ParamSet(String[] names) throws NameNotFoundException {
112        int num = names.length;
113        this.params = new LinkedHashMap<>();
114        int i, j;
115
116        for (i = 0; i < num - 1; i++) { //Проверяем, нет ли совпадающих имен
117            for (j = i + 1; j < num; j++) {
118                if (names[i].equals(names[j])) {
119                    throw new NameNotFoundException("ParamSet naming error: Names are not unique");
120                }
121            }
122        }
123
124        for (i = 0; i < num; i++) {
125            this.params.put(names[i], new Param(names[i]));
126        }
127    }
128
129    /**
130     * <p>
131     * Constructor for ParamSet.</p>
132     *
133     * @param other a {@link hep.dataforge.datafitter.ParamSet} object.
134     */
135    public ParamSet(ParamSet other) {
136        this.params = new LinkedHashMap<>();
137        for (Param par : other.getParams()) {
138            params.put(par.name(), par.copy());
139        }
140    }
141
142    /**
143     * <p>
144     * Constructor for ParamSet.</p>
145     */
146    public ParamSet() {
147        this.params = new LinkedHashMap<>();
148    }
149
150    /**
151     * <p>
152     * Constructor for ParamSet.</p>
153     *
154     * @param values a {@link hep.dataforge.maths.NamedDoubleSet} object.
155     */
156    public ParamSet(NamedDoubleSet values) {
157        this.params = new LinkedHashMap<>(values.names().getDimension());
158        for (String name : values.names()) {
159            this.params.put(name, new Param(name, values.getValue(name)));
160        }
161    }
162
163    /**
164     * <p>
165     * copy.</p>
166     *
167     * @return a {@link hep.dataforge.datafitter.ParamSet} object.
168     */
169    public ParamSet copy() {
170        return new ParamSet(this);
171    }
172
173    /**
174     * Returns link to parameter with specific name. Возвращает параметр по его
175     * имени.
176     *
177     * @param str a {@link java.lang.String} object.
178     * @return null if name is not found.
179     * @throws hep.dataforge.exceptions.NameNotFoundException if any.
180     */
181    public Param getByName(String str) throws NameNotFoundException {
182        Param res = this.params.get(str);
183        if (res != null) {
184            return res;
185        } else {
186            throw new NameNotFoundException(str);
187        }
188    }
189
190    /**
191     * {@inheritDoc}
192     */
193    @Override
194    public int getDimension() {
195        assert params != null;
196        return params.size();
197    }
198
199    /**
200     * <p>
201     * getError.</p>
202     *
203     * @param str a {@link java.lang.String} object.
204     * @return a double.
205     * @throws hep.dataforge.exceptions.NameNotFoundException if any.
206     */
207    public double getError(String str) throws NameNotFoundException {
208        Param P;
209        P = this.getByName(str);
210        return P.getErr();
211    }
212
213    /**
214     * {@inheritDoc}
215     *
216     * @return
217     */
218    @Override
219    public Names names() {
220        return Names.of(this.params.keySet());
221    }
222
223    /**
224     * <p>
225     * getParErrors.</p>
226     *
227     * @param names a {@link java.lang.String} object.
228     * @return a {@link hep.dataforge.maths.NamedDoubleArray} object.
229     * @throws hep.dataforge.exceptions.NameNotFoundException if any.
230     */
231    public NamedDoubleArray getParErrors(String... names) throws NameNotFoundException {
232        if (names.length == 0) {
233            names = this.namesAsArray();
234        }
235        assert this.names().contains(names);
236
237        double[] res = new double[names.length];
238
239        for (int i = 0; i < res.length; i++) {
240            res[i] = this.getError(names[i]);
241
242        }
243        return new NamedDoubleArray(names, res);
244    }
245
246    /**
247     * <p>
248     * getParValues.</p>
249     *
250     * @param names a {@link java.lang.String} object.
251     * @return a {@link hep.dataforge.maths.NamedDoubleArray} object.
252     * @throws hep.dataforge.exceptions.NameNotFoundException if any.
253     */
254    public NamedDoubleArray getParValues(String... names) throws NameNotFoundException {
255        if (names.length == 0) {
256            names = this.namesAsArray();
257        }
258        assert this.names().contains(names);
259
260        double[] res = new double[names.length];
261
262        for (int i = 0; i < res.length; i++) {
263            res[i] = this.getValue(names[i]);
264
265        }
266        return new NamedDoubleArray(names, res);
267    }
268
269    /**
270     * <p>
271     * Getter for the field <code>params</code>.</p>
272     *
273     * @return a {@link java.util.Collection} object.
274     */
275    public Collection<Param> getParams() {
276        return params.values();
277    }
278
279    /**
280     * Returns a parameter set witch consists only of names presented as
281     * parameter (values are also copied).
282     *
283     * @param names a {@link java.lang.String} object.
284     * @return a {@link hep.dataforge.datafitter.ParamSet} object.
285     * @throws hep.dataforge.exceptions.NameNotFoundException if any.
286     */
287    public ParamSet getSubSet(String... names) throws NameNotFoundException {
288        if (names.length == 0) {
289            return this.copy();
290        }
291        int i;
292        ParamSet res = new ParamSet(names);
293        for (i = 0; i < names.length; i++) {
294            res.params.put(names[i], this.getByName(names[i]).copy());
295        }
296        return res;
297    }
298
299    /**
300     * {@inheritDoc}
301     *
302     * Метод возвращает значение параметра с именем str
303     * @param str
304     */
305    @Override
306    public double getValue(String str) throws NameNotFoundException {
307        Param P;
308        P = this.getByName(str);
309        return P.value();
310    }
311
312    /**
313     * {@inheritDoc}
314     */
315    @Override
316    public double[] getValues(String... names) {
317        return this.getParValues(names).getValues();
318    }
319
320    /**
321     * Searches set for a parameter with the same name and replaces it. Only
322     * link is replaced, use {@code copy} to make a deep copy.
323     *
324     * In case name not found adds a new parameter
325     *
326     * @param input a {@link hep.dataforge.datafitter.Param} object.
327     * @return a {@link hep.dataforge.datafitter.ParamSet} object.
328     */
329    public ParamSet setPar(Param input) {
330        this.params.put(input.name(), input);
331        return this;
332    }
333
334    /**
335     * <p>
336     * setPar.</p>
337     *
338     * @param name a {@link java.lang.String} object.
339     * @param value a double.
340     * @param error a double.
341     * @return a {@link hep.dataforge.datafitter.ParamSet} object.
342     */
343    public ParamSet setPar(String name, double value, double error) {
344        Param par;
345        if (!params.containsKey(name)) {
346            GlobalContext.instance().logString(Logable.LogTag.DEBUG, "Parameter with name '%s' not found. Adding a new parameter with this name.", name);
347            par = new Param(name);
348            this.params.put(name, par);
349        } else {
350            par = getByName(name);
351        }
352
353        par.setValue(value);
354        par.setErr(error);
355
356        return this;
357    }
358
359    /**
360     * <p>
361     * setPar.</p>
362     *
363     * @param name a {@link java.lang.String} object.
364     * @param value a double.
365     * @param error a double.
366     * @param lower a {@link java.lang.Double} object.
367     * @param upper a {@link java.lang.Double} object.
368     * @return a {@link hep.dataforge.datafitter.ParamSet} object.
369     */
370    public ParamSet setPar(String name, double value, double error, Double lower, Double upper) {
371        Param par;
372        if (!params.containsKey(name)) {
373            GlobalContext.instance().logString(Logable.LogTag.DEBUG, "Parameter with name '%s' not found. Adding a new parameter with this name.", name);
374            par = new Param(name);
375            this.params.put(name, par);
376        } else {
377            par = getByName(name);
378        }
379
380        par.setValue(value);
381        par.setErr(error);
382        par.setDomain(lower, upper);
383
384        return this;
385    }
386
387    /**
388     * <p>
389     * setParDomain.</p>
390     *
391     * @param name a {@link java.lang.String} object.
392     * @param lower a {@link java.lang.Double} object.
393     * @param upper a {@link java.lang.Double} object.
394     * @return a {@link hep.dataforge.datafitter.ParamSet} object.
395     * @throws hep.dataforge.exceptions.NameNotFoundException if any.
396     */
397    public ParamSet setParDomain(String name, Double lower, Double upper) throws NameNotFoundException {
398        Param Par;
399        Par = getByName(name);
400
401        Par.setDomain(lower, upper);
402        return this;
403    }
404
405    /**
406     * <p>
407     * setParError.</p>
408     *
409     * @param name a {@link java.lang.String} object.
410     * @param value a double.
411     * @return a {@link hep.dataforge.datafitter.ParamSet} object.
412     * @throws hep.dataforge.exceptions.NameNotFoundException if any.
413     */
414    public ParamSet setParError(String name, double value) throws NameNotFoundException {
415        Param Par;
416        Par = getByName(name);
417        Par.setErr(value);
418        return this;
419    }
420
421    /**
422     * method to set all parameter errors.
423     *
424     * @param errors a {@link hep.dataforge.maths.NamedDoubleSet} object.
425     * @return a {@link hep.dataforge.datafitter.ParamSet} object.
426     * @throws hep.dataforge.exceptions.NameNotFoundException if any.
427     */
428    public ParamSet setParErrors(NamedDoubleSet errors) throws NameNotFoundException {
429        if (!this.names().contains(errors.names())) {
430            throw new NameNotFoundException();
431        }
432        for (String name : errors.names()) {
433            this.setParError(name, errors.getValue(name));
434        }
435        return this;
436    }
437
438    /**
439     * <p>
440     * setParValue.</p>
441     *
442     * @param name parameter name.
443     * @param value a double.
444     * @return a {@link hep.dataforge.datafitter.ParamSet} object.
445     */
446    public ParamSet setParValue(String name, double value) {
447        Param par;
448        if (!params.containsKey(name)) {
449            GlobalContext.instance().logString(Logable.LogTag.DEBUG, "Parameter with name '%s' not found. Adding a new parameter with this name.", name);
450            par = new Param(name);
451            this.params.put(name, par);
452        } else {
453            par = getByName(name);
454        }
455
456        par.setValue(value);
457        return this;
458    }
459
460    /**
461     * method to set all parameter values.
462     *
463     * @param values a {@link hep.dataforge.maths.NamedDoubleSet} object.
464     * @return a {@link hep.dataforge.datafitter.ParamSet} object.
465     * @throws hep.dataforge.exceptions.NameNotFoundException if any.
466     */
467    public ParamSet setParValues(NamedDoubleSet values) throws NameNotFoundException {
468        if (!this.names().contains(values.names())) {
469            throw new NameNotFoundException();
470        }
471        int i;
472        for (String name : values.names()) {
473            this.setParValue(name, values.getValue(name));
474        }
475        return this;
476    }
477
478    /**
479     * <p>
480     * updateFrom.</p>
481     *
482     * @param set a {@link hep.dataforge.datafitter.ParamSet} object.
483     */
484    public void updateFrom(ParamSet set) {
485        for (Param p : set.getParams()) {
486            setPar(p);
487        }
488    }
489
490}