protected void TrusterMF() {
		for (int iter = 1; iter <= numIters; iter++) {
			loss = 0;
			errs = 0;

			// gradients of B, V, W
			DenseMatrix BS = new DenseMatrix(numUsers, numFactors);
			DenseMatrix WS = new DenseMatrix(numUsers, numFactors);
			DenseMatrix VS = new DenseMatrix(numItems, numFactors);

			// rate matrix
			for (MatrixEntry me : trainMatrix) {

				int u = me.row();
				int j = me.column();
				double ruj = me.get();
				if (ruj > 0) {
					double pred = predict(u, j, false);
					double euj = g(pred) - normalize(ruj);

					loss += euj * euj;
					errs += euj * euj;

					double csgd = gd(pred) * euj;

					for (int f = 0; f < numFactors; f++) {
						BS.add(u, f, csgd * Vr.get(j, f) + regU * Br.get(u, f));
						VS.add(j, f, csgd * Br.get(u, f) + regI * Vr.get(j, f));

						loss += regU * Br.get(u, f) * Br.get(u, f);
						loss += regI * Vr.get(j, f) * Vr.get(j, f);
					}
				}
			}

			// social matrix
			for (MatrixEntry me : socialMatrix) {

				int u = me.row();
				int k = me.column();
				double tuk = me.get();

				if (tuk > 0) {
					double pred = DenseMatrix.rowMult(Br, u, Wr, k);
					double euj = g(pred) - tuk;

					loss += regS * euj * euj;

					double csgd = gd(pred) * euj;

					for (int f = 0; f < numFactors; f++) {
						BS.add(u, f, regS * csgd * Wr.get(k, f) + regU * Br.get(u, f));
						WS.add(k, f, regS * csgd * Br.get(u, f) + regU * Wr.get(k, f));

						loss += regU * Br.get(u, f) * Br.get(u, f);
						loss += regU * Wr.get(u, f) * Wr.get(u, f);
					}
				}
			}

			Br = Br.add(BS.scale(-lRate));
			Vr = Vr.add(VS.scale(-lRate));
			Wr = Wr.add(WS.scale(-lRate));

			loss *= 0.5;
			errs *= 0.5;

			if (isConverged(iter))
				break;
		}
	}