Compare commits
5 Commits
2fe04040c6
...
fc0393436f
Author | SHA1 | Date | |
---|---|---|---|
fc0393436f | |||
9228e6019c | |||
edbf8c05be | |||
|
f335d63659 | ||
|
c696a22f62 |
@ -6,7 +6,7 @@ plugins {
|
||||
id("org.jetbrains.kotlinx.kover") version "0.7.6"
|
||||
}
|
||||
|
||||
val attributesVersion by extra("0.1.0")
|
||||
val attributesVersion by extra("0.2.0")
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
|
@ -68,6 +68,9 @@ public operator fun ShapeND.component3(): Int = get(2)
|
||||
*/
|
||||
public fun ShapeND.toArray(): IntArray = array.copyOf()
|
||||
|
||||
/**
|
||||
* Provide internal content of [ShapeND]. Must not be modified.
|
||||
*/
|
||||
@UnsafeKMathAPI
|
||||
public fun ShapeND.asArray(): IntArray = array
|
||||
|
||||
|
@ -38,9 +38,9 @@ class MinuitParameter {
|
||||
* @param name a [String] object.
|
||||
* @param val a double.
|
||||
*/
|
||||
constructor(num: Int, name: String, `val`: Double) {
|
||||
constructor(num: Int, name: String, value: Double) {
|
||||
theNum = num
|
||||
theValue = `val`
|
||||
theValue = value
|
||||
theConst = true
|
||||
theName = name
|
||||
}
|
||||
@ -53,9 +53,9 @@ class MinuitParameter {
|
||||
* @param val a double.
|
||||
* @param err a double.
|
||||
*/
|
||||
constructor(num: Int, name: String, `val`: Double, err: Double) {
|
||||
constructor(num: Int, name: String, value: Double, err: Double) {
|
||||
theNum = num
|
||||
theValue = `val`
|
||||
theValue = value
|
||||
theError = err
|
||||
theName = name
|
||||
}
|
||||
@ -70,9 +70,9 @@ class MinuitParameter {
|
||||
* @param min a double.
|
||||
* @param max a double.
|
||||
*/
|
||||
constructor(num: Int, name: String, `val`: Double, err: Double, min: Double, max: Double) {
|
||||
constructor(num: Int, name: String, value: Double, err: Double, min: Double, max: Double) {
|
||||
theNum = num
|
||||
theValue = `val`
|
||||
theValue = value
|
||||
theError = err
|
||||
theLoLimit = min
|
||||
theUpLimit = max
|
||||
@ -288,8 +288,8 @@ class MinuitParameter {
|
||||
*
|
||||
* @param val a double.
|
||||
*/
|
||||
fun setValue(`val`: Double) {
|
||||
theValue = `val`
|
||||
fun setValue(value: Double) {
|
||||
theValue = value
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -77,8 +77,8 @@ abstract class MnApplication {
|
||||
* @param val a double.
|
||||
* @param name a [String] object.
|
||||
*/
|
||||
fun add(name: String, `val`: Double, err: Double) {
|
||||
theState.add(name, `val`, err)
|
||||
fun add(name: String, value: Double, err: Double) {
|
||||
theState.add(name, value, err)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,8 +90,8 @@ abstract class MnApplication {
|
||||
* @param val a double.
|
||||
* @param err a double.
|
||||
*/
|
||||
fun add(name: String, `val`: Double, err: Double, low: Double, up: Double) {
|
||||
theState.add(name, `val`, err, low, up)
|
||||
fun add(name: String, value: Double, err: Double, low: Double, up: Double) {
|
||||
theState.add(name, value, err, low, up)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,8 +100,8 @@ abstract class MnApplication {
|
||||
* @param name a [String] object.
|
||||
* @param val a double.
|
||||
*/
|
||||
fun add(name: String, `val`: Double) {
|
||||
theState.add(name, `val`)
|
||||
fun add(name: String, value: Double) {
|
||||
theState.add(name, value)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -471,8 +471,8 @@ abstract class MnApplication {
|
||||
* @param index a int.
|
||||
* @param val a double.
|
||||
*/
|
||||
fun setValue(index: Int, `val`: Double) {
|
||||
theState.setValue(index, `val`)
|
||||
fun setValue(index: Int, value: Double) {
|
||||
theState.setValue(index, value)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -482,8 +482,8 @@ abstract class MnApplication {
|
||||
* @param name a [String] object.
|
||||
* @param val a double.
|
||||
*/
|
||||
fun setValue(name: String?, `val`: Double) {
|
||||
theState.setValue(name, `val`)
|
||||
fun setValue(name: String?, value: Double) {
|
||||
theState.setValue(name, value)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -106,8 +106,8 @@ class MnMinos(fcn: MultiFunction?, min: FunctionMinimum?, stra: MnStrategy?) {
|
||||
val para = intArrayOf(par)
|
||||
val upar: MnUserParameterState = theMinimum!!.userState().copy()
|
||||
val err: Double = upar.error(par)
|
||||
val `val`: Double = upar.value(par) - err
|
||||
val xmid = doubleArrayOf(`val`)
|
||||
val value: Double = upar.value(par) - err
|
||||
val xmid = doubleArrayOf(value)
|
||||
val xdir = doubleArrayOf(-err)
|
||||
val ind: Int = upar.intOfExt(par)
|
||||
val m: MnAlgebraicSymMatrix = theMinimum!!.error().matrix()
|
||||
@ -121,7 +121,7 @@ class MnMinos(fcn: MultiFunction?, min: FunctionMinimum?, stra: MnStrategy?) {
|
||||
upar.setValue(ext, upar.value(ext) - xdev)
|
||||
}
|
||||
upar.fix(par)
|
||||
upar.setValue(par, `val`)
|
||||
upar.setValue(par, value)
|
||||
val toler = 0.1
|
||||
val cross = MnFunctionCross(theFCN, upar, theMinimum!!.fval(), theStrategy, errDef)
|
||||
val aopt: MnCross = cross.cross(para, xmid, xdir, toler, maxcalls)
|
||||
@ -330,8 +330,8 @@ class MnMinos(fcn: MultiFunction?, min: FunctionMinimum?, stra: MnStrategy?) {
|
||||
val para = intArrayOf(par)
|
||||
val upar: MnUserParameterState = theMinimum!!.userState().copy()
|
||||
val err: Double = upar.error(par)
|
||||
val `val`: Double = upar.value(par) + err
|
||||
val xmid = doubleArrayOf(`val`)
|
||||
val value: Double = upar.value(par) + err
|
||||
val xmid = doubleArrayOf(value)
|
||||
val xdir = doubleArrayOf(err)
|
||||
val ind: Int = upar.intOfExt(par)
|
||||
val m: MnAlgebraicSymMatrix = theMinimum!!.error().matrix()
|
||||
@ -345,7 +345,7 @@ class MnMinos(fcn: MultiFunction?, min: FunctionMinimum?, stra: MnStrategy?) {
|
||||
upar.setValue(ext, upar.value(ext) + xdev)
|
||||
}
|
||||
upar.fix(par)
|
||||
upar.setValue(par, `val`)
|
||||
upar.setValue(par, value)
|
||||
val toler = 0.1
|
||||
val cross = MnFunctionCross(theFCN, upar, theMinimum!!.fval(), theStrategy, errDef)
|
||||
val aopt: MnCross = cross.cross(para, xmid, xdir, toler, maxcalls)
|
||||
|
@ -52,17 +52,9 @@ internal class MnSeedGenerator : MinimumSeedGenerator {
|
||||
val delta: Double = hgrd.getSecond().getEntry(i)
|
||||
if (abs(calculated - provided) > delta) {
|
||||
MINUITPlugin.logStatic(
|
||||
""
|
||||
+ "gradient discrepancy of external parameter \"%d\" "
|
||||
+ "(internal parameter \"%d\") too large. Expected: \"%f\", provided: \"%f\"",
|
||||
"""gradient discrepancy of external parameter "%d" (internal parameter "%d") too large. Expected: "%f", provided: "%f"""",
|
||||
st.getTransformation().extOfInt(i), i, provided, calculated
|
||||
)
|
||||
|
||||
//
|
||||
// MINUITPlugin.logStatic("gradient discrepancy of external parameter "
|
||||
// + st.getTransformation().extOfInt(i)
|
||||
// + " (internal parameter " + i + ") too large.");
|
||||
// good = false;
|
||||
}
|
||||
}
|
||||
if (!good) {
|
||||
@ -84,10 +76,7 @@ internal class MnSeedGenerator : MinimumSeedGenerator {
|
||||
dcovar = 0.0
|
||||
} else {
|
||||
for (i in 0 until n) {
|
||||
mat[i, i] = if (abs(
|
||||
dgrad.getGradientDerivative()
|
||||
.getEntry(i)
|
||||
) > prec.eps2()
|
||||
mat[i, i] = if (abs(dgrad.getGradientDerivative().getEntry(i)) > prec.eps2()
|
||||
) 1.0 / dgrad.getGradientDerivative().getEntry(i) else 1.0
|
||||
}
|
||||
}
|
||||
|
@ -235,9 +235,9 @@ class MnUserParameterState {
|
||||
* @param val a double.
|
||||
* @param name a [String] object.
|
||||
*/
|
||||
fun add(name: String, `val`: Double, err: Double) {
|
||||
theParameters.add(name, `val`, err)
|
||||
theIntParameters.add(`val`)
|
||||
fun add(name: String, value: Double, err: Double) {
|
||||
theParameters.add(name, value, err)
|
||||
theIntParameters.add(value)
|
||||
theCovarianceValid = false
|
||||
theGCCValid = false
|
||||
theValid = true
|
||||
@ -252,10 +252,10 @@ class MnUserParameterState {
|
||||
* @param err a double.
|
||||
* @param up a double.
|
||||
*/
|
||||
fun add(name: String, `val`: Double, err: Double, low: Double, up: Double) {
|
||||
theParameters.add(name, `val`, err, low, up)
|
||||
fun add(name: String, value: Double, err: Double, low: Double, up: Double) {
|
||||
theParameters.add(name, value, err, low, up)
|
||||
theCovarianceValid = false
|
||||
theIntParameters.add(ext2int(index(name), `val`))
|
||||
theIntParameters.add(ext2int(index(name), value))
|
||||
theGCCValid = false
|
||||
theValid = true
|
||||
}
|
||||
@ -266,8 +266,8 @@ class MnUserParameterState {
|
||||
* @param name a [String] object.
|
||||
* @param val a double.
|
||||
*/
|
||||
fun add(name: String, `val`: Double) {
|
||||
theParameters.add(name, `val`)
|
||||
fun add(name: String, value: Double) {
|
||||
theParameters.add(name, value)
|
||||
theValid = true
|
||||
}
|
||||
|
||||
@ -331,8 +331,8 @@ class MnUserParameterState {
|
||||
return theParameters.errors()
|
||||
}
|
||||
|
||||
fun ext2int(i: Int, `val`: Double): Double {
|
||||
return theParameters.trafo().ext2int(i, `val`)
|
||||
fun ext2int(i: Int, value: Double): Double {
|
||||
return theParameters.trafo().ext2int(i, value)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -426,8 +426,8 @@ class MnUserParameterState {
|
||||
}
|
||||
|
||||
// transformation internal <-> external
|
||||
fun int2ext(i: Int, `val`: Double): Double {
|
||||
return theParameters.trafo().int2ext(i, `val`)
|
||||
fun int2ext(i: Int, value: Double): Double {
|
||||
return theParameters.trafo().int2ext(i, value)
|
||||
}
|
||||
|
||||
fun intCovariance(): MnUserCovariance {
|
||||
@ -700,14 +700,14 @@ class MnUserParameterState {
|
||||
* @param e a int.
|
||||
* @param val a double.
|
||||
*/
|
||||
fun setValue(e: Int, `val`: Double) {
|
||||
theParameters.setValue(e, `val`)
|
||||
fun setValue(e: Int, value: Double) {
|
||||
theParameters.setValue(e, value)
|
||||
if (!parameter(e).isFixed() && !parameter(e).isConst()) {
|
||||
val i = intOfExt(e)
|
||||
if (parameter(e).hasLimits()) {
|
||||
theIntParameters[i] = ext2int(e, `val`)
|
||||
theIntParameters[i] = ext2int(e, value)
|
||||
} else {
|
||||
theIntParameters[i] = `val`
|
||||
theIntParameters[i] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -719,8 +719,8 @@ class MnUserParameterState {
|
||||
* @param name a [String] object.
|
||||
* @param val a double.
|
||||
*/
|
||||
fun setValue(name: String?, `val`: Double) {
|
||||
setValue(index(name), `val`)
|
||||
fun setValue(name: String?, value: Double) {
|
||||
setValue(index(name), value)
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
|
@ -65,8 +65,8 @@ class MnUserParameters {
|
||||
* @param val a double.
|
||||
* @param name a [String] object.
|
||||
*/
|
||||
fun add(name: String, `val`: Double, err: Double) {
|
||||
theTransformation.add(name, `val`, err)
|
||||
fun add(name: String, value: Double, err: Double) {
|
||||
theTransformation.add(name, value, err)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -78,8 +78,8 @@ class MnUserParameters {
|
||||
* @param val a double.
|
||||
* @param err a double.
|
||||
*/
|
||||
fun add(name: String, `val`: Double, err: Double, low: Double, up: Double) {
|
||||
theTransformation.add(name, `val`, err, low, up)
|
||||
fun add(name: String, value: Double, err: Double, low: Double, up: Double) {
|
||||
theTransformation.add(name, value, err, low, up)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,8 +88,8 @@ class MnUserParameters {
|
||||
* @param name a [String] object.
|
||||
* @param val a double.
|
||||
*/
|
||||
fun add(name: String, `val`: Double) {
|
||||
theTransformation.add(name, `val`)
|
||||
fun add(name: String, value: Double) {
|
||||
theTransformation.add(name, value)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -344,8 +344,8 @@ class MnUserParameters {
|
||||
* @param index a int.
|
||||
* @param val a double.
|
||||
*/
|
||||
fun setValue(index: Int, `val`: Double) {
|
||||
theTransformation.setValue(index, `val`)
|
||||
fun setValue(index: Int, value: Double) {
|
||||
theTransformation.setValue(index, value)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -355,8 +355,8 @@ class MnUserParameters {
|
||||
* @param name a [String] object.
|
||||
* @param val a double.
|
||||
*/
|
||||
fun setValue(name: String?, `val`: Double) {
|
||||
theTransformation.setValue(name, `val`)
|
||||
fun setValue(name: String?, value: Double) {
|
||||
theTransformation.setValue(name, value)
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
|
@ -64,12 +64,12 @@ class MnUserTransformation {
|
||||
* @param err
|
||||
* @param val
|
||||
*/
|
||||
fun add(name: String, `val`: Double, err: Double) {
|
||||
fun add(name: String, value: Double, err: Double) {
|
||||
require(!nameMap.containsKey(name)) { "duplicate name: $name" }
|
||||
nameMap[name] = theParameters.size
|
||||
theExtOfInt.add(theParameters.size)
|
||||
theCache.add(`val`)
|
||||
theParameters.add(MinuitParameter(theParameters.size, name, `val`, err))
|
||||
theCache.add(value)
|
||||
theParameters.add(MinuitParameter(theParameters.size, name, value, err))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -77,12 +77,12 @@ class MnUserTransformation {
|
||||
* @param up
|
||||
* @param low
|
||||
*/
|
||||
fun add(name: String, `val`: Double, err: Double, low: Double, up: Double) {
|
||||
fun add(name: String, value: Double, err: Double, low: Double, up: Double) {
|
||||
require(!nameMap.containsKey(name)) { "duplicate name: $name" }
|
||||
nameMap[name] = theParameters.size
|
||||
theExtOfInt.add(theParameters.size)
|
||||
theCache.add(`val`)
|
||||
theParameters.add(MinuitParameter(theParameters.size, name, `val`, err, low, up))
|
||||
theCache.add(value)
|
||||
theParameters.add(MinuitParameter(theParameters.size, name, value, err, low, up))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,11 +90,11 @@ class MnUserTransformation {
|
||||
* @param name
|
||||
* @param val
|
||||
*/
|
||||
fun add(name: String, `val`: Double) {
|
||||
fun add(name: String, value: Double) {
|
||||
require(!nameMap.containsKey(name)) { "duplicate name: $name" }
|
||||
nameMap[name] = theParameters.size
|
||||
theCache.add(`val`)
|
||||
theParameters.add(MinuitParameter(theParameters.size, name, `val`))
|
||||
theCache.add(value)
|
||||
theParameters.add(MinuitParameter(theParameters.size, name, value))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -107,20 +107,20 @@ class MnUserTransformation {
|
||||
return MnUserTransformation(this)
|
||||
}
|
||||
|
||||
fun dInt2Ext(i: Int, `val`: Double): Double {
|
||||
fun dInt2Ext(i: Int, value: Double): Double {
|
||||
var dd = 1.0
|
||||
val parm: MinuitParameter = theParameters[theExtOfInt[i]]
|
||||
if (parm.hasLimits()) {
|
||||
dd = if (parm.hasUpperLimit() && parm.hasLowerLimit()) {
|
||||
theDoubleLimTrafo.dInt2Ext(
|
||||
`val`,
|
||||
value,
|
||||
parm.upperLimit(),
|
||||
parm.lowerLimit()
|
||||
)
|
||||
} else if (parm.hasUpperLimit() && !parm.hasLowerLimit()) {
|
||||
theUpperLimTrafo.dInt2Ext(`val`, parm.upperLimit())
|
||||
theUpperLimTrafo.dInt2Ext(value, parm.upperLimit())
|
||||
} else {
|
||||
theLowerLimTrafo.dInt2Ext(`val`, parm.lowerLimit())
|
||||
theLowerLimTrafo.dInt2Ext(value, parm.lowerLimit())
|
||||
}
|
||||
}
|
||||
return dd
|
||||
@ -143,30 +143,30 @@ class MnUserTransformation {
|
||||
return result
|
||||
}
|
||||
|
||||
fun ext2int(i: Int, `val`: Double): Double {
|
||||
fun ext2int(i: Int, value: Double): Double {
|
||||
val parm: MinuitParameter = theParameters[i]
|
||||
return if (parm.hasLimits()) {
|
||||
if (parm.hasUpperLimit() && parm.hasLowerLimit()) {
|
||||
theDoubleLimTrafo.ext2int(
|
||||
`val`,
|
||||
value,
|
||||
parm.upperLimit(),
|
||||
parm.lowerLimit(),
|
||||
precision()
|
||||
)
|
||||
} else if (parm.hasUpperLimit() && !parm.hasLowerLimit()) {
|
||||
theUpperLimTrafo.ext2int(
|
||||
`val`,
|
||||
value,
|
||||
parm.upperLimit(),
|
||||
precision()
|
||||
)
|
||||
} else {
|
||||
theLowerLimTrafo.ext2int(
|
||||
`val`,
|
||||
value,
|
||||
parm.lowerLimit(),
|
||||
precision()
|
||||
)
|
||||
}
|
||||
} else `val`
|
||||
} else value
|
||||
}
|
||||
|
||||
fun extOfInt(internal: Int): Int {
|
||||
@ -200,21 +200,21 @@ class MnUserTransformation {
|
||||
return nameMap[name]!!
|
||||
}
|
||||
|
||||
fun int2ext(i: Int, `val`: Double): Double {
|
||||
fun int2ext(i: Int, value: Double): Double {
|
||||
val parm: MinuitParameter = theParameters[theExtOfInt[i]]
|
||||
return if (parm.hasLimits()) {
|
||||
if (parm.hasUpperLimit() && parm.hasLowerLimit()) {
|
||||
theDoubleLimTrafo.int2ext(
|
||||
`val`,
|
||||
value,
|
||||
parm.upperLimit(),
|
||||
parm.lowerLimit()
|
||||
)
|
||||
} else if (parm.hasUpperLimit() && !parm.hasLowerLimit()) {
|
||||
theUpperLimTrafo.int2ext(`val`, parm.upperLimit())
|
||||
theUpperLimTrafo.int2ext(value, parm.upperLimit())
|
||||
} else {
|
||||
theLowerLimTrafo.int2ext(`val`, parm.lowerLimit())
|
||||
theLowerLimTrafo.int2ext(value, parm.lowerLimit())
|
||||
}
|
||||
} else `val`
|
||||
} else value
|
||||
}
|
||||
|
||||
fun int2extCovariance(vec: RealVector, cov: MnAlgebraicSymMatrix): MnUserCovariance {
|
||||
@ -235,13 +235,13 @@ class MnUserTransformation {
|
||||
return result
|
||||
}
|
||||
|
||||
fun int2extError(i: Int, `val`: Double, err: Double): Double {
|
||||
fun int2extError(i: Int, value: Double, err: Double): Double {
|
||||
var dx = err
|
||||
val parm: MinuitParameter = theParameters[theExtOfInt[i]]
|
||||
if (parm.hasLimits()) {
|
||||
val ui = int2ext(i, `val`)
|
||||
var du1 = int2ext(i, `val` + dx) - ui
|
||||
val du2 = int2ext(i, `val` - dx) - ui
|
||||
val ui = int2ext(i, value)
|
||||
var du1 = int2ext(i, value + dx) - ui
|
||||
val du2 = int2ext(i, value - dx) - ui
|
||||
if (parm.hasUpperLimit() && parm.hasLowerLimit()) {
|
||||
if (dx > 1.0) {
|
||||
du1 = parm.upperLimit() - parm.lowerLimit()
|
||||
@ -354,13 +354,13 @@ class MnUserTransformation {
|
||||
setUpperLimit(index(name), up)
|
||||
}
|
||||
|
||||
fun setValue(index: Int, `val`: Double) {
|
||||
theParameters[index].setValue(`val`)
|
||||
theCache[index] = `val`
|
||||
fun setValue(index: Int, value: Double) {
|
||||
theParameters[index].setValue(value)
|
||||
theCache[index] = value
|
||||
}
|
||||
|
||||
fun setValue(name: String?, `val`: Double) {
|
||||
setValue(index(name), `val`)
|
||||
fun setValue(name: String?, value: Double) {
|
||||
setValue(index(name), value)
|
||||
}
|
||||
|
||||
fun transform(pstates: RealVector): ArrayRealVector {
|
||||
|
@ -71,10 +71,7 @@ internal object NegativeG2LineSearch {
|
||||
} while (iter++ < 2 * n && iterate)
|
||||
val mat = MnAlgebraicSymMatrix(n)
|
||||
for (i in 0 until n) {
|
||||
mat[i, i] = if (abs(
|
||||
dgrad.getGradientDerivative()
|
||||
.getEntry(i)
|
||||
) > prec.eps2()
|
||||
mat[i, i] = if (abs(dgrad.getGradientDerivative().getEntry(i)) > prec.eps2()
|
||||
) 1.0 / dgrad.getGradientDerivative().getEntry(i) else 1.0
|
||||
}
|
||||
val err = MinimumError(mat, 1.0)
|
||||
|
@ -41,10 +41,7 @@ internal class SimplexSeedGenerator : MinimumSeedGenerator {
|
||||
val mat = MnAlgebraicSymMatrix(n)
|
||||
val dcovar = 1.0
|
||||
for (i in 0 until n) {
|
||||
mat[i, i] = if (abs(
|
||||
dgrad.getGradientDerivative()
|
||||
.getEntry(i)
|
||||
) > prec.eps2()
|
||||
mat[i, i] = if (abs(dgrad.getGradientDerivative().getEntry(i)) > prec.eps2()
|
||||
) 1.0 / dgrad.getGradientDerivative().getEntry(i) else 1.0
|
||||
}
|
||||
val err = MinimumError(mat, dcovar)
|
||||
|
Loading…
Reference in New Issue
Block a user