chore: import upstream snapshot with attribution
This commit is contained in:
+264
@@ -0,0 +1,264 @@
|
||||
# Created by https://www.gitignore.io/api/java,linux,macos,maven,windows,eclipse,netbeans,intellij
|
||||
|
||||
### Eclipse ###
|
||||
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
.recommenders
|
||||
|
||||
# Eclipse Core
|
||||
.project
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# PyDev specific (Python IDE for Eclipse)
|
||||
*.pydevproject
|
||||
|
||||
# CDT-specific (C/C++ Development Tooling)
|
||||
.cproject
|
||||
|
||||
# JDT-specific (Eclipse Java Development Tools)
|
||||
.classpath
|
||||
|
||||
# Java annotation processor (APT)
|
||||
.factorypath
|
||||
|
||||
# PDT-specific (PHP Development Tools)
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# Tern plugin
|
||||
.tern-project
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
||||
# STS (Spring Tool Suite)
|
||||
.springBeans
|
||||
|
||||
# Code Recommenders
|
||||
.recommenders/
|
||||
|
||||
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||
.cache-main
|
||||
.scala_dependencies
|
||||
.worksheet
|
||||
|
||||
### Intellij ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/dictionaries
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.xml
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
### Intellij Patch ###
|
||||
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
|
||||
|
||||
# *.iml
|
||||
# modules.xml
|
||||
# .idea/misc.xml
|
||||
# *.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
.idea/sonarlint
|
||||
|
||||
### Java ###
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
### Linux ###
|
||||
*~
|
||||
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
### macOS ###
|
||||
*.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### Maven ###
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
|
||||
# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
|
||||
!/.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
### NetBeans ###
|
||||
nbproject/private/
|
||||
build/
|
||||
nbbuild/
|
||||
dist/
|
||||
nbdist/
|
||||
.nb-gradle/
|
||||
|
||||
### VSCode ###
|
||||
.vscode/
|
||||
|
||||
### Windows ###
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# End of https://www.gitignore.io/api/java,linux,macos,maven,windows,eclipse,netbeans,intellij
|
||||
|
||||
|
||||
# Specific for Nd4j
|
||||
*.md5
|
||||
*.pom
|
||||
*.sha1
|
||||
*.ser
|
||||
*.so
|
||||
application.home_IS_UNDEFINEiD
|
||||
*.jpg
|
||||
*.png
|
||||
*.bin
|
||||
*.out
|
||||
*.iml
|
||||
.idea/
|
||||
*.prefs
|
||||
model-saver-*
|
||||
mnist-pretrain-dbn.bin-*
|
||||
deeplearning4j-scaleout/deeplearning4j-aws/src/main/java/org/deeplearning4j/aws/ec2/provision/Ec2CommandRunner.java.settings/
|
||||
*.dylib
|
||||
lib/
|
||||
nd4j-shade/jackson/dependency-reduced-pom.xml
|
||||
!nd4j-backends/nd4j-api-parent/nd4j-api/src/main/resources/bin
|
||||
nd4j-backends/nd4j-backend-impls/nd4j-native/src/test/resources/writeNumpy.csv
|
||||
nd4j-backends/nd4j-tests/src/test/resources/tf_graphs/examples/**/data-all*
|
||||
nd4j-backends/nd4j-tests/src/test/resources/tf_graphs/examples/**/checkpoint
|
||||
*-git.properties
|
||||
nd4j-backends/nd4j-api-parent/nd4j-api/src/main/java/onnx/
|
||||
nd4j-backends/nd4j-api-parent/nd4j-api/src/main/java/org/tensorflow/
|
||||
nd4j-backends/nd4j-api-parent/nd4j-api/src/main/java/tensorflow/
|
||||
|
||||
@@ -0,0 +1,421 @@
|
||||
<?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>
|
||||
|
||||
<parent>
|
||||
<groupId>org.eclipse.deeplearning4j</groupId>
|
||||
<artifactId>nd4j-api-parent</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>nd4j-api</artifactId>
|
||||
|
||||
<name>nd4j-api</name>
|
||||
|
||||
<properties>
|
||||
<byteunits.version>0.9.1</byteunits.version>
|
||||
<neoitertools.version>1.0.0</neoitertools.version>
|
||||
<module.name>nd4j.api</module.name>
|
||||
<cuda.version>12.3</cuda.version>
|
||||
<cudnn.version>8.9</cudnn.version>
|
||||
<libnd4j.platform>${javacpp.platform}</libnd4j.platform>
|
||||
<libnd4j.extension></libnd4j.extension>
|
||||
<libnd4j.chip></libnd4j.chip>
|
||||
<libnd4j.classifier>${libnd4j.platform}</libnd4j.classifier>
|
||||
</properties>
|
||||
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<!-- Annotation scanning for discovering UDFs -->
|
||||
<dependency>
|
||||
<groupId>com.dorkbox</groupId>
|
||||
<artifactId>Annotations</artifactId>
|
||||
<version>3.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.jakewharton.byteunits</groupId>
|
||||
<artifactId>byteunits</artifactId>
|
||||
<version>${byteunits.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-math3</artifactId>
|
||||
<version>${commons-math3.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons-lang3.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-collections4</artifactId>
|
||||
<version>${commons-collections4.version}</version>
|
||||
</dependency>
|
||||
<!-- Tensorflow import -->
|
||||
<dependency>
|
||||
<groupId>com.google.flatbuffers</groupId>
|
||||
<artifactId>flatbuffers-java</artifactId>
|
||||
<version>${flatbuffers.version}</version>
|
||||
</dependency>
|
||||
<!-- Note that this is shaded protobuf. We use this instead of google's version mainly due ot other systems packaging
|
||||
their own older (incompatible) protobuf versions-->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.deeplearning4j</groupId>
|
||||
<artifactId>protobuf</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<!-- oshi: Used for collecting system information for system info reporting -->
|
||||
<dependency>
|
||||
<groupId>com.github.oshi</groupId>
|
||||
<artifactId>oshi-core</artifactId>
|
||||
<version>${oshi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<!-- Thi sis to redirect jackson logging to slf4j-->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>log4j-over-slf4j</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
<!-- Shaded version of Jackson -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.deeplearning4j</groupId>
|
||||
<artifactId>jackson</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-net</groupId>
|
||||
<artifactId>commons-net</artifactId>
|
||||
<version>${commons-net.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.ericaro</groupId>
|
||||
<artifactId>neoitertools</artifactId>
|
||||
<version>${neoitertools.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.deeplearning4j</groupId>
|
||||
<artifactId>nd4j-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<!-- Mirror the same profile logic from libnd4j -->
|
||||
<profile>
|
||||
<id>chip</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>libnd4j.chip</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<libnd4j.classifier>${libnd4j.platform}-${libnd4j.chip}-${cuda.version}</libnd4j.classifier>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>extension</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>libnd4j.extension</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<libnd4j.classifier>${libnd4j.platform}-${libnd4j.extension}</libnd4j.classifier>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
<!-- The main profile that includes libnd4j -->
|
||||
<profile>
|
||||
<id>cpu</id>
|
||||
<activation>
|
||||
<activeByDefault>false</activeByDefault>
|
||||
</activation>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.deeplearning4j</groupId>
|
||||
<artifactId>libnd4j</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>${libnd4j.classifier}</classifier>
|
||||
<type>zip</type>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
|
||||
|
||||
<profile>
|
||||
<id>cuda</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>libnd4j.generate.flatc</name>
|
||||
<value>true</value>
|
||||
</property>
|
||||
</activation>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.deeplearning4j</groupId>
|
||||
<artifactId>libnd4j</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>zip</type>
|
||||
<classifier>${javacpp.platform}-cuda-${cuda.version}</classifier>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
<profile>
|
||||
<activation>
|
||||
<!-- By default protoc provided binaries don't need a protocCommand. One is downloaded. Protoc plugin does not support
|
||||
protoc yet. This means we need to use a system protoc on non intel platforms. -->
|
||||
<os>
|
||||
<arch>amd64</arch>
|
||||
</os>
|
||||
</activation>
|
||||
<id>protoc-provided-binaries</id>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.github.os72</groupId>
|
||||
<artifactId>protoc-jar-maven-plugin</artifactId>
|
||||
<version>${protoc-jar-maven-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>tensorflow</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<protocVersion>${google.protobuf.version}</protocVersion>
|
||||
<extension>.proto</extension>
|
||||
<includeDirectories>
|
||||
<include>src/main/protobuf/tf</include>
|
||||
<include>src/main/protobuf/onnx</include>
|
||||
<include>src/main/protobuf/nd4j</include>
|
||||
</includeDirectories>
|
||||
<inputDirectories>
|
||||
<include>src/main/protobuf/tf/tensorflow</include>
|
||||
<include>src/main/protobuf/onnx</include>
|
||||
<include>src/main/protobuf/nd4j</include>
|
||||
</inputDirectories>
|
||||
<addSources>main</addSources>
|
||||
<cleanOutputFolder>false</cleanOutputFolder>
|
||||
<outputDirectory>src/main/java/</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>com.google.code.maven-replacer-plugin</groupId>
|
||||
<artifactId>replacer</artifactId>
|
||||
<version>${maven-replacer-plugin.version}</version>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>${project.build.sourceDirectory}/org/tensorflow/**</include>
|
||||
<include>${project.build.sourceDirectory}/tensorflow/**</include>
|
||||
<include>${project.build.sourceDirectory}/onnx/**</include>
|
||||
<include>${project.build.sourceDirectory}/org/nd4j/ir/**</include>
|
||||
</includes>
|
||||
<token>com.google.protobuf.</token>
|
||||
<value>org.nd4j.shade.protobuf.</value>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>replace-imports</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>replace</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</profile>
|
||||
|
||||
|
||||
|
||||
<!-- See: https://github.com/os72/protoc-jar/issues/93 We need to use a system compiled protoc since one is not provided
|
||||
by the plugin.-->
|
||||
<profile>
|
||||
<id>osx-aarch64-protoc</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.github.os72</groupId>
|
||||
<artifactId>protoc-jar-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>tensorflow</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<protocVersion>${google.protobuf.version}</protocVersion>
|
||||
<extension>.proto</extension>
|
||||
<includeDirectories>
|
||||
<include>src/main/protobuf/tf</include>
|
||||
<include>src/main/protobuf/onnx</include>
|
||||
<include>src/main/protobuf/nd4j</include>
|
||||
</includeDirectories>
|
||||
<inputDirectories>
|
||||
<include>src/main/protobuf/tf/tensorflow</include>
|
||||
<include>src/main/protobuf/onnx</include>
|
||||
<include>src/main/protobuf/nd4j</include>
|
||||
</inputDirectories>
|
||||
<addSources>main</addSources>
|
||||
<cleanOutputFolder>false</cleanOutputFolder>
|
||||
<outputDirectory>src/main/java/</outputDirectory>
|
||||
<protocCommand>protoc</protocCommand> <!-- brew install protobuf -->
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>com.google.code.maven-replacer-plugin</groupId>
|
||||
<artifactId>replacer</artifactId>
|
||||
<version>${maven-replacer-plugin.version}</version>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>${project.build.sourceDirectory}/org/tensorflow/**</include>
|
||||
<include>${project.build.sourceDirectory}/tensorflow/**</include>
|
||||
<include>${project.build.sourceDirectory}/onnx/**</include>
|
||||
<include>${project.build.sourceDirectory}/org/nd4j/ir/**</include>
|
||||
</includes>
|
||||
<token>com.google.protobuf.</token>
|
||||
<value>org.nd4j.shade.protobuf.</value>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>replace-imports</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>replace</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
<activation>
|
||||
<os>
|
||||
<name>mac os x</name>
|
||||
<arch>aarch64</arch>
|
||||
<family>mac</family>
|
||||
</os>
|
||||
</activation>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.moditect</groupId>
|
||||
<artifactId>moditect-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<version>${maven-antrun-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<target>
|
||||
<delete
|
||||
file="${project.build.sourceDirectory}/onnx/OnnxMlProto3.java"/>
|
||||
<delete
|
||||
file="${project.build.sourceDirectory}/onnx/OnnxOperatorsProto3.java"/>
|
||||
<delete
|
||||
file="${project.build.sourceDirectory}/onnx/OnnxProto3.java"/>
|
||||
</target>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>com.google.code.maven-replacer-plugin</groupId>
|
||||
<artifactId>replacer</artifactId>
|
||||
<version>${maven-replacer-plugin.version}</version>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>${project.build.sourceDirectory}/org/tensorflow/**</include>
|
||||
<include>${project.build.sourceDirectory}/tensorflow/**</include>
|
||||
<include>${project.build.sourceDirectory}/onnx/**</include>
|
||||
<include>${project.build.sourceDirectory}/org/nd4j/ir/**</include>
|
||||
</includes>
|
||||
<token>com.google.protobuf.</token>
|
||||
<value>org.nd4j.shade.protobuf.</value>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>replace-imports</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>replace</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
+40
@@ -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;
|
||||
|
||||
|
||||
import org.nd4j.linalg.api.ndarray.INDArray;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface TFGraphRunnerService{
|
||||
TFGraphRunnerService init(
|
||||
List<String> inputNames,
|
||||
List<String> outputNames,
|
||||
byte[] graphBytes,
|
||||
Map<String, INDArray> constants,
|
||||
Map<String, String> inputDataTypes
|
||||
);
|
||||
|
||||
Map<String,INDArray> run(Map<String,INDArray> inputs);
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.adapters;
|
||||
|
||||
public interface InferenceAdapter<I, O> extends InputAdapter<I>, OutputAdapter<O> {
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.adapters;
|
||||
|
||||
import org.nd4j.linalg.dataset.MultiDataSet;
|
||||
|
||||
public interface InputAdapter<I> {
|
||||
/**
|
||||
* This method converts input object to MultiDataSet
|
||||
* @param input
|
||||
* @return
|
||||
*/
|
||||
MultiDataSet apply(I input);
|
||||
}
|
||||
+36
@@ -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.adapters;
|
||||
|
||||
import org.nd4j.linalg.api.ndarray.INDArray;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public interface OutputAdapter<T> extends Serializable {
|
||||
|
||||
/**
|
||||
* This method provides conversion from multiple INDArrays to T
|
||||
*
|
||||
* @param outputs
|
||||
* @return
|
||||
*/
|
||||
T apply(INDArray... outputs);
|
||||
}
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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.allocator.impl;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import lombok.val;
|
||||
import org.nd4j.linalg.factory.Nd4j;
|
||||
import org.nd4j.nativeblas.NativeOpsHolder;
|
||||
|
||||
@Slf4j
|
||||
public class MemoryTracker {
|
||||
|
||||
private List<AtomicLong> allocatedPerDevice = new ArrayList<>();
|
||||
private List<AtomicLong> cachedPerDevice = new ArrayList<>();
|
||||
private List<AtomicLong> totalPerDevice = new ArrayList<>();
|
||||
private List<AtomicLong> freePerDevice = new ArrayList<>();
|
||||
private List<AtomicLong> workspacesPerDevice = new ArrayList<>();
|
||||
private AtomicLong cachedHost = new AtomicLong(0);
|
||||
private AtomicLong allocatedHost = new AtomicLong(0);
|
||||
private final static MemoryTracker INSTANCE = new MemoryTracker();
|
||||
|
||||
public MemoryTracker() {
|
||||
for (int i = 0; i < Nd4j.getAffinityManager().getNumberOfDevices(); ++i) {
|
||||
allocatedPerDevice.add(i, new AtomicLong(0));
|
||||
cachedPerDevice.add(i, new AtomicLong(0));
|
||||
workspacesPerDevice.add(i, new AtomicLong(0));
|
||||
|
||||
totalPerDevice.add(i, new AtomicLong(NativeOpsHolder.getInstance().getDeviceNativeOps().getDeviceTotalMemory(i)));
|
||||
|
||||
val f = new AtomicLong(NativeOpsHolder.getInstance().getDeviceNativeOps().getDeviceFreeMemory(i));
|
||||
|
||||
freePerDevice.add(i, f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* toString() overview of every device's current status including available memory,
|
||||
* number of workspaces per device, free memory per device, total memory available for a device
|
||||
* @return
|
||||
*/
|
||||
public String memoryPerDevice() {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for(int i = 0; i < Nd4j.getAffinityManager().getNumberOfDevices(); i++) {
|
||||
stringBuilder.append("------Device: " + i + "---------------\n");
|
||||
stringBuilder.append("Allocated on device: " + allocatedPerDevice.get(i).get() + "\n");
|
||||
stringBuilder.append("Total workspace memory allocated for device: " + workspacesPerDevice.get(i).get() + "\n");
|
||||
stringBuilder.append("Cached memory for device: " + cachedPerDevice.get(i).get() + "\n");
|
||||
stringBuilder.append("Total device memory available: " + totalPerDevice.get(i).get() + "\n");
|
||||
stringBuilder.append("Free total memory for device: " + freePerDevice.get(i).get() + "\n");
|
||||
stringBuilder.append("-----------------------------------------\n");
|
||||
}
|
||||
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
public static MemoryTracker getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public long getAllocatedAmount(int deviceId) {
|
||||
return allocatedPerDevice.get(deviceId).get();
|
||||
}
|
||||
|
||||
public long getCachedAmount(int deviceId) {
|
||||
return cachedPerDevice.get(deviceId).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns number of bytes currently cached from host memory
|
||||
* @return
|
||||
*/
|
||||
public long getCachedHostAmount() {
|
||||
return cachedHost.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns number of bytes currently allocated from host memory
|
||||
* @return
|
||||
*/
|
||||
public long getAllocatedHostAmount() {
|
||||
return allocatedHost.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns number of bytes allocated and cached in host ram
|
||||
* @return
|
||||
*/
|
||||
public long getActiveHostAmount() {
|
||||
return getAllocatedHostAmount() + getCachedHostAmount();
|
||||
}
|
||||
|
||||
public void incrementCachedHostAmount(long numBytes) {
|
||||
cachedHost.addAndGet(numBytes);
|
||||
}
|
||||
|
||||
public void incrementAllocatedHostAmount(long numBytes) {
|
||||
allocatedHost.addAndGet(numBytes);
|
||||
}
|
||||
|
||||
public void decrementCachedHostAmount(long numBytes) {
|
||||
cachedHost.addAndGet(-numBytes);
|
||||
}
|
||||
|
||||
public void decrementAllocatedHostAmount(long numBytes) {
|
||||
allocatedHost.addAndGet(-numBytes);
|
||||
}
|
||||
|
||||
public long getWorkspaceAllocatedAmount(int deviceId) {
|
||||
return workspacesPerDevice.get(deviceId).get();
|
||||
}
|
||||
|
||||
public long getTotalMemory(int deviceId) {
|
||||
return totalPerDevice.get(deviceId).get();
|
||||
}
|
||||
|
||||
public long getFreeMemory(int deviceId) {
|
||||
return freePerDevice.get(deviceId).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns approximate free memory on specified device
|
||||
* @param deviceId
|
||||
* @return
|
||||
*/
|
||||
public long getApproximateFreeMemory(int deviceId) {
|
||||
val externalAllocations = getTotalMemory(deviceId) - getFreeMemory(deviceId);
|
||||
val active = getActiveMemory(deviceId);
|
||||
val free = getTotalMemory(deviceId) - (active + externalAllocations);
|
||||
return free;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns precise amount of free memory on specified device
|
||||
* @param deviceId
|
||||
* @return
|
||||
*/
|
||||
public long getPreciseFreeMemory(int deviceId) {
|
||||
// we refresh free memory on device
|
||||
val extFree =Nd4j.getNativeOps().getDeviceFreeMemory(deviceId);
|
||||
return extFree;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns delta between total memory and free memory
|
||||
* @param deviceId
|
||||
* @return
|
||||
*/
|
||||
public long getUsableMemory(int deviceId) {
|
||||
return getTotalMemory(deviceId) - getFreeMemory(deviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns total amount of device memory allocated on specified device
|
||||
*
|
||||
* Includes: workspace memory, cached memory, regular memory
|
||||
* @param deviceId
|
||||
* @return
|
||||
*/
|
||||
public long getActiveMemory(int deviceId) {
|
||||
return getWorkspaceAllocatedAmount(deviceId) + getAllocatedAmount(deviceId) + getCachedAmount(deviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns amount of memory that relies on JVM GC
|
||||
*
|
||||
* Includes: cached memory, regular allocated memory
|
||||
*
|
||||
* @param deviceId
|
||||
* @return
|
||||
*/
|
||||
public long getManagedMemory(int deviceId) {
|
||||
return getAllocatedAmount(deviceId) + getCachedAmount(deviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method increments amount of regular allocated memory
|
||||
*
|
||||
* @param deviceId
|
||||
* @param memoryAdded
|
||||
*/
|
||||
public void incrementAllocatedAmount(int deviceId, long memoryAdded) {
|
||||
allocatedPerDevice.get(deviceId).getAndAdd(matchBlock(memoryAdded));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method increments amount of cached memory
|
||||
*
|
||||
* @param deviceId
|
||||
* @param memoryAdded
|
||||
*/
|
||||
public void incrementCachedAmount(int deviceId, long memoryAdded) {
|
||||
cachedPerDevice.get(deviceId).getAndAdd(matchBlock(memoryAdded));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method decrements amount of regular allocated memory
|
||||
*
|
||||
* @param deviceId
|
||||
* @param memorySubtracted
|
||||
*/
|
||||
public void decrementAllocatedAmount(int deviceId, long memorySubtracted) {
|
||||
allocatedPerDevice.get(deviceId).getAndAdd(-matchBlock(memorySubtracted));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method decrements amount of cached memory
|
||||
*
|
||||
* @param deviceId
|
||||
* @param memorySubtracted
|
||||
*/
|
||||
public void decrementCachedAmount(int deviceId, long memorySubtracted) {
|
||||
cachedPerDevice.get(deviceId).getAndAdd(-matchBlock(memorySubtracted));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method increments amount of memory allocated within workspaces
|
||||
*
|
||||
* @param deviceId
|
||||
* @param memoryAdded
|
||||
*/
|
||||
public void incrementWorkspaceAllocatedAmount(int deviceId, long memoryAdded) {
|
||||
workspacesPerDevice.get(deviceId).getAndAdd(matchBlock(memoryAdded));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method decrements amount of memory allocated within workspaces
|
||||
*
|
||||
* @param deviceId
|
||||
* @param memorySubtracted
|
||||
*/
|
||||
public void decrementWorkspaceAmount(int deviceId, long memorySubtracted) {
|
||||
workspacesPerDevice.get(deviceId).getAndAdd(-matchBlock(memorySubtracted));
|
||||
}
|
||||
|
||||
|
||||
private void setTotalPerDevice(int device, long memoryAvailable) {
|
||||
totalPerDevice.add(device, new AtomicLong(memoryAvailable));
|
||||
}
|
||||
|
||||
|
||||
private long matchBlock(long numBytes) {
|
||||
return numBytes;
|
||||
}
|
||||
}
|
||||
+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.autodiff.execution.conf;
|
||||
|
||||
public enum ExecutionMode {
|
||||
/**
|
||||
* all operations will be executed sequentially
|
||||
*/
|
||||
SEQUENTIAL,
|
||||
|
||||
/**
|
||||
* all operations will be following device id for execution mode selection
|
||||
*/
|
||||
STRICT,
|
||||
|
||||
/**
|
||||
* all operations that can be executed in parallel - will be executed in parallel
|
||||
*/
|
||||
AUTO,
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.execution.conf;
|
||||
|
||||
import com.google.flatbuffers.FlatBufferBuilder;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nd4j.graph.Direction;
|
||||
import org.nd4j.graph.FlatConfiguration;
|
||||
import org.nd4j.graph.ProfilingMode;
|
||||
import org.nd4j.linalg.api.ops.executioner.OpExecutioner;
|
||||
|
||||
@Data
|
||||
@Slf4j
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class ExecutorConfiguration {
|
||||
@Builder.Default private OpExecutioner.ProfilingMode profilingMode = OpExecutioner.ProfilingMode.DISABLED;
|
||||
@Builder.Default private ExecutionMode executionMode = ExecutionMode.SEQUENTIAL;
|
||||
@Builder.Default private OutputMode outputMode = OutputMode.IMPLICIT;
|
||||
@Builder.Default boolean gatherTimings = true;
|
||||
@Builder.Default private long footprintForward = 0L;
|
||||
@Builder.Default private long footprintBackward = 0L;
|
||||
|
||||
|
||||
/**
|
||||
* This method
|
||||
* @param builder
|
||||
* @return
|
||||
*/
|
||||
public int getFlatConfiguration(FlatBufferBuilder builder) {
|
||||
|
||||
byte prof = profilingMode == OpExecutioner.ProfilingMode.INF_PANIC ? ProfilingMode.INF_PANIC :
|
||||
profilingMode == OpExecutioner.ProfilingMode.NAN_PANIC ? ProfilingMode.NAN_PANIC :
|
||||
profilingMode == OpExecutioner.ProfilingMode.ANY_PANIC ? ProfilingMode.ANY_PANIC : ProfilingMode.NONE;
|
||||
|
||||
byte exec = executionMode == ExecutionMode.SEQUENTIAL ? org.nd4j.graph.ExecutionMode.SEQUENTIAL :
|
||||
executionMode == ExecutionMode.AUTO ? org.nd4j.graph.ExecutionMode.AUTO :
|
||||
executionMode == ExecutionMode.STRICT ? org.nd4j.graph.ExecutionMode.STRICT : -1;
|
||||
|
||||
byte outp = outputMode == OutputMode.IMPLICIT ? org.nd4j.graph.OutputMode.IMPLICIT :
|
||||
outputMode == OutputMode.EXPLICIT ? org.nd4j.graph.OutputMode.EXPLICIT :
|
||||
outputMode == OutputMode.EXPLICIT_AND_IMPLICIT ? org.nd4j.graph.OutputMode.EXPLICIT_AND_IMPLICIT :
|
||||
outputMode == OutputMode.VARIABLE_SPACE ? org.nd4j.graph.OutputMode.VARIABLE_SPACE : -1;
|
||||
|
||||
if (exec == -1)
|
||||
throw new UnsupportedOperationException("Unknown values were passed into configuration as ExecutionMode: [" + executionMode + "]");
|
||||
|
||||
if (outp == -1)
|
||||
throw new UnsupportedOperationException("Unknown values were passed into configuration as OutputMode: [" + outputMode + "]");
|
||||
|
||||
return FlatConfiguration.createFlatConfiguration(builder, -1, prof, exec, outp, gatherTimings, footprintForward, footprintBackward, Direction.FORWARD_ONLY);
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.execution.conf;
|
||||
|
||||
public enum OutputMode {
|
||||
/**
|
||||
* only final nodes of graph will be returned
|
||||
*/
|
||||
IMPLICIT,
|
||||
|
||||
/**
|
||||
* only declared output fields
|
||||
*/
|
||||
EXPLICIT,
|
||||
|
||||
/**
|
||||
* both options enabled
|
||||
*/
|
||||
EXPLICIT_AND_IMPLICIT,
|
||||
|
||||
/**
|
||||
* whole content of VariableSpace will be returned back
|
||||
*/
|
||||
VARIABLE_SPACE,
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.execution.input;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import lombok.val;
|
||||
|
||||
import org.nd4j.linalg.api.ndarray.INDArray;
|
||||
import org.nd4j.common.primitives.Pair;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Operands {
|
||||
private Map<NodeDescriptor, INDArray> map = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* This method allows to pass array to the node identified by its name
|
||||
*
|
||||
* @param id
|
||||
* @param array
|
||||
* @return
|
||||
*/
|
||||
public Operands addArgument(@NonNull String id, @NonNull INDArray array) {
|
||||
map.put(NodeDescriptor.builder().name(id).build(), array);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows to pass array to the node identified by numeric id
|
||||
*
|
||||
* @param id
|
||||
* @param array
|
||||
* @return
|
||||
*/
|
||||
public Operands addArgument(int id, @NonNull INDArray array) {
|
||||
map.put(NodeDescriptor.builder().id(id).build(), array);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows to pass array to multi-output node in the graph
|
||||
*
|
||||
* @param id
|
||||
* @param index
|
||||
* @param array
|
||||
* @return
|
||||
*/
|
||||
public Operands addArgument( int id, int index, @NonNull INDArray array) {
|
||||
map.put(NodeDescriptor.builder().id(id).index(index).build(), array);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows to pass array to multi-output node in the graph
|
||||
*
|
||||
* @param id
|
||||
* @param index
|
||||
* @param array
|
||||
* @return
|
||||
*/
|
||||
public Operands addArgument(String name, int id, int index, @NonNull INDArray array) {
|
||||
map.put(NodeDescriptor.builder().name(name).id(id).index(index).build(), array);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns array identified its name
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public INDArray getById(@NonNull String name) {
|
||||
return map.get(NodeDescriptor.builder().name(name).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns array identified its numeric id
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public INDArray getById(int id) {
|
||||
return map.get(NodeDescriptor.builder().id(id).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns array identified its numeric id and index
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public INDArray getById(int id, int index) {
|
||||
return map.get(NodeDescriptor.builder().id(id).index(index).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* This method return operands as array, in order of addition
|
||||
* @return
|
||||
*/
|
||||
public INDArray[] asArray() {
|
||||
val val = map.values();
|
||||
val res = new INDArray[val.size()];
|
||||
int cnt = 0;
|
||||
for (val v: val)
|
||||
res[cnt++] = v;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns contents of this entity as collection of key->value pairs
|
||||
* @return
|
||||
*/
|
||||
public Collection<Pair<NodeDescriptor, INDArray>> asCollection() {
|
||||
val c = new HashSet<Pair<NodeDescriptor, INDArray>>();
|
||||
for (val k: map.keySet())
|
||||
c.add(Pair.makePair(k, map.get(k)));
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns number of values in this entity
|
||||
* @return
|
||||
*/
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Data
|
||||
public static class NodeDescriptor {
|
||||
private String name;
|
||||
private int id;
|
||||
private int index;
|
||||
}
|
||||
}
|
||||
+1047
File diff suppressed because it is too large
Load Diff
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
import org.nd4j.autodiff.samediff.internal.FrameIter;
|
||||
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
@Builder
|
||||
@Setter
|
||||
public class At {
|
||||
|
||||
private int epoch;
|
||||
private int iteration;
|
||||
private int trainingThreadNum;
|
||||
private long javaThreadNum;
|
||||
private FrameIter frameIter;
|
||||
private Operation operation;
|
||||
|
||||
/**
|
||||
* @return A new instance with everything set to 0, and operation set to INFERENCE
|
||||
*/
|
||||
public static At defaultAt(){
|
||||
return new At(0, 0, 0, 0, null, Operation.INFERENCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param op Operation
|
||||
* @return A new instance with everything set to 0, except for the specified operation
|
||||
*/
|
||||
public static At defaultAt(@NonNull Operation op){
|
||||
return new At(0, 0, 0, 0, null, op);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The current training epoch
|
||||
*/
|
||||
public int epoch(){
|
||||
return epoch;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The current training iteration
|
||||
*/
|
||||
public int iteration(){
|
||||
return iteration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The number of the SameDiff thread
|
||||
*/
|
||||
public int trainingThreadNum(){
|
||||
return trainingThreadNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The Java/JVM thread number for training
|
||||
*/
|
||||
public long javaThreadNum(){
|
||||
return javaThreadNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The current operation
|
||||
*/
|
||||
public Operation operation(){
|
||||
return operation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A copy of the current At instance
|
||||
*/
|
||||
public At copy(){
|
||||
return new At(epoch, iteration, trainingThreadNum, javaThreadNum, frameIter, operation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param operation Operation to set in the new instance
|
||||
* @return A copy of the current instance, but with the specified operation
|
||||
*/
|
||||
public At copy(Operation operation){
|
||||
return new At(epoch, iteration, trainingThreadNum, javaThreadNum, frameIter, operation);
|
||||
}
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.nd4j.autodiff.listeners.records.EvaluationRecord;
|
||||
import org.nd4j.autodiff.listeners.records.LossCurve;
|
||||
import org.nd4j.autodiff.samediff.SameDiff;
|
||||
import org.nd4j.autodiff.samediff.internal.SameDiffOp;
|
||||
import org.nd4j.evaluation.IEvaluation;
|
||||
import org.nd4j.linalg.api.ndarray.INDArray;
|
||||
import org.nd4j.linalg.dataset.api.MultiDataSet;
|
||||
|
||||
public abstract class BaseEvaluationListener extends BaseListener {
|
||||
|
||||
private Map<String, List<IEvaluation>> trainingEvaluations = new HashMap<>();
|
||||
private Map<String, List<IEvaluation>> validationEvaluations = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Return the requested evaluations. New instances of these evaluations will be made each time they are used
|
||||
*/
|
||||
public abstract ListenerEvaluations evaluations();
|
||||
|
||||
@Override
|
||||
public final ListenerVariables requiredVariables(SameDiff sd) {
|
||||
return evaluations().requiredVariables().merge(otherRequiredVariables(sd));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return any requested variables that are not part of the evaluations
|
||||
*/
|
||||
public ListenerVariables otherRequiredVariables(SameDiff sd){
|
||||
return ListenerVariables.empty();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final void epochStart(SameDiff sd, At at) {
|
||||
trainingEvaluations = new HashMap<>();
|
||||
for(Map.Entry<String, List<IEvaluation>> entry : evaluations().trainEvaluations().entrySet()){
|
||||
|
||||
List<IEvaluation> evals = new ArrayList<>();
|
||||
for(IEvaluation ie : entry.getValue())
|
||||
evals.add(ie.newInstance());
|
||||
|
||||
trainingEvaluations.put(entry.getKey(), evals);
|
||||
}
|
||||
validationEvaluations = new HashMap<>();
|
||||
for(Map.Entry<String, List<IEvaluation>> entry : evaluations().validationEvaluations().entrySet()){
|
||||
|
||||
List<IEvaluation> evals = new ArrayList<>();
|
||||
for(IEvaluation ie : entry.getValue())
|
||||
evals.add(ie.newInstance());
|
||||
|
||||
validationEvaluations.put(entry.getKey(), evals);
|
||||
}
|
||||
|
||||
epochStartEvaluations(sd, at);
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link Listener#epochStart(SameDiff, At)}
|
||||
*/
|
||||
public void epochStartEvaluations(SameDiff sd, At at){
|
||||
//No op
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ListenerResponse epochEnd(SameDiff sd, At at, LossCurve lossCurve, long epochTimeMillis) {
|
||||
return epochEndEvaluations(sd, at, lossCurve, epochTimeMillis, new EvaluationRecord(trainingEvaluations));
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link Listener#epochEnd(SameDiff, At, LossCurve, long)}, also provided the requested evaluations
|
||||
*/
|
||||
public ListenerResponse epochEndEvaluations(SameDiff sd, At at, LossCurve lossCurve, long epochTimeMillis, EvaluationRecord evaluations) {
|
||||
//No op
|
||||
return ListenerResponse.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ListenerResponse validationDone(SameDiff sd, At at, long validationTimeMillis) {
|
||||
return validationDoneEvaluations(sd, at, validationTimeMillis, new EvaluationRecord(validationEvaluations));
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link Listener#validationDone(SameDiff, At, long)}, also provided the requested evaluations
|
||||
*/
|
||||
public ListenerResponse validationDoneEvaluations(SameDiff sd, At at, long validationTimeMillis, EvaluationRecord evaluations) {
|
||||
//No op
|
||||
return ListenerResponse.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void activationAvailable(SameDiff sd, At at, MultiDataSet batch, SameDiffOp op, String varName,
|
||||
INDArray activation) {
|
||||
if(at.operation() == Operation.TRAINING) {
|
||||
if (trainingEvaluations.containsKey(varName)) {
|
||||
INDArray labels = batch.getLabels(evaluations().trainEvaluationLabels().get(varName));
|
||||
INDArray mask = batch.getLabelsMaskArray(evaluations().trainEvaluationLabels().get(varName));
|
||||
|
||||
for (IEvaluation e : trainingEvaluations.get(varName))
|
||||
e.eval(labels, activation, mask);
|
||||
}
|
||||
} else if(at.operation() == Operation.TRAINING_VALIDATION) {
|
||||
if (validationEvaluations.containsKey(varName)) {
|
||||
INDArray labels = batch.getLabels(evaluations().validationEvaluationLabels().get(varName));
|
||||
INDArray mask = batch.getLabelsMaskArray(evaluations().validationEvaluationLabels().get(varName));
|
||||
|
||||
for (IEvaluation e : validationEvaluations.get(varName))
|
||||
e.eval(labels, activation, mask);
|
||||
}
|
||||
}
|
||||
|
||||
activationAvailableEvaluations(sd, at, batch, op, varName, activation);
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link Listener#activationAvailable(SameDiff, At, MultiDataSet, SameDiffOp, String, INDArray)}
|
||||
*/
|
||||
public void activationAvailableEvaluations(SameDiff sd, At at, MultiDataSet batch, SameDiffOp op, String varName,
|
||||
INDArray activation){
|
||||
//No op
|
||||
}
|
||||
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners;
|
||||
|
||||
import org.nd4j.autodiff.listeners.records.LossCurve;
|
||||
import org.nd4j.autodiff.samediff.SameDiff;
|
||||
import org.nd4j.autodiff.samediff.internal.SameDiffOp;
|
||||
import org.nd4j.autodiff.samediff.internal.Variable;
|
||||
import org.nd4j.linalg.api.ndarray.INDArray;
|
||||
import org.nd4j.linalg.api.ops.OpContext;
|
||||
import org.nd4j.linalg.dataset.api.MultiDataSet;
|
||||
|
||||
public abstract class BaseListener implements Listener {
|
||||
|
||||
|
||||
@Override
|
||||
public ListenerVariables requiredVariables(SameDiff sd){
|
||||
return ListenerVariables.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void epochStart(SameDiff sd, At at) {
|
||||
//No op
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenerResponse epochEnd(SameDiff sd, At at, LossCurve lossCurve, long epochTimeMillis) {
|
||||
return ListenerResponse.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenerResponse validationDone(SameDiff sd, At at, long validationTimeMillis) {
|
||||
//No op
|
||||
return ListenerResponse.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void iterationStart(SameDiff sd, At at, MultiDataSet data, long etlMs) {
|
||||
//No op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void iterationDone(SameDiff sd, At at, MultiDataSet dataSet, Loss loss) {
|
||||
//No op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void operationStart(SameDiff sd, Operation op) {
|
||||
//No op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void operationEnd(SameDiff sd, Operation op) {
|
||||
//No op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preOpExecution(SameDiff sd, At at, SameDiffOp op, OpContext opContext) {
|
||||
//No op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void opExecution(SameDiff sd, At at, MultiDataSet batch, SameDiffOp op, OpContext opContext, INDArray[] outputs) {
|
||||
//No op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activationAvailable(SameDiff sd, At at, MultiDataSet batch, SameDiffOp op, String varName,
|
||||
INDArray activation) {
|
||||
//No op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preUpdate(SameDiff sd, At at, Variable v, INDArray update) {
|
||||
//No op
|
||||
}
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners;
|
||||
|
||||
import org.nd4j.autodiff.listeners.records.LossCurve;
|
||||
import org.nd4j.autodiff.samediff.SameDiff;
|
||||
import org.nd4j.autodiff.samediff.internal.SameDiffOp;
|
||||
import org.nd4j.autodiff.samediff.internal.Variable;
|
||||
import org.nd4j.linalg.api.ndarray.INDArray;
|
||||
import org.nd4j.linalg.api.ops.OpContext;
|
||||
import org.nd4j.linalg.dataset.api.MultiDataSet;
|
||||
|
||||
public interface Listener {
|
||||
|
||||
|
||||
/**
|
||||
* Required variables for this listener.
|
||||
* <p>
|
||||
* Used to ensure these variables end up in the minimum required subgraph calculated by {@link org.nd4j.autodiff.samediff.internal.InferenceSession}.
|
||||
* Otherwise, if the variables weren't required by a loss variable, they would not be calculated.
|
||||
* <p>
|
||||
* Any variables in here are guaranteed to have {@link Listener#activationAvailable(SameDiff, At, MultiDataSet, SameDiffOp, String, INDArray)}
|
||||
* called for them, regardless of whether they would normally be calculated or not.
|
||||
*/
|
||||
ListenerVariables requiredVariables(SameDiff sd);
|
||||
|
||||
/**
|
||||
* Returns whether this listener is active during the given operation. If this returns false for the given operation,
|
||||
* those listener methods will not be called.
|
||||
*/
|
||||
boolean isActive(Operation operation);
|
||||
|
||||
/**
|
||||
* Called at the start of every epoch, when fitting from an iterator
|
||||
*
|
||||
* @param sd The SameDiff instance
|
||||
* @param at Current iteration/epoch etc
|
||||
*/
|
||||
void epochStart(SameDiff sd, At at);
|
||||
|
||||
/**
|
||||
* Called at the end of every epoch, when fitting from an iterator
|
||||
*
|
||||
* @param sd The SameDiff instance
|
||||
* @param at Current iteration/epoch etc
|
||||
* @param lossCurve The losses so far
|
||||
* @param epochTimeMillis How long this epoch took
|
||||
* @return ListenerResponse.STOP to stop training, CONTINUE or null to continue
|
||||
*/
|
||||
ListenerResponse epochEnd(SameDiff sd, At at, LossCurve lossCurve, long epochTimeMillis);
|
||||
|
||||
/**
|
||||
* Called after the end of every epoch, once validation evaluation is done, when training
|
||||
*
|
||||
* @param sd The SameDiff instance
|
||||
* @param at Current iteration/epoch etc
|
||||
* @param validationTimeMillis How long validation took for this epoch
|
||||
* @return ListenerResponse.STOP to stop training, CONTINUE or null to continue
|
||||
*/
|
||||
ListenerResponse validationDone(SameDiff sd, At at, long validationTimeMillis);
|
||||
|
||||
/**
|
||||
* Called at the start of every iteration (minibatch), before any operations have been executed
|
||||
*
|
||||
* @param sd The SameDiff instance
|
||||
* @param at Current iteration/epoch etc
|
||||
*/
|
||||
void iterationStart(SameDiff sd, At at, MultiDataSet data, long etlTimeMs);
|
||||
|
||||
/**
|
||||
* Called at the end of every iteration, after all operations (including updating parameters) has been completed
|
||||
*
|
||||
* @param sd The SameDiff instance
|
||||
* @param at Current iteration/epoch etc
|
||||
* @param dataSet The current dataset (minibatch) used for training
|
||||
* @param loss The loss value for the current minibatch. Will be null except for during training
|
||||
*/
|
||||
void iterationDone(SameDiff sd, At at, MultiDataSet dataSet, Loss loss);
|
||||
|
||||
/**
|
||||
* Called at the start of an operation, e.g. training or validation
|
||||
*
|
||||
* @param sd The SameDiff instance
|
||||
* @param op The operation being started
|
||||
*/
|
||||
void operationStart(SameDiff sd, Operation op);
|
||||
|
||||
/**
|
||||
* Called at the end of an operation, e.g. training or validation
|
||||
*
|
||||
* @param sd The SameDiff instance
|
||||
* @param op The operation being started
|
||||
*/
|
||||
void operationEnd(SameDiff sd, Operation op);
|
||||
|
||||
/**
|
||||
* Called just before each operation is executed (native code called, etc) - after all inputs etc have been set
|
||||
*
|
||||
* @param sd The SameDiff instance
|
||||
* @param at Current iteration/epoch etc
|
||||
* @param op Operation that has just been executed
|
||||
*/
|
||||
void preOpExecution(SameDiff sd, At at, SameDiffOp op, OpContext opContext);
|
||||
|
||||
/**
|
||||
* Called at the end of each operation execution<br>
|
||||
* <p>
|
||||
* Note: Outputs will most likely be freed later, use detach() if you need to save it.
|
||||
*
|
||||
* @param sd The SameDiff instance
|
||||
* @param at Current iteration/epoch etc
|
||||
* @param batch The batch's input data. May be null if not called with a batch
|
||||
* @param op Operation that has just been executed
|
||||
* @param outputs The output arrays for the just-executed operation
|
||||
*/
|
||||
void opExecution(SameDiff sd, At at, MultiDataSet batch, SameDiffOp op, OpContext opContext, INDArray[] outputs);
|
||||
|
||||
/**
|
||||
* Called when any activation becomes available.
|
||||
* <p>
|
||||
* The activation will most likely be freed later, use dup() if you need to save it.<br>
|
||||
* <br>
|
||||
* Note that this method will be called when any activation becomes available, not just ones from {@link #requiredVariables(SameDiff)}<br>
|
||||
* It is guaranteed to be called for variables from requiredVariables().<br>
|
||||
* <br>
|
||||
* Note that the activations here overlap with {@link #opExecution(SameDiff, At, MultiDataSet, SameDiffOp, OpContext, INDArray[])} -
|
||||
* both contain the same information/arrays
|
||||
*
|
||||
* @param sd The SameDiff instance
|
||||
* @param at Current iteration/epoch etc
|
||||
* @param batch The batch's input data. May be null if not called with a batch
|
||||
* @param op Operation that has just been executed
|
||||
* @param varName The name of the variable
|
||||
* @param activation The variable's activation
|
||||
*/
|
||||
void activationAvailable(SameDiff sd, At at, MultiDataSet batch, SameDiffOp op, String varName, INDArray activation);
|
||||
|
||||
/**
|
||||
* Called just before each parameter is to be updated - i.e., just before each parameter is modified.
|
||||
*
|
||||
* @param sd SameDiff instance
|
||||
* @param at The current iteration/epoch etc
|
||||
* @param v Variable about to be updated during backprop
|
||||
* @param update The array representing the update (i.e., the gradient after applying learning rate, momentum, etc)
|
||||
*/
|
||||
void preUpdate(SameDiff sd, At at, Variable v, INDArray update);
|
||||
|
||||
}
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
import org.nd4j.autodiff.samediff.SDVariable;
|
||||
import org.nd4j.common.base.Preconditions;
|
||||
import org.nd4j.evaluation.IEvaluation;
|
||||
|
||||
@Getter
|
||||
public class ListenerEvaluations {
|
||||
private Map<String, List<IEvaluation>> trainEvaluations;
|
||||
private Map<String, Integer> trainEvaluationLabels;
|
||||
|
||||
private Map<String, List<IEvaluation>> validationEvaluations;
|
||||
private Map<String, Integer> validationEvaluationLabels;
|
||||
|
||||
public ListenerEvaluations(Map<String, List<IEvaluation>> trainEvaluations,
|
||||
Map<String, Integer> trainEvaluationLabels, Map<String, List<IEvaluation>> validationEvaluations,
|
||||
Map<String, Integer> validationEvaluationLabels) {
|
||||
this.trainEvaluations = trainEvaluations;
|
||||
this.trainEvaluationLabels = trainEvaluationLabels;
|
||||
this.validationEvaluations = validationEvaluations;
|
||||
this.validationEvaluationLabels = validationEvaluationLabels;
|
||||
|
||||
Preconditions.checkArgument(trainEvaluations.keySet().equals(trainEvaluationLabels.keySet()),
|
||||
"Must specify a label index for each train evaluation. Expected: %s, got: %s",
|
||||
trainEvaluations.keySet(), trainEvaluationLabels.keySet());
|
||||
|
||||
Preconditions.checkArgument(validationEvaluations.keySet().equals(validationEvaluationLabels.keySet()),
|
||||
"Must specify a label index for each validation evaluation. Expected: %s, got: %s",
|
||||
validationEvaluations.keySet(), validationEvaluationLabels.keySet());
|
||||
}
|
||||
|
||||
private ListenerEvaluations() {
|
||||
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the requested training evaluations
|
||||
*/
|
||||
public Map<String, List<IEvaluation>> trainEvaluations() {
|
||||
return trainEvaluations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the label indices for the requested training evaluations
|
||||
*/
|
||||
public Map<String, Integer> trainEvaluationLabels() {
|
||||
return trainEvaluationLabels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the requested validation evaluations
|
||||
*/
|
||||
public Map<String, List<IEvaluation>> validationEvaluations() {
|
||||
return validationEvaluations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the label indices for the requested validation evaluations
|
||||
*/
|
||||
public Map<String, Integer> validationEvaluationLabels() {
|
||||
return validationEvaluationLabels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required variables for these evaluations
|
||||
*/
|
||||
public ListenerVariables requiredVariables() {
|
||||
return new ListenerVariables(trainEvaluations.keySet(), validationEvaluations.keySet(),
|
||||
new HashSet<String>(), new HashSet<String>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if there are no requested evaluations
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return trainEvaluations.isEmpty() && validationEvaluations.isEmpty();
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public static class Builder {
|
||||
private Map<String, List<IEvaluation>> trainEvaluations = new HashMap<>();
|
||||
private Map<String, Integer> trainEvaluationLabels = new HashMap<>();
|
||||
|
||||
private Map<String, List<IEvaluation>> validationEvaluations = new HashMap<>();
|
||||
private Map<String, Integer> validationEvaluationLabels = new HashMap<>();
|
||||
|
||||
private void addEvaluations(boolean validation, @NonNull Map<String, List<IEvaluation>> evaluationMap, @NonNull Map<String, Integer> labelMap,
|
||||
@NonNull String variableName, int labelIndex, @NonNull IEvaluation... evaluations) {
|
||||
if (evaluationMap.containsKey(variableName) && labelMap.get(variableName) != labelIndex) {
|
||||
String s;
|
||||
|
||||
if (validation) {
|
||||
s = "This ListenerEvaluations.Builder already has validation evaluations for ";
|
||||
} else {
|
||||
s = "This ListenerEvaluations.Builder already has train evaluations for ";
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(s + "variable " +
|
||||
variableName + " with label index " + labelIndex + ". You can't add " +
|
||||
" evaluations with a different label index. Got label index " + labelIndex);
|
||||
}
|
||||
|
||||
if (evaluationMap.containsKey(variableName)) {
|
||||
evaluationMap.get(variableName).addAll(Arrays.asList(evaluations));
|
||||
} else {
|
||||
evaluationMap.put(variableName, Arrays.asList(evaluations));
|
||||
labelMap.put(variableName, labelIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add requested training evaluations for a parm/variable
|
||||
*
|
||||
* @param variableName The variable to evaluate
|
||||
* @param labelIndex The index of the label to evaluate against
|
||||
* @param evaluations The evaluations to run
|
||||
*/
|
||||
public Builder trainEvaluation(@NonNull String variableName, int labelIndex, @NonNull IEvaluation... evaluations) {
|
||||
addEvaluations(false, this.trainEvaluations, this.trainEvaluationLabels, variableName,
|
||||
labelIndex, evaluations);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add requested training evaluations for a parm/variable
|
||||
*
|
||||
* @param variable The variable to evaluate
|
||||
* @param labelIndex The index of the label to evaluate against
|
||||
* @param evaluations The evaluations to run
|
||||
*/
|
||||
public Builder trainEvaluation(@NonNull SDVariable variable, int labelIndex, @NonNull IEvaluation... evaluations) {
|
||||
return trainEvaluation(variable.name(), labelIndex, evaluations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add requested validation evaluations for a parm/variable
|
||||
*
|
||||
* @param variableName The variable to evaluate
|
||||
* @param labelIndex The index of the label to evaluate against
|
||||
* @param evaluations The evaluations to run
|
||||
*/
|
||||
public Builder validationEvaluation(@NonNull String variableName, int labelIndex, @NonNull IEvaluation... evaluations) {
|
||||
addEvaluations(true, this.validationEvaluations, this.validationEvaluationLabels, variableName,
|
||||
labelIndex, evaluations);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add requested validation evaluations for a parm/variable
|
||||
*
|
||||
* @param variable The variable to evaluate
|
||||
* @param labelIndex The index of the label to evaluate against
|
||||
* @param evaluations The evaluations to run
|
||||
*/
|
||||
public Builder validationEvaluation(@NonNull SDVariable variable, int labelIndex, @NonNull IEvaluation... evaluations) {
|
||||
return validationEvaluation(variable.name(), labelIndex, evaluations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add requested evaluations for a parm/variable, for either training or validation
|
||||
*
|
||||
* @param validation Whether to add these evaluations as validation or training
|
||||
* @param variableName The variable to evaluate
|
||||
* @param labelIndex The index of the label to evaluate against
|
||||
* @param evaluations The evaluations to run
|
||||
*/
|
||||
public Builder addEvaluations(boolean validation, @NonNull String variableName, int labelIndex, @NonNull IEvaluation... evaluations) {
|
||||
if (validation) {
|
||||
return validationEvaluation(variableName, labelIndex, evaluations);
|
||||
} else {
|
||||
return trainEvaluation(variableName, labelIndex, evaluations);
|
||||
}
|
||||
}
|
||||
|
||||
public ListenerEvaluations build() {
|
||||
return new ListenerEvaluations(trainEvaluations, trainEvaluationLabels, validationEvaluations, validationEvaluationLabels);
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners;
|
||||
|
||||
public enum ListenerResponse {
|
||||
CONTINUE, STOP;
|
||||
}
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners;
|
||||
|
||||
import org.nd4j.shade.guava.collect.Sets;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.nd4j.autodiff.samediff.SDVariable;
|
||||
import org.nd4j.autodiff.samediff.SameDiff;
|
||||
import org.nd4j.autodiff.samediff.internal.SameDiffOp;
|
||||
import org.nd4j.linalg.api.ndarray.INDArray;
|
||||
import org.nd4j.linalg.dataset.api.MultiDataSet;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public class ListenerVariables {
|
||||
|
||||
public static ListenerVariables empty() {
|
||||
return ListenerVariables.builder().build();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Set<String> trainingVariables;
|
||||
@NonNull
|
||||
private Set<String> validationVariables;
|
||||
@NonNull
|
||||
private Set<String> evaluationVariables;
|
||||
@NonNull
|
||||
private Set<String> inferenceVariables;
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get required training variables
|
||||
*/
|
||||
public Set<String> trainingVariables() {
|
||||
return trainingVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get required validation variables
|
||||
*/
|
||||
public Set<String> validationVariables() {
|
||||
return validationVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get required evaluation variables
|
||||
*/
|
||||
public Set<String> evaluationVariables() {
|
||||
return evaluationVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get required inference variables
|
||||
*/
|
||||
public Set<String> inferenceVariables() {
|
||||
return inferenceVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get required variables for specified op
|
||||
*/
|
||||
public Set<String> requiredVariables(Operation op) {
|
||||
switch (op) {
|
||||
case TRAINING:
|
||||
return trainingVariables;
|
||||
case TRAINING_VALIDATION:
|
||||
return validationVariables;
|
||||
case INFERENCE:
|
||||
return inferenceVariables;
|
||||
case EVALUATION:
|
||||
return evaluationVariables;
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown operation " + op);
|
||||
}
|
||||
|
||||
private ListenerVariables() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new ListenerVariables that contains the variables of this ListenerVariables and of other
|
||||
*/
|
||||
public ListenerVariables merge(ListenerVariables other) {
|
||||
return new ListenerVariables(
|
||||
Sets.newHashSet(Sets.union(trainingVariables, other.trainingVariables)),
|
||||
Sets.newHashSet(Sets.union(validationVariables, other.validationVariables)),
|
||||
Sets.newHashSet(Sets.union(evaluationVariables, other.evaluationVariables)),
|
||||
Sets.newHashSet(Sets.union(inferenceVariables, other.inferenceVariables)));
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public static class Builder {
|
||||
@NonNull
|
||||
private Set<String> trainingVariables = new HashSet<>();
|
||||
@NonNull
|
||||
private Set<String> validationVariables = new HashSet<>();
|
||||
@NonNull
|
||||
private Set<String> evaluationVariables = new HashSet<>();
|
||||
@NonNull
|
||||
private Set<String> inferenceVariables = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Add required variables for the specified op
|
||||
*
|
||||
* @param op The op to require the variable for
|
||||
*/
|
||||
public Builder requireVariables(@NonNull Operation op, @NonNull String... variables) {
|
||||
switch (op) {
|
||||
case TRAINING:
|
||||
trainingVariables.addAll(Arrays.asList(variables));
|
||||
break;
|
||||
case TRAINING_VALIDATION:
|
||||
validationVariables.addAll(Arrays.asList(variables));
|
||||
break;
|
||||
case INFERENCE:
|
||||
inferenceVariables.addAll(Arrays.asList(variables));
|
||||
break;
|
||||
case EVALUATION:
|
||||
evaluationVariables.addAll(Arrays.asList(variables));
|
||||
break;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add required variables for the specified op
|
||||
*
|
||||
* @param op The op to require the variable for
|
||||
*/
|
||||
public Builder requireVariables(@NonNull Operation op, @NonNull SDVariable... variables) {
|
||||
String[] names = new String[variables.length];
|
||||
|
||||
for (int i = 0; i < variables.length; i++)
|
||||
names[i] = variables[i].name();
|
||||
|
||||
return requireVariables(op, names);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add required variables for training
|
||||
*/
|
||||
public Builder trainingVariables(@NonNull String... variables) {
|
||||
return requireVariables(Operation.TRAINING, variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add required variables for training
|
||||
*/
|
||||
public Builder trainingVariables(@NonNull SDVariable... variables) {
|
||||
return requireVariables(Operation.TRAINING, variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add required variables for validation
|
||||
*/
|
||||
public Builder validationVariables(@NonNull String... variables) {
|
||||
return requireVariables(Operation.TRAINING_VALIDATION, variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add required variables for validation
|
||||
*/
|
||||
public Builder validationVariables(@NonNull SDVariable... variables) {
|
||||
return requireVariables(Operation.TRAINING_VALIDATION, variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add required variables for inference
|
||||
*/
|
||||
public Builder inferenceVariables(@NonNull String... variables) {
|
||||
return requireVariables(Operation.INFERENCE, variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add required variables for inference
|
||||
*/
|
||||
public Builder inferenceVariables(@NonNull SDVariable... variables) {
|
||||
return requireVariables(Operation.INFERENCE, variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add required variables for evaluation
|
||||
*/
|
||||
public Builder evaluationVariables(@NonNull String... variables) {
|
||||
return requireVariables(Operation.EVALUATION, variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add required variables for evaluation
|
||||
*/
|
||||
public Builder evaluationVariables(@NonNull SDVariable... variables) {
|
||||
return requireVariables(Operation.EVALUATION, variables);
|
||||
}
|
||||
|
||||
public ListenerVariables build() {
|
||||
return new ListenerVariables(trainingVariables, validationVariables, evaluationVariables, inferenceVariables);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import org.nd4j.common.base.Preconditions;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class Loss {
|
||||
|
||||
private final List<String> lossNames;
|
||||
private final double[] losses;
|
||||
|
||||
/**
|
||||
* @param lossNames Names of the losses
|
||||
* @param losses Values for each loss. Must be same length as lossNames
|
||||
*/
|
||||
public Loss(@NonNull List<String> lossNames, @NonNull double[] losses) {
|
||||
Preconditions.checkState(lossNames.size() == losses.length, "Expected equal number of loss names and loss values");
|
||||
this.lossNames = lossNames;
|
||||
this.losses = losses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Number of loss values (i.e., length of lossNames and losses)
|
||||
*/
|
||||
public int numLosses() {
|
||||
return lossNames.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Names of all of the loss components
|
||||
*/
|
||||
public List<String> lossNames() {
|
||||
return lossNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Values corresponding to each of the losses (same order as lossNames())
|
||||
*/
|
||||
public double[] lossValues() {
|
||||
return losses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specified loss by name
|
||||
*
|
||||
* @param lossName Name of the loss (must exist)
|
||||
* @return Specified loss value
|
||||
*/
|
||||
public double getLoss(@NonNull String lossName) {
|
||||
int idx = lossNames.indexOf(lossName);
|
||||
Preconditions.checkState(idx >= 0, "No loss with name \"%s\" exists. All loss names: %s", lossName, lossNames);
|
||||
return losses[idx];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The total loss (sum of all loss components)
|
||||
*/
|
||||
public double totalLoss() {
|
||||
double sum = 0.0;
|
||||
for (double d : losses) {
|
||||
sum += d;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public Loss copy() {
|
||||
return new Loss(lossNames, losses);
|
||||
}
|
||||
|
||||
public static Loss sum(List<Loss> losses) {
|
||||
|
||||
if (losses.isEmpty())
|
||||
return new Loss(Collections.<String>emptyList(), new double[0]);
|
||||
|
||||
double[] lossValues = new double[losses.get(0).losses.length];
|
||||
List<String> lossNames = new ArrayList<>(losses.get(0).lossNames);
|
||||
|
||||
for (int i = 0; i < losses.size(); i++) {
|
||||
Loss l = losses.get(i);
|
||||
Preconditions.checkState(l.losses.length == lossValues.length,
|
||||
"Loss %s has %s losses, the others before it had %s.", i, l.losses.length, lossValues.length);
|
||||
|
||||
Preconditions.checkState(l.lossNames.equals(lossNames),
|
||||
"Loss %s has different loss names from the others before it. Expected %s, got %s.",
|
||||
i, lossNames, l.lossNames);
|
||||
|
||||
for (int j = 0; j < lossValues.length; j++)
|
||||
lossValues[j] += l.losses[j];
|
||||
|
||||
}
|
||||
|
||||
return new Loss(lossNames, lossValues);
|
||||
}
|
||||
|
||||
public static Loss average(List<Loss> losses) {
|
||||
Loss sum = sum(losses);
|
||||
|
||||
for (int i = 0; i < sum.losses.length; i++) {
|
||||
sum.losses[i] /= losses.size();
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static Loss add(Loss a, Loss b) {
|
||||
Preconditions.checkState(a.lossNames.equals(b.lossNames),
|
||||
"Loss names differ. First loss has names %s, second has names %s.",
|
||||
a.lossNames, b.lossNames);
|
||||
|
||||
double[] lossValues = new double[a.losses.length];
|
||||
for (int i = 0; i < lossValues.length; i++)
|
||||
lossValues[i] = a.losses[i] + b.losses[i];
|
||||
|
||||
return new Loss(a.lossNames, lossValues);
|
||||
}
|
||||
|
||||
public static Loss sub(Loss a, Loss b) {
|
||||
Preconditions.checkState(a.lossNames.equals(b.lossNames),
|
||||
"Loss names differ. First loss has names %s, second has names %s.",
|
||||
a.lossNames, b.lossNames);
|
||||
|
||||
double[] lossValues = new double[a.losses.length];
|
||||
for (int i = 0; i < lossValues.length; i++)
|
||||
lossValues[i] = a.losses[i] - b.losses[i];
|
||||
|
||||
return new Loss(a.lossNames, lossValues);
|
||||
}
|
||||
|
||||
public static Loss div(Loss a, Number b) {
|
||||
double[] lossValues = new double[a.losses.length];
|
||||
for (int i = 0; i < lossValues.length; i++)
|
||||
lossValues[i] = a.losses[i] / b.doubleValue();
|
||||
|
||||
return new Loss(a.lossNames, lossValues);
|
||||
}
|
||||
|
||||
public Loss add(Loss other) {
|
||||
return add(this, other);
|
||||
}
|
||||
|
||||
public Loss sub(Loss other) {
|
||||
return sub(this, other);
|
||||
}
|
||||
|
||||
public Loss plus(Loss other) {
|
||||
return add(this, other);
|
||||
}
|
||||
|
||||
public Loss minus(Loss other) {
|
||||
return sub(this, other);
|
||||
}
|
||||
|
||||
public Loss div(Number other) {
|
||||
return div(this, other);
|
||||
}
|
||||
|
||||
}
|
||||
+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.autodiff.listeners;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.nd4j.autodiff.samediff.SDVariable;
|
||||
import org.nd4j.autodiff.samediff.SameDiff;
|
||||
|
||||
public enum Operation {
|
||||
/**
|
||||
* The training operation: {@link SameDiff#fit()} methods training step (everything except validation).
|
||||
*/
|
||||
TRAINING,
|
||||
/**
|
||||
* The training validation operation: the validation step during {@link SameDiff#fit()} methods.
|
||||
*/
|
||||
TRAINING_VALIDATION,
|
||||
/**
|
||||
* Inference operations: {@link SameDiff#output()}, {@link SameDiff#batchOutput()} and {@link SameDiff#exec(Map, String...)} ()} methods,
|
||||
* as well as {@link SameDiff#execBackwards(Map, Operation, String...)} methods.
|
||||
*/
|
||||
INFERENCE,
|
||||
/**
|
||||
* Evaluation operations: {@link SameDiff#evaluate()} methods.
|
||||
*/
|
||||
EVALUATION;
|
||||
|
||||
public boolean isTrainingPhase() {
|
||||
return this == TRAINING || this == TRAINING_VALIDATION;
|
||||
}
|
||||
|
||||
public boolean isValidation() {
|
||||
return this == TRAINING_VALIDATION;
|
||||
}
|
||||
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners.checkpoint;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Data
|
||||
public class Checkpoint implements Serializable {
|
||||
|
||||
private int checkpointNum;
|
||||
private long timestamp;
|
||||
private int iteration;
|
||||
private int epoch;
|
||||
private String filename;
|
||||
|
||||
public static String getFileHeader(){
|
||||
return "checkpointNum,timestamp,iteration,epoch,filename";
|
||||
}
|
||||
|
||||
public static Checkpoint fromFileString(String str){
|
||||
String[] split = str.split(",");
|
||||
if(split.length != 5){
|
||||
throw new IllegalStateException("Cannot parse checkpoint entry: expected 5 entries, got " + split.length
|
||||
+ " - values = " + Arrays.toString(split));
|
||||
}
|
||||
return new Checkpoint(
|
||||
Integer.parseInt(split[0]),
|
||||
Long.parseLong(split[1]),
|
||||
Integer.parseInt(split[2]),
|
||||
Integer.parseInt(split[3]),
|
||||
split[4]);
|
||||
}
|
||||
|
||||
public String toFileString(){
|
||||
return checkpointNum + "," + timestamp + "," + iteration + "," + epoch + "," + filename;
|
||||
}
|
||||
}
|
||||
+604
@@ -0,0 +1,604 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners.checkpoint;
|
||||
|
||||
|
||||
import org.nd4j.shade.guava.io.Files;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.nd4j.autodiff.listeners.At;
|
||||
import org.nd4j.autodiff.listeners.BaseListener;
|
||||
import org.nd4j.autodiff.listeners.ListenerResponse;
|
||||
import org.nd4j.autodiff.listeners.Loss;
|
||||
import org.nd4j.autodiff.listeners.records.LossCurve;
|
||||
import org.nd4j.autodiff.listeners.Operation;
|
||||
import org.nd4j.autodiff.samediff.SameDiff;
|
||||
import org.nd4j.common.base.Preconditions;
|
||||
import org.nd4j.linalg.dataset.api.MultiDataSet;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Slf4j
|
||||
public class CheckpointListener extends BaseListener implements Serializable {
|
||||
|
||||
private enum KeepMode {ALL, LAST, LAST_AND_EVERY};
|
||||
|
||||
private File rootDir;
|
||||
private String fileNamePrefix;
|
||||
private KeepMode keepMode;
|
||||
private int keepLast;
|
||||
private int keepEvery;
|
||||
private boolean logSaving;
|
||||
private boolean deleteExisting;
|
||||
private boolean saveUpdaterState;
|
||||
|
||||
private Integer saveEveryNEpochs;
|
||||
private Integer saveEveryNIterations;
|
||||
private boolean saveEveryNIterSinceLast;
|
||||
private Long saveEveryAmount;
|
||||
private TimeUnit saveEveryUnit;
|
||||
private Long saveEveryMs;
|
||||
private boolean saveEverySinceLast;
|
||||
|
||||
private int lastCheckpointNum = -1;
|
||||
private File checkpointRecordFile;
|
||||
|
||||
private Checkpoint lastCheckpoint;
|
||||
private long startTime = -1;
|
||||
private int startIter = -1;
|
||||
private Long lastSaveEveryMsNoSinceLast;
|
||||
|
||||
private CheckpointListener(Builder builder){
|
||||
this.rootDir = builder.rootDir;
|
||||
this.fileNamePrefix = builder.fileNamePrefix;
|
||||
this.keepMode = builder.keepMode;
|
||||
this.keepLast = builder.keepLast;
|
||||
this.keepEvery = builder.keepEvery;
|
||||
this.logSaving = builder.logSaving;
|
||||
this.deleteExisting = builder.deleteExisting;
|
||||
this.saveUpdaterState = builder.saveUpdaterState;
|
||||
|
||||
this.saveEveryNEpochs = builder.saveEveryNEpochs;
|
||||
this.saveEveryNIterations = builder.saveEveryNIterations;
|
||||
this.saveEveryNIterSinceLast = builder.saveEveryNIterSinceLast;
|
||||
this.saveEveryAmount = builder.saveEveryAmount;
|
||||
this.saveEveryUnit = builder.saveEveryUnit;
|
||||
this.saveEverySinceLast = builder.saveEverySinceLast;
|
||||
|
||||
if(saveEveryAmount != null){
|
||||
saveEveryMs = TimeUnit.MILLISECONDS.convert(saveEveryAmount, saveEveryUnit);
|
||||
}
|
||||
|
||||
if(!rootDir.exists()){
|
||||
rootDir.mkdir();
|
||||
}
|
||||
|
||||
this.checkpointRecordFile = new File(rootDir, "checkpointInfo.txt");
|
||||
if(this.checkpointRecordFile.exists() && this.checkpointRecordFile.length() > 0){
|
||||
|
||||
if(deleteExisting){
|
||||
//Delete any files matching:
|
||||
//"checkpoint_" + checkpointNum + "_" + modelType + ".zip";
|
||||
this.checkpointRecordFile.delete();
|
||||
File[] files = rootDir.listFiles();
|
||||
if(files != null && files.length > 0){
|
||||
for(File f : files){
|
||||
String name = f.getName();
|
||||
if(name.startsWith("checkpoint_") && (name.endsWith("MultiLayerNetwork.zip") || name.endsWith("ComputationGraph.zip"))){
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Detected existing checkpoint files at directory " + rootDir.getAbsolutePath() +
|
||||
". Use deleteExisting(true) to delete existing checkpoint files when present.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenerResponse epochEnd(SameDiff sameDiff, At at, LossCurve lossCurve, long epochTimeMillis) {
|
||||
if(saveEveryNEpochs != null && (at.epoch()+1) % saveEveryNEpochs == 0){
|
||||
//Save:
|
||||
saveCheckpoint(sameDiff, at);
|
||||
}
|
||||
//General saving conditions: don't need to check here - will check in iterationDone
|
||||
return ListenerResponse.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive(Operation operation) {
|
||||
return operation == Operation.TRAINING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void iterationDone(SameDiff sd, At at, MultiDataSet dataSet, Loss loss) {
|
||||
if (startTime < 0) {
|
||||
startTime = System.currentTimeMillis();
|
||||
startIter = at.iteration();
|
||||
return;
|
||||
}
|
||||
|
||||
//Check iterations saving condition:
|
||||
if(saveEveryNIterations != null){
|
||||
if(saveEveryNIterSinceLast){
|
||||
//Consider last saved model when deciding whether to save
|
||||
long lastSaveIter = (lastCheckpoint != null ? lastCheckpoint.getIteration() : startIter);
|
||||
if(at.iteration() - lastSaveIter >= saveEveryNIterations){
|
||||
saveCheckpoint(sd, at);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
//Same every N iterations, regardless of saving time
|
||||
if((at.iteration()+1) % saveEveryNIterations == 0){
|
||||
saveCheckpoint(sd, at);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Check time saving condition:
|
||||
long time = System.currentTimeMillis();
|
||||
if(saveEveryUnit != null){
|
||||
if(saveEverySinceLast){
|
||||
//Consider last saved when deciding whether to save
|
||||
long lastSaveTime = (lastCheckpoint != null ? lastCheckpoint.getTimestamp() : startTime);
|
||||
if((time - lastSaveTime) >= saveEveryMs){
|
||||
saveCheckpoint(sd, at);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
//Save periodically, regardless of when last model was saved
|
||||
long lastSave = (lastSaveEveryMsNoSinceLast != null ? lastSaveEveryMsNoSinceLast : startTime);
|
||||
if((time - lastSave) > saveEveryMs){
|
||||
saveCheckpoint(sd, at);
|
||||
lastSaveEveryMsNoSinceLast = time;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveCheckpoint(SameDiff sd, At at) {
|
||||
try{
|
||||
saveCheckpointHelper(sd, at);
|
||||
} catch (Exception e){
|
||||
throw new RuntimeException("Error saving checkpoint", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveCheckpointHelper(SameDiff model, At at) throws Exception {
|
||||
if(!checkpointRecordFile.exists()){
|
||||
checkpointRecordFile.createNewFile();
|
||||
writeCheckpointInfo(Checkpoint.getFileHeader() + "\n", checkpointRecordFile);
|
||||
}
|
||||
|
||||
Checkpoint c = new Checkpoint(++lastCheckpointNum, System.currentTimeMillis(), at.iteration(), at.epoch(),null);
|
||||
String filename = getFileName(lastCheckpointNum, at, c.getTimestamp());
|
||||
c.setFilename(filename);
|
||||
|
||||
File saveFile = new File(rootDir, c.getFilename());
|
||||
model.save(saveFile, this.saveUpdaterState);
|
||||
|
||||
String s = c.toFileString();
|
||||
writeCheckpointInfo(s + "\n", checkpointRecordFile);
|
||||
|
||||
if(logSaving){
|
||||
log.info("Model checkpoint saved: epoch {}, iteration {}, path: {}", c.getEpoch(), c.getIteration(),
|
||||
new File(rootDir, c.getFilename()).getPath() );
|
||||
}
|
||||
this.lastCheckpoint = c;
|
||||
|
||||
|
||||
//Finally: determine if we should delete some old models...
|
||||
if(keepMode == null || keepMode == KeepMode.ALL){
|
||||
return;
|
||||
} else if(keepMode == KeepMode.LAST){
|
||||
List<Checkpoint> checkpoints = availableCheckpoints();
|
||||
Iterator<Checkpoint> iter = checkpoints.iterator();
|
||||
while(checkpoints.size() > keepLast){
|
||||
Checkpoint toRemove = iter.next();
|
||||
File f = getFileForCheckpoint(toRemove);
|
||||
f.delete();
|
||||
iter.remove();
|
||||
}
|
||||
} else {
|
||||
//Keep mode: last N and every M
|
||||
for(Checkpoint cp : availableCheckpoints()){
|
||||
if(cp.getCheckpointNum() > 0 && (cp.getCheckpointNum()+1) % keepEvery == 0){
|
||||
//One of the "every M to keep" models
|
||||
continue;
|
||||
} else if(cp.getCheckpointNum() > lastCheckpointNum - keepLast ){ //Example: latest is 5, keep last 2 -> keep checkpoints 4 and 5
|
||||
//One of last N to keep
|
||||
continue;
|
||||
}
|
||||
//Otherwise: delete file
|
||||
File f = getFileForCheckpoint(cp);
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Filename format: "<prefix>_checkpoint-#_epoch-#_iter-#_YYYY-MM-dd_HH-MM-ss.bin"
|
||||
private String getFileName(int checkpointNum, At at, long time){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(fileNamePrefix != null){
|
||||
sb.append(fileNamePrefix);
|
||||
if(!fileNamePrefix.endsWith("_")){
|
||||
sb.append("_");
|
||||
}
|
||||
}
|
||||
sb.append("checkpoint-")
|
||||
.append(checkpointNum)
|
||||
.append("_epoch-").append(at.epoch())
|
||||
.append("_iter-").append(at.iteration());
|
||||
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd_HH-mm-ss");
|
||||
String date = sdf.format(new Date(time));
|
||||
sb.append("_").append(date)
|
||||
.append(".bin");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String writeCheckpointInfo(String str, File f){
|
||||
try {
|
||||
if(!f.exists()){
|
||||
f.createNewFile();
|
||||
}
|
||||
Files.append(str, f, StandardCharsets.UTF_8);
|
||||
} catch (IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* List all available checkpoints. A checkpoint is 'available' if the file can be loaded. Any checkpoint files that
|
||||
* have been automatically deleted (given the configuration) will not be returned here.
|
||||
*
|
||||
* @return List of checkpoint files that can be loaded
|
||||
*/
|
||||
public List<Checkpoint> availableCheckpoints(){
|
||||
if(!checkpointRecordFile.exists()){
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return availableCheckpoints(rootDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* List all available checkpoints. A checkpoint is 'available' if the file can be loaded. Any checkpoint files that
|
||||
* have been automatically deleted (given the configuration) will not be returned here.
|
||||
* Note that the checkpointInfo.txt file must exist, as this stores checkpoint information
|
||||
*
|
||||
* @return List of checkpoint files that can be loaded from the specified directory
|
||||
*/
|
||||
public static List<Checkpoint> availableCheckpoints(File directory){
|
||||
File checkpointRecordFile = new File(directory, "checkpointInfo.txt");
|
||||
Preconditions.checkState(checkpointRecordFile.exists(), "Could not find checkpoint record file at expected path %s", checkpointRecordFile.getAbsolutePath());
|
||||
|
||||
List<String> lines;
|
||||
try(InputStream is = new BufferedInputStream(new FileInputStream(checkpointRecordFile))){
|
||||
lines = IOUtils.readLines(is);
|
||||
} catch (IOException e){
|
||||
throw new RuntimeException("Error loading checkpoint data from file: " + checkpointRecordFile.getAbsolutePath(), e);
|
||||
}
|
||||
|
||||
List<Checkpoint> out = new ArrayList<>(lines.size()-1); //Assume first line is header
|
||||
for( int i=1; i<lines.size(); i++ ){
|
||||
Checkpoint c = Checkpoint.fromFileString(lines.get(i));
|
||||
if(new File(directory, c.getFilename()).exists()){
|
||||
out.add(c);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the most recent checkpoint, if one exists - otherwise returns null
|
||||
* @return Checkpoint
|
||||
*/
|
||||
public Checkpoint lastCheckpoint(){
|
||||
if(!checkpointRecordFile.exists()){
|
||||
return null;
|
||||
}
|
||||
return lastCheckpoint(rootDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the most recent checkpoint, if one exists - otherwise returns null
|
||||
* @param rootDir Root direcotry for the checkpoint files
|
||||
* @return Checkpoint
|
||||
*/
|
||||
public static Checkpoint lastCheckpoint(File rootDir){
|
||||
List<Checkpoint> all = availableCheckpoints(rootDir);
|
||||
if(all.isEmpty()){
|
||||
return null;
|
||||
}
|
||||
return all.get(all.size()-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model file for the given checkpoint. Checkpoint model file must exist
|
||||
*
|
||||
* @param checkpoint Checkpoint to get the model file for
|
||||
* @return Model file for the checkpoint
|
||||
*/
|
||||
public File getFileForCheckpoint(Checkpoint checkpoint){
|
||||
return getFileForCheckpoint(checkpoint.getCheckpointNum());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model file for the given checkpoint number. Checkpoint model file must exist
|
||||
*
|
||||
* @param checkpointNum Checkpoint number to get the model file for
|
||||
* @return Model file for the checkpoint
|
||||
*/
|
||||
public File getFileForCheckpoint(int checkpointNum) {
|
||||
return getFileForCheckpoint(rootDir, checkpointNum);
|
||||
}
|
||||
|
||||
public static File getFileForCheckpoint(File rootDir, int checkpointNum){
|
||||
//Scan the root directory, for a file matching the checkpoint filename pattern:
|
||||
//Filename format: "<prefix>_checkpoint-#_epoch-#_iter-#_YYYY-MM-dd_HH-MM-ss.bin"
|
||||
|
||||
if(checkpointNum < 0){
|
||||
throw new IllegalArgumentException("Invalid checkpoint number: " + checkpointNum);
|
||||
}
|
||||
|
||||
String contains = "_checkpoint-" + checkpointNum + "_epoch-";
|
||||
|
||||
File[] allFiles = rootDir.listFiles();
|
||||
if(allFiles != null){
|
||||
for(File f : allFiles){
|
||||
if(f.getAbsolutePath().contains(contains)){
|
||||
return f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Model file for checkpoint " + checkpointNum + " does not exist");
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a given checkpoint number
|
||||
*
|
||||
* @param loadUpdaterState If true: load the updater state. See {@link SameDiff#load(File, boolean)} for more details
|
||||
*
|
||||
*/
|
||||
public SameDiff loadCheckpoint(int checkpointNum, boolean loadUpdaterState){
|
||||
return loadCheckpoint(rootDir, checkpointNum, loadUpdaterState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a SameDiff instance for the given checkpoint that resides in the specified root directory
|
||||
*
|
||||
* @param rootDir Directory that the checkpoint resides in
|
||||
* @param checkpointNum Checkpoint model number to load
|
||||
* @param loadUpdaterState If true: load the updater state. See {@link SameDiff#load(File, boolean)} for more details
|
||||
* @return The loaded model
|
||||
*/
|
||||
public static SameDiff loadCheckpoint(File rootDir, int checkpointNum, boolean loadUpdaterState) {
|
||||
File f = getFileForCheckpoint(rootDir, checkpointNum);
|
||||
return SameDiff.load(f, loadUpdaterState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the last (most recent) checkpoint from the specified root directory
|
||||
* @param rootDir Root directory to load checpoint from
|
||||
* @return ComputationGraph for last checkpoint
|
||||
*/
|
||||
public static SameDiff loadLastCheckpoint(File rootDir, boolean loadUpdaterState){
|
||||
Checkpoint last = lastCheckpoint(rootDir);
|
||||
return loadCheckpoint(rootDir, last.getCheckpointNum(), loadUpdaterState);
|
||||
}
|
||||
|
||||
public static Builder builder(@NonNull File rootDir){
|
||||
return new Builder(rootDir);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private File rootDir;
|
||||
private String fileNamePrefix = "SameDiff";
|
||||
private KeepMode keepMode;
|
||||
private int keepLast;
|
||||
private int keepEvery;
|
||||
private boolean saveUpdaterState = true;
|
||||
private boolean logSaving = true;
|
||||
private boolean deleteExisting = false;
|
||||
|
||||
private Integer saveEveryNEpochs;
|
||||
private Integer saveEveryNIterations;
|
||||
private boolean saveEveryNIterSinceLast;
|
||||
private Long saveEveryAmount;
|
||||
private TimeUnit saveEveryUnit;
|
||||
private boolean saveEverySinceLast;
|
||||
|
||||
/**
|
||||
* @param rootDir Root directory to save models to
|
||||
*/
|
||||
public Builder(@NonNull String rootDir){
|
||||
this(new File(rootDir));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rootDir Root directory to save models to
|
||||
*/
|
||||
public Builder(@NonNull File rootDir){
|
||||
this.rootDir = rootDir;
|
||||
}
|
||||
|
||||
public Builder fileNamePrefix(String fileNamePrefix){
|
||||
this.fileNamePrefix = fileNamePrefix;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a model at the end of every epoch
|
||||
*/
|
||||
public Builder saveEveryEpoch(){
|
||||
return saveEveryNEpochs(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a model at the end of every N epochs
|
||||
*/
|
||||
public Builder saveEveryNEpochs(int n){
|
||||
this.saveEveryNEpochs = n;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a model every N iterations
|
||||
*/
|
||||
public Builder saveEveryNIterations(int n){
|
||||
return saveEveryNIterations(n, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a model every N iterations (if sinceLast == false), or if N iterations have passed since
|
||||
* the last model vas saved (if sinceLast == true)
|
||||
*/
|
||||
public Builder saveEveryNIterations(int n, boolean sinceLast){
|
||||
this.saveEveryNIterations = n;
|
||||
this.saveEveryNIterSinceLast = sinceLast;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a model periodically
|
||||
*
|
||||
* @param amount Quantity of the specified time unit
|
||||
* @param timeUnit Time unit
|
||||
*/
|
||||
public Builder saveEvery(long amount, TimeUnit timeUnit){
|
||||
return saveEvery(amount, timeUnit, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a model periodically (if sinceLast == false), or if the specified amount of time has elapsed since
|
||||
* the last model was saved (if sinceLast == true)
|
||||
*
|
||||
* @param amount Quantity of the specified time unit
|
||||
* @param timeUnit Time unit
|
||||
*/
|
||||
public Builder saveEvery(long amount, TimeUnit timeUnit, boolean sinceLast){
|
||||
this.saveEveryAmount = amount;
|
||||
this.saveEveryUnit = timeUnit;
|
||||
this.saveEverySinceLast = sinceLast;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep all model checkpoints - i.e., don't delete any. Note that this is the default.
|
||||
*/
|
||||
public Builder keepAll(){
|
||||
this.keepMode = KeepMode.ALL;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep only the last N most recent model checkpoint files. Older checkpoints will automatically be deleted.
|
||||
* @param n Number of most recent checkpoints to keep
|
||||
*/
|
||||
public Builder keepLast(int n){
|
||||
if(n <= 0){
|
||||
throw new IllegalArgumentException("Number of model files to keep should be > 0 (got: " + n + ")");
|
||||
}
|
||||
this.keepMode = KeepMode.LAST;
|
||||
this.keepLast = n;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep the last N most recent model checkpoint files, <i>and</i> every M checkpoint files.<br>
|
||||
* For example: suppose you save every 100 iterations, for 2050 iteration, and use keepLastAndEvery(3,5).
|
||||
* This means after 2050 iterations you would have saved 20 checkpoints - some of which will be deleted.
|
||||
* Those remaining in this example: iterations 500, 1000, 1500, 1800, 1900, 2000.
|
||||
* @param nLast Most recent checkpoints to keep
|
||||
* @param everyN Every N checkpoints to keep (regardless of age)
|
||||
*/
|
||||
public Builder keepLastAndEvery(int nLast, int everyN){
|
||||
if(nLast <= 0){
|
||||
throw new IllegalArgumentException("Most recent number of model files to keep should be > 0 (got: "
|
||||
+ nLast + ")");
|
||||
}
|
||||
if(everyN <= 0){
|
||||
throw new IllegalArgumentException("Every n model files to keep should be > 0 (got: "
|
||||
+ everyN + ")");
|
||||
}
|
||||
|
||||
this.keepMode = KeepMode.LAST_AND_EVERY;
|
||||
this.keepLast = nLast;
|
||||
this.keepEvery = everyN;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true (the default) log a message every time a model is saved
|
||||
*
|
||||
* @param logSaving Whether checkpoint saves should be logged or not
|
||||
*/
|
||||
public Builder logSaving(boolean logSaving){
|
||||
this.logSaving = logSaving;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the updater state (history/state for Adam, Nesterov momentum, etc) should be saved with each checkpoint.<br>
|
||||
* Updater state is saved by default.
|
||||
* If you expect to continue training on any of the checkpoints, this should be set to true. However, it will increase
|
||||
* the file size.
|
||||
* @param saveUpdaterState If true: updater state will be saved with checkpoints. False: not saved.
|
||||
*/
|
||||
public Builder saveUpdaterState(boolean saveUpdaterState){
|
||||
this.saveUpdaterState = saveUpdaterState;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the checkpoint listener is set to save to a non-empty directory, should the CheckpointListener-related
|
||||
* content be deleted?<br>
|
||||
* This is disabled by default (and instead, an exception will be thrown if existing data is found)<br>
|
||||
* WARNING: Be careful when enabling this, as it deletes all saved checkpoint models in the specified directory!
|
||||
*/
|
||||
public Builder deleteExisting(boolean deleteExisting){
|
||||
this.deleteExisting = deleteExisting;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CheckpointListener build(){
|
||||
if(saveEveryNEpochs == null && saveEveryAmount == null && saveEveryNIterations == null){
|
||||
throw new IllegalStateException("Cannot construct listener: no models will be saved (must use at least" +
|
||||
" one of: save every N epochs, every N iterations, or every T time periods)");
|
||||
}
|
||||
|
||||
return new CheckpointListener(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners.debugging;
|
||||
|
||||
import lombok.NonNull;
|
||||
import org.nd4j.autodiff.listeners.At;
|
||||
import org.nd4j.autodiff.listeners.BaseListener;
|
||||
import org.nd4j.autodiff.listeners.Operation;
|
||||
import org.nd4j.autodiff.samediff.SameDiff;
|
||||
import org.nd4j.autodiff.samediff.internal.SameDiffOp;
|
||||
import org.nd4j.common.base.Preconditions;
|
||||
import org.nd4j.linalg.api.buffer.DataType;
|
||||
import org.nd4j.linalg.api.ndarray.INDArray;
|
||||
import org.nd4j.linalg.api.ops.OpContext;
|
||||
import org.nd4j.linalg.api.ops.impl.transforms.pairwise.bool.Xor;
|
||||
import org.nd4j.linalg.dataset.api.MultiDataSet;
|
||||
import org.nd4j.linalg.factory.Nd4j;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ArraySavingListener extends BaseListener {
|
||||
|
||||
protected final File dir;
|
||||
protected int count = 0;
|
||||
|
||||
public ArraySavingListener(@NonNull File dir){
|
||||
|
||||
if(!dir.exists()){
|
||||
dir.mkdir();
|
||||
}
|
||||
|
||||
if(dir.listFiles() != null && dir.listFiles().length > 0){
|
||||
throw new IllegalStateException("Directory is not empty: " + dir.getAbsolutePath());
|
||||
}
|
||||
|
||||
this.dir = dir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive(Operation operation) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void opExecution(SameDiff sd, At at, MultiDataSet batch, SameDiffOp op, OpContext opContext, INDArray[] outputs) {
|
||||
List<String> outNames = op.getOutputsOfOp();
|
||||
for(int i=0; i<outputs.length; i++ ){
|
||||
String filename = (count++) + "_" + outNames.get(i).replaceAll("/", "__") + ".bin";
|
||||
File outFile = new File(dir, filename);
|
||||
|
||||
INDArray arr = outputs[i];
|
||||
try {
|
||||
Nd4j.saveBinary(arr, outFile);
|
||||
System.out.println(outFile.getAbsolutePath());
|
||||
} catch (IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void compare(File dir1, File dir2, double eps) throws Exception {
|
||||
File[] files1 = dir1.listFiles();
|
||||
File[] files2 = dir2.listFiles();
|
||||
Preconditions.checkNotNull(files1, "No files in directory 1: %s", dir1);
|
||||
Preconditions.checkNotNull(files2, "No files in directory 2: %s", dir2);
|
||||
Preconditions.checkState(files1.length == files2.length, "Different number of files: %s vs %s", files1.length, files2.length);
|
||||
|
||||
Map<String,File> m1 = toMap(files1);
|
||||
Map<String,File> m2 = toMap(files2);
|
||||
|
||||
for(File f : files1){
|
||||
String name = f.getName();
|
||||
String varName = name.substring(name.indexOf('_') + 1, name.length()-4); //Strip "x_" and ".bin"
|
||||
File f2 = m2.get(varName);
|
||||
|
||||
INDArray arr1 = Nd4j.readBinary(f);
|
||||
INDArray arr2 = Nd4j.readBinary(f2);
|
||||
|
||||
//TODO String arrays won't work here!
|
||||
boolean eq = arr1.equalsWithEps(arr2, eps);
|
||||
if(eq){
|
||||
System.out.println("Equals: " + varName.replaceAll("__", "/"));
|
||||
} else {
|
||||
if(arr1.dataType() == DataType.BOOL){
|
||||
INDArray xor = Nd4j.exec(new Xor(arr1, arr2));
|
||||
int count = xor.castTo(DataType.INT).sumNumber().intValue();
|
||||
System.out.println("FAILS: " + varName.replaceAll("__", "/") + " - boolean, # differences = " + count);
|
||||
System.out.println("\t" + f.getAbsolutePath());
|
||||
System.out.println("\t" + f2.getAbsolutePath());
|
||||
xor.close();
|
||||
} else {
|
||||
INDArray sub = arr1.sub(arr2);
|
||||
INDArray diff = Nd4j.math.abs(sub);
|
||||
double maxDiff = diff.maxNumber().doubleValue();
|
||||
System.out.println("FAILS: " + varName.replaceAll("__", "/") + " - max difference = " + maxDiff);
|
||||
System.out.println("\t" + f.getAbsolutePath());
|
||||
System.out.println("\t" + f2.getAbsolutePath());
|
||||
sub.close();
|
||||
diff.close();
|
||||
}
|
||||
}
|
||||
arr1.close();
|
||||
arr2.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String,File> toMap(File[] files){
|
||||
Map<String,File> ret = new HashMap<>();
|
||||
for(File f : files) {
|
||||
String name = f.getName();
|
||||
String varName = name.substring(name.indexOf('_') + 1, name.length() - 4); //Strip "x_" and ".bin"
|
||||
ret.put(varName, f);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners.debugging;
|
||||
|
||||
import org.nd4j.autodiff.listeners.At;
|
||||
import org.nd4j.autodiff.listeners.BaseListener;
|
||||
import org.nd4j.autodiff.listeners.Operation;
|
||||
import org.nd4j.autodiff.samediff.SameDiff;
|
||||
import org.nd4j.autodiff.samediff.internal.SameDiffOp;
|
||||
import org.nd4j.common.primitives.Counter;
|
||||
import org.nd4j.linalg.api.ndarray.INDArray;
|
||||
import org.nd4j.linalg.api.ops.OpContext;
|
||||
import org.nd4j.linalg.api.ops.impl.controlflow.compat.*;
|
||||
import org.nd4j.linalg.dataset.api.MultiDataSet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ControlflowListener extends BaseListener {
|
||||
|
||||
private Counter<String> entersExecuted = new Counter<>();
|
||||
private Counter<String> exitsExecuted = new Counter<>();
|
||||
private Counter<String> mergesExecuted = new Counter<>();
|
||||
private Counter<String> nextIterationExecuted = new Counter<>();
|
||||
|
||||
private Counter<String> switchesExecuted = new Counter<>();
|
||||
|
||||
private Counter<String> loopCondExecuted = new Counter<>();
|
||||
|
||||
@Override
|
||||
public boolean isActive(Operation operation) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void operationStart(SameDiff sd, Operation op) {
|
||||
super.operationStart(sd, op);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void operationEnd(SameDiff sd, Operation op) {
|
||||
super.operationEnd(sd, op);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preOpExecution(SameDiff sd, At at, SameDiffOp op, OpContext opContext) {
|
||||
super.preOpExecution(sd, at, op, opContext);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void opExecution(SameDiff sd, At at, MultiDataSet batch, SameDiffOp op, OpContext opContext, INDArray[] outputs) {
|
||||
super.opExecution(sd, at, batch, op, opContext, outputs);
|
||||
if(op.getOp() instanceof Enter) {
|
||||
entersExecuted.incrementCount(op.getName(),1.0);
|
||||
} else if(op.getOp() instanceof Exit) {
|
||||
exitsExecuted.incrementCount(op.getName(),1.0);
|
||||
} else if(op.getOp() instanceof NextIteration) {
|
||||
nextIterationExecuted.incrementCount(op.getName(),1.0);
|
||||
} else if(op.getOp() instanceof Switch) {
|
||||
switchesExecuted.incrementCount(op.getName(),1.0);
|
||||
} else if(op.getOp() instanceof Merge) {
|
||||
mergesExecuted.incrementCount(op.getName(),1.0);
|
||||
} else if(op.getOp() instanceof LoopCond) {
|
||||
loopCondExecuted.incrementCount(op.getName(),1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
+252
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners.debugging;
|
||||
|
||||
import lombok.val;
|
||||
import org.nd4j.autodiff.functions.DifferentialFunction;
|
||||
import org.nd4j.autodiff.listeners.At;
|
||||
import org.nd4j.autodiff.listeners.BaseListener;
|
||||
import org.nd4j.autodiff.listeners.Operation;
|
||||
import org.nd4j.autodiff.samediff.SameDiff;
|
||||
import org.nd4j.autodiff.samediff.internal.SameDiffOp;
|
||||
import org.nd4j.linalg.api.buffer.DataType;
|
||||
import org.nd4j.linalg.api.ndarray.INDArray;
|
||||
import org.nd4j.linalg.api.ops.CustomOp;
|
||||
import org.nd4j.linalg.api.ops.Op;
|
||||
import org.nd4j.linalg.api.ops.OpContext;
|
||||
import org.nd4j.linalg.api.ops.ScalarOp;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ExecDebuggingListener extends BaseListener {
|
||||
|
||||
public enum PrintMode {OPS_ONLY, SHAPES_ONLY, REPRODUCE}
|
||||
|
||||
private final PrintMode printMode;
|
||||
private final int maxIterations;
|
||||
private final boolean logIter;
|
||||
|
||||
private long printIterations = 0;
|
||||
private int lastIter = -1;
|
||||
private int stepThisIter = 0;
|
||||
|
||||
/**
|
||||
* @param printMode Print mode, see {@link PrintMode}
|
||||
* @param maxIterations Maximum number of iterations to print. <= 0 for "all iterations"
|
||||
* @param logIter If true: prefix iteration/epoch, such as "(iter=1,epoch=0,op=3)" to the output
|
||||
*/
|
||||
public ExecDebuggingListener(PrintMode printMode, int maxIterations, boolean logIter){
|
||||
this.printMode = printMode;
|
||||
this.maxIterations = maxIterations;
|
||||
this.logIter = logIter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive(Operation operation) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preOpExecution(SameDiff sd, At at, SameDiffOp op, OpContext opContext) {
|
||||
if(lastIter != at.iteration()){
|
||||
lastIter = at.iteration();
|
||||
stepThisIter = 0;
|
||||
printIterations++;
|
||||
}
|
||||
|
||||
if(maxIterations > 0 && printIterations > maxIterations){
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(logIter){
|
||||
sb.append("(iter=").append(at.iteration())
|
||||
.append(",epoch=").append(at.epoch())
|
||||
.append(",");
|
||||
}
|
||||
sb.append("op=").append(stepThisIter++)
|
||||
.append(logIter ? ") " : " - ");
|
||||
|
||||
DifferentialFunction df = op.getOp();
|
||||
sb.append(op.getOp().getClass().getName());
|
||||
CustomOp co = df instanceof CustomOp ? (CustomOp) df : null;
|
||||
Op lOp = df instanceof Op ? (Op) df : null;
|
||||
if(printMode == PrintMode.OPS_ONLY){
|
||||
sb.append("\n");
|
||||
} else if(printMode == PrintMode.SHAPES_ONLY){
|
||||
if(co != null){
|
||||
if(co.iArgs() != null && co.iArgs().length > 0) {
|
||||
sb.append("\n\tiArgs=").append(Arrays.toString(co.iArgs()));
|
||||
}
|
||||
if(co.bArgs() != null && co.bArgs().length > 0) {
|
||||
sb.append("\n\tbArgs=").append(Arrays.toString(co.bArgs()));
|
||||
}
|
||||
if(co.tArgs() != null && co.tArgs().length > 0) {
|
||||
sb.append("\n\ttArgs=").append(Arrays.toString(co.tArgs()));
|
||||
}
|
||||
val inputs = co.inputArguments();
|
||||
val outputs = co.outputArguments();
|
||||
if(inputs != null ) {
|
||||
for (int i = 0; i < inputs.size(); i++) {
|
||||
sb.append("\n\tInput[").append(i).append("]=").append(inputs.get(i).shapeInfoToString());
|
||||
}
|
||||
}
|
||||
if(outputs != null ) {
|
||||
for (int i = 0; i < outputs.size(); i++) {
|
||||
sb.append("\n\tOutputs[").append(i).append("]=").append(outputs.get(i).shapeInfoToString());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(lOp.x() != null) {
|
||||
sb.append("\n\tx: ").append(lOp.x().shapeInfoToString());
|
||||
}
|
||||
if(lOp.y() != null) {
|
||||
sb.append("\n\ty: ").append(lOp.y().shapeInfoToString());
|
||||
}
|
||||
if(lOp.z() != null) {
|
||||
sb.append("\n\tz: ").append(lOp.z().shapeInfoToString());
|
||||
}
|
||||
if(lOp instanceof ScalarOp){
|
||||
INDArray scalar = ((ScalarOp)lOp).scalar();
|
||||
if(scalar != null){
|
||||
sb.append("\n\tscalar: ").append(scalar.shapeInfoToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.append("\n");
|
||||
} else if(printMode == PrintMode.REPRODUCE){
|
||||
sb.append("\n");
|
||||
if(co != null){
|
||||
sb.append("DynamicCustomOp op = new ").append(co.getClass().getName()).append("();\n");
|
||||
if(co.iArgs() != null && co.iArgs().length > 0 ){
|
||||
sb.append("op.addIArgument(").append(Arrays.toString(co.iArgs()).replaceAll("[\\[\\]]", "")).append(");\n");
|
||||
}
|
||||
if(co.bArgs() != null && co.bArgs().length > 0 ){
|
||||
sb.append("op.addBArgument(").append(Arrays.toString(co.bArgs()).replaceAll("[\\[\\]]", "")).append(");\n");
|
||||
}
|
||||
if(co.tArgs() != null && co.tArgs().length > 0 ){
|
||||
sb.append("op.addTArgument(").append(Arrays.toString(co.tArgs()).replaceAll("[\\[\\]]", "")).append(");\n");
|
||||
}
|
||||
val inputs = co.inputArguments();
|
||||
val outputs = co.outputArguments();
|
||||
if(inputs != null ) {
|
||||
sb.append("INDArray[] inputs = new INDArray[").append(inputs.size()).append("];\n");
|
||||
for (int i = 0; i < inputs.size(); i++) {
|
||||
sb.append("inputs[").append(i).append("] = ");
|
||||
sb.append(createString(inputs.get(i)))
|
||||
.append(";\n");
|
||||
}
|
||||
sb.append("op.addInputArgument(inputs);\n");
|
||||
}
|
||||
if(outputs != null ) {
|
||||
sb.append("INDArray[] outputs = new INDArray[").append(outputs.size()).append("];\n");
|
||||
for (int i = 0; i < outputs.size(); i++) {
|
||||
sb.append("outputs[").append(i).append("] = ");
|
||||
sb.append(createString(outputs.get(i)))
|
||||
.append(";\n");
|
||||
}
|
||||
sb.append("op.addOutputArgument(outputs);\n");
|
||||
}
|
||||
} else {
|
||||
sb.append("Op op = new ").append(op.getClass().getName()).append("();\n");
|
||||
if(lOp.x() != null) {
|
||||
sb.append("op.setX(").append(createString(lOp.x())).append(");\n");
|
||||
}
|
||||
if(lOp.y() != null) {
|
||||
sb.append("op.setY(").append(createString(lOp.y())).append(");\n");
|
||||
}
|
||||
if(lOp.z() != null) {
|
||||
sb.append("op.setZ").append(createString(lOp.z())).append(");\n");
|
||||
}
|
||||
if(lOp instanceof ScalarOp){
|
||||
INDArray scalar = ((ScalarOp)lOp).scalar();
|
||||
if(scalar != null){
|
||||
sb.append("((ScalarOp)op).setScalar(").append(createString(scalar)).append(");\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.append("Nd4j.exec(op);\n");
|
||||
}
|
||||
|
||||
System.out.print(sb);
|
||||
}
|
||||
|
||||
private static String createString(INDArray arr) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if(arr.isEmpty()){
|
||||
sb.append("Nd4j.empty(DataType.").append(arr.dataType()).append(");");
|
||||
} else {
|
||||
sb.append("Nd4j.createFromArray(");
|
||||
|
||||
DataType dt = arr.dataType();
|
||||
switch (dt){
|
||||
case DOUBLE:
|
||||
double[] dArr = arr.dup().data().asDouble();
|
||||
sb.append(Arrays.toString(dArr).replaceAll("[\\[\\]]", ""));
|
||||
break;
|
||||
case FLOAT:
|
||||
case HALF:
|
||||
case BFLOAT16:
|
||||
float[] fArr = arr.dup().data().asFloat();
|
||||
sb.append(Arrays.toString(fArr)
|
||||
.replaceAll(",", "f,")
|
||||
.replaceAll("]", "f")
|
||||
.replaceAll("[\\[\\]]", ""));
|
||||
break;
|
||||
case LONG:
|
||||
case UINT32:
|
||||
case UINT64:
|
||||
long[] lArr = arr.dup().data().asLong();
|
||||
sb.append(Arrays.toString(lArr)
|
||||
.replaceAll(",", "L,")
|
||||
.replaceAll("]", "L")
|
||||
.replaceAll("[\\[\\]]", ""));
|
||||
break;
|
||||
case INT:
|
||||
case SHORT:
|
||||
case UBYTE:
|
||||
case BYTE:
|
||||
case UINT16:
|
||||
case BOOL:
|
||||
int[] iArr = arr.dup().data().asInt();
|
||||
sb.append(Arrays.toString(iArr).replaceAll("[\\[\\]]", ""));
|
||||
break;
|
||||
case UTF8:
|
||||
break;
|
||||
case COMPRESSED:
|
||||
case UNKNOWN:
|
||||
break;
|
||||
}
|
||||
|
||||
sb.append(").reshape(").append(Arrays.toString(arr.shape()).replaceAll("[\\[\\]]", ""))
|
||||
.append(")");
|
||||
|
||||
if(dt == DataType.HALF || dt == DataType.BFLOAT16 || dt == DataType.UINT32 || dt == DataType.UINT64 ||
|
||||
dt == DataType.SHORT || dt == DataType.UBYTE || dt == DataType.BYTE || dt == DataType.UINT16 || dt == DataType.BOOL){
|
||||
sb.append(".cast(DataType.").append(arr.dataType()).append(")");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners.debugging;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
|
||||
import org.nd4j.autodiff.listeners.At;
|
||||
import org.nd4j.autodiff.listeners.BaseListener;
|
||||
import org.nd4j.autodiff.listeners.Operation;
|
||||
import org.nd4j.autodiff.samediff.SameDiff;
|
||||
import org.nd4j.autodiff.samediff.internal.SameDiffOp;
|
||||
import org.nd4j.linalg.api.ndarray.INDArray;
|
||||
import org.nd4j.linalg.api.ops.DynamicCustomOp;
|
||||
import org.nd4j.linalg.api.ops.Op;
|
||||
import org.nd4j.linalg.api.ops.OpContext;
|
||||
import org.nd4j.linalg.dataset.api.MultiDataSet;
|
||||
import org.nd4j.linalg.factory.Nd4j;
|
||||
import org.nd4j.common.util.ArrayUtil;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.*;
|
||||
|
||||
@Getter
|
||||
public class OpBenchmarkListener extends BaseListener {
|
||||
|
||||
public enum Mode {SINGLE_ITER_PRINT, AGGREGATE}
|
||||
|
||||
private final Operation operation;
|
||||
private final Mode mode;
|
||||
private final long minRuntime;
|
||||
private Map<String,OpExec> aggregateModeMap;
|
||||
|
||||
@Getter(AccessLevel.PRIVATE)
|
||||
private long start;
|
||||
@Getter(AccessLevel.PRIVATE)
|
||||
private boolean printActive;
|
||||
private boolean printDone;
|
||||
|
||||
public OpBenchmarkListener(Operation operation, @NonNull Mode mode) {
|
||||
this(operation, mode, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param operation Operation to collect stats for
|
||||
* @param mode Mode - see {@link OpBenchmarkListener}
|
||||
* @param minRuntime Minimum runtime - only applies to Mode.SINGLE_ITER_PRINT. If op runtime below this: don't print
|
||||
*/
|
||||
public OpBenchmarkListener(Operation operation, @NonNull Mode mode, long minRuntime) {
|
||||
this.operation = operation;
|
||||
this.mode = mode;
|
||||
this.minRuntime = minRuntime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive(Operation operation) {
|
||||
return this.operation == null || this.operation == operation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void operationStart(SameDiff sd, Operation op) {
|
||||
if(printDone)
|
||||
return;
|
||||
if(this.operation == null || this.operation == op)
|
||||
printActive = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void operationEnd(SameDiff sd, Operation op) {
|
||||
if(printDone)
|
||||
return;
|
||||
if(this.operation == null || this.operation == op) {
|
||||
printActive = false;
|
||||
printDone = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preOpExecution(SameDiff sd, At at, SameDiffOp op, OpContext opContext) {
|
||||
start = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void opExecution(SameDiff sd, At at, MultiDataSet batch, SameDiffOp op, OpContext opContext, INDArray[] outputs) {
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
if (mode == Mode.SINGLE_ITER_PRINT && printActive && (now-start) > this.minRuntime) {
|
||||
System.out.println(getOpString(op, now));
|
||||
} else if (mode == Mode.AGGREGATE) {
|
||||
if(aggregateModeMap == null)
|
||||
aggregateModeMap = new LinkedHashMap<>();
|
||||
|
||||
if(!aggregateModeMap.containsKey(op.getName())){
|
||||
String s = getOpString(op, null);
|
||||
OpExec oe = new OpExec(op.getName(), op.getOp().opName(), op.getOp().getClass(),
|
||||
new ArrayList<Long>(), s);
|
||||
aggregateModeMap.put(op.getName(), oe);
|
||||
}
|
||||
|
||||
aggregateModeMap.get(op.getName()).getRuntimeMs().add(now-start);
|
||||
}
|
||||
}
|
||||
|
||||
private String getOpString(SameDiffOp op, Long now){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(op.getName()).append(" - ").append(op.getOp().getClass().getSimpleName())
|
||||
.append("(").append(op.getOp().opName()).append(") - ");
|
||||
if(now != null) {
|
||||
sb.append(now - start).append(" ms\n");
|
||||
}
|
||||
|
||||
if (op.getOp() instanceof DynamicCustomOp) {
|
||||
DynamicCustomOp dco = (DynamicCustomOp) op.getOp();
|
||||
int x = 0;
|
||||
|
||||
for (INDArray i : dco.inputArguments()) {
|
||||
sb.append(" in ").append(x++).append(": ").append(i.shapeInfoToString()).append("\n");
|
||||
}
|
||||
x = 0;
|
||||
for (INDArray o : dco.outputArguments()) {
|
||||
sb.append(" out ").append(x++).append(": ").append(o.shapeInfoToString()).append("\n");
|
||||
}
|
||||
long[] iargs = dco.iArgs();
|
||||
boolean[] bargs = dco.bArgs();
|
||||
double[] targs = dco.tArgs();
|
||||
if (iargs != null && iargs.length > 0) {
|
||||
sb.append(" iargs: ").append(Arrays.toString(iargs)).append("\n");
|
||||
}
|
||||
if (bargs != null && bargs.length > 0) {
|
||||
sb.append(" bargs: ").append(Arrays.toString(bargs)).append("\n");
|
||||
}
|
||||
if (targs != null && targs.length > 0) {
|
||||
sb.append(" targs: ").append(Arrays.toString(targs)).append("\n");
|
||||
}
|
||||
} else {
|
||||
Op o = (Op) op.getOp();
|
||||
if (o.x() != null)
|
||||
sb.append(" x: ").append(o.x().shapeInfoToString());
|
||||
if (o.y() != null)
|
||||
sb.append(" y: ").append(o.y().shapeInfoToString());
|
||||
if (o.z() != null)
|
||||
sb.append(" z: ").append(o.z().shapeInfoToString());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@AllArgsConstructor
|
||||
@Data
|
||||
public static class OpExec {
|
||||
private final String opOwnName;
|
||||
private final String opName;
|
||||
private final Class<?> opClass;
|
||||
private List<Long> runtimeMs;
|
||||
private String firstIter;
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
DecimalFormat df = new DecimalFormat("0.000");
|
||||
|
||||
return opOwnName + " - op class: " + opClass.getSimpleName() + " (op name: " + opName + ")\n"
|
||||
+ "count: " + runtimeMs.size() + ", mean: " + df.format(avgMs()) + "ms, std: " + df.format(stdMs()) + "ms, min: " + minMs() + "ms, max: " + maxMs() + "ms\n"
|
||||
+ firstIter;
|
||||
}
|
||||
|
||||
public double avgMs() {
|
||||
long sum = 0;
|
||||
for (Long l : runtimeMs) {
|
||||
sum += l;
|
||||
}
|
||||
return sum / (double) runtimeMs.size();
|
||||
}
|
||||
|
||||
public double stdMs() {
|
||||
return Nd4j.createFromArray(ArrayUtil.toArrayLong(runtimeMs)).stdNumber().doubleValue();
|
||||
}
|
||||
|
||||
public long minMs() {
|
||||
return Nd4j.createFromArray(ArrayUtil.toArrayLong(runtimeMs)).minNumber().longValue();
|
||||
}
|
||||
|
||||
public long maxMs() {
|
||||
return Nd4j.createFromArray(ArrayUtil.toArrayLong(runtimeMs)).maxNumber().longValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.nd4j.autodiff.listeners.At;
|
||||
import org.nd4j.autodiff.listeners.BaseEvaluationListener;
|
||||
import org.nd4j.autodiff.listeners.records.EvaluationRecord;
|
||||
import org.nd4j.autodiff.listeners.records.History;
|
||||
import org.nd4j.autodiff.listeners.ListenerEvaluations;
|
||||
import org.nd4j.autodiff.listeners.ListenerResponse;
|
||||
import org.nd4j.autodiff.listeners.records.LossCurve;
|
||||
import org.nd4j.autodiff.listeners.Operation;
|
||||
import org.nd4j.autodiff.samediff.SameDiff;
|
||||
import org.nd4j.autodiff.samediff.TrainingConfig;
|
||||
|
||||
public class HistoryListener extends BaseEvaluationListener {
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private ListenerEvaluations evaluations;
|
||||
|
||||
private List<EvaluationRecord> trainingHistory = new ArrayList<>();
|
||||
private List<EvaluationRecord> validationHistory = new ArrayList<>();
|
||||
private LossCurve loss = null;
|
||||
|
||||
private long startTime;
|
||||
private long endTime;
|
||||
|
||||
private List<Long> validationTimes = new ArrayList<>();
|
||||
private long validationStartTime;
|
||||
|
||||
|
||||
public HistoryListener(TrainingConfig tc) {
|
||||
this.evaluations = new ListenerEvaluations(tc.getTrainEvaluations(), tc.getTrainEvaluationLabels(),
|
||||
tc.getValidationEvaluations(), tc.getValidationEvaluationLabels());
|
||||
}
|
||||
|
||||
public HistoryListener(ListenerEvaluations evaluations) {
|
||||
this.evaluations = evaluations;
|
||||
}
|
||||
|
||||
public HistoryListener newInstance() {
|
||||
return new HistoryListener(evaluations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenerEvaluations evaluations() {
|
||||
return evaluations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive(Operation operation) {
|
||||
return operation.isTrainingPhase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenerResponse epochEndEvaluations(SameDiff sd, At at, LossCurve lossCurve, long epochTimeMillis, EvaluationRecord evaluations) {
|
||||
trainingHistory.add(evaluations);
|
||||
loss = lossCurve;
|
||||
|
||||
return ListenerResponse.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenerResponse validationDoneEvaluations(SameDiff sd, At at, long validationTimeMillis, EvaluationRecord evaluations) {
|
||||
validationHistory.add(evaluations);
|
||||
return ListenerResponse.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void operationStart(SameDiff sd, Operation op) {
|
||||
if (op == Operation.TRAINING) {
|
||||
startTime = System.currentTimeMillis();
|
||||
} else if (op == Operation.TRAINING_VALIDATION) {
|
||||
validationStartTime = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void operationEnd(SameDiff sd, Operation op) {
|
||||
if (op == Operation.TRAINING) {
|
||||
endTime = System.currentTimeMillis();
|
||||
} else if (op == Operation.TRAINING_VALIDATION) {
|
||||
validationTimes.add(System.currentTimeMillis() - validationStartTime);
|
||||
}
|
||||
}
|
||||
|
||||
public History getReport() {
|
||||
return new History(trainingHistory, validationHistory, loss, endTime - startTime, validationTimes);
|
||||
}
|
||||
|
||||
}
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners.impl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nd4j.autodiff.listeners.At;
|
||||
import org.nd4j.autodiff.listeners.BaseListener;
|
||||
import org.nd4j.autodiff.listeners.ListenerResponse;
|
||||
import org.nd4j.autodiff.listeners.Loss;
|
||||
import org.nd4j.autodiff.listeners.records.LossCurve;
|
||||
import org.nd4j.autodiff.listeners.Operation;
|
||||
import org.nd4j.autodiff.samediff.SameDiff;
|
||||
import org.nd4j.common.base.Preconditions;
|
||||
import org.nd4j.linalg.dataset.api.MultiDataSet;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
@Slf4j
|
||||
public class ScoreListener extends BaseListener {
|
||||
|
||||
private final int frequency;
|
||||
private final boolean reportEpochs;
|
||||
private final boolean reportIterPerformance;
|
||||
|
||||
private long epochExampleCount;
|
||||
private int epochBatchCount;
|
||||
private long etlTotalTimeEpoch;
|
||||
|
||||
private long lastIterTime;
|
||||
private long etlTimeSumSinceLastReport;
|
||||
private long iterTimeSumSinceLastReport;
|
||||
private int examplesSinceLastReportIter;
|
||||
private long lastReportTime = -1;
|
||||
|
||||
/**
|
||||
* Create a ScoreListener reporting every 10 iterations, and at the end of each epoch
|
||||
*/
|
||||
public ScoreListener() {
|
||||
this(10, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ScoreListener reporting every N iterations, and at the end of each epoch
|
||||
*/
|
||||
public ScoreListener(int frequency) {
|
||||
this(frequency, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ScoreListener reporting every N iterations, and optionally at the end of each epoch
|
||||
*/
|
||||
public ScoreListener(int frequency, boolean reportEpochs) {
|
||||
this(frequency, reportEpochs, true);
|
||||
}
|
||||
|
||||
public ScoreListener(int frequency, boolean reportEpochs, boolean reportIterPerformance) {
|
||||
Preconditions.checkArgument(frequency > 0, "ScoreListener frequency must be > 0, got %s", frequency);
|
||||
this.frequency = frequency;
|
||||
this.reportEpochs = reportEpochs;
|
||||
this.reportIterPerformance = reportIterPerformance;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isActive(Operation operation) {
|
||||
return operation == Operation.TRAINING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void epochStart(SameDiff sd, At at) {
|
||||
if (reportEpochs) {
|
||||
epochExampleCount = 0;
|
||||
epochBatchCount = 0;
|
||||
etlTotalTimeEpoch = 0;
|
||||
}
|
||||
lastReportTime = -1;
|
||||
examplesSinceLastReportIter = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenerResponse epochEnd(SameDiff sd, At at, LossCurve lossCurve, long epochTimeMillis) {
|
||||
if (reportEpochs) {
|
||||
double batchesPerSec = epochBatchCount / (epochTimeMillis / 1000.0);
|
||||
double examplesPerSec = epochExampleCount / (epochTimeMillis / 1000.0);
|
||||
double pcEtl = 100.0 * etlTotalTimeEpoch / (double) epochTimeMillis;
|
||||
String etl = formatDurationMs(etlTotalTimeEpoch) + " ETL time" + (etlTotalTimeEpoch > 0 ? "(" + format2dp(pcEtl) + " %)" : "");
|
||||
log.info("Epoch {} complete on iteration {} - {} batches ({} examples) in {} - {} batches/sec, {} examples/sec, {}",
|
||||
at.epoch(), at.iteration(), epochBatchCount, epochExampleCount, formatDurationMs(epochTimeMillis),
|
||||
format2dp(batchesPerSec), format2dp(examplesPerSec), etl);
|
||||
}
|
||||
|
||||
return ListenerResponse.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void iterationStart(SameDiff sd, At at, MultiDataSet data, long etlMs) {
|
||||
lastIterTime = System.currentTimeMillis();
|
||||
etlTimeSumSinceLastReport += etlMs;
|
||||
etlTotalTimeEpoch += etlMs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void iterationDone(SameDiff sd, At at, MultiDataSet dataSet, Loss loss) {
|
||||
iterTimeSumSinceLastReport += System.currentTimeMillis() - lastIterTime;
|
||||
epochBatchCount++;
|
||||
if (dataSet.numFeatureArrays() > 0 && dataSet.getFeatures(0) != null) {
|
||||
int n = (int) dataSet.getFeatures(0).size(0);
|
||||
examplesSinceLastReportIter += n;
|
||||
epochExampleCount += n;
|
||||
}
|
||||
|
||||
if (at.iteration() > 0 && at.iteration() % frequency == 0) {
|
||||
double l = loss.totalLoss();
|
||||
String etl = "";
|
||||
if (etlTimeSumSinceLastReport > 0) {
|
||||
etl = "(" + formatDurationMs(etlTimeSumSinceLastReport) + " ETL";
|
||||
if (frequency == 1) {
|
||||
etl += ")";
|
||||
} else {
|
||||
etl += " in " + frequency + " iter)";
|
||||
}
|
||||
}
|
||||
|
||||
if(!reportIterPerformance) {
|
||||
log.info("Loss at epoch {}, iteration {}: {}{}", at.epoch(), at.iteration(), format5dp(l), etl);
|
||||
} else {
|
||||
long time = System.currentTimeMillis();
|
||||
if(lastReportTime > 0){
|
||||
double batchPerSec = 1000 * frequency / (double)(time - lastReportTime);
|
||||
double exPerSec = 1000 * examplesSinceLastReportIter / (double)(time - lastReportTime);
|
||||
log.info("Loss at epoch {}, iteration {}: {}{}, batches/sec: {}, examples/sec: {}", at.epoch(), at.iteration(), format5dp(l),
|
||||
etl, format5dp(batchPerSec), format5dp(exPerSec));
|
||||
} else {
|
||||
log.info("Loss at epoch {}, iteration {}: {}{}", at.epoch(), at.iteration(), format5dp(l), etl);
|
||||
}
|
||||
|
||||
lastReportTime = time;
|
||||
}
|
||||
|
||||
iterTimeSumSinceLastReport = 0;
|
||||
etlTimeSumSinceLastReport = 0;
|
||||
examplesSinceLastReportIter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected String formatDurationMs(long ms) {
|
||||
if (ms <= 100) {
|
||||
return ms + " ms";
|
||||
} else if (ms <= 60000L) {
|
||||
double sec = ms / 1000.0;
|
||||
return format2dp(sec) + " sec";
|
||||
} else if (ms <= 60 * 60000L) {
|
||||
double min = ms / 60_000.0;
|
||||
return format2dp(min) + " min";
|
||||
} else {
|
||||
double hr = ms / 360_000.0;
|
||||
return format2dp(hr) + " hr";
|
||||
}
|
||||
}
|
||||
|
||||
protected static final ThreadLocal<DecimalFormat> DF_2DP = new ThreadLocal<>();
|
||||
protected static final ThreadLocal<DecimalFormat> DF_2DP_SCI = new ThreadLocal<>();
|
||||
|
||||
protected String format2dp(double d) {
|
||||
if (d < 0.01) {
|
||||
DecimalFormat f = DF_2DP_SCI.get();
|
||||
if (f == null) {
|
||||
f = new DecimalFormat("0.00E0");
|
||||
DF_2DP.set(f);
|
||||
}
|
||||
return f.format(d);
|
||||
} else {
|
||||
DecimalFormat f = DF_2DP.get();
|
||||
if (f == null) {
|
||||
f = new DecimalFormat("#.00");
|
||||
DF_2DP.set(f);
|
||||
}
|
||||
return f.format(d);
|
||||
}
|
||||
}
|
||||
|
||||
protected static final ThreadLocal<DecimalFormat> DF_5DP = new ThreadLocal<>();
|
||||
protected static final ThreadLocal<DecimalFormat> DF_5DP_SCI = new ThreadLocal<>();
|
||||
|
||||
protected String format5dp(double d) {
|
||||
|
||||
if (d < 1e-4 || d > 1e4) {
|
||||
//Use scientific
|
||||
DecimalFormat f = DF_5DP_SCI.get();
|
||||
if (f == null) {
|
||||
f = new DecimalFormat("0.00000E0");
|
||||
DF_5DP_SCI.set(f);
|
||||
}
|
||||
return f.format(d);
|
||||
} else {
|
||||
DecimalFormat f = DF_5DP.get();
|
||||
if (f == null) {
|
||||
f = new DecimalFormat("0.00000");
|
||||
DF_5DP.set(f);
|
||||
}
|
||||
return f.format(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
+331
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners.profiler;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.nd4j.autodiff.listeners.At;
|
||||
import org.nd4j.autodiff.listeners.BaseListener;
|
||||
import org.nd4j.autodiff.listeners.Loss;
|
||||
import org.nd4j.autodiff.listeners.Operation;
|
||||
import org.nd4j.autodiff.listeners.profiler.data.Phase;
|
||||
import org.nd4j.autodiff.listeners.profiler.data.TraceEvent;
|
||||
import org.nd4j.autodiff.samediff.SameDiff;
|
||||
import org.nd4j.autodiff.samediff.internal.SameDiffOp;
|
||||
import org.nd4j.common.base.Preconditions;
|
||||
import org.nd4j.linalg.api.ndarray.INDArray;
|
||||
import org.nd4j.linalg.api.ops.OpContext;
|
||||
import org.nd4j.linalg.dataset.api.MultiDataSet;
|
||||
import org.nd4j.common.primitives.AtomicBoolean;
|
||||
import org.nd4j.shade.jackson.databind.DeserializationFeature;
|
||||
import org.nd4j.shade.jackson.databind.MapperFeature;
|
||||
import org.nd4j.shade.jackson.databind.ObjectMapper;
|
||||
import org.nd4j.shade.jackson.databind.SerializationFeature;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
|
||||
@Getter
|
||||
@Slf4j
|
||||
public class ProfilingListener extends BaseListener {
|
||||
|
||||
private final File outputFile;
|
||||
private final boolean all;
|
||||
private final int warmup;
|
||||
private final int nIter;
|
||||
private final long nMs;
|
||||
private final Operation[] operations;
|
||||
|
||||
private final long pid;
|
||||
private final long tid;
|
||||
private Long firstOpStart = null; //Used for time termination
|
||||
private int countTotalIter = 0;
|
||||
private boolean logActive = false;
|
||||
private long opStartNano;
|
||||
|
||||
private Writer writer;
|
||||
private ObjectMapper json;
|
||||
|
||||
private final Thread fileWritingThread;
|
||||
private final BlockingQueue<TraceEvent> writeQueue;
|
||||
private final AtomicBoolean writing = new AtomicBoolean(false);
|
||||
|
||||
protected ProfilingListener(@NonNull File outputFile, boolean all, int warmup, int nIter, long nMs, Operation[] operations) {
|
||||
Preconditions.checkArgument(!outputFile.exists(), "Output file already exists: %s", outputFile);
|
||||
this.outputFile = outputFile;
|
||||
this.all = all;
|
||||
this.warmup = warmup;
|
||||
this.nIter = nIter;
|
||||
this.nMs = nMs;
|
||||
this.operations = operations;
|
||||
|
||||
this.pid = getProcessId();
|
||||
this.tid = Thread.currentThread().getId();
|
||||
|
||||
try {
|
||||
this.writer = new BufferedWriter(new FileWriter(outputFile, false));
|
||||
this.writer.write("["); //JSON array open (array close is optional for Chrome profiler format)
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
this.json = jsonMapper();
|
||||
|
||||
//Set up a queue so file access doesn't add latency to the execution thread
|
||||
writeQueue = new LinkedBlockingDeque<>();
|
||||
fileWritingThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
runHelper();
|
||||
} catch (Throwable t) {
|
||||
log.error("Error when attempting to write results to file", t);
|
||||
}
|
||||
}
|
||||
|
||||
public void runHelper() throws Exception {
|
||||
while (true) {
|
||||
TraceEvent te = writeQueue.take(); //Blocking
|
||||
writing.set(true);
|
||||
try {
|
||||
String j = json.writeValueAsString(te);
|
||||
writer.append(j);
|
||||
writer.append(",\n");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
writing.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
fileWritingThread.setDaemon(true);
|
||||
fileWritingThread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive(Operation operation) {
|
||||
return operations == null || ArrayUtils.contains(operations, operation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void operationStart(SameDiff sd, Operation op) {
|
||||
this.logActive = operations == null || ArrayUtils.contains(operations, op);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void operationEnd(SameDiff sd, Operation op) {
|
||||
if (this.logActive) {
|
||||
while ((!writeQueue.isEmpty() || writing.get()) && fileWritingThread.isAlive()) {
|
||||
//Wait for file writing thread to catch up
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
writer.flush();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
this.logActive = false;
|
||||
if (op == Operation.INFERENCE) {
|
||||
//Increment for inference; iteration done is called only for TRAINING
|
||||
countTotalIter++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void iterationDone(SameDiff sd, At at, MultiDataSet dataSet, Loss loss) {
|
||||
//Increment for training
|
||||
if (logActive) {
|
||||
countTotalIter++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preOpExecution(SameDiff sd, At at, SameDiffOp op, OpContext opContext) {
|
||||
if (logActive) {
|
||||
opStartNano = System.nanoTime();
|
||||
|
||||
if(!all && nMs > 0 && firstOpStart == null)
|
||||
firstOpStart = opStartNano;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void opExecution(SameDiff sd, At at, MultiDataSet batch, SameDiffOp op, OpContext opContext, INDArray[] outputs) {
|
||||
if (logActive) {
|
||||
long now = System.nanoTime();
|
||||
|
||||
if (warmup > 0 && countTotalIter < warmup) {
|
||||
return; //Skip due to warmup phase
|
||||
}
|
||||
|
||||
//Iteration termination
|
||||
int terminationPt = this.nIter > 0 ? this.nIter : Integer.MAX_VALUE;
|
||||
if (warmup > 0 && this.nIter > 0)
|
||||
terminationPt += this.warmup;
|
||||
|
||||
if (countTotalIter > terminationPt) {
|
||||
logActive = false;
|
||||
return; //Skip due to max number of itertions
|
||||
}
|
||||
|
||||
//Time termination
|
||||
if(!all && nMs > 0 && (now - firstOpStart)/1000 > nMs) {
|
||||
logActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
TraceEvent event = TraceEvent.builder()
|
||||
.name(op.getOp().opName())
|
||||
.categories(Collections.singletonList("Op"))
|
||||
.ts(opStartNano / 1000)
|
||||
.dur((now - opStartNano) / 1000)
|
||||
.pid((int)pid)
|
||||
.tid(tid)
|
||||
.ph(Phase.X)
|
||||
.args(Collections.<String, Object>singletonMap("name", op.getName()))
|
||||
.build();
|
||||
|
||||
writeQueue.add(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private long getProcessId() {
|
||||
// Note: may fail in some JVM implementations
|
||||
// therefore fallback has to be provided
|
||||
|
||||
// something like '<pid>@<hostname>', at least in SUN / Oracle JVMs
|
||||
final String jvmName = ManagementFactory.getRuntimeMXBean().getName();
|
||||
final int index = jvmName.indexOf('@');
|
||||
|
||||
if (index < 1) {
|
||||
// part before '@' empty (index = 0) / '@' not found (index = -1)
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
return Long.parseLong(jvmName.substring(0, index));
|
||||
} catch (NumberFormatException e) {
|
||||
// ignore
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new JSON mapper for use in serializing/deserializing JSON format
|
||||
*/
|
||||
public static ObjectMapper jsonMapper() {
|
||||
ObjectMapper json = new ObjectMapper();
|
||||
json.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
json.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
|
||||
json.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, false);
|
||||
json.disable(SerializationFeature.INDENT_OUTPUT); //One line
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new builder
|
||||
* @param outputFile Output file. Will be overwritten if file already exists
|
||||
*/
|
||||
public static Builder builder(File outputFile) {
|
||||
return new Builder(outputFile);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private final File outputFile;
|
||||
private boolean all = true;
|
||||
private int warmup = 0;
|
||||
private int nIter = -1;
|
||||
private long nMs = -1;
|
||||
private Operation[] operations;
|
||||
|
||||
public Builder(@NonNull File outputFile) {
|
||||
this.outputFile = outputFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* If called, all data will be profiled with no limits (other than a warmup, if set)
|
||||
*/
|
||||
public Builder recordAll() {
|
||||
this.all = true;
|
||||
this.nIter = -1;
|
||||
this.nMs = -1;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the number of warmup iterations - i.e., these will be excluded from profiling results
|
||||
*/
|
||||
public Builder warmup(int iterations) {
|
||||
this.warmup = iterations;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a limit on the maximum number of iterations to profile (after warmup, if any).
|
||||
* Any ops executed after the specified number of iterations will not be profiled/recorded
|
||||
*/
|
||||
public Builder maxProfileIterations(int iterations) {
|
||||
this.nIter = iterations;
|
||||
this.all = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a limit on the maximum duration for profiling, in milliseconds.
|
||||
* Any ops executed after the specified amount of time since the first (non-warmup) operation start will not be
|
||||
* profiled/recorded
|
||||
*/
|
||||
public Builder maxProfilerMilliseconds(long ms) {
|
||||
this.nMs = ms;
|
||||
this.all = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the operations (training, inference, etc) to profile.
|
||||
* If not set, all operations are profiled
|
||||
*/
|
||||
public Builder operations(Operation... operations) {
|
||||
this.operations = operations;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the profiling listener
|
||||
*/
|
||||
public ProfilingListener build() {
|
||||
return new ProfilingListener(outputFile, all, warmup, nIter, nMs, operations);
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -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.autodiff.listeners.profiler.comparison;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.nd4j.list.NDArrayList;
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public class OpStats {
|
||||
private String opInstanceName;
|
||||
private String opName;
|
||||
private int count;
|
||||
private NDArrayList timesUs;
|
||||
private Long sumUs;
|
||||
}
|
||||
+23
@@ -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.autodiff.listeners.profiler.data;
|
||||
|
||||
public enum ColorName {
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners.profiler.data;
|
||||
|
||||
public enum Phase {
|
||||
B,
|
||||
E,
|
||||
X,
|
||||
I,
|
||||
C,
|
||||
b,
|
||||
n,
|
||||
e,
|
||||
s,
|
||||
t,
|
||||
f,
|
||||
P,
|
||||
N,
|
||||
O,
|
||||
D,
|
||||
M,
|
||||
V,
|
||||
v,
|
||||
R,
|
||||
c
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners.profiler.data;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Builder
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class TraceEvent {
|
||||
|
||||
private String name; //Name of event (usually op name)
|
||||
private List<String> categories; //Comma separated list of categories
|
||||
private Phase ph; //Event type - phase (see table for options)
|
||||
private long ts; //Timestamp, in microseconds (us)
|
||||
private Long dur; //Duration, optional
|
||||
private Long tts; //Optional, thlread timestamp, in microseconds
|
||||
private long pid; //Process ID
|
||||
private long tid; //Thread ID
|
||||
private Map<String, Object> args; //Args
|
||||
private ColorName cname; //Optional, color name (must be one of reserved color names: https://github.com/catapult-project/catapult/blob/master/tracing/tracing/base/color_scheme.html )
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners.profiler.data;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public class TraceEvents {
|
||||
private List<TraceEvent> traceEvents;
|
||||
}
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners.records;
|
||||
|
||||
import org.nd4j.shade.guava.base.Predicates;
|
||||
import org.nd4j.shade.guava.collect.Collections2;
|
||||
import org.nd4j.shade.guava.collect.ImmutableMap;
|
||||
import org.nd4j.shade.guava.collect.Lists;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.nd4j.autodiff.samediff.SDVariable;
|
||||
import org.nd4j.common.base.Preconditions;
|
||||
import org.nd4j.evaluation.IEvaluation;
|
||||
import org.nd4j.evaluation.IMetric;
|
||||
|
||||
@Getter
|
||||
public class EvaluationRecord {
|
||||
|
||||
private Map<String, List<IEvaluation>> evaluations;
|
||||
private Map<Class<? extends IEvaluation>, IEvaluation> classEvaluations = new HashMap<>();
|
||||
private boolean isEmpty = true;
|
||||
|
||||
public EvaluationRecord(Map<String, List<IEvaluation>> evaluations) {
|
||||
this.evaluations = Collections.unmodifiableMap(evaluations);
|
||||
|
||||
for (List<IEvaluation> le : evaluations.values()) {
|
||||
for (IEvaluation e : le) {
|
||||
isEmpty = false;
|
||||
if (classEvaluations.containsKey(e.getClass()))
|
||||
classEvaluations.remove(e.getClass());
|
||||
else
|
||||
classEvaluations.put(e.getClass(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private EvaluationRecord() {
|
||||
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return isEmpty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all evaluations
|
||||
*/
|
||||
public Map<String, List<IEvaluation>> evaluations() {
|
||||
return evaluations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get evaluations for a given param/variable
|
||||
*
|
||||
* @param param The target param/variable
|
||||
*/
|
||||
public List<IEvaluation> evaluations(String param) {
|
||||
Preconditions.checkArgument(evaluations.containsKey(param),
|
||||
"No evaluations for %s.", param);
|
||||
|
||||
return evaluations.get(param);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get evaluations for a given param/variable
|
||||
*
|
||||
* @param param The target param/variable
|
||||
*/
|
||||
public List<IEvaluation> evaluations(SDVariable param) {
|
||||
return evaluations(param.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the evaluation for param at the specified index
|
||||
*/
|
||||
public IEvaluation evaluation(String param, int index) {
|
||||
return evaluations(param).get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the evaluation for param at the specified index
|
||||
*/
|
||||
public IEvaluation evaluation(SDVariable param, int index) {
|
||||
return evaluation(param.name(), index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the evaluation for a given param/variable
|
||||
* <p>
|
||||
* Will throw an exception if there are more than one or no evaluations for the param
|
||||
*
|
||||
* @param param The target param/variable
|
||||
*/
|
||||
public <T extends IEvaluation> T evaluation(String param) {
|
||||
Preconditions.checkArgument(evaluations.containsKey(param),
|
||||
"No evaluations for %s.", param);
|
||||
Preconditions.checkArgument(evaluations.get(param).size() == 1,
|
||||
"Multiple evaluations for %s. Use evaluations().", param);
|
||||
|
||||
return (T) evaluations.get(param).get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the evaluation for a given param/variable
|
||||
* <p>
|
||||
* Will throw an exception if there are more than one or no evaluations for the param
|
||||
*
|
||||
* @param param The target param/variable
|
||||
*/
|
||||
public <T extends IEvaluation> T evaluation(SDVariable param) {
|
||||
return evaluation(param.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the evaluation of a given type
|
||||
* <p>
|
||||
* Will throw an exception if there are more than one or no evaluations of that type
|
||||
*
|
||||
* @param evalClass The type of evaluation to look for
|
||||
*/
|
||||
public <T extends IEvaluation<T>> T evaluation(Class<T> evalClass) {
|
||||
Preconditions.checkArgument(classEvaluations.containsKey(evalClass),
|
||||
"Can't get evaluation for %s. Either no evaluations with that class are present, or more than one are.", evalClass);
|
||||
|
||||
return (T) classEvaluations.get(evalClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the evaluation of a given type, for a given param/variable
|
||||
* <p>
|
||||
* Will throw an exception if there are more than one or no evaluations of that type for the given param
|
||||
*
|
||||
* @param param The target param/variable
|
||||
* @param evalClass The type of evaluation to look for
|
||||
*/
|
||||
public <T extends IEvaluation<T>> T evaluation(String param, Class<T> evalClass) {
|
||||
Collection<IEvaluation> evals = Collections2.filter(evaluations(param), Predicates.instanceOf(evalClass));
|
||||
|
||||
Preconditions.checkArgument(evals.size() == 1, "Multiple or no evaluations of type %s for param %s.", evalClass, param);
|
||||
|
||||
return (T) evals.iterator().next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the evaluation of a given type, for a given param/variable
|
||||
* <p>
|
||||
* Will throw an exception if there are more than one or no evaluations of that type for the given param
|
||||
*
|
||||
* @param param The target param/variable
|
||||
* @param evalClass The type of evaluation to look for
|
||||
*/
|
||||
public <T extends IEvaluation<T>> T evaluation(SDVariable param, Class<T> evalClass) {
|
||||
return evaluation(param.name(), evalClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the metric's value for the evaluation of the metric's type
|
||||
* <p>
|
||||
* Will throw an exception if there are more than one or no evaluations of that type
|
||||
*
|
||||
* @param metric The metric to calculate
|
||||
*/
|
||||
public double getValue(IMetric metric) {
|
||||
return evaluation(metric.getEvaluationClass()).getValue(metric);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the metric's value for the evaluation of the metric's type, for a given param/variable
|
||||
* <p>
|
||||
* Will throw an exception if there are more than one or no evaluations of that type for the given param
|
||||
*
|
||||
* @param param The target param/variable
|
||||
* @param metric The metric to calculate
|
||||
*/
|
||||
public double getValue(String param, IMetric metric) {
|
||||
return evaluation(param, metric.getEvaluationClass()).getValue(metric);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the metric's value for the evaluation of the metric's type, for a given param/variable
|
||||
* <p>
|
||||
* Will throw an exception if there are more than one or no evaluations of that type for the given param
|
||||
*
|
||||
* @param param The target param/variable
|
||||
* @param metric The metric to calculate
|
||||
*/
|
||||
public double getValue(SDVariable param, IMetric metric) {
|
||||
return getValue(param.name(), metric);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the metric's value for the evaluation for a given param/variable at the given index
|
||||
* <p>
|
||||
* Will throw an exception if the target evaluation doesn't support the given metric
|
||||
*
|
||||
* @param param The target param/variable
|
||||
* @param index The index of the target evaluation on the param
|
||||
* @param metric The metric to calculate
|
||||
*/
|
||||
public double getValue(String param, int index, IMetric metric) {
|
||||
return evaluation(param, index).getValue(metric);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the metric's value for the evaluation for a given param/variable at the given index
|
||||
* <p>
|
||||
* Will throw an exception if the target evaluation doesn't support the given metric
|
||||
*
|
||||
* @param param The target param/variable
|
||||
* @param index The index of the target evaluation on the param
|
||||
* @param metric The metric to calculate
|
||||
*/
|
||||
public double getValue(SDVariable param, int index, IMetric metric) {
|
||||
return getValue(param.name(), index, metric);
|
||||
}
|
||||
|
||||
}
|
||||
+355
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners.records;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.nd4j.autodiff.listeners.Listener;
|
||||
import org.nd4j.autodiff.samediff.SDVariable;
|
||||
import org.nd4j.autodiff.samediff.SameDiff;
|
||||
import org.nd4j.common.base.Preconditions;
|
||||
import org.nd4j.evaluation.IEvaluation;
|
||||
import org.nd4j.evaluation.IMetric;
|
||||
import org.nd4j.linalg.dataset.api.iterator.DataSetIterator;
|
||||
|
||||
@Getter
|
||||
public class History {
|
||||
|
||||
private List<EvaluationRecord> trainingHistory;
|
||||
private List<EvaluationRecord> validationHistory;
|
||||
|
||||
private LossCurve lossCurve;
|
||||
|
||||
private long trainingTimeMillis;
|
||||
private List<Long> validationTimesMillis;
|
||||
|
||||
public History(List<EvaluationRecord> training, List<EvaluationRecord> validation, LossCurve loss,
|
||||
long trainingTimeMillis, List<Long> validationTimesMillis){
|
||||
trainingHistory = Collections.unmodifiableList(training);
|
||||
validationHistory = Collections.unmodifiableList(validation);
|
||||
this.lossCurve = loss;
|
||||
this.trainingTimeMillis = trainingTimeMillis;
|
||||
this.validationTimesMillis = Collections.unmodifiableList(validationTimesMillis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the training evaluations
|
||||
*/
|
||||
public List<EvaluationRecord> trainingEval(){
|
||||
return trainingHistory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation evaluations
|
||||
*/
|
||||
public List<EvaluationRecord> validationEval(){
|
||||
return validationHistory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the loss curve
|
||||
*/
|
||||
public LossCurve lossCurve(){
|
||||
return lossCurve;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total training time, in milliseconds
|
||||
*/
|
||||
public long trainingTimeMillis(){
|
||||
return trainingTimeMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total validation time, in milliseconds
|
||||
*/
|
||||
public List<Long> validationTimesMillis(){
|
||||
return validationTimesMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of epochs trained for
|
||||
*/
|
||||
public int trainingEpochs(){
|
||||
return trainingHistory.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of epochs validation was ran on
|
||||
*/
|
||||
public int validationEpochs(){
|
||||
return validationHistory.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results of a training evaluation on a given parameter for a given metric
|
||||
*
|
||||
* Only works if there is only one evaluation with the given metric for param
|
||||
*/
|
||||
public List<Double> trainingEval(String param, IMetric metric) {
|
||||
List<Double> data = new ArrayList<>();
|
||||
for(EvaluationRecord er : trainingHistory)
|
||||
data.add(er.getValue(param, metric));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results of a training evaluation on a given parameter for a given metric
|
||||
*
|
||||
* Only works if there is only one evaluation with the given metric for param
|
||||
*/
|
||||
public List<Double> trainingEval(SDVariable param, IMetric metric){
|
||||
return trainingEval(param.name(), metric);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results of a training evaluation on a given parameter at a given index, for a given metric
|
||||
*
|
||||
* Note that it returns all recorded evaluations.
|
||||
* Index determines the evaluation used not the epoch's results to return.
|
||||
*/
|
||||
public List<Double> trainingEval(String param, int index, IMetric metric) {
|
||||
List<Double> data = new ArrayList<>();
|
||||
for(EvaluationRecord er : trainingHistory)
|
||||
data.add(er.getValue(param, index, metric));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results of a training evaluation on a given parameter at a given index, for a given metric
|
||||
*
|
||||
* Note that it returns all recorded evaluations.
|
||||
* Index determines the evaluation used not the epoch's results to return.
|
||||
*/
|
||||
public List<Double> trainingEval(SDVariable param, int index, IMetric metric) {
|
||||
return trainingEval(param.name(), index, metric);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results of a training evaluation for a given metric
|
||||
*
|
||||
* Only works if there is only one evaluation with the given metric
|
||||
*/
|
||||
public List<Double> trainingEval(IMetric metric) {
|
||||
List<Double> data = new ArrayList<>();
|
||||
for(EvaluationRecord er : trainingHistory)
|
||||
data.add(er.getValue(metric));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results of a training evaluation on a given parameter
|
||||
*
|
||||
* Only works if there is only one evaluation for param.
|
||||
*/
|
||||
public List<IEvaluation> trainingEval(String param) {
|
||||
List<IEvaluation> data = new ArrayList<>();
|
||||
for(EvaluationRecord er : trainingHistory)
|
||||
data.add(er.evaluation(param));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results of a training evaluation on a given parameter
|
||||
*
|
||||
* Only works if there is only one evaluation for param.
|
||||
*/
|
||||
public List<IEvaluation> trainingEval(SDVariable param){
|
||||
return trainingEval(param.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results of a training evaluation on a given parameter at a given index
|
||||
*
|
||||
* Note that it returns all recorded evaluations.
|
||||
* Index determines the evaluation used not the epoch's results to return.
|
||||
*/
|
||||
public List<IEvaluation> trainingEval(String param, int index) {
|
||||
List<IEvaluation> data = new ArrayList<>();
|
||||
for(EvaluationRecord er : trainingHistory)
|
||||
data.add(er.evaluation(param, index));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results of a training evaluation on a given parameter at a given index
|
||||
*
|
||||
* Note that it returns all recorded evaluations.
|
||||
* Index determines the evaluation used not the epoch's results to return.
|
||||
*/
|
||||
public List<IEvaluation> trainingEval(SDVariable param, int index){
|
||||
return trainingEval(param.name(), index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results of a validation evaluation on a given parameter for a given metric
|
||||
*
|
||||
* Only works if there is only one evaluation with the given metric for param
|
||||
*/
|
||||
public List<Double> validationEval(String param, IMetric metric) {
|
||||
List<Double> data = new ArrayList<>();
|
||||
for(EvaluationRecord er : validationHistory)
|
||||
data.add(er.getValue(param, metric));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results of a validation evaluation on a given parameter for a given metric
|
||||
*
|
||||
* Only works if there is only one evaluation with the given metric for param
|
||||
*/
|
||||
public List<Double> validationEval(SDVariable param, IMetric metric) {
|
||||
return validationEval(param.name(), metric);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results of a validation evaluation on a given parameter at a given index, for a given metric
|
||||
*
|
||||
* Note that it returns all recorded evaluations.
|
||||
* Index determines the evaluation used not the epoch's results to return.
|
||||
*/
|
||||
public List<Double> validationEval(String param, int index, IMetric metric) {
|
||||
List<Double> data = new ArrayList<>();
|
||||
for(EvaluationRecord er : validationHistory)
|
||||
data.add(er.getValue(param, index, metric));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results of a validation evaluation on a given parameter at a given index, for a given metric
|
||||
*
|
||||
* Note that it returns all recorded evaluations.
|
||||
* Index determines the evaluation used not the epoch's results to return.
|
||||
*/
|
||||
public List<Double> validationEval(SDVariable param, int index, IMetric metric) {
|
||||
return validationEval(param.name(), index, metric);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results of a validation evaluation for a given metric
|
||||
*
|
||||
* Only works if there is only one evaluation with the given metric
|
||||
*/
|
||||
public List<Double> validationEval(IMetric metric) {
|
||||
List<Double> data = new ArrayList<>();
|
||||
for(EvaluationRecord er : validationHistory)
|
||||
data.add(er.getValue(metric));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results of a validation evaluation on a given parameter
|
||||
*
|
||||
* Only works if there is only one evaluation for param.
|
||||
*/
|
||||
public List<IEvaluation> validationEval(String param) {
|
||||
List<IEvaluation> data = new ArrayList<>();
|
||||
for(EvaluationRecord er : validationHistory)
|
||||
data.add(er.evaluation(param));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results of a validation evaluation on a given parameter
|
||||
*
|
||||
* Only works if there is only one evaluation for param.
|
||||
*/
|
||||
public List<IEvaluation> validationEval(SDVariable param){
|
||||
return validationEval(param.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results of a validation evaluation on a given parameter at a given index
|
||||
*
|
||||
* Note that it returns all recorded evaluations.
|
||||
* Index determines the evaluation used not the epoch's results to return.
|
||||
*/
|
||||
public List<IEvaluation> validationEval(String param, int index) {
|
||||
List<IEvaluation> data = new ArrayList<>();
|
||||
for(EvaluationRecord er : validationHistory)
|
||||
data.add(er.evaluation(param, index));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results of a validation evaluation on a given parameter at a given index
|
||||
*
|
||||
* Note that it returns all recorded evaluations.
|
||||
* Index determines the evaluation used not the epoch's results to return.
|
||||
*/
|
||||
public List<IEvaluation> validationEval(SDVariable param, int index){
|
||||
return validationEval(param.name(), index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the training evaluations ran during the last epoch
|
||||
*/
|
||||
public EvaluationRecord finalTrainingEvaluations() {
|
||||
Preconditions.checkState(!trainingHistory.isEmpty(), "Cannot get final training evaluation - history is empty");
|
||||
return trainingHistory.get(trainingHistory.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the validation evaluations ran during the last epoch
|
||||
*/
|
||||
public EvaluationRecord finalValidationEvaluations() {
|
||||
Preconditions.checkState(!validationHistory.isEmpty(), "Cannot get final validation evaluation - history is empty");
|
||||
return validationHistory.get(validationHistory.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the evaluation record for a given epoch.
|
||||
* @param epoch The epoch to get results for. If negative, returns results for the epoch that many epochs from the end.
|
||||
*/
|
||||
public EvaluationRecord trainingEvaluations(int epoch) {
|
||||
if(epoch >= 0){
|
||||
return trainingHistory.get(epoch);
|
||||
} else {
|
||||
return trainingHistory.get(trainingHistory.size() - epoch);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the evaluation record for a given epoch.
|
||||
* @param epoch The epoch to get results for. If negative, returns results for the epoch that many epochs from the end.
|
||||
*/
|
||||
public EvaluationRecord validationEvaluations(int epoch) {
|
||||
if(epoch >= 0){
|
||||
return trainingHistory.get(epoch);
|
||||
} else {
|
||||
return validationHistory.get(validationHistory.size() - epoch);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.listeners.records;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import org.nd4j.autodiff.listeners.Loss;
|
||||
import org.nd4j.autodiff.samediff.SDVariable;
|
||||
import org.nd4j.common.base.Preconditions;
|
||||
import org.nd4j.linalg.api.buffer.DataType;
|
||||
import org.nd4j.linalg.api.ndarray.INDArray;
|
||||
import org.nd4j.linalg.factory.Nd4j;
|
||||
|
||||
public class LossCurve {
|
||||
@Getter
|
||||
private List<String> lossNames;
|
||||
@Getter
|
||||
private INDArray lossValues;
|
||||
|
||||
public LossCurve(List<Loss> losses) {
|
||||
lossNames = Collections.unmodifiableList(losses.get(0).getLossNames());
|
||||
int numLossValues = losses.get(0).lossValues().length;
|
||||
lossValues = Nd4j.create(DataType.FLOAT, losses.size(), losses.get(0).lossValues().length);
|
||||
|
||||
for(int i = 0 ; i < losses.size() ; i++) {
|
||||
Loss l = losses.get(i);
|
||||
Preconditions.checkArgument(l.getLossNames().equals(lossNames),
|
||||
"Loss names for loss %s differ from others. Expected %s, got %s",
|
||||
i, lossNames, l.getLossNames());
|
||||
|
||||
Preconditions.checkArgument(l.getLosses().length == numLossValues,
|
||||
"Number of loss values for loss %s differ from others. Expected %s, got %s",
|
||||
i, numLossValues, l.getLosses().length);
|
||||
|
||||
lossValues = lossValues.putRow(i, Nd4j.createFromArray(l.getLosses()).castTo(DataType.FLOAT));
|
||||
}
|
||||
}
|
||||
|
||||
public LossCurve(double[] lossValues, List<String> lossNames) {
|
||||
this.lossValues = Nd4j.createFromArray(new double[][]{ lossValues}).castTo(DataType.FLOAT);
|
||||
this.lossNames = lossNames;
|
||||
}
|
||||
|
||||
protected LossCurve(INDArray lossValues, List<String> lossNames) {
|
||||
Preconditions.checkArgument(lossValues.rank() == 2, "lossValues must have a rank of 2, got %s", lossValues.rank());
|
||||
Preconditions.checkArgument(lossValues.dataType() == DataType.FLOAT, "lossValues must be type FLOAT, got %s", lossValues.dataType());
|
||||
this.lossValues = lossValues;
|
||||
this.lossNames = lossNames;
|
||||
}
|
||||
|
||||
public List<Loss> losses(){
|
||||
List<Loss> losses = new ArrayList<>();
|
||||
for(int i = 0 ; i < lossValues.size(0) ; i++){
|
||||
losses.add(new Loss(lossNames, lossValues.getRow(i).toDoubleVector()));
|
||||
}
|
||||
return losses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mean loss for a given epoch
|
||||
*
|
||||
* If epoch is negative, counts backwards from the end.
|
||||
* E.g. losses(-1) gets the last epoch.
|
||||
*
|
||||
* @param epoch The epoch to get. If negative, returns results for the epoch that many epochs from the end
|
||||
*/
|
||||
public Loss meanLoss(int epoch){
|
||||
if(epoch >= 0){
|
||||
return new Loss(lossNames, lossValues.getRow(epoch).toDoubleVector());
|
||||
} else {
|
||||
return new Loss(lossNames, lossValues.getRow(lossValues.rows() + epoch).toDoubleVector());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mean loss for the last epoch.
|
||||
*/
|
||||
public Loss lastMeanLoss(){
|
||||
return meanLoss(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all mean loss values for a given variable
|
||||
*/
|
||||
public float[] meanLoss(@NonNull String lossName){
|
||||
|
||||
int idx = lossNames.indexOf(lossName);
|
||||
|
||||
Preconditions.checkArgument(idx >= 0, "No loss value for %s. Existing losses: %s", lossName, lossNames);
|
||||
|
||||
float[] loss = new float[(int) lossValues.size(0)];
|
||||
for(int i = 0 ; i < lossValues.size(0) ; i++){
|
||||
loss[i] = lossValues.getFloat(i, idx);
|
||||
}
|
||||
return loss;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all mean loss values for a given variable
|
||||
*/
|
||||
public float[] meanLoss(@NonNull SDVariable loss){
|
||||
return meanLoss(loss.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mean loss value for a given variable on a given epoch.
|
||||
*
|
||||
* See {@link #meanLoss(int)}
|
||||
*/
|
||||
public float meanLoss(@NonNull String lossName, int epoch){
|
||||
|
||||
int idx = lossNames.indexOf(lossName);
|
||||
|
||||
Preconditions.checkArgument(idx >= 0, "No loss value for %s. Existing losses: %s", lossName, lossNames);
|
||||
|
||||
if(epoch >= 0) {
|
||||
return lossValues.getFloat(epoch, idx);
|
||||
} else {
|
||||
return lossValues.getFloat(lossValues.rows() + epoch, idx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mean loss value for a given variable on a given epoch.
|
||||
*
|
||||
* See {@link #meanLoss(int)}
|
||||
*/
|
||||
public float meanLoss(@NonNull SDVariable loss, int epoch){
|
||||
return meanLoss(loss.name(), epoch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mean loss value for a given variable on the last epoch.
|
||||
*/
|
||||
public float lastMeanLoss(@NonNull String lossName){
|
||||
|
||||
int idx = lossNames.indexOf(lossName);
|
||||
|
||||
Preconditions.checkArgument(idx >= 0, "No loss value for %s. Existing losses: %s", lossName, lossNames);
|
||||
|
||||
return lossValues.getFloat(lossValues.rows() - 1, idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mean loss value for a given variable on the last epoch.
|
||||
*/
|
||||
public float lastMeanLoss(@NonNull SDVariable loss){
|
||||
return lastMeanLoss(loss.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the loss delta between the last epoch and the one before it.
|
||||
* Equivalent to meanLoss(-1) - meanLoss(-2).
|
||||
* A positive delta means the loss is increasing, and a negative delta means it is decreasing.
|
||||
*/
|
||||
public Loss lastMeanDelta(){
|
||||
return lastMeanLoss().sub(meanLoss(-2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the loss delta between the last epoch and the one before it, for a given variable.
|
||||
* Equivalent to meanLoss(-1) - meanLoss(-2).
|
||||
* A positive delta means the loss is increasing, and a negative delta means it is decreasing.
|
||||
*/
|
||||
public double lastMeanDelta(String lossName){
|
||||
return lastMeanDelta().getLoss(lossName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the loss delta between the last epoch and the one before it, for a given variable.
|
||||
* Equivalent to meanLoss(-1) - meanLoss(-2).
|
||||
* A positive delta means the loss is increasing, and a negative delta means it is decreasing.
|
||||
*/
|
||||
public double lastMeanDelta(SDVariable loss){
|
||||
return lastMeanDelta(loss.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new LossCurve with the given losses added on as the most recent epoch
|
||||
*/
|
||||
public LossCurve addLossAndCopy(Loss loss){
|
||||
return addLossAndCopy(loss.getLosses(), loss.lossNames());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new LossCurve with the given losses added on as the most recent epoch
|
||||
*/
|
||||
public LossCurve addLossAndCopy(double[] values, List<String> lossNames){
|
||||
return new LossCurve(
|
||||
Nd4j.concat(0, lossValues,
|
||||
Nd4j.createFromArray(new double[][]{values}).castTo(DataType.FLOAT)),
|
||||
lossNames);
|
||||
}
|
||||
}
|
||||
+58
@@ -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.autodiff.loss;
|
||||
|
||||
public enum LossReduce {
|
||||
/**
|
||||
* No reduction. In most cases, output is the same shape as the predictions/labels.<br>
|
||||
* Weights (if any) are applied<br>
|
||||
* Example Input: 2d input array with mean squared error loss.<br>
|
||||
* Example Output: squaredDifference(predictions,labels), with same shape as input/labels<br>
|
||||
*/
|
||||
NONE,
|
||||
|
||||
/**
|
||||
* Weigted sum across all loss values, returning a scalar.<br>
|
||||
*/
|
||||
SUM,
|
||||
|
||||
/**
|
||||
* Weighted mean: sum(weights * perOutputLoss) / sum(weights) - gives a single scalar output<br>
|
||||
* Example: 2d input, mean squared error<br>
|
||||
* Output: squared_error_per_ex = weights * squaredDifference(predictions,labels)<br>
|
||||
* output = sum(squared_error_per_ex) / sum(weights)<br>
|
||||
* <br>
|
||||
* NOTE: if weights array is not provided, then weights default to 1.0 for all entries - and hence
|
||||
* MEAN_BY_WEIGHT is equivalent to MEAN_BY_NONZERO_WEIGHT_COUNT
|
||||
*/
|
||||
MEAN_BY_WEIGHT,
|
||||
|
||||
/**
|
||||
* Weighted mean: sum(weights * perOutputLoss) / count(weights != 0)<br>
|
||||
* Example: 2d input, mean squared error loss.<br>
|
||||
* Output: squared_error_per_ex = weights * squaredDifference(predictions,labels)<br>
|
||||
* output = sum(squared_error_per_ex) / count(weights != 0)<br>
|
||||
*
|
||||
* NOTE: if weights array is not provided, then weights default to scalar 1.0 and hence MEAN_BY_NONZERO_WEIGHT_COUNT
|
||||
* is equivalent to MEAN_BY_WEIGHT
|
||||
*/
|
||||
MEAN_BY_NONZERO_WEIGHT_COUNT
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
public interface ArgumentInterceptor {
|
||||
SDVariable intercept(SDVariable argument);
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
import org.nd4j.linalg.api.ndarray.INDArray;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface ArrayHolder {
|
||||
|
||||
/**
|
||||
* @return True if an array by that name exists
|
||||
*/
|
||||
boolean hasArray(String name);
|
||||
|
||||
/**
|
||||
* @param name Name of the array to get
|
||||
* @return The array, or null if no array with that name exists
|
||||
*/
|
||||
INDArray getArray(String name);
|
||||
|
||||
/**
|
||||
* Set the array for the specified name (new array, or replace if it already exists)
|
||||
*
|
||||
* @param name Name of the array
|
||||
* @param array Array to set
|
||||
*/
|
||||
void setArray(String name, INDArray array);
|
||||
|
||||
/**
|
||||
* Remove the array from the ArrayHolder, returning it (if it exists)
|
||||
*
|
||||
* @param name Name of the array to return
|
||||
* @return The now-removed array
|
||||
*/
|
||||
INDArray removeArray(String name);
|
||||
|
||||
/**
|
||||
* @return Number of arrays in the ArrayHolder
|
||||
*/
|
||||
int size();
|
||||
|
||||
/**
|
||||
* Initialize from the specified array holder.
|
||||
* This clears all internal arrays, and adds all arrays from the specified array holder
|
||||
*
|
||||
* @param arrayHolder Array holder to initialize this based on
|
||||
*/
|
||||
void initFrom(ArrayHolder arrayHolder);
|
||||
|
||||
/**
|
||||
* @return Names of the arrays currently in the ArrayHolder
|
||||
*/
|
||||
Collection<String> arrayNames();
|
||||
|
||||
/**
|
||||
* Rename the entry with the specified name
|
||||
*
|
||||
* @param from Original name
|
||||
* @param to New name
|
||||
*/
|
||||
void rename(String from, String to);
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package org.nd4j.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class ConditionEvaluation {
|
||||
private int iteration;
|
||||
private String conditionOperation;
|
||||
private Object conditionValue;
|
||||
private String conditionSource;
|
||||
private boolean terminationTriggered;
|
||||
private Map<String, Object> inputValues = new HashMap<>();
|
||||
private String evaluationContext;
|
||||
private long timestamp;
|
||||
}
|
||||
+636
@@ -0,0 +1,636 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import org.nd4j.autodiff.samediff.internal.SameDiffOp;
|
||||
import org.nd4j.common.base.Preconditions;
|
||||
import org.nd4j.linalg.api.buffer.DataType;
|
||||
import org.nd4j.linalg.api.ops.custom.Invoke;
|
||||
import org.nd4j.linalg.api.ops.impl.controlflow.compat.Enter;
|
||||
import org.nd4j.linalg.api.ops.impl.controlflow.compat.Exit;
|
||||
import org.nd4j.linalg.api.ops.impl.controlflow.compat.Merge;
|
||||
import org.nd4j.linalg.api.ops.impl.controlflow.compat.NextIteration;
|
||||
import org.nd4j.linalg.factory.Nd4j;
|
||||
import org.nd4j.shade.guava.collect.Sets;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Top level class for looping constructs in samediff.
|
||||
* This includes the ability to create for and while loops as well as
|
||||
* encapsulate the usage of invoke as a function body. This spec can be read here:
|
||||
* https://github.com/onnx/onnx/blob/main/docs/Operators.md#Loop
|
||||
*
|
||||
* The core components of the looping function are as follows:
|
||||
* 1. Loop variables:
|
||||
* a. current iteration (gets updated during loop body) (defaults to 0)
|
||||
* b. max number of iterations (defaults to {@link Long#MAX_VALUE}
|
||||
* c. a current condition a user passes in and is updated during lambda invocation
|
||||
* Any variables beyond the first 3 are extra variables by the user
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class ControlFlow {
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the loop variables with default parameters. The variables are as follows:
|
||||
* current iteration
|
||||
* max number of iterations
|
||||
* extra condition to use
|
||||
*
|
||||
*
|
||||
*
|
||||
* The passed in variable names will be assumed to be names for each of these variables
|
||||
* mentioned above respectively. Please ensure that these are the intended names
|
||||
* of the variables.
|
||||
* @param namesToUse the names of the variables to use. Must be length 2.
|
||||
* @param loopBody the loop body to initialize
|
||||
* @param maxIterations the max iterations to iterate over
|
||||
*/
|
||||
public static SDVariable[] initializeLoopBody(String[] namesToUse,SameDiff loopBody,int maxIterations) {
|
||||
Preconditions.checkState( namesToUse != null && namesToUse.length == 2,"Number of input names must be 2.");
|
||||
SDVariable[] ret = new SDVariable[] {
|
||||
loopBody.constant(namesToUse[1], maxIterations),
|
||||
loopBody.var(namesToUse[0], Nd4j.zeros(1)),
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the loop variables with default parameters. The variables are as follows:
|
||||
* current iteration
|
||||
* max number of iterations
|
||||
* extra condition to use
|
||||
*
|
||||
* The passed in variable names will be assumed to be names for each of these variables
|
||||
* mentioned above respectively. Please ensure that these are the intended names
|
||||
* of the variables.
|
||||
* @param namesToUse the names of the variables to use. Must be length 3.
|
||||
* @param loopBody the loop body to initialize
|
||||
* @param maxIterations the max iterations to iterate over
|
||||
* @param extraCond the extra condition to use
|
||||
*/
|
||||
public static SDVariable[] initializeLoopBody(String[] namesToUse,SameDiff loopBody,int maxIterations,boolean extraCond) {
|
||||
Preconditions.checkState( namesToUse != null && namesToUse.length == 3,"Number of input names must be 3.");
|
||||
SDVariable[] ret = new SDVariable[] {
|
||||
loopBody.var(namesToUse[0], Nd4j.zeros(1)),
|
||||
loopBody.constant(namesToUse[1], maxIterations),
|
||||
loopBody.constant(namesToUse[2], extraCond)
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the arguments used in {@link #condBody()}
|
||||
* and {@link #loopWithConditions(String[], String, SameDiff, SameDiff, String, SDVariable[], String[], String[])}
|
||||
* @param maxIterations the max number of iterations
|
||||
* @param condIn the input conditions
|
||||
* @param startIterations the start iterations
|
||||
* @param extraArgs the extra arguments for the user
|
||||
* @return the ordered arguments
|
||||
*/
|
||||
public static SDVariable[] args(SDVariable maxIterations,SDVariable condIn,SDVariable startIterations,SDVariable[] extraArgs) {
|
||||
return LoopArgs.builder().extraArgs(extraArgs)
|
||||
.condIn(condIn)
|
||||
.maxIters(maxIterations)
|
||||
.startIter(startIterations).build().toArgs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a If statement using the tensorflow style control flow operations (Switch and Merge)
|
||||
*
|
||||
* If the result of cond is true, returns the result of trueBody, otherwise returns the result of falseBody
|
||||
*
|
||||
* Note that cond and body lambdas are only called once to construct the graph. The constructed graph is used to evaluate.
|
||||
*
|
||||
* See <a href="http://download.tensorflow.org/paper/white_paper_tf_control_flow_implementation_2017_11_1.pdf">Tensorflow Control Flow Implementation</a>
|
||||
*
|
||||
* @param outputName Name to give the output variable. If null, doesn't rename
|
||||
* @param ifName The name of the if block. If null, uses "if"
|
||||
* @param cond A lambda evaluating to the if condition
|
||||
* @param trueBody A lambda to be executed if cond is true (the if block)
|
||||
* @param falseBody A lambda to be executed if cond is false (the else block)
|
||||
* @return The value of trueBody if cond is true, or falseBody if it isn't
|
||||
*/
|
||||
public static SDVariable ifCond(SameDiff sameDiff,String outputName, String ifName, @NonNull SameDiffNoArgSingleLambda cond,
|
||||
@NonNull SameDiffNoArgSingleLambda trueBody, @NonNull SameDiffNoArgSingleLambda falseBody){
|
||||
|
||||
ifName = sameDiff.newBlockName(ifName == null ? "if" : ifName);
|
||||
|
||||
NameScope ifScope = sameDiff.withNameScope(ifName);
|
||||
|
||||
NameScope condScope = sameDiff.withNameScope("cond");
|
||||
final SDVariable pred = cond.define(sameDiff);
|
||||
condScope.close();
|
||||
|
||||
if (pred.dataType() != DataType.BOOL) {
|
||||
//cleanup partially added block
|
||||
|
||||
for(SDVariable v : sameDiff.getVariablesInScope(ifScope))
|
||||
sameDiff.getVariables().remove(v.name());
|
||||
|
||||
for(SameDiffOp op : sameDiff.getOpsInScope(ifScope)) {
|
||||
for(String in : op.getInputsToOp()){
|
||||
sameDiff.removeArgFromOp(in, op.getOp());
|
||||
}
|
||||
sameDiff.getOps().remove(op.getName());
|
||||
}
|
||||
|
||||
|
||||
throw new IllegalStateException("Can not use " + pred.name()
|
||||
+ " as the condition of an If statement, the condition must be a boolean.");
|
||||
}
|
||||
|
||||
final Map<String, SDVariable[]> switches = new HashMap<>();
|
||||
|
||||
final Set<String> declared = Sets.newHashSet(sameDiff.variableMap().keySet());
|
||||
|
||||
sameDiff.addArgumentInterceptor(argument -> {
|
||||
|
||||
if(argument == null)
|
||||
return null;
|
||||
// if its declared in the if, we don't care about it
|
||||
if(declared == null || !declared.contains(argument.name()))
|
||||
return argument;
|
||||
|
||||
// if we've already added a switch, move on
|
||||
if(switches.containsKey(argument.name()))
|
||||
return switches.get(argument.name())[1];
|
||||
|
||||
SDVariable[] s = sameDiff.switchOp(argument, pred);
|
||||
switches.put(argument.name(), s);
|
||||
return s[1];
|
||||
});
|
||||
NameScope trueScope = sameDiff.withNameScope("trueBody");
|
||||
SDVariable trueOut = trueBody.define(sameDiff);
|
||||
sameDiff.removeArgumentInterceptor();
|
||||
|
||||
if(declared.contains(trueOut.name())) {
|
||||
SDVariable[] s = sameDiff.switchOp(trueOut, pred);
|
||||
switches.put(trueOut.name(), s);
|
||||
trueOut = s[1];
|
||||
}
|
||||
|
||||
trueScope.close();
|
||||
|
||||
final Set<String> declared2 = Sets.newHashSet(sameDiff.variableMap().keySet());
|
||||
sameDiff.addArgumentInterceptor(argument -> {
|
||||
|
||||
// if its declared in the if, we don't care about it
|
||||
if(!declared2.contains(argument.name()))
|
||||
return argument;
|
||||
|
||||
// if we've already added a switch, move on
|
||||
if(switches.containsKey(argument.name()))
|
||||
return switches.get(argument.name())[0];
|
||||
|
||||
SDVariable[] s = sameDiff.switchOp(argument, pred);
|
||||
switches.put(argument.name(), s);
|
||||
return s[0];
|
||||
});
|
||||
NameScope falseScope = sameDiff.withNameScope("falseBody");
|
||||
SDVariable falseOut = falseBody.define(sameDiff);
|
||||
sameDiff.removeArgumentInterceptor();
|
||||
|
||||
if(declared2.contains(falseOut.name())) {
|
||||
SDVariable[] s = sameDiff.switchOp(falseOut, pred);
|
||||
switches.put(falseOut.name(), s);
|
||||
falseOut = s[0];
|
||||
}
|
||||
falseScope.close();
|
||||
|
||||
SDVariable output = sameDiff.merge(trueOut, falseOut);
|
||||
|
||||
ifScope.close();
|
||||
|
||||
return sameDiff.updateVariableNameAndReference(output, outputName);
|
||||
}
|
||||
|
||||
@Builder
|
||||
@Data
|
||||
public static class LoopArgs {
|
||||
private SDVariable condIn,maxIters,startIter;
|
||||
private SDVariable[] extraArgs;
|
||||
|
||||
public SDVariable[] toArgs() {
|
||||
SDVariable[] ret = new SDVariable[3 + extraArgs.length];
|
||||
ret[0] = startIter;
|
||||
ret[1] = maxIters;
|
||||
ret[2] = condIn;
|
||||
for(int i = 0; i < extraArgs.length; i++) {
|
||||
ret[i + 3] = extraArgs[i];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Builder
|
||||
@Data
|
||||
public static class LoopParams {
|
||||
private String[] outputVarNames;
|
||||
private String loopName;
|
||||
private SameDiff parent;
|
||||
private SameDiff functionBody;
|
||||
private String functionName;
|
||||
private SDVariable[] loopVars;
|
||||
private String[] functionBodyInputs;
|
||||
private String[] functionBodyOutputs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A simplified function using {@link LoopParams}
|
||||
* invoking the same function as {@link #loopWithConditions(String[], String, SameDiff, SameDiff, String, SDVariable[], String[], String[])}
|
||||
* @param loopParams the loop parameters to use
|
||||
* @return
|
||||
*/
|
||||
public static SDVariable[] loopWithConditions(LoopParams loopParams) {
|
||||
return loopWithConditions(loopParams.outputVarNames,
|
||||
loopParams.loopName,loopParams.parent,
|
||||
loopParams.functionBody,
|
||||
loopParams.functionName,
|
||||
loopParams.loopVars,
|
||||
loopParams.functionBodyInputs,
|
||||
loopParams.functionBodyOutputs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop with conditions allows a user to provide a lambda to invoke
|
||||
* any number of times.
|
||||
* @param outputVarNames the output variable names to use
|
||||
* @param loopName the name of the loop to use when creating the variables/ops
|
||||
* @param parent the parent samediff instance to put the loop
|
||||
* @param functionBody the function body to use
|
||||
* @param functionName the name of the function to use within the samediff instance
|
||||
* @param loopVars the loop variables to use during execution
|
||||
* @param functionBodyInputs the inputs to invoke the function with
|
||||
* @param functionBodyOutputs the outputs to be retrieved from the function itself
|
||||
* @return the output exit variables at the end of the loop
|
||||
*/
|
||||
public static SDVariable[] loopWithConditions(
|
||||
String[] outputVarNames,
|
||||
String loopName,
|
||||
SameDiff parent,
|
||||
SameDiff functionBody,
|
||||
String functionName,
|
||||
SDVariable[] loopVars,
|
||||
String[] functionBodyInputs,
|
||||
String[] functionBodyOutputs) {
|
||||
Preconditions.checkState(functionBodyInputs != null && functionBodyOutputs != null && functionBodyInputs.length == functionBodyOutputs.length,"Sub graph input and output names must be defined and equal in length.");
|
||||
Preconditions.checkState(loopVars.length == functionBodyInputs.length,"Loop variables and function body inputs must be equal in length.");
|
||||
for(SDVariable variable : loopVars) {
|
||||
if(variable.getSameDiff() != parent) {
|
||||
throw new IllegalArgumentException("Variable named " + variable.name() + " does not have correct samediff instance. Must have parent outer samediff instance.");
|
||||
}
|
||||
}
|
||||
|
||||
SameDiffSingleLambda cond = condBody();
|
||||
SameDiffLambda loopBody = loopBody(parent,functionBody,functionName,functionBodyInputs,functionBodyOutputs,outputVarNames);
|
||||
return parent.whileLoop(outputVarNames,loopName,loopVars,cond,loopBody);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create {@link LoopLambdaArgs} from the given arguments.
|
||||
* This is used to properly order arguments for use with {@link #loopBody(SameDiff, SameDiff, String, String[], String[])}
|
||||
* and {@link #condBody()}
|
||||
* @param inputs the inputs to order, these generally should be from within a lambda. The first 3 arguments are:
|
||||
* current iter count, maximum number of iterations, extra arguments if any
|
||||
* @return
|
||||
*/
|
||||
public static LoopLambdaArgs argsFromInputs(SDVariable[] inputs) {
|
||||
|
||||
SDVariable[] extraArgs = inputs.length > 3 ? new SDVariable[inputs.length - 3] : new SDVariable[0];
|
||||
//add extra arguments offset by 3 representing custom inputs
|
||||
if(extraArgs.length > 0) {
|
||||
for(int i = 0; i < extraArgs.length; i++) {
|
||||
extraArgs[i] = inputs[i + 3];
|
||||
}
|
||||
}
|
||||
return LoopLambdaArgs.builder()
|
||||
.iterCount(inputs[1])
|
||||
.iterStart(inputs[0])
|
||||
.condIn(inputs[2])
|
||||
.extraArgs(extraArgs)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class LoopLambdaArgs {
|
||||
|
||||
private SDVariable iterStart;
|
||||
private SDVariable iterCount;
|
||||
private SDVariable condIn;
|
||||
private SDVariable[] extraArgs;
|
||||
|
||||
@Builder
|
||||
public LoopLambdaArgs(SDVariable iterStart,SDVariable iterCount,SDVariable[] extraArgs,SDVariable condIn) {
|
||||
if(condIn.dataType() != DataType.BOOL) {
|
||||
throw new IllegalArgumentException("Data type for condition must be boolean!");
|
||||
}
|
||||
|
||||
if(!iterCount.dataType().isNumerical()) {
|
||||
throw new IllegalArgumentException("Data type for condition must be numerical!");
|
||||
}
|
||||
|
||||
this.iterCount = iterCount;
|
||||
this.extraArgs = extraArgs;
|
||||
this.condIn = condIn;
|
||||
this.iterStart = iterStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct {@link org.nd4j.linalg.api.ops.custom.Invoke.InvokeParams}
|
||||
* for usage with {@link SameDiff#invoke(Invoke.InvokeParams)}
|
||||
* the variables here reflect what is used in the loop.
|
||||
* A user can use {@link LoopLambdaArgs} to create an appropriately configured
|
||||
* {@link org.nd4j.linalg.api.ops.custom.Invoke.InvokeParams} to be used
|
||||
* with the body.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param functionName the name of the function to invoke
|
||||
* @param subGraphInputNames the subgraph input names to invoke the function with
|
||||
* @param subGraphOutputNames the subgraph output names to expect returned from the function
|
||||
* @return the appropriate invoke parameters for use with {@link #condBody()} and {@link #loopBody(SameDiff, SameDiff, String, String[], String[])}
|
||||
*/
|
||||
public Invoke.InvokeParams invokeParams(String functionName,String[] subGraphInputNames,String[] subGraphOutputNames) {
|
||||
return invokeParams(functionName, subGraphInputNames, subGraphOutputNames, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct {@link org.nd4j.linalg.api.ops.custom.Invoke.InvokeParams}
|
||||
* for usage with {@link SameDiff#invoke(Invoke.InvokeParams)}
|
||||
* the variables here reflect what is used in the loop.
|
||||
* A user can use {@link LoopLambdaArgs} to create an appropriately configured
|
||||
* {@link org.nd4j.linalg.api.ops.custom.Invoke.InvokeParams} to be used
|
||||
* with the body.
|
||||
*
|
||||
* @param functionName the name of the function to invoke
|
||||
* @param subGraphInputNames the subgraph input names to invoke the function with
|
||||
* @param subGraphOutputNames the subgraph output names to expect returned from the function
|
||||
* @param outputVarNames the output variable names for the parent graph
|
||||
* @return the appropriate invoke parameters for use with {@link #condBody()} and {@link #loopBody(SameDiff, SameDiff, String, String[], String[])}
|
||||
*/
|
||||
public Invoke.InvokeParams invokeParams(String functionName, String[] subGraphInputNames, String[] subGraphOutputNames, String[] outputVarNames) {
|
||||
List<SDVariable> inputs = new ArrayList<>();
|
||||
//starting iteration
|
||||
inputs.add(iterStart);
|
||||
//ending iteration
|
||||
inputs.add(iterCount);
|
||||
//user custom condition
|
||||
inputs.add(condIn);
|
||||
inputs.addAll(Arrays.asList(extraArgs));
|
||||
return Invoke.InvokeParams.builder()
|
||||
.functionName(functionName)
|
||||
.inputs(inputs.toArray(new SDVariable[inputs.size()]))
|
||||
.subGraphInputVarNames(subGraphInputNames)
|
||||
.subGraphOutputVarNames(subGraphOutputNames)
|
||||
.inputVarNames(inputs.stream().map(input ->
|
||||
input.name()).collect(Collectors.toList())
|
||||
.toArray(new String[inputs.size()]))
|
||||
.outputVarNames(outputVarNames)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a {@link SameDiffLambda} to be used in combination with
|
||||
* {@link #condBody()} and {@link SameDiff#invoke(Invoke.InvokeParams)}
|
||||
* this lambda will use samediff invoke as the function body
|
||||
* and setup the appropriate parameters to create a looping construct
|
||||
* as described in {@link #loopBody(SameDiff, SameDiff, String, String[], String[])}
|
||||
* @param parent
|
||||
* @param functionBody
|
||||
* @param functionName
|
||||
* @param subGraphInputNames the subgraph input names for use to invoke the graph with
|
||||
* @param subGraphOutputNames the subgraph output names to expect to be returned from the subgraph invoke
|
||||
* @return
|
||||
*/
|
||||
public static SameDiffLambda loopBody(SameDiff parent,
|
||||
SameDiff functionBody,
|
||||
String functionName,
|
||||
String[] subGraphInputNames,
|
||||
String[] subGraphOutputNames) {
|
||||
return loopBody(parent, functionBody, functionName, subGraphInputNames, subGraphOutputNames, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link SameDiffLambda} to be used in combination with
|
||||
* {@link #condBody()} and {@link SameDiff#invoke(Invoke.InvokeParams)}
|
||||
* this lambda will use samediff invoke as the function body
|
||||
* and setup the appropriate parameters to create a looping construct
|
||||
* as described in {@link #loopBody(SameDiff, SameDiff, String, String[], String[])}
|
||||
* @param parent
|
||||
* @param functionBody
|
||||
* @param functionName
|
||||
* @param subGraphInputNames the subgraph input names for use to invoke the graph with
|
||||
* @param subGraphOutputNames the subgraph output names to expect to be returned from the subgraph invoke
|
||||
* @param outputVarNames the output variable names for the parent graph
|
||||
* @return
|
||||
*/
|
||||
public static SameDiffLambda loopBody(SameDiff parent,
|
||||
SameDiff functionBody,
|
||||
String functionName,
|
||||
String[] subGraphInputNames,
|
||||
String[] subGraphOutputNames,
|
||||
String[] outputVarNames) {
|
||||
Preconditions.checkState(subGraphInputNames != null && subGraphOutputNames != null && subGraphInputNames.length == subGraphOutputNames.length,"Sub graph input and output names must be defined and equal in length.");
|
||||
if(parent.getFunction(functionName) == null)
|
||||
parent.putSubFunction(functionName,functionBody);
|
||||
return (sameDiff, inputs) -> {
|
||||
LoopLambdaArgs loopLambdaArgs = ControlFlow.argsFromInputs(inputs);
|
||||
Invoke.InvokeParams invokeParams = loopLambdaArgs.invokeParams(functionName, subGraphInputNames, subGraphOutputNames, outputVarNames);
|
||||
SDVariable[] invoke = sameDiff.invoke(invokeParams);
|
||||
List<SDVariable> retList = new ArrayList<>();
|
||||
//current iterations + 1 (each time the body is invoked update the current iteration)
|
||||
retList.add(inputs[0].add(1.0));
|
||||
retList.add(inputs[1]);
|
||||
retList.add(invoke[2]);
|
||||
|
||||
//assign extra parameters to the invoke output
|
||||
//loop over non condition out variables starting from the end
|
||||
for(int i = 3; i < invoke.length; i++) {
|
||||
retList.add(invoke[i]);
|
||||
}
|
||||
|
||||
return retList.toArray(new SDVariable[retList.size()]);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a While loop using the tensorflow style control flow operations (Switch, Merge, Enter, Exit, and NextIteration)
|
||||
* <p>
|
||||
* Repeatedly executes body on the loop variables and updates them with the results, until cond evaluates to false
|
||||
* <p>
|
||||
* Note that cond and body lambdas are only called once to construct the graph. The constructed graph is used for further iterations.
|
||||
* <p>
|
||||
* See <a href="http://download.tensorflow.org/paper/white_paper_tf_control_flow_implementation_2017_11_1.pdf">Tensorflow Control Flow Implementation</a>
|
||||
*
|
||||
* @param outputNames Names to give the output variables. If null, doesn't rename
|
||||
* @param loopName The name of the loop block and frame (must be unique). If null, uses "if"
|
||||
* @param loopVars Loop variables' inputs
|
||||
* @param cond A lambda evaluating to the loop condition
|
||||
* @param body A lambda doing the loop operation and returning the new loop variable values
|
||||
* @return The values of the loop variables once condition is false
|
||||
*/
|
||||
public static SDVariable[] whileLoop(SameDiff sameDiff, String[] outputNames, final String loopName, @NonNull SDVariable[] loopVars,
|
||||
@NonNull SameDiffSingleLambda cond, @NonNull SameDiffLambda body) {
|
||||
|
||||
final String frameName = sameDiff.newBlockName(loopName == null ? "while" : loopName);
|
||||
|
||||
NameScope loopScope = sameDiff.withNameScope(frameName);
|
||||
|
||||
SDVariable counter = sameDiff.scalar(sameDiff.generateNewVarName("counter", 0), 0);
|
||||
|
||||
SDVariable[] entered = new SDVariable[loopVars.length];
|
||||
for (int i = 0; i < loopVars.length; i++) {
|
||||
entered[i] = new Enter(sameDiff, frameName, loopVars[i]).outputVariable();
|
||||
}
|
||||
|
||||
SDVariable[] merged = new SDVariable[loopVars.length];
|
||||
Merge[] mergeOps = new Merge[loopVars.length];
|
||||
for (int i = 0; i < loopVars.length; i++) {
|
||||
// the second arg will later be replaced with the output of NextIteration
|
||||
// but that isn't available yet (and can't be, as it depends on this)
|
||||
mergeOps[i] = new Merge(sameDiff, entered[i], entered[i]);
|
||||
mergeOps[i].setFrameName(frameName);
|
||||
merged[i] = mergeOps[i].outputVariable();
|
||||
}
|
||||
|
||||
Merge counterMerge = new Merge(sameDiff, counter, counter);
|
||||
counter = counterMerge.outputVariable();
|
||||
counterMerge.setFrameName(frameName);
|
||||
|
||||
NameScope condScope = sameDiff.withNameScope("cond");
|
||||
SDVariable condResult = cond.define(sameDiff, merged);
|
||||
condScope.close();
|
||||
|
||||
|
||||
if (condResult.dataType() != DataType.BOOL)
|
||||
throw new IllegalStateException("Can not use " + condResult.name() + " as the condition of an While loop, the condition must be a boolean.");
|
||||
|
||||
|
||||
final Set<String> alreadyEntered = Sets.newHashSet();
|
||||
SDVariable[] trueSwitches = new SDVariable[loopVars.length];
|
||||
SDVariable[] exits = new SDVariable[loopVars.length];
|
||||
for (int i = 0; i < loopVars.length; i++) {
|
||||
SDVariable[] s = sameDiff.switchOp(merged[i], condResult);
|
||||
trueSwitches[i] = s[1];
|
||||
alreadyEntered.add(s[1].name());
|
||||
Exit exit = new Exit(sameDiff, s[0]);
|
||||
exit.setFrameName(frameName);
|
||||
exits[i] = exit.outputVariable();
|
||||
}
|
||||
|
||||
final Set<String> declared = Sets.newHashSet(sameDiff.variableMap().keySet());
|
||||
final Map<String, SDVariable> done = new HashMap<>();
|
||||
|
||||
final SameDiff sd = sameDiff;
|
||||
sameDiff.addArgumentInterceptor(argument -> {
|
||||
if (argument == null)
|
||||
return null;
|
||||
|
||||
if (!declared.contains(argument.name()))
|
||||
return argument;
|
||||
|
||||
if (alreadyEntered.contains(argument.name()))
|
||||
return argument;
|
||||
|
||||
if (done.containsKey(argument.name()))
|
||||
return done.get(argument.name());
|
||||
|
||||
SDVariable e = new Enter(sd, frameName, argument, true).outputVariable();
|
||||
done.put(argument.name(), e);
|
||||
return e;
|
||||
});
|
||||
|
||||
NameScope bodyScope = sameDiff.withNameScope("body");
|
||||
SDVariable[] outs = body.define(sameDiff, trueSwitches);
|
||||
if (outs.length != mergeOps.length)
|
||||
throw new IllegalArgumentException("Number of loop variables must be equal to number of outputs.");
|
||||
bodyScope.close();
|
||||
sameDiff.removeArgumentInterceptor();
|
||||
|
||||
counter.add(1);
|
||||
|
||||
for (int i = 0; i < outs.length; i++) {
|
||||
NextIteration nextIteration = new NextIteration(sameDiff, outs[i]);
|
||||
nextIteration.setFrameName(frameName);
|
||||
SDVariable n = nextIteration.outputVariable();
|
||||
mergeOps[i].replaceArg(1, n);
|
||||
}
|
||||
|
||||
counterMerge.replaceArg(1, counter);
|
||||
|
||||
loopScope.close();
|
||||
return sameDiff.updateVariableNamesAndReferences(exits, outputNames);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a lambda that takes in a custom condition and a built-in for
|
||||
* loop counter concept in the following manner:
|
||||
* int currIteration = 0;
|
||||
* boolean cond = ...;
|
||||
* int maxIterations = ...;
|
||||
* for(int i = currIteration; i < maxIterations && cond; i++) {
|
||||
* //body....
|
||||
* }
|
||||
*
|
||||
* The inputs to the lambda are the following order:
|
||||
* currIteration (the starting iteration)
|
||||
* maxIterations (the number of times to loop)
|
||||
* cond: the custom condition the user passes in
|
||||
*
|
||||
*
|
||||
* @return the lambda described above for usage in the {@link #whileLoop(SameDiff, String[], String, SDVariable[], SameDiffSingleLambda, SameDiffLambda)}
|
||||
* routine
|
||||
*/
|
||||
public static SameDiffSingleLambda condBody() {
|
||||
// combine for loop and while loop together
|
||||
return (sameDiff, inputs) -> {
|
||||
SDVariable currIteration = inputs[0];
|
||||
SDVariable maxIterations = inputs[1];
|
||||
SDVariable extraCond = inputs[2];
|
||||
SDVariable and = sameDiff.bitwise().and(
|
||||
currIteration.lt(maxIterations.castTo(currIteration.dataType()))
|
||||
.castTo(DataType.INT64),
|
||||
extraCond.castTo(DataType.INT64));
|
||||
|
||||
|
||||
SDVariable ret = and.castTo( DataType.BOOL);
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
+46
@@ -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.autodiff.samediff;
|
||||
|
||||
/**
|
||||
* Cross-frame variable reference information
|
||||
*/
|
||||
public class CrossFrameReference {
|
||||
public String variableName;
|
||||
public String sourceFrame;
|
||||
public String targetFrame;
|
||||
public int sourceIteration;
|
||||
public int targetIteration;
|
||||
public String mediatingOperation;
|
||||
public CrossFrameReferenceType referenceType;
|
||||
|
||||
public CrossFrameReference() {
|
||||
// Default constructor
|
||||
}
|
||||
|
||||
public CrossFrameReference(String variableName, String sourceFrame, String targetFrame,
|
||||
CrossFrameReferenceType referenceType) {
|
||||
this.variableName = variableName;
|
||||
this.sourceFrame = sourceFrame;
|
||||
this.targetFrame = targetFrame;
|
||||
this.referenceType = referenceType;
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
/**
|
||||
* Types of cross-frame variable references
|
||||
*/
|
||||
public enum CrossFrameReferenceType {
|
||||
DIRECT, // Direct variable reference
|
||||
ENTER, // Variable entering frame
|
||||
EXIT, // Variable exiting frame
|
||||
LOOP_CARRIED, // Variable carried across loop iterations
|
||||
CONDITIONAL // Variable from conditional branch
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package org.nd4j.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class CrossLoopAnalysis {
|
||||
private Map<TerminationType, Long> terminationTypeDistribution = new HashMap<>();
|
||||
private List<String> terminationCorrelations = new ArrayList<>();
|
||||
private List<String> systemWideIssues = new ArrayList<>();
|
||||
private Map<String, Integer> commonProblematicVariables = new HashMap<>();
|
||||
}
|
||||
+853
@@ -0,0 +1,853 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
import org.nd4j.linalg.api.buffer.DataType;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Class to hold the results of a dry run execution analysis with enhanced frame granularity
|
||||
*/
|
||||
public class DAGExecutionPlan {
|
||||
// Basic execution plan data
|
||||
private List<String> executionOrder = new ArrayList<>();
|
||||
private Map<String, Set<String>> dependencies = new HashMap<>();
|
||||
private List<String> requestedOutputs = new ArrayList<>();
|
||||
private Map<String, VariableInfo> variables = new HashMap<>();
|
||||
private Map<String, OperationInfo> operations = new HashMap<>();
|
||||
private Set<String> leafVariables = new HashSet<>();
|
||||
private Set<String> trainableVariables = new HashSet<>();
|
||||
private Set<String> sequenceVariables = new HashSet<>();
|
||||
private List<String> missingVariables = new ArrayList<>();
|
||||
private List<String> orphanedVariables = new ArrayList<>();
|
||||
private List<String> cycles = new ArrayList<>();
|
||||
private Map<String, List<String>> controlDependencies = new HashMap<>();
|
||||
private Map<String, List<String>> variableControlDependencies = new HashMap<>();
|
||||
|
||||
// Enhanced frame management structures
|
||||
private Set<String> enterOperations = new HashSet<>();
|
||||
private Set<String> exitOperations = new HashSet<>();
|
||||
private Set<String> switchOperations = new HashSet<>();
|
||||
private Set<String> mergeOperations = new HashSet<>();
|
||||
private Set<String> nextIterationOperations = new HashSet<>();
|
||||
private Set<String> loopConditionOperations = new HashSet<>();
|
||||
|
||||
// Frame hierarchy and relationships
|
||||
private Map<String, String> frameHierarchy = new HashMap<>(); // frame -> parent frame
|
||||
private Map<String, Set<String>> framesUsed = new HashMap<>(); // frame -> operations in frame
|
||||
private Map<String, Set<String>> frameChildren = new HashMap<>(); // frame -> child frames
|
||||
private Map<String, Integer> frameDepth = new HashMap<>(); // frame -> nesting depth
|
||||
private Map<String, FrameMetadata> frameMetadata = new HashMap<>(); // frame -> detailed metadata
|
||||
|
||||
// Frame execution analysis
|
||||
private Map<String, List<String>> frameExecutionOrder = new HashMap<>(); // frame -> ops in execution order
|
||||
private Map<String, Set<String>> frameVariables = new HashMap<>(); // frame -> variables in frame
|
||||
private Map<String, Set<String>> frameBoundaryOperations = new HashMap<>(); // frame -> boundary ops
|
||||
private Map<String, Map<String, Integer>> frameIterationCounts = new HashMap<>(); // frame -> var -> max iteration
|
||||
|
||||
// Frame transitions and dependencies
|
||||
private Map<String, List<FrameTransitionInfo>> frameTransitions = new HashMap<>(); // from frame -> transitions
|
||||
private Map<String, Set<String>> frameDependencies = new HashMap<>(); // frame -> dependent frames
|
||||
private Map<String, Set<String>> frameProducers = new HashMap<>(); // frame -> frames that produce data for this frame
|
||||
private Map<String, Set<String>> frameConsumers = new HashMap<>(); // frame -> frames that consume data from this frame
|
||||
|
||||
// Cross-frame data flow
|
||||
private Map<String, List<CrossFrameReference>> crossFrameReferences = new HashMap<>(); // var -> cross-frame refs
|
||||
private Map<String, Set<String>> frameInputVariables = new HashMap<>(); // frame -> input variables from other frames
|
||||
private Map<String, Set<String>> frameOutputVariables = new HashMap<>(); // frame -> output variables to other frames
|
||||
|
||||
// Basic getters and setters
|
||||
public List<String> getExecutionOrder() {
|
||||
return executionOrder;
|
||||
}
|
||||
|
||||
public void setExecutionOrder(List<String> executionOrder) {
|
||||
this.executionOrder = executionOrder;
|
||||
}
|
||||
|
||||
public Map<String, Set<String>> getDependencies() {
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
public void setDependencies(Map<String, Set<String>> dependencies) {
|
||||
this.dependencies = dependencies;
|
||||
}
|
||||
|
||||
public List<String> getRequestedOutputs() {
|
||||
return requestedOutputs;
|
||||
}
|
||||
|
||||
public void setRequestedOutputs(List<String> requestedOutputs) {
|
||||
this.requestedOutputs = requestedOutputs;
|
||||
}
|
||||
|
||||
// Frame management methods
|
||||
|
||||
/**
|
||||
* Add a frame with detailed metadata
|
||||
*/
|
||||
public void addFrame(String frameName, String parentFrame, FrameType type) {
|
||||
int depth = parentFrame == null ? 0 : frameDepth.getOrDefault(parentFrame, 0) + 1;
|
||||
FrameMetadata metadata = new FrameMetadata(frameName, parentFrame, depth, type);
|
||||
frameMetadata.put(frameName, metadata);
|
||||
frameDepth.put(frameName, depth);
|
||||
|
||||
if (parentFrame != null) {
|
||||
frameHierarchy.put(frameName, parentFrame);
|
||||
frameChildren.computeIfAbsent(parentFrame, k -> new HashSet<>()).add(frameName);
|
||||
frameMetadata.get(parentFrame).childFrames.add(frameName);
|
||||
}
|
||||
|
||||
framesUsed.computeIfAbsent(frameName, k -> new HashSet<>());
|
||||
frameExecutionOrder.computeIfAbsent(frameName, k -> new ArrayList<>());
|
||||
frameVariables.computeIfAbsent(frameName, k -> new HashSet<>());
|
||||
frameBoundaryOperations.computeIfAbsent(frameName, k -> new HashSet<>());
|
||||
frameIterationCounts.computeIfAbsent(frameName, k -> new HashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add frame transition information
|
||||
*/
|
||||
public void addFrameTransition(String fromFrame, String toFrame, FrameTransition transitionType,
|
||||
String operationName, List<String> affectedVariables, int iterationChange) {
|
||||
FrameTransitionInfo transition = new FrameTransitionInfo(fromFrame, toFrame, transitionType, operationName);
|
||||
if (affectedVariables != null) {
|
||||
transition.affectedVariables.addAll(affectedVariables);
|
||||
}
|
||||
transition.iterationChange = iterationChange;
|
||||
|
||||
frameTransitions.computeIfAbsent(fromFrame, k -> new ArrayList<>()).add(transition);
|
||||
|
||||
// Update frame dependencies
|
||||
if (toFrame != null && !fromFrame.equals(toFrame)) {
|
||||
frameDependencies.computeIfAbsent(toFrame, k -> new HashSet<>()).add(fromFrame);
|
||||
frameProducers.computeIfAbsent(toFrame, k -> new HashSet<>()).add(fromFrame);
|
||||
frameConsumers.computeIfAbsent(fromFrame, k -> new HashSet<>()).add(toFrame);
|
||||
}
|
||||
|
||||
// Update metadata
|
||||
FrameMetadata fromMeta = frameMetadata.get(fromFrame);
|
||||
if (fromMeta != null) {
|
||||
fromMeta.transitionCounts.merge(transitionType, 1, Integer::sum);
|
||||
if (transitionType == FrameTransition.NEXT_ITERATION) {
|
||||
fromMeta.hasLoops = true;
|
||||
} else if (transitionType == FrameTransition.SWITCH) {
|
||||
fromMeta.hasConditionals = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add cross-frame variable reference
|
||||
*/
|
||||
public void addCrossFrameReference(String variableName, String sourceFrame, String targetFrame,
|
||||
int sourceIteration, int targetIteration, String mediatingOperation,
|
||||
CrossFrameReferenceType type) {
|
||||
CrossFrameReference ref = new CrossFrameReference();
|
||||
ref.variableName = variableName;
|
||||
ref.sourceFrame = sourceFrame;
|
||||
ref.targetFrame = targetFrame;
|
||||
ref.sourceIteration = sourceIteration;
|
||||
ref.targetIteration = targetIteration;
|
||||
ref.mediatingOperation = mediatingOperation;
|
||||
ref.referenceType = type;
|
||||
|
||||
crossFrameReferences.computeIfAbsent(variableName, k -> new ArrayList<>()).add(ref);
|
||||
|
||||
// Update frame input/output tracking
|
||||
if (targetFrame != null) {
|
||||
frameInputVariables.computeIfAbsent(targetFrame, k -> new HashSet<>()).add(variableName);
|
||||
}
|
||||
if (sourceFrame != null) {
|
||||
frameOutputVariables.computeIfAbsent(sourceFrame, k -> new HashSet<>()).add(variableName);
|
||||
}
|
||||
}
|
||||
|
||||
public void addFrameOperation(String opName, FrameTransition transition, String frame, String parentFrame) {
|
||||
switch (transition) {
|
||||
case ENTER:
|
||||
enterOperations.add(opName);
|
||||
if (frame != null) {
|
||||
frameBoundaryOperations.computeIfAbsent(frame, k -> new HashSet<>()).add(opName);
|
||||
FrameMetadata meta = frameMetadata.get(frame);
|
||||
if (meta != null) {
|
||||
meta.entryPoints.add(opName);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EXIT:
|
||||
exitOperations.add(opName);
|
||||
if (frame != null) {
|
||||
frameBoundaryOperations.computeIfAbsent(frame, k -> new HashSet<>()).add(opName);
|
||||
FrameMetadata meta = frameMetadata.get(frame);
|
||||
if (meta != null) {
|
||||
meta.exitPoints.add(opName);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SWITCH:
|
||||
switchOperations.add(opName);
|
||||
break;
|
||||
case MERGE:
|
||||
mergeOperations.add(opName);
|
||||
break;
|
||||
case NEXT_ITERATION:
|
||||
nextIterationOperations.add(opName);
|
||||
break;
|
||||
case LOOP_CONDITION:
|
||||
loopConditionOperations.add(opName);
|
||||
break;
|
||||
}
|
||||
|
||||
if (parentFrame != null && frame != null) {
|
||||
frameHierarchy.put(frame, parentFrame);
|
||||
}
|
||||
}
|
||||
|
||||
public void addOperationWithFrame(String opName, String opType, String className,
|
||||
List<String> inputs, List<String> outputs, FrameInfo frameInfo) {
|
||||
OperationInfo opInfo = new OperationInfo(opName, opType, className, inputs, outputs);
|
||||
opInfo.setFrameInfo(frameInfo);
|
||||
operations.put(opName, opInfo);
|
||||
|
||||
// Track frame usage and execution order
|
||||
String frame = frameInfo.outputFrame;
|
||||
if (frame != null) {
|
||||
framesUsed.computeIfAbsent(frame, k -> new HashSet<>()).add(opName);
|
||||
frameExecutionOrder.computeIfAbsent(frame, k -> new ArrayList<>()).add(opName);
|
||||
|
||||
// Update frame metadata
|
||||
FrameMetadata meta = frameMetadata.get(frame);
|
||||
if (meta != null) {
|
||||
meta.totalOperations++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addVariableWithFrame(String name, VariableType type, DataType dataType,
|
||||
String frame, int iteration, String parentFrame) {
|
||||
VariableInfo varInfo = new VariableInfo(name, type, dataType);
|
||||
varInfo.frame = frame;
|
||||
varInfo.iteration = iteration;
|
||||
varInfo.parentFrame = parentFrame;
|
||||
variables.put(name, varInfo);
|
||||
|
||||
// Track frame variables and iterations
|
||||
if (frame != null) {
|
||||
frameVariables.computeIfAbsent(frame, k -> new HashSet<>()).add(name);
|
||||
frameIterationCounts.computeIfAbsent(frame, k -> new HashMap<>())
|
||||
.merge(name, iteration, Integer::max);
|
||||
|
||||
// Update frame metadata
|
||||
FrameMetadata meta = frameMetadata.get(frame);
|
||||
if (meta != null) {
|
||||
meta.totalVariables++;
|
||||
meta.maxIterations = Math.max(meta.maxIterations, iteration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Production utility methods
|
||||
|
||||
/**
|
||||
* Get all operations within a specific frame
|
||||
*/
|
||||
public List<String> getOperationsInFrame(String frameName) {
|
||||
return frameExecutionOrder.getOrDefault(frameName, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all variables within a specific frame
|
||||
*/
|
||||
public Set<String> getVariablesInFrame(String frameName) {
|
||||
return frameVariables.getOrDefault(frameName, Collections.emptySet());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get frame for a specific operation
|
||||
*/
|
||||
public String getOperationFrame(String operationName) {
|
||||
OperationInfo opInfo = operations.get(operationName);
|
||||
if (opInfo != null && opInfo.getFrameInfo() != null) {
|
||||
return opInfo.getFrameInfo().outputFrame;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get frame for a specific variable
|
||||
*/
|
||||
public String getVariableFrame(String variableName) {
|
||||
VariableInfo varInfo = variables.get(variableName);
|
||||
if (varInfo != null) {
|
||||
return varInfo.frame;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all variables that are shared between frames
|
||||
*/
|
||||
public Map<String, List<String>> getSharedVariables() {
|
||||
Map<String, List<String>> sharedVars = new HashMap<>();
|
||||
Map<String, Set<String>> varToFrames = new HashMap<>();
|
||||
|
||||
// Build mapping of variables to frames
|
||||
for (Map.Entry<String, Set<String>> entry : frameVariables.entrySet()) {
|
||||
String frame = entry.getKey();
|
||||
for (String var : entry.getValue()) {
|
||||
varToFrames.computeIfAbsent(var, k -> new HashSet<>()).add(frame);
|
||||
}
|
||||
}
|
||||
|
||||
// Find variables in multiple frames
|
||||
for (Map.Entry<String, Set<String>> entry : varToFrames.entrySet()) {
|
||||
if (entry.getValue().size() > 1) {
|
||||
sharedVars.put(entry.getKey(), new ArrayList<>(entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
return sharedVars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get operations that produce outputs for a specific frame
|
||||
*/
|
||||
public List<String> getFrameProducerOperations(String frameName) {
|
||||
Set<String> producers = frameProducers.getOrDefault(frameName, Collections.emptySet());
|
||||
List<String> producerOps = new ArrayList<>();
|
||||
|
||||
for (String producerFrame : producers) {
|
||||
producerOps.addAll(getOperationsInFrame(producerFrame));
|
||||
}
|
||||
|
||||
return producerOps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get operations that consume outputs from a specific frame
|
||||
*/
|
||||
public List<String> getFrameConsumerOperations(String frameName) {
|
||||
Set<String> consumers = frameConsumers.getOrDefault(frameName, Collections.emptySet());
|
||||
List<String> consumerOps = new ArrayList<>();
|
||||
|
||||
for (String consumerFrame : consumers) {
|
||||
consumerOps.addAll(getOperationsInFrame(consumerFrame));
|
||||
}
|
||||
|
||||
return consumerOps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all child frames for a given parent frame
|
||||
*/
|
||||
public Set<String> getChildFrames(String parentFrame) {
|
||||
return frameChildren.getOrDefault(parentFrame, Collections.emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parent frame for a given frame
|
||||
*/
|
||||
public String getParentFrame(String frameName) {
|
||||
return frameHierarchy.get(frameName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a frame contains loops
|
||||
*/
|
||||
public boolean frameHasLoops(String frameName) {
|
||||
FrameMetadata meta = frameMetadata.get(frameName);
|
||||
return meta != null && meta.hasLoops;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a frame contains conditionals
|
||||
*/
|
||||
public boolean frameHasConditionals(String frameName) {
|
||||
FrameMetadata meta = frameMetadata.get(frameName);
|
||||
return meta != null && meta.hasConditionals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get maximum iteration count for variables in a frame
|
||||
*/
|
||||
public int getFrameMaxIterations(String frameName) {
|
||||
FrameMetadata meta = frameMetadata.get(frameName);
|
||||
return meta != null ? meta.maxIterations : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all frames that this frame depends on (transitive closure)
|
||||
*/
|
||||
public Set<String> getAllFrameDependencies(String frameName) {
|
||||
Set<String> allDeps = new HashSet<>();
|
||||
collectFrameDependencies(frameName, allDeps, new HashSet<>());
|
||||
return allDeps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get operations by frame transition type
|
||||
*/
|
||||
public Set<String> getOperationsByTransition(FrameTransition transitionType) {
|
||||
switch (transitionType) {
|
||||
case ENTER: return enterOperations;
|
||||
case EXIT: return exitOperations;
|
||||
case SWITCH: return switchOperations;
|
||||
case MERGE: return mergeOperations;
|
||||
case NEXT_ITERATION: return nextIterationOperations;
|
||||
case LOOP_CONDITION: return loopConditionOperations;
|
||||
default: return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find operations that execute before entering a specific frame
|
||||
*/
|
||||
public List<String> getPreFrameOperations(String frameName) {
|
||||
List<String> preOps = new ArrayList<>();
|
||||
FrameMetadata meta = frameMetadata.get(frameName);
|
||||
|
||||
if (meta != null && !meta.entryPoints.isEmpty()) {
|
||||
String firstEntryOp = meta.entryPoints.get(0);
|
||||
int entryIndex = executionOrder.indexOf(firstEntryOp);
|
||||
|
||||
if (entryIndex > 0) {
|
||||
for (int i = 0; i < entryIndex; i++) {
|
||||
String opName = executionOrder.get(i);
|
||||
String opFrame = getOperationFrame(opName);
|
||||
if (opFrame == null || !opFrame.equals(frameName)) {
|
||||
preOps.add(opName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return preOps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find operations that execute after exiting a specific frame
|
||||
*/
|
||||
public List<String> getPostFrameOperations(String frameName) {
|
||||
List<String> postOps = new ArrayList<>();
|
||||
FrameMetadata meta = frameMetadata.get(frameName);
|
||||
|
||||
if (meta != null && !meta.exitPoints.isEmpty()) {
|
||||
String lastExitOp = meta.exitPoints.get(meta.exitPoints.size() - 1);
|
||||
int exitIndex = executionOrder.indexOf(lastExitOp);
|
||||
|
||||
if (exitIndex >= 0 && exitIndex < executionOrder.size() - 1) {
|
||||
for (int i = exitIndex + 1; i < executionOrder.size(); i++) {
|
||||
String opName = executionOrder.get(i);
|
||||
String opFrame = getOperationFrame(opName);
|
||||
if (opFrame == null || !opFrame.equals(frameName)) {
|
||||
postOps.add(opName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return postOps;
|
||||
}
|
||||
|
||||
// Analysis methods
|
||||
|
||||
/**
|
||||
* Get all frames at a specific nesting depth
|
||||
*/
|
||||
public Set<String> getFramesAtDepth(int depth) {
|
||||
return frameDepth.entrySet().stream()
|
||||
.filter(e -> e.getValue() == depth)
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the execution path through frames
|
||||
*/
|
||||
public List<String> getFrameExecutionPath() {
|
||||
List<String> path = new ArrayList<>();
|
||||
Map<String, Integer> frameFirstOp = new HashMap<>();
|
||||
|
||||
// Find first operation in each frame
|
||||
for (int i = 0; i < executionOrder.size(); i++) {
|
||||
String opName = executionOrder.get(i);
|
||||
OperationInfo opInfo = operations.get(opName);
|
||||
if (opInfo != null && opInfo.getFrameInfo() != null && opInfo.getFrameInfo().outputFrame != null) {
|
||||
String frame = opInfo.getFrameInfo().outputFrame;
|
||||
if (!frameFirstOp.containsKey(frame)) {
|
||||
frameFirstOp.put(frame, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort frames by first operation order
|
||||
return frameFirstOp.entrySet().stream()
|
||||
.sorted(Map.Entry.comparingByValue())
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze frame dependencies and detect circular dependencies
|
||||
*/
|
||||
public Map<String, Set<String>> analyzeFrameDependencies() {
|
||||
Map<String, Set<String>> result = new HashMap<>();
|
||||
|
||||
for (String frame : frameMetadata.keySet()) {
|
||||
Set<String> allDeps = new HashSet<>();
|
||||
collectFrameDependencies(frame, allDeps, new HashSet<>());
|
||||
result.put(frame, allDeps);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void collectFrameDependencies(String frame, Set<String> allDeps, Set<String> visited) {
|
||||
if (visited.contains(frame)) {
|
||||
// Circular dependency detected
|
||||
cycles.add("Frame circular dependency involving: " + frame);
|
||||
return;
|
||||
}
|
||||
|
||||
visited.add(frame);
|
||||
Set<String> directDeps = frameDependencies.getOrDefault(frame, Collections.emptySet());
|
||||
allDeps.addAll(directDeps);
|
||||
|
||||
for (String dep : directDeps) {
|
||||
collectFrameDependencies(dep, allDeps, visited);
|
||||
}
|
||||
|
||||
visited.remove(frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get detailed frame statistics
|
||||
*/
|
||||
public Map<String, Object> getDetailedFrameStats() {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
|
||||
stats.put("totalFrames", frameMetadata.size());
|
||||
stats.put("maxFrameDepth", frameDepth.values().stream().mapToInt(Integer::intValue).max().orElse(0));
|
||||
stats.put("framesWithLoops", frameMetadata.values().stream().mapToLong(m -> m.hasLoops ? 1 : 0).sum());
|
||||
stats.put("framesWithConditionals", frameMetadata.values().stream().mapToLong(m -> m.hasConditionals ? 1 : 0).sum());
|
||||
stats.put("totalCrossFrameReferences", crossFrameReferences.values().stream().mapToInt(List::size).sum());
|
||||
|
||||
// Frame type distribution
|
||||
Map<FrameType, Long> typeDistribution = frameMetadata.values().stream()
|
||||
.collect(Collectors.groupingBy(m -> m.frameType, Collectors.counting()));
|
||||
stats.put("frameTypeDistribution", typeDistribution);
|
||||
|
||||
// Transition statistics
|
||||
Map<FrameTransition, Integer> totalTransitions = new HashMap<>();
|
||||
for (FrameMetadata meta : frameMetadata.values()) {
|
||||
for (Map.Entry<FrameTransition, Integer> entry : meta.transitionCounts.entrySet()) {
|
||||
totalTransitions.merge(entry.getKey(), entry.getValue(), Integer::sum);
|
||||
}
|
||||
}
|
||||
stats.put("frameTransitions", totalTransitions);
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
// Legacy methods (maintained for compatibility)
|
||||
|
||||
public void addVariable(String name, VariableType type, DataType dataType) {
|
||||
variables.put(name, new VariableInfo(name, type, dataType));
|
||||
}
|
||||
|
||||
public void addOperation(String opName, String opType, String className, List<String> inputs, List<String> outputs) {
|
||||
operations.put(opName, new OperationInfo(opName, opType, className, inputs, outputs));
|
||||
}
|
||||
|
||||
public void addLeafVariable(String name) {
|
||||
leafVariables.add(name);
|
||||
}
|
||||
|
||||
public void addTrainableVariable(String name) {
|
||||
trainableVariables.add(name);
|
||||
}
|
||||
|
||||
public void addSequenceVariable(String name) {
|
||||
sequenceVariables.add(name);
|
||||
}
|
||||
|
||||
public void addMissingVariable(String name) {
|
||||
missingVariables.add(name);
|
||||
}
|
||||
|
||||
public void addOrphanedVariable(String varName, String missingOp) {
|
||||
orphanedVariables.add(varName + (missingOp != null ? " (op: " + missingOp + ")" : ""));
|
||||
}
|
||||
|
||||
public void addCycle(String name) {
|
||||
cycles.add(name);
|
||||
}
|
||||
|
||||
public void addControlDependencies(String opName, List<String> deps) {
|
||||
controlDependencies.put(opName, new ArrayList<>(deps));
|
||||
}
|
||||
|
||||
public void addVariableControlDependencies(String opName, List<String> deps) {
|
||||
variableControlDependencies.put(opName, new ArrayList<>(deps));
|
||||
}
|
||||
|
||||
public boolean isOperationProcessed(String opName) {
|
||||
return operations.containsKey(opName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhanced format summary with detailed frame information
|
||||
*/
|
||||
public String formatSummary() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("=== SameDiff Execution DAG Analysis ===\n\n");
|
||||
|
||||
// Basic summary statistics
|
||||
sb.append("Summary:\n");
|
||||
sb.append(String.format(" Requested outputs: %d\n", requestedOutputs.size()));
|
||||
sb.append(String.format(" Total operations: %d\n", operations.size()));
|
||||
sb.append(String.format(" Total variables: %d\n", variables.size()));
|
||||
sb.append(String.format(" Leaf variables: %d\n", leafVariables.size()));
|
||||
sb.append(String.format(" Trainable variables: %d\n", trainableVariables.size()));
|
||||
|
||||
// Enhanced frame analysis
|
||||
sb.append("\nFrame Analysis:\n");
|
||||
sb.append(String.format(" Total frames: %d\n", frameMetadata.size()));
|
||||
sb.append(String.format(" Max frame depth: %d\n", frameDepth.values().stream().mapToInt(Integer::intValue).max().orElse(0)));
|
||||
sb.append(String.format(" Frames with loops: %d\n", frameMetadata.values().stream().mapToLong(m -> m.hasLoops ? 1 : 0).sum()));
|
||||
sb.append(String.format(" Frames with conditionals: %d\n", frameMetadata.values().stream().mapToLong(m -> m.hasConditionals ? 1 : 0).sum()));
|
||||
sb.append(String.format(" Cross-frame references: %d\n", crossFrameReferences.values().stream().mapToInt(List::size).sum()));
|
||||
|
||||
// Frame hierarchy
|
||||
if (!frameMetadata.isEmpty()) {
|
||||
sb.append("\nFrame Hierarchy:\n");
|
||||
for (String rootFrame : getFramesAtDepth(0)) {
|
||||
printFrameHierarchy(sb, rootFrame, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Frame execution path
|
||||
List<String> framePath = getFrameExecutionPath();
|
||||
if (!framePath.isEmpty()) {
|
||||
sb.append("\nFrame Execution Path:\n");
|
||||
for (int i = 0; i < framePath.size(); i++) {
|
||||
String frame = framePath.get(i);
|
||||
FrameMetadata meta = frameMetadata.get(frame);
|
||||
sb.append(String.format(" %d. %s (%s) - %d ops, %d vars\n",
|
||||
i + 1, frame, meta.frameType, meta.totalOperations, meta.totalVariables));
|
||||
}
|
||||
}
|
||||
|
||||
// Issues
|
||||
if (!missingVariables.isEmpty() || !orphanedVariables.isEmpty() || !cycles.isEmpty()) {
|
||||
sb.append("\nISSUES DETECTED:\n");
|
||||
if (!missingVariables.isEmpty()) {
|
||||
sb.append(String.format(" Missing variables: %s\n", missingVariables));
|
||||
}
|
||||
if (!orphanedVariables.isEmpty()) {
|
||||
sb.append(String.format(" Orphaned variables: %s\n", orphanedVariables));
|
||||
}
|
||||
if (!cycles.isEmpty()) {
|
||||
sb.append(String.format(" Potential cycles: %s\n", cycles));
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void printFrameHierarchy(StringBuilder sb, String frameName, int depth) {
|
||||
FrameMetadata meta = frameMetadata.get(frameName);
|
||||
if (meta == null) return;
|
||||
|
||||
String indentStr = " ".repeat(depth);
|
||||
sb.append(String.format("%s- %s (%s): %d ops, %d vars, max iter: %d\n",
|
||||
indentStr, frameName, meta.frameType, meta.totalOperations, meta.totalVariables, meta.maxIterations));
|
||||
|
||||
for (String child : meta.childFrames) {
|
||||
printFrameHierarchy(sb, child, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get control flow statistics (enhanced)
|
||||
*/
|
||||
public Map<String, Integer> getControlFlowStats() {
|
||||
Map<String, Integer> stats = new HashMap<>();
|
||||
stats.put("enterOps", enterOperations.size());
|
||||
stats.put("exitOps", exitOperations.size());
|
||||
stats.put("switchOps", switchOperations.size());
|
||||
stats.put("mergeOps", mergeOperations.size());
|
||||
stats.put("nextIterationOps", nextIterationOperations.size());
|
||||
stats.put("loopConditionOps", loopConditionOperations.size());
|
||||
stats.put("totalFrames", framesUsed.size());
|
||||
stats.put("maxFrameDepth", frameDepth.values().stream().mapToInt(Integer::intValue).max().orElse(0));
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get operations that directly produce the requested outputs
|
||||
*/
|
||||
public List<String> getOutputProducingOperations() {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (String output : requestedOutputs) {
|
||||
for (OperationInfo op : operations.values()) {
|
||||
if (op.getOutputs() != null && op.getOutputs().contains(output)) {
|
||||
result.add(op.getOperationName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the depth of the execution DAG (longest path)
|
||||
*/
|
||||
public int getExecutionDepth() {
|
||||
return executionOrder.size();
|
||||
}
|
||||
|
||||
// Getters for frame-related data structures
|
||||
public Map<String, FrameMetadata> getFrameMetadata() {
|
||||
return Collections.unmodifiableMap(frameMetadata);
|
||||
}
|
||||
|
||||
public Map<String, List<CrossFrameReference>> getCrossFrameReferences() {
|
||||
return Collections.unmodifiableMap(crossFrameReferences);
|
||||
}
|
||||
|
||||
public Map<String, List<FrameTransitionInfo>> getFrameTransitions() {
|
||||
return Collections.unmodifiableMap(frameTransitions);
|
||||
}
|
||||
|
||||
public Map<String, Set<String>> getFrameDependencies() {
|
||||
return Collections.unmodifiableMap(frameDependencies);
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getFrameExecutionOrder() {
|
||||
return Collections.unmodifiableMap(frameExecutionOrder);
|
||||
}
|
||||
|
||||
public Map<String, Set<String>> getFrameInputVariables() {
|
||||
return Collections.unmodifiableMap(frameInputVariables);
|
||||
}
|
||||
|
||||
public Map<String, Set<String>> getFrameOutputVariables() {
|
||||
return Collections.unmodifiableMap(frameOutputVariables);
|
||||
}
|
||||
|
||||
public Map<String, Set<String>> getFrameBoundaryOperations() {
|
||||
return Collections.unmodifiableMap(frameBoundaryOperations);
|
||||
}
|
||||
|
||||
public Map<String, Set<String>> getFrameProducers() {
|
||||
return Collections.unmodifiableMap(frameProducers);
|
||||
}
|
||||
|
||||
public Map<String, Set<String>> getFrameConsumers() {
|
||||
return Collections.unmodifiableMap(frameConsumers);
|
||||
}
|
||||
|
||||
public Map<String, Integer> getFrameDepth() {
|
||||
return Collections.unmodifiableMap(frameDepth);
|
||||
}
|
||||
|
||||
public Map<String, String> getFrameHierarchy() {
|
||||
return Collections.unmodifiableMap(frameHierarchy);
|
||||
}
|
||||
|
||||
public Map<String, Set<String>> getFrameChildren() {
|
||||
return Collections.unmodifiableMap(frameChildren);
|
||||
}
|
||||
|
||||
public Map<String, Map<String, Integer>> getFrameIterationCounts() {
|
||||
return Collections.unmodifiableMap(frameIterationCounts);
|
||||
}
|
||||
|
||||
public Set<String> getEnterOperations() {
|
||||
return Collections.unmodifiableSet(enterOperations);
|
||||
}
|
||||
|
||||
public Set<String> getExitOperations() {
|
||||
return Collections.unmodifiableSet(exitOperations);
|
||||
}
|
||||
|
||||
public Set<String> getSwitchOperations() {
|
||||
return Collections.unmodifiableSet(switchOperations);
|
||||
}
|
||||
|
||||
public Set<String> getMergeOperations() {
|
||||
return Collections.unmodifiableSet(mergeOperations);
|
||||
}
|
||||
|
||||
public Set<String> getNextIterationOperations() {
|
||||
return Collections.unmodifiableSet(nextIterationOperations);
|
||||
}
|
||||
|
||||
public Set<String> getLoopConditionOperations() {
|
||||
return Collections.unmodifiableSet(loopConditionOperations);
|
||||
}
|
||||
|
||||
public Map<String, VariableInfo> getVariables() {
|
||||
return Collections.unmodifiableMap(variables);
|
||||
}
|
||||
|
||||
public Map<String, OperationInfo> getOperations() {
|
||||
return Collections.unmodifiableMap(operations);
|
||||
}
|
||||
|
||||
public Set<String> getLeafVariables() {
|
||||
return Collections.unmodifiableSet(leafVariables);
|
||||
}
|
||||
|
||||
public Set<String> getTrainableVariables() {
|
||||
return Collections.unmodifiableSet(trainableVariables);
|
||||
}
|
||||
|
||||
public Set<String> getSequenceVariables() {
|
||||
return Collections.unmodifiableSet(sequenceVariables);
|
||||
}
|
||||
|
||||
public List<String> getMissingVariables() {
|
||||
return Collections.unmodifiableList(missingVariables);
|
||||
}
|
||||
|
||||
public List<String> getOrphanedVariables() {
|
||||
return Collections.unmodifiableList(orphanedVariables);
|
||||
}
|
||||
|
||||
public List<String> getCycles() {
|
||||
return Collections.unmodifiableList(cycles);
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getControlDependencies() {
|
||||
return Collections.unmodifiableMap(controlDependencies);
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getVariableControlDependencies() {
|
||||
return Collections.unmodifiableMap(variableControlDependencies);
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Entity classification result for execution failure analysis.
|
||||
* Contains counts and lists of different entity types that are missing.
|
||||
*/
|
||||
@Data
|
||||
public class EntityClassificationResult {
|
||||
|
||||
private final Set<String> operations = new LinkedHashSet<>();
|
||||
private final Set<String> variables = new LinkedHashSet<>();
|
||||
private final Set<String> constants = new LinkedHashSet<>();
|
||||
private final Set<String> placeholders = new LinkedHashSet<>();
|
||||
private final Set<String> unknown = new LinkedHashSet<>();
|
||||
|
||||
/**
|
||||
* Add an entity to the appropriate category
|
||||
*/
|
||||
public void addEntity(String entityName, EntityType type) {
|
||||
switch (type) {
|
||||
case OPERATION:
|
||||
operations.add(entityName);
|
||||
break;
|
||||
case VARIABLE:
|
||||
variables.add(entityName);
|
||||
break;
|
||||
case CONSTANT:
|
||||
constants.add(entityName);
|
||||
break;
|
||||
case PLACEHOLDER:
|
||||
placeholders.add(entityName);
|
||||
break;
|
||||
case UNKNOWN:
|
||||
unknown.add(entityName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total count of all missing entities
|
||||
*/
|
||||
public int getTotalCount() {
|
||||
return operations.size() + variables.size() + constants.size() +
|
||||
placeholders.size() + unknown.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any variables are missing (indicates potential execution framework issue)
|
||||
*/
|
||||
public boolean hasVariables() {
|
||||
return !variables.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any legitimate operations are missing
|
||||
*/
|
||||
public boolean hasOperations() {
|
||||
return !operations.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are any unknown entities
|
||||
*/
|
||||
public boolean hasUnknown() {
|
||||
return !unknown.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count for a specific entity type
|
||||
*/
|
||||
public int getCountForType(EntityType type) {
|
||||
switch (type) {
|
||||
case OPERATION: return operations.size();
|
||||
case VARIABLE: return variables.size();
|
||||
case CONSTANT: return constants.size();
|
||||
case PLACEHOLDER: return placeholders.size();
|
||||
case UNKNOWN: return unknown.size();
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get entities for a specific type
|
||||
*/
|
||||
public Set<String> getEntitiesForType(EntityType type) {
|
||||
switch (type) {
|
||||
case OPERATION: return new LinkedHashSet<>(operations);
|
||||
case VARIABLE: return new LinkedHashSet<>(variables);
|
||||
case CONSTANT: return new LinkedHashSet<>(constants);
|
||||
case PLACEHOLDER: return new LinkedHashSet<>(placeholders);
|
||||
case UNKNOWN: return new LinkedHashSet<>(unknown);
|
||||
default: return new LinkedHashSet<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all classifications
|
||||
*/
|
||||
public void clear() {
|
||||
operations.clear();
|
||||
variables.clear();
|
||||
constants.clear();
|
||||
placeholders.clear();
|
||||
unknown.clear();
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
import org.nd4j.autodiff.samediff.internal.VarId;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Utility class for entity classification and analysis in SameDiff execution.
|
||||
* Provides methods to classify graph entities and analyze missing entities during execution failures.
|
||||
*/
|
||||
public class EntityClassificationUtils {
|
||||
|
||||
/**
|
||||
* Classify a single entity by name
|
||||
*/
|
||||
public static EntityType classifyEntity(String entityName, SameDiff sameDiff) {
|
||||
if (sameDiff.getOps().containsKey(entityName)) {
|
||||
return EntityType.OPERATION;
|
||||
} else if (sameDiff.getVariables().containsKey(entityName)) {
|
||||
return EntityType.VARIABLE;
|
||||
} else if (sameDiff.getConstantArrays().hasArray(entityName)) {
|
||||
return EntityType.CONSTANT;
|
||||
} else if (sameDiff.isPlaceHolder(entityName)) {
|
||||
return EntityType.PLACEHOLDER;
|
||||
} else {
|
||||
return EntityType.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Classify a collection of entities
|
||||
*/
|
||||
public static EntityClassificationResult classifyEntities(Collection<String> entityNames, SameDiff sameDiff) {
|
||||
EntityClassificationResult result = new EntityClassificationResult();
|
||||
|
||||
for (String entityName : entityNames) {
|
||||
EntityType type = classifyEntity(entityName, sameDiff);
|
||||
result.addEntity(entityName, type);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get display icon for entity type
|
||||
*/
|
||||
public static String getEntityTypeIcon(EntityType type) {
|
||||
switch (type) {
|
||||
case OPERATION: return "⚙️";
|
||||
case VARIABLE: return "📊";
|
||||
case CONSTANT: return "📋";
|
||||
case PLACEHOLDER: return "🔲";
|
||||
case UNKNOWN: return "❓";
|
||||
default: return "❓";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get display name for entity type
|
||||
*/
|
||||
public static String getEntityTypeName(EntityType type) {
|
||||
switch (type) {
|
||||
case OPERATION: return "OPERATION";
|
||||
case VARIABLE: return "VARIABLE";
|
||||
case CONSTANT: return "CONSTANT";
|
||||
case PLACEHOLDER: return "PLACEHOLDER";
|
||||
case UNKNOWN: return "UNKNOWN ENTITY";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find variable locations across all frames
|
||||
*/
|
||||
public static List<VarId> findVariableLocations(String variableName,
|
||||
Map<VarId, org.nd4j.autodiff.samediff.config.SDValue> nodeValueOutputs) {
|
||||
List<VarId> locations = new ArrayList<>();
|
||||
|
||||
for (VarId varId : nodeValueOutputs.keySet()) {
|
||||
if (varId.getVariable().equals(variableName)) {
|
||||
locations.add(varId);
|
||||
}
|
||||
}
|
||||
|
||||
return locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an entity name represents a variable that should be accessed via VarId rather than executed
|
||||
*/
|
||||
public static boolean isVariableEntity(String entityName, SameDiff sameDiff) {
|
||||
return classifyEntity(entityName, sameDiff) == EntityType.VARIABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an entity name represents an operation that can be executed
|
||||
*/
|
||||
public static boolean isOperationEntity(String entityName, SameDiff sameDiff) {
|
||||
return classifyEntity(entityName, sameDiff) == EntityType.OPERATION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all entity names of a specific type from SameDiff
|
||||
*/
|
||||
public static Set<String> getEntitiesByType(EntityType type, SameDiff sameDiff) {
|
||||
switch (type) {
|
||||
case OPERATION:
|
||||
return new LinkedHashSet<>(sameDiff.getOps().keySet());
|
||||
case VARIABLE:
|
||||
return new LinkedHashSet<>(sameDiff.getVariables().keySet());
|
||||
case CONSTANT:
|
||||
return new LinkedHashSet<>(sameDiff.getConstantArrays().arrayNames());
|
||||
case PLACEHOLDER:
|
||||
return new LinkedHashSet<>(sameDiff.variables().stream().filter(input -> input.getVariableType() == VariableType.PLACEHOLDER).map(input -> input.name()).collect(Collectors.toList()));
|
||||
default:
|
||||
return new LinkedHashSet<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
/**
|
||||
* Entity type enumeration for SameDiff graph elements.
|
||||
* Used to classify different types of entities in the computation graph.
|
||||
*/
|
||||
public enum EntityType {
|
||||
/**
|
||||
* Operation in the computation graph
|
||||
*/
|
||||
OPERATION,
|
||||
|
||||
/**
|
||||
* Variable in the computation graph (including outputs of operations)
|
||||
*/
|
||||
VARIABLE,
|
||||
|
||||
/**
|
||||
* Constant value in the computation graph
|
||||
*/
|
||||
CONSTANT,
|
||||
|
||||
/**
|
||||
* Placeholder that requires runtime input
|
||||
*/
|
||||
PLACEHOLDER,
|
||||
|
||||
/**
|
||||
* Unknown or unclassified entity
|
||||
*/
|
||||
UNKNOWN
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
package org.nd4j.autodiff.samediff;
|
||||
|
||||
import org.nd4j.autodiff.samediff.internal.ExecType;
|
||||
import org.nd4j.autodiff.samediff.internal.FrameIter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a single execution step in the visualization
|
||||
*/
|
||||
public class ExecutionStep {
|
||||
private final int stepNumber;
|
||||
private final String timestamp;
|
||||
private final ExecType type;
|
||||
private final String name;
|
||||
private final String frame;
|
||||
private final int iteration;
|
||||
private final FrameIter parentFrame;
|
||||
private final List<String> inputs;
|
||||
private final List<String> outputs;
|
||||
private final String status;
|
||||
|
||||
public ExecutionStep(int stepNumber, String timestamp,
|
||||
ExecType type, String name,
|
||||
String frame, int iteration,
|
||||
FrameIter parentFrame,
|
||||
List<String> inputs, List<String> outputs, String status) {
|
||||
this.stepNumber = stepNumber;
|
||||
this.timestamp = timestamp;
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.frame = frame;
|
||||
this.iteration = iteration;
|
||||
this.parentFrame = parentFrame;
|
||||
this.inputs = inputs != null ? new ArrayList<>(inputs) : new ArrayList<>();
|
||||
this.outputs = outputs != null ? new ArrayList<>(outputs) : new ArrayList<>();
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
// Getters
|
||||
public int getStepNumber() {
|
||||
return stepNumber;
|
||||
}
|
||||
|
||||
public String getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public ExecType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getFrame() {
|
||||
return frame;
|
||||
}
|
||||
|
||||
public int getIteration() {
|
||||
return iteration;
|
||||
}
|
||||
|
||||
public FrameIter getParentFrame() {
|
||||
return parentFrame;
|
||||
}
|
||||
|
||||
public List<String> getInputs() {
|
||||
return inputs;
|
||||
}
|
||||
|
||||
public List<String> getOutputs() {
|
||||
return outputs;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Utility class for analyzing frame structures and dependencies in DAG execution plans
|
||||
*/
|
||||
public class FrameAnalyzer {
|
||||
|
||||
/**
|
||||
* Analyze frame execution patterns and detect potential issues
|
||||
*/
|
||||
public static FrameAnalysisResult analyzeFrameExecution(DAGExecutionPlan plan) {
|
||||
FrameAnalysisResult result = new FrameAnalysisResult();
|
||||
|
||||
// Analyze frame depth and nesting
|
||||
analyzeFrameNesting(plan, result);
|
||||
|
||||
// Detect frame dependency cycles
|
||||
detectFrameCycles(plan, result);
|
||||
|
||||
// Analyze frame transition patterns
|
||||
analyzeTransitionPatterns(plan, result);
|
||||
|
||||
// Check for frame isolation issues
|
||||
checkFrameIsolation(plan, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the critical path through frames
|
||||
*/
|
||||
public static List<String> findFrameCriticalPath(DAGExecutionPlan plan) {
|
||||
Map<String, Integer> frameOperationCounts = new HashMap<>();
|
||||
|
||||
for (Map.Entry<String, List<String>> entry : plan.getFrameExecutionOrder().entrySet()) {
|
||||
frameOperationCounts.put(entry.getKey(), entry.getValue().size());
|
||||
}
|
||||
|
||||
return frameOperationCounts.entrySet().stream()
|
||||
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(ArrayList::new, (list, item) -> list.add(item), ArrayList::addAll);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate frame execution efficiency metrics
|
||||
*/
|
||||
public static Map<String, Double> calculateFrameEfficiency(DAGExecutionPlan plan) {
|
||||
Map<String, Double> efficiency = new HashMap<>();
|
||||
|
||||
for (String frameName : plan.getFrameMetadata().keySet()) {
|
||||
List<String> frameOps = plan.getOperationsInFrame(frameName);
|
||||
Set<String> frameVars = plan.getVariablesInFrame(frameName);
|
||||
|
||||
if (!frameOps.isEmpty()) {
|
||||
double opsToVarsRatio = frameVars.isEmpty() ? 0.0 : (double) frameOps.size() / frameVars.size();
|
||||
efficiency.put(frameName, opsToVarsRatio);
|
||||
}
|
||||
}
|
||||
|
||||
return efficiency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find frames that could be parallelized
|
||||
*/
|
||||
public static Set<Set<String>> findParallelizableFrames(DAGExecutionPlan plan) {
|
||||
Set<Set<String>> parallelGroups = new HashSet<>();
|
||||
Map<String, Set<String>> frameDeps = plan.analyzeFrameDependencies();
|
||||
|
||||
// Find frames at the same depth with no dependencies between them
|
||||
Map<Integer, Set<String>> framesByDepth = new HashMap<>();
|
||||
for (Map.Entry<String, FrameMetadata> entry : plan.getFrameMetadata().entrySet()) {
|
||||
framesByDepth.computeIfAbsent(entry.getValue().depth, k -> new HashSet<>()).add(entry.getKey());
|
||||
}
|
||||
|
||||
for (Set<String> framesAtDepth : framesByDepth.values()) {
|
||||
if (framesAtDepth.size() > 1) {
|
||||
Set<String> parallelizable = new HashSet<>();
|
||||
for (String frame : framesAtDepth) {
|
||||
boolean canParallelize = true;
|
||||
for (String otherFrame : framesAtDepth) {
|
||||
if (!frame.equals(otherFrame)) {
|
||||
Set<String> deps = frameDeps.getOrDefault(frame, Collections.emptySet());
|
||||
if (deps.contains(otherFrame)) {
|
||||
canParallelize = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (canParallelize) {
|
||||
parallelizable.add(frame);
|
||||
}
|
||||
}
|
||||
if (parallelizable.size() > 1) {
|
||||
parallelGroups.add(parallelizable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parallelGroups;
|
||||
}
|
||||
|
||||
private static void analyzeFrameNesting(DAGExecutionPlan plan, FrameAnalysisResult result) {
|
||||
int maxDepth = 0;
|
||||
Map<Integer, Integer> depthCounts = new HashMap<>();
|
||||
|
||||
for (FrameMetadata meta : plan.getFrameMetadata().values()) {
|
||||
maxDepth = Math.max(maxDepth, meta.depth);
|
||||
depthCounts.merge(meta.depth, 1, Integer::sum);
|
||||
}
|
||||
|
||||
result.maxNestingDepth = maxDepth;
|
||||
result.frameCountByDepth = depthCounts;
|
||||
|
||||
// Flag deeply nested frames as potential issues
|
||||
if (maxDepth > 5) {
|
||||
result.warnings.add("Deep frame nesting detected (depth: " + maxDepth + "). Consider flattening.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void detectFrameCycles(DAGExecutionPlan plan, FrameAnalysisResult result) {
|
||||
Map<String, Set<String>> frameDeps = plan.getFrameDependencies();
|
||||
Set<String> visited = new HashSet<>();
|
||||
Set<String> recursionStack = new HashSet<>();
|
||||
|
||||
for (String frame : plan.getFrameMetadata().keySet()) {
|
||||
if (!visited.contains(frame)) {
|
||||
if (hasCycleDFS(frame, frameDeps, visited, recursionStack, result.frameCycles)) {
|
||||
result.hasCycles = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasCycleDFS(String frame, Map<String, Set<String>> deps,
|
||||
Set<String> visited, Set<String> stack, List<String> cycles) {
|
||||
visited.add(frame);
|
||||
stack.add(frame);
|
||||
|
||||
Set<String> frameDeps = deps.getOrDefault(frame, Collections.emptySet());
|
||||
for (String dep : frameDeps) {
|
||||
if (!visited.contains(dep)) {
|
||||
if (hasCycleDFS(dep, deps, visited, stack, cycles)) {
|
||||
return true;
|
||||
}
|
||||
} else if (stack.contains(dep)) {
|
||||
cycles.add("Cycle detected: " + frame + " -> " + dep);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
stack.remove(frame);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void analyzeTransitionPatterns(DAGExecutionPlan plan, FrameAnalysisResult result) {
|
||||
Map<FrameTransition, Integer> transitionCounts = new HashMap<>();
|
||||
|
||||
for (FrameMetadata meta : plan.getFrameMetadata().values()) {
|
||||
for (Map.Entry<FrameTransition, Integer> entry : meta.transitionCounts.entrySet()) {
|
||||
transitionCounts.merge(entry.getKey(), entry.getValue(), Integer::sum);
|
||||
}
|
||||
}
|
||||
|
||||
result.transitionPatterns = transitionCounts;
|
||||
|
||||
// Analyze patterns for potential optimizations
|
||||
int enterExitRatio = transitionCounts.getOrDefault(FrameTransition.ENTER, 0) -
|
||||
transitionCounts.getOrDefault(FrameTransition.EXIT, 0);
|
||||
if (Math.abs(enterExitRatio) > 5) {
|
||||
result.warnings.add("Unbalanced ENTER/EXIT transitions (difference: " + enterExitRatio + ")");
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkFrameIsolation(DAGExecutionPlan plan, FrameAnalysisResult result) {
|
||||
for (String frameName : plan.getFrameMetadata().keySet()) {
|
||||
Set<String> inputs = plan.getFrameInputVariables().getOrDefault(frameName, Collections.emptySet());
|
||||
Set<String> outputs = plan.getFrameOutputVariables().getOrDefault(frameName, Collections.emptySet());
|
||||
|
||||
if (inputs.isEmpty() && outputs.isEmpty()) {
|
||||
List<String> frameOps = plan.getOperationsInFrame(frameName);
|
||||
if (!frameOps.isEmpty()) {
|
||||
result.isolatedFrames.add(frameName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!result.isolatedFrames.isEmpty()) {
|
||||
result.warnings.add("Found " + result.isolatedFrames.size() + " isolated frames with no external I/O");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of frame analysis
|
||||
*/
|
||||
public static class FrameAnalysisResult {
|
||||
public int maxNestingDepth;
|
||||
public Map<Integer, Integer> frameCountByDepth = new HashMap<>();
|
||||
public boolean hasCycles = false;
|
||||
public List<String> frameCycles = new ArrayList<>();
|
||||
public Map<FrameTransition, Integer> transitionPatterns = new HashMap<>();
|
||||
public List<String> isolatedFrames = new ArrayList<>();
|
||||
public List<String> warnings = new ArrayList<>();
|
||||
|
||||
public boolean hasIssues() {
|
||||
return hasCycles || !isolatedFrames.isEmpty() || !warnings.isEmpty();
|
||||
}
|
||||
|
||||
public String getSummary() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Frame Analysis Summary:\n");
|
||||
sb.append(" Max nesting depth: ").append(maxNestingDepth).append("\n");
|
||||
sb.append(" Has cycles: ").append(hasCycles).append("\n");
|
||||
sb.append(" Isolated frames: ").append(isolatedFrames.size()).append("\n");
|
||||
sb.append(" Warnings: ").append(warnings.size()).append("\n");
|
||||
|
||||
if (!warnings.isEmpty()) {
|
||||
sb.append("\nWarnings:\n");
|
||||
for (String warning : warnings) {
|
||||
sb.append(" - ").append(warning).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package org.nd4j.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class FrameContextInfo {
|
||||
private String frameName;
|
||||
private int iteration;
|
||||
private String parentFrame;
|
||||
private int nestingDepth;
|
||||
private List<String> relatedFrames = new ArrayList<>();
|
||||
private Map<String, List<String>> crossFrameReferences = new HashMap<>();
|
||||
}
|
||||
+539
@@ -0,0 +1,539 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Utility class for optimizing frame execution order and identifying optimization opportunities
|
||||
*/
|
||||
public class FrameExecutionOptimizer {
|
||||
|
||||
/**
|
||||
* Optimize frame execution order to minimize frame transitions
|
||||
*/
|
||||
public static List<String> optimizeFrameExecutionOrder(DAGExecutionPlan plan) {
|
||||
List<String> originalOrder = plan.getExecutionOrder();
|
||||
Map<String, String> opToFrame = new HashMap<>();
|
||||
|
||||
// Build operation to frame mapping
|
||||
for (String opName : originalOrder) {
|
||||
String frame = plan.getOperationFrame(opName);
|
||||
if (frame != null) {
|
||||
opToFrame.put(opName, frame);
|
||||
}
|
||||
}
|
||||
|
||||
// Group operations by frame while preserving dependencies
|
||||
Map<String, List<String>> frameOps = new LinkedHashMap<>();
|
||||
String currentFrame = null;
|
||||
|
||||
for (String opName : originalOrder) {
|
||||
String opFrame = opToFrame.get(opName);
|
||||
if (opFrame != null) {
|
||||
if (!opFrame.equals(currentFrame)) {
|
||||
currentFrame = opFrame;
|
||||
}
|
||||
frameOps.computeIfAbsent(currentFrame, k -> new ArrayList<>()).add(opName);
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild execution order with optimized frame grouping
|
||||
List<String> optimizedOrder = new ArrayList<>();
|
||||
for (List<String> ops : frameOps.values()) {
|
||||
optimizedOrder.addAll(ops);
|
||||
}
|
||||
|
||||
return optimizedOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify operations that could be moved to reduce frame switches
|
||||
*/
|
||||
public static Map<String, String> identifyMovableOperations(DAGExecutionPlan plan) {
|
||||
Map<String, String> suggestions = new HashMap<>();
|
||||
List<String> execOrder = plan.getExecutionOrder();
|
||||
|
||||
for (int i = 1; i < execOrder.size() - 1; i++) {
|
||||
String currentOp = execOrder.get(i);
|
||||
String prevOp = execOrder.get(i - 1);
|
||||
String nextOp = execOrder.get(i + 1);
|
||||
|
||||
String currentFrame = plan.getOperationFrame(currentOp);
|
||||
String prevFrame = plan.getOperationFrame(prevOp);
|
||||
String nextFrame = plan.getOperationFrame(nextOp);
|
||||
|
||||
// If current operation is in different frame but could be moved
|
||||
if (currentFrame != null && prevFrame != null && nextFrame != null) {
|
||||
if (!currentFrame.equals(prevFrame) && !currentFrame.equals(nextFrame) &&
|
||||
prevFrame.equals(nextFrame)) {
|
||||
|
||||
// Check if operation has dependencies that would allow moving
|
||||
if (canMoveOperation(plan, currentOp, prevFrame)) {
|
||||
suggestions.put(currentOp, "Could move to frame: " + prevFrame + " to reduce frame switches");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find frame boundaries that could be optimized
|
||||
*/
|
||||
public static List<FrameBoundaryOptimization> findBoundaryOptimizations(DAGExecutionPlan plan) {
|
||||
List<FrameBoundaryOptimization> optimizations = new ArrayList<>();
|
||||
|
||||
for (String frameName : plan.getFrameMetadata().keySet()) {
|
||||
Set<String> boundaryOps = plan.getFrameBoundaryOperations().getOrDefault(frameName, Collections.emptySet());
|
||||
|
||||
for (String boundaryOp : boundaryOps) {
|
||||
FrameBoundaryOptimization opt = analyzeBoundaryOperation(plan, frameName, boundaryOp);
|
||||
if (opt != null) {
|
||||
optimizations.add(opt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return optimizations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate frame execution cost based on transitions and operations
|
||||
*/
|
||||
public static Map<String, Integer> calculateFrameExecutionCost(DAGExecutionPlan plan) {
|
||||
Map<String, Integer> costs = new HashMap<>();
|
||||
|
||||
for (String frameName : plan.getFrameMetadata().keySet()) {
|
||||
int cost = 0;
|
||||
|
||||
// Base cost from number of operations
|
||||
List<String> frameOps = plan.getOperationsInFrame(frameName);
|
||||
cost += frameOps.size();
|
||||
|
||||
// Additional cost from frame transitions
|
||||
FrameMetadata meta = plan.getFrameMetadata().get(frameName);
|
||||
for (Map.Entry<FrameTransition, Integer> entry : meta.transitionCounts.entrySet()) {
|
||||
cost += entry.getValue() * getTransitionCost(entry.getKey());
|
||||
}
|
||||
|
||||
// Cost from cross-frame references
|
||||
Set<String> inputs = plan.getFrameInputVariables().getOrDefault(frameName, Collections.emptySet());
|
||||
Set<String> outputs = plan.getFrameOutputVariables().getOrDefault(frameName, Collections.emptySet());
|
||||
cost += (inputs.size() + outputs.size()) * 2;
|
||||
|
||||
costs.put(frameName, cost);
|
||||
}
|
||||
|
||||
return costs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Suggest frame consolidation opportunities
|
||||
*/
|
||||
public static List<FrameConsolidationSuggestion> suggestFrameConsolidations(DAGExecutionPlan plan) {
|
||||
List<FrameConsolidationSuggestion> suggestions = new ArrayList<>();
|
||||
|
||||
// Find small frames that could be merged with their parent
|
||||
for (FrameMetadata meta : plan.getFrameMetadata().values()) {
|
||||
if (meta.totalOperations < 5 && meta.parentFrame != null) {
|
||||
String parentFrame = meta.parentFrame;
|
||||
FrameMetadata parentMeta = plan.getFrameMetadata().get(parentFrame);
|
||||
|
||||
if (parentMeta != null && !meta.hasLoops && !meta.hasConditionals) {
|
||||
FrameConsolidationSuggestion suggestion = new FrameConsolidationSuggestion();
|
||||
suggestion.sourceFrame = meta.frameName;
|
||||
suggestion.targetFrame = parentFrame;
|
||||
suggestion.reason = "Small frame (" + meta.totalOperations + " ops) without control flow";
|
||||
suggestion.estimatedSavings = calculateConsolidationSavings(plan, meta.frameName, parentFrame);
|
||||
suggestions.add(suggestion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze parallel execution opportunities within frames
|
||||
*/
|
||||
public static Map<String, List<ParallelizationOpportunity>> analyzeParallelizationOpportunities(DAGExecutionPlan plan) {
|
||||
Map<String, List<ParallelizationOpportunity>> opportunities = new HashMap<>();
|
||||
|
||||
for (String frameName : plan.getFrameMetadata().keySet()) {
|
||||
List<String> frameOps = plan.getOperationsInFrame(frameName);
|
||||
List<ParallelizationOpportunity> frameOpportunities = new ArrayList<>();
|
||||
|
||||
// Find operations that can be parallelized within the frame
|
||||
Map<String, Set<String>> opDependencies = buildOperationDependencies(plan, frameOps);
|
||||
List<Set<String>> parallelGroups = findParallelOperationGroups(opDependencies);
|
||||
|
||||
for (Set<String> group : parallelGroups) {
|
||||
if (group.size() > 1) {
|
||||
ParallelizationOpportunity opp = new ParallelizationOpportunity();
|
||||
opp.frameName = frameName;
|
||||
opp.parallelOperations = new ArrayList<>(group);
|
||||
opp.estimatedSpeedup = calculateEstimatedSpeedup(group.size());
|
||||
frameOpportunities.add(opp);
|
||||
}
|
||||
}
|
||||
|
||||
if (!frameOpportunities.isEmpty()) {
|
||||
opportunities.put(frameName, frameOpportunities);
|
||||
}
|
||||
}
|
||||
|
||||
return opportunities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify frames that could benefit from batching
|
||||
*/
|
||||
public static List<BatchingOpportunity> identifyBatchingOpportunities(DAGExecutionPlan plan) {
|
||||
List<BatchingOpportunity> opportunities = new ArrayList<>();
|
||||
|
||||
for (String frameName : plan.getFrameMetadata().keySet()) {
|
||||
FrameMetadata meta = plan.getFrameMetadata().get(frameName);
|
||||
|
||||
// Look for frames with similar operation patterns
|
||||
if (meta.hasLoops && meta.maxIterations > 1) {
|
||||
List<String> frameOps = plan.getOperationsInFrame(frameName);
|
||||
|
||||
// Analyze operation patterns for batching potential
|
||||
Map<String, Integer> opTypeCounts = countOperationTypes(plan, frameOps);
|
||||
|
||||
if (hasBatchingPotential(opTypeCounts)) {
|
||||
BatchingOpportunity opp = new BatchingOpportunity();
|
||||
opp.frameName = frameName;
|
||||
opp.iterationCount = meta.maxIterations;
|
||||
opp.operationTypes = opTypeCounts;
|
||||
opp.estimatedBenefit = calculateBatchingBenefit(meta.maxIterations, frameOps.size());
|
||||
opportunities.add(opp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return opportunities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate optimization report for the entire execution plan
|
||||
*/
|
||||
public static OptimizationReport generateOptimizationReport(DAGExecutionPlan plan) {
|
||||
OptimizationReport report = new OptimizationReport();
|
||||
|
||||
// Calculate current execution costs
|
||||
report.currentExecutionCosts = calculateFrameExecutionCost(plan);
|
||||
report.totalCurrentCost = report.currentExecutionCosts.values().stream().mapToInt(Integer::intValue).sum();
|
||||
|
||||
// Find optimization opportunities
|
||||
report.movableOperations = identifyMovableOperations(plan);
|
||||
report.boundaryOptimizations = findBoundaryOptimizations(plan);
|
||||
report.consolidationSuggestions = suggestFrameConsolidations(plan);
|
||||
report.parallelizationOpportunities = analyzeParallelizationOpportunities(plan);
|
||||
report.batchingOpportunities = identifyBatchingOpportunities(plan);
|
||||
|
||||
// Calculate potential savings
|
||||
report.estimatedSavings = calculateTotalSavings(report);
|
||||
report.optimizationPriority = prioritizeOptimizations(report);
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
private static boolean canMoveOperation(DAGExecutionPlan plan, String opName, String targetFrame) {
|
||||
OperationInfo opInfo = plan.getOperations().get(opName);
|
||||
if (opInfo == null) return false;
|
||||
|
||||
// Check if all input variables are available in target frame
|
||||
for (String input : opInfo.getInputs()) {
|
||||
String inputFrame = plan.getVariableFrame(input);
|
||||
if (inputFrame != null && !inputFrame.equals(targetFrame)) {
|
||||
// Check if input is available via cross-frame reference
|
||||
List<CrossFrameReference> refs = plan.getCrossFrameReferences().get(input);
|
||||
if (refs == null || refs.stream().noneMatch(ref -> ref.targetFrame.equals(targetFrame))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static FrameBoundaryOptimization analyzeBoundaryOperation(DAGExecutionPlan plan, String frameName, String opName) {
|
||||
OperationInfo opInfo = plan.getOperations().get(opName);
|
||||
if (opInfo == null || opInfo.getFrameInfo() == null) return null;
|
||||
|
||||
FrameTransition transition = opInfo.getFrameInfo().frameTransition;
|
||||
if (transition == FrameTransition.ENTER || transition == FrameTransition.EXIT) {
|
||||
FrameBoundaryOptimization opt = new FrameBoundaryOptimization();
|
||||
opt.frameName = frameName;
|
||||
opt.operationName = opName;
|
||||
opt.transitionType = transition;
|
||||
|
||||
// Check if this boundary operation is necessary
|
||||
if (transition == FrameTransition.ENTER) {
|
||||
// Check if variables being entered are actually used in frame
|
||||
Set<String> frameVars = plan.getVariablesInFrame(frameName);
|
||||
boolean allInputsUsed = opInfo.getInputs().stream().allMatch(frameVars::contains);
|
||||
if (!allInputsUsed) {
|
||||
opt.suggestion = "Some input variables may not be used in frame";
|
||||
opt.priority = OptimizationPriority.LOW;
|
||||
}
|
||||
}
|
||||
|
||||
return opt;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int getTransitionCost(FrameTransition transition) {
|
||||
switch (transition) {
|
||||
case ENTER: return 3;
|
||||
case EXIT: return 3;
|
||||
case SWITCH: return 5;
|
||||
case MERGE: return 4;
|
||||
case NEXT_ITERATION: return 6;
|
||||
case LOOP_CONDITION: return 4;
|
||||
default: return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static int calculateConsolidationSavings(DAGExecutionPlan plan, String sourceFrame, String targetFrame) {
|
||||
int savings = 0;
|
||||
|
||||
// Savings from eliminated frame transitions
|
||||
FrameMetadata sourceMeta = plan.getFrameMetadata().get(sourceFrame);
|
||||
if (sourceMeta != null) {
|
||||
for (Map.Entry<FrameTransition, Integer> entry : sourceMeta.transitionCounts.entrySet()) {
|
||||
savings += entry.getValue() * getTransitionCost(entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
// Savings from reduced cross-frame references
|
||||
Set<String> inputs = plan.getFrameInputVariables().getOrDefault(sourceFrame, Collections.emptySet());
|
||||
Set<String> outputs = plan.getFrameOutputVariables().getOrDefault(sourceFrame, Collections.emptySet());
|
||||
savings += (inputs.size() + outputs.size()) * 2;
|
||||
|
||||
return savings;
|
||||
}
|
||||
|
||||
private static Map<String, Set<String>> buildOperationDependencies(DAGExecutionPlan plan, List<String> frameOps) {
|
||||
Map<String, Set<String>> dependencies = new HashMap<>();
|
||||
|
||||
for (String op : frameOps) {
|
||||
dependencies.put(op, plan.getDependencies().getOrDefault(op, Collections.emptySet()));
|
||||
}
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
private static List<Set<String>> findParallelOperationGroups(Map<String, Set<String>> dependencies) {
|
||||
List<Set<String>> parallelGroups = new ArrayList<>();
|
||||
Set<String> processed = new HashSet<>();
|
||||
|
||||
for (String op : dependencies.keySet()) {
|
||||
if (!processed.contains(op)) {
|
||||
Set<String> group = new HashSet<>();
|
||||
findParallelGroup(op, dependencies, group, processed);
|
||||
if (group.size() > 1) {
|
||||
parallelGroups.add(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parallelGroups;
|
||||
}
|
||||
|
||||
private static void findParallelGroup(String op, Map<String, Set<String>> dependencies,
|
||||
Set<String> group, Set<String> processed) {
|
||||
if (processed.contains(op)) return;
|
||||
|
||||
processed.add(op);
|
||||
group.add(op);
|
||||
|
||||
// Find operations that don't depend on this one and this one doesn't depend on
|
||||
for (String otherOp : dependencies.keySet()) {
|
||||
if (!processed.contains(otherOp)) {
|
||||
Set<String> opDeps = dependencies.get(op);
|
||||
Set<String> otherDeps = dependencies.get(otherOp);
|
||||
|
||||
if (!opDeps.contains(otherOp) && !otherDeps.contains(op)) {
|
||||
findParallelGroup(otherOp, dependencies, group, processed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static double calculateEstimatedSpeedup(int parallelOperations) {
|
||||
// Simple model: speedup = min(parallelOperations, available_cores) * efficiency
|
||||
int availableCores = Runtime.getRuntime().availableProcessors();
|
||||
double efficiency = 0.8; // Assume 80% parallel efficiency
|
||||
return Math.min(parallelOperations, availableCores) * efficiency;
|
||||
}
|
||||
|
||||
private static Map<String, Integer> countOperationTypes(DAGExecutionPlan plan, List<String> operations) {
|
||||
Map<String, Integer> counts = new HashMap<>();
|
||||
|
||||
for (String opName : operations) {
|
||||
OperationInfo opInfo = plan.getOperations().get(opName);
|
||||
if (opInfo != null) {
|
||||
counts.merge(opInfo.getOpType(), 1, Integer::sum);
|
||||
}
|
||||
}
|
||||
|
||||
return counts;
|
||||
}
|
||||
|
||||
private static boolean hasBatchingPotential(Map<String, Integer> opTypeCounts) {
|
||||
// Heuristic: if there are many similar operations, batching might help
|
||||
return opTypeCounts.values().stream().anyMatch(count -> count > 3);
|
||||
}
|
||||
|
||||
private static double calculateBatchingBenefit(int iterations, int opsPerIteration) {
|
||||
// Simple model: benefit increases with iterations and operations
|
||||
return Math.log(iterations) * opsPerIteration * 0.1;
|
||||
}
|
||||
|
||||
private static int calculateTotalSavings(OptimizationReport report) {
|
||||
int savings = 0;
|
||||
|
||||
savings += report.consolidationSuggestions.stream()
|
||||
.mapToInt(s -> s.estimatedSavings).sum();
|
||||
|
||||
savings += report.batchingOpportunities.stream()
|
||||
.mapToInt(b -> (int) b.estimatedBenefit).sum();
|
||||
|
||||
return savings;
|
||||
}
|
||||
|
||||
private static List<String> prioritizeOptimizations(OptimizationReport report) {
|
||||
List<String> priorities = new ArrayList<>();
|
||||
|
||||
// High impact optimizations first
|
||||
if (!report.consolidationSuggestions.isEmpty()) {
|
||||
priorities.add("Frame consolidation: " + report.consolidationSuggestions.size() + " opportunities");
|
||||
}
|
||||
|
||||
if (!report.batchingOpportunities.isEmpty()) {
|
||||
priorities.add("Batching optimization: " + report.batchingOpportunities.size() + " opportunities");
|
||||
}
|
||||
|
||||
if (!report.parallelizationOpportunities.isEmpty()) {
|
||||
int totalParallelOps = report.parallelizationOpportunities.values().stream()
|
||||
.mapToInt(List::size).sum();
|
||||
priorities.add("Parallelization: " + totalParallelOps + " opportunities");
|
||||
}
|
||||
|
||||
return priorities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frame boundary optimization suggestion
|
||||
*/
|
||||
public static class FrameBoundaryOptimization {
|
||||
public String frameName;
|
||||
public String operationName;
|
||||
public FrameTransition transitionType;
|
||||
public String suggestion;
|
||||
public OptimizationPriority priority = OptimizationPriority.MEDIUM;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frame consolidation suggestion
|
||||
*/
|
||||
public static class FrameConsolidationSuggestion {
|
||||
public String sourceFrame;
|
||||
public String targetFrame;
|
||||
public String reason;
|
||||
public int estimatedSavings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parallelization opportunity within a frame
|
||||
*/
|
||||
public static class ParallelizationOpportunity {
|
||||
public String frameName;
|
||||
public List<String> parallelOperations = new ArrayList<>();
|
||||
public double estimatedSpeedup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Batching opportunity for repetitive operations
|
||||
*/
|
||||
public static class BatchingOpportunity {
|
||||
public String frameName;
|
||||
public int iterationCount;
|
||||
public Map<String, Integer> operationTypes = new HashMap<>();
|
||||
public double estimatedBenefit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comprehensive optimization report
|
||||
*/
|
||||
public static class OptimizationReport {
|
||||
public Map<String, Integer> currentExecutionCosts = new HashMap<>();
|
||||
public int totalCurrentCost;
|
||||
public Map<String, String> movableOperations = new HashMap<>();
|
||||
public List<FrameBoundaryOptimization> boundaryOptimizations = new ArrayList<>();
|
||||
public List<FrameConsolidationSuggestion> consolidationSuggestions = new ArrayList<>();
|
||||
public Map<String, List<ParallelizationOpportunity>> parallelizationOpportunities = new HashMap<>();
|
||||
public List<BatchingOpportunity> batchingOpportunities = new ArrayList<>();
|
||||
public int estimatedSavings;
|
||||
public List<String> optimizationPriority = new ArrayList<>();
|
||||
|
||||
public String generateSummary() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Optimization Report Summary\n");
|
||||
sb.append("==========================\n\n");
|
||||
|
||||
sb.append("Current Execution Cost: ").append(totalCurrentCost).append("\n");
|
||||
sb.append("Estimated Savings: ").append(estimatedSavings).append("\n");
|
||||
sb.append("Potential Improvement: ").append(String.format("%.1f%%",
|
||||
(double) estimatedSavings / totalCurrentCost * 100)).append("\n\n");
|
||||
|
||||
sb.append("Optimization Opportunities:\n");
|
||||
sb.append("- Movable Operations: ").append(movableOperations.size()).append("\n");
|
||||
sb.append("- Boundary Optimizations: ").append(boundaryOptimizations.size()).append("\n");
|
||||
sb.append("- Consolidation Suggestions: ").append(consolidationSuggestions.size()).append("\n");
|
||||
sb.append("- Parallelization Opportunities: ").append(parallelizationOpportunities.size()).append("\n");
|
||||
sb.append("- Batching Opportunities: ").append(batchingOpportunities.size()).append("\n\n");
|
||||
|
||||
if (!optimizationPriority.isEmpty()) {
|
||||
sb.append("Recommended Priority:\n");
|
||||
for (int i = 0; i < optimizationPriority.size(); i++) {
|
||||
sb.append((i + 1)).append(". ").append(optimizationPriority.get(i)).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimization priority levels
|
||||
*/
|
||||
public enum OptimizationPriority {
|
||||
LOW, MEDIUM, HIGH, CRITICAL
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package org.nd4j.autodiff.samediff;
|
||||
|
||||
/**
|
||||
* Information about frame context and transitions
|
||||
*/
|
||||
class FrameInfo {
|
||||
String inputFrame;
|
||||
int inputIteration;
|
||||
String inputParentFrame;
|
||||
String outputFrame;
|
||||
int outputIteration;
|
||||
String outputParentFrame;
|
||||
FrameTransition frameTransition = FrameTransition.NONE;
|
||||
String targetFrame; // For Enter operations
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Detailed frame metadata for execution analysis
|
||||
*/
|
||||
public class FrameMetadata {
|
||||
public String frameName;
|
||||
public String parentFrame;
|
||||
public Set<String> childFrames = new HashSet<>();
|
||||
public int depth;
|
||||
public FrameType frameType;
|
||||
public int totalOperations;
|
||||
public int totalVariables;
|
||||
public boolean hasLoops;
|
||||
public boolean hasConditionals;
|
||||
public int maxIterations;
|
||||
public List<String> entryPoints = new ArrayList<>();
|
||||
public List<String> exitPoints = new ArrayList<>();
|
||||
public Map<FrameTransition, Integer> transitionCounts = new HashMap<>();
|
||||
|
||||
public FrameMetadata(String frameName, String parentFrame, int depth, FrameType type) {
|
||||
this.frameName = frameName;
|
||||
this.parentFrame = parentFrame;
|
||||
this.depth = depth;
|
||||
this.frameType = type;
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package org.nd4j.autodiff.samediff;
|
||||
|
||||
/**
|
||||
* Types of frame transitions
|
||||
*/
|
||||
enum FrameTransition {
|
||||
NONE, // No frame change
|
||||
ENTER, // Enter a new frame (loop/conditional)
|
||||
EXIT, // Exit current frame
|
||||
NEXT_ITERATION, // Move to next iteration in current frame
|
||||
SWITCH, // Conditional branch
|
||||
MERGE, // Merge conditional branches
|
||||
LOOP_CONDITION // Loop condition check
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Information about frame transitions during execution
|
||||
*/
|
||||
public class FrameTransitionInfo {
|
||||
public String fromFrame;
|
||||
public String toFrame;
|
||||
public FrameTransition transitionType;
|
||||
public String operationName;
|
||||
public List<String> affectedVariables = new ArrayList<>();
|
||||
public int iterationChange;
|
||||
|
||||
public FrameTransitionInfo(String fromFrame, String toFrame, FrameTransition type, String operation) {
|
||||
this.fromFrame = fromFrame;
|
||||
this.toFrame = toFrame;
|
||||
this.transitionType = type;
|
||||
this.operationName = operation;
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
/**
|
||||
* Types of frames in execution analysis
|
||||
*/
|
||||
public enum FrameType {
|
||||
ROOT, // Top-level frame
|
||||
LOOP, // Loop frame with iterations
|
||||
CONDITIONAL, // Conditional/switch frame
|
||||
FUNCTION, // Function call frame
|
||||
NESTED // Generic nested frame
|
||||
}
|
||||
+571
@@ -0,0 +1,571 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Utility class for visualizing frame structures and generating textual representations
|
||||
*/
|
||||
public class FrameVisualizer {
|
||||
|
||||
/**
|
||||
* Generate a detailed textual representation of the frame hierarchy
|
||||
*/
|
||||
public static String generateFrameHierarchyDiagram(DAGExecutionPlan plan) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Frame Hierarchy Diagram\n");
|
||||
sb.append("======================\n\n");
|
||||
|
||||
Set<String> rootFrames = plan.getFramesAtDepth(0);
|
||||
if (rootFrames.isEmpty()) {
|
||||
sb.append("No frames detected\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
for (String rootFrame : rootFrames) {
|
||||
printFrameTree(plan, rootFrame, 0, sb, new HashSet<>());
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate execution flow diagram showing frame transitions
|
||||
*/
|
||||
public static String generateExecutionFlowDiagram(DAGExecutionPlan plan) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Execution Flow Diagram\n");
|
||||
sb.append("=====================\n\n");
|
||||
|
||||
List<String> executionOrder = plan.getExecutionOrder();
|
||||
String currentFrame = null;
|
||||
int frameCounter = 1;
|
||||
|
||||
for (String opName : executionOrder) {
|
||||
String opFrame = plan.getOperationFrame(opName);
|
||||
|
||||
if (opFrame != null && !opFrame.equals(currentFrame)) {
|
||||
if (currentFrame != null) {
|
||||
sb.append(" └─ End Frame: ").append(currentFrame).append("\n");
|
||||
sb.append(" ↓\n");
|
||||
}
|
||||
sb.append("[").append(frameCounter++).append("] Start Frame: ").append(opFrame).append("\n");
|
||||
currentFrame = opFrame;
|
||||
}
|
||||
|
||||
if (opFrame != null) {
|
||||
sb.append(" ├─ ").append(opName);
|
||||
OperationInfo opInfo = plan.getOperations().get(opName);
|
||||
if (opInfo != null && opInfo.getFrameInfo() != null &&
|
||||
opInfo.getFrameInfo().frameTransition != FrameTransition.NONE) {
|
||||
sb.append(" [").append(opInfo.getFrameInfo().frameTransition).append("]");
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (currentFrame != null) {
|
||||
sb.append(" └─ End Frame: ").append(currentFrame).append("\n");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate cross-frame dependency matrix
|
||||
*/
|
||||
public static String generateDependencyMatrix(DAGExecutionPlan plan) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Frame Dependency Matrix\n");
|
||||
sb.append("======================\n\n");
|
||||
|
||||
List<String> frames = new ArrayList<>(plan.getFrameMetadata().keySet());
|
||||
frames.sort(String::compareTo);
|
||||
|
||||
if (frames.isEmpty()) {
|
||||
sb.append("No frames to analyze\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// Header
|
||||
sb.append(" ");
|
||||
for (String frame : frames) {
|
||||
sb.append(String.format("%8s", frame.substring(0, Math.min(7, frame.length()))));
|
||||
}
|
||||
sb.append("\n");
|
||||
|
||||
// Matrix rows
|
||||
Map<String, Set<String>> dependencies = plan.getFrameDependencies();
|
||||
for (String fromFrame : frames) {
|
||||
sb.append(String.format("%-8s", fromFrame.substring(0, Math.min(7, fromFrame.length()))));
|
||||
Set<String> deps = dependencies.getOrDefault(fromFrame, Collections.emptySet());
|
||||
|
||||
for (String toFrame : frames) {
|
||||
if (fromFrame.equals(toFrame)) {
|
||||
sb.append(" - ");
|
||||
} else if (deps.contains(toFrame)) {
|
||||
sb.append(" X ");
|
||||
} else {
|
||||
sb.append(" . ");
|
||||
}
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
sb.append("\nLegend: X = depends on, . = no dependency, - = self\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate frame statistics summary
|
||||
*/
|
||||
public static String generateFrameStatistics(DAGExecutionPlan plan) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Frame Statistics Summary\n");
|
||||
sb.append("=======================\n\n");
|
||||
|
||||
Map<String, Object> stats = plan.getDetailedFrameStats();
|
||||
|
||||
sb.append("Overall Statistics:\n");
|
||||
sb.append(" Total Frames: ").append(stats.get("totalFrames")).append("\n");
|
||||
sb.append(" Max Depth: ").append(stats.get("maxFrameDepth")).append("\n");
|
||||
sb.append(" Frames with Loops: ").append(stats.get("framesWithLoops")).append("\n");
|
||||
sb.append(" Frames with Conditionals: ").append(stats.get("framesWithConditionals")).append("\n");
|
||||
sb.append(" Cross-frame References: ").append(stats.get("totalCrossFrameReferences")).append("\n\n");
|
||||
|
||||
// Frame type distribution
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<FrameType, Long> typeDistribution =
|
||||
(Map<FrameType, Long>) stats.get("frameTypeDistribution");
|
||||
|
||||
if (typeDistribution != null && !typeDistribution.isEmpty()) {
|
||||
sb.append("Frame Type Distribution:\n");
|
||||
for (Map.Entry<FrameType, Long> entry : typeDistribution.entrySet()) {
|
||||
sb.append(" ").append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
// Transition statistics
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<FrameTransition, Integer> transitions =
|
||||
(Map<FrameTransition, Integer>) stats.get("frameTransitions");
|
||||
|
||||
if (transitions != null && !transitions.isEmpty()) {
|
||||
sb.append("Frame Transitions:\n");
|
||||
for (Map.Entry<FrameTransition, Integer> entry : transitions.entrySet()) {
|
||||
sb.append(" ").append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
// Individual frame details
|
||||
sb.append("Individual Frame Details:\n");
|
||||
sb.append("------------------------\n");
|
||||
|
||||
List<FrameMetadata> sortedFrames = plan.getFrameMetadata().values().stream()
|
||||
.sorted(Comparator.comparing(m -> m.frameName))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (FrameMetadata meta : sortedFrames) {
|
||||
sb.append("Frame: ").append(meta.frameName).append("\n");
|
||||
sb.append(" Type: ").append(meta.frameType).append("\n");
|
||||
sb.append(" Depth: ").append(meta.depth).append("\n");
|
||||
sb.append(" Parent: ").append(meta.parentFrame != null ? meta.parentFrame : "None").append("\n");
|
||||
sb.append(" Operations: ").append(meta.totalOperations).append("\n");
|
||||
sb.append(" Variables: ").append(meta.totalVariables).append("\n");
|
||||
sb.append(" Max Iterations: ").append(meta.maxIterations).append("\n");
|
||||
sb.append(" Has Loops: ").append(meta.hasLoops).append("\n");
|
||||
sb.append(" Has Conditionals: ").append(meta.hasConditionals).append("\n");
|
||||
|
||||
if (!meta.entryPoints.isEmpty()) {
|
||||
sb.append(" Entry Points: ").append(meta.entryPoints).append("\n");
|
||||
}
|
||||
if (!meta.exitPoints.isEmpty()) {
|
||||
sb.append(" Exit Points: ").append(meta.exitPoints).append("\n");
|
||||
}
|
||||
if (!meta.childFrames.isEmpty()) {
|
||||
sb.append(" Child Frames: ").append(meta.childFrames).append("\n");
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a compact frame execution timeline
|
||||
*/
|
||||
public static String generateExecutionTimeline(DAGExecutionPlan plan) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Frame Execution Timeline\n");
|
||||
sb.append("=======================\n\n");
|
||||
|
||||
List<String> executionOrder = plan.getExecutionOrder();
|
||||
Map<String, Integer> frameStartPos = new HashMap<>();
|
||||
Map<String, Integer> frameEndPos = new HashMap<>();
|
||||
|
||||
// Find frame start and end positions
|
||||
for (int i = 0; i < executionOrder.size(); i++) {
|
||||
String opName = executionOrder.get(i);
|
||||
String frame = plan.getOperationFrame(opName);
|
||||
if (frame != null) {
|
||||
frameStartPos.putIfAbsent(frame, i);
|
||||
frameEndPos.put(frame, i);
|
||||
}
|
||||
}
|
||||
|
||||
// Create timeline visualization
|
||||
List<String> frames = frameStartPos.keySet().stream()
|
||||
.sorted(Comparator.comparing(frameStartPos::get))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
int timelineLength = executionOrder.size();
|
||||
char[] timeline = new char[timelineLength];
|
||||
Arrays.fill(timeline, '.');
|
||||
|
||||
for (String frame : frames) {
|
||||
int start = frameStartPos.get(frame);
|
||||
int end = frameEndPos.get(frame);
|
||||
char symbol = getFrameSymbol(frame);
|
||||
|
||||
for (int i = start; i <= end; i++) {
|
||||
timeline[i] = symbol;
|
||||
}
|
||||
}
|
||||
|
||||
// Print timeline with labels
|
||||
sb.append("Timeline (each character = 1 operation):\n");
|
||||
sb.append("Position: ");
|
||||
for (int i = 0; i < Math.min(timelineLength, 100); i += 10) {
|
||||
sb.append(String.format("%-10d", i));
|
||||
}
|
||||
sb.append("\n");
|
||||
|
||||
sb.append("Frames: ");
|
||||
for (int i = 0; i < Math.min(timelineLength, 100); i++) {
|
||||
sb.append(timeline[i]);
|
||||
}
|
||||
sb.append("\n\n");
|
||||
|
||||
// Frame legend
|
||||
sb.append("Frame Legend:\n");
|
||||
for (String frame : frames) {
|
||||
char symbol = getFrameSymbol(frame);
|
||||
int start = frameStartPos.get(frame);
|
||||
int end = frameEndPos.get(frame);
|
||||
sb.append(" ").append(symbol).append(" = ").append(frame)
|
||||
.append(" (ops ").append(start).append("-").append(end).append(")\n");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate cross-frame variable flow diagram
|
||||
*/
|
||||
public static String generateVariableFlowDiagram(DAGExecutionPlan plan) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Cross-Frame Variable Flow\n");
|
||||
sb.append("========================\n\n");
|
||||
|
||||
Map<String, List<CrossFrameReference>> crossRefs = plan.getCrossFrameReferences();
|
||||
|
||||
if (crossRefs.isEmpty()) {
|
||||
sb.append("No cross-frame variable references found\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// Group by source frame
|
||||
Map<String, List<CrossFrameReference>> bySourceFrame = new HashMap<>();
|
||||
for (List<CrossFrameReference> refs : crossRefs.values()) {
|
||||
for (CrossFrameReference ref : refs) {
|
||||
bySourceFrame.computeIfAbsent(ref.sourceFrame, k -> new ArrayList<>()).add(ref);
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<String, List<CrossFrameReference>> entry : bySourceFrame.entrySet()) {
|
||||
String sourceFrame = entry.getKey();
|
||||
List<CrossFrameReference> refs = entry.getValue();
|
||||
|
||||
sb.append("From Frame: ").append(sourceFrame).append("\n");
|
||||
|
||||
Map<String, List<CrossFrameReference>> byTarget = refs.stream()
|
||||
.collect(Collectors.groupingBy(ref -> ref.targetFrame));
|
||||
|
||||
for (Map.Entry<String, List<CrossFrameReference>> targetEntry : byTarget.entrySet()) {
|
||||
String targetFrame = targetEntry.getKey();
|
||||
List<CrossFrameReference> targetRefs = targetEntry.getValue();
|
||||
|
||||
sb.append(" └─> To Frame: ").append(targetFrame).append("\n");
|
||||
for (CrossFrameReference ref : targetRefs) {
|
||||
sb.append(" ├─ Variable: ").append(ref.variableName)
|
||||
.append(" (").append(ref.referenceType).append(")");
|
||||
if (ref.mediatingOperation != null) {
|
||||
sb.append(" via ").append(ref.mediatingOperation);
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate frame transition flow diagram
|
||||
*/
|
||||
public static String generateFrameTransitionDiagram(DAGExecutionPlan plan) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Frame Transition Flow\n");
|
||||
sb.append("====================\n\n");
|
||||
|
||||
Map<String, List<FrameTransitionInfo>> transitions = plan.getFrameTransitions();
|
||||
|
||||
if (transitions.isEmpty()) {
|
||||
sb.append("No frame transitions found\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
for (Map.Entry<String, List<FrameTransitionInfo>> entry : transitions.entrySet()) {
|
||||
String fromFrame = entry.getKey();
|
||||
List<FrameTransitionInfo> frameTransitions = entry.getValue();
|
||||
|
||||
sb.append("From Frame: ").append(fromFrame).append("\n");
|
||||
|
||||
for (FrameTransitionInfo transition : frameTransitions) {
|
||||
sb.append(" ├─ ").append(transition.transitionType)
|
||||
.append(" → ").append(transition.toFrame)
|
||||
.append(" (").append(transition.operationName).append(")");
|
||||
|
||||
if (transition.iterationChange != 0) {
|
||||
sb.append(" [iter: ").append(transition.iterationChange > 0 ? "+" : "")
|
||||
.append(transition.iterationChange).append("]");
|
||||
}
|
||||
|
||||
if (!transition.affectedVariables.isEmpty()) {
|
||||
sb.append(" vars: ").append(transition.affectedVariables);
|
||||
}
|
||||
|
||||
sb.append("\n");
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate frame execution performance analysis
|
||||
*/
|
||||
public static String generateFramePerformanceAnalysis(DAGExecutionPlan plan) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Frame Performance Analysis\n");
|
||||
sb.append("=========================\n\n");
|
||||
|
||||
Map<String, Integer> frameCosts = FrameExecutionOptimizer.calculateFrameExecutionCost(plan);
|
||||
int totalCost = frameCosts.values().stream().mapToInt(Integer::intValue).sum();
|
||||
|
||||
sb.append("Total Execution Cost: ").append(totalCost).append("\n\n");
|
||||
|
||||
// Sort frames by cost (descending)
|
||||
List<Map.Entry<String, Integer>> sortedCosts = frameCosts.entrySet().stream()
|
||||
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
sb.append("Frame Cost Analysis:\n");
|
||||
sb.append("Frame Name Cost % of Total Operations Variables\n");
|
||||
sb.append("------------------------------------------------------------------------\n");
|
||||
|
||||
for (Map.Entry<String, Integer> entry : sortedCosts) {
|
||||
String frameName = entry.getKey();
|
||||
int cost = entry.getValue();
|
||||
double percentage = (double) cost / totalCost * 100;
|
||||
|
||||
FrameMetadata meta = plan.getFrameMetadata().get(frameName);
|
||||
int ops = meta != null ? meta.totalOperations : 0;
|
||||
int vars = meta != null ? meta.totalVariables : 0;
|
||||
|
||||
sb.append(String.format("%-28s %6d %8.1f%% %12d %10d\n",
|
||||
frameName.substring(0, Math.min(28, frameName.length())),
|
||||
cost, percentage, ops, vars));
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate comprehensive frame analysis report
|
||||
*/
|
||||
public static String generateComprehensiveFrameReport(DAGExecutionPlan plan) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("═══════════════════════════════════════════════════════════════\n");
|
||||
sb.append(" COMPREHENSIVE FRAME ANALYSIS REPORT\n");
|
||||
sb.append("═══════════════════════════════════════════════════════════════\n\n");
|
||||
|
||||
// Executive summary
|
||||
sb.append("EXECUTIVE SUMMARY\n");
|
||||
sb.append("================\n");
|
||||
Map<String, Object> stats = plan.getDetailedFrameStats();
|
||||
sb.append("• Total Frames: ").append(stats.get("totalFrames")).append("\n");
|
||||
sb.append("• Maximum Nesting Depth: ").append(stats.get("maxFrameDepth")).append("\n");
|
||||
sb.append("• Frames with Control Flow: ").append(
|
||||
(Long) stats.get("framesWithLoops") + (Long) stats.get("framesWithConditionals")).append("\n");
|
||||
sb.append("• Cross-frame Data References: ").append(stats.get("totalCrossFrameReferences")).append("\n");
|
||||
|
||||
// Get optimization opportunities
|
||||
FrameExecutionOptimizer.OptimizationReport optReport = FrameExecutionOptimizer.generateOptimizationReport(plan);
|
||||
sb.append("• Optimization Opportunities: ").append(
|
||||
optReport.consolidationSuggestions.size() +
|
||||
optReport.parallelizationOpportunities.size() +
|
||||
optReport.batchingOpportunities.size()).append("\n");
|
||||
sb.append("• Potential Performance Improvement: ").append(
|
||||
String.format("%.1f%%", (double) optReport.estimatedSavings / optReport.totalCurrentCost * 100)).append("\n\n");
|
||||
|
||||
// Section divider
|
||||
sb.append("─────────────────────────────────────────────────────────────────\n\n");
|
||||
|
||||
// Frame hierarchy
|
||||
sb.append("1. FRAME HIERARCHY\n");
|
||||
sb.append("==================\n");
|
||||
sb.append(generateFrameHierarchyDiagram(plan)).append("\n");
|
||||
|
||||
// Frame statistics
|
||||
sb.append("2. DETAILED FRAME STATISTICS\n");
|
||||
sb.append("============================\n");
|
||||
sb.append(generateFrameStatistics(plan)).append("\n");
|
||||
|
||||
// Performance analysis
|
||||
sb.append("3. PERFORMANCE ANALYSIS\n");
|
||||
sb.append("=======================\n");
|
||||
sb.append(generateFramePerformanceAnalysis(plan)).append("\n");
|
||||
|
||||
// Variable flow analysis
|
||||
sb.append("4. CROSS-FRAME DATA FLOW\n");
|
||||
sb.append("========================\n");
|
||||
sb.append(generateVariableFlowDiagram(plan)).append("\n");
|
||||
|
||||
// Frame transitions
|
||||
sb.append("5. FRAME TRANSITIONS\n");
|
||||
sb.append("===================\n");
|
||||
sb.append(generateFrameTransitionDiagram(plan)).append("\n");
|
||||
|
||||
// Optimization recommendations
|
||||
sb.append("6. OPTIMIZATION RECOMMENDATIONS\n");
|
||||
sb.append("===============================\n");
|
||||
sb.append(optReport.generateSummary()).append("\n");
|
||||
|
||||
// Timeline visualization
|
||||
sb.append("7. EXECUTION TIMELINE\n");
|
||||
sb.append("=====================\n");
|
||||
sb.append(generateExecutionTimeline(plan)).append("\n");
|
||||
|
||||
sb.append("═══════════════════════════════════════════════════════════════\n");
|
||||
sb.append(" END OF REPORT\n");
|
||||
sb.append("═══════════════════════════════════════════════════════════════\n");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a simple ASCII art visualization of frame structure
|
||||
*/
|
||||
public static String generateFrameStructureAscii(DAGExecutionPlan plan) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Frame Structure (ASCII)\n");
|
||||
sb.append("======================\n\n");
|
||||
|
||||
Set<String> rootFrames = plan.getFramesAtDepth(0);
|
||||
for (String rootFrame : rootFrames) {
|
||||
generateFrameAscii(plan, rootFrame, sb, "", true);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void generateFrameAscii(DAGExecutionPlan plan, String frameName,
|
||||
StringBuilder sb, String prefix, boolean isLast) {
|
||||
FrameMetadata meta = plan.getFrameMetadata().get(frameName);
|
||||
if (meta == null) return;
|
||||
|
||||
// Draw current frame
|
||||
sb.append(prefix);
|
||||
sb.append(isLast ? "└── " : "├── ");
|
||||
sb.append(frameName);
|
||||
sb.append(" [").append(meta.frameType).append("]");
|
||||
sb.append(" (").append(meta.totalOperations).append(" ops, ");
|
||||
sb.append(meta.totalVariables).append(" vars)");
|
||||
if (meta.hasLoops) sb.append(" 🔄");
|
||||
if (meta.hasConditionals) sb.append(" 🔀");
|
||||
sb.append("\n");
|
||||
|
||||
// Draw children
|
||||
List<String> children = new ArrayList<>(meta.childFrames);
|
||||
children.sort(String::compareTo);
|
||||
|
||||
for (int i = 0; i < children.size(); i++) {
|
||||
boolean childIsLast = (i == children.size() - 1);
|
||||
String childPrefix = prefix + (isLast ? " " : "│ ");
|
||||
generateFrameAscii(plan, children.get(i), sb, childPrefix, childIsLast);
|
||||
}
|
||||
}
|
||||
|
||||
private static void printFrameTree(DAGExecutionPlan plan, String frameName, int depth,
|
||||
StringBuilder sb, Set<String> visited) {
|
||||
if (visited.contains(frameName)) {
|
||||
sb.append(" ".repeat(depth)).append("└─ ").append(frameName).append(" [CYCLE DETECTED]\n");
|
||||
return;
|
||||
}
|
||||
|
||||
visited.add(frameName);
|
||||
FrameMetadata meta = plan.getFrameMetadata().get(frameName);
|
||||
|
||||
if (meta != null) {
|
||||
String prefix = depth == 0 ? "┌─ " : "├─ ";
|
||||
sb.append(" ".repeat(depth)).append(prefix).append(frameName);
|
||||
sb.append(" (").append(meta.frameType).append(", ");
|
||||
sb.append("ops: ").append(meta.totalOperations).append(", ");
|
||||
sb.append("vars: ").append(meta.totalVariables).append(")");
|
||||
|
||||
if (meta.hasLoops) sb.append(" [LOOPS]");
|
||||
if (meta.hasConditionals) sb.append(" [CONDITIONALS]");
|
||||
sb.append("\n");
|
||||
|
||||
Set<String> children = plan.getChildFrames(frameName);
|
||||
for (String child : children) {
|
||||
printFrameTree(plan, child, depth + 1, sb, visited);
|
||||
}
|
||||
}
|
||||
|
||||
visited.remove(frameName);
|
||||
}
|
||||
|
||||
private static char getFrameSymbol(String frameName) {
|
||||
// Simple hash-based symbol assignment
|
||||
int hash = frameName.hashCode();
|
||||
char[] symbols = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
||||
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
|
||||
return symbols[Math.abs(hash) % symbols.length];
|
||||
}
|
||||
}
|
||||
+27
@@ -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.autodiff.samediff;
|
||||
|
||||
import org.nd4j.autodiff.samediff.internal.InferenceSession;
|
||||
|
||||
public interface InferenceFactory {
|
||||
|
||||
InferenceSession create(SameDiff sameDiff);
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package org.nd4j.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class IterationSnapshot {
|
||||
private int iteration;
|
||||
private long timestamp;
|
||||
private Map<String, Object> variableValues = new HashMap<>();
|
||||
private Map<String, String> variableShapes = new HashMap<>();
|
||||
private List<String> executedOperations = new ArrayList<>();
|
||||
private boolean conditionEvaluated;
|
||||
private Object conditionValue;
|
||||
private String conditionSource;
|
||||
private Map<String, Object> debugInfo = new HashMap<>();
|
||||
}
|
||||
+536
@@ -0,0 +1,536 @@
|
||||
package org.nd4j.autodiff.samediff;
|
||||
|
||||
import org.nd4j.autodiff.samediff.config.SDValue;
|
||||
import org.nd4j.autodiff.samediff.config.SDValueType;
|
||||
import org.nd4j.autodiff.samediff.internal.VarId;
|
||||
import org.nd4j.linalg.api.ndarray.INDArray;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Helper methods for loop analysis in the LoopTerminationAnalyzer
|
||||
*/
|
||||
public class LoopAnalysisHelpers {
|
||||
|
||||
/**
|
||||
* Analyze the cause of early termination
|
||||
*
|
||||
* @param loopInfo The loop information
|
||||
* @param iteration The iteration at which termination occurred
|
||||
* @param terminationType The type of termination that occurred
|
||||
* @return Detailed analysis of the early termination cause
|
||||
*/
|
||||
public static String analyzeEarlyTerminationCause(LoopInfo loopInfo, int iteration,
|
||||
TerminationType terminationType,
|
||||
Map<String, LoopIterationTrace> iterationTraces) {
|
||||
StringBuilder cause = new StringBuilder();
|
||||
|
||||
switch (terminationType) {
|
||||
case CONDITION_FALSE:
|
||||
cause.append("Loop condition became false earlier than expected");
|
||||
|
||||
// Analyze condition evolution
|
||||
LoopIterationTrace trace = iterationTraces.get(loopInfo.getFrameName());
|
||||
if (trace != null && !trace.getConditionEvaluations().isEmpty()) {
|
||||
List<ConditionEvaluation> recent = trace.getConditionEvaluations().stream()
|
||||
.filter(eval -> eval.getIteration() >= iteration - 3 && eval.getIteration() <= iteration)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (recent.size() >= 2) {
|
||||
cause.append(" - Recent condition values: ");
|
||||
for (ConditionEvaluation eval : recent) {
|
||||
cause.append("[").append(eval.getIteration()).append(": ").append(formatValue(eval.getConditionValue())).append("] ");
|
||||
}
|
||||
|
||||
// Analyze sudden changes
|
||||
if (recent.size() >= 2) {
|
||||
Object lastValue = recent.get(recent.size() - 1).getConditionValue();
|
||||
Object secondLastValue = recent.get(recent.size() - 2).getConditionValue();
|
||||
|
||||
if (valueChangedSuddenly(secondLastValue, lastValue)) {
|
||||
cause.append("\n SUDDEN CHANGE DETECTED: ");
|
||||
cause.append("Previous: ").append(formatValue(secondLastValue));
|
||||
cause.append(" → Current: ").append(formatValue(lastValue));
|
||||
|
||||
// Calculate change magnitude
|
||||
Double prevNum = extractNumericValue(secondLastValue);
|
||||
Double currNum = extractNumericValue(lastValue);
|
||||
if (prevNum != null && currNum != null && prevNum != 0) {
|
||||
double changePercent = Math.abs((currNum - prevNum) / prevNum) * 100;
|
||||
cause.append(" (").append(String.format("%.1f%%", changePercent)).append(" change)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for convergence patterns
|
||||
if (recent.size() >= 3) {
|
||||
List<Double> numericValues = recent.stream()
|
||||
.map(eval -> extractNumericValue(eval.getConditionValue()))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (numericValues.size() >= 3) {
|
||||
double convergenceRate = calculateConvergenceRate(numericValues);
|
||||
if (convergenceRate > 0.1) {
|
||||
cause.append("\n RAPID CONVERGENCE: Rate = ").append(String.format("%.4f", convergenceRate));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for variable-based causes
|
||||
analyzeVariableBasedCauses(cause, loopInfo, iteration, trace);
|
||||
break;
|
||||
|
||||
case CONDITION_TRUE_EXIT:
|
||||
cause.append("Exit condition met sooner than expected");
|
||||
|
||||
// Analyze what caused the exit condition to be true
|
||||
analyzeExitConditionCauses(cause, loopInfo, iteration, iterationTraces);
|
||||
break;
|
||||
|
||||
case SWITCH_TERMINATION:
|
||||
cause.append("Switch operation took unexpected branch leading to early exit");
|
||||
|
||||
// Analyze switch decision patterns
|
||||
analyzeSwitchTerminationCauses(cause, loopInfo, iteration, iterationTraces);
|
||||
break;
|
||||
|
||||
case ERROR_TERMINATION:
|
||||
cause.append("Error occurred before normal termination condition");
|
||||
|
||||
// Analyze error patterns
|
||||
analyzeErrorTerminationCauses(cause, loopInfo, iteration, iterationTraces);
|
||||
break;
|
||||
|
||||
case TIMEOUT_TERMINATION:
|
||||
cause.append("Loop exceeded maximum allowed iterations");
|
||||
|
||||
// Analyze timeout patterns
|
||||
analyzeTimeoutCauses(cause, loopInfo, iteration);
|
||||
break;
|
||||
|
||||
case EARLY_BREAK:
|
||||
cause.append("Loop was terminated by an early break condition");
|
||||
|
||||
// Analyze break patterns
|
||||
analyzeEarlyBreakCauses(cause, loopInfo, iteration, iterationTraces);
|
||||
break;
|
||||
|
||||
case RESOURCE_EXHAUSTION:
|
||||
cause.append("Loop terminated due to resource exhaustion");
|
||||
|
||||
// Analyze resource usage patterns
|
||||
analyzeResourceExhaustionCauses(cause, loopInfo, iteration);
|
||||
break;
|
||||
|
||||
case MANUAL_TERMINATION:
|
||||
cause.append("Loop was manually terminated");
|
||||
break;
|
||||
|
||||
default:
|
||||
cause.append("Unexpected termination type: ").append(terminationType);
|
||||
cause.append(" - This may indicate a new termination pattern");
|
||||
}
|
||||
|
||||
// Add general early termination indicators
|
||||
addGeneralEarlyTerminationIndicators(cause, loopInfo, iteration, iterationTraces);
|
||||
|
||||
return cause.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Map termination type to loop status
|
||||
*
|
||||
* @param terminationType The type of termination
|
||||
* @return The corresponding loop status
|
||||
*/
|
||||
public static LoopTerminationStatus mapTerminationTypeToStatus(
|
||||
TerminationType terminationType) {
|
||||
switch (terminationType) {
|
||||
case CONDITION_FALSE:
|
||||
case CONDITION_TRUE_EXIT:
|
||||
return LoopTerminationStatus.TERMINATED_NORMAL;
|
||||
|
||||
case SWITCH_TERMINATION:
|
||||
case EARLY_BREAK:
|
||||
case RESOURCE_EXHAUSTION:
|
||||
return LoopTerminationStatus.TERMINATED_EARLY;
|
||||
|
||||
case ERROR_TERMINATION:
|
||||
return LoopTerminationStatus.TERMINATED_ERROR;
|
||||
|
||||
case TIMEOUT_TERMINATION:
|
||||
return LoopTerminationStatus.TERMINATED_TIMEOUT;
|
||||
|
||||
case MANUAL_TERMINATION:
|
||||
return LoopTerminationStatus.TERMINATED_EARLY;
|
||||
|
||||
default:
|
||||
return LoopTerminationStatus.TERMINATED_EARLY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture current loop state for debugging
|
||||
*
|
||||
* @param frameName The name of the loop frame
|
||||
* @param iteration The current iteration
|
||||
* @param nodeValueOutputs The current node value outputs
|
||||
* @param sameDiff The SameDiff instance for additional context
|
||||
* @return A comprehensive snapshot of the loop state
|
||||
*/
|
||||
public static LoopState captureLoopState(String frameName, int iteration,
|
||||
Map<VarId, SDValue> nodeValueOutputs,
|
||||
org.nd4j.autodiff.samediff.SameDiff sameDiff) {
|
||||
LoopState state = new LoopState();
|
||||
state.setIteration(iteration);
|
||||
|
||||
// Capture variable states in the current frame
|
||||
Map<String, Object> variableStates = new HashMap<>();
|
||||
Map<String, String> operationStates = new HashMap<>();
|
||||
List<String> activeOperations = new ArrayList<>();
|
||||
Map<String, Object> frameContext = new HashMap<>();
|
||||
|
||||
// Collect variables from the current frame and iteration
|
||||
for (Map.Entry<VarId, SDValue> entry : nodeValueOutputs.entrySet()) {
|
||||
VarId varId = entry.getKey();
|
||||
|
||||
// Check if this variable belongs to our frame
|
||||
if (frameName.equals(varId.getFrame()) && varId.getIteration() == iteration) {
|
||||
String varName = varId.getVariable();
|
||||
SDValue value = entry.getValue();
|
||||
|
||||
if (value != null) {
|
||||
Object extractedValue = extractValueFromSDValue(value);
|
||||
variableStates.put(varName, extractedValue);
|
||||
|
||||
// Add variable metadata
|
||||
frameContext.put(varName + "_type", value.getSdValueType().toString());
|
||||
if (value.getSdValueType() == SDValueType.TENSOR && value.getTensorValue() != null) {
|
||||
INDArray tensor = value.getTensorValue();
|
||||
frameContext.put(varName + "_shape", Arrays.toString(tensor.shape()));
|
||||
frameContext.put(varName + "_dataType", tensor.dataType().toString());
|
||||
frameContext.put(varName + "_length", tensor.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Capture operation states
|
||||
for (Map.Entry<String, org.nd4j.autodiff.samediff.internal.SameDiffOp> opEntry : sameDiff.getOps().entrySet()) {
|
||||
String opName = opEntry.getKey();
|
||||
org.nd4j.autodiff.samediff.internal.SameDiffOp op = opEntry.getValue();
|
||||
|
||||
// Check if this operation is relevant to our frame
|
||||
if (isOperationRelevantToFrame(op, frameName)) {
|
||||
String opType = op.getOp().getClass().getSimpleName();
|
||||
operationStates.put(opName, opType);
|
||||
|
||||
// Check if operation is currently active/executed
|
||||
if (isOperationActive(opName, frameName, iteration)) {
|
||||
activeOperations.add(opName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add frame-specific context
|
||||
frameContext.put("frameName", frameName);
|
||||
frameContext.put("iteration", iteration);
|
||||
frameContext.put("timestamp", System.currentTimeMillis());
|
||||
frameContext.put("variableCount", variableStates.size());
|
||||
frameContext.put("operationCount", operationStates.size());
|
||||
frameContext.put("activeOperationCount", activeOperations.size());
|
||||
|
||||
// Add memory usage information if available
|
||||
try {
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
long totalMemory = runtime.totalMemory();
|
||||
long freeMemory = runtime.freeMemory();
|
||||
long usedMemory = totalMemory - freeMemory;
|
||||
|
||||
frameContext.put("memoryUsed", usedMemory);
|
||||
frameContext.put("memoryTotal", totalMemory);
|
||||
frameContext.put("memoryFree", freeMemory);
|
||||
} catch (Exception e) {
|
||||
frameContext.put("memoryError", e.getMessage());
|
||||
}
|
||||
|
||||
// Set all collected data
|
||||
state.setVariableStates(variableStates);
|
||||
state.setOperationStates(operationStates);
|
||||
state.setActiveOperations(activeOperations);
|
||||
state.setFrameContext(frameContext);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
// Helper methods for analyzing specific termination causes
|
||||
|
||||
private static void analyzeVariableBasedCauses(StringBuilder cause, LoopInfo loopInfo, int iteration,
|
||||
LoopIterationTrace trace) {
|
||||
if (trace == null || loopInfo.getLoopVariables().isEmpty()) return;
|
||||
|
||||
cause.append("\n VARIABLE ANALYSIS:");
|
||||
for (String varName : loopInfo.getLoopVariables()) {
|
||||
List<Object> evolution = trace.getVariableEvolution().get(varName);
|
||||
if (evolution != null && evolution.size() >= 2) {
|
||||
Object lastValue = evolution.get(evolution.size() - 1);
|
||||
Object secondLastValue = evolution.get(evolution.size() - 2);
|
||||
|
||||
if (isNumericallyUnstable(lastValue)) {
|
||||
cause.append("\n Variable '").append(varName).append("' became unstable: ").append(formatValue(lastValue));
|
||||
} else if (valueChangedSuddenly(secondLastValue, lastValue)) {
|
||||
cause.append("\n Variable '").append(varName).append("' changed suddenly: ");
|
||||
cause.append(formatValue(secondLastValue)).append(" → ").append(formatValue(lastValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void analyzeExitConditionCauses(StringBuilder cause, LoopInfo loopInfo, int iteration,
|
||||
Map<String, LoopIterationTrace> iterationTraces) {
|
||||
cause.append("\n EXIT ANALYSIS:");
|
||||
|
||||
// Check what led to the exit condition being true
|
||||
if (!loopInfo.getExitOperations().isEmpty()) {
|
||||
cause.append("\n Exit operations: ").append(loopInfo.getExitOperations());
|
||||
}
|
||||
|
||||
// Look for patterns in recent iterations
|
||||
LoopIterationTrace trace = iterationTraces.get(loopInfo.getFrameName());
|
||||
if (trace != null && !trace.getIterations().isEmpty()) {
|
||||
List<IterationSnapshot> recentSnapshots = trace.getIterations().stream()
|
||||
.filter(snap -> snap.getIteration() >= iteration - 2 && snap.getIteration() <= iteration)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!recentSnapshots.isEmpty()) {
|
||||
cause.append("\n Recent iterations showed: ");
|
||||
for (IterationSnapshot snap : recentSnapshots) {
|
||||
cause.append("iter").append(snap.getIteration()).append("(").append(snap.getVariableValues().size()).append(" vars) ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void analyzeSwitchTerminationCauses(StringBuilder cause, LoopInfo loopInfo, int iteration,
|
||||
Map<String, LoopIterationTrace> iterationTraces) {
|
||||
cause.append("\n SWITCH ANALYSIS:");
|
||||
|
||||
if (!loopInfo.getSwitchOperations().isEmpty()) {
|
||||
cause.append("\n Switch operations in loop: ").append(loopInfo.getSwitchOperations());
|
||||
}
|
||||
|
||||
// Analyze switch decision patterns
|
||||
LoopIterationTrace trace = iterationTraces.get(loopInfo.getFrameName());
|
||||
if (trace != null) {
|
||||
// Look for switch-related patterns in recent iterations
|
||||
cause.append("\n Switch operations may have taken unexpected branches");
|
||||
cause.append("\n This could indicate: predicate values changed unexpectedly,");
|
||||
cause.append("\n or control flow logic differs from expected patterns");
|
||||
}
|
||||
}
|
||||
|
||||
private static void analyzeErrorTerminationCauses(StringBuilder cause, LoopInfo loopInfo, int iteration,
|
||||
Map<String, LoopIterationTrace> iterationTraces) {
|
||||
cause.append("\n ERROR ANALYSIS:");
|
||||
cause.append("\n Error occurred at iteration ").append(iteration);
|
||||
cause.append("\n This suggests: numerical instability, invalid operations,");
|
||||
cause.append("\n or resource constraints during loop execution");
|
||||
|
||||
// Check for numerical instability patterns
|
||||
LoopIterationTrace trace = iterationTraces.get(loopInfo.getFrameName());
|
||||
if (trace != null) {
|
||||
for (String varName : loopInfo.getLoopVariables()) {
|
||||
List<Object> evolution = trace.getVariableEvolution().get(varName);
|
||||
if (evolution != null && !evolution.isEmpty()) {
|
||||
Object lastValue = evolution.get(evolution.size() - 1);
|
||||
if (isNumericallyUnstable(lastValue)) {
|
||||
cause.append("\n Variable '").append(varName).append("' shows instability: ").append(formatValue(lastValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void analyzeTimeoutCauses(StringBuilder cause, LoopInfo loopInfo, int iteration) {
|
||||
cause.append("\n TIMEOUT ANALYSIS:");
|
||||
cause.append("\n Iteration ").append(iteration).append(" exceeded maximum allowed");
|
||||
cause.append("\n This suggests: infinite loop, very slow convergence,");
|
||||
cause.append("\n or incorrect termination conditions");
|
||||
|
||||
if (loopInfo.getExpectedIterations() > 0) {
|
||||
cause.append("\n Expected iterations: ").append(loopInfo.getExpectedIterations());
|
||||
cause.append(" | Actual iterations: ").append(iteration);
|
||||
}
|
||||
}
|
||||
|
||||
private static void analyzeEarlyBreakCauses(StringBuilder cause, LoopInfo loopInfo, int iteration,
|
||||
Map<String, LoopIterationTrace> iterationTraces) {
|
||||
cause.append("\n EARLY BREAK ANALYSIS:");
|
||||
cause.append("\n Loop was terminated by an early break condition");
|
||||
cause.append("\n This could indicate: optimization stopping criteria met,");
|
||||
cause.append("\n convergence threshold reached, or manual intervention");
|
||||
}
|
||||
|
||||
private static void analyzeResourceExhaustionCauses(StringBuilder cause, LoopInfo loopInfo, int iteration) {
|
||||
cause.append("\n RESOURCE EXHAUSTION ANALYSIS:");
|
||||
cause.append("\n Loop consumed too many resources");
|
||||
cause.append("\n This suggests: memory leak, excessive computation,");
|
||||
cause.append("\n or inefficient algorithms within the loop");
|
||||
|
||||
// Add memory information if available
|
||||
try {
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
long totalMemory = runtime.totalMemory();
|
||||
long freeMemory = runtime.freeMemory();
|
||||
long usedMemory = totalMemory - freeMemory;
|
||||
|
||||
cause.append("\n Current memory usage: ").append(usedMemory / 1024 / 1024).append(" MB");
|
||||
cause.append(" of ").append(totalMemory / 1024 / 1024).append(" MB");
|
||||
} catch (Exception e) {
|
||||
cause.append("\n Memory information unavailable");
|
||||
}
|
||||
}
|
||||
|
||||
private static void addGeneralEarlyTerminationIndicators(StringBuilder cause, LoopInfo loopInfo, int iteration,
|
||||
Map<String, LoopIterationTrace> iterationTraces) {
|
||||
cause.append("\n\n GENERAL INDICATORS:");
|
||||
|
||||
// Check if this was much earlier than expected
|
||||
if (loopInfo.getExpectedIterations() > 0 && iteration < loopInfo.getExpectedIterations() * 0.5) {
|
||||
cause.append("\n SIGNIFICANTLY EARLY: Terminated at ").append(iteration);
|
||||
cause.append(" iterations (expected ~").append(loopInfo.getExpectedIterations()).append(")");
|
||||
}
|
||||
|
||||
// Check for prediction accuracy
|
||||
if (!loopInfo.getTerminationPredictions().isEmpty()) {
|
||||
TerminationPrediction bestPrediction = loopInfo.getTerminationPredictions()
|
||||
.stream()
|
||||
.max(Comparator.comparingDouble(TerminationPrediction::getConfidence))
|
||||
.orElse(null);
|
||||
|
||||
if (bestPrediction != null) {
|
||||
int predictedIteration = bestPrediction.getPredictedTerminationIteration();
|
||||
int actualIteration = iteration;
|
||||
int difference = Math.abs(predictedIteration - actualIteration);
|
||||
|
||||
cause.append("\n PREDICTION ACCURACY: Predicted ").append(predictedIteration);
|
||||
cause.append(", Actual ").append(actualIteration);
|
||||
cause.append(" (difference: ").append(difference).append(" iterations)");
|
||||
|
||||
if (difference > 5) {
|
||||
cause.append("\n PREDICTION MISS: Large difference suggests unexpected behavior");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check execution time
|
||||
long executionTime = System.currentTimeMillis() - loopInfo.getStartTime();
|
||||
if (executionTime < 100) { // Less than 100ms
|
||||
cause.append("\n RAPID TERMINATION: Loop completed in ").append(executionTime).append("ms");
|
||||
}
|
||||
}
|
||||
|
||||
// Utility methods
|
||||
|
||||
private static Object extractValueFromSDValue(SDValue value) {
|
||||
if (value == null) return null;
|
||||
|
||||
switch (value.getSdValueType()) {
|
||||
case TENSOR:
|
||||
return value.getTensorValue();
|
||||
case LIST:
|
||||
return value.getListValue();
|
||||
default:
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isOperationRelevantToFrame(org.nd4j.autodiff.samediff.internal.SameDiffOp op, String frameName) {
|
||||
// This is a simplified check - in practice, you'd need to track frame associations
|
||||
// For now, we'll consider all operations potentially relevant
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isOperationActive(String opName, String frameName, int iteration) {
|
||||
// This would need integration with the actual execution state
|
||||
// For now, we'll return false as a placeholder
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String formatValue(Object value) {
|
||||
if (value == null) return "null";
|
||||
|
||||
if (value instanceof INDArray) {
|
||||
INDArray arr = (INDArray) value;
|
||||
if (arr.isScalar()) {
|
||||
return String.format("%.6f", arr.getDouble(0));
|
||||
} else if (arr.length() <= 5) {
|
||||
return arr.toString();
|
||||
} else {
|
||||
return String.format("Array[%s] (length: %d)", Arrays.toString(arr.shape()), arr.length());
|
||||
}
|
||||
} else if (value instanceof Number) {
|
||||
return String.format("%.6f", ((Number) value).doubleValue());
|
||||
} else if (value instanceof Boolean) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
private static boolean valueChangedSuddenly(Object oldValue, Object newValue) {
|
||||
Double oldNum = extractNumericValue(oldValue);
|
||||
Double newNum = extractNumericValue(newValue);
|
||||
|
||||
if (oldNum != null && newNum != null && Math.abs(oldNum) > 1e-10) {
|
||||
double changeRatio = Math.abs((newNum - oldNum) / oldNum);
|
||||
return changeRatio > 0.5; // Changed by more than 50%
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Double extractNumericValue(Object value) {
|
||||
if (value == null) return null;
|
||||
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).doubleValue();
|
||||
} else if (value instanceof INDArray) {
|
||||
INDArray arr = (INDArray) value;
|
||||
if (arr.isScalar()) {
|
||||
return arr.getDouble(0);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isNumericallyUnstable(Object value) {
|
||||
if (value instanceof Number) {
|
||||
double d = ((Number) value).doubleValue();
|
||||
return Double.isNaN(d) || Double.isInfinite(d) || Math.abs(d) > 1e10;
|
||||
} else if (value instanceof INDArray) {
|
||||
INDArray arr = (INDArray) value;
|
||||
if (arr.isScalar()) {
|
||||
double d = arr.getDouble(0);
|
||||
return Double.isNaN(d) || Double.isInfinite(d) || Math.abs(d) > 1e10;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static double calculateConvergenceRate(List<Double> values) {
|
||||
if (values.size() < 2) return 0.0;
|
||||
|
||||
double totalChange = 0.0;
|
||||
for (int i = 1; i < values.size(); i++) {
|
||||
totalChange += Math.abs(values.get(i) - values.get(i-1));
|
||||
}
|
||||
|
||||
return totalChange / (values.size() - 1);
|
||||
}
|
||||
}
|
||||
+715
@@ -0,0 +1,715 @@
|
||||
package org.nd4j.autodiff.samediff;/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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 lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.nd4j.autodiff.samediff.internal.SameDiffOp;
|
||||
import org.nd4j.autodiff.functions.DifferentialFunction;
|
||||
import org.nd4j.linalg.api.ops.impl.controlflow.compat.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* LoopInfo holds comprehensive information about a loop during execution.
|
||||
* This class tracks all aspects of loop behavior including operations, variables,
|
||||
* execution state, termination predictions, and performance metrics.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class LoopInfo {
|
||||
|
||||
// === BASIC LOOP IDENTIFICATION ===
|
||||
|
||||
/**
|
||||
* The name of the loop frame (e.g., "while_loop_1", "for_loop_2")
|
||||
*/
|
||||
private String frameName;
|
||||
|
||||
/**
|
||||
* Unique identifier for this loop instance
|
||||
*/
|
||||
private String loopId;
|
||||
|
||||
/**
|
||||
* The parent frame name, if this is a nested loop
|
||||
*/
|
||||
private String parentFrameName;
|
||||
|
||||
/**
|
||||
* The depth of loop nesting (0 for outermost loop)
|
||||
*/
|
||||
private int nestingDepth = 0;
|
||||
|
||||
// === LOOP OPERATIONS ===
|
||||
|
||||
/**
|
||||
* The main loop condition operation (typically LoopCond)
|
||||
*/
|
||||
private String loopCondOperation;
|
||||
|
||||
/**
|
||||
* All exit operations that can terminate this loop
|
||||
*/
|
||||
private List<String> exitOperations = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* All switch operations within this loop
|
||||
*/
|
||||
private List<String> switchOperations = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* All NextIteration operations that advance the loop
|
||||
*/
|
||||
private List<String> nextIterationOperations = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* All Enter operations that feed into this loop
|
||||
*/
|
||||
private List<String> enterOperations = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* All Merge operations within this loop
|
||||
*/
|
||||
private List<String> mergeOperations = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* All other operations contained within this loop frame
|
||||
*/
|
||||
private Set<String> loopOperations = new HashSet<>();
|
||||
|
||||
// === LOOP VARIABLES ===
|
||||
|
||||
/**
|
||||
* Variables that are modified within the loop (loop variables)
|
||||
*/
|
||||
private List<String> loopVariables = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Variables that are constants within the loop
|
||||
*/
|
||||
private List<String> loopConstants = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Variables that are passed into the loop from outside
|
||||
*/
|
||||
private List<String> inputVariables = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Variables that are produced by the loop
|
||||
*/
|
||||
private List<String> outputVariables = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Variables that serve as loop invariants
|
||||
*/
|
||||
private List<String> invariantVariables = new ArrayList<>();
|
||||
|
||||
// === EXECUTION STATE ===
|
||||
|
||||
/**
|
||||
* Current iteration number
|
||||
*/
|
||||
private int currentIteration = 0;
|
||||
|
||||
/**
|
||||
* Maximum number of iterations observed during execution
|
||||
*/
|
||||
private int maxIterationsObserved = 0;
|
||||
|
||||
/**
|
||||
* Minimum number of iterations observed (for loops that reset)
|
||||
*/
|
||||
private int minIterationsObserved = 0;
|
||||
|
||||
/**
|
||||
* Total number of times this loop has been executed
|
||||
*/
|
||||
private int executionCount = 0;
|
||||
|
||||
/**
|
||||
* Time when loop execution started
|
||||
*/
|
||||
private long startTime;
|
||||
|
||||
/**
|
||||
* Time when loop execution ended
|
||||
*/
|
||||
private long endTime;
|
||||
|
||||
/**
|
||||
* Current status of the loop
|
||||
*/
|
||||
private LoopTerminationStatus status = LoopTerminationStatus.ACTIVE;
|
||||
|
||||
/**
|
||||
* Reason for loop termination
|
||||
*/
|
||||
private String terminationReason;
|
||||
|
||||
// === TERMINATION PREDICTIONS ===
|
||||
|
||||
/**
|
||||
* Predictions about when this loop will terminate
|
||||
*/
|
||||
private List<TerminationPrediction> terminationPredictions = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Whether early termination has been detected
|
||||
*/
|
||||
private boolean earlyTerminationDetected = false;
|
||||
|
||||
/**
|
||||
* Expected number of iterations (if known)
|
||||
*/
|
||||
private int expectedIterations = -1; // -1 means unknown
|
||||
|
||||
/**
|
||||
* Confidence in the expected iteration count
|
||||
*/
|
||||
private double expectedIterationsConfidence = 0.0;
|
||||
|
||||
// === PERFORMANCE METRICS ===
|
||||
|
||||
/**
|
||||
* Total execution time in milliseconds
|
||||
*/
|
||||
private long totalExecutionTime = 0;
|
||||
|
||||
/**
|
||||
* Average time per iteration in milliseconds
|
||||
*/
|
||||
private double averageIterationTime = 0.0;
|
||||
|
||||
/**
|
||||
* Peak memory usage during loop execution
|
||||
*/
|
||||
private long peakMemoryUsage = 0;
|
||||
|
||||
/**
|
||||
* Average memory usage during loop execution
|
||||
*/
|
||||
private long averageMemoryUsage = 0;
|
||||
|
||||
/**
|
||||
* Number of operations executed per iteration
|
||||
*/
|
||||
private Map<Integer, Integer> operationsPerIteration = new HashMap<>();
|
||||
|
||||
// === ANALYSIS DATA ===
|
||||
|
||||
/**
|
||||
* General metadata about the loop
|
||||
*/
|
||||
private Map<String, Object> metadata = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Statistical information about loop behavior
|
||||
*/
|
||||
private Map<String, Double> statistics = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Flags indicating various loop characteristics
|
||||
*/
|
||||
private Map<String, Boolean> flags = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Custom properties that can be set during analysis
|
||||
*/
|
||||
private Map<String, Object> customProperties = new HashMap<>();
|
||||
|
||||
// === NESTED ENUMS ===
|
||||
|
||||
// === CONSTRUCTORS ===
|
||||
|
||||
/**
|
||||
* Create a new LoopInfo with frame name
|
||||
*/
|
||||
public LoopInfo(String frameName) {
|
||||
this.frameName = frameName;
|
||||
this.loopId = generateLoopId(frameName);
|
||||
this.startTime = System.currentTimeMillis();
|
||||
initializeDefaults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new LoopInfo with frame name and parent
|
||||
*/
|
||||
public LoopInfo(String frameName, String parentFrameName) {
|
||||
this.frameName = frameName;
|
||||
this.parentFrameName = parentFrameName;
|
||||
this.loopId = generateLoopId(frameName);
|
||||
this.startTime = System.currentTimeMillis();
|
||||
initializeDefaults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new LoopInfo with full details
|
||||
*/
|
||||
public LoopInfo(String frameName, String parentFrameName, int nestingDepth) {
|
||||
this.frameName = frameName;
|
||||
this.parentFrameName = parentFrameName;
|
||||
this.nestingDepth = nestingDepth;
|
||||
this.loopId = generateLoopId(frameName);
|
||||
this.startTime = System.currentTimeMillis();
|
||||
initializeDefaults();
|
||||
}
|
||||
|
||||
// === INITIALIZATION ===
|
||||
|
||||
/**
|
||||
* Initialize default values and flags
|
||||
*/
|
||||
private void initializeDefaults() {
|
||||
// Initialize flags
|
||||
flags.put("hasCondition", false);
|
||||
flags.put("hasExit", false);
|
||||
flags.put("hasSwitches", false);
|
||||
flags.put("hasNextIteration", false);
|
||||
flags.put("isNested", parentFrameName != null);
|
||||
flags.put("isInfinite", false);
|
||||
flags.put("isConverging", false);
|
||||
flags.put("isOscillating", false);
|
||||
flags.put("hasNumericalIssues", false);
|
||||
|
||||
// Initialize statistics
|
||||
statistics.put("iterationsPerSecond", 0.0);
|
||||
statistics.put("convergenceRate", 0.0);
|
||||
statistics.put("memoryGrowthRate", 0.0);
|
||||
statistics.put("operationEfficiency", 0.0);
|
||||
|
||||
// Initialize metadata
|
||||
metadata.put("createdAt", System.currentTimeMillis());
|
||||
metadata.put("version", "1.0");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique loop ID
|
||||
*/
|
||||
private String generateLoopId(String frameName) {
|
||||
return frameName + "_" + System.nanoTime();
|
||||
}
|
||||
|
||||
// === OPERATION DISCOVERY ===
|
||||
|
||||
/**
|
||||
* Discover and categorize all operations related to this loop
|
||||
*/
|
||||
public void discoverLoopOperations(SameDiff sameDiff) {
|
||||
for (Map.Entry<String, SameDiffOp> entry : sameDiff.getOps().entrySet()) {
|
||||
String opName = entry.getKey();
|
||||
SameDiffOp op = entry.getValue();
|
||||
DifferentialFunction func = op.getOp();
|
||||
|
||||
// Check if this operation is associated with this loop frame
|
||||
if (isOperationInLoop(opName, func)) {
|
||||
loopOperations.add(opName);
|
||||
|
||||
// Categorize the operation
|
||||
if (func instanceof LoopCond) {
|
||||
loopCondOperation = opName;
|
||||
flags.put("hasCondition", true);
|
||||
} else if (func instanceof Exit) {
|
||||
exitOperations.add(opName);
|
||||
flags.put("hasExit", true);
|
||||
} else if (func instanceof Switch) {
|
||||
switchOperations.add(opName);
|
||||
flags.put("hasSwitches", true);
|
||||
} else if (func instanceof NextIteration) {
|
||||
nextIterationOperations.add(opName);
|
||||
flags.put("hasNextIteration", true);
|
||||
} else if (func instanceof Enter) {
|
||||
enterOperations.add(opName);
|
||||
} else if (func instanceof Merge) {
|
||||
mergeOperations.add(opName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update operation counts
|
||||
metadata.put("totalOperations", loopOperations.size());
|
||||
metadata.put("controlFlowOperations",
|
||||
exitOperations.size() + switchOperations.size() +
|
||||
nextIterationOperations.size() + enterOperations.size() +
|
||||
mergeOperations.size() + (loopCondOperation != null ? 1 : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an operation belongs to this loop (simplified)
|
||||
*/
|
||||
private boolean isOperationInLoop(String opName, DifferentialFunction func) {
|
||||
// This is a simplified check - in practice, you'd need to analyze
|
||||
// the graph structure to determine frame associations
|
||||
return true;
|
||||
}
|
||||
|
||||
// === VARIABLE DISCOVERY ===
|
||||
|
||||
/**
|
||||
* Discover and categorize variables related to this loop
|
||||
*/
|
||||
public void discoverLoopVariables(SameDiff sameDiff) {
|
||||
// Discover loop variables from NextIteration operations
|
||||
for (String nextIterOp : nextIterationOperations) {
|
||||
SameDiffOp op = sameDiff.getOps().get(nextIterOp);
|
||||
if (op != null) {
|
||||
List<String> inputs = op.getInputsToOp();
|
||||
if (inputs != null) {
|
||||
for (String input : inputs) {
|
||||
if (!loopVariables.contains(input)) {
|
||||
loopVariables.add(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Discover input variables from Enter operations
|
||||
for (String enterOp : enterOperations) {
|
||||
SameDiffOp op = sameDiff.getOps().get(enterOp);
|
||||
if (op != null) {
|
||||
List<String> inputs = op.getInputsToOp();
|
||||
if (inputs != null) {
|
||||
for (String input : inputs) {
|
||||
if (!inputVariables.contains(input)) {
|
||||
inputVariables.add(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Discover output variables from Exit operations
|
||||
for (String exitOp : exitOperations) {
|
||||
SameDiffOp op = sameDiff.getOps().get(exitOp);
|
||||
if (op != null) {
|
||||
List<String> outputs = op.getOutputsOfOp();
|
||||
if (outputs != null) {
|
||||
for (String output : outputs) {
|
||||
if (!outputVariables.contains(output)) {
|
||||
outputVariables.add(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove duplicates and update metadata
|
||||
loopVariables = loopVariables.stream().distinct().collect(Collectors.toList());
|
||||
inputVariables = inputVariables.stream().distinct().collect(Collectors.toList());
|
||||
outputVariables = outputVariables.stream().distinct().collect(Collectors.toList());
|
||||
|
||||
metadata.put("loopVariableCount", loopVariables.size());
|
||||
metadata.put("inputVariableCount", inputVariables.size());
|
||||
metadata.put("outputVariableCount", outputVariables.size());
|
||||
}
|
||||
|
||||
// === ITERATION TRACKING ===
|
||||
|
||||
/**
|
||||
* Update iteration count and related metrics
|
||||
*/
|
||||
public void updateIteration(int iteration) {
|
||||
this.currentIteration = iteration;
|
||||
this.maxIterationsObserved = Math.max(this.maxIterationsObserved, iteration);
|
||||
|
||||
// Update timing statistics
|
||||
long currentTime = System.currentTimeMillis();
|
||||
this.totalExecutionTime = currentTime - startTime;
|
||||
|
||||
if (iteration > 0) {
|
||||
this.averageIterationTime = (double) totalExecutionTime / iteration;
|
||||
statistics.put("iterationsPerSecond", 1000.0 / averageIterationTime);
|
||||
}
|
||||
|
||||
// Update flags
|
||||
if (iteration > 1000) {
|
||||
flags.put("isLongRunning", true);
|
||||
}
|
||||
|
||||
if (iteration > expectedIterations && expectedIterations > 0) {
|
||||
flags.put("exceededExpected", true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record operation count for an iteration
|
||||
*/
|
||||
public void recordOperationCount(int iteration, int operationCount) {
|
||||
operationsPerIteration.put(iteration, operationCount);
|
||||
|
||||
// Update efficiency metric
|
||||
if (!operationsPerIteration.isEmpty()) {
|
||||
double avgOpsPerIter = operationsPerIteration.values().stream()
|
||||
.mapToInt(Integer::intValue)
|
||||
.average()
|
||||
.orElse(0.0);
|
||||
statistics.put("averageOperationsPerIteration", avgOpsPerIter);
|
||||
}
|
||||
}
|
||||
|
||||
// === MEMORY TRACKING ===
|
||||
|
||||
/**
|
||||
* Update memory usage statistics
|
||||
*/
|
||||
public void updateMemoryUsage(long memoryUsage) {
|
||||
this.peakMemoryUsage = Math.max(this.peakMemoryUsage, memoryUsage);
|
||||
|
||||
// Update average (simplified moving average)
|
||||
if (this.averageMemoryUsage == 0) {
|
||||
this.averageMemoryUsage = memoryUsage;
|
||||
} else {
|
||||
this.averageMemoryUsage = (long) ((this.averageMemoryUsage * 0.9) + (memoryUsage * 0.1));
|
||||
}
|
||||
|
||||
statistics.put("memoryEfficiency", (double) averageMemoryUsage / Math.max(peakMemoryUsage, 1));
|
||||
|
||||
// Check for memory growth
|
||||
if (memoryUsage > averageMemoryUsage * 1.5) {
|
||||
flags.put("highMemoryUsage", true);
|
||||
}
|
||||
}
|
||||
|
||||
// === TERMINATION PREDICTION ===
|
||||
|
||||
/**
|
||||
* Add a termination prediction
|
||||
*/
|
||||
public void addTerminationPrediction(TerminationPrediction prediction) {
|
||||
terminationPredictions.add(prediction);
|
||||
|
||||
// Update expected iterations based on highest confidence prediction
|
||||
TerminationPrediction bestPrediction = terminationPredictions.stream()
|
||||
.max(Comparator.comparingDouble(TerminationPrediction::getConfidence))
|
||||
.orElse(null);
|
||||
|
||||
if (bestPrediction != null && bestPrediction.getConfidence() > expectedIterationsConfidence) {
|
||||
expectedIterations = bestPrediction.getPredictedTerminationIteration();
|
||||
expectedIterationsConfidence = bestPrediction.getConfidence();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the most confident termination prediction
|
||||
*/
|
||||
public TerminationPrediction getBestTerminationPrediction() {
|
||||
return terminationPredictions.stream()
|
||||
.max(Comparator.comparingDouble(TerminationPrediction::getConfidence))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
// === TERMINATION HANDLING ===
|
||||
|
||||
/**
|
||||
* Mark the loop as terminated
|
||||
*/
|
||||
public void markTerminated(LoopTerminationStatus status, String reason) {
|
||||
this.status = status;
|
||||
this.terminationReason = reason;
|
||||
this.endTime = System.currentTimeMillis();
|
||||
|
||||
// Update final statistics
|
||||
updateFinalStatistics();
|
||||
|
||||
// Set termination flags
|
||||
flags.put("isTerminated", true);
|
||||
flags.put("terminatedNormally", status == LoopTerminationStatus.TERMINATED_NORMAL);
|
||||
flags.put("terminatedEarly", status == LoopTerminationStatus.TERMINATED_EARLY);
|
||||
flags.put("terminatedWithError", status == LoopTerminationStatus.TERMINATED_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update final statistics when loop terminates
|
||||
*/
|
||||
private void updateFinalStatistics() {
|
||||
if (endTime > 0) {
|
||||
totalExecutionTime = endTime - startTime;
|
||||
if (maxIterationsObserved > 0) {
|
||||
averageIterationTime = (double) totalExecutionTime / maxIterationsObserved;
|
||||
statistics.put("finalIterationsPerSecond", 1000.0 / averageIterationTime);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate prediction accuracy
|
||||
if (expectedIterations > 0) {
|
||||
double accuracy = 1.0 - (double) Math.abs(maxIterationsObserved - expectedIterations) / expectedIterations;
|
||||
statistics.put("predictionAccuracy", Math.max(0.0, accuracy));
|
||||
}
|
||||
}
|
||||
|
||||
// === ANALYSIS METHODS ===
|
||||
|
||||
/**
|
||||
* Check if the loop appears to be converging
|
||||
*/
|
||||
public boolean isConverging() {
|
||||
return flags.getOrDefault("isConverging", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the loop appears to be oscillating
|
||||
*/
|
||||
public boolean isOscillating() {
|
||||
return flags.getOrDefault("isOscillating", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the loop has numerical issues
|
||||
*/
|
||||
public boolean hasNumericalIssues() {
|
||||
return flags.getOrDefault("hasNumericalIssues", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the loop is running longer than expected
|
||||
*/
|
||||
public boolean isRunningLongerThanExpected() {
|
||||
return expectedIterations > 0 && currentIteration > expectedIterations * 1.2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the loop efficiency (operations per second)
|
||||
*/
|
||||
public double getLoopEfficiency() {
|
||||
return statistics.getOrDefault("iterationsPerSecond", 0.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the prediction accuracy
|
||||
*/
|
||||
public double getPredictionAccuracy() {
|
||||
return statistics.getOrDefault("predictionAccuracy", 0.0);
|
||||
}
|
||||
|
||||
// === UTILITY METHODS ===
|
||||
|
||||
/**
|
||||
* Get a summary of the loop information
|
||||
*/
|
||||
public String getSummary() {
|
||||
StringBuilder summary = new StringBuilder();
|
||||
summary.append("Loop '").append(frameName).append("'");
|
||||
summary.append(" (").append(status).append(")");
|
||||
summary.append(" - Iterations: ").append(maxIterationsObserved);
|
||||
|
||||
if (totalExecutionTime > 0) {
|
||||
summary.append(", Time: ").append(totalExecutionTime).append("ms");
|
||||
}
|
||||
|
||||
if (expectedIterations > 0) {
|
||||
summary.append(", Expected: ").append(expectedIterations);
|
||||
}
|
||||
|
||||
if (terminationReason != null) {
|
||||
summary.append(", Reason: ").append(terminationReason);
|
||||
}
|
||||
|
||||
return summary.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get detailed information about the loop
|
||||
*/
|
||||
public String getDetailedInfo() {
|
||||
StringBuilder info = new StringBuilder();
|
||||
info.append("=== Loop Information ===\n");
|
||||
info.append("Frame: ").append(frameName).append("\n");
|
||||
info.append("Status: ").append(status).append("\n");
|
||||
info.append("Iterations: ").append(maxIterationsObserved).append("\n");
|
||||
info.append("Execution Time: ").append(totalExecutionTime).append("ms\n");
|
||||
|
||||
if (parentFrameName != null) {
|
||||
info.append("Parent Frame: ").append(parentFrameName).append("\n");
|
||||
info.append("Nesting Depth: ").append(nestingDepth).append("\n");
|
||||
}
|
||||
|
||||
info.append("\nOperations:\n");
|
||||
info.append(" Condition: ").append(loopCondOperation).append("\n");
|
||||
info.append(" Exit: ").append(exitOperations).append("\n");
|
||||
info.append(" Switch: ").append(switchOperations).append("\n");
|
||||
info.append(" NextIteration: ").append(nextIterationOperations).append("\n");
|
||||
info.append(" Total: ").append(loopOperations.size()).append("\n");
|
||||
|
||||
info.append("\nVariables:\n");
|
||||
info.append(" Loop Variables: ").append(loopVariables.size()).append("\n");
|
||||
info.append(" Input Variables: ").append(inputVariables.size()).append("\n");
|
||||
info.append(" Output Variables: ").append(outputVariables.size()).append("\n");
|
||||
|
||||
if (!terminationPredictions.isEmpty()) {
|
||||
info.append("\nPredictions: ").append(terminationPredictions.size()).append("\n");
|
||||
TerminationPrediction best = getBestTerminationPrediction();
|
||||
if (best != null) {
|
||||
info.append(" Best: ").append(best.getPredictedTerminationIteration());
|
||||
info.append(" (").append(String.format("%.2f", best.getConfidence())).append(")\n");
|
||||
}
|
||||
}
|
||||
|
||||
return info.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the loop has specific characteristics
|
||||
*/
|
||||
public boolean hasCharacteristic(String characteristic) {
|
||||
return flags.getOrDefault(characteristic, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a characteristic flag
|
||||
*/
|
||||
public void setCharacteristic(String characteristic, boolean value) {
|
||||
flags.put(characteristic, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a statistic value
|
||||
*/
|
||||
public double getStatistic(String name) {
|
||||
return statistics.getOrDefault(name, 0.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a statistic value
|
||||
*/
|
||||
public void setStatistic(String name, double value) {
|
||||
statistics.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get metadata value
|
||||
*/
|
||||
public Object getMetadataValue(String key) {
|
||||
return metadata.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set metadata value
|
||||
*/
|
||||
public void setMetadataValue(String key, Object value) {
|
||||
metadata.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getSummary();
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package org.nd4j.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class LoopIterationTrace {
|
||||
private String frameName;
|
||||
private List<IterationSnapshot> iterations = new ArrayList<>();
|
||||
private Map<String, List<Object>> variableEvolution = new HashMap<>();
|
||||
private List<ConditionEvaluation> conditionEvaluations = new ArrayList<>();
|
||||
private Map<String, Integer> operationExecutionCounts = new HashMap<>();
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
/**
|
||||
* Enumeration of operation roles within loop control flow
|
||||
*/
|
||||
public enum LoopOperationRole {
|
||||
/**
|
||||
* Regular operation that is not part of loop control flow
|
||||
*/
|
||||
REGULAR,
|
||||
|
||||
/**
|
||||
* Loop condition operation (LoopCond) that determines when loop should terminate
|
||||
*/
|
||||
CONDITION,
|
||||
|
||||
/**
|
||||
* Exit operation that exits the loop when condition is true
|
||||
*/
|
||||
EXIT,
|
||||
|
||||
/**
|
||||
* Switch operation that routes values based on predicate
|
||||
*/
|
||||
SWITCH,
|
||||
|
||||
/**
|
||||
* NextIteration operation that advances to the next loop iteration
|
||||
*/
|
||||
NEXT_ITERATION,
|
||||
|
||||
/**
|
||||
* Enter operation that enters values into the loop frame
|
||||
*/
|
||||
ENTER,
|
||||
|
||||
/**
|
||||
* Merge operation that merges values from different control flow paths
|
||||
*/
|
||||
MERGE,
|
||||
|
||||
/**
|
||||
* Operation that computes loop invariant values
|
||||
*/
|
||||
INVARIANT,
|
||||
|
||||
/**
|
||||
* Operation that is part of loop initialization
|
||||
*/
|
||||
INITIALIZATION,
|
||||
|
||||
/**
|
||||
* Operation that is part of loop finalization/cleanup
|
||||
*/
|
||||
FINALIZATION
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package org.nd4j.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class LoopState {
|
||||
private int iteration;
|
||||
private Map<String, Object> variableStates = new HashMap<>();
|
||||
private Map<String, String> operationStates = new HashMap<>();
|
||||
private List<String> activeOperations = new ArrayList<>();
|
||||
private Map<String, Object> frameContext = new HashMap<>();
|
||||
}
|
||||
+1183
File diff suppressed because it is too large
Load Diff
+30
@@ -0,0 +1,30 @@
|
||||
package org.nd4j.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
// Data classes for the error report structure
|
||||
@Data
|
||||
public class LoopTerminationErrorReport {
|
||||
private String frameName;
|
||||
private int iteration;
|
||||
private long timestamp;
|
||||
private TerminationType terminationType;
|
||||
private String triggerOperation;
|
||||
private String terminationReason;
|
||||
private boolean wasEarlyTermination;
|
||||
private String earlyTerminationCause;
|
||||
|
||||
// Analysis sections
|
||||
private VariableStateAnalysis variableStateAnalysis;
|
||||
private OperationAnalysis operationAnalysis;
|
||||
private FrameContextInfo frameContext;
|
||||
private VariableEvolutionAnalysis variableEvolution;
|
||||
private PerformanceMetrics performanceMetrics;
|
||||
private RootCauseAnalysis rootCauseAnalysis;
|
||||
private VisualizationData visualizationData;
|
||||
|
||||
// Loop-specific metrics
|
||||
private long loopExecutionTime;
|
||||
private int expectedIterations;
|
||||
private int maxIterationsObserved;
|
||||
}
|
||||
+1209
File diff suppressed because it is too large
Load Diff
+24
@@ -0,0 +1,24 @@
|
||||
package org.nd4j.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class LoopTerminationEvent {
|
||||
private String frameName;
|
||||
private int iteration;
|
||||
private long timestamp;
|
||||
private TerminationType terminationType;
|
||||
private String triggerOperation;
|
||||
private Object terminationValue;
|
||||
private String terminationReason;
|
||||
private Map<String, Object> contextData = new HashMap<>();
|
||||
private List<String> affectedVariables = new ArrayList<>();
|
||||
private LoopState loopStateAtTermination;
|
||||
private boolean wasEarlyTermination;
|
||||
private String earlyTerminationCause;
|
||||
}
|
||||
+515
@@ -0,0 +1,515 @@
|
||||
package org.nd4j.autodiff.samediff;/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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 lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Methods for retrieving and analyzing loop termination events
|
||||
*/
|
||||
@Slf4j
|
||||
public class LoopTerminationEventUtils {
|
||||
|
||||
/**
|
||||
* Get the latest termination event for a specific frame
|
||||
*
|
||||
* @param frameName The name of the loop frame
|
||||
* @param terminationHistory Map of frame names to their termination events
|
||||
* @return The most recent termination event for the frame, or null if none exists
|
||||
*/
|
||||
public static LoopTerminationEvent getLatestTerminationEvent(String frameName,
|
||||
Map<String, List<LoopTerminationEvent>> terminationHistory) {
|
||||
List<LoopTerminationEvent> events = terminationHistory.get(frameName);
|
||||
if (events != null && !events.isEmpty()) {
|
||||
return events.get(events.size() - 1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest termination event for a specific frame, with additional filtering
|
||||
*
|
||||
* @param frameName The name of the loop frame
|
||||
* @param terminationHistory Map of frame names to their termination events
|
||||
* @param terminationType Filter by specific termination type (null for any type)
|
||||
* @return The most recent termination event matching criteria, or null if none exists
|
||||
*/
|
||||
public static LoopTerminationEvent getLatestTerminationEvent(String frameName,
|
||||
Map<String, List<LoopTerminationEvent>> terminationHistory,
|
||||
TerminationType terminationType) {
|
||||
List<LoopTerminationEvent> events = terminationHistory.get(frameName);
|
||||
if (events != null && !events.isEmpty()) {
|
||||
// Filter by termination type if specified
|
||||
if (terminationType != null) {
|
||||
List<LoopTerminationEvent> filteredEvents = events.stream()
|
||||
.filter(event -> event.getTerminationType() == terminationType)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!filteredEvents.isEmpty()) {
|
||||
return filteredEvents.get(filteredEvents.size() - 1);
|
||||
}
|
||||
} else {
|
||||
return events.get(events.size() - 1);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest termination event across all frames
|
||||
*
|
||||
* @param terminationHistory Map of frame names to their termination events
|
||||
* @return The most recent termination event across all frames, or null if none exists
|
||||
*/
|
||||
public static LoopTerminationEvent getLatestTerminationEventGlobal(Map<String, List<LoopTerminationEvent>> terminationHistory) {
|
||||
LoopTerminationEvent latestEvent = null;
|
||||
long latestTimestamp = 0;
|
||||
|
||||
for (Map.Entry<String, List<LoopTerminationEvent>> entry : terminationHistory.entrySet()) {
|
||||
List<LoopTerminationEvent> events = entry.getValue();
|
||||
if (events != null && !events.isEmpty()) {
|
||||
LoopTerminationEvent lastEvent = events.get(events.size() - 1);
|
||||
if (lastEvent.getTimestamp() > latestTimestamp) {
|
||||
latestTimestamp = lastEvent.getTimestamp();
|
||||
latestEvent = lastEvent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return latestEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest early termination event for a specific frame
|
||||
*
|
||||
* @param frameName The name of the loop frame
|
||||
* @param terminationHistory Map of frame names to their termination events
|
||||
* @return The most recent early termination event, or null if none exists
|
||||
*/
|
||||
public static LoopTerminationEvent getLatestEarlyTerminationEvent(String frameName,
|
||||
Map<String, List<LoopTerminationEvent>> terminationHistory) {
|
||||
List<LoopTerminationEvent> events = terminationHistory.get(frameName);
|
||||
if (events != null && !events.isEmpty()) {
|
||||
// Find the latest early termination event
|
||||
for (int i = events.size() - 1; i >= 0; i--) {
|
||||
LoopTerminationEvent event = events.get(i);
|
||||
if (event.isWasEarlyTermination()) {
|
||||
return event;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest termination event by timestamp for a specific frame
|
||||
*
|
||||
* @param frameName The name of the loop frame
|
||||
* @param terminationHistory Map of frame names to their termination events
|
||||
* @return The termination event with the highest timestamp, or null if none exists
|
||||
*/
|
||||
public static LoopTerminationEvent getLatestTerminationEventByTimestamp(String frameName,
|
||||
Map<String, List<LoopTerminationEvent>> terminationHistory) {
|
||||
List<LoopTerminationEvent> events = terminationHistory.get(frameName);
|
||||
if (events != null && !events.isEmpty()) {
|
||||
return events.stream()
|
||||
.max(Comparator.comparingLong(LoopTerminationEvent::getTimestamp))
|
||||
.orElse(null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest termination event with detailed context information
|
||||
*
|
||||
* @param frameName The name of the loop frame
|
||||
* @param terminationHistory Map of frame names to their termination events
|
||||
* @return Enhanced termination event information, or null if none exists
|
||||
*/
|
||||
public static EnhancedTerminationEvent getLatestTerminationEventWithContext(String frameName,
|
||||
Map<String, List<LoopTerminationEvent>> terminationHistory) {
|
||||
List<LoopTerminationEvent> events = terminationHistory.get(frameName);
|
||||
if (events != null && !events.isEmpty()) {
|
||||
LoopTerminationEvent latestEvent = events.get(events.size() - 1);
|
||||
|
||||
// Create enhanced event with additional context
|
||||
EnhancedTerminationEvent enhancedEvent = new EnhancedTerminationEvent(latestEvent);
|
||||
|
||||
// Add context about previous events
|
||||
if (events.size() > 1) {
|
||||
enhancedEvent.setPreviousEvent(events.get(events.size() - 2));
|
||||
enhancedEvent.setEventSequenceNumber(events.size());
|
||||
}
|
||||
|
||||
// Add timing analysis
|
||||
if (events.size() > 1) {
|
||||
long timeBetweenEvents = latestEvent.getTimestamp() - events.get(0).getTimestamp();
|
||||
enhancedEvent.setTotalExecutionTime(timeBetweenEvents);
|
||||
}
|
||||
|
||||
// Add pattern analysis
|
||||
enhancedEvent.setTerminationPattern(analyzeTerminationPattern(events));
|
||||
|
||||
return enhancedEvent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all termination events for a frame, sorted by timestamp
|
||||
*
|
||||
* @param frameName The name of the loop frame
|
||||
* @param terminationHistory Map of frame names to their termination events
|
||||
* @return List of termination events sorted by timestamp (oldest first)
|
||||
*/
|
||||
public static List<LoopTerminationEvent> getAllTerminationEventsSorted(String frameName,
|
||||
Map<String, List<LoopTerminationEvent>> terminationHistory) {
|
||||
List<LoopTerminationEvent> events = terminationHistory.get(frameName);
|
||||
if (events != null && !events.isEmpty()) {
|
||||
return events.stream()
|
||||
.sorted(Comparator.comparingLong(LoopTerminationEvent::getTimestamp))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get termination events within a specific time range
|
||||
*
|
||||
* @param frameName The name of the loop frame
|
||||
* @param terminationHistory Map of frame names to their termination events
|
||||
* @param startTime Start of time range (inclusive)
|
||||
* @param endTime End of time range (inclusive)
|
||||
* @return List of termination events within the time range
|
||||
*/
|
||||
public static List<LoopTerminationEvent> getTerminationEventsInTimeRange(String frameName,
|
||||
Map<String, List<LoopTerminationEvent>> terminationHistory,
|
||||
long startTime, long endTime) {
|
||||
List<LoopTerminationEvent> events = terminationHistory.get(frameName);
|
||||
if (events != null && !events.isEmpty()) {
|
||||
return events.stream()
|
||||
.filter(event -> event.getTimestamp() >= startTime && event.getTimestamp() <= endTime)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get termination events by type
|
||||
*
|
||||
* @param frameName The name of the loop frame
|
||||
* @param terminationHistory Map of frame names to their termination events
|
||||
* @param terminationType The type of termination to filter by
|
||||
* @return List of termination events of the specified type
|
||||
*/
|
||||
public static List<LoopTerminationEvent> getTerminationEventsByType(String frameName,
|
||||
Map<String, List<LoopTerminationEvent>> terminationHistory,
|
||||
TerminationType terminationType) {
|
||||
List<LoopTerminationEvent> events = terminationHistory.get(frameName);
|
||||
if (events != null && !events.isEmpty()) {
|
||||
return events.stream()
|
||||
.filter(event -> event.getTerminationType() == terminationType)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the most recent termination event that matches specific criteria
|
||||
*
|
||||
* @param frameName The name of the loop frame
|
||||
* @param terminationHistory Map of frame names to their termination events
|
||||
* @param criteria Predicate to filter events
|
||||
* @return The most recent event matching criteria, or null if none exists
|
||||
*/
|
||||
public static LoopTerminationEvent getLatestTerminationEventMatching(String frameName,
|
||||
Map<String, List<LoopTerminationEvent>> terminationHistory,
|
||||
java.util.function.Predicate<LoopTerminationEvent> criteria) {
|
||||
List<LoopTerminationEvent> events = terminationHistory.get(frameName);
|
||||
if (events != null && !events.isEmpty()) {
|
||||
// Search from most recent to oldest
|
||||
for (int i = events.size() - 1; i >= 0; i--) {
|
||||
LoopTerminationEvent event = events.get(i);
|
||||
if (criteria.test(event)) {
|
||||
return event;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a frame has any termination events
|
||||
*
|
||||
* @param frameName The name of the loop frame
|
||||
* @param terminationHistory Map of frame names to their termination events
|
||||
* @return true if the frame has termination events, false otherwise
|
||||
*/
|
||||
public static boolean hasTerminationEvents(String frameName,
|
||||
Map<String, List<LoopTerminationEvent>> terminationHistory) {
|
||||
List<LoopTerminationEvent> events = terminationHistory.get(frameName);
|
||||
return events != null && !events.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of termination events for a frame
|
||||
*
|
||||
* @param frameName The name of the loop frame
|
||||
* @param terminationHistory Map of frame names to their termination events
|
||||
* @return Number of termination events for the frame
|
||||
*/
|
||||
public static int getTerminationEventCount(String frameName,
|
||||
Map<String, List<LoopTerminationEvent>> terminationHistory) {
|
||||
List<LoopTerminationEvent> events = terminationHistory.get(frameName);
|
||||
return events != null ? events.size() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get summary statistics for termination events
|
||||
*
|
||||
* @param frameName The name of the loop frame
|
||||
* @param terminationHistory Map of frame names to their termination events
|
||||
* @return TerminationEventSummary with statistics
|
||||
*/
|
||||
public static TerminationEventSummary getTerminationEventSummary(String frameName,
|
||||
Map<String, List<LoopTerminationEvent>> terminationHistory) {
|
||||
List<LoopTerminationEvent> events = terminationHistory.get(frameName);
|
||||
if (events == null || events.isEmpty()) {
|
||||
return new TerminationEventSummary(frameName, 0, 0, 0, null, null);
|
||||
}
|
||||
|
||||
long totalEvents = events.size();
|
||||
long earlyTerminations = events.stream()
|
||||
.mapToLong(event -> event.isWasEarlyTermination() ? 1 : 0)
|
||||
.sum();
|
||||
|
||||
long errorTerminations = events.stream()
|
||||
.mapToLong(event -> event.getTerminationType() == TerminationType.ERROR_TERMINATION ? 1 : 0)
|
||||
.sum();
|
||||
|
||||
LoopTerminationEvent firstEvent = events.get(0);
|
||||
LoopTerminationEvent lastEvent = events.get(events.size() - 1);
|
||||
|
||||
return new TerminationEventSummary(frameName, totalEvents, earlyTerminations,
|
||||
errorTerminations, firstEvent, lastEvent);
|
||||
}
|
||||
|
||||
// Helper method to analyze termination patterns
|
||||
private static String analyzeTerminationPattern(List<LoopTerminationEvent> events) {
|
||||
if (events.size() < 2) {
|
||||
return "SINGLE_EVENT";
|
||||
}
|
||||
|
||||
// Check for consistent termination types
|
||||
TerminationType lastType = events.get(events.size() - 1).getTerminationType();
|
||||
boolean allSameType = events.stream()
|
||||
.allMatch(event -> event.getTerminationType() == lastType);
|
||||
|
||||
if (allSameType) {
|
||||
return "CONSISTENT_" + lastType.name();
|
||||
}
|
||||
|
||||
// Check for alternating patterns
|
||||
boolean alternating = true;
|
||||
for (int i = 1; i < events.size(); i++) {
|
||||
if (events.get(i).getTerminationType() == events.get(i - 1).getTerminationType()) {
|
||||
alternating = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (alternating) {
|
||||
return "ALTERNATING_PATTERN";
|
||||
}
|
||||
|
||||
return "MIXED_PATTERN";
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhanced termination event with additional context
|
||||
*/
|
||||
public static class EnhancedTerminationEvent {
|
||||
private final LoopTerminationEvent baseEvent;
|
||||
private LoopTerminationEvent previousEvent;
|
||||
private int eventSequenceNumber;
|
||||
private long totalExecutionTime;
|
||||
private String terminationPattern;
|
||||
|
||||
public EnhancedTerminationEvent(LoopTerminationEvent baseEvent) {
|
||||
this.baseEvent = baseEvent;
|
||||
}
|
||||
|
||||
// Getters and setters
|
||||
public LoopTerminationEvent getBaseEvent() { return baseEvent; }
|
||||
public LoopTerminationEvent getPreviousEvent() { return previousEvent; }
|
||||
public void setPreviousEvent(LoopTerminationEvent previousEvent) { this.previousEvent = previousEvent; }
|
||||
public int getEventSequenceNumber() { return eventSequenceNumber; }
|
||||
public void setEventSequenceNumber(int eventSequenceNumber) { this.eventSequenceNumber = eventSequenceNumber; }
|
||||
public long getTotalExecutionTime() { return totalExecutionTime; }
|
||||
public void setTotalExecutionTime(long totalExecutionTime) { this.totalExecutionTime = totalExecutionTime; }
|
||||
public String getTerminationPattern() { return terminationPattern; }
|
||||
public void setTerminationPattern(String terminationPattern) { this.terminationPattern = terminationPattern; }
|
||||
|
||||
public String getEnhancedSummary() {
|
||||
StringBuilder summary = new StringBuilder();
|
||||
summary.append("Event #").append(eventSequenceNumber);
|
||||
summary.append(" - ").append(baseEvent.getTerminationType());
|
||||
summary.append(" at iteration ").append(baseEvent.getIteration());
|
||||
|
||||
if (previousEvent != null) {
|
||||
long timeBetween = baseEvent.getTimestamp() - previousEvent.getTimestamp();
|
||||
summary.append(" (").append(timeBetween).append("ms after previous)");
|
||||
}
|
||||
|
||||
if (terminationPattern != null) {
|
||||
summary.append(" [").append(terminationPattern).append("]");
|
||||
}
|
||||
|
||||
return summary.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Summary statistics for termination events
|
||||
*/
|
||||
public static class TerminationEventSummary {
|
||||
private final String frameName;
|
||||
private final long totalEvents;
|
||||
private final long earlyTerminations;
|
||||
private final long errorTerminations;
|
||||
private final LoopTerminationEvent firstEvent;
|
||||
private final LoopTerminationEvent lastEvent;
|
||||
|
||||
public TerminationEventSummary(String frameName, long totalEvents, long earlyTerminations,
|
||||
long errorTerminations, LoopTerminationEvent firstEvent,
|
||||
LoopTerminationEvent lastEvent) {
|
||||
this.frameName = frameName;
|
||||
this.totalEvents = totalEvents;
|
||||
this.earlyTerminations = earlyTerminations;
|
||||
this.errorTerminations = errorTerminations;
|
||||
this.firstEvent = firstEvent;
|
||||
this.lastEvent = lastEvent;
|
||||
}
|
||||
|
||||
// Getters
|
||||
public String getFrameName() { return frameName; }
|
||||
public long getTotalEvents() { return totalEvents; }
|
||||
public long getEarlyTerminations() { return earlyTerminations; }
|
||||
public long getErrorTerminations() { return errorTerminations; }
|
||||
public LoopTerminationEvent getFirstEvent() { return firstEvent; }
|
||||
public LoopTerminationEvent getLastEvent() { return lastEvent; }
|
||||
|
||||
public double getEarlyTerminationRate() {
|
||||
return totalEvents > 0 ? (double) earlyTerminations / totalEvents : 0.0;
|
||||
}
|
||||
|
||||
public double getErrorTerminationRate() {
|
||||
return totalEvents > 0 ? (double) errorTerminations / totalEvents : 0.0;
|
||||
}
|
||||
|
||||
public long getTotalExecutionTime() {
|
||||
if (firstEvent != null && lastEvent != null) {
|
||||
return lastEvent.getTimestamp() - firstEvent.getTimestamp();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("TerminationEventSummary[frame=%s, total=%d, early=%d, errors=%d, earlyRate=%.2f%%]",
|
||||
frameName, totalEvents, earlyTerminations, errorTerminations,
|
||||
getEarlyTerminationRate() * 100);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get all early termination events across all frames
|
||||
*/
|
||||
public static List<LoopTerminationEvent> getAllEarlyTerminationEvents(
|
||||
Map<String, List<LoopTerminationEvent>> terminationHistory) {
|
||||
return terminationHistory.values().stream()
|
||||
.flatMap(List::stream)
|
||||
.filter(LoopTerminationEvent::isWasEarlyTermination)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Find termination events that occurred within a time window
|
||||
*/
|
||||
public static List<LoopTerminationEvent> getTerminationEventsInTimeWindow(
|
||||
Map<String, List<LoopTerminationEvent>> terminationHistory,
|
||||
long startTime, long endTime) {
|
||||
return terminationHistory.values().stream()
|
||||
.flatMap(List::stream)
|
||||
.filter(event -> event.getTimestamp() >= startTime && event.getTimestamp() <= endTime)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Group termination events by their termination type
|
||||
*/
|
||||
public static Map<TerminationType, List<LoopTerminationEvent>> groupEventsByType(
|
||||
Map<String, List<LoopTerminationEvent>> terminationHistory) {
|
||||
return terminationHistory.values().stream()
|
||||
.flatMap(List::stream)
|
||||
.collect(Collectors.groupingBy(LoopTerminationEvent::getTerminationType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate termination statistics
|
||||
*/
|
||||
public static Map<String, Object> calculateTerminationStatistics(
|
||||
Map<String, List<LoopTerminationEvent>> terminationHistory) {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
|
||||
List<LoopTerminationEvent> allEvents = terminationHistory.values().stream()
|
||||
.flatMap(List::stream)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
stats.put("totalEvents", allEvents.size());
|
||||
stats.put("uniqueFrames", terminationHistory.keySet().size());
|
||||
|
||||
// Count by type
|
||||
Map<TerminationType, Long> typeCounts = allEvents.stream()
|
||||
.collect(Collectors.groupingBy(LoopTerminationEvent::getTerminationType, Collectors.counting()));
|
||||
stats.put("eventsByType", typeCounts);
|
||||
|
||||
// Early termination rate
|
||||
long earlyTerminations = allEvents.stream()
|
||||
.filter(LoopTerminationEvent::isWasEarlyTermination)
|
||||
.count();
|
||||
double earlyTerminationRate = allEvents.size() > 0 ? (double) earlyTerminations / allEvents.size() : 0.0;
|
||||
stats.put("earlyTerminationRate", earlyTerminationRate);
|
||||
|
||||
// Average iteration at termination
|
||||
double avgIteration = allEvents.stream()
|
||||
.mapToInt(LoopTerminationEvent::getIteration)
|
||||
.average()
|
||||
.orElse(0.0);
|
||||
stats.put("averageTerminationIteration", avgIteration);
|
||||
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package org.nd4j.autodiff.samediff;
|
||||
|
||||
/**
|
||||
* Possible loop termination statuses
|
||||
*/
|
||||
public enum LoopTerminationStatus {
|
||||
ACTIVE, // Loop is currently executing
|
||||
TERMINATED_NORMAL, // Loop terminated normally (condition became false)
|
||||
TERMINATED_EARLY, // Loop terminated earlier than expected
|
||||
TERMINATED_ERROR, // Loop terminated due to an error
|
||||
TERMINATED_TIMEOUT, // Loop terminated due to timeout
|
||||
TERMINATED_MANUAL, // Loop was manually terminated
|
||||
TERMINATED_RESOURCE, // Loop terminated due to resource exhaustion
|
||||
PAUSED, // Loop execution is paused
|
||||
UNKNOWN // Status is unknown
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package org.nd4j.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class MultiLoopTerminationErrorReport {
|
||||
private Map<String, LoopTerminationErrorReport> individualReports = new HashMap<>();
|
||||
private CrossLoopAnalysis crossLoopAnalysis;
|
||||
private long totalAnalysisTime;
|
||||
private int totalLoopsAnalyzed;
|
||||
}
|
||||
+46
@@ -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.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
||||
@Data
|
||||
public class NameScope implements Closeable {
|
||||
private final SameDiff sameDiff;
|
||||
private final String name;
|
||||
|
||||
public NameScope(SameDiff sameDiff, String name){
|
||||
this.sameDiff = sameDiff;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
sameDiff.closeNameScope(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return "NameScope(" + name + ")";
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package org.nd4j.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class OperationAnalysis {
|
||||
private String loopConditionOp;
|
||||
private List<String> exitOperations = new ArrayList<>();
|
||||
private List<String> switchOperations = new ArrayList<>();
|
||||
private List<String> nextIterationOperations = new ArrayList<>();
|
||||
private List<String> enterOperations = new ArrayList<>();
|
||||
private List<String> mergeOperations = new ArrayList<>();
|
||||
private Map<String, Integer> operationExecutionCounts = new HashMap<>();
|
||||
private Map<Integer, List<String>> recentExecutionHistory = new HashMap<>();
|
||||
private OperationInfo triggerOperationInfo;
|
||||
}
|
||||
+377
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nd4j.autodiff.samediff.config.SDValue;
|
||||
import org.nd4j.autodiff.samediff.internal.FrameIter;
|
||||
import org.nd4j.autodiff.samediff.internal.SameDiffOp;
|
||||
import org.nd4j.autodiff.samediff.internal.VarId;
|
||||
import org.nd4j.linalg.api.ndarray.INDArray;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Utility class for analyzing operations and their context
|
||||
*/
|
||||
@Slf4j
|
||||
public class OperationAnalysisUtils {
|
||||
|
||||
/**
|
||||
* Determine the loop role based on operation type
|
||||
*/
|
||||
public static LoopOperationRole determineLoopRole(String opType) {
|
||||
if (opType == null) return LoopOperationRole.REGULAR;
|
||||
|
||||
switch (opType.toLowerCase()) {
|
||||
case "loopcond":
|
||||
return LoopOperationRole.CONDITION;
|
||||
case "exit":
|
||||
return LoopOperationRole.EXIT;
|
||||
case "switch":
|
||||
return LoopOperationRole.SWITCH;
|
||||
case "nextiteration":
|
||||
return LoopOperationRole.NEXT_ITERATION;
|
||||
case "enter":
|
||||
return LoopOperationRole.ENTER;
|
||||
case "merge":
|
||||
return LoopOperationRole.MERGE;
|
||||
default:
|
||||
return LoopOperationRole.REGULAR;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if operation is critical for loop termination
|
||||
*/
|
||||
public static boolean isTerminationCriticalOperation(String opType) {
|
||||
LoopOperationRole role = determineLoopRole(opType);
|
||||
return role == LoopOperationRole.CONDITION ||
|
||||
role == LoopOperationRole.EXIT ||
|
||||
role == LoopOperationRole.SWITCH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create VarId with proper frame and iteration context
|
||||
*/
|
||||
public static VarId createVarId(String varName, FrameIter frameContext, FrameInfo frameInfo) {
|
||||
if (frameContext != null) {
|
||||
return new VarId(varName, frameContext.getFrame(), frameContext.getIteration(),null);
|
||||
} else if (frameInfo != null && frameInfo.targetFrame != null) {
|
||||
return new VarId(varName, frameInfo.targetFrame, 0,null);
|
||||
} else {
|
||||
return new VarId(varName, "OUTER_FRAME", 0,null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract value for analysis, handling different SDValue types
|
||||
*/
|
||||
public static Object extractValueForAnalysis(SDValue value) {
|
||||
return extractValueForAnalysis(value, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract value for analysis with specified max elements for arrays
|
||||
*/
|
||||
public static Object extractValueForAnalysis(SDValue value, int maxElements) {
|
||||
if (value == null) return null;
|
||||
|
||||
switch (value.getSdValueType()) {
|
||||
case TENSOR:
|
||||
INDArray tensor = value.getTensorValue();
|
||||
if (tensor == null) return null;
|
||||
|
||||
if (tensor.isScalar()) {
|
||||
return tensor.getDouble(0);
|
||||
} else if (tensor.length() <= maxElements) {
|
||||
return createTensorSummary(tensor, true);
|
||||
} else {
|
||||
return createTensorSummary(tensor, false);
|
||||
}
|
||||
|
||||
case LIST:
|
||||
return value.getListValue();
|
||||
|
||||
default:
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a summary representation of a tensor
|
||||
*/
|
||||
public static Map<String, Object> createTensorSummary(INDArray tensor, boolean includeValues) {
|
||||
Map<String, Object> summary = new HashMap<>();
|
||||
summary.put("shape", Arrays.toString(tensor.shape()));
|
||||
summary.put("dataType", tensor.dataType().toString());
|
||||
summary.put("length", tensor.length());
|
||||
|
||||
if (tensor.isScalar()) {
|
||||
summary.put("value", tensor.getDouble(0));
|
||||
summary.put("isScalar", true);
|
||||
} else {
|
||||
summary.put("isScalar", false);
|
||||
|
||||
if (includeValues && tensor.length() <= 10) {
|
||||
// Show all values for small tensors
|
||||
double[] values = tensor.toDoubleVector();
|
||||
summary.put("values", Arrays.toString(values));
|
||||
} else {
|
||||
// Show statistics for large tensors
|
||||
try {
|
||||
summary.put("min", tensor.minNumber().doubleValue());
|
||||
summary.put("max", tensor.maxNumber().doubleValue());
|
||||
summary.put("mean", tensor.meanNumber().doubleValue());
|
||||
} catch (Exception e) {
|
||||
log.debug("Could not compute tensor statistics: {}", e.getMessage());
|
||||
summary.put("statisticsError", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Check for numerical issues
|
||||
NumericalHealthInfo healthInfo = analyzeTensorHealth(tensor);
|
||||
summary.put("hasNaN", healthInfo.hasNaN);
|
||||
summary.put("hasInf", healthInfo.hasInf);
|
||||
summary.put("hasExtreme", healthInfo.hasExtreme);
|
||||
summary.put("numericalHealth", healthInfo.getHealthDescription());
|
||||
}
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze tensor for numerical health issues
|
||||
*/
|
||||
public static NumericalHealthInfo analyzeTensorHealth(INDArray tensor) {
|
||||
NumericalHealthInfo healthInfo = new NumericalHealthInfo();
|
||||
|
||||
if (tensor == null) {
|
||||
healthInfo.isNull = true;
|
||||
return healthInfo;
|
||||
}
|
||||
|
||||
// For large tensors, sample a subset to avoid performance issues
|
||||
int sampleSize = Math.min(1000, (int) tensor.length());
|
||||
|
||||
for (int i = 0; i < sampleSize; i++) {
|
||||
double val = tensor.getDouble(i);
|
||||
|
||||
if (Double.isNaN(val)) {
|
||||
healthInfo.hasNaN = true;
|
||||
healthInfo.nanCount++;
|
||||
} else if (Double.isInfinite(val)) {
|
||||
healthInfo.hasInf = true;
|
||||
healthInfo.infCount++;
|
||||
} else if (Math.abs(val) > 1e10) {
|
||||
healthInfo.hasExtreme = true;
|
||||
healthInfo.extremeCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// If we sampled, extrapolate counts
|
||||
if (sampleSize < tensor.length()) {
|
||||
double scaleFactor = (double) tensor.length() / sampleSize;
|
||||
healthInfo.nanCount = (int) (healthInfo.nanCount * scaleFactor);
|
||||
healthInfo.infCount = (int) (healthInfo.infCount * scaleFactor);
|
||||
healthInfo.extremeCount = (int) (healthInfo.extremeCount * scaleFactor);
|
||||
}
|
||||
|
||||
return healthInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate memory usage of a value
|
||||
*/
|
||||
public static long estimateValueMemoryUsage(SDValue value) {
|
||||
if (value == null) return 0;
|
||||
|
||||
switch (value.getSdValueType()) {
|
||||
case TENSOR:
|
||||
INDArray tensor = value.getTensorValue();
|
||||
if (tensor == null) return 0;
|
||||
return tensor.length() * tensor.dataType().width();
|
||||
|
||||
case LIST:
|
||||
// Rough estimate for list values
|
||||
Object listValue = value.getListValue();
|
||||
if (listValue instanceof List) {
|
||||
return ((List<?>) listValue).size() * 8; // Rough estimate
|
||||
}
|
||||
return 100;
|
||||
|
||||
default:
|
||||
return 50; // Placeholder for other types
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the operation that produces a given variable
|
||||
*/
|
||||
public static SameDiffOp findProducerOperation(SameDiff sameDiff, String variableName) {
|
||||
for (SameDiffOp op : sameDiff.getOps().values()) {
|
||||
if (op.getOutputsOfOp() != null && op.getOutputsOfOp().contains(variableName)) {
|
||||
return op;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find operations that consume a given variable
|
||||
*/
|
||||
public static List<SameDiffOp> findConsumerOperations(SameDiff sameDiff, String variableName) {
|
||||
List<SameDiffOp> consumers = new ArrayList<>();
|
||||
|
||||
for (SameDiffOp op : sameDiff.getOps().values()) {
|
||||
if (op.getInputsToOp() != null && op.getInputsToOp().contains(variableName)) {
|
||||
consumers.add(op);
|
||||
}
|
||||
}
|
||||
|
||||
return consumers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a specific value is problematic (NaN, Inf, etc.)
|
||||
*/
|
||||
public static boolean isProblematicValue(Object value) {
|
||||
if (value == null) return false;
|
||||
|
||||
if (value instanceof Number) {
|
||||
double d = ((Number) value).doubleValue();
|
||||
return Double.isNaN(d) || Double.isInfinite(d);
|
||||
}
|
||||
|
||||
if (value instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = (Map<String, Object>) value;
|
||||
Boolean hasNaN = (Boolean) map.get("hasNaN");
|
||||
Boolean hasInf = (Boolean) map.get("hasInf");
|
||||
return (hasNaN != null && hasNaN) || (hasInf != null && hasInf);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the specific problem with a value
|
||||
*/
|
||||
public static String describeProblem(Object value) {
|
||||
if (value instanceof Number) {
|
||||
double d = ((Number) value).doubleValue();
|
||||
if (Double.isNaN(d)) return "NaN value";
|
||||
if (Double.isInfinite(d)) return "Infinite value";
|
||||
if (Math.abs(d) > 1e10) return "Extreme value: " + d;
|
||||
}
|
||||
|
||||
if (value instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = (Map<String, Object>) value;
|
||||
List<String> problems = new ArrayList<>();
|
||||
|
||||
Boolean hasNaN = (Boolean) map.get("hasNaN");
|
||||
Boolean hasInf = (Boolean) map.get("hasInf");
|
||||
Boolean hasExtreme = (Boolean) map.get("hasExtreme");
|
||||
|
||||
if (hasNaN != null && hasNaN) problems.add("Contains NaN values");
|
||||
if (hasInf != null && hasInf) problems.add("Contains Infinite values");
|
||||
if (hasExtreme != null && hasExtreme) problems.add("Contains extreme values");
|
||||
|
||||
return problems.isEmpty() ? "Unknown problem" : String.join(", ", problems);
|
||||
}
|
||||
|
||||
return "Unknown problem";
|
||||
}
|
||||
|
||||
/**
|
||||
* Format value for display
|
||||
*/
|
||||
public static String formatValue(Object value) {
|
||||
if (value == null) return "null";
|
||||
|
||||
if (value instanceof Number) {
|
||||
return String.format("%.6f", ((Number) value).doubleValue());
|
||||
}
|
||||
|
||||
if (value instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = (Map<String, Object>) value;
|
||||
|
||||
if (map.containsKey("isScalar") && Boolean.TRUE.equals(map.get("isScalar"))) {
|
||||
return String.format("%.6f", (Double) map.get("value"));
|
||||
} else {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Tensor").append(map.get("shape"));
|
||||
|
||||
if (map.containsKey("values")) {
|
||||
sb.append(" = ").append(map.get("values"));
|
||||
} else {
|
||||
sb.append(" [").append(map.get("dataType")).append("]");
|
||||
if (map.containsKey("mean")) {
|
||||
sb.append(" (mean: ").append(String.format("%.6f", (Double) map.get("mean"))).append(")");
|
||||
}
|
||||
}
|
||||
|
||||
String health = (String) map.get("numericalHealth");
|
||||
if (health != null && !health.equals("HEALTHY")) {
|
||||
sb.append(" [").append(health).append("]");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to hold numerical health information
|
||||
*/
|
||||
public static class NumericalHealthInfo {
|
||||
public boolean isNull = false;
|
||||
public boolean hasNaN = false;
|
||||
public boolean hasInf = false;
|
||||
public boolean hasExtreme = false;
|
||||
public int nanCount = 0;
|
||||
public int infCount = 0;
|
||||
public int extremeCount = 0;
|
||||
|
||||
public String getHealthDescription() {
|
||||
if (isNull) return "NULL";
|
||||
|
||||
List<String> issues = new ArrayList<>();
|
||||
if (hasNaN) issues.add(nanCount + " NaN values");
|
||||
if (hasInf) issues.add(infCount + " Inf values");
|
||||
if (hasExtreme) issues.add(extremeCount + " extreme values");
|
||||
|
||||
if (issues.isEmpty()) {
|
||||
return "HEALTHY";
|
||||
} else {
|
||||
return "ISSUES: " + String.join(", ", issues);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isHealthy() {
|
||||
return !isNull && !hasNaN && !hasInf && !hasExtreme;
|
||||
}
|
||||
}
|
||||
}
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Information about operation dependencies and relationships
|
||||
*/
|
||||
@Data
|
||||
public class OperationDependencyInfo {
|
||||
/**
|
||||
* Map of input variable names to the operations that produce them
|
||||
*/
|
||||
private Map<String, String> inputDependencies = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Map of output variable names to the operations that consume them
|
||||
*/
|
||||
private Map<String, List<String>> outputDependencies = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Operations that must execute before this operation
|
||||
*/
|
||||
private List<String> predecessors = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Operations that must execute after this operation
|
||||
*/
|
||||
private List<String> successors = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Operations in the same frame/scope
|
||||
*/
|
||||
private List<String> siblings = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Control dependencies (operations that must execute before this one for control flow reasons)
|
||||
*/
|
||||
private List<String> controlDependencies = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Operations that are part of the same loop
|
||||
*/
|
||||
private List<String> loopPeers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Add an input dependency
|
||||
*
|
||||
* @param inputName name of the input variable
|
||||
* @param producerOperation name of the operation that produces this input
|
||||
*/
|
||||
public void addInputDependency(String inputName, String producerOperation) {
|
||||
inputDependencies.put(inputName, producerOperation);
|
||||
if (!predecessors.contains(producerOperation)) {
|
||||
predecessors.add(producerOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an output dependency
|
||||
*
|
||||
* @param outputName name of the output variable
|
||||
* @param consumerOperation name of the operation that consumes this output
|
||||
*/
|
||||
public void addOutputDependency(String outputName, String consumerOperation) {
|
||||
outputDependencies.computeIfAbsent(outputName, k -> new ArrayList<>()).add(consumerOperation);
|
||||
if (!successors.contains(consumerOperation)) {
|
||||
successors.add(consumerOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a control dependency
|
||||
*
|
||||
* @param controlOperation operation that must execute before this one
|
||||
*/
|
||||
public void addControlDependency(String controlOperation) {
|
||||
if (!controlDependencies.contains(controlOperation)) {
|
||||
controlDependencies.add(controlOperation);
|
||||
}
|
||||
if (!predecessors.contains(controlOperation)) {
|
||||
predecessors.add(controlOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a sibling operation (in same frame/scope)
|
||||
*
|
||||
* @param siblingOperation name of the sibling operation
|
||||
*/
|
||||
public void addSibling(String siblingOperation) {
|
||||
if (!siblings.contains(siblingOperation)) {
|
||||
siblings.add(siblingOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a loop peer operation
|
||||
*
|
||||
* @param loopPeerOperation name of the operation in the same loop
|
||||
*/
|
||||
public void addLoopPeer(String loopPeerOperation) {
|
||||
if (!loopPeers.contains(loopPeerOperation)) {
|
||||
loopPeers.add(loopPeerOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the producer operation for a specific input
|
||||
*
|
||||
* @param inputName name of the input variable
|
||||
* @return name of the producer operation, or null if not found
|
||||
*/
|
||||
public String getInputProducer(String inputName) {
|
||||
return inputDependencies.get(inputName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all consumer operations for a specific output
|
||||
*
|
||||
* @param outputName name of the output variable
|
||||
* @return list of consumer operation names
|
||||
*/
|
||||
public List<String> getOutputConsumers(String outputName) {
|
||||
return outputDependencies.getOrDefault(outputName, new ArrayList<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this operation has input dependencies
|
||||
*
|
||||
* @return true if there are input dependencies
|
||||
*/
|
||||
public boolean hasInputDependencies() {
|
||||
return !inputDependencies.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this operation has output dependencies
|
||||
*
|
||||
* @return true if there are output dependencies
|
||||
*/
|
||||
public boolean hasOutputDependencies() {
|
||||
return !outputDependencies.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this operation has control dependencies
|
||||
*
|
||||
* @return true if there are control dependencies
|
||||
*/
|
||||
public boolean hasControlDependencies() {
|
||||
return !controlDependencies.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total number of dependencies
|
||||
*
|
||||
* @return total count of all dependencies
|
||||
*/
|
||||
public int getTotalDependencyCount() {
|
||||
return predecessors.size() + successors.size() + controlDependencies.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if operation depends on another operation
|
||||
*
|
||||
* @param operationName name of the other operation
|
||||
* @return true if this operation depends on the other operation
|
||||
*/
|
||||
public boolean dependsOn(String operationName) {
|
||||
return predecessors.contains(operationName) ||
|
||||
controlDependencies.contains(operationName) ||
|
||||
inputDependencies.containsValue(operationName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if another operation depends on this operation
|
||||
*
|
||||
* @param operationName name of the other operation
|
||||
* @return true if the other operation depends on this operation
|
||||
*/
|
||||
public boolean isDependedOnBy(String operationName) {
|
||||
return successors.contains(operationName) ||
|
||||
outputDependencies.values().stream().anyMatch(consumers -> consumers.contains(operationName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all dependency information
|
||||
*/
|
||||
public void clearDependencies() {
|
||||
inputDependencies.clear();
|
||||
outputDependencies.clear();
|
||||
predecessors.clear();
|
||||
successors.clear();
|
||||
siblings.clear();
|
||||
controlDependencies.clear();
|
||||
loopPeers.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dependency summary as formatted string
|
||||
*
|
||||
* @return formatted dependency summary
|
||||
*/
|
||||
public String getDependencySummary() {
|
||||
StringBuilder summary = new StringBuilder();
|
||||
summary.append("Dependencies: ");
|
||||
summary.append("Inputs: ").append(inputDependencies.size());
|
||||
summary.append(", Outputs: ").append(outputDependencies.size());
|
||||
summary.append(", Control: ").append(controlDependencies.size());
|
||||
summary.append(", Predecessors: ").append(predecessors.size());
|
||||
summary.append(", Successors: ").append(successors.size());
|
||||
|
||||
if (!loopPeers.isEmpty()) {
|
||||
summary.append(", Loop peers: ").append(loopPeers.size());
|
||||
}
|
||||
|
||||
return summary.toString();
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Information about operation execution errors
|
||||
*/
|
||||
@Data
|
||||
public class OperationErrorInfo {
|
||||
/**
|
||||
* Error message describing what went wrong
|
||||
*/
|
||||
private String errorMessage;
|
||||
|
||||
/**
|
||||
* Type/class of the error that occurred
|
||||
*/
|
||||
private String errorType;
|
||||
|
||||
/**
|
||||
* Timestamp when the error occurred
|
||||
*/
|
||||
private long timestamp;
|
||||
|
||||
/**
|
||||
* Full stack trace of the error
|
||||
*/
|
||||
private String stackTrace;
|
||||
|
||||
/**
|
||||
* Context information when the error occurred
|
||||
*/
|
||||
private String errorContext;
|
||||
|
||||
/**
|
||||
* Whether this error is recoverable
|
||||
*/
|
||||
private boolean recoverable = false;
|
||||
|
||||
/**
|
||||
* Suggested recovery actions
|
||||
*/
|
||||
private String recoveryActions;
|
||||
|
||||
/**
|
||||
* Error severity level
|
||||
*/
|
||||
private ErrorSeverity severity = ErrorSeverity.ERROR;
|
||||
|
||||
/**
|
||||
* Additional error metadata
|
||||
*/
|
||||
private java.util.Map<String, Object> errorMetadata = new java.util.HashMap<>();
|
||||
|
||||
/**
|
||||
* Constructor for basic error info
|
||||
*/
|
||||
public OperationErrorInfo(String errorMessage, String errorType) {
|
||||
this.errorMessage = errorMessage;
|
||||
this.errorType = errorType;
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public OperationErrorInfo() {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumeration of error severity levels
|
||||
*/
|
||||
public enum ErrorSeverity {
|
||||
WARNING,
|
||||
ERROR,
|
||||
CRITICAL,
|
||||
FATAL
|
||||
}
|
||||
}
|
||||
+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.nd4j.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Record of a single operation execution
|
||||
*/
|
||||
@Data
|
||||
public class OperationExecutionRecord {
|
||||
/**
|
||||
* Timestamp when execution occurred
|
||||
*/
|
||||
private long timestamp;
|
||||
|
||||
/**
|
||||
* Execution time in nanoseconds
|
||||
*/
|
||||
private long executionTime;
|
||||
|
||||
/**
|
||||
* Status of the execution
|
||||
*/
|
||||
private OperationExecutionStatus status;
|
||||
|
||||
/**
|
||||
* Number of input variables
|
||||
*/
|
||||
private int inputCount;
|
||||
|
||||
/**
|
||||
* Number of output variables
|
||||
*/
|
||||
private int outputCount;
|
||||
|
||||
/**
|
||||
* Loop iteration when this execution occurred (if applicable)
|
||||
*/
|
||||
private int iteration = -1;
|
||||
|
||||
/**
|
||||
* Frame name when this execution occurred (if applicable)
|
||||
*/
|
||||
private String frame;
|
||||
|
||||
/**
|
||||
* Error message if execution failed
|
||||
*/
|
||||
private String errorMessage;
|
||||
|
||||
/**
|
||||
* Memory usage during execution (in bytes)
|
||||
*/
|
||||
private long memoryUsage = 0;
|
||||
|
||||
/**
|
||||
* Additional execution context information
|
||||
*/
|
||||
private java.util.Map<String, Object> executionContext = new java.util.HashMap<>();
|
||||
|
||||
/**
|
||||
* Constructor for successful execution
|
||||
*/
|
||||
public OperationExecutionRecord(long timestamp, long executionTime, OperationExecutionStatus status) {
|
||||
this.timestamp = timestamp;
|
||||
this.executionTime = executionTime;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for failed execution
|
||||
*/
|
||||
public OperationExecutionRecord(long timestamp, long executionTime, OperationExecutionStatus status, String errorMessage) {
|
||||
this(timestamp, executionTime, status);
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public OperationExecutionRecord() {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get execution time in milliseconds
|
||||
*
|
||||
* @return execution time in milliseconds
|
||||
*/
|
||||
public double getExecutionTimeMs() {
|
||||
return executionTime / 1_000_000.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this execution was successful
|
||||
*
|
||||
* @return true if execution was successful
|
||||
*/
|
||||
public boolean isSuccessful() {
|
||||
return status == OperationExecutionStatus.SUCCESS || status == OperationExecutionStatus.ANALYZED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this execution had errors
|
||||
*
|
||||
* @return true if execution had errors
|
||||
*/
|
||||
public boolean hasError() {
|
||||
return status == OperationExecutionStatus.ERROR || errorMessage != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add context information
|
||||
*
|
||||
* @param key context key
|
||||
* @param value context value
|
||||
*/
|
||||
public void addContext(String key, Object value) {
|
||||
executionContext.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get context information
|
||||
*
|
||||
* @param key context key
|
||||
* @return context value or null if not found
|
||||
*/
|
||||
public Object getContext(String key) {
|
||||
return executionContext.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get formatted summary of this execution record
|
||||
*
|
||||
* @return formatted summary string
|
||||
*/
|
||||
public String getSummary() {
|
||||
StringBuilder summary = new StringBuilder();
|
||||
summary.append("Execution at ").append(new java.util.Date(timestamp));
|
||||
summary.append(": ").append(status);
|
||||
summary.append(" (").append(String.format("%.2f", getExecutionTimeMs())).append("ms)");
|
||||
|
||||
if (frame != null && iteration >= 0) {
|
||||
summary.append(" [").append(frame).append(":").append(iteration).append("]");
|
||||
}
|
||||
|
||||
if (hasError()) {
|
||||
summary.append(" ERROR: ").append(errorMessage);
|
||||
}
|
||||
|
||||
return summary.toString();
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
/**
|
||||
* Enumeration of possible operation execution statuses
|
||||
*/
|
||||
public enum OperationExecutionStatus {
|
||||
/**
|
||||
* Operation has not been executed yet
|
||||
*/
|
||||
NOT_EXECUTED,
|
||||
|
||||
/**
|
||||
* Operation is currently being executed
|
||||
*/
|
||||
EXECUTING,
|
||||
|
||||
/**
|
||||
* Operation executed successfully
|
||||
*/
|
||||
SUCCESS,
|
||||
|
||||
/**
|
||||
* Operation was analyzed (values captured but not necessarily executed)
|
||||
*/
|
||||
ANALYZED,
|
||||
|
||||
/**
|
||||
* Operation execution failed with an error
|
||||
*/
|
||||
ERROR,
|
||||
|
||||
/**
|
||||
* Operation was skipped during execution
|
||||
*/
|
||||
SKIPPED,
|
||||
|
||||
/**
|
||||
* Operation execution was cancelled
|
||||
*/
|
||||
CANCELLED,
|
||||
|
||||
/**
|
||||
* Operation execution timed out
|
||||
*/
|
||||
TIMEOUT,
|
||||
|
||||
/**
|
||||
* Operation status is unknown
|
||||
*/
|
||||
UNKNOWN
|
||||
}
|
||||
+646
@@ -0,0 +1,646 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nd4j.autodiff.samediff.config.SDValue;
|
||||
import org.nd4j.autodiff.samediff.internal.FrameIter;
|
||||
import org.nd4j.autodiff.samediff.internal.SameDiffOp;
|
||||
import org.nd4j.autodiff.samediff.internal.VarId;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Enhanced OperationInfo class for comprehensive operation analysis and error reporting.
|
||||
*
|
||||
* This class provides detailed information about operations including their execution context,
|
||||
* input/output analysis, error conditions, and integration with loop termination analysis.
|
||||
*/
|
||||
@Data
|
||||
@Slf4j
|
||||
public class OperationInfo {
|
||||
|
||||
// === BASIC OPERATION INFORMATION ===
|
||||
|
||||
/**
|
||||
* The unique name of the operation
|
||||
*/
|
||||
private final String operationName;
|
||||
|
||||
/**
|
||||
* The type of operation (e.g., "Add", "MatMul", "LoopCond")
|
||||
*/
|
||||
private final String operationType;
|
||||
|
||||
/**
|
||||
* The full class name of the operation
|
||||
*/
|
||||
private final String className;
|
||||
|
||||
/**
|
||||
* List of input variable names
|
||||
*/
|
||||
private final List<String> inputs;
|
||||
|
||||
/**
|
||||
* List of output variable names
|
||||
*/
|
||||
private final List<String> outputs;
|
||||
|
||||
/**
|
||||
* Frame information for this operation
|
||||
*/
|
||||
private FrameInfo frameInfo;
|
||||
|
||||
// === ENHANCED EXECUTION CONTEXT ===
|
||||
|
||||
/**
|
||||
* Current input values at the time of analysis
|
||||
*/
|
||||
private Map<String, Object> inputValues = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Current output values at the time of analysis
|
||||
*/
|
||||
private Map<String, Object> outputValues = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Execution status of this operation
|
||||
*/
|
||||
private OperationExecutionStatus executionStatus = OperationExecutionStatus.UNKNOWN;
|
||||
|
||||
/**
|
||||
* Error information if operation failed
|
||||
*/
|
||||
private OperationErrorInfo errorInfo;
|
||||
|
||||
/**
|
||||
* Execution timing information
|
||||
*/
|
||||
private OperationTimingInfo timingInfo = new OperationTimingInfo();
|
||||
|
||||
/**
|
||||
* Memory usage information for this operation
|
||||
*/
|
||||
private OperationMemoryInfo memoryInfo = new OperationMemoryInfo();
|
||||
|
||||
/**
|
||||
* Operation-specific metadata
|
||||
*/
|
||||
private Map<String, Object> metadata = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Execution history for this operation
|
||||
*/
|
||||
private List<OperationExecutionRecord> executionHistory = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Dependencies and relationships
|
||||
*/
|
||||
private OperationDependencyInfo dependencyInfo = new OperationDependencyInfo();
|
||||
|
||||
// === LOOP-SPECIFIC INFORMATION ===
|
||||
|
||||
/**
|
||||
* Role of this operation in loop control flow
|
||||
*/
|
||||
private LoopOperationRole loopRole = LoopOperationRole.REGULAR;
|
||||
|
||||
/**
|
||||
* Loop iteration context when this operation was analyzed
|
||||
*/
|
||||
private FrameIter loopContext;
|
||||
|
||||
/**
|
||||
* Whether this operation is critical for loop termination
|
||||
*/
|
||||
private boolean isTerminationCritical = false;
|
||||
|
||||
/**
|
||||
* Loop-specific execution patterns
|
||||
*/
|
||||
private Map<Integer, Object> iterationResults = new HashMap<>();
|
||||
|
||||
// === CONSTRUCTORS ===
|
||||
|
||||
/**
|
||||
* Basic constructor maintaining backward compatibility
|
||||
*/
|
||||
public OperationInfo(String name, String opType, String className, List<String> inputs, List<String> outputs) {
|
||||
this.operationName = name;
|
||||
this.operationType = opType;
|
||||
this.className = className;
|
||||
this.inputs = inputs != null ? new ArrayList<>(inputs) : new ArrayList<>();
|
||||
this.outputs = outputs != null ? new ArrayList<>(outputs) : new ArrayList<>();
|
||||
|
||||
// Initialize enhanced properties
|
||||
initializeDefaults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhanced constructor with frame context
|
||||
*/
|
||||
public OperationInfo(String name, String opType, String className, List<String> inputs, List<String> outputs,
|
||||
FrameInfo frameInfo) {
|
||||
this(name, opType, className, inputs, outputs);
|
||||
this.frameInfo = frameInfo;
|
||||
|
||||
// Determine loop role based on operation type
|
||||
this.loopRole = OperationAnalysisUtils.determineLoopRole(opType);
|
||||
this.isTerminationCritical = OperationAnalysisUtils.isTerminationCriticalOperation(opType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Full constructor with execution context
|
||||
*/
|
||||
public OperationInfo(String name, String opType, String className, List<String> inputs, List<String> outputs,
|
||||
FrameInfo frameInfo, FrameIter loopContext) {
|
||||
this(name, opType, className, inputs, outputs, frameInfo);
|
||||
this.loopContext = loopContext;
|
||||
}
|
||||
|
||||
// === INITIALIZATION ===
|
||||
|
||||
/**
|
||||
* Initialize default values
|
||||
*/
|
||||
private void initializeDefaults() {
|
||||
this.metadata.put("created_at", System.currentTimeMillis());
|
||||
this.metadata.put("analysis_version", "2.0");
|
||||
|
||||
// Initialize loop role
|
||||
this.loopRole = OperationAnalysisUtils.determineLoopRole(operationType);
|
||||
this.isTerminationCritical = OperationAnalysisUtils.isTerminationCriticalOperation(operationType);
|
||||
}
|
||||
|
||||
// === ANALYSIS METHODS ===
|
||||
|
||||
/**
|
||||
* Analyze operation with current SameDiff state
|
||||
*/
|
||||
public void analyzeWithCurrentState(SameDiff sameDiff, Map<VarId, SDValue> nodeValueOutputs) {
|
||||
analyzeWithCurrentState(sameDiff, nodeValueOutputs, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze operation with current SameDiff state and specific frame context
|
||||
*/
|
||||
public void analyzeWithCurrentState(SameDiff sameDiff, Map<VarId, SDValue> nodeValueOutputs,
|
||||
FrameIter frameContext) {
|
||||
long startTime = System.nanoTime();
|
||||
|
||||
try {
|
||||
// Update loop context if provided
|
||||
if (frameContext != null) {
|
||||
this.loopContext = frameContext;
|
||||
}
|
||||
|
||||
// Analyze input values
|
||||
analyzeInputValues(nodeValueOutputs, frameContext);
|
||||
|
||||
// Analyze output values
|
||||
analyzeOutputValues(nodeValueOutputs, frameContext);
|
||||
|
||||
// Analyze operation dependencies
|
||||
analyzeDependencies(sameDiff);
|
||||
|
||||
// Update memory usage
|
||||
memoryInfo.updateTotalMemoryUsage();
|
||||
|
||||
// Record execution
|
||||
recordExecution(startTime, OperationExecutionStatus.ANALYZED);
|
||||
|
||||
} catch (Exception e) {
|
||||
recordError(e, startTime);
|
||||
log.warn("Error analyzing operation '{}': {}", operationName, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze input values from node outputs
|
||||
*/
|
||||
private void analyzeInputValues(Map<VarId, SDValue> nodeValueOutputs, FrameIter frameContext) {
|
||||
inputValues.clear();
|
||||
|
||||
for (String inputName : inputs) {
|
||||
try {
|
||||
VarId varId = OperationAnalysisUtils.createVarId(inputName, frameContext, frameInfo);
|
||||
SDValue value = nodeValueOutputs.get(varId);
|
||||
|
||||
if (value != null) {
|
||||
Object extractedValue = OperationAnalysisUtils.extractValueForAnalysis(value);
|
||||
inputValues.put(inputName, extractedValue);
|
||||
|
||||
// Update memory usage
|
||||
long memUsage = OperationAnalysisUtils.estimateValueMemoryUsage(value);
|
||||
memoryInfo.addInputMemoryUsage(inputName, memUsage);
|
||||
} else {
|
||||
inputValues.put(inputName, null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("Could not analyze input '{}' for operation '{}': {}", inputName, operationName, e.getMessage());
|
||||
inputValues.put(inputName, "ERROR: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze output values from node outputs
|
||||
*/
|
||||
private void analyzeOutputValues(Map<VarId, SDValue> nodeValueOutputs, FrameIter frameContext) {
|
||||
outputValues.clear();
|
||||
|
||||
for (String outputName : outputs) {
|
||||
try {
|
||||
VarId varId = OperationAnalysisUtils.createVarId(outputName, frameContext, frameInfo);
|
||||
SDValue value = nodeValueOutputs.get(varId);
|
||||
|
||||
if (value != null) {
|
||||
Object extractedValue = OperationAnalysisUtils.extractValueForAnalysis(value);
|
||||
outputValues.put(outputName, extractedValue);
|
||||
|
||||
// Update memory usage
|
||||
long memUsage = OperationAnalysisUtils.estimateValueMemoryUsage(value);
|
||||
memoryInfo.addOutputMemoryUsage(outputName, memUsage);
|
||||
|
||||
// Store iteration result for loop analysis
|
||||
if (loopContext != null && isTerminationCritical) {
|
||||
iterationResults.put(loopContext.getIteration(), extractedValue);
|
||||
}
|
||||
} else {
|
||||
outputValues.put(outputName, null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("Could not analyze output '{}' for operation '{}': {}", outputName, operationName, e.getMessage());
|
||||
outputValues.put(outputName, "ERROR: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze operation dependencies
|
||||
*/
|
||||
private void analyzeDependencies(SameDiff sameDiff) {
|
||||
dependencyInfo.clearDependencies();
|
||||
|
||||
// Find operations that produce our inputs
|
||||
for (String inputName : inputs) {
|
||||
SameDiffOp producerOp = OperationAnalysisUtils.findProducerOperation(sameDiff, inputName);
|
||||
if (producerOp != null) {
|
||||
dependencyInfo.addInputDependency(inputName, producerOp.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// Find operations that consume our outputs
|
||||
for (String outputName : outputs) {
|
||||
List<SameDiffOp> consumerOps = OperationAnalysisUtils.findConsumerOperations(sameDiff, outputName);
|
||||
for (SameDiffOp consumerOp : consumerOps) {
|
||||
dependencyInfo.addOutputDependency(outputName, consumerOp.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record successful execution
|
||||
*/
|
||||
private void recordExecution(long startTime, OperationExecutionStatus status) {
|
||||
long endTime = System.nanoTime();
|
||||
long executionTime = endTime - startTime;
|
||||
|
||||
this.executionStatus = status;
|
||||
this.timingInfo.addExecutionTime(executionTime);
|
||||
|
||||
OperationExecutionRecord record = new OperationExecutionRecord();
|
||||
record.setTimestamp(System.currentTimeMillis());
|
||||
record.setExecutionTime(executionTime);
|
||||
record.setStatus(status);
|
||||
record.setInputCount(inputs.size());
|
||||
record.setOutputCount(outputs.size());
|
||||
|
||||
if (loopContext != null) {
|
||||
record.setIteration(loopContext.getIteration());
|
||||
record.setFrame(loopContext.getFrame());
|
||||
}
|
||||
|
||||
executionHistory.add(record);
|
||||
|
||||
// Keep history limited
|
||||
if (executionHistory.size() > 100) {
|
||||
executionHistory.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record execution error
|
||||
*/
|
||||
private void recordError(Exception error, long startTime) {
|
||||
long endTime = System.nanoTime();
|
||||
long executionTime = endTime - startTime;
|
||||
|
||||
this.executionStatus = OperationExecutionStatus.ERROR;
|
||||
|
||||
this.errorInfo = new OperationErrorInfo();
|
||||
this.errorInfo.setErrorMessage(error.getMessage());
|
||||
this.errorInfo.setErrorType(error.getClass().getSimpleName());
|
||||
this.errorInfo.setTimestamp(System.currentTimeMillis());
|
||||
this.errorInfo.setStackTrace(getStackTrace(error));
|
||||
|
||||
OperationExecutionRecord record = new OperationExecutionRecord();
|
||||
record.setTimestamp(System.currentTimeMillis());
|
||||
record.setExecutionTime(executionTime);
|
||||
record.setStatus(OperationExecutionStatus.ERROR);
|
||||
record.setErrorMessage(error.getMessage());
|
||||
|
||||
if (loopContext != null) {
|
||||
record.setIteration(loopContext.getIteration());
|
||||
record.setFrame(loopContext.getFrame());
|
||||
}
|
||||
|
||||
executionHistory.add(record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stack trace as string
|
||||
*/
|
||||
private String getStackTrace(Exception e) {
|
||||
java.io.StringWriter sw = new java.io.StringWriter();
|
||||
java.io.PrintWriter pw = new java.io.PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
// === QUERY METHODS ===
|
||||
|
||||
/**
|
||||
* Check if this operation is a loop control operation
|
||||
*/
|
||||
public boolean isLoopControlOperation() {
|
||||
return loopRole != LoopOperationRole.REGULAR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this operation has execution errors
|
||||
*/
|
||||
public boolean hasExecutionErrors() {
|
||||
return executionStatus == OperationExecutionStatus.ERROR || errorInfo != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the most recent execution record
|
||||
*/
|
||||
public OperationExecutionRecord getLatestExecutionRecord() {
|
||||
if (executionHistory.isEmpty()) return null;
|
||||
return executionHistory.get(executionHistory.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get execution records for a specific iteration (loop context)
|
||||
*/
|
||||
public List<OperationExecutionRecord> getExecutionRecordsForIteration(int iteration) {
|
||||
return executionHistory.stream()
|
||||
.filter(record -> record.getIteration() == iteration)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get input value for a specific input name
|
||||
*/
|
||||
public Object getInputValue(String inputName) {
|
||||
return inputValues.get(inputName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get output value for a specific output name
|
||||
*/
|
||||
public Object getOutputValue(String outputName) {
|
||||
return outputValues.get(outputName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if operation has problematic values (NaN, Inf, etc.)
|
||||
*/
|
||||
public boolean hasProblematicValues() {
|
||||
// Check input values
|
||||
for (Object value : inputValues.values()) {
|
||||
if (OperationAnalysisUtils.isProblematicValue(value)) return true;
|
||||
}
|
||||
|
||||
// Check output values
|
||||
for (Object value : outputValues.values()) {
|
||||
if (OperationAnalysisUtils.isProblematicValue(value)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get problematic value details
|
||||
*/
|
||||
public List<String> getProblematicValueDetails() {
|
||||
List<String> details = new ArrayList<>();
|
||||
|
||||
// Check inputs
|
||||
for (Map.Entry<String, Object> entry : inputValues.entrySet()) {
|
||||
if (OperationAnalysisUtils.isProblematicValue(entry.getValue())) {
|
||||
details.add("Input '" + entry.getKey() + "': " +
|
||||
OperationAnalysisUtils.describeProblem(entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
// Check outputs
|
||||
for (Map.Entry<String, Object> entry : outputValues.entrySet()) {
|
||||
if (OperationAnalysisUtils.isProblematicValue(entry.getValue())) {
|
||||
details.add("Output '" + entry.getKey() + "': " +
|
||||
OperationAnalysisUtils.describeProblem(entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get average execution time
|
||||
*/
|
||||
public double getAverageExecutionTime() {
|
||||
return timingInfo.getAverageExecutionTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total memory usage
|
||||
*/
|
||||
public long getTotalMemoryUsage() {
|
||||
return memoryInfo.getTotalMemoryUsage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if operation execution time is abnormally high
|
||||
*/
|
||||
public boolean hasAbnormalExecutionTime() {
|
||||
return timingInfo.isAbnormalExecutionTime(3.0); // 3x average threshold
|
||||
}
|
||||
|
||||
/**
|
||||
* Get execution summary for loop analysis
|
||||
*/
|
||||
public String getExecutionSummary() {
|
||||
StringBuilder summary = new StringBuilder();
|
||||
summary.append("Operation: ").append(operationName);
|
||||
summary.append(" (").append(operationType).append(")");
|
||||
summary.append(" [").append(loopRole).append("]");
|
||||
|
||||
if (loopContext != null) {
|
||||
summary.append(" Frame: ").append(loopContext.getFrame());
|
||||
summary.append(" Iter: ").append(loopContext.getIteration());
|
||||
}
|
||||
|
||||
summary.append(" Status: ").append(executionStatus);
|
||||
|
||||
if (hasExecutionErrors()) {
|
||||
summary.append(" ERROR: ").append(errorInfo.getErrorMessage());
|
||||
}
|
||||
|
||||
if (hasProblematicValues()) {
|
||||
summary.append(" [PROBLEMATIC VALUES]");
|
||||
}
|
||||
|
||||
return summary.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate detailed analysis report
|
||||
*/
|
||||
public String generateDetailedReport() {
|
||||
StringBuilder report = new StringBuilder();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
report.append("=== OPERATION ANALYSIS REPORT ===\n");
|
||||
report.append("Name: ").append(operationName).append("\n");
|
||||
report.append("Type: ").append(operationType).append("\n");
|
||||
report.append("Class: ").append(className).append("\n");
|
||||
report.append("Loop Role: ").append(loopRole).append("\n");
|
||||
report.append("Termination Critical: ").append(isTerminationCritical).append("\n");
|
||||
report.append("Status: ").append(executionStatus).append("\n");
|
||||
|
||||
if (loopContext != null) {
|
||||
report.append("Frame: ").append(loopContext.getFrame()).append("\n");
|
||||
report.append("Iteration: ").append(loopContext.getIteration()).append("\n");
|
||||
}
|
||||
|
||||
// Input/Output summary
|
||||
report.append("\nInputs (").append(inputs.size()).append("): ").append(inputs).append("\n");
|
||||
report.append("Outputs (").append(outputs.size()).append("): ").append(outputs).append("\n");
|
||||
|
||||
// Values (if available)
|
||||
if (!inputValues.isEmpty()) {
|
||||
report.append("\nInput Values:\n");
|
||||
for (Map.Entry<String, Object> entry : inputValues.entrySet()) {
|
||||
report.append(" ").append(entry.getKey()).append(" = ")
|
||||
.append(OperationAnalysisUtils.formatValue(entry.getValue())).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (!outputValues.isEmpty()) {
|
||||
report.append("\nOutput Values:\n");
|
||||
for (Map.Entry<String, Object> entry : outputValues.entrySet()) {
|
||||
report.append(" ").append(entry.getKey()).append(" = ")
|
||||
.append(OperationAnalysisUtils.formatValue(entry.getValue())).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Error information
|
||||
if (hasExecutionErrors()) {
|
||||
report.append("\nERROR INFORMATION:\n");
|
||||
report.append("Error Type: ").append(errorInfo.getErrorType()).append("\n");
|
||||
report.append("Error Message: ").append(errorInfo.getErrorMessage()).append("\n");
|
||||
report.append("Error Time: ").append(
|
||||
LocalDateTime.ofInstant(java.time.Instant.ofEpochMilli(errorInfo.getTimestamp()),
|
||||
java.time.ZoneId.systemDefault()).format(formatter)).append("\n");
|
||||
}
|
||||
|
||||
// Performance metrics
|
||||
report.append("\nPERFORMANCE METRICS:\n");
|
||||
report.append("Executions: ").append(executionHistory.size()).append("\n");
|
||||
report.append(timingInfo.getTimingStatistics()).append("\n");
|
||||
report.append(memoryInfo.getMemoryUsageSummary()).append("\n");
|
||||
|
||||
// Problematic values
|
||||
List<String> problems = getProblematicValueDetails();
|
||||
if (!problems.isEmpty()) {
|
||||
report.append("\nPROBLEMATIC VALUES:\n");
|
||||
for (String problem : problems) {
|
||||
report.append(" ⚠️ ").append(problem).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Dependencies
|
||||
if (dependencyInfo.hasInputDependencies() || dependencyInfo.hasOutputDependencies()) {
|
||||
report.append("\nDEPENDENCIES:\n");
|
||||
report.append(dependencyInfo.getDependencySummary()).append("\n");
|
||||
}
|
||||
|
||||
return report.toString();
|
||||
}
|
||||
|
||||
// === DEPRECATED COMPATIBILITY ===
|
||||
|
||||
/**
|
||||
* @deprecated Use operationName instead
|
||||
*/
|
||||
@Deprecated
|
||||
public String getName() {
|
||||
return operationName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use operationType instead
|
||||
*/
|
||||
@Deprecated
|
||||
public String getOpType() {
|
||||
return operationType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Access frameInfo directly
|
||||
*/
|
||||
@Deprecated
|
||||
public FrameInfo getFrameInfo() {
|
||||
return frameInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use inputs field directly
|
||||
*/
|
||||
@Deprecated
|
||||
public List<String> getInputs() {
|
||||
return inputs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use outputs field directly
|
||||
*/
|
||||
@Deprecated
|
||||
public List<String> getOutputs() {
|
||||
return outputs;
|
||||
}
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Memory usage information for operations
|
||||
*/
|
||||
@Data
|
||||
public class OperationMemoryInfo {
|
||||
/**
|
||||
* Memory usage for each input variable (in bytes)
|
||||
*/
|
||||
private Map<String, Long> inputMemoryUsage = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Memory usage for each output variable (in bytes)
|
||||
*/
|
||||
private Map<String, Long> outputMemoryUsage = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Total memory usage for this operation (in bytes)
|
||||
*/
|
||||
private long totalMemoryUsage = 0;
|
||||
|
||||
/**
|
||||
* Peak memory usage observed (in bytes)
|
||||
*/
|
||||
private long peakMemoryUsage = 0;
|
||||
|
||||
/**
|
||||
* Memory allocated during operation execution (in bytes)
|
||||
*/
|
||||
private long allocatedMemory = 0;
|
||||
|
||||
/**
|
||||
* Memory deallocated after operation execution (in bytes)
|
||||
*/
|
||||
private long deallocatedMemory = 0;
|
||||
|
||||
/**
|
||||
* Timestamp when memory usage was last updated
|
||||
*/
|
||||
private long lastUpdated = 0;
|
||||
|
||||
/**
|
||||
* Add memory usage for an input variable
|
||||
*
|
||||
* @param inputName name of the input variable
|
||||
* @param memoryUsage memory usage in bytes
|
||||
*/
|
||||
public void addInputMemoryUsage(String inputName, long memoryUsage) {
|
||||
inputMemoryUsage.put(inputName, memoryUsage);
|
||||
updateTotalMemoryUsage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add memory usage for an output variable
|
||||
*
|
||||
* @param outputName name of the output variable
|
||||
* @param memoryUsage memory usage in bytes
|
||||
*/
|
||||
public void addOutputMemoryUsage(String outputName, long memoryUsage) {
|
||||
outputMemoryUsage.put(outputName, memoryUsage);
|
||||
updateTotalMemoryUsage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update total memory usage calculation
|
||||
*/
|
||||
public void updateTotalMemoryUsage() {
|
||||
long inputTotal = inputMemoryUsage.values().stream().mapToLong(Long::longValue).sum();
|
||||
long outputTotal = outputMemoryUsage.values().stream().mapToLong(Long::longValue).sum();
|
||||
|
||||
totalMemoryUsage = inputTotal + outputTotal + allocatedMemory - deallocatedMemory;
|
||||
peakMemoryUsage = Math.max(peakMemoryUsage, totalMemoryUsage);
|
||||
lastUpdated = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Record memory allocation during operation
|
||||
*
|
||||
* @param allocated bytes allocated
|
||||
*/
|
||||
public void recordAllocation(long allocated) {
|
||||
this.allocatedMemory += allocated;
|
||||
updateTotalMemoryUsage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Record memory deallocation during operation
|
||||
*
|
||||
* @param deallocated bytes deallocated
|
||||
*/
|
||||
public void recordDeallocation(long deallocated) {
|
||||
this.deallocatedMemory += deallocated;
|
||||
updateTotalMemoryUsage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total input memory usage
|
||||
*
|
||||
* @return total memory usage of all inputs in bytes
|
||||
*/
|
||||
public long getTotalInputMemoryUsage() {
|
||||
return inputMemoryUsage.values().stream().mapToLong(Long::longValue).sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total output memory usage
|
||||
*
|
||||
* @return total memory usage of all outputs in bytes
|
||||
*/
|
||||
public long getTotalOutputMemoryUsage() {
|
||||
return outputMemoryUsage.values().stream().mapToLong(Long::longValue).sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get memory usage for a specific input
|
||||
*
|
||||
* @param inputName name of the input variable
|
||||
* @return memory usage in bytes, or 0 if not found
|
||||
*/
|
||||
public long getInputMemoryUsage(String inputName) {
|
||||
return inputMemoryUsage.getOrDefault(inputName, 0L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get memory usage for a specific output
|
||||
*
|
||||
* @param outputName name of the output variable
|
||||
* @return memory usage in bytes, or 0 if not found
|
||||
*/
|
||||
public long getOutputMemoryUsage(String outputName) {
|
||||
return outputMemoryUsage.getOrDefault(outputName, 0L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if memory usage is considered high
|
||||
*
|
||||
* @param thresholdBytes threshold in bytes
|
||||
* @return true if total memory usage exceeds threshold
|
||||
*/
|
||||
public boolean isHighMemoryUsage(long thresholdBytes) {
|
||||
return totalMemoryUsage > thresholdBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get memory usage in MB
|
||||
*
|
||||
* @return total memory usage in megabytes
|
||||
*/
|
||||
public double getTotalMemoryUsageMB() {
|
||||
return totalMemoryUsage / (1024.0 * 1024.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get peak memory usage in MB
|
||||
*
|
||||
* @return peak memory usage in megabytes
|
||||
*/
|
||||
public double getPeakMemoryUsageMB() {
|
||||
return peakMemoryUsage / (1024.0 * 1024.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get memory efficiency (ratio of current to peak usage)
|
||||
*
|
||||
* @return memory efficiency ratio (0.0 to 1.0)
|
||||
*/
|
||||
public double getMemoryEfficiency() {
|
||||
if (peakMemoryUsage == 0) return 1.0;
|
||||
return (double) totalMemoryUsage / peakMemoryUsage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get memory usage summary as formatted string
|
||||
*
|
||||
* @return formatted string with memory statistics
|
||||
*/
|
||||
public String getMemoryUsageSummary() {
|
||||
return String.format("Memory: %.2f MB (peak: %.2f MB), Inputs: %.2f MB, Outputs: %.2f MB",
|
||||
getTotalMemoryUsageMB(),
|
||||
getPeakMemoryUsageMB(),
|
||||
getTotalInputMemoryUsage() / (1024.0 * 1024.0),
|
||||
getTotalOutputMemoryUsage() / (1024.0 * 1024.0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all memory usage information
|
||||
*/
|
||||
public void reset() {
|
||||
inputMemoryUsage.clear();
|
||||
outputMemoryUsage.clear();
|
||||
totalMemoryUsage = 0;
|
||||
peakMemoryUsage = 0;
|
||||
allocatedMemory = 0;
|
||||
deallocatedMemory = 0;
|
||||
lastUpdated = 0;
|
||||
}
|
||||
}
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Timing information for operation execution
|
||||
*/
|
||||
@Data
|
||||
public class OperationTimingInfo {
|
||||
/**
|
||||
* List of execution times for this operation (in nanoseconds)
|
||||
*/
|
||||
private List<Long> executionTimes = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Total accumulated execution time (in nanoseconds)
|
||||
*/
|
||||
private long totalExecutionTime = 0;
|
||||
|
||||
/**
|
||||
* Minimum execution time observed (in nanoseconds)
|
||||
*/
|
||||
private long minExecutionTime = Long.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* Maximum execution time observed (in nanoseconds)
|
||||
*/
|
||||
private long maxExecutionTime = Long.MIN_VALUE;
|
||||
|
||||
/**
|
||||
* Number of times this operation has been executed
|
||||
*/
|
||||
private int executionCount = 0;
|
||||
|
||||
/**
|
||||
* Timestamp of first execution
|
||||
*/
|
||||
private long firstExecutionTime = 0;
|
||||
|
||||
/**
|
||||
* Timestamp of last execution
|
||||
*/
|
||||
private long lastExecutionTime = 0;
|
||||
|
||||
/**
|
||||
* Add a new execution time measurement
|
||||
*
|
||||
* @param executionTime execution time in nanoseconds
|
||||
*/
|
||||
public void addExecutionTime(long executionTime) {
|
||||
executionTimes.add(executionTime);
|
||||
totalExecutionTime += executionTime;
|
||||
|
||||
minExecutionTime = Math.min(minExecutionTime, executionTime);
|
||||
maxExecutionTime = Math.max(maxExecutionTime, executionTime);
|
||||
|
||||
executionCount++;
|
||||
lastExecutionTime = System.currentTimeMillis();
|
||||
|
||||
if (firstExecutionTime == 0) {
|
||||
firstExecutionTime = lastExecutionTime;
|
||||
}
|
||||
|
||||
// Keep only recent execution times to avoid memory issues
|
||||
if (executionTimes.size() > 1000) {
|
||||
executionTimes.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get average execution time in nanoseconds
|
||||
*
|
||||
* @return average execution time, or 0 if no executions recorded
|
||||
*/
|
||||
public double getAverageExecutionTime() {
|
||||
if (executionCount == 0) return 0.0;
|
||||
return (double) totalExecutionTime / executionCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get average execution time in milliseconds
|
||||
*
|
||||
* @return average execution time in milliseconds
|
||||
*/
|
||||
public double getAverageExecutionTimeMs() {
|
||||
return getAverageExecutionTime() / 1_000_000.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total execution time in milliseconds
|
||||
*
|
||||
* @return total execution time in milliseconds
|
||||
*/
|
||||
public double getTotalExecutionTimeMs() {
|
||||
return totalExecutionTime / 1_000_000.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get minimum execution time in milliseconds
|
||||
*
|
||||
* @return minimum execution time in milliseconds
|
||||
*/
|
||||
public double getMinExecutionTimeMs() {
|
||||
if (minExecutionTime == Long.MAX_VALUE) return 0.0;
|
||||
return minExecutionTime / 1_000_000.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get maximum execution time in milliseconds
|
||||
*
|
||||
* @return maximum execution time in milliseconds
|
||||
*/
|
||||
public double getMaxExecutionTimeMs() {
|
||||
if (maxExecutionTime == Long.MIN_VALUE) return 0.0;
|
||||
return maxExecutionTime / 1_000_000.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if execution time is abnormally high compared to average
|
||||
*
|
||||
* @param threshold multiplier for average (e.g., 3.0 for 3x average)
|
||||
* @return true if last execution was abnormally high
|
||||
*/
|
||||
public boolean isAbnormalExecutionTime(double threshold) {
|
||||
if (executionTimes.isEmpty()) return false;
|
||||
|
||||
double avgTime = getAverageExecutionTime();
|
||||
if (avgTime <= 0) return false;
|
||||
|
||||
long lastExecution = executionTimes.get(executionTimes.size() - 1);
|
||||
return lastExecution > avgTime * threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get execution time statistics as a summary string
|
||||
*
|
||||
* @return formatted string with timing statistics
|
||||
*/
|
||||
public String getTimingStatistics() {
|
||||
if (executionCount == 0) {
|
||||
return "No executions recorded";
|
||||
}
|
||||
|
||||
return String.format("Executions: %d, Avg: %.2fms, Min: %.2fms, Max: %.2fms, Total: %.2fms",
|
||||
executionCount,
|
||||
getAverageExecutionTimeMs(),
|
||||
getMinExecutionTimeMs(),
|
||||
getMaxExecutionTimeMs(),
|
||||
getTotalExecutionTimeMs());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all timing information
|
||||
*/
|
||||
public void reset() {
|
||||
executionTimes.clear();
|
||||
totalExecutionTime = 0;
|
||||
minExecutionTime = Long.MAX_VALUE;
|
||||
maxExecutionTime = Long.MIN_VALUE;
|
||||
executionCount = 0;
|
||||
firstExecutionTime = 0;
|
||||
lastExecutionTime = 0;
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package org.nd4j.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class PerformanceMetrics {
|
||||
private long totalMemory;
|
||||
private long freeMemory;
|
||||
private long usedMemory;
|
||||
private long maxMemory;
|
||||
private long totalVariableMemory;
|
||||
private long loopExecutionTime;
|
||||
private double averageIterationTime;
|
||||
private double iterationsPerSecond;
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package org.nd4j.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class RootCauseAnalysis {
|
||||
private String primaryCause;
|
||||
private List<String> contributingFactors = new ArrayList<>();
|
||||
private List<String> recommendedActions = new ArrayList<>();
|
||||
private List<String> similarPatternsInHistory = new ArrayList<>();
|
||||
private double confidenceLevel;
|
||||
}
|
||||
+324
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
import lombok.Getter;
|
||||
import org.nd4j.linalg.exception.ND4JIllegalArgumentException;
|
||||
|
||||
|
||||
/**
|
||||
* SDIndex is the {@link SameDiff}
|
||||
* equivalent to {@link org.nd4j.linalg.indexing.INDArrayIndex}
|
||||
* it uses {@link org.nd4j.linalg.api.ops.impl.shape.StridedSlice} underneath to obtain varying elements.
|
||||
* It also supports {@link SDVariable} inputs allowing for graph definitions of
|
||||
* indexing operations.
|
||||
*
|
||||
* @author Alex Black
|
||||
* @author Adam Gibson
|
||||
*/
|
||||
@Getter
|
||||
public class SDIndex {
|
||||
|
||||
/**
|
||||
* Index types include the following:
|
||||
* 1. all: get all elements of this dimension
|
||||
* 2. point: get only elements at the particular point in this dimension
|
||||
* 3. interval: get only elements from a begin point to an end point in the interval
|
||||
* 4. point input: dynamic version of point
|
||||
* 5. interval input: dynamic version of interval
|
||||
*/
|
||||
public enum IndexType {
|
||||
ALL,
|
||||
POINT,
|
||||
INTERVAL,
|
||||
//inputs aren't integers/longs but SDVariables
|
||||
POINT_INPUT,
|
||||
INTERVAL_INPUT
|
||||
}
|
||||
|
||||
private IndexType indexType = IndexType.ALL;
|
||||
private long pointIndex;
|
||||
|
||||
private SDVariable pointVar;
|
||||
|
||||
|
||||
private boolean pointKeepDim;
|
||||
private Long intervalBegin = null;
|
||||
private Long intervalEnd = null;
|
||||
|
||||
|
||||
private SDVariable intervalInputBegin = null;
|
||||
private SDVariable intervalInputEnd = null;
|
||||
private SDVariable intervalStrideInput = null;
|
||||
|
||||
private Long intervalStrides = 1l;
|
||||
|
||||
private boolean inclusive = false;
|
||||
|
||||
private SDVariable inclusiveInput = null;
|
||||
|
||||
|
||||
public SDIndex(){}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Represents all the elements in along this dimension.
|
||||
* @return
|
||||
*/
|
||||
public static SDIndex all(){
|
||||
return new SDIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents all elements at a singular point in this dimension (think row or column)
|
||||
* Note this is the SDVariable version. For static please use {@link #point(long)}
|
||||
* @param i the input index
|
||||
* @return
|
||||
*/
|
||||
public static SDIndex point(SDVariable i) {
|
||||
return point(i,false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents all elements at a singular point in this dimension (think row or column)
|
||||
* This is a static index
|
||||
* @param i the input index
|
||||
* @return
|
||||
*/
|
||||
public static SDIndex point(long i) {
|
||||
SDIndex sdIndex = new SDIndex();
|
||||
sdIndex.indexType = IndexType.POINT;
|
||||
sdIndex.pointIndex = i;
|
||||
sdIndex.pointKeepDim = false;
|
||||
return sdIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents all elements at a singular point in this dimension (think row or column)
|
||||
* This is a dynamic index
|
||||
* @param i the input index
|
||||
* @return
|
||||
*/
|
||||
public static SDIndex point(SDVariable i, boolean keepDim) {
|
||||
SDIndex sdIndex = new SDIndex();
|
||||
sdIndex.indexType = IndexType.POINT_INPUT;
|
||||
sdIndex.pointVar = i;
|
||||
sdIndex.pointKeepDim = keepDim;
|
||||
return sdIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents all elements at a singular point in this dimension (think row or column)
|
||||
* This is a static index
|
||||
* @param i the input index
|
||||
* @return
|
||||
*/
|
||||
public static SDIndex point(long i, boolean keepDim) {
|
||||
SDIndex sdIndex = new SDIndex();
|
||||
sdIndex.indexType = IndexType.POINT;
|
||||
sdIndex.pointIndex = i;
|
||||
sdIndex.pointKeepDim = keepDim;
|
||||
return sdIndex;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents all elements begin to end (think get row from beginning to end)
|
||||
* Note these are dynamic indices.
|
||||
* @param begin the begin index
|
||||
* @param end the end index
|
||||
* @return
|
||||
*/
|
||||
public static SDIndex interval(SDVariable begin, SDVariable end) {
|
||||
SDIndex sdIndex = new SDIndex();
|
||||
sdIndex.indexType = IndexType.INTERVAL_INPUT;
|
||||
sdIndex.intervalInputBegin = begin;
|
||||
sdIndex.intervalInputEnd = end;
|
||||
sdIndex.inclusiveInput = begin.getSameDiff().constant(0);
|
||||
return sdIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents all elements begin to end (think get row from beginning to end)
|
||||
* Note these are static indices.
|
||||
* @param begin the begin index
|
||||
* @param end the end index
|
||||
* @return
|
||||
*/
|
||||
public static SDIndex interval(Long begin, Long end) {
|
||||
return interval(begin,end,false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents all elements begin to end (think get row from beginning to end)
|
||||
* Note these are static indices.
|
||||
* @param begin the begin index
|
||||
* @param end the end index
|
||||
* @return
|
||||
*/
|
||||
public static SDIndex interval(Long begin, Long end,Boolean inclusive) {
|
||||
SDIndex sdIndex = new SDIndex();
|
||||
sdIndex.indexType = IndexType.INTERVAL;
|
||||
if(begin != null) {
|
||||
sdIndex.intervalBegin = begin.longValue();
|
||||
}
|
||||
if(end != null) {
|
||||
sdIndex.intervalEnd = end.longValue();
|
||||
}
|
||||
|
||||
if(inclusive != null) {
|
||||
sdIndex.inclusive = inclusive;
|
||||
} else {
|
||||
sdIndex.inclusive = false;
|
||||
}
|
||||
|
||||
return sdIndex;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents all elements begin to end (think get row from beginning to end)
|
||||
* Note these are static indices.
|
||||
* @param begin the begin index
|
||||
* @param end the end index
|
||||
* @return
|
||||
*/
|
||||
public static SDIndex interval(Integer begin, Integer end) {
|
||||
SDIndex sdIndex = new SDIndex();
|
||||
sdIndex.indexType = IndexType.INTERVAL;
|
||||
if(begin != null) {
|
||||
sdIndex.intervalBegin = begin.longValue();
|
||||
}
|
||||
if(end != null){
|
||||
sdIndex.intervalEnd = end.longValue();
|
||||
}
|
||||
|
||||
sdIndex.inclusive = false;
|
||||
|
||||
return sdIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents all elements begin to end (think get row from beginning to end)
|
||||
* Note these are static indices.
|
||||
* @param begin the begin index
|
||||
* @param strides the stride to increment by to end
|
||||
* @param end the end index
|
||||
* @return
|
||||
*/
|
||||
public static SDIndex interval(Long begin, Long strides, Long end) {
|
||||
if(strides == 0){
|
||||
throw new ND4JIllegalArgumentException("Invalid index : strides can not be 0.");
|
||||
}
|
||||
SDIndex sdIndex = new SDIndex();
|
||||
sdIndex.indexType = IndexType.INTERVAL;
|
||||
sdIndex.intervalBegin = begin;
|
||||
sdIndex.intervalEnd = end;
|
||||
sdIndex.intervalStrides = strides;
|
||||
sdIndex.inclusive = false;
|
||||
return sdIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents all elements begin to end (think get row from beginning to end)
|
||||
* Note these are static indices.
|
||||
* @param begin the begin index
|
||||
* @param strides the stride to increment by to end
|
||||
* @param end the end index
|
||||
* @param inclusive whether the index is inclusive or not
|
||||
* @return
|
||||
*/
|
||||
public static SDIndex interval(Long begin, Long strides, Long end,Boolean inclusive) {
|
||||
if(strides == 0) {
|
||||
throw new ND4JIllegalArgumentException("Invalid index : strides can not be 0.");
|
||||
}
|
||||
|
||||
SDIndex sdIndex = new SDIndex();
|
||||
sdIndex.indexType = IndexType.INTERVAL;
|
||||
sdIndex.intervalBegin = begin;
|
||||
sdIndex.intervalEnd = end;
|
||||
sdIndex.intervalStrides = strides;
|
||||
if(inclusive != null) {
|
||||
sdIndex.inclusive = inclusive;
|
||||
} else {
|
||||
sdIndex.inclusive = false;
|
||||
}
|
||||
return sdIndex;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents all elements begin to end (think get row from beginning to end)
|
||||
* Note these are static indices.
|
||||
* @param begin the begin index
|
||||
* @param strides the stride to increment by to end
|
||||
* @param end the end index
|
||||
* @return
|
||||
*/
|
||||
public static SDIndex interval(Integer begin, Integer strides, Integer end) {
|
||||
return interval(begin.longValue(),strides.longValue(),end.longValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents all elements begin to end (think get row from beginning to end)
|
||||
* Note these are static indices.
|
||||
* @param begin the begin index
|
||||
* @param strides the stride to increment by to end
|
||||
* @param end the end index
|
||||
* @return
|
||||
*/
|
||||
public static SDIndex interval(SDVariable begin, SDVariable strides, SDVariable end) {
|
||||
return interval(begin,strides,end,begin.getSameDiff().constant(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents all elements begin to end (think get row from beginning to end)
|
||||
* Note these are static indices.
|
||||
* @param begin the begin index
|
||||
* @param strides the stride to increment by to end
|
||||
* @param end the end index
|
||||
* @return
|
||||
*/
|
||||
public static SDIndex interval(SDVariable begin, SDVariable strides, SDVariable end,SDVariable inclusive) {
|
||||
SDIndex sdIndex = new SDIndex();
|
||||
sdIndex.indexType = IndexType.INTERVAL_INPUT;
|
||||
if(begin != null) {
|
||||
sdIndex.intervalInputBegin = begin;
|
||||
}
|
||||
|
||||
if(end != null) {
|
||||
sdIndex.intervalInputEnd = end;
|
||||
}
|
||||
|
||||
if(strides != null) {
|
||||
sdIndex.intervalStrideInput = strides;
|
||||
}
|
||||
|
||||
if(inclusive != null) {
|
||||
sdIndex.inclusiveInput = inclusive;
|
||||
} else {
|
||||
sdIndex.inclusiveInput = begin.getSameDiff().constant(false);
|
||||
}
|
||||
|
||||
return sdIndex;
|
||||
}
|
||||
}
|
||||
+2206
File diff suppressed because it is too large
Load Diff
+8641
File diff suppressed because it is too large
Load Diff
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
public interface SameDiffConditional {
|
||||
|
||||
|
||||
/**
|
||||
* @param context
|
||||
* @param body
|
||||
* @return
|
||||
*/
|
||||
SDVariable eval(SameDiff context, SameDiffFunctionDefinition body, SDVariable[] inputVars);
|
||||
|
||||
}
|
||||
+2210
File diff suppressed because it is too large
Load Diff
+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.autodiff.samediff;
|
||||
|
||||
import org.nd4j.linalg.api.ndarray.INDArray;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface SameDiffFunctionDefinition {
|
||||
|
||||
/**
|
||||
* @param inputs
|
||||
* @param variableInputs
|
||||
* @return
|
||||
*/
|
||||
SDVariable[] define(SameDiff sameDiff, Map<String, INDArray> inputs, SDVariable[] variableInputs);
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
public interface SameDiffLambda {
|
||||
SDVariable[] define(SameDiff sameDiff, SDVariable[] inputs);
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
public interface SameDiffNoArgSingleLambda {
|
||||
SDVariable define(SameDiff sameDiff);
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
public interface SameDiffSingleLambda {
|
||||
SDVariable define(SameDiff sameDiff, SDVariable[] inputs);
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package org.nd4j.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class TerminationPrediction {
|
||||
private int predictedAtIteration;
|
||||
private int predictedTerminationIteration;
|
||||
private double confidence;
|
||||
private String predictionMethod;
|
||||
private String reasoning;
|
||||
private Map<String, Object> evidenceData = new HashMap<>();
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package org.nd4j.autodiff.samediff;
|
||||
|
||||
public enum TerminationType {
|
||||
CONDITION_FALSE, // Normal termination via loop condition
|
||||
CONDITION_TRUE_EXIT, // Exit operation triggered by true condition
|
||||
SWITCH_TERMINATION, // Switch operation caused termination
|
||||
ERROR_TERMINATION, // Error/exception during loop
|
||||
TIMEOUT_TERMINATION, // Maximum iterations exceeded
|
||||
EARLY_BREAK, // Early termination before expected completion
|
||||
RESOURCE_EXHAUSTION, // Memory or other resource limits
|
||||
MANUAL_TERMINATION // Explicitly terminated by user/system
|
||||
}
|
||||
+566
@@ -0,0 +1,566 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.nd4j.common.base.Preconditions;
|
||||
import org.nd4j.evaluation.IEvaluation;
|
||||
import org.nd4j.linalg.api.buffer.DataType;
|
||||
import org.nd4j.linalg.learning.config.IUpdater;
|
||||
import org.nd4j.linalg.learning.regularization.L1Regularization;
|
||||
import org.nd4j.linalg.learning.regularization.L2Regularization;
|
||||
import org.nd4j.linalg.learning.regularization.Regularization;
|
||||
import org.nd4j.linalg.learning.regularization.WeightDecay;
|
||||
import org.nd4j.serde.json.JsonMappers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Slf4j
|
||||
public class TrainingConfig {
|
||||
|
||||
private IUpdater updater;
|
||||
private List<Regularization> regularization = new ArrayList<>(); //Regularization for all trainable parameters
|
||||
private boolean minimize = true;
|
||||
private List<String> dataSetFeatureMapping;
|
||||
private List<String> dataSetLabelMapping;
|
||||
private List<String> dataSetFeatureMaskMapping;
|
||||
private List<String> dataSetLabelMaskMapping;
|
||||
private int iterationCount;
|
||||
private int epochCount;
|
||||
private DataType initialLossDataType;
|
||||
|
||||
|
||||
private Map<String, List<IEvaluation>> trainEvaluations = new HashMap<>();
|
||||
private Map<String, Integer> trainEvaluationLabels = new HashMap<>();
|
||||
|
||||
private Map<String, List<IEvaluation>> validationEvaluations = new HashMap<>();
|
||||
private Map<String, Integer> validationEvaluationLabels = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Create a training configuration suitable for training a single input, single output network.<br>
|
||||
* See also the {@link Builder} for creating a TrainingConfig
|
||||
*
|
||||
* @param updater The updater configuration to use
|
||||
* @param dataSetFeatureMapping The name of the placeholder/variable that should be set using the feature INDArray from the DataSet
|
||||
* (or the first/only feature from a MultiDataSet). For example, if the network input placeholder was
|
||||
* called "input" then this should be set to "input"
|
||||
* @param dataSetLabelMapping The name of the placeholder/variable that should be set using the label INDArray from the DataSet
|
||||
* (or the first/only feature from a MultiDataSet). For example, if the network input placeholder was
|
||||
* called "input" then this should be set to "input"
|
||||
*/
|
||||
public TrainingConfig(IUpdater updater, List<Regularization> regularization, String dataSetFeatureMapping, String dataSetLabelMapping) {
|
||||
this(updater, regularization, true, Collections.singletonList(dataSetFeatureMapping), Collections.singletonList(dataSetLabelMapping),
|
||||
Collections.<String>emptyList(), null,DataType.FLOAT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a training configuration suitable for training both single input/output and multi input/output networks.<br>
|
||||
* See also the {@link Builder} for creating a TrainingConfig
|
||||
*
|
||||
* @param updater The updater configuration to use
|
||||
* @param regularization Regularization for all trainable parameters;\
|
||||
* @param minimize Set to true if the loss function should be minimized (usually true). False to maximize
|
||||
* @param dataSetFeatureMapping The name of the placeholders/variables that should be set using the feature INDArray(s) from the
|
||||
* DataSet or MultiDataSet. For example, if the network had 2 inputs called "input1" and "input2"
|
||||
* and the MultiDataSet features should be mapped with {@code MultiDataSet.getFeatures(0)->"input1"}
|
||||
* and {@code MultiDataSet.getFeatures(1)->"input2"}, then this should be set to {@code List<>("input1", "input2")}.
|
||||
* @param dataSetLabelMapping As per dataSetFeatureMapping, but for the DataSet/MultiDataSet labels
|
||||
* @param dataSetFeatureMaskMapping May be null. If non-null, the variables that the MultiDataSet feature mask arrays should be associated with.
|
||||
* @param dataSetLabelMaskMapping May be null. If non-null, the variables that the MultiDataSet label mask arrays should be associated with.
|
||||
*/
|
||||
public TrainingConfig(IUpdater updater, List<Regularization> regularization, boolean minimize, List<String> dataSetFeatureMapping, List<String> dataSetLabelMapping,
|
||||
List<String> dataSetFeatureMaskMapping, List<String> dataSetLabelMaskMapping, DataType initialLossDataType) {
|
||||
this.updater = updater;
|
||||
this.regularization = regularization;
|
||||
this.minimize = minimize;
|
||||
this.dataSetFeatureMapping = dataSetFeatureMapping;
|
||||
this.dataSetLabelMapping = dataSetLabelMapping;
|
||||
this.dataSetFeatureMaskMapping = dataSetFeatureMaskMapping;
|
||||
this.dataSetLabelMaskMapping = dataSetLabelMaskMapping;
|
||||
this.initialLossDataType = initialLossDataType;
|
||||
}
|
||||
|
||||
protected TrainingConfig(IUpdater updater, List<Regularization> regularization, boolean minimize, List<String> dataSetFeatureMapping, List<String> dataSetLabelMapping,
|
||||
List<String> dataSetFeatureMaskMapping, List<String> dataSetLabelMaskMapping,
|
||||
Map<String, List<IEvaluation>> trainEvaluations, Map<String, Integer> trainEvaluationLabels,
|
||||
Map<String, List<IEvaluation>> validationEvaluations, Map<String, Integer> validationEvaluationLabels,DataType initialLossDataType) {
|
||||
this(updater, regularization, minimize, dataSetFeatureMapping, dataSetLabelMapping, dataSetFeatureMaskMapping, dataSetLabelMaskMapping,initialLossDataType);
|
||||
this.trainEvaluations = trainEvaluations;
|
||||
this.trainEvaluationLabels = trainEvaluationLabels;
|
||||
this.validationEvaluations = validationEvaluations;
|
||||
this.validationEvaluationLabels = validationEvaluationLabels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the iteration count by 1
|
||||
*/
|
||||
public void incrementIterationCount(){
|
||||
iterationCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the epoch count by 1
|
||||
*/
|
||||
public void incrementEpochCount(){
|
||||
epochCount++;
|
||||
}
|
||||
|
||||
public static Builder builder(){
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index of the label array that the specified variable is associated with
|
||||
* @param s Name of the variable
|
||||
* @return The index of the label variable, or -1 if not found
|
||||
*/
|
||||
public int labelIdx(String s){
|
||||
return dataSetLabelMapping.indexOf(s);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private IUpdater updater;
|
||||
private List<Regularization> regularization = new ArrayList<>();
|
||||
private boolean minimize = true;
|
||||
private List<String> dataSetFeatureMapping;
|
||||
private List<String> dataSetLabelMapping;
|
||||
private List<String> dataSetFeatureMaskMapping;
|
||||
private List<String> dataSetLabelMaskMapping;
|
||||
private boolean skipValidation = false;
|
||||
private boolean markLabelsUnused = false;
|
||||
private DataType initialLossDataType = DataType.FLOAT;
|
||||
|
||||
private Map<String, List<IEvaluation>> trainEvaluations = new HashMap<>();
|
||||
private Map<String, Integer> trainEvaluationLabels = new HashMap<>();
|
||||
|
||||
private Map<String, List<IEvaluation>> validationEvaluations = new HashMap<>();
|
||||
private Map<String, Integer> validationEvaluationLabels = new HashMap<>();
|
||||
|
||||
|
||||
/**
|
||||
* Set the initial loss data type, defaults to
|
||||
* {@link DataType#FLOAT} - when setting a data type for a loss function
|
||||
* we need a beginning data type to compute the gradients. In order to do so,
|
||||
* we need to set an initial number of zero that acts as the initial gradient.
|
||||
* This initial loss data type controls the data type of that number.
|
||||
* This is critical when wanting more fine grained control over the data types
|
||||
* used in the training process.
|
||||
* @param initialLossDataType the initial loss data type
|
||||
* @return
|
||||
*/
|
||||
public Builder initialLossDataType(DataType initialLossDataType) {
|
||||
this.initialLossDataType = initialLossDataType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the updater (such as {@link org.nd4j.linalg.learning.config.Adam}, {@link org.nd4j.linalg.learning.config.Nesterovs}
|
||||
* etc. This is also how the learning rate (or learning rate schedule) is set.
|
||||
* @param updater Updater to set
|
||||
*/
|
||||
public Builder updater(IUpdater updater) {
|
||||
this.updater = updater;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the L1 regularization coefficient for all trainable parameters. Must be >= 0.<br>
|
||||
* See {@link L1Regularization} for more details
|
||||
* @param l1 L1 regularization coefficient
|
||||
*/
|
||||
public Builder l1(double l1) {
|
||||
Preconditions.checkState(l1 >= 0, "L1 regularization coefficient must be >= 0. Got %s", l1);
|
||||
removeInstances(this.regularization, L1Regularization.class);
|
||||
this.regularization.add(new L1Regularization(l1));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
Sets the L2 regularization coefficient for all trainable parameters. Must be >= 0.<br>
|
||||
* <b>Note</b>: Generally, {@link WeightDecay} (set via {@link #weightDecay(double,boolean)} should be preferred to
|
||||
* L2 regularization. See {@link WeightDecay} javadoc for further details.<br>
|
||||
* Note: L2 regularization and weight decay usually should not be used together; if any weight decay (or L2) has
|
||||
* been added for the biases, these will be removed first.
|
||||
*
|
||||
* @see #weightDecay(double, boolean)
|
||||
*/
|
||||
public Builder l2(double l2){
|
||||
Preconditions.checkState(l2 >= 0.0, "L2 regularization coefficient must be >= 0. Got %s", l2);
|
||||
//Check if existing L2 exists; if so, replace it. Also remove weight decay - it doesn't make sense to use both
|
||||
removeInstances(this.regularization, L2Regularization.class);
|
||||
if(l2 > 0.0) {
|
||||
removeInstancesWithWarning(this.regularization, WeightDecay.class, "WeightDecay regularization removed: incompatible with added L2 regularization");
|
||||
this.regularization.add(new L2Regularization(l2));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add weight decay regularization for all trainable parameters. See {@link WeightDecay} for more details.<br>
|
||||
* Note: values set by this method will be applied to all applicable layers in the network, unless a different
|
||||
* value is explicitly set on a given layer. In other words: values set via this method are used as the default
|
||||
* value, and can be overridden on a per-layer basis.<br>
|
||||
*
|
||||
* @param coefficient Weight decay regularization coefficient
|
||||
* @param applyLR Whether the learning rate should be multiplied in when performing weight decay updates. See {@link WeightDecay} for more details.
|
||||
*/
|
||||
public Builder weightDecay(double coefficient, boolean applyLR) {
|
||||
//Check if existing weight decay if it exists; if so, replace it. Also remove L2 - it doesn't make sense to use both
|
||||
removeInstances(this.regularization, WeightDecay.class);
|
||||
if(coefficient > 0.0) {
|
||||
removeInstancesWithWarning(this.regularization, L2Regularization.class, "L2 regularization removed: incompatible with added WeightDecay regularization");
|
||||
this.regularization.add(new WeightDecay(coefficient, applyLR));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add regularization to all trainable parameters in the network
|
||||
*
|
||||
* @param regularizations Regularization type(s) to add
|
||||
*/
|
||||
public Builder addRegularization(Regularization... regularizations){
|
||||
Collections.addAll(this.regularization, regularizations);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the regularization for all trainable parameters in the network.
|
||||
* Note that if any existing regularization types have been added, they will be removed
|
||||
*
|
||||
* @param regularization Regularization type(s) to add
|
||||
*/
|
||||
public Builder regularization(Regularization... regularization){
|
||||
if(regularization == null || regularization.length == 0)
|
||||
return this;
|
||||
List<Regularization> r = new ArrayList<>();
|
||||
Collections.addAll(r, regularization);
|
||||
return regularization(r);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the regularization for all trainable parameters in the network.
|
||||
* Note that if any existing regularization types have been added, they will be removed
|
||||
*
|
||||
* @param regularization Regularization type(s) to add
|
||||
*/
|
||||
public Builder regularization(List<Regularization> regularization){
|
||||
this.regularization = regularization;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the loss function should be minimized (true) or maximized (false).<br>
|
||||
* The loss function is usually minimized in SGD.<br>
|
||||
* Default: true.
|
||||
* @param minimize True to minimize, false to maximize
|
||||
*/
|
||||
public Builder minimize(boolean minimize){
|
||||
this.minimize = minimize;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the placeholders/variables that should be set using the feature INDArray(s) from the
|
||||
* DataSet or MultiDataSet. For example, if the network had 2 inputs called "input1" and "input2"
|
||||
* and the MultiDataSet features should be mapped with {@code MultiDataSet.getFeatures(0)->"input1"}
|
||||
* and {@code MultiDataSet.getFeatures(1)->"input2"}, then this should be set to {@code List<>("input1", "input2")}.
|
||||
*
|
||||
* @param dataSetFeatureMapping Name of the variables/placeholders that the feature arrays should be mapped to
|
||||
*/
|
||||
public Builder dataSetFeatureMapping(String... dataSetFeatureMapping){
|
||||
return dataSetFeatureMapping(Arrays.asList(dataSetFeatureMapping));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the placeholders/variables that should be set using the feature INDArray(s) from the
|
||||
* DataSet or MultiDataSet. For example, if the network had 2 inputs called "input1" and "input2"
|
||||
* and the MultiDataSet features should be mapped with {@code MultiDataSet.getFeatures(0)->"input1"}
|
||||
* and {@code MultiDataSet.getFeatures(1)->"input2"}, then this should be set to {@code "input1", "input2"}.
|
||||
*
|
||||
* @param dataSetFeatureMapping Name of the variables/placeholders that the feature arrays should be mapped to
|
||||
*/
|
||||
public Builder dataSetFeatureMapping(List<String> dataSetFeatureMapping){
|
||||
Preconditions.checkNotNull(dataSetFeatureMapping != null && dataSetFeatureMapping.size() > 0, "No feature mapping was provided");
|
||||
this.dataSetFeatureMapping = dataSetFeatureMapping;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the placeholders/variables that should be set using the labels INDArray(s) from the
|
||||
* DataSet or MultiDataSet. For example, if the network had 2 labels called "label1" and "label2"
|
||||
* and the MultiDataSet labels should be mapped with {@code MultiDataSet.getLabel(0)->"label1"}
|
||||
* and {@code MultiDataSet.getLabels(1)->"label"}, then this should be set to {@code "label1", "label2"}.
|
||||
*
|
||||
* @param dataSetLabelMapping Name of the variables/placeholders that the label arrays should be mapped to
|
||||
*/
|
||||
public Builder dataSetLabelMapping(String... dataSetLabelMapping){
|
||||
return dataSetLabelMapping(Arrays.asList(dataSetLabelMapping));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the placeholders/variables that should be set using the labels INDArray(s) from the
|
||||
* DataSet or MultiDataSet. For example, if the network had 2 labels called "label1" and "label2"
|
||||
* and the MultiDataSet labels should be mapped with {@code MultiDataSet.getLabel(0)->"label1"}
|
||||
* and {@code MultiDataSet.getLabels(1)->"label"}, then this should be set to {@code "label1", "label2"}.
|
||||
*
|
||||
* @param dataSetLabelMapping Name of the variables/placeholders that the label arrays should be mapped to
|
||||
*/
|
||||
public Builder dataSetLabelMapping(List<String> dataSetLabelMapping){
|
||||
Preconditions.checkNotNull(dataSetLabelMapping != null && dataSetLabelMapping.size() > 0, "No label mapping was provided");
|
||||
this.dataSetLabelMapping = dataSetLabelMapping;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling this method will mark the label as unused. This is basically a way to turn off label mapping validation in
|
||||
* TrainingConfig builder, for training models without labels.<br>
|
||||
* Put another way: usually you need to call {@link #dataSetLabelMapping(String...)} to set labels, this method
|
||||
* allows you to say that the DataSet/MultiDataSet labels aren't used in training.
|
||||
*/
|
||||
public Builder markLabelsUnused(){
|
||||
this.markLabelsUnused = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #dataSetFeatureMaskMapping(List)}
|
||||
*/
|
||||
public Builder dataSetFeatureMaskMapping(String... dataSetFeatureMaskMapping){
|
||||
return dataSetFeatureMaskMapping(Arrays.asList(dataSetFeatureMaskMapping));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the placeholders/variables that should be set using the feature mask INDArray(s) from the
|
||||
* DataSet or MultiDataSet. For example, if the network had 2 mask variables called "mask1" and "mask2"
|
||||
* and the MultiDataSet features masks should be mapped with {@code MultiDataSet.getFeatureMaskArray(0)->"mask1"}
|
||||
* and {@code MultiDataSet.getFeatureMaskArray(1)->"mask2"}, then this should be set to {@code "mask1", "mask2"}.
|
||||
*
|
||||
* @param dataSetFeatureMaskMapping Name of the variables/placeholders that the feature arrays should be mapped to
|
||||
*/
|
||||
public Builder dataSetFeatureMaskMapping(List<String> dataSetFeatureMaskMapping){
|
||||
this.dataSetFeatureMaskMapping = dataSetFeatureMaskMapping;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #dataSetLabelMaskMapping(List)}
|
||||
*/
|
||||
public Builder dataSetLabelMaskMapping(String... dataSetLabelMaskMapping){
|
||||
return dataSetLabelMaskMapping(Arrays.asList(dataSetLabelMaskMapping));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the placeholders/variables that should be set using the label mask INDArray(s) from the
|
||||
* DataSet or MultiDataSet. For example, if the network had 2 mask variables called "mask1" and "mask2"
|
||||
* and the MultiDataSet label masks should be mapped with {@code MultiDataSet.getLabelMaskArray(0)->"mask1"}
|
||||
* and {@code MultiDataSet.getLabelMaskArray(1)->"mask2"}, then this should be set to {@code "mask1", "mask2"}.
|
||||
*
|
||||
* @param dataSetLabelMaskMapping Name of the variables/placeholders that the feature arrays should be mapped to
|
||||
*/
|
||||
public Builder dataSetLabelMaskMapping(List<String> dataSetLabelMaskMapping){
|
||||
this.dataSetLabelMaskMapping = dataSetLabelMaskMapping;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder skipBuilderValidation(boolean skip) {
|
||||
this.skipValidation = skip;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
private void addEvaluations(boolean validation, @NonNull Map<String, List<IEvaluation>> evaluationMap, @NonNull Map<String, Integer> labelMap,
|
||||
@NonNull String variableName, int labelIndex, @NonNull IEvaluation... evaluations) {
|
||||
if(evaluationMap.containsKey(variableName) && labelMap.get(variableName) != labelIndex){
|
||||
String s;
|
||||
|
||||
if(validation){
|
||||
s = "This ListenerEvaluations.Builder already has validation evaluations for ";
|
||||
} else {
|
||||
s = "This ListenerEvaluations.Builder already has train evaluations for ";
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(s + "variable " +
|
||||
variableName + " with label index " + labelIndex + ". You can't add " +
|
||||
" evaluations with a different label index. Got label index " + labelIndex);
|
||||
}
|
||||
|
||||
if(evaluationMap.containsKey(variableName)){
|
||||
evaluationMap.get(variableName).addAll(Arrays.asList(evaluations));
|
||||
} else {
|
||||
evaluationMap.put(variableName, Arrays.asList(evaluations));
|
||||
labelMap.put(variableName, labelIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add requested History training evaluations for a parm/variable.
|
||||
*
|
||||
* These evaluations will be reported in the {@link org.nd4j.autodiff.listeners.records.History} object returned by fit.
|
||||
*
|
||||
* @param variableName The variable to evaluate
|
||||
* @param labelIndex The index of the label to evaluate against
|
||||
* @param evaluations The evaluations to run
|
||||
*/
|
||||
public Builder trainEvaluation(@NonNull String variableName, int labelIndex, @NonNull IEvaluation... evaluations){
|
||||
addEvaluations(false, this.trainEvaluations, this.trainEvaluationLabels, variableName,
|
||||
labelIndex, evaluations);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add requested History training evaluations for a parm/variable.
|
||||
*
|
||||
* These evaluations will be reported in the {@link org.nd4j.autodiff.listeners.records.History} object returned by fit.
|
||||
*
|
||||
* @param variable The variable to evaluate
|
||||
* @param labelIndex The index of the label to evaluate against
|
||||
* @param evaluations The evaluations to run
|
||||
*/
|
||||
public Builder trainEvaluation(@NonNull SDVariable variable, int labelIndex, @NonNull IEvaluation... evaluations){
|
||||
return trainEvaluation(variable.name(), labelIndex, evaluations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add requested History validation evaluations for a parm/variable.
|
||||
*
|
||||
* These evaluations will be reported in the {@link org.nd4j.autodiff.listeners.records.History} object returned by fit.
|
||||
*
|
||||
* @param variableName The variable to evaluate
|
||||
* @param labelIndex The index of the label to evaluate against
|
||||
* @param evaluations The evaluations to run
|
||||
*/
|
||||
public Builder validationEvaluation(@NonNull String variableName, int labelIndex, @NonNull IEvaluation... evaluations){
|
||||
addEvaluations(true, this.validationEvaluations, this.validationEvaluationLabels, variableName,
|
||||
labelIndex, evaluations);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add requested History validation evaluations for a parm/variable.
|
||||
*
|
||||
* These evaluations will be reported in the {@link org.nd4j.autodiff.listeners.records.History} object returned by fit.
|
||||
*
|
||||
* @param variable The variable to evaluate
|
||||
* @param labelIndex The index of the label to evaluate against
|
||||
* @param evaluations The evaluations to run
|
||||
*/
|
||||
public Builder validationEvaluation(@NonNull SDVariable variable, int labelIndex, @NonNull IEvaluation... evaluations){
|
||||
return validationEvaluation(variable.name(), labelIndex, evaluations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add requested evaluations for a parm/variable, for either training or validation.
|
||||
*
|
||||
* These evaluations will be reported in the {@link org.nd4j.autodiff.listeners.records.History} object returned by fit.
|
||||
*
|
||||
* @param validation Whether to add these evaluations as validation or training
|
||||
* @param variableName The variable to evaluate
|
||||
* @param labelIndex The index of the label to evaluate against
|
||||
* @param evaluations The evaluations to run
|
||||
*/
|
||||
public Builder addEvaluations(boolean validation, @NonNull String variableName, int labelIndex, @NonNull IEvaluation... evaluations){
|
||||
if(validation){
|
||||
return validationEvaluation(variableName, labelIndex, evaluations);
|
||||
} else{
|
||||
return trainEvaluation(variableName, labelIndex, evaluations);
|
||||
}
|
||||
}
|
||||
|
||||
public TrainingConfig build(){
|
||||
if(!skipValidation) {
|
||||
Preconditions.checkState(updater != null, "Updater (optimizer) must not be null. Use updater(IUpdater) to set an updater");
|
||||
Preconditions.checkState(dataSetFeatureMapping != null, "No DataSet feature mapping has been provided. A " +
|
||||
"mapping between DataSet array positions and variables/placeholders must be provided - use dateSetFeatureMapping(...) to set this");
|
||||
Preconditions.checkState(markLabelsUnused || dataSetLabelMapping != null, "No DataSet label mapping has been provided. A " +
|
||||
"mapping between DataSet array positions and variables/placeholders must be provided - use dataSetLabelMapping(...) to set this," +
|
||||
" or use markLabelsUnused() to mark labels as unused (for example, for unsupervised learning)");
|
||||
|
||||
|
||||
Preconditions.checkArgument(trainEvaluations.keySet().equals(trainEvaluationLabels.keySet()),
|
||||
"Must specify a label index for each train evaluation. Expected: %s, got: %s",
|
||||
trainEvaluations.keySet(), trainEvaluationLabels.keySet());
|
||||
|
||||
Preconditions.checkArgument(validationEvaluations.keySet().equals(validationEvaluationLabels.keySet()),
|
||||
"Must specify a label index for each validation evaluation. Expected: %s, got: %s",
|
||||
validationEvaluations.keySet(), validationEvaluationLabels.keySet());
|
||||
}
|
||||
|
||||
return new TrainingConfig(updater, regularization, minimize, dataSetFeatureMapping, dataSetLabelMapping,
|
||||
dataSetFeatureMaskMapping, dataSetLabelMaskMapping,
|
||||
trainEvaluations, trainEvaluationLabels, validationEvaluations, validationEvaluationLabels,initialLossDataType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove any instances of the specified type from the list.
|
||||
* This includes any subtypes.
|
||||
* @param list List. May be null
|
||||
* @param remove Type of objects to remove
|
||||
*/
|
||||
public static void removeInstances(List<?> list, Class<?> remove) {
|
||||
removeInstancesWithWarning(list, remove, null);
|
||||
}
|
||||
|
||||
public static void removeInstancesWithWarning(List<?> list, Class<?> remove, String warning){
|
||||
if(list == null || list.isEmpty())
|
||||
return;
|
||||
Iterator<?> iter = list.iterator();
|
||||
while(iter.hasNext()){
|
||||
Object o = iter.next();
|
||||
if(remove.isAssignableFrom(o.getClass())){
|
||||
if(warning != null) {
|
||||
log.warn(warning);
|
||||
}
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String toJson(){
|
||||
try {
|
||||
return JsonMappers.getMapper().writeValueAsString(this);
|
||||
} catch (IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static TrainingConfig fromJson(@NonNull String json){
|
||||
try{
|
||||
return JsonMappers.getMapper().readValue(json, TrainingConfig.class);
|
||||
} catch (IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package org.nd4j.autodiff.samediff;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class VariableEvolutionAnalysis {
|
||||
private Map<String, List<Object>> variableEvolution = new HashMap<>();
|
||||
private Map<String, VariablePattern> detectedPatterns = new HashMap<>();
|
||||
private List<ConditionEvaluation> conditionEvaluationHistory = new ArrayList<>();
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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.autodiff.samediff;
|
||||
|
||||
import org.nd4j.linalg.api.buffer.DataType;
|
||||
|
||||
// Inner classes for holding information
|
||||
public class VariableInfo {
|
||||
public final String name;
|
||||
public final VariableType type;
|
||||
public final DataType dataType;
|
||||
public String frame;
|
||||
public int iteration;
|
||||
public String parentFrame;
|
||||
|
||||
public VariableInfo(String name, VariableType type, DataType dataType) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.dataType = dataType;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user