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}