com.itemanalysis.psychometrics.irt.model.Irm3PL.java Source code

Java tutorial

Introduction

Here is the source code for com.itemanalysis.psychometrics.irt.model.Irm3PL.java

Source

/*
 * Copyright 2012 J. Patrick Meyer
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.itemanalysis.psychometrics.irt.model;

import com.itemanalysis.psychometrics.irt.estimation.ItemParamPrior;
import org.apache.commons.math3.stat.descriptive.moment.Mean;
import org.apache.commons.math3.stat.descriptive.moment.StandardDeviation;

import java.util.Arrays;
import java.util.Formatter;

/**
 * An implementation of {@link AbstractItemResponseModel} that allows for the three-parameter logistic (3PL) model,
 * two-parameter logistic (2PL) model, one-parameter logistic (1PL) model, and the Rasch model. The particular
 * type of item response model is determined by the constructor used to create the object. See the constructors
 * for more details on which one to use for each type of model.
 */
public class Irm3PL extends AbstractItemResponseModelWithGradient {

    private double discrimination = 1.0;
    private double difficulty = 0.0;
    private double guessing = 0.0;
    private double slipping = 1.0;
    private double D = 1.7;
    private double discriminationStdError = 0.0;
    private double difficultyStdError = 0.0;
    private double guessingStdError = 0.0;
    private int numberOfParameters = 1;
    private double proposalDiscrimination = 1.0;
    private double proposalDifficulty = 0.0;
    private double proposalGuessing = 0.0;
    private double proposalSlipping = 1.0;
    private ItemParamPrior discriminationPrior = null;
    private ItemParamPrior difficultyPrior = null;
    private ItemParamPrior guessingPrior = null;

    /**
     * Constructor for three parameter logistic model
     *
     * @param discrimination item discrimination parameter
     * @param difficulty item difficulty parameter
     * @param guessing lower-asymptote parameter
     * @param D scaling factor
     */
    public Irm3PL(double discrimination, double difficulty, double guessing, double D) {
        this.discrimination = discrimination;
        this.difficulty = difficulty;
        this.guessing = guessing;
        this.proposalDiscrimination = discrimination;
        this.proposalDifficulty = difficulty;
        this.proposalGuessing = guessing;
        this.D = D;
        this.numberOfParameters = 3;
        this.ncat = 2;
        defaultScoreWeights();
    }

    /**
     * Constructor for two parameter logistic model.
     *
     * @param discrimination item discrimination parameter
     * @param difficulty item difficulty parameter
     * @param D scaling factor
     */
    public Irm3PL(double discrimination, double difficulty, double D) {
        this.discrimination = discrimination;
        this.difficulty = difficulty;
        this.proposalDiscrimination = discrimination;
        this.proposalDifficulty = difficulty;
        this.proposalGuessing = 0.0;
        this.D = D;
        numberOfParameters = 2;
        this.ncat = 2;
        defaultScoreWeights();
    }

    /**
     * Constructor for one parameter logistic model
     *
     * @param difficulty item difficulty parameter
     * @param D scaling factor
     */
    public Irm3PL(double difficulty, double D) {
        this.difficulty = difficulty;
        this.proposalDiscrimination = 1.0;
        this.proposalDifficulty = difficulty;
        this.proposalGuessing = 0.0;
        this.D = D;
        numberOfParameters = 1;
        this.ncat = 2;
        defaultScoreWeights();
    }

    /**
     * Comute the probability of a correct response. This method uses a combination of stored
     * parameters and those passed in the iparam array. This arrangement allows for fixing
     * some item parameters during estimation. For example, instantiating this object as a 2PL
     * and setting the stored value of the guessing parameter will result in a 3PL with a fixed
     * guessing parameter. If you do not want to use the stored values of any parameter, then
     * the iparam array should be of length = 3 and contain the discrimination, difficulty,
     * and guessing parameters, respectively.
     *
     * The order of parameters in iparam is as follows:
     * 1. Rasch model: iparam[0] = difficulty
     * 2. 2PL: iparam[0] = discrimination, iparam[1] = difficulty
     * 3. 3PL: iparam[1] = discrimination, iparam[1] = difficulty, iparam[2] = guessing
     *
     * @param theta person ability parameter.
     * @param iparam item parameter array that is one to three in length.
     * @param D scaling constant.
     * @return probability of a correct answer.
     */
    public double probability(double theta, double[] iparam, int response, double D) {
        double prob = 0.0;
        if (response == 1) {
            return probRight(theta, iparam, D);
        } else {
            return probWrong(theta, iparam, D);
        }
    }

    public double probability(double theta, int response) {
        double prob = 0.0;
        if (response == 1) {
            prob = probRight(theta);
        } else {
            prob = probWrong(theta);
        }
        return prob;
    }

    /**
     * Comute the probability of a correct response. This method uses a combination of stored
     * parameters and those passed in the iparam array. This arrangement allows for fixing
     * some item parameters during estimation. For example, instantiating this object as a 2PL
     * and setting the stored value of the guessing parameter will result in a 3PL with a fixed
     * guessing parameter. If you do not want to use the stored values of any parameter, then
     * the iparam array should be of length = 3 and contain the discrimination, difficulty,
     * and guessing parameters, respectively.
     *
     * The order of parameters in iparam is as follows:
     * 1. Rasch model: iparam[0] = difficulty
     * 2. 2PL: iparam[0] = discrimination, iparam[1] = difficulty
     * 3. 3PL: iparam[1] = discrimination, iparam[1] = difficulty, iparam[2] = guessing
     *
     * @param theta person ability parameter.
     * @param iparam item parameter array that is one to three in length.
     * @param D scaling constant.
     * @return probability of a correct answer.
     */
    private double probRight(double theta, double[] iparam, double D) {
        double z = 0;

        if (iparam.length == 1) {
            z = Math.exp(D * discrimination * (theta - iparam[0]));
            return guessing + (slipping - guessing) * z / (1 + z);
        } else if (iparam.length == 2) {
            z = Math.exp(D * iparam[0] * (theta - iparam[1]));
            return guessing + (slipping - guessing) * z / (1 + z);
        } else {
            z = Math.exp(D * iparam[0] * (theta - iparam[1]));
            return iparam[2] + (slipping - iparam[2]) * z / (1 + z);
        }

    }

    private double probWrong(double theta, double[] iparam, double D) {
        if (iparam.length == 3 && iparam[2] < 0)
            return 0;
        return 1.0 - probRight(theta, iparam, D);
    }

    private double probRight(double theta) {
        double z = Math.exp(D * discrimination * (theta - difficulty));
        double prob = guessing + (slipping - guessing) * z / (1 + z);
        return prob;
    }

    private double probWrong(double theta) {
        return 1.0 - probRight(theta);
    }

    public double expectedValue(double theta) {
        return scoreWeight[1] * probRight(theta);
    }

    //    public double[] gradientAt(double theta){
    //        //Note: The second argument (-1) is not actually used by this class.
    //        //It is here to satisfy the interface.
    //        return gradientAt(theta, -1);
    //    }

    /**
     * Computes the gradientAt (vector of first partial derivatives) with respect to the item parameters.
     * This method uses item parameters passed to teh method. It does NOT use item parameters stored in the
     * object.
     *
     * Note: The second argument (int k) is not actually used by this class. It is here to satisfy the interface.
     *
     * @param theta person ability estimate.
     * @param iparam array of item parameters.
     * @param k response category
     * @param D scaling constant that is either 1 or 1.7
     * @return an array of first partial derivatives (i.e. the gradientAt).
     */
    public double[] gradient(double theta, double[] iparam, int k, double D) {
        double a = 1;
        double b = 0;
        double c = 0;
        double u = slipping;
        double[] deriv = new double[numberOfParameters];

        //This gradient is based on the 3PL only
        //        if(numberOfParameters==3){
        //            a = iparam[0];
        //            b = iparam[1];
        //            c = iparam[2];
        //        }else if(numberOfParameters==2){
        //            a = iparam[0];
        //            b = iparam[1];
        //            c = guessing;
        //        }else{
        //            a = discrimination;
        //            b = iparam[0];
        //            c = guessing;
        //        }
        //
        //        if(k==0){
        //            double z = Math.exp(D*a*(theta-b));
        //            double g = 1 + z;
        //            double g2 = g*g;
        //            double db = (1-c)*z*D*a/g2;//first derivative wrt difficulty
        //
        //            if(numberOfParameters==1){
        //                deriv[0] = db;
        //                return deriv;
        //            }
        //
        //            deriv[0] = -(1-c)*z*D*(theta-b)/g2;//first derivative wrt discrimination
        //            deriv[1] = db;
        //
        //            if(numberOfParameters==3){
        //                deriv[2] = -1/(1+z); //first derivative wrt guessing
        //            }
        //
        //        }else{
        //
        //            double t = Math.exp(-a*D*(theta-b));
        //            double onept2 = 1.0 + t;
        //            onept2 *= onept2;
        //
        //            //derivative with respect to b parameter
        //            double derivb = -a*(1.0-c)*D*t;
        //            derivb /= onept2;
        //
        //            if(numberOfParameters==1){
        //                deriv[0] = derivb;
        //                return deriv;
        //            }
        //
        //            deriv[1] = derivb;
        //
        //            //derivative with respect to the a parameter
        //            deriv[0] = (1.0 - c)*(theta - b)*D*t;
        //            deriv[0] /= onept2;
        //
        //            //derivative with respect to c parameter
        //            if(numberOfParameters==3){
        //                deriv[2] = -1.0/(1.0 + t);
        //                deriv[2] += 1.0;
        //            }
        //
        //        }
        //        return deriv;

        //=======================================================================
        //This gradient is based on teh gradient for the 4PL. See Irm4PL.java

        if (numberOfParameters == 3) {
            a = iparam[0];
            b = iparam[1];
            c = iparam[2];
        } else if (numberOfParameters == 2) {
            a = iparam[0];
            b = iparam[1];
            c = guessing;
        } else {
            a = discrimination;
            b = iparam[0];
            c = guessing;
        }

        double w = D * (theta - b);
        double z = Math.exp(D * a * (theta - b));
        double z2 = z * z;
        double d = 1 + z;
        double d2 = d * d;
        double xmc = u - c;

        if (numberOfParameters == 3) {
            deriv[0] = xmc * z * w / d - xmc * z2 * w / d2;
            deriv[1] = -(xmc * z * D * a / d - xmc * z2 * D * a / d2);
            deriv[2] = 1.0 - z / d;
            if (k == 0) {
                deriv[0] = -deriv[0];
                deriv[1] = -deriv[1];
                deriv[2] = -deriv[2];
            }
        } else if (numberOfParameters == 2) {
            deriv[0] = xmc * z * w / d - xmc * z2 * w / d2;
            deriv[1] = -(xmc * z * D * a / d - xmc * z2 * D * a / d2);
            if (k == 0) {
                deriv[0] = -deriv[0];
                deriv[1] = -deriv[1];
            }
        } else {
            deriv[0] = -(xmc * z * D * a / d - xmc * z2 * D * a / d2);
            if (k == 0) {
                deriv[0] = -deriv[0];
            }
        }
        return deriv;
    }

    /**
     * Computes gradientAt using item parameters stored in teh object.
     *
     * @param theta person ability value
     * @param k response category
     * @return gradientAt
     */
    public double[] gradient(double theta, int k) {
        double[] iparam = { discrimination, difficulty, guessing };
        return gradient(theta, iparam, k, D);
    }

    /**
     * Hessian or matrix of second derivatives.
     *
     * @param theta person ability value.
     * @return a two-way array containing the Hessian matrix values.
     */
    public double[][] hessian(double theta) {
        double[][] deriv = new double[numberOfParameters][numberOfParameters];
        double e = Math.exp(discrimination * D * (theta - difficulty));
        double onepe3 = 1.0 + e;
        double onepe2 = onepe3 * onepe3;
        onepe3 *= onepe2;
        double d2 = D * D;
        double cm1 = guessing - 1.0;
        double em1 = e - 1.0;

        //second derivative with respect to the b parameter
        double derivt = discrimination * discrimination;
        derivt *= cm1 * d2;
        derivt *= e * em1;
        derivt /= onepe3;

        if (numberOfParameters == 1) {
            deriv[0][0] = derivt;
            return deriv;
        }
        deriv[1][1] = derivt;

        double bmt = difficulty - theta;

        //second derivative with respect to the a parameter
        derivt = cm1 * d2;
        derivt *= e * em1;
        derivt *= bmt * bmt;
        deriv[0][0] = derivt / onepe3;

        //second derivative with respect to a and b
        double t2 = 1 + e;
        t2 += discrimination * D * em1 * bmt;
        derivt = t2 * cm1;
        derivt *= D * e;
        deriv[1][0] = derivt / onepe3;
        deriv[0][1] = deriv[1][0];

        if (numberOfParameters == 3) {
            //second derivative with respect to the c parameter
            deriv[2][2] = 0.0;
            double einv = 1.0 / e;
            double onepeinv2 = 1.0 + einv;
            onepeinv2 *= onepeinv2;

            //second derivative with respect to a and c
            derivt = D * einv;
            derivt *= bmt;
            deriv[2][0] = derivt / onepeinv2;
            deriv[0][2] = deriv[2][0];

            //second derivative with respect to b and c
            derivt = discrimination * D * einv;
            deriv[2][1] = derivt / onepeinv2;
        }
        return deriv;
    }

    /**
     * From Equating recipes. Computes the derivative with respect to person ability.
     *
     * @param theta person ability value.
     * @param response item response value.
     * @return derivative wrt theta.
     */
    public double derivTheta2(double theta, int response) {
        double z = Math.exp(D * discrimination * (theta - difficulty));

        //incorrect response
        if (response == 0)
            return (-D * discrimination * (1.0 - guessing) * z / ((1.0 + z) * (1.0 + z)));

        //correct response
        return (D * discrimination * (1.0 - guessing) * z / ((1.0 + z) * (1.0 + z)));
    }

    /**
     * Derivative from Mathematica. First derivative with respect to person ability.
     *
     * @param theta person ability value.
     * @return first derivative wrt theta.
     */
    public double derivTheta(double theta) {
        double p1 = D * discrimination * (1 - guessing) * Math.exp(2.0 * D * discrimination * (theta - difficulty));
        double p2 = Math.pow(1 + Math.exp(D * discrimination * (theta - difficulty)), 2);
        double p3 = D * discrimination * (1.0 - guessing) * Math.exp(D * discrimination * (theta - difficulty));
        double p4 = 1 + Math.exp(D * discrimination * (theta - difficulty));
        double deriv = -p1 / p2 + p3 / p4;
        return deriv;
    }

    /**
     * Computes the item information function at theta.
     *
     * @param theta person ability value.
     * @return item information.
     */
    public double itemInformationAt(double theta) {
        double p = probRight(theta);
        double part1 = Math.pow(p - guessing, 2);
        double part2 = Math.pow(slipping - guessing, 2);
        double a2 = discrimination * discrimination;
        double info = D * D * a2 * (part1 / part2) * ((slipping - p) / p);
        return info;
    }

    public double[] nonZeroPrior(double[] param) {
        double[] p = Arrays.copyOf(param, param.length);

        if (numberOfParameters == 1) {
            if (difficultyPrior != null)
                p[0] = difficultyPrior.nearestNonZero(param[0]);
        } else if (numberOfParameters == 2) {
            if (discriminationPrior != null)
                p[0] = discriminationPrior.nearestNonZero(param[0]);
            if (difficultyPrior != null)
                p[1] = difficultyPrior.nearestNonZero(param[1]);
        } else {
            if (discriminationPrior != null)
                p[0] = discriminationPrior.nearestNonZero(param[0]);
            if (difficultyPrior != null)
                p[1] = difficultyPrior.nearestNonZero(param[1]);
            if (guessingPrior != null)
                p[2] = guessingPrior.nearestNonZero(param[2]);
        }
        return p;
    }

    public void setDiscriminationPrior(ItemParamPrior discriminationPrior) {
        this.discriminationPrior = discriminationPrior;
    }

    public void setDifficultyPrior(ItemParamPrior difficultyPrior) {
        this.difficultyPrior = difficultyPrior;
    }

    public void setGuessingPrior(ItemParamPrior guessingPrior) {
        this.guessingPrior = guessingPrior;
    }

    public void setSlippingPrior(ItemParamPrior slippingPrior) {

    }

    public void setStepPriorAt(ItemParamPrior prior, int index) {

    }

    public ItemParamPrior getDiscriminationPrior() {
        return discriminationPrior;
    }

    public ItemParamPrior getDifficultyPrior() {
        return difficultyPrior;
    }

    public ItemParamPrior getGuessingPrior() {
        return guessingPrior;
    }

    public double addPriorsToLogLikelihood(double loglike, double[] iparam) {
        //        double priorProb = 0.0;
        double ll = loglike;

        if (numberOfParameters == 3) {
            if (discriminationPrior != null) {
                ll += discriminationPrior.logDensity(iparam[0]);
            }

            if (difficultyPrior != null) {
                ll += difficultyPrior.logDensity(iparam[1]);
            }

            if (guessingPrior != null) {
                ll += guessingPrior.logDensity(iparam[2]);
            }

        } else if (numberOfParameters == 2) {
            if (discriminationPrior != null) {
                ll += discriminationPrior.logDensity(iparam[0]);
            }

            if (difficultyPrior != null) {
                ll += difficultyPrior.logDensity(iparam[1]);
            }
        } else {
            if (difficultyPrior != null) {
                ll += difficultyPrior.logDensity(iparam[0]);
            }
        }

        //        //difficulty prior
        //        if(numberOfParameters==1){
        //            if(difficultyPrior!=null){
        //                priorProb = difficultyPrior.logDensity(iparam[0]);
        //                ll += priorProb;
        //            }
        //        }
        //
        //        //difficulty and discrimination prior
        //        if(numberOfParameters>=2){
        //
        //            if(discriminationPrior!=null){
        //                priorProb = discriminationPrior.logDensity(iparam[0]);
        //                ll += priorProb;
        //            }
        //
        //            if(difficultyPrior!=null){
        //                priorProb = difficultyPrior.logDensity(iparam[1]);
        //                ll += priorProb;
        //            }
        //        }
        //
        //        //guessing prior
        //        if(numberOfParameters==3){
        //            if(guessingPrior!=null){
        //                priorProb = guessingPrior.logDensity(iparam[2]);
        //                ll += priorProb;
        //            }
        //        }

        return ll;

    }

    public double[] addPriorsToLogLikelihoodGradient(double[] loglikegrad, double[] iparam) {
        double[] llg = loglikegrad;

        if (numberOfParameters == 3) {
            if (discriminationPrior != null) {
                llg[0] -= discriminationPrior.logDensityDeriv1(iparam[0]);
            }

            if (difficultyPrior != null) {
                llg[1] -= difficultyPrior.logDensityDeriv1(iparam[1]);
            }

            if (guessingPrior != null) {
                llg[2] -= guessingPrior.logDensityDeriv1(iparam[2]);
            }
        } else if (numberOfParameters == 2) {
            if (discriminationPrior != null) {
                llg[0] -= discriminationPrior.logDensityDeriv1(iparam[0]);
            }

            if (difficultyPrior != null) {
                llg[1] -= difficultyPrior.logDensityDeriv1(iparam[1]);
            }
        } else {
            if (difficultyPrior != null) {
                llg[0] -= difficultyPrior.logDensityDeriv1(iparam[0]);
            }
        }
        return llg;
    }

    //=======================================================================================================================
    // Methods related to scale linking
    //=======================================================================================================================

    /**
     * Mean/sigma linking coefficients are computed from the mean and standard deviation of item difficulty.
     * The summary statistics are computed in a storeless manner. This method allows for the incremental
     * update to item difficulty summary statistics by combining them with other summary statistics.
     *
     * @param mean item difficulty mean.
     * @param sd item difficulty standard deviation.
     */
    public void incrementMeanSigma(Mean mean, StandardDeviation sd) {
        mean.increment(difficulty);
        sd.increment(difficulty);
    }

    /**
     * Mean/mean linking coefficients are computed from the mean item difficulty and mean item discrimination.
     * The summary statistics are computed in a storeless manner. This method allows for the incremental
     * update to item difficulty summary statistics by combining them with other summary statistics.
     *
     * @param meanDiscrimination item discrimination mean.
     * @param meanDifficulty item difficulty mean.
     */
    public void incrementMeanMean(Mean meanDiscrimination, Mean meanDifficulty) {
        meanDiscrimination.increment(discrimination);
        meanDifficulty.increment(difficulty);
    }

    /**
     * Computes probability of a response under a linear transformation. This method is mainly used for the
     * characteristic curve linking methods (see {@link com.itemanalysis.psychometrics.irt.equating.StockingLordMethod}).
     * It applies the linear transformation such that the New form is transformed to the Old Form.
     *
     * @param theta examinee proficiency value
     * @param response target category
     * @param intercept linking coefficient for intercept
     * @param slope linking coefficient for slope
     * @return
     */
    public double tStarProbability(double theta, int response, double intercept, double slope) {
        double[] iparam = new double[numberOfParameters];
        if (numberOfParameters == 3) {
            iparam[0] = discrimination / slope;
            iparam[1] = difficulty * slope + intercept;
            iparam[2] = guessing;
        } else if (numberOfParameters == 2) {
            iparam[0] = discrimination / slope;
            iparam[1] = difficulty * slope + intercept;
        } else {
            iparam[0] = difficulty + intercept;//Rasch model
        }

        if (response == 1) {
            return probRight(theta, iparam, D);

        } else {
            return 1.0 - probRight(theta, iparam, D);
        }

    }

    /**
     * Computes probability of a response under a linear transformation. This method is mainly used for the
     * characteristic curve linking methods (see {@link com.itemanalysis.psychometrics.irt.equating.StockingLordMethod}).
     * It applies the linear transformation such that the Old form is transformed to the New Form.
     *
     * @param theta examinee proficiency value
     * @param response target category
     * @param intercept linking coefficient for intercept
     * @param slope linking coefficient for slope
     * @return
     */
    public double tSharpProbability(double theta, int response, double intercept, double slope) {
        double[] iparam = new double[numberOfParameters];
        if (numberOfParameters == 3) {
            iparam[0] = discrimination * slope;
            iparam[1] = (difficulty - intercept) / slope;
            iparam[2] = guessing;
        } else if (numberOfParameters == 2) {
            iparam[0] = discrimination * slope;
            iparam[1] = (difficulty - intercept) / slope;
        } else {
            iparam[0] = (difficulty - intercept);//Rasch model
        }

        if (response == 1) {
            return probRight(theta, iparam, D);
        } else {
            return 1.0 - probRight(theta, iparam, D);
        }

    }

    /**
     * Computes item expected value under a linear transformation. This method is mainly used for the characteristic
     * curve linking methods (see {@link com.itemanalysis.psychometrics.irt.equating.StockingLordMethod}).
     * It applies the linear transformation such that the New form is transformed to the Old Form.
     *
     * @param theta person ability value
     * @param intercept intercept linking coefficient.
     * @param slope slope linking coefficient.
     * @return expected value under a linear transformation.
     */
    public double tStarExpectedValue(double theta, double intercept, double slope) {
        return tStarProbability(theta, 1, intercept, slope);
    }

    /**
     * Computes probability of a response under a linear transformation. This method is mainly used for the
     * characteristic curve linking methods (see {@link com.itemanalysis.psychometrics.irt.equating.StockingLordMethod}).
     * It applies the linear transformation such that the Old form is transformed to the New Form.
     *
     * @param theta examinee proficiency value
     * @param intercept linking coefficient for intercept
     * @param slope linking coefficient for slope
     * @return expected value under a linear transformation.
     */
    public double tSharpExpectedValue(double theta, double intercept, double slope) {
        return tSharpProbability(theta, 1, intercept, slope);
    }

    /**
     * Gets the number of item parameters.
     *
     * @return number of item parameters.
     */
    public int getNumberOfParameters() {
        return numberOfParameters;
    }

    public int getNumberOfEstimatedParameters() {
        if (isFixed)
            return 0;
        return numberOfParameters;
    }

    /**
     * Linear transformation of item parameters.
     *
     * @param intercept intercept transformation coefficient.
     * @param slope slope transformation coefficient.
     */
    public void scale(double intercept, double slope) {
        difficulty = intercept + slope * difficulty;
        discrimination = discrimination / slope;
        difficultyStdError *= slope;
        discriminationStdError *= slope;
    }

    //=====================================================================================================================//
    // GETTER AND SETTER METHODS MAINLY FOR USE WHEN ESTIMATING PARAMETERS                                                 //
    //=====================================================================================================================//

    public double[] getItemParameterArray() {
        double[] ip = new double[numberOfParameters];
        if (numberOfParameters == 1) {
            ip[0] = difficulty;
        } else if (numberOfParameters == 2) {
            ip[0] = discrimination;
            ip[1] = difficulty;
        } else {
            ip[0] = discrimination;
            ip[1] = difficulty;
            ip[2] = guessing;
        }
        return ip;
    }

    public void setStandardErrors(double[] x) {
        if (numberOfParameters == 1) {
            difficultyStdError = x[0];
        } else if (numberOfParameters == 2) {
            discriminationStdError = x[0];
            difficultyStdError = x[1];
        } else {
            discriminationStdError = x[0];
            difficultyStdError = x[1];
            guessingStdError = x[2];
        }
    }

    /**
     * Gets the type of item response model.
     *
     * @return type of item response model.
     */
    public IrmType getType() {
        return IrmType.L3;
    }

    /**
     * Gets the item difficulty parameter.
     *
     * @return item difficulty.
     */
    public double getDifficulty() {
        return difficulty;
    }

    /**
     * Set difficulty parameter to an existing value. If you are using this method to fix an item parameter during
     * estimation, you must also set the proposal value in {@link #setProposalDifficulty(double)}.
     *
     */
    public void setDifficulty(double difficulty) {
        this.difficulty = difficulty;
    }

    /**
     * A proposal difficulty value is obtained during each iteration of the estimation routine. This method gets
     * the proposal item difficulty values. This method is needed for estimating item difficulty.
     *
     * @return proposed item difficulty value.
     */
    public double getProposalDifficulty() {
        return proposalDifficulty;
    }

    /**
     * A proposal difficulty value is obtained during each iteration of the estimation routine. This method
     * sets the proposal value.
     *
     * @param proposalDifficulty proposed item difficulty value.
     */
    public void setProposalDifficulty(double proposalDifficulty) {
        if (!isFixed)
            this.proposalDifficulty = proposalDifficulty;
    }

    /**
     * Gets the item difficulty standard error.
     *
     * @return item difficulty standard error.
     */
    public double getDifficultyStdError() {
        return difficultyStdError;
    }

    /**
     * Item difficulty standard error may be computed external to the class. This method sets the difficulty
     * standard error to a computed value.
     *
     * @param stdError item difficulty standard error.
     */
    public void setDifficultyStdError(double stdError) {
        difficultyStdError = stdError;
    }

    /**
     * Gets item discrimination.
     *
     * @return item discrimination.
     */
    public double getDiscrimination() {
        return discrimination;
    }

    /**
     * Set discrimination parameter to an existing value. If you are using this method to fix an item parameter
     * during estimation, you must also set the proposal value with {@link #setProposalDiscrimination(double)}.
     *
     */
    public void setDiscrimination(double discrimination) {
        this.discrimination = discrimination;
    }

    public double getProposalDiscrimination() {
        return proposalDiscrimination;
    }

    /**
     * Set the proposed discrimination estimate.
     *
     * @param discrimination proposed item discrimination value.
     */
    public void setProposalDiscrimination(double discrimination) {
        if (!isFixed)
            this.proposalDiscrimination = discrimination;
    }

    /**
     * Gets the standard error for the item discrimination estimate.
     *
     * @return item discrimination standard error.
     */
    public double getDiscriminationStdError() {
        return discriminationStdError;
    }

    /**
     * The standard error may be computed external to the class. It can be set to a specific value with this method.
     *
     * @param stdError item discrimination standard error.
     */
    public void setDiscriminationStdError(double stdError) {
        discriminationStdError = stdError;
    }

    /**
     * Gets the pseudo-guessing (i.e. lower asymptote) parameter.
     *
     * @return guessing parameter.
     */
    public double getGuessing() {
        return guessing;
    }

    /**
     * Set lower asymptote parameter to an existing value. If you are using this method to fix an item parameter
     * during estimation, you must also set the proposal value in {@link #setProposalGuessing(double)}.
     *
     */
    public void setGuessing(double guessing) {
        this.guessing = guessing;
    }

    /**
     * A proposal guessing parameter value is obtained during each iteration of the estimation routine. This method
     * sets the proposal value.
     *
     * @param guessing proposed guessing parameter estimate.
     */
    public void setProposalGuessing(double guessing) {
        if (!isFixed)
            this.proposalGuessing = guessing;
    }

    /**
     * Gets the guessing parameter estimate standard error.
     *
     * @return guessing parameter estimate standard error.
     */
    public double getGuessingStdError() {
        return guessingStdError;
    }

    /**
     * The guessing parameter standard error may be computed external to the class. Use this method to set the
     * standard error to a particular value.
     *
     * @param stdError standard error for the guessing parameter estimate.
     */
    public void setGuessingStdError(double stdError) {
        guessingStdError = stdError;
    }

    public void setSlipping(double slipping) {
        this.slipping = slipping;
    }

    public void setProposalSlipping(double slipping) {
        this.proposalSlipping = slipping;
    }

    public void setSlippingStdError(double slipping) {

    }

    public double getSlipping() {
        return slipping;
    }

    public double getSlippingStdError() {
        return Double.NaN;
    }

    public double getScalingConstant() {
        return D;
    }

    /**
     * Proposal values for every item parameter are obtained at each iteration of the estimation routine. The
     * proposal values for each parameters are obtained for each in turn using the estimated values from the
     * previous iteration. For example, a proposal difficulty estimate for itemA is obtained in iteration k+1
     * using estimates from iteration k. Then, a proposal difficulty estimate for itemB is obtained in iteration k+1
     * using estimates from iteration k (even though a new estimate exists for itemA). After obtaining proposal
     * values for every item on the test, the proposal values can be accepted as the new parameter estimates. This
     * method must be called to accept the proposal values as the new estimates.
     *
     * Returns teh maximum relative absolute difference between existing parameters and new parameters.
     *
     */
    public double acceptAllProposalValues() {
        double max = 0;

        double delta = Math.abs(this.difficulty - proposalDifficulty);
        if (proposalDifficulty >= 1)
            delta /= proposalDifficulty;
        max = Math.max(max, delta);
        this.difficulty = proposalDifficulty;

        if (numberOfParameters >= 2) {
            delta = Math.abs(this.discrimination - proposalDiscrimination);
            if (proposalDiscrimination >= 1)
                delta /= proposalDiscrimination;
            max = Math.max(max, delta);
            this.discrimination = proposalDiscrimination;
        }

        if (numberOfParameters == 3) {
            delta = Math.abs(this.guessing - proposalGuessing);
            if (proposalGuessing >= 1)
                delta /= proposalGuessing;
            max = Math.max(max, delta);
            this.guessing = proposalGuessing;
        }

        return max;
    }

    public double[] getStepParameters() {
        double[] sp = new double[ncat];
        for (int k = 0; k < ncat; k++) {
            sp[k] = Double.NaN;
        }
        return sp;
    }

    public void setStepStdError(double[] stdError) {

    }

    public double[] getStepStdError() {
        double[] sp = new double[ncat];
        for (int k = 0; k < ncat; k++) {
            sp[k] = Double.NaN;
        }
        return sp;
    }

    public double[] getThresholdParameters() {
        double[] t = { 0 };
        return t;
    }

    public double[] getThresholdStdError() {
        double[] sp = new double[ncat];
        for (int k = 0; k < ncat; k++) {
            sp[k] = Double.NaN;
        }
        return sp;
    }

    public void setThresholdStdError(double[] stdError) {

    }

    public void setStepParameters(double[] step) {

    }

    public void setProposalStepParameters(double[] step) {

    }

    public void setThresholdParameters(double[] thresholdParameters) {

    }

    public void setProposalThresholds(double[] thresholds) {

    }
    //=====================================================================================================================//
    // END GETTER AND SETTER METHODS                                                                                       //
    //=====================================================================================================================//

    /**
     * A string representation of the item parameters. Mainly used for printing and debugging.
     *
     * @return a string of item parameters.
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        Formatter f = new Formatter(sb);

        String name = getName().toString();
        if (getName() != null) {
            name = getName().toString().substring(0, Math.min(18, name.length()));
        } else {
            name = "";
        }
        f.format("%-18s", name);
        f.format("%2s", "");

        String m = "";
        if (numberOfParameters == 3) {
            m = "L3";
        } else if (numberOfParameters == 2) {
            m = "L2";
        } else {
            m = "L1";
        }
        f.format("%-3s", m);
        f.format("%4s", "");

        f.format("% 4.2f", getDiscrimination());
        f.format("%1s", "");
        f.format("(%4.2f)", getDiscriminationStdError());
        f.format("%4s", "");

        f.format("% 4.2f", getDifficulty());
        f.format("%1s", "");
        f.format("(%4.2f)", getDifficultyStdError());
        f.format("%4s", "");

        if ((numberOfParameters < 3 && getGuessing() > 0) || numberOfParameters == 3) {
            f.format("% 4.2f", getGuessing());
            f.format("%1s", "");
            f.format("(%4.2f)", getGuessingStdError());
            f.format("%4s", "");
        } else {
            f.format("%13s", "");
        }

        if (getSlipping() < 1) {
            f.format("% 4.2f", getSlipping());
            f.format("%1s", "");
            f.format("(%4.2f)", getSlippingStdError());
            f.format("%4s", "");
        }
        return f.toString();

        //        //OLD==================================================================
        //        String name = "";
        //        if(getName()!=null){
        //            name = getName().toString();
        //        }
        //
        //        f.format("%10s", name);f.format("%2s", ": ");
        //        f.format("%1s", "[");
        //        f.format("% .6f", getDiscrimination()); f.format("%2s", ", ");
        //        f.format("% .6f", getDifficulty()); f.format("%2s", ", ");
        //        f.format("% .6f", getGuessing());
        //
        //        if(getSlipping()<1) {
        //            f.format("%2s", ", ");
        //            f.format("% .6f", getSlipping());
        //        }
        //        f.format("%1s", "]");
        //        f.format("%n");
        //        f.format("%10s", "");f.format("%2s", "");
        //        f.format("%1s", "(");
        //        f.format("% .6f", getDiscriminationStdError()); f.format("%2s", ", ");
        //        f.format("% .6f", getDifficultyStdError()); f.format("%2s", ", ");
        //        f.format("% .6f", getGuessingStdError());
        //        if(getSlipping()<1){
        //            f.format("%2s", ", ");
        //            f.format("% .6f", getSlippingStdError());
        //        }
        //        f.format("%1s", ")");
        //
        //        return f.toString();
    }

}