chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.eclipse.deeplearning4j</groupId>
|
||||
<artifactId>blas-lapack-generator</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
|
||||
<parent>
|
||||
<groupId>org.eclipse.deeplearning4j</groupId>
|
||||
<artifactId>codegen</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>blas-lapack-generator</name>
|
||||
|
||||
<properties>
|
||||
<javaparser.version>3.24.4</javaparser.version>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<javapoet.version>1.13.0</javapoet.version>
|
||||
<openblas.version>0.3.28</openblas.version>
|
||||
<javacpp.version>1.5.11</javacpp.version>
|
||||
<openblas.javacpp.version>${openblas.version}-${javacpp.version}</openblas.javacpp.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.deeplearning4j</groupId>
|
||||
<artifactId>nd4j-cpu-backend-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.deeplearning4j</groupId>
|
||||
<artifactId>nd4j-native</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.reflections</groupId>
|
||||
<artifactId>reflections</artifactId>
|
||||
<version>0.10.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>openblas</artifactId>
|
||||
<version>${openblas.javacpp.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup</groupId>
|
||||
<artifactId>javapoet</artifactId>
|
||||
<version>${javapoet.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.javaparser</groupId>
|
||||
<artifactId>javaparser-core-serialization</artifactId>
|
||||
<version>${javaparser.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.javaparser</groupId>
|
||||
<artifactId>javaparser-symbol-solver-core</artifactId>
|
||||
<version>${javaparser.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
package org.deeplearning4j;
|
||||
|
||||
import com.github.javaparser.ParserConfiguration;
|
||||
import com.github.javaparser.StaticJavaParser;
|
||||
import com.github.javaparser.symbolsolver.JavaSymbolSolver;
|
||||
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
|
||||
import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver;
|
||||
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
|
||||
import com.github.javaparser.utils.SourceRoot;
|
||||
import com.squareup.javapoet.*;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.bytedeco.javacpp.Pointer;
|
||||
import org.bytedeco.openblas.global.openblas;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class BlasLapackGenerator {
|
||||
|
||||
private SourceRoot sourceRoot;
|
||||
private File rootDir;
|
||||
private File targetFile;
|
||||
|
||||
private static String copyright =
|
||||
"/*\n" +
|
||||
" * ******************************************************************************\n" +
|
||||
" * *\n" +
|
||||
" * *\n" +
|
||||
" * * This program and the accompanying materials are made available under the\n" +
|
||||
" * * terms of the Apache License, Version 2.0 which is available at\n" +
|
||||
" * * https://www.apache.org/licenses/LICENSE-2.0.\n" +
|
||||
" * *\n" +
|
||||
" * * See the NOTICE file distributed with this work for additional\n" +
|
||||
" * * information regarding copyright ownership.\n" +
|
||||
" * * Unless required by applicable law or agreed to in writing, software\n" +
|
||||
" * * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n" +
|
||||
" * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n" +
|
||||
" * * License for the specific language governing permissions and limitations\n" +
|
||||
" * * under the License.\n" +
|
||||
" * *\n" +
|
||||
" * * SPDX-License-Identifier: Apache-2.0\n" +
|
||||
" * *****************************************************************************\n" +
|
||||
" */\n";
|
||||
private static String codeGenWarning =
|
||||
"\n//================== GENERATED CODE - DO NOT MODIFY THIS FILE ==================\n\n";
|
||||
|
||||
|
||||
private static Set<String> methodsToSkip = new HashSet<>(Arrays.asList(
|
||||
"LAPACK_zlangb_base",
|
||||
"LAPACK_lsame_base",
|
||||
"LAPACK_clangb_base",
|
||||
"LAPACK_lsame_base",
|
||||
"LAPACK_dlangb_base",
|
||||
"LAPACK_slangb_base"
|
||||
|
||||
));
|
||||
|
||||
|
||||
public BlasLapackGenerator(File nd4jApiRootDir) {
|
||||
this.sourceRoot = initSourceRoot(nd4jApiRootDir);
|
||||
this.rootDir = nd4jApiRootDir;
|
||||
}
|
||||
|
||||
public SourceRoot getSourceRoot() {
|
||||
return sourceRoot;
|
||||
}
|
||||
|
||||
public void setSourceRoot(SourceRoot sourceRoot) {
|
||||
this.sourceRoot = sourceRoot;
|
||||
}
|
||||
|
||||
public File getTargetFile() {
|
||||
return targetFile;
|
||||
}
|
||||
|
||||
public void setTargetFile(File targetFile) {
|
||||
this.targetFile = targetFile;
|
||||
}
|
||||
|
||||
public void parse() throws Exception {
|
||||
targetFile = new File(rootDir,"org/nd4j/linalg/api/blas/BLASLapackDelegator.java");
|
||||
String packageName = "org.nd4j.linalg.api.blas";
|
||||
TypeSpec.Builder openblasLapackDelegator = TypeSpec.interfaceBuilder("BLASLapackDelegator");
|
||||
openblasLapackDelegator.addModifiers(Modifier.PUBLIC);
|
||||
Class<openblas> clazz = openblas.class;
|
||||
List<Method> objectMethods = Arrays.asList(Object.class.getMethods());
|
||||
Arrays.stream(clazz.getMethods())
|
||||
.filter(input -> !objectMethods.contains(input))
|
||||
.filter(input -> !input.getName().equals("map") && !input.getName().equals("init"))
|
||||
.filter(input -> !methodsToSkip.contains(input.getName()))
|
||||
.forEach(method -> {
|
||||
MethodSpec.Builder builder = MethodSpec.methodBuilder(
|
||||
method.getName()
|
||||
).returns(method.getReturnType())
|
||||
.addModifiers(Modifier.DEFAULT,Modifier.PUBLIC);
|
||||
Arrays.stream(method.getParameters()).forEach(param -> {
|
||||
builder.addParameter(ParameterSpec.builder(
|
||||
!lapackType(param.getType()) ?
|
||||
TypeName.get(param.getType()) :
|
||||
TypeName.get(Pointer.class),
|
||||
param.getName()
|
||||
).build());
|
||||
});
|
||||
|
||||
openblasLapackDelegator.addMethod(builder.build());
|
||||
});
|
||||
|
||||
JavaFile finalFile = JavaFile.builder(packageName, openblasLapackDelegator.build())
|
||||
.addFileComment(copyright)
|
||||
.build();
|
||||
finalFile
|
||||
.writeTo(rootDir);
|
||||
}
|
||||
|
||||
private boolean lapackType(Class<?> clazz) {
|
||||
return clazz.equals(openblas.LAPACK_C_SELECT1.class) ||
|
||||
clazz.equals(openblas.LAPACK_C_SELECT2.class) ||
|
||||
clazz.equals(openblas.LAPACK_D_SELECT2.class) ||
|
||||
clazz.equals(openblas.LAPACK_S_SELECT2.class) ||
|
||||
clazz.equals(openblas.LAPACK_Z_SELECT1.class)
|
||||
|| clazz.equals(openblas.LAPACK_Z_SELECT2.class) ||
|
||||
clazz.equals(openblas.LAPACK_D_SELECT3.class) ||
|
||||
clazz.equals(openblas.LAPACK_S_SELECT3.class);
|
||||
}
|
||||
|
||||
|
||||
private SourceRoot initSourceRoot(File nd4jApiRootDir) {
|
||||
CombinedTypeSolver typeSolver = new CombinedTypeSolver();
|
||||
typeSolver.add(new ReflectionTypeSolver(false));
|
||||
typeSolver.add(new JavaParserTypeSolver(nd4jApiRootDir));
|
||||
JavaSymbolSolver symbolSolver = new JavaSymbolSolver(typeSolver);
|
||||
StaticJavaParser.getConfiguration().setSymbolResolver(symbolSolver);
|
||||
SourceRoot sourceRoot = new SourceRoot(nd4jApiRootDir.toPath(),new ParserConfiguration().setSymbolResolver(symbolSolver));
|
||||
return sourceRoot;
|
||||
}
|
||||
|
||||
|
||||
public static void main(String...args) throws Exception {
|
||||
BlasLapackGenerator blasLapackGenerator = new BlasLapackGenerator(new File("../../nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/src/main/java/"));
|
||||
blasLapackGenerator.parse();
|
||||
String generated = FileUtils.readFileToString(blasLapackGenerator.getTargetFile(), Charset.defaultCharset());
|
||||
generated = generated.replaceAll("\\{\\s+\\}",";");
|
||||
generated = generated.replace("default","");
|
||||
FileUtils.write(blasLapackGenerator.getTargetFile(),generated,Charset.defaultCharset());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
package org.deeplearning4j;
|
||||
|
||||
import com.github.javaparser.ParserConfiguration;
|
||||
import com.github.javaparser.StaticJavaParser;
|
||||
import com.github.javaparser.symbolsolver.JavaSymbolSolver;
|
||||
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
|
||||
import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver;
|
||||
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
|
||||
import com.github.javaparser.utils.SourceRoot;
|
||||
import com.squareup.javapoet.*;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.bytedeco.javacpp.Pointer;
|
||||
import org.bytedeco.openblas.global.openblas;
|
||||
import org.bytedeco.openblas.global.openblas_nolapack;
|
||||
import org.nd4j.linalg.api.blas.BLASLapackDelegator;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
|
||||
public class NoOpBlasLapackGenerator {
|
||||
|
||||
private SourceRoot sourceRoot;
|
||||
private File rootDir;
|
||||
private File targetFile;
|
||||
|
||||
|
||||
private static String copyright =
|
||||
"/*\n" +
|
||||
" * ******************************************************************************\n" +
|
||||
" * *\n" +
|
||||
" * *\n" +
|
||||
" * * This program and the accompanying materials are made available under the\n" +
|
||||
" * * terms of the Apache License, Version 2.0 which is available at\n" +
|
||||
" * * https://www.apache.org/licenses/LICENSE-2.0.\n" +
|
||||
" * *\n" +
|
||||
" * * See the NOTICE file distributed with this work for additional\n" +
|
||||
" * * information regarding copyright ownership.\n" +
|
||||
" * * Unless required by applicable law or agreed to in writing, software\n" +
|
||||
" * * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n" +
|
||||
" * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n" +
|
||||
" * * License for the specific language governing permissions and limitations\n" +
|
||||
" * * under the License.\n" +
|
||||
" * *\n" +
|
||||
" * * SPDX-License-Identifier: Apache-2.0\n" +
|
||||
" * *****************************************************************************\n" +
|
||||
" */\n";
|
||||
private static String codeGenWarning =
|
||||
"\n//================== GENERATED CODE - DO NOT MODIFY THIS FILE ==================\n\n";
|
||||
|
||||
|
||||
public NoOpBlasLapackGenerator(File nd4jApiRootDir) {
|
||||
this.sourceRoot = initSourceRoot(nd4jApiRootDir);
|
||||
this.rootDir = nd4jApiRootDir;
|
||||
}
|
||||
|
||||
|
||||
public void parse() throws Exception {
|
||||
targetFile = new File(rootDir,"org/nd4j/linalg/cpu/nativecpu/blas/NoOpBLASDelegator.java");
|
||||
String packageName = "org.nd4j.linalg.cpu.nativecpu.blas";
|
||||
TypeSpec.Builder openblasLapackDelegator = TypeSpec.classBuilder("NoOpBLASDelegator");
|
||||
openblasLapackDelegator.addModifiers(Modifier.PUBLIC);
|
||||
openblasLapackDelegator.addSuperinterface(BLASLapackDelegator.class);
|
||||
|
||||
Class<BLASLapackDelegator> clazz = BLASLapackDelegator.class;
|
||||
List<Method> objectMethods = Arrays.asList(Object.class.getMethods());
|
||||
Set<MethodSpec> addedCodeLines = new HashSet<>();
|
||||
Arrays.stream(clazz.getMethods())
|
||||
.filter(input -> !objectMethods.contains(input))
|
||||
.forEach(method -> {
|
||||
MethodSpec.Builder builder = MethodSpec.methodBuilder(
|
||||
method.getName()
|
||||
).addModifiers(Modifier.PUBLIC)
|
||||
.returns(method.getReturnType())
|
||||
.addAnnotation(Override.class);
|
||||
StringBuilder codeStatement = new StringBuilder();
|
||||
//don't return anything when void
|
||||
if(method.getReturnType().equals(Void.TYPE)) {
|
||||
|
||||
} else if(method.getReturnType().equals(int.class)){
|
||||
codeStatement.append("return 0;");
|
||||
|
||||
} else if(method.getReturnType().equals(double.class)) {
|
||||
codeStatement.append("return 0.0;");
|
||||
|
||||
} else if(method.getReturnType().equals(float.class)) {
|
||||
codeStatement.append("return 0.0f;");
|
||||
|
||||
}
|
||||
else if(method.getReturnType().equals(long.class)) {
|
||||
codeStatement.append("return 0L;");
|
||||
}
|
||||
|
||||
Arrays.stream(method.getParameters()).forEach(param -> {
|
||||
builder.addParameter(ParameterSpec.builder(param.getType(),param.getName())
|
||||
.build());
|
||||
|
||||
});
|
||||
|
||||
|
||||
builder.addCode(CodeBlock
|
||||
.builder()
|
||||
.addStatement(codeStatement.toString().replace(",)",")"))
|
||||
.build());
|
||||
|
||||
MethodSpec build = builder.build();
|
||||
openblasLapackDelegator.addMethod(build);
|
||||
addedCodeLines.add(build);
|
||||
|
||||
|
||||
});
|
||||
|
||||
JavaFile.builder(packageName,openblasLapackDelegator.build())
|
||||
.addFileComment(copyright)
|
||||
.build()
|
||||
.writeTo(rootDir);
|
||||
}
|
||||
|
||||
|
||||
private SourceRoot initSourceRoot(File nd4jApiRootDir) {
|
||||
CombinedTypeSolver typeSolver = new CombinedTypeSolver();
|
||||
typeSolver.add(new ReflectionTypeSolver(false));
|
||||
typeSolver.add(new JavaParserTypeSolver(nd4jApiRootDir));
|
||||
JavaSymbolSolver symbolSolver = new JavaSymbolSolver(typeSolver);
|
||||
StaticJavaParser.getConfiguration().setSymbolResolver(symbolSolver);
|
||||
SourceRoot sourceRoot = new SourceRoot(nd4jApiRootDir.toPath(),new ParserConfiguration().setSymbolResolver(symbolSolver));
|
||||
return sourceRoot;
|
||||
}
|
||||
|
||||
public SourceRoot getSourceRoot() {
|
||||
return sourceRoot;
|
||||
}
|
||||
|
||||
public File getRootDir() {
|
||||
return rootDir;
|
||||
}
|
||||
|
||||
public File getTargetFile() {
|
||||
return targetFile;
|
||||
}
|
||||
|
||||
public static void main(String...args) throws Exception {
|
||||
NoOpBlasLapackGenerator openblasBlasLapackGenerator = new NoOpBlasLapackGenerator(new File("nd4j/nd4j-backends/nd4j-backend-impls/nd4j-cpu-backend-common/src/main/java"));
|
||||
openblasBlasLapackGenerator.parse();
|
||||
String generated = FileUtils.readFileToString(openblasBlasLapackGenerator.getTargetFile(), Charset.defaultCharset());
|
||||
generated = generated.replace(";;",";");
|
||||
FileUtils.write(openblasBlasLapackGenerator.getTargetFile(),generated);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
package org.deeplearning4j;
|
||||
|
||||
import com.github.javaparser.ParserConfiguration;
|
||||
import com.github.javaparser.StaticJavaParser;
|
||||
import com.github.javaparser.symbolsolver.JavaSymbolSolver;
|
||||
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
|
||||
import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver;
|
||||
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
|
||||
import com.github.javaparser.utils.SourceRoot;
|
||||
import com.squareup.javapoet.*;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.bytedeco.javacpp.Pointer;
|
||||
import org.bytedeco.openblas.global.openblas;
|
||||
import org.bytedeco.openblas.global.openblas_nolapack;
|
||||
import org.nd4j.linalg.api.blas.BLASLapackDelegator;
|
||||
import org.nd4j.shade.guava.collect.HashBasedTable;
|
||||
import org.nd4j.shade.guava.collect.Table;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
|
||||
public class OpenblasBlasLapackGenerator {
|
||||
|
||||
private SourceRoot sourceRoot;
|
||||
private File rootDir;
|
||||
private File targetFile;
|
||||
|
||||
private Map<String,String> casting = new HashMap<>() {{
|
||||
put("LAPACKE_sgees", "openblas.LAPACK_S_SELECT2");
|
||||
put("LAPACKE_dgees", "openblas.LAPACK_D_SELECT2");
|
||||
put("LAPACKE_cgees", "openblas.LAPACK_C_SELECT1");
|
||||
put("LAPACKE_zgees", "openblas.LAPACK_Z_SELECT1");
|
||||
put("LAPACKE_sgeesx", "openblas.LAPACK_S_SELECT2");
|
||||
put("LAPACKE_dgeesx", "openblas.LAPACK_D_SELECT2");
|
||||
put("LAPACKE_cgeesx", "openblas.LAPACK_C_SELECT1");
|
||||
put("LAPACKE_zgeesx", "openblas.LAPACK_Z_SELECT1");
|
||||
put("LAPACKE_sgges", "openblas.LAPACK_S_SELECT3");
|
||||
put("LAPACKE_dgges", "openblas.LAPACK_D_SELECT3");
|
||||
put("LAPACKE_cgges", "openblas.LAPACK_C_SELECT2");
|
||||
put("LAPACKE_zgges", "openblas.LAPACK_Z_SELECT2");
|
||||
put("LAPACKE_sgges3", "openblas.LAPACK_S_SELECT3");
|
||||
put("LAPACKE_dgges3", "openblas.LAPACK_D_SELECT3");
|
||||
put("LAPACKE_cgges3", "openblas.LAPACK_C_SELECT2");
|
||||
put("LAPACKE_zgges3", "openblas.LAPACK_Z_SELECT2");
|
||||
put("LAPACKE_sggesx", "openblas.LAPACK_S_SELECT3");
|
||||
put("LAPACKE_dggesx", "openblas.LAPACK_D_SELECT3");
|
||||
put("LAPACKE_cggesx", "openblas.LAPACK_C_SELECT2");
|
||||
put("LAPACKE_zggesx", "openblas.LAPACK_Z_SELECT2");
|
||||
put("LAPACKE_sgees_work", "openblas.LAPACK_S_SELECT2");
|
||||
put("LAPACKE_dgees_work", "openblas.LAPACK_D_SELECT2");
|
||||
put("LAPACKE_cgees_work", "openblas.LAPACK_C_SELECT1");
|
||||
put("LAPACKE_zgees_work", "openblas.LAPACK_Z_SELECT1");
|
||||
put("LAPACKE_sgeesx_work", "openblas.LAPACK_S_SELECT2");
|
||||
put("LAPACKE_dgeesx_work", "openblas.LAPACK_D_SELECT2");
|
||||
put("LAPACKE_cgeesx_work", "openblas.LAPACK_C_SELECT1");
|
||||
put("LAPACKE_zgeesx_work", "openblas.LAPACK_Z_SELECT1");
|
||||
put("LAPACKE_sgges_work", "openblas.LAPACK_S_SELECT3");
|
||||
put("LAPACKE_dgges_work", "openblas.LAPACK_D_SELECT3");
|
||||
put("LAPACKE_cgges_work", "openblas.LAPACK_C_SELECT2");
|
||||
put("LAPACKE_zgges_work", "openblas.LAPACK_Z_SELECT2");
|
||||
put("LAPACKE_sgges3_work", "openblas.LAPACK_S_SELECT3");
|
||||
put("LAPACKE_dgges3_work", "openblas.LAPACK_D_SELECT3");
|
||||
put("LAPACKE_cgges3_work", "openblas.LAPACK_C_SELECT2");
|
||||
put("LAPACKE_zgges3_work", "openblas.LAPACK_Z_SELECT2");
|
||||
put("LAPACKE_sggesx_work", "openblas.LAPACK_S_SELECT3");
|
||||
put("LAPACKE_dggesx_work", "openblas.LAPACK_D_SELECT3");
|
||||
put("LAPACKE_cggesx_work", "openblas.LAPACK_C_SELECT2");
|
||||
put("LAPACKE_zggesx_work", "openblas.LAPACK_Z_SELECT2");
|
||||
|
||||
put("LAPACK_sgges3_base", "openblas.LAPACK_S_SELECT3");
|
||||
put("LAPACK_dgges3_base", "openblas.LAPACK_D_SELECT3");
|
||||
put("LAPACK_cgges3_base", "openblas.LAPACK_C_SELECT2");
|
||||
put("LAPACK_zgges3_base", "openblas.LAPACK_Z_SELECT2");
|
||||
|
||||
|
||||
put("LAPACK_sgges_base", "openblas.LAPACK_S_SELECT3");
|
||||
put("LAPACK_dgges_base", "openblas.LAPACK_D_SELECT3");
|
||||
put("LAPACK_cgges_base", "openblas.LAPACK_C_SELECT2");
|
||||
put("LAPACK_zgges_base", "openblas.LAPACK_Z_SELECT2");
|
||||
|
||||
|
||||
put("LAPACK_sggesx_base", "openblas.LAPACK_S_SELECT3");
|
||||
put("LAPACK_dggesx_base", "openblas.LAPACK_D_SELECT3");
|
||||
put("LAPACK_cggesx_base", "openblas.LAPACK_C_SELECT2");
|
||||
put("LAPACK_zggesx_base", "openblas.LAPACK_Z_SELECT2");
|
||||
|
||||
//LAPACK_zgeesx
|
||||
put("LAPACK_cgees_base", "openblas.LAPACK_C_SELECT1");
|
||||
put("LAPACK_dgees_base", "openblas.LAPACK_D_SELECT2");
|
||||
put("LAPACK_zgees_base", "openblas.LAPACK_Z_SELECT1");
|
||||
put("LAPACK_sgees_base", "openblas.LAPACK_S_SELECT2");
|
||||
|
||||
|
||||
put("LAPACK_cgeesx_base", "openblas.LAPACK_C_SELECT1");
|
||||
put("LAPACK_dgeesx_base", "openblas.LAPACK_D_SELECT2");
|
||||
put("LAPACK_zgeesx_base", "openblas.LAPACK_Z_SELECT1");
|
||||
put("LAPACK_sgeesx_base", "openblas.LAPACK_S_SELECT2");
|
||||
|
||||
}};
|
||||
private static String copyright =
|
||||
"/*\n" +
|
||||
" * ******************************************************************************\n" +
|
||||
" * *\n" +
|
||||
" * *\n" +
|
||||
" * * This program and the accompanying materials are made available under the\n" +
|
||||
" * * terms of the Apache License, Version 2.0 which is available at\n" +
|
||||
" * * https://www.apache.org/licenses/LICENSE-2.0.\n" +
|
||||
" * *\n" +
|
||||
" * * See the NOTICE file distributed with this work for additional\n" +
|
||||
" * * information regarding copyright ownership.\n" +
|
||||
" * * Unless required by applicable law or agreed to in writing, software\n" +
|
||||
" * * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n" +
|
||||
" * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n" +
|
||||
" * * License for the specific language governing permissions and limitations\n" +
|
||||
" * * under the License.\n" +
|
||||
" * *\n" +
|
||||
" * * SPDX-License-Identifier: Apache-2.0\n" +
|
||||
" * *****************************************************************************\n" +
|
||||
" */\n";
|
||||
private static String codeGenWarning =
|
||||
"\n//================== GENERATED CODE - DO NOT MODIFY THIS FILE ==================\n\n";
|
||||
|
||||
|
||||
public OpenblasBlasLapackGenerator(File nd4jApiRootDir) {
|
||||
this.sourceRoot = initSourceRoot(nd4jApiRootDir);
|
||||
this.rootDir = nd4jApiRootDir;
|
||||
}
|
||||
|
||||
public void parse() throws Exception {
|
||||
targetFile = new File(rootDir,"org/nd4j/linalg/cpu/nativecpu/OpenblasLapackDelegator.java");
|
||||
String packageName = "org.nd4j.linalg.cpu.nativecpu";
|
||||
TypeSpec.Builder openblasLapackDelegator = TypeSpec.classBuilder("OpenblasLapackDelegator");
|
||||
openblasLapackDelegator.addModifiers(Modifier.PUBLIC);
|
||||
openblasLapackDelegator.addSuperinterface(BLASLapackDelegator.class);
|
||||
|
||||
Class<BLASLapackDelegator> clazz = BLASLapackDelegator.class;
|
||||
List<Method> objectMethods = Arrays.asList(Object.class.getMethods());
|
||||
Set<MethodSpec> addedCodeLines = new HashSet<>();
|
||||
Arrays.stream(clazz.getMethods())
|
||||
.filter(input -> !objectMethods.contains(input))
|
||||
.forEach(method -> {
|
||||
MethodSpec.Builder builder = MethodSpec.methodBuilder(
|
||||
method.getName()
|
||||
).addModifiers(Modifier.PUBLIC)
|
||||
.returns(method.getReturnType())
|
||||
.addAnnotation(Override.class);
|
||||
StringBuilder codeStatement = new StringBuilder();
|
||||
//don't return anything when void
|
||||
if(method.getReturnType().equals(Void.TYPE)) {
|
||||
codeStatement.append("openblas." + method.getName() + "(");
|
||||
|
||||
} else if(method.getReturnType().equals(int.class)) {
|
||||
codeStatement.append("return openblas." + method.getName() + "(");
|
||||
|
||||
} else if(method.getReturnType().equals(double.class)) {
|
||||
//codeStatement.append("return 0.0;");
|
||||
codeStatement.append("return openblas." + method.getName() + "(");
|
||||
|
||||
} else if(method.getReturnType().equals(float.class)) {
|
||||
//codeStatement.append("return 0.0f;");
|
||||
codeStatement.append("return openblas." + method.getName() + "(");
|
||||
|
||||
}
|
||||
else if(method.getReturnType().equals(long.class)) {
|
||||
//codeStatement.append("return 0L;");
|
||||
codeStatement.append("return openblas." + method.getName() + "(");
|
||||
|
||||
}
|
||||
|
||||
//TODO: LAPACK_cgees
|
||||
//TODO: LAPACK_dgees
|
||||
//TODO: LAPACK_zgees
|
||||
//TODO: LAPACK_cgeesx
|
||||
//TODO: LAPACK_dgeesx
|
||||
//TODO: LAPACK_sgeesx
|
||||
//TODO: LAPACK_zgeesx
|
||||
//TODO: LAPACK_cgges
|
||||
//TODO: LAPACK_dgges
|
||||
//TODO: LAPACK_sgges
|
||||
//TODO: LAPACK_zgges
|
||||
//TODO: LAPACK_cgges3
|
||||
//TODO: LAPACK_dgges3
|
||||
//TODO: LAPACK_sgges3
|
||||
//TODO: LAPACK_zgges3
|
||||
//TODO: LAPACK_cggesx
|
||||
//TODO: LAPACK_dggesx
|
||||
//TODO: LAPACK_sggesx
|
||||
//TODO: LAPACK_zggesx
|
||||
|
||||
|
||||
//TODO: issue could be LAPACK_Z_SELECT_2
|
||||
//TODO: LAPACK_S_SELECT_3
|
||||
Arrays.stream(method.getParameters()).forEach(param -> {
|
||||
if(casting.containsKey(method.getName()) && param.getType().equals(Pointer.class)) {
|
||||
System.out.println("In function casting for " + method.getName());
|
||||
codeStatement.append("((" + casting.get(method.getName()) + ")" + param.getName() + ")");
|
||||
codeStatement.append(",");
|
||||
} else {
|
||||
codeStatement.append(param.getName());
|
||||
codeStatement.append(",");
|
||||
}
|
||||
|
||||
builder.addParameter(ParameterSpec.builder(param.getType(),param.getName())
|
||||
.build());
|
||||
|
||||
});
|
||||
|
||||
codeStatement.append(")");
|
||||
|
||||
builder.addCode(CodeBlock
|
||||
.builder()
|
||||
.addStatement(codeStatement.toString().replace(",)",")"))
|
||||
.build());
|
||||
|
||||
MethodSpec build = builder.build();
|
||||
openblasLapackDelegator.addMethod(build);
|
||||
addedCodeLines.add(build);
|
||||
|
||||
|
||||
});
|
||||
|
||||
JavaFile.builder(packageName,openblasLapackDelegator.build())
|
||||
.addFileComment(copyright)
|
||||
.addStaticImport(openblas.class,"*")
|
||||
.addStaticImport(openblas_nolapack.class,"*")
|
||||
.build()
|
||||
.writeTo(rootDir);
|
||||
}
|
||||
|
||||
|
||||
private SourceRoot initSourceRoot(File nd4jApiRootDir) {
|
||||
CombinedTypeSolver typeSolver = new CombinedTypeSolver();
|
||||
typeSolver.add(new ReflectionTypeSolver(false));
|
||||
typeSolver.add(new JavaParserTypeSolver(nd4jApiRootDir));
|
||||
JavaSymbolSolver symbolSolver = new JavaSymbolSolver(typeSolver);
|
||||
StaticJavaParser.getConfiguration().setSymbolResolver(symbolSolver);
|
||||
SourceRoot sourceRoot = new SourceRoot(nd4jApiRootDir.toPath(),new ParserConfiguration().setSymbolResolver(symbolSolver));
|
||||
return sourceRoot;
|
||||
}
|
||||
|
||||
public SourceRoot getSourceRoot() {
|
||||
return sourceRoot;
|
||||
}
|
||||
|
||||
public File getRootDir() {
|
||||
return rootDir;
|
||||
}
|
||||
|
||||
public File getTargetFile() {
|
||||
return targetFile;
|
||||
}
|
||||
|
||||
public static void main(String...args) throws Exception {
|
||||
OpenblasBlasLapackGenerator openblasBlasLapackGenerator = new OpenblasBlasLapackGenerator(new File("nd4j/nd4j-backends/nd4j-backend-impls/nd4j-native/src/main/java"));
|
||||
openblasBlasLapackGenerator.parse();
|
||||
String generated = FileUtils.readFileToString(openblasBlasLapackGenerator.getTargetFile(), Charset.defaultCharset());
|
||||
generated = generated.replace(";;",";");
|
||||
generated = generated.replaceAll("import static org.bytedeco.openblas.global.openblas\\.\\*","import org.bytedeco.openblas.global.openblas");
|
||||
generated = generated.replaceAll("import static org.bytedeco.openblas.global.openblas_nolapack\\.\\*","import org.bytedeco.openblas.global.openblas_nolapack");
|
||||
FileUtils.write(openblasBlasLapackGenerator.getTargetFile(),generated,Charset.defaultCharset());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
# Libnd4j Op Descriptor Generator
|
||||
|
||||
This module contains a few files for generating op descriptors for libnd4j.
|
||||
An op descriptor contains its number of inputs and outputs as well as the names
|
||||
in the code of those attributes when they are assigned.
|
||||
|
||||
The main class parses a root directory specified by the user containing the libnd4j code base.
|
||||
In the libnd4j code base, it will scan for op signatures and certain macros found in https://github.com/eclipse/deeplearning4j/blob/master/libnd4j/include/system/op_boilerplate.h
|
||||
|
||||
From that, it will automatically generate a set of op descriptors including counts of types of ops as well as names.
|
||||
The up to date OpDescriptor class can be found [here](src/main/java/org/nd4j/descriptor/OpDescriptor.java)
|
||||
The main class can be found [here](src/main/java/org/nd4j/descriptor/ParseGen.java)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
mvn clean package -DskipTests
|
||||
java -cp target/libnd4j-gen-1.0.0-SNAPSHOT-shaded.jar org.nd4j.descriptor.ParseOpFile "$@"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,214 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
~ /* ******************************************************************************
|
||||
~ *
|
||||
~ *
|
||||
~ * This program and the accompanying materials are made available under the
|
||||
~ * terms of the Apache License, Version 2.0 which is available at
|
||||
~ * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
~ *
|
||||
~ * See the NOTICE file distributed with this work for additional
|
||||
~ * information regarding copyright ownership.
|
||||
~ * Unless required by applicable law or agreed to in writing, software
|
||||
~ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
~ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
~ * License for the specific language governing permissions and limitations
|
||||
~ * under the License.
|
||||
~ *
|
||||
~ * SPDX-License-Identifier: Apache-2.0
|
||||
~ ******************************************************************************/
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.eclipse.deeplearning4j</groupId>
|
||||
<artifactId>libnd4j-gen</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<name>libnd4j-gen</name>
|
||||
|
||||
|
||||
<parent>
|
||||
<groupId>org.eclipse.deeplearning4j</groupId>
|
||||
<artifactId>codegen</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<nd4j.version>1.0.0-SNAPSHOT</nd4j.version>
|
||||
<maven-shade-plugin.version>3.5.1</maven-shade-plugin.version>
|
||||
<javaparser.version>3.24.4</javaparser.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.codepoetics</groupId>
|
||||
<artifactId>protonpack</artifactId>
|
||||
<version>1.16</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.javaparser</groupId>
|
||||
<artifactId>javaparser-core-serialization</artifactId>
|
||||
<version>${javaparser.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.javaparser</groupId>
|
||||
<artifactId>javaparser-symbol-solver-core</artifactId>
|
||||
<version>${javaparser.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-text</artifactId>
|
||||
<version>1.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-collections4</artifactId>
|
||||
<version>4.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.reflections</groupId>
|
||||
<artifactId>reflections</artifactId>
|
||||
<version>0.9.10</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.deeplearning4j</groupId>
|
||||
<artifactId>protobuf</artifactId>
|
||||
<version>${nd4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.20</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.deeplearning4j</groupId>
|
||||
<artifactId>nd4j-native</artifactId>
|
||||
<version>${nd4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.deeplearning4j</groupId>
|
||||
<artifactId>nd4j-api</artifactId>
|
||||
<version>${nd4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-source</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals><goal>add-source</goal></goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>src/main/stubs</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>get-cpu-count</id>
|
||||
<goals>
|
||||
<goal>cpu-count</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<cpuCount>cpu.core.count</cpuCount>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>${maven-shade-plugin.version}</version>
|
||||
<configuration>
|
||||
<shadedArtifactAttached>true</shadedArtifactAttached>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>org/datanucleus/**</exclude>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
|
||||
</configuration>
|
||||
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>reference.conf</resource>
|
||||
</transformer>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer" />
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
|
||||
|
||||
<!-- https://kotlinlang.org/docs/reference/using-maven.html -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<executions>
|
||||
<!-- Replacing default-compile as it is treated specially by maven -->
|
||||
<execution>
|
||||
<id>default-compile</id>
|
||||
<phase>none</phase>
|
||||
</execution>
|
||||
<!-- Replacing default-testCompile as it is treated specially by maven -->
|
||||
<execution>
|
||||
<id>default-testCompile</id>
|
||||
<phase>none</phase>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>java-compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals> <goal>compile</goal> </goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>java-test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals> <goal>testCompile</goal> </goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.descriptor;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The op descriptor for the libnd4j code base.
|
||||
* Each op represents a serialized version of
|
||||
* {@link org.nd4j.linalg.api.ops.DynamicCustomOp}
|
||||
* that has naming metadata attached.
|
||||
*
|
||||
* @author Adam Gibson
|
||||
*/
|
||||
@Data
|
||||
@Builder(toBuilder = true)
|
||||
public class OpDeclarationDescriptor implements Serializable {
|
||||
private String name;
|
||||
private int nIn,nOut,tArgs,iArgs;
|
||||
private boolean inplaceAble;
|
||||
private List<String> inArgNames;
|
||||
private List<String> outArgNames;
|
||||
private List<String> tArgNames;
|
||||
private List<String> iArgNames;
|
||||
private List<String> bArgNames;
|
||||
|
||||
|
||||
private OpDeclarationType opDeclarationType;
|
||||
@Builder.Default
|
||||
private Map<String,Boolean> argOptional = new HashMap<>();
|
||||
|
||||
|
||||
public enum OpDeclarationType {
|
||||
CUSTOM_OP_IMPL,
|
||||
BOOLEAN_OP_IMPL,
|
||||
LIST_OP_IMPL,
|
||||
LOGIC_OP_IMPL,
|
||||
OP_IMPL,
|
||||
DIVERGENT_OP_IMPL,
|
||||
CONFIGURABLE_OP_IMPL,
|
||||
REDUCTION_OP_IMPL,
|
||||
BROADCASTABLE_OP_IMPL,
|
||||
BROADCASTABLE_BOOL_OP_IMPL,
|
||||
LEGACY_XYZ,
|
||||
PLATFORM_IMPL,
|
||||
PLATFORM_TRANSFORM_STRICT_IMPL,
|
||||
PLATFORM_SCALAR_OP_IMPL,
|
||||
PLATFORM_CHECK
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void validate() {
|
||||
if(nIn >= 0 && nIn != inArgNames.size() && !isVariableInputSize()) {
|
||||
System.err.println("In arg names was not equal to number of inputs found for op " + name);
|
||||
}
|
||||
|
||||
if(nOut >= 0 && nOut != outArgNames.size() && !isVariableOutputSize()) {
|
||||
System.err.println("Output arg names was not equal to number of outputs found for op " + name);
|
||||
}
|
||||
|
||||
if(tArgs >= 0 && tArgs != tArgNames.size() && !isVariableTArgs()) {
|
||||
System.err.println("T arg names was not equal to number of T found for op " + name);
|
||||
}
|
||||
if(iArgs >= 0 && iArgs != iArgNames.size() && !isVariableIntArgs()) {
|
||||
System.err.println("Integer arg names was not equal to number of integer args found for op " + name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if there is a variable number
|
||||
* of integer arguments for an op
|
||||
* @return
|
||||
*/
|
||||
public boolean isVariableIntArgs() {
|
||||
return iArgs < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there is a variable
|
||||
* number of t arguments for an op
|
||||
* @return
|
||||
*/
|
||||
public boolean isVariableTArgs() {
|
||||
return tArgs < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the number of outputs is variable size
|
||||
* @return
|
||||
*/
|
||||
public boolean isVariableOutputSize() {
|
||||
return nOut < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the number of
|
||||
* inputs is variable size
|
||||
* @return
|
||||
*/
|
||||
public boolean isVariableInputSize() {
|
||||
return nIn < 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.descriptor;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.nd4j.common.base.Preconditions;
|
||||
import org.nd4j.common.primitives.Pair;
|
||||
import org.nd4j.descriptor.proposal.ArgDescriptorProposal;
|
||||
import org.nd4j.descriptor.proposal.ArgDescriptorSource;
|
||||
import org.nd4j.descriptor.proposal.impl.JavaSourceArgDescriptorSource;
|
||||
import org.nd4j.descriptor.proposal.impl.Libnd4jArgDescriptorSource;
|
||||
import org.nd4j.descriptor.proposal.impl.ArgDescriptorParserUtils;
|
||||
import org.nd4j.ir.OpNamespace;
|
||||
import org.nd4j.shade.protobuf.TextFormat;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
* Parses the libnd4j code base based on a relative path
|
||||
* default of ../deeplearning4j/libnd4j
|
||||
* or a passed in file path.
|
||||
* It generates a descriptor for each op.
|
||||
* The file properties can be found at {@link OpDeclarationDescriptor}
|
||||
*
|
||||
*
|
||||
* @author Adam Gibson
|
||||
*/
|
||||
public class ParseOpFile {
|
||||
|
||||
|
||||
public static void main(String...args) throws Exception {
|
||||
String libnd4jPath = args.length > 0 ? args[0] : Libnd4jArgDescriptorSource.DEFAULT_LIBND4J_DIRECTORY;
|
||||
String outputFilePath = args.length > 1 ? args[1] : ArgDescriptorParserUtils.DEFAULT_OUTPUT_FILE;
|
||||
|
||||
File libnd4jRootDir = new File(libnd4jPath);
|
||||
StringBuilder nd4jApiSourceDir = new StringBuilder();
|
||||
nd4jApiSourceDir.append("nd4j");
|
||||
nd4jApiSourceDir.append(File.separator);
|
||||
nd4jApiSourceDir.append("nd4j-backends");
|
||||
nd4jApiSourceDir.append(File.separator);
|
||||
nd4jApiSourceDir.append("nd4j-api-parent");
|
||||
nd4jApiSourceDir.append(File.separator);
|
||||
nd4jApiSourceDir.append("nd4j-api");
|
||||
nd4jApiSourceDir.append(File.separator);
|
||||
nd4jApiSourceDir.append("src");
|
||||
nd4jApiSourceDir.append(File.separator);
|
||||
nd4jApiSourceDir.append("main");
|
||||
nd4jApiSourceDir.append(File.separator);
|
||||
nd4jApiSourceDir.append("java");
|
||||
File nd4jApiRootDir = new File(new File(libnd4jPath).getParent(),nd4jApiSourceDir.toString());
|
||||
System.out.println("Parsing libnd4j code base at " + libnd4jRootDir.getAbsolutePath() + " and writing to " + outputFilePath);
|
||||
Libnd4jArgDescriptorSource libnd4jArgDescriptorSource = Libnd4jArgDescriptorSource.builder()
|
||||
.libnd4jPath(libnd4jPath)
|
||||
.weight(99999.0)
|
||||
.build();
|
||||
|
||||
|
||||
|
||||
JavaSourceArgDescriptorSource javaSourceArgDescriptorSource = JavaSourceArgDescriptorSource.builder()
|
||||
.nd4jApiRootDir(nd4jApiRootDir)
|
||||
.weight(1.0)
|
||||
.build();
|
||||
|
||||
Map<String, OpNamespace.OpDescriptor.OpDeclarationType> opTypes = new HashMap<>();
|
||||
|
||||
Map<String,List<ArgDescriptorProposal>> proposals = new HashMap<>();
|
||||
for(ArgDescriptorSource argDescriptorSource : new ArgDescriptorSource[] {libnd4jArgDescriptorSource,javaSourceArgDescriptorSource}) {
|
||||
Map<String, List<ArgDescriptorProposal>> currProposals = argDescriptorSource.getProposals();
|
||||
for(Map.Entry<String,List<ArgDescriptorProposal>> entry : currProposals.entrySet()) {
|
||||
Preconditions.checkState(!entry.getKey().isEmpty());
|
||||
Set<String> seenNames = new HashSet<>();
|
||||
if(proposals.containsKey(entry.getKey())) {
|
||||
List<ArgDescriptorProposal> currProposalsList = proposals.get(entry.getKey());
|
||||
currProposalsList.addAll(entry.getValue().stream().filter(proposal -> {
|
||||
Preconditions.checkState(!proposal.getDescriptor().getName().isEmpty());
|
||||
boolean ret = proposal.getDescriptor().getArgIndex() >= 0 && !seenNames.contains(proposal.getDescriptor().getName());
|
||||
seenNames.add(proposal.getDescriptor().getName());
|
||||
return ret;
|
||||
}).collect(Collectors.toList()));
|
||||
|
||||
}
|
||||
else {
|
||||
Preconditions.checkState(!entry.getKey().isEmpty());
|
||||
proposals.put(entry.getKey(),entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
javaSourceArgDescriptorSource.getOpTypes().forEach((k,v) -> {
|
||||
opTypes.put(k, OpNamespace.OpDescriptor.OpDeclarationType.valueOf(v.name()));
|
||||
});
|
||||
|
||||
libnd4jArgDescriptorSource.getOpTypes().forEach((k,v) -> {
|
||||
opTypes.put(k, OpNamespace.OpDescriptor.OpDeclarationType.valueOf(v.name()));
|
||||
|
||||
});
|
||||
|
||||
opTypes.putAll(javaSourceArgDescriptorSource.getOpTypes());
|
||||
opTypes.putAll(libnd4jArgDescriptorSource.getOpTypes());
|
||||
|
||||
OpNamespace.OpDescriptorList.Builder listBuilder = OpNamespace.OpDescriptorList.newBuilder();
|
||||
for(Map.Entry<String,List<ArgDescriptorProposal>> proposal : proposals.entrySet()) {
|
||||
Preconditions.checkState(!proposal.getKey().isEmpty());
|
||||
Map<String, List<ArgDescriptorProposal>> collect = proposal.getValue().stream()
|
||||
.collect(Collectors.groupingBy(input -> input.getDescriptor().getName()));
|
||||
//merge boolean and int64
|
||||
collect.entrySet().forEach(entry -> {
|
||||
ArgDescriptorParserUtils.standardizeTypes(entry.getValue());
|
||||
});
|
||||
|
||||
Map<Pair<Integer, OpNamespace.ArgDescriptor.ArgType>, OpNamespace.ArgDescriptor> rankedProposals = ArgDescriptorParserUtils.
|
||||
standardizeNames(collect, proposal.getKey());
|
||||
OpNamespace.OpDescriptor.Builder opDescriptorBuilder = OpNamespace.OpDescriptor.newBuilder()
|
||||
.setOpDeclarationType(opTypes.get(proposal.getKey()))
|
||||
.setName(proposal.getKey());
|
||||
rankedProposals.entrySet().stream().map(input -> input.getValue())
|
||||
.forEach(argDescriptor -> {
|
||||
opDescriptorBuilder.addArgDescriptor(argDescriptor);
|
||||
});
|
||||
|
||||
listBuilder.addOpList(opDescriptorBuilder.build());
|
||||
|
||||
}
|
||||
|
||||
OpNamespace.OpDescriptorList.Builder sortedListBuilder = OpNamespace.OpDescriptorList.newBuilder();
|
||||
List<OpNamespace.OpDescriptor> sortedDescriptors = new ArrayList<>();
|
||||
for(int i = 0; i < listBuilder.getOpListCount(); i++) {
|
||||
OpNamespace.OpDescriptor opList = listBuilder.getOpList(i);
|
||||
OpNamespace.OpDescriptor.Builder sortedOpBuilder = OpNamespace.OpDescriptor.newBuilder();
|
||||
Map<OpNamespace.ArgDescriptor.ArgType, List<OpNamespace.ArgDescriptor>> sortedByType = opList.getArgDescriptorList().stream().collect(Collectors.groupingBy(input -> input.getArgType()));
|
||||
Set<String> namesEncountered = new HashSet<>();
|
||||
sortedByType.entrySet().forEach(entry -> {
|
||||
Collections.sort(entry.getValue(),Comparator.comparing(inputArg -> inputArg.getArgIndex()));
|
||||
for(int j = 0; j < entry.getValue().size(); j++) {
|
||||
OpNamespace.ArgDescriptor currDescriptor = entry.getValue().get(j);
|
||||
boolean isArrayArg = false;
|
||||
String finalName = currDescriptor.getName();
|
||||
if(currDescriptor.getName().contains("[")) {
|
||||
isArrayArg = true;
|
||||
finalName = finalName.replaceAll("\\[.*\\]","").replace("*","");
|
||||
}
|
||||
|
||||
if(currDescriptor.getArgIndex() != j && !namesEncountered.contains(currDescriptor.getName())) {
|
||||
throw new IllegalStateException("Op name " + opList.getName() + " has incontiguous indices for type " + entry.getKey() + " with descriptor being " +currDescriptor);
|
||||
} else if(currDescriptor.getArgIndex() != j && namesEncountered.contains(currDescriptor.getName())) {
|
||||
//skip names we already mapped
|
||||
System.err.println("Op name " + opList.getName() + " has incontiguous indices for type " + entry.getKey() + " with descriptor being " +currDescriptor + " skipping");
|
||||
}
|
||||
|
||||
OpNamespace.ArgDescriptor.Builder newDescriptor = OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setName(finalName)
|
||||
.setArgIndex(currDescriptor.getArgIndex())
|
||||
.setIsArray(isArrayArg)
|
||||
.setArgType(currDescriptor.getArgType())
|
||||
.setConvertBoolToInt(currDescriptor.getConvertBoolToInt());
|
||||
|
||||
sortedOpBuilder.addArgDescriptor(newDescriptor.build());
|
||||
|
||||
namesEncountered.add(currDescriptor.getName());
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
sortedOpBuilder.setOpDeclarationType(opList.getOpDeclarationType());
|
||||
sortedOpBuilder.setName(opList.getName());
|
||||
sortedDescriptors.add(sortedOpBuilder.build());
|
||||
|
||||
}
|
||||
|
||||
|
||||
//sort alphabetically
|
||||
Collections.sort(sortedDescriptors,Comparator.comparing(opDescriptor -> opDescriptor.getName()));
|
||||
//add placeholder as an op to map
|
||||
sortedDescriptors.add(OpNamespace.OpDescriptor.newBuilder()
|
||||
.setName("placeholder")
|
||||
.setOpDeclarationType(OpNamespace.OpDescriptor.OpDeclarationType.LOGIC_OP_IMPL)
|
||||
.build());
|
||||
sortedDescriptors.forEach(input -> {
|
||||
sortedListBuilder.addOpList(input);
|
||||
});
|
||||
|
||||
|
||||
String write = TextFormat.printToString(sortedListBuilder.build());
|
||||
FileUtils.writeStringToFile(new File(outputFilePath),write, Charset.defaultCharset());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.descriptor.proposal;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.nd4j.ir.OpNamespace;
|
||||
|
||||
@Builder
|
||||
@Data
|
||||
public class ArgDescriptorProposal {
|
||||
|
||||
private OpNamespace.ArgDescriptor descriptor;
|
||||
|
||||
private String sourceLine;
|
||||
|
||||
private double proposalWeight;
|
||||
|
||||
private String sourceOfProposal;
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.descriptor.proposal;
|
||||
|
||||
import org.nd4j.ir.OpNamespace;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface ArgDescriptorSource {
|
||||
|
||||
Map<String, List<ArgDescriptorProposal>> getProposals();
|
||||
|
||||
OpNamespace.OpDescriptor.OpDeclarationType typeFor(String name);
|
||||
|
||||
|
||||
}
|
||||
+842
@@ -0,0 +1,842 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.descriptor.proposal.impl;
|
||||
|
||||
import com.github.javaparser.ast.expr.MethodCallExpr;
|
||||
import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
|
||||
import com.github.javaparser.resolution.declarations.ResolvedParameterDeclaration;
|
||||
import lombok.val;
|
||||
import org.apache.commons.text.similarity.LevenshteinDistance;
|
||||
import org.nd4j.autodiff.samediff.SDVariable;
|
||||
import org.nd4j.common.base.Preconditions;
|
||||
import org.nd4j.common.primitives.*;
|
||||
import org.nd4j.descriptor.OpDeclarationDescriptor;
|
||||
import org.nd4j.descriptor.proposal.ArgDescriptorProposal;
|
||||
import org.nd4j.ir.OpNamespace;
|
||||
import org.nd4j.linalg.api.buffer.DataType;
|
||||
import org.nd4j.linalg.api.ndarray.INDArray;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ArgDescriptorParserUtils {
|
||||
public final static String DEFAULT_OUTPUT_FILE = "op-ir.proto";
|
||||
public final static Pattern numberPattern = Pattern.compile("\\([\\d]+\\)");
|
||||
|
||||
|
||||
public final static String ARGUMENT_ENDING_PATTERN = "\\([\\w\\d+-\\\\*\\/]+\\);";
|
||||
public final static String ARGUMENT_PATTERN = "\\([\\w\\d+-\\\\*\\/]+\\)";
|
||||
|
||||
public final static String ARRAY_ASSIGNMENT = "\\w+\\[[a-zA-Z]+\\]\\s*=\\s*[A-Z]+_[A-Z]+\\(\\s*[\\d\\w+-\\/\\*\\s]+\\);";
|
||||
|
||||
public final static Set<String> bannedMaxIndexOps = new HashSet<String>() {{
|
||||
add("embedding_lookup");
|
||||
add("stack");
|
||||
}};
|
||||
|
||||
public final static Set<String> bannedIndexChangeOps = new HashSet<String>() {{
|
||||
add("gemm");
|
||||
add("mmul");
|
||||
add("matmul");
|
||||
}};
|
||||
|
||||
|
||||
public static final Set<String> cppTypes = new HashSet<String>() {{
|
||||
add("int");
|
||||
add("bool");
|
||||
add("auto");
|
||||
add("string");
|
||||
add("float");
|
||||
add("double");
|
||||
add("char");
|
||||
add("class");
|
||||
add("uint");
|
||||
}};
|
||||
|
||||
public final static Set<String> fieldNameFilters = new HashSet<String>() {{
|
||||
add("sameDiff");
|
||||
add("xVertexId");
|
||||
add("yVertexId");
|
||||
add("zVertexId");
|
||||
add("extraArgs");
|
||||
add("extraArgz");
|
||||
add("dimensionz");
|
||||
add("scalarValue");
|
||||
add("dimensions");
|
||||
add("jaxis");
|
||||
add("inPlace");
|
||||
}};
|
||||
|
||||
public final static Set<String> fieldNameFiltersDynamicCustomOps = new HashSet<String>() {{
|
||||
add("sameDiff");
|
||||
add("xVertexId");
|
||||
add("yVertexId");
|
||||
add("zVertexId");
|
||||
add("extraArgs");
|
||||
add("extraArgz");
|
||||
add("dimensionz");
|
||||
add("scalarValue");
|
||||
add("jaxis");
|
||||
add("inPlace");
|
||||
add("inplaceCall");
|
||||
}};
|
||||
|
||||
public static Map<String,String> equivalentAttributeNames = new HashMap<String,String>() {{
|
||||
put("axis","dimensions");
|
||||
put("dimensions","axis");
|
||||
put("jaxis","dimensions");
|
||||
put("dimensions","jaxis");
|
||||
put("inplaceCall","inPlace");
|
||||
put("inPlace","inplaceCall");
|
||||
}};
|
||||
|
||||
|
||||
public static Set<String> dimensionNames = new HashSet<String>() {{
|
||||
add("jaxis");
|
||||
add("axis");
|
||||
add("dimensions");
|
||||
add("dimensionz");
|
||||
add("dim");
|
||||
add("axisVector");
|
||||
add("axesI");
|
||||
add("ax");
|
||||
add("dims");
|
||||
add("axes");
|
||||
add("axesVector");
|
||||
}};
|
||||
|
||||
public static Set<String> inputNames = new HashSet<String>() {{
|
||||
add("input");
|
||||
add("inputs");
|
||||
add("i_v");
|
||||
add("x");
|
||||
add("in");
|
||||
add("args");
|
||||
add("i_v1");
|
||||
add("first");
|
||||
add("layerInput");
|
||||
add("in1");
|
||||
add("arrays");
|
||||
}};
|
||||
public static Set<String> input2Names = new HashSet<String>() {{
|
||||
add("y");
|
||||
add("i_v2");
|
||||
add("second");
|
||||
add("in2");
|
||||
}};
|
||||
|
||||
public static Set<String> outputNames = new HashSet<String>() {{
|
||||
add("output");
|
||||
add("outputs");
|
||||
add("out");
|
||||
add("result");
|
||||
add("z");
|
||||
add("outputArrays");
|
||||
}};
|
||||
|
||||
|
||||
public static Set<String> inplaceNames = new HashSet<String>() {{
|
||||
add("inPlace");
|
||||
add("inplaceCall");
|
||||
}};
|
||||
|
||||
|
||||
public static OpNamespace.ArgDescriptor.ArgType argTypeForParam(ResolvedParameterDeclaration parameterDeclaration) {
|
||||
String type = parameterDeclaration.describeType();
|
||||
boolean isEnum = false;
|
||||
try {
|
||||
isEnum = Class.forName(parameterDeclaration.asParameter().describeType()).isEnum();
|
||||
} catch(ClassNotFoundException e) {
|
||||
|
||||
}
|
||||
|
||||
if(type.contains(INDArray.class.getName()) || type.contains(SDVariable.class.getName())) {
|
||||
if(!outputNames.contains(parameterDeclaration.getName())) {
|
||||
return OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR;
|
||||
}
|
||||
else return OpNamespace.ArgDescriptor.ArgType.OUTPUT_TENSOR;
|
||||
} else if(type.contains(DataType.class.getName())) {
|
||||
return OpNamespace.ArgDescriptor.ArgType.DATA_TYPE;
|
||||
|
||||
} else if(type.contains(double.class.getName()) || type.contains(float.class.getName()) || type.contains(Float.class.getName()) || type.contains(Double.class.getName())) {
|
||||
return OpNamespace.ArgDescriptor.ArgType.DOUBLE;
|
||||
} else if(type.contains(int.class.getName()) || type.contains(long.class.getName()) ||
|
||||
type.contains(Integer.class.getName()) || type.contains(Long.class.getName()) || isEnum) {
|
||||
return OpNamespace.ArgDescriptor.ArgType.INT64;
|
||||
} else if(type.contains(boolean.class.getName()) || type.contains(Boolean.class.getName())) {
|
||||
return OpNamespace.ArgDescriptor.ArgType.BOOL;
|
||||
} else {
|
||||
return OpNamespace.ArgDescriptor.ArgType.UNRECOGNIZED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static boolean paramIsEnum(String paramType) {
|
||||
try {
|
||||
return Class.forName(paramType).isEnum();
|
||||
} catch(ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static boolean paramIsEnum(ResolvedParameterDeclaration param) {
|
||||
return paramIsEnum(param.describeType());
|
||||
}
|
||||
|
||||
|
||||
public static boolean isValidParam(ResolvedParameterDeclaration param) {
|
||||
boolean describedClassIsEnum = false;
|
||||
boolean ret = param.describeType().contains(INDArray.class.getName()) ||
|
||||
param.describeType().contains(boolean.class.getName()) ||
|
||||
param.describeType().contains(Boolean.class.getName()) ||
|
||||
param.describeType().contains(SDVariable.class.getName()) ||
|
||||
param.describeType().contains(Integer.class.getName()) ||
|
||||
param.describeType().contains(int.class.getName()) ||
|
||||
param.describeType().contains(double.class.getName()) ||
|
||||
param.describeType().contains(Double.class.getName()) ||
|
||||
param.describeType().contains(float.class.getName()) ||
|
||||
param.describeType().contains(Float.class.getName()) ||
|
||||
param.describeType().contains(Long.class.getName()) ||
|
||||
param.describeType().contains(long.class.getName());
|
||||
try {
|
||||
describedClassIsEnum = Class.forName(param.asParameter().describeType()).isEnum();
|
||||
} catch(ClassNotFoundException e) {
|
||||
|
||||
}
|
||||
return ret || describedClassIsEnum;
|
||||
}
|
||||
|
||||
public static ResolvedMethodDeclaration tryResolve(MethodCallExpr methodCallExpr) {
|
||||
try {
|
||||
return methodCallExpr.resolve();
|
||||
}catch(Exception e) {
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean typeNameOrArrayOfTypeNameMatches(String typeName,String...types) {
|
||||
boolean ret = false;
|
||||
for(String type : types) {
|
||||
ret = typeName.equals(type) ||
|
||||
typeName.equals(type + "...") ||
|
||||
typeName.equals(type + "[]") || ret;
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
public static boolean equivalentAttribute(OpNamespace.ArgDescriptor comp1, OpNamespace.ArgDescriptor comp2) {
|
||||
if(equivalentAttributeNames.containsKey(comp1.getName())) {
|
||||
return equivalentAttributeNames.get(comp1.getName()).equals(comp2.getName());
|
||||
}
|
||||
|
||||
if(equivalentAttributeNames.containsKey(comp2.getName())) {
|
||||
return equivalentAttributeNames.get(comp2.getName()).equals(comp1.getName());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean argsListContainsEquivalentAttribute(List<OpNamespace.ArgDescriptor> argDescriptors, OpNamespace.ArgDescriptor to) {
|
||||
for(OpNamespace.ArgDescriptor argDescriptor : argDescriptors) {
|
||||
if(argDescriptor.getArgType() == to.getArgType() && equivalentAttribute(argDescriptor,to)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean argsListContainsSimilarArg(List<OpNamespace.ArgDescriptor> argDescriptors, OpNamespace.ArgDescriptor to, int threshold) {
|
||||
for(OpNamespace.ArgDescriptor argDescriptor : argDescriptors) {
|
||||
if(argDescriptor.getArgType() == to.getArgType() && LevenshteinDistance.getDefaultInstance().apply(argDescriptor.getName().toLowerCase(),to.getName().toLowerCase()) <= threshold) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static OpNamespace.ArgDescriptor mergeDescriptorsOfSameIndex(OpNamespace.ArgDescriptor one, OpNamespace.ArgDescriptor two) {
|
||||
if(one.getArgIndex() != two.getArgIndex()) {
|
||||
throw new IllegalArgumentException("Argument indices for both arg descriptors were not the same. First one was " + one.getArgIndex() + " and second was " + two.getArgIndex());
|
||||
}
|
||||
|
||||
if(one.getArgType() != two.getArgType()) {
|
||||
throw new IllegalArgumentException("Merging two arg descriptors requires both be the same type. First one was " + one.getArgType().name() + " and second one was " + two.getArgType().name());
|
||||
}
|
||||
|
||||
OpNamespace.ArgDescriptor.Builder newDescriptor = OpNamespace.ArgDescriptor.newBuilder();
|
||||
//arg indices will be the same
|
||||
newDescriptor.setArgIndex(one.getArgIndex());
|
||||
newDescriptor.setArgType(one.getArgType());
|
||||
if(!isValidIdentifier(one.getName()) && !isValidIdentifier(two.getName())) {
|
||||
newDescriptor.setName("arg" + newDescriptor.getArgIndex());
|
||||
} else if(!isValidIdentifier(one.getName())) {
|
||||
newDescriptor.setName(two.getName());
|
||||
} else {
|
||||
newDescriptor.setName(one.getName());
|
||||
}
|
||||
|
||||
|
||||
return newDescriptor.build();
|
||||
}
|
||||
|
||||
public static boolean isValidIdentifier(String input) {
|
||||
if(input == null || input.isEmpty())
|
||||
return false;
|
||||
|
||||
for(int i = 0; i < input.length(); i++) {
|
||||
if(!Character.isJavaIdentifierPart(input.charAt(i)))
|
||||
return false;
|
||||
}
|
||||
|
||||
if(cppTypes.contains(input))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean containsOutputTensor(Collection<ArgDescriptorProposal> proposals) {
|
||||
for(ArgDescriptorProposal proposal : proposals) {
|
||||
if(proposal.getDescriptor().getArgType() == OpNamespace.ArgDescriptor.ArgType.OUTPUT_TENSOR) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static OpNamespace.ArgDescriptor getDescriptorWithName(String name, Collection<ArgDescriptorProposal> proposals) {
|
||||
for(ArgDescriptorProposal proposal : proposals) {
|
||||
if(proposal.getDescriptor().getName().equals(name)) {
|
||||
return proposal.getDescriptor();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static int numProposalsWithType(OpNamespace.ArgDescriptor.ArgType argType, Collection<ArgDescriptorProposal> proposals) {
|
||||
int count = 0;
|
||||
for(ArgDescriptorProposal proposal : proposals) {
|
||||
if(proposal.getDescriptor().getArgType() == argType) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public static boolean containsProposalWithDescriptorName(String name, Collection<ArgDescriptorProposal> proposals) {
|
||||
for(ArgDescriptorProposal proposal : proposals) {
|
||||
if(proposal.getDescriptor().getName().equals(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<ArgDescriptorProposal> updateOpDescriptor(OpNamespace.OpDescriptor opDescriptor, OpDeclarationDescriptor declarationDescriptor, List<String> argsByIIndex, OpNamespace.ArgDescriptor.ArgType int64) {
|
||||
List<OpNamespace.ArgDescriptor> copyValuesInt = addArgDescriptors(opDescriptor, declarationDescriptor, argsByIIndex, int64);
|
||||
List<ArgDescriptorProposal> proposals = new ArrayList<>();
|
||||
|
||||
return proposals;
|
||||
}
|
||||
|
||||
public static List<OpNamespace.ArgDescriptor> addArgDescriptors(OpNamespace.OpDescriptor opDescriptor, OpDeclarationDescriptor declarationDescriptor, List<String> argsByTIndex, OpNamespace.ArgDescriptor.ArgType argType) {
|
||||
List<OpNamespace.ArgDescriptor> copyValuesFloat = new ArrayList<>(opDescriptor.getArgDescriptorList());
|
||||
for(int i = 0; i < argsByTIndex.size(); i++) {
|
||||
OpNamespace.ArgDescriptor argDescriptor = OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(argType)
|
||||
.setName(argsByTIndex.get(i))
|
||||
.setArgIndex(i)
|
||||
//this can happen when there are still missing names from c++
|
||||
.setArgOptional(declarationDescriptor != null && i <= declarationDescriptor.getTArgs() ? false : true)
|
||||
.build();
|
||||
copyValuesFloat.add(argDescriptor);
|
||||
|
||||
}
|
||||
return copyValuesFloat;
|
||||
}
|
||||
|
||||
public static Map<String,Integer> argIndexForCsv(String line) {
|
||||
Map<String,Integer> ret = new HashMap<>();
|
||||
String[] lineSplit = line.split(",");
|
||||
for(int i = 0; i < lineSplit.length; i++) {
|
||||
ret.put(lineSplit[i],i);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static Integer extractArgFromJava(String line) {
|
||||
Matcher matcher = numberPattern.matcher(line);
|
||||
if(!matcher.find()) {
|
||||
throw new IllegalArgumentException("No number found for line " + line);
|
||||
}
|
||||
|
||||
return Integer.parseInt(matcher.group());
|
||||
}
|
||||
|
||||
public static Integer extractArgFromCpp(String line,String argType) {
|
||||
Matcher matcher = Pattern.compile(argType + "\\([\\d]+\\)").matcher(line);
|
||||
if(!matcher.find()) {
|
||||
//Generally not resolvable
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(matcher.groupCount() > 1) {
|
||||
throw new IllegalArgumentException("Line contains more than 1 index");
|
||||
}
|
||||
|
||||
try {
|
||||
return Integer.parseInt(matcher.group().replace("(","").replace(")","").replace(argType,""));
|
||||
} catch(NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Field> getAllFields(Class clazz) {
|
||||
if (clazz == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Field> result = new ArrayList<>(getAllFields(clazz.getSuperclass()));
|
||||
List<Field> filteredFields = Arrays.stream(clazz.getDeclaredFields())
|
||||
.filter(f -> Modifier.isPublic(f.getModifiers()) || Modifier.isProtected(f.getModifiers()))
|
||||
.collect(Collectors.toList());
|
||||
result.addAll(filteredFields);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String removeBracesFromDeclarationMacro(String line, String nameOfMacro) {
|
||||
line = line.replace(nameOfMacro + "(", "");
|
||||
line = line.replace(")", "");
|
||||
line = line.replace("{", "");
|
||||
line = line.replace(";","");
|
||||
return line;
|
||||
}
|
||||
|
||||
public static void addNameToList(String line, List<String> list, List<Integer> argIndices, String argType) {
|
||||
String[] split = line.split(" = ");
|
||||
String[] arrSplit = split[0].split(" ");
|
||||
//type + name
|
||||
String name = arrSplit[arrSplit.length - 1];
|
||||
Preconditions.checkState(!name.isEmpty());
|
||||
if(!list.contains(name))
|
||||
list.add(name);
|
||||
|
||||
Integer index = extractArgFromCpp(line,argType);
|
||||
if(index != null)
|
||||
argIndices.add(index);
|
||||
}
|
||||
|
||||
public static void addArrayNameToList(String line, List<String> list, List<Integer> argIndices, String argType) {
|
||||
String[] split = line.split(" = ");
|
||||
String[] arrSplit = split[0].split(" ");
|
||||
//type + name
|
||||
String name = arrSplit[arrSplit.length - 1];
|
||||
Preconditions.checkState(!name.isEmpty());
|
||||
if(!list.contains(name))
|
||||
list.add(name);
|
||||
//arrays are generally appended to the end
|
||||
Integer index = - 1;
|
||||
if(index != null)
|
||||
argIndices.add(index);
|
||||
}
|
||||
|
||||
public static void standardizeTypes(List<ArgDescriptorProposal> input) {
|
||||
input.stream().forEach(proposal -> {
|
||||
//note that if automatic conversion should not happen, set convertBoolToInt to false
|
||||
if(proposal.getDescriptor().getArgType() == OpNamespace.ArgDescriptor.ArgType.BOOL && proposal.getDescriptor().getConvertBoolToInt()) {
|
||||
OpNamespace.ArgDescriptor newDescriptor = OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgIndex(proposal.getDescriptor().getArgIndex())
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INT64)
|
||||
.setName(proposal.getDescriptor().getName())
|
||||
.build();
|
||||
proposal.setDescriptor(newDescriptor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static ArgDescriptorProposal aggregateProposals(List<ArgDescriptorProposal> listOfProposals) {
|
||||
val descriptorBuilder = OpNamespace.ArgDescriptor.newBuilder();
|
||||
Counter<Integer> mostLikelyIndex = new Counter<>();
|
||||
|
||||
AtomicDouble aggregatedWeight = new AtomicDouble(0.0);
|
||||
listOfProposals.forEach(proposal -> {
|
||||
mostLikelyIndex.incrementCount(proposal.getDescriptor().getArgIndex(),1.0);
|
||||
aggregatedWeight.addAndGet(proposal.getProposalWeight());
|
||||
descriptorBuilder.setName(proposal.getDescriptor().getName());
|
||||
descriptorBuilder.setIsArray(proposal.getDescriptor().getIsArray());
|
||||
descriptorBuilder.setArgType(proposal.getDescriptor().getArgType());
|
||||
descriptorBuilder.setConvertBoolToInt(proposal.getDescriptor().getConvertBoolToInt());
|
||||
descriptorBuilder.setIsArray(proposal.getDescriptor().getIsArray());
|
||||
});
|
||||
|
||||
//set the index after computing the most likely index
|
||||
descriptorBuilder.setArgIndex(mostLikelyIndex.argMax());
|
||||
|
||||
return ArgDescriptorProposal
|
||||
.builder()
|
||||
.descriptor(descriptorBuilder.build())
|
||||
.proposalWeight(aggregatedWeight.doubleValue())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static Map<Pair<Integer, OpNamespace.ArgDescriptor.ArgType>, OpNamespace.ArgDescriptor> standardizeNames
|
||||
(Map<String, List<ArgDescriptorProposal>> toStandardize, String opName) {
|
||||
Map<String,List<ArgDescriptorProposal>> ret = new HashMap<>();
|
||||
Map<Pair<Integer, OpNamespace.ArgDescriptor.ArgType>,List<ArgDescriptorProposal>> dimensionProposals = new HashMap<>();
|
||||
Map<Pair<Integer, OpNamespace.ArgDescriptor.ArgType>,List<ArgDescriptorProposal>> inPlaceProposals = new HashMap<>();
|
||||
Map<Pair<Integer, OpNamespace.ArgDescriptor.ArgType>,List<ArgDescriptorProposal>> inputsProposals = new HashMap<>();
|
||||
Map<Pair<Integer, OpNamespace.ArgDescriptor.ArgType>,List<ArgDescriptorProposal>> inputs2Proposals = new HashMap<>();
|
||||
Map<Pair<Integer, OpNamespace.ArgDescriptor.ArgType>,List<ArgDescriptorProposal>> outputsProposals = new HashMap<>();
|
||||
|
||||
toStandardize.entrySet().forEach(entry -> {
|
||||
if(entry.getKey().isEmpty()) {
|
||||
throw new IllegalStateException("Name must not be empty!");
|
||||
}
|
||||
|
||||
if(dimensionNames.contains(entry.getKey())) {
|
||||
extractProposals(dimensionProposals, entry);
|
||||
} else if(inplaceNames.contains(entry.getKey())) {
|
||||
extractProposals(inPlaceProposals, entry);
|
||||
} else if(inputNames.contains(entry.getKey())) {
|
||||
extractProposals(inputsProposals, entry);
|
||||
} else if(input2Names.contains(entry.getKey())) {
|
||||
extractProposals(inputs2Proposals, entry);
|
||||
} else if(outputNames.contains(entry.getKey())) {
|
||||
extractProposals(outputsProposals, entry);
|
||||
}
|
||||
else {
|
||||
ret.put(entry.getKey(),entry.getValue());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Two core ops have issues:
|
||||
* argmax and cumsum both have the same issue
|
||||
* other boolean attributes are present
|
||||
* that are converted to ints and get clobbered
|
||||
* by dimensions having the wrong index
|
||||
*
|
||||
* For argmax, exclusive gets clobbered. It should have index 0,
|
||||
* but for some reason dimensions gets index 0.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* TODO: make this method return name/type
|
||||
* combinations rather than just name/single list.
|
||||
*/
|
||||
if(!dimensionProposals.isEmpty()) {
|
||||
// List<Pair<Pair<Integer, OpNamespace.ArgDescriptor.ArgType>, ArgDescriptorProposal>> d
|
||||
computeAggregatedProposalsPerType(ret, dimensionProposals, "dimensions");
|
||||
}
|
||||
|
||||
if(!inPlaceProposals.isEmpty()) {
|
||||
computeAggregatedProposalsPerType(ret, inPlaceProposals, "inPlace");
|
||||
}
|
||||
|
||||
if(!inputsProposals.isEmpty()) {
|
||||
computeAggregatedProposalsPerType(ret, inputsProposals, "input");
|
||||
|
||||
}
|
||||
|
||||
if(!inputs2Proposals.isEmpty()) {
|
||||
computeAggregatedProposalsPerType(ret, inputs2Proposals, "y");
|
||||
|
||||
}
|
||||
|
||||
if(!outputsProposals.isEmpty()) {
|
||||
computeAggregatedProposalsPerType(ret, outputsProposals, "outputs");
|
||||
}
|
||||
|
||||
Map<Pair<Integer, OpNamespace.ArgDescriptor.ArgType>, OpNamespace.ArgDescriptor> ret2 = new HashMap<>();
|
||||
CounterMap<Pair<Integer, OpNamespace.ArgDescriptor.ArgType>,ArgDescriptorProposal> proposalsByType = new CounterMap<>();
|
||||
ret.values().forEach(input -> {
|
||||
input.forEach(proposal1 -> {
|
||||
proposalsByType.incrementCount(Pair.of(proposal1.getDescriptor().getArgIndex(),proposal1.getDescriptor().getArgType()),proposal1,proposal1.getProposalWeight());
|
||||
});
|
||||
});
|
||||
|
||||
ret.clear();
|
||||
proposalsByType.keySet().stream().forEach(argTypeIndexPair -> {
|
||||
val proposal = proposalsByType.getCounter(argTypeIndexPair).argMax();
|
||||
val name = proposal.getDescriptor().getName();
|
||||
List<ArgDescriptorProposal> proposalsForName;
|
||||
if(!ret.containsKey(name)) {
|
||||
proposalsForName = new ArrayList<>();
|
||||
ret.put(name,proposalsForName);
|
||||
}
|
||||
else
|
||||
proposalsForName = ret.get(name);
|
||||
proposalsForName.add(proposal);
|
||||
ret.put(proposal.getDescriptor().getName(),proposalsForName);
|
||||
});
|
||||
|
||||
ret.forEach((name,proposals) -> {
|
||||
val proposalsGroupedByType = proposals.stream().collect(Collectors.groupingBy(proposal -> proposal.getDescriptor().getArgType()));
|
||||
List<ArgDescriptorProposal> maxProposalsForEachType = new ArrayList<>();
|
||||
proposalsGroupedByType.forEach((type,proposalGroupByType) -> {
|
||||
Counter<ArgDescriptorProposal> proposalsCounter = new Counter<>();
|
||||
proposalGroupByType.forEach(proposalByType -> {
|
||||
proposalsCounter.incrementCount(proposalByType,proposalByType.getProposalWeight());
|
||||
});
|
||||
|
||||
maxProposalsForEachType.add(proposalsCounter.argMax());
|
||||
});
|
||||
|
||||
proposals = maxProposalsForEachType;
|
||||
|
||||
|
||||
//group by index and type
|
||||
val collected = proposals.stream()
|
||||
.collect(Collectors.groupingBy(input -> Pair.of(input.getDescriptor().getArgIndex(),input.getDescriptor().getArgType())))
|
||||
.entrySet()
|
||||
.stream().map(input -> Pair.of(input.getKey(),
|
||||
aggregateProposals(input.getValue()).getDescriptor()))
|
||||
.collect(Collectors.toMap(pair -> pair.getKey(),pair -> pair.getValue()));
|
||||
val groupedByType = collected.entrySet().stream().collect(Collectors.groupingBy(input -> input.getKey().getRight()));
|
||||
groupedByType.forEach((argType,list) -> {
|
||||
//count number of elements that aren't -1
|
||||
int numGreaterThanNegativeOne = list.stream().map(input -> input.getKey().getFirst() >= 0 ? 1 : 0)
|
||||
.reduce(0,(a,b) -> a + b);
|
||||
if(numGreaterThanNegativeOne > 1) {
|
||||
throw new IllegalStateException("Name of " + name + " with type " + argType + " not aggregated properly.");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
val arrEntries = collected.entrySet().stream()
|
||||
.filter(pair -> pair.getValue().getIsArray())
|
||||
.collect(Collectors.toList());
|
||||
//process arrays separately and aggregate by type
|
||||
if(!arrEntries.isEmpty()) {
|
||||
val initialType = arrEntries.get(0).getValue().getArgType();
|
||||
val allSameType = new AtomicBoolean(true);
|
||||
val negativeOnePresent = new AtomicBoolean(false);
|
||||
arrEntries.forEach(entry -> {
|
||||
allSameType.set(allSameType.get() && entry.getValue().getArgType() == initialType);
|
||||
negativeOnePresent.set(negativeOnePresent.get() || entry.getValue().getArgIndex() == -1);
|
||||
//only remove if we see -1
|
||||
if(negativeOnePresent.get())
|
||||
collected.remove(entry.getKey());
|
||||
});
|
||||
|
||||
if(allSameType.get() && negativeOnePresent.get()) {
|
||||
collected.put(Pair.of(-1,initialType), OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(initialType)
|
||||
.setArgIndex(-1)
|
||||
.setIsArray(true)
|
||||
.setName(arrEntries.get(0).getValue().getName()).build());
|
||||
}
|
||||
}
|
||||
|
||||
ret2.putAll(collected);
|
||||
|
||||
});
|
||||
|
||||
Map<OpNamespace.ArgDescriptor.ArgType,Integer> maxIndex = new HashMap<>();
|
||||
if(!bannedMaxIndexOps.contains(opName))
|
||||
ret2.forEach((key,value) -> {
|
||||
if(!maxIndex.containsKey(key.getRight())) {
|
||||
maxIndex.put(key.getValue(),key.getFirst());
|
||||
} else {
|
||||
maxIndex.put(key.getValue(),Math.max(key.getFirst(),maxIndex.get(key.getValue())));
|
||||
}
|
||||
});
|
||||
|
||||
//update -1 values to be valid indices relative to whatever the last index is when an array is found
|
||||
//and -1 is present
|
||||
Map<Pair<Integer, OpNamespace.ArgDescriptor.ArgType>, OpNamespace.ArgDescriptor> updateValues = new HashMap<>();
|
||||
Set<Pair<Integer, OpNamespace.ArgDescriptor.ArgType>> removeKeys = new HashSet<>();
|
||||
if(!bannedMaxIndexOps.contains(opName))
|
||||
ret2.forEach((key,value) -> {
|
||||
if(value.getArgIndex() < 0) {
|
||||
removeKeys.add(key);
|
||||
int maxIdx = maxIndex.get(value.getArgType());
|
||||
updateValues.put(Pair.of(maxIdx + 1,value.getArgType()), OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setName(value.getName())
|
||||
.setIsArray(value.getIsArray())
|
||||
.setArgType(value.getArgType())
|
||||
.setArgIndex(maxIdx + 1)
|
||||
.setConvertBoolToInt(value.getConvertBoolToInt())
|
||||
.build());
|
||||
}
|
||||
});
|
||||
|
||||
removeKeys.forEach(key -> ret2.remove(key));
|
||||
ret2.putAll(updateValues);
|
||||
return ret2;
|
||||
}
|
||||
|
||||
private static void computeAggregatedProposalsPerType(Map<String, List<ArgDescriptorProposal>> ret, Map<Pair<Integer, OpNamespace.ArgDescriptor.ArgType>, List<ArgDescriptorProposal>> dimensionProposals, String name) {
|
||||
List<ArgDescriptorProposal> dimensions = dimensionProposals.entrySet().stream().map(indexTypeAndList -> {
|
||||
if(indexTypeAndList.getValue().isEmpty()) {
|
||||
throw new IllegalStateException("Unable to compute aggregated proposals for an empty list");
|
||||
}
|
||||
OpNamespace.ArgDescriptor template = indexTypeAndList.getValue().get(0).getDescriptor();
|
||||
|
||||
int idx = indexTypeAndList.getKey().getFirst();
|
||||
OpNamespace.ArgDescriptor.ArgType type = indexTypeAndList.getKey().getRight();
|
||||
return Pair.of(indexTypeAndList.getKey(),
|
||||
ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("computedAggregate")
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgIndex(idx)
|
||||
.setArgType(type)
|
||||
.setName(name)
|
||||
.setIsArray(template.getIsArray() || idx < 0)
|
||||
.build())
|
||||
.proposalWeight(indexTypeAndList.getValue().stream()
|
||||
.collect(Collectors.summingDouble(input -> input.getProposalWeight()))
|
||||
).build());
|
||||
}).map(input -> input.getSecond()).
|
||||
collect(Collectors.toList());
|
||||
|
||||
|
||||
ret.put(name,dimensions);
|
||||
}
|
||||
|
||||
private static void extractProposals(Map<Pair<Integer, OpNamespace.ArgDescriptor.ArgType>, List<ArgDescriptorProposal>> inPlaceProposals, Map.Entry<String, List<ArgDescriptorProposal>> entry) {
|
||||
entry.getValue().forEach(proposal -> {
|
||||
List<ArgDescriptorProposal> proposals = null;
|
||||
if (!inPlaceProposals.containsKey(extractKey(proposal))) {
|
||||
proposals = new ArrayList<>();
|
||||
inPlaceProposals.put(extractKey(proposal), proposals);
|
||||
} else {
|
||||
proposals = inPlaceProposals.get(extractKey(proposal));
|
||||
}
|
||||
|
||||
proposals.add(proposal);
|
||||
inPlaceProposals.put(extractKey(proposal), proposals);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a key reflecting index and type of arg descriptor
|
||||
* @param proposal the input proposal
|
||||
* @return
|
||||
*/
|
||||
public static Pair<Integer, OpNamespace.ArgDescriptor.ArgType> extractKey(ArgDescriptorProposal proposal) {
|
||||
return Pair.of(proposal.getDescriptor().getArgIndex(),proposal.getDescriptor().getArgType());
|
||||
}
|
||||
|
||||
|
||||
public static boolean proposalsAllSameType(List<ArgDescriptorProposal> proposals) {
|
||||
OpNamespace.ArgDescriptor.ArgType firstType = proposals.get(0).getDescriptor().getArgType();
|
||||
for(ArgDescriptorProposal proposal : proposals) {
|
||||
if(proposal.getDescriptor().getArgType() != firstType) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private static List<ArgDescriptorProposal> mergeProposals(Map<String, List<ArgDescriptorProposal>> ret, List<ArgDescriptorProposal> dimensionsList, OpNamespace.ArgDescriptor.ArgType argType, String nameOfArgDescriptor) {
|
||||
double priorityWeight = 0.0;
|
||||
ArgDescriptorProposal.ArgDescriptorProposalBuilder newProposalBuilder = ArgDescriptorProposal.builder();
|
||||
Counter<Integer> indexCounter = new Counter<>();
|
||||
List<ArgDescriptorProposal> proposalsOutsideType = new ArrayList<>();
|
||||
boolean allArrayType = true;
|
||||
for(ArgDescriptorProposal argDescriptorProposal : dimensionsList) {
|
||||
allArrayType = argDescriptorProposal.getDescriptor().getIsArray() && allArrayType;
|
||||
//handle arrays separately
|
||||
if(argDescriptorProposal.getDescriptor().getArgType() == argType) {
|
||||
indexCounter.incrementCount(argDescriptorProposal.getDescriptor().getArgIndex(),1);
|
||||
priorityWeight += argDescriptorProposal.getProposalWeight();
|
||||
} else if(argDescriptorProposal.getDescriptor().getArgType() != argType) {
|
||||
proposalsOutsideType.add(argDescriptorProposal);
|
||||
}
|
||||
}
|
||||
|
||||
dimensionsList.clear();
|
||||
//don't add a list if one is not present
|
||||
if(!indexCounter.isEmpty()) {
|
||||
newProposalBuilder
|
||||
.proposalWeight(priorityWeight)
|
||||
.descriptor(
|
||||
OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setName(nameOfArgDescriptor)
|
||||
.setArgType(argType)
|
||||
.setIsArray(allArrayType)
|
||||
.setArgIndex(indexCounter.argMax())
|
||||
.build());
|
||||
|
||||
dimensionsList.add(newProposalBuilder.build());
|
||||
ret.put(nameOfArgDescriptor, dimensionsList);
|
||||
}
|
||||
|
||||
//standardize the names
|
||||
proposalsOutsideType.forEach(proposalOutsideType -> {
|
||||
proposalOutsideType.setDescriptor(
|
||||
OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setName(nameOfArgDescriptor)
|
||||
.setArgType(proposalOutsideType.getDescriptor().getArgType())
|
||||
.setArgIndex(proposalOutsideType.getDescriptor().getArgIndex())
|
||||
.setIsArray(proposalOutsideType.getDescriptor().getIsArray())
|
||||
.setConvertBoolToInt(proposalOutsideType.getDescriptor().getConvertBoolToInt())
|
||||
.build()
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
return proposalsOutsideType;
|
||||
}
|
||||
|
||||
|
||||
public static boolean matchesArrayArgDeclaration(String testLine) {
|
||||
boolean ret = Pattern.matches(ARRAY_ASSIGNMENT,testLine);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static boolean matchesArgDeclaration(String argType,String testLine) {
|
||||
Matcher matcher = Pattern.compile(argType + ARGUMENT_ENDING_PATTERN).matcher(testLine);
|
||||
Matcher argOnly = Pattern.compile(argType + ARGUMENT_PATTERN).matcher(testLine);
|
||||
// Matcher arrArg = Pattern.compile(argType + ARGUMENT_PATTERN)
|
||||
boolean ret = matcher.find();
|
||||
boolean argOnlyResult = argOnly.find();
|
||||
return ret || testLine.contains("?") && argOnlyResult
|
||||
|| testLine.contains("static_cast") && argOnlyResult
|
||||
|| (testLine.contains("))") && argOnlyResult && !testLine.contains("if") && !testLine.contains("REQUIRE_TRUE")) && !testLine.contains("->rankOf()")
|
||||
|| (testLine.contains("==") && argOnlyResult && !testLine.contains("if") && !testLine.contains("REQUIRE_TRUE")) && !testLine.contains("->rankOf()")
|
||||
|| (testLine.contains("(" + argType) && argOnlyResult && !testLine.contains("if") && !testLine.contains("REQUIRE_TRUE")) && !testLine.contains("->rankOf()")
|
||||
|| (testLine.contains("->") && argOnlyResult && !testLine.contains("if") && !testLine.contains("REQUIRE_TRUE")) && !testLine.contains("->rankOf()");
|
||||
}
|
||||
|
||||
}
|
||||
+999
@@ -0,0 +1,999 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.descriptor.proposal.impl;
|
||||
|
||||
import com.github.javaparser.ParserConfiguration;
|
||||
import com.github.javaparser.StaticJavaParser;
|
||||
import com.github.javaparser.ast.CompilationUnit;
|
||||
import com.github.javaparser.ast.body.ConstructorDeclaration;
|
||||
import com.github.javaparser.ast.body.FieldDeclaration;
|
||||
import com.github.javaparser.ast.expr.MethodCallExpr;
|
||||
import com.github.javaparser.resolution.declarations.ResolvedConstructorDeclaration;
|
||||
import com.github.javaparser.resolution.declarations.ResolvedFieldDeclaration;
|
||||
import com.github.javaparser.resolution.declarations.ResolvedParameterDeclaration;
|
||||
import com.github.javaparser.symbolsolver.JavaSymbolSolver;
|
||||
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
|
||||
import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver;
|
||||
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
|
||||
import com.github.javaparser.utils.Log;
|
||||
import com.github.javaparser.utils.SourceRoot;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import org.nd4j.autodiff.functions.DifferentialFunction;
|
||||
import org.nd4j.autodiff.samediff.SDVariable;
|
||||
import org.nd4j.common.primitives.Counter;
|
||||
import org.nd4j.common.primitives.CounterMap;
|
||||
import org.nd4j.common.primitives.Pair;
|
||||
import org.nd4j.descriptor.proposal.ArgDescriptorProposal;
|
||||
import org.nd4j.descriptor.proposal.ArgDescriptorSource;
|
||||
import org.nd4j.ir.OpNamespace;
|
||||
import org.nd4j.linalg.api.ndarray.INDArray;
|
||||
import org.nd4j.linalg.api.ops.*;
|
||||
import org.nd4j.linalg.api.ops.impl.indexaccum.custom.ArgMax;
|
||||
import org.nd4j.linalg.api.ops.impl.indexaccum.custom.ArgMin;
|
||||
import org.nd4j.linalg.api.ops.impl.transforms.BaseDynamicTransformOp;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.nd4j.descriptor.proposal.impl.ArgDescriptorParserUtils.*;
|
||||
|
||||
public class JavaSourceArgDescriptorSource implements ArgDescriptorSource {
|
||||
|
||||
|
||||
private SourceRoot sourceRoot;
|
||||
private File nd4jOpsRootDir;
|
||||
private double weight;
|
||||
|
||||
/**
|
||||
* void addTArgument(double... arg);
|
||||
*
|
||||
* void addIArgument(int... arg);
|
||||
*
|
||||
* void addIArgument(long... arg);
|
||||
*
|
||||
* void addBArgument(boolean... arg);
|
||||
*
|
||||
* void addDArgument(DataType... arg);
|
||||
*/
|
||||
|
||||
public final static String ADD_T_ARGUMENT_INVOCATION = "addTArgument";
|
||||
public final static String ADD_I_ARGUMENT_INVOCATION = "addIArgument";
|
||||
public final static String ADD_B_ARGUMENT_INVOCATION = "addBArgument";
|
||||
public final static String ADD_D_ARGUMENT_INVOCATION = "addDArgument";
|
||||
public final static String ADD_INPUT_ARGUMENT = "addInputArgument";
|
||||
public final static String ADD_OUTPUT_ARGUMENT = "addOutputArgument";
|
||||
@Getter
|
||||
private Map<String, OpNamespace.OpDescriptor.OpDeclarationType> opTypes;
|
||||
static {
|
||||
Log.setAdapter(new Log.StandardOutStandardErrorAdapter());
|
||||
|
||||
}
|
||||
|
||||
@Builder
|
||||
public JavaSourceArgDescriptorSource(File nd4jApiRootDir,double weight) {
|
||||
this.sourceRoot = initSourceRoot(nd4jApiRootDir);
|
||||
this.nd4jOpsRootDir = nd4jApiRootDir;
|
||||
if(opTypes == null) {
|
||||
opTypes = new HashMap<>();
|
||||
}
|
||||
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
public Map<String, List<ArgDescriptorProposal>> doReflectionsExtraction() {
|
||||
Map<String, List<ArgDescriptorProposal>> ret = new HashMap<>();
|
||||
|
||||
Reflections reflections = new Reflections("org.nd4j");
|
||||
Set<Class<? extends DifferentialFunction>> subTypesOf = reflections.getSubTypesOf(DifferentialFunction.class);
|
||||
Set<Class<? extends CustomOp>> subTypesOfOp = reflections.getSubTypesOf(CustomOp.class);
|
||||
Set<Class<?>> allClasses = new HashSet<>();
|
||||
allClasses.addAll(subTypesOf);
|
||||
allClasses.addAll(subTypesOfOp);
|
||||
Set<String> opNamesForDifferentialFunction = new HashSet<>();
|
||||
|
||||
|
||||
for(Class<?> clazz : allClasses) {
|
||||
if(Modifier.isAbstract(clazz.getModifiers()) || clazz.isInterface()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
processClazz(ret, opNamesForDifferentialFunction, clazz);
|
||||
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void processClazz(Map<String, List<ArgDescriptorProposal>> ret, Set<String> opNamesForDifferentialFunction, Class<?> clazz) {
|
||||
try {
|
||||
Object funcInstance = clazz.newInstance();
|
||||
String name = null;
|
||||
|
||||
if(funcInstance instanceof DifferentialFunction) {
|
||||
DifferentialFunction differentialFunction = (DifferentialFunction) funcInstance;
|
||||
name = differentialFunction.opName();
|
||||
} else if(funcInstance instanceof CustomOp) {
|
||||
CustomOp customOp = (CustomOp) funcInstance;
|
||||
name = customOp.opName();
|
||||
}
|
||||
|
||||
|
||||
if(name == null)
|
||||
return;
|
||||
opNamesForDifferentialFunction.add(name);
|
||||
if(!(funcInstance instanceof DynamicCustomOp))
|
||||
opTypes.put(name,OpNamespace.OpDescriptor.OpDeclarationType.LEGACY_XYZ);
|
||||
else
|
||||
opTypes.put(name,OpNamespace.OpDescriptor.OpDeclarationType.CUSTOM_OP_IMPL);
|
||||
|
||||
|
||||
String fileName = clazz.getName().replace(".",File.separator);
|
||||
StringBuilder fileBuilder = new StringBuilder();
|
||||
fileBuilder.append(fileName);
|
||||
fileBuilder.append(".java");
|
||||
CounterMap<Pair<String, OpNamespace.ArgDescriptor.ArgType>,Integer> paramIndicesCount = new CounterMap<>();
|
||||
|
||||
// Our sample is in the root of this directory, so no package name.
|
||||
CompilationUnit cu = sourceRoot.parse(clazz.getPackage().getName(), clazz.getSimpleName() + ".java");
|
||||
cu.findAll(MethodCallExpr.class).forEach(method -> {
|
||||
String methodInvoked = method.getNameAsString();
|
||||
final AtomicInteger indexed = new AtomicInteger(0);
|
||||
//need to figure out how to consolidate multiple method calls
|
||||
//as well as the right indices
|
||||
//typical patterns in the code base will reflect adding arguments all at once
|
||||
//one thing we can just check for is if more than 1 argument is passed in and
|
||||
//treat that as a complete list of arguments
|
||||
if(methodInvoked.equals(ADD_T_ARGUMENT_INVOCATION)) {
|
||||
method.getArguments().forEach(argument -> {
|
||||
if(argument.isNameExpr())
|
||||
paramIndicesCount.incrementCount(Pair.of(argument.asNameExpr().getNameAsString(), OpNamespace.ArgDescriptor.ArgType.DOUBLE),indexed.get(),100.0);
|
||||
else if(argument.isMethodCallExpr()) {
|
||||
if(argument.asMethodCallExpr().getName().toString().equals("ordinal")) {
|
||||
paramIndicesCount.incrementCount(Pair.of(argument.asNameExpr().getNameAsString(), OpNamespace.ArgDescriptor.ArgType.DOUBLE),indexed.get(),100.0);
|
||||
|
||||
}
|
||||
}
|
||||
indexed.incrementAndGet();
|
||||
});
|
||||
} else if(methodInvoked.equals(ADD_B_ARGUMENT_INVOCATION)) {
|
||||
method.getArguments().forEach(argument -> {
|
||||
if(argument.isNameExpr())
|
||||
paramIndicesCount.incrementCount(Pair.of(argument.asNameExpr().getNameAsString(), OpNamespace.ArgDescriptor.ArgType.BOOL),indexed.get(),100.0);
|
||||
else if(argument.isMethodCallExpr()) {
|
||||
if(argument.asMethodCallExpr().getName().equals("ordinal")) {
|
||||
paramIndicesCount.incrementCount(Pair.of(argument.asNameExpr().getNameAsString(), OpNamespace.ArgDescriptor.ArgType.BOOL),indexed.get(),100.0);
|
||||
}
|
||||
}
|
||||
indexed.incrementAndGet();
|
||||
});
|
||||
} else if(methodInvoked.equals(ADD_I_ARGUMENT_INVOCATION)) {
|
||||
method.getArguments().forEach(argument -> {
|
||||
if(argument.isNameExpr())
|
||||
paramIndicesCount.incrementCount(Pair.of(argument.asNameExpr().getNameAsString(), OpNamespace.ArgDescriptor.ArgType.INT64),indexed.get(),100.0);
|
||||
else if(argument.isMethodCallExpr()) {
|
||||
if(argument.asMethodCallExpr().getName().toString().equals("ordinal")) {
|
||||
paramIndicesCount.incrementCount(Pair.of(argument.toString().replace(".ordinal()",""), OpNamespace.ArgDescriptor.ArgType.INT64),indexed.get(),100.0);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
indexed.incrementAndGet();
|
||||
});
|
||||
} else if(methodInvoked.equals(ADD_D_ARGUMENT_INVOCATION)) {
|
||||
method.getArguments().forEach(argument -> {
|
||||
if(argument.isNameExpr())
|
||||
paramIndicesCount.incrementCount(Pair.of(argument.asNameExpr().getNameAsString(), OpNamespace.ArgDescriptor.ArgType.DATA_TYPE),indexed.get(),100.0);
|
||||
else if(argument.isMethodCallExpr()) {
|
||||
if(argument.asMethodCallExpr().getName().toString().equals("ordinal")) {
|
||||
paramIndicesCount.incrementCount(Pair.of(argument.toString().replace(".ordinal()",""), OpNamespace.ArgDescriptor.ArgType.DATA_TYPE),indexed.get(),100.0);
|
||||
|
||||
}
|
||||
}
|
||||
indexed.incrementAndGet();
|
||||
});
|
||||
} else if(methodInvoked.equals(ADD_INPUT_ARGUMENT)) {
|
||||
method.getArguments().forEach(argument -> {
|
||||
if(argument.isNameExpr())
|
||||
paramIndicesCount.incrementCount(Pair.of(argument.asNameExpr().getNameAsString(), OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR),indexed.get(),100.0);
|
||||
else if(argument.isMethodCallExpr()) {
|
||||
if(argument.asMethodCallExpr().getName().toString().equals("ordinal")) {
|
||||
paramIndicesCount.incrementCount(Pair.of(argument.toString().replace(".ordinal()",""), OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR),indexed.get(),100.0);
|
||||
|
||||
}
|
||||
}
|
||||
indexed.incrementAndGet();
|
||||
});
|
||||
} else if(methodInvoked.equals(ADD_OUTPUT_ARGUMENT)) {
|
||||
method.getArguments().forEach(argument -> {
|
||||
if(argument.isNameExpr())
|
||||
paramIndicesCount.incrementCount(Pair.of(argument.asNameExpr().getNameAsString(), OpNamespace.ArgDescriptor.ArgType.OUTPUT_TENSOR),indexed.get(),100.0);
|
||||
else if(argument.isMethodCallExpr()) {
|
||||
if(argument.asMethodCallExpr().getName().toString().equals("ordinal")) {
|
||||
paramIndicesCount.incrementCount(Pair.of(argument.toString().replace(".ordinal()",""), OpNamespace.ArgDescriptor.ArgType.OUTPUT_TENSOR),indexed.get(),100.0);
|
||||
|
||||
}
|
||||
}
|
||||
indexed.incrementAndGet();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
List<ResolvedConstructorDeclaration> collect = cu.findAll(ConstructorDeclaration.class).stream()
|
||||
.map(input -> input.resolve())
|
||||
.filter(constructor -> constructor.getNumberOfParams() > 0)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
//only process final constructor with all arguments for indexing purposes
|
||||
Counter<ResolvedConstructorDeclaration> constructorArgCount = new Counter<>();
|
||||
collect.stream().filter(input -> input != null).forEach(constructor -> {
|
||||
constructorArgCount.incrementCount(constructor,constructor.getNumberOfParams());
|
||||
});
|
||||
|
||||
if(constructorArgCount.argMax() != null)
|
||||
collect = Arrays.asList(constructorArgCount.argMax());
|
||||
|
||||
List<ArgDescriptorProposal> argDescriptorProposals = ret.get(name);
|
||||
if(argDescriptorProposals == null) {
|
||||
argDescriptorProposals = new ArrayList<>();
|
||||
ret.put(name,argDescriptorProposals);
|
||||
}
|
||||
|
||||
Set<ResolvedParameterDeclaration> parameters = new LinkedHashSet<>();
|
||||
|
||||
int floatIdx = 0;
|
||||
int inputIdx = 0;
|
||||
int outputIdx = 0;
|
||||
int intIdx = 0;
|
||||
int boolIdx = 0;
|
||||
int dTypeIndex = 0;
|
||||
|
||||
for(ResolvedConstructorDeclaration parameterDeclaration : collect) {
|
||||
floatIdx = 0;
|
||||
inputIdx = 0;
|
||||
outputIdx = 0;
|
||||
intIdx = 0;
|
||||
boolIdx = 0;
|
||||
dTypeIndex = 0;
|
||||
for(int i = 0; i < parameterDeclaration.getNumberOfParams(); i++) {
|
||||
ResolvedParameterDeclaration param = parameterDeclaration.getParam(i);
|
||||
OpNamespace.ArgDescriptor.ArgType argType = argTypeForParam(param);
|
||||
if(isValidParam(param)) {
|
||||
parameters.add(param);
|
||||
switch(argType) {
|
||||
case INPUT_TENSOR:
|
||||
paramIndicesCount.incrementCount(Pair.of(param.getName(),argType), inputIdx, 100.0);
|
||||
inputIdx++;
|
||||
break;
|
||||
case INT64:
|
||||
case INT32:
|
||||
paramIndicesCount.incrementCount(Pair.of(param.getName(), OpNamespace.ArgDescriptor.ArgType.INT64), intIdx, 100.0);
|
||||
intIdx++;
|
||||
break;
|
||||
case DATA_TYPE:
|
||||
paramIndicesCount.incrementCount(Pair.of(param.getName(), OpNamespace.ArgDescriptor.ArgType.DATA_TYPE), dTypeIndex, 100.0);
|
||||
dTypeIndex++;
|
||||
break;
|
||||
case DOUBLE:
|
||||
case FLOAT:
|
||||
paramIndicesCount.incrementCount(Pair.of(param.getName(), OpNamespace.ArgDescriptor.ArgType.FLOAT), floatIdx, 100.0);
|
||||
paramIndicesCount.incrementCount(Pair.of(param.getName(), OpNamespace.ArgDescriptor.ArgType.DOUBLE), floatIdx, 100.0);
|
||||
floatIdx++;
|
||||
break;
|
||||
case BOOL:
|
||||
paramIndicesCount.incrementCount(Pair.of(param.getName(),argType), boolIdx, 100.0);
|
||||
boolIdx++;
|
||||
break;
|
||||
case OUTPUT_TENSOR:
|
||||
paramIndicesCount.incrementCount(Pair.of(param.getName(),argType), outputIdx, 100.0);
|
||||
outputIdx++;
|
||||
break;
|
||||
case UNRECOGNIZED:
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
floatIdx = 0;
|
||||
inputIdx = 0;
|
||||
outputIdx = 0;
|
||||
intIdx = 0;
|
||||
boolIdx = 0;
|
||||
Set<List<Pair<String, String>>> typesAndParams = parameters.stream().map(collectedParam ->
|
||||
Pair.of(collectedParam.describeType(), collectedParam.getName()))
|
||||
.collect(Collectors.groupingBy(input -> input.getSecond())).entrySet()
|
||||
.stream()
|
||||
.map(inputPair -> inputPair.getValue())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
|
||||
Set<String> constructorNamesEncountered = new HashSet<>();
|
||||
List<ArgDescriptorProposal> finalArgDescriptorProposals = argDescriptorProposals;
|
||||
typesAndParams.forEach(listOfTypesAndNames -> {
|
||||
|
||||
listOfTypesAndNames.forEach(parameter -> {
|
||||
if(typeNameOrArrayOfTypeNameMatches(parameter.getFirst(),SDVariable.class.getName(),INDArray.class.getName())) {
|
||||
constructorNamesEncountered.add(parameter.getValue());
|
||||
if(outputNames.contains(parameter.getValue())) {
|
||||
Counter<Integer> counter = paramIndicesCount.getCounter(Pair.of(parameter.getSecond(), OpNamespace.ArgDescriptor.ArgType.OUTPUT_TENSOR));
|
||||
if(counter != null)
|
||||
finalArgDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.proposalWeight(99.0 * (counter == null ? 1 : counter.size()))
|
||||
.sourceOfProposal("java")
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.OUTPUT_TENSOR)
|
||||
.setName(parameter.getSecond())
|
||||
.setIsArray(parameter.getFirst().contains("[]") || parameter.getFirst().contains("..."))
|
||||
.setArgIndex(counter.argMax())
|
||||
.build()).build());
|
||||
|
||||
} else {
|
||||
Counter<Integer> counter = paramIndicesCount.getCounter(Pair.of(parameter.getSecond(), OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR));
|
||||
if(counter != null)
|
||||
finalArgDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.proposalWeight(99.0 * (counter == null ? 1 : counter.size()))
|
||||
.sourceOfProposal("java")
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR)
|
||||
.setName(parameter.getSecond())
|
||||
.setIsArray(parameter.getFirst().contains("[]") || parameter.getFirst().contains("..."))
|
||||
.setArgIndex(counter.argMax())
|
||||
.build()).build());
|
||||
}
|
||||
} else if(typeNameOrArrayOfTypeNameMatches(parameter.getFirst(),int.class.getName(),long.class.getName(),Integer.class.getName(),Long.class.getName()) || paramIsEnum(parameter.getFirst())) {
|
||||
constructorNamesEncountered.add(parameter.getValue());
|
||||
|
||||
Counter<Integer> counter = paramIndicesCount.getCounter(Pair.of(parameter.getSecond(), OpNamespace.ArgDescriptor.ArgType.INT64));
|
||||
if(counter != null)
|
||||
finalArgDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(99.0 * (counter == null ? 1 : counter.size()))
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INT64)
|
||||
.setName(parameter.getSecond())
|
||||
.setIsArray(parameter.getFirst().contains("[]") || parameter.getFirst().contains("..."))
|
||||
.setArgIndex(counter.argMax())
|
||||
.build()).build());
|
||||
} else if(typeNameOrArrayOfTypeNameMatches(parameter.getFirst(),float.class.getName(),double.class.getName(),Float.class.getName(),Double.class.getName())) {
|
||||
constructorNamesEncountered.add(parameter.getValue());
|
||||
Counter<Integer> counter = paramIndicesCount.getCounter(Pair.of(parameter.getSecond(), OpNamespace.ArgDescriptor.ArgType.FLOAT));
|
||||
if(counter != null)
|
||||
finalArgDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(99.0 * (counter == null ? 1 :(counter == null ? 1 : counter.size()) ))
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.DOUBLE)
|
||||
.setName(parameter.getSecond())
|
||||
.setIsArray(parameter.getFirst().contains("[]"))
|
||||
.setArgIndex(counter.argMax())
|
||||
.build()).build());
|
||||
} else if(typeNameOrArrayOfTypeNameMatches(parameter.getFirst(),boolean.class.getName(),Boolean.class.getName())) {
|
||||
constructorNamesEncountered.add(parameter.getValue());
|
||||
Counter<Integer> counter = paramIndicesCount.getCounter(Pair.of(parameter.getSecond(), OpNamespace.ArgDescriptor.ArgType.BOOL));
|
||||
if(counter != null)
|
||||
finalArgDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(99.0 * (counter == null ? 1 :(counter == null ? 1 : counter.size()) ))
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.BOOL)
|
||||
.setName(parameter.getSecond())
|
||||
.setIsArray(parameter.getFirst().contains("[]"))
|
||||
.setArgIndex(counter.argMax())
|
||||
.build()).build());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
List<ResolvedFieldDeclaration> fields = cu.findAll(FieldDeclaration.class).stream()
|
||||
.map(input -> getResolve(input))
|
||||
//filter fields
|
||||
.filter(input -> input != null && !input.isStatic())
|
||||
.collect(Collectors.toList());
|
||||
floatIdx = 0;
|
||||
inputIdx = 0;
|
||||
outputIdx = 0;
|
||||
intIdx = 0;
|
||||
boolIdx = 0;
|
||||
|
||||
for(ResolvedFieldDeclaration field : fields) {
|
||||
if(!constructorNamesEncountered.contains(field.getName()) && typeNameOrArrayOfTypeNameMatches(field.getType().describe(),SDVariable.class.getName(),INDArray.class.getName())) {
|
||||
if(outputNames.contains(field.getName())) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(99.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.OUTPUT_TENSOR)
|
||||
.setName(field.getName())
|
||||
.setIsArray(field.getType().describe().contains("[]"))
|
||||
.setArgIndex(outputIdx)
|
||||
.build()).build());
|
||||
outputIdx++;
|
||||
} else if(!constructorNamesEncountered.contains(field.getName())){
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(99.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR)
|
||||
.setName(field.getName())
|
||||
.setIsArray(field.getType().describe().contains("[]"))
|
||||
.setArgIndex(inputIdx)
|
||||
.build()).build());
|
||||
inputIdx++;
|
||||
}
|
||||
} else if(!constructorNamesEncountered.contains(field.getName()) && typeNameOrArrayOfTypeNameMatches(field.getType().describe(),int.class.getName(),long.class.getName(),Long.class.getName(),Integer.class.getName())) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(99.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INT64)
|
||||
.setName(field.getName())
|
||||
.setIsArray(field.getType().describe().contains("[]"))
|
||||
.setArgIndex(intIdx)
|
||||
.build()).build());
|
||||
intIdx++;
|
||||
} else if(!constructorNamesEncountered.contains(field.getName()) && typeNameOrArrayOfTypeNameMatches(field.getType().describe(),double.class.getName(),float.class.getName(),Double.class.getName(),Float.class.getName())) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(99.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.DOUBLE)
|
||||
.setName(field.getName())
|
||||
.setIsArray(field.getType().describe().contains("[]"))
|
||||
.setArgIndex(floatIdx)
|
||||
.build()).build());
|
||||
floatIdx++;
|
||||
} else if(!constructorNamesEncountered.contains(field.getName()) && typeNameOrArrayOfTypeNameMatches(field.getType().describe(),Boolean.class.getName(),boolean.class.getName())) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(99.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.BOOL)
|
||||
.setName(field.getName())
|
||||
.setIsArray(field.getType().describe().contains("[]"))
|
||||
.setArgIndex(boolIdx)
|
||||
.build()).build());
|
||||
boolIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
if(funcInstance instanceof BaseReduceOp ||
|
||||
funcInstance instanceof BaseReduceBoolOp || funcInstance instanceof BaseReduceSameOp) {
|
||||
if(!containsProposalWithDescriptorName("keepDims",argDescriptorProposals)) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(9999.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.BOOL)
|
||||
.setName("keepDims")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(boolIdx)
|
||||
.build()).build());
|
||||
|
||||
|
||||
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(9999.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INT64)
|
||||
.setName("dimensions")
|
||||
.setIsArray(true)
|
||||
.setArgIndex(intIdx)
|
||||
.build()).build());
|
||||
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(9999.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR)
|
||||
.setName("dimensions")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(1)
|
||||
.build()).build());
|
||||
}
|
||||
|
||||
|
||||
if(funcInstance instanceof ArgMax || funcInstance instanceof ArgMin) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(99999.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INT64)
|
||||
.setName("dimensions")
|
||||
.setIsArray(true)
|
||||
.setArgIndex(intIdx)
|
||||
.build()).build());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if(!containsProposalWithDescriptorName("dimensions",argDescriptorProposals)) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(9999.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INT64)
|
||||
.setName("dimensions")
|
||||
.setIsArray(true)
|
||||
.setArgIndex(0)
|
||||
.build()).build());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(funcInstance instanceof BaseTransformBoolOp) {
|
||||
BaseTransformBoolOp baseTransformBoolOp = (BaseTransformBoolOp) funcInstance;
|
||||
if(baseTransformBoolOp.getOpType() == Op.Type.PAIRWISE_BOOL) {
|
||||
if(numProposalsWithType(OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR,argDescriptorProposals) < 2) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(9999.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR)
|
||||
.setName("y")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(1)
|
||||
.build()).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(funcInstance instanceof BaseDynamicTransformOp) {
|
||||
if(!containsProposalWithDescriptorName("inPlace",argDescriptorProposals)) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(9999.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.BOOL)
|
||||
.setName("inPlace")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(boolIdx)
|
||||
.build()).build());
|
||||
}
|
||||
}
|
||||
|
||||
//hard coded case, impossible to parse from as the code exists today, and it doesn't exist anywhere in the libnd4j code base
|
||||
if(name.contains("maxpool2d")) {
|
||||
if(!containsProposalWithDescriptorName("extraParam0",argDescriptorProposals)) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("extraParam0")
|
||||
.proposalWeight(9999.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INT64)
|
||||
.setName("extraParam0")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(9)
|
||||
.build()).build());
|
||||
}
|
||||
}
|
||||
|
||||
if(name.contains("scatter_update")) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(Double.MAX_VALUE)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR)
|
||||
.setName("indices")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(2)
|
||||
.build()).build());
|
||||
|
||||
}
|
||||
|
||||
|
||||
if(name.contains("fill")) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(Double.MAX_VALUE)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR)
|
||||
.setName("shape")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(0)
|
||||
.build()).build());
|
||||
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(Double.MAX_VALUE)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR)
|
||||
.setName("result")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(1)
|
||||
.build()).build());
|
||||
|
||||
}
|
||||
|
||||
if(name.contains("loop_cond")) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(9999.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR)
|
||||
.setName("input")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(0)
|
||||
.build()).build());
|
||||
|
||||
}
|
||||
|
||||
|
||||
if(name.equals("top_k")) {
|
||||
if(!containsProposalWithDescriptorName("sorted",argDescriptorProposals)) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("sorted")
|
||||
.proposalWeight(9999.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INT64)
|
||||
.setName("sorted")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(0)
|
||||
.build()).build());
|
||||
}
|
||||
}
|
||||
|
||||
//dummy output tensor
|
||||
if(name.equals("next_iteration")) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.proposalWeight(9999.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder().setArgIndex(0)
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.OUTPUT_TENSOR)
|
||||
.setName("output").build())
|
||||
.build());
|
||||
}
|
||||
|
||||
if(!containsOutputTensor(argDescriptorProposals)) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("z")
|
||||
.proposalWeight(9999.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.OUTPUT_TENSOR)
|
||||
.setName("z")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(0)
|
||||
.build()).build());
|
||||
}
|
||||
|
||||
if(name.equals("gather")) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("axis")
|
||||
.proposalWeight(Double.MAX_VALUE)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INT64)
|
||||
.setName("axis")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(0)
|
||||
.build()).build());
|
||||
}
|
||||
|
||||
if(name.equals("pow")) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("pow")
|
||||
.proposalWeight(Double.MAX_VALUE)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR)
|
||||
.setName("pow")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(1)
|
||||
.build()).build());
|
||||
}
|
||||
|
||||
if(name.equals("concat")) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("isDynamicAxis")
|
||||
.proposalWeight(Double.MAX_VALUE)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.BOOL)
|
||||
.setName("isDynamicAxis")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(0)
|
||||
.build()).build());
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("concatDimension")
|
||||
.proposalWeight(Double.MAX_VALUE)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR)
|
||||
.setName("isDynamicAxis")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(1)
|
||||
.build()).build());
|
||||
}
|
||||
|
||||
if(name.equals("merge")) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.proposalWeight(99999.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder().setArgIndex(0)
|
||||
.setIsArray(true)
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR)
|
||||
.setName("inputs").build())
|
||||
.build());
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(name.equals("split") || name.equals("split_v")) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("numSplit")
|
||||
.proposalWeight(Double.MAX_VALUE)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INT64)
|
||||
.setName("numSplit")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(0)
|
||||
.build()).build());
|
||||
}
|
||||
|
||||
if(name.equals("reshape")) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("shape")
|
||||
.proposalWeight(Double.MAX_VALUE)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INT64)
|
||||
.setName("shape")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(0)
|
||||
.build()).build());
|
||||
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("shape")
|
||||
.proposalWeight(Double.MAX_VALUE)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR)
|
||||
.setName("shape")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(1)
|
||||
.build()).build());
|
||||
|
||||
}
|
||||
|
||||
if(name.equals("create")) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(Double.MAX_VALUE)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.DATA_TYPE)
|
||||
.setName("outputType")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(0)
|
||||
.build()).build());
|
||||
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(Double.MAX_VALUE)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INT64)
|
||||
.setName("order")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(0)
|
||||
.build()).build());
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("java")
|
||||
.proposalWeight(Double.MAX_VALUE)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INT64)
|
||||
.setName("outputType")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(1)
|
||||
.build()).build());
|
||||
}
|
||||
|
||||
if(name.equals("eye")) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("numRows")
|
||||
.proposalWeight(Double.MAX_VALUE)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INT64)
|
||||
.setName("numRows")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(0)
|
||||
.build()).build());
|
||||
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("numCols")
|
||||
.proposalWeight(Double.MAX_VALUE)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INT64)
|
||||
.setName("numCols")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(1)
|
||||
.build()).build());
|
||||
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("batchDimension")
|
||||
.proposalWeight(Double.MAX_VALUE)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INT64)
|
||||
.setName("batchDimension")
|
||||
.setIsArray(true)
|
||||
.setArgIndex(2)
|
||||
.build()).build());
|
||||
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("dataType")
|
||||
.proposalWeight(Double.MAX_VALUE)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INT64)
|
||||
.setName("dataType")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(3)
|
||||
.build()).build());
|
||||
|
||||
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("dataType")
|
||||
.proposalWeight(Double.MAX_VALUE)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.DOUBLE)
|
||||
.setName("dataType")
|
||||
.setIsArray(true)
|
||||
.setArgIndex(0)
|
||||
.build()).build());
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(name.equals("bincount")) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("cpp")
|
||||
.proposalWeight(Double.MAX_VALUE)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.DATA_TYPE)
|
||||
.setName("outputType")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(0)
|
||||
.build()).build());
|
||||
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("cpp")
|
||||
.proposalWeight(Double.POSITIVE_INFINITY)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR)
|
||||
.setName("values")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(0)
|
||||
.build()).build());
|
||||
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("cpp")
|
||||
.proposalWeight(Double.POSITIVE_INFINITY)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR)
|
||||
.setName("weights")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(1)
|
||||
.build()).build());
|
||||
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("cpp")
|
||||
.proposalWeight(Double.POSITIVE_INFINITY)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR)
|
||||
.setName("min")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(2)
|
||||
.build()).build());
|
||||
|
||||
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.sourceOfProposal("cpp")
|
||||
.proposalWeight(Double.POSITIVE_INFINITY)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder()
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.INPUT_TENSOR)
|
||||
.setName("max")
|
||||
.setIsArray(false)
|
||||
.setArgIndex(3)
|
||||
.build()).build());
|
||||
|
||||
}
|
||||
|
||||
if(name.equals("while") || name.equals("enter") || name.equals("exit") || name.equals("next_iteration")
|
||||
|| name.equals("loop_cond") || name.equals("switch") || name.equals("While")) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.proposalWeight(9999.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder().setArgIndex(0)
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.STRING)
|
||||
.setName("frameName").build())
|
||||
.build());
|
||||
}
|
||||
|
||||
if(name.equals("resize_bilinear")) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.proposalWeight(99999.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder().setArgIndex(0)
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.BOOL)
|
||||
.setName("alignCorners").build())
|
||||
.build());
|
||||
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.proposalWeight(99999.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder().setArgIndex(1)
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.BOOL)
|
||||
.setName("halfPixelCenters").build())
|
||||
.build());
|
||||
}
|
||||
|
||||
if(funcInstance instanceof BaseTransformSameOp || funcInstance instanceof BaseTransformOp || funcInstance instanceof BaseDynamicTransformOp) {
|
||||
argDescriptorProposals.add(ArgDescriptorProposal.builder()
|
||||
.proposalWeight(9999.0)
|
||||
.descriptor(OpNamespace.ArgDescriptor.newBuilder().setArgIndex(0)
|
||||
.setArgType(OpNamespace.ArgDescriptor.ArgType.DATA_TYPE)
|
||||
.setName("dataType").build())
|
||||
.build());
|
||||
}
|
||||
|
||||
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static ResolvedFieldDeclaration getResolve(FieldDeclaration input) {
|
||||
try {
|
||||
return input.resolve();
|
||||
}catch(Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private SourceRoot initSourceRoot(File nd4jApiRootDir) {
|
||||
CombinedTypeSolver typeSolver = new CombinedTypeSolver();
|
||||
typeSolver.add(new ReflectionTypeSolver(false));
|
||||
typeSolver.add(new JavaParserTypeSolver(nd4jApiRootDir));
|
||||
JavaSymbolSolver symbolSolver = new JavaSymbolSolver(typeSolver);
|
||||
StaticJavaParser.getConfiguration().setSymbolResolver(symbolSolver);
|
||||
SourceRoot sourceRoot = new SourceRoot(nd4jApiRootDir.toPath(),new ParserConfiguration().setSymbolResolver(symbolSolver));
|
||||
return sourceRoot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<ArgDescriptorProposal>> getProposals() {
|
||||
return doReflectionsExtraction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OpNamespace.OpDescriptor.OpDeclarationType typeFor(String name) {
|
||||
return opTypes.get(name);
|
||||
}
|
||||
}
|
||||
+2468
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
Onnx op definition loading
|
||||
---------------------------------
|
||||
|
||||
Setup
|
||||
-------
|
||||
Use anaconda and install onnx:
|
||||
```
|
||||
conda install onnx
|
||||
```
|
||||
|
||||
Generate a file
|
||||
---------------------
|
||||
```
|
||||
python onnx_def_gen.py
|
||||
```
|
||||
|
||||
This will generate a file with all op definitions
|
||||
loadable as NodeProto in onnx serialized as a text file
|
||||
split by --\n.
|
||||
@@ -0,0 +1,82 @@
|
||||
import onnx
|
||||
from onnx import version_converter, helper, ModelProto
|
||||
|
||||
|
||||
# Referenced from: https://github.com/onnx/onnx/issues/2660#issuecomment-605874784
|
||||
def add_value_info_for_constants(model : onnx.ModelProto):
|
||||
"""
|
||||
Currently onnx.shape_inference doesn't use the shape of initializers, so add
|
||||
that info explicitly as ValueInfoProtos.
|
||||
Mutates the model.
|
||||
Args:
|
||||
model: The ModelProto to update.
|
||||
"""
|
||||
# All (top-level) constants will have ValueInfos before IRv4 as they are all inputs
|
||||
if model.ir_version < 4:
|
||||
return
|
||||
|
||||
def add_const_value_infos_to_graph(graph : onnx.GraphProto):
|
||||
inputs = {i.name for i in graph.input}
|
||||
existing_info = {vi.name: vi for vi in graph.value_info}
|
||||
for init in graph.initializer:
|
||||
# Check it really is a constant, not an input
|
||||
if init.name in inputs:
|
||||
continue
|
||||
|
||||
# The details we want to add
|
||||
elem_type = init.data_type
|
||||
shape = init.dims
|
||||
|
||||
# Get existing or create new value info for this constant
|
||||
vi = existing_info.get(init.name)
|
||||
if vi is None:
|
||||
vi = graph.value_info.add()
|
||||
vi.name = init.name
|
||||
|
||||
# Even though it would be weird, we will not overwrite info even if it doesn't match
|
||||
tt = vi.type.tensor_type
|
||||
if tt.elem_type == onnx.TensorProto.UNDEFINED:
|
||||
tt.elem_type = elem_type
|
||||
if not tt.HasField("shape"):
|
||||
# Ensure we set an empty list if the const is scalar (zero dims)
|
||||
tt.shape.dim.extend([])
|
||||
for dim in shape:
|
||||
tt.shape.dim.add().dim_value = dim
|
||||
graph_input = graph.input.add()
|
||||
graph_input.name = vi.name
|
||||
graph_input.type.tensor_type.elem_type = elem_type
|
||||
|
||||
|
||||
# Handle subgraphs
|
||||
for node in graph.node:
|
||||
for attr in node.attribute:
|
||||
# Ref attrs refer to other attrs, so we don't need to do anything
|
||||
if attr.ref_attr_name != "":
|
||||
continue
|
||||
|
||||
if attr.type == onnx.AttributeProto.GRAPH:
|
||||
add_const_value_infos_to_graph(attr.g)
|
||||
if attr.type == onnx.AttributeProto.GRAPHS:
|
||||
for g in attr.graphs:
|
||||
add_const_value_infos_to_graph(g)
|
||||
|
||||
|
||||
return add_const_value_infos_to_graph(model.graph)
|
||||
|
||||
def summarize_model(input: ModelProto):
|
||||
return f'Inputs {len(input.graph.input)} Nodes {len(input.graph.node)} Initializer {len(input.graph.initializer)} Value info {len(input.graph.value_info)}'
|
||||
|
||||
model = onnx.load('C:\\Users\\agibs\\Downloads\\V9\\V9\\best_bracket.onnx')
|
||||
kotlin_model = onnx.load('C:\\Users\\agibs\\Documents\\GitHub\\dl4j-PR-split\\deeplearning4j\\nd4j\\samediff-import\\samediff-import-onnx\\input-adjusted-model.onnx')
|
||||
input_names_2 = [node.name for node in kotlin_model.graph.node]
|
||||
input_init__names_2 = [initializer.name for initializer in kotlin_model.graph.initializer]
|
||||
#model = onnx.shape_inference.infer_shapes(model)
|
||||
add_value_info_for_constants(model)
|
||||
input_names = [node.name for node in model.graph.node]
|
||||
input_init__names = [initializer.name for initializer in model.graph.initializer]
|
||||
input_val_info__names = [value_info.name for value_info in model.graph.value_info]
|
||||
converted_model = version_converter.convert_version(kotlin_model, 13)
|
||||
converted_input_val_info__names = [value_info.name for value_info in converted_model.graph.value_info]
|
||||
converted_node_names = [node.name for node in converted_model.graph.node]
|
||||
onnx.save(converted_model,'output.onnx')
|
||||
print('Converted model')
|
||||
@@ -0,0 +1,137 @@
|
||||
# /* ******************************************************************************
|
||||
# *
|
||||
# *
|
||||
# * This program and the accompanying materials are made available under the
|
||||
# * terms of the Apache License, Version 2.0 which is available at
|
||||
# * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
# *
|
||||
# * See the NOTICE file distributed with this work for additional
|
||||
# * information regarding copyright ownership.
|
||||
# * Unless required by applicable law or agreed to in writing, software
|
||||
# * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# * License for the specific language governing permissions and limitations
|
||||
# * under the License.
|
||||
# *
|
||||
# * SPDX-License-Identifier: Apache-2.0
|
||||
# ******************************************************************************/
|
||||
|
||||
from onnx.defs import get_all_schemas
|
||||
from onnx import NodeProto,GraphProto
|
||||
from google.protobuf import text_format
|
||||
import onnx.helper
|
||||
|
||||
|
||||
nodes = []
|
||||
schemas = get_all_schemas()
|
||||
|
||||
|
||||
def load_node(input_str):
|
||||
"""
|
||||
Return a node
|
||||
:param input_str:
|
||||
:return:
|
||||
"""
|
||||
node_proto = NodeProto()
|
||||
text_format.Parse(input_str,node_proto)
|
||||
return node_proto
|
||||
|
||||
# default values for each type for serialization
|
||||
|
||||
|
||||
def convert_attr_type_to_enum(attr_value):
|
||||
"""
|
||||
Pass in an attribute from OpDescriptor and
|
||||
get back out the equivalent enum value
|
||||
for conversion to an attribute proto.
|
||||
:param attr_value: the attribute value
|
||||
:return:
|
||||
"""
|
||||
if str(attr_value.type) == 'AttrType.INTS':
|
||||
return 7
|
||||
elif str(attr_value.type) == 'AttrType.UNDEFINED':
|
||||
return 0
|
||||
elif str(attr_value.type) == 'AttrType.FLOATS':
|
||||
return 6
|
||||
elif str(attr_value.type) == 'AttrType.GRAPH':
|
||||
return 5
|
||||
elif str(attr_value.type) == 'AttrType.GRAPHS':
|
||||
return 10
|
||||
elif str(attr_value.type) == 'AttrType.INT':
|
||||
return 2
|
||||
elif str(attr_value.type) == 'AttrType.STRING':
|
||||
return 3
|
||||
elif str(attr_value.type) == 'AttrType.TENSOR':
|
||||
return 4
|
||||
elif str(attr_value.type) == 'AttrType.TENSORS':
|
||||
return 9
|
||||
elif str(attr_value.type) == 'AttrType.SPARSE_TENSOR':
|
||||
return 11
|
||||
elif str(attr_value.type) == 'AttrType.SPARSE_TENSORS':
|
||||
return 12
|
||||
elif str(attr_value.type) == 'AttrType.FLOAT':
|
||||
return 1
|
||||
elif str(attr_value.type) == 'AttrType.STRINGS':
|
||||
return 8
|
||||
else:
|
||||
raise Exception('Invalid type passed in')
|
||||
|
||||
def create_node_from_schema(schema):
|
||||
|
||||
"""
|
||||
Convert an OpSchema to a NodeProto
|
||||
:param schema: the input OpSchema
|
||||
:return: the equivalent NodeProto
|
||||
"""
|
||||
|
||||
node_proto = NodeProto()
|
||||
for attribute in schema.attributes:
|
||||
attr_value = schema.attributes[attribute]
|
||||
if attr_value.default_value.name == '':
|
||||
attr_value_new = onnx.helper.make_attribute(attr_value.name,'')
|
||||
attr_value_new.type = convert_attr_type_to_enum(attr_value)
|
||||
node_proto.attribute.append(attr_value_new)
|
||||
else:
|
||||
node_proto.attribute.append(attr_value.default_value)
|
||||
node_proto.op_type = schema.name
|
||||
node_proto.doc_string = schema.doc
|
||||
node_proto.name = schema.name
|
||||
for input_arr in schema.inputs:
|
||||
input_types = input_arr.types
|
||||
type_attr = onnx.helper.make_attribute(input_arr.name + '-types', [str(data_type).replace('tensor(', '').replace(')', '') for data_type in input_types])
|
||||
node_proto.attribute.append(type_attr)
|
||||
|
||||
if node_proto.input is None:
|
||||
node_proto.input = []
|
||||
node_proto.input.append(input_arr.name)
|
||||
for output_arr in schema.outputs:
|
||||
if node_proto.output is None:
|
||||
node_proto.output = []
|
||||
output_types = output_arr.types
|
||||
type_attr = onnx.helper.make_attribute(output_arr.name + '-types',
|
||||
[str(data_type).replace('tensor(', '').replace(')', '') for data_type
|
||||
in output_types])
|
||||
node_proto.attribute.append(type_attr)
|
||||
node_proto.output.append(output_arr.name)
|
||||
return node_proto
|
||||
|
||||
|
||||
nodes = [create_node_from_schema(schema) for schema
|
||||
in sorted(schemas, key=lambda s: s.name)]
|
||||
graph_proto = GraphProto()
|
||||
graph_proto.node.extend(nodes)
|
||||
text_proto = text_format.MessageToString(graph_proto)
|
||||
with open('onnx-op-defs.pb', 'wb') as f:
|
||||
f.write(graph_proto.SerializeToString())
|
||||
|
||||
with open('onnx-op-def.pbtxt','w+') as f:
|
||||
f.write(text_proto)
|
||||
|
||||
# for node in nodes:
|
||||
# message_to_string = text_format.MessageToString(node, as_utf8=True)
|
||||
# node_2 = load_node(message_to_string)
|
||||
# f.write(message_to_string + '----f\n')
|
||||
|
||||
# with open('onnx.pbtxt','r') as f:
|
||||
# nodes = [load_node(node_str) for node_str in f.read().split('----f\n')]
|
||||
# print(nodes)
|
||||
@@ -0,0 +1,29 @@
|
||||
# /* ******************************************************************************
|
||||
# *
|
||||
# *
|
||||
# * This program and the accompanying materials are made available under the
|
||||
# * terms of the Apache License, Version 2.0 which is available at
|
||||
# * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
# *
|
||||
# * See the NOTICE file distributed with this work for additional
|
||||
# * information regarding copyright ownership.
|
||||
# * Unless required by applicable law or agreed to in writing, software
|
||||
# * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# * License for the specific language governing permissions and limitations
|
||||
# * under the License.
|
||||
# *
|
||||
# * SPDX-License-Identifier: Apache-2.0
|
||||
# ******************************************************************************/
|
||||
|
||||
import tensorflow as tf
|
||||
from tensorflow.python.framework.convert_to_constants import convert_variables_to_constants_v2
|
||||
|
||||
def graph_as_func():
|
||||
S = tf.Variable(tf.constant([1, 2, 3, 4]))
|
||||
result = tf.scatter_add(S, [0], [10])
|
||||
return result
|
||||
a_function_that_uses_a_graph = tf.function(graph_as_func)
|
||||
print(a_function_that_uses_a_graph.__attr__)
|
||||
converted = convert_variables_to_constants_v2(a_function_that_uses_a_graph)
|
||||
print(type(converted))
|
||||
@@ -0,0 +1,20 @@
|
||||
# /* ******************************************************************************
|
||||
# *
|
||||
# *
|
||||
# * This program and the accompanying materials are made available under the
|
||||
# * terms of the Apache License, Version 2.0 which is available at
|
||||
# * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
# *
|
||||
# * See the NOTICE file distributed with this work for additional
|
||||
# * information regarding copyright ownership.
|
||||
# * Unless required by applicable law or agreed to in writing, software
|
||||
# * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# * License for the specific language governing permissions and limitations
|
||||
# * under the License.
|
||||
# *
|
||||
# * SPDX-License-Identifier: Apache-2.0
|
||||
# ******************************************************************************/
|
||||
|
||||
import onnx
|
||||
loaded = onnx.load('lenet.onnx')
|
||||
@@ -0,0 +1,35 @@
|
||||
# /* ******************************************************************************
|
||||
# *
|
||||
# *
|
||||
# * This program and the accompanying materials are made available under the
|
||||
# * terms of the Apache License, Version 2.0 which is available at
|
||||
# * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
# *
|
||||
# * See the NOTICE file distributed with this work for additional
|
||||
# * information regarding copyright ownership.
|
||||
# * Unless required by applicable law or agreed to in writing, software
|
||||
# * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# * License for the specific language governing permissions and limitations
|
||||
# * under the License.
|
||||
# *
|
||||
# * SPDX-License-Identifier: Apache-2.0
|
||||
# ******************************************************************************/
|
||||
|
||||
from onnx_tf.common import attr_converter,attr_translator
|
||||
from onnx_tf.handlers.backend import *
|
||||
import onnx_tf
|
||||
import onnx_tf.handlers.handler
|
||||
import sys,inspect
|
||||
import tensorflow as tf
|
||||
from onnx_tf.backend import TensorflowBackend
|
||||
|
||||
|
||||
current_module = sys.modules['onnx_tf.handlers.backend']
|
||||
modules = inspect.getmembers(current_module)
|
||||
for name, obj in modules:
|
||||
obj_modules = inspect.getmembers(obj)
|
||||
for name2,module2 in obj_modules:
|
||||
if inspect.isclass(module2):
|
||||
result = module2
|
||||
print(module2)
|
||||
@@ -0,0 +1,614 @@
|
||||
# ND4J Op Definitions and Code Generation
|
||||
This project contains the ND4J Op definitions, the DSL (Domain Specific Language) that is used for those definitions and
|
||||
code generators that use those definitions to create the actual Java code that is used to use the defined operations.
|
||||
|
||||
|
||||
## Why define ops externally?
|
||||
As we started to support SameDiff, we also started to introduce inconsistencies between SameDiff and ND4J. Even though
|
||||
both of those libraries use the same underlying implementations for operations, there are both small and large
|
||||
differences in the API that we provide for them. Sometimes, we have provided an official API only for one usage, and not
|
||||
the other. And very often the documentation for a single op is in many different places.
|
||||
|
||||
In the future we want to support other programming languages with libnd4j, and provide more ways to use our C++ backend.
|
||||
This would only increase the aforementioned problems.
|
||||
|
||||
The root of all of those problems, is that Ops are used across different environments, and there is no single way of
|
||||
defining them with an enforced interface.
|
||||
|
||||
|
||||
## How does this project help with enforcing a single consistent interface for ops?
|
||||
The solution we propose, is to define the operations separately, and then generate the necessary API code for them. All
|
||||
of the generated code is to be considered untouchable, editing it will result in the changes being overwritten sooner
|
||||
rather than later.
|
||||
|
||||
The combination of external op definition and code generation, opens up many opportunities for us. The first one being
|
||||
that we can easily create consistent APIs for both ND4J and SameDiff in Java. But, looking into the future, we can also
|
||||
create those APIs for other programming languages like Python, Swift, or even C#. We can even go beyond programming
|
||||
languages, and use the op definitions to create better documentation than what JavaDoc or similar might support out of
|
||||
the box.
|
||||
|
||||
## Maintenance
|
||||
This project is currently maintained by Paul Dubs, with feedback often collected from raver119 and Alex Black.
|
||||
|
||||
## Current Status
|
||||
At the moment we still focus on nailing down an easily readable and contribution friendly DSL for op definition and code
|
||||
generation that can replace namespace definitions. This means that at the moment we still rely on the pre-existing Op
|
||||
definition classes that already exist in ND4J.
|
||||
|
||||
## Roadmap
|
||||
* Replace Bitwise and Random namespaces with autogenerated code – In progress.
|
||||
* Implement a convenient CLI tool.
|
||||
* Define all Ops using the DSL.
|
||||
* Automatically generate derivative op declarations from existing ops
|
||||
* Replace all namespace definitions in ND4J / SameDiff with automatically generated ones
|
||||
* Replace all Op classes with automatically generated ones.
|
||||
|
||||
# Usage
|
||||
Pre-requisites:
|
||||
* JDK 8 or higher
|
||||
* Maven 3.3 or higher
|
||||
|
||||
TODO: Show usage output of the project itself
|
||||
|
||||
TODO: Show how to use from mvn
|
||||
|
||||
|
||||
## Generating Code - ND4J Namespaces
|
||||
|
||||
A script - `generate.sh` - is provided in the project root. This can be used (at present) to generate ND4J namespace classes.
|
||||
It is assumed that the deeplearning4j mono repo and the dl4j-dev-tools repo both exist and have a common parent directory
|
||||
i.e., `somedir/deeplearning4j` and `somedir/dl4j-dev-tools` both exist.
|
||||
|
||||
The script takes as argument the name (or names) of the ND4J namespaces to generate (not case sensitive) and projects (supported
|
||||
projects are nd4j, sd and both by default).
|
||||
|
||||
As of 26/11, namespaces names (and hence valid args) include: `bitwise`, `neuralnetwork`, `random`, and `math`
|
||||
Note also that `all` may be passed to the script to generate all namespaces.
|
||||
|
||||
For example, to generate both bitwise and random namespaces for both nd4j and SameDiff:
|
||||
```
|
||||
./generate.sh bitwise,random
|
||||
```
|
||||
Or to generate all namespaces for both nd4j and SameDiff, use:
|
||||
```
|
||||
./generate.sh all
|
||||
```
|
||||
To generate namespaces for one project only, use:
|
||||
```
|
||||
./generate.sh linalg -projects sd
|
||||
```
|
||||
or:
|
||||
```
|
||||
./generate.sh linalg -projects nd4j
|
||||
```
|
||||
The script will first compile the project, before running.
|
||||
Internally, the `org.nd4j.codegen.cli.PicoCliCodeGen` class is used.
|
||||
Classes are written to `deeplearning4j/nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/src/main/java/org/nd4j/linalg/factory/ops/`
|
||||
|
||||
## Generating documentation.
|
||||
It is possible to use generate.sh for generation of code only, docs in markdown format only, or both docs and code.
|
||||
To generate docs only and store them to new folder "docs" for all namespaces:
|
||||
```
|
||||
./generate.sh all -docsdir ../../docs
|
||||
```
|
||||
Generation for selected namespaces works in the same way as for code:
|
||||
```
|
||||
./generate.sh -docsdir ../../docs bitwise,linalg
|
||||
```
|
||||
|
||||
# Code structure
|
||||
The project is implemented using a mix of Java and Kotlin. The DSL definition and the accompanying data structures are
|
||||
implemented in Kotlin. At the moment the code generators are implemented in Java, in order to allow people who are not
|
||||
fluent in Kotlin, but know Java to be able to contribute to the code generators.
|
||||
|
||||
The source code for this project is structured a bit different that what you would typically see in a Java or Kotlin
|
||||
project. When you take a look inside the `src/main` directory, you will find 4 sub-directories.
|
||||
|
||||
The `java` and `kotlin` directories contain Java and Kotlin code respectively.
|
||||
|
||||
In order to not confuse op definitions with the machinery that allows them to be defined in that way, ops are kept in a
|
||||
separate folder called `ops`.
|
||||
|
||||
Because we use JavaPoet for Java code generator implementation, we also have yet another folder called `stubs`. That
|
||||
folder contains stub classes, that are used to reference other classes available in ND4J. These stub classes are
|
||||
intentionally left empty, as JavaPoet only requires them for naming and automatically creating proper imports. We use
|
||||
stub classes instead of depending on the actual nd4j API in order to break a cyclic dependency that would otherwise be
|
||||
created (i.e. in order to be able to generate code for ND4J, we would need an already compiled nd4j to be available).
|
||||
**Note:** If something is stubbed here and is moved in ND4J, then it also has to be moved to the appropriate place here,
|
||||
otherwise the generated code will be wrong.
|
||||
|
||||
The `adr` folder contains "Architecture Decision Records". These files give you more insight into the "why" of some of
|
||||
the bigger decisions within this project.
|
||||
|
||||
# DSL for Op Definition
|
||||
Ops are defined using a DSL that is implemented in Kotlin. This means that other than the DSL, as defined in the
|
||||
following, you can also use all of Kotlin when defining Ops. However, doing things the obvious and clearly
|
||||
understandable way is better than coming up with a clever way, so prefer to use the DSL as described if unsure.
|
||||
|
||||
```kotlin
|
||||
val mathNs = Namespace("math") {
|
||||
Op("add") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.pairwise.arithmetic"
|
||||
|
||||
Input(NUMERIC, "x") { description = "First input to add" }
|
||||
Input(NUMERIC,"y") { count = AtLeast(1); description = "Second input to add" }
|
||||
Arg(INT,"shape") { count = AtLeast(1); description = "shape" }
|
||||
|
||||
|
||||
Output(NUMERIC, "z") { description = "Output (x+y)" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
(From AddOp) Add op doc text that will appear everywhere - classes, constructors, op creators
|
||||
""".trimIndent()
|
||||
}
|
||||
Doc(Language.ANY, DocScope.CLASS_DOC_ONLY) {
|
||||
"Add op doc text that will appear in all class docs (javadoc etc)"
|
||||
}
|
||||
Doc(Language.ANY, DocScope.CONSTRUCTORS_ONLY) {
|
||||
"Add op doc text for constructors only"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This example shows how a namespace is defined. Namespaces are at the top layer, and ops can only be defined within the
|
||||
context of a namespace. This example namespace contains only a single op, called "add". If we wanted to add another op,
|
||||
we would simply add it below the first.
|
||||
|
||||
As you can see, every op has to have a name, if you try to create one without a name, you will get a compile error.
|
||||
Within the context of the op, we first set in which java package the op class can be found in, then define its inputs,
|
||||
arguments and outputs and finally add some free form documentation about what that op is doing.
|
||||
|
||||
Like with the op itself, the inputs, arguments and outputs all have to have a name, but unlike the op, they also require
|
||||
a type. Within their context, you can set a description and a count of how many parameters they can take respectively.
|
||||
|
||||
If an input, argument or output take anything else than exactly 1, they will be treated as arrays. Typically you would
|
||||
use this to define ops like `concat` which can take multiple input tensors or ops that might take shape arguments.
|
||||
|
||||
## Examples
|
||||
The following shows how a typical op definition looks like and how the generated Java code may look.
|
||||
|
||||
An op might be defined like this:
|
||||
|
||||
```kotlin
|
||||
Op("binomial") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.random.custom"
|
||||
Arg(INT, "nTrials") { description = "Number of trials parameter for the binomial distribution" }
|
||||
Arg(FLOATING_POINT, "p") { description = "Probability of success for each trial" }
|
||||
Arg(INT, "shape") { count = AtLeast(1); description = "Shape of the new random SDVariable, as a 1D array" }
|
||||
|
||||
Output(NUMERIC, "output") { description = "new random SDVariable, where values are randomly sampled according to a Binomial distribution" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Generate a new random SDVariable, where values are randomly sampled according to a Binomial distribution,
|
||||
with the specified number of trials and probability.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The java code generator will create a method like the following for it:
|
||||
```java
|
||||
/**
|
||||
* Generate a new random SDVariable, where values are randomly sampled according to a Binomial distribution,
|
||||
* with the specified number of trials and probability.
|
||||
*
|
||||
* @param nTrials Number of trials parameter for the binomial distribution
|
||||
* @param p Probability of success for each trial
|
||||
* @param shape Shape of the new random SDVariable, as a 1D array (Size: AtLeast(min=1))
|
||||
* @return output new random SDVariable, where values are randomly sampled according to a Binomial distribution (NUMERIC type)
|
||||
*/
|
||||
public static INDArray binomial(long nTrials, double p, long... shape) {
|
||||
Preconditions.checkArgument(shape.length >= 1, "shape has incorrect count. Expected: AtLeast(min=1)");
|
||||
return Nd4j.exec(new org.nd4j.linalg.api.ops.random.custom.BinomialOp(nTrials, p, shape))[0];
|
||||
}
|
||||
```
|
||||
|
||||
Or an op with some more constraints:
|
||||
|
||||
```kotlin
|
||||
Op("and") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
val x = Input(INT, "x") { description = "First input array" }
|
||||
val y = Input(INT, "y") { description = "Second input array" }
|
||||
Constraint("Must be same types"){ sameType(x, y) }
|
||||
Constraint("Must have broadcastable shapes"){ broadcastableShapes(x, y) }
|
||||
|
||||
Output(INT, "output"){ description = "Bitwise AND array" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Bitwise AND operation. Supports broadcasting.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
will be converted to java like this:
|
||||
|
||||
```java
|
||||
/**
|
||||
* Bitwise AND operation. Supports broadcasting.
|
||||
*
|
||||
* Inputs must satisfy the following constraints:
|
||||
* Must be same types: isSameType(x, y)
|
||||
* Must have broadcastable shapes: isBroadcastableShapes(x, y)
|
||||
*
|
||||
* @param x First input array (INT type)
|
||||
* @param y Second input array (INT type)
|
||||
* @return output Bitwise AND array (INT type)
|
||||
*/
|
||||
public static INDArray and(INDArray x, INDArray y) {
|
||||
NDValidation.validateInteger("and", x);
|
||||
NDValidation.validateInteger("and", y);
|
||||
Preconditions.checkArgument(isSameType(x, y), "Must be same types");
|
||||
Preconditions.checkArgument(isBroadcastableShapes(x, y), "Must have broadcastable shapes");
|
||||
return Nd4j.exec(new org.nd4j.linalg.api.ops.impl.transforms.custom.AndOp(x, y))[0];
|
||||
}
|
||||
```
|
||||
|
||||
# Full DSL Description
|
||||
## Namespace
|
||||
|
||||
fun NamespaceName() = Namespace("name"){ /* Op definitions in namespace context */}
|
||||
|
||||
Defines a namespace.
|
||||
|
||||
## Op
|
||||
Only available within a namespace context
|
||||
|
||||
Op("opName") { /* op properties in op context */ }
|
||||
Op("anotherOp", mixin) { /* op properties in op context */ }
|
||||
Op("anotherOp2", mixin, keepInputs=false) { /* op properties in op context */ }
|
||||
|
||||
Every op requires a namespace unique op name.
|
||||
|
||||
When defining an op, you can also pass a mixin that it should inherit initial properties from. This has the same effect
|
||||
as using `useMixin(mixin)` as the very first thing in the op definition. If you don't want to inherit all of the
|
||||
parameters of the mixin, you can pass the same additional configuration as you would pass to
|
||||
`useMixin(mixin, ...options..)`. See [Mixin](#mixin) for more information.
|
||||
|
||||
### Op properties
|
||||
* `javaPackage` (String): Package where the op is to be found in the java implementation.
|
||||
* `javaOpClass` (String): Name of java op class if inconsistent with opName. Default: same as opName
|
||||
* `libnd4jName` (String): The name the op has in libnd4j. Default: same as opName
|
||||
|
||||
|
||||
## Mixin
|
||||
Available in global context.
|
||||
|
||||
val mixin = Mixin("name"){ /* op properties in op context */ }
|
||||
// within an op context:
|
||||
useMixin(mixin)
|
||||
useMixin(mixin, ...options...)
|
||||
|
||||
// When needing to access something from within the mixin
|
||||
mixin.input("name")
|
||||
mixin.arg("name")
|
||||
mixin.config("name")
|
||||
mixin.output("name")
|
||||
|
||||
Mixins provide the facility to share commonalities between Ops. You can think of it like inheritance, especially when
|
||||
you declare the use of a mixin on Op definition. In contrast to normal (single) inheritance where only a single super
|
||||
class is possible, the mixin mechanism allows to "inherit" from multiple sources.
|
||||
|
||||
You can define almost all the same things within a mixin that you can within an Op. The only things that *can not* be
|
||||
configured within a mixin are Op `name`, `libnd4jName` and `javaOpClass`.
|
||||
|
||||
As mixins can be configured within the global context, you can share them across namespaces by defining them in their
|
||||
own file. If a mixin is namespace specific, you can also define it within the namespace context.
|
||||
|
||||
Mixins are used either on definition as a parameter `Op("opname", mixin){...}`, or with `useMixin(mixin)` within the op
|
||||
definition. While the former version only supports a single mixin, the latter version allows you to use as many mixins
|
||||
as are required.
|
||||
|
||||
You can also build up mixins by using `useMixin(mixin)` inside a Mixin itself.
|
||||
|
||||
`useMixin(mixin, ...options...)` supports a few additional options: `keepInputs`, `keepArgs`, `keepConfigs`,
|
||||
`keepOutputs`, `keepSignatures`, `keepDoc`, `keepConstraints`. They default to `true`. If you want to skip including
|
||||
some of them, you simply set the parameter for it to `false`, e.g. `useMixin(mixin, keepDoc=false)`.
|
||||
|
||||
When using `useMixin(mixin)`, all definitions within the mixin are applied as if this invocation was replaced with the
|
||||
content of the mixin itself. This means, that if you have already defined anything prior to using a mixin, the mixin's
|
||||
definitions will be **after** the previously defined things. This can be very useful if the commonality between ops is
|
||||
that they have a few trailing options.
|
||||
|
||||
If a named property or section is defined in both a mixin (or multiple mixins) and the op, then the **last** to define it will
|
||||
win. Named properties are `legacy`, `javaPackage`, named sections are `Input`, `Arg`, `Output`, `Config`.
|
||||
|
||||
For example, assume you have `javaPackage` defined in both an op and a mixin. Then you can have the following two
|
||||
cases:
|
||||
|
||||
First case:
|
||||
```kotlin
|
||||
Op("foo"){
|
||||
useMixin(exampleMixin)
|
||||
javaPackage = "some.example.package"
|
||||
}
|
||||
```
|
||||
|
||||
Second case:
|
||||
```kotlin
|
||||
Op("foo"){
|
||||
javaPackage = "some.example.package"
|
||||
useMixin(exampleMixin)
|
||||
}
|
||||
```
|
||||
|
||||
In the first case, the op will have the `javaPackage` value that is defined within the op. In the second case it will
|
||||
have the `javaPackage` value defined in the mixin.
|
||||
|
||||
For inputs, args, outputs, it works similarly. Assume you have `Input(dataType, "a")` defined in both the mixin and the
|
||||
op. Again you can have two cases:
|
||||
|
||||
First case:
|
||||
```kotlin
|
||||
Op("foo"){
|
||||
useMixin(exampleMixin)
|
||||
Input(NUMERIC, "a")
|
||||
}
|
||||
```
|
||||
|
||||
Second case:
|
||||
```kotlin
|
||||
Op("foo"){
|
||||
Input(NUMERIC, "a")
|
||||
useMixin(exampleMixin)
|
||||
}
|
||||
```
|
||||
|
||||
In the first case, it will overwrite the input from the mixin. In the second case, the mixin will overwrite that the
|
||||
input from the op.
|
||||
|
||||
## Config
|
||||
Only available within a namespace context
|
||||
|
||||
val nameConfig = Config("Name"){
|
||||
/* input, arg, constraint, doc properties */
|
||||
}
|
||||
|
||||
Every config requires a namespace unique name.
|
||||
|
||||
A config allows to define a configuration class, that can be used as a holder for complex properties of specific ops
|
||||
which will be passed to an op as a parameter.
|
||||
|
||||
Similar to an op itself, it supports `Input`, `Arg`, `Constraint` and `Doc` definitions.
|
||||
|
||||
in order to use the config within an op you either use `useConfig(cfg)` or `val configRef = useConfig(cfg)`. The second
|
||||
form allows you to reference the config.
|
||||
|
||||
Referencing the config allows to you reference its inputs and args by name: `configRef.input("name")` and
|
||||
`configRef.arg("name")`. Also it allows you to use a config in a signature `Signature(a, b, c, configRef)`.
|
||||
|
||||
When default and shorthand signatures are used, configs will be always placed at the end.
|
||||
|
||||
If a config is defined but not used, an `IllegalStateException` will be thrown.
|
||||
|
||||
See also [ADR 0007 "Configuration Objects"](adr/0007-configuration_objects.md).
|
||||
|
||||
|
||||
## Input
|
||||
Available within an op, mixin and a config context
|
||||
|
||||
Input(FLOATING_POINT, "b"){ /* input properties in input context */ }
|
||||
val a = Input(INT, "a"){ /* input properties in input context */ }
|
||||
|
||||
Inputs represent tensors. They are what the op will work on.
|
||||
|
||||
Every input requires a data type (either `INT`, `FLOATING_POINT`, `NUMERIC` or `BOOLEAN`) and an op unique name.
|
||||
|
||||
When defining an input, you can assign it to a variable in order to be able to reference it later on. You might want to
|
||||
do this when defining constraints.
|
||||
|
||||
If you want an input to represent an array, you will have to set a count accordingly. If no count is set, it is assumed
|
||||
that the count is meant to be `Exactly(1)`.
|
||||
|
||||
### Input properties
|
||||
* `description` (String): A short description what this input represents. Setting this is recommended.
|
||||
* `count` (Count): Can take one of `Exactly(n)`; `AtLeast(n)`; `AtMost(n)`; `Range(from, to)`
|
||||
* `defaultValue` (Input): use another input as the default if this isn't set explicitly. The data type of the other
|
||||
input has to match the data type of this input. The other input may also have a default value.
|
||||
|
||||
## Argument
|
||||
Available within an op, mixin and config context
|
||||
|
||||
Arg(FLOATING_POINT, "b"){ /* Arg properties in arg context */ }
|
||||
val a = Arg(INT, "a"){ /* Arg properties in arg context */ }
|
||||
|
||||
Args represent arguments. They modify how the op works on its inputs.
|
||||
|
||||
Every arg requires a data type (either `INT`, `FLOATING_POINT`, `NUMERIC` or `BOOLEAN`) and an op unique name.
|
||||
|
||||
When defining an arg, you can assign it to a variable in order to be able to reference it later on. You might want to do
|
||||
this when defining constraints.
|
||||
|
||||
If you want an arg to represent an array, you will have to set a count accordingly. If no count is set, it is assumed
|
||||
that the count is meant to be `Exactly(1)`.
|
||||
|
||||
Note (Java specific): If the last arg is defined to represent an array, it will be translated to a vararg parameter, e.g.
|
||||
`Arg(INT, "a"){ count = AtLeast(1); description = "..." }` will be turned into `long... a`.
|
||||
|
||||
### Argument properties
|
||||
* `description` (String): A short description what this argument represents. Setting this is recommended.
|
||||
* `count` (Count): Can take one of `Exactly(n)`; `AtLeast(n)`; `AtMost(n)`; `Range(from, to)`
|
||||
* `defaultValue` (null|Number|Boolean|int[]|double[]|boolean[]|Arg|TensorShapeValue|TensorDataTypeValue|String):
|
||||
Use given value as default value, if this isn't explicitly set. Can refer to *inputs* and *outputs* using `x.shape()`
|
||||
and `x.dataType()`. The given default values has to match the data type for this argument. May also refer to another
|
||||
Arg, and that Arg may also have a default value. Default values based on outputs are treated like without a default
|
||||
in SameDiff mode.
|
||||
* `possibleValues` (String[]): only available when ENUM data type is used for the argument. Takes a list of possible
|
||||
values for the Enum. If used in in abstract base op, the enum will only be created once. See also
|
||||
[ADR 0006 "Op specific enums"](adr/0006-op_specific_enums.md).
|
||||
|
||||
|
||||
## Output
|
||||
Only available within an op and mixin context
|
||||
|
||||
Output(FLOATING_POINT, "b"){ /* Arg properties in arg context */ }
|
||||
|
||||
Every output requires a data type (either `INT`, `FLOATING_POINT`, `NUMERIC` or `BOOLEAN`) and an op unique name.
|
||||
|
||||
While outputs can be assigned to a variable, there is no intended use-case for it. In contrast to inputs and args,
|
||||
outputs can not be used in constraints.
|
||||
|
||||
### Output properties
|
||||
* `description` (String): A short description what this argument represents. Setting this is recommended.
|
||||
|
||||
|
||||
## Signature
|
||||
Only available within an op and mixin context
|
||||
|
||||
Signature(a,b,c)
|
||||
Signature(a,b,c) { "Some Documentation" }
|
||||
AllParamSignature()
|
||||
AllDefaultParamSignature()
|
||||
|
||||
For some ops only specific signatures make sense, as for example some optional parameters may become required in the
|
||||
presence of other optional parameters. This feature is mainly meant to help with the fact that not all programming
|
||||
languages (e.g. Java) support default parameters. Each signature is meant to describe one overload in those languages.
|
||||
|
||||
See also [ADR 0005 "Optional parameters and signatures"](adr/0005-optional_parameters_and_signatures.md).
|
||||
|
||||
Signatures can also reference the output(s) of an op. Those signatures are only relevant in NDArray programming mode.
|
||||
They are not to be generated in SameDiff mode.
|
||||
|
||||
`AllParamSignature()` and `AllDefaultParamSignature()` are short hands for `Signature(...all parameters...)` and
|
||||
`Signature(...only parameters with no default values...)`. Their parameters include references to outputs unless
|
||||
disabled using `withOutput=false` (e.g. `AllParamSignature(withOutput=false)`).
|
||||
|
||||
If no signature is specified for an op, it is treated as if `AllParamSignature()` and `AllDefaultParamSignature()` are
|
||||
both specified.
|
||||
|
||||
Each signature must satisfy the condition, that all required parameters are listed there. If this condition is not
|
||||
satisfied, an `IllegalStateException` will be thrown on construction.
|
||||
|
||||
|
||||
## Documentation
|
||||
Only available within an op and mixin context
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
""" Some documentation
|
||||
It can be multiline. And indented.
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
Documentation can be language specific, and can be set to be only given at specific places. The documentation itself is
|
||||
given as a string. Because Kotlin supports multiline strings along with proper indentation, we are using them directly
|
||||
here.
|
||||
|
||||
Note: At the moment we are only creating java code, so the documentation can use JavaDoc syntax.
|
||||
|
||||
You can have multiple Doc definitions; they are treated as additive.
|
||||
|
||||
Any instances of the following values will be replaced when generating code:
|
||||
|
||||
* `%OPNAME%` -> operation name ("Add", "Sub", etc)
|
||||
* `%LIBND4J_OPNAME%` -> libnd4j op name ("add", "sub", etc)
|
||||
* `%INPUT_TYPE%` -> input / output type depending on the generated api, i.e. `SDVariable` for SameDiff and `INDArray`
|
||||
for ND4J
|
||||
|
||||
See `DocTokens` class for more details.
|
||||
|
||||
## Constraints
|
||||
Available within an op, mixin and a config context.
|
||||
|
||||
Constraint("Error Message if constraint isn't satisfied"){ /* constraint definition */ }
|
||||
BackendConstraint("Error Message if constraint isn't satisfied"){ /* constraint definition */ }
|
||||
|
||||
Many ops expect their inputs and arguments to satisfy some specific rules. Those rules can be expressed with the
|
||||
constraint system.
|
||||
|
||||
Constraints are to be enforced within the frontend language, while BackendConstraints are currently only to be used as
|
||||
a part of the documentation. They will be enforced within the C++ backend, so there is no point in double checking them.
|
||||
|
||||
There is a system in place to define even complex constraints for inputs and arguments.
|
||||
|
||||
In a constraint definition, you can reference inputs and arguments directly, if they are previously assigned to
|
||||
a variable using `val name = Input(...)`. Inside the Constraint block, you can use the following operations:
|
||||
|
||||
* `eq`: Compare equality (applicable to numbers and booleans), e.g. `x eq 7`, `x eq true`
|
||||
* `neq`: Compare inequality (applicable to numbers and booleans), e.g. `x neq 3`, `x neq true`
|
||||
* `lt`, `lte`: less than, less than equal (applicable to numbers), e.g. `x lt 3`, `x lte 4`
|
||||
* `gt`, `gte`: greater than, grater than equal (applicable to numbers), e.g. `x gt 5`, `x gte 6`
|
||||
* `and`: combine two comparisons where both have to be true, e.g. `(x eq 8) and (y lt 3)`
|
||||
* `or`: combine two comparisons where one has to be true, e.g. `(x eq 8) or (y eq true)`
|
||||
* `all`: combine N comparisons where all have to be true, e.g. `all(x eq 8, y lt 3, z eq true)`
|
||||
* `some`: combine N comparisons where at least one has to be true, e.g. `some(x eq 8, y lt 3, z eq true)`
|
||||
* `not`: negates a comparison, e.g. `not(x eq 3)`
|
||||
|
||||
In addition to those operations, you also get access to some more complex constraints:
|
||||
* `sameType(...)`: true if all given inputs are the same type, e.g. `sameType(x,y,z)`
|
||||
* `sameShape(...)`: true if all given inputs have the same shape, e.g. `sameShape(x,y,z)`
|
||||
* `broadcastableShapes(...)`: true if all given inputs have broadcast compatible shapes, e.g. `broadcastableShapes(x,y,z)`
|
||||
|
||||
Inputs also get some additional methods on them to define useful constraints:
|
||||
* `input.rank()`: Rank of the given input
|
||||
* `input.sizeAt(i)`: size of the given input at the i-th dimension
|
||||
* `input.isScalar()`: Short hand for `x.rank() == 1`
|
||||
|
||||
### Examples
|
||||
Some examples of constraints, and what they evaluate to. The example code contains a little bit of context.
|
||||
|
||||
```kotlin
|
||||
val x = Input(INT, "x") { description = "First input array" }
|
||||
val y = Input(INT, "y") { description = "Second input array" }
|
||||
Constraint("foo bar"){
|
||||
x.sizeAt(7) eq 7 and y.isScalar()
|
||||
}
|
||||
```
|
||||
|
||||
will evaluate to:
|
||||
```java
|
||||
Preconditions.checkArgument((x.sizeAt(7) == 7) && (y.rank() == 1), "foo bar");
|
||||
```
|
||||
|
||||
More examples (only the constraint itself, without context code):
|
||||
|
||||
#### Some
|
||||
```kotlin
|
||||
some(input.rank() eq 3, input.sizeAt(2) gte 7, input.sizeAt(4) lt 5)
|
||||
```
|
||||
turns to:
|
||||
```java
|
||||
((x.rank() == 3) || (x.sizeAt(2) >= 7)) || (x.sizeAt(4) < 5)
|
||||
```
|
||||
|
||||
# Contributing to this project
|
||||
If you want to contribute to this project other than by adding or improving op definitions, the following sections might
|
||||
be of special interest to you.
|
||||
|
||||
## Extending the DSL
|
||||
The DSL is implemented using Kotlin’s type-safe builders feature
|
||||
(see https://kotlinlang.org/docs/reference/type-safe-builders.html). The basic principle is that functions calls can
|
||||
receive blocks that can be executed in a specified context. When combined with the fact that we are just looking to
|
||||
create an object graph that is then going to be used as input to the code generators, this allows us to create a very
|
||||
feature rich DSL without actually having to write a lot of code to support it.
|
||||
|
||||
Most of the DSL specific code can be found in `src/kotlin/org/nd4j/codegen/dsl/OpBuilder.kt`. The actual class
|
||||
definitions for the object graph we are building, can be found in `src/kotlin/org/nd4j/codegen/api`.
|
||||
|
||||
If you want to add just a simple field to one of the objects, it is usually enough to directly add it to the particular
|
||||
class.
|
||||
|
||||
If you want to add a specific section to the op definition, i.e. a section like Input or Doc, you will have to add both
|
||||
the class for the object that it is going to be creating, as well as a function within OpBuilder.kt to create and
|
||||
register that section within the op.
|
||||
|
||||
**Note:** When you extend the DSL you will most likely also have to update all code generators to support the feature
|
||||
you have added.
|
||||
|
||||
## Adding / extending code generators
|
||||
Code generators can be written in either Java or Kotlin. Java has the advantage that more people will have experience in
|
||||
using it. Kotlin has the advantage of more convenient syntax, especially for plain string manipulation and when dealing
|
||||
with Enums and fixed sets of subclasses (called sealed classes in Kotlin).
|
||||
|
||||
All generators have to implement the `org.nd4j.codegen.api.generator.Generator` interface. For automatic detection by
|
||||
the CLI tool, they should also be within the `org.nd4j.codegen.impl.LANGUAGE` package, where `LANGUAGE` is the actual
|
||||
language that they generate.
|
||||
|
||||
Code generators can also use an auxiliary generator for constraint generation. Those auxiliary generators, have to
|
||||
implement ` org.nd4j.codegen.api.generator.ConstraintCodeGenerator` interface.
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# Use Kotlin-based DSL as the source of truth
|
||||
|
||||
## Status
|
||||
|
||||
ACCEPTED
|
||||
|
||||
Discussed by: Paul Dubs, Alex Black, raver119 on 19. October 2019
|
||||
|
||||
|
||||
## Context
|
||||
|
||||
This code generation experiment is meant to be our starting point for both the API unification for ND4J and SameDiff,
|
||||
and the multi-language support. For this reason we have to define ops, or their interface, in a language neutral way.
|
||||
|
||||
The initial idea was to use a Language Workbench like MPS. This had to be discarded because of bugs and limitations
|
||||
encountered while trying to define a language that would work for a few simple examples.
|
||||
|
||||
The next idea was to use Ops defined in JSON files. This would have allowed us to define Ops as human readable data and
|
||||
read and write those files from any programming language. However, the drawback with this approach is that writing json
|
||||
manually invites many problems if written manually (e.g. typos, bad structuring, having to look up the proper keys,...).
|
||||
In order to rectify that drawback, we would have to create custom tooling, that we would have to maintain and that
|
||||
contributors would have to use.
|
||||
|
||||
Using a Java builder pattern based approach is very verbose.
|
||||
|
||||
## Decision
|
||||
|
||||
We use a Kotlin-based DSL to define Ops.
|
||||
|
||||
## Consequences
|
||||
|
||||
Using a full programming language as the platform to build our DSL has both advantages and drawbacks.
|
||||
|
||||
### Drawbacks
|
||||
* Contributors will have to install a JVM in order to be able to run code generation or get a serialized op graph
|
||||
* Serialization is a one way road, we only output to JSON, but don't read from it
|
||||
* Contributors to Op definitions have to learn about our DSL and maybe some Kotlin
|
||||
* Contributors to the DSL will have to learn about Kotlin
|
||||
|
||||
|
||||
### Advantages
|
||||
* We can utilize the Java knowledge of the existing team for writing code generators as Kotlin is two-way interoperable
|
||||
with Java and other JVM languages.
|
||||
* We can utilize IntelliJ (or other IDEs supporting Kotlin) as an existing editor for Ops definitions. This provides us
|
||||
with benefits like code completion, error highlighting
|
||||
* We get compile time checks, freeing us from trivial errors like typos
|
||||
* We can utilize some base features of Kotlin, like variable assignment to simplify the implementation
|
||||
* Kotlin has first class DSL definition support, allowing us to make Op definitions almost as easy to read as a full
|
||||
language workbench would have allowed
|
||||
@@ -0,0 +1,40 @@
|
||||
# Splitting Object Graph for Code Gen and Serialization
|
||||
|
||||
## Status
|
||||
|
||||
ACCEPTED
|
||||
|
||||
Discussed by: Paul Dubs & Alex Black on 07. November 2019
|
||||
|
||||
|
||||
## Context
|
||||
|
||||
Serialization and Code Generation have different needs when it comes to how the object graph should be laid out. When
|
||||
generating code, it is a lot easier to be able to directly access referenced objects and traverse through the graph.
|
||||
However, if the same object graph is used for serialization, the same object will appear in multiple places.
|
||||
|
||||
This becomes very apparent when defining constraints. A single constraint might be referring to an input multiple times,
|
||||
and then there can also be multiple constraints that refer to multiple inputs.
|
||||
|
||||
The main reason why we want to keep serialization in mind, is that we want to keep code generators in other languages as
|
||||
a viable option. Just serializing the graph that is meant to be used during runtime in code generation however, would
|
||||
can easily become a problem when object identity is required for equality comparisons.
|
||||
|
||||
An implementer in a different language would therefore need work through that graph and find identical objects AND would
|
||||
have to know where that identity is a coincidence and where it is meant to be that way. By creating an object graph that
|
||||
makes this explicit, we make that work easier.
|
||||
|
||||
## Decision
|
||||
|
||||
We use two distinct object graphs. One is used for code generation, and the other is used for serialization.
|
||||
|
||||
|
||||
## Consequences
|
||||
|
||||
### Advantages
|
||||
* Easier to work with object graphs that are aligned with their use-case
|
||||
* Less error prone when access is direct
|
||||
|
||||
### Disadvantages
|
||||
* We have to explicitly transform one object graph into another
|
||||
* If we want to support reading JSON back, we will have to also define the backwards transformation
|
||||
@@ -0,0 +1,33 @@
|
||||
# Dealing with Inconsistencies in Java Naming
|
||||
|
||||
## Status
|
||||
|
||||
ACCEPTED
|
||||
|
||||
Discussed by: Paul Dubs, Alex Black, raver119 on 22. November 2019
|
||||
|
||||
|
||||
## Context
|
||||
|
||||
There are slight inconsistencies in naming between existing op class definitions and factory methods. For example a
|
||||
factory method called `bernoulli` in the `random` namespace with a corresponding op class called
|
||||
`BernoulliDistribution`.
|
||||
|
||||
Two possible solutions where suggested:
|
||||
1. Add an additional property that provides us with the correct class name
|
||||
2. Rename classes in ND4J to ensure consistency and provide backwards compatibility via deprecated subclasses
|
||||
|
||||
## Decision
|
||||
|
||||
For now we will introduce a `javaOpClass` property which in cases of inconsistency provides us with the correct class
|
||||
name.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Advantages
|
||||
* We can start using this property immediately
|
||||
* No need to change anything of the existing ND4J / SameDiff API
|
||||
|
||||
### Disadvantages
|
||||
* Inconsistency continues to exist within the Java codebase
|
||||
* We have to take extra care to add the new property where needed
|
||||
@@ -0,0 +1,44 @@
|
||||
# Auto initialization for in-place operations
|
||||
|
||||
## Status
|
||||
|
||||
REJECTED
|
||||
|
||||
Discussed by: Paul Dubs, Alex Black, raver119 on 25. November 2019
|
||||
|
||||
|
||||
## Context
|
||||
|
||||
Some operations work in-place on the inputs that they are given in ND4J, but in SameDiff the same operations will
|
||||
generate an array from a given shape. Examples for this include `BernoulliDistribution`, and other random ops, that
|
||||
effectively initialize the array that they are given.
|
||||
|
||||
From a consistency point of view, it would be nice if both API's would support both ways of using those ops.
|
||||
|
||||
|
||||
## Decision
|
||||
|
||||
We introduce an option to mark inputs as `inPlace = true` to make it clear that this input is going to be changed
|
||||
in-place. In addition we introduce an option `supportsInPlaceInit = true` to mark an input as initialize-able. If the
|
||||
`supportsInPlaceInit` option is enabled, two signatures for the Op will be created, one that takes an input, and one
|
||||
that takes the appropriate shape and data type information in its stead.
|
||||
|
||||
|
||||
## Consequences
|
||||
|
||||
### Advantages
|
||||
* We get support for both in-place and initialization use-cases
|
||||
* Support for this use-case is explicitly defined in the DSL instead of implicit support by the code generator
|
||||
|
||||
### Disadvantages
|
||||
* Codegen becomes more complex, as it requires us to generate more signatures
|
||||
|
||||
|
||||
## Reasons for Rejection
|
||||
This feature would only come in handy on legacy ops. However, those ops will not be exposed to frontend languages
|
||||
anymore starting from the next release.
|
||||
|
||||
For the ops that replace them ("Custom Ops"), it is *always* possible to pass an output array, when used in NDArray
|
||||
programming mode. Thereby making the in-place initialization support a side-effect of that general design.
|
||||
|
||||
For support of both `op(out)` and `op(dataType, shape)` see ADR 0005 "Optional parameters and signatures".
|
||||
@@ -0,0 +1,108 @@
|
||||
# Optional parameters and signatures
|
||||
|
||||
## Status
|
||||
|
||||
ACCEPTED
|
||||
|
||||
Discussed by: Alex Black, raver 119 and Paul Dubs on 25. November 2019
|
||||
|
||||
## Context
|
||||
|
||||
Not all inputs or args (= parameters) are always required.
|
||||
|
||||
Often there are sensible defaults available. We want to be able to make those defaults explicit where possible.
|
||||
|
||||
Even though some parameters may be optional, they might become required in the presence of other optional parameters.
|
||||
|
||||
We need a way to explicitly define what combinations are possible.
|
||||
|
||||
|
||||
## Decision
|
||||
We drop the `optional` property on parameters. Instead, parameters get an additional property `defaultValue`. It can be
|
||||
set to either a fixed literal value (e.g. `7`, `"something"`, `null`), an Arg, or it may reference the specific methods
|
||||
`shape()` and `dataType()` on inputs and outputs. Parameters with `defaultValue` specified are treated as optional.
|
||||
|
||||
To be able to deal with languages that do not support default values for arguments, Signatures will be specified.
|
||||
Signatures are specified using a `Signature(a,b,c){ "signature specific documentation" }` section for each signature.
|
||||
With the signature specific documentation being optional.
|
||||
|
||||
Signatures making use of outputs will only be generated for NDArray programming mode, not in SameDiff mode. This also
|
||||
means that parameters with a `defaultValue` based on an output will be treated as required in SameDiff mode.
|
||||
|
||||
If signatures are specified, only the specified signatures will be generated.
|
||||
|
||||
If no signatures are explicitly specified, only the "all-arg" and "no-optional-arg" signatures will be generated. In
|
||||
NDArray programming mode, the default signatures also include a variant that includes the output.
|
||||
|
||||
|
||||
## Examples
|
||||
### BatchNorm with all otherwise auto generated signatures stated explicitly
|
||||
|
||||
```kotlin
|
||||
Op("batchNorm") {
|
||||
val input = Input(NUMERIC, "input") { description = "Input variable" }
|
||||
val mean = Input(NUMERIC, "mean") { description = "Mean value. For 1d axis, this should match input.size(axis)" }
|
||||
val variance = Input(NUMERIC, "variance") { description = "Variance value. For 1d axis, this should match input.size(axis)" }
|
||||
val gamma = Input(NUMERIC, "gamma") { description = "Gamma value. For 1d axis, this should match input.size(axis)" }
|
||||
val beta = Input(NUMERIC, "beta") { description = "Beta value. For 1d axis, this should match input.size(axis)" }
|
||||
|
||||
val applyGamma = Arg(BOOL, "applyGamma") { description = ""; defaultValue = true}
|
||||
val applyBeta = Arg(BOOL, "applyBeta") { description = ""; defaultValue = true}
|
||||
val axis = Arg(INT, "axis"){
|
||||
count = AtLeast(1)
|
||||
description = """
|
||||
For 2d CNN activations: 1 for NCHW format activations, or 3 for NHWC format activations.
|
||||
For 3d CNN activations: 1 for NCDHW format, 4 for NDHWC
|
||||
For 1d/RNN activations: 1 for NCW format, 2 for NWC
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
val out = Output(INT, "output"){ description = "Output variable for batch normalization" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Neural network batch normalization operation.
|
||||
For details, see <a href="https://arxiv.org/abs/1502.03167">https://arxiv.org/abs/1502.03167</a>
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
Signature(input, mean, variance, gamma, beta, axis)
|
||||
Signature(input, mean, variance, gamma, beta, applyGamma, applyBeta, axis)
|
||||
Signature(out, input, mean, variance, gamma, beta, axis)
|
||||
Signature(out, input, mean, variance, gamma, beta, applyGamma, applyBeta, axis)
|
||||
}
|
||||
```
|
||||
|
||||
### Random Uniform initialization with support for (dataType, shape) and (out) invocation
|
||||
```kotlin
|
||||
Op("uniform") {
|
||||
val out = Output(NUMERIC, "output") { description = "new random %INPUT_TYPE%, where values are randomly sampled according to a uniform distribution" }
|
||||
|
||||
val min = Arg(FLOATING_POINT, "min") { description = "Minimum value" }
|
||||
val max = Arg(FLOATING_POINT, "max") { description = "Maximum value." }
|
||||
val dataType = Arg(DATA_TYPE, "dataType") { description = "Data Type of the output array"; defaultValue = out.dataType() }
|
||||
val shape = Arg(INT, "shape") { count = AtLeast(1); description = "Shape of the new random %INPUT_TYPE%, as a 1D array"; defaultValue = out.dataType() }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Generate a new random %INPUT_TYPE%, where values are randomly sampled according to a uniform distribution,
|
||||
U(min,max)
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
Signature(min, max, dataType, shape)
|
||||
Signature(out, min, max)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Consequences
|
||||
|
||||
### Advantages
|
||||
* We get to explicitly define edge cases
|
||||
* We can make Signatures compatible with existing code
|
||||
* Even in languages with default value support, the added signatures may become useful parts of the documentation
|
||||
|
||||
### Disadvantages
|
||||
* The order of definitions within the op changes to Output first, if inputs need to reference it
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# Op specific enums
|
||||
|
||||
## Status
|
||||
|
||||
ACCEPTED
|
||||
|
||||
Discussed by: Alex Black, Robert Altena and Paul Dubs on 26. November 2019
|
||||
|
||||
## Context
|
||||
Some ops have an ordinal parameter which switches between a few possible modes. Giving those modes a proper name
|
||||
makes usage and documentation easier.
|
||||
|
||||
|
||||
## Decision
|
||||
We allow `Arg` sections to have an `ENUM` data type and add a `possibleValues` property to define the possible values
|
||||
for this arg. The ordinal number of the enum is the same as its position within the `possibleValues` list starting from
|
||||
`0`.
|
||||
|
||||
A runtime check on op construction, will ensure that each enum arg has one or more possible values, and that default
|
||||
values match one of the possible values (if applicable).
|
||||
|
||||
On code generation, an appropriate representation of this enum will be generated in the target language. The name of
|
||||
the generated enum will be derived from the name of the arg.
|
||||
|
||||
### Example
|
||||
```kotlin
|
||||
Arg(ENUM, "padMode"){
|
||||
possibleValues = listOf("CONSTANT", "REFLECT", "SYMMETRIC")
|
||||
description = "padding mode"
|
||||
}
|
||||
```
|
||||
|
||||
## Consequences
|
||||
|
||||
### Advantages
|
||||
* We get easily understandable names for otherwise in-transparent ordinal mode modifiers
|
||||
|
||||
### Disadvantages
|
||||
* The defined enum can only be used for a single op
|
||||
* The defined enum is only usable with a single arg
|
||||
@@ -0,0 +1,81 @@
|
||||
# Configuration Objects
|
||||
|
||||
## Status
|
||||
|
||||
ACCEPTED
|
||||
|
||||
Discussed by Alex Black, raver119 and Paul Dubs on 27. November 2019.
|
||||
|
||||
## Context
|
||||
Some Ops (esp. convolution) have many parameters. Many of them can have reasonable defaults, but even then creating
|
||||
signatures for evey reasonable configuration may be impossible, as those signatures would require different naming in
|
||||
order to be actually distinguishable from each other.
|
||||
|
||||
In other cases, an op may have a lot of same typed parameters that are required (e.g. GRU, LSTM, SRU) but it is very
|
||||
easy to mix them up.
|
||||
|
||||
For both of those cases (many optional parameters, easily mixed up required parameters) it is reasonable to use a
|
||||
config holder with builder pattern in languages that do not support named or default parameters.
|
||||
|
||||
In our current codebase those configurations are often used across several related ops.
|
||||
|
||||
|
||||
## Decision
|
||||
We add a `Config("name"){ ... }` section to the namespace context. It supports `Input` and `Arg` definitions in the same
|
||||
way that `Op` does.
|
||||
|
||||
Ops that want to use that config can use `useConfig(conf)`. As configs are often reused across related objects, this
|
||||
will have the effect of a mixin: All inputs and args defined in that config will also be automatically defined on that
|
||||
Op. If there is a naming conflict, an exception will be thrown at construction time.
|
||||
|
||||
For default signatures, configs will be passed at the end, in the order that they were added to the Op.
|
||||
|
||||
If other signatures are desired, configs, like regular inputs and args, can be passed to `Signature`.
|
||||
|
||||
In languages that do not support default or named parameters, a config holder will be created, that will take the
|
||||
parameters of the config using a builder pattern. For languages with default and named parameters, no additional config
|
||||
holder will be created, and the parameters of the config will be treated as if they were directly configured on the Op.
|
||||
|
||||
### Example
|
||||
This example shows a very simple case in order to highlight how this feature would be used.
|
||||
```kotlin
|
||||
fun RNN() = Namespace("RNN"){
|
||||
val sruWeights = Config("SRUWeights"){
|
||||
Input(FLOATING_POINT, "weights"){ description = "Weights, with shape [inSize, 3*inSize]" }
|
||||
Input(FLOATING_POINT, "bias"){ description = "Biases, with shape [2*inSize]" }
|
||||
}
|
||||
|
||||
Op("SRU"){
|
||||
Input(FLOATING_POINT, "x"){ description = "..." }
|
||||
Input(FLOATING_POINT, "initialC"){ description = "..." }
|
||||
Input(FLOATING_POINT, "mask"){ description = "..." }
|
||||
|
||||
useConfig(sruWeights)
|
||||
|
||||
Output(FLOATING_POINT, "out"){ description = "..." }
|
||||
}
|
||||
|
||||
Op("SRUCell"){
|
||||
val x = Input(FLOATING_POINT, "x"){ description = "..." }
|
||||
val cLast = Input(FLOATING_POINT, "cLast"){ description = "..." }
|
||||
|
||||
val conf = useConfig(sruWeights)
|
||||
|
||||
Output(FLOATING_POINT, "out"){ description = "..." }
|
||||
|
||||
// Just for demonstration purposes
|
||||
Signature(x, cLast, conf)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Consequences
|
||||
|
||||
### Advantages
|
||||
* Ops that share parameters can make that sharing explicit
|
||||
* Easier definition of related ops with common parameters
|
||||
* Simplifies usage of complex ops in languages without named parameters
|
||||
|
||||
### Disadvantages
|
||||
* Not all parameters are defined directly within an op anymore
|
||||
* Configs are not shareable across namespaces
|
||||
@@ -0,0 +1,89 @@
|
||||
# Inheritance
|
||||
|
||||
## Status
|
||||
ACCEPTED
|
||||
|
||||
Discussed by Alex Black and Paul Dubs on 29. November 2019 and 2. December 2019.
|
||||
|
||||
## Context
|
||||
In many cases ops have a similar interface. For example all transform ops take a single input, but some of them take
|
||||
additional arguments; all pairwise ops take two inputs, and so on. The documentation of those ops is often the result
|
||||
of copy & paste with just a few little modifications, and changing anything later on suddenly becomes a huge undertaking
|
||||
because what should effectively be a change in a single place, has to be changed in many places.
|
||||
|
||||
Another issue that copy & paste based definitions bring to the table is that this practice effectively makes any
|
||||
relationship between those ops implicit.
|
||||
|
||||
When defining ops with the DSL we prefer to make things as explicit as possible, while also reducing repetition and
|
||||
boilerplate.
|
||||
|
||||
The existing inheritance mechanism, added without a formal proposal at the beginning of the project, allows the
|
||||
definition of an abstract base op. The new op based on it, can copy any of the given parts before it gets to define its
|
||||
own properties. This approach has the problem that we can't inherit from multiple base ops, and that we do not get any
|
||||
direct access to its fields for ease of use.
|
||||
|
||||
# Decision
|
||||
We introduce an explicit mixin mechanism `Mixin("name") {...}` which can define any parts of any op, but isn't an Op
|
||||
definition on its own. Mixins can be defined at top-level, thereby being usable across namespaces.
|
||||
|
||||
A mixin is mixed into an op with `useMixin(mixinReference, ...options...)` within an op context. It will add all (if
|
||||
not otherwise configured) definitions of the mixin to the current op as if they were copied into its place.
|
||||
If `useMixin(ref)` is used as the first thing within an op definition, then it will behave exactly like the old
|
||||
inheritance mechanism.
|
||||
|
||||
`useMixin(ref)` returns a holder object, that can be used to reference its parameters.
|
||||
|
||||
The available options on `useMixin` are `keepInputs`, `keepArgs`, `keepOutputs`, `keepSignatures`, `keepDoc`,
|
||||
`keepConstraints`. They default to `true`.
|
||||
|
||||
If there is a naming conflict between mixins or between mixin and op definition, the last definition wins.
|
||||
|
||||
### Example
|
||||
```kotlin
|
||||
|
||||
val indexAccum = Mixin("indexAccum"){
|
||||
legacy = true
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.indexaccum"
|
||||
val input = Input(NUMERIC, "in") { description = "Input variable" }
|
||||
val keepDims = Arg(BOOL, "keepDims") { description = "If true: keep the dimensions that are reduced on (as length 1). False: remove the reduction dimensions"; defaultValue = false }
|
||||
val dims = Arg(INT, "dimensions"){ count = AtLeast(1); description = "Dimensions to reduce over. If dimensions are not specified, full array reduction is performed" }
|
||||
Output(NUMERIC, "output"){ description = "Reduced array of rank (input rank - num dimensions)" }
|
||||
|
||||
Signature(input, dims)
|
||||
AllParamSignature(withOutput = false)
|
||||
}
|
||||
|
||||
|
||||
Namespace("math"){
|
||||
Op("firstIndex") {
|
||||
val idxAccum = useMixin(indexAccum, keepSignatures=false)
|
||||
var c = Arg(CONDITION, "condition") { description = "Condition to check on input variable" }
|
||||
Signature(idxAccum.input("in"), c, idxAccum.arg("dimensions"))
|
||||
Signature(idxAccum.input("in"), c, idxAccum.arg("keepDims"), idxAccum.arg("dimensions"))
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
First index reduction operation.
|
||||
Returns a variable that contains the index of the first element that matches the specified condition (for each
|
||||
slice along the specified dimensions)
|
||||
Note that if keepDims = true, the output variable has the same rank as the input variable,
|
||||
with the reduced dimensions having size 1. This can be useful for later broadcast operations (such as subtracting
|
||||
the mean along a dimension).
|
||||
Example: if input has shape [a,b,c] and dimensions=[1] then output has shape:
|
||||
keepDims = true: [a,1,c]
|
||||
keepDims = false: [a,c]
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Consequences
|
||||
### Advantages
|
||||
* We can have multiple inheritance
|
||||
* We can share op similarities across namespaces
|
||||
* We get explicit access to parameters defined in mixins
|
||||
|
||||
### Disadvantages
|
||||
* We have to adapt our current usage
|
||||
@@ -0,0 +1,72 @@
|
||||
# Aliasing
|
||||
|
||||
## Status
|
||||
ACCEPTED
|
||||
|
||||
Discussed by Alex Black and Paul Dubs on 05. March 2020.
|
||||
|
||||
|
||||
## Context
|
||||
The API surface area is very large over all those namespaces. For consistency with our previous manually created API we want to be able to alias ops into different namespaces. Those aliases are meant as a convenience for users, so they can find ops more easily.
|
||||
|
||||
# Decision
|
||||
We introduce an aliasing mechanism `Alias(NamespaceName().op("opName"))` at the Namespace level, which can reference an op directly.
|
||||
|
||||
When an op is aliased like that, all of that ops signatures will be available in the referencing namespace. However, they will get an additional note in their documentation saying that it is an alias to the original op. In addition, the implementation of an alias signature, is a direct call of the same signature in the original namespace.
|
||||
|
||||
It is not allowed to alias an op from a namespace that only has it because it has aliased it itself.
|
||||
|
||||
When the requested op is not part of the given namespace, trying to alias it will throw an OpNotFoundException.
|
||||
|
||||
### Example
|
||||
#### Definitions
|
||||
```kotlin
|
||||
fun BaseOps() = Namespace("BaseOps"){
|
||||
Op("mmul") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.reduce"
|
||||
Input(NUMERIC, "x") { description = "First input variable" }
|
||||
Input(NUMERIC, "y") { description = "Second input variable" }
|
||||
Arg(BOOL, "transposeX") { description = "Transpose x (first argument)"; defaultValue=false }
|
||||
Arg(BOOL, "transposeY") { description = "Transpose y (second argument)"; defaultValue=false }
|
||||
Arg(BOOL, "transposeZ") { description = "Transpose result array"; defaultValue=false }
|
||||
Output(NUMERIC, "output"){ description = "" }
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Matrix multiplication: out = mmul(x,y)
|
||||
Supports specifying transpose argument to perform operation such as mmul(a^T, b), etc.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Linalg() = Namespace("Linalg"){
|
||||
Alias(BaseOps(), "mmul")
|
||||
}
|
||||
```
|
||||
|
||||
#### Output
|
||||
This output is meant as a possible example. It is not what it will look like exactly once this feature is implemented.
|
||||
|
||||
```java
|
||||
public class Linalg{
|
||||
/**
|
||||
* Matrix multiplication: out = mmul(x,y)<br>
|
||||
* Supports specifying transpose argument to perform operation such as mmul(a^T, b), etc. <br>
|
||||
*
|
||||
* Alias of basepackage.baseOps.mmul(x, y)<br>
|
||||
*
|
||||
* @param x First input variable
|
||||
* @param y Second input variable
|
||||
*/
|
||||
public static INDArray mmul(INDArray x, INDArray y){
|
||||
return basepakage.baseOps.mmul(x,y);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Consequences
|
||||
### Advantages
|
||||
* We can make the API more flexible by allowing ops to be available from other namespaces
|
||||
|
||||
### Disadvantages
|
||||
* The API becomes more crowded
|
||||
@@ -0,0 +1,283 @@
|
||||
###Interpreter
|
||||
|
||||
An interpreter takes a tensorflow or pytorch model and figure out how to map
|
||||
various ops. Their attributes and op names are mapped to libnd4j
|
||||
using information from the above op descriptor.
|
||||
|
||||
An interpreter can take in an individual op from tensorflow, onnx or
|
||||
another framework and translate it to an equivalent op in libnd4j represented
|
||||
as the equivalent op descriptor.
|
||||
|
||||
The usage is as follows:
|
||||
|
||||
|
||||
## Op def files
|
||||
|
||||
For each framework in tensorflow/onnx, we have inbuilt definition files
|
||||
for each tensorflow and pytorch.
|
||||
|
||||
For onnx, we have an onnx.pbtxt generated by the dl4j-dev tools submodule
|
||||
onnx-defs. This definition file has each op serialized as an [onnx NodeProto](https://github.com/onnx/onnx/blob/25fd2c332cf854fd38a92aa8f60d232530ab0065/onnx/onnx-ml.proto#L193)
|
||||
For tensorflow, we have an ops.proto pulled from tensorflow's official repo.
|
||||
|
||||
We use these files to map operation attributes serialized by
|
||||
nd4j's generated operation definition tool found in dl4j-dev-tools
|
||||
to their equivalents in tensorflow and pytorch.
|
||||
|
||||
An interpreter has 2 methods:
|
||||
|
||||
|
||||
```java
|
||||
Interpreter interpreter = ...;
|
||||
|
||||
OpDescriptor descriptor = interpreter.interpretTensorflow(nodeFromOtherFramework);
|
||||
OpDescriptor descriptor = interpreter.interpretOnnx(nodeFromOtherFramework);
|
||||
|
||||
//proceed to use descriptor to map for model import...
|
||||
```
|
||||
|
||||
##Interpreter file format
|
||||
|
||||
An interpreter is language neutral. We have a mini syntax
|
||||
for mapping attributes from one format to another.
|
||||
|
||||
Through indexing every attribute and input/output in libnd4j,
|
||||
we can maintain an index of operation names and attributes
|
||||
with a mapping syntax. If we want to map a trivial operation like say:
|
||||
Abs, let's compare tensorflow, onnx and the descriptor in nd4j.
|
||||
|
||||
Tensorflow:
|
||||
```prototext
|
||||
op {
|
||||
name: "Floor"
|
||||
input_arg {
|
||||
name: "x"
|
||||
type_attr: "T"
|
||||
}
|
||||
output_arg {
|
||||
name: "y"
|
||||
type_attr: "T"
|
||||
}
|
||||
attr {
|
||||
name: "T"
|
||||
type: "type"
|
||||
allowed_values {
|
||||
list {
|
||||
type: DT_BFLOAT16
|
||||
type: DT_HALF
|
||||
type: DT_FLOAT
|
||||
type: DT_DOUBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Onnx:
|
||||
```prototext
|
||||
input: "X"
|
||||
output: "Y"
|
||||
name: "Floor"
|
||||
op_type: "Floor"
|
||||
attribute {
|
||||
name: "X-types"
|
||||
strings: "float"
|
||||
strings: "float16"
|
||||
strings: "double"
|
||||
type: STRINGS
|
||||
}
|
||||
doc_string: "\nFloor takes one input data (Tensor<T>) and produces one output data\n(Tensor<T>) where the floor is, y = floor(x), is applied to\nthe tensor elementwise.\n"
|
||||
```
|
||||
|
||||
The op descriptor for libnd4j is:
|
||||
```
|
||||
OpDeclarationDescriptor(name=Floor, nIn=1, nOut=1, tArgs=0, iArgs=0, inplaceAble=true, inArgNames=[first], outArgNames=[z], tArgNames=[], iArgNames=[], bArgNames=[], opDeclarationType=OP_IMPL)
|
||||
```
|
||||
|
||||
Floor is a fairly simple op with 1 input and 1 output.
|
||||
Inputs and outputs are implicitly tensors.
|
||||
This is true for both onnx and tensorflow.
|
||||
|
||||
Tensorflow has an attribute defined for valid types.
|
||||
The way we generated the onnx schema proto, we have something equivalent
|
||||
that allows for a list of types presented as a string.
|
||||
|
||||
|
||||
Mapping a descriptor happens based on attribute.
|
||||
An example of abs below:
|
||||
|
||||
```prototext
|
||||
floor {
|
||||
tensorflow_mapping: {
|
||||
input_mappings: {
|
||||
input_mapping {
|
||||
first: "x"
|
||||
}
|
||||
|
||||
}
|
||||
output_mappings: {
|
||||
z: "y"
|
||||
}
|
||||
|
||||
attribute_mapping_functions: {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
onnx_mapping {
|
||||
input_mappings {
|
||||
first: "X"
|
||||
}
|
||||
output_mappings {
|
||||
z: "Y"
|
||||
}
|
||||
|
||||
attribute_mapping_functions {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now we can compare this to Convolution.
|
||||
In tensorflow, the convolution op is represented as:
|
||||
```prototext
|
||||
op {
|
||||
name: "Conv2D"
|
||||
input_arg {
|
||||
name: "input"
|
||||
type_attr: "T"
|
||||
}
|
||||
input_arg {
|
||||
name: "filter"
|
||||
type_attr: "T"
|
||||
}
|
||||
output_arg {
|
||||
name: "output"
|
||||
type_attr: "T"
|
||||
}
|
||||
attr {
|
||||
name: "T"
|
||||
type: "type"
|
||||
allowed_values {
|
||||
list {
|
||||
type: DT_HALF
|
||||
type: DT_BFLOAT16
|
||||
type: DT_FLOAT
|
||||
type: DT_DOUBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
attr {
|
||||
name: "strides"
|
||||
type: "list(int)"
|
||||
}
|
||||
attr {
|
||||
name: "use_cudnn_on_gpu"
|
||||
type: "bool"
|
||||
default_value {
|
||||
b: true
|
||||
}
|
||||
}
|
||||
attr {
|
||||
name: "padding"
|
||||
type: "string"
|
||||
allowed_values {
|
||||
list {
|
||||
s: "SAME"
|
||||
s: "VALID"
|
||||
}
|
||||
}
|
||||
}
|
||||
attr {
|
||||
name: "data_format"
|
||||
type: "string"
|
||||
default_value {
|
||||
s: "NHWC"
|
||||
}
|
||||
allowed_values {
|
||||
list {
|
||||
s: "NHWC"
|
||||
s: "NCHW"
|
||||
}
|
||||
}
|
||||
}
|
||||
attr {
|
||||
name: "dilations"
|
||||
type: "list(int)"
|
||||
default_value {
|
||||
list {
|
||||
i: 1
|
||||
i: 1
|
||||
i: 1
|
||||
i: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
In onnx, it's represented as:
|
||||
```prototext
|
||||
input: "X"
|
||||
input: "W"
|
||||
input: "B"
|
||||
output: "Y"
|
||||
name: "Conv"
|
||||
op_type: "Conv"
|
||||
attribute {
|
||||
name: "auto_pad"
|
||||
s: "NOTSET"
|
||||
type: STRING
|
||||
}
|
||||
attribute {
|
||||
name: "dilations"
|
||||
s: ""
|
||||
type: INTS
|
||||
}
|
||||
attribute {
|
||||
name: "group"
|
||||
i: 1
|
||||
type: INT
|
||||
}
|
||||
attribute {
|
||||
name: "kernel_shape"
|
||||
s: ""
|
||||
type: INTS
|
||||
}
|
||||
attribute {
|
||||
name: "pads"
|
||||
s: ""
|
||||
type: INTS
|
||||
}
|
||||
attribute {
|
||||
name: "strides"
|
||||
s: ""
|
||||
type: INTS
|
||||
}
|
||||
attribute {
|
||||
name: "X-types"
|
||||
strings: "double"
|
||||
strings: "float"
|
||||
strings: "float16"
|
||||
type: STRINGS
|
||||
}
|
||||
attribute {
|
||||
name: "W-types"
|
||||
strings: "double"
|
||||
strings: "float"
|
||||
strings: "float16"
|
||||
type: STRINGS
|
||||
}
|
||||
attribute {
|
||||
name: "B-types"
|
||||
strings: "double"
|
||||
strings: "float"
|
||||
strings: "float16"
|
||||
type: STRINGS
|
||||
}
|
||||
doc_string: "\nThe convolution operator consumes an input tensor and a filter, and\ncomputes the output."
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
if test "$#" -eq 0; then
|
||||
echo "No namespaces were specified. One or more namespaces must be provided as an argument"
|
||||
echo "Usage example 1 (single namespace): ./generate.sh math"
|
||||
echo "Usage example 2 (multiple namespaces): ./generate.sh math,random"
|
||||
echo "Usage example 2 (all namespaces): ./generate.sh all"
|
||||
else
|
||||
mvn clean package -DskipTests
|
||||
mvn exec:java -Dexec.mainClass=org.nd4j.codegen.cli.CLI -Dexec.args="-dir ../../ -namespaces \"$@\""
|
||||
fi
|
||||
@@ -0,0 +1,257 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.eclipse.deeplearning4j</groupId>
|
||||
<artifactId>op-codegen</artifactId>
|
||||
|
||||
|
||||
<parent>
|
||||
<groupId>org.eclipse.deeplearning4j</groupId>
|
||||
<artifactId>codegen</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<commonsio.version>2.7</commonsio.version>
|
||||
<commons.lang.version>3.12.0</commons.lang.version>
|
||||
<commons.dbutils.version>1.7</commons.dbutils.version>
|
||||
<lombok.version>1.18.42</lombok.version>
|
||||
<logback.version>1.2.3</logback.version>
|
||||
<junit.version>5.8.0-M1</junit.version>
|
||||
<junit.platform.launcher.version>1.8.0-M1</junit.platform.launcher.version>
|
||||
<junit-jupiter.version>5.4.2</junit-jupiter.version>
|
||||
<java.version>11</java.version>
|
||||
<maven-shade-plugin.version>3.5.1</maven-shade-plugin.version>
|
||||
<kotlin.compiler.jvmTarget>11</kotlin.compiler.jvmTarget>
|
||||
<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
|
||||
<javapoet.version>1.13.0</javapoet.version>
|
||||
<picocli.version>4.6.3</picocli.version>
|
||||
<kotlin.version>1.9.24</kotlin.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>info.picocli</groupId>
|
||||
<artifactId>picocli</artifactId>
|
||||
<version>${picocli.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>info.picocli</groupId>
|
||||
<artifactId>picocli</artifactId>
|
||||
<version>${picocli.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.28</version>
|
||||
</dependency>
|
||||
<!-- Redirect jackson to slf4j. -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>log4j-over-slf4j</artifactId>
|
||||
<version>1.7.28</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>${commonsio.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons.lang.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup</groupId>
|
||||
<artifactId>javapoet</artifactId>
|
||||
<version>${javapoet.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Test Dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>${junit-jupiter.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>${junit-jupiter.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-common</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-test</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.module</groupId>
|
||||
<artifactId>jackson-module-kotlin</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.beust</groupId>
|
||||
<artifactId>jcommander</artifactId>
|
||||
<version>1.78</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.deeplearning4j</groupId>
|
||||
<artifactId>nd4j-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-source</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals><goal>add-source</goal></goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>src/main/stubs</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>get-cpu-count</id>
|
||||
<goals>
|
||||
<goal>cpu-count</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<cpuCount>cpu.core.count</cpuCount>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>${maven-shade-plugin.version}</version>
|
||||
<configuration>
|
||||
<shadedArtifactAttached>true</shadedArtifactAttached>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>org/datanucleus/**</exclude>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
|
||||
</configuration>
|
||||
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>reference.conf</resource>
|
||||
</transformer>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer" />
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- https://kotlinlang.org/docs/reference/using-maven.html -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<executions>
|
||||
<!-- Replacing default-compile as it is treated specially by maven -->
|
||||
<execution>
|
||||
<id>default-compile</id>
|
||||
<phase>none</phase>
|
||||
</execution>
|
||||
<!-- Replacing default-testCompile as it is treated specially by maven -->
|
||||
<execution>
|
||||
<id>default-testCompile</id>
|
||||
<phase>none</phase>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>java-compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals> <goal>compile</goal> </goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>java-test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals> <goal>testCompile</goal> </goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen;
|
||||
|
||||
import org.nd4j.codegen.api.NamespaceOps;
|
||||
import org.nd4j.codegen.ops.*;
|
||||
|
||||
public enum Namespace {
|
||||
BITWISE,
|
||||
NEURALNETWORK,
|
||||
RANDOM,
|
||||
IMAGE,
|
||||
CNN,
|
||||
RNN,
|
||||
MATH,
|
||||
BASE,
|
||||
LOSS,
|
||||
LINALG;
|
||||
|
||||
|
||||
public static Namespace fromString(String in){
|
||||
switch (in.toLowerCase()){
|
||||
case "bitwise":
|
||||
return BITWISE;
|
||||
case "nn":
|
||||
case "neuralnetwork":
|
||||
return NEURALNETWORK;
|
||||
case "random":
|
||||
return RANDOM;
|
||||
case "math":
|
||||
return MATH;
|
||||
case "image":
|
||||
return IMAGE;
|
||||
case "cnn":
|
||||
return CNN;
|
||||
case "rnn":
|
||||
return RNN;
|
||||
case "base":
|
||||
return BASE;
|
||||
case "loss":
|
||||
return LOSS;
|
||||
case "linalg":
|
||||
return LINALG;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String javaClassName() {
|
||||
switch (this){
|
||||
case BITWISE:
|
||||
return "NDBitwise";
|
||||
case NEURALNETWORK:
|
||||
return "NDNN";
|
||||
case RANDOM:
|
||||
return "NDRandom";
|
||||
case IMAGE:
|
||||
return "NDImage";
|
||||
case CNN:
|
||||
return "NDCNN";
|
||||
case RNN:
|
||||
return "NDRNN";
|
||||
case MATH:
|
||||
return "NDMath";
|
||||
case BASE:
|
||||
return "NDBase";
|
||||
case LOSS:
|
||||
return "NDLoss";
|
||||
case LINALG:
|
||||
return "NDLinalg";
|
||||
}
|
||||
throw new IllegalStateException("No java class name defined for: " + this);
|
||||
}
|
||||
|
||||
public String javaSameDiffClassName() {
|
||||
switch (this){
|
||||
case BITWISE:
|
||||
return "SDBitwise";
|
||||
case NEURALNETWORK:
|
||||
return "SDNN";
|
||||
case RANDOM:
|
||||
return "SDRandom";
|
||||
case IMAGE:
|
||||
return "SDImage";
|
||||
case CNN:
|
||||
return "SDCNN";
|
||||
case RNN:
|
||||
return "SDRNN";
|
||||
case MATH:
|
||||
return "SDMath";
|
||||
case BASE:
|
||||
return "SDBaseOps";
|
||||
case LOSS:
|
||||
return "SDLoss";
|
||||
/*case VALIDATION:
|
||||
return "SDValidation";*/
|
||||
case LINALG:
|
||||
return "SDLinalg";
|
||||
}
|
||||
throw new IllegalStateException("No java SameDiff class name defined for: " + this);
|
||||
}
|
||||
|
||||
public NamespaceOps getNamespace() {
|
||||
switch (this){
|
||||
case BITWISE:
|
||||
return BitwiseKt.Bitwise();
|
||||
case RANDOM:
|
||||
return RandomKt.Random();
|
||||
case MATH:
|
||||
return MathKt.Math();
|
||||
case IMAGE:
|
||||
return ImageKt.SDImage();
|
||||
case CNN:
|
||||
return CNNKt.SDCNN();
|
||||
case RNN:
|
||||
return RNNKt.SDRNN();
|
||||
case NEURALNETWORK:
|
||||
return NeuralNetworkKt.NN();
|
||||
case BASE:
|
||||
return SDBaseOpsKt.SDBaseOps();
|
||||
case LOSS:
|
||||
return SDLossKt.SDLoss();
|
||||
case LINALG:
|
||||
return LinalgKt.Linalg();
|
||||
}
|
||||
throw new IllegalStateException("No namespace definition available for: " + this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.cli;
|
||||
|
||||
import com.beust.jcommander.IParameterValidator;
|
||||
import com.beust.jcommander.JCommander;
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.ParameterException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.nd4j.codegen.Namespace;
|
||||
import org.nd4j.codegen.api.NamespaceOps;
|
||||
import org.nd4j.codegen.impl.java.DocsGenerator;
|
||||
import org.nd4j.codegen.impl.java.Nd4jNamespaceGenerator;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Planned CLI for generating classes
|
||||
*/
|
||||
@Slf4j
|
||||
public class CLI {
|
||||
private static final String relativePath = "nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/src/main/java/";
|
||||
private static final String allProjects = "all";
|
||||
private static final String sdProject = "sd";
|
||||
private static final String ndProject = "nd4j";
|
||||
|
||||
public static class ProjectsValidator implements IParameterValidator {
|
||||
|
||||
@Override
|
||||
public void validate(String name, String value) throws ParameterException {
|
||||
if (name.equals("-projects")) {
|
||||
if (!(value.equals(allProjects) || value.equals(ndProject) || value.equals(sdProject))) {
|
||||
throw new ParameterException("Wrong projects " + value + " passed! Must be one of [all, sd, nd4j]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Parameter(names = "-dir", description = "Root directory of deeplearning4j mono repo")
|
||||
private String repoRootDir;
|
||||
|
||||
@Parameter(names = "-docsdir", description = "Root directory for generated docs")
|
||||
private String docsdir;
|
||||
|
||||
@Parameter(names = "-namespaces", description = "List of namespaces to generate, or 'ALL' to generate all namespaces", required = true)
|
||||
private List<String> namespaces;
|
||||
|
||||
@Parameter(names = "-projects", description = "List of sub-projects - ND4J, SameDiff or both", required = false, validateWith = ProjectsValidator.class)
|
||||
private List<String> projects = Collections.singletonList("all");
|
||||
|
||||
enum NS_PROJECT {
|
||||
ND4J,
|
||||
SAMEDIFF;
|
||||
}
|
||||
|
||||
private void generateNamespaces(NS_PROJECT project, File outputDir, String basePackage) throws IOException {
|
||||
|
||||
List<Namespace> usedNamespaces = new ArrayList<>();
|
||||
|
||||
for(String s : namespaces) {
|
||||
if ("all".equalsIgnoreCase(s)) {
|
||||
Collections.addAll(usedNamespaces, Namespace.values());
|
||||
break;
|
||||
}
|
||||
Namespace ns = null;
|
||||
if (project == NS_PROJECT.ND4J) {
|
||||
ns = Namespace.fromString(s);
|
||||
if (ns == null) {
|
||||
log.error("Invalid/unknown ND4J namespace provided: " + s);
|
||||
}
|
||||
else {
|
||||
usedNamespaces.add(ns);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ns = Namespace.fromString(s);
|
||||
if (ns == null) {
|
||||
log.error("Invalid/unknown SD namespace provided: " + s);
|
||||
}
|
||||
else {
|
||||
usedNamespaces.add(ns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int cnt = 0;
|
||||
for (int i = 0; i < usedNamespaces.size(); ++i) {
|
||||
Namespace ns = usedNamespaces.get(i);
|
||||
log.info("Starting generation of namespace: {}", ns);
|
||||
|
||||
String javaClassName = project == NS_PROJECT.ND4J ? ns.javaClassName() : ns.javaSameDiffClassName();
|
||||
NamespaceOps ops = ns.getNamespace();
|
||||
|
||||
String basePackagePath = basePackage.replace(".", "/") + "/ops/";
|
||||
|
||||
if (StringUtils.isNotEmpty(docsdir)) {
|
||||
DocsGenerator.generateDocs(i, ops, docsdir, basePackage);
|
||||
}
|
||||
if (outputDir != null) {
|
||||
File outputPath = new File(outputDir, basePackagePath + javaClassName + ".java");
|
||||
log.info("Output path: {}", outputPath.getAbsolutePath());
|
||||
|
||||
if (NS_PROJECT.ND4J == project)
|
||||
Nd4jNamespaceGenerator.generate(ops, null, outputDir, javaClassName, basePackage, docsdir);
|
||||
else
|
||||
Nd4jNamespaceGenerator.generate(ops, null, outputDir, javaClassName, basePackage,
|
||||
"org.nd4j.autodiff.samediff.ops.SDOps", docsdir);
|
||||
}
|
||||
++cnt;
|
||||
}
|
||||
log.info("Complete - generated {} namespaces", cnt);
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
new CLI().runMain(args);
|
||||
}
|
||||
|
||||
public void runMain(String[] args) throws Exception {
|
||||
JCommander.newBuilder()
|
||||
.addObject(this)
|
||||
.build()
|
||||
.parse(args);
|
||||
|
||||
// Either root directory for source code generation or docs directory must be present. If root directory is
|
||||
// absenbt - then it's "generate docs only" mode.
|
||||
if (StringUtils.isEmpty(repoRootDir) && StringUtils.isEmpty(docsdir)) {
|
||||
throw new IllegalStateException("Provide one or both of arguments : -dir, -docsdir");
|
||||
}
|
||||
File outputDir = null;
|
||||
if (StringUtils.isNotEmpty(repoRootDir)) {
|
||||
//First: Check root directory.
|
||||
File dir = new File(repoRootDir);
|
||||
if (!dir.exists() || !dir.isDirectory()) {
|
||||
throw new IllegalStateException("Provided root directory does not exist (or not a directory): " + dir.getAbsolutePath());
|
||||
}
|
||||
|
||||
outputDir = new File(dir, relativePath);
|
||||
if (!outputDir.exists() || !dir.isDirectory()) {
|
||||
throw new IllegalStateException("Expected output directory does not exist: " + outputDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
if(namespaces == null || namespaces.isEmpty()){
|
||||
throw new IllegalStateException("No namespaces were provided");
|
||||
}
|
||||
|
||||
try {
|
||||
if (projects == null)
|
||||
projects.add(allProjects);
|
||||
boolean forAllProjects = projects.isEmpty() || projects.contains(allProjects);
|
||||
if (forAllProjects || projects.contains(ndProject)) {
|
||||
generateNamespaces(NS_PROJECT.ND4J, outputDir, "org.nd4j.linalg.factory");
|
||||
}
|
||||
if (forAllProjects || projects.contains(sdProject)) {
|
||||
generateNamespaces(NS_PROJECT.SAMEDIFF, outputDir, "org.nd4j.autodiff.samediff");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.cli;
|
||||
|
||||
import com.beust.jcommander.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.nd4j.autodiff.samediff.SDVariable;
|
||||
import org.nd4j.codegen.Namespace;
|
||||
import org.nd4j.codegen.api.LossReduce;
|
||||
import org.nd4j.linalg.api.buffer.DataType;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Planned CLI for generating classes
|
||||
*/
|
||||
@Slf4j
|
||||
public class PicoCliCodeGen {
|
||||
private static final String relativePath = "nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/src/main/java/";
|
||||
private static final String allProjects = "all";
|
||||
|
||||
|
||||
@Parameter(names = "-dir", description = "Root directory of deeplearning4j mono repo")
|
||||
private String repoRootDir;
|
||||
|
||||
@Parameter(names = "-docsdir", description = "Root directory for generated docs")
|
||||
private String docsdir;
|
||||
|
||||
@Parameter(names = "-namespaces", description = "List of namespaces to generate, or 'ALL' to generate all namespaces", required = true)
|
||||
private List<String> namespaces;
|
||||
|
||||
|
||||
|
||||
private void generateNamespaces() {
|
||||
|
||||
List<Namespace> usedNamespaces = new ArrayList<>();
|
||||
|
||||
for (String s : namespaces) {
|
||||
if ("all".equalsIgnoreCase(s)) {
|
||||
Collections.addAll(usedNamespaces, Namespace.values());
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
CommandLine.Model.CommandSpec commandSpec = CommandLine.Model.CommandSpec.create();
|
||||
|
||||
int cnt = 0;
|
||||
for (int i = 0; i < usedNamespaces.size(); ++i) {
|
||||
Namespace ns = usedNamespaces.get(i);
|
||||
CommandLine.Model.CommandSpec subCommand = CommandLine.Model.CommandSpec.create();
|
||||
commandSpec.addSubcommand(ns.name(), subCommand);
|
||||
ns.getNamespace().getOps().forEach(op -> {
|
||||
CommandLine.Model.CommandSpec commandSpec1 = CommandLine.Model.CommandSpec.create();
|
||||
subCommand.addSubcommand(op.name(), commandSpec1);
|
||||
op.inputs().forEach(input -> {
|
||||
//TODO: Add SDVariable converter for picocli and figure out where to put that converter
|
||||
commandSpec1.addOption(CommandLine.Model.OptionSpec.builder("--" + input.getName())
|
||||
.type(SDVariable.class)
|
||||
.required(true)
|
||||
.description(input.getDescription())
|
||||
.build());
|
||||
});
|
||||
|
||||
op.getArgs().forEach(arg -> {
|
||||
CommandLine.Model.OptionSpec.Builder builder = CommandLine.Model.OptionSpec.builder("--" + arg.getName())
|
||||
.description(arg.getDescription());
|
||||
|
||||
switch (arg.getType()) {
|
||||
case INT:
|
||||
builder.type(Integer.class);
|
||||
break;
|
||||
case BOOL:
|
||||
builder.type(Boolean.class);
|
||||
break;
|
||||
case ENUM:
|
||||
break;
|
||||
case LONG:
|
||||
builder.type(Long.class);
|
||||
break;
|
||||
case STRING:
|
||||
builder.type(String.class);
|
||||
break;
|
||||
case NDARRAY:
|
||||
break;
|
||||
case NUMERIC:
|
||||
break;
|
||||
case CONDITION:
|
||||
break;
|
||||
case DATA_TYPE:
|
||||
builder.type(DataType.class);
|
||||
break;
|
||||
case LOSS_REDUCE:
|
||||
builder.type(LossReduce.class);
|
||||
break;
|
||||
case FLOATING_POINT:
|
||||
break;
|
||||
}
|
||||
|
||||
builder.required(arg.getDefaultValue() == null);
|
||||
|
||||
if (arg.getDefaultValue() != null) {
|
||||
builder.defaultValue(arg.getDefaultValue().toString());
|
||||
}
|
||||
|
||||
commandSpec1.addOption(builder.build());
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
log.info("Starting generation of namespace: {}", ns);
|
||||
|
||||
++cnt;
|
||||
}
|
||||
|
||||
|
||||
log.info("Complete - generated {} namespaces", cnt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
new CLI().runMain(args);
|
||||
}
|
||||
|
||||
public void runMain(String[] args) throws Exception {
|
||||
JCommander.newBuilder()
|
||||
.addObject(this)
|
||||
.build()
|
||||
.parse(args);
|
||||
|
||||
// Either root directory for source code generation or docs directory must be present. If root directory is
|
||||
// absenbt - then it's "generate docs only" mode.
|
||||
if (StringUtils.isEmpty(repoRootDir) && StringUtils.isEmpty(docsdir)) {
|
||||
throw new IllegalStateException("Provide one or both of arguments : -dir, -docsdir");
|
||||
}
|
||||
|
||||
File outputDir = null;
|
||||
if (StringUtils.isNotEmpty(repoRootDir)) {
|
||||
//First: Check root directory.
|
||||
File dir = new File(repoRootDir);
|
||||
if (!dir.exists() || !dir.isDirectory()) {
|
||||
throw new IllegalStateException("Provided root directory does not exist (or not a directory): " + dir.getAbsolutePath());
|
||||
}
|
||||
|
||||
outputDir = new File(dir, relativePath);
|
||||
if (!outputDir.exists() || !dir.isDirectory()) {
|
||||
throw new IllegalStateException("Expected output directory does not exist: " + outputDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
if(namespaces == null || namespaces.isEmpty() ) {
|
||||
throw new IllegalStateException("No namespaces were provided");
|
||||
}
|
||||
|
||||
generateNamespaces();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.impl.cpp;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.nd4j.codegen.api.*;
|
||||
import org.nd4j.codegen.api.generator.Generator;
|
||||
import org.nd4j.codegen.api.generator.GeneratorConfig;
|
||||
import org.nd4j.codegen.util.GenUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A very simple, manual CPP generator
|
||||
* As per Python, this could be implemented using a templating library such as freemarker
|
||||
*/
|
||||
public class CppGenerator implements Generator {
|
||||
@Override
|
||||
public Language language() {
|
||||
return Language.CPP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateNamespaceNd4j(NamespaceOps namespace, GeneratorConfig config, File directory, String fileName) throws IOException {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("#include <NDArrayFactory.h>\n\n")
|
||||
.append("namespace nd4j {\n");
|
||||
|
||||
append(4, sb, "namespace " + namespace.getName().toLowerCase());
|
||||
|
||||
List<Op> ops = new ArrayList<>();
|
||||
for(Op o : namespace.getOps()){
|
||||
if(o.isAbstract())
|
||||
continue;
|
||||
ops.add(o);
|
||||
}
|
||||
|
||||
//TODO: handle includes
|
||||
|
||||
for(Op o : ops){
|
||||
String s = generateFunction(o);
|
||||
sb.append(GenUtil.addIndent(s, 8));
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
append(4, sb, "}");
|
||||
sb.append("}");
|
||||
|
||||
//TODO generate header also
|
||||
|
||||
String out = sb.toString();
|
||||
File outFile = new File(directory, GenUtil.ensureFirstIsCap(namespace.getName()) + ".cpp");
|
||||
FileUtils.writeStringToFile(outFile, out, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
protected static void append(int indent, StringBuilder sb, String line){
|
||||
sb.append(GenUtil.repeat(" ", indent))
|
||||
.append(line)
|
||||
.append("\n");
|
||||
}
|
||||
|
||||
protected static String generateFunction(Op op){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
List<Output> outputs = op.getOutputs();
|
||||
boolean singleOut = outputs.size() == 1;
|
||||
if(singleOut){
|
||||
sb.append("NDArray* ");
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Multi-output op generation not yet implemented");
|
||||
}
|
||||
|
||||
sb.append(GenUtil.ensureFirstIsNotCap(op.getOpName())).append("(");
|
||||
|
||||
//Add inputs to signature
|
||||
boolean firstArg = true;
|
||||
if(op.getInputs() != null){
|
||||
for(Input i : op.getInputs()){
|
||||
if(!firstArg)
|
||||
sb.append(", ");
|
||||
|
||||
sb.append("NDArray* ").append(i.getName());
|
||||
|
||||
firstArg = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Add arguments and default args to signature
|
||||
sb.append("):\n");
|
||||
|
||||
|
||||
sb.append(" Context c(1);\n");
|
||||
int j=0;
|
||||
for(Input i : op.getInputs()){
|
||||
sb.append(" c.setInputArray(").append(j++).append(", ").append(i.getName()).append(");\n");
|
||||
}
|
||||
|
||||
sb.append("\n //TODO: args\n\n");
|
||||
|
||||
|
||||
sb.append(" nd4j::ops::").append(op.getLibnd4jOpName()).append(" op;\n");
|
||||
|
||||
sb.append(" ShapeList shapeList({");
|
||||
j = 0;
|
||||
for(Input i : op.getInputs()){
|
||||
if(j > 0)
|
||||
sb.append(",");
|
||||
sb.append(i.getName());
|
||||
j++;
|
||||
}
|
||||
|
||||
sb.append("});\n\n")
|
||||
.append(" auto outShape = op.calculateOutputShape(&shapeList, c);\n");
|
||||
|
||||
sb.append(" auto out = nullptr; //TODO\n\n")
|
||||
.append(" op.exec(c);\n")
|
||||
.append(" delete shapes;\n");
|
||||
|
||||
sb.append(" return out;\n")
|
||||
.append("}\n");
|
||||
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateNamespaceSameDiff(NamespaceOps namespace, GeneratorConfig config, File directory, String fileName) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.impl.java;
|
||||
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.TypeName;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.nd4j.codegen.api.*;
|
||||
import org.nd4j.codegen.api.doc.DocSection;
|
||||
import org.nd4j.codegen.api.doc.DocTokens;
|
||||
import org.nd4j.codegen.util.GenUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
import static org.nd4j.codegen.impl.java.Nd4jNamespaceGenerator.exactlyOne;
|
||||
|
||||
public class DocsGenerator {
|
||||
|
||||
// Markdown marker for start-end of code section
|
||||
private static final String MD_CODE = "```";
|
||||
// Javadoc constants which should be dropped or replaced for markdown generation
|
||||
private static final String JD_CODE = "{@code ";
|
||||
private static final String JD_CODE_END = "}";
|
||||
private static final String JD_INPUT_TYPE = "%INPUT_TYPE%";
|
||||
|
||||
public static class JavaDocToMDAdapter {
|
||||
private String current;
|
||||
|
||||
public JavaDocToMDAdapter(String original) {
|
||||
this.current = original;
|
||||
}
|
||||
|
||||
public JavaDocToMDAdapter filter(String pattern, String replaceWith) {
|
||||
String result = StringUtils.replace(current, pattern, replaceWith);
|
||||
this.current = result;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
private static String generateMethodText(Op op, Signature s, boolean isSameDiff, boolean isLoss, boolean withName) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
MethodSpec.Builder c = MethodSpec.methodBuilder(GenUtil.ensureFirstIsNotCap(op.getOpName()));
|
||||
List<Parameter> params = s.getParameters();
|
||||
List<Output> outs = op.getOutputs();
|
||||
String retType = "void";
|
||||
|
||||
if (outs.size() == 1) {
|
||||
retType = isSameDiff ? "SDVariable" : "INDArray";
|
||||
}
|
||||
else if (outs.size() >= 1) {
|
||||
retType = isSameDiff ? "SDVariable[]" : "INDArray[]";
|
||||
}
|
||||
sb.append(retType).append(" ").append(op.getOpName()).append("(");
|
||||
boolean first = true;
|
||||
for (Parameter param : params) {
|
||||
if (param instanceof Arg) {
|
||||
Arg arg = (Arg) param;
|
||||
if (!first)
|
||||
sb.append(", ");
|
||||
else if (withName)
|
||||
sb.append("String name, ");
|
||||
String className;
|
||||
if(arg.getType() == DataType.ENUM) {
|
||||
className = GenUtil.ensureFirstIsCap(arg.name());
|
||||
} else {
|
||||
TypeName tu = Nd4jNamespaceGenerator.getArgType(arg);
|
||||
className = tu.toString();
|
||||
}
|
||||
if(className.contains(".")){
|
||||
className = className.substring(className.lastIndexOf('.')+1);
|
||||
}
|
||||
sb.append(className).append(" ").append(arg.name());
|
||||
first = false;
|
||||
}
|
||||
else if (param instanceof Input) {
|
||||
Input arg = (Input) param;
|
||||
if (!first)
|
||||
sb.append(", ");
|
||||
else if (withName)
|
||||
sb.append("String name, ");
|
||||
sb.append(isSameDiff ? "SDVariable " : "INDArray ").append(arg.name());
|
||||
first = false;
|
||||
} else if(param instanceof Config){
|
||||
if(!first)
|
||||
sb.append(", ");
|
||||
Config conf = (Config)param;
|
||||
String name = conf.getName();
|
||||
sb.append(name).append(" ").append(GenUtil.ensureFirstIsNotCap(name));
|
||||
}
|
||||
}
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static StringBuilder buildDocSectionText(List<DocSection> docSections) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (DocSection ds : docSections) {
|
||||
//if(ds.applies(Language.JAVA, CodeComponent.OP_CREATOR)){
|
||||
String text = ds.getText();
|
||||
String[] lines = text.split("\n");
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
if (!lines[i].endsWith("<br>")) {
|
||||
String filteredLine = new JavaDocToMDAdapter(lines[i])
|
||||
.filter(JD_CODE, "`")
|
||||
.filter(JD_CODE_END, "`")
|
||||
.filter(JD_INPUT_TYPE, "INDArray").toString();
|
||||
|
||||
lines[i] = filteredLine + System.lineSeparator();
|
||||
}
|
||||
}
|
||||
text = String.join("\n", lines);
|
||||
sb.append(text).append(System.lineSeparator());
|
||||
//}
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
public static void generateDocs(int namespaceNum, NamespaceOps namespace, String docsDirectory, String basePackage) throws IOException {
|
||||
File outputDirectory = new File(docsDirectory);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String headerName = namespace.getName();
|
||||
if(headerName.startsWith("SD"))
|
||||
headerName = headerName.substring(2);
|
||||
|
||||
// File Header for Gitbook
|
||||
sb.append("---").append(System.lineSeparator());
|
||||
sb.append("title: ").append(headerName).append(System.lineSeparator());
|
||||
sb.append("short_title: ").append(headerName).append(System.lineSeparator());
|
||||
sb.append("description: ").append(System.lineSeparator());
|
||||
sb.append("category: Operations").append(System.lineSeparator());
|
||||
sb.append("weight: ").append(namespaceNum * 10).append(System.lineSeparator());
|
||||
sb.append("---").append(System.lineSeparator());
|
||||
|
||||
List<Op> ops = namespace.getOps();
|
||||
|
||||
ops.sort(Comparator.comparing(Op::getOpName));
|
||||
|
||||
if (ops.size() > 0)
|
||||
sb.append("# Operation classes").append(System.lineSeparator());
|
||||
for (Op op : ops) {
|
||||
sb.append("## ").append(op.getOpName()).append(System.lineSeparator());
|
||||
List<DocSection> doc = op.getDoc();
|
||||
if(!doc.isEmpty()) {
|
||||
boolean first = true;
|
||||
StringBuilder ndSignatures = new StringBuilder();
|
||||
StringBuilder sdSignatures = new StringBuilder();
|
||||
StringBuilder sdNameSignatures = new StringBuilder();
|
||||
for(Signature s : op.getSignatures()) {
|
||||
if (first) {
|
||||
Language lang = doc.get(0).getLanguage();
|
||||
sb.append(MD_CODE).append(lang.equals(Language.ANY) ? Language.JAVA : lang).append(System.lineSeparator());
|
||||
first = false;
|
||||
}
|
||||
String ndCode = generateMethodText(op, s, false, false, false);
|
||||
ndSignatures.append(ndCode).append(System.lineSeparator());
|
||||
String sdCode = generateMethodText(op, s, true, false, false);
|
||||
sdSignatures.append(sdCode).append(System.lineSeparator());
|
||||
String withNameCode = generateMethodText(op, s, true, false, true);
|
||||
sdNameSignatures.append(withNameCode).append(System.lineSeparator());
|
||||
}
|
||||
sb.append(ndSignatures.toString());
|
||||
sb.append(System.lineSeparator()); // New line between INDArray and SDVariable signatures
|
||||
|
||||
sb.append(sdSignatures.toString());
|
||||
sb.append(sdNameSignatures.toString());
|
||||
|
||||
sb.append(MD_CODE).append(System.lineSeparator());
|
||||
StringBuilder tsb = buildDocSectionText(doc);
|
||||
sb.append(tsb.toString());
|
||||
List<Signature> l = op.getSignatures();
|
||||
Set<Parameter> alreadySeen = new HashSet<>();
|
||||
for(Signature s : l) {
|
||||
List<Parameter> params = s.getParameters();
|
||||
for (Parameter p : params) {
|
||||
if(alreadySeen.contains(p)) continue;
|
||||
alreadySeen.add(p);
|
||||
if(p instanceof Input){
|
||||
Input i = (Input)p;
|
||||
sb.append("* **").append(i.getName()).append("** ").append(" (").append(i.getType()).append(") - ").append(i.getDescription() == null ? "" : DocTokens.processDocText(i.getDescription(),
|
||||
op, DocTokens.GenerationType.ND4J)).append(System.lineSeparator());
|
||||
}
|
||||
else if (p instanceof Config) {
|
||||
Config c = (Config)p;
|
||||
sb.append("* **").append(c.getName()).append("** - see ").append("[").append(c.getName()).append("]").append("(#").append(toAnchor(c.getName())).append(")").append(System.lineSeparator());
|
||||
}
|
||||
else if(p instanceof Arg) {
|
||||
Arg arg = (Arg) p;
|
||||
final Count count = arg.getCount();
|
||||
if (count == null || count.equals(exactlyOne)) {
|
||||
sb.append("* **").append(arg.getName()).append("** - ").append(arg.getDescription() == null ? "" : DocTokens.processDocText(arg.getDescription(),
|
||||
op, DocTokens.GenerationType.ND4J)); //.append(System.lineSeparator());
|
||||
} else {
|
||||
sb.append("* **").append(arg.getName()).append("** - ").append(arg.getDescription() == null ? "" : DocTokens.processDocText(arg.getDescription(),
|
||||
op, DocTokens.GenerationType.ND4J)).append(" (Size: ").append(count.toString()).append(")"); //.append(System.lineSeparator());
|
||||
}
|
||||
|
||||
Object defaultValue = arg.defaultValue();
|
||||
if(defaultValue != null){
|
||||
sb.append(" - default = ").append(formatDefaultValue(defaultValue));
|
||||
}
|
||||
|
||||
sb.append(System.lineSeparator());
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.append(System.lineSeparator());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (namespace.getConfigs().size() > 0)
|
||||
sb.append("# Configuration Classes").append(System.lineSeparator());
|
||||
for (Config config : namespace.getConfigs()) {
|
||||
sb.append("## ").append(config.getName()).append(System.lineSeparator());
|
||||
for (Input i : config.getInputs()) {
|
||||
sb.append("* **").append(i.getName()).append("**- ").append(i.getDescription()).append(" (").append(i.getType()).append(" type)");
|
||||
if (i.hasDefaultValue() && (i.defaultValue() != null))
|
||||
sb.append(" Default value:").append(formatDefaultValue(i.defaultValue())).append(System.lineSeparator());
|
||||
else
|
||||
sb.append(System.lineSeparator());
|
||||
}
|
||||
for (Arg arg : config.getArgs()) {
|
||||
sb.append("* **").append(arg.getName()).append("** ").append("(").append(arg.getType()).append(") - ").append(arg.getDescription());
|
||||
if (arg.hasDefaultValue() && (arg.defaultValue() != null))
|
||||
sb.append(" - default = ").append(formatDefaultValue(arg.defaultValue())).append(System.lineSeparator());
|
||||
else
|
||||
sb.append(System.lineSeparator());
|
||||
}
|
||||
StringBuilder tsb = buildDocSectionText(config.getDoc());
|
||||
sb.append(tsb.toString());
|
||||
sb.append(System.lineSeparator());
|
||||
for (Op op : ops) {
|
||||
if (op.getConfigs().contains(config)) {
|
||||
sb.append("Used in these ops: " + System.lineSeparator());
|
||||
break;
|
||||
}
|
||||
}
|
||||
ops.stream().filter(op -> op.getConfigs().contains(config)).forEach(op ->
|
||||
sb.append("[").append(op.getOpName()).append("]").append("(#").append(toAnchor(op.getOpName())).append(")").
|
||||
append(System.lineSeparator()));
|
||||
|
||||
}
|
||||
File outFile = new File(outputDirectory + "/operation-namespaces", "/" + namespace.getName().toLowerCase() + ".md");
|
||||
FileUtils.writeStringToFile(outFile, sb.toString(), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static String formatDefaultValue(Object v){
|
||||
if(v == null){ return "null"; }
|
||||
else if(v instanceof int[]){ return Arrays.toString((int[]) v); }
|
||||
else if(v instanceof long[]){ return Arrays.toString((long[]) v); }
|
||||
else if(v instanceof float[]){ return Arrays.toString((float[]) v); }
|
||||
else if(v instanceof double[]){ return Arrays.toString((double[]) v); }
|
||||
else if(v instanceof boolean[]){ return Arrays.toString((boolean[]) v); }
|
||||
else if(v instanceof Input){ return ((Input)v).getName(); }
|
||||
else if(v instanceof org.nd4j.linalg.api.buffer.DataType){ return "DataType." + v; }
|
||||
else if(v instanceof LossReduce || v instanceof org.nd4j.autodiff.loss.LossReduce){ return "LossReduce." + v; }
|
||||
else return v.toString();
|
||||
}
|
||||
|
||||
private static String toAnchor(String name){
|
||||
int[] codepoints = name.toLowerCase().codePoints().toArray();
|
||||
int type = Character.getType(codepoints[0]);
|
||||
StringBuilder anchor = new StringBuilder(new String(Character.toChars(codepoints[0])));
|
||||
for (int i = 1; i < codepoints.length; i++) {
|
||||
int curType = Character.getType(codepoints[i]);
|
||||
if(curType != type){
|
||||
anchor.append("-");
|
||||
}
|
||||
type = curType;
|
||||
anchor.append(new String(Character.toChars(codepoints[i])));
|
||||
}
|
||||
return anchor.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.impl.java;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.nd4j.codegen.api.Language;
|
||||
import org.nd4j.codegen.api.NamespaceOps;
|
||||
import org.nd4j.codegen.api.generator.Generator;
|
||||
import org.nd4j.codegen.api.generator.GeneratorConfig;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class JavaPoetGenerator implements Generator {
|
||||
|
||||
|
||||
@Override
|
||||
public Language language() {
|
||||
return Language.JAVA;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateNamespaceNd4j(NamespaceOps namespace, GeneratorConfig config, File directory, String className) throws java.io.IOException {
|
||||
Nd4jNamespaceGenerator.generate(namespace, config, directory, className, "org.nd4j.linalg.factory", StringUtils.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateNamespaceSameDiff(NamespaceOps namespace, GeneratorConfig config, File directory, String className) throws java.io.IOException {
|
||||
//throw new UnsupportedOperationException("Not yet implemented");
|
||||
Nd4jNamespaceGenerator.generate(namespace, config, directory, className, "org.nd4j.autodiff.samediff", StringUtils.EMPTY);
|
||||
}
|
||||
}
|
||||
+989
@@ -0,0 +1,989 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.impl.java;
|
||||
|
||||
import com.squareup.javapoet.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.nd4j.autodiff.samediff.SDVariable;
|
||||
import org.nd4j.autodiff.samediff.SameDiff;
|
||||
import org.nd4j.autodiff.samediff.ops.SDOps;
|
||||
import org.nd4j.autodiff.samediff.ops.SDValidation;
|
||||
import org.nd4j.codegen.api.*;
|
||||
import org.nd4j.codegen.api.doc.DocSection;
|
||||
import org.nd4j.codegen.api.doc.DocTokens;
|
||||
import org.nd4j.codegen.api.generator.ConstraintCodeGenerator;
|
||||
import org.nd4j.codegen.api.generator.GeneratorConfig;
|
||||
import org.nd4j.codegen.util.GenUtil;
|
||||
import org.nd4j.common.base.Preconditions;
|
||||
import org.nd4j.linalg.api.ndarray.INDArray;
|
||||
import org.nd4j.linalg.factory.NDValidation;
|
||||
import org.nd4j.linalg.factory.Nd4j;
|
||||
import org.nd4j.linalg.indexing.conditions.Condition;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
public class Nd4jNamespaceGenerator {
|
||||
private static Map<DataType, Class<?>> typeMapping = new HashMap<>();
|
||||
private static Map<DataType, String> validationMapping = new HashMap<>();
|
||||
private static Map<Arg, TypeName> enumMapping = new HashMap<>();
|
||||
private static Map<Config, TypeName> configMapping = new HashMap<>();
|
||||
public static Count exactlyOne = new Exactly(1);
|
||||
private static String copyright =
|
||||
"/*\n" +
|
||||
" * ******************************************************************************\n" +
|
||||
" * *\n" +
|
||||
" * *\n" +
|
||||
" * * This program and the accompanying materials are made available under the\n" +
|
||||
" * * terms of the Apache License, Version 2.0 which is available at\n" +
|
||||
" * * https://www.apache.org/licenses/LICENSE-2.0.\n" +
|
||||
" * *\n" +
|
||||
" * * See the NOTICE file distributed with this work for additional\n" +
|
||||
" * * information regarding copyright ownership.\n" +
|
||||
" * * Unless required by applicable law or agreed to in writing, software\n" +
|
||||
" * * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n" +
|
||||
" * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n" +
|
||||
" * * License for the specific language governing permissions and limitations\n" +
|
||||
" * * under the License.\n" +
|
||||
" * *\n" +
|
||||
" * * SPDX-License-Identifier: Apache-2.0\n" +
|
||||
" * *****************************************************************************\n" +
|
||||
" */\n";
|
||||
private static String codeGenWarning =
|
||||
"\n//================== GENERATED CODE - DO NOT MODIFY THIS FILE ==================\n\n";
|
||||
|
||||
static {
|
||||
typeMapping.put(DataType.BOOL, boolean.class);
|
||||
typeMapping.put(DataType.FLOATING_POINT, double.class);
|
||||
typeMapping.put(DataType.NUMERIC, double.class);
|
||||
typeMapping.put(DataType.INT, int.class);
|
||||
typeMapping.put(DataType.LONG, long.class);
|
||||
typeMapping.put(DataType.DATA_TYPE, org.nd4j.linalg.api.buffer.DataType.class);
|
||||
typeMapping.put(DataType.LOSS_REDUCE, org.nd4j.autodiff.loss.LossReduce.class);
|
||||
typeMapping.put(DataType.CONDITION, Condition.class);
|
||||
typeMapping.put(DataType.STRING, String.class);
|
||||
|
||||
validationMapping.put(DataType.BOOL, "validateBool");
|
||||
validationMapping.put(DataType.FLOATING_POINT, "validateFloatingPoint");
|
||||
validationMapping.put(DataType.NUMERIC, "validateNumerical");
|
||||
validationMapping.put(DataType.INT, "validateInteger");
|
||||
validationMapping.put(DataType.LONG, "validateInteger");
|
||||
}
|
||||
|
||||
private static ConstraintCodeGenerator constraintCodeGenerator = new JavaConstraintCodeGenerator();
|
||||
|
||||
private Nd4jNamespaceGenerator() { }
|
||||
|
||||
public static void generate(NamespaceOps namespace, GeneratorConfig config, File outputDirectory, String className,
|
||||
String basePackage, String docsDirectory) throws IOException {
|
||||
//String basePackage = "org.nd4j.linalg.factory";
|
||||
|
||||
generateEnums(outputDirectory, basePackage);
|
||||
generateConfigs(outputDirectory, basePackage);
|
||||
try {
|
||||
generateOpFactory(namespace, outputDirectory, className, basePackage, StringUtils.EMPTY);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static void generate(NamespaceOps namespace, GeneratorConfig config, File outputDirectory, String className,
|
||||
String basePackage, String parentClass, String docsDirectory) throws IOException {
|
||||
//String basePackage = "org.nd4j.linalg.factory";
|
||||
|
||||
generateEnums(outputDirectory, basePackage);
|
||||
generateConfigs(outputDirectory, basePackage);
|
||||
try {
|
||||
generateOpFactory(namespace, outputDirectory, className, basePackage, parentClass);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void generateOpFactory(NamespaceOps namespace, File outputDirectory, String className, String basePackage,
|
||||
String parentClass) throws IOException, ClassNotFoundException {
|
||||
boolean isBaseSameDiff = StringUtils.equals("SDBaseOps", className);
|
||||
boolean isSameDiff = StringUtils.isNotEmpty(parentClass);
|
||||
boolean isLoss = StringUtils.equals("SDLoss", className);
|
||||
|
||||
TypeSpec.Builder builder = !isSameDiff || isBaseSameDiff ?
|
||||
TypeSpec.classBuilder(className)
|
||||
.addModifiers(Modifier.PUBLIC) :
|
||||
|
||||
TypeSpec.classBuilder(className)
|
||||
.superclass(Class.forName(parentClass))
|
||||
.addModifiers(Modifier.PUBLIC);
|
||||
|
||||
if (isSameDiff && !isBaseSameDiff) {
|
||||
addSameDiffConstructor(builder);
|
||||
}
|
||||
else if (isBaseSameDiff) {
|
||||
builder.addField(TypeName.get(SameDiff.class), "sd", Modifier.PROTECTED);
|
||||
addBaseSameDiffConstructor(builder);
|
||||
}
|
||||
else
|
||||
addDefaultConstructor(builder);
|
||||
|
||||
//Add ops
|
||||
namespace.getOps()
|
||||
.stream()
|
||||
.filter(it -> !it.isAbstract())
|
||||
.sorted(Comparator.comparing(Op::getOpName))
|
||||
.forEachOrdered(o -> generateMethods(builder, o, isSameDiff, isLoss));
|
||||
|
||||
|
||||
TypeSpec ts = builder.build();
|
||||
|
||||
final String opsPackage = basePackage + ".ops";
|
||||
JavaFile jf = StringUtils.isEmpty(parentClass) ?
|
||||
|
||||
JavaFile.builder(opsPackage, ts)
|
||||
.addStaticImport(NDValidation.class, "isSameType")
|
||||
.build() :
|
||||
|
||||
JavaFile.builder(opsPackage, ts)
|
||||
.addStaticImport(SDValidation.class, "isSameType")
|
||||
.build();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(copyright);
|
||||
sb.append(codeGenWarning);
|
||||
jf.writeTo(sb);
|
||||
|
||||
File outFile = new File(outputDirectory, packageToDirectory(opsPackage) + "/" + className + ".java");
|
||||
FileUtils.writeStringToFile(outFile, sb.toString(), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static String packageToDirectory(String packageName){
|
||||
return packageName.replace(".", File.separator);
|
||||
}
|
||||
|
||||
private static void addDefaultConstructor(TypeSpec.Builder builder) {
|
||||
//Add private no-arg constructor
|
||||
MethodSpec noArg = MethodSpec.constructorBuilder()
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.build();
|
||||
|
||||
builder.addMethod(noArg);
|
||||
}
|
||||
|
||||
private static void addBaseSameDiffConstructor(TypeSpec.Builder builder) {
|
||||
|
||||
MethodSpec ctor = MethodSpec.constructorBuilder()
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addParameter(SameDiff.class, "sameDiff")
|
||||
.addStatement("this.sd = sameDiff")
|
||||
.build();
|
||||
|
||||
builder.addMethod(ctor);
|
||||
}
|
||||
|
||||
private static void addSameDiffConstructor(TypeSpec.Builder builder) {
|
||||
MethodSpec ctor = MethodSpec.constructorBuilder()
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addParameter(SameDiff.class, "sameDiff")
|
||||
.addStatement("super(sameDiff)")
|
||||
.build();
|
||||
|
||||
builder.addMethod(ctor);
|
||||
}
|
||||
|
||||
private static void generateMethods(TypeSpec.Builder builder, Op op, boolean isSameDiff, boolean isLoss ){
|
||||
List<Signature> l = op.getSignatures();
|
||||
for(Signature s : l){
|
||||
builder.addMethod(signatureCreatorMethod(op, s, isSameDiff, false, isLoss));
|
||||
if (isSameDiff)
|
||||
builder.addMethod(signatureCreatorMethod(op, s, true, true, isLoss));
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodSpec signatureCreatorMethod(Op op, Signature s, boolean isSameDiff, boolean withName,
|
||||
boolean isLoss){
|
||||
MethodSpec.Builder c = MethodSpec.methodBuilder(GenUtil.ensureFirstIsNotCap(op.getOpName()))
|
||||
.addModifiers(Modifier.PUBLIC);
|
||||
enableVarargsOnLastArg(c, op, s);
|
||||
|
||||
buildJavaDoc(op, s, c, withName);
|
||||
List<String> inNames = buildParameters(c, op, s, isSameDiff, withName);
|
||||
buildConstraints(c, op.getConstraints());
|
||||
buildExecution(c, op, inNames, isSameDiff, withName, isLoss);
|
||||
|
||||
return c.build();
|
||||
}
|
||||
|
||||
private static void buildJavaDoc(Op op, Signature s, MethodSpec.Builder c, boolean withName) {
|
||||
//Method javadoc:
|
||||
List<DocSection> doc = op.getDoc();
|
||||
if(!doc.isEmpty()){
|
||||
for(DocSection ds : doc){
|
||||
if(ds.applies(Language.JAVA, CodeComponent.OP_CREATOR)){
|
||||
String text = DocTokens.processDocText(ds.getText(), op, DocTokens.GenerationType.ND4J);
|
||||
//Add <br> tags at the end of each line, where none already exists
|
||||
String[] lines = text.split("\n");
|
||||
for( int i = 0; i < lines.length; i++) {
|
||||
if(!lines[i].endsWith("<br>")){
|
||||
lines[i] = lines[i] + "<br>";
|
||||
}
|
||||
}
|
||||
text = String.join("\n", lines);
|
||||
c.addJavadoc(text + "\n\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Document Constraints:
|
||||
//TODO what if constraint is on default value arg/s - no point specifying them here...
|
||||
final List<Constraint> constraints = op.getConstraints();
|
||||
if(!constraints.isEmpty()){
|
||||
c.addJavadoc("Inputs must satisfy the following constraints: <br>\n");
|
||||
for (Constraint constraint : constraints) {
|
||||
c.addJavadoc(constraint.getMessage() +": " + constraintCodeGenerator.generateExpression(constraint.getCheck()) + "<br>\n");
|
||||
}
|
||||
|
||||
c.addJavadoc("\n");
|
||||
}
|
||||
if (withName) {
|
||||
if (op.getOutputs().size() == 1 && !op.getOutputs().get(0).getMultiOutput())
|
||||
c.addJavadoc("@param name name May be null. Name for the output variable\n");
|
||||
else
|
||||
c.addJavadoc("@param names names May be null. Arrays of names for the output variables.\n");
|
||||
}
|
||||
List<Parameter> params = s.getParameters();
|
||||
if(!params.isEmpty()){
|
||||
for(Parameter p : params){
|
||||
if(p instanceof Input){
|
||||
Input i = (Input)p;
|
||||
c.addJavadoc("@param " + i.getName() + " " + (i.getDescription() == null ? "" : DocTokens.processDocText(i.getDescription(), op, DocTokens.GenerationType.ND4J)) + " (" + i.getType() + " type)\n");
|
||||
} else if(p instanceof Arg) {
|
||||
Arg arg = (Arg) p;
|
||||
final Count count = arg.getCount();
|
||||
if (count == null || count.equals(exactlyOne)) {
|
||||
c.addJavadoc("@param " + arg.getName() + " " + (arg.getDescription() == null ? "" : DocTokens.processDocText(arg.getDescription(), op, DocTokens.GenerationType.ND4J)) + "\n");
|
||||
} else {
|
||||
c.addJavadoc("@param " + arg.getName() + " " + (arg.getDescription() == null ? "" : DocTokens.processDocText(arg.getDescription(), op, DocTokens.GenerationType.ND4J)) + " (Size: " + count.toString() + ")\n");
|
||||
}
|
||||
} else if(p instanceof Config){
|
||||
Config config = (Config) p;
|
||||
c.addJavadoc("@param " + config.getName() + " Configuration Object\n");
|
||||
} else {
|
||||
throw new RuntimeException("Unknown parameter type: " + p + " - " + p.getClass() + " - op = " + op.getOpName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
//Outputs:
|
||||
List<Output> outputs = op.getOutputs();
|
||||
if(!outputs.isEmpty()){
|
||||
if(outputs.size() == 1 && !outputs.get(0).getMultiOutput()){
|
||||
Output o = outputs.get(0);
|
||||
c.addJavadoc("@return " + o.getName() + " " + (o.getDescription() == null ? "" : DocTokens.processDocText(o.getDescription(), op, DocTokens.GenerationType.ND4J)) + " (" + o.getType() + " type)\n");
|
||||
} else {
|
||||
//throw new UnsupportedOperationException("Javadoc for multi-output ops not yet implemented");
|
||||
log.error("Javadoc for multi-output ops not yet implemented");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> buildParameters(MethodSpec.Builder c, Op op, Signature s, boolean isSameDiff, boolean withName) {
|
||||
List<String> inNames = new ArrayList<>();
|
||||
|
||||
List<Parameter> params = s.getParameters();
|
||||
|
||||
if(op.getArgsFirst()){
|
||||
//Assuming sort is stable (doesn't change order of equal elements)
|
||||
params.sort((p1,p2) -> Boolean.compare(p1 instanceof Input, p2 instanceof Input));
|
||||
}
|
||||
|
||||
if (withName) {
|
||||
if (op.getOutputs().size() == 1 && !op.getOutputs().get(0).getMultiOutput())
|
||||
c.addParameter(String.class, "name");
|
||||
else
|
||||
c.addParameter(String[].class, "names");
|
||||
}
|
||||
if(!params.isEmpty()){
|
||||
int pCount = 0;
|
||||
for(Parameter p : params){
|
||||
pCount++;
|
||||
boolean isLast = pCount == params.size();
|
||||
if(p instanceof Input){
|
||||
Input i = (Input)p;
|
||||
final String inputName = i.getName();
|
||||
inNames.add(inputName);
|
||||
|
||||
final Count count = i.getCount();
|
||||
if(count == null || count.equals(exactlyOne)) {
|
||||
//Single input
|
||||
if (isSameDiff)
|
||||
c.addParameter(SDVariable.class, inputName);
|
||||
else
|
||||
c.addParameter(INDArray.class, inputName);
|
||||
} else {
|
||||
//Array input
|
||||
if (isSameDiff)
|
||||
c.addParameter(SDVariable[].class, inputName).varargs(isLast);
|
||||
else
|
||||
c.addParameter(INDArray[].class, inputName).varargs(isLast);
|
||||
}
|
||||
// Check for parameter types
|
||||
final DataType paramType = i.getType();
|
||||
String validationName = validationMapping.get(paramType);
|
||||
if(validationName != null) {
|
||||
c.addStatement(CodeBlock.of("$T.$L($S, $S, $L)", isSameDiff ? SDValidation.class : NDValidation.class, validationName, op.getOpName(), inputName, inputName));
|
||||
}
|
||||
checkParameterCount(c, count, inputName);
|
||||
} else if(p instanceof Arg){
|
||||
Arg arg = (Arg)p;
|
||||
final String argName = arg.getName();
|
||||
if(argName.isEmpty()){
|
||||
throw new IllegalStateException("Got null argument name for op " + op.getOpName());
|
||||
}
|
||||
inNames.add(argName);
|
||||
|
||||
|
||||
final Count count = arg.getCount();
|
||||
TypeName type = getArgType(arg);
|
||||
if(type == null){
|
||||
throw new IllegalStateException("No type mapping has been specified for type " + arg.getType() + " (op=" + op.getOpName() + ", arg=" + arg.getName() + ")" );
|
||||
}
|
||||
c.addParameter(type, argName);
|
||||
|
||||
checkParameterCount(c, count, argName);
|
||||
} else if(p instanceof Config) {
|
||||
Config config = (Config) p;
|
||||
final String configName = config.getName();
|
||||
inNames.add(configName);
|
||||
c.addParameter(configMapping.get(config), config.name());
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown parameter type: " + p + " - " + p.getClass());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return inNames;
|
||||
}
|
||||
|
||||
public static TypeName getArgType(Arg arg) {
|
||||
DataType argType = arg.getType();
|
||||
Count count = arg.getCount();
|
||||
TypeName type;
|
||||
if(argType == DataType.ENUM){
|
||||
type = enumMapping.get(arg);
|
||||
if(type == null){
|
||||
throw new IllegalStateException(arg + " is using an unregistered ENUM. This is probably a bug.");
|
||||
}
|
||||
}else{
|
||||
if(!typeMapping.containsKey(argType)){
|
||||
return null;
|
||||
}
|
||||
type = TypeName.get(typeMapping.get(argType));
|
||||
}
|
||||
|
||||
if (!(count == null || count.equals(exactlyOne))) {
|
||||
// array Arg
|
||||
type = ArrayTypeName.of(type);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
private static void buildConstraints(MethodSpec.Builder c, List<Constraint> constraints) {
|
||||
if(constraints.isEmpty())
|
||||
return;
|
||||
|
||||
//TODO not all contsraints apply to all signatures?
|
||||
|
||||
// Don't materialize the Backend Constraints
|
||||
for (Constraint constraint : constraints.stream().filter(it -> !(it instanceof BackendConstraint)).collect(Collectors.toList())) {
|
||||
c.addStatement(CodeBlock.of("$T.checkArgument($L, $S)", Preconditions.class, constraintCodeGenerator.generateExpression(constraint.getCheck()), constraint.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private static void buildExecution(MethodSpec.Builder c, Op op, List<String> inNames, boolean isSameDiff,
|
||||
boolean withName, boolean isLoss) {
|
||||
boolean singleOut = op.getOutputs().size() == 1 && !op.getOutputs().get(0).getMultiOutput();
|
||||
if(singleOut){
|
||||
if (isSameDiff)
|
||||
c.returns(SDVariable.class);
|
||||
else
|
||||
c.returns(INDArray.class);
|
||||
} else {
|
||||
if (isSameDiff)
|
||||
c.returns(SDVariable[].class);
|
||||
else
|
||||
c.returns(INDArray[].class);
|
||||
}
|
||||
|
||||
// We have to pass all parameters, always. But not all signatures will be taking all parameters.
|
||||
// inNames tells us which parameters this signatures has. For all others we want to pass default values
|
||||
List<String> parameters = op.allParameters().stream().sorted(
|
||||
(p1,p2) -> {
|
||||
if (p1.isVararg()) return 1;
|
||||
else if (p2.isVararg()) return -1;
|
||||
return 0;
|
||||
}
|
||||
).map(it -> {
|
||||
if(inNames.contains(it.name())){
|
||||
return it.name();
|
||||
}else{
|
||||
if(!it.hasDefaultValue()) throw new IllegalStateException("The parameter "+it.name()+" has no default value, but is also not part of "+inNames.toString());
|
||||
return anyToCode(it, it.defaultValue());
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
//Op execution:
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (isSameDiff) {
|
||||
if (withName) {
|
||||
if (singleOut)
|
||||
sb.append("SDVariable out = ");
|
||||
else
|
||||
sb.append("SDVariable[] out = ");
|
||||
|
||||
sb.append(" new ")
|
||||
.append(op.getJavaPackage())
|
||||
.append(".")
|
||||
.append(op.getJavaOpClass() == null ? GenUtil.ensureFirstIsCap(op.getOpName()) : op.getJavaOpClass())
|
||||
.append("(sd,")
|
||||
.append(String.join(", ", parameters))
|
||||
.append(")");
|
||||
|
||||
if (singleOut)
|
||||
sb.append(".outputVariable()");
|
||||
else
|
||||
sb.append(".outputVariables()");
|
||||
|
||||
c.addStatement(sb.toString());
|
||||
if (isLoss)
|
||||
c.addStatement("out.markAsLoss()");
|
||||
|
||||
if (singleOut)
|
||||
c.addStatement("return sd.updateVariableNameAndReference(out, name)");
|
||||
else
|
||||
c.addStatement("return sd.updateVariableNamesAndReferences(out, names)");
|
||||
}
|
||||
else {
|
||||
if (isLoss) {
|
||||
sb.append("SDVariable out = new ")
|
||||
.append(op.getJavaPackage())
|
||||
.append(".")
|
||||
.append(op.getJavaOpClass() == null ? GenUtil.ensureFirstIsCap(op.getOpName()) : op.getJavaOpClass())
|
||||
.append("(sd,")
|
||||
.append(String.join(", ", parameters))
|
||||
.append(")");
|
||||
}
|
||||
else {
|
||||
sb.append("return new ")
|
||||
.append(op.getJavaPackage())
|
||||
.append(".")
|
||||
.append(op.getJavaOpClass() == null ? GenUtil.ensureFirstIsCap(op.getOpName()) : op.getJavaOpClass())
|
||||
.append("(sd,")
|
||||
.append(String.join(", ", parameters))
|
||||
.append(")");
|
||||
}
|
||||
//if (!op.getLegacy()) {
|
||||
if (singleOut)
|
||||
sb.append(".outputVariable()");
|
||||
else
|
||||
sb.append(".outputVariables()");
|
||||
//}
|
||||
c.addStatement(sb.toString());
|
||||
if (isLoss) {
|
||||
c.addStatement("out.markAsLoss()");
|
||||
c.addStatement("return out");
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
sb.append("return $T.exec(new ")
|
||||
.append(op.getJavaPackage())
|
||||
.append(".")
|
||||
.append(op.getJavaOpClass() == null ? GenUtil.ensureFirstIsCap(op.getOpName()) : op.getJavaOpClass())
|
||||
.append("(")
|
||||
.append(String.join(", ", parameters))
|
||||
.append("))");
|
||||
if (!op.getLegacy() && singleOut) //Note: legacy ops Nd4j.exec(Op) returns INDArray; Nd4j.exec(CustomOp) returns INDArray[]
|
||||
sb.append("[0]");
|
||||
|
||||
c.addStatement(sb.toString(), Nd4j.class);
|
||||
}
|
||||
}
|
||||
|
||||
private static void enableVarargsOnLastArg(MethodSpec.Builder c, Op op, Signature s) {
|
||||
List<Parameter> p = s.getParameters();
|
||||
if(!p.isEmpty()){
|
||||
Parameter lastP = p.get(p.size() - 1);
|
||||
if (lastP instanceof Arg) {
|
||||
Arg arg = (Arg) lastP;
|
||||
final Count count = arg.getCount();
|
||||
if (count != null && !count.equals(exactlyOne)) {
|
||||
c.varargs(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String countToJava(Count count,String paramName) {
|
||||
final String paramLength = paramName + ".length";
|
||||
if(count instanceof Exactly){
|
||||
return paramLength + " == " + ((Exactly) count).getCount();
|
||||
}else if(count instanceof AtLeast){
|
||||
return paramLength + " >= " + ((AtLeast) count).getMin();
|
||||
}else if(count instanceof AtMost){
|
||||
return paramLength + " <= "+ ((AtMost) count).getMax();
|
||||
}else if(count instanceof Range){
|
||||
return ((Range) count).getFrom() + " <= " + paramLength + " && " + paramLength + " <= " + ((Range) count).getTo();
|
||||
}else{
|
||||
throw new IllegalArgumentException("Can not deal with Count of type " + count.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkParameterCount(MethodSpec.Builder c, Count count, String paramName) {
|
||||
// Check for parameter counts
|
||||
if(count != null && !count.equals(exactlyOne)){
|
||||
final String errorMessage = paramName + " has incorrect size/length. Expected: " + countToJava(count, paramName) + ", got %s";
|
||||
if(count instanceof Exactly){
|
||||
c.addStatement(CodeBlock.of("$T.checkArgument($L.length == $L, $S, $L)", Preconditions.class, paramName, ((Exactly) count).getCount(), errorMessage, paramName + ".length"));
|
||||
}else if(count instanceof AtLeast){
|
||||
c.addStatement(CodeBlock.of("$T.checkArgument($L.length >= $L, $S, $L)", Preconditions.class, paramName, ((AtLeast) count).getMin(), errorMessage, paramName + ".length"));
|
||||
}else if(count instanceof AtMost){
|
||||
c.addStatement(CodeBlock.of("$T.checkArgument($L.length <= $L, $S, $L)", Preconditions.class, paramName, ((AtMost) count).getMax(), errorMessage, paramName + ".length"));
|
||||
}else if(count instanceof Range){
|
||||
c.addStatement(CodeBlock.of("$T.checkArgument($L.length >= $L && $L.length <= $L, $S, $L)", Preconditions.class, paramName, ((Range) count).getFrom(), paramName, ((Range) count).getTo(), errorMessage, paramName + ".length"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void generateEnums(File outputDirectory, String basePackage) throws IOException {
|
||||
for (Arg it : Registry.INSTANCE.enums()) {
|
||||
generateEnum(outputDirectory, "org.nd4j.enums", it);
|
||||
}
|
||||
}
|
||||
|
||||
private static String generateMethodText(Op op, Signature s, boolean isSameDiff, boolean isLoss, boolean withName) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
MethodSpec.Builder c = MethodSpec.methodBuilder(GenUtil.ensureFirstIsNotCap(op.getOpName()));
|
||||
List<Parameter> params = s.getParameters();
|
||||
List<Output> outs = op.getOutputs();
|
||||
String retType = "void";
|
||||
|
||||
if (outs.size() == 1) {
|
||||
retType = isSameDiff ? "SDVariable" : "INDArray";
|
||||
}
|
||||
else if (outs.size() >= 1) {
|
||||
retType = isSameDiff ? "SDVariable[]" : "INDArray[]";
|
||||
}
|
||||
sb.append(retType + " " + op.getOpName() + "(");
|
||||
boolean first = true;
|
||||
for (Parameter param : params) {
|
||||
if (param instanceof Arg) {
|
||||
Arg arg = (Arg) param;
|
||||
if (!first)
|
||||
sb.append(",");
|
||||
else if (withName)
|
||||
sb.append("String name,");
|
||||
TypeName tu = getArgType(arg);
|
||||
sb.append(tu.toString() + " " + arg.name());
|
||||
first = false;
|
||||
}
|
||||
else if (param instanceof Input) {
|
||||
Input arg = (Input) param;
|
||||
if (!first)
|
||||
sb.append(",");
|
||||
else if (withName)
|
||||
sb.append("String name,");
|
||||
sb.append((isSameDiff ? "SDVariable " : "INDArray ") + arg.name());
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static StringBuilder buildDocSectionText(List<DocSection> docSections) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (DocSection ds : docSections) {
|
||||
//if(ds.applies(Language.JAVA, CodeComponent.OP_CREATOR)){
|
||||
String text = ds.getText();
|
||||
String[] lines = text.split("\n");
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
if (!lines[i].endsWith("<br>")) {
|
||||
lines[i] = lines[i] + System.lineSeparator();
|
||||
}
|
||||
}
|
||||
text = String.join("\n", lines);
|
||||
sb.append(text + System.lineSeparator());
|
||||
//}
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
private static void generateDocs(NamespaceOps namespace, File outputDirectory, String basePackage) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("# Namespace " + namespace.getName() + System.lineSeparator());
|
||||
List<Op> ops = namespace.getOps();
|
||||
for (Op op : ops) {
|
||||
sb.append("## <a name=" + "\"").append(op.name()).append("\">").append(op.name()).append("</a>").append(System.lineSeparator());
|
||||
List<DocSection> doc = op.getDoc();
|
||||
if(!doc.isEmpty()) {
|
||||
boolean first = true;
|
||||
for(Signature s : op.getSignatures()) {
|
||||
if (first) {
|
||||
sb.append("````" + doc.get(0).getLanguage() + System.lineSeparator());
|
||||
first = false;
|
||||
}
|
||||
String ndCode = generateMethodText(op, s, false, false, false);
|
||||
sb.append(ndCode).append(System.lineSeparator());
|
||||
String sdCode = generateMethodText(op, s, true, false, false);
|
||||
sb.append(sdCode).append(System.lineSeparator());
|
||||
String withNameCode = generateMethodText(op, s, true, false, true);
|
||||
sb.append(withNameCode).append(System.lineSeparator());
|
||||
}
|
||||
sb.append("````").append(System.lineSeparator());
|
||||
StringBuilder tsb = buildDocSectionText(doc);
|
||||
sb.append(tsb.toString());
|
||||
List<Signature> l = op.getSignatures();
|
||||
for(Signature s : l) {
|
||||
List<Parameter> params = s.getParameters();
|
||||
for (Parameter p : params) {
|
||||
if(p instanceof Input){
|
||||
Input i = (Input)p;
|
||||
sb.append("* " + i.getName() + " " + (i.getDescription() == null ? "" : DocTokens.processDocText(i.getDescription(),
|
||||
op, DocTokens.GenerationType.ND4J)) + " (" + i.getType() + " type)" + System.lineSeparator());
|
||||
} else if(p instanceof Arg) {
|
||||
Arg arg = (Arg) p;
|
||||
final Count count = arg.getCount();
|
||||
if (count == null || count.equals(exactlyOne)) {
|
||||
sb.append("* " + arg.getName() + " " + (arg.getDescription() == null ? "" : DocTokens.processDocText(arg.getDescription(),
|
||||
op, DocTokens.GenerationType.ND4J)) + System.lineSeparator());
|
||||
} else {
|
||||
sb.append("* " + arg.getName() + " " + (arg.getDescription() == null ? "" : DocTokens.processDocText(arg.getDescription(),
|
||||
op, DocTokens.GenerationType.ND4J)) + " (Size: " + count.toString() + System.lineSeparator());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.append(System.lineSeparator());
|
||||
tsb = buildDocSectionText(doc);
|
||||
sb.append(tsb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
for (Config config : Registry.INSTANCE.configs()) {
|
||||
sb.append("## " + config.getName() + System.lineSeparator());
|
||||
boolean first = true;
|
||||
for (Input i : config.getInputs()) {
|
||||
if (first) {
|
||||
sb.append("````" + System.lineSeparator());
|
||||
first = false;
|
||||
}
|
||||
sb.append("* " + i.getName() + " " + i.getDescription() + " (" + i.getType() + " type)" + System.lineSeparator());
|
||||
}
|
||||
for (Arg arg : config.getArgs()) {
|
||||
if (first) {
|
||||
sb.append("````" + System.lineSeparator());
|
||||
first = false;
|
||||
}
|
||||
sb.append("* " + arg.getName() + " " + " (" + arg.getType() + " type)" + System.lineSeparator());
|
||||
}
|
||||
StringBuilder tsb = buildDocSectionText(config.getDoc());
|
||||
sb.append(tsb.toString());
|
||||
sb.append("````" + System.lineSeparator());
|
||||
ops.stream().filter(op -> op.getConfigs().contains(config)).forEach(op ->
|
||||
sb.append("[" + op.getOpName() + "]" + "(#" + op.getOpName() + ")" + System.lineSeparator()));
|
||||
}
|
||||
File outFile = new File(outputDirectory + "/ops", "/namespace-" + namespace.getName() + ".md");
|
||||
FileUtils.writeStringToFile(outFile, sb.toString(), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static void generateEnum(File outputDirectory, String targetPackage, Arg arg) throws IOException {
|
||||
final String className = GenUtil.ensureFirstIsCap(arg.name());
|
||||
enumMapping.put(arg, ClassName.get(targetPackage, className));
|
||||
|
||||
TypeSpec.Builder builder = TypeSpec.enumBuilder(className)
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addJavadoc(CodeBlock.of(arg.getDescription()));
|
||||
|
||||
for (String possibleValue : arg.getPossibleValues()) {
|
||||
builder.addEnumConstant(possibleValue);
|
||||
}
|
||||
|
||||
TypeSpec ts = builder.build();
|
||||
|
||||
JavaFile jf = JavaFile.builder(targetPackage, ts)
|
||||
.build();
|
||||
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(copyright);
|
||||
sb.append(codeGenWarning);
|
||||
jf.writeTo(sb);
|
||||
|
||||
File outFile = new File(outputDirectory, packageToDirectory(targetPackage) + "/" + className + ".java");
|
||||
FileUtils.writeStringToFile(outFile, sb.toString(), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static void generateConfigs(File outputDirectory, String basePackage) throws IOException {
|
||||
for (Config config : Registry.INSTANCE.configs()) {
|
||||
generateConfig(outputDirectory, basePackage+".configs", config);
|
||||
}
|
||||
}
|
||||
|
||||
private static void generateConfig(File outputDirectory, String targetPackage, Config config) throws IOException {
|
||||
if(config.getJavaClassOverride() != null && !config.getJavaClassOverride().isEmpty()){
|
||||
//Java class override means "don't generate, use the existing one instead"
|
||||
String c = config.getJavaClassOverride();
|
||||
int idx = c.lastIndexOf('.');
|
||||
String pkg = c.substring(0,idx);
|
||||
String className = c.substring(idx+1);
|
||||
configMapping.put(config, ClassName.get(pkg, className));
|
||||
return;
|
||||
}
|
||||
|
||||
final String className = GenUtil.ensureFirstIsCap(config.name());
|
||||
configMapping.put(config, ClassName.get(targetPackage, className));
|
||||
|
||||
// Build Config Builder Class
|
||||
final TypeSpec.Builder sdb = TypeSpec.classBuilder("SdBuilder").addModifiers(Modifier.STATIC, Modifier.PUBLIC);
|
||||
final TypeSpec.Builder ndb = TypeSpec.classBuilder("NdBuilder").addModifiers(Modifier.STATIC, Modifier.PUBLIC);
|
||||
|
||||
for (Input input : config.getInputs()) {
|
||||
addConfigBuilderParam(className, sdb, input.getName(), input.getType(), getType(TypeName.get(SDVariable.class), input.getCount()), input.getDescription(), input.getCount());
|
||||
addConfigBuilderParam(className, ndb, input.getName(), input.getType(), getType(TypeName.get(INDArray.class), input.getCount()), input.getDescription(), input.getCount());
|
||||
}
|
||||
|
||||
for (Arg arg : config.getArgs()) {
|
||||
addConfigBuilderParam(className, sdb, arg.getName(), null, getArgType(arg), arg.getDescription(), arg.getCount());
|
||||
addConfigBuilderParam(className, ndb, arg.getName(), null, getArgType(arg), arg.getDescription(), arg.getCount());
|
||||
}
|
||||
|
||||
ArrayList<String> parts = new ArrayList<>();
|
||||
ArrayList<Object> parameters = new ArrayList<>();
|
||||
for (Input input : config.getInputs()) {
|
||||
parts.add("$L");
|
||||
parameters.add(
|
||||
input.hasDefaultValue() ?
|
||||
input.name() + " == null ? " + ((Input)input.defaultValue()).getName() +" : "+input.name()
|
||||
: input.name()
|
||||
); }
|
||||
for (Arg input : config.getArgs()) {
|
||||
parts.add("$L");
|
||||
parameters.add(
|
||||
input.hasDefaultValue() ?
|
||||
input.name() + " == null ? " + anyToCode(input, input.defaultValue()) +" : "+input.name()
|
||||
: input.name()
|
||||
);
|
||||
}
|
||||
parameters.add(0, className);
|
||||
|
||||
final MethodSpec.Builder build = MethodSpec.methodBuilder("build")
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.returns(ClassName.bestGuess(className));
|
||||
buildConstraints(build, config.getConstraints());
|
||||
build.addStatement("return new $N("+(String.join(", ", parts))+")", parameters.toArray());
|
||||
|
||||
sdb.addMethod(build.build());
|
||||
ndb.addMethod(build.build());
|
||||
|
||||
|
||||
final TypeSpec ndBuilder = ndb.build();
|
||||
final TypeSpec sdBuilder = sdb.build();
|
||||
|
||||
|
||||
// Build Config Holder Class
|
||||
TypeSpec.Builder holder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC);
|
||||
|
||||
final MethodSpec.Builder ndConstructorBuilder = MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE);
|
||||
final MethodSpec.Builder sdConstructorBuilder = MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE);
|
||||
|
||||
|
||||
for (Input input : config.getInputs()) {
|
||||
final String inputName = GenUtil.ensureFirstIsCap(input.getName());
|
||||
addConfigParam(holder, ndConstructorBuilder, "nd" + inputName, getType(TypeName.get(INDArray.class), input.getCount()), input.getDescription(), true);
|
||||
addConfigParam(holder, sdConstructorBuilder, "sd" + inputName, getType(TypeName.get(SDVariable.class), input.getCount()), input.getDescription(), true);
|
||||
}
|
||||
|
||||
for (Arg arg : config.getArgs()) {
|
||||
addConfigParam(holder, ndConstructorBuilder, arg.getName(), getArgType(arg), arg.getDescription(), true);
|
||||
addConfigParam(holder, sdConstructorBuilder, arg.getName(), getArgType(arg), arg.getDescription(), false);
|
||||
}
|
||||
holder.addMethod(sdConstructorBuilder.build());
|
||||
holder.addMethod(ndConstructorBuilder.build());
|
||||
|
||||
holder.addMethod(MethodSpec.methodBuilder("sdBuilder")
|
||||
.addModifiers(Modifier.STATIC, Modifier.PUBLIC)
|
||||
.addStatement("return new $N()", sdBuilder.name)
|
||||
.returns(ClassName.bestGuess(sdBuilder.name))
|
||||
.build());
|
||||
holder.addType(sdBuilder);
|
||||
holder.addMethod(MethodSpec.methodBuilder("ndBuilder")
|
||||
.addModifiers(Modifier.STATIC, Modifier.PUBLIC)
|
||||
.addStatement("return new $N()", ndBuilder.name)
|
||||
.returns(ClassName.bestGuess(ndBuilder.name))
|
||||
.build());
|
||||
holder.addType(ndBuilder);
|
||||
|
||||
// add javadoc
|
||||
//Method javadoc:
|
||||
List<DocSection> doc = config.getDoc();
|
||||
if(!doc.isEmpty()){
|
||||
for(DocSection ds : doc){
|
||||
if(ds.applies(Language.JAVA, CodeComponent.OP_CREATOR)){
|
||||
String text = ds.getText();
|
||||
//Add <br> tags at the end of each line, where none already exists
|
||||
String[] lines = text.split("\n");
|
||||
for( int i=0; i<lines.length; i++ ){
|
||||
if(!lines[i].endsWith("<br>")){
|
||||
lines[i] = lines[i] + "<br>";
|
||||
}
|
||||
}
|
||||
text = String.join("\n", lines);
|
||||
holder.addJavadoc(text + "\n\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Document Constraints:
|
||||
final List<Constraint> constraints = config.getConstraints();
|
||||
if(!constraints.isEmpty()){
|
||||
holder.addJavadoc("Inputs must satisfy the following constraints: <br>\n");
|
||||
for (Constraint constraint : constraints) {
|
||||
holder.addJavadoc(constraint.getMessage() +": " + constraintCodeGenerator.generateExpression(constraint.getCheck()) + "<br>\n");
|
||||
}
|
||||
|
||||
holder.addJavadoc("\n");
|
||||
}
|
||||
|
||||
TypeSpec ts = holder.build();
|
||||
|
||||
|
||||
JavaFile jf = JavaFile.builder(targetPackage, ts)
|
||||
.build();
|
||||
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(copyright);
|
||||
sb.append(codeGenWarning);
|
||||
jf.writeTo(sb);
|
||||
|
||||
File outFile = new File(outputDirectory, packageToDirectory(targetPackage) + "/" + className + ".java");
|
||||
FileUtils.writeStringToFile(outFile, sb.toString(), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static void addConfigParam(TypeSpec.Builder builder, MethodSpec.Builder constructorBuilder, String paramName, TypeName paramType, String paramDescription, boolean addField) {
|
||||
if(addField){
|
||||
// Add param fields
|
||||
builder.addField(paramType, paramName, Modifier.PRIVATE);
|
||||
|
||||
// Add param getters
|
||||
builder.addMethod(generateGetter(paramType, paramName, paramDescription, false));
|
||||
}
|
||||
|
||||
// Add param constructor parameters
|
||||
constructorBuilder.addParameter(paramType, paramName, Modifier.FINAL);
|
||||
constructorBuilder.addStatement("this.$L = $L", paramName, paramName);
|
||||
}
|
||||
|
||||
private static void addConfigBuilderParam(String configClassName, TypeSpec.Builder builder, String paramName, DataType inputType, TypeName paramType, String paramDescription, Count count) {
|
||||
final String builderName = builder.build().name;
|
||||
// Add param fields
|
||||
builder.addField(paramType.box(), paramName, Modifier.PRIVATE);
|
||||
|
||||
// Add param getters
|
||||
builder.addMethod(generateGetter(paramType, paramName, paramDescription, true));
|
||||
|
||||
// Add param setter
|
||||
final MethodSpec.Builder setter = MethodSpec.methodBuilder(paramName)
|
||||
.addParameter(paramType, paramName)
|
||||
.addModifiers(Modifier.PUBLIC);
|
||||
checkParameterCount(setter, count, paramName);
|
||||
if(inputType != null){
|
||||
if(builderName.equals("SdBuilder")){
|
||||
setter.addStatement("$T.$L($S, $S, $L)", SDValidation.class, validationMapping.get(inputType), "Config: " + configClassName, paramName, paramName);
|
||||
}else if(builderName.equals("NdBuilder")){
|
||||
setter.addStatement("$T.$L($S, $S, $L)", NDValidation.class, validationMapping.get(inputType), "Config: " + configClassName, paramName, paramName);
|
||||
}else{
|
||||
throw new IllegalArgumentException("Unknown Builder Type "+builderName);
|
||||
}
|
||||
}
|
||||
setter.addStatement("this.$L = $L", paramName, paramName)
|
||||
.addStatement("return this")
|
||||
.returns(ClassName.bestGuess(builderName));
|
||||
|
||||
if(count != null && !count.equals(exactlyOne)){
|
||||
setter.varargs(true);
|
||||
}
|
||||
|
||||
if(paramDescription != null){
|
||||
setter.addJavadoc(paramDescription);
|
||||
}
|
||||
builder.addMethod(setter.build());
|
||||
}
|
||||
|
||||
private static TypeName getType(TypeName typeVariable, Count count) {
|
||||
if(count != null && !count.equals(exactlyOne)){
|
||||
return ArrayTypeName.of(typeVariable);
|
||||
}else{
|
||||
return typeVariable;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static MethodSpec generateGetter(TypeName typeVariable, String paramName, String paramDescription, boolean fluent) {
|
||||
final MethodSpec.Builder getter = MethodSpec.methodBuilder((fluent ? paramName : "get" + GenUtil.ensureFirstIsCap(paramName)))
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.returns(typeVariable);
|
||||
if(paramDescription != null){
|
||||
getter.addJavadoc(paramDescription);
|
||||
}
|
||||
getter.addStatement("return this.$L", paramName);
|
||||
return getter.build();
|
||||
}
|
||||
|
||||
private static String anyToCode(Parameter parameter, Object v){
|
||||
if(v == null){ return "null"; }
|
||||
else if(v instanceof int[]){ return "new int[]"+Arrays.toString((int[]) v).replace("[", "{").replace("]", "}"); }
|
||||
else if(v instanceof long[]){ return "new long[]"+Arrays.toString((long[]) v).replace("[", "{").replace("]", "}"); }
|
||||
else if(v instanceof float[]){ return "new float[]"+Arrays.toString((float[]) v).replace("[", "{").replace("]", "}"); }
|
||||
else if(v instanceof double[]){ return "new double[]"+Arrays.toString((double[]) v).replace("[", "{").replace("]", "}"); }
|
||||
else if(v instanceof boolean[]){ return "new boolean[]"+Arrays.toString((boolean[]) v).replace("[", "{").replace("]", "}"); }
|
||||
else if(v instanceof Input){ return ((Input)v).getName(); }
|
||||
else if(v instanceof org.nd4j.linalg.api.buffer.DataType){ return "DataType." + v; }
|
||||
else if(v instanceof LossReduce || v instanceof org.nd4j.autodiff.loss.LossReduce){ return "org.nd4j.autodiff.loss.LossReduce." + v; }
|
||||
else if(parameter instanceof Arg && ((Arg)parameter).getType() == DataType.ENUM){
|
||||
return GenUtil.ensureFirstIsCap(parameter.name()) + "." + v.toString();
|
||||
} else return v.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.impl.python;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.nd4j.codegen.api.*;
|
||||
import org.nd4j.codegen.api.doc.DocTokens;
|
||||
import org.nd4j.codegen.api.generator.Generator;
|
||||
import org.nd4j.codegen.api.generator.GeneratorConfig;
|
||||
import org.nd4j.codegen.util.GenUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is a very simple, manual namespace generator
|
||||
* We could of course use a templating library such as Freemarker, which woudl work fine - but:
|
||||
* (a) on the one hand, it's overkill/unnecessary
|
||||
* (b) on the other hand, may provide less flexibility than a manual implementation
|
||||
*
|
||||
*/
|
||||
public class PythonGenerator implements Generator {
|
||||
|
||||
private static final String I4 = " ";
|
||||
|
||||
@Override
|
||||
public Language language() {
|
||||
return Language.PYTHON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateNamespaceNd4j(NamespaceOps namespace, GeneratorConfig config, File directory, String fileName) throws IOException {
|
||||
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("class ").append(GenUtil.ensureFirstIsCap(namespace.getName())).append(":\n\n");
|
||||
|
||||
List<Op> ops = new ArrayList<>();
|
||||
for(Op o : namespace.getOps()){
|
||||
if(o.isAbstract())
|
||||
continue;
|
||||
ops.add(o);
|
||||
}
|
||||
|
||||
//TODO: handle includes
|
||||
|
||||
for(Op o : ops){
|
||||
String s = generateMethod(o);
|
||||
sb.append(GenUtil.addIndent(s, 4));
|
||||
sb.append("\n\n");
|
||||
}
|
||||
|
||||
File f = new File(directory, GenUtil.ensureFirstIsCap(namespace.getName()) + ".py");
|
||||
String content = sb.toString();
|
||||
|
||||
FileUtils.writeStringToFile(f, content, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
protected static String generateMethod(Op op){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("@staticmethod\n")
|
||||
.append("def ").append(GenUtil.ensureFirstIsNotCap(op.getOpName())).append("(");
|
||||
|
||||
//Add inputs to signature
|
||||
boolean firstArg = true;
|
||||
if(op.getInputs() != null){
|
||||
for(Input i : op.getInputs()){
|
||||
if(!firstArg)
|
||||
sb.append(", ");
|
||||
|
||||
sb.append(i.getName());
|
||||
|
||||
firstArg = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Add arguments and default args to signature
|
||||
|
||||
sb.append("):\n");
|
||||
|
||||
String docString = genDocString(op);
|
||||
sb.append(GenUtil.addIndent(docString, 4));
|
||||
|
||||
sb.append(I4).append("# Execution code here\n");
|
||||
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
protected static String genDocString(Op op){
|
||||
//Following roughly: https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("\"\"\"")
|
||||
.append(op.getOpName())
|
||||
.append(" operation")
|
||||
.append("\n\n");
|
||||
if(op.getInputs() != null){
|
||||
sb.append("Args:");
|
||||
sb.append("\n");
|
||||
for(Input i : op.getInputs()){
|
||||
sb.append(I4).append(i.getName()).append(" (ndarray): ");
|
||||
if(i.getDescription() != null)
|
||||
sb.append(DocTokens.processDocText(i.getDescription(), op, DocTokens.GenerationType.ND4J));
|
||||
sb.append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
sb.append("\n");
|
||||
|
||||
if(op.getOutputs() != null){
|
||||
sb.append("Returns:\n");
|
||||
List<Output> o = op.getOutputs();
|
||||
|
||||
if(o.size() == 1){
|
||||
sb.append(I4).append("ndarray: ").append(o.get(0).getName());
|
||||
String d = o.get(0).getDescription();
|
||||
if(d != null){
|
||||
sb.append(" - ").append(DocTokens.processDocText(d, op, DocTokens.GenerationType.ND4J));
|
||||
}
|
||||
sb.append("\n");
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Not yet implemented: Python docstring generation for multiple output ops");
|
||||
}
|
||||
}
|
||||
|
||||
if(op.getArgs() != null){
|
||||
//Args and default args
|
||||
throw new UnsupportedOperationException("Generating method with args not yet implemented");
|
||||
}
|
||||
|
||||
sb.append("\"\"\"\n");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void generateNamespaceSameDiff(NamespaceOps namespace, GeneratorConfig config, File directory, String fileName) throws IOException {
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.util;
|
||||
|
||||
import org.nd4j.codegen.api.Op;
|
||||
|
||||
public class GenUtil {
|
||||
|
||||
private GenUtil(){ }
|
||||
|
||||
public static String ensureFirstIsCap(String in){
|
||||
if(Character.isUpperCase(in.charAt(0))){
|
||||
return in;
|
||||
}
|
||||
|
||||
return Character.toUpperCase(in.charAt(0)) + in.substring(1);
|
||||
}
|
||||
|
||||
public static String ensureFirstIsNotCap(String in){
|
||||
if(Character.isLowerCase(in.charAt(0))){
|
||||
return in;
|
||||
}
|
||||
|
||||
return Character.toLowerCase(in.charAt(0)) + in.substring(1);
|
||||
}
|
||||
|
||||
public static String repeat(String in, int count){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for( int i=0; i<count; i++ ){
|
||||
sb.append(in);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String addIndent(String in, int count){
|
||||
if(in == null)
|
||||
return null;
|
||||
String[] lines = in.split("\n");
|
||||
StringBuilder out = new StringBuilder();
|
||||
String indent = repeat(" ", count);
|
||||
for(String s : lines){
|
||||
out.append(indent).append(s).append("\n");
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.util;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
|
||||
public class JsonMapper {
|
||||
|
||||
public static ObjectMapper getMapper(){
|
||||
ObjectMapper om = new ObjectMapper();
|
||||
// om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
om.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
|
||||
om.enable(SerializationFeature.INDENT_OUTPUT);
|
||||
om.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
|
||||
om.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
|
||||
om.setVisibility(PropertyAccessor.CREATOR, JsonAutoDetect.Visibility.ANY);
|
||||
om.setVisibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.ANY);
|
||||
|
||||
return om;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.api
|
||||
|
||||
enum class CodeComponent { CLASS_DOC, CONSTRUCTOR, OP_CREATOR }
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.api
|
||||
|
||||
enum class DataType {
|
||||
NDARRAY, // Any NDArray type (input only) - INDArray or SDVariable
|
||||
FLOATING_POINT, // Any floating point data type
|
||||
INT, // integer data type
|
||||
LONG, //long, signed int64 datatype
|
||||
NUMERIC, // any floating point or integer data type
|
||||
BOOL, // boolean data type
|
||||
STRING, //String value
|
||||
// Arg only
|
||||
DATA_TYPE, // tensor data type
|
||||
CONDITION, // A condition
|
||||
LOSS_REDUCE, // Loss reduction mode
|
||||
ENUM; // defines an enum along with possibleValues property in Arg
|
||||
|
||||
fun isTensorDataType() = setOf(NDARRAY, FLOATING_POINT, INT, LONG, NUMERIC, BOOL).contains(this)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.api
|
||||
|
||||
enum class Language { ANY, JAVA, SCALA, KOTLIN, PYTHON, CPP }
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.api
|
||||
|
||||
/**
|
||||
* See org.nd4j.autodiff.los.LossReduce in nd4j for documentation.
|
||||
*/
|
||||
enum class LossReduce {
|
||||
NONE,
|
||||
SUM,
|
||||
MEAN_BY_WEIGHT,
|
||||
MEAN_BY_NONZERO_WEIGHT_COUNT
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.api
|
||||
|
||||
data class NamespaceOps @JvmOverloads constructor(
|
||||
var name: String,
|
||||
var include: MutableList<String>? = null,
|
||||
var ops: MutableList<Op> = mutableListOf(),
|
||||
var configs: MutableList<Config> = mutableListOf(),
|
||||
var parentNamespaceOps: Map<String,MutableList<Op>> = mutableMapOf()
|
||||
) {
|
||||
fun addConfig(config: Config) {
|
||||
configs.add(config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that all required properties are set
|
||||
*/
|
||||
fun checkInvariants() {
|
||||
val usedConfigs = mutableSetOf<Config>()
|
||||
ops.forEach { op ->
|
||||
usedConfigs.addAll(op.configs)
|
||||
}
|
||||
val unusedConfigs = configs.toSet() - usedConfigs
|
||||
if(unusedConfigs.size > 0){
|
||||
throw IllegalStateException("Found unused configs: ${unusedConfigs.joinToString(", ") { it.name }}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get op by name
|
||||
*/
|
||||
fun op(name:String):Op {
|
||||
val op = ops.find { op -> op.opName.equals(name) } ?: throw java.lang.IllegalStateException("Operation $name not found")
|
||||
return op
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.api
|
||||
|
||||
import org.nd4j.codegen.api.doc.DocSection
|
||||
|
||||
interface OpLike {
|
||||
fun name(): String
|
||||
fun inputs(): List<Input>
|
||||
fun args(): List<Arg>
|
||||
fun configs(): List<Config>
|
||||
fun outputs(): List<Output>
|
||||
|
||||
fun allParameters(): List<Parameter>
|
||||
|
||||
fun addInput(input: Input)
|
||||
fun addArgument(arg: Arg)
|
||||
fun addOutput(output: Output)
|
||||
fun addDoc(docs: DocSection)
|
||||
fun addSignature(signature: Signature)
|
||||
fun addConstraint(constraint: Constraint)
|
||||
fun addConfig(config: Config)
|
||||
|
||||
fun Config.input(name: String) = inputs.find { it.name == name }!!
|
||||
fun Config.arg(name: String) = args.find { it.name == name }!!
|
||||
|
||||
fun Mixin.input(name: String) = inputs.find { it.name == name }!!
|
||||
fun Mixin.arg(name: String) = args.find { it.name == name }!!
|
||||
fun Mixin.output(name: String) = outputs.find { it.name == name }!!
|
||||
fun Mixin.config(name: String) = configs.find { it.name == name }!!
|
||||
}
|
||||
|
||||
data class Op (
|
||||
val opName: String,
|
||||
var libnd4jOpName: String? = null,
|
||||
var javaOpClass: String? = null,
|
||||
var isAbstract: Boolean = false,
|
||||
var legacy: Boolean = false,
|
||||
var argsFirst: Boolean = false,
|
||||
var javaPackage: String? = null,
|
||||
val inputs: MutableList<Input> = mutableListOf(),
|
||||
val outputs: MutableList<Output> = mutableListOf(),
|
||||
val args: MutableList<Arg> = mutableListOf(),
|
||||
val constraints: MutableList<Constraint> = mutableListOf(),
|
||||
val signatures: MutableList<Signature> = mutableListOf(),
|
||||
val doc: MutableList<DocSection> = mutableListOf(),
|
||||
val configs: MutableList<Config> = mutableListOf()
|
||||
): OpLike {
|
||||
override fun name() = opName
|
||||
override fun inputs(): List<Input> = inputs
|
||||
override fun args(): List<Arg> = args
|
||||
override fun configs(): List<Config> = configs
|
||||
override fun outputs(): List<Output> = outputs
|
||||
override fun allParameters(): List<Parameter> = inputs + args + configs
|
||||
|
||||
|
||||
override fun toString(): String {
|
||||
return "Op(opName=$opName, libnd4jOpName=$libnd4jOpName, isAbstract=$isAbstract)"
|
||||
}
|
||||
|
||||
override fun addInput(input: Input) { inputs.addOrReplace(input) }
|
||||
override fun addArgument(arg: Arg) { args.addOrReplace(arg) }
|
||||
override fun addOutput(output: Output) { outputs.addOrReplace(output) }
|
||||
override fun addDoc(docs: DocSection){ doc.add(docs) }
|
||||
override fun addSignature(signature: Signature){ signatures.add(signature) }
|
||||
override fun addConstraint(constraint: Constraint){ constraints.add(constraint) }
|
||||
override fun addConfig(config: Config) { configs.addOrReplace(config) }
|
||||
|
||||
/**
|
||||
* Check that all required properties are set
|
||||
*/
|
||||
fun checkInvariants() {
|
||||
if( !isAbstract && (doc.size == 0 || doc.all { it.text.isNullOrBlank() } != false )){
|
||||
throw IllegalStateException("$opName: Ops must be documented!")
|
||||
}
|
||||
|
||||
signatures.forEach {
|
||||
val opParameters = mutableListOf<Parameter>()
|
||||
opParameters.addAll(inputs)
|
||||
opParameters.addAll(args)
|
||||
|
||||
val notCovered = opParameters.fold(mutableListOf<Parameter>()){acc, parameter ->
|
||||
if(!(it.parameters.contains(parameter) || parameter.defaultValueIsApplicable(it.parameters))){
|
||||
acc.add(parameter)
|
||||
}
|
||||
acc
|
||||
}
|
||||
|
||||
if(notCovered.size > 0){
|
||||
throw IllegalStateException("$opName: $it does not cover all parameters! Missing: ${notCovered.joinToString(", ") { it.name() }}")
|
||||
}
|
||||
}
|
||||
|
||||
args.filter { it.type == DataType.ENUM }.forEach {
|
||||
if(it.description == null){
|
||||
throw IllegalStateException("$opName: Argument ${it.name} is ENUM but has no documentation!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Mixin (
|
||||
val name: String,
|
||||
|
||||
val inputs: MutableList<Input> = mutableListOf(),
|
||||
val outputs: MutableList<Output> = mutableListOf(),
|
||||
val args: MutableList<Arg> = mutableListOf(),
|
||||
val constraints: MutableList<Constraint> = mutableListOf(),
|
||||
val signatures: MutableList<Signature> = mutableListOf(),
|
||||
val doc: MutableList<DocSection> = mutableListOf(),
|
||||
val configs: MutableList<Config> = mutableListOf()
|
||||
): OpLike {
|
||||
override fun name() = name
|
||||
override fun inputs(): List<Input> = inputs
|
||||
override fun args(): List<Arg> = args
|
||||
override fun configs(): List<Config> = configs
|
||||
override fun outputs(): List<Output> = outputs
|
||||
override fun allParameters(): List<Parameter> = inputs + args + configs
|
||||
|
||||
override fun toString(): String {
|
||||
return "Mixin($name)"
|
||||
}
|
||||
|
||||
override fun addInput(input: Input) { inputs.addOrReplace(input) }
|
||||
override fun addArgument(arg: Arg) { args.addOrReplace(arg) }
|
||||
override fun addOutput(output: Output) { outputs.addOrReplace(output) }
|
||||
override fun addDoc(docs: DocSection){ doc.add(docs) }
|
||||
override fun addSignature(signature: Signature){ signatures.add(signature) }
|
||||
override fun addConstraint(constraint: Constraint){ constraints.add(constraint) }
|
||||
override fun addConfig(config: Config) { configs.addOrReplace(config) }
|
||||
|
||||
|
||||
var legacyWasSet: Boolean = false
|
||||
var legacy: Boolean = false
|
||||
set(value) {
|
||||
field = value
|
||||
legacyWasSet = true
|
||||
}
|
||||
var javaPackageWasSet: Boolean = false
|
||||
var javaPackage: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
javaPackageWasSet = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that all required properties are set
|
||||
*/
|
||||
fun checkInvariants() {
|
||||
signatures.forEach {
|
||||
val opParameters = mutableListOf<Parameter>()
|
||||
opParameters.addAll(inputs)
|
||||
opParameters.addAll(args)
|
||||
|
||||
val notCovered = opParameters.fold(mutableListOf<Parameter>()){acc, parameter ->
|
||||
if(!(it.parameters.contains(parameter) || parameter.defaultValueIsApplicable(it.parameters))){
|
||||
acc.add(parameter)
|
||||
}
|
||||
acc
|
||||
}
|
||||
|
||||
if(notCovered.size > 0){
|
||||
throw IllegalStateException("$this: $it does not cover all parameters! Missing: ${notCovered.joinToString(", ") { it.name() }}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T: Parameter> MutableList<T>.addOrReplaceAll(params: List<T>){
|
||||
params.forEach {
|
||||
this.addOrReplace(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T: Parameter> MutableList<T>.addOrReplace(param: T){
|
||||
val found = this.find { it.name() == param.name() }
|
||||
if(found != null){
|
||||
this.replaceAll { if(it.name() == param.name()){ param } else { it } }
|
||||
}else{
|
||||
this.add(param)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.api
|
||||
|
||||
object Registry {
|
||||
private val enums: MutableMap<String, Arg> = mutableMapOf()
|
||||
private val configs: MutableMap<String, Config> = mutableMapOf()
|
||||
|
||||
fun enums() = enums.values.sortedBy { it.name }
|
||||
fun configs() = configs.values.sortedBy { it.name }
|
||||
|
||||
fun registerEnum(arg: Arg){
|
||||
when(enums[arg.name]){
|
||||
null -> enums[arg.name] = arg
|
||||
arg -> { /* noop */ }
|
||||
else -> throw IllegalStateException("Another enum with the name ${arg.name} already exists! Enums have to use unique names. If you want to use an enum in multiple places, use mixins to define them.")
|
||||
}
|
||||
}
|
||||
|
||||
fun registerConfig(config: Config){
|
||||
when(configs[config.name]){
|
||||
null -> configs[config.name] = config
|
||||
config -> { /* noop */ }
|
||||
else -> throw IllegalStateException("Another config with the name ${config.name} already exists! Configs have to use unique names.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.api
|
||||
|
||||
import org.nd4j.codegen.api.doc.DocSection
|
||||
import java.util.*
|
||||
|
||||
|
||||
open class Constraint (
|
||||
var message: String? = null,
|
||||
var check: Expression
|
||||
)
|
||||
|
||||
class BackendConstraint(message: String? = null, check: Expression): Constraint(message, check)
|
||||
|
||||
// Used in Constraint Expressions
|
||||
sealed class Reference
|
||||
data class NumberReference<T: Number>(val value: T): Reference()
|
||||
data class BooleanReference(val value: Boolean): Reference()
|
||||
data class InputShapeReference(val input: Input, val idx: Int): Reference()
|
||||
data class InputRankReference(val input: Input): Reference()
|
||||
sealed class Expression: Reference()
|
||||
data class BooleanExpression(val left: Reference, val right: Reference, val op: BooleanOperation): Expression()
|
||||
data class SameTypeExpression(val inputs: List<Input>): Expression()
|
||||
data class SameShapeExpression(val inputs: List<Input>): Expression()
|
||||
data class BroadcastableShapesExpression(val inputs: List<Input>): Expression()
|
||||
enum class BooleanOperation{EQ, NEQ, LT, LTE, GT, GTE, AND, OR}
|
||||
|
||||
|
||||
// Used to define array sizes
|
||||
sealed class Count
|
||||
data class Range(val from: Int, val to: Int): Count()
|
||||
data class AtLeast(val min: Int): Count()
|
||||
data class AtMost(val max: Int): Count()
|
||||
data class Exactly(val count: Int): Count()
|
||||
|
||||
// Actual parameters
|
||||
interface Parameter {
|
||||
fun name(): String
|
||||
fun defaultValue() : Any?
|
||||
|
||||
fun hasDefaultValue(): Boolean
|
||||
|
||||
fun isVararg():Boolean
|
||||
|
||||
/**
|
||||
* A default value only is applicable if it is a literal value, or the referenced value is either directly a part of
|
||||
* the signature, or there is a reference chain that ends in something that is actually a part of the signature
|
||||
*/
|
||||
fun defaultValueIsApplicable(otherParams: List<Parameter>): Boolean = if(hasDefaultValue()){
|
||||
when(val defaultValue = this.defaultValue()){
|
||||
is Number, is Boolean, null -> true
|
||||
is IntArray, is BooleanArray, is DoubleArray -> true
|
||||
is String -> true
|
||||
is org.nd4j.linalg.api.buffer.DataType -> true
|
||||
is org.nd4j.codegen.api.LossReduce -> true
|
||||
is Parameter -> otherParams.contains(defaultValue) || defaultValue.defaultValueIsApplicable(otherParams)
|
||||
is TensorDataTypeValue -> otherParams.contains(defaultValue.tensor) || defaultValue.tensor.defaultValueIsApplicable(otherParams)
|
||||
is TensorShapeValue -> otherParams.contains(defaultValue.tensor) || defaultValue.tensor.defaultValueIsApplicable(otherParams)
|
||||
else -> false
|
||||
}
|
||||
}else{
|
||||
false
|
||||
}
|
||||
}
|
||||
interface Tensor: Parameter
|
||||
|
||||
data class Arg(
|
||||
val name: String,
|
||||
val type: DataType,
|
||||
var description: String? = null,
|
||||
var isVargarg: Boolean = false
|
||||
) : Reference(), Parameter {
|
||||
override fun name(): String = name
|
||||
override fun defaultValue(): Any? = defaultValue
|
||||
override fun hasDefaultValue(): Boolean = defaultValueIsSet
|
||||
override fun isVararg(): Boolean {
|
||||
return isVargarg
|
||||
}
|
||||
|
||||
private var defaultValueIsSet = false
|
||||
var defaultValue: Any? = null
|
||||
set(value) = if(isAssignableFrom(value)) {
|
||||
field = value
|
||||
defaultValueIsSet = true
|
||||
}else{
|
||||
throw IllegalArgumentException("Illegal default value for $this. Got ${value.toDescriptiveString()} (${value?.javaClass?.name})")
|
||||
}
|
||||
|
||||
var possibleValues: List<String>? = null
|
||||
set(value) = if(type == DataType.ENUM) when {
|
||||
value == null -> field = null
|
||||
value.isEmpty() -> throw IllegalArgumentException("$this: Can not set empty possibleValues.")
|
||||
else -> field = value
|
||||
} else {
|
||||
throw IllegalArgumentException("$this: Can not set possibleValues on non ENUM typed Arg.")
|
||||
}
|
||||
|
||||
var count: Count? = null
|
||||
set(value) = if(type == DataType.ENUM && value != Exactly(1)) {
|
||||
throw IllegalArgumentException("$this: ENUM typed Arg can not be array")
|
||||
}else{
|
||||
field = value
|
||||
}
|
||||
|
||||
private fun matchesDataType(value: Any?) = when(type){
|
||||
DataType.FLOATING_POINT -> value is Double
|
||||
DataType.INT -> (value is Int) || (value is Long)
|
||||
DataType.LONG -> (value is Int) || (value is Long)
|
||||
DataType.NUMERIC -> value is Number
|
||||
DataType.BOOL -> value is Boolean
|
||||
else -> false
|
||||
}
|
||||
|
||||
private fun isAssignableFrom(value: Any?) = when(value){
|
||||
is TensorShapeValue -> isArray() && type == DataType.INT
|
||||
is TensorDataTypeValue -> type == DataType.DATA_TYPE
|
||||
is Number, is Boolean -> matchesDataType(value)
|
||||
is IntArray -> isArray() && (type == DataType.INT || type == DataType.NUMERIC) && countMatches(value.size)
|
||||
is DoubleArray -> isArray() && (type == DataType.FLOATING_POINT || type == DataType.NUMERIC) && countMatches(value.size)
|
||||
is BooleanArray -> isArray() && type == DataType.BOOL && countMatches(value.size)
|
||||
is Arg -> value.count == count && value.type == type
|
||||
is String -> type == DataType.STRING || type == DataType.ENUM && possibleValues != null && possibleValues?.contains(value) ?: false
|
||||
//is String -> type == DataType.ENUM && possibleValues != null && possibleValues?.contains(value) ?: false
|
||||
is org.nd4j.linalg.api.buffer.DataType -> type == DataType.DATA_TYPE
|
||||
is org.nd4j.codegen.api.LossReduce -> type == DataType.LOSS_REDUCE
|
||||
null -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
fun isArray() = count != Exactly(1) && count != null
|
||||
fun countMatches(size: Int) = when(val c = count!!){
|
||||
is Range -> c.from <= size && size <= c.to
|
||||
is AtLeast -> c.min <= size
|
||||
is AtMost -> size <= c.max
|
||||
is Exactly -> c.count == size
|
||||
}
|
||||
|
||||
fun Tensor.shape() = TensorShapeValue(this)
|
||||
fun Tensor.dataType() = TensorDataTypeValue(this)
|
||||
|
||||
override fun toString() = "Arg(${if(type == DataType.ENUM){
|
||||
"ENUM(${possibleValues?.joinToString(", ")})"
|
||||
}else{
|
||||
type.toString()
|
||||
}}, $name)${if(count != null) "{ count = $count }" else "" }"
|
||||
}
|
||||
|
||||
data class Input (
|
||||
val name: String,
|
||||
val type: DataType,
|
||||
var description: String? = null,
|
||||
var count: Count? = null
|
||||
) : Parameter, Tensor {
|
||||
override fun isVararg(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun name(): String = name
|
||||
override fun defaultValue(): Any? = defaultValue
|
||||
override fun hasDefaultValue(): Boolean = defaultValueIsSet
|
||||
|
||||
private var defaultValueIsSet = false
|
||||
var defaultValue: Input? = null
|
||||
set(value) = if(matchesDataType(value)){
|
||||
field = value
|
||||
defaultValueIsSet = true
|
||||
}else{
|
||||
throw IllegalArgumentException("Illegal default value for Input($name). Allowed values have to match data type $type, but got ${value.toDescriptiveString()} (${value?.javaClass?.name})")
|
||||
}
|
||||
|
||||
private fun matchesDataType(value: Input?) = when(value){
|
||||
null -> true
|
||||
else -> value.type == type
|
||||
}
|
||||
}
|
||||
|
||||
data class Output(
|
||||
var name: String,
|
||||
var type: DataType,
|
||||
var multiOutput: Boolean,
|
||||
var description: String? = null
|
||||
) : Parameter, Tensor{
|
||||
override fun isVararg(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun name(): String = name
|
||||
override fun defaultValue(): Any? = null
|
||||
override fun hasDefaultValue(): Boolean = false
|
||||
}
|
||||
|
||||
data class Signature(
|
||||
val parameters: List<Parameter>,
|
||||
val description: String? = null
|
||||
){
|
||||
override fun toString(): String {
|
||||
return "Signature(${parameters.joinToString {it.name()}})"
|
||||
}
|
||||
}
|
||||
|
||||
// Used in defining default values
|
||||
data class TensorShapeValue(val tensor: Tensor) {
|
||||
override fun toString(): String = "${tensor.name()}.shape()"
|
||||
}
|
||||
data class TensorDataTypeValue(val tensor: Tensor){
|
||||
override fun toString(): String = "${tensor.name()}.dataType()"
|
||||
}
|
||||
|
||||
fun Any?.toDescriptiveString() = when(this){
|
||||
null -> "null"
|
||||
is IntArray -> Arrays.toString(this)
|
||||
is LongArray -> Arrays.toString(this)
|
||||
is DoubleArray -> Arrays.toString(this)
|
||||
is FloatArray -> Arrays.toString(this)
|
||||
is BooleanArray -> Arrays.toString(this)
|
||||
is Array<*> -> Arrays.toString(this)
|
||||
else -> this.toString()
|
||||
}
|
||||
|
||||
data class Config(
|
||||
val name: String,
|
||||
val inputs: MutableList<Input> = mutableListOf(),
|
||||
val args: MutableList<Arg> = mutableListOf(),
|
||||
val constraints: MutableList<Constraint> = mutableListOf(),
|
||||
val doc: MutableList<DocSection> = mutableListOf()
|
||||
): Parameter {
|
||||
override fun isVararg(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun name(): String = name
|
||||
override fun defaultValue(): Any? = null
|
||||
override fun hasDefaultValue(): Boolean = false
|
||||
|
||||
fun addInput(input: Input) { inputs.add(input) }
|
||||
fun addArgument(arg: Arg) { args.add(arg) }
|
||||
fun addConstraint(constraint: Constraint){ constraints.add(constraint) }
|
||||
fun addDoc(doc: DocSection){ this.doc.add(doc) }
|
||||
|
||||
var javaClassOverride: String = ""
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.api.doc
|
||||
|
||||
import org.nd4j.codegen.api.CodeComponent
|
||||
|
||||
enum class DocScope {
|
||||
ALL, CLASS_DOC_ONLY, CREATORS_ONLY, CONSTRUCTORS_ONLY;
|
||||
|
||||
fun applies(codeComponent: CodeComponent): Boolean {
|
||||
return when (this) {
|
||||
ALL -> true
|
||||
CLASS_DOC_ONLY -> codeComponent === CodeComponent.CLASS_DOC
|
||||
CREATORS_ONLY -> codeComponent === CodeComponent.OP_CREATOR
|
||||
CONSTRUCTORS_ONLY -> codeComponent === CodeComponent.CONSTRUCTOR
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.api.doc
|
||||
|
||||
import org.nd4j.codegen.api.CodeComponent
|
||||
import org.nd4j.codegen.api.Language
|
||||
|
||||
|
||||
data class DocSection(var scope: DocScope? = null,
|
||||
var language: Language? = null,
|
||||
var text: String? = null) {
|
||||
|
||||
fun applies(language: Language, codeComponent: CodeComponent): Boolean {
|
||||
return (this.language === Language.ANY || language === this.language) && scope!!.applies(codeComponent)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.api.doc
|
||||
|
||||
import org.nd4j.codegen.api.Op
|
||||
|
||||
object DocTokens {
|
||||
enum class GenerationType { SAMEDIFF, ND4J }
|
||||
private val OPNAME = "%OPNAME%".toRegex()
|
||||
private val LIBND4J_OPNAME = "%LIBND4J_OPNAME%".toRegex()
|
||||
private val INPUT_TYPE = "%INPUT_TYPE%".toRegex()
|
||||
|
||||
@JvmStatic fun processDocText(doc: String?, op: Op, type: GenerationType): String {
|
||||
return doc
|
||||
?.replace(OPNAME, op.opName)
|
||||
?.replace(LIBND4J_OPNAME, op.libnd4jOpName!!)
|
||||
?.replace(INPUT_TYPE, when(type){
|
||||
GenerationType.SAMEDIFF -> "SDVariable"
|
||||
GenerationType.ND4J -> "INDArray"
|
||||
}) ?: ""
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.api.generator
|
||||
|
||||
import org.nd4j.codegen.api.Expression
|
||||
|
||||
|
||||
interface ConstraintCodeGenerator {
|
||||
fun generateExpression(expression: Expression): String
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.api.generator
|
||||
|
||||
import org.nd4j.codegen.api.Language
|
||||
import org.nd4j.codegen.api.NamespaceOps
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
interface Generator {
|
||||
fun language(): Language?
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun generateNamespaceNd4j(namespace: NamespaceOps?, config: GeneratorConfig?, directory: File?, className: String?)
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun generateNamespaceSameDiff(namespace: NamespaceOps?, config: GeneratorConfig?, directory: File?, className: String?)
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.api.generator
|
||||
|
||||
import org.nd4j.codegen.api.Op
|
||||
|
||||
class GeneratorConfig {
|
||||
fun acceptOp(op: Op?): Boolean = true
|
||||
}
|
||||
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.dsl
|
||||
|
||||
import org.nd4j.codegen.api.*
|
||||
import org.nd4j.codegen.api.doc.DocScope
|
||||
import org.nd4j.codegen.api.doc.DocSection
|
||||
import org.nd4j.codegen.ops.SDBaseOps
|
||||
import java.lang.IllegalStateException
|
||||
|
||||
fun Namespace(name: String, block: NamespaceOps.() -> Unit): NamespaceOps {
|
||||
val ns = NamespaceOps(name)
|
||||
ns.block()
|
||||
|
||||
ns.checkInvariants()
|
||||
return ns
|
||||
}
|
||||
|
||||
fun Mixin(name: String, block: Mixin.() -> Unit): Mixin {
|
||||
return Mixin(name).apply(block).also {
|
||||
it.checkInvariants()
|
||||
}
|
||||
}
|
||||
|
||||
fun NamespaceOps.Alias(ns:NamespaceOps, opName:String):Op {
|
||||
val op:Op? = ns.op(opName)
|
||||
op?.let {
|
||||
this.parentNamespaceOps[ns.name]?.add(op)
|
||||
this.ops.add(op)
|
||||
return op
|
||||
}
|
||||
throw IllegalStateException("Failed to create alias for op: $opName")
|
||||
}
|
||||
|
||||
fun NamespaceOps.Op(name: String, block: Op.() -> Unit): Op {
|
||||
val op = Op(name)
|
||||
op.libnd4jOpName = name
|
||||
|
||||
op.block()
|
||||
|
||||
if (!op.isAbstract && op.signatures.isEmpty()) {
|
||||
op.AllParamSignature()
|
||||
op.AllDefaultsSignature()
|
||||
}
|
||||
|
||||
op.checkInvariants()
|
||||
|
||||
this.ops.add(op)
|
||||
return op
|
||||
}
|
||||
|
||||
fun NamespaceOps.Op(name: String,
|
||||
extends: Mixin,
|
||||
keepArgs: Boolean = true,
|
||||
keepInputs: Boolean = true,
|
||||
keepOutputs: Boolean = true,
|
||||
keepConstraints: Boolean = true,
|
||||
keepSignatures: Boolean = true,
|
||||
keepDocs: Boolean = true,
|
||||
block: (Op.() -> Unit)? = null): Op {
|
||||
return this.Op(name) {
|
||||
useMixin(extends, keepArgs = keepArgs, keepInputs = keepInputs, keepOutputs = keepOutputs, keepConstraints = keepConstraints, keepSignatures = keepSignatures, keepDocs = keepDocs)
|
||||
|
||||
if (block != null) {
|
||||
this.block()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun OpLike.Input(dataType: DataType, name: String, block: (Input.() -> Unit)? = null): Input {
|
||||
val input = Input(name, dataType)
|
||||
if (block != null) input.block()
|
||||
|
||||
if (!dataType.isTensorDataType()) {
|
||||
throw IllegalArgumentException("Invalid datatype for input \"$name\" of op ${this.name()}: inputs arrays cannot have type $dataType - wrong type, or should be Arg type?");
|
||||
}
|
||||
|
||||
this.addInput(input)
|
||||
|
||||
|
||||
return input
|
||||
}
|
||||
|
||||
fun OpLike.Arg(dataType: DataType, name: String, block: (Arg.() -> Unit)? = null): Arg {
|
||||
val input = Arg(name, dataType)
|
||||
if (block != null) input.block()
|
||||
|
||||
this.addArgument(input)
|
||||
if(dataType == DataType.ENUM){
|
||||
Registry.registerEnum(input)
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
||||
fun OpLike.Output(dataType: DataType, name: String, block: (Output.() -> Unit)? = null): Output {
|
||||
val output = Output(name, dataType, false)
|
||||
if (block != null) output.block()
|
||||
|
||||
if (!dataType.isTensorDataType()) {
|
||||
throw IllegalArgumentException("Invalid datatype for output \"$name\" of op ${this.name()}: output arrays cannot have type $dataType");
|
||||
}
|
||||
|
||||
this.addOutput(output)
|
||||
return output
|
||||
}
|
||||
|
||||
fun OpLike.Doc(language: Language, scope: DocScope, block: DocSection.() -> String): DocSection {
|
||||
val doc = DocSection().apply {
|
||||
this.language = language
|
||||
this.scope = scope
|
||||
text = this.block()
|
||||
}
|
||||
this.addDoc(doc)
|
||||
return doc
|
||||
}
|
||||
|
||||
fun OpLike.AllParamSignature(withOutput: Boolean = false) {
|
||||
val allParameters = allParameters()
|
||||
|
||||
this.addSignature(Signature(allParameters))
|
||||
if (withOutput) {
|
||||
val withOutputParams = mutableListOf<Parameter>().also {
|
||||
it.addAll(this.outputs())
|
||||
it.addAll(allParameters)
|
||||
}
|
||||
this.addSignature(Signature(withOutputParams))
|
||||
}
|
||||
}
|
||||
|
||||
fun OpLike.AllDefaultsSignature(withOutput: Boolean = false) {
|
||||
val allParameters = allParameters()
|
||||
|
||||
if (allParameters.find { it.hasDefaultValue() } != null) {
|
||||
val params = allParameters.filterNot { it.hasDefaultValue() }
|
||||
this.addSignature(Signature(params))
|
||||
if (withOutput) {
|
||||
val withOutputParams = mutableListOf<Parameter>().also {
|
||||
it.addAll(this.outputs())
|
||||
it.addAll(allParameters)
|
||||
}
|
||||
this.addSignature(Signature(withOutputParams))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun OpLike.Signature(vararg params: Parameter, block: (Signature.() -> String)? = null): Signature {
|
||||
if (params.toSet().size < params.size) {
|
||||
throw IllegalArgumentException("A parameter may not be used twice in a signature!")
|
||||
}
|
||||
val paramsAndOutputs = allParameters() + outputs()
|
||||
if (!paramsAndOutputs.containsAll(params.toList())) {
|
||||
throw IllegalArgumentException("You can only use parameters in a signature that are actually defined in $this! Did you forget to useMixin(...) a mixin?")
|
||||
}
|
||||
|
||||
val signature = Signature(params.toList())
|
||||
if (block != null) {
|
||||
signature.block()
|
||||
}
|
||||
this.addSignature(signature)
|
||||
return signature
|
||||
}
|
||||
|
||||
fun OpLike.Constraint(desc: String, block: ConstraintBuilder.() -> Expression): Constraint {
|
||||
val check = ConstraintBuilder().block()
|
||||
val constraint = Constraint(desc, check)
|
||||
this.addConstraint(constraint)
|
||||
return constraint
|
||||
}
|
||||
|
||||
fun OpLike.BackendConstraint(desc: String, block: ConstraintBuilder.() -> Expression): Constraint {
|
||||
val check = ConstraintBuilder().block()
|
||||
val constraint = BackendConstraint(desc, check)
|
||||
this.addConstraint(constraint)
|
||||
return constraint
|
||||
}
|
||||
|
||||
class ConstraintBuilder {
|
||||
fun broadcastableShapes(vararg inputs: Input) = BroadcastableShapesExpression(inputs.toList())
|
||||
fun sameShape(vararg inputs: Input) = SameShapeExpression(inputs.toList())
|
||||
fun sameType(vararg inputs: Input) = SameTypeExpression(inputs.toList())
|
||||
|
||||
fun Input.sizeAt(i: Int) = InputShapeReference(this, i)
|
||||
fun Input.rank() = InputRankReference(this)
|
||||
fun Input.isScalar() = this.rank() eq 0
|
||||
|
||||
fun some(expr: BooleanExpression, vararg exprs: BooleanExpression) = exprs.fold(expr, { acc, cur -> acc or cur })
|
||||
fun all(expr: BooleanExpression, vararg exprs: BooleanExpression) = exprs.fold(expr, { acc, cur -> acc and cur })
|
||||
fun not(expr: BooleanExpression) = expr eq false
|
||||
|
||||
infix fun BooleanExpression.and(other: BooleanExpression) = BooleanExpression(this, other, BooleanOperation.AND)
|
||||
infix fun BooleanExpression.or(other: BooleanExpression) = BooleanExpression(this, other, BooleanOperation.OR)
|
||||
|
||||
|
||||
infix fun Reference.eq(other: Reference) = BooleanExpression(this, other, BooleanOperation.EQ)
|
||||
infix fun Reference.eq(other: Number) = this eq NumberReference(other)
|
||||
infix fun Reference.eq(other: Boolean) = this eq BooleanReference(other)
|
||||
|
||||
|
||||
infix fun Reference.neq(other: Reference) = BooleanExpression(this, other, BooleanOperation.NEQ)
|
||||
infix fun <T : Number> Reference.neq(other: T) = this neq NumberReference(other)
|
||||
infix fun Reference.neq(other: Boolean) = this neq BooleanReference(other)
|
||||
|
||||
infix fun Reference.gt(other: Reference) = BooleanExpression(this, other, BooleanOperation.GT)
|
||||
infix fun <T : Number> Reference.gt(other: T) = this gt NumberReference(other)
|
||||
|
||||
infix fun Reference.lt(other: Reference) = BooleanExpression(this, other, BooleanOperation.LT)
|
||||
infix fun <T : Number> Reference.lt(other: T) = this lt NumberReference(other)
|
||||
|
||||
|
||||
infix fun <T : Number> Reference.gte(other: T) = this gte NumberReference(other)
|
||||
infix fun Reference.gte(other: Reference) = BooleanExpression(this, other, BooleanOperation.GTE)
|
||||
|
||||
infix fun <T : Number> Reference.lte(other: T) = this lte NumberReference(other)
|
||||
infix fun Reference.lte(other: Reference) = BooleanExpression(this, other, BooleanOperation.LTE)
|
||||
}
|
||||
|
||||
fun NamespaceOps.Config(name: String, block: (Config.() -> Unit)): Config {
|
||||
val config = Config(name)
|
||||
config.block()
|
||||
this.addConfig(config)
|
||||
Registry.registerConfig(config)
|
||||
return config
|
||||
}
|
||||
|
||||
fun Config.Input(dataType: DataType, name: String, block: (Input.() -> Unit)? = null): Input {
|
||||
val input = Input(name, dataType)
|
||||
if (block != null) input.block()
|
||||
|
||||
if (!dataType.isTensorDataType()) {
|
||||
throw IllegalArgumentException("Invalid datatype for input \"$name\" of config ${this.name}: inputs arrays cannot have type $dataType - wrong type, or should be Arg type?");
|
||||
}
|
||||
|
||||
this.addInput(input)
|
||||
return input
|
||||
}
|
||||
|
||||
fun Config.Arg(dataType: DataType, name: String, block: (Arg.() -> Unit)? = null): Arg {
|
||||
val input = Arg(name, dataType)
|
||||
if (block != null) input.block()
|
||||
|
||||
this.addArgument(input)
|
||||
if(dataType == DataType.ENUM){
|
||||
Registry.registerEnum(input)
|
||||
}
|
||||
|
||||
return input
|
||||
}
|
||||
|
||||
fun Config.Constraint(desc: String, block: ConstraintBuilder.() -> Expression): Constraint {
|
||||
val check = ConstraintBuilder().block()
|
||||
val constraint = Constraint(desc, check)
|
||||
this.addConstraint(constraint)
|
||||
return constraint
|
||||
}
|
||||
|
||||
fun Config.BackendConstraint(desc: String, block: ConstraintBuilder.() -> Expression): Constraint {
|
||||
val check = ConstraintBuilder().block()
|
||||
val constraint = BackendConstraint(desc, check)
|
||||
this.addConstraint(constraint)
|
||||
return constraint
|
||||
}
|
||||
|
||||
fun Config.Doc(language: Language, scope: DocScope, block: DocSection.() -> String): DocSection {
|
||||
val doc = DocSection().apply {
|
||||
this.language = language
|
||||
this.scope = scope
|
||||
text = this.block()
|
||||
}
|
||||
this.addDoc(doc)
|
||||
return doc
|
||||
}
|
||||
|
||||
fun OpLike.useConfig(config: Config): Config {
|
||||
this.addConfig(config)
|
||||
return config
|
||||
}
|
||||
|
||||
fun Op.useMixin(mixin: Mixin,
|
||||
keepArgs: Boolean = true,
|
||||
keepInputs: Boolean = true,
|
||||
keepOutputs: Boolean = true,
|
||||
keepConstraints: Boolean = true,
|
||||
keepSignatures: Boolean = true,
|
||||
keepDocs: Boolean = true,
|
||||
keepConfigs: Boolean = true
|
||||
) {
|
||||
if(mixin.legacyWasSet){
|
||||
legacy = mixin.legacy
|
||||
}
|
||||
if(mixin.javaPackageWasSet){
|
||||
javaPackage = mixin.javaPackage
|
||||
}
|
||||
if (keepArgs) {
|
||||
args.addOrReplaceAll(mixin.args)
|
||||
}
|
||||
if (keepInputs) {
|
||||
inputs.addOrReplaceAll(mixin.inputs)
|
||||
}
|
||||
if (keepOutputs) {
|
||||
outputs.addOrReplaceAll(mixin.outputs)
|
||||
}
|
||||
if (keepConstraints) {
|
||||
constraints.addAll(mixin.constraints)
|
||||
}
|
||||
if (keepSignatures) {
|
||||
signatures.addAll(mixin.signatures)
|
||||
}
|
||||
if (keepDocs) {
|
||||
doc.addAll(mixin.doc)
|
||||
}
|
||||
if(keepConfigs){
|
||||
configs.addOrReplaceAll(mixin.configs)
|
||||
}
|
||||
}
|
||||
|
||||
fun Mixin.useMixin(mixin: Mixin,
|
||||
keepArgs: Boolean = true,
|
||||
keepInputs: Boolean = true,
|
||||
keepOutputs: Boolean = true,
|
||||
keepConstraints: Boolean = true,
|
||||
keepSignatures: Boolean = true,
|
||||
keepDocs: Boolean = true,
|
||||
keepConfigs: Boolean = true) {
|
||||
if(mixin.legacyWasSet){
|
||||
legacy = mixin.legacy
|
||||
}
|
||||
if(mixin.javaPackageWasSet){
|
||||
javaPackage = mixin.javaPackage
|
||||
}
|
||||
if (keepArgs) {
|
||||
args.addOrReplaceAll(mixin.args)
|
||||
}
|
||||
if (keepInputs) {
|
||||
inputs.addOrReplaceAll(mixin.inputs)
|
||||
}
|
||||
if (keepOutputs) {
|
||||
outputs.addOrReplaceAll(mixin.outputs)
|
||||
}
|
||||
if (keepConstraints) {
|
||||
constraints.addAll(mixin.constraints)
|
||||
}
|
||||
if (keepSignatures) {
|
||||
signatures.addAll(mixin.signatures)
|
||||
}
|
||||
if (keepDocs) {
|
||||
doc.addAll(mixin.doc)
|
||||
}
|
||||
if(keepConfigs){
|
||||
configs.addOrReplaceAll(mixin.configs)
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.impl.java
|
||||
|
||||
import org.nd4j.codegen.api.*
|
||||
import org.nd4j.codegen.api.generator.ConstraintCodeGenerator
|
||||
|
||||
class JavaConstraintCodeGenerator: ConstraintCodeGenerator {
|
||||
override fun generateExpression(expression: Expression): String = when(expression) {
|
||||
is BooleanExpression -> {
|
||||
val left = generateReference(expression.left)
|
||||
val right = generateReference(expression.right)
|
||||
when(expression.op){
|
||||
BooleanOperation.EQ -> "$left == $right"
|
||||
BooleanOperation.NEQ -> "$left != $right"
|
||||
BooleanOperation.LT -> "$left < $right"
|
||||
BooleanOperation.LTE -> "$left <= $right"
|
||||
BooleanOperation.GT -> "$left > $right"
|
||||
BooleanOperation.GTE -> "$left >= $right"
|
||||
BooleanOperation.AND -> "$left && $right"
|
||||
BooleanOperation.OR -> "$left || $right"
|
||||
}
|
||||
}
|
||||
is SameTypeExpression -> "isSameType(${expression.inputs.joinToString(", "){ it.name }})"
|
||||
is SameShapeExpression -> "isSameShape(${expression.inputs.joinToString(", "){ it.name }})"
|
||||
is BroadcastableShapesExpression -> "isBroadcastableShapes(${expression.inputs.joinToString(", "){ it.name }})"
|
||||
}
|
||||
|
||||
private fun generateReference(reference: Reference): String = when(reference){
|
||||
is NumberReference<*> -> reference.value.toString()
|
||||
is BooleanReference -> reference.value.toString()
|
||||
is InputShapeReference -> "${reference.input.name}.sizeAt(${reference.idx})"
|
||||
is InputRankReference -> "${reference.input.name}.rank()"
|
||||
is Arg -> reference.name
|
||||
is Expression -> "(${generateExpression(reference)})"
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.impl.python
|
||||
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.nd4j.codegen.api.Language
|
||||
import org.nd4j.codegen.api.NamespaceOps
|
||||
import org.nd4j.codegen.api.Op
|
||||
import org.nd4j.codegen.api.doc.DocTokens
|
||||
import org.nd4j.codegen.api.generator.Generator
|
||||
import org.nd4j.codegen.api.generator.GeneratorConfig
|
||||
import org.nd4j.codegen.util.GenUtil
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
class KotlinExamplePythonGenerator: Generator {
|
||||
override fun language() = Language.PYTHON
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun generateNamespaceNd4j(namespace: NamespaceOps?, config: GeneratorConfig?, directory: File?, className: String?) {
|
||||
val f = File(directory, GenUtil.ensureFirstIsCap(namespace!!.name) + ".py")
|
||||
val content =
|
||||
"""
|
||||
|class ${GenUtil.ensureFirstIsCap(namespace.name)}:
|
||||
|${namespace.ops.filterNot { it.isAbstract }.joinToString("\n\n") { generateMethod(it).addIndent(4) }}
|
||||
""".trimMargin()
|
||||
FileUtils.writeStringToFile(f, content, StandardCharsets.UTF_8)
|
||||
}
|
||||
|
||||
fun generateMethod(op: Op): String =
|
||||
"""
|
||||
|@staticmethod
|
||||
|def ${GenUtil.ensureFirstIsNotCap(op.opName)}(${op.inputs.joinToString(", "){it.name}}):
|
||||
|${genDocString(op).addIndent(4)}
|
||||
|${"# Execution code here".addIndent(4)}
|
||||
|
||||
""".trimMargin()
|
||||
|
||||
fun genDocString(op: Op): String {
|
||||
//Following roughly: https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html
|
||||
// python docstring / multiline string delimiter is the same as in kotlin, so use this little workaround
|
||||
if (op.args.isNotEmpty()) {
|
||||
//Args and default args
|
||||
throw UnsupportedOperationException("Generating method with args not yet implemented")
|
||||
}
|
||||
if(op.outputs.size != 1) throw UnsupportedOperationException("Not yet implemented: Python docstring generation for multiple output ops")
|
||||
|
||||
val docStringDelimiter = "\"\"\""
|
||||
return """
|
||||
|$docStringDelimiter
|
||||
|${op.opName} operation
|
||||
|
|
||||
|${op.inputs.let { """
|
||||
|Args:
|
||||
|${it.joinToString("\n") {
|
||||
"| ${it.name} (ndarray): ${DocTokens.processDocText(it.description, op, DocTokens.GenerationType.ND4J)}"
|
||||
}}
|
||||
|""".trimMargin() }}
|
||||
|${op.outputs.let {"""
|
||||
|Returns:
|
||||
| ndarray: ${it[0].name} ${it[0].description?.let {"- ${DocTokens.processDocText(it, op, DocTokens.GenerationType.ND4J)}"
|
||||
}}""".trimMargin()
|
||||
}}
|
||||
|$docStringDelimiter
|
||||
""".trimMargin()
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun generateNamespaceSameDiff(namespace: NamespaceOps?, config: GeneratorConfig?, directory: File?, className: String?) {
|
||||
throw UnsupportedOperationException("Not yet implemented")
|
||||
}
|
||||
|
||||
private fun String.addIndent(size: Int): String = GenUtil.addIndent(this, size)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.util
|
||||
|
||||
import org.nd4j.codegen.impl.java.JavaPoetGenerator
|
||||
import org.nd4j.codegen.ops.Bitwise
|
||||
import org.nd4j.codegen.ops.Random
|
||||
import java.io.File
|
||||
|
||||
fun main() {
|
||||
val outDir = File("F:\\dl4j-builds\\deeplearning4j\\nd4j\\nd4j-backends\\nd4j-api-parent\\nd4j-api\\src\\main\\java\\")
|
||||
outDir.mkdirs()
|
||||
|
||||
listOf(Bitwise(), Random()).forEach {
|
||||
val generator = JavaPoetGenerator()
|
||||
generator.generateNamespaceNd4j(it, null, outDir, it.name + ".java")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.mixins
|
||||
|
||||
import org.nd4j.codegen.api.AtLeast
|
||||
import org.nd4j.codegen.api.DataType
|
||||
import org.nd4j.codegen.api.Exactly
|
||||
import org.nd4j.codegen.api.Language
|
||||
import org.nd4j.codegen.api.doc.DocScope
|
||||
import org.nd4j.codegen.dsl.*
|
||||
|
||||
val broadcastingDoc = Mixin("broadcastingDoc"){
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
//TODO: finalize content for this broadcasting mixin doc.
|
||||
"""
|
||||
Note: supports broadcasting if x and y have different shapes and are broadcastable.
|
||||
For example, if X has shape [1,10] and Y has shape [5,10] then op(X,Y) has output shape [5,10]
|
||||
Broadcast rules are the same as NumPy: https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
val transform = Mixin("transform"){
|
||||
legacy = true
|
||||
Input(DataType.NUMERIC, "x") { description = "Input variable" }
|
||||
Output(DataType.NUMERIC, "output"){ description = "Output variable" }
|
||||
}
|
||||
|
||||
val transformArithmetic = Mixin("transformArithmetic"){
|
||||
useMixin(transform)
|
||||
legacy = false
|
||||
Input(DataType.NUMERIC, "y") { description = "Input variable" }
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.pairwise.arithmetic"
|
||||
}
|
||||
|
||||
val transformCustom2 = Mixin("transformCustom2"){
|
||||
Input(DataType.NUMERIC, "x") { description = "First input variable, x" }
|
||||
Input(DataType.NUMERIC, "y") { description = "Second input variable, y" }
|
||||
Output(DataType.NUMERIC, "out"){ description = "Output"}
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
}
|
||||
|
||||
val transformStrict = Mixin("transformStrict"){
|
||||
useMixin(transform)
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.strict"
|
||||
}
|
||||
|
||||
val transformSame = Mixin("transformSame"){
|
||||
useMixin(transform)
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.same"
|
||||
}
|
||||
|
||||
val transformBool = Mixin("transformBool"){
|
||||
useMixin(transform)
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.bool"
|
||||
}
|
||||
|
||||
val transformAny = Mixin("transformAny"){
|
||||
useMixin(transform)
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.any"
|
||||
}
|
||||
|
||||
val transformFloating = Mixin("transformFloating"){
|
||||
useMixin(transform)
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.floating"
|
||||
}
|
||||
|
||||
val scalar = Mixin("scalar"){
|
||||
legacy = true
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.scalar"
|
||||
Input(DataType.NUMERIC, "x") { description = "Input variable" }
|
||||
Arg(DataType.NUMERIC, "value") { description = "Scalar value for op" }
|
||||
Output(DataType.NUMERIC, "output"){ description = "Output variable" }
|
||||
}
|
||||
|
||||
val reduce = Mixin("reduce"){
|
||||
Input(DataType.NUMERIC, "in") { description = "Input variable" }
|
||||
Arg(DataType.BOOL,"keepDims"){ description = "Whether to keep the original dimensions or produce a shrunk array with less dimensions"; defaultValue = false}
|
||||
Arg(DataType.LONG, "dimensions"){ count = AtLeast(0); isVargarg = true; description = "Dimensions to reduce over. If dimensions are not specified, full array reduction is performed" }
|
||||
Output(DataType.NUMERIC, "output"){ description = "Reduced array of rank (input rank - num dimensions)" }
|
||||
}
|
||||
|
||||
val reduceVariableDimensions = Mixin("reduceVariable") {
|
||||
Input(DataType.NUMERIC, "in") { description = "Input variable" }
|
||||
Arg(DataType.BOOL,"keepDims"){ description = "Whether to keep the original dimensions or produce a shrunk array with less dimensions"; defaultValue = false}
|
||||
Input(DataType.NUMERIC, name = "dimensions"){description = "Dimensions to reduce along"; }
|
||||
Output(DataType.NUMERIC, "output"){ description = "Reduced array of rank (input rank - num dimensions)" }
|
||||
}
|
||||
|
||||
val reduceFloating = Mixin("reduceFloating"){
|
||||
useMixin(reduce)
|
||||
legacy = true
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.reduce.floating"
|
||||
}
|
||||
|
||||
val reduceFloatingVariable = Mixin("reduceFloatingVariable"){
|
||||
useMixin(reduceVariableDimensions)
|
||||
legacy = true
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.reduce.floating"
|
||||
}
|
||||
|
||||
|
||||
val reduceSame = Mixin("reduceSame"){
|
||||
useMixin(reduce)
|
||||
legacy = true
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.reduce.same"
|
||||
}
|
||||
|
||||
val reduceSameVariable = Mixin("reduceSameVariable"){
|
||||
useMixin(reduceVariableDimensions)
|
||||
legacy = true
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.reduce.same"
|
||||
}
|
||||
|
||||
val reduceLong = Mixin("reduceLong"){
|
||||
useMixin(reduce)
|
||||
legacy = true
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.reduce.longer"
|
||||
}
|
||||
|
||||
val reduceLongVariable = Mixin("reduceLongVariable"){
|
||||
useMixin(reduceVariableDimensions)
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.reduce.longer"
|
||||
}
|
||||
|
||||
val reduce3 = Mixin("reduce3"){
|
||||
legacy = true
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.reduce3"
|
||||
Input(DataType.NUMERIC, "x") { description = "Input variable x" }
|
||||
Input(DataType.NUMERIC, "y") { description = "Input variable y" }
|
||||
Arg(DataType.BOOL,"keepDims",{description = "Whether to preserve original dimensions or not"; defaultValue = false})
|
||||
Arg(DataType.BOOL,"isComplex",{description = "Depending on the implementation, such as distance calculations, this can determine whether all distance calculations for all points should be done."; defaultValue = false})
|
||||
val dims = Arg(DataType.LONG, "dimensions"){ count = AtLeast(0); isVargarg = true; description = "Dimensions to reduce over. If dimensions are not specified, full array reduction is performed" }
|
||||
Output(DataType.NUMERIC, "output"){ description = "Output variable" }
|
||||
}
|
||||
|
||||
val reduce3Variable = Mixin("reduce3Variable"){
|
||||
legacy = true
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.reduce3"
|
||||
Input(DataType.NUMERIC, "x") { description = "Input variable x" }
|
||||
Input(DataType.NUMERIC, "y") { description = "Input variable y" }
|
||||
Input(DataType.NUMERIC, "dimensions"){ description = "Dimensions to calculate %OPNAME% over" }
|
||||
Output(DataType.NUMERIC, "output"){ description = "Output variable" }
|
||||
Arg(DataType.BOOL,"keepDims",{description = "Whether to preserve original dimensions or not"; defaultValue = false})
|
||||
Arg(DataType.BOOL,"isComplex",{description = "Depending on the implementation, such as distance calculations, this can determine whether all distance calculations for all points should be done."; defaultValue = false})
|
||||
|
||||
}
|
||||
|
||||
val indexAccum = Mixin("indexAccum"){
|
||||
legacy = true
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.indexaccum"
|
||||
val input = Input(DataType.NUMERIC, "in") { description = "Input variable" }
|
||||
val keepDims = Arg(DataType.BOOL, "keepDims") { description = "If true: keep the dimensions that are reduced on (as length 1). False: remove the reduction dimensions"; defaultValue = false }
|
||||
val dims = Arg(DataType.LONG, "dimensions"){ count = AtLeast(1); isVargarg = true; description = "Dimensions to reduce over. If dimensions are not specified, full array reduction is performed" }
|
||||
Output(DataType.NUMERIC, "output"){ description = "Reduced array of rank (input rank - num dimensions)" }
|
||||
|
||||
Signature(input, dims)
|
||||
AllParamSignature(withOutput = false)
|
||||
}
|
||||
|
||||
val indexAccumVariable = Mixin("indexAccumVariable"){
|
||||
legacy = true
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.indexaccum"
|
||||
val input = Input(DataType.NUMERIC, "in") { description = "Input variable" }
|
||||
val keepDims = Arg(DataType.BOOL, "keepDims") { description = "If true: keep the dimensions that are reduced on (as length 1). False: remove the reduction dimensions"; defaultValue = false }
|
||||
val dims = Input(DataType.NUMERIC, "dimensions"){ description = "Dimensions to reduce over. If dimensions are not specified, full array reduction is performed" }
|
||||
Output(DataType.NUMERIC, "output"){ description = "Reduced array of rank (input rank - num dimensions)" }
|
||||
|
||||
Signature(input, dims)
|
||||
AllParamSignature(withOutput = false)
|
||||
}
|
||||
|
||||
|
||||
|
||||
val indexAccumCustom = Mixin("indexAccumCustom"){
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.indexaccum.custom"
|
||||
val input = Input(DataType.NUMERIC, "in") { description = "Input variable" }
|
||||
val keepDims = Arg(DataType.BOOL, "keepDims") { description = "If true: keep the dimensions that are reduced on (as length 1). False: remove the reduction dimensions"; defaultValue = false }
|
||||
val dims = Arg(DataType.LONG, "dimensions"){ count = AtLeast(1); isVargarg = true; description = "Dimensions to reduce over. If dimensions are not specified, full array reduction is performed" }
|
||||
Output(DataType.NUMERIC, "output"){ description = "Reduced array of rank (input rank - num dimensions)" }
|
||||
|
||||
Signature(input, dims)
|
||||
AllParamSignature(withOutput = false)
|
||||
}
|
||||
|
||||
val indexAccumVariableCustom = Mixin("indexAccumVariableCustom"){
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.indexaccum.custom"
|
||||
val input = Input(DataType.NUMERIC, "in") { description = "Input variable" }
|
||||
val keepDims = Arg(DataType.BOOL, "keepDims") { description = "If true: keep the dimensions that are reduced on (as length 1). False: remove the reduction dimensions"; defaultValue = false }
|
||||
val dims = Input(DataType.NUMERIC, "dimensions"){ description = "Dimensions to reduce over. If dimensions are not specified, full array reduction is performed" }
|
||||
Output(DataType.NUMERIC, "output"){ description = "Reduced array of rank (input rank - num dimensions)" }
|
||||
|
||||
Signature(input, dims)
|
||||
AllParamSignature(withOutput = false)
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.ops
|
||||
|
||||
import org.nd4j.codegen.api.DataType.INT
|
||||
import org.nd4j.codegen.api.Language
|
||||
import org.nd4j.codegen.api.doc.DocScope
|
||||
import org.nd4j.codegen.dsl.*
|
||||
|
||||
|
||||
fun Bitwise() = Namespace("Bitwise"){
|
||||
val namespaceJavaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
|
||||
Op("leftShift") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "ShiftBits"
|
||||
|
||||
Input(INT, "x") { description = "Input to be bit shifted" }
|
||||
Input(INT, "y") { description = "Amount to shift elements of x array" }
|
||||
|
||||
Output(INT, "output"){ description = "Bitwise shifted input x" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Bitwise left shift operation. Supports broadcasting.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("rightShift") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "RShiftBits"
|
||||
|
||||
Input(INT, "x") { description = "Input to be bit shifted" }
|
||||
Input(INT, "y") { description = "Amount to shift elements of x array" }
|
||||
|
||||
Output(INT, "output"){ description = "Bitwise shifted input x" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Bitwise right shift operation. Supports broadcasting.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("leftShiftCyclic") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "CyclicShiftBits"
|
||||
|
||||
Input(INT, "x") { description = "Input to be bit shifted" }
|
||||
Input(INT, "y") { description = "Amount to shift elements of x array" }
|
||||
|
||||
Output(INT, "output"){ description = "Bitwise cyclic shifted input x" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Bitwise left cyclical shift operation. Supports broadcasting.
|
||||
Unlike #leftShift(%INPUT_TYPE%, %INPUT_TYPE%) the bits will "wrap around":
|
||||
{@code leftShiftCyclic(01110000, 2) -> 11000001}
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("rightShiftCyclic") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "CyclicRShiftBits"
|
||||
|
||||
Input(INT, "x") { description = "Input to be bit shifted" }
|
||||
Input(INT, "y") { description = "Amount to shift elements of x array" }
|
||||
|
||||
Output(INT, "output"){ description = "Bitwise cyclic shifted input x" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Bitwise right cyclical shift operation. Supports broadcasting.
|
||||
Unlike rightShift(%INPUT_TYPE%, %INPUT_TYPE%) the bits will "wrap around":
|
||||
{@code rightShiftCyclic(00001110, 2) -> 10000011}
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("bitsHammingDistance") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "BitsHammingDistance"
|
||||
|
||||
val x = Input(INT, "x") { description = "First input array." }
|
||||
val y = Input(INT, "y") { description = "Second input array." }
|
||||
Constraint("Must be same types"){ sameType(x, y) }
|
||||
|
||||
Output(INT, "output"){ description = "bitwise Hamming distance" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Bitwise Hamming distance reduction over all elements of both input arrays.<br>
|
||||
For example, if x=01100000 and y=1010000 then the bitwise Hamming distance is 2 (due to differences at positions 0 and 1)
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("and") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "BitwiseAnd"
|
||||
|
||||
val x = Input(INT, "x") { description = "First input array" }
|
||||
val y = Input(INT, "y") { description = "Second input array" }
|
||||
Constraint("Must be same types"){ sameType(x, y) }
|
||||
BackendConstraint("Must have broadcastable shapes"){ broadcastableShapes(x, y) }
|
||||
|
||||
Output(INT, "output"){ description = "Bitwise AND array" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Bitwise AND operation. Supports broadcasting.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("or") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "BitwiseOr"
|
||||
|
||||
val x = Input(INT, "x") { description = "First input array" }
|
||||
val y = Input(INT, "y") { description = "First input array" }
|
||||
Constraint("Must be same types"){ sameType(x, y) }
|
||||
BackendConstraint("Must have broadcastable shapes"){ broadcastableShapes(x, y) }
|
||||
|
||||
Output(INT, "output"){ description = "Bitwise OR array" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Bitwise OR operation. Supports broadcasting.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("xor") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "BitwiseXor"
|
||||
|
||||
val x = Input(INT, "x") { description = "First input array" }
|
||||
val y = Input(INT, "y") { description = "First input array" }
|
||||
Constraint("Must be same types"){ sameType(x, y) }
|
||||
BackendConstraint("Must have broadcastable shapes"){ broadcastableShapes(x, y) }
|
||||
|
||||
Output(INT, "output"){ description = "Bitwise XOR array" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Bitwise XOR operation (exclusive OR). Supports broadcasting.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("bitShift") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
javaOpClass = "ShiftBits"
|
||||
Input(INT, "x") { description = "Input 1" }
|
||||
Input(INT, "shift") { description = "Number of bits to shift." }
|
||||
Output(INT, "output"){ description = "SDVariable with shifted bits" }
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Shift integer bits to the left, i.e. var << 4
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("bitShiftRight") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
javaOpClass = "RShiftBits"
|
||||
Input(INT, "x") { description = "Input 1" }
|
||||
Input(INT, "shift") { description = "Number of bits to shift." }
|
||||
Output(INT, "output"){ description = "SDVariable with shifted bits" }
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Shift integer bits to the right, i.e. var >> 4
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("bitRotl") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
javaOpClass = "CyclicShiftBits"
|
||||
Input(INT, "x") { description = "Input 1" }
|
||||
Input(INT, "shift") { description = "Number of bits to shift." }
|
||||
Output(INT, "output"){ description = "SDVariable with shifted bits" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Roll integer bits to the left, i.e. var << 4 | var >> (32 - 4)
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("bitRotr") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
javaOpClass = "CyclicRShiftBits"
|
||||
Input(INT, "x") { description = "Input 1" }
|
||||
Input(INT, "shift") { description = "Number of bits to shift." }
|
||||
Output(INT, "output"){ description = "SDVariable with shifted bits" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Roll integer bits to the right, i.e. var >> 4 | var << (32 - 4)
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,586 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.ops
|
||||
|
||||
import org.nd4j.codegen.api.AtLeast
|
||||
import org.nd4j.codegen.api.Language
|
||||
import org.nd4j.codegen.api.doc.DocScope
|
||||
import org.nd4j.codegen.dsl.*
|
||||
import org.nd4j.codegen.api.DataType.*
|
||||
import org.nd4j.codegen.api.Exactly
|
||||
|
||||
fun SDCNN() = Namespace("CNN"){
|
||||
val namespaceJavaPackage = "org.nd4j.linalg.api.ops.impl.layers.convolution"
|
||||
|
||||
val dataFormat = Mixin("dataFormat"){
|
||||
Arg(ENUM, "dataFormat") { possibleValues = listOf("NCHW", "NHWC"); description = "Data format: \"NCHW\" or \"NHWC\"" }
|
||||
}
|
||||
|
||||
|
||||
val conv1DConfig = Config("Conv1DConfig"){
|
||||
Arg(LONG, "k"){ description = "Kernel"; defaultValue=-1L}
|
||||
Arg(LONG, "s"){ description = "stride"; defaultValue=1}
|
||||
Arg(LONG, "p"){ description = "padding"; defaultValue=0}
|
||||
Arg(LONG, "d"){ description = "dilation"; defaultValue=1}
|
||||
Arg(BOOL, "isSameMode"){ description = "Same mode"; defaultValue=true}
|
||||
Arg(STRING, "dataFormat"){ description = "Data format"; defaultValue="NCW"}
|
||||
javaClassOverride = "org.nd4j.linalg.api.ops.impl.layers.convolution.config.Conv1DConfig"
|
||||
}
|
||||
|
||||
val conv2DConfig = Config("Conv2DConfig"){
|
||||
Arg(LONG, "kH"){ description = "Kernel height"; defaultValue=-1L}
|
||||
Arg(LONG, "kW"){ description = "Kernel width"; defaultValue=-1L}
|
||||
Arg(LONG, "sH"){ description = "Stride along height dimension"; defaultValue=1};
|
||||
Arg(LONG, "sW"){ description = "Stride along width dimension"; defaultValue=1};
|
||||
Arg(LONG, "pH"){ description = "Padding along height dimension"; defaultValue=0};
|
||||
Arg(LONG, "pW"){ description = "Padding along width dimension"; defaultValue=0};
|
||||
Arg(LONG, "dH"){ description = "Dilation along height dimension"; defaultValue=1};
|
||||
Arg(LONG, "dW"){ description = "Dilation along width dimension"; defaultValue=1};
|
||||
Arg(BOOL, "isSameMode"){ description = "Same mode"; defaultValue=true}
|
||||
Arg(STRING, "dataFormat"){ description = "Data format"; defaultValue="NCHW"}
|
||||
javaClassOverride = "org.nd4j.linalg.api.ops.impl.layers.convolution.config.Conv2DConfig"
|
||||
}
|
||||
|
||||
val conv3DConfig = Config("Conv3DConfig"){
|
||||
Arg(LONG, "kD"){ description = "Kernel depth"; defaultValue=-1}
|
||||
Arg(LONG, "kW"){ description = "Kernel width"; defaultValue=-1}
|
||||
Arg(LONG, "kH"){ description = "Kernel height"; defaultValue=-1};
|
||||
Arg(LONG, "sD"){ description = "Stride depth"; defaultValue=1};
|
||||
Arg(LONG, "sW"){ description = "Stride width"; defaultValue=1};
|
||||
Arg(LONG, "sH"){ description = "Stride height"; defaultValue=1};
|
||||
Arg(LONG, "pD"){ description = "Padding depth"; defaultValue=0};
|
||||
Arg(LONG, "pW"){ description = "Padding width"; defaultValue=0};
|
||||
Arg(LONG, "pH"){ description = "Padding height"; defaultValue=0};
|
||||
Arg(LONG, "dD"){ description = "Dilation depth"; defaultValue=1};
|
||||
Arg(LONG, "dW"){ description = "Dilation width"; defaultValue=1};
|
||||
Arg(LONG, "dH"){ description = "Dilation height"; defaultValue=1};
|
||||
Arg(BOOL, "biasUsed"){ description = "biasUsed"; defaultValue=false}
|
||||
Arg(BOOL, "isSameMode"){ description = "Same mode"; defaultValue=true}
|
||||
Arg(STRING, "dataFormat"){ description = "Data format"; defaultValue="NDHWC"}
|
||||
javaClassOverride = "org.nd4j.linalg.api.ops.impl.layers.convolution.config.Conv3DConfig"
|
||||
}
|
||||
|
||||
|
||||
val deconv2DConfig = Config("DeConv2DConfig"){
|
||||
Arg(LONG, "kH"){ description = "Kernel height"; defaultValue=-1L}
|
||||
Arg(LONG, "kW"){ description = "Kernel width"; defaultValue=-1L}
|
||||
Arg(LONG, "sH"){ description = "Stride along height dimension"; defaultValue=1L};
|
||||
Arg(LONG, "sW"){ description = "Stride along width dimension"; defaultValue=1L};
|
||||
Arg(LONG, "pH"){ description = "Padding along height dimension"; defaultValue=0};
|
||||
Arg(LONG, "pW"){ description = "Padding along width dimension"; defaultValue=0};
|
||||
Arg(LONG, "dH"){ description = "Dilation along height dimension"; defaultValue=1L};
|
||||
Arg(LONG, "dW"){ description = "Dilation along width dimension"; defaultValue=1L};
|
||||
Arg(BOOL, "isSameMode"){ description = "Same mode"; defaultValue=false}
|
||||
Arg(STRING, "dataFormat"){ description = "Data format"; defaultValue="NCHW"}
|
||||
javaClassOverride = "org.nd4j.linalg.api.ops.impl.layers.convolution.config.DeConv2DConfig"
|
||||
}
|
||||
|
||||
|
||||
val deconv3DConfig = Config("DeConv3DConfig"){
|
||||
Arg(LONG, "kD"){ description = "Kernel depth"; defaultValue=-1L}
|
||||
Arg(LONG, "kW"){ description = "Kernel width"; defaultValue=-1L}
|
||||
Arg(LONG, "kH"){ description = "Kernel height"; defaultValue=-1L};
|
||||
Arg(LONG, "sD"){ description = "Stride depth"; defaultValue=1L};
|
||||
Arg(LONG, "sW"){ description = "Stride width"; defaultValue=1L};
|
||||
Arg(LONG, "sH"){ description = "Stride height"; defaultValue=1L};
|
||||
Arg(LONG, "pD"){ description = "Padding depth"; defaultValue=0};
|
||||
Arg(LONG, "pW"){ description = "Padding width"; defaultValue=0};
|
||||
Arg(LONG, "pH"){ description = "Padding height"; defaultValue=0};
|
||||
Arg(LONG, "dD"){ description = "Dilation depth"; defaultValue=1L};
|
||||
Arg(LONG, "dW"){ description = "Dilation width"; defaultValue=1L};
|
||||
Arg(LONG, "dH"){ description = "Dilation height"; defaultValue=1L};
|
||||
Arg(BOOL, "isSameMode"){ description = "Same mode"; defaultValue=false}
|
||||
Arg(STRING, "dataFormat"){ description = "Data format"; defaultValue="NCDHW"}
|
||||
javaClassOverride = "org.nd4j.linalg.api.ops.impl.layers.convolution.config.DeConv3DConfig"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
val pooling2DConfig = Config("Pooling2DConfig"){
|
||||
Arg(LONG, "kH"){ description = "Kernel height"; defaultValue=-1}
|
||||
Arg(LONG, "kW"){ description = "Kernel width"; defaultValue=-1}
|
||||
Arg(LONG, "sH"){ description = "Stride along height dimension"; defaultValue=1};
|
||||
Arg(LONG, "sW"){ description = "Stride along width dimension"; defaultValue=1};
|
||||
Arg(LONG, "pH"){ description = "Padding along height dimension"; defaultValue=0};
|
||||
Arg(LONG, "pW"){ description = "Padding along width dimension"; defaultValue=0};
|
||||
Arg(LONG, "dH"){ description = "Dilation along height dimension"; defaultValue=1};
|
||||
Arg(LONG, "dW"){ description = "Dilation along width dimension"; defaultValue=1};
|
||||
Arg(BOOL, "isSameMode"){ description = "Same mode"; defaultValue=true}
|
||||
Arg(STRING, "dataFormat"){ description = "Data format"; defaultValue="nchw"}
|
||||
javaClassOverride = "org.nd4j.linalg.api.ops.impl.layers.convolution.config.Pooling2DConfig"
|
||||
}
|
||||
|
||||
val pooling3DConfig = Config("Pooling3DConfig"){
|
||||
Arg(LONG, "kD"){ description = "Kernel depth"; defaultValue=-1}
|
||||
Arg(LONG, "kW"){ description = "Kernel width"; defaultValue=-1}
|
||||
Arg(LONG, "kH"){ description = "Kernel height"; defaultValue=-1};
|
||||
Arg(LONG, "sD"){ description = "Stride depth"; defaultValue=1};
|
||||
Arg(LONG, "sW"){ description = "Stride width"; defaultValue=1};
|
||||
Arg(LONG, "sH"){ description = "Stride height"; defaultValue=1};
|
||||
Arg(LONG, "pD"){ description = "Padding depth"; defaultValue=0};
|
||||
Arg(LONG, "pW"){ description = "Padding width"; defaultValue=0};
|
||||
Arg(LONG, "pH"){ description = "Padding height"; defaultValue=0};
|
||||
Arg(LONG, "dD"){ description = "Dilation depth"; defaultValue=1};
|
||||
Arg(LONG, "dW"){ description = "Dilation width"; defaultValue=1};
|
||||
Arg(LONG, "dH"){ description = "Dilation height"; defaultValue=1};
|
||||
Arg(BOOL, "isSameMode"){ description = "Same mode"; defaultValue=true}
|
||||
Arg(STRING, "dataFormat"){ description = "Data format"; defaultValue="NCDHW"}
|
||||
javaClassOverride = "org.nd4j.linalg.api.ops.impl.layers.convolution.config.Pooling3DConfig"
|
||||
}
|
||||
|
||||
|
||||
val LocalResponseNormalizationConfig = Config("LocalResponseNormalizationConfig"){
|
||||
Arg(NUMERIC, "alpha"){ description = "alpha"; defaultValue=1}
|
||||
Arg(NUMERIC, "beta"){ description = "beta"; defaultValue=0.5}
|
||||
Arg(NUMERIC, "bias"){ description = "bias"; defaultValue=1}
|
||||
Arg(INT, "depth"){ description = "depth"; defaultValue=5}
|
||||
javaClassOverride = "org.nd4j.linalg.api.ops.impl.layers.convolution.config.LocalResponseNormalizationConfig"
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Op("avgPooling2d") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "AvgPooling2D"
|
||||
Input(NUMERIC, "input") { description = "the input to average pooling 2d operation - 4d CNN (image) activations in NCHW format (shape [minibatch, channels, height, width]) or NHWC format (shape [minibatch, height, width, channels])" }
|
||||
useConfig(pooling2DConfig)
|
||||
|
||||
Output(NUMERIC, "output"){ description = "Result after applying average pooling on the input" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
2D Convolution layer operation - average pooling 2d
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("avgPooling3d") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "AvgPooling3D"
|
||||
Input(NUMERIC, "input") {description = "the input to average pooling 3d operation - 5d activations in NCDHW format (shape [minibatch, channels, depth, height, width]) or NDHWC format (shape [minibatch, depth, height, width, channels])" }
|
||||
useConfig(pooling3DConfig)
|
||||
|
||||
Output(NUMERIC, "output"){ description = "after applying average pooling on the input" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
3D convolution layer operation - average pooling 3d
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("batchToSpace") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
javaOpClass = "BatchToSpace"
|
||||
Input(NUMERIC, "x") { description = "Input variable. 4d input" }
|
||||
Arg(INT, "blocks") { count=Exactly(2); description = "Block size, in the height/width dimension" }
|
||||
Arg(INT, "croppingTop") { count=Exactly(2)}
|
||||
Arg(INT, "croppingBottom") { count=Exactly(2)}
|
||||
Output(NUMERIC, "output"){ description = "Output variable" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Convolution 2d layer batch to space operation on 4d input.
|
||||
Reduces input batch dimension by rearranging data into a larger spatial dimensions
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("col2Im") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "Col2Im"
|
||||
|
||||
Input(NUMERIC, "in") { description = "Input - rank 6 input with shape [minibatch, inputChannels, kernelHeight, kernelWidth, outputHeight, outputWidth]" }
|
||||
useConfig(conv2DConfig)
|
||||
|
||||
Output(NUMERIC, "output"){ description = "Col2Im output variable" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
col2im operation for use in 2D convolution operations. Outputs a 4d array with shape
|
||||
[minibatch, inputChannels, height, width]
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Op("conv1d") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "Conv1D"
|
||||
Input(NUMERIC, "input") { description = "the inputs to conv1d" }
|
||||
Input(NUMERIC, "weights") { description = "weights for conv1d op - rank 3 array with shape [kernelSize, inputChannels, outputChannels]" }
|
||||
Input(NUMERIC, "bias") { description = "bias for conv1d op - rank 1 array with shape [outputChannels]. May be null."; defaultValue=null }
|
||||
useConfig(conv1DConfig)
|
||||
|
||||
Output(NUMERIC, "output"){ description = "result of conv1d op" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Conv1d operation.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Op("conv2d") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "Conv2D"
|
||||
Input(NUMERIC, "layerInput") { description = "the input to max pooling 2d operation - 4d CNN (image) activations in NCHW format" }
|
||||
Input(NUMERIC, "weights") { description = "Weights for the convolution operation. 4 dimensions with format [kernelHeight, kernelWidth, inputChannels, outputChannels]" }
|
||||
Input(NUMERIC, "bias") { description = "Optional 1D bias array with shape [outputChannels]. May be null."; defaultValue=null }
|
||||
useConfig(conv2DConfig)
|
||||
|
||||
Output(NUMERIC, "output"){ description = "result of conv2d op" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
2D Convolution operation with optional bias
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Op("conv3d") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "Conv3D"
|
||||
Input(NUMERIC, "input") { description = "the input to average pooling 3d operation - 5d activations in NCDHW format (shape [minibatch, channels, depth, height, width]) or NDHWC format (shape [minibatch, depth, height, width, channels])" }
|
||||
Input(NUMERIC, "weights") { description = " Weights for conv3d. Rank 5 with shape [kernelDepth, kernelHeight, kernelWidth, inputChannels, outputChannels]." }
|
||||
Input(NUMERIC, "bias") { description = " Optional 1D bias array with shape [outputChannels]. May be null."; defaultValue=null }
|
||||
useConfig(conv3DConfig)
|
||||
|
||||
Output(NUMERIC, "output"){ description = "Conv3d output variable" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Convolution 3D operation with optional bias
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Op("deconv2d") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "DeConv2D"
|
||||
Input(NUMERIC, "layerInput") { description = "the input to deconvolution 2d operation - 4d CNN (image) activations in NCHW format (shape [minibatch, channels, height, width]) or NHWC format (shape [minibatch, height, width, channels])" }
|
||||
Input(NUMERIC, "weights") { description = "Weights for the 2d deconvolution operation. 4 dimensions with format [inputChannels, outputChannels, kernelHeight, kernelWidth]" }
|
||||
Input(NUMERIC, "bias") { description = "Optional 1D bias array with shape [outputChannels]. May be null."; defaultValue=null }
|
||||
useConfig(deconv2DConfig)
|
||||
Output(NUMERIC, "output"){ description = "result of deconv2d op" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
2D deconvolution operation with optional bias
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Op("deconv3d") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "DeConv3D"
|
||||
Input(NUMERIC, "input") { description = "Input array - shape [bS, iD, iH, iW, iC] (NDHWC) or [bS, iC, iD, iH, iW] (NCDHW)" }
|
||||
Input(NUMERIC, "weights") { description = "Weights array - shape [kD, kH, kW, oC, iC]" }
|
||||
Input(NUMERIC, "bias") { description = "Bias array - optional, may be null. If non-null, must have shape [outputChannels]"; defaultValue=null }
|
||||
useConfig(deconv3DConfig)
|
||||
|
||||
Output(NUMERIC, "output"){ description = "result of 3D CNN deconvolution operation" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
3D CNN deconvolution operation with or without optional bias
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("depthToSpace") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "DepthToSpace"
|
||||
Input(NUMERIC, "x") { description = "the input to depth to space pooling 2d operation - 4d activations in NCHW format (shape [minibatch, channels, height, width]) or NHWC format (shape [minibatch, height, width, channels])" }
|
||||
Arg(INT, "blockSize") { description = "Block size, in the height/width dimension" }
|
||||
useMixin(dataFormat)
|
||||
Output(NUMERIC, "output"){ description = "Output variable" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Convolution 2d layer batch to space operation on 4d input.<br>
|
||||
Reduces input channels dimension by rearranging data into a larger spatial dimensions<br>
|
||||
Example: if input has shape [mb, 8, 2, 2] and block size is 2, then output size is [mb, 8/(2*2), 2*2, 2*2]
|
||||
= [mb, 2, 4, 4]
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Op("depthWiseConv2d") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "DepthwiseConv2D"
|
||||
Input(NUMERIC, "layerInput") { description = "the input to max pooling 2d operation - 4d CNN (image) activations in NCHW format" }
|
||||
Input(NUMERIC, "depthWeights") { description = "Depth-wise conv2d weights. 4 dimensions with format [kernelHeight, kernelWidth, inputChannels, depthMultiplier]" }
|
||||
Input(NUMERIC, "bias") { description = "Optional 1D bias array with shape [outputChannels]. May be null."; defaultValue=null }
|
||||
useConfig(conv2DConfig)
|
||||
|
||||
Output(NUMERIC, "output"){ description = "result of depthwise conv2d op" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Depth-wise 2D convolution operation with optional bias
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Op("dilation2D") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
javaOpClass = "Dilation2D"
|
||||
Input(NUMERIC, "df") { description = "" }
|
||||
Input(NUMERIC, "weights") { description = "df" }
|
||||
Arg(INT, "strides") { count = Exactly(2); description = "weights" }
|
||||
Arg(INT, "rates") {count = Exactly(2); description = "strides" }
|
||||
Arg(BOOL, "isSameMode") { description = "isSameMode" }
|
||||
|
||||
Output(NUMERIC, "output"){ description = "Computed the grayscale dilation of 4-D input and 3-D filters tensors." }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
TODO doc string
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("extractImagePatches") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.image"
|
||||
javaOpClass = "ExtractImagePatches"
|
||||
Input(NUMERIC, "input") { description = "Input array. Must be rank 4, with shape [minibatch, height, width, channels]" }
|
||||
Arg(INT, "kH") { description = "Kernel height" }
|
||||
Arg(INT, "kW") { description = "Kernel width" }
|
||||
Arg(INT, "sH") { description = "Stride height" }
|
||||
Arg(INT, "sW") { description = "Stride width" }
|
||||
Arg(INT, "rH") { description = "Rate height" }
|
||||
Arg(INT, "rW") { description = "Rate width" }
|
||||
Arg(BOOL, "sameMode") { description = "If true: use same mode padding. If false" }
|
||||
|
||||
Output(NUMERIC, "output"){ description = "The result is a 4D tensor which is indexed by batch, row, and column." }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Extract image patches
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("im2Col") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "Im2col"
|
||||
Input(NUMERIC, "in") { description = "Input - rank 4 input with shape [minibatch, inputChannels, height, width]" }
|
||||
useConfig(conv2DConfig)
|
||||
|
||||
Output(NUMERIC, "output"){ description = "Im2Col output variable" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
im2col operation for use in 2D convolution operations. Outputs a 6d array with shape
|
||||
[minibatch, inputChannels, kernelHeight, kernelWidth, outputHeight, outputWidth]
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("localResponseNormalization") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "LocalResponseNormalization"
|
||||
Input(NUMERIC, "input") { description = "the inputs to lrn" }
|
||||
useConfig(LocalResponseNormalizationConfig)
|
||||
|
||||
Output(NUMERIC, "output"){ description = "Result after Local Response Normalization"}
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
2D convolution layer operation - local response normalization
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("maxPooling2d") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "MaxPooling2D"
|
||||
Input(NUMERIC, "input") { description = "the input to max pooling 2d operation - 4d CNN (image) activations in NCHW format (shape [minibatch, channels, height, width]) or NHWC format (shape [minibatch, height, width, channels])" }
|
||||
useConfig(pooling2DConfig)
|
||||
|
||||
Output(NUMERIC, "output"){ description = "Result after applying max pooling on the input" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
2D Convolution layer operation - max pooling 2d
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("maxPoolWithArgmax") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "MaxPoolWithArgmax"
|
||||
Input(NUMERIC, "input") { description = "the input to max pooling 2d operation - 4d CNN (image) activations in NCHW format (shape [minibatch, channels, height, width]) or NHWC format (shape [minibatch, height, width, channels])" }
|
||||
useConfig(pooling2DConfig)
|
||||
|
||||
Output(NUMERIC, "output"){ description = "Result after applying max pooling on the input" }
|
||||
Output(NUMERIC, "indexes"){ description = "Argmax array" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
2D Convolution layer operation - Max pooling on the input and outputs both max values and indices
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("maxPooling3d") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "MaxPooling3D"
|
||||
Input(NUMERIC, "input") { description = "the input to average pooling 3d operation - 5d activations in NCDHW format (shape [minibatch, channels, depth, height, width]) or NDHWC format (shape [minibatch, depth, height, width, channels])" }
|
||||
useConfig(pooling3DConfig)
|
||||
|
||||
Output(NUMERIC, "output"){ description = "Result after applying max pooling on the input" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
3D convolution layer operation - max pooling 3d operation.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Op("separableConv2d") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.layers.convolution"
|
||||
javaOpClass = "SConv2D"
|
||||
Input(NUMERIC, "layerInput") { description = "the input to max pooling 2d operation - 4d CNN (image) activations in NCHW format (shape [minibatch, channels, height, width]) or NHWC format (shape [minibatch, height, width, channels])" }
|
||||
Input(NUMERIC, "depthWeights") { description = "Separable conv2d depth weights. 4 dimensions with format [kernelHeight, kernelWidth, inputChannels, depthMultiplier]" }
|
||||
Input(NUMERIC, "pointWeights") { description = "Point weights, rank 4 with format [1, 1, inputChannels*depthMultiplier, outputChannels]. May be null" }
|
||||
Input(NUMERIC, "bias") { description = "Optional bias, rank 1 with shape [outputChannels]. May be null."; defaultValue=null}
|
||||
useConfig(conv2DConfig)
|
||||
|
||||
Output(NUMERIC, "output"){ description = "result of separable convolution 2d operation" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Separable 2D convolution operation with optional bias
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Op("spaceToBatch") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
javaOpClass = "SpaceToBatch"
|
||||
Input(NUMERIC, "x") { description = "Input variable. 4d input" }
|
||||
Arg(INT, "blocks") { count = Exactly(2); description = "Block size, in the height/width dimension" }
|
||||
Arg(INT, "paddingTop") {count = Exactly(2); description = "Optional 2d int[] array for padding the result: values [[pad top, pad bottom], [pad left, pad right]]" }
|
||||
Arg(INT, "paddingBottom") {count = Exactly(2); description = "Optional 2d int[] array for padding the result: values [[pad top, pad bottom], [pad left, pad right]]" }
|
||||
|
||||
Output(NUMERIC, "output"){ description = "Output variable" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Convolution 2d layer space to batch operation on 4d input.
|
||||
Increases input batch dimension by rearranging data from spatial dimensions into batch dimension
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("spaceToDepth") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
Input(NUMERIC, "x") { description = "the input to depth to space pooling 2d operation - 4d activations in NCHW format (shape [minibatch, channels, height, width]) or NHWC format (shape [minibatch, height, width, channels])" }
|
||||
Arg(INT, "blockSize") { description = " Block size, in the height/width dimension" }
|
||||
useMixin(dataFormat)
|
||||
Output(NUMERIC, "output"){ description = "Output variable" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Convolution 2d layer space to depth operation on 4d input.<br>
|
||||
Increases input channels (reduced spatial dimensions) by rearranging data into a larger channels dimension<br>
|
||||
Example: if input has shape [mb, 2, 4, 4] and block size is 2, then output size is [mb, 8/(2*2), 2*2, 2*2]
|
||||
= [mb, 2, 4, 4]
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("upsampling2d") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
Input(NUMERIC, "input") { description = "Input in NCHW format" }
|
||||
Arg(INT, "scale") { description = "The scale for both height and width dimensions." }
|
||||
|
||||
Output(NUMERIC, "output"){ description = "Upsampled input"}
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Upsampling layer for 2D inputs.
|
||||
scale is used for both height and width dimensions.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("upsampling2d") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
Input(NUMERIC, "input") { description = "Input in NCHW format" }
|
||||
Arg(INT, "scaleH") { description = "Scale to upsample in height dimension" }
|
||||
Arg(INT, "scaleW") { description = "Scale to upsample in width dimension" }
|
||||
Arg(BOOL ,"nchw") { description = "If true: input is in NCHW (minibatch, channels, height, width) format. False: NHWC format" }
|
||||
|
||||
|
||||
Output(NUMERIC, "output"){ description = "Upsampled input" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
2D Convolution layer operation - Upsampling 2d
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("upsampling3d") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "Upsampling3d"
|
||||
Input(NUMERIC, "input") { description = "Input in NCHW format" }
|
||||
Arg(BOOL ,"ncdhw") { description = "If true: input is in NCDHW (minibatch, channels, depth, height, width) format. False: NDHWC format" }
|
||||
Arg(INT, "scaleD") { description = "Scale to upsample in depth dimension" }
|
||||
Arg(INT, "scaleH") { description = "Scale to upsample in height dimension" }
|
||||
Arg(INT, "scaleW") { description = "Scale to upsample in width dimension" }
|
||||
|
||||
|
||||
Output(NUMERIC, "output"){ description = "Upsampled input" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
3D Convolution layer operation - Upsampling 3d
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.ops
|
||||
|
||||
import org.nd4j.codegen.api.AtLeast
|
||||
import org.nd4j.codegen.api.Language
|
||||
import org.nd4j.codegen.api.doc.DocScope
|
||||
import org.nd4j.codegen.dsl.*
|
||||
import org.nd4j.codegen.api.DataType.*
|
||||
import org.nd4j.codegen.api.Exactly
|
||||
|
||||
|
||||
fun SDImage() = Namespace("Image"){
|
||||
val namespaceJavaPackage = "org.nd4j.linalg.api.ops.custom"
|
||||
Op("CropAndResize") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.image"
|
||||
javaOpClass = "CropAndResize"
|
||||
Input(NUMERIC, "image") { description = "Input image, with shape [batch, height, width, channels]" }
|
||||
Input(NUMERIC, "cropBoxes") { description = "Float32 crop, shape [numBoxes, 4] with values in range 0 to 1" }
|
||||
Input(NUMERIC, "boxIndices") { description = "Indices: which image (index to dimension 0) the cropBoxes belong to. Rank 1, shape [numBoxes]" }
|
||||
Input(INT, "cropOutSize") { description = "Output size for the images - int32, rank 1 with values [outHeight, outWidth]" }
|
||||
Arg(NUMERIC, "extrapolationValue") { description = "Used for extrapolation, when applicable. 0.0 should be used for the default"; defaultValue=0.0 }
|
||||
|
||||
Output(NUMERIC, "output"){ description = "Cropped and resized images" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Given an input image and some crop boxes, extract out the image subsets and resize them to the specified size.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("extractImagePatches") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.image"
|
||||
javaOpClass = "ExtractImagePatches"
|
||||
Input(NUMERIC, "image") { description = "Input image to extract image patches from - shape [batch, height, width, channels]" }
|
||||
Arg(INT, "kSizes") { count = Exactly(2); description = "Kernel size - size of the image patches, [height, width]" }
|
||||
Arg(INT, "strides") { count = Exactly(2);description = "Stride in the input dimension for extracting image patches, [stride_height, stride_width]" }
|
||||
Arg(INT, "rates") { count = AtLeast(0); description = "Usually [1,1]. Equivalent to dilation rate in dilated convolutions - how far apart the output pixels\n" +
|
||||
" in the patches should be, in the input. A dilation of [a,b] means every {@code a}th pixel is taken\n" +
|
||||
" along the height/rows dimension, and every {@code b}th pixel is take along the width/columns dimension" }
|
||||
Arg(BOOL, "sameMode") { description = "Padding algorithm. If true: use Same padding" }
|
||||
|
||||
Output(NUMERIC, "output"){ description = "The extracted image patches" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Given an input image, extract out image patches (of size kSizes - h x w) and place them in the depth dimension.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("nonMaxSuppression") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.image"
|
||||
javaOpClass = "NonMaxSuppression"
|
||||
Input(NUMERIC, "boxes") { description = "Might be null. Name for the output variable" }
|
||||
Input(NUMERIC, "scores") { description = "vector of shape [num_boxes]" }
|
||||
Arg(INT, "maxOutSize") { description = "scalar representing the maximum number of boxes to be selected" }
|
||||
Arg(NUMERIC, "iouThreshold") { description = "threshold for deciding whether boxes overlap too much with respect to IOU" }
|
||||
Arg(NUMERIC, "scoreThreshold") { description = "threshold for deciding when to remove boxes based on score" }
|
||||
|
||||
Output(NUMERIC, "output"){ description = "vectort of shape [M] representing the selected indices from the boxes tensor, where M <= max_output_size" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Greedily selects a subset of bounding boxes in descending order of score
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("adjustContrast") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "AdjustContrast"
|
||||
Input(NUMERIC, "in") { description = "images to adjust. 3D shape or higher" }
|
||||
Arg(FLOATING_POINT, "factor") { description = "multiplier for adjusting contrast" }
|
||||
|
||||
Output(NUMERIC, "output"){ description = "Contrast-adjusted image" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Adjusts contrast of RGB or grayscale images.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("adjustSaturation") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "AdjustSaturation"
|
||||
Input(NUMERIC, "in") { description = "RGB image as 3D array" }
|
||||
Arg(FLOATING_POINT, "factor") { description = "factor for saturation" }
|
||||
|
||||
Output(NUMERIC, "output"){ description = "adjusted image" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Adjust saturation of RGB images
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("adjustHue") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "AdjustHue"
|
||||
Input(NUMERIC, "in") { description = "image as 3D array" }
|
||||
Arg(NUMERIC, "delta") { description = "value to add to hue channel" }
|
||||
|
||||
Output(NUMERIC, "output"){ description = "adjusted image" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Adjust hue of RGB image
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Op("pad") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms"
|
||||
javaOpClass = "Pad"
|
||||
Input(NUMERIC, "input") { description = "input array" }
|
||||
Input(NUMERIC, "padding") { description = "padding input" }
|
||||
Arg(ENUM,"Mode") {possibleValues = listOf("CONSTANT", "REFLECT", "SYMMETRIC"); description = "padding mode: CONSTANT, REFLECT, SYMMETRIC"}
|
||||
Arg(NUMERIC, "padValue") { description = "The value to pad with" }
|
||||
|
||||
Output(NUMERIC, "output"){ description = "the padded array" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Pads an image according to the given padding type
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Op("randomCrop") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "RandomCrop"
|
||||
Input(NUMERIC, "input") { description = "input array" }
|
||||
Input(INT, "shape") { description = "shape for crop" }
|
||||
|
||||
Output(NUMERIC, "output"){ description = "cropped array" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Randomly crops image
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("rgbToHsv") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "RgbToHsv"
|
||||
Input(NUMERIC, "input") { description = "3D image" }
|
||||
|
||||
Output(NUMERIC, "output"){ description = "3D image" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Converting array from HSV to RGB format
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("hsvToRgb") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "HsvToRgb"
|
||||
Input(NUMERIC, "input") { description = "3D image" }
|
||||
|
||||
Output(NUMERIC, "output"){ description = "3D image" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Converting image from HSV to RGB format
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("rgbToYiq") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "RgbToYiq"
|
||||
Input(NUMERIC, "input") { description = "3D image" }
|
||||
|
||||
Output(NUMERIC, "output"){ description = "3D image" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Converting array from RGB to YIQ format
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("yiqToRgb") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "YiqToRgb"
|
||||
Input(NUMERIC, "input") { description = "3D image" }
|
||||
|
||||
Output(NUMERIC, "output"){ description = "3D image" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Converting image from YIQ to RGB format
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("rgbToYuv") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "RgbToYuv"
|
||||
|
||||
Input(NUMERIC, "input") { description = "3D image" }
|
||||
|
||||
Output(NUMERIC, "output"){ description = "3D image" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Converting array from RGB to YUV format
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("yuvToRgb") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "YuvToRgb"
|
||||
|
||||
Input(NUMERIC, "input") { description = "3D image" }
|
||||
|
||||
Output(NUMERIC, "output"){ description = "3D image" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Converting image from YUV to RGB format
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("imageResize") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.image"
|
||||
javaOpClass = "ImageResize"
|
||||
|
||||
Input(NUMERIC, "input") { description = "4D image [NHWC]" }
|
||||
Input(INT, "size") { description = "new height and width" }
|
||||
Arg(BOOL, "preserveAspectRatio") { description = "Whether to preserve the aspect ratio." +
|
||||
" If this is set, then images will be resized to a size that fits in size while preserving the aspect ratio" +
|
||||
" of the original image. Scales up the image if size is bigger than the current size of the image. Defaults to False."; defaultValue=false; }
|
||||
Arg(BOOL, "antialias") { description = "Whether to use an anti-aliasing filter when downsampling an image"; defaultValue=false; }
|
||||
Arg(ENUM, "ImageResizeMethod") { possibleValues = listOf( "ResizeBilinear", "ResizeBicubic", "ResizeNearest", "ResizeGaussian",
|
||||
"ResizeLanczos5", "ResizeMitchellcubic", "ResizeArea"); description = "ResizeBilinear: Bilinear interpolation. If 'antialias' is true, becomes a hat/tent filter function with radius 1 when downsampling.\n" +
|
||||
"ResizeLanczos5: Lanczos kernel with radius 5. Very-high-quality filter but may have stronger ringing.\n" +
|
||||
"ResizeBicubic: Cubic interpolant of Keys. Equivalent to Catmull-Rom kernel. Reasonably good quality and faster than Lanczos3Kernel, particularly when upsampling.\n" +
|
||||
"ResizeGaussian: Gaussian kernel with radius 3, sigma = 1.5 / 3.0.\n" +
|
||||
"ResizeNearest: Nearest neighbor interpolation. 'antialias' has no effect when used with nearest neighbor interpolation.\n" +
|
||||
"ResizeArea: Anti-aliased resampling with area interpolation. 'antialias' has no effect when used with area interpolation; it always anti-aliases.\n" +
|
||||
"ResizeMitchellcubic: Mitchell-Netravali Cubic non-interpolating filter. For synthetic images (especially those lacking proper prefiltering), less ringing than Keys cubic kernel but less sharp." }
|
||||
|
||||
Output(NUMERIC, "output"){ description = "Output image" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Resize images to size using the specified method.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("resizeBiLinear") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.image"
|
||||
javaOpClass = "ResizeBilinear"
|
||||
Input(NUMERIC,"input") { description = "4D image"}
|
||||
Arg(INT ,"height") { description = "target height for resizing to "}
|
||||
Arg(INT ,"width") { description = "target width for resizing to"}
|
||||
Arg(BOOL ,"alignCorners") { description = "whether to align corners during resizing. Images are aligned to preserve corners."}
|
||||
Arg(BOOL,"halfPixelCenters") { description = "When resizing, assumes pixels are centered at 0.5."}
|
||||
Output(NUMERIC, "output"){ description = "Output image" }
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Resize images to size using the specified method.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("resizeBiCubic") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.image"
|
||||
javaOpClass = "ResizeBicubic"
|
||||
Input(NUMERIC,"input") { description = "4D image"}
|
||||
Input(INT ,"size") { description = "the target size to resize to "}
|
||||
Arg(BOOL ,"alignCorners") { description = "whether to align corners during resizing. Images are aligned to preserve corners."}
|
||||
Arg(BOOL,"alignPixelCenters") { description = "When resizing, assumes pixels are centered at 0.5."}
|
||||
Output(NUMERIC, "output"){ description = "Output image" }
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Resize images to size using the specified method.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.ops
|
||||
|
||||
import org.nd4j.codegen.api.DataType
|
||||
import org.nd4j.codegen.api.DataType.*
|
||||
import org.nd4j.codegen.api.Language
|
||||
import org.nd4j.codegen.api.doc.DocScope
|
||||
import org.nd4j.codegen.dsl.*
|
||||
import org.nd4j.codegen.api.Range
|
||||
|
||||
|
||||
fun Linalg() = Namespace("Linalg") {
|
||||
//val namespaceJavaPackage = "org.nd4j.linalg"
|
||||
|
||||
Op("Cholesky") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms"
|
||||
javaOpClass = "Cholesky"
|
||||
Input(DataType.NUMERIC, "input") { description = "Input tensor with inner-most 2 dimensions forming square matrices" }
|
||||
Output(DataType.NUMERIC, "output"){ description = "Transformed tensor" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Computes the Cholesky decomposition of one or more square matrices.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("Lstsq") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.custom"
|
||||
javaOpClass = "Lstsq"
|
||||
|
||||
Input(DataType.NUMERIC, "matrix") {description = "input tensor"}
|
||||
Input(DataType.NUMERIC, "rhs") {description = "input tensor"}
|
||||
Arg(DataType.FLOATING_POINT, "l2_reguralizer") {description = "regularizer"}
|
||||
Arg(DataType.BOOL, "fast") {description = "fast mode, defaults to True"; defaultValue = true}
|
||||
Output(DataType.FLOATING_POINT, "output"){ description = "Transformed tensor" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Solver for linear squares problems.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("Solve") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.custom"
|
||||
javaOpClass = "LinearSolve"
|
||||
|
||||
Input(DataType.NUMERIC, "matrix") {description = "input tensor"}
|
||||
Input(DataType.NUMERIC, "rhs") {description = "input tensor"}
|
||||
Arg(DataType.BOOL, "adjoint") {description = "adjoint mode, defaults to False"; defaultValue = false}
|
||||
Output(FLOATING_POINT, "output"){ description = "Output tensor" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Solver for systems of linear equations.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("TriangularSolve") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.custom"
|
||||
javaOpClass = "TriangularSolve"
|
||||
|
||||
Input(DataType.NUMERIC, "matrix") {description = "input tensor"}
|
||||
Input(DataType.NUMERIC, "rhs") {description = "input tensor"}
|
||||
Arg(DataType.BOOL, "lower") {description = "defines whether innermost matrices in matrix are lower or upper triangular"}
|
||||
Arg(DataType.BOOL, "adjoint") {description = "adjoint mode"}
|
||||
Output(DataType.FLOATING_POINT, "output")
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Solver for systems of linear questions.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("Lu") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.custom"
|
||||
javaOpClass = "Lu"
|
||||
|
||||
Input(DataType.NUMERIC, "input") {description = "input tensor"}
|
||||
Output(FLOATING_POINT, "output")
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Computes LU decomposition.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("Matmul") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.reduce"
|
||||
javaOpClass = "Mmul"
|
||||
|
||||
Input(DataType.NUMERIC, "a") {description = "input tensor"}
|
||||
Input(DataType.NUMERIC, "b") {description = "input tensor"}
|
||||
Arg(DataType.FLOATING_POINT,"alpha",{defaultValue = 1.0; description = "Defaults to 1.0: the scalar multiplier for the product of a* b "})
|
||||
Arg(DataType.FLOATING_POINT,"beta",{defaultValue = 1.0; description = "Defaults to 1.0: the scalar multiplier for c "})
|
||||
Arg(DataType.BOOL,"transA",{defaultValue = false; description = "Whether to transpose a when running multiply "})
|
||||
Arg(DataType.BOOL,"transB",{defaultValue = false; description = "Whether to transpose b when running multiply "})
|
||||
Output(DataType.FLOATING_POINT, "output")
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Performs matrix multiplication on input tensors.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Op("Qr") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
javaOpClass = "Qr"
|
||||
|
||||
Input(DataType.NUMERIC, "input") {description = "input tensor"}
|
||||
Arg(DataType.BOOL, "full") {description = "full matrices mode"; defaultValue = false}
|
||||
Output(FLOATING_POINT, "outputQ")
|
||||
Output(FLOATING_POINT, "outputR")
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Computes the QR decompositions of input matrix.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("MatrixBandPart") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.custom"
|
||||
javaOpClass = "MatrixBandPart"
|
||||
|
||||
Input(DataType.NUMERIC, "input") { description = "input tensor" }
|
||||
Arg(DataType.INT, "minLower") { description = "lower diagonal count" }
|
||||
Arg(DataType.INT, "maxUpper") { description = "upper diagonal count" }
|
||||
Output(DataType.FLOATING_POINT, "output1")
|
||||
Output(DataType.FLOATING_POINT, "output2")
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Copy a tensor setting outside a central band in each innermost matrix.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("cross") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.shape"
|
||||
javaOpClass = "Cross"
|
||||
|
||||
Input(DataType.NUMERIC, "a") {"Input tensor a"}
|
||||
Input(DataType.NUMERIC, "b") {"Input tensor b"}
|
||||
Output(FLOATING_POINT, "output")
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Computes pairwise cross product.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("diag") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.shape"
|
||||
javaOpClass = "Diag"
|
||||
|
||||
Input(DataType.NUMERIC, "input") {"Input tensor"}
|
||||
Output(DataType.FLOATING_POINT, "output")
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Calculates diagonal tensor.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("diag_part") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.shape"
|
||||
javaOpClass = "DiagPart"
|
||||
|
||||
Input(DataType.NUMERIC, "input") {"Input tensor"}
|
||||
Output(DataType.FLOATING_POINT, "output")
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Calculates diagonal tensor.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Op("matrixDeterminant") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
javaOpClass = "MatrixDeterminant"
|
||||
|
||||
Input(DataType.NUMERIC, "input") {"Input tensor"}
|
||||
Output(FLOATING_POINT, "output")
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Calculates matrix determinant.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Op("logdet") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.custom"
|
||||
javaOpClass = "Logdet"
|
||||
|
||||
Input(DataType.NUMERIC, "input") {"Input tensor"}
|
||||
Output(FLOATING_POINT, "output")
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Calculates log of determinant.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("matrixInverse") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
javaOpClass = "MatrixInverse"
|
||||
|
||||
Input(DataType.NUMERIC, "input") {"Input tensor"}
|
||||
Output(FLOATING_POINT, "output")
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Inverts a matrix
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Op("eig") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.custom"
|
||||
javaOpClass = "Eig"
|
||||
|
||||
Input(DataType.NUMERIC, "input") {"Input tensor"}
|
||||
Output(FLOATING_POINT, "eigenValues")
|
||||
Output(FLOATING_POINT, "eigenVectors")
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Calculates eigen values
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("svd") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
javaOpClass = "Svd"
|
||||
|
||||
Input(DataType.NUMERIC, "input") {"Input tensor"}
|
||||
Arg(DataType.BOOL, "fullUV") {"Full matrices mode"}
|
||||
Arg(DataType.BOOL, "computeUV") {"Compute U and V"}
|
||||
Arg(DataType.INT, "switchNum") {"Switch number"; defaultValue = 16}
|
||||
Output(FLOATING_POINT, "output")
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Calculates singular value decomposition.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("tri") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.custom"
|
||||
javaOpClass = "Tri"
|
||||
|
||||
Arg(DATA_TYPE, "dataType") { description = "Data type"; defaultValue = org.nd4j.linalg.api.buffer.DataType.FLOAT }
|
||||
Arg(INT, "row") {"Number of rows in the array"; }
|
||||
Arg(INT, "column") {"Number of columns in the array"; }
|
||||
Arg(INT, "diagonal") {"The sub-diagonal at and below which the array is filled. k = 0 is the main diagonal, while k < 0 is below it, and k > 0 is above. The default is 0."; defaultValue = 0}
|
||||
|
||||
|
||||
Output(FLOATING_POINT, "output")
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
An array with ones at and below the given diagonal and zeros elsewhere.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("triu") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.custom"
|
||||
javaOpClass = "Triu"
|
||||
Input(DataType.NUMERIC, "input") {"Input tensor"}
|
||||
Arg(DataType.INT, "diag") {"diagonal"; defaultValue = 0}
|
||||
|
||||
Output(FLOATING_POINT, "output")
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Upper triangle of an array. Return a copy of a input tensor with the elements below the k-th diagonal zeroed.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Alias(SDBaseOps(), "mmul")
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,602 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.ops
|
||||
|
||||
import org.nd4j.codegen.api.AtLeast
|
||||
import org.nd4j.codegen.api.DataType.*
|
||||
import org.nd4j.codegen.api.Language
|
||||
import org.nd4j.codegen.api.doc.DocScope
|
||||
import org.nd4j.codegen.dsl.*
|
||||
import org.nd4j.codegen.mixins.transformStrict
|
||||
|
||||
fun NN() = Namespace("NN") {
|
||||
val convPkg = "org.nd4j.linalg.api.ops.impl.layers.convolution"
|
||||
|
||||
Op("batchNorm") {
|
||||
javaPackage = convPkg
|
||||
Input(NUMERIC, "input") { description = "Input variable." }
|
||||
Input(NUMERIC, "mean") { description = "Mean value. For 1d axis, this should match input.size(axis)" }
|
||||
Input(NUMERIC, "variance") { description = "Variance value. For 1d axis, this should match input.size(axis)" }
|
||||
Input(NUMERIC, "gamma") { description = "Gamma value. For 1d axis, this should match input.size(axis)" }
|
||||
Input(NUMERIC, "beta") { description = "Beta value. For 1d axis, this should match input.size(axis)" }
|
||||
Arg(NUMERIC, "epsilon") { description = "Epsilon constant for numerical stability (to avoid division by 0)" }
|
||||
Arg(INT, "axis") {
|
||||
count = AtLeast(1)
|
||||
description = "For 2d CNN activations: 1 for NCHW format activations, or 3 for NHWC format activations.\n" +
|
||||
"For 3d CNN activations: 1 for NCDHW format, 4 for NDHWC\n" +
|
||||
"For 1d/RNN activations: 1 for NCW format, 2 for NWC"
|
||||
}
|
||||
|
||||
Output(NUMERIC, "output") { description = "variable for batch normalization" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Neural network batch normalization operation.
|
||||
For details, see <a href="https://arxiv.org/abs/1502.03167">https://arxiv.org/abs/1502.03167</a>
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("biasAdd") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.broadcast"
|
||||
Input(NUMERIC, "input") { description = "4d input variable" }
|
||||
Input(NUMERIC, "bias") { description = "1d bias" }
|
||||
Arg(BOOL, "nchw") { description = "The format - nchw=true means [minibatch, channels, height, width] format; nchw=false - [minibatch, height, width, channels].\n" +
|
||||
"Unused for 2d inputs" }
|
||||
|
||||
Output(NUMERIC, "output") { description = "Output variable, after applying bias add operation" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Bias addition operation: a special case of addition, typically used with CNN 4D activations and a 1D bias vector
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("dropout") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.random.impl"
|
||||
javaOpClass = "CustomDropOut"
|
||||
Input(NUMERIC, "input") { description = "Input array" }
|
||||
Arg(BOOL, "inverted") { description = "Whether dropout should be inverted or not." }
|
||||
Arg(INT, "seed") { description = "the seed for dropout"; defaultValue = 0 }
|
||||
Arg(NUMERIC,"probabilityValue") { description = "the chance of dropping a value to 0. Maybe interpreted as 1 - p if inverted is true."}
|
||||
Output(NUMERIC, "output") { description = "Output" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Dropout operation
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Op("elu", transformStrict) {
|
||||
javaOpClass = "ELU"
|
||||
legacy = false
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Element-wise exponential linear unit (ELU) function:
|
||||
out = x if x > 0
|
||||
out = a * (exp(x) - 1) if x <= 0
|
||||
with constant a = 1.0
|
||||
<p>
|
||||
See: <a href="https://arxiv.org/abs/1511.07289">https://arxiv.org/abs/1511.07289</a>
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("gelu", transformStrict) {
|
||||
javaOpClass = "GELU"
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
GELU activation function - Gaussian Error Linear Units
|
||||
For more details, see <i>Gaussian Error Linear Units (GELUs)</i> - <a href="https://arxiv.org/abs/1606.08415">https://arxiv.org/abs/1606.08415</a>
|
||||
This method uses the sigmoid approximation
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("hardSigmoid", transformStrict) {
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Element-wise hard sigmoid function:
|
||||
out[i] = 0 if in[i] <= -2.5
|
||||
out[1] = 0.2*in[i]+0.5 if -2.5 < in[i] < 2.5
|
||||
out[i] = 1 if in[i] >= 2.5
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("hardTanh", transformStrict) {
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Element-wise hard tanh function:
|
||||
out[i] = -1 if in[i] <= -1
|
||||
out[1] = in[i] if -1 < in[i] < 1
|
||||
out[i] = 1 if in[i] >= 1
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("hardTanhDerivative") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.gradient"
|
||||
legacy = true
|
||||
Input(NUMERIC, "x") { description = "Input variable" }
|
||||
Output(NUMERIC, "output"){ description = "Output variable" }
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Derivative (dOut/dIn) of the element-wise hard Tanh function - hardTanh(%INPUT_TYPE%)
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("leakyRelu") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.scalar"
|
||||
javaOpClass = "LeakyReLU"
|
||||
legacy = true
|
||||
Input(NUMERIC, "x") { description = "Input variable" }
|
||||
Arg(NUMERIC, "alpha") { description = "Cutoff - commonly 0.01" }
|
||||
|
||||
Output(NUMERIC, "output") { description = "Output variable" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Element-wise leaky ReLU function:
|
||||
out = x if x >= 0.0
|
||||
out = alpha * x if x < cutoff
|
||||
Alpha value is most commonly set to 0.01
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("leakyReluDerivative") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.gradient"
|
||||
javaOpClass = "LeakyReLUDerivative"
|
||||
legacy = true
|
||||
Input(NUMERIC, "x") { description = "Input variable" }
|
||||
Arg(FLOATING_POINT, "alpha") { description = "Cutoff - commonly 0.01" }
|
||||
|
||||
Output(NUMERIC, "output") { description = "Output variable" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Leaky ReLU derivative: dOut/dIn given input.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("CReLU") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
javaOpClass = "CReLU"
|
||||
Input(NUMERIC, "x") { description = "Input variable" }
|
||||
Output(NUMERIC, "output") { description = "Output variable" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Concatenates a ReLU which selects only the positive part of the activation with a ReLU which selects only the negative part of the activation. Note that as a result this non-linearity doubles the depth of the activations.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("linear") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
javaOpClass = "XwPlusB"
|
||||
Input(NUMERIC, "input") { description = "Input data" }
|
||||
Input(NUMERIC, "weights") { description = "Weights variable, shape [nIn, nOut]" }
|
||||
Input(NUMERIC, "bias") { description = "Optional bias variable (may be null)" /*; optional = true*/ }
|
||||
Arg(BOOL,"transposeA") { description = "Whether to transpose input or not"; defaultValue= false}
|
||||
Arg(BOOL,"transposeB") { description = "Whether to transpose second input or not"; defaultValue= false}
|
||||
Arg(BOOL,"transposeC") { description = "Whether to transpose result or not"; defaultValue= false}
|
||||
Output(NUMERIC, "output") { description = "Output variable" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Linear layer operation: out = mmul(in,w) + bias
|
||||
Note that bias array is optional
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Op("logSigmoid", transformStrict) {
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Element-wise sigmoid function: out[i] = log(sigmoid(in[i]))
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("logSoftmax") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
javaOpClass = "LogSoftMax"
|
||||
Input(NUMERIC, "x") { description = "" }
|
||||
Output(NUMERIC, "output") { description = "" }
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Log softmax activation
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("logSoftmax") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
javaOpClass = "LogSoftMax"
|
||||
Input(NUMERIC, "x") { description = "Input" }
|
||||
Arg(INT, "dimension") { description = "Dimension along which to apply log softmax" }
|
||||
Output(NUMERIC, "output") { description = "Output - log(softmax(input))" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Log softmax activation
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("relu") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.scalar"
|
||||
javaOpClass = "RectifiedLinear"
|
||||
legacy = true
|
||||
Input(NUMERIC, "x") { description = "Input" }
|
||||
Arg(NUMERIC, "cutoff") { description = "Cutoff value for ReLU operation - x > cutoff ? x : 0. Usually 0" }
|
||||
Output(NUMERIC, "output") { description = "Output" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Element-wise rectified linear function with specified cutoff:
|
||||
out[i] = in[i] if in[i] >= cutoff
|
||||
out[i] = 0 otherwise
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("relu6") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.scalar"
|
||||
legacy = true
|
||||
Input(NUMERIC, "x") { description = "Input" }
|
||||
Arg(NUMERIC, "cutoff") { description = "Cutoff value for ReLU operation. Usually 0" }
|
||||
Output(NUMERIC, "output") { description = "Output" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Element-wise "rectified linear 6" function with specified cutoff:
|
||||
out[i] = min(max(in, cutoff), 6)
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("reluLayer") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms"
|
||||
Input(NUMERIC, "input") { description = "Input data" }
|
||||
Input(NUMERIC, "weights") { description = "Weights variable" }
|
||||
Input(NUMERIC, "bias") { description = " Bias variable" }
|
||||
Output(NUMERIC, "output") { description = "Output variable" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
ReLU (Rectified Linear Unit) layer operation: out = relu(mmul(in,w) + bias)
|
||||
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("preciseGelu", transformStrict) {
|
||||
javaOpClass = "PreciseGELU"
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
GELU activation function - Gaussian Error Linear Units
|
||||
For more details, see <i>Gaussian Error Linear Units (GELUs)</i> - <a href="https://arxiv.org/abs/1606.08415">https://arxiv.org/abs/1606.08415</a>
|
||||
This method uses the precise method
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("prelu") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.scalar"
|
||||
javaOpClass = "PRelu"
|
||||
Input(NUMERIC, "input") { description = "Input data" }
|
||||
Input(NUMERIC, "alpha") { description = "The cutoff variable. Note that the batch dimension (the 0th, whether it is batch or not) should not be part of alpha." }
|
||||
Arg(INT, "sharedAxes") { count = AtLeast(1); description = "Which axes to share cutoff parameters along." }
|
||||
|
||||
Output(NUMERIC, "output") { description = "Output" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
PReLU (Parameterized Rectified Linear Unit) operation. Like LeakyReLU with a learnable alpha:
|
||||
out[i] = in[i] if in[i] >= 0
|
||||
out[i] = in[i] * alpha[i] otherwise
|
||||
|
||||
sharedAxes allows you to share learnable parameters along axes.
|
||||
For example, if the input has shape [batchSize, channels, height, width]
|
||||
and you want each channel to have its own cutoff, use sharedAxes = [2, 3] and an
|
||||
alpha with shape [channels].
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("selu", transformStrict) {
|
||||
javaOpClass = "SELU"
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Element-wise SeLU function - Scaled exponential Lineal Unit: see <a href="https://arxiv.org/abs/1706.02515">Self-Normalizing Neural Networks</a>
|
||||
|
||||
out[i] = scale * alpha * (exp(in[i])-1) if in[i]>0, or 0 if in[i] <= 0
|
||||
Uses default scale and alpha values.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("sigmoid", transformStrict) {
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Element-wise sigmoid function: out[i] = 1.0/(1+exp(-in[i]))
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("sigmoidDerivative") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.gradient"
|
||||
Input(NUMERIC, "x") { description = "Input Variable" }
|
||||
Input(NUMERIC, "wrt") { description = "Gradient at the output - dL/dOut. Must have same shape as the input" }
|
||||
Output(NUMERIC, "output") { description = "Output (gradient at input of sigmoid)" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Element-wise sigmoid function derivative: dL/dIn given input and dL/dOut
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("softmax") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
javaOpClass = "SoftMax"
|
||||
Input(NUMERIC, "x") { description = "Input" }
|
||||
Arg(INT, "dimension") { description = "Dimension along which to apply softmax"; defaultValue = -1 }
|
||||
Output(NUMERIC, "output") { description = "Output variable" }
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Softmax activation, along the specified dimension
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Op("softplus", transformStrict) {
|
||||
javaOpClass = "SoftPlus"
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Element-wise softplus function: out = log(exp(x) + 1)
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("softsign", transformStrict) {
|
||||
javaOpClass = "SoftSign"
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Element-wise softsign function: out = x / (abs(x) + 1)
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("softsignDerivative") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.gradient"
|
||||
javaOpClass = "SoftSignDerivative"
|
||||
legacy = true
|
||||
Input(NUMERIC, "x") { description = "Input variable" }
|
||||
Output(NUMERIC, "output") { description = "Output" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Element-wise derivative (dOut/dIn) of the softsign function softsign(%INPUT_TYPE%)
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("swish", transformStrict) {
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Element-wise "swish" function: out = x * sigmoid(b*x) with b=1.0
|
||||
See: <a href="https://arxiv.org/abs/1710.05941">https://arxiv.org/abs/1710.05941</a>
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("layerNorm") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
val input = Input(NUMERIC, "input") { description = "Input variable" }
|
||||
val g = Input(NUMERIC, "gain") { description = "Gain" }
|
||||
Input(NUMERIC, "bias") { description = "Bias"; defaultValue = null}
|
||||
val ch = Arg(BOOL, "channelsFirst") { description = "For 2D input - unused. True for NCHW (minibatch, channels, height, width), false for NHWC data" }
|
||||
val dim = Arg(LONG, "dimensions") { count = AtLeast(1); description = "Dimensions to perform layer norm over - dimension=1 for 2d/MLP data, dimension=1,2,3 for CNNs" }
|
||||
|
||||
Output(NUMERIC, "output") { description = "Output variable" }
|
||||
|
||||
AllParamSignature()
|
||||
Signature(input, g, ch, dim)
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Apply Layer Normalization
|
||||
|
||||
y = gain * standardize(x) + bias
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Op("dotProductAttentionV2") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
val q = Input(NUMERIC, "queries") { description = "A {@link SDVariable} representing the query tensor. Shape: [batchSize, numQueries, queryDim]" }
|
||||
val v = Input(NUMERIC, "values") { description = "A {@link SDVariable} representing the value tensor. Shape: [batchSize, numValues, valueDim]" }
|
||||
|
||||
val k = Input(NUMERIC, "keys") { description = "A {@link SDVariable} representing the key tensor. Shape: [batchSize, numValues, keyDim]" }
|
||||
val queryMask = Input(NUMERIC, "queryMask") { description = "A {@link SDVariable} representing the query mask tensor. Shape: [batchSize, numQueries]" }
|
||||
val valueMask = Input(NUMERIC, "valueMask") { description = "@param valueMask A {@link SDVariable} representing the value mask tensor. Shape: [batchSize, numValues]" }
|
||||
|
||||
val s = Arg(FLOATING_POINT, "scaleFactor") { defaultValue = 1.0; description = "@param scaleFactor A {@code double} scaling factor applied to the dot product between queries and keys." }
|
||||
val dropout = Arg(FLOATING_POINT, "dropoutProbability") { defaultValue = 0.0; description = "A {@code double} specifying the dropout probability to be applied to attention weights." }
|
||||
val useCausalMask = Arg(BOOL, "useCausalMask") { defaultValue = false; description = " A {@code boolean} flag to indicate whether to apply a causal mask to the attention scores, for autoregressive tasks." }
|
||||
val training = Arg(BOOL, "training") { defaultValue = false; description = " A {@code boolean} flag to indicate whether the layer is in training mode or inference mode, affecting dropout." }
|
||||
|
||||
Output(NUMERIC, "output") { description = " A {@link SDVariable} representing the output tensor of the dot product attention operation. Shape: [batchSize, numQueries, valueDim]"}
|
||||
|
||||
Signature(q,v,k,queryMask,valueMask, s,dropout,useCausalMask,training)
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
This operation performs dot product attention on the given timeseries input with the given queries
|
||||
out = sum(similarity(k_i, q) * v_i)
|
||||
|
||||
similarity(k, q) = softmax(k * q) where x * q is the dot product of x and q
|
||||
|
||||
Optionally with normalization step:
|
||||
similarity(k, q) = softmax(k * q / sqrt(size(q))
|
||||
|
||||
See also "Attention is all you need" (https://arxiv.org/abs/1706.03762, p. 4, eq. 1)
|
||||
|
||||
Note: This supports multiple queries at once, if only one query is available the queries vector still has to
|
||||
be 3D but can have queryCount = 1
|
||||
|
||||
Note: keys and values usually is the same array. If you want to use it as the same array, simply pass it for
|
||||
both.
|
||||
|
||||
Note: Queries, keys and values must either be all rank 3 or all rank 4 arrays. Mixing them doesn't work. The
|
||||
output rank will depend on the input rank.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("dotProductAttention") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
val q = Input(NUMERIC, "queries") { description = "input 3D array \"queries\" of shape [batchSize, featureKeys, queryCount]\n" +
|
||||
"or 4D array of shape [batchSize, numHeads, featureKeys, queryCount]" }
|
||||
val k = Input(NUMERIC, "keys") { description = "input 3D array \"keys\" of shape [batchSize, featureKeys, timesteps]\n" +
|
||||
"or 4D array of shape [batchSize, numHeads, featureKeys, timesteps]" }
|
||||
val v = Input(NUMERIC, "values") { description = "input 3D array \"values\" of shape [batchSize, featureValues, timesteps]\n" +
|
||||
"or 4D array of shape [batchSize, numHeads, featureValues, timesteps]" }
|
||||
val m = Input(NUMERIC, "mask") { description = "OPTIONAL; array that defines which values should be skipped of shape [batchSize, timesteps]" }
|
||||
val s = Arg(BOOL, "scaled") { description = "normalization, false -> do not apply normalization, true -> apply normalization" }
|
||||
Arg(BOOL, "withWeights") { defaultValue = false; description = "withWeights return attention weights as well, false -> only one output, true -> two outputs" }
|
||||
|
||||
Output(NUMERIC, "output") { description = " Attention result arrays of shape [batchSize, featureValues, queryCount] or [batchSize, numHeads, featureValues, queryCount],\n" +
|
||||
"(optionally) Attention Weights of shape [batchSize, timesteps, queryCount] or [batchSize, numHeads, timesteps, queryCount]" }
|
||||
|
||||
Signature(q, k, v, m, s)
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
This operation performs dot product attention on the given timeseries input with the given queries
|
||||
out = sum(similarity(k_i, q) * v_i)
|
||||
|
||||
similarity(k, q) = softmax(k * q) where x * q is the dot product of x and q
|
||||
|
||||
Optionally with normalization step:
|
||||
similarity(k, q) = softmax(k * q / sqrt(size(q))
|
||||
|
||||
See also "Attention is all you need" (https://arxiv.org/abs/1706.03762, p. 4, eq. 1)
|
||||
|
||||
Note: This supports multiple queries at once, if only one query is available the queries vector still has to
|
||||
be 3D but can have queryCount = 1
|
||||
|
||||
Note: keys and values usually is the same array. If you want to use it as the same array, simply pass it for
|
||||
both.
|
||||
|
||||
Note: Queries, keys and values must either be all rank 3 or all rank 4 arrays. Mixing them doesn't work. The
|
||||
output rank will depend on the input rank.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Op("multiHeadDotProductAttention") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
val q = Input(NUMERIC, "queries") { description = "input 3D array \"queries\" of shape [batchSize, featureKeys, queryCount]" }
|
||||
val k = Input(NUMERIC, "keys") { description = "input 3D array \"keys\" of shape [batchSize, featureKeys, timesteps]" }
|
||||
val v = Input(NUMERIC, "values") { description = "input 3D array \"values\" of shape [batchSize, featureValues, timesteps]" }
|
||||
val wq = Input(NUMERIC, "Wq") { description = "input query projection weights of shape [numHeads, projectedKeys, featureKeys]" }
|
||||
val wk = Input(NUMERIC, "Wk") { description = "input key projection weights of shape [numHeads, projectedKeys, featureKeys]" }
|
||||
val wv = Input(NUMERIC, "Wv") { description = "input value projection weights of shape [numHeads, projectedValues, featureValues]" }
|
||||
val wo = Input(NUMERIC, "Wo") { description = "output projection weights of shape [numHeads * projectedValues, outSize]" }
|
||||
val m = Input(NUMERIC, "mask") { description = "OPTIONAL; array that defines which values should be skipped of shape [batchSize, timesteps]" }
|
||||
val s = Arg(BOOL, "scaled") { description = "normalization, false -> do not apply normalization, true -> apply normalization" }
|
||||
Arg(BOOL, "withWeights") { defaultValue = false; description = "return attention weights as well, false -> only one output, true -> two outputs" }
|
||||
|
||||
Output(NUMERIC, "output") { description = "Attention result arrays of shape [batchSize, outSize, queryCount]\n" +
|
||||
"(optionally) Attention Weights of shape [batchSize, numHeads, timesteps, queryCount]" }
|
||||
|
||||
Signature(q, k, v, wq, wk, wv, wo, m, s)
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
This performs multi-headed dot product attention on the given timeseries input
|
||||
out = concat(head_1, head_2, ..., head_n) * Wo
|
||||
head_i = dot_product_attention(Wq_i*q, Wk_i*k, Wv_i*v)
|
||||
|
||||
Optionally with normalization when calculating the attention for each head.
|
||||
|
||||
See also "Attention is all you need" (https://arxiv.org/abs/1706.03762, pp. 4,5, "3.2.2 Multi-Head Attention")
|
||||
|
||||
This makes use of dot_product_attention OP support for rank 4 inputs.
|
||||
see dotProductAttention(%INPUT_TYPE%, %INPUT_TYPE%, %INPUT_TYPE%, %INPUT_TYPE%, boolean, boolean)
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("pad") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms"
|
||||
Input(NUMERIC, "input") { description = "Input tensor"}
|
||||
Input(NUMERIC, "padding") { description = "Padding value" }
|
||||
Arg(ENUM, "PadMode") { possibleValues = listOf("CONSTANT", "REFLECT", "SYMMETRIC"); description = "Padding format"; defaultValue="CONSTANT" }
|
||||
Arg(NUMERIC, "constant") { description = "Padding constant" }
|
||||
|
||||
Output(NUMERIC, "output"){ description = "Padded input" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Padding operation
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Op("topK") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.custom"
|
||||
Input(NUMERIC, "input") { description = "Input data" }
|
||||
Arg(NUMERIC, "k") { description = "The number of values to return" }
|
||||
Arg(BOOL, "sorted") { description = "Whether to return the values sorted or not" }
|
||||
Output(NUMERIC, "output") { description = "the top k values in the input" }
|
||||
Output(NUMERIC, "indices") { description = "the indices of the top k values" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Find values and indices for the largest k entries along the last dimension.<br>
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Alias(Math(), "tanh")
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.ops
|
||||
|
||||
import org.nd4j.codegen.api.DataType.*
|
||||
import org.nd4j.codegen.api.Language
|
||||
import org.nd4j.codegen.api.doc.DocScope
|
||||
import org.nd4j.codegen.dsl.*
|
||||
|
||||
fun SDRNN() = Namespace("RNN") {
|
||||
|
||||
|
||||
val LSTMConfiguration = Config("LSTMConfiguration") {
|
||||
|
||||
Arg(ENUM, "RnnDataFormat") {
|
||||
possibleValues = listOf("TNS", "NST", "NTS"); description = " The data format of the input. Input shape depends on data format (in config):<br>\n" +
|
||||
" TNS -> [timeSteps, batchSize, inSize]<br>\n" +
|
||||
" NST -> [batchSize, inSize, timeSteps]<br>\n" +
|
||||
" NTS -> [batchSize, timeSteps, inSize]<br>"
|
||||
}
|
||||
|
||||
|
||||
Arg(BOOL, "peepHole") { description = "Whether to provide peephole connections"; }
|
||||
Arg(NUMERIC, "forgetBias") { description = "The bias added to forget gates in order to reduce the scale of forgetting in the beginning of the training."; }
|
||||
Arg(NUMERIC, "clippingCellValue") { description = "The bias added to forget gates in order to reduce the scale of forgetting in the beginning of the training."; }
|
||||
|
||||
javaClassOverride = "org.nd4j.linalg.api.ops.impl.layers.recurrent.config.LSTMConfiguration"
|
||||
}
|
||||
|
||||
|
||||
val LSTMLayerConfig = Config("LSTMLayerConfig") {
|
||||
|
||||
Arg(ENUM, "LSTMDataFormat") {
|
||||
possibleValues = listOf("TNS", "NST", "NTS", "T2NS");
|
||||
description = "for unidirectional:" +
|
||||
" TNS: shape [timeLength, numExamples, inOutSize] - sometimes referred to as \"time major\"<br>\n" +
|
||||
" NST: shape [numExamples, inOutSize, timeLength]<br>\n" +
|
||||
" NTS: shape [numExamples, timeLength, inOutSize] - TF \"time_major=false\" layout<br>" +
|
||||
" for bidirectional:\n" +
|
||||
" T2NS: 3 = [timeLength, 2, numExamples, inOutSize] (for ONNX)"
|
||||
}
|
||||
|
||||
|
||||
Arg(ENUM, "LSTMDirectionMode") {
|
||||
possibleValues = listOf("FWD", "BWD", "BIDIR_SUM", "BIDIR_CONCAT", "BIDIR_EXTRA_DIM"); description = "direction <br>\n" +
|
||||
" FWD: 0 = fwd\n" +
|
||||
" BWD: 1 = bwd\n" +
|
||||
" BIDIR_SUM: 2 = bidirectional sum\n" +
|
||||
" BIDIR_CONCAT: 3 = bidirectional concat\n" +
|
||||
" BIDIR_EXTRA_DIM: 4 = bidirectional extra output dim (in conjunction with format dataFormat = 3)"
|
||||
}
|
||||
|
||||
Arg(ENUM, "gateAct") {
|
||||
possibleValues = listOf("TANH",
|
||||
"RELU",
|
||||
"SIGMOID",
|
||||
"AFFINE",
|
||||
"LEAKY_RELU",
|
||||
"THRESHHOLD_RELU",
|
||||
"SCALED_TAHN",
|
||||
"HARD_SIGMOID",
|
||||
"ELU",
|
||||
"SOFTSIGN",
|
||||
"SOFTPLUS"); description = "Activations"
|
||||
}
|
||||
|
||||
|
||||
Arg(ENUM, "cellAct") {
|
||||
possibleValues = listOf("TANH",
|
||||
"RELU",
|
||||
"SIGMOID",
|
||||
"AFFINE",
|
||||
"LEAKY_RELU",
|
||||
"THRESHHOLD_RELU",
|
||||
"SCALED_TAHN",
|
||||
"HARD_SIGMOID",
|
||||
"ELU",
|
||||
"SOFTSIGN",
|
||||
"SOFTPLUS"); description = "Activations"
|
||||
}
|
||||
|
||||
|
||||
Arg(ENUM, "outAct") {
|
||||
possibleValues = listOf("TANH",
|
||||
"RELU",
|
||||
"SIGMOID",
|
||||
"AFFINE",
|
||||
"LEAKY_RELU",
|
||||
"THRESHHOLD_RELU",
|
||||
"SCALED_TAHN",
|
||||
"HARD_SIGMOID",
|
||||
"ELU",
|
||||
"SOFTSIGN",
|
||||
"SOFTPLUS"); description = "Activations"
|
||||
}
|
||||
|
||||
|
||||
Arg(BOOL, "retFullSequence") { description = "indicates whether to return whole time sequence h {h_0, h_1, ... , h_sL-1}"; defaultValue = true }
|
||||
Arg(BOOL, "retLastH") {
|
||||
description = "indicates whether to return output at last time step only,\n" +
|
||||
" in this case shape would be [bS, nOut] (exact shape depends on dataFormat argument)"; defaultValue = false
|
||||
}
|
||||
Arg(BOOL, "retLastC") {
|
||||
description = "indicates whether to return cells state at last time step only,\n" +
|
||||
" in this case shape would be [bS, nOut] (exact shape depends on dataFormat argument)"; defaultValue = false
|
||||
}
|
||||
Arg(NUMERIC, "cellClip") { description = "Cell clipping value, if it = 0 then do not apply clipping"; defaultValue = 0.0}
|
||||
|
||||
Arg(NUMERIC, "gateAlpha") {defaultValue=0.0}
|
||||
Arg(NUMERIC, "gateBeta") {defaultValue=0.0}
|
||||
Arg(NUMERIC, "cellAlpha") {defaultValue=0.0}
|
||||
Arg(NUMERIC, "cellBeta") {defaultValue=0.0}
|
||||
Arg(NUMERIC, "outAlpha") {defaultValue=0.0}
|
||||
Arg(NUMERIC, "outBeta") {defaultValue=0.0}
|
||||
|
||||
|
||||
javaClassOverride = "org.nd4j.linalg.api.ops.impl.layers.recurrent.config.LSTMLayerConfig"
|
||||
}
|
||||
|
||||
|
||||
val GRUWeights = Config("GRUWeights") {
|
||||
Input(NUMERIC, "ruWeight")
|
||||
Input(NUMERIC, "cWeight")
|
||||
Input(NUMERIC, "ruBias")
|
||||
Input(NUMERIC, "cBias")
|
||||
javaClassOverride = "org.nd4j.linalg.api.ops.impl.layers.recurrent.weights.GRUWeights"
|
||||
}
|
||||
|
||||
val SRUWeights = Config("SRUWeights") {
|
||||
Input(NUMERIC, "weights")
|
||||
Input(NUMERIC, "bias")
|
||||
javaClassOverride = "org.nd4j.linalg.api.ops.impl.layers.recurrent.weights.SRUWeights"
|
||||
}
|
||||
|
||||
val LSTMWeights = Config("LSTMWeights") {
|
||||
Input(NUMERIC, "ruWeight")
|
||||
Input(NUMERIC, "inputPeepholeWeights")
|
||||
Input(NUMERIC, "forgetPeepholeWeights")
|
||||
Input(NUMERIC, "outputPeepholeWeights")
|
||||
Input(NUMERIC, "bias")
|
||||
|
||||
javaClassOverride = "org.nd4j.linalg.api.ops.impl.layers.recurrent.weights.LSTMWeights"
|
||||
}
|
||||
|
||||
val LSTMLayerWeights = Config("LSTMLayerWeights") {
|
||||
Input(NUMERIC, "inputWeights") {description="input weights Wx:\n" +
|
||||
" 1) shapes `[nIn, 4*nOut]` for FWD,BWD " +
|
||||
" 2) shapes `[2, nIn, 4*nOut]` BIDIR_SUM, BIDIR_CONCAT and BIDIR_EXTRA_DIM"}
|
||||
Input(NUMERIC, "recurrentWeights") {description="recurrent weights Wr:\n" +
|
||||
" 1) shapes `[nIn, 4*nOut]` for FWD, BWD " +
|
||||
" 2) shapes `[2, nIn, 4*nOut]` BIDIR_SUM, BIDIR_CONCAT and BIDIR_EXTRA_DIM"}
|
||||
Input(NUMERIC, "biases") {description="biases\n"+
|
||||
" 1) shapes `[4*nOut]` for FWD, BWD " +
|
||||
" 2) shapes `[2, 4*nOut]` for BIDIR_SUM, BIDIR_CONCAT and BIDIR_EXTRA_DIM"
|
||||
defaultValue=null}
|
||||
Input(NUMERIC, "peepholeWeights") {description="peephole weights Wp:\n" +
|
||||
" 1) `[3*nOut]` when directionMode < 2\n" +
|
||||
" 2) `[2, 3*nOut]` when directionMode >= 2"; defaultValue=null}
|
||||
|
||||
|
||||
javaClassOverride = "org.nd4j.linalg.api.ops.impl.layers.recurrent.weights.LSTMLayerWeights"
|
||||
}
|
||||
|
||||
|
||||
val namespaceJavaPackage = "org.nd4j.linalg.api.ops.impl.layers.recurrent"
|
||||
Op("gruCell") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "GRUCell"
|
||||
Input(NUMERIC, "x") { description = "Input, with shape [batchSize, inSize]" }
|
||||
Input(NUMERIC, "hLast") { description = "Output of the previous cell/time step, with shape [batchSize, numUnits]" }
|
||||
useConfig(GRUWeights)
|
||||
Output(NUMERIC, "r") { description = "Reset gate output" }
|
||||
Output(NUMERIC, "u") { description = "Update gate output" }
|
||||
Output(NUMERIC, "c") { description = "Cell gate output" }
|
||||
Output(NUMERIC, "h") { description = "Cell output" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
The GRU cell. Does a single time step operation
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("gru") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "GRU"
|
||||
Input(NUMERIC, "x") { description = "input [time, bS, nIn]" }
|
||||
Input(NUMERIC, "hLast") { description = "initial cell output (at time step = 0) [bS, nOut]" }
|
||||
Input(NUMERIC, "Wx") { description = "input-to-hidden weights, [nIn, 3*nOut]" }
|
||||
Input(NUMERIC, "Wh") { description = "hidden-to-hidden weights, [nOut, 3*nOut]" }
|
||||
Input(NUMERIC, "biases") { description = "biases, [3*nOut]" }
|
||||
|
||||
Output(NUMERIC, "h") { description = "cell outputs [time, bS, nOut], that is per each time step" }
|
||||
|
||||
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
The GRU operation. Gated Recurrent Unit - Cho et al. 2014.
|
||||
|
||||
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Op("lstmCell") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "LSTMBlockCell"
|
||||
Input(NUMERIC, "x") { description = "Input, with shape [batchSize, inSize]" }
|
||||
Input(NUMERIC, "cLast") { description = "Previous cell state, with shape [batchSize, numUnits]" }
|
||||
Input(NUMERIC, "yLast") { description = "revious cell output, with shape [batchSize, numUnits]" }
|
||||
useConfig(LSTMWeights)
|
||||
useConfig(LSTMConfiguration)
|
||||
|
||||
Output(NUMERIC, "i") { description = "Output - input modulation gate activations [batchSize, numUnits]." }
|
||||
Output(NUMERIC, "c") { description = "Output - Activations, cell state (pre tanh) [batchSize, numUnits]." }
|
||||
Output(NUMERIC, "f") { description = "Output - forget gate activations [batchSize, numUnits]." }
|
||||
Output(NUMERIC, "o") { description = "Output - output gate activations [batchSize, numUnits]." }
|
||||
Output(NUMERIC, "z") { description = "Output - input gate activations [batchSize, numUnits]." }
|
||||
Output(NUMERIC, "h") { description = "Cell state, post tanh [batchSize, numUnits]." }
|
||||
Output(NUMERIC, "y") { description = "Current cell output [batchSize, numUnits]." }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
The LSTM cell. Does a single time step operation.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Op("lstmblock") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "LSTMBlock"
|
||||
Input(NUMERIC, "maxTSLength") {defaultValue=null}
|
||||
Input(NUMERIC, "x") { description = " Input, with shape dependent on the data format (in config)." }
|
||||
Input(NUMERIC, "cLast") { description = "Previous/initial cell state, with shape [batchSize, numUnits]" ; defaultValue=null}
|
||||
Input(NUMERIC, "yLast") { description = "Previous/initial cell output, with shape [batchSize, numUnits]" ; defaultValue=null }
|
||||
useConfig(LSTMWeights)
|
||||
useConfig(LSTMConfiguration)
|
||||
|
||||
Output(NUMERIC, "output") { description = "The layer's outputs." }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
The LSTM block
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Op("lstmLayer") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "LSTMLayer"
|
||||
Input(NUMERIC, "x") { description = " Input, with shape dependent on the data format (in config)." }
|
||||
Input(NUMERIC, "cLast") { description = "Previous/initial cell state, with shape [batchSize, numUnits]"; defaultValue=null }
|
||||
Input(NUMERIC, "yLast") { description = "Previous/initial cell output, with shape [batchSize, numUnits]"; defaultValue=null }
|
||||
Input(NUMERIC, "maxTSLength") { description = "maxTSLength with shape [batchSize]"; defaultValue=null }
|
||||
useConfig(LSTMLayerWeights)
|
||||
useConfig(LSTMLayerConfig)
|
||||
|
||||
//TODO these are optional
|
||||
Output(NUMERIC, "output") { description = "The layer's outputs - full time series" }
|
||||
Output(NUMERIC, "yLast") { description = "The layer's outputs - last time step activations (yLast)" }
|
||||
Output(NUMERIC, "cLast") { description = "The layer's outputs - last time step cell state (cLast)" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Long Short-Term Memory layer - Hochreiter 1997.
|
||||
SUPPORTS following data formats:
|
||||
for unidirectional:
|
||||
TNS: shapes [timeLength, numExamples, inOutSize]
|
||||
NST: shapes [numExamples, inOutSize, timeLength]
|
||||
NTS: shapes [numExamples, timeLength, inOutSize]
|
||||
for bidirectional:
|
||||
T2NS: shapes [timeLength, 2, numExamples, inOutSize] (for ONNX)
|
||||
SUPPORTS following direction modes:
|
||||
FWD: forward
|
||||
BWD: backward
|
||||
BIDIR_SUM: bidirectional sum
|
||||
BIDIR_CONCAT: bidirectional concat
|
||||
BIDIR_EXTRA_DIM: bidirectional extra output dim (in conjunction with format dataFormat - T2NS)
|
||||
You may use different gate configurations:
|
||||
specify gate/cell/out aplha/beta and numbers of activations for gate/cell/out described in activations enum
|
||||
("RELU","SIGMOID","AFFINE","LEAKY_RELU","THRESHHOLD_RELU","SCALED_TAHN","HARD_SIGMOID","ELU","SOFTSIGN","SOFTPLUS")
|
||||
Also this layer supports MKLDNN (DNNL) and cuDNN acceleration
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Op("sruCell") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "SRUCell"
|
||||
Input(NUMERIC, "x") { description = "Input, with shape [batchSize, inSize]" }
|
||||
Input(NUMERIC, "cLast") { description = "Previous cell state, with shape [batchSize, inSize]" }
|
||||
useConfig(SRUWeights)
|
||||
|
||||
Output(NUMERIC, "output") { description = "The cell's outputs." }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
The SRU layer. Does a single time step operation.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Op("sru") {
|
||||
javaPackage = namespaceJavaPackage
|
||||
javaOpClass = "SRU"
|
||||
Input(NUMERIC, "x") { description = "Input, with shape [batchSize, inSize]" }
|
||||
Input(NUMERIC, "initialC") { description = "Initial cell state, with shape [batchSize, inSize]" }
|
||||
Input(NUMERIC, "mask") { description = "An optional dropout mask, with shape [batchSize, inSize]"; defaultValue = null }
|
||||
|
||||
useConfig(SRUWeights)
|
||||
|
||||
Output(NUMERIC, "output") { description = "The cell's outputs.." }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
The SRU layer. Does a single time step operation.
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.ops
|
||||
|
||||
import org.nd4j.codegen.api.AtLeast
|
||||
import org.nd4j.codegen.api.DataType.*
|
||||
import org.nd4j.codegen.api.Language
|
||||
import org.nd4j.codegen.api.doc.DocScope
|
||||
import org.nd4j.codegen.dsl.*
|
||||
|
||||
fun Random() = Namespace("Random") {
|
||||
val random = Mixin("random"){
|
||||
Arg(DATA_TYPE, "datatype"){ description = "Data type of the output variable"}
|
||||
Arg(LONG, "shape") { count = AtLeast(0); description = "Shape of the new random %INPUT_TYPE%, as a 1D array" }
|
||||
Output(NUMERIC, "output") { description = "Tensor with the given shape where values are randomly sampled according to a %OP_NAME% distribution" }
|
||||
}
|
||||
|
||||
val legacyRandom = Mixin("legacyRandom"){
|
||||
useMixin(random)
|
||||
javaPackage = "org.nd4j.linalg.api.ops.random.impl"
|
||||
legacy = true
|
||||
}
|
||||
|
||||
val normalRandom = Mixin("normalRandom"){
|
||||
Arg(FLOATING_POINT, "mean") { description = "Mean value for the random array" }
|
||||
Arg(FLOATING_POINT, "stddev") { description = "Standard deviation for the random array" }
|
||||
useMixin(legacyRandom)
|
||||
}
|
||||
|
||||
Op("bernoulli") {
|
||||
javaOpClass = "BernoulliDistribution"
|
||||
Arg(FLOATING_POINT, "p") { description = "Probability of value 1" }
|
||||
useMixin(legacyRandom)
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Generate a new random %INPUT_TYPE%, where values are randomly sampled according to a Bernoulli distribution,
|
||||
with the specified probability. Array values will have value 1 with probability P and value 0 with probability
|
||||
1-P.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("binomial") {
|
||||
javaOpClass = "BinomialDistribution"
|
||||
|
||||
Arg(INT, "nTrials") { description = "Number of trials parameter for the binomial distribution" }
|
||||
Arg(FLOATING_POINT, "p") { description = "Probability of success for each trial" }
|
||||
useMixin(legacyRandom)
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Generate a new random %INPUT_TYPE%, where values are randomly sampled according to a Binomial distribution,
|
||||
with the specified number of trials and probability.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("exponential") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.random.custom"
|
||||
javaOpClass = "RandomExponential"
|
||||
|
||||
val lambda = Arg(FLOATING_POINT, "lambda") { description = "lambda parameter" }
|
||||
Constraint("Must be positive") { lambda gt 0 }
|
||||
useMixin(random)
|
||||
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Generate a new random %INPUT_TYPE%, where values are randomly sampled according to a exponential distribution:
|
||||
P(x) = lambda * exp(-lambda * x)
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("logNormal", normalRandom) {
|
||||
javaOpClass = "LogNormalDistribution"
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Generate a new random %INPUT_TYPE%, where values are randomly sampled according to a Log Normal distribution,
|
||||
i.e., {@code log(x) ~ N(mean, stdev)}
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("normal", normalRandom) {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.random.impl"
|
||||
javaOpClass = "GaussianDistribution"
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Generate a new random %INPUT_TYPE%, where values are randomly sampled according to a Gaussian (normal) distribution,
|
||||
N(mean, stdev)<br>
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("normalTruncated", normalRandom) {
|
||||
javaOpClass = "TruncatedNormalDistribution"
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Generate a new random %INPUT_TYPE%, where values are randomly sampled according to a Gaussian (normal) distribution,
|
||||
N(mean, stdev). However, any values more than 1 standard deviation from the mean are dropped and re-sampled
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("uniform") {
|
||||
javaOpClass = "UniformDistribution"
|
||||
|
||||
Arg(FLOATING_POINT, "min") { description = "Minimum value" }
|
||||
Arg(FLOATING_POINT, "max") { description = "Maximum value." }
|
||||
useMixin(legacyRandom)
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
Generate a new random %INPUT_TYPE%, where values are randomly sampled according to a uniform distribution,
|
||||
U(min,max)
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generated using ExtractFromExisting.kt
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.ops
|
||||
|
||||
import org.nd4j.codegen.api.Language
|
||||
import org.nd4j.codegen.api.doc.DocScope
|
||||
import org.nd4j.codegen.dsl.*
|
||||
import org.nd4j.codegen.api.DataType.*
|
||||
import org.nd4j.codegen.api.LossReduce
|
||||
|
||||
fun SDLoss() = Namespace("Loss"){
|
||||
|
||||
Op("absoluteDifference") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.loss"
|
||||
javaOpClass = "AbsoluteDifferenceLoss"
|
||||
Input(NUMERIC, "label") { description = "Label array" }
|
||||
Input(NUMERIC, "predictions") { description = "Predictions array" }
|
||||
Input(NUMERIC, "weights") { description = "Weights array. May be null. If null, a weight of 1.0 is used" }
|
||||
Arg(LOSS_REDUCE, "lossReduce") { description = "Reduction type for the loss. See LossReduce for more details. Default: LossReduce#MEAN_BY_NONZERO_WEIGHT_COUNT"; defaultValue = LossReduce.MEAN_BY_NONZERO_WEIGHT_COUNT}
|
||||
Output(NUMERIC, "output"){ description = "loss variable" }
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Absolute difference loss: {@code sum_i abs( label[i] - predictions[i] )}
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Op("ctcLoss") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.loss"
|
||||
javaOpClass = "CtcLoss"
|
||||
Input(NUMERIC, "targetLabels") { description = "Label array" }
|
||||
Input(NUMERIC, "logitInput") { description = "Inputs" }
|
||||
Input(NUMERIC, "targetLabelLengths") { description = "Length of the target label" }
|
||||
Input(NUMERIC, "logitInputLengths") { description = "Length of the input"}
|
||||
Arg(INT,"blankIndex") {description = "The index of the blank label"; defaultValue = 0}
|
||||
Output(NUMERIC, "output"){ description = "Ctc loss " }
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
CTC Loss: Connectionist Temporal Classification Loss. See:
|
||||
https://dl.acm.org/citation.cfm?id=1143891
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("cosineDistance") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.loss"
|
||||
javaOpClass = "CosineDistanceLoss"
|
||||
Input(NUMERIC, "label") { description = "Label array" }
|
||||
Input(NUMERIC, "predictions") { description = "Predictions array" }
|
||||
Input(NUMERIC, "weights") { description = "Weights array. May be null. If null, a weight of 1.0 is use" }
|
||||
Arg(LOSS_REDUCE, "lossReduce") { description = "Reduction type for the loss. See LossReduce for more details. Default: LossReduce#MEAN_BY_NONZERO_WEIGHT_COUNT"; defaultValue = LossReduce.MEAN_BY_NONZERO_WEIGHT_COUNT}
|
||||
Arg(INT, "dimension") { description = "Dimension to perform the cosine distance over" }
|
||||
Output(NUMERIC, "output"){ description = "Cosine distance loss " }
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Cosine distance loss: {@code 1 - cosineSimilarity(x,y)} or {@code 1 - sum_i label[i] * prediction[i]}, which is
|
||||
equivalent to cosine distance when both the predictions and labels are normalized.<br>
|
||||
<b>Note</b>: This loss function assumes that both the predictions and labels are normalized to have unit l2 norm.
|
||||
If this is not the case, you should normalize them first by dividing by norm2(String, SDVariable, boolean, int...)
|
||||
along the cosine distance dimension (with keepDims=true).
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("hingeLoss") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.loss"
|
||||
javaOpClass = "HingeLoss"
|
||||
Input(NUMERIC, "label") { description = "Label array. Each value should be 0.0 or 1.0 (internally -1 to 1 is used)" }
|
||||
Input(NUMERIC, "predictions") { description = "Predictions array" }
|
||||
Input(NUMERIC, "weights") { description = "Weights array. May be null. If null, a weight of 1.0 is used" }
|
||||
Arg(LOSS_REDUCE, "lossReduce") { description = "Reduction type for the loss. See LossReduce for more details. Default: LossReduce#MEAN_BY_NONZERO_WEIGHT_COUNT"; defaultValue = LossReduce.MEAN_BY_NONZERO_WEIGHT_COUNT}
|
||||
Output(NUMERIC, "output"){ description = "Loss variable" }
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Hinge loss: a loss function used for training classifiers.
|
||||
Implements {@code L = max(0, 1 - t * predictions)} where t is the label values after internally converting to {-1,1}
|
||||
from the user specified {0,1}. Note that Labels should be provided with values {0,1}.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Op("huberLoss") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.loss"
|
||||
javaOpClass = "HuberLoss"
|
||||
Input(NUMERIC, "label") { description = "Label array" }
|
||||
Input(NUMERIC, "predictions") { description = "Predictions array" }
|
||||
Input(NUMERIC, "weights") { description = "Weights array. May be null. If null, a weight of 1.0 is used" }
|
||||
Arg(LOSS_REDUCE, "lossReduce") { description = "Reduction type for the loss. See LossReduce for more details. Default: LossReduce#MEAN_BY_NONZERO_WEIGHT_COUNT"; defaultValue = LossReduce.MEAN_BY_NONZERO_WEIGHT_COUNT}
|
||||
Arg(FLOATING_POINT, "delta") { description = "Loss function delta value" }
|
||||
Output(NUMERIC, "output"){ description = "Huber loss" }
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Huber loss function, used for robust regression. It is similar both squared error loss and absolute difference loss,
|
||||
though is less sensitive to outliers than squared error.<br>
|
||||
Huber loss implements:
|
||||
<pre>
|
||||
{@code L = 0.5 * (label[i] - predictions[i])^2 if abs(label[i] - predictions[i]) < delta}
|
||||
{@code L = delta * abs(label[i] - predictions[i]) - 0.5 * delta^2 otherwise}
|
||||
</pre>
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("l2Loss") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.loss"
|
||||
javaOpClass = "L2Loss"
|
||||
Input(NUMERIC, "var") { description = "Variable to calculate L2 loss of" }
|
||||
Output(NUMERIC, "output"){ description = "L2 loss" }
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
L2 loss: 1/2 * sum(x^2)
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("logLoss") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.loss"
|
||||
javaOpClass = "LogLoss"
|
||||
Input(NUMERIC, "label") { description = "Label array" }
|
||||
Input(NUMERIC, "predictions") { description = "Predictions array" }
|
||||
Input(NUMERIC, "weights") { description = "Weights array. May be null. If null, a weight of 1.0 is used"; defaultValue = null }
|
||||
Arg(LOSS_REDUCE, "lossReduce") { description = "Reduction type for the loss. See LossReduce for more details. Default: LossReduce#MEAN_BY_NONZERO_WEIGHT_COUNT"; defaultValue = LossReduce.MEAN_BY_NONZERO_WEIGHT_COUNT}
|
||||
Arg(FLOATING_POINT, "epsilon") { description = "epsilon"; defaultValue = 0.0 }
|
||||
Output(NUMERIC, "output"){ description = "Log loss " }
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Log loss, i.e., binary cross entropy loss, usually used for binary multi-label classification. Implements:
|
||||
{@code -1/numExamples * sum_i (labels[i] * log(predictions[i] + epsilon) + (1-labels[i]) * log(1-predictions[i] + epsilon))}
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("logPoisson") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.loss"
|
||||
javaOpClass = "LogPoissonLoss"
|
||||
Input(NUMERIC, "label") { description = "Label array. Each value should be 0.0 or 1.0" }
|
||||
Input(NUMERIC, "predictions") { description = "Predictions array (has to be log(x) of actual predictions)" }
|
||||
Input(NUMERIC, "weights") { description = "Weights array. May be null. If null, a weight of 1.0 is used" }
|
||||
Arg(LOSS_REDUCE, "lossReduce") { description = "Reduction type for the loss. See LossReduce for more details. Default: LossReduce#MEAN_BY_NONZERO_WEIGHT_COUNT"; defaultValue = LossReduce.MEAN_BY_NONZERO_WEIGHT_COUNT}
|
||||
Arg(BOOL, "full") {description = "Boolean flag. true for logPoissonFull, false for logPoisson"}
|
||||
Output(NUMERIC, "output"){ description = "Loss variable" }
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Log poisson loss: a loss function used for training classifiers.
|
||||
Implements {@code L = exp(c) - z * c} where c is log(predictions) and z is labels.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
// logPoissonFull is not implemented. Simply a moniker for logPoisson with full = true
|
||||
|
||||
Op("meanPairwiseSquaredError") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.loss"
|
||||
javaOpClass = "MeanPairwiseSquaredErrorLoss"
|
||||
Input(NUMERIC, "label") { description = "Label array" }
|
||||
Input(NUMERIC, "predictions") { description = "Predictions array" }
|
||||
Input(NUMERIC, "weights") { description = "Weights array. May be null. If null, a weight of 1.0 is used. Must be either null, scalar, or have shape [batchSize]" }
|
||||
Arg(LOSS_REDUCE, "lossReduce") { description = "Reduction type for the loss. See LossReduce for more details. Default: LossReduce#MEAN_BY_NONZERO_WEIGHT_COUNT"; defaultValue = LossReduce.MEAN_BY_NONZERO_WEIGHT_COUNT}
|
||||
Output(NUMERIC, "output"){ description = "Loss variable, scalar output" }
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Mean pairwise squared error.<br>
|
||||
MPWSE loss calculates the difference between pairs of consecutive elements in the predictions and labels arrays.
|
||||
For example, if predictions = [p0, p1, p2] and labels are [l0, l1, l2] then MPWSE is:
|
||||
{@code [((p0-p1) - (l0-l1))^2 + ((p0-p2) - (l0-l2))^2 + ((p1-p2) - (l1-l2))^2] / 3}<br>
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("meanSquaredError") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.loss"
|
||||
javaOpClass = "MeanSquaredErrorLoss"
|
||||
Input(NUMERIC, "label") { description = "Label array" }
|
||||
Input(NUMERIC, "predictions") { description = "Predictions array" }
|
||||
Input(NUMERIC, "weights") { description = "Weights array. May be null. If null, a weight of 1.0 is used" }
|
||||
Arg(LOSS_REDUCE, "lossReduce") { description = "Reduction type for the loss. See LossReduce for more details. Default: LossReduce#MEAN_BY_NONZERO_WEIGHT_COUNT"; defaultValue = LossReduce.MEAN_BY_NONZERO_WEIGHT_COUNT}
|
||||
Output(NUMERIC, "output"){ description = "Loss variable" }
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Mean squared error loss function. Implements {@code (label[i] - prediction[i])^2} - i.e., squared error on a per-element basis.
|
||||
When averaged (using LossReduce#MEAN_BY_WEIGHT or LossReduce#MEAN_BY_NONZERO_WEIGHT_COUNT (the default))
|
||||
this is the mean squared error loss function.
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("sigmoidCrossEntropy") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.loss"
|
||||
javaOpClass = "SigmoidCrossEntropyLoss"
|
||||
Input(NUMERIC, "label") { description = "Label array" }
|
||||
Input(NUMERIC, "predictionLogits") { description = "Predictions array" }
|
||||
Input(NUMERIC, "weights") { description = "Weights array. May be null. If null, a weight of 1.0 is used" }
|
||||
Arg(LOSS_REDUCE, "lossReduce") { description = "Reduction type for the loss. See LossReduce for more details. Default: LossReduce#MEAN_BY_NONZERO_WEIGHT_COUNT"; defaultValue = LossReduce.MEAN_BY_NONZERO_WEIGHT_COUNT}
|
||||
Arg(FLOATING_POINT, "labelSmoothing") { description = "Label smoothing value. Default value: 0"; defaultValue = 0.0}
|
||||
Output(NUMERIC, "output"){ description = "Loss variable" }
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Sigmoid cross entropy: applies the sigmoid activation function on the input logits (input "pre-sigmoid preductions")
|
||||
and implements the binary cross entropy loss function. This implementation is numerically more stable than using
|
||||
standard (but separate) sigmoid activation function and log loss (binary cross entropy) loss function.<br>
|
||||
Implements:
|
||||
{@code -1/numExamples * sum_i (labels[i] * log(sigmoid(logits[i])) + (1-labels[i]) * log(1-sigmoid(logits[i])))}
|
||||
though this is done in a mathematically equivalent but more numerical stable form.<br>
|
||||
<br>
|
||||
When label smoothing is > 0, the following label smoothing is used:<br>
|
||||
<pre>
|
||||
{@code numClasses = labels.size(1);
|
||||
label = (1.0 - labelSmoothing) * label + 0.5 * labelSmoothing}
|
||||
</pre>
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("softmaxCrossEntropy") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.loss"
|
||||
javaOpClass = "SoftmaxCrossEntropyLoss"
|
||||
Input(NUMERIC, "oneHotLabels") { description = "Label array. Should be one-hot per example and same shape as predictions (for example, [mb, nOut])" }
|
||||
Input(NUMERIC, "logitPredictions") { description = "Predictions array (pre-softmax)" }
|
||||
Input(NUMERIC, "weights") { description = "Weights array. May be null. If null, a weight of 1.0 is used" }
|
||||
Arg(LOSS_REDUCE, "lossReduce") { description = "Reduction type for the loss. See LossReduce for more details. Default: LossReduce#MEAN_BY_NONZERO_WEIGHT_COUNT"; defaultValue = LossReduce.MEAN_BY_NONZERO_WEIGHT_COUNT}
|
||||
Arg(FLOATING_POINT, "labelSmoothing") { description = "Label smoothing value. Default value: 0"; defaultValue = 0.0}
|
||||
Output(NUMERIC, "output"){ description = "Loss variable" }
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
Applies the softmax activation function to the input, then implement multi-class cross entropy:<br>
|
||||
{@code -sum_classes label[i] * log(p[c])} where {@code p = softmax(logits)}<br>
|
||||
If LossReduce#NONE is used, returned shape is [numExamples] out for [numExamples, numClasses] predicitons/labels;
|
||||
otherwise, the output is a scalar.<br>
|
||||
<p>
|
||||
When label smoothing is > 0, the following label smoothing is used:<br>
|
||||
<pre>
|
||||
{@code numClasses = labels.size(1);
|
||||
oneHotLabel = (1.0 - labelSmoothing) * oneHotLabels + labelSmoothing/numClasses}
|
||||
</pre>
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Op("sparseSoftmaxCrossEntropy") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.loss"
|
||||
javaOpClass = "SparseSoftmaxCrossEntropyLossWithLogits"
|
||||
Input(NUMERIC, "logits") { description = "Logits array (\"pre-softmax activations\")" }
|
||||
Input(INT, "labels") { description = "Labels array. Must be an integer type." }
|
||||
Output(NUMERIC, "output"){ description = "Softmax cross entropy" }
|
||||
Doc(Language.ANY, DocScope.ALL){
|
||||
"""
|
||||
As per softmaxCrossEntropy(String, SDVariable, SDVariable, LossReduce) but the labels variable
|
||||
is represented as an integer array instead of the equivalent one-hot array.<br>
|
||||
i.e., if logits are rank N, then labels have rank N-1
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<!--
|
||||
~ /* ******************************************************************************
|
||||
~ *
|
||||
~ *
|
||||
~ * This program and the accompanying materials are made available under the
|
||||
~ * terms of the Apache License, Version 2.0 which is available at
|
||||
~ * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
~ *
|
||||
~ * See the NOTICE file distributed with this work for additional
|
||||
~ * information regarding copyright ownership.
|
||||
~ * Unless required by applicable law or agreed to in writing, software
|
||||
~ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
~ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
~ * License for the specific language governing permissions and limitations
|
||||
~ * under the License.
|
||||
~ *
|
||||
~ * SPDX-License-Identifier: Apache-2.0
|
||||
~ ******************************************************************************/
|
||||
-->
|
||||
|
||||
<configuration>
|
||||
|
||||
|
||||
|
||||
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
|
||||
<file>logs/application.log</file>
|
||||
<encoder>
|
||||
<pattern> %logger{15} - %message%n%xException{5}
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern> %logger{15} - %message%n%xException{5}
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.apache.catalina.core" level="DEBUG" />
|
||||
<logger name="org.springframework" level="WARN" />
|
||||
<logger name="org.nd4j" level="INFO" />
|
||||
<logger name="org.deeplearning4j" level="INFO" />
|
||||
|
||||
|
||||
<root level="ERROR">
|
||||
<appender-ref ref="STDOUT" />
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
@@ -0,0 +1,54 @@
|
||||
{ "name" : "math",
|
||||
|
||||
"include" : [
|
||||
|
||||
],
|
||||
|
||||
"ops": [
|
||||
|
||||
{
|
||||
"opName" : "BaseArithmeticOp",
|
||||
"isAbstract" : true,
|
||||
"javaPackage" : "org.nd4j.linalg.api.ops.impl.transforms.pairwise.arithmetic",
|
||||
"inputs" : [ {
|
||||
"name" : "x",
|
||||
"description" : "First input to %OPNAME%",
|
||||
"constraints" : ["T"]
|
||||
}, {
|
||||
"name" : "y",
|
||||
"description" : "Second input to %OPNAME%",
|
||||
"constraints" : ["T"]
|
||||
} ],
|
||||
"outputs" : [ {
|
||||
"name" : "z",
|
||||
"description" : "Output array after executing %OPNAME% on inputs"
|
||||
} ],
|
||||
"args" : null,
|
||||
"constraints" : {
|
||||
"T": {
|
||||
"type": "allowed_dtype",
|
||||
"values": [
|
||||
"NUMERICAL"
|
||||
]
|
||||
}
|
||||
},
|
||||
"doc" : [ {
|
||||
"scope" : "ALL",
|
||||
"language" : "ANY",
|
||||
"text" : "%OPNAME% op doc text that will appear everywhere - classes, constructors, op creators"
|
||||
} ]
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"opName" : "Add",
|
||||
"libnd4jOpName" : "add",
|
||||
"extendsOp" : "BaseArithmeticOp"
|
||||
},
|
||||
|
||||
{
|
||||
"opName" : "Sub",
|
||||
"libnd4jOpName" : "sub",
|
||||
"extendsOp" : "BaseArithmeticOp"
|
||||
}
|
||||
]}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
package org.nd4j.codegen.dsl;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.nd4j.codegen.impl.java.DocsGenerator;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
@DisplayName("Docs Generator Test")
|
||||
class DocsGeneratorTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test J Dto MD Adapter")
|
||||
void testJDtoMDAdapter() {
|
||||
String original = "{@code %INPUT_TYPE% eye = eye(3,2)\n" + " eye:\n" + " [ 1, 0]\n" + " [ 0, 1]\n" + " [ 0, 0]}";
|
||||
String expected = "{ INDArray eye = eye(3,2)\n" + " eye:\n" + " [ 1, 0]\n" + " [ 0, 1]\n" + " [ 0, 0]}";
|
||||
DocsGenerator.JavaDocToMDAdapter adapter = new DocsGenerator.JavaDocToMDAdapter(original);
|
||||
String out = adapter.filter("@code", StringUtils.EMPTY).filter("%INPUT_TYPE%", "INDArray").toString();
|
||||
assertEquals(out, expected);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.dsl;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.nd4j.codegen.api.NamespaceOps;
|
||||
import org.nd4j.codegen.impl.java.Nd4jNamespaceGenerator;
|
||||
import org.nd4j.codegen.ops.RNNKt;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
class TestGeneration {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@TempDir
|
||||
public File testDir;
|
||||
|
||||
@Test
|
||||
void test() throws Exception {
|
||||
File f = testDir;
|
||||
|
||||
// List<NamespaceOps> list = Arrays.asList(BitwiseKt.Bitwise(), RandomKt.Random());
|
||||
List<NamespaceOps> list = Arrays.asList(RNNKt.SDRNN());
|
||||
|
||||
for(NamespaceOps ops : list) {
|
||||
Nd4jNamespaceGenerator.generate(ops, null, f, ops.getName() + ".java", "org.nd4j.linalg.factory", StringUtils.EMPTY);
|
||||
}
|
||||
|
||||
File[] files = f.listFiles();
|
||||
Iterator<File> iter = FileUtils.iterateFiles(f, null, true);
|
||||
if(files != null) {
|
||||
while(iter.hasNext()){
|
||||
File file = iter.next();
|
||||
if(file.isDirectory())
|
||||
continue;
|
||||
System.out.println(FileUtils.readFileToString(file, StandardCharsets.UTF_8));
|
||||
System.out.println("\n\n================\n\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.dsl
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.nd4j.codegen.api.DataType.FLOATING_POINT
|
||||
import org.nd4j.codegen.api.Language
|
||||
import org.nd4j.codegen.api.doc.DocScope
|
||||
|
||||
class ConfigTest {
|
||||
@Test
|
||||
fun allGood(){
|
||||
Namespace("RNN"){
|
||||
val sruWeights = Config("SRUWeights"){
|
||||
Input(FLOATING_POINT, "weights"){ description = "Weights, with shape [inSize, 3*inSize]" }
|
||||
Input(FLOATING_POINT, "bias"){ description = "Biases, with shape [2*inSize]" }
|
||||
}
|
||||
|
||||
Op("SRU"){
|
||||
Input(FLOATING_POINT, "x"){ description = "..." }
|
||||
Input(FLOATING_POINT, "initialC"){ description = "..." }
|
||||
Input(FLOATING_POINT, "mask"){ description = "..." }
|
||||
|
||||
useConfig(sruWeights)
|
||||
|
||||
Output(FLOATING_POINT, "out"){ description = "..." }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){ "some doc" }
|
||||
}
|
||||
|
||||
Op("SRUCell"){
|
||||
val x = Input(FLOATING_POINT, "x"){ description = "..." }
|
||||
val cLast = Input(FLOATING_POINT, "cLast"){ description = "..." }
|
||||
|
||||
val conf = useConfig(sruWeights)
|
||||
|
||||
Output(FLOATING_POINT, "out"){ description = "..." }
|
||||
|
||||
// Just for demonstration purposes
|
||||
Signature(x, cLast, conf)
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL){ "some doc" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.dsl
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.nd4j.codegen.api.Arg
|
||||
import org.nd4j.codegen.api.DataType
|
||||
import org.nd4j.codegen.api.Expression
|
||||
import org.nd4j.codegen.api.Input
|
||||
import org.nd4j.codegen.impl.java.JavaConstraintCodeGenerator
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
|
||||
class ConstraintTest {
|
||||
|
||||
|
||||
private fun buildConstraint(block: ConstraintBuilder.() -> Expression): Expression {
|
||||
return ConstraintBuilder().block()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simpleConstraintTest() {
|
||||
val expected = "x.rank() == 3"
|
||||
val input = Input(name = "x", type = DataType.INT)
|
||||
val constraint = buildConstraint { input.rank() eq 3 }
|
||||
val out = JavaConstraintCodeGenerator().generateExpression(constraint)
|
||||
assertEquals(expected, out)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simple2ConstraintTest() {
|
||||
val expected = "(x.rank() == 3) && (x.sizeAt(2) >= 7)"
|
||||
val input = Input(name = "x", type = DataType.INT)
|
||||
val constraint = buildConstraint { (input.rank() eq 3) and (input.sizeAt(2) gte 7) }
|
||||
val out = JavaConstraintCodeGenerator().generateExpression(constraint)
|
||||
assertEquals(expected, out)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simple3ConstraintTest() {
|
||||
val expected = "((x.rank() == 3) || (x.sizeAt(2) >= 7)) || (x.sizeAt(4) < 5)"
|
||||
val input = Input(name = "x", type = DataType.INT)
|
||||
val constraint = buildConstraint { some(
|
||||
input.rank() eq 3,
|
||||
input.sizeAt(2) gte 7,
|
||||
input.sizeAt(4) lt 5
|
||||
) }
|
||||
val out = JavaConstraintCodeGenerator().generateExpression(constraint)
|
||||
assertEquals(expected, out)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun complexConstraintTest() {
|
||||
val expected = "(x.rank() == 3) == false"
|
||||
val input = Input(name = "x", type = DataType.INT)
|
||||
val constraint = buildConstraint { not(input.rank() eq 3) }
|
||||
val out = JavaConstraintCodeGenerator().generateExpression(constraint)
|
||||
assertEquals(expected, out)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun argConstraintTest() {
|
||||
val expected = "(x.rank() == rank) == false"
|
||||
val arg = Arg(name = "rank", type = DataType.NUMERIC)
|
||||
val input = Input(name = "x", type = DataType.INT)
|
||||
val constraint = buildConstraint { not(input.rank() eq arg) }
|
||||
val out = JavaConstraintCodeGenerator().generateExpression(constraint)
|
||||
assertEquals(expected, out)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun specificConstraintTest(){
|
||||
val expected = "isSameType(x, y, z)"
|
||||
val x = Input(name = "x", type = DataType.INT)
|
||||
val y = Input(name = "y", type = DataType.INT)
|
||||
val z = Input(name = "z", type = DataType.INT)
|
||||
val constraint = buildConstraint { sameType(x, y, z) }
|
||||
val out = JavaConstraintCodeGenerator().generateExpression(constraint)
|
||||
assertEquals(expected, out)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.dsl
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import org.nd4j.codegen.api.DataType
|
||||
|
||||
class NamespaceInvariantTest {
|
||||
@Test
|
||||
fun checkForUnusedConfigs(){
|
||||
val thrown = assertThrows<IllegalStateException> {
|
||||
Namespace("RNN"){
|
||||
Config("SRUWeights"){
|
||||
Input(DataType.FLOATING_POINT, "weights"){ description = "Weights, with shape [inSize, 3*inSize]" }
|
||||
Input(DataType.FLOATING_POINT, "bias"){ description = "Biases, with shape [2*inSize]" }
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEquals("Found unused configs: SRUWeights", thrown.message)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.dsl
|
||||
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.nd4j.codegen.api.AtLeast
|
||||
import org.nd4j.codegen.api.AtMost
|
||||
import org.nd4j.codegen.api.DataType.*
|
||||
import org.nd4j.codegen.api.Exactly
|
||||
import org.nd4j.codegen.api.Language
|
||||
import org.nd4j.codegen.api.doc.DocScope
|
||||
import org.nd4j.codegen.impl.java.JavaPoetGenerator
|
||||
import java.io.File
|
||||
import java.nio.charset.StandardCharsets
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class OpBuilderTest {
|
||||
private var testDir = createTempDir()
|
||||
|
||||
@Test
|
||||
fun opBuilderTest() {
|
||||
val outDir = testDir
|
||||
|
||||
val mathNs = Namespace("math") {
|
||||
val config = Config("bla"){
|
||||
val a = Input(NUMERIC, "a") { description = "This is A!"}
|
||||
Input(NUMERIC, "c") { count = AtLeast(1); description = "This is C!"}
|
||||
Input(NUMERIC, "e") { defaultValue = a}
|
||||
val b = Arg(NUMERIC, "b") { description = "This is B!"}
|
||||
Arg(NUMERIC, "d") { count = AtMost(7); description = "This is D!"}
|
||||
Arg(NUMERIC, "f") { defaultValue = 12}
|
||||
|
||||
Constraint("Some constraint"){
|
||||
a.isScalar()
|
||||
}
|
||||
Constraint("Some different constraint"){
|
||||
b eq 7
|
||||
}
|
||||
|
||||
Doc(Language.JAVA, DocScope.ALL){
|
||||
"This is some config documentation"
|
||||
}
|
||||
}
|
||||
Op("add") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.pairwise.arithmetic"
|
||||
|
||||
val x = Input(NUMERIC, "x") { description = "First input to add" }
|
||||
Input(NUMERIC,"y") { count = AtLeast(1); description = "Second input to add"; defaultValue = x }
|
||||
Arg(INT,"shape") { count = AtLeast(1); description = "shape"; defaultValue = intArrayOf(1,2,3) }
|
||||
|
||||
|
||||
Output(NUMERIC, "z") { description = "Output (x+y)" }
|
||||
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"""
|
||||
(From AddOp) Add op doc text that will appear everywhere - classes, constructors, op creators
|
||||
""".trimIndent()
|
||||
}
|
||||
Doc(Language.ANY, DocScope.CLASS_DOC_ONLY) {
|
||||
"Add op doc text that will appear in all class docs (javadoc etc)"
|
||||
}
|
||||
Doc(Language.ANY, DocScope.CONSTRUCTORS_ONLY) {
|
||||
"Add op doc text for constructors only"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val baseArithmeticOp = Mixin("BaseArithmeticOp") {
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.pairwise.arithmetic"
|
||||
|
||||
Input(NUMERIC,"x") { count = Exactly(1); description = "First operand to %OPNAME%" }
|
||||
Input(NUMERIC,"y") { count = Exactly(1); description = "Second operand" }
|
||||
|
||||
|
||||
Output(NUMERIC,"z") { description = "Output" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"(From BaseArithmeticOp) op doc text that will appear everywhere - classes, constructors, op creators"
|
||||
}
|
||||
Doc(Language.ANY, DocScope.CLASS_DOC_ONLY) {
|
||||
"op doc text that will appear in all class docs (javadoc etc)"
|
||||
}
|
||||
Doc(Language.ANY, DocScope.CONSTRUCTORS_ONLY) {
|
||||
"op doc text for constructors only"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Op("sub", extends = baseArithmeticOp)
|
||||
|
||||
Op("mul", extends = baseArithmeticOp)
|
||||
|
||||
Op("rsub", extends = baseArithmeticOp) {
|
||||
isAbstract = false
|
||||
javaPackage = "org.nd4j.some.other.package"
|
||||
Doc(Language.ANY, DocScope.CREATORS_ONLY) {
|
||||
"(From rsub) This doc section will appear only in creator method for %OPNAME%"
|
||||
}
|
||||
}
|
||||
|
||||
Op("div"){
|
||||
javaPackage = "org.nd4j.linalg.api.ops.impl.transforms.pairwise.arithmetic"
|
||||
|
||||
val x = Input(NUMERIC,"x") { description = "First operand to div" }
|
||||
val y = Input(NUMERIC,"y") { description = "Second operand to div" }
|
||||
val idx = Arg(INT, "idx") { description = "Some kind of Index" }
|
||||
Constraint("Compatible Rank"){
|
||||
x.rank() eq idx
|
||||
}
|
||||
|
||||
Constraint("Compatible Shapes"){
|
||||
sameShape(x,y)
|
||||
}
|
||||
|
||||
|
||||
Output(NUMERIC,"z") { description = "Output" }
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"op doc text that will appear everywhere - classes, constructors, op creators"
|
||||
}
|
||||
}
|
||||
|
||||
Op("zfoo"){
|
||||
javaPackage = "bar"
|
||||
javaOpClass = "FooBarOp"
|
||||
val x = Input(NUMERIC,"x") { description = "First operand to %OPNAME% (%INPUT_TYPE%)" }
|
||||
val y = Input(NUMERIC,"y") { description = "Second operand to div" }
|
||||
val z = Arg(ENUM, "fooMode"){ description = "Something or other"; possibleValues = listOf("SOME", "value", "Spam", "eGGs")}
|
||||
val bla = useConfig(config)
|
||||
|
||||
Constraint("foo bar"){
|
||||
x.sizeAt(7) eq 7 and y.isScalar()
|
||||
}
|
||||
|
||||
Doc(Language.ANY, DocScope.ALL) {
|
||||
"op doc text that will appear everywhere - classes, constructors, op creators"
|
||||
}
|
||||
|
||||
Signature(x, y, z, bla)
|
||||
}
|
||||
}
|
||||
|
||||
val generator = JavaPoetGenerator()
|
||||
generator.generateNamespaceNd4j(mathNs, null, outDir, "Nd4jMath")
|
||||
val exp = File(outDir, "org/nd4j/linalg/factory/ops/Nd4jMath.java")
|
||||
assertTrue(exp.isFile)
|
||||
|
||||
println(FileUtils.readFileToString(exp, StandardCharsets.UTF_8))
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,833 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.dsl
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertSame
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import org.nd4j.codegen.api.*
|
||||
import org.nd4j.codegen.api.doc.DocScope
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotSame
|
||||
|
||||
|
||||
class OpInvariantTest {
|
||||
|
||||
@Test
|
||||
fun opMustBeDocumented() {
|
||||
val thrown = assertThrows<java.lang.IllegalStateException> {
|
||||
Namespace("math") {
|
||||
Op("foo") {}
|
||||
}
|
||||
}
|
||||
assertEquals("foo: Ops must be documented!", thrown.message)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun opMustBeDocumentedAndNotEmpty() {
|
||||
val thrown = assertThrows<java.lang.IllegalStateException> {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "" }
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEquals("foo: Ops must be documented!", thrown.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opMustBeDocumentedWithDoc() {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureMustCoverAllParameters() {
|
||||
val thrown = assertThrows<java.lang.IllegalStateException> {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Input(DataType.NUMERIC, "y")
|
||||
|
||||
Signature(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEquals("foo: Signature(x) does not cover all parameters! Missing: y", thrown.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureMustCoverAllParameters2() {
|
||||
val thrown = assertThrows<java.lang.IllegalStateException> {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.NUMERIC, "y")
|
||||
|
||||
Signature(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals("foo: Signature(x) does not cover all parameters! Missing: y", thrown.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureMustCoverAllParametersWithoutDefaults() {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.NUMERIC, "y") {
|
||||
defaultValue = 7
|
||||
}
|
||||
|
||||
Signature(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureMustTakeEachParameterOnlyOnce() {
|
||||
val thrown = assertThrows<java.lang.IllegalArgumentException> {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.NUMERIC, "y")
|
||||
|
||||
Signature(x, x, x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals("A parameter may not be used twice in a signature!", thrown.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureMustAllowOutputs() {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.NUMERIC, "y") {
|
||||
defaultValue = 7
|
||||
}
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
|
||||
Signature(out, x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureMustAllowOutputsOnlyOnce() {
|
||||
val thrown = assertThrows<java.lang.IllegalArgumentException> {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.NUMERIC, "y") {
|
||||
defaultValue = 7
|
||||
}
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
|
||||
Signature(out, x, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals("A parameter may not be used twice in a signature!", thrown.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureDefaultValueMustHaveCorrectShape() {
|
||||
val thrown = assertThrows<java.lang.IllegalArgumentException> {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.INT, "y") {
|
||||
defaultValue = x.shape()
|
||||
}
|
||||
|
||||
Signature(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals("Illegal default value for Arg(INT, y). Got x.shape() (org.nd4j.codegen.api.TensorShapeValue)", thrown.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureDefaultValue() {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.INT, "y") {
|
||||
defaultValue = 2
|
||||
}
|
||||
|
||||
Signature(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureDefaultValueMustHaveCorrectDataType() {
|
||||
val thrown = assertThrows<java.lang.IllegalArgumentException> {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.INT, "y") {
|
||||
defaultValue = 1.7
|
||||
}
|
||||
|
||||
Signature(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals("Illegal default value for Arg(INT, y). Got 1.7 (java.lang.Double)", thrown.message)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun opSignatureDefaultInputReference() {
|
||||
val thrown = assertThrows<java.lang.IllegalStateException> {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val z = Input(DataType.NUMERIC, "z")
|
||||
val y = Arg(DataType.INT, "y") {
|
||||
count = AtLeast(1)
|
||||
defaultValue = z.shape()
|
||||
}
|
||||
|
||||
Signature(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals("foo: Signature(x) does not cover all parameters! Missing: z, y", thrown.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureDefaultOutputReference() {
|
||||
val thrown = assertThrows<java.lang.IllegalStateException> {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.INT, "y") {
|
||||
count = AtLeast(1)
|
||||
defaultValue = out.shape()
|
||||
}
|
||||
|
||||
Signature(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals("foo: Signature(x) does not cover all parameters! Missing: y", thrown.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureDefaultWithOutputReference() {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.INT, "y") {
|
||||
count = AtLeast(1)
|
||||
defaultValue = out.shape()
|
||||
}
|
||||
|
||||
Signature(out, x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureDefaultReferenceChain() {
|
||||
val thrown = assertThrows<java.lang.IllegalStateException> {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val z = Input(DataType.NUMERIC, "z")
|
||||
val u = Input(DataType.NUMERIC, "u") { defaultValue = z }
|
||||
val v = Input(DataType.NUMERIC, "v") { defaultValue = u }
|
||||
val y = Arg(DataType.INT, "y") {
|
||||
count = AtLeast(1)
|
||||
defaultValue = v.shape()
|
||||
}
|
||||
|
||||
Signature(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals("foo: Signature(x) does not cover all parameters! Missing: z, u, v, y", thrown.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureDefaultReferenceChainWorking() {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val z = Input(DataType.NUMERIC, "z") { defaultValue = x }
|
||||
val u = Input(DataType.NUMERIC, "u") { defaultValue = z }
|
||||
val v = Input(DataType.NUMERIC, "v") { defaultValue = u }
|
||||
val y = Arg(DataType.INT, "y") {
|
||||
count = AtLeast(1)
|
||||
defaultValue = v.shape()
|
||||
}
|
||||
|
||||
Signature(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureShorthandAllParams() {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val z = Input(DataType.NUMERIC, "z") { defaultValue = x }
|
||||
val u = Input(DataType.NUMERIC, "u") { defaultValue = z }
|
||||
val v = Input(DataType.NUMERIC, "v") { defaultValue = u }
|
||||
val y = Arg(DataType.INT, "y") {
|
||||
count = AtLeast(1)
|
||||
defaultValue = v.shape()
|
||||
}
|
||||
|
||||
AllParamSignature()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureNullDefaults() {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.INT, "y") {
|
||||
count = AtLeast(1)
|
||||
defaultValue = null
|
||||
}
|
||||
|
||||
AllDefaultsSignature()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureNullDefaultsForInputs() {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x") { defaultValue = null }
|
||||
|
||||
AllDefaultsSignature()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureShorthandDefaultParams() {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val z = Input(DataType.NUMERIC, "z") { defaultValue = x }
|
||||
val u = Input(DataType.NUMERIC, "u") { defaultValue = z }
|
||||
val v = Input(DataType.NUMERIC, "v") { defaultValue = u }
|
||||
val y = Arg(DataType.INT, "y") {
|
||||
count = AtLeast(1)
|
||||
defaultValue = v.shape()
|
||||
}
|
||||
|
||||
AllDefaultsSignature()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureSupportsArrayDefaults() {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.INT, "y") { count = AtLeast(0); defaultValue = intArrayOf() }
|
||||
val z = Arg(DataType.FLOATING_POINT, "z") { count = Range(2, 5); defaultValue = doubleArrayOf(1.0, 2.0, 3.0) }
|
||||
val a = Arg(DataType.BOOL, "a") { count = AtLeast(1); defaultValue = booleanArrayOf(true) }
|
||||
|
||||
AllDefaultsSignature()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureSupportsArrayDefaultsAtLeast() {
|
||||
val thrown = assertThrows<java.lang.IllegalArgumentException> {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
Output(DataType.NUMERIC, "out")
|
||||
Input(DataType.NUMERIC, "x")
|
||||
Arg(DataType.INT, "y") { count = AtLeast(1); defaultValue = intArrayOf() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals("Illegal default value for Arg(INT, y){ count = AtLeast(min=1) }. Got [] ([I)", thrown.message)
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureSupportsArrayDefaultsAtMost() {
|
||||
val thrown = assertThrows<java.lang.IllegalArgumentException> {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
Output(DataType.NUMERIC, "out")
|
||||
Input(DataType.NUMERIC, "x")
|
||||
Arg(DataType.INT, "y") { count = AtMost(1); defaultValue = intArrayOf(1, 2) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals("Illegal default value for Arg(INT, y){ count = AtMost(max=1) }. Got [1, 2] ([I)", thrown.message)
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureSupportsArrayDefaultsRange() {
|
||||
val thrown = assertThrows<java.lang.IllegalArgumentException> {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
Output(DataType.NUMERIC, "out")
|
||||
Input(DataType.NUMERIC, "x")
|
||||
Arg(DataType.INT, "y") { count = Range(3, 7); defaultValue = intArrayOf() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals("Illegal default value for Arg(INT, y){ count = Range(from=3, to=7) }. Got [] ([I)", thrown.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureSupportsArrayDefaultsExactly() {
|
||||
val thrown = assertThrows<java.lang.IllegalArgumentException> {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
Output(DataType.NUMERIC, "out")
|
||||
Input(DataType.NUMERIC, "x")
|
||||
Arg(DataType.INT, "y") { count = Exactly(7); defaultValue = intArrayOf() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals("Illegal default value for Arg(INT, y){ count = Exactly(count=7) }. Got [] ([I)", thrown.message)
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureHasExpectedNumberOfSignatures() {
|
||||
Namespace("math") {
|
||||
val op = Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.INT, "y") { count = AtLeast(0); defaultValue = intArrayOf() }
|
||||
|
||||
AllParamSignature()
|
||||
AllDefaultsSignature()
|
||||
}
|
||||
|
||||
assertEquals(2, op.signatures.size)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureHasExpectedNumberOfSignaturesWithOutput() {
|
||||
Namespace("math") {
|
||||
val op = Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.INT, "y") { count = AtLeast(0); defaultValue = intArrayOf() }
|
||||
|
||||
AllParamSignature(true)
|
||||
AllDefaultsSignature(true)
|
||||
}
|
||||
|
||||
assertEquals(4, op.signatures.size)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureHasExpectedNumberOfSignaturesNoDefaults() {
|
||||
Namespace("math") {
|
||||
val op = Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.INT, "y") { count = AtLeast(0); }
|
||||
|
||||
AllParamSignature()
|
||||
AllDefaultsSignature()
|
||||
}
|
||||
|
||||
assertEquals(1, op.signatures.size)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opSignatureHasExpectedNumberOfSignaturesWithOutputNoDefaults() {
|
||||
Namespace("math") {
|
||||
val op = Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.INT, "y") { count = AtLeast(0); }
|
||||
|
||||
AllParamSignature(true)
|
||||
AllDefaultsSignature(true)
|
||||
}
|
||||
|
||||
assertEquals(2, op.signatures.size)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun argEnum() {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.ENUM, "y") { possibleValues = listOf("FOO", "BAR", "BAZ"); description = "Enums require some docs" }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun argEnumDefaultValue() {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.ENUM, "y") {
|
||||
possibleValues = listOf("FOO", "BAR", "BAZ")
|
||||
defaultValue = "BAZ"
|
||||
description = "Enums require some docs"
|
||||
}
|
||||
|
||||
AllDefaultsSignature()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun argEnumBadDefaultValue() {
|
||||
val thrown = assertThrows<java.lang.IllegalArgumentException> {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.ENUM, "y") {
|
||||
possibleValues = listOf("FOO", "BAR", "BAZ")
|
||||
defaultValue = "SPAM"
|
||||
}
|
||||
|
||||
AllDefaultsSignature()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals("Illegal default value for Arg(ENUM(FOO, BAR, BAZ), y). Got SPAM (java.lang.String)", thrown.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun argEnumEmptyPossibleValues() {
|
||||
val thrown = assertThrows<java.lang.IllegalArgumentException> {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.ENUM, "y") {
|
||||
possibleValues = listOf()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals("Arg(ENUM(null), y): Can not set empty possibleValues.", thrown.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun argEnumBadType() {
|
||||
val thrown = assertThrows<java.lang.IllegalArgumentException> {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.NUMERIC, "y") {
|
||||
possibleValues = listOf("FOO", "BAR", "BAZ")
|
||||
defaultValue = "SPAM"
|
||||
}
|
||||
|
||||
AllDefaultsSignature()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals("Arg(NUMERIC, y): Can not set possibleValues on non ENUM typed Arg.", thrown.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun argEnumBadCount() {
|
||||
val thrown = assertThrows<java.lang.IllegalArgumentException> {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.ENUM, "y") {
|
||||
count = AtLeast(1)
|
||||
possibleValues = listOf("FOO", "BAR", "BAZ")
|
||||
}
|
||||
|
||||
AllDefaultsSignature()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals("Arg(ENUM(null), y): ENUM typed Arg can not be array", thrown.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun argEnumGoodCount() {
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
val y = Arg(DataType.ENUM, "y") {
|
||||
count = Exactly(1)
|
||||
possibleValues = listOf("FOO", "BAR", "BAZ")
|
||||
description = "Enums require some docs"
|
||||
}
|
||||
|
||||
AllDefaultsSignature()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onlyValidParametersAreUsedInSignaturesBadCase() {
|
||||
val thrown = assertThrows<IllegalArgumentException> {
|
||||
val mixin = Mixin("Bar") {
|
||||
Input(DataType.NUMERIC, "a")
|
||||
Arg(DataType.BOOL, "b")
|
||||
}
|
||||
|
||||
Namespace("math") {
|
||||
Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
|
||||
Signature(out, x, mixin.input("a"), mixin.arg("b"))
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEquals("You can only use parameters in a signature that are actually defined in Op(opName=foo, libnd4jOpName=foo, isAbstract=false)! Did you forget to useMixin(...) a mixin?", thrown.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onlyValidParametersAreUsedInSignaturesGoodCase() {
|
||||
val mixin = Mixin("Bar") {
|
||||
Input(DataType.NUMERIC, "a")
|
||||
Arg(DataType.BOOL, "b")
|
||||
}
|
||||
|
||||
Namespace("math") {
|
||||
Op("foo", mixin) {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
val out = Output(DataType.NUMERIC, "out")
|
||||
val x = Input(DataType.NUMERIC, "x")
|
||||
|
||||
Signature(out, x, mixin.input("a"), mixin.arg("b"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun lastMixinDefinitionWins(){
|
||||
val mixin = Mixin("Bar") {
|
||||
Input(DataType.NUMERIC, "a")
|
||||
Arg(DataType.BOOL, "b")
|
||||
}
|
||||
|
||||
Namespace("math") {
|
||||
val op = Op("foo", mixin) {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
Output(DataType.NUMERIC, "out")
|
||||
Input(DataType.NUMERIC, "a") { count=Exactly(1)}
|
||||
}
|
||||
|
||||
assertNotSame(mixin.inputs.find { it.name == "a"}, op.inputs.find { it.name == "a"})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun lastMixinDefinitionWins2(){
|
||||
val mixin = Mixin("Bar") {
|
||||
Input(DataType.NUMERIC, "a")
|
||||
Arg(DataType.BOOL, "b")
|
||||
}
|
||||
|
||||
Namespace("math") {
|
||||
val op = Op("foo") {
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
Output(DataType.NUMERIC, "out")
|
||||
Input(DataType.NUMERIC, "a")
|
||||
useMixin(mixin)
|
||||
}
|
||||
|
||||
assertSame(mixin.inputs.find { it.name == "a"}, op.inputs.find { it.name == "a"})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mixinDoesOnlyOverwritePropertiesIfSetNoneSetCase(){
|
||||
val mixin = Mixin("Bar") {
|
||||
Input(DataType.NUMERIC, "a")
|
||||
Arg(DataType.BOOL, "b")
|
||||
}
|
||||
|
||||
Namespace("math") {
|
||||
val op = Op("foo") {
|
||||
javaPackage = "fooPackage"
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
Output(DataType.NUMERIC, "out")
|
||||
Input(DataType.NUMERIC, "a")
|
||||
useMixin(mixin)
|
||||
}
|
||||
|
||||
assertEquals("fooPackage", op.javaPackage)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mixinDoesOnlyOverwritePropertiesIfSetSetCase(){
|
||||
val mixin = Mixin("Bar") {
|
||||
javaPackage = "MixinPackage"
|
||||
Input(DataType.NUMERIC, "a")
|
||||
Arg(DataType.BOOL, "b")
|
||||
}
|
||||
|
||||
Namespace("math") {
|
||||
val op = Op("foo") {
|
||||
javaPackage = "fooPackage"
|
||||
Doc(Language.ANY, DocScope.ALL) { "Some Documentation" }
|
||||
Output(DataType.NUMERIC, "out")
|
||||
Input(DataType.NUMERIC, "a")
|
||||
useMixin(mixin)
|
||||
}
|
||||
|
||||
assertEquals("MixinPackage", op.javaPackage)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mixinDoesOnlyOverwritePropertiesIfSetNoneSetCaseOnMixins(){
|
||||
val mixin = Mixin("Bar") {
|
||||
Input(DataType.NUMERIC, "a")
|
||||
Arg(DataType.BOOL, "b")
|
||||
}
|
||||
|
||||
val op = Mixin("foo") {
|
||||
javaPackage = "fooPackage"
|
||||
useMixin(mixin)
|
||||
}
|
||||
|
||||
assertEquals("fooPackage", op.javaPackage)
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mixinDoesOnlyOverwritePropertiesIfSetSetCaseOnMixins(){
|
||||
val mixin = Mixin("Bar") {
|
||||
javaPackage = "MixinPackage"
|
||||
Input(DataType.NUMERIC, "a")
|
||||
Arg(DataType.BOOL, "b")
|
||||
}
|
||||
|
||||
val op = Mixin("foo") {
|
||||
javaPackage = "fooPackage"
|
||||
useMixin(mixin)
|
||||
}
|
||||
|
||||
assertEquals("MixinPackage", op.javaPackage)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package org.nd4j.codegen.ops
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
/**
|
||||
* Test that each Namespace actually constructs properly.
|
||||
*
|
||||
* This is allows us to utilize run-time consistency checks during the build process - if tests are enabled.
|
||||
*/
|
||||
class ConstructionTest {
|
||||
|
||||
@Test
|
||||
fun bitwise() { Bitwise() }
|
||||
|
||||
@Test
|
||||
fun random() { Random() }
|
||||
|
||||
@Test
|
||||
fun math() { Math() }
|
||||
|
||||
@Test
|
||||
fun base() { SDBaseOps() }
|
||||
|
||||
@Test
|
||||
fun loss() { SDLoss() }
|
||||
|
||||
@Test
|
||||
fun cnn() { SDCNN() }
|
||||
|
||||
@Test
|
||||
fun rnn() { SDRNN() }
|
||||
|
||||
@Test
|
||||
fun image() { SDImage() }
|
||||
|
||||
@Test
|
||||
fun nn() { NN() }
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,25 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>deeplearning4j</artifactId>
|
||||
<groupId>org.eclipse.deeplearning4j</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>codegen</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<properties>
|
||||
<kotlin.version>1.7.20</kotlin.version>
|
||||
</properties>
|
||||
|
||||
<name>codegen</name>
|
||||
<modules>
|
||||
<module>op-codegen</module>
|
||||
<module>libnd4j-gen</module>
|
||||
<module>blas-lapack-generator</module>
|
||||
</modules>
|
||||
|
||||
|
||||
</project>
|
||||
Reference in New Issue
Block a user