Unverified Commit 5e546893 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #66623 from deads2k/gc-01-graph

Automatic merge from submit-queue (batch tested with PRs 66623, 66718). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. expose GC graph via debug handler Many times when debugging GC problems, it's important to understand the state of the GC graph at a given point in time. This pull adds the ability to dump that graph in DOT format for later consumption. It does this by exposing an additional debug handler and allowing any controller init function to produce such a handler that is included under debug. Sample full output ``` curl http://localhost:10252/debug/controllers/garbagecollector/graph digraph full { // Node definitions. 0 [ label="uid=8581a030-9043-11e8-ad4a-54e1ad486dd3 namespace=kube-system Pod.v1/kube-dns-7b479ccbc6-qz468 " group="" version="v1" kind="Pod" namespace="kube-system" name="kube-dns-7b479ccbc6-qz468" uid="8581a030-9043-11e8-ad4a-54e1ad486dd3" missing="false" beingDeleted="false" deletingDependents="false" virtual="false" ]; 1 [ label="uid=822052fc-9043-11e8-ad4a-54e1ad486dd3 namespace=kube-system Deployment.v1.apps/kube-dns " group="apps" version="v1" kind="Deployment" namespace="kube-system" name="kube-dns" uid="822052fc-9043-11e8-ad4a-54e1ad486dd3" missing="false" beingDeleted="false" deletingDependents="false" virtual="false" ]; 2 [ label="uid=857bd8ac-9043-11e8-ad4a-54e1ad486dd3 namespace=kube-system ReplicaSet.v1.apps/kube-dns-7b479ccbc6 " group="apps" version="v1" kind="ReplicaSet" namespace="kube-system" name="kube-dns-7b479ccbc6" uid="857bd8ac-9043-11e8-ad4a-54e1ad486dd3" missing="false" beingDeleted="false" deletingDependents="false" virtual="false" ]; // Edge definitions. 0 -> 2; 2 -> 1; } ``` You can also select via UID and have all transitive dependencies output: ``` curl http://localhost:10252/debug/controllers/garbagecollector/graph?uid=8581a030-9043-11e8-ad4a-54e1ad486dd3 digraph full { // Node definitions. 0 [ label="uid=822052fc-9043-11e8-ad4a-54e1ad486dd3 namespace=kube-system Deployment.v1.apps/kube-dns " group="apps" version="v1" kind="Deployment" namespace="kube-system" name="kube-dns" uid="822052fc-9043-11e8-ad4a-54e1ad486dd3" missing="false" beingDeleted="false" deletingDependents="false" virtual="false" ]; 1 [ label="uid=8581a030-9043-11e8-ad4a-54e1ad486dd3 namespace=kube-system Pod.v1/kube-dns-7b479ccbc6-qz468 " group="" version="v1" kind="Pod" namespace="kube-system" name="kube-dns-7b479ccbc6-qz468" uid="8581a030-9043-11e8-ad4a-54e1ad486dd3" missing="false" beingDeleted="false" deletingDependents="false" virtual="false" ]; 2 [ label="uid=857bd8ac-9043-11e8-ad4a-54e1ad486dd3 namespace=kube-system ReplicaSet.v1.apps/kube-dns-7b479ccbc6 " group="apps" version="v1" kind="ReplicaSet" namespace="kube-system" name="kube-dns-7b479ccbc6" uid="857bd8ac-9043-11e8-ad4a-54e1ad486dd3" missing="false" beingDeleted="false" deletingDependents="false" virtual="false" ]; // Edge definitions. 1 -> 2; 2 -> 0; } ``` And with some sample rendering: ``` curl http://localhost:10252/debug/controllers/garbagecollector/graph | dot -T svg -o project.svg ``` produces ![gc](https://user-images.githubusercontent.com/8225098/43223895-8e33c126-9022-11e8-8ad9-6b2f986fd974.png) @kubernetes/sig-api-machinery-pr-reviews /assign @caesarxuchao @liggitt ```release-note NONE ```
parents 05acb32a fb7d137e
...@@ -24,12 +24,12 @@ ...@@ -24,12 +24,12 @@
}, },
{ {
"ImportPath": "cloud.google.com/go/compute/metadata", "ImportPath": "cloud.google.com/go/compute/metadata",
"Comment": "v0.1.0-115-g3b1ae45", "Comment": "v0.1.0-115-g3b1ae453",
"Rev": "3b1ae45394a234c385be014e9a488f2bb6eef821" "Rev": "3b1ae45394a234c385be014e9a488f2bb6eef821"
}, },
{ {
"ImportPath": "cloud.google.com/go/internal", "ImportPath": "cloud.google.com/go/internal",
"Comment": "v0.1.0-115-g3b1ae45", "Comment": "v0.1.0-115-g3b1ae453",
"Rev": "3b1ae45394a234c385be014e9a488f2bb6eef821" "Rev": "3b1ae45394a234c385be014e9a488f2bb6eef821"
}, },
{ {
...@@ -3078,6 +3078,110 @@ ...@@ -3078,6 +3078,110 @@
"Rev": "2382e3994d48b1d22acc2c86bcad0a2aff028e32" "Rev": "2382e3994d48b1d22acc2c86bcad0a2aff028e32"
}, },
{ {
"ImportPath": "gonum.org/v1/gonum/blas",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/blas/blas64",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/blas/gonum",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/floats",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/graph",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/graph/encoding",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/graph/encoding/dot",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/graph/formats/dot",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/graph/formats/dot/ast",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/graph/formats/dot/internal/astx",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/graph/formats/dot/internal/errors",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/graph/formats/dot/internal/lexer",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/graph/formats/dot/internal/parser",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/graph/formats/dot/internal/token",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/graph/internal/ordered",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/graph/internal/set",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/graph/internal/uid",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/graph/simple",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/internal/asm/c128",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/internal/asm/f32",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/internal/asm/f64",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/internal/math32",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/lapack",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/lapack/gonum",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/lapack/lapack64",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "gonum.org/v1/gonum/mat",
"Rev": "cebdade430ccb61c1feba4878085f6cf8cb3320e"
},
{
"ImportPath": "google.golang.org/api/compute/v0.alpha", "ImportPath": "google.golang.org/api/compute/v0.alpha",
"Rev": "3639d6d93f377f39a1de765fa4ef37b3c7ca8bd9" "Rev": "3639d6d93f377f39a1de765fa4ef37b3c7ca8bd9"
}, },
......
...@@ -110,15 +110,15 @@ func Run(c *cloudcontrollerconfig.CompletedConfig) error { ...@@ -110,15 +110,15 @@ func Run(c *cloudcontrollerconfig.CompletedConfig) error {
// Start the controller manager HTTP server // Start the controller manager HTTP server
stopCh := make(chan struct{}) stopCh := make(chan struct{})
if c.SecureServing != nil { if c.SecureServing != nil {
handler := genericcontrollermanager.NewBaseHandler(&c.ComponentConfig.Debugging) unsecuredMux := genericcontrollermanager.NewBaseHandler(&c.ComponentConfig.Debugging)
handler = genericcontrollermanager.BuildHandlerChain(handler, &c.Authorization, &c.Authentication) handler := genericcontrollermanager.BuildHandlerChain(unsecuredMux, &c.Authorization, &c.Authentication)
if err := c.SecureServing.Serve(handler, 0, stopCh); err != nil { if err := c.SecureServing.Serve(handler, 0, stopCh); err != nil {
return err return err
} }
} }
if c.InsecureServing != nil { if c.InsecureServing != nil {
handler := genericcontrollermanager.NewBaseHandler(&c.ComponentConfig.Debugging) unsecuredMux := genericcontrollermanager.NewBaseHandler(&c.ComponentConfig.Debugging)
handler = genericcontrollermanager.BuildHandlerChain(handler, &c.Authorization, &c.Authentication) handler := genericcontrollermanager.BuildHandlerChain(unsecuredMux, &c.Authorization, &c.Authentication)
if err := c.InsecureServing.Serve(handler, 0, stopCh); err != nil { if err := c.InsecureServing.Serve(handler, 0, stopCh); err != nil {
return err return err
} }
......
...@@ -48,7 +48,7 @@ func BuildHandlerChain(apiHandler http.Handler, authorizationInfo *apiserver.Aut ...@@ -48,7 +48,7 @@ func BuildHandlerChain(apiHandler http.Handler, authorizationInfo *apiserver.Aut
} }
// NewBaseHandler takes in CompletedConfig and returns a handler. // NewBaseHandler takes in CompletedConfig and returns a handler.
func NewBaseHandler(c *componentconfig.DebuggingConfiguration) http.Handler { func NewBaseHandler(c *componentconfig.DebuggingConfiguration) *mux.PathRecorderMux {
mux := mux.NewPathRecorderMux("controller-manager") mux := mux.NewPathRecorderMux("controller-manager")
healthz.InstallHandler(mux) healthz.InstallHandler(mux)
if c.EnableProfiling { if c.EnableProfiling {
......
...@@ -111,6 +111,7 @@ go_library( ...@@ -111,6 +111,7 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/server/mux:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/client-go/discovery/cached:go_default_library", "//staging/src/k8s.io/client-go/discovery/cached:go_default_library",
"//staging/src/k8s.io/client-go/dynamic:go_default_library", "//staging/src/k8s.io/client-go/dynamic:go_default_library",
......
...@@ -23,6 +23,8 @@ package app ...@@ -23,6 +23,8 @@ package app
import ( import (
"fmt" "fmt"
"net/http"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/controller/daemon" "k8s.io/kubernetes/pkg/controller/daemon"
"k8s.io/kubernetes/pkg/controller/deployment" "k8s.io/kubernetes/pkg/controller/deployment"
...@@ -30,9 +32,9 @@ import ( ...@@ -30,9 +32,9 @@ import (
"k8s.io/kubernetes/pkg/controller/statefulset" "k8s.io/kubernetes/pkg/controller/statefulset"
) )
func startDaemonSetController(ctx ControllerContext) (bool, error) { func startDaemonSetController(ctx ControllerContext) (http.Handler, bool, error) {
if !ctx.AvailableResources[schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "daemonsets"}] { if !ctx.AvailableResources[schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "daemonsets"}] {
return false, nil return nil, false, nil
} }
dsc, err := daemon.NewDaemonSetsController( dsc, err := daemon.NewDaemonSetsController(
ctx.InformerFactory.Apps().V1().DaemonSets(), ctx.InformerFactory.Apps().V1().DaemonSets(),
...@@ -42,15 +44,15 @@ func startDaemonSetController(ctx ControllerContext) (bool, error) { ...@@ -42,15 +44,15 @@ func startDaemonSetController(ctx ControllerContext) (bool, error) {
ctx.ClientBuilder.ClientOrDie("daemon-set-controller"), ctx.ClientBuilder.ClientOrDie("daemon-set-controller"),
) )
if err != nil { if err != nil {
return true, fmt.Errorf("error creating DaemonSets controller: %v", err) return nil, true, fmt.Errorf("error creating DaemonSets controller: %v", err)
} }
go dsc.Run(int(ctx.ComponentConfig.DaemonSetController.ConcurrentDaemonSetSyncs), ctx.Stop) go dsc.Run(int(ctx.ComponentConfig.DaemonSetController.ConcurrentDaemonSetSyncs), ctx.Stop)
return true, nil return nil, true, nil
} }
func startStatefulSetController(ctx ControllerContext) (bool, error) { func startStatefulSetController(ctx ControllerContext) (http.Handler, bool, error) {
if !ctx.AvailableResources[schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "statefulsets"}] { if !ctx.AvailableResources[schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "statefulsets"}] {
return false, nil return nil, false, nil
} }
go statefulset.NewStatefulSetController( go statefulset.NewStatefulSetController(
ctx.InformerFactory.Core().V1().Pods(), ctx.InformerFactory.Core().V1().Pods(),
...@@ -59,12 +61,12 @@ func startStatefulSetController(ctx ControllerContext) (bool, error) { ...@@ -59,12 +61,12 @@ func startStatefulSetController(ctx ControllerContext) (bool, error) {
ctx.InformerFactory.Apps().V1().ControllerRevisions(), ctx.InformerFactory.Apps().V1().ControllerRevisions(),
ctx.ClientBuilder.ClientOrDie("statefulset-controller"), ctx.ClientBuilder.ClientOrDie("statefulset-controller"),
).Run(1, ctx.Stop) ).Run(1, ctx.Stop)
return true, nil return nil, true, nil
} }
func startReplicaSetController(ctx ControllerContext) (bool, error) { func startReplicaSetController(ctx ControllerContext) (http.Handler, bool, error) {
if !ctx.AvailableResources[schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "replicasets"}] { if !ctx.AvailableResources[schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "replicasets"}] {
return false, nil return nil, false, nil
} }
go replicaset.NewReplicaSetController( go replicaset.NewReplicaSetController(
ctx.InformerFactory.Apps().V1().ReplicaSets(), ctx.InformerFactory.Apps().V1().ReplicaSets(),
...@@ -72,12 +74,12 @@ func startReplicaSetController(ctx ControllerContext) (bool, error) { ...@@ -72,12 +74,12 @@ func startReplicaSetController(ctx ControllerContext) (bool, error) {
ctx.ClientBuilder.ClientOrDie("replicaset-controller"), ctx.ClientBuilder.ClientOrDie("replicaset-controller"),
replicaset.BurstReplicas, replicaset.BurstReplicas,
).Run(int(ctx.ComponentConfig.ReplicaSetController.ConcurrentRSSyncs), ctx.Stop) ).Run(int(ctx.ComponentConfig.ReplicaSetController.ConcurrentRSSyncs), ctx.Stop)
return true, nil return nil, true, nil
} }
func startDeploymentController(ctx ControllerContext) (bool, error) { func startDeploymentController(ctx ControllerContext) (http.Handler, bool, error) {
if !ctx.AvailableResources[schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}] { if !ctx.AvailableResources[schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}] {
return false, nil return nil, false, nil
} }
dc, err := deployment.NewDeploymentController( dc, err := deployment.NewDeploymentController(
ctx.InformerFactory.Apps().V1().Deployments(), ctx.InformerFactory.Apps().V1().Deployments(),
...@@ -86,8 +88,8 @@ func startDeploymentController(ctx ControllerContext) (bool, error) { ...@@ -86,8 +88,8 @@ func startDeploymentController(ctx ControllerContext) (bool, error) {
ctx.ClientBuilder.ClientOrDie("deployment-controller"), ctx.ClientBuilder.ClientOrDie("deployment-controller"),
) )
if err != nil { if err != nil {
return true, fmt.Errorf("error creating Deployment controller: %v", err) return nil, true, fmt.Errorf("error creating Deployment controller: %v", err)
} }
go dc.Run(int(ctx.ComponentConfig.DeploymentController.ConcurrentDeploymentSyncs), ctx.Stop) go dc.Run(int(ctx.ComponentConfig.DeploymentController.ConcurrentDeploymentSyncs), ctx.Stop)
return true, nil return nil, true, nil
} }
...@@ -21,6 +21,8 @@ limitations under the License. ...@@ -21,6 +21,8 @@ limitations under the License.
package app package app
import ( import (
"net/http"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
"k8s.io/client-go/scale" "k8s.io/client-go/scale"
...@@ -31,9 +33,9 @@ import ( ...@@ -31,9 +33,9 @@ import (
"k8s.io/metrics/pkg/client/external_metrics" "k8s.io/metrics/pkg/client/external_metrics"
) )
func startHPAController(ctx ControllerContext) (bool, error) { func startHPAController(ctx ControllerContext) (http.Handler, bool, error) {
if !ctx.AvailableResources[schema.GroupVersionResource{Group: "autoscaling", Version: "v1", Resource: "horizontalpodautoscalers"}] { if !ctx.AvailableResources[schema.GroupVersionResource{Group: "autoscaling", Version: "v1", Resource: "horizontalpodautoscalers"}] {
return false, nil return nil, false, nil
} }
if ctx.ComponentConfig.HPAController.HorizontalPodAutoscalerUseRESTClients { if ctx.ComponentConfig.HPAController.HorizontalPodAutoscalerUseRESTClients {
...@@ -44,7 +46,7 @@ func startHPAController(ctx ControllerContext) (bool, error) { ...@@ -44,7 +46,7 @@ func startHPAController(ctx ControllerContext) (bool, error) {
return startHPAControllerWithLegacyClient(ctx) return startHPAControllerWithLegacyClient(ctx)
} }
func startHPAControllerWithRESTClient(ctx ControllerContext) (bool, error) { func startHPAControllerWithRESTClient(ctx ControllerContext) (http.Handler, bool, error) {
clientConfig := ctx.ClientBuilder.ConfigOrDie("horizontal-pod-autoscaler") clientConfig := ctx.ClientBuilder.ConfigOrDie("horizontal-pod-autoscaler")
metricsClient := metrics.NewRESTMetricsClient( metricsClient := metrics.NewRESTMetricsClient(
resourceclient.NewForConfigOrDie(clientConfig), resourceclient.NewForConfigOrDie(clientConfig),
...@@ -54,7 +56,7 @@ func startHPAControllerWithRESTClient(ctx ControllerContext) (bool, error) { ...@@ -54,7 +56,7 @@ func startHPAControllerWithRESTClient(ctx ControllerContext) (bool, error) {
return startHPAControllerWithMetricsClient(ctx, metricsClient) return startHPAControllerWithMetricsClient(ctx, metricsClient)
} }
func startHPAControllerWithLegacyClient(ctx ControllerContext) (bool, error) { func startHPAControllerWithLegacyClient(ctx ControllerContext) (http.Handler, bool, error) {
hpaClient := ctx.ClientBuilder.ClientOrDie("horizontal-pod-autoscaler") hpaClient := ctx.ClientBuilder.ClientOrDie("horizontal-pod-autoscaler")
metricsClient := metrics.NewHeapsterMetricsClient( metricsClient := metrics.NewHeapsterMetricsClient(
hpaClient, hpaClient,
...@@ -66,7 +68,7 @@ func startHPAControllerWithLegacyClient(ctx ControllerContext) (bool, error) { ...@@ -66,7 +68,7 @@ func startHPAControllerWithLegacyClient(ctx ControllerContext) (bool, error) {
return startHPAControllerWithMetricsClient(ctx, metricsClient) return startHPAControllerWithMetricsClient(ctx, metricsClient)
} }
func startHPAControllerWithMetricsClient(ctx ControllerContext, metricsClient metrics.MetricsClient) (bool, error) { func startHPAControllerWithMetricsClient(ctx ControllerContext, metricsClient metrics.MetricsClient) (http.Handler, bool, error) {
hpaClient := ctx.ClientBuilder.ClientOrDie("horizontal-pod-autoscaler") hpaClient := ctx.ClientBuilder.ClientOrDie("horizontal-pod-autoscaler")
hpaClientConfig := ctx.ClientBuilder.ConfigOrDie("horizontal-pod-autoscaler") hpaClientConfig := ctx.ClientBuilder.ConfigOrDie("horizontal-pod-autoscaler")
...@@ -75,7 +77,7 @@ func startHPAControllerWithMetricsClient(ctx ControllerContext, metricsClient me ...@@ -75,7 +77,7 @@ func startHPAControllerWithMetricsClient(ctx ControllerContext, metricsClient me
scaleKindResolver := scale.NewDiscoveryScaleKindResolver(hpaClient.Discovery()) scaleKindResolver := scale.NewDiscoveryScaleKindResolver(hpaClient.Discovery())
scaleClient, err := scale.NewForConfig(hpaClientConfig, ctx.RESTMapper, dynamic.LegacyAPIPathResolverFunc, scaleKindResolver) scaleClient, err := scale.NewForConfig(hpaClientConfig, ctx.RESTMapper, dynamic.LegacyAPIPathResolverFunc, scaleKindResolver)
if err != nil { if err != nil {
return false, err return nil, false, err
} }
replicaCalc := podautoscaler.NewReplicaCalculator( replicaCalc := podautoscaler.NewReplicaCalculator(
...@@ -94,5 +96,5 @@ func startHPAControllerWithMetricsClient(ctx ControllerContext, metricsClient me ...@@ -94,5 +96,5 @@ func startHPAControllerWithMetricsClient(ctx ControllerContext, metricsClient me
ctx.ComponentConfig.HPAController.HorizontalPodAutoscalerUpscaleForbiddenWindow.Duration, ctx.ComponentConfig.HPAController.HorizontalPodAutoscalerUpscaleForbiddenWindow.Duration,
ctx.ComponentConfig.HPAController.HorizontalPodAutoscalerDownscaleForbiddenWindow.Duration, ctx.ComponentConfig.HPAController.HorizontalPodAutoscalerDownscaleForbiddenWindow.Duration,
).Run(ctx.Stop) ).Run(ctx.Stop)
return true, nil return nil, true, nil
} }
...@@ -23,33 +23,35 @@ package app ...@@ -23,33 +23,35 @@ package app
import ( import (
"fmt" "fmt"
"net/http"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/controller/cronjob" "k8s.io/kubernetes/pkg/controller/cronjob"
"k8s.io/kubernetes/pkg/controller/job" "k8s.io/kubernetes/pkg/controller/job"
) )
func startJobController(ctx ControllerContext) (bool, error) { func startJobController(ctx ControllerContext) (http.Handler, bool, error) {
if !ctx.AvailableResources[schema.GroupVersionResource{Group: "batch", Version: "v1", Resource: "jobs"}] { if !ctx.AvailableResources[schema.GroupVersionResource{Group: "batch", Version: "v1", Resource: "jobs"}] {
return false, nil return nil, false, nil
} }
go job.NewJobController( go job.NewJobController(
ctx.InformerFactory.Core().V1().Pods(), ctx.InformerFactory.Core().V1().Pods(),
ctx.InformerFactory.Batch().V1().Jobs(), ctx.InformerFactory.Batch().V1().Jobs(),
ctx.ClientBuilder.ClientOrDie("job-controller"), ctx.ClientBuilder.ClientOrDie("job-controller"),
).Run(int(ctx.ComponentConfig.JobController.ConcurrentJobSyncs), ctx.Stop) ).Run(int(ctx.ComponentConfig.JobController.ConcurrentJobSyncs), ctx.Stop)
return true, nil return nil, true, nil
} }
func startCronJobController(ctx ControllerContext) (bool, error) { func startCronJobController(ctx ControllerContext) (http.Handler, bool, error) {
if !ctx.AvailableResources[schema.GroupVersionResource{Group: "batch", Version: "v1beta1", Resource: "cronjobs"}] { if !ctx.AvailableResources[schema.GroupVersionResource{Group: "batch", Version: "v1beta1", Resource: "cronjobs"}] {
return false, nil return nil, false, nil
} }
cjc, err := cronjob.NewCronJobController( cjc, err := cronjob.NewCronJobController(
ctx.ClientBuilder.ClientOrDie("cronjob-controller"), ctx.ClientBuilder.ClientOrDie("cronjob-controller"),
) )
if err != nil { if err != nil {
return true, fmt.Errorf("error creating CronJob controller: %v", err) return nil, true, fmt.Errorf("error creating CronJob controller: %v", err)
} }
go cjc.Run(ctx.Stop) go cjc.Run(ctx.Stop)
return true, nil return nil, true, nil
} }
...@@ -19,10 +19,12 @@ package app ...@@ -19,10 +19,12 @@ package app
import ( import (
"fmt" "fmt"
"net/http"
"k8s.io/kubernetes/pkg/controller/bootstrap" "k8s.io/kubernetes/pkg/controller/bootstrap"
) )
func startBootstrapSignerController(ctx ControllerContext) (bool, error) { func startBootstrapSignerController(ctx ControllerContext) (http.Handler, bool, error) {
bsc, err := bootstrap.NewBootstrapSigner( bsc, err := bootstrap.NewBootstrapSigner(
ctx.ClientBuilder.ClientOrDie("bootstrap-signer"), ctx.ClientBuilder.ClientOrDie("bootstrap-signer"),
ctx.InformerFactory.Core().V1().Secrets(), ctx.InformerFactory.Core().V1().Secrets(),
...@@ -30,21 +32,21 @@ func startBootstrapSignerController(ctx ControllerContext) (bool, error) { ...@@ -30,21 +32,21 @@ func startBootstrapSignerController(ctx ControllerContext) (bool, error) {
bootstrap.DefaultBootstrapSignerOptions(), bootstrap.DefaultBootstrapSignerOptions(),
) )
if err != nil { if err != nil {
return true, fmt.Errorf("error creating BootstrapSigner controller: %v", err) return nil, true, fmt.Errorf("error creating BootstrapSigner controller: %v", err)
} }
go bsc.Run(ctx.Stop) go bsc.Run(ctx.Stop)
return true, nil return nil, true, nil
} }
func startTokenCleanerController(ctx ControllerContext) (bool, error) { func startTokenCleanerController(ctx ControllerContext) (http.Handler, bool, error) {
tcc, err := bootstrap.NewTokenCleaner( tcc, err := bootstrap.NewTokenCleaner(
ctx.ClientBuilder.ClientOrDie("token-cleaner"), ctx.ClientBuilder.ClientOrDie("token-cleaner"),
ctx.InformerFactory.Core().V1().Secrets(), ctx.InformerFactory.Core().V1().Secrets(),
bootstrap.DefaultTokenCleanerOptions(), bootstrap.DefaultTokenCleanerOptions(),
) )
if err != nil { if err != nil {
return true, fmt.Errorf("error creating TokenCleaner controller: %v", err) return nil, true, fmt.Errorf("error creating TokenCleaner controller: %v", err)
} }
go tcc.Run(ctx.Stop) go tcc.Run(ctx.Stop)
return true, nil return nil, true, nil
} }
...@@ -26,6 +26,8 @@ import ( ...@@ -26,6 +26,8 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
"net/http"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
kubeoptions "k8s.io/kubernetes/cmd/kube-controller-manager/app/options" kubeoptions "k8s.io/kubernetes/cmd/kube-controller-manager/app/options"
"k8s.io/kubernetes/pkg/controller/certificates/approver" "k8s.io/kubernetes/pkg/controller/certificates/approver"
...@@ -33,12 +35,12 @@ import ( ...@@ -33,12 +35,12 @@ import (
"k8s.io/kubernetes/pkg/controller/certificates/signer" "k8s.io/kubernetes/pkg/controller/certificates/signer"
) )
func startCSRSigningController(ctx ControllerContext) (bool, error) { func startCSRSigningController(ctx ControllerContext) (http.Handler, bool, error) {
if !ctx.AvailableResources[schema.GroupVersionResource{Group: "certificates.k8s.io", Version: "v1beta1", Resource: "certificatesigningrequests"}] { if !ctx.AvailableResources[schema.GroupVersionResource{Group: "certificates.k8s.io", Version: "v1beta1", Resource: "certificatesigningrequests"}] {
return false, nil return nil, false, nil
} }
if ctx.ComponentConfig.CSRSigningController.ClusterSigningCertFile == "" || ctx.ComponentConfig.CSRSigningController.ClusterSigningKeyFile == "" { if ctx.ComponentConfig.CSRSigningController.ClusterSigningCertFile == "" || ctx.ComponentConfig.CSRSigningController.ClusterSigningKeyFile == "" {
return false, nil return nil, false, nil
} }
// Deprecation warning for old defaults. // Deprecation warning for old defaults.
...@@ -72,7 +74,7 @@ func startCSRSigningController(ctx ControllerContext) (bool, error) { ...@@ -72,7 +74,7 @@ func startCSRSigningController(ctx ControllerContext) (bool, error) {
// setting up the signing controller. This isn't // setting up the signing controller. This isn't
// actually a problem since the signer is not a // actually a problem since the signer is not a
// required controller. // required controller.
return false, nil return nil, false, nil
default: default:
// Note that '!filesExist && !usesDefaults' is obviously // Note that '!filesExist && !usesDefaults' is obviously
// operator error. We don't handle this case here and instead // operator error. We don't handle this case here and instead
...@@ -89,16 +91,16 @@ func startCSRSigningController(ctx ControllerContext) (bool, error) { ...@@ -89,16 +91,16 @@ func startCSRSigningController(ctx ControllerContext) (bool, error) {
ctx.ComponentConfig.CSRSigningController.ClusterSigningDuration.Duration, ctx.ComponentConfig.CSRSigningController.ClusterSigningDuration.Duration,
) )
if err != nil { if err != nil {
return false, fmt.Errorf("failed to start certificate controller: %v", err) return nil, false, fmt.Errorf("failed to start certificate controller: %v", err)
} }
go signer.Run(1, ctx.Stop) go signer.Run(1, ctx.Stop)
return true, nil return nil, true, nil
} }
func startCSRApprovingController(ctx ControllerContext) (bool, error) { func startCSRApprovingController(ctx ControllerContext) (http.Handler, bool, error) {
if !ctx.AvailableResources[schema.GroupVersionResource{Group: "certificates.k8s.io", Version: "v1beta1", Resource: "certificatesigningrequests"}] { if !ctx.AvailableResources[schema.GroupVersionResource{Group: "certificates.k8s.io", Version: "v1beta1", Resource: "certificatesigningrequests"}] {
return false, nil return nil, false, nil
} }
approver := approver.NewCSRApprovingController( approver := approver.NewCSRApprovingController(
...@@ -107,14 +109,14 @@ func startCSRApprovingController(ctx ControllerContext) (bool, error) { ...@@ -107,14 +109,14 @@ func startCSRApprovingController(ctx ControllerContext) (bool, error) {
) )
go approver.Run(1, ctx.Stop) go approver.Run(1, ctx.Stop)
return true, nil return nil, true, nil
} }
func startCSRCleanerController(ctx ControllerContext) (bool, error) { func startCSRCleanerController(ctx ControllerContext) (http.Handler, bool, error) {
cleaner := cleaner.NewCSRCleanerController( cleaner := cleaner.NewCSRCleanerController(
ctx.ClientBuilder.ClientOrDie("certificate-controller").CertificatesV1beta1().CertificateSigningRequests(), ctx.ClientBuilder.ClientOrDie("certificate-controller").CertificatesV1beta1().CertificateSigningRequests(),
ctx.InformerFactory.Certificates().V1beta1().CertificateSigningRequests(), ctx.InformerFactory.Certificates().V1beta1().CertificateSigningRequests(),
) )
go cleaner.Run(1, ctx.Stop) go cleaner.Run(1, ctx.Stop)
return true, nil return nil, true, nil
} }
...@@ -31,11 +31,14 @@ import ( ...@@ -31,11 +31,14 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"net/http"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/uuid" "k8s.io/apimachinery/pkg/util/uuid"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/server/mux"
cacheddiscovery "k8s.io/client-go/discovery/cached" cacheddiscovery "k8s.io/client-go/discovery/cached"
"k8s.io/client-go/informers" "k8s.io/client-go/informers"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
...@@ -129,16 +132,18 @@ func Run(c *config.CompletedConfig, stopCh <-chan struct{}) error { ...@@ -129,16 +132,18 @@ func Run(c *config.CompletedConfig, stopCh <-chan struct{}) error {
} }
// Start the controller manager HTTP server // Start the controller manager HTTP server
// unsecuredMux is the handler for these controller *after* authn/authz filters have been applied
var unsecuredMux *mux.PathRecorderMux
if c.SecureServing != nil { if c.SecureServing != nil {
handler := genericcontrollermanager.NewBaseHandler(&c.ComponentConfig.Debugging) unsecuredMux = genericcontrollermanager.NewBaseHandler(&c.ComponentConfig.Debugging)
handler = genericcontrollermanager.BuildHandlerChain(handler, &c.Authorization, &c.Authentication) handler := genericcontrollermanager.BuildHandlerChain(unsecuredMux, &c.Authorization, &c.Authentication)
if err := c.SecureServing.Serve(handler, 0, stopCh); err != nil { if err := c.SecureServing.Serve(handler, 0, stopCh); err != nil {
return err return err
} }
} }
if c.InsecureServing != nil { if c.InsecureServing != nil {
handler := genericcontrollermanager.NewBaseHandler(&c.ComponentConfig.Debugging) unsecuredMux = genericcontrollermanager.NewBaseHandler(&c.ComponentConfig.Debugging)
handler = genericcontrollermanager.BuildHandlerChain(handler, &c.Authorization, &c.Authentication) handler := genericcontrollermanager.BuildHandlerChain(unsecuredMux, &c.Authorization, &c.Authentication)
if err := c.InsecureServing.Serve(handler, 0, stopCh); err != nil { if err := c.InsecureServing.Serve(handler, 0, stopCh); err != nil {
return err return err
} }
...@@ -170,7 +175,7 @@ func Run(c *config.CompletedConfig, stopCh <-chan struct{}) error { ...@@ -170,7 +175,7 @@ func Run(c *config.CompletedConfig, stopCh <-chan struct{}) error {
} }
saTokenControllerInitFunc := serviceAccountTokenControllerStarter{rootClientBuilder: rootClientBuilder}.startServiceAccountTokenController saTokenControllerInitFunc := serviceAccountTokenControllerStarter{rootClientBuilder: rootClientBuilder}.startServiceAccountTokenController
if err := StartControllers(controllerContext, saTokenControllerInitFunc, NewControllerInitializers(controllerContext.LoopMode)); err != nil { if err := StartControllers(controllerContext, saTokenControllerInitFunc, NewControllerInitializers(controllerContext.LoopMode), unsecuredMux); err != nil {
glog.Fatalf("error starting controllers: %v", err) glog.Fatalf("error starting controllers: %v", err)
} }
...@@ -291,7 +296,7 @@ func IsControllerEnabled(name string, disabledByDefaultControllers sets.String, ...@@ -291,7 +296,7 @@ func IsControllerEnabled(name string, disabledByDefaultControllers sets.String,
// InitFunc is used to launch a particular controller. It may run additional "should I activate checks". // InitFunc is used to launch a particular controller. It may run additional "should I activate checks".
// Any error returned will cause the controller process to `Fatal` // Any error returned will cause the controller process to `Fatal`
// The bool indicates whether the controller was enabled. // The bool indicates whether the controller was enabled.
type InitFunc func(ctx ControllerContext) (bool, error) type InitFunc func(ctx ControllerContext) (debuggingHandler http.Handler, enabled bool, err error)
func KnownControllers() []string { func KnownControllers() []string {
ret := sets.StringKeySet(NewControllerInitializers(IncludeCloudLoops)) ret := sets.StringKeySet(NewControllerInitializers(IncludeCloudLoops))
...@@ -434,10 +439,10 @@ func CreateControllerContext(s *config.CompletedConfig, rootClientBuilder, clien ...@@ -434,10 +439,10 @@ func CreateControllerContext(s *config.CompletedConfig, rootClientBuilder, clien
return ctx, nil return ctx, nil
} }
func StartControllers(ctx ControllerContext, startSATokenController InitFunc, controllers map[string]InitFunc) error { func StartControllers(ctx ControllerContext, startSATokenController InitFunc, controllers map[string]InitFunc, unsecuredMux *mux.PathRecorderMux) error {
// Always start the SA token controller first using a full-power client, since it needs to mint tokens for the rest // Always start the SA token controller first using a full-power client, since it needs to mint tokens for the rest
// If this fails, just return here and fail since other controllers won't be able to get credentials. // If this fails, just return here and fail since other controllers won't be able to get credentials.
if _, err := startSATokenController(ctx); err != nil { if _, _, err := startSATokenController(ctx); err != nil {
return err return err
} }
...@@ -456,7 +461,7 @@ func StartControllers(ctx ControllerContext, startSATokenController InitFunc, co ...@@ -456,7 +461,7 @@ func StartControllers(ctx ControllerContext, startSATokenController InitFunc, co
time.Sleep(wait.Jitter(ctx.ComponentConfig.GenericComponent.ControllerStartInterval.Duration, ControllerStartJitter)) time.Sleep(wait.Jitter(ctx.ComponentConfig.GenericComponent.ControllerStartInterval.Duration, ControllerStartJitter))
glog.V(1).Infof("Starting %q", controllerName) glog.V(1).Infof("Starting %q", controllerName)
started, err := initFn(ctx) debugHandler, started, err := initFn(ctx)
if err != nil { if err != nil {
glog.Errorf("Error starting %q", controllerName) glog.Errorf("Error starting %q", controllerName)
return err return err
...@@ -465,6 +470,11 @@ func StartControllers(ctx ControllerContext, startSATokenController InitFunc, co ...@@ -465,6 +470,11 @@ func StartControllers(ctx ControllerContext, startSATokenController InitFunc, co
glog.Warningf("Skipping %q", controllerName) glog.Warningf("Skipping %q", controllerName)
continue continue
} }
if debugHandler != nil && unsecuredMux != nil {
basePath := "/debug/controllers/" + controllerName
unsecuredMux.UnlistedHandle(basePath, http.StripPrefix(basePath, debugHandler))
unsecuredMux.UnlistedHandlePrefix(basePath+"/", http.StripPrefix(basePath, debugHandler))
}
glog.Infof("Started %q", controllerName) glog.Infof("Started %q", controllerName)
} }
...@@ -478,29 +488,29 @@ type serviceAccountTokenControllerStarter struct { ...@@ -478,29 +488,29 @@ type serviceAccountTokenControllerStarter struct {
rootClientBuilder controller.ControllerClientBuilder rootClientBuilder controller.ControllerClientBuilder
} }
func (c serviceAccountTokenControllerStarter) startServiceAccountTokenController(ctx ControllerContext) (bool, error) { func (c serviceAccountTokenControllerStarter) startServiceAccountTokenController(ctx ControllerContext) (http.Handler, bool, error) {
if !ctx.IsControllerEnabled(saTokenControllerName) { if !ctx.IsControllerEnabled(saTokenControllerName) {
glog.Warningf("%q is disabled", saTokenControllerName) glog.Warningf("%q is disabled", saTokenControllerName)
return false, nil return nil, false, nil
} }
if len(ctx.ComponentConfig.SAController.ServiceAccountKeyFile) == 0 { if len(ctx.ComponentConfig.SAController.ServiceAccountKeyFile) == 0 {
glog.Warningf("%q is disabled because there is no private key", saTokenControllerName) glog.Warningf("%q is disabled because there is no private key", saTokenControllerName)
return false, nil return nil, false, nil
} }
privateKey, err := certutil.PrivateKeyFromFile(ctx.ComponentConfig.SAController.ServiceAccountKeyFile) privateKey, err := certutil.PrivateKeyFromFile(ctx.ComponentConfig.SAController.ServiceAccountKeyFile)
if err != nil { if err != nil {
return true, fmt.Errorf("error reading key for service account token controller: %v", err) return nil, true, fmt.Errorf("error reading key for service account token controller: %v", err)
} }
var rootCA []byte var rootCA []byte
if ctx.ComponentConfig.SAController.RootCAFile != "" { if ctx.ComponentConfig.SAController.RootCAFile != "" {
rootCA, err = ioutil.ReadFile(ctx.ComponentConfig.SAController.RootCAFile) rootCA, err = ioutil.ReadFile(ctx.ComponentConfig.SAController.RootCAFile)
if err != nil { if err != nil {
return true, fmt.Errorf("error reading root-ca-file at %s: %v", ctx.ComponentConfig.SAController.RootCAFile, err) return nil, true, fmt.Errorf("error reading root-ca-file at %s: %v", ctx.ComponentConfig.SAController.RootCAFile, err)
} }
if _, err := certutil.ParseCertsPEM(rootCA); err != nil { if _, err := certutil.ParseCertsPEM(rootCA); err != nil {
return true, fmt.Errorf("error parsing root-ca-file at %s: %v", ctx.ComponentConfig.SAController.RootCAFile, err) return nil, true, fmt.Errorf("error parsing root-ca-file at %s: %v", ctx.ComponentConfig.SAController.RootCAFile, err)
} }
} else { } else {
rootCA = c.rootClientBuilder.ConfigOrDie("tokens-controller").CAData rootCA = c.rootClientBuilder.ConfigOrDie("tokens-controller").CAData
...@@ -516,12 +526,12 @@ func (c serviceAccountTokenControllerStarter) startServiceAccountTokenController ...@@ -516,12 +526,12 @@ func (c serviceAccountTokenControllerStarter) startServiceAccountTokenController
}, },
) )
if err != nil { if err != nil {
return true, fmt.Errorf("error creating Tokens controller: %v", err) return nil, true, fmt.Errorf("error creating Tokens controller: %v", err)
} }
go controller.Run(int(ctx.ComponentConfig.SAController.ConcurrentSATokenSyncs), ctx.Stop) go controller.Run(int(ctx.ComponentConfig.SAController.ConcurrentSATokenSyncs), ctx.Stop)
// start the first set of informers now so that other controllers can start // start the first set of informers now so that other controllers can start
ctx.InformerFactory.Start(ctx.Stop) ctx.InformerFactory.Start(ctx.Stop)
return true, nil return nil, true, nil
} }
...@@ -24,10 +24,12 @@ import ( ...@@ -24,10 +24,12 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/controller/disruption" "k8s.io/kubernetes/pkg/controller/disruption"
"net/http"
"github.com/golang/glog" "github.com/golang/glog"
) )
func startDisruptionController(ctx ControllerContext) (bool, error) { func startDisruptionController(ctx ControllerContext) (http.Handler, bool, error) {
var group = "policy" var group = "policy"
var version = "v1beta1" var version = "v1beta1"
var resource = "poddisruptionbudgets" var resource = "poddisruptionbudgets"
...@@ -36,7 +38,7 @@ func startDisruptionController(ctx ControllerContext) (bool, error) { ...@@ -36,7 +38,7 @@ func startDisruptionController(ctx ControllerContext) (bool, error) {
glog.Infof( glog.Infof(
"Refusing to start disruption because resource %q in group %q is not available.", "Refusing to start disruption because resource %q in group %q is not available.",
resource, group+"/"+version) resource, group+"/"+version)
return false, nil return nil, false, nil
} }
go disruption.NewDisruptionController( go disruption.NewDisruptionController(
ctx.InformerFactory.Core().V1().Pods(), ctx.InformerFactory.Core().V1().Pods(),
...@@ -47,5 +49,5 @@ func startDisruptionController(ctx ControllerContext) (bool, error) { ...@@ -47,5 +49,5 @@ func startDisruptionController(ctx ControllerContext) (bool, error) {
ctx.InformerFactory.Apps().V1beta1().StatefulSets(), ctx.InformerFactory.Apps().V1beta1().StatefulSets(),
ctx.ClientBuilder.ClientOrDie("disruption-controller"), ctx.ClientBuilder.ClientOrDie("disruption-controller"),
).Run(ctx.Stop) ).Run(ctx.Stop)
return true, nil return nil, true, nil
} }
...@@ -17,17 +17,19 @@ limitations under the License. ...@@ -17,17 +17,19 @@ limitations under the License.
package app package app
import ( import (
"net/http"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/controller/clusterroleaggregation" "k8s.io/kubernetes/pkg/controller/clusterroleaggregation"
) )
func startClusterRoleAggregrationController(ctx ControllerContext) (bool, error) { func startClusterRoleAggregrationController(ctx ControllerContext) (http.Handler, bool, error) {
if !ctx.AvailableResources[schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterroles"}] { if !ctx.AvailableResources[schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterroles"}] {
return false, nil return nil, false, nil
} }
go clusterroleaggregation.NewClusterRoleAggregation( go clusterroleaggregation.NewClusterRoleAggregation(
ctx.InformerFactory.Rbac().V1().ClusterRoles(), ctx.InformerFactory.Rbac().V1().ClusterRoles(),
ctx.ClientBuilder.ClientOrDie("clusterrole-aggregation-controller").RbacV1(), ctx.ClientBuilder.ClientOrDie("clusterrole-aggregation-controller").RbacV1(),
).Run(5, ctx.Stop) ).Run(5, ctx.Stop)
return true, nil return nil, true, nil
} }
...@@ -73,7 +73,7 @@ process_content () { ...@@ -73,7 +73,7 @@ process_content () {
# Start search at package root # Start search at package root
case ${package} in case ${package} in
github.com/*|golang.org/*|bitbucket.org/*) github.com/*|golang.org/*|bitbucket.org/*|gonum.org/*)
package_root=$(echo "${package}" |awk -F/ '{ print $1"/"$2"/"$3 }') package_root=$(echo "${package}" |awk -F/ '{ print $1"/"$2"/"$3 }')
;; ;;
go4.org/*) go4.org/*)
......
...@@ -9,6 +9,7 @@ load( ...@@ -9,6 +9,7 @@ load(
go_library( go_library(
name = "go_default_library", name = "go_default_library",
srcs = [ srcs = [
"dump.go",
"errors.go", "errors.go",
"garbagecollector.go", "garbagecollector.go",
"graph.go", "graph.go",
...@@ -44,12 +45,19 @@ go_library( ...@@ -44,12 +45,19 @@ go_library(
"//staging/src/k8s.io/client-go/util/workqueue:go_default_library", "//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/golang/groupcache/lru:go_default_library", "//vendor/github.com/golang/groupcache/lru:go_default_library",
"//vendor/gonum.org/v1/gonum/graph:go_default_library",
"//vendor/gonum.org/v1/gonum/graph/encoding:go_default_library",
"//vendor/gonum.org/v1/gonum/graph/encoding/dot:go_default_library",
"//vendor/gonum.org/v1/gonum/graph/simple:go_default_library",
], ],
) )
go_test( go_test(
name = "go_default_test", name = "go_default_test",
srcs = ["garbagecollector_test.go"], srcs = [
"dump_test.go",
"garbagecollector_test.go",
],
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//pkg/api/legacyscheme:go_default_library", "//pkg/api/legacyscheme:go_default_library",
...@@ -70,7 +78,10 @@ go_test( ...@@ -70,7 +78,10 @@ go_test(
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
"//staging/src/k8s.io/client-go/rest:go_default_library", "//staging/src/k8s.io/client-go/rest:go_default_library",
"//staging/src/k8s.io/client-go/util/workqueue:go_default_library", "//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/gonum.org/v1/gonum/graph:go_default_library",
"//vendor/gonum.org/v1/gonum/graph/simple:go_default_library",
], ],
) )
......
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
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.
*/
package garbagecollector
import (
"fmt"
"net/http"
"strings"
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/encoding"
"gonum.org/v1/gonum/graph/encoding/dot"
"gonum.org/v1/gonum/graph/simple"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
)
type gonumVertex struct {
uid types.UID
gvk schema.GroupVersionKind
namespace string
name string
missingFromGraph bool
beingDeleted bool
deletingDependents bool
virtual bool
vertexID int64
}
func (v *gonumVertex) ID() int64 {
return v.vertexID
}
func (v *gonumVertex) String() string {
kind := v.gvk.Kind + "." + v.gvk.Version
if len(v.gvk.Group) > 0 {
kind = kind + "." + v.gvk.Group
}
missing := ""
if v.missingFromGraph {
missing = "(missing)"
}
deleting := ""
if v.beingDeleted {
deleting = "(deleting)"
}
deletingDependents := ""
if v.deletingDependents {
deleting = "(deletingDependents)"
}
virtual := ""
if v.virtual {
virtual = "(virtual)"
}
return fmt.Sprintf(`%s/%s[%s]-%v%s%s%s%s`, kind, v.name, v.namespace, v.uid, missing, deleting, deletingDependents, virtual)
}
func (v *gonumVertex) Attributes() []encoding.Attribute {
kubectlString := v.gvk.Kind + "." + v.gvk.Version
if len(v.gvk.Group) > 0 {
kubectlString = kubectlString + "." + v.gvk.Group
}
kubectlString = kubectlString + "/" + v.name
label := fmt.Sprintf(`uid=%v
namespace=%v
%v
`,
v.uid,
v.namespace,
kubectlString,
)
conditionStrings := []string{}
if v.beingDeleted {
conditionStrings = append(conditionStrings, "beingDeleted")
}
if v.deletingDependents {
conditionStrings = append(conditionStrings, "deletingDependents")
}
if v.virtual {
conditionStrings = append(conditionStrings, "virtual")
}
if v.missingFromGraph {
conditionStrings = append(conditionStrings, "missingFromGraph")
}
conditionString := strings.Join(conditionStrings, ",")
if len(conditionString) > 0 {
label = label + conditionString + "\n"
}
return []encoding.Attribute{
{Key: "label", Value: fmt.Sprintf(`"%v"`, label)},
// these place metadata in the correct location, but don't conform to any normal attribute for rendering
{Key: "group", Value: fmt.Sprintf(`"%v"`, v.gvk.Group)},
{Key: "version", Value: fmt.Sprintf(`"%v"`, v.gvk.Version)},
{Key: "kind", Value: fmt.Sprintf(`"%v"`, v.gvk.Kind)},
{Key: "namespace", Value: fmt.Sprintf(`"%v"`, v.namespace)},
{Key: "name", Value: fmt.Sprintf(`"%v"`, v.name)},
{Key: "uid", Value: fmt.Sprintf(`"%v"`, v.uid)},
{Key: "missing", Value: fmt.Sprintf(`"%v"`, v.missingFromGraph)},
{Key: "beingDeleted", Value: fmt.Sprintf(`"%v"`, v.beingDeleted)},
{Key: "deletingDependents", Value: fmt.Sprintf(`"%v"`, v.deletingDependents)},
{Key: "virtual", Value: fmt.Sprintf(`"%v"`, v.virtual)},
}
}
func NewGonumVertex(node *node, nodeID int64) *gonumVertex {
gv, err := schema.ParseGroupVersion(node.identity.APIVersion)
if err != nil {
// this indicates a bad data serialization that should be prevented during storage of the API
utilruntime.HandleError(err)
}
return &gonumVertex{
uid: node.identity.UID,
gvk: gv.WithKind(node.identity.Kind),
namespace: node.identity.Namespace,
name: node.identity.Name,
beingDeleted: node.beingDeleted,
deletingDependents: node.deletingDependents,
virtual: node.virtual,
vertexID: nodeID,
}
}
func NewMissingGonumVertex(ownerRef metav1.OwnerReference, nodeID int64) *gonumVertex {
gv, err := schema.ParseGroupVersion(ownerRef.APIVersion)
if err != nil {
// this indicates a bad data serialization that should be prevented during storage of the API
utilruntime.HandleError(err)
}
return &gonumVertex{
uid: ownerRef.UID,
gvk: gv.WithKind(ownerRef.Kind),
name: ownerRef.Name,
missingFromGraph: true,
vertexID: nodeID,
}
}
func (m *concurrentUIDToNode) ToGonumGraph() graph.Directed {
m.uidToNodeLock.Lock()
defer m.uidToNodeLock.Unlock()
return toGonumGraph(m.uidToNode)
}
func toGonumGraph(uidToNode map[types.UID]*node) graph.Directed {
uidToVertex := map[types.UID]*gonumVertex{}
graphBuilder := simple.NewDirectedGraph()
// add the vertices first, then edges. That avoids having to deal with missing refs.
for _, node := range uidToNode {
// skip adding objects that don't have owner references and aren't referred to.
if len(node.dependents) == 0 && len(node.owners) == 0 {
continue
}
vertex := NewGonumVertex(node, graphBuilder.NewNode().ID())
uidToVertex[node.identity.UID] = vertex
graphBuilder.AddNode(vertex)
}
for _, node := range uidToNode {
currVertex := uidToVertex[node.identity.UID]
for _, ownerRef := range node.owners {
currOwnerVertex, ok := uidToVertex[ownerRef.UID]
if !ok {
currOwnerVertex = NewMissingGonumVertex(ownerRef, graphBuilder.NewNode().ID())
uidToVertex[node.identity.UID] = currOwnerVertex
graphBuilder.AddNode(currOwnerVertex)
}
graphBuilder.SetEdge(simple.Edge{
F: currVertex,
T: currOwnerVertex,
})
}
}
return graphBuilder
}
func (m *concurrentUIDToNode) ToGonumGraphForObj(uids ...types.UID) graph.Directed {
m.uidToNodeLock.Lock()
defer m.uidToNodeLock.Unlock()
return toGonumGraphForObj(m.uidToNode, uids...)
}
func toGonumGraphForObj(uidToNode map[types.UID]*node, uids ...types.UID) graph.Directed {
uidsToCheck := append([]types.UID{}, uids...)
interestingNodes := map[types.UID]*node{}
// build the set of nodes to inspect first, then use the normal construction on the subset
for i := 0; i < len(uidsToCheck); i++ {
uid := uidsToCheck[i]
// if we've already been observed, there was a bug, but skip it so we don't loop forever
if _, ok := interestingNodes[uid]; ok {
continue
}
node, ok := uidToNode[uid]
// if there is no node for the UID, skip over it. We may add it to the list multiple times
// but we won't loop forever and hopefully the condition doesn't happen very often
if !ok {
continue
}
interestingNodes[node.identity.UID] = node
for _, ownerRef := range node.owners {
// if we've already inspected this UID, don't add it to be inspected again
if _, ok := interestingNodes[ownerRef.UID]; ok {
continue
}
uidsToCheck = append(uidsToCheck, ownerRef.UID)
}
for dependent := range node.dependents {
// if we've already inspected this UID, don't add it to be inspected again
if _, ok := interestingNodes[dependent.identity.UID]; ok {
continue
}
uidsToCheck = append(uidsToCheck, dependent.identity.UID)
}
}
return toGonumGraph(interestingNodes)
}
func NewDebugHandler(controller *GarbageCollector) http.Handler {
return &debugHTTPHandler{controller: controller}
}
type debugHTTPHandler struct {
controller *GarbageCollector
}
func (h *debugHTTPHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if req.URL.Path != "/graph" {
w.WriteHeader(http.StatusNotFound)
return
}
var graph graph.Directed
if uidStrings := req.URL.Query()["uid"]; len(uidStrings) > 0 {
uids := []types.UID{}
for _, uidString := range uidStrings {
uids = append(uids, types.UID(uidString))
}
graph = h.controller.dependencyGraphBuilder.uidToNode.ToGonumGraphForObj(uids...)
} else {
graph = h.controller.dependencyGraphBuilder.uidToNode.ToGonumGraph()
}
data, err := dot.Marshal(graph, "full", "", " ", false)
if err != nil {
w.Write([]byte(err.Error()))
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Write(data)
w.WriteHeader(http.StatusOK)
}
...@@ -390,6 +390,15 @@ filegroup( ...@@ -390,6 +390,15 @@ filegroup(
"//vendor/golang.org/x/tools/go/ast/astutil:all-srcs", "//vendor/golang.org/x/tools/go/ast/astutil:all-srcs",
"//vendor/golang.org/x/tools/go/vcs:all-srcs", "//vendor/golang.org/x/tools/go/vcs:all-srcs",
"//vendor/golang.org/x/tools/imports:all-srcs", "//vendor/golang.org/x/tools/imports:all-srcs",
"//vendor/gonum.org/v1/gonum/blas:all-srcs",
"//vendor/gonum.org/v1/gonum/floats:all-srcs",
"//vendor/gonum.org/v1/gonum/graph:all-srcs",
"//vendor/gonum.org/v1/gonum/internal/asm/c128:all-srcs",
"//vendor/gonum.org/v1/gonum/internal/asm/f32:all-srcs",
"//vendor/gonum.org/v1/gonum/internal/asm/f64:all-srcs",
"//vendor/gonum.org/v1/gonum/internal/math32:all-srcs",
"//vendor/gonum.org/v1/gonum/lapack:all-srcs",
"//vendor/gonum.org/v1/gonum/mat:all-srcs",
"//vendor/google.golang.org/api/compute/v0.alpha:all-srcs", "//vendor/google.golang.org/api/compute/v0.alpha:all-srcs",
"//vendor/google.golang.org/api/compute/v0.beta:all-srcs", "//vendor/google.golang.org/api/compute/v0.beta:all-srcs",
"//vendor/google.golang.org/api/compute/v1:all-srcs", "//vendor/google.golang.org/api/compute/v1:all-srcs",
......
# This is the official list of gonum authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS files.
# See the latter for an explanation.
# Names should be added to this file as
# Name or Organization <email address>
# The email address is not required for organizations.
# Please keep the list sorted.
Brendan Tracey <tracey.brendan@gmail.com>
Bill Gray <wgray@gogray.com>
Bill Noon <noon.bill@gmail.com>
Chad Kunde <kunde21@gmail.com>
Chih-Wei Chang <bert.cwchang@gmail.com>
Chris Tessum <ctessum@gmail.com>
Dan Kortschak <dan.kortschak@adelaide.edu.au> <dan@kortschak.io>
Daniel Fireman <danielfireman@gmail.com>
David Samborski <bloggingarrow@gmail.com>
Davor Kapsa <davor.kapsa@gmail.com>
Egon Elbre <egonelbre@gmail.com>
Ekaterina Efimova <katerina.efimova@gmail.com>
Ethan Burns <burns.ethan@gmail.com>
Evert Lammerts <evert.lammerts@gmail.com>
Facundo Gaich <facugaich@gmail.com>
Fazlul Shahriar <fshahriar@gmail.com>
Francesc Campoy <campoy@golang.org>
Google Inc
Gustaf Johansson <gustaf@pinon.se>
Iakov Davydov <iakov.davydov@unil.ch>
Jalem Raj Rohit <jrajrohit33@gmail.com>
James Bell <james@stellentus.com>
James Bowman <james.edward.bowman@gmail.com>
James Holmes <32bitkid@gmail.com>
Janne Snabb <snabb@epipe.com>
Jeff Juozapaitis <jjjuozap@email.arizona.edu>
Jeremy Atkinson <jchatkinson@gmail.com>
Jonas Kahler <jonas@derkahler.de>
Jonathan J Lawlor <jonathan.lawlor@gmail.com>
Jonathan Schroeder <jd.schroeder@gmail.com>
Joseph Watson <jtwatson@linux-consulting.us>
Josh Wilson <josh.craig.wilson@gmail.com>
Julien Roland <juroland@gmail.com>
Kent English <kent.english@gmail.com>
Kevin C. Zimmerman <kevinczimmerman@gmail.com>
Konstantin Shaposhnikov <k.shaposhnikov@gmail.com>
Leonid Kneller <recondite.matter@gmail.com>
Lyron Winderbaum <lyron.winderbaum@student.adelaide.edu.au>
Matthieu Di Mercurio <matthieu.dimercurio@gmail.com>
Max Halford <maxhalford25@gmail.com>
MinJae Kwon <k239507@gmail.com>
Or Rikon <rikonor@gmail.com>
Pontus Melke <pontusmelke@gmail.com>
Renée French
Robin Eklind <r.eklind.87@gmail.com>
Samuel Kelemen <Samuel@Kelemen.us>
Sam Zaydel <szaydel@gmail.com>
Scott Holden <scott@sshconnection.com>
Sebastien Binet <seb.binet@gmail.com>
source{d} <hello@sourced.tech>
Shawn Smith <shawnpsmith@gmail.com>
Spencer Lyon <spencerlyon2@gmail.com>
Steve McCoy <mccoyst@gmail.com>
Takeshi Yoneda <cz.rk.t0415y.g@gmail.com>
The University of Adelaide
The University of Minnesota
The University of Washington
Tobin Harding <me@tobin.cc>
Vladimír Chalupecký <vladimir.chalupecky@gmail.com>
Yevgeniy Vahlis <evahlis@gmail.com>
# This is the official list of people who can contribute
# (and typically have contributed) code to the gonum
# repository.
#
# The AUTHORS file lists the copyright holders; this file
# lists people. For example, Google employees would be listed here
# but not in AUTHORS, because Google would hold the copyright.
#
# When adding J Random Contributor's name to this file,
# either J's name or J's organization's name should be
# added to the AUTHORS file.
#
# Names should be added to this file like so:
# Name <email address>
#
# Please keep the list sorted.
Andrew Brampton <brampton@gmail.com>
Brendan Tracey <tracey.brendan@gmail.com>
Bill Gray <wgray@gogray.com>
Bill Noon <noon.bill@gmail.com>
Chad Kunde <kunde21@gmail.com>
Chih-Wei Chang <bert.cwchang@gmail.com>
Chris Tessum <ctessum@gmail.com>
Dan Kortschak <dan.kortschak@adelaide.edu.au> <dan@kortschak.io>
Daniel Fireman <danielfireman@gmail.com>
David Samborski <bloggingarrow@gmail.com>
Davor Kapsa <davor.kapsa@gmail.com>
Egon Elbre <egonelbre@gmail.com>
Ekaterina Efimova <katerina.efimova@gmail.com>
Ethan Burns <burns.ethan@gmail.com>
Evert Lammerts <evert.lammerts@gmail.com>
Facundo Gaich <facugaich@gmail.com>
Fazlul Shahriar <fshahriar@gmail.com>
Francesc Campoy <campoy@golang.org>
Gustaf Johansson <gustaf@pinon.se>
Iakov Davydov <iakov.davydov@unil.ch>
Jalem Raj Rohit <jrajrohit33@gmail.com>
James Bell <james@stellentus.com>
James Bowman <james.edward.bowman@gmail.com>
James Holmes <32bitkid@gmail.com>
Janne Snabb <snabb@epipe.com>
Jeff Juozapaitis <jjjuozap@email.arizona.edu>
Jeremy Atkinson <jchatkinson@gmail.com>
Jonas Kahler <jonas@derkahler.de>
Jonathan J Lawlor <jonathan.lawlor@gmail.com>
Jonathan Schroeder <jd.schroeder@gmail.com>
Joseph Watson <jtwatson@linux-consulting.us>
Josh Wilson <josh.craig.wilson@gmail.com>
Julien Roland <juroland@gmail.com>
Kent English <kent.english@gmail.com>
Kevin C. Zimmerman <kevinczimmerman@gmail.com>
Konstantin Shaposhnikov <k.shaposhnikov@gmail.com>
Leonid Kneller <recondite.matter@gmail.com>
Lyron Winderbaum <lyron.winderbaum@student.adelaide.edu.au>
Matthieu Di Mercurio <matthieu.dimercurio@gmail.com>
Max Halford <maxhalford25@gmail.com>
MinJae Kwon <k239507@gmail.com>
Or Rikon <rikonor@gmail.com>
Pontus Melke <pontusmelke@gmail.com>
Renée French
Robin Eklind <r.eklind.87@gmail.com>
Samuel Kelemen <Samuel@Kelemen.us>
Sam Zaydel <szaydel@gmail.com>
Scott Holden <scott@sshconnection.com>
Sebastien Binet <seb.binet@gmail.com>
Shawn Smith <shawnpsmith@gmail.com>
Spencer Lyon <spencerlyon2@gmail.com>
Steve McCoy <mccoyst@gmail.com>
Takeshi Yoneda <cz.rk.t0415y.g@gmail.com>
Tobin Harding <me@tobin.cc>
Vladimír Chalupecký <vladimir.chalupecky@gmail.com>
Yevgeniy Vahlis <evahlis@gmail.com>
Copyright ©2013 The Gonum Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the gonum project nor the names of its authors and
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"blas.go",
"doc.go",
],
importmap = "k8s.io/kubernetes/vendor/gonum.org/v1/gonum/blas",
importpath = "gonum.org/v1/gonum/blas",
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/gonum.org/v1/gonum/blas/blas64:all-srcs",
"//vendor/gonum.org/v1/gonum/blas/gonum:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
# Gonum BLAS [![GoDoc](https://godoc.org/gonum.org/v1/gonum/blas?status.svg)](https://godoc.org/gonum.org/v1/gonum/blas)
A collection of packages to provide BLAS functionality for the [Go programming
language](http://golang.org)
## Installation
```sh
go get gonum.org/v1/gonum/blas/...
```
## Packages
### blas
Defines [BLAS API](http://www.netlib.org/blas/blast-forum/cinterface.pdf) split in several
interfaces.
### blas/gonum
Go implementation of the BLAS API (incomplete, implements the `float32` and `float64` API).
### blas/blas64 and blas/blas32
Wrappers for an implementation of the double (i.e., `float64`) and single (`float32`)
precision real parts of the BLAS API.
```Go
package main
import (
"fmt"
"gonum.org/v1/gonum/blas/blas64"
)
func main() {
v := blas64.Vector{Inc: 1, Data: []float64{1, 1, 1}}
fmt.Println("v has length:", blas64.Nrm2(len(v.Data), v))
}
```
### blas/cblas128 and blas/cblas64
Wrappers for an implementation of the double (i.e., `complex128`) and single (`complex64`)
precision complex parts of the blas API.
Currently blas/cblas64 and blas/cblas128 require gonum.org/v1/netlib/blas.
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"blas64.go",
"conv.go",
"conv_symmetric.go",
"doc.go",
],
importmap = "k8s.io/kubernetes/vendor/gonum.org/v1/gonum/blas/blas64",
importpath = "gonum.org/v1/gonum/blas/blas64",
visibility = ["//visibility:public"],
deps = [
"//vendor/gonum.org/v1/gonum/blas:go_default_library",
"//vendor/gonum.org/v1/gonum/blas/gonum:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Copyright ©2015 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package blas64
import "gonum.org/v1/gonum/blas"
// GeneralCols represents a matrix using the conventional column-major storage scheme.
type GeneralCols General
// From fills the receiver with elements from a. The receiver
// must have the same dimensions as a and have adequate backing
// data storage.
func (t GeneralCols) From(a General) {
if t.Rows != a.Rows || t.Cols != a.Cols {
panic("blas64: mismatched dimension")
}
if len(t.Data) < (t.Cols-1)*t.Stride+t.Rows {
panic("blas64: short data slice")
}
for i := 0; i < a.Rows; i++ {
for j, v := range a.Data[i*a.Stride : i*a.Stride+a.Cols] {
t.Data[i+j*t.Stride] = v
}
}
}
// From fills the receiver with elements from a. The receiver
// must have the same dimensions as a and have adequate backing
// data storage.
func (t General) From(a GeneralCols) {
if t.Rows != a.Rows || t.Cols != a.Cols {
panic("blas64: mismatched dimension")
}
if len(t.Data) < (t.Rows-1)*t.Stride+t.Cols {
panic("blas64: short data slice")
}
for j := 0; j < a.Cols; j++ {
for i, v := range a.Data[j*a.Stride : j*a.Stride+a.Rows] {
t.Data[i*t.Stride+j] = v
}
}
}
// TriangularCols represents a matrix using the conventional column-major storage scheme.
type TriangularCols Triangular
// From fills the receiver with elements from a. The receiver
// must have the same dimensions, uplo and diag as a and have
// adequate backing data storage.
func (t TriangularCols) From(a Triangular) {
if t.N != a.N {
panic("blas64: mismatched dimension")
}
if t.Uplo != a.Uplo {
panic("blas64: mismatched BLAS uplo")
}
if t.Diag != a.Diag {
panic("blas64: mismatched BLAS diag")
}
switch a.Uplo {
default:
panic("blas64: bad BLAS uplo")
case blas.Upper:
for i := 0; i < a.N; i++ {
for j := i; j < a.N; j++ {
t.Data[i+j*t.Stride] = a.Data[i*a.Stride+j]
}
}
case blas.Lower:
for i := 0; i < a.N; i++ {
for j := 0; j <= i; j++ {
t.Data[i+j*t.Stride] = a.Data[i*a.Stride+j]
}
}
case blas.All:
for i := 0; i < a.N; i++ {
for j := 0; j < a.N; j++ {
t.Data[i+j*t.Stride] = a.Data[i*a.Stride+j]
}
}
}
}
// From fills the receiver with elements from a. The receiver
// must have the same dimensions, uplo and diag as a and have
// adequate backing data storage.
func (t Triangular) From(a TriangularCols) {
if t.N != a.N {
panic("blas64: mismatched dimension")
}
if t.Uplo != a.Uplo {
panic("blas64: mismatched BLAS uplo")
}
if t.Diag != a.Diag {
panic("blas64: mismatched BLAS diag")
}
switch a.Uplo {
default:
panic("blas64: bad BLAS uplo")
case blas.Upper:
for i := 0; i < a.N; i++ {
for j := i; j < a.N; j++ {
t.Data[i*t.Stride+j] = a.Data[i+j*a.Stride]
}
}
case blas.Lower:
for i := 0; i < a.N; i++ {
for j := 0; j <= i; j++ {
t.Data[i*t.Stride+j] = a.Data[i+j*a.Stride]
}
}
case blas.All:
for i := 0; i < a.N; i++ {
for j := 0; j < a.N; j++ {
t.Data[i*t.Stride+j] = a.Data[i+j*a.Stride]
}
}
}
}
// BandCols represents a matrix using the band column-major storage scheme.
type BandCols Band
// From fills the receiver with elements from a. The receiver
// must have the same dimensions and bandwidth as a and have
// adequate backing data storage.
func (t BandCols) From(a Band) {
if t.Rows != a.Rows || t.Cols != a.Cols {
panic("blas64: mismatched dimension")
}
if t.KL != a.KL || t.KU != a.KU {
panic("blas64: mismatched bandwidth")
}
if a.Stride < a.KL+a.KU+1 {
panic("blas64: short stride for source")
}
if t.Stride < t.KL+t.KU+1 {
panic("blas64: short stride for destination")
}
for i := 0; i < a.Rows; i++ {
for j := max(0, i-a.KL); j < min(i+a.KU+1, a.Cols); j++ {
t.Data[i+t.KU-j+j*t.Stride] = a.Data[j+a.KL-i+i*a.Stride]
}
}
}
// From fills the receiver with elements from a. The receiver
// must have the same dimensions and bandwidth as a and have
// adequate backing data storage.
func (t Band) From(a BandCols) {
if t.Rows != a.Rows || t.Cols != a.Cols {
panic("blas64: mismatched dimension")
}
if t.KL != a.KL || t.KU != a.KU {
panic("blas64: mismatched bandwidth")
}
if a.Stride < a.KL+a.KU+1 {
panic("blas64: short stride for source")
}
if t.Stride < t.KL+t.KU+1 {
panic("blas64: short stride for destination")
}
for j := 0; j < a.Cols; j++ {
for i := max(0, j-a.KU); i < min(j+a.KL+1, a.Rows); i++ {
t.Data[j+a.KL-i+i*a.Stride] = a.Data[i+t.KU-j+j*t.Stride]
}
}
}
// TriangularBandCols represents a symmetric matrix using the band column-major storage scheme.
type TriangularBandCols TriangularBand
// From fills the receiver with elements from a. The receiver
// must have the same dimensions, bandwidth and uplo as a and
// have adequate backing data storage.
func (t TriangularBandCols) From(a TriangularBand) {
if t.N != a.N {
panic("blas64: mismatched dimension")
}
if t.K != a.K {
panic("blas64: mismatched bandwidth")
}
if a.Stride < a.K+1 {
panic("blas64: short stride for source")
}
if t.Stride < t.K+1 {
panic("blas64: short stride for destination")
}
if t.Uplo != a.Uplo {
panic("blas64: mismatched BLAS uplo")
}
if t.Diag != a.Diag {
panic("blas64: mismatched BLAS diag")
}
dst := BandCols{
Rows: t.N, Cols: t.N,
Stride: t.Stride,
Data: t.Data,
}
src := Band{
Rows: a.N, Cols: a.N,
Stride: a.Stride,
Data: a.Data,
}
switch a.Uplo {
default:
panic("blas64: bad BLAS uplo")
case blas.Upper:
dst.KU = t.K
src.KU = a.K
case blas.Lower:
dst.KL = t.K
src.KL = a.K
}
dst.From(src)
}
// From fills the receiver with elements from a. The receiver
// must have the same dimensions, bandwidth and uplo as a and
// have adequate backing data storage.
func (t TriangularBand) From(a TriangularBandCols) {
if t.N != a.N {
panic("blas64: mismatched dimension")
}
if t.K != a.K {
panic("blas64: mismatched bandwidth")
}
if a.Stride < a.K+1 {
panic("blas64: short stride for source")
}
if t.Stride < t.K+1 {
panic("blas64: short stride for destination")
}
if t.Uplo != a.Uplo {
panic("blas64: mismatched BLAS uplo")
}
if t.Diag != a.Diag {
panic("blas64: mismatched BLAS diag")
}
dst := Band{
Rows: t.N, Cols: t.N,
Stride: t.Stride,
Data: t.Data,
}
src := BandCols{
Rows: a.N, Cols: a.N,
Stride: a.Stride,
Data: a.Data,
}
switch a.Uplo {
default:
panic("blas64: bad BLAS uplo")
case blas.Upper:
dst.KU = t.K
src.KU = a.K
case blas.Lower:
dst.KL = t.K
src.KL = a.K
}
dst.From(src)
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
// Copyright ©2015 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package blas64
import "gonum.org/v1/gonum/blas"
// SymmetricCols represents a matrix using the conventional column-major storage scheme.
type SymmetricCols Symmetric
// From fills the receiver with elements from a. The receiver
// must have the same dimensions and uplo as a and have adequate
// backing data storage.
func (t SymmetricCols) From(a Symmetric) {
if t.N != a.N {
panic("blas64: mismatched dimension")
}
if t.Uplo != a.Uplo {
panic("blas64: mismatched BLAS uplo")
}
switch a.Uplo {
default:
panic("blas64: bad BLAS uplo")
case blas.Upper:
for i := 0; i < a.N; i++ {
for j := i; j < a.N; j++ {
t.Data[i+j*t.Stride] = a.Data[i*a.Stride+j]
}
}
case blas.Lower:
for i := 0; i < a.N; i++ {
for j := 0; j <= i; j++ {
t.Data[i+j*t.Stride] = a.Data[i*a.Stride+j]
}
}
}
}
// From fills the receiver with elements from a. The receiver
// must have the same dimensions and uplo as a and have adequate
// backing data storage.
func (t Symmetric) From(a SymmetricCols) {
if t.N != a.N {
panic("blas64: mismatched dimension")
}
if t.Uplo != a.Uplo {
panic("blas64: mismatched BLAS uplo")
}
switch a.Uplo {
default:
panic("blas64: bad BLAS uplo")
case blas.Upper:
for i := 0; i < a.N; i++ {
for j := i; j < a.N; j++ {
t.Data[i*t.Stride+j] = a.Data[i+j*a.Stride]
}
}
case blas.Lower:
for i := 0; i < a.N; i++ {
for j := 0; j <= i; j++ {
t.Data[i*t.Stride+j] = a.Data[i+j*a.Stride]
}
}
}
}
// SymmetricBandCols represents a symmetric matrix using the band column-major storage scheme.
type SymmetricBandCols SymmetricBand
// From fills the receiver with elements from a. The receiver
// must have the same dimensions, bandwidth and uplo as a and
// have adequate backing data storage.
func (t SymmetricBandCols) From(a SymmetricBand) {
if t.N != a.N {
panic("blas64: mismatched dimension")
}
if t.K != a.K {
panic("blas64: mismatched bandwidth")
}
if a.Stride < a.K+1 {
panic("blas64: short stride for source")
}
if t.Stride < t.K+1 {
panic("blas64: short stride for destination")
}
if t.Uplo != a.Uplo {
panic("blas64: mismatched BLAS uplo")
}
dst := BandCols{
Rows: t.N, Cols: t.N,
Stride: t.Stride,
Data: t.Data,
}
src := Band{
Rows: a.N, Cols: a.N,
Stride: a.Stride,
Data: a.Data,
}
switch a.Uplo {
default:
panic("blas64: bad BLAS uplo")
case blas.Upper:
dst.KU = t.K
src.KU = a.K
case blas.Lower:
dst.KL = t.K
src.KL = a.K
}
dst.From(src)
}
// From fills the receiver with elements from a. The receiver
// must have the same dimensions, bandwidth and uplo as a and
// have adequate backing data storage.
func (t SymmetricBand) From(a SymmetricBandCols) {
if t.N != a.N {
panic("blas64: mismatched dimension")
}
if t.K != a.K {
panic("blas64: mismatched bandwidth")
}
if a.Stride < a.K+1 {
panic("blas64: short stride for source")
}
if t.Stride < t.K+1 {
panic("blas64: short stride for destination")
}
if t.Uplo != a.Uplo {
panic("blas64: mismatched BLAS uplo")
}
dst := Band{
Rows: t.N, Cols: t.N,
Stride: t.Stride,
Data: t.Data,
}
src := BandCols{
Rows: a.N, Cols: a.N,
Stride: a.Stride,
Data: a.Data,
}
switch a.Uplo {
default:
panic("blas64: bad BLAS uplo")
case blas.Upper:
dst.KU = t.K
src.KU = a.K
case blas.Lower:
dst.KL = t.K
src.KL = a.K
}
dst.From(src)
}
// Copyright ©2017 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package blas64 provides a simple interface to the float64 BLAS API.
package blas64
#!/usr/bin/env bash
# Copyright ©2017 The Gonum Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
# Generate code for blas32.
echo Generating blas32/conv.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas”; DO NOT EDIT.\n' > blas32/conv.go
cat blas64/conv.go \
| gofmt -r 'float64 -> float32' \
\
| sed -e 's/blas64/blas32/' \
\
>> blas32/conv.go
echo Generating blas32/conv_test.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas”; DO NOT EDIT.\n' > blas32/conv_test.go
cat blas64/conv_test.go \
| gofmt -r 'float64 -> float32' \
\
| sed -e 's/blas64/blas32/' \
-e 's_"math"_math "gonum.org/v1/gonum/internal/math32"_' \
\
>> blas32/conv_test.go
echo Generating blas32/conv_symmetric.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas”; DO NOT EDIT.\n' > blas32/conv_symmetric.go
cat blas64/conv_symmetric.go \
| gofmt -r 'float64 -> float32' \
\
| sed -e 's/blas64/blas32/' \
\
>> blas32/conv_symmetric.go
echo Generating blas32/conv_symmetric_test.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas”; DO NOT EDIT.\n' > blas32/conv_symmetric_test.go
cat blas64/conv_symmetric_test.go \
| gofmt -r 'float64 -> float32' \
\
| sed -e 's/blas64/blas32/' \
-e 's_"math"_math "gonum.org/v1/gonum/internal/math32"_' \
\
>> blas32/conv_symmetric_test.go
# Generate code for cblas128.
echo Generating cblas128/conv.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas”; DO NOT EDIT.\n' > cblas128/conv.go
cat blas64/conv.go \
| gofmt -r 'float64 -> complex128' \
\
| sed -e 's/blas64/cblas128/' \
\
>> cblas128/conv.go
echo Generating cblas128/conv_test.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas”; DO NOT EDIT.\n' > cblas128/conv_test.go
cat blas64/conv_test.go \
| gofmt -r 'float64 -> complex128' \
\
| sed -e 's/blas64/cblas128/' \
-e 's_"math"_math "math/cmplx"_' \
\
>> cblas128/conv_test.go
echo Generating cblas128/conv_symmetric.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas”; DO NOT EDIT.\n' > cblas128/conv_symmetric.go
cat blas64/conv_symmetric.go \
| gofmt -r 'float64 -> complex128' \
\
| sed -e 's/blas64/cblas128/' \
\
>> cblas128/conv_symmetric.go
echo Generating cblas128/conv_symmetric_test.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas”; DO NOT EDIT.\n' > cblas128/conv_symmetric_test.go
cat blas64/conv_symmetric_test.go \
| gofmt -r 'float64 -> complex128' \
\
| sed -e 's/blas64/cblas128/' \
-e 's_"math"_math "math/cmplx"_' \
\
>> cblas128/conv_symmetric_test.go
echo Generating cblas128/conv_hermitian.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas”; DO NOT EDIT.\n' > cblas128/conv_hermitian.go
cat blas64/conv_symmetric.go \
| gofmt -r 'float64 -> complex128' \
\
| sed -e 's/blas64/cblas128/' \
-e 's/Symmetric/Hermitian/g' \
-e 's/a symmetric/an Hermitian/g' \
-e 's/symmetric/hermitian/g' \
-e 's/Sym/Herm/g' \
\
>> cblas128/conv_hermitian.go
echo Generating cblas128/conv_hermitian_test.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas”; DO NOT EDIT.\n' > cblas128/conv_hermitian_test.go
cat blas64/conv_symmetric_test.go \
| gofmt -r 'float64 -> complex128' \
\
| sed -e 's/blas64/cblas128/' \
-e 's/Symmetric/Hermitian/g' \
-e 's/a symmetric/an Hermitian/g' \
-e 's/symmetric/hermitian/g' \
-e 's/Sym/Herm/g' \
-e 's_"math"_math "math/cmplx"_' \
\
>> cblas128/conv_hermitian_test.go
# Generate code for cblas64.
echo Generating cblas64/conv.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas”; DO NOT EDIT.\n' > cblas64/conv.go
cat blas64/conv.go \
| gofmt -r 'float64 -> complex64' \
\
| sed -e 's/blas64/cblas64/' \
\
>> cblas64/conv.go
echo Generating cblas64/conv_test.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas”; DO NOT EDIT.\n' > cblas64/conv_test.go
cat blas64/conv_test.go \
| gofmt -r 'float64 -> complex64' \
\
| sed -e 's/blas64/cblas64/' \
-e 's_"math"_math "gonum.org/v1/gonum/internal/cmplx64"_' \
\
>> cblas64/conv_test.go
echo Generating cblas64/conv_hermitian.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas”; DO NOT EDIT.\n' > cblas64/conv_hermitian.go
cat blas64/conv_symmetric.go \
| gofmt -r 'float64 -> complex64' \
\
| sed -e 's/blas64/cblas64/' \
-e 's/Symmetric/Hermitian/g' \
-e 's/a symmetric/an Hermitian/g' \
-e 's/symmetric/hermitian/g' \
-e 's/Sym/Herm/g' \
\
>> cblas64/conv_hermitian.go
echo Generating cblas64/conv_hermitian_test.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas”; DO NOT EDIT.\n' > cblas64/conv_hermitian_test.go
cat blas64/conv_symmetric_test.go \
| gofmt -r 'float64 -> complex64' \
\
| sed -e 's/blas64/cblas64/' \
-e 's/Symmetric/Hermitian/g' \
-e 's/a symmetric/an Hermitian/g' \
-e 's/symmetric/hermitian/g' \
-e 's/Sym/Herm/g' \
-e 's_"math"_math "gonum.org/v1/gonum/internal/cmplx64"_' \
\
>> cblas64/conv_hermitian_test.go
// Copyright ©2017 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package blas provides interfaces for the BLAS linear algebra standard.
All methods must perform appropriate parameter checking and panic if
provided parameters that do not conform to the requirements specified
by the BLAS standard.
Quick Reference Guide to the BLAS from http://www.netlib.org/lapack/lug/node145.html
This version is modified to remove the "order" option. All matrix operations are
on row-order matrices.
Level 1 BLAS
dim scalar vector vector scalars 5-element prefixes
struct
_rotg ( a, b ) S, D
_rotmg( d1, d2, a, b ) S, D
_rot ( n, x, incX, y, incY, c, s ) S, D
_rotm ( n, x, incX, y, incY, param ) S, D
_swap ( n, x, incX, y, incY ) S, D, C, Z
_scal ( n, alpha, x, incX ) S, D, C, Z, Cs, Zd
_copy ( n, x, incX, y, incY ) S, D, C, Z
_axpy ( n, alpha, x, incX, y, incY ) S, D, C, Z
_dot ( n, x, incX, y, incY ) S, D, Ds
_dotu ( n, x, incX, y, incY ) C, Z
_dotc ( n, x, incX, y, incY ) C, Z
__dot ( n, alpha, x, incX, y, incY ) Sds
_nrm2 ( n, x, incX ) S, D, Sc, Dz
_asum ( n, x, incX ) S, D, Sc, Dz
I_amax( n, x, incX ) s, d, c, z
Level 2 BLAS
options dim b-width scalar matrix vector scalar vector prefixes
_gemv ( trans, m, n, alpha, a, lda, x, incX, beta, y, incY ) S, D, C, Z
_gbmv ( trans, m, n, kL, kU, alpha, a, lda, x, incX, beta, y, incY ) S, D, C, Z
_hemv ( uplo, n, alpha, a, lda, x, incX, beta, y, incY ) C, Z
_hbmv ( uplo, n, k, alpha, a, lda, x, incX, beta, y, incY ) C, Z
_hpmv ( uplo, n, alpha, ap, x, incX, beta, y, incY ) C, Z
_symv ( uplo, n, alpha, a, lda, x, incX, beta, y, incY ) S, D
_sbmv ( uplo, n, k, alpha, a, lda, x, incX, beta, y, incY ) S, D
_spmv ( uplo, n, alpha, ap, x, incX, beta, y, incY ) S, D
_trmv ( uplo, trans, diag, n, a, lda, x, incX ) S, D, C, Z
_tbmv ( uplo, trans, diag, n, k, a, lda, x, incX ) S, D, C, Z
_tpmv ( uplo, trans, diag, n, ap, x, incX ) S, D, C, Z
_trsv ( uplo, trans, diag, n, a, lda, x, incX ) S, D, C, Z
_tbsv ( uplo, trans, diag, n, k, a, lda, x, incX ) S, D, C, Z
_tpsv ( uplo, trans, diag, n, ap, x, incX ) S, D, C, Z
options dim scalar vector vector matrix prefixes
_ger ( m, n, alpha, x, incX, y, incY, a, lda ) S, D
_geru ( m, n, alpha, x, incX, y, incY, a, lda ) C, Z
_gerc ( m, n, alpha, x, incX, y, incY, a, lda ) C, Z
_her ( uplo, n, alpha, x, incX, a, lda ) C, Z
_hpr ( uplo, n, alpha, x, incX, ap ) C, Z
_her2 ( uplo, n, alpha, x, incX, y, incY, a, lda ) C, Z
_hpr2 ( uplo, n, alpha, x, incX, y, incY, ap ) C, Z
_syr ( uplo, n, alpha, x, incX, a, lda ) S, D
_spr ( uplo, n, alpha, x, incX, ap ) S, D
_syr2 ( uplo, n, alpha, x, incX, y, incY, a, lda ) S, D
_spr2 ( uplo, n, alpha, x, incX, y, incY, ap ) S, D
Level 3 BLAS
options dim scalar matrix matrix scalar matrix prefixes
_gemm ( transA, transB, m, n, k, alpha, a, lda, b, ldb, beta, c, ldc ) S, D, C, Z
_symm ( side, uplo, m, n, alpha, a, lda, b, ldb, beta, c, ldc ) S, D, C, Z
_hemm ( side, uplo, m, n, alpha, a, lda, b, ldb, beta, c, ldc ) C, Z
_syrk ( uplo, trans, n, k, alpha, a, lda, beta, c, ldc ) S, D, C, Z
_herk ( uplo, trans, n, k, alpha, a, lda, beta, c, ldc ) C, Z
_syr2k( uplo, trans, n, k, alpha, a, lda, b, ldb, beta, c, ldc ) S, D, C, Z
_her2k( uplo, trans, n, k, alpha, a, lda, b, ldb, beta, c, ldc ) C, Z
_trmm ( side, uplo, transA, diag, m, n, alpha, a, lda, b, ldb ) S, D, C, Z
_trsm ( side, uplo, transA, diag, m, n, alpha, a, lda, b, ldb ) S, D, C, Z
Meaning of prefixes
S - float32 C - complex64
D - float64 Z - complex128
Matrix types
GE - GEneral GB - General Band
SY - SYmmetric SB - Symmetric Band SP - Symmetric Packed
HE - HErmitian HB - Hermitian Band HP - Hermitian Packed
TR - TRiangular TB - Triangular Band TP - Triangular Packed
Options
trans = NoTrans, Trans, ConjTrans
uplo = Upper, Lower
diag = Nonunit, Unit
side = Left, Right (A or op(A) on the left, or A or op(A) on the right)
For real matrices, Trans and ConjTrans have the same meaning.
For Hermitian matrices, trans = Trans is not allowed.
For complex symmetric matrices, trans = ConjTrans is not allowed.
*/
package blas
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"cmplx.go",
"dgemm.go",
"doc.go",
"gemv.go",
"gonum.go",
"level1cmplx128.go",
"level1double.go",
"level1double_ddot.go",
"level1single.go",
"level1single_dsdot.go",
"level1single_sdot.go",
"level1single_sdsdot.go",
"level2cmplx128.go",
"level2double.go",
"level2single.go",
"level3double.go",
"level3single.go",
"sgemm.go",
],
importmap = "k8s.io/kubernetes/vendor/gonum.org/v1/gonum/blas/gonum",
importpath = "gonum.org/v1/gonum/blas/gonum",
visibility = ["//visibility:public"],
deps = [
"//vendor/gonum.org/v1/gonum/blas:go_default_library",
"//vendor/gonum.org/v1/gonum/internal/asm/c128:go_default_library",
"//vendor/gonum.org/v1/gonum/internal/asm/f32:go_default_library",
"//vendor/gonum.org/v1/gonum/internal/asm/f64:go_default_library",
"//vendor/gonum.org/v1/gonum/internal/math32:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Copyright ©2017 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gonum
import "gonum.org/v1/gonum/blas"
var (
_ blas.Complex64 = Implementation{}
_ blas.Complex128 = Implementation{}
)
// TODO(btracey): Replace this as complex routines are added, and instead
// automatically generate the complex64 routines from the complex128 ones.
var noComplex = "native: implementation does not implement this routine, see the cgo wrapper in gonum.org/v1/netlib/blas"
// Level 1 complex64 routines.
func (Implementation) Cdotu(n int, x []complex64, incX int, y []complex64, incY int) (dotu complex64) {
panic(noComplex)
}
func (Implementation) Cdotc(n int, x []complex64, incX int, y []complex64, incY int) (dotc complex64) {
panic(noComplex)
}
func (Implementation) Scnrm2(n int, x []complex64, incX int) float32 {
panic(noComplex)
}
func (Implementation) Scasum(n int, x []complex64, incX int) float32 {
panic(noComplex)
}
func (Implementation) Icamax(n int, x []complex64, incX int) int {
panic(noComplex)
}
func (Implementation) Cswap(n int, x []complex64, incX int, y []complex64, incY int) {
panic(noComplex)
}
func (Implementation) Ccopy(n int, x []complex64, incX int, y []complex64, incY int) {
panic(noComplex)
}
func (Implementation) Caxpy(n int, alpha complex64, x []complex64, incX int, y []complex64, incY int) {
panic(noComplex)
}
func (Implementation) Cscal(n int, alpha complex64, x []complex64, incX int) {
panic(noComplex)
}
func (Implementation) Csscal(n int, alpha float32, x []complex64, incX int) {
panic(noComplex)
}
// Level 2 complex64 routines.
func (Implementation) Cgemv(tA blas.Transpose, m, n int, alpha complex64, a []complex64, lda int, x []complex64, incX int, beta complex64, y []complex64, incY int) {
panic(noComplex)
}
func (Implementation) Cgbmv(tA blas.Transpose, m, n, kL, kU int, alpha complex64, a []complex64, lda int, x []complex64, incX int, beta complex64, y []complex64, incY int) {
panic(noComplex)
}
func (Implementation) Ctrmv(ul blas.Uplo, tA blas.Transpose, d blas.Diag, n int, a []complex64, lda int, x []complex64, incX int) {
panic(noComplex)
}
func (Implementation) Ctbmv(ul blas.Uplo, tA blas.Transpose, d blas.Diag, n, k int, a []complex64, lda int, x []complex64, incX int) {
panic(noComplex)
}
func (Implementation) Ctpmv(ul blas.Uplo, tA blas.Transpose, d blas.Diag, n int, ap []complex64, x []complex64, incX int) {
panic(noComplex)
}
func (Implementation) Ctrsv(ul blas.Uplo, tA blas.Transpose, d blas.Diag, n int, a []complex64, lda int, x []complex64, incX int) {
panic(noComplex)
}
func (Implementation) Ctbsv(ul blas.Uplo, tA blas.Transpose, d blas.Diag, n, k int, a []complex64, lda int, x []complex64, incX int) {
panic(noComplex)
}
func (Implementation) Ctpsv(ul blas.Uplo, tA blas.Transpose, d blas.Diag, n int, ap []complex64, x []complex64, incX int) {
panic(noComplex)
}
func (Implementation) Chemv(ul blas.Uplo, n int, alpha complex64, a []complex64, lda int, x []complex64, incX int, beta complex64, y []complex64, incY int) {
panic(noComplex)
}
func (Implementation) Chbmv(ul blas.Uplo, n, k int, alpha complex64, a []complex64, lda int, x []complex64, incX int, beta complex64, y []complex64, incY int) {
panic(noComplex)
}
func (Implementation) Chpmv(ul blas.Uplo, n int, alpha complex64, ap []complex64, x []complex64, incX int, beta complex64, y []complex64, incY int) {
panic(noComplex)
}
func (Implementation) Cgeru(m, n int, alpha complex64, x []complex64, incX int, y []complex64, incY int, a []complex64, lda int) {
panic(noComplex)
}
func (Implementation) Cgerc(m, n int, alpha complex64, x []complex64, incX int, y []complex64, incY int, a []complex64, lda int) {
panic(noComplex)
}
func (Implementation) Cher(ul blas.Uplo, n int, alpha float32, x []complex64, incX int, a []complex64, lda int) {
panic(noComplex)
}
func (Implementation) Chpr(ul blas.Uplo, n int, alpha float32, x []complex64, incX int, a []complex64) {
panic(noComplex)
}
func (Implementation) Cher2(ul blas.Uplo, n int, alpha complex64, x []complex64, incX int, y []complex64, incY int, a []complex64, lda int) {
panic(noComplex)
}
func (Implementation) Chpr2(ul blas.Uplo, n int, alpha complex64, x []complex64, incX int, y []complex64, incY int, ap []complex64) {
panic(noComplex)
}
// Level 3 complex64 routines.
func (Implementation) Cgemm(tA, tB blas.Transpose, m, n, k int, alpha complex64, a []complex64, lda int, b []complex64, ldb int, beta complex64, c []complex64, ldc int) {
panic(noComplex)
}
func (Implementation) Csymm(s blas.Side, ul blas.Uplo, m, n int, alpha complex64, a []complex64, lda int, b []complex64, ldb int, beta complex64, c []complex64, ldc int) {
panic(noComplex)
}
func (Implementation) Csyrk(ul blas.Uplo, t blas.Transpose, n, k int, alpha complex64, a []complex64, lda int, beta complex64, c []complex64, ldc int) {
panic(noComplex)
}
func (Implementation) Csyr2k(ul blas.Uplo, t blas.Transpose, n, k int, alpha complex64, a []complex64, lda int, b []complex64, ldb int, beta complex64, c []complex64, ldc int) {
panic(noComplex)
}
func (Implementation) Ctrmm(s blas.Side, ul blas.Uplo, tA blas.Transpose, d blas.Diag, m, n int, alpha complex64, a []complex64, lda int, b []complex64, ldb int) {
panic(noComplex)
}
func (Implementation) Ctrsm(s blas.Side, ul blas.Uplo, tA blas.Transpose, d blas.Diag, m, n int, alpha complex64, a []complex64, lda int, b []complex64, ldb int) {
panic(noComplex)
}
func (Implementation) Chemm(s blas.Side, ul blas.Uplo, m, n int, alpha complex64, a []complex64, lda int, b []complex64, ldb int, beta complex64, c []complex64, ldc int) {
panic(noComplex)
}
func (Implementation) Cherk(ul blas.Uplo, t blas.Transpose, n, k int, alpha float32, a []complex64, lda int, beta float32, c []complex64, ldc int) {
panic(noComplex)
}
func (Implementation) Cher2k(ul blas.Uplo, t blas.Transpose, n, k int, alpha complex64, a []complex64, lda int, b []complex64, ldb int, beta float32, c []complex64, ldc int) {
panic(noComplex)
}
// Level 3 complex128 routines.
func (Implementation) Zgemm(tA, tB blas.Transpose, m, n, k int, alpha complex128, a []complex128, lda int, b []complex128, ldb int, beta complex128, c []complex128, ldc int) {
panic(noComplex)
}
func (Implementation) Zsymm(s blas.Side, ul blas.Uplo, m, n int, alpha complex128, a []complex128, lda int, b []complex128, ldb int, beta complex128, c []complex128, ldc int) {
panic(noComplex)
}
func (Implementation) Zsyrk(ul blas.Uplo, t blas.Transpose, n, k int, alpha complex128, a []complex128, lda int, beta complex128, c []complex128, ldc int) {
panic(noComplex)
}
func (Implementation) Zsyr2k(ul blas.Uplo, t blas.Transpose, n, k int, alpha complex128, a []complex128, lda int, b []complex128, ldb int, beta complex128, c []complex128, ldc int) {
panic(noComplex)
}
func (Implementation) Ztrmm(s blas.Side, ul blas.Uplo, tA blas.Transpose, d blas.Diag, m, n int, alpha complex128, a []complex128, lda int, b []complex128, ldb int) {
panic(noComplex)
}
func (Implementation) Ztrsm(s blas.Side, ul blas.Uplo, tA blas.Transpose, d blas.Diag, m, n int, alpha complex128, a []complex128, lda int, b []complex128, ldb int) {
panic(noComplex)
}
func (Implementation) Zhemm(s blas.Side, ul blas.Uplo, m, n int, alpha complex128, a []complex128, lda int, b []complex128, ldb int, beta complex128, c []complex128, ldc int) {
panic(noComplex)
}
func (Implementation) Zherk(ul blas.Uplo, t blas.Transpose, n, k int, alpha float64, a []complex128, lda int, beta float64, c []complex128, ldc int) {
panic(noComplex)
}
func (Implementation) Zher2k(ul blas.Uplo, t blas.Transpose, n, k int, alpha complex128, a []complex128, lda int, b []complex128, ldb int, beta float64, c []complex128, ldc int) {
panic(noComplex)
}
// Copyright ©2014 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gonum
import (
"runtime"
"sync"
"gonum.org/v1/gonum/blas"
"gonum.org/v1/gonum/internal/asm/f64"
)
// Dgemm performs one of the matrix-matrix operations
// C = alpha * A * B + beta * C
// C = alpha * A^T * B + beta * C
// C = alpha * A * B^T + beta * C
// C = alpha * A^T * B^T + beta * C
// where A is an m×k or k×m dense matrix, B is an n×k or k×n dense matrix, C is
// an m×n matrix, and alpha and beta are scalars. tA and tB specify whether A or
// B are transposed.
func (Implementation) Dgemm(tA, tB blas.Transpose, m, n, k int, alpha float64, a []float64, lda int, b []float64, ldb int, beta float64, c []float64, ldc int) {
if tA != blas.NoTrans && tA != blas.Trans && tA != blas.ConjTrans {
panic(badTranspose)
}
if tB != blas.NoTrans && tB != blas.Trans && tB != blas.ConjTrans {
panic(badTranspose)
}
aTrans := tA == blas.Trans || tA == blas.ConjTrans
if aTrans {
checkDMatrix('a', k, m, a, lda)
} else {
checkDMatrix('a', m, k, a, lda)
}
bTrans := tB == blas.Trans || tB == blas.ConjTrans
if bTrans {
checkDMatrix('b', n, k, b, ldb)
} else {
checkDMatrix('b', k, n, b, ldb)
}
checkDMatrix('c', m, n, c, ldc)
// scale c
if beta != 1 {
if beta == 0 {
for i := 0; i < m; i++ {
ctmp := c[i*ldc : i*ldc+n]
for j := range ctmp {
ctmp[j] = 0
}
}
} else {
for i := 0; i < m; i++ {
ctmp := c[i*ldc : i*ldc+n]
for j := range ctmp {
ctmp[j] *= beta
}
}
}
}
dgemmParallel(aTrans, bTrans, m, n, k, a, lda, b, ldb, c, ldc, alpha)
}
func dgemmParallel(aTrans, bTrans bool, m, n, k int, a []float64, lda int, b []float64, ldb int, c []float64, ldc int, alpha float64) {
// dgemmParallel computes a parallel matrix multiplication by partitioning
// a and b into sub-blocks, and updating c with the multiplication of the sub-block
// In all cases,
// A = [ A_11 A_12 ... A_1j
// A_21 A_22 ... A_2j
// ...
// A_i1 A_i2 ... A_ij]
//
// and same for B. All of the submatrix sizes are blockSize×blockSize except
// at the edges.
//
// In all cases, there is one dimension for each matrix along which
// C must be updated sequentially.
// Cij = \sum_k Aik Bki, (A * B)
// Cij = \sum_k Aki Bkj, (A^T * B)
// Cij = \sum_k Aik Bjk, (A * B^T)
// Cij = \sum_k Aki Bjk, (A^T * B^T)
//
// This code computes one {i, j} block sequentially along the k dimension,
// and computes all of the {i, j} blocks concurrently. This
// partitioning allows Cij to be updated in-place without race-conditions.
// Instead of launching a goroutine for each possible concurrent computation,
// a number of worker goroutines are created and channels are used to pass
// available and completed cases.
//
// http://alexkr.com/docs/matrixmult.pdf is a good reference on matrix-matrix
// multiplies, though this code does not copy matrices to attempt to eliminate
// cache misses.
maxKLen := k
parBlocks := blocks(m, blockSize) * blocks(n, blockSize)
if parBlocks < minParBlock {
// The matrix multiplication is small in the dimensions where it can be
// computed concurrently. Just do it in serial.
dgemmSerial(aTrans, bTrans, m, n, k, a, lda, b, ldb, c, ldc, alpha)
return
}
nWorkers := runtime.GOMAXPROCS(0)
if parBlocks < nWorkers {
nWorkers = parBlocks
}
// There is a tradeoff between the workers having to wait for work
// and a large buffer making operations slow.
buf := buffMul * nWorkers
if buf > parBlocks {
buf = parBlocks
}
sendChan := make(chan subMul, buf)
// Launch workers. A worker receives an {i, j} submatrix of c, and computes
// A_ik B_ki (or the transposed version) storing the result in c_ij. When the
// channel is finally closed, it signals to the waitgroup that it has finished
// computing.
var wg sync.WaitGroup
for i := 0; i < nWorkers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// Make local copies of otherwise global variables to reduce shared memory.
// This has a noticeable effect on benchmarks in some cases.
alpha := alpha
aTrans := aTrans
bTrans := bTrans
m := m
n := n
for sub := range sendChan {
i := sub.i
j := sub.j
leni := blockSize
if i+leni > m {
leni = m - i
}
lenj := blockSize
if j+lenj > n {
lenj = n - j
}
cSub := sliceView64(c, ldc, i, j, leni, lenj)
// Compute A_ik B_kj for all k
for k := 0; k < maxKLen; k += blockSize {
lenk := blockSize
if k+lenk > maxKLen {
lenk = maxKLen - k
}
var aSub, bSub []float64
if aTrans {
aSub = sliceView64(a, lda, k, i, lenk, leni)
} else {
aSub = sliceView64(a, lda, i, k, leni, lenk)
}
if bTrans {
bSub = sliceView64(b, ldb, j, k, lenj, lenk)
} else {
bSub = sliceView64(b, ldb, k, j, lenk, lenj)
}
dgemmSerial(aTrans, bTrans, leni, lenj, lenk, aSub, lda, bSub, ldb, cSub, ldc, alpha)
}
}
}()
}
// Send out all of the {i, j} subblocks for computation.
for i := 0; i < m; i += blockSize {
for j := 0; j < n; j += blockSize {
sendChan <- subMul{
i: i,
j: j,
}
}
}
close(sendChan)
wg.Wait()
}
// dgemmSerial is serial matrix multiply
func dgemmSerial(aTrans, bTrans bool, m, n, k int, a []float64, lda int, b []float64, ldb int, c []float64, ldc int, alpha float64) {
switch {
case !aTrans && !bTrans:
dgemmSerialNotNot(m, n, k, a, lda, b, ldb, c, ldc, alpha)
return
case aTrans && !bTrans:
dgemmSerialTransNot(m, n, k, a, lda, b, ldb, c, ldc, alpha)
return
case !aTrans && bTrans:
dgemmSerialNotTrans(m, n, k, a, lda, b, ldb, c, ldc, alpha)
return
case aTrans && bTrans:
dgemmSerialTransTrans(m, n, k, a, lda, b, ldb, c, ldc, alpha)
return
default:
panic("unreachable")
}
}
// dgemmSerial where neither a nor b are transposed
func dgemmSerialNotNot(m, n, k int, a []float64, lda int, b []float64, ldb int, c []float64, ldc int, alpha float64) {
// This style is used instead of the literal [i*stride +j]) is used because
// approximately 5 times faster as of go 1.3.
for i := 0; i < m; i++ {
ctmp := c[i*ldc : i*ldc+n]
for l, v := range a[i*lda : i*lda+k] {
tmp := alpha * v
if tmp != 0 {
f64.AxpyUnitaryTo(ctmp, tmp, b[l*ldb:l*ldb+n], ctmp)
}
}
}
}
// dgemmSerial where neither a is transposed and b is not
func dgemmSerialTransNot(m, n, k int, a []float64, lda int, b []float64, ldb int, c []float64, ldc int, alpha float64) {
// This style is used instead of the literal [i*stride +j]) is used because
// approximately 5 times faster as of go 1.3.
for l := 0; l < k; l++ {
btmp := b[l*ldb : l*ldb+n]
for i, v := range a[l*lda : l*lda+m] {
tmp := alpha * v
if tmp != 0 {
ctmp := c[i*ldc : i*ldc+n]
f64.AxpyUnitaryTo(ctmp, tmp, btmp, ctmp)
}
}
}
}
// dgemmSerial where neither a is not transposed and b is
func dgemmSerialNotTrans(m, n, k int, a []float64, lda int, b []float64, ldb int, c []float64, ldc int, alpha float64) {
// This style is used instead of the literal [i*stride +j]) is used because
// approximately 5 times faster as of go 1.3.
for i := 0; i < m; i++ {
atmp := a[i*lda : i*lda+k]
ctmp := c[i*ldc : i*ldc+n]
for j := 0; j < n; j++ {
ctmp[j] += alpha * f64.DotUnitary(atmp, b[j*ldb:j*ldb+k])
}
}
}
// dgemmSerial where both are transposed
func dgemmSerialTransTrans(m, n, k int, a []float64, lda int, b []float64, ldb int, c []float64, ldc int, alpha float64) {
// This style is used instead of the literal [i*stride +j]) is used because
// approximately 5 times faster as of go 1.3.
for l := 0; l < k; l++ {
for i, v := range a[l*lda : l*lda+m] {
tmp := alpha * v
if tmp != 0 {
ctmp := c[i*ldc : i*ldc+n]
f64.AxpyInc(tmp, b[l:], ctmp, uintptr(n), uintptr(ldb), 1, 0, 0)
}
}
}
}
func sliceView64(a []float64, lda, i, j, r, c int) []float64 {
return a[i*lda+j : (i+r-1)*lda+j+c]
}
// Copyright ©2015 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Ensure changes made to blas/native are reflected in blas/cgo where relevant.
/*
Package gonum is a Go implementation of the BLAS API. This implementation
panics when the input arguments are invalid as per the standard, for example
if a vector increment is zero. Note that the treatment of NaN values
is not specified, and differs among the BLAS implementations.
gonum.org/v1/gonum/blas/blas64 provides helpful wrapper functions to the BLAS
interface. The rest of this text describes the layout of the data for the input types.
Note that in the function documentation, x[i] refers to the i^th element
of the vector, which will be different from the i^th element of the slice if
incX != 1.
See http://www.netlib.org/lapack/explore-html/d4/de1/_l_i_c_e_n_s_e_source.html
for more license information.
Vector arguments are effectively strided slices. They have two input arguments,
a number of elements, n, and an increment, incX. The increment specifies the
distance between elements of the vector. The actual Go slice may be longer
than necessary.
The increment may be positive or negative, except in functions with only
a single vector argument where the increment may only be positive. If the increment
is negative, s[0] is the last element in the slice. Note that this is not the same
as counting backward from the end of the slice, as len(s) may be longer than
necessary. So, for example, if n = 5 and incX = 3, the elements of s are
[0 * * 1 * * 2 * * 3 * * 4 * * * ...]
where ∗ elements are never accessed. If incX = -3, the same elements are
accessed, just in reverse order (4, 3, 2, 1, 0).
Dense matrices are specified by a number of rows, a number of columns, and a stride.
The stride specifies the number of entries in the slice between the first element
of successive rows. The stride must be at least as large as the number of columns
but may be longer.
[a00 ... a0n a0* ... a1stride-1 a21 ... amn am* ... amstride-1]
Thus, dense[i*ld + j] refers to the {i, j}th element of the matrix.
Symmetric and triangular matrices (non-packed) are stored identically to Dense,
except that only elements in one triangle of the matrix are accessed.
Packed symmetric and packed triangular matrices are laid out with the entries
condensed such that all of the unreferenced elements are removed. So, the upper triangular
matrix
[
1 2 3
0 4 5
0 0 6
]
and the lower-triangular matrix
[
1 0 0
2 3 0
4 5 6
]
will both be compacted as [1 2 3 4 5 6]. The (i, j) element of the original
dense matrix can be found at element i*n - (i-1)*i/2 + j for upper triangular,
and at element i * (i+1) /2 + j for lower triangular.
Banded matrices are laid out in a compact format, constructed by removing the
zeros in the rows and aligning the diagonals. For example, the matrix
[
1 2 3 0 0 0
4 5 6 7 0 0
0 8 9 10 11 0
0 0 12 13 14 15
0 0 0 16 17 18
0 0 0 0 19 20
]
implicitly becomes (∗ entries are never accessed)
[
* 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
16 17 18 *
19 20 * *
]
which is given to the BLAS routine as [∗ 1 2 3 4 ...].
See http://www.crest.iu.edu/research/mtl/reference/html/banded.html
for more information
*/
package gonum
// Copyright ©2018 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gonum
import (
"gonum.org/v1/gonum/blas"
"gonum.org/v1/gonum/internal/asm/f32"
"gonum.org/v1/gonum/internal/asm/f64"
)
// TODO(Kunde21): Merge these methods back into level2double/level2single when Sgemv assembly kernels are merged into f32.
// Dgemv computes
// y = alpha * A * x + beta * y if tA = blas.NoTrans
// y = alpha * A^T * x + beta * y if tA = blas.Trans or blas.ConjTrans
// where A is an m×n dense matrix, x and y are vectors, and alpha and beta are scalars.
func (Implementation) Dgemv(tA blas.Transpose, m, n int, alpha float64, a []float64, lda int, x []float64, incX int, beta float64, y []float64, incY int) {
if tA != blas.NoTrans && tA != blas.Trans && tA != blas.ConjTrans {
panic(badTranspose)
}
if m < 0 {
panic(mLT0)
}
if n < 0 {
panic(nLT0)
}
if lda < max(1, n) {
panic(badLdA)
}
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
// Set up indexes
lenX := m
lenY := n
if tA == blas.NoTrans {
lenX = n
lenY = m
}
if (incX > 0 && (lenX-1)*incX >= len(x)) || (incX < 0 && (1-lenX)*incX >= len(x)) {
panic(badX)
}
if (incY > 0 && (lenY-1)*incY >= len(y)) || (incY < 0 && (1-lenY)*incY >= len(y)) {
panic(badY)
}
if lda*(m-1)+n > len(a) || lda < max(1, n) {
panic(badLdA)
}
// Quick return if possible
if m == 0 || n == 0 || (alpha == 0 && beta == 1) {
return
}
if alpha == 0 {
// First form y = beta * y
if incY > 0 {
Implementation{}.Dscal(lenY, beta, y, incY)
} else {
Implementation{}.Dscal(lenY, beta, y, -incY)
}
return
}
// Form y = alpha * A * x + y
if tA == blas.NoTrans {
f64.GemvN(uintptr(m), uintptr(n), alpha, a, uintptr(lda), x, uintptr(incX), beta, y, uintptr(incY))
return
}
// Cases where a is transposed.
f64.GemvT(uintptr(m), uintptr(n), alpha, a, uintptr(lda), x, uintptr(incX), beta, y, uintptr(incY))
}
// Sgemv computes
// y = alpha * A * x + beta * y if tA = blas.NoTrans
// y = alpha * A^T * x + beta * y if tA = blas.Trans or blas.ConjTrans
// where A is an m×n dense matrix, x and y are vectors, and alpha and beta are scalars.
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Sgemv(tA blas.Transpose, m, n int, alpha float32, a []float32, lda int, x []float32, incX int, beta float32, y []float32, incY int) {
if tA != blas.NoTrans && tA != blas.Trans && tA != blas.ConjTrans {
panic(badTranspose)
}
if m < 0 {
panic(mLT0)
}
if n < 0 {
panic(nLT0)
}
if lda < max(1, n) {
panic(badLdA)
}
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
// Set up indexes
lenX := m
lenY := n
if tA == blas.NoTrans {
lenX = n
lenY = m
}
if (incX > 0 && (lenX-1)*incX >= len(x)) || (incX < 0 && (1-lenX)*incX >= len(x)) {
panic(badX)
}
if (incY > 0 && (lenY-1)*incY >= len(y)) || (incY < 0 && (1-lenY)*incY >= len(y)) {
panic(badY)
}
if lda*(m-1)+n > len(a) || lda < max(1, n) {
panic(badLdA)
}
// Quick return if possible
if m == 0 || n == 0 || (alpha == 0 && beta == 1) {
return
}
var kx, ky int
if incX < 0 {
kx = -(lenX - 1) * incX
}
if incY < 0 {
ky = -(lenY - 1) * incY
}
// First form y = beta * y
if incY > 0 {
Implementation{}.Sscal(lenY, beta, y, incY)
} else {
Implementation{}.Sscal(lenY, beta, y, -incY)
}
if alpha == 0 {
return
}
// Form y = alpha * A * x + y
if tA == blas.NoTrans {
if incX == 1 && incY == 1 {
for i := 0; i < m; i++ {
y[i] += alpha * f32.DotUnitary(a[lda*i:lda*i+n], x)
}
return
}
iy := ky
for i := 0; i < m; i++ {
y[iy] += alpha * f32.DotInc(x, a[lda*i:lda*i+n], uintptr(n), uintptr(incX), 1, uintptr(kx), 0)
iy += incY
}
return
}
// Cases where a is transposed.
if incX == 1 && incY == 1 {
for i := 0; i < m; i++ {
tmp := alpha * x[i]
if tmp != 0 {
f32.AxpyUnitaryTo(y, tmp, a[lda*i:lda*i+n], y)
}
}
return
}
ix := kx
for i := 0; i < m; i++ {
tmp := alpha * x[ix]
if tmp != 0 {
f32.AxpyInc(tmp, a[lda*i:lda*i+n], y, uintptr(n), 1, uintptr(incY), 0, uintptr(ky))
}
ix += incX
}
}
// Copyright ©2015 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:generate ./single_precision.bash
package gonum
import "math"
type Implementation struct{}
// The following are panic strings used during parameter checks.
const (
zeroIncX = "blas: zero x index increment"
zeroIncY = "blas: zero y index increment"
mLT0 = "blas: m < 0"
nLT0 = "blas: n < 0"
kLT0 = "blas: k < 0"
kLLT0 = "blas: kL < 0"
kULT0 = "blas: kU < 0"
badUplo = "blas: illegal triangle"
badTranspose = "blas: illegal transpose"
badDiag = "blas: illegal diagonal"
badSide = "blas: illegal side"
badLdA = "blas: bad leading dimension of A"
badLdB = "blas: bad leading dimension of B"
badLdC = "blas: bad leading dimension of C"
badX = "blas: bad length of x"
badY = "blas: bad length of y"
)
// [SD]gemm behavior constants. These are kept here to keep them out of the
// way during single precision code genration.
const (
blockSize = 64 // b x b matrix
minParBlock = 4 // minimum number of blocks needed to go parallel
buffMul = 4 // how big is the buffer relative to the number of workers
)
// subMul is a common type shared by [SD]gemm.
type subMul struct {
i, j int // index of block
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
func checkSMatrix(name byte, m, n int, a []float32, lda int) {
if m < 0 {
panic(mLT0)
}
if n < 0 {
panic(nLT0)
}
if lda < n {
panic("blas: illegal stride of " + string(name))
}
if len(a) < (m-1)*lda+n {
panic("blas: index of " + string(name) + " out of range")
}
}
func checkDMatrix(name byte, m, n int, a []float64, lda int) {
if m < 0 {
panic(mLT0)
}
if n < 0 {
panic(nLT0)
}
if lda < n {
panic("blas: illegal stride of " + string(name))
}
if len(a) < (m-1)*lda+n {
panic("blas: index of " + string(name) + " out of range")
}
}
func checkZMatrix(name byte, m, n int, a []complex128, lda int) {
if m < 0 {
panic(mLT0)
}
if n < 0 {
panic(nLT0)
}
if lda < max(1, n) {
panic("blas: illegal stride of " + string(name))
}
if len(a) < (m-1)*lda+n {
panic("blas: insufficient " + string(name) + " matrix slice length")
}
}
func checkZBandMatrix(name byte, m, n, kL, kU int, ab []complex128, ldab int) {
if m < 0 {
panic(mLT0)
}
if n < 0 {
panic(nLT0)
}
if kL < 0 {
panic(kLLT0)
}
if kU < 0 {
panic(kULT0)
}
if ldab < kL+kU+1 {
panic("blas: illegal stride of band matrix " + string(name))
}
nRow := min(m, n+kL)
if len(ab) < (nRow-1)*ldab+kL+1+kU {
panic("blas: insufficient " + string(name) + " band matrix slice length")
}
}
func checkZhbMatrix(name byte, n, k int, ab []complex128, ldab int) {
if n < 0 {
panic(nLT0)
}
if k < 0 {
panic(kLT0)
}
if ldab < k+1 {
panic("blas: illegal stride of Hermitian band matrix " + string(name))
}
if len(ab) < (n-1)*ldab+k+1 {
panic("blas: insufficient " + string(name) + " Hermitian band matrix slice length")
}
}
func checkZtbMatrix(name byte, n, k int, ab []complex128, ldab int) {
if n < 0 {
panic(nLT0)
}
if k < 0 {
panic(kLT0)
}
if ldab < k+1 {
panic("blas: illegal stride of triangular band matrix " + string(name))
}
if len(ab) < (n-1)*ldab+k+1 {
panic("blas: insufficient " + string(name) + " triangular band matrix slice length")
}
}
func checkZVector(name byte, n int, x []complex128, incX int) {
if n < 0 {
panic(nLT0)
}
if incX == 0 {
panic(zeroIncX)
}
if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
panic("blas: insufficient " + string(name) + " vector slice length")
}
}
// blocks returns the number of divisions of the dimension length with the given
// block size.
func blocks(dim, bsize int) int {
return (dim + bsize - 1) / bsize
}
// dcabs1 returns |real(z)|+|imag(z)|.
func dcabs1(z complex128) float64 {
return math.Abs(real(z)) + math.Abs(imag(z))
}
// Copyright ©2017 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gonum
import (
"math"
"gonum.org/v1/gonum/internal/asm/c128"
)
// Dzasum returns the sum of the absolute values of the elements of x
// \sum_i |Re(x[i])| + |Im(x[i])|
// Dzasum returns 0 if incX is negative.
func (Implementation) Dzasum(n int, x []complex128, incX int) float64 {
if n < 0 {
panic(nLT0)
}
if incX < 1 {
if incX == 0 {
panic(zeroIncX)
}
return 0
}
var sum float64
if incX == 1 {
if len(x) < n {
panic(badX)
}
for _, v := range x[:n] {
sum += dcabs1(v)
}
return sum
}
if (n-1)*incX >= len(x) {
panic(badX)
}
for i := 0; i < n; i++ {
v := x[i*incX]
sum += dcabs1(v)
}
return sum
}
// Dznrm2 computes the Euclidean norm of the complex vector x,
// ‖x‖_2 = sqrt(\sum_i x[i] * conj(x[i])).
// This function returns 0 if incX is negative.
func (Implementation) Dznrm2(n int, x []complex128, incX int) float64 {
if incX < 1 {
if incX == 0 {
panic(zeroIncX)
}
return 0
}
if n < 1 {
if n == 0 {
return 0
}
panic(nLT0)
}
if (n-1)*incX >= len(x) {
panic(badX)
}
var (
scale float64
ssq float64 = 1
)
if incX == 1 {
for _, v := range x[:n] {
re, im := math.Abs(real(v)), math.Abs(imag(v))
if re != 0 {
if re > scale {
ssq = 1 + ssq*(scale/re)*(scale/re)
scale = re
} else {
ssq += (re / scale) * (re / scale)
}
}
if im != 0 {
if im > scale {
ssq = 1 + ssq*(scale/im)*(scale/im)
scale = im
} else {
ssq += (im / scale) * (im / scale)
}
}
}
if math.IsInf(scale, 1) {
return math.Inf(1)
}
return scale * math.Sqrt(ssq)
}
for ix := 0; ix < n*incX; ix += incX {
re, im := math.Abs(real(x[ix])), math.Abs(imag(x[ix]))
if re != 0 {
if re > scale {
ssq = 1 + ssq*(scale/re)*(scale/re)
scale = re
} else {
ssq += (re / scale) * (re / scale)
}
}
if im != 0 {
if im > scale {
ssq = 1 + ssq*(scale/im)*(scale/im)
scale = im
} else {
ssq += (im / scale) * (im / scale)
}
}
}
if math.IsInf(scale, 1) {
return math.Inf(1)
}
return scale * math.Sqrt(ssq)
}
// Izamax returns the index of the first element of x having largest |Re(·)|+|Im(·)|.
// Izamax returns -1 if n is 0 or incX is negative.
func (Implementation) Izamax(n int, x []complex128, incX int) int {
if incX < 1 {
if incX == 0 {
panic(zeroIncX)
}
// Return invalid index.
return -1
}
if n < 1 {
if n == 0 {
// Return invalid index.
return -1
}
panic(nLT0)
}
if len(x) <= (n-1)*incX {
panic(badX)
}
idx := 0
max := dcabs1(x[0])
if incX == 1 {
for i, v := range x[1:n] {
absV := dcabs1(v)
if absV > max {
max = absV
idx = i + 1
}
}
return idx
}
ix := incX
for i := 1; i < n; i++ {
absV := dcabs1(x[ix])
if absV > max {
max = absV
idx = i
}
ix += incX
}
return idx
}
// Zaxpy adds alpha times x to y:
// y[i] += alpha * x[i] for all i
func (Implementation) Zaxpy(n int, alpha complex128, x []complex128, incX int, y []complex128, incY int) {
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if n < 1 {
if n == 0 {
return
}
panic(nLT0)
}
if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
panic(badX)
}
if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
panic(badY)
}
if alpha == 0 {
return
}
if incX == 1 && incY == 1 {
c128.AxpyUnitary(alpha, x[:n], y[:n])
return
}
var ix, iy int
if incX < 0 {
ix = (1 - n) * incX
}
if incY < 0 {
iy = (1 - n) * incY
}
c128.AxpyInc(alpha, x, y, uintptr(n), uintptr(incX), uintptr(incY), uintptr(ix), uintptr(iy))
}
// Zcopy copies the vector x to vector y.
func (Implementation) Zcopy(n int, x []complex128, incX int, y []complex128, incY int) {
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if n < 1 {
if n == 0 {
return
}
panic(nLT0)
}
if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
panic(badX)
}
if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
panic(badY)
}
if incX == 1 && incY == 1 {
copy(y[:n], x[:n])
return
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
for i := 0; i < n; i++ {
y[iy] = x[ix]
ix += incX
iy += incY
}
}
// Zdotc computes the dot product
// x^H · y
// of two complex vectors x and y.
func (Implementation) Zdotc(n int, x []complex128, incX int, y []complex128, incY int) complex128 {
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if n <= 0 {
if n == 0 {
return 0
}
panic(nLT0)
}
if incX == 1 && incY == 1 {
if len(x) < n {
panic(badX)
}
if len(y) < n {
panic(badY)
}
return c128.DotcUnitary(x[:n], y[:n])
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
if ix >= len(x) || (n-1)*incX >= len(x) {
panic(badX)
}
if iy >= len(y) || (n-1)*incY >= len(y) {
panic(badY)
}
return c128.DotcInc(x, y, uintptr(n), uintptr(incX), uintptr(incY), uintptr(ix), uintptr(iy))
}
// Zdotu computes the dot product
// x^T · y
// of two complex vectors x and y.
func (Implementation) Zdotu(n int, x []complex128, incX int, y []complex128, incY int) complex128 {
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if n <= 0 {
if n == 0 {
return 0
}
panic(nLT0)
}
if incX == 1 && incY == 1 {
if len(x) < n {
panic(badX)
}
if len(y) < n {
panic(badY)
}
return c128.DotuUnitary(x[:n], y[:n])
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
if ix >= len(x) || (n-1)*incX >= len(x) {
panic(badX)
}
if iy >= len(y) || (n-1)*incY >= len(y) {
panic(badY)
}
return c128.DotuInc(x, y, uintptr(n), uintptr(incX), uintptr(incY), uintptr(ix), uintptr(iy))
}
// Zdscal scales the vector x by a real scalar alpha.
// Zdscal has no effect if incX < 0.
func (Implementation) Zdscal(n int, alpha float64, x []complex128, incX int) {
if incX < 1 {
if incX == 0 {
panic(zeroIncX)
}
return
}
if (n-1)*incX >= len(x) {
panic(badX)
}
if n < 1 {
if n == 0 {
return
}
panic(nLT0)
}
if alpha == 0 {
if incX == 1 {
x = x[:n]
for i := range x {
x[i] = 0
}
return
}
for ix := 0; ix < n*incX; ix += incX {
x[ix] = 0
}
return
}
if incX == 1 {
x = x[:n]
for i, v := range x {
x[i] = complex(alpha*real(v), alpha*imag(v))
}
return
}
for ix := 0; ix < n*incX; ix += incX {
v := x[ix]
x[ix] = complex(alpha*real(v), alpha*imag(v))
}
}
// Zscal scales the vector x by a complex scalar alpha.
// Zscal has no effect if incX < 0.
func (Implementation) Zscal(n int, alpha complex128, x []complex128, incX int) {
if incX < 1 {
if incX == 0 {
panic(zeroIncX)
}
return
}
if (n-1)*incX >= len(x) {
panic(badX)
}
if n < 1 {
if n == 0 {
return
}
panic(nLT0)
}
if alpha == 0 {
if incX == 1 {
x = x[:n]
for i := range x {
x[i] = 0
}
return
}
for ix := 0; ix < n*incX; ix += incX {
x[ix] = 0
}
return
}
if incX == 1 {
c128.ScalUnitary(alpha, x[:n])
return
}
c128.ScalInc(alpha, x, uintptr(n), uintptr(incX))
}
// Zswap exchanges the elements of two complex vectors x and y.
func (Implementation) Zswap(n int, x []complex128, incX int, y []complex128, incY int) {
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if n < 1 {
if n == 0 {
return
}
panic(nLT0)
}
if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
panic(badX)
}
if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
panic(badY)
}
if incX == 1 && incY == 1 {
x = x[:n]
for i, v := range x {
x[i], y[i] = y[i], v
}
return
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
for i := 0; i < n; i++ {
x[ix], y[iy] = y[iy], x[ix]
ix += incX
iy += incY
}
}
// Copyright ©2015 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gonum
import (
"gonum.org/v1/gonum/internal/asm/f64"
)
// Ddot computes the dot product of the two vectors
// \sum_i x[i]*y[i]
func (Implementation) Ddot(n int, x []float64, incX int, y []float64, incY int) float64 {
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if n <= 0 {
if n == 0 {
return 0
}
panic(nLT0)
}
if incX == 1 && incY == 1 {
if len(x) < n {
panic(badX)
}
if len(y) < n {
panic(badY)
}
return f64.DotUnitary(x[:n], y)
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
if ix >= len(x) || ix+(n-1)*incX >= len(x) {
panic(badX)
}
if iy >= len(y) || iy+(n-1)*incY >= len(y) {
panic(badY)
}
return f64.DotInc(x, y, uintptr(n), uintptr(incX), uintptr(incY), uintptr(ix), uintptr(iy))
}
// Code generated by "go generate gonum.org/v1/gonum/blas/gonum”; DO NOT EDIT.
// Copyright ©2015 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gonum
import (
"gonum.org/v1/gonum/internal/asm/f32"
)
// Dsdot computes the dot product of the two vectors
// \sum_i x[i]*y[i]
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Dsdot(n int, x []float32, incX int, y []float32, incY int) float64 {
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if n <= 0 {
if n == 0 {
return 0
}
panic(nLT0)
}
if incX == 1 && incY == 1 {
if len(x) < n {
panic(badX)
}
if len(y) < n {
panic(badY)
}
return f32.DdotUnitary(x[:n], y)
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
if ix >= len(x) || ix+(n-1)*incX >= len(x) {
panic(badX)
}
if iy >= len(y) || iy+(n-1)*incY >= len(y) {
panic(badY)
}
return f32.DdotInc(x, y, uintptr(n), uintptr(incX), uintptr(incY), uintptr(ix), uintptr(iy))
}
// Code generated by "go generate gonum.org/v1/gonum/blas/gonum”; DO NOT EDIT.
// Copyright ©2015 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gonum
import (
"gonum.org/v1/gonum/internal/asm/f32"
)
// Sdot computes the dot product of the two vectors
// \sum_i x[i]*y[i]
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Sdot(n int, x []float32, incX int, y []float32, incY int) float32 {
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if n <= 0 {
if n == 0 {
return 0
}
panic(nLT0)
}
if incX == 1 && incY == 1 {
if len(x) < n {
panic(badX)
}
if len(y) < n {
panic(badY)
}
return f32.DotUnitary(x[:n], y)
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
if ix >= len(x) || ix+(n-1)*incX >= len(x) {
panic(badX)
}
if iy >= len(y) || iy+(n-1)*incY >= len(y) {
panic(badY)
}
return f32.DotInc(x, y, uintptr(n), uintptr(incX), uintptr(incY), uintptr(ix), uintptr(iy))
}
// Code generated by "go generate gonum.org/v1/gonum/blas/gonum”; DO NOT EDIT.
// Copyright ©2015 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gonum
import (
"gonum.org/v1/gonum/internal/asm/f32"
)
// Sdsdot computes the dot product of the two vectors plus a constant
// alpha + \sum_i x[i]*y[i]
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Sdsdot(n int, alpha float32, x []float32, incX int, y []float32, incY int) float32 {
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if n <= 0 {
if n == 0 {
return 0
}
panic(nLT0)
}
if incX == 1 && incY == 1 {
if len(x) < n {
panic(badX)
}
if len(y) < n {
panic(badY)
}
return alpha + float32(f32.DdotUnitary(x[:n], y))
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
if ix >= len(x) || ix+(n-1)*incX >= len(x) {
panic(badX)
}
if iy >= len(y) || iy+(n-1)*incY >= len(y) {
panic(badY)
}
return alpha + float32(f32.DdotInc(x, y, uintptr(n), uintptr(incX), uintptr(incY), uintptr(ix), uintptr(iy)))
}
// Code generated by "go generate gonum.org/v1/gonum/blas/gonum”; DO NOT EDIT.
// Copyright ©2014 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gonum
import (
"runtime"
"sync"
"gonum.org/v1/gonum/blas"
"gonum.org/v1/gonum/internal/asm/f32"
)
// Sgemm performs one of the matrix-matrix operations
// C = alpha * A * B + beta * C
// C = alpha * A^T * B + beta * C
// C = alpha * A * B^T + beta * C
// C = alpha * A^T * B^T + beta * C
// where A is an m×k or k×m dense matrix, B is an n×k or k×n dense matrix, C is
// an m×n matrix, and alpha and beta are scalars. tA and tB specify whether A or
// B are transposed.
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Sgemm(tA, tB blas.Transpose, m, n, k int, alpha float32, a []float32, lda int, b []float32, ldb int, beta float32, c []float32, ldc int) {
if tA != blas.NoTrans && tA != blas.Trans && tA != blas.ConjTrans {
panic(badTranspose)
}
if tB != blas.NoTrans && tB != blas.Trans && tB != blas.ConjTrans {
panic(badTranspose)
}
aTrans := tA == blas.Trans || tA == blas.ConjTrans
if aTrans {
checkSMatrix('a', k, m, a, lda)
} else {
checkSMatrix('a', m, k, a, lda)
}
bTrans := tB == blas.Trans || tB == blas.ConjTrans
if bTrans {
checkSMatrix('b', n, k, b, ldb)
} else {
checkSMatrix('b', k, n, b, ldb)
}
checkSMatrix('c', m, n, c, ldc)
// scale c
if beta != 1 {
if beta == 0 {
for i := 0; i < m; i++ {
ctmp := c[i*ldc : i*ldc+n]
for j := range ctmp {
ctmp[j] = 0
}
}
} else {
for i := 0; i < m; i++ {
ctmp := c[i*ldc : i*ldc+n]
for j := range ctmp {
ctmp[j] *= beta
}
}
}
}
sgemmParallel(aTrans, bTrans, m, n, k, a, lda, b, ldb, c, ldc, alpha)
}
func sgemmParallel(aTrans, bTrans bool, m, n, k int, a []float32, lda int, b []float32, ldb int, c []float32, ldc int, alpha float32) {
// dgemmParallel computes a parallel matrix multiplication by partitioning
// a and b into sub-blocks, and updating c with the multiplication of the sub-block
// In all cases,
// A = [ A_11 A_12 ... A_1j
// A_21 A_22 ... A_2j
// ...
// A_i1 A_i2 ... A_ij]
//
// and same for B. All of the submatrix sizes are blockSize×blockSize except
// at the edges.
//
// In all cases, there is one dimension for each matrix along which
// C must be updated sequentially.
// Cij = \sum_k Aik Bki, (A * B)
// Cij = \sum_k Aki Bkj, (A^T * B)
// Cij = \sum_k Aik Bjk, (A * B^T)
// Cij = \sum_k Aki Bjk, (A^T * B^T)
//
// This code computes one {i, j} block sequentially along the k dimension,
// and computes all of the {i, j} blocks concurrently. This
// partitioning allows Cij to be updated in-place without race-conditions.
// Instead of launching a goroutine for each possible concurrent computation,
// a number of worker goroutines are created and channels are used to pass
// available and completed cases.
//
// http://alexkr.com/docs/matrixmult.pdf is a good reference on matrix-matrix
// multiplies, though this code does not copy matrices to attempt to eliminate
// cache misses.
maxKLen := k
parBlocks := blocks(m, blockSize) * blocks(n, blockSize)
if parBlocks < minParBlock {
// The matrix multiplication is small in the dimensions where it can be
// computed concurrently. Just do it in serial.
sgemmSerial(aTrans, bTrans, m, n, k, a, lda, b, ldb, c, ldc, alpha)
return
}
nWorkers := runtime.GOMAXPROCS(0)
if parBlocks < nWorkers {
nWorkers = parBlocks
}
// There is a tradeoff between the workers having to wait for work
// and a large buffer making operations slow.
buf := buffMul * nWorkers
if buf > parBlocks {
buf = parBlocks
}
sendChan := make(chan subMul, buf)
// Launch workers. A worker receives an {i, j} submatrix of c, and computes
// A_ik B_ki (or the transposed version) storing the result in c_ij. When the
// channel is finally closed, it signals to the waitgroup that it has finished
// computing.
var wg sync.WaitGroup
for i := 0; i < nWorkers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// Make local copies of otherwise global variables to reduce shared memory.
// This has a noticeable effect on benchmarks in some cases.
alpha := alpha
aTrans := aTrans
bTrans := bTrans
m := m
n := n
for sub := range sendChan {
i := sub.i
j := sub.j
leni := blockSize
if i+leni > m {
leni = m - i
}
lenj := blockSize
if j+lenj > n {
lenj = n - j
}
cSub := sliceView32(c, ldc, i, j, leni, lenj)
// Compute A_ik B_kj for all k
for k := 0; k < maxKLen; k += blockSize {
lenk := blockSize
if k+lenk > maxKLen {
lenk = maxKLen - k
}
var aSub, bSub []float32
if aTrans {
aSub = sliceView32(a, lda, k, i, lenk, leni)
} else {
aSub = sliceView32(a, lda, i, k, leni, lenk)
}
if bTrans {
bSub = sliceView32(b, ldb, j, k, lenj, lenk)
} else {
bSub = sliceView32(b, ldb, k, j, lenk, lenj)
}
sgemmSerial(aTrans, bTrans, leni, lenj, lenk, aSub, lda, bSub, ldb, cSub, ldc, alpha)
}
}
}()
}
// Send out all of the {i, j} subblocks for computation.
for i := 0; i < m; i += blockSize {
for j := 0; j < n; j += blockSize {
sendChan <- subMul{
i: i,
j: j,
}
}
}
close(sendChan)
wg.Wait()
}
// sgemmSerial is serial matrix multiply
func sgemmSerial(aTrans, bTrans bool, m, n, k int, a []float32, lda int, b []float32, ldb int, c []float32, ldc int, alpha float32) {
switch {
case !aTrans && !bTrans:
sgemmSerialNotNot(m, n, k, a, lda, b, ldb, c, ldc, alpha)
return
case aTrans && !bTrans:
sgemmSerialTransNot(m, n, k, a, lda, b, ldb, c, ldc, alpha)
return
case !aTrans && bTrans:
sgemmSerialNotTrans(m, n, k, a, lda, b, ldb, c, ldc, alpha)
return
case aTrans && bTrans:
sgemmSerialTransTrans(m, n, k, a, lda, b, ldb, c, ldc, alpha)
return
default:
panic("unreachable")
}
}
// sgemmSerial where neither a nor b are transposed
func sgemmSerialNotNot(m, n, k int, a []float32, lda int, b []float32, ldb int, c []float32, ldc int, alpha float32) {
// This style is used instead of the literal [i*stride +j]) is used because
// approximately 5 times faster as of go 1.3.
for i := 0; i < m; i++ {
ctmp := c[i*ldc : i*ldc+n]
for l, v := range a[i*lda : i*lda+k] {
tmp := alpha * v
if tmp != 0 {
f32.AxpyUnitaryTo(ctmp, tmp, b[l*ldb:l*ldb+n], ctmp)
}
}
}
}
// sgemmSerial where neither a is transposed and b is not
func sgemmSerialTransNot(m, n, k int, a []float32, lda int, b []float32, ldb int, c []float32, ldc int, alpha float32) {
// This style is used instead of the literal [i*stride +j]) is used because
// approximately 5 times faster as of go 1.3.
for l := 0; l < k; l++ {
btmp := b[l*ldb : l*ldb+n]
for i, v := range a[l*lda : l*lda+m] {
tmp := alpha * v
if tmp != 0 {
ctmp := c[i*ldc : i*ldc+n]
f32.AxpyUnitaryTo(ctmp, tmp, btmp, ctmp)
}
}
}
}
// sgemmSerial where neither a is not transposed and b is
func sgemmSerialNotTrans(m, n, k int, a []float32, lda int, b []float32, ldb int, c []float32, ldc int, alpha float32) {
// This style is used instead of the literal [i*stride +j]) is used because
// approximately 5 times faster as of go 1.3.
for i := 0; i < m; i++ {
atmp := a[i*lda : i*lda+k]
ctmp := c[i*ldc : i*ldc+n]
for j := 0; j < n; j++ {
ctmp[j] += alpha * f32.DotUnitary(atmp, b[j*ldb:j*ldb+k])
}
}
}
// sgemmSerial where both are transposed
func sgemmSerialTransTrans(m, n, k int, a []float32, lda int, b []float32, ldb int, c []float32, ldc int, alpha float32) {
// This style is used instead of the literal [i*stride +j]) is used because
// approximately 5 times faster as of go 1.3.
for l := 0; l < k; l++ {
for i, v := range a[l*lda : l*lda+m] {
tmp := alpha * v
if tmp != 0 {
ctmp := c[i*ldc : i*ldc+n]
f32.AxpyInc(tmp, b[l:], ctmp, uintptr(n), uintptr(ldb), 1, 0, 0)
}
}
}
}
func sliceView32(a []float32, lda, i, j, r, c int) []float32 {
return a[i*lda+j : (i+r-1)*lda+j+c]
}
#!/usr/bin/env bash
# Copyright ©2015 The Gonum Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
WARNING='//\
// Float32 implementations are autogenerated and not directly tested.\
'
# Level1 routines.
echo Generating level1single.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas/gonum”; DO NOT EDIT.\n' > level1single.go
cat level1double.go \
| gofmt -r 'blas.Float64Level1 -> blas.Float32Level1' \
\
| gofmt -r 'float64 -> float32' \
| gofmt -r 'blas.DrotmParams -> blas.SrotmParams' \
\
| gofmt -r 'f64.AxpyInc -> f32.AxpyInc' \
| gofmt -r 'f64.AxpyIncTo -> f32.AxpyIncTo' \
| gofmt -r 'f64.AxpyUnitary -> f32.AxpyUnitary' \
| gofmt -r 'f64.AxpyUnitaryTo -> f32.AxpyUnitaryTo' \
| gofmt -r 'f64.DotUnitary -> f32.DotUnitary' \
| gofmt -r 'f64.ScalInc -> f32.ScalInc' \
| gofmt -r 'f64.ScalUnitary -> f32.ScalUnitary' \
\
| sed -e "s_^\(func (Implementation) \)D\(.*\)\$_$WARNING\1S\2_" \
-e 's_^// D_// S_' \
-e "s_^\(func (Implementation) \)Id\(.*\)\$_$WARNING\1Is\2_" \
-e 's_^// Id_// Is_' \
-e 's_"gonum.org/v1/gonum/internal/asm/f64"_"gonum.org/v1/gonum/internal/asm/f32"_' \
-e 's_"math"_math "gonum.org/v1/gonum/internal/math32"_' \
>> level1single.go
echo Generating level1single_sdot.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas/gonum”; DO NOT EDIT.\n' > level1single_sdot.go
cat level1double_ddot.go \
| gofmt -r 'float64 -> float32' \
\
| gofmt -r 'f64.DotInc -> f32.DotInc' \
| gofmt -r 'f64.DotUnitary -> f32.DotUnitary' \
\
| sed -e "s_^\(func (Implementation) \)D\(.*\)\$_$WARNING\1S\2_" \
-e 's_^// D_// S_' \
-e 's_"gonum.org/v1/gonum/internal/asm/f64"_"gonum.org/v1/gonum/internal/asm/f32"_' \
>> level1single_sdot.go
echo Generating level1single_dsdot.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas/gonum”; DO NOT EDIT.\n' > level1single_dsdot.go
cat level1double_ddot.go \
| gofmt -r '[]float64 -> []float32' \
\
| gofmt -r 'f64.DotInc -> f32.DdotInc' \
| gofmt -r 'f64.DotUnitary -> f32.DdotUnitary' \
\
| sed -e "s_^\(func (Implementation) \)D\(.*\)\$_$WARNING\1Ds\2_" \
-e 's_^// D_// Ds_' \
-e 's_"gonum.org/v1/gonum/internal/asm/f64"_"gonum.org/v1/gonum/internal/asm/f32"_' \
>> level1single_dsdot.go
echo Generating level1single_sdsdot.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas/gonum”; DO NOT EDIT.\n' > level1single_sdsdot.go
cat level1double_ddot.go \
| gofmt -r 'float64 -> float32' \
\
| gofmt -r 'f64.DotInc(x, y, f(n), f(incX), f(incY), f(ix), f(iy)) -> alpha + float32(f32.DdotInc(x, y, f(n), f(incX), f(incY), f(ix), f(iy)))' \
| gofmt -r 'f64.DotUnitary(a, b) -> alpha + float32(f32.DdotUnitary(a, b))' \
\
| sed -e "s_^\(func (Implementation) \)D\(.*\)\$_$WARNING\1Sds\2_" \
-e 's_^// D\(.*\)$_// Sds\1 plus a constant_' \
-e 's_\\sum_alpha + \\sum_' \
-e 's/n int/n int, alpha float32/' \
-e 's_"gonum.org/v1/gonum/internal/asm/f64"_"gonum.org/v1/gonum/internal/asm/f32"_' \
>> level1single_sdsdot.go
# Level2 routines.
echo Generating level2single.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas/gonum”; DO NOT EDIT.\n' > level2single.go
cat level2double.go \
| gofmt -r 'blas.Float64Level2 -> blas.Float32Level2' \
\
| gofmt -r 'float64 -> float32' \
\
| gofmt -r 'Dscal -> Sscal' \
\
| gofmt -r 'f64.AxpyInc -> f32.AxpyInc' \
| gofmt -r 'f64.AxpyIncTo -> f32.AxpyIncTo' \
| gofmt -r 'f64.AxpyUnitary -> f32.AxpyUnitary' \
| gofmt -r 'f64.AxpyUnitaryTo -> f32.AxpyUnitaryTo' \
| gofmt -r 'f64.DotInc -> f32.DotInc' \
| gofmt -r 'f64.DotUnitary -> f32.DotUnitary' \
| gofmt -r 'f64.Ger -> f32.Ger' \
\
| sed -e "s_^\(func (Implementation) \)D\(.*\)\$_$WARNING\1S\2_" \
-e 's_^// D_// S_' \
-e 's_"gonum.org/v1/gonum/internal/asm/f64"_"gonum.org/v1/gonum/internal/asm/f32"_' \
>> level2single.go
# Level3 routines.
echo Generating level3single.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas/gonum”; DO NOT EDIT.\n' > level3single.go
cat level3double.go \
| gofmt -r 'blas.Float64Level3 -> blas.Float32Level3' \
\
| gofmt -r 'float64 -> float32' \
\
| gofmt -r 'f64.AxpyUnitaryTo -> f32.AxpyUnitaryTo' \
| gofmt -r 'f64.DotUnitary -> f32.DotUnitary' \
\
| sed -e "s_^\(func (Implementation) \)D\(.*\)\$_$WARNING\1S\2_" \
-e 's_^// D_// S_' \
-e 's_"gonum.org/v1/gonum/internal/asm/f64"_"gonum.org/v1/gonum/internal/asm/f32"_' \
>> level3single.go
echo Generating sgemm.go
echo -e '// Code generated by "go generate gonum.org/v1/gonum/blas/gonum”; DO NOT EDIT.\n' > sgemm.go
cat dgemm.go \
| gofmt -r 'float64 -> float32' \
| gofmt -r 'sliceView64 -> sliceView32' \
| gofmt -r 'checkDMatrix -> checkSMatrix' \
\
| gofmt -r 'dgemmParallel -> sgemmParallel' \
| gofmt -r 'computeNumBlocks64 -> computeNumBlocks32' \
| gofmt -r 'dgemmSerial -> sgemmSerial' \
| gofmt -r 'dgemmSerialNotNot -> sgemmSerialNotNot' \
| gofmt -r 'dgemmSerialTransNot -> sgemmSerialTransNot' \
| gofmt -r 'dgemmSerialNotTrans -> sgemmSerialNotTrans' \
| gofmt -r 'dgemmSerialTransTrans -> sgemmSerialTransTrans' \
\
| gofmt -r 'f64.AxpyInc -> f32.AxpyInc' \
| gofmt -r 'f64.AxpyIncTo -> f32.AxpyIncTo' \
| gofmt -r 'f64.AxpyUnitaryTo -> f32.AxpyUnitaryTo' \
| gofmt -r 'f64.DotUnitary -> f32.DotUnitary' \
\
| sed -e "s_^\(func (Implementation) \)D\(.*\)\$_$WARNING\1S\2_" \
-e 's_^// D_// S_' \
-e 's_^// d_// s_' \
-e 's_"gonum.org/v1/gonum/internal/asm/f64"_"gonum.org/v1/gonum/internal/asm/f32"_' \
>> sgemm.go
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"floats.go",
],
importmap = "k8s.io/kubernetes/vendor/gonum.org/v1/gonum/floats",
importpath = "gonum.org/v1/gonum/floats",
visibility = ["//visibility:public"],
deps = ["//vendor/gonum.org/v1/gonum/internal/asm/f64:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
# Gonum floats [![GoDoc](https://godoc.org/gonum.org/v1/gonum/floats?status.svg)](https://godoc.org/gonum.org/v1/gonum/floats)
Package floats provides a set of helper routines for dealing with slices of float64.
The functions avoid allocations to allow for use within tight loops without garbage collection overhead.
// Copyright ©2017 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package floats provides a set of helper routines for dealing with slices
// of float64. The functions avoid allocations to allow for use within tight
// loops without garbage collection overhead.
//
// The convention used is that when a slice is being modified in place, it has
// the name dst.
package floats
test.out
\ No newline at end of file
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"graph.go",
"multigraph.go",
"undirect.go",
],
importmap = "k8s.io/kubernetes/vendor/gonum.org/v1/gonum/graph",
importpath = "gonum.org/v1/gonum/graph",
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/gonum.org/v1/gonum/graph/encoding:all-srcs",
"//vendor/gonum.org/v1/gonum/graph/formats/dot:all-srcs",
"//vendor/gonum.org/v1/gonum/graph/internal/ordered:all-srcs",
"//vendor/gonum.org/v1/gonum/graph/internal/set:all-srcs",
"//vendor/gonum.org/v1/gonum/graph/internal/uid:all-srcs",
"//vendor/gonum.org/v1/gonum/graph/simple:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
# Gonum graph [![GoDoc](https://godoc.org/gonum.org/v1/gonum/graph?status.svg)](https://godoc.org/gonum.org/v1/gonum/graph)
This is a generalized graph package for the Go language.
// Copyright ©2014 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package graph defines graph interfaces.
package graph
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"encoding.go",
],
importmap = "k8s.io/kubernetes/vendor/gonum.org/v1/gonum/graph/encoding",
importpath = "gonum.org/v1/gonum/graph/encoding",
visibility = ["//visibility:public"],
deps = ["//vendor/gonum.org/v1/gonum/graph:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/gonum.org/v1/gonum/graph/encoding/dot:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Copyright ©2017 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package encoding provides a common graph encoding API.
package encoding
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"decode.go",
"doc.go",
"dot.go",
"encode.go",
],
importmap = "k8s.io/kubernetes/vendor/gonum.org/v1/gonum/graph/encoding/dot",
importpath = "gonum.org/v1/gonum/graph/encoding/dot",
visibility = ["//visibility:public"],
deps = [
"//vendor/gonum.org/v1/gonum/graph:go_default_library",
"//vendor/gonum.org/v1/gonum/graph/encoding:go_default_library",
"//vendor/gonum.org/v1/gonum/graph/formats/dot:go_default_library",
"//vendor/gonum.org/v1/gonum/graph/formats/dot/ast:go_default_library",
"//vendor/gonum.org/v1/gonum/graph/internal/ordered:go_default_library",
"//vendor/gonum.org/v1/gonum/graph/internal/set:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Copyright ©2017 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package dot
import (
"fmt"
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/encoding"
"gonum.org/v1/gonum/graph/formats/dot"
"gonum.org/v1/gonum/graph/formats/dot/ast"
"gonum.org/v1/gonum/graph/internal/set"
)
// AttributeSetters is implemented by graph values that can set global
// DOT attributes.
type AttributeSetters interface {
// DOTAttributeSetters returns the global attribute setters.
DOTAttributeSetters() (graph, node, edge encoding.AttributeSetter)
}
// DOTIDSetter is implemented by types that can set a DOT ID.
type DOTIDSetter interface {
SetDOTID(id string)
}
// PortSetter is implemented by graph.Edge and graph.Line that can set
// the DOT port and compass directions of an edge.
type PortSetter interface {
// SetFromPort sets the From port and
// compass direction of the receiver.
SetFromPort(port, compass string) error
// SetToPort sets the To port and compass
// direction of the receiver.
SetToPort(port, compass string) error
}
// Unmarshal parses the Graphviz DOT-encoded data and stores the result in dst.
func Unmarshal(data []byte, dst encoding.Builder) error {
file, err := dot.ParseBytes(data)
if err != nil {
return err
}
if len(file.Graphs) != 1 {
return fmt.Errorf("invalid number of graphs; expected 1, got %d", len(file.Graphs))
}
return copyGraph(dst, file.Graphs[0])
}
// copyGraph copies the nodes and edges from the Graphviz AST source graph to
// the destination graph. Edge direction is maintained if present.
func copyGraph(dst encoding.Builder, src *ast.Graph) (err error) {
defer func() {
switch e := recover().(type) {
case nil:
case error:
err = e
default:
panic(e)
}
}()
gen := &generator{
directed: src.Directed,
ids: make(map[string]graph.Node),
}
if dst, ok := dst.(DOTIDSetter); ok {
dst.SetDOTID(src.ID)
}
if a, ok := dst.(AttributeSetters); ok {
gen.graphAttr, gen.nodeAttr, gen.edgeAttr = a.DOTAttributeSetters()
}
for _, stmt := range src.Stmts {
gen.addStmt(dst, stmt)
}
return err
}
// A generator keeps track of the information required for generating a gonum
// graph from a dot AST graph.
type generator struct {
// Directed graph.
directed bool
// Map from dot AST node ID to gonum node.
ids map[string]graph.Node
// Nodes processed within the context of a subgraph, that is to be used as a
// vertex of an edge.
subNodes []graph.Node
// Stack of start indices into the subgraph node slice. The top element
// corresponds to the start index of the active (or inner-most) subgraph.
subStart []int
// graphAttr, nodeAttr and edgeAttr are global graph attributes.
graphAttr, nodeAttr, edgeAttr encoding.AttributeSetter
}
// node returns the gonum node corresponding to the given dot AST node ID,
// generating a new such node if none exist.
func (gen *generator) node(dst encoding.Builder, id string) graph.Node {
if n, ok := gen.ids[id]; ok {
return n
}
n := dst.NewNode()
dst.AddNode(n)
if n, ok := n.(DOTIDSetter); ok {
n.SetDOTID(id)
}
gen.ids[id] = n
// Check if within the context of a subgraph, that is to be used as a vertex
// of an edge.
if gen.isInSubgraph() {
// Append node processed within the context of a subgraph, that is to be
// used as a vertex of an edge
gen.appendSubgraphNode(n)
}
return n
}
// addStmt adds the given statement to the graph.
func (gen *generator) addStmt(dst encoding.Builder, stmt ast.Stmt) {
switch stmt := stmt.(type) {
case *ast.NodeStmt:
n, ok := gen.node(dst, stmt.Node.ID).(encoding.AttributeSetter)
if !ok {
return
}
for _, attr := range stmt.Attrs {
a := encoding.Attribute{
Key: attr.Key,
Value: attr.Val,
}
if err := n.SetAttribute(a); err != nil {
panic(fmt.Errorf("unable to unmarshal node DOT attribute (%s=%s)", a.Key, a.Value))
}
}
case *ast.EdgeStmt:
gen.addEdgeStmt(dst, stmt)
case *ast.AttrStmt:
var n encoding.AttributeSetter
var dst string
switch stmt.Kind {
case ast.GraphKind:
if gen.graphAttr == nil {
return
}
n = gen.graphAttr
dst = "graph"
case ast.NodeKind:
if gen.nodeAttr == nil {
return
}
n = gen.nodeAttr
dst = "node"
case ast.EdgeKind:
if gen.edgeAttr == nil {
return
}
n = gen.edgeAttr
dst = "edge"
default:
panic("unreachable")
}
for _, attr := range stmt.Attrs {
a := encoding.Attribute{
Key: attr.Key,
Value: attr.Val,
}
if err := n.SetAttribute(a); err != nil {
panic(fmt.Errorf("unable to unmarshal global %s DOT attribute (%s=%s)", dst, a.Key, a.Value))
}
}
case *ast.Attr:
// ignore.
case *ast.Subgraph:
for _, stmt := range stmt.Stmts {
gen.addStmt(dst, stmt)
}
default:
panic(fmt.Sprintf("unknown statement type %T", stmt))
}
}
// applyPortsToEdge applies the available port metadata from an ast.Edge
// to a graph.Edge
func applyPortsToEdge(from ast.Vertex, to *ast.Edge, edge graph.Edge) {
if ps, isPortSetter := edge.(PortSetter); isPortSetter {
if n, vertexIsNode := from.(*ast.Node); vertexIsNode {
if n.Port != nil {
err := ps.SetFromPort(n.Port.ID, n.Port.CompassPoint.String())
if err != nil {
panic(fmt.Errorf("unable to unmarshal edge port (:%s:%s)", n.Port.ID, n.Port.CompassPoint.String()))
}
}
}
if n, vertexIsNode := to.Vertex.(*ast.Node); vertexIsNode {
if n.Port != nil {
err := ps.SetToPort(n.Port.ID, n.Port.CompassPoint.String())
if err != nil {
panic(fmt.Errorf("unable to unmarshal edge DOT port (:%s:%s)", n.Port.ID, n.Port.CompassPoint.String()))
}
}
}
}
}
// addEdgeStmt adds the given edge statement to the graph.
func (gen *generator) addEdgeStmt(dst encoding.Builder, stmt *ast.EdgeStmt) {
fs := gen.addVertex(dst, stmt.From)
ts := gen.addEdge(dst, stmt.To, stmt.Attrs)
for _, f := range fs {
for _, t := range ts {
edge := dst.NewEdge(f, t)
dst.SetEdge(edge)
applyPortsToEdge(stmt.From, stmt.To, edge)
addEdgeAttrs(edge, stmt.Attrs)
}
}
}
// addVertex adds the given vertex to the graph, and returns its set of nodes.
func (gen *generator) addVertex(dst encoding.Builder, v ast.Vertex) []graph.Node {
switch v := v.(type) {
case *ast.Node:
n := gen.node(dst, v.ID)
return []graph.Node{n}
case *ast.Subgraph:
gen.pushSubgraph()
for _, stmt := range v.Stmts {
gen.addStmt(dst, stmt)
}
return gen.popSubgraph()
default:
panic(fmt.Sprintf("unknown vertex type %T", v))
}
}
// addEdge adds the given edge to the graph, and returns its set of nodes.
func (gen *generator) addEdge(dst encoding.Builder, to *ast.Edge, attrs []*ast.Attr) []graph.Node {
if !gen.directed && to.Directed {
panic(fmt.Errorf("directed edge to %v in undirected graph", to.Vertex))
}
fs := gen.addVertex(dst, to.Vertex)
if to.To != nil {
ts := gen.addEdge(dst, to.To, attrs)
for _, f := range fs {
for _, t := range ts {
edge := dst.NewEdge(f, t)
dst.SetEdge(edge)
applyPortsToEdge(to.Vertex, to.To, edge)
addEdgeAttrs(edge, attrs)
}
}
}
return fs
}
// pushSubgraph pushes the node start index of the active subgraph onto the
// stack.
func (gen *generator) pushSubgraph() {
gen.subStart = append(gen.subStart, len(gen.subNodes))
}
// popSubgraph pops the node start index of the active subgraph from the stack,
// and returns the nodes processed since.
func (gen *generator) popSubgraph() []graph.Node {
// Get nodes processed since the subgraph became active.
start := gen.subStart[len(gen.subStart)-1]
// TODO: Figure out a better way to store subgraph nodes, so that duplicates
// may not occur.
nodes := unique(gen.subNodes[start:])
// Remove subgraph from stack.
gen.subStart = gen.subStart[:len(gen.subStart)-1]
if len(gen.subStart) == 0 {
// Remove subgraph nodes when the bottom-most subgraph has been processed.
gen.subNodes = gen.subNodes[:0]
}
return nodes
}
// unique returns the set of unique nodes contained within ns.
func unique(ns []graph.Node) []graph.Node {
var nodes []graph.Node
seen := make(set.Int64s)
for _, n := range ns {
id := n.ID()
if seen.Has(id) {
// skip duplicate node
continue
}
seen.Add(id)
nodes = append(nodes, n)
}
return nodes
}
// isInSubgraph reports whether the active context is within a subgraph, that is
// to be used as a vertex of an edge.
func (gen *generator) isInSubgraph() bool {
return len(gen.subStart) > 0
}
// appendSubgraphNode appends the given node to the slice of nodes processed
// within the context of a subgraph.
func (gen *generator) appendSubgraphNode(n graph.Node) {
gen.subNodes = append(gen.subNodes, n)
}
// addEdgeAttrs adds the attributes to the given edge.
func addEdgeAttrs(edge graph.Edge, attrs []*ast.Attr) {
e, ok := edge.(encoding.AttributeSetter)
if !ok {
return
}
for _, attr := range attrs {
a := encoding.Attribute{
Key: attr.Key,
Value: attr.Val,
}
if err := e.SetAttribute(a); err != nil {
panic(fmt.Errorf("unable to unmarshal edge DOT attribute (%s=%s)", a.Key, a.Value))
}
}
}
// Copyright ©2017 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package dot implements GraphViz DOT marshaling and unmarshaling of graphs.
//
// See the GraphViz DOT Guide and the DOT grammar for more information
// on using specific aspects of the DOT language:
//
// DOT Guide: http://www.graphviz.org/Documentation/dotguide.pdf
//
// DOT grammar: http://www.graphviz.org/doc/info/lang.html
//
package dot
// Copyright ©2015 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package dot
// Copyright ©2015 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package dot
import (
"bytes"
"errors"
"fmt"
"sort"
"strings"
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/encoding"
"gonum.org/v1/gonum/graph/internal/ordered"
)
// Node is a DOT graph node.
type Node interface {
// DOTID returns a DOT node ID.
//
// An ID is one of the following:
//
// - a string of alphabetic ([a-zA-Z\x80-\xff]) characters, underscores ('_').
// digits ([0-9]), not beginning with a digit.
// - a numeral [-]?(.[0-9]+ | [0-9]+(.[0-9]*)?).
// - a double-quoted string ("...") possibly containing escaped quotes (\").
// - an HTML string (<...>).
DOTID() string
}
// Attributers are graph.Graph values that specify top-level DOT
// attributes.
type Attributers interface {
DOTAttributers() (graph, node, edge encoding.Attributer)
}
// Porter defines the behavior of graph.Edge values that can specify
// connection ports for their end points. The returned port corresponds
// to the the DOT node port to be used by the edge, compass corresponds
// to DOT compass point to which the edge will be aimed.
type Porter interface {
// FromPort returns the port and compass for the
// From node of a graph.Edge.
FromPort() (port, compass string)
// ToPort returns the port and compass for the
// To node of a graph.Edge.
ToPort() (port, compass string)
}
// Structurer represents a graph.Graph that can define subgraphs.
type Structurer interface {
Structure() []Graph
}
// Graph wraps named graph.Graph values.
type Graph interface {
graph.Graph
DOTID() string
}
// Subgrapher wraps graph.Node values that represent subgraphs.
type Subgrapher interface {
Subgraph() graph.Graph
}
// Marshal returns the DOT encoding for the graph g, applying the prefix
// and indent to the encoding. Name is used to specify the graph name. If
// name is empty and g implements Graph, the returned string from DOTID
// will be used. If strict is true the output bytes will be prefixed with
// the DOT "strict" keyword.
//
// Graph serialization will work for a graph.Graph without modification,
// however, advanced GraphViz DOT features provided by Marshal depend on
// implementation of the Node, Attributer, Porter, Attributers, Structurer,
// Subgrapher and Graph interfaces.
func Marshal(g graph.Graph, name, prefix, indent string, strict bool) ([]byte, error) {
var p printer
p.indent = indent
p.prefix = prefix
p.visited = make(map[edge]bool)
if strict {
p.buf.WriteString("strict ")
}
err := p.print(g, name, false, false)
if err != nil {
return nil, err
}
return p.buf.Bytes(), nil
}
type printer struct {
buf bytes.Buffer
prefix string
indent string
depth int
visited map[edge]bool
err error
}
type edge struct {
inGraph string
from, to int64
}
func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool) error {
nodes := g.Nodes()
sort.Sort(ordered.ByID(nodes))
p.buf.WriteString(p.prefix)
if needsIndent {
for i := 0; i < p.depth; i++ {
p.buf.WriteString(p.indent)
}
}
_, isDirected := g.(graph.Directed)
if isSubgraph {
p.buf.WriteString("sub")
} else if isDirected {
p.buf.WriteString("di")
}
p.buf.WriteString("graph")
if name == "" {
if g, ok := g.(Graph); ok {
name = g.DOTID()
}
}
if name != "" {
p.buf.WriteByte(' ')
p.buf.WriteString(name)
}
p.openBlock(" {")
if a, ok := g.(Attributers); ok {
p.writeAttributeComplex(a)
}
if s, ok := g.(Structurer); ok {
for _, g := range s.Structure() {
_, subIsDirected := g.(graph.Directed)
if subIsDirected != isDirected {
return errors.New("dot: mismatched graph type")
}
p.buf.WriteByte('\n')
p.print(g, g.DOTID(), true, true)
}
}
havePrintedNodeHeader := false
for _, n := range nodes {
if s, ok := n.(Subgrapher); ok {
// If the node is not linked to any other node
// the graph needs to be written now.
if len(g.From(n.ID())) == 0 {
g := s.Subgraph()
_, subIsDirected := g.(graph.Directed)
if subIsDirected != isDirected {
return errors.New("dot: mismatched graph type")
}
if !havePrintedNodeHeader {
p.newline()
p.buf.WriteString("// Node definitions.")
havePrintedNodeHeader = true
}
p.newline()
p.print(g, graphID(g, n), false, true)
}
continue
}
if !havePrintedNodeHeader {
p.newline()
p.buf.WriteString("// Node definitions.")
havePrintedNodeHeader = true
}
p.newline()
p.writeNode(n)
if a, ok := n.(encoding.Attributer); ok {
p.writeAttributeList(a)
}
p.buf.WriteByte(';')
}
havePrintedEdgeHeader := false
for _, n := range nodes {
nid := n.ID()
to := g.From(nid)
sort.Sort(ordered.ByID(to))
for _, t := range to {
tid := t.ID()
if isDirected {
if p.visited[edge{inGraph: name, from: nid, to: tid}] {
continue
}
p.visited[edge{inGraph: name, from: nid, to: tid}] = true
} else {
if p.visited[edge{inGraph: name, from: nid, to: tid}] {
continue
}
p.visited[edge{inGraph: name, from: nid, to: tid}] = true
p.visited[edge{inGraph: name, from: tid, to: n.ID()}] = true
}
if !havePrintedEdgeHeader {
p.buf.WriteByte('\n')
p.buf.WriteString(strings.TrimRight(p.prefix, " \t\n")) // Trim whitespace suffix.
p.newline()
p.buf.WriteString("// Edge definitions.")
havePrintedEdgeHeader = true
}
p.newline()
if s, ok := n.(Subgrapher); ok {
g := s.Subgraph()
_, subIsDirected := g.(graph.Directed)
if subIsDirected != isDirected {
return errors.New("dot: mismatched graph type")
}
p.print(g, graphID(g, n), false, true)
} else {
p.writeNode(n)
}
e := g.Edge(nid, tid)
porter, edgeIsPorter := e.(Porter)
if edgeIsPorter {
if e.From().ID() == nid {
p.writePorts(porter.FromPort())
} else {
p.writePorts(porter.ToPort())
}
}
if isDirected {
p.buf.WriteString(" -> ")
} else {
p.buf.WriteString(" -- ")
}
if s, ok := t.(Subgrapher); ok {
g := s.Subgraph()
_, subIsDirected := g.(graph.Directed)
if subIsDirected != isDirected {
return errors.New("dot: mismatched graph type")
}
p.print(g, graphID(g, t), false, true)
} else {
p.writeNode(t)
}
if edgeIsPorter {
if e.From().ID() == nid {
p.writePorts(porter.ToPort())
} else {
p.writePorts(porter.FromPort())
}
}
if a, ok := g.Edge(nid, tid).(encoding.Attributer); ok {
p.writeAttributeList(a)
}
p.buf.WriteByte(';')
}
}
p.closeBlock("}")
return nil
}
func (p *printer) writeNode(n graph.Node) {
p.buf.WriteString(nodeID(n))
}
func (p *printer) writePorts(port, cp string) {
if port != "" {
p.buf.WriteByte(':')
p.buf.WriteString(port)
}
if cp != "" {
p.buf.WriteByte(':')
p.buf.WriteString(cp)
}
}
func nodeID(n graph.Node) string {
switch n := n.(type) {
case Node:
return n.DOTID()
default:
return fmt.Sprint(n.ID())
}
}
func graphID(g graph.Graph, n graph.Node) string {
switch g := g.(type) {
case Node:
return g.DOTID()
default:
return nodeID(n)
}
}
func (p *printer) writeAttributeList(a encoding.Attributer) {
attributes := a.Attributes()
switch len(attributes) {
case 0:
case 1:
p.buf.WriteString(" [")
p.buf.WriteString(attributes[0].Key)
p.buf.WriteByte('=')
p.buf.WriteString(attributes[0].Value)
p.buf.WriteString("]")
default:
p.openBlock(" [")
for _, att := range attributes {
p.newline()
p.buf.WriteString(att.Key)
p.buf.WriteByte('=')
p.buf.WriteString(att.Value)
}
p.closeBlock("]")
}
}
var attType = []string{"graph", "node", "edge"}
func (p *printer) writeAttributeComplex(ca Attributers) {
g, n, e := ca.DOTAttributers()
haveWrittenBlock := false
for i, a := range []encoding.Attributer{g, n, e} {
attributes := a.Attributes()
if len(attributes) == 0 {
continue
}
if haveWrittenBlock {
p.buf.WriteByte(';')
}
p.newline()
p.buf.WriteString(attType[i])
p.openBlock(" [")
for _, att := range attributes {
p.newline()
p.buf.WriteString(att.Key)
p.buf.WriteByte('=')
p.buf.WriteString(att.Value)
}
p.closeBlock("]")
haveWrittenBlock = true
}
if haveWrittenBlock {
p.buf.WriteString(";\n")
}
}
func (p *printer) newline() {
p.buf.WriteByte('\n')
p.buf.WriteString(p.prefix)
for i := 0; i < p.depth; i++ {
p.buf.WriteString(p.indent)
}
}
func (p *printer) openBlock(b string) {
p.buf.WriteString(b)
p.depth++
}
func (p *printer) closeBlock(b string) {
p.depth--
p.newline()
p.buf.WriteString(b)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment