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 }