1 /*
  2  * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 package hat.test;
 26 
 27 import hat.Accelerator;
 28 import hat.ComputeContext;
 29 import hat.NDRange;
 30 import hat.KernelContext;
 31 import hat.backend.Backend;
 32 import hat.buffer.F32Array;
 33 import optkl.ifacemapper.MappableIface;
 34 import optkl.ifacemapper.MappableIface.RO;
 35 import jdk.incubator.code.Reflect;
 36 import hat.test.annotation.HatTest;
 37 import hat.test.exceptions.HATAsserts;
 38 
 39 import java.lang.invoke.MethodHandles;
 40 import java.util.Random;
 41 
 42 import static optkl.ifacemapper.MappableIface.WO;
 43 
 44 public class TestBlackscholes {
 45 
 46     @Reflect
 47     public static void blackScholesKernel(@RO KernelContext kc, @WO F32Array call, @WO F32Array put,
 48                                           @RO F32Array sArray, @RO F32Array xArray, @RO F32Array tArray,
 49                                           float r, float v) {
 50         if (kc.gix < kc.gsx) {
 51             float S = sArray.array(kc.gix);
 52             float X = xArray.array(kc.gix);
 53             float T = tArray.array(kc.gix);
 54             float expNegRt = (float) Math.exp(-r * T);
 55             float d1 = (float) ((Math.log(S / X) + (r + v * v * .5f) * T) / (v * Math.sqrt(T)));
 56             float d2 = (float) (d1 - v * Math.sqrt(T));
 57             float cnd1 = CND(d1);
 58             float cnd2 = CND(d2);
 59             float value = S * cnd1 - expNegRt * X * cnd2;
 60             call.array(kc.gix, value);
 61             put.array(kc.gix, expNegRt * X * (1 - cnd2) - S * (1 - cnd1));
 62         }
 63     }
 64 
 65     @Reflect
 66     public static float CND(float input) {
 67         float x = input;
 68         if (input < 0f) {
 69             x = -input;
 70         }
 71         float term = 1f / (1f + (0.2316419f * x));
 72         float term_pow2 = term * term;
 73         float term_pow3 = term_pow2 * term;
 74         float term_pow4 = term_pow2 * term_pow2;
 75         float term_pow5 = term_pow2 * term_pow3;
 76         float part1 = (1f / (float)Math.sqrt(2f * 3.1415926535f)) * (float)Math.exp((-x * x) * 0.5f);
 77         float part2 = (0.31938153f * term) +
 78                 (-0.356563782f * term_pow2) +
 79                 (1.781477937f * term_pow3) +
 80                 (-1.821255978f * term_pow4) +
 81                 (1.330274429f * term_pow5);
 82         if (input >= 0f) {
 83             return 1f - part1 * part2;
 84         }
 85         return part1 * part2;
 86     }
 87 
 88     @Reflect
 89     public static void blackScholes(@MappableIface.RO ComputeContext cc, @WO F32Array call, @WO F32Array put, @MappableIface.RO F32Array S, @MappableIface.RO F32Array X, @MappableIface.RO F32Array T, float r, float v) {
 90         cc.dispatchKernel(NDRange.of1D(call.length()),
 91                 kc -> blackScholesKernel(kc, call, put, S, X, T, r, v)
 92         );
 93     }
 94 
 95     static F32Array floatArray(Accelerator accelerator, int size, float low, float high) {
 96         F32Array array = F32Array.create(accelerator, size);
 97         Random random = new Random();
 98         for (int i = 0; i <size; i++) {
 99             array.array(i, random.nextFloat() * (high - low) + low);
100         }
101         return array;
102     }
103 
104     public static void blackScholesKernelSeq(F32Array call, F32Array put, F32Array sArray, F32Array xArray, F32Array tArray, float r, float v) {
105         for (int i = 0; i <call.length() ; i++) {
106             float S = sArray.array(i);
107             float X = xArray.array(i);
108             float T = tArray.array(i);
109             float expNegRt = (float) Math.exp(-r * T);
110             float d1 = (float) ((Math.log(S / X) + (r + v * v * .5f) * T) / (v * Math.sqrt(T)));
111             float d2 = (float) (d1 - v * Math.sqrt(T));
112             float cnd1 = CND(d1);
113             float cnd2 = CND(d2);
114             float value = S * cnd1 - expNegRt * X * cnd2;
115             call.array(i, value);
116             put.array(i, expNegRt * X * (1 - cnd2) - S * (1 - cnd1));
117         }
118     }
119 
120     @HatTest
121     @Reflect
122     public void testBlackscholes() {
123         int size = 1024;
124         var accelerator = new Accelerator(MethodHandles.lookup(), Backend.FIRST);
125         var call = F32Array.create(accelerator, size);
126         var put = F32Array.create(accelerator, size);
127         for (int i = 0; i < put.length(); i++) {
128             put.array(i, i);
129             call.array(i, i);
130         }
131 
132         var S = floatArray(accelerator, size,1f, 100f);
133         var X = floatArray(accelerator, size,1f, 100f);
134         var T = floatArray(accelerator,size, 0.25f, 10f);
135         float r = 0.02f;
136         float v = 0.30f;
137 
138         accelerator.compute(cc -> blackScholes(cc, call, put, S, X, T, r, v));
139 
140         var seqCall = F32Array.create(accelerator, size);
141         var seqPut = F32Array.create(accelerator, size);
142         for (int i = 0; i < seqCall.length(); i++) {
143             seqCall.array(i, i);
144             seqPut.array(i, i);
145         }
146 
147         blackScholesKernelSeq(seqCall, seqPut, S, X, T, r, v);
148 
149         for (int i = 0; i < call.length(); i++) {
150             HATAsserts.assertEquals(seqCall.array(i), call.array(i), 0.01f);
151             HATAsserts.assertEquals(seqPut.array(i), put.array(i), 0.01f);
152         }
153     }
154 }