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
31 import static hat.KernelContext.*;
32 import hat.backend.Backend;
33 import hat.buffer.F32Array;
34 import jdk.incubator.code.Reflect;
35 import hat.test.annotation.HatTest;
36 import hat.test.exceptions.HATAsserts;
37
38 import java.lang.invoke.MethodHandles;
39 import java.util.Random;
40
41 public class TestBlackscholes {
42
43 @Reflect
44 public static void blackScholesKernel(
45 F32Array call, F32Array put,
46 F32Array sArray, F32Array xArray, F32Array tArray,
47 float r, float v) {
48 if (GIX() < GSX()) {
49 float S = sArray.array(GIX());
50 float X = xArray.array(GIX());
51 float T = tArray.array(GIX());
52 float expNegRt = (float) Math.exp(-r * T);
53 float d1 = (float) ((Math.log(S / X) + (r + v * v * .5f) * T) / (v * Math.sqrt(T)));
54 float d2 = (float) (d1 - v * Math.sqrt(T));
55 float cnd1 = CND(d1);
56 float cnd2 = CND(d2);
57 float value = S * cnd1 - expNegRt * X * cnd2;
58 call.array(GIX(), value);
59 put.array(GIX(), expNegRt * X * (1 - cnd2) - S * (1 - cnd1));
60 }
61 }
62
63 @Reflect
64 public static float CND(float input) {
65 float x = input;
66 if (input < 0f) {
67 x = -input;
68 }
69 float term = 1f / (1f + (0.2316419f * x));
70 float term_pow2 = term * term;
71 float term_pow3 = term_pow2 * term;
72 float term_pow4 = term_pow2 * term_pow2;
73 float term_pow5 = term_pow2 * term_pow3;
74 float part1 = (1f / (float)Math.sqrt(2f * 3.1415926535f)) * (float)Math.exp((-x * x) * 0.5f);
75 float part2 = (0.31938153f * term) +
76 (-0.356563782f * term_pow2) +
77 (1.781477937f * term_pow3) +
78 (-1.821255978f * term_pow4) +
79 (1.330274429f * term_pow5);
80 if (input >= 0f) {
81 return 1f - part1 * part2;
82 }
83 return part1 * part2;
84 }
85
86 @Reflect
87 public static void blackScholes(ComputeContext cc, F32Array call, F32Array put, F32Array S, F32Array X, F32Array T, float r, float v) {
88 cc.dispatchKernel(NDRange.of1D(call.length()),()-> blackScholesKernel( call, put, S, X, T, r, v));
89 }
90
91 static F32Array floatArray(Accelerator accelerator, int size, float low, float high) {
92 F32Array array = F32Array.create(accelerator, size);
93 Random random = new Random();
94 for (int i = 0; i <size; i++) {
95 array.array(i, random.nextFloat() * (high - low) + low);
96 }
97 return array;
98 }
99
100 public static void blackScholesKernelSeq(F32Array call, F32Array put, F32Array sArray, F32Array xArray, F32Array tArray, float r, float v) {
101 for (int i = 0; i <call.length() ; i++) {
102 float S = sArray.array(i);
103 float X = xArray.array(i);
104 float T = tArray.array(i);
105 float expNegRt = (float) Math.exp(-r * T);
106 float d1 = (float) ((Math.log(S / X) + (r + v * v * .5f) * T) / (v * Math.sqrt(T)));
107 float d2 = (float) (d1 - v * Math.sqrt(T));
108 float cnd1 = CND(d1);
109 float cnd2 = CND(d2);
110 float value = S * cnd1 - expNegRt * X * cnd2;
111 call.array(i, value);
112 put.array(i, expNegRt * X * (1 - cnd2) - S * (1 - cnd1));
113 }
114 }
115
116 @HatTest
117 @Reflect
118 public void testBlackscholes() {
119 int size = 1024;
120 var accelerator = new Accelerator(MethodHandles.lookup(), Backend.FIRST);
121 var call = F32Array.create(accelerator, size);
122 var put = F32Array.create(accelerator, size);
123 for (int i = 0; i < put.length(); i++) {
124 put.array(i, i);
125 call.array(i, i);
126 }
127
128 var S = floatArray(accelerator, size,1f, 100f);
129 var X = floatArray(accelerator, size,1f, 100f);
130 var T = floatArray(accelerator,size, 0.25f, 10f);
131 float r = 0.02f;
132 float v = 0.30f;
133
134 accelerator.compute(cc -> blackScholes(cc, call, put, S, X, T, r, v));
135
136 var seqCall = F32Array.create(accelerator, size);
137 var seqPut = F32Array.create(accelerator, size);
138 for (int i = 0; i < seqCall.length(); i++) {
139 seqCall.array(i, i);
140 seqPut.array(i, i);
141 }
142
143 blackScholesKernelSeq(seqCall, seqPut, S, X, T, r, v);
144
145 for (int i = 0; i < call.length(); i++) {
146 HATAsserts.assertEquals(seqCall.array(i), call.array(i), 0.01f);
147 HATAsserts.assertEquals(seqPut.array(i), put.array(i), 0.01f);
148 }
149 }
150 }