Import code from my Subversion repository
[black_box_cellml.git] / runtime / BlackBoxVM.cpp
blob2943f1683db402c0bbc442d5405189b6ef867474
1 #include "BlackBoxRuntime.hpp"
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <sys/mman.h>
7 static double
8 random_double_logUniform()
10 union
12 double asDouble;
13 #ifdef WIN32
14 uint16_t asIntegers[4];
15 #else
16 uint32_t asIntegers[2];
17 #endif
18 } X;
20 #ifdef WIN32
21 uint16_t spareRand;
22 #else
23 uint32_t spareRand;
24 #endif
28 spareRand = rand();
29 #ifdef WIN32
30 X.asIntegers[0] = rand() | ((spareRand & 0x1) << 15);
31 X.asIntegers[1] = rand() | ((spareRand & 0x2) << 14);
32 X.asIntegers[2] = rand() | ((spareRand & 0x4) << 13);
33 X.asIntegers[3] = rand() | ((spareRand & 0x8) << 12);
34 #else
35 X.asIntegers[0] = rand() | ((spareRand & 0x1) << 31);
36 X.asIntegers[1] = rand() | ((spareRand & 0x2) << 30);
37 #endif
39 while (!isfinite(X.asDouble));
41 return X.asDouble;
44 class MixedModelVM
45 : public MixedModelBase
47 public:
48 MixedModelVM(const char* filename)
49 : mFile(NULL), mData(NULL), mCode(NULL), mInitBlackBoxes(NULL),
50 mInitConstants(NULL), mRun(NULL), mTrain(NULL), mFileEnd(NULL),
51 mInputVariableCount(0), mOutputVariableCount(0), mConstantCount(0),
52 mOtherVariableCount(0), mTempInputCount(0), mTempOutputCount(0),
53 mBlackBoxCount(0), mnSetJumps(10), mVMFlags(0), mReady(false),
54 mReturned(0.0)
56 memset(mSetJumps, 0xFF, sizeof(mSetJumps));
57 mStack = new uint32_t[1000];
58 mStackEnd = mStack + 1000 - 1;
60 int fd;
61 fd = open(filename, O_RDONLY);
62 if (fd == -1)
64 perror("Load black/white model VM file");
65 return;
68 struct stat sb;
69 if (fstat(fd, &sb) < 0)
71 perror("Stat black/white model VM file");
72 close(fd);
73 return;
76 if (!S_ISREG(sb.st_mode))
78 fprintf(stderr, "Black/white model VM file isn't a regular file.\n");
79 close(fd);
80 return;
83 mFileSize = sb.st_size;
84 mFile = (uint8_t*)mmap(0, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
85 if (mFile == NULL)
87 perror("mmap black/white model VM file");
88 close(fd);
89 return;
92 close(fd);
94 // Check the header is right...
95 if (mFileSize < 8)
97 unmap();
98 fprintf(stderr, "Black/white model VM file < 8 bytes!\n");
99 return;
102 if (memcmp(mFile, "\xB1\xAB\x0C\x00", 4))
104 unmap();
105 fprintf(stderr, "Black/white model has wrong signature.\n");
106 return;
109 mHeaderEnd = *reinterpret_cast<uint32_t*>(mFile + 4);
110 mHeaderEnd += 8;
111 if (mHeaderEnd >= mFileSize)
113 unmap();
114 fprintf(stderr, "Black/white file truncated in header.\n");
115 return;
118 mFileEnd = mFile + mFileSize;
120 mCode = mFile + mHeaderEnd;
122 uint8_t * ptr = mFile + 8;
123 while (ptr + 8 < mCode)
125 uint32_t slen = *reinterpret_cast<uint32_t*>(ptr);
126 ptr += 4;
127 if (slen + ptr + 4 < ptr || slen + ptr + 4 > mCode)
129 unmap();
130 fprintf(stderr, "Black/white header has invalid string length.\n");
131 return;
134 char* str = reinterpret_cast<char*>(ptr);
136 ptr += slen;
138 uint32_t offset = *reinterpret_cast<uint32_t*>(ptr);
139 ptr += 4;
141 if (slen == 18 && !strncmp(str, "inputVariableCount", 18))
143 mInputVariableCount = offset;
144 continue;
147 if (slen == 19 && !strncmp(str, "outputVariableCount", 19))
149 mOutputVariableCount = offset;
150 continue;
153 if (slen == 13 && !strncmp(str, "constantCount", 13))
155 mConstantCount = offset;
156 continue;
159 if (slen == 18 && !strncmp(str, "otherVariableCount", 18))
161 mOtherVariableCount = offset;
162 continue;
165 if (slen == 14 && !strncmp(str, "tempInputCount", 14))
167 mTempInputCount = offset;
168 continue;
171 if (slen == 15 && !strncmp(str, "tempOutputCount", 15))
173 mTempOutputCount = offset;
174 continue;
177 if (slen == 13 && !strncmp(str, "blackBoxCount", 13))
179 mBlackBoxCount = offset;
180 continue;
183 if (mCode + offset < mCode || mCode + offset >= mFileEnd)
185 unmap();
186 fprintf(stderr, "Black/white header has invalid function offset.\n");
187 return;
189 if (slen == 4 && !strncmp(str, "data", 4))
190 mData = mCode + offset;
191 else if (slen == 14 && !strncmp(str, "initBlackBoxes", 14))
192 mInitBlackBoxes = mCode + offset;
193 else if (slen == 13 && !strncmp(str, "initConstants", 13))
194 mInitConstants = mCode + offset;
195 else if (slen == 3 && !strncmp(str, "run", 3))
196 mRun = mCode + offset;
197 else if (slen == 5 && !strncmp(str, "train", 5))
198 mTrain = mCode + offset;
201 if (mData == NULL)
202 mData = mFileEnd;
204 INPUTS = reinterpret_cast<double*>(mData);
205 OUTPUTS = INPUTS + mInputVariableCount;
206 CONSTANTS = OUTPUTS + mOutputVariableCount;
207 VARIABLES = CONSTANTS + mConstantCount;
208 TMPINPUTS = VARIABLES + mOtherVariableCount;
209 TMPOUTPUTS = TMPINPUTS + mTempInputCount;
211 mReady = true;
214 ~MixedModelVM()
216 unmap();
219 uint32_t
220 countInputVariables() const
222 return mInputVariableCount;
225 uint32_t
226 countOutputVariables() const
228 return mOutputVariableCount;
231 uint32_t
232 countConstants() const
234 return mConstantCount;
237 uint32_t
238 countOtherVariables() const
240 return mOtherVariableCount;
243 uint32_t
244 countTempInputs() const
246 return mTempInputCount;
249 uint32_t
250 countTempOutputs() const
252 return mTempOutputCount;
255 uint32_t
256 countBlackBoxes() const
258 return mBlackBoxCount;
261 void
262 initConstants()
264 if (mInitConstants != NULL)
265 execute(mInitConstants);
268 void
269 initBlackBoxes()
271 if (mInitBlackBoxes != NULL)
272 execute(mInitBlackBoxes);
275 void
276 train(double * aINPUTS, double * aOUTPUTS)
278 memcpy(INPUTS, aINPUTS, mInputVariableCount * sizeof(double));
279 memcpy(OUTPUTS, aOUTPUTS, mOutputVariableCount * sizeof(double));
281 if (mTrain != NULL)
282 execute(mTrain);
285 void
286 run(double * aINPUTS, double * aOUTPUTS)
288 memcpy(INPUTS, aINPUTS, mInputVariableCount * sizeof(double));
290 if (mRun != NULL)
291 execute(mRun);
293 memcpy(aOUTPUTS, OUTPUTS, mOutputVariableCount * sizeof(double));
296 private:
297 size_t mFileSize;
298 uint8_t * mFile, * mData, * mCode, * mInitBlackBoxes, * mInitConstants,
299 * mRun, * mTrain, * mFileEnd, * mEIP, * mSetJumps[10];
300 uint32_t * mStack, * mStackEnd, * mESP;
301 uint32_t mHeaderEnd;
302 uint32_t mInputVariableCount, mOutputVariableCount, mConstantCount,
303 mOtherVariableCount, mTempInputCount, mTempOutputCount, mBlackBoxCount,
304 mnSetJumps;
305 #define VMFLAG_HALT 1
306 #define VMFLAG_GPE 2
307 uint32_t mVMFlags;
308 #define VM_HAD_HALT (mVMFlags & VMFLAG_HALT)
309 #define VM_HAD_GPE (mVMFlags & VMFLAG_GPE)
310 bool mReady;
311 double mReturned;
313 typedef void (MixedModelVM::* OpcodeFunction)();
314 static OpcodeFunction opCodes[256];
316 void unmap()
318 if (mFile != NULL)
319 munmap(mFile, mFileSize);
320 mFile = NULL;
323 void execute(uint8_t* aEIP);
325 void gpe(const char* aWhy)
327 printf("VM General Protection Error: %s\n", aWhy);
328 mVMFlags |= VMFLAG_HALT | VMFLAG_GPE;
331 uint32_t pop()
333 if (mESP != mStack)
335 mESP--;
336 return *mESP;
339 gpe("Attempt to pop from an empty stack.");
340 return 0;
343 double popDouble()
345 if (mESP - 1 > mStack)
347 mESP -= 2;
348 return *reinterpret_cast<double*>(mESP);
351 gpe("Attempt to pop double from an empty stack.");
352 return 0.0;
355 void push(uint32_t aVal)
357 if (mESP == mStackEnd - 1)
358 gpe("Stack overflow.");
359 *mESP++ = aVal;
362 void pushDouble(double aVal)
364 if (mESP >= mStackEnd - 2)
366 gpe("Stack overflow.");
367 return;
369 uint32_t* aRep = reinterpret_cast<uint32_t*>(&aVal);
370 *mESP++ = aRep[0];
371 *mESP++ = aRep[1];
374 void invalidOpcode()
376 gpe("Invalid opcode invoked.");
379 uint32_t getCodeLong()
381 if (mEIP + 3 >= mData)
383 gpe("Instruction pointer outside code range.");
384 return 0;
386 uint32_t ret = *reinterpret_cast<uint32_t*>(mEIP);
387 mEIP += 4;
389 return ret;
392 void pushImmed()
394 push(getCodeLong());
397 void opAcos()
399 pushDouble(acos(popDouble()));
402 void opAcosh()
404 pushDouble(acosh(popDouble()));
407 void opAnd()
409 uint32_t i = 0, l = pop();
410 for (; i < l; i++)
411 if (popDouble() == 0.0)
413 pushDouble(0.0);
414 for (i++; i < l; i++)
415 popDouble();
416 return;
418 pushDouble(1.0);
421 void opAsin()
423 pushDouble(asin(popDouble()));
426 void opAsinh()
428 pushDouble(asinh(popDouble()));
431 void opAtan()
433 pushDouble(atan(popDouble()));
436 void opAtanh()
438 pushDouble(atanh(popDouble()));
441 void opCeil()
443 pushDouble(ceil(popDouble()));
446 void opCos()
448 pushDouble(cos(popDouble()));
451 void opCosh()
453 pushDouble(cosh(popDouble()));
456 void opDivide()
458 double d = popDouble(), n = popDouble();
459 pushDouble(n / d);
462 void opEqual()
464 uint32_t i = 0, l = pop();
465 if (l == 0)
467 pushDouble(1.0);
468 return;
470 double val = popDouble();
472 for (i--; i < l; i++)
473 if (popDouble() != val)
475 pushDouble(0.0);
476 for (; i < l; i++)
477 popDouble();
478 return;
481 pushDouble(1.0);
484 void opExp()
486 pushDouble(exp(popDouble()));
489 void opFabs()
491 pushDouble(fabs(popDouble()));
494 void opFactorial()
496 double f = popDouble();
497 double v = 1.0;
498 while (f > 0.0)
500 v *= f;
501 f -= 1.0;
503 pushDouble(v);
506 void opFloor()
508 pushDouble(floor(popDouble()));
511 double gcd_pair(double a, double b)
513 uint32_t ai = (uint32_t)fabs(a), bi = (uint32_t)fabs(b);
514 unsigned int shift = 0;
515 if (ai == 0)
516 return bi;
517 if (bi == 0)
518 return ai;
519 #define EVEN(x) ((x&1)==0)
520 while (EVEN(ai) && EVEN(bi))
522 shift++;
523 ai >>= 1;
524 bi >>= 1;
528 if (EVEN(ai))
529 ai >>= 1;
530 else if (EVEN(bi))
531 bi >>= 1;
532 else if (ai >= bi)
533 ai = (ai - bi) >> 1;
534 else
535 bi = (bi - ai) >> 1;
537 while (ai > 0);
539 return (bi << shift);
542 double lcm_pair(double a, double b)
544 return (a * b) / gcd_pair(a, b);
547 void opGcd()
549 uint32_t i = 0, l = pop(), j = 0;
551 if (l == 0)
553 pushDouble(1.0);
554 return;
557 double * storage1, * storage2, * t, ret;
558 storage1 = new double[l];
559 if (storage1 == NULL)
561 gpe("Not enough memory to compute operation result.");
562 return;
564 storage2 = new double[l >> 1];
565 if (storage2 == NULL)
567 delete [] storage1;
568 gpe("Not enough memory to compute operation result.");
569 return;
572 for (; i < l - 1; i += 2)
573 storage1[j++] = gcd_pair(popDouble(), popDouble());
575 if (i < l)
576 storage1[j++] = popDouble();
578 while (j != 1)
580 l = j;
581 j = 0;
583 for (i = 0; i < j - 1; i += 2)
584 storage2[j++] = gcd_pair(storage1[i], storage1[i + 1]);
585 if (i < j)
586 storage2[j++] = storage1[i];
588 t = storage1;
589 storage1 = storage2;
590 storage2 = t;
593 ret = storage1[0];
594 delete [] storage1;
595 delete [] storage2;
597 pushDouble(ret);
600 void opGeq()
602 uint32_t i = 0, l = pop();
603 if (l == 0)
605 pushDouble(1.0);
606 return;
608 double val = popDouble(), t;
610 // Remember, the order is reversed due to the stack.
611 for (i--; i < l; i++)
612 if ((t = popDouble()) > val)
614 pushDouble(0.0);
615 for (; i < l; i++)
616 popDouble();
617 return;
619 else
620 val = t;
622 pushDouble(1.0);
625 void opGt()
627 uint32_t i = 0, l = pop();
628 if (l == 0)
630 pushDouble(1.0);
631 return;
633 double val = popDouble(), t;
635 // Remember, the order is reversed due to the stack.
636 for (i--; i < l; i++)
637 if ((t = popDouble()) >= val)
639 pushDouble(0.0);
640 for (; i < l; i++)
641 popDouble();
642 return;
644 else
645 val = t;
647 pushDouble(1.0);
650 void opIntegrate()
652 gpe("Sorry, definite integrals not implemented.");
655 void opLcm()
657 uint32_t i = 0, l = pop(), j = 0;
659 if (l == 0)
661 pushDouble(1.0);
662 return;
665 double * storage1, * storage2, * t, ret;
666 storage1 = new double[l];
667 if (storage1 == NULL)
669 gpe("Not enough memory to compute operation result.");
670 return;
672 storage2 = new double[l >> 1];
673 if (storage2 == NULL)
675 delete [] storage1;
676 gpe("Not enough memory to compute operation result.");
677 return;
680 for (; i < l - 1; i += 2)
681 storage1[j++] = lcm_pair(popDouble(), popDouble());
683 if (i < l)
684 storage1[j++] = popDouble();
686 while (j != 1)
688 l = j;
689 j = 0;
691 for (i = 0; i < j - 1; i += 2)
692 storage2[j++] = lcm_pair(storage1[i], storage1[i + 1]);
693 if (i < j)
694 storage2[j++] = storage1[i];
696 t = storage1;
697 storage1 = storage2;
698 storage2 = t;
701 ret = storage1[0];
702 delete [] storage1;
703 delete [] storage2;
705 pushDouble(ret);
708 void opLeq()
710 uint32_t i = 0, l = pop();
711 if (l == 0)
713 pushDouble(1.0);
714 return;
716 double val = popDouble(), t;
718 // Remember, the order is reversed due to the stack.
719 for (i--; i < l; i++)
720 if ((t = popDouble()) > val)
722 pushDouble(0.0);
723 for (; i < l; i++)
724 popDouble();
725 return;
727 else
728 val = t;
730 pushDouble(1.0);
733 void opLog()
735 pushDouble(log(popDouble()));
738 void opLt()
740 uint32_t i = 0, l = pop();
741 if (l == 0)
743 pushDouble(1.0);
744 return;
746 double val = popDouble(), t;
748 // Remember, the order is reversed due to the stack.
749 for (i--; i < l; i++)
750 if ((t = popDouble()) <= val)
752 pushDouble(0.0);
753 for (; i < l; i++)
754 popDouble();
755 return;
757 else
758 val = t;
760 pushDouble(1.0);
763 void opMax()
765 uint32_t i = 0, l = pop();
766 if (l == 0)
768 pushDouble(strtod("NAN", NULL));
769 return;
772 double best = popDouble(), t;
773 for (l--; i < l; i++)
775 t = popDouble();
776 if (t > best)
777 best = t;
779 pushDouble(best);
782 void opMin()
784 uint32_t i = 0, l = pop();
785 if (l == 0)
787 pushDouble(strtod("NAN", NULL));
788 return;
791 double best = popDouble(), t;
792 for (l--; i < l; i++)
794 t = popDouble();
795 if (t < best)
796 best = t;
798 pushDouble(best);
801 void opMinus()
803 pushDouble(-(popDouble() - popDouble()));
806 void opMod()
808 double d = popDouble(), n = popDouble();
809 if (!isfinite(n) || !isfinite(d))
811 pushDouble(strtod("NAN", NULL));
812 return;
814 int in = (int)n;
815 int id = (int)d;
816 if (id == 0)
818 pushDouble(strtod("NAN", NULL));
819 return;
822 pushDouble(in % id);
825 void opNeq()
827 pushDouble((popDouble() == popDouble()) ? 0.0 : 1.0);
830 void opNot()
832 pushDouble((popDouble() == 0.0) ? 1.0 : 0.0);
835 void opOr()
837 uint32_t i = 0, l = pop();
838 for (; i < l; i++)
839 if (popDouble() != 0.0)
841 pushDouble(1.0);
842 for (i++; i < l; i++)
843 popDouble();
844 return;
846 pushDouble(0.0);
849 void opPlus()
851 uint32_t i = 0, l = pop();
852 double sum = 0.0;
853 for (; i < l; i++)
854 sum += popDouble();
855 pushDouble(sum);
858 void opPow()
860 double e = popDouble(), v = popDouble();
861 pushDouble(pow(v, e));
864 void opQuot()
866 double d = popDouble(), n = popDouble();
867 if (!isfinite(n) || !isfinite(d))
869 pushDouble(strtod("NAN", NULL));
870 return;
872 int in = (int)n;
873 int id = (int)d;
874 if (id == 0)
876 pushDouble(strtod("NAN", NULL));
877 return;
880 pushDouble(in / id);
883 void opReturn()
885 // Return with no stack means halt...
886 if (mESP == mStack)
888 mVMFlags |= VMFLAG_HALT;
889 return;
892 mEIP = mCode + pop();
893 if (mEIP < mCode || mEIP >= mData)
894 gpe("Return into non-code address.");
897 void opReturn1()
899 if (mESP == mStack)
901 gpe("Return1 without stack operand.");
902 return;
905 double retVal = popDouble();
907 // Return with no stack means halt...
908 if (mESP == mStack)
910 mVMFlags |= VMFLAG_HALT;
911 mReturned = retVal;
912 return;
915 mEIP = mCode + pop();
916 if (mEIP < mCode || mEIP >= mData)
917 gpe("Return into non-code address.");
919 pushDouble(retVal);
922 void opSin()
924 pushDouble(sin(popDouble()));
927 void opSinh()
929 pushDouble(sinh(popDouble()));
932 void opTan()
934 pushDouble(tan(popDouble()));
937 void opTanh()
939 pushDouble(tanh(popDouble()));
942 void opTimes()
944 uint32_t i = 0, l = pop();
945 double prod = 0.0;
946 for (; i < l; i++)
947 prod *= popDouble();
948 pushDouble(prod);
951 void opUminus()
953 pushDouble(-popDouble());
956 void opXor()
958 uint32_t i = 0, l = pop();
959 bool val = false;
960 for (; i < l; i++)
961 if (popDouble() != 0.0)
962 val = !val;
963 pushDouble(val ? 1.0 : 0.0);
966 void opConverged()
968 pushDouble(runConverged() ? 1.0 : 0.0);
971 void opJmpIf()
973 bool doJump = (popDouble() != 0.0);
974 uint32_t jmpTo = pop();
976 if (doJump == 0.0)
977 return;
979 if (jmpTo > sizeof(mnSetJumps))
981 gpe("jmpif for invalid setjmp ID.");
982 return;
984 if (mSetJumps[jmpTo] == (uint8_t*)0xFFFFFFFF)
986 gpe("jmpif to ID not previously setjmpd.");
987 return;
990 mEIP = mSetJumps[jmpTo];
993 void opResetConvergence()
995 resetConvergenceCounter();
998 void opSavePoint()
1000 convergenceSavePoint();
1003 void opCallTrain()
1005 uint32_t bbn = pop();
1006 if (bbn >= mBlackBoxCount)
1008 gpe("calltrain: reference to black box which doesn't exist.");
1009 return;
1011 if (blackBoxes[bbn] == NULL)
1013 gpe("calltrain: reference to black box which hasn't been initialised.");
1014 return;
1017 blackBoxes[bbn]->train(TMPINPUTS, TMPOUTPUTS);
1020 void opCallRun()
1022 uint32_t bbn = pop();
1023 if (bbn >= mBlackBoxCount)
1025 printf("Attempted to access black box 0x%x but only %u\n",
1026 bbn, mBlackBoxCount);
1027 gpe("callrun: reference to black box which doesn't exist.");
1028 return;
1030 if (blackBoxes[bbn] == NULL)
1032 gpe("callrun: reference to black box which hasn't been initialised.");
1033 return;
1036 blackBoxes[bbn]->run(TMPINPUTS, TMPOUTPUTS);
1039 void opCreateBB()
1041 uint32_t bbn = pop();
1042 if (bbn >= mBlackBoxCount)
1044 gpe("createbb: reference to black box which doesn't exist.");
1045 return;
1047 if (blackBoxes[bbn] != NULL)
1049 gpe("createbb: attempt to create the same black box twice.");
1050 return;
1053 uint32_t bbOutCount = pop(), bbInCount = pop(), bbStringAddr = pop();
1055 if (bbInCount > mTempInputCount)
1057 gpe("Black box input count exceeds specified maximal input count.");
1058 return;
1061 if (bbOutCount > mTempOutputCount)
1063 gpe("Black box output count exceeds specified maximal output count.");
1064 return;
1067 if (bbStringAddr >= (mFileEnd - mData) ||
1068 (bbStringAddr + 3) >= (mFileEnd - mData))
1070 gpe("String constant address out of data range.");
1071 return;
1073 uint32_t len = *reinterpret_cast<uint32_t*>(mData + bbStringAddr);
1075 if (bbStringAddr + 4 + len > (mFileEnd - mData))
1077 gpe("String length puts end of string out of data region.");
1078 return;
1081 std::wstring URI(reinterpret_cast<wchar_t*>(bbStringAddr + 4 + mData),
1082 len / sizeof(wchar_t));
1086 createBlackBox(bbn, URI.c_str(), bbInCount, bbOutCount);
1088 catch (...)
1090 gpe("createBlackBox instruction failed.");
1094 void opGetAvgInput()
1096 uint32_t idx = pop();
1097 uint32_t bbn = pop();
1098 if (bbn >= mBlackBoxCount)
1100 gpe("getavginput: reference to black box which doesn't exist.");
1101 return;
1103 if (blackBoxes[bbn] == NULL)
1105 gpe("getavginput: reference to black box which hasn't been initialised.");
1106 return;
1109 pushDouble(blackBoxes[bbn]->GetAverageInput(idx));
1112 double
1113 executeCallback(uint8_t* func)
1115 uint32_t* stackSave = mStack;
1116 execute(func);
1117 mStack = stackSave;
1118 if (VM_HAD_GPE)
1119 return 0.0;
1120 mVMFlags &= ~VMFLAG_HALT;
1121 return mReturned;
1124 double
1125 take_numeric_derivative
1127 uint8_t* aFunc,
1128 double* aVal
1131 double saved_x, value0, value1, value2, delta, slope1, slope2, ratio;
1132 saved_x = *aVal;
1134 * Since we have only 52 bits of mantissa, we need delta to flip only the
1135 * last couple of bits.
1137 delta = saved_x * 1E-10;
1138 value0 = executeCallback(aFunc);
1139 *aVal -= delta;
1140 value1 = executeCallback(aFunc);
1141 *aVal = saved_x + delta;
1142 value2 = executeCallback(aFunc);
1143 *aVal = saved_x;
1144 /* We take two numeric derivatives, so we can compare them. This stops us
1145 * from getting trapped at around discontinuities(which otherwise produce
1146 * very steep trapping slopes).
1148 slope1 = (value0 - value1) / delta;
1149 slope2 = (value2 - value0) / delta;
1151 /* If one slope is zero, return the other... */
1152 if (slope1 == 0.0 || !isfinite(slope1))
1153 return slope2;
1154 if (slope2 == 0.0 || !isfinite(slope2))
1155 return slope1;
1157 ratio = slope1 / slope2;
1158 /* If the slopes are similar, return the average... */
1159 if (ratio > 0.5 && ratio < 2)
1160 return (slope1 + slope2) / 2.0;
1161 /* Otherwise, return the least steep of the two... */
1162 if (fabs(slope1) < fabs(slope2))
1163 return slope1;
1164 return slope2;
1167 #define NR_RANDOM_STARTS 100
1168 #define NR_MAX_STEPS 1000
1169 #define NR_MAX_STEPS_INITIAL 10
1171 void opMinimise()
1173 uint32_t minVar = pop();
1174 uint32_t minFunc = pop();
1176 if (minFunc >= mData - mCode)
1178 gpe("Attempt to minimise a function which isn't in the code block.");
1179 return;
1181 uint8_t * func = mCode + minFunc;
1183 if (minVar + 2 > (mFileEnd - mData) * 4 ||
1184 minVar > minVar + 2)
1186 gpe("Attempt to minimise a function over a variable not in the data block.");
1187 return;
1190 double * val = reinterpret_cast<double*>(mData + minVar * 4);
1192 double best_X, best_fX = INFINITY;
1193 double current_X, current_fX, current_dfX_dX;
1194 uint32_t steps, maxsteps;
1195 uint32_t i;
1197 current_X = *val;
1199 /* We use a 100 random start Newton-Raphson algorithm... */
1200 for (i = 0; i < NR_RANDOM_STARTS; i++)
1202 /* Choose a random X as a starting point, except for the first run. */
1203 if (i > 0 || !isfinite(current_X))
1204 current_X = random_double_logUniform();
1206 maxsteps = NR_MAX_STEPS_INITIAL;
1207 for (steps = 0; steps < maxsteps; steps++)
1209 *val = current_X;
1210 current_fX = executeCallback(func);
1211 if (!isfinite(current_fX))
1213 break;
1216 if (best_fX > current_fX)
1218 /* We can go past NR_MAX_STEPS_INITIAL steps up to NR_MAX_STEPS steps,
1219 * but only if we keep improving on our previous answer.
1221 maxsteps += 2;
1222 if (maxsteps > NR_MAX_STEPS)
1223 maxsteps = NR_MAX_STEPS;
1224 best_fX = current_fX;
1225 best_X = current_X;
1226 /* This is quite far from 0(relatively speaking for a double) to avoid
1227 * numerical instability issues when the minimum doesn't exist but the
1228 * limit at a point from a particular direction would otherwise be the
1229 * minimum.
1231 if (best_fX <= 1E-250)
1233 break;
1237 current_dfX_dX = take_numeric_derivative(func, val);
1238 /* If it is completely flat, or infinite, we are done with this round. */
1239 if (!isfinite(current_dfX_dX) || current_dfX_dX == 0.0)
1241 break;
1244 current_X -= current_fX / current_dfX_dX;
1245 if (!isfinite(current_X))
1247 break;
1250 if (best_fX <= 1E-250)
1251 break;
1254 *val = best_X;
1256 take_numeric_derivative(func, val);
1259 void opSetJmp()
1261 uint32_t sj = pop();
1262 if (sj >= mnSetJumps)
1264 gpe("Attempt to setjmp to an index out of range.");
1265 return;
1268 mSetJumps[sj] = mEIP;
1271 void opFetchDouble()
1273 uint32_t fetchVar = getCodeLong();
1275 if (fetchVar + 2 > (mFileEnd - mData) * 4 ||
1276 fetchVar > fetchVar + 2)
1278 gpe("Attempt to fetch a variable which isn't in the data block.");
1279 return;
1282 pushDouble(*reinterpret_cast<double*>(mData + fetchVar * 4));
1285 void opMov()
1287 uint32_t movInto = pop();
1288 double movWhat = popDouble();
1290 if (movInto + 2 > (mFileEnd - mData) * 4 ||
1291 movInto > movInto + 2)
1293 gpe("Attempt to move into a variable which isn't in the data block.");
1294 return;
1297 *reinterpret_cast<double*>(mData + movInto * 4) = movWhat;
1301 MixedModelVM::OpcodeFunction
1302 MixedModelVM::opCodes[256] =
1304 &MixedModelVM::invalidOpcode /* 00 */,
1305 &MixedModelVM::pushImmed /* 01 */,
1306 &MixedModelVM::opAcos /* 02 */,
1307 &MixedModelVM::opAcosh /* 03 */,
1308 &MixedModelVM::opAnd /* 04 */,
1309 &MixedModelVM::opAsin /* 05 */,
1310 &MixedModelVM::opAsinh /* 06 */,
1311 &MixedModelVM::opAtan /* 07 */,
1312 &MixedModelVM::opAtanh /* 08 */,
1313 &MixedModelVM::opCeil /* 09 */,
1314 &MixedModelVM::opCos /* 0A */,
1315 &MixedModelVM::opCosh /* 0B */,
1316 &MixedModelVM::opDivide /* 0C */,
1317 &MixedModelVM::opEqual /* 0D */,
1318 &MixedModelVM::opExp /* 0E */,
1319 &MixedModelVM::opFabs /* 0F */,
1320 &MixedModelVM::opFactorial /* 10 */,
1321 &MixedModelVM::opFloor /* 11 */,
1322 &MixedModelVM::opGcd /* 12 */,
1323 &MixedModelVM::opGeq /* 13 */,
1324 &MixedModelVM::opGt /* 14 */,
1325 &MixedModelVM::opIntegrate /* 15 */,
1326 &MixedModelVM::opLcm /* 16 */,
1327 &MixedModelVM::opLeq /* 17 */,
1328 &MixedModelVM::opLog /* 18 */,
1329 &MixedModelVM::opLt /* 19 */,
1330 &MixedModelVM::opMax /* 1A */,
1331 &MixedModelVM::opMin /* 1B */,
1332 &MixedModelVM::opMinus /* 1C */,
1333 &MixedModelVM::opMod /* 1D */,
1334 &MixedModelVM::opNeq /* 1E */,
1335 &MixedModelVM::opNot /* 1F */,
1336 &MixedModelVM::opOr /* 20 */,
1337 &MixedModelVM::opPlus /* 21 */,
1338 &MixedModelVM::opPow /* 22 */,
1339 &MixedModelVM::opQuot /* 23 */,
1340 &MixedModelVM::opReturn /* 24 */,
1341 &MixedModelVM::opSin /* 25 */,
1342 &MixedModelVM::opSinh /* 26 */,
1343 &MixedModelVM::opTan /* 27 */,
1344 &MixedModelVM::opTanh /* 28 */,
1345 &MixedModelVM::opTimes /* 29 */,
1346 &MixedModelVM::opUminus /* 2A */,
1347 &MixedModelVM::opXor /* 2B */,
1348 &MixedModelVM::opConverged /* 2C */,
1349 &MixedModelVM::opJmpIf /* 2D */,
1350 &MixedModelVM::opResetConvergence /* 2E */,
1351 &MixedModelVM::opSavePoint /* 2F */,
1352 &MixedModelVM::opCallTrain /* 30 */,
1353 &MixedModelVM::opCallRun /* 31 */,
1354 &MixedModelVM::opCreateBB /* 32 */,
1355 &MixedModelVM::opGetAvgInput /* 33 */,
1356 &MixedModelVM::opMinimise /* 34 */,
1357 &MixedModelVM::opSetJmp /* 35 */,
1358 &MixedModelVM::opFetchDouble /* 36 */,
1359 &MixedModelVM::opMov /* 37 */,
1360 &MixedModelVM::opReturn1 /* 38 */,
1361 &MixedModelVM::invalidOpcode /* 39 */,
1362 &MixedModelVM::invalidOpcode /* 3A */,
1363 &MixedModelVM::invalidOpcode /* 3B */,
1364 &MixedModelVM::invalidOpcode /* 3C */,
1365 &MixedModelVM::invalidOpcode /* 3D */,
1366 &MixedModelVM::invalidOpcode /* 3E */,
1367 &MixedModelVM::invalidOpcode /* 3F */,
1368 &MixedModelVM::invalidOpcode /* 40 */,
1369 &MixedModelVM::invalidOpcode /* 41 */,
1370 &MixedModelVM::invalidOpcode /* 42 */,
1371 &MixedModelVM::invalidOpcode /* 43 */,
1372 &MixedModelVM::invalidOpcode /* 44 */,
1373 &MixedModelVM::invalidOpcode /* 45 */,
1374 &MixedModelVM::invalidOpcode /* 46 */,
1375 &MixedModelVM::invalidOpcode /* 47 */,
1376 &MixedModelVM::invalidOpcode /* 48 */,
1377 &MixedModelVM::invalidOpcode /* 49 */,
1378 &MixedModelVM::invalidOpcode /* 4A */,
1379 &MixedModelVM::invalidOpcode /* 4B */,
1380 &MixedModelVM::invalidOpcode /* 4C */,
1381 &MixedModelVM::invalidOpcode /* 4D */,
1382 &MixedModelVM::invalidOpcode /* 4E */,
1383 &MixedModelVM::invalidOpcode /* 4F */,
1384 &MixedModelVM::invalidOpcode /* 50 */,
1385 &MixedModelVM::invalidOpcode /* 51 */,
1386 &MixedModelVM::invalidOpcode /* 52 */,
1387 &MixedModelVM::invalidOpcode /* 53 */,
1388 &MixedModelVM::invalidOpcode /* 54 */,
1389 &MixedModelVM::invalidOpcode /* 55 */,
1390 &MixedModelVM::invalidOpcode /* 56 */,
1391 &MixedModelVM::invalidOpcode /* 57 */,
1392 &MixedModelVM::invalidOpcode /* 58 */,
1393 &MixedModelVM::invalidOpcode /* 59 */,
1394 &MixedModelVM::invalidOpcode /* 5A */,
1395 &MixedModelVM::invalidOpcode /* 5B */,
1396 &MixedModelVM::invalidOpcode /* 5C */,
1397 &MixedModelVM::invalidOpcode /* 5D */,
1398 &MixedModelVM::invalidOpcode /* 5E */,
1399 &MixedModelVM::invalidOpcode /* 5F */,
1400 &MixedModelVM::invalidOpcode /* 60 */,
1401 &MixedModelVM::invalidOpcode /* 61 */,
1402 &MixedModelVM::invalidOpcode /* 62 */,
1403 &MixedModelVM::invalidOpcode /* 63 */,
1404 &MixedModelVM::invalidOpcode /* 64 */,
1405 &MixedModelVM::invalidOpcode /* 65 */,
1406 &MixedModelVM::invalidOpcode /* 66 */,
1407 &MixedModelVM::invalidOpcode /* 67 */,
1408 &MixedModelVM::invalidOpcode /* 68 */,
1409 &MixedModelVM::invalidOpcode /* 69 */,
1410 &MixedModelVM::invalidOpcode /* 6A */,
1411 &MixedModelVM::invalidOpcode /* 6B */,
1412 &MixedModelVM::invalidOpcode /* 6C */,
1413 &MixedModelVM::invalidOpcode /* 6D */,
1414 &MixedModelVM::invalidOpcode /* 6E */,
1415 &MixedModelVM::invalidOpcode /* 6F */,
1416 &MixedModelVM::invalidOpcode /* 70 */,
1417 &MixedModelVM::invalidOpcode /* 71 */,
1418 &MixedModelVM::invalidOpcode /* 72 */,
1419 &MixedModelVM::invalidOpcode /* 73 */,
1420 &MixedModelVM::invalidOpcode /* 74 */,
1421 &MixedModelVM::invalidOpcode /* 75 */,
1422 &MixedModelVM::invalidOpcode /* 76 */,
1423 &MixedModelVM::invalidOpcode /* 77 */,
1424 &MixedModelVM::invalidOpcode /* 78 */,
1425 &MixedModelVM::invalidOpcode /* 79 */,
1426 &MixedModelVM::invalidOpcode /* 7A */,
1427 &MixedModelVM::invalidOpcode /* 7B */,
1428 &MixedModelVM::invalidOpcode /* 7C */,
1429 &MixedModelVM::invalidOpcode /* 7D */,
1430 &MixedModelVM::invalidOpcode /* 7E */,
1431 &MixedModelVM::invalidOpcode /* 7F */,
1432 &MixedModelVM::invalidOpcode /* 80 */,
1433 &MixedModelVM::invalidOpcode /* 81 */,
1434 &MixedModelVM::invalidOpcode /* 82 */,
1435 &MixedModelVM::invalidOpcode /* 83 */,
1436 &MixedModelVM::invalidOpcode /* 84 */,
1437 &MixedModelVM::invalidOpcode /* 85 */,
1438 &MixedModelVM::invalidOpcode /* 86 */,
1439 &MixedModelVM::invalidOpcode /* 87 */,
1440 &MixedModelVM::invalidOpcode /* 88 */,
1441 &MixedModelVM::invalidOpcode /* 89 */,
1442 &MixedModelVM::invalidOpcode /* 8A */,
1443 &MixedModelVM::invalidOpcode /* 8B */,
1444 &MixedModelVM::invalidOpcode /* 8C */,
1445 &MixedModelVM::invalidOpcode /* 8D */,
1446 &MixedModelVM::invalidOpcode /* 8E */,
1447 &MixedModelVM::invalidOpcode /* 8F */,
1448 &MixedModelVM::invalidOpcode /* 90 */,
1449 &MixedModelVM::invalidOpcode /* 91 */,
1450 &MixedModelVM::invalidOpcode /* 92 */,
1451 &MixedModelVM::invalidOpcode /* 93 */,
1452 &MixedModelVM::invalidOpcode /* 94 */,
1453 &MixedModelVM::invalidOpcode /* 95 */,
1454 &MixedModelVM::invalidOpcode /* 96 */,
1455 &MixedModelVM::invalidOpcode /* 97 */,
1456 &MixedModelVM::invalidOpcode /* 98 */,
1457 &MixedModelVM::invalidOpcode /* 99 */,
1458 &MixedModelVM::invalidOpcode /* 9A */,
1459 &MixedModelVM::invalidOpcode /* 9B */,
1460 &MixedModelVM::invalidOpcode /* 9C */,
1461 &MixedModelVM::invalidOpcode /* 9D */,
1462 &MixedModelVM::invalidOpcode /* 9E */,
1463 &MixedModelVM::invalidOpcode /* 9F */,
1464 &MixedModelVM::invalidOpcode /* A0 */,
1465 &MixedModelVM::invalidOpcode /* A1 */,
1466 &MixedModelVM::invalidOpcode /* A2 */,
1467 &MixedModelVM::invalidOpcode /* A3 */,
1468 &MixedModelVM::invalidOpcode /* A4 */,
1469 &MixedModelVM::invalidOpcode /* A5 */,
1470 &MixedModelVM::invalidOpcode /* A6 */,
1471 &MixedModelVM::invalidOpcode /* A7 */,
1472 &MixedModelVM::invalidOpcode /* A8 */,
1473 &MixedModelVM::invalidOpcode /* A9 */,
1474 &MixedModelVM::invalidOpcode /* AA */,
1475 &MixedModelVM::invalidOpcode /* AB */,
1476 &MixedModelVM::invalidOpcode /* AC */,
1477 &MixedModelVM::invalidOpcode /* AD */,
1478 &MixedModelVM::invalidOpcode /* AE */,
1479 &MixedModelVM::invalidOpcode /* AF */,
1480 &MixedModelVM::invalidOpcode /* B0 */,
1481 &MixedModelVM::invalidOpcode /* B1 */,
1482 &MixedModelVM::invalidOpcode /* B2 */,
1483 &MixedModelVM::invalidOpcode /* B3 */,
1484 &MixedModelVM::invalidOpcode /* B4 */,
1485 &MixedModelVM::invalidOpcode /* B5 */,
1486 &MixedModelVM::invalidOpcode /* B6 */,
1487 &MixedModelVM::invalidOpcode /* B7 */,
1488 &MixedModelVM::invalidOpcode /* B8 */,
1489 &MixedModelVM::invalidOpcode /* B9 */,
1490 &MixedModelVM::invalidOpcode /* BA */,
1491 &MixedModelVM::invalidOpcode /* BB */,
1492 &MixedModelVM::invalidOpcode /* BC */,
1493 &MixedModelVM::invalidOpcode /* BD */,
1494 &MixedModelVM::invalidOpcode /* BE */,
1495 &MixedModelVM::invalidOpcode /* BF */,
1496 &MixedModelVM::invalidOpcode /* C0 */,
1497 &MixedModelVM::invalidOpcode /* C1 */,
1498 &MixedModelVM::invalidOpcode /* C2 */,
1499 &MixedModelVM::invalidOpcode /* C3 */,
1500 &MixedModelVM::invalidOpcode /* C4 */,
1501 &MixedModelVM::invalidOpcode /* C5 */,
1502 &MixedModelVM::invalidOpcode /* C6 */,
1503 &MixedModelVM::invalidOpcode /* C7 */,
1504 &MixedModelVM::invalidOpcode /* C8 */,
1505 &MixedModelVM::invalidOpcode /* C9 */,
1506 &MixedModelVM::invalidOpcode /* CA */,
1507 &MixedModelVM::invalidOpcode /* CB */,
1508 &MixedModelVM::invalidOpcode /* CC */,
1509 &MixedModelVM::invalidOpcode /* CD */,
1510 &MixedModelVM::invalidOpcode /* CE */,
1511 &MixedModelVM::invalidOpcode /* CF */,
1512 &MixedModelVM::invalidOpcode /* D0 */,
1513 &MixedModelVM::invalidOpcode /* D1 */,
1514 &MixedModelVM::invalidOpcode /* D2 */,
1515 &MixedModelVM::invalidOpcode /* D3 */,
1516 &MixedModelVM::invalidOpcode /* D4 */,
1517 &MixedModelVM::invalidOpcode /* D5 */,
1518 &MixedModelVM::invalidOpcode /* D6 */,
1519 &MixedModelVM::invalidOpcode /* D7 */,
1520 &MixedModelVM::invalidOpcode /* D8 */,
1521 &MixedModelVM::invalidOpcode /* D9 */,
1522 &MixedModelVM::invalidOpcode /* DA */,
1523 &MixedModelVM::invalidOpcode /* DB */,
1524 &MixedModelVM::invalidOpcode /* DC */,
1525 &MixedModelVM::invalidOpcode /* DD */,
1526 &MixedModelVM::invalidOpcode /* DE */,
1527 &MixedModelVM::invalidOpcode /* DF */,
1528 &MixedModelVM::invalidOpcode /* E0 */,
1529 &MixedModelVM::invalidOpcode /* E1 */,
1530 &MixedModelVM::invalidOpcode /* E2 */,
1531 &MixedModelVM::invalidOpcode /* E3 */,
1532 &MixedModelVM::invalidOpcode /* E4 */,
1533 &MixedModelVM::invalidOpcode /* E5 */,
1534 &MixedModelVM::invalidOpcode /* E6 */,
1535 &MixedModelVM::invalidOpcode /* E7 */,
1536 &MixedModelVM::invalidOpcode /* E8 */,
1537 &MixedModelVM::invalidOpcode /* E9 */,
1538 &MixedModelVM::invalidOpcode /* EA */,
1539 &MixedModelVM::invalidOpcode /* EB */,
1540 &MixedModelVM::invalidOpcode /* EC */,
1541 &MixedModelVM::invalidOpcode /* ED */,
1542 &MixedModelVM::invalidOpcode /* EE */,
1543 &MixedModelVM::invalidOpcode /* EF */,
1544 &MixedModelVM::invalidOpcode /* F0 */,
1545 &MixedModelVM::invalidOpcode /* F1 */,
1546 &MixedModelVM::invalidOpcode /* F2 */,
1547 &MixedModelVM::invalidOpcode /* F3 */,
1548 &MixedModelVM::invalidOpcode /* F4 */,
1549 &MixedModelVM::invalidOpcode /* F5 */,
1550 &MixedModelVM::invalidOpcode /* F6 */,
1551 &MixedModelVM::invalidOpcode /* F7 */,
1552 &MixedModelVM::invalidOpcode /* F8 */,
1553 &MixedModelVM::invalidOpcode /* F9 */,
1554 &MixedModelVM::invalidOpcode /* FA */,
1555 &MixedModelVM::invalidOpcode /* FB */,
1556 &MixedModelVM::invalidOpcode /* FC */,
1557 &MixedModelVM::invalidOpcode /* FD */,
1558 &MixedModelVM::invalidOpcode /* FE */,
1559 &MixedModelVM::invalidOpcode /* FF */
1562 void
1563 MixedModelVM::execute(uint8_t* aEIP)
1565 mVMFlags = 0;
1566 mEIP = aEIP;
1567 mESP = mStack;
1569 while (!VM_HAD_HALT)
1571 if (mEIP >= mData)
1573 gpe("Instruction pointer outside code range.");
1574 return;
1577 (this->*opCodes[*mEIP++])();
1582 MixedModelBase*
1583 createMixedModelVM(const char* filename)
1585 return new MixedModelVM(filename);